From e05b61a9db90331d0b0557b856943cb0d38725ea Mon Sep 17 00:00:00 2001 From: eugenvoronov <104138627+eugenvoronov@users.noreply.github.com> Date: Tue, 19 Dec 2023 19:35:12 +0300 Subject: [PATCH 001/104] =?UTF-8?q?[Job=20Launcher]=20Launch=20h=D0=A1aptc?= =?UTF-8?q?ha=20jobs=20(#1220)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added hCaptcha job type * Updated job service * Updated generate manifest method * Added immo type * Added storage module and updated dependencies * Add storage module * Implemented json stringify module and fixed storage module * Added unit tests coverage * Removed unused exports * Removed job api key param * Resolved comments * Improved Storage module * Updated upload file method * Updated task data uri * Resolved comments * Added ground truth validator * Added site key * Removed undefined decorator * Added token fix * Updated unit tests, funds, fixes manifests * Logs * Updated manifest properties * Removed requester_question_example for categorization * Removed constrain from buildHCaptchaRestrictedAnswerSet * Updated constrain from buildHCaptchaRestrictedAnswerSet * Added default case for RestrictedAnswerSet * Removed logs * Resolved PRs comments * Resolved PRs comments * Added create manifest unit tests * Resolved PR comments * [Job Launcher] Lint fixes (#1249) * Lint fixes * Resolved error * Fixed lint error * Update packages/apps/job-launcher/server/src/main.ts Co-authored-by: portuu3 <61605646+portuu3@users.noreply.github.com> * merge develop into feature/launcher/hcaptcha * generate yarn.lock * remove duplicate * remove duplicates after merge * remove duplicate and clean imports * disable hcaptcha endpoint --------- Co-authored-by: portuu3 <61605646+portuu3@users.noreply.github.com> Co-authored-by: portuu3 --- .../apps/job-launcher/server/.env.example | 8 + .../apps/job-launcher/server/package.json | 3 + .../server/src/common/config/env.ts | 14 + .../server/src/common/constants/errors.ts | 4 + .../server/src/common/constants/index.ts | 15 + .../server/src/common/enums/job.ts | 395 +++++++++ .../server/src/common/enums/storage.ts | 9 + .../server/src/common/enums/webhook.ts | 1 + .../server/src/common/interfaces/index.ts | 1 + .../server/src/common/interfaces/s3.ts | 6 +- .../server/src/common/utils/index.ts | 19 + .../server/src/modules/job/job.controller.ts | 15 + .../server/src/modules/job/job.dto.ts | 298 ++++++- .../src/modules/job/job.service.spec.ts | 804 ++++++++++++++---- .../server/src/modules/job/job.service.ts | 605 ++++++++++--- .../modules/storage/storage.service.spec.ts | 83 +- .../src/modules/storage/storage.service.ts | 85 +- .../job-launcher/server/test/constants.ts | 53 ++ yarn.lock | 750 ++++++++-------- 19 files changed, 2416 insertions(+), 752 deletions(-) create mode 100644 packages/apps/job-launcher/server/src/common/enums/storage.ts diff --git a/packages/apps/job-launcher/server/.env.example b/packages/apps/job-launcher/server/.env.example index 3d465c8dd6..ea27e61be7 100644 --- a/packages/apps/job-launcher/server/.env.example +++ b/packages/apps/job-launcher/server/.env.example @@ -17,12 +17,20 @@ POSTGRES_LOGGING='all' #Web3 WEB3_ENV=testnet WEB3_PRIVATE_KEY= +#GAS_PRICE_MULTIPLIER= +PGP_PRIVATE_KEY= +PGP_PUBLIC_KEY= JOB_LAUNCHER_FEE=1 FORTUNE_EXCHANGE_ORACLE_ADDRESS= FORTUNE_RECORDING_ORACLE_ADDRESS= CVAT_EXCHANGE_ORACLE_ADDRESS= CVAT_RECORDING_ORACLE_ADDRESS= REPUTATION_ORACLE_ADDRESS= +HCAPTCHA_RECORDING_ORACLE_URI= +HCAPTCHA_REPUTATION_ORACLE_URI= +HCAPTCHA_ORACLE_ADDRESS= +HCAPTCHA_PGP_PUBLIC_KEY= +HCAPTCHA_SITE_KEY= # Auth HASH_SECRET=a328af3fc1dad15342cc3d68936008fa diff --git a/packages/apps/job-launcher/server/package.json b/packages/apps/job-launcher/server/package.json index b2f505f140..53c17327f7 100644 --- a/packages/apps/job-launcher/server/package.json +++ b/packages/apps/job-launcher/server/package.json @@ -52,6 +52,7 @@ "ethers": "^5.7.2", "express-session": "^1.17.3", "joi": "^17.9.2", + "json-stable-stringify": "^1.0.2", "nestjs-minio-client": "^2.2.0", "passport": "^0.6.0", "passport-jwt": "^4.0.1", @@ -71,9 +72,11 @@ "@types/bcrypt": "^5.0.2", "@types/express": "^4.17.13", "@types/jest": "29.5.1", + "@types/json-stable-stringify": "^1.0.36", "@types/node": "20.10.4", "@types/supertest": "^2.0.15", "@types/zxcvbn": "4.4.1", + "@types/xml2js": "0.4.13", "@typescript-eslint/eslint-plugin": "^5.0.0", "@typescript-eslint/parser": "^5.0.0", "eslint": "^8.55.0", diff --git a/packages/apps/job-launcher/server/src/common/config/env.ts b/packages/apps/job-launcher/server/src/common/config/env.ts index 5a144f3d80..08b62de6a8 100644 --- a/packages/apps/job-launcher/server/src/common/config/env.ts +++ b/packages/apps/job-launcher/server/src/common/config/env.ts @@ -20,6 +20,8 @@ export const ConfigNames = { WEB3_ENV: 'WEB3_ENV', WEB3_PRIVATE_KEY: 'WEB3_PRIVATE_KEY', GAS_PRICE_MULTIPLIER: 'GAS_PRICE_MULTIPLIER', + PGP_PRIVATE_KEY: 'PGP_PRIVATE_KEY', + PGP_PUBLIC_KEY: 'PGP_PUBLIC_KEY', JOB_LAUNCHER_FEE: 'JOB_LAUNCHER_FEE', RECORDING_ORACLE_FEE: 'RECORDING_ORACLE_FEE', REPUTATION_ORACLE_FEE: 'REPUTATION_ORACLE_FEE', @@ -31,6 +33,11 @@ export const ConfigNames = { CVAT_EXCHANGE_ORACLE_ADDRESS: 'CVAT_EXCHANGE_ORACLE_ADDRESS', CVAT_EXCHANGE_ORACLE_WEBHOOK_URL: 'CVAT_EXCHANGE_ORACLE_WEBHOOK_URL', CVAT_RECORDING_ORACLE_ADDRESS: 'CVAT_RECORDING_ORACLE_ADDRESS', + HCAPTCHA_RECORDING_ORACLE_URI: 'HCAPTCHA_RECORDING_ORACLE_URI', + HCAPTCHA_REPUTATION_ORACLE_URI: 'HCAPTCHA_REPUTATION_ORACLE_URI', + HCAPTCHA_ORACLE_ADDRESS: 'HCAPTCHA_ORACLE_ADDRESS', + HCAPTCHA_PGP_PUBLIC_KEY: 'HCAPTCHA_PGP_PUBLIC_KEY', + HCAPTCHA_SITE_KEY: 'HCAPTCHA_SITE_KEY', S3_ENDPOINT: 'S3_ENDPOINT', S3_PORT: 'S3_PORT', S3_ACCESS_KEY: 'S3_ACCESS_KEY', @@ -78,12 +85,19 @@ export const envValidator = Joi.object({ WEB3_ENV: Joi.string().default('testnet'), WEB3_PRIVATE_KEY: Joi.string().required(), GAS_PRICE_MULTIPLIER: Joi.number().default(null), + PGP_PRIVATE_KEY: Joi.string().required(), + PGP_PUBLIC_KEY: Joi.string().required(), JOB_LAUNCHER_FEE: Joi.string().default(10), REPUTATION_ORACLE_ADDRESS: Joi.string().required(), FORTUNE_EXCHANGE_ORACLE_ADDRESS: Joi.string().required(), FORTUNE_RECORDING_ORACLE_ADDRESS: Joi.string().required(), CVAT_EXCHANGE_ORACLE_ADDRESS: Joi.string().required(), CVAT_RECORDING_ORACLE_ADDRESS: Joi.string().required(), + HCAPTCHA_RECORDING_ORACLE_URI: Joi.string().required(), + HCAPTCHA_REPUTATION_ORACLE_URI: Joi.string().required(), + HCAPTCHA_ORACLE_ADDRESS: Joi.string().required(), + HCAPTCHA_PGP_PUBLIC_KEY: Joi.string().required(), + HCAPTCHA_SITE_KEY: Joi.string().required(), // S3 S3_ENDPOINT: Joi.string().default('127.0.0.1'), S3_PORT: Joi.string().default(9000), diff --git a/packages/apps/job-launcher/server/src/common/constants/errors.ts b/packages/apps/job-launcher/server/src/common/constants/errors.ts index 4dc1a3288c..1e7c6d6b80 100644 --- a/packages/apps/job-launcher/server/src/common/constants/errors.ts +++ b/packages/apps/job-launcher/server/src/common/constants/errors.ts @@ -14,6 +14,9 @@ export enum ErrorJob { InvalidEventType = 'Invalid event type', InvalidStatusCancellation = 'Job has an invalid status for cancellation', NotLaunched = 'Not launched', + TaskDataNotFound = 'Task data not found', + HCaptchaInvalidJobType = 'hCaptcha invalid job type', + GroundThuthValidationFailed = 'Ground thuth validation failed', } /** @@ -100,6 +103,7 @@ export enum ErrorBucket { NotExist = 'Bucket does not exist', NotPublic = 'Bucket is not public', UnableSaveFile = 'Unable to save file', + FailedToFetchBucketContents = 'Failed to fetch bucket contents', } /** diff --git a/packages/apps/job-launcher/server/src/common/constants/index.ts b/packages/apps/job-launcher/server/src/common/constants/index.ts index 865db1f0e4..9822c8ae9b 100644 --- a/packages/apps/job-launcher/server/src/common/constants/index.ts +++ b/packages/apps/job-launcher/server/src/common/constants/index.ts @@ -42,3 +42,18 @@ export const SENDGRID_TEMPLATES = { resetPassword: 'd-3ac74546352a4e1abdd1689947632c22', passwordChanged: 'd-ca0ac7e6fff845829cd0167af09f25cf', }; + +export const HCAPTCHA_MIN_SHAPES_PER_IMAGE = 1; +export const HCAPTCHA_MAX_SHAPES_PER_IMAGE = 1; +export const HCAPTCHA_MINIMUM_SELECTION_AREA_PER_SHAPE = 5; +export const HCAPTCHA_LANDMARK_MIN_POINTS = 1; +export const HCAPTCHA_LANDMARK_MAX_POINTS = 8; +export const HCAPTCHA_BOUNDING_BOX_MIN_POINTS = 4; +export const HCAPTCHA_BOUNDING_BOX_MAX_POINTS = 4; +export const HCAPTCHA_POLYGON_MIN_POINTS = 4; +export const HCAPTCHA_POLYGON_MAX_POINTS = 4; +export const HCAPTCHA_IMMO_MIN_LENGTH = 1; +export const HCAPTCHA_IMMO_MAX_LENGTH = 100; +export const HCAPTCHA_ORACLE_STAKE = 0.05; + +export const HCAPTCHA_NOT_PRESENTED_LABEL = 'Not presented'; 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 bbe5824dfb..559ccba4bf 100644 --- a/packages/apps/job-launcher/server/src/common/enums/job.ts +++ b/packages/apps/job-launcher/server/src/common/enums/job.ts @@ -21,5 +21,400 @@ export enum JobStatusFilter { export enum JobRequestType { IMAGE_POINTS = 'IMAGE_POINTS', IMAGE_BOXES = 'IMAGE_BOXES', + HCAPTCHA = 'HCAPTCHA', FORTUNE = 'FORTUNE', } + +export enum JobCaptchaMode { + BATCH = 'batch', +} + +export enum JobCaptchaRequestType { + IMAGE_LABEL_BINARY = 'image_label_binary', + IMAGE_LABEL_MULTIPLE_CHOICE = 'image_label_multiple_choice', + IMAGE_LABEL_AREA_SELECT = 'image_label_area_select', + TEXT_FREEE_NTRY = 'text_free_entry', +} + +export enum JobCaptchaShapeType { + POLYGON = 'polygon', + CATEGORIZATION = 'categorization', + POINT = 'point', + BOUNDING_BOX = 'bounding_box', + COMPARISON = 'comparison', + IMMO = 'immo', +} + +export enum WorkerLanguage { + AF = 'af', + SQ = 'sq', + AM = 'am', + AR = 'ar', + HY = 'hy', + AZ = 'az', + EU = 'eu', + BE = 'be', + BN = 'bn', + BG = 'bg', + BS = 'bs', + MY = 'my', + CA = 'ca', + CEB = 'ceb', + ZH = 'zh', + CO = 'co', + HR = 'hr', + CS = 'cs', + DA = 'da', + NL = 'nl', + EN = 'en', + EO = 'eo', + ET = 'et', + FA = 'fa', + FI = 'fi', + FR = 'fr', + FY = 'fy', + GD = 'gd', + GL = 'gl', + KA = 'ka', + DE = 'de', + EL = 'el', + GU = 'gu', + HT = 'ht', + HA = 'ha', + HAW = 'haw', + HE = 'he', + HI = 'hi', + HMN = 'hmn', + HU = 'hu', + IS = 'is', + IG = 'ig', + ID = 'id', + GA = 'ga', + IT = 'it', + JA = 'ja', + JW = 'jw', + KN = 'kn', + KK = 'kk', + KM = 'km', + RW = 'rw', + KY = 'ky', + KO = 'ko', + KU = 'ku', + LO = 'lo', + LA = 'la', + LV = 'lv', + LT = 'lt', + LB = 'lb', + MK = 'mk', + MG = 'mg', + MS = 'ms', + ML = 'ml', + MT = 'mt', + MI = 'mi', + MR = 'mr', + MN = 'mn', + NE = 'ne', + NO = 'no', + NY = 'ny', + OR = 'or', + PL = 'pl', + PT = 'pt', + PS = 'ps', + PA = 'pa', + RO = 'ro', + RU = 'ru', + SM = 'sm', + SN = 'sn', + SD = 'sd', + SI = 'si', + SR = 'sr', + SK = 'sk', + SL = 'sl', + SO = 'so', + ST = 'st', + ES = 'es', + SU = 'su', + SW = 'sw', + SV = 'sv', + TL = 'tl', + TG = 'tg', + TA = 'ta', + TT = 'tt', + TE = 'te', + TH = 'th', + TR = 'tr', + TK = 'tk', + UG = 'ug', + UK = 'uk', + UR = 'ur', + UZ = 'uz', + VI = 'vi', + CY = 'cy', + XH = 'xh', + YI = 'yi', + YO = 'yo', + ZU = 'zu', +} + +export enum WorkerLocation { + AF = 'AF', + AL = 'AL', + DZ = 'DZ', + AS = 'AS', + AD = 'AD', + AO = 'AO', + AI = 'AI', + AQ = 'AQ', + AG = 'AG', + AR = 'AR', + AM = 'AM', + AW = 'AW', + AC = 'AC', + AU = 'AU', + AT = 'AT', + AZ = 'AZ', + BS = 'BS', + BH = 'BH', + BD = 'BD', + BB = 'BB', + BY = 'BY', + BE = 'BE', + BZ = 'BZ', + BJ = 'BJ', + BM = 'BM', + BT = 'BT', + BO = 'BO', + BQ = 'BQ', + BA = 'BA', + BW = 'BW', + BV = 'BV', + BR = 'BR', + IO = 'IO', + BN = 'BN', + BG = 'BG', + BF = 'BF', + BI = 'BI', + KH = 'KH', + CM = 'CM', + CA = 'CA', + IC = 'IC', + CV = 'CV', + KY = 'KY', + CF = 'CF', + EA = 'EA', + TD = 'TD', + CL = 'CL', + CN = 'CN', + CX = 'CX', + CP = 'CP', + CC = 'CC', + CO = 'CO', + KM = 'KM', + CK = 'CK', + CR = 'CR', + CI = 'CI', + HR = 'HR', + CU = 'CU', + CW = 'CW', + CY = 'CY', + CZ = 'CZ', + CD = 'CD', + DK = 'DK', + DG = 'DG', + DJ = 'DJ', + DM = 'DM', + DO = 'DO', + TL = 'TL', + EC = 'EC', + EG = 'EG', + SV = 'SV', + GQ = 'GQ', + ER = 'ER', + EE = 'EE', + ET = 'ET', + EU = 'EU', + FK = 'FK', + FO = 'FO', + FJ = 'FJ', + FI = 'FI', + FR = 'FR', + FX = 'FX', + GF = 'GF', + PF = 'PF', + TF = 'TF', + GA = 'GA', + GM = 'GM', + GE = 'GE', + DE = 'DE', + GH = 'GH', + GI = 'GI', + GR = 'GR', + GL = 'GL', + GD = 'GD', + GP = 'GP', + GU = 'GU', + GT = 'GT', + GG = 'GG', + GN = 'GN', + GW = 'GW', + GY = 'GY', + HT = 'HT', + HM = 'HM', + HN = 'HN', + HK = 'HK', + HU = 'HU', + IS = 'IS', + IN = 'IN', + ID = 'ID', + IR = 'IR', + IQ = 'IQ', + IE = 'IE', + IM = 'IM', + IL = 'IL', + IT = 'IT', + JM = 'JM', + JP = 'JP', + JE = 'JE', + JO = 'JO', + KZ = 'KZ', + KE = 'KE', + KI = 'KI', + KP = 'KP', + KR = 'KR', + KW = 'KW', + KG = 'KG', + LA = 'LA', + LV = 'LV', + LB = 'LB', + LS = 'LS', + LR = 'LR', + LY = 'LY', + LI = 'LI', + LT = 'LT', + LU = 'LU', + MO = 'MO', + MK = 'MK', + MG = 'MG', + MW = 'MW', + MY = 'MY', + ML = 'ML', + MT = 'MT', + MH = 'MH', + MQ = 'MQ', + MR = 'MR', + MU = 'MU', + YT = 'YT', + MX = 'MX', + FM = 'FM', + MD = 'MD', + MC = 'MC', + MN = 'MN', + ME = 'ME', + MS = 'MS', + MA = 'MA', + MZ = 'MZ', + MM = 'MM', + NA = 'NA', + NR = 'NR', + NP = 'NP', + NL = 'NL', + NC = 'NC', + NZ = 'NZ', + NI = 'NI', + NE = 'NE', + NG = 'NG', + NU = 'NU', + NF = 'NF', + MP = 'MP', + NO = 'NO', + OM = 'OM', + PK = 'PK', + PW = 'PW', + PS = 'PS', + PA = 'PA', + PG = 'PG', + PY = 'PY', + PE = 'PE', + PH = 'PH', + PN = 'PN', + PL = 'PL', + PT = 'PT', + PR = 'PR', + QA = 'QA', + CG = 'CG', + RE = 'RE', + RO = 'RO', + RU = 'RU', + RW = 'RW', + BL = 'BL', + SH = 'SH', + KN = 'KN', + LC = 'LC', + MF = 'MF', + PM = 'PM', + VC = 'VC', + WS = 'WS', + SM = 'SM', + ST = 'ST', + SA = 'SA', + SN = 'SN', + RS = 'RS', + SC = 'SC', + SL = 'SL', + SG = 'SG', + SX = 'SX', + SK = 'SK', + SI = 'SI', + SB = 'SB', + SO = 'SO', + ZA = 'ZA', + GS = 'GS', + ES = 'ES', + LK = 'LK', + SD = 'SD', + SR = 'SR', + SJ = 'SJ', + SZ = 'SZ', + SE = 'SE', + CH = 'CH', + SY = 'SY', + TW = 'TW', + TJ = 'TJ', + TZ = 'TZ', + TH = 'TH', + TG = 'TG', + TK = 'TK', + TO = 'TO', + TT = 'TT', + TA = 'TA', + TN = 'TN', + TR = 'TR', + TM = 'TM', + TC = 'TC', + TV = 'TV', + UG = 'UG', + UA = 'UA', + AE = 'AE', + GB = 'GB', + US = 'US', + UM = 'UM', + UY = 'UY', + UZ = 'UZ', + VU = 'VU', + VA = 'VA', + VE = 'VE', + VN = 'VN', + VG = 'VG', + VI = 'VI', + WF = 'WF', + EH = 'EH', + YE = 'YE', + ZM = 'ZM', + ZW = 'ZW', +} + +export enum WorkerBrowser { + DESKTOP = 'desktop', + MOBILE = 'mobile', + MODERN_BROWSER = 'modern_browser', +} diff --git a/packages/apps/job-launcher/server/src/common/enums/storage.ts b/packages/apps/job-launcher/server/src/common/enums/storage.ts new file mode 100644 index 0000000000..c2bf37d34c --- /dev/null +++ b/packages/apps/job-launcher/server/src/common/enums/storage.ts @@ -0,0 +1,9 @@ +export enum ContentType { + TEXT_PLAIN = 'text/plain', + APPLICATION_JSON = 'application/json', +} + +export enum Extension { + JSON = '.json', + ZIP = '.zip', +} diff --git a/packages/apps/job-launcher/server/src/common/enums/webhook.ts b/packages/apps/job-launcher/server/src/common/enums/webhook.ts index 9404e2a38c..220bfca729 100644 --- a/packages/apps/job-launcher/server/src/common/enums/webhook.ts +++ b/packages/apps/job-launcher/server/src/common/enums/webhook.ts @@ -7,6 +7,7 @@ export enum EventType { export enum OracleType { FORTUNE = 'fortune', CVAT = 'cvat', + HCAPTCHA = 'hcaptcha', } export enum WebhookStatus { diff --git a/packages/apps/job-launcher/server/src/common/interfaces/index.ts b/packages/apps/job-launcher/server/src/common/interfaces/index.ts index 9766666c29..198e6443e1 100644 --- a/packages/apps/job-launcher/server/src/common/interfaces/index.ts +++ b/packages/apps/job-launcher/server/src/common/interfaces/index.ts @@ -2,3 +2,4 @@ export * from './base'; export * from './job'; export * from './user'; export * from './payments'; +export * from './s3'; diff --git a/packages/apps/job-launcher/server/src/common/interfaces/s3.ts b/packages/apps/job-launcher/server/src/common/interfaces/s3.ts index 81e92ae447..e4523dfc01 100644 --- a/packages/apps/job-launcher/server/src/common/interfaces/s3.ts +++ b/packages/apps/job-launcher/server/src/common/interfaces/s3.ts @@ -1,4 +1,4 @@ -export class UploadedFile { - public url: string; - public hash: string; +export interface UploadedFile { + url: string; + hash: string; } diff --git a/packages/apps/job-launcher/server/src/common/utils/index.ts b/packages/apps/job-launcher/server/src/common/utils/index.ts index fc0c1811c8..12cd752794 100644 --- a/packages/apps/job-launcher/server/src/common/utils/index.ts +++ b/packages/apps/job-launcher/server/src/common/utils/index.ts @@ -117,3 +117,22 @@ export function hashStream(stream: Readable): Promise { }); }); } + +export function hashString(data: string): string { + return crypto.createHash('sha1').update(data).digest('hex'); +} + +export function isValidJSON(str: string): boolean { + try { + JSON.parse(str); + return true; + } catch (e) { + return false; + } +} + +export function isPGPMessage(str: string): boolean { + const pattern = + /-----BEGIN PGP MESSAGE-----\n\n[\s\S]+?\n-----END PGP MESSAGE-----/; + return pattern.test(str); +} diff --git a/packages/apps/job-launcher/server/src/modules/job/job.controller.ts b/packages/apps/job-launcher/server/src/modules/job/job.controller.ts index 99d559d2c7..b7fb34dd97 100644 --- a/packages/apps/job-launcher/server/src/modules/job/job.controller.ts +++ b/packages/apps/job-launcher/server/src/modules/job/job.controller.ts @@ -9,6 +9,7 @@ import { Post, Query, Request, + UnauthorizedException, UseGuards, } from '@nestjs/common'; import { @@ -30,6 +31,7 @@ import { JobDetailsDto, JobIdDto, FortuneFinalResultDto, + JobCaptchaDto, } from './job.dto'; import { JobService } from './job.service'; import { JobRequestType, JobStatusFilter } from '../../common/enums/job'; @@ -102,6 +104,19 @@ export class JobController { return this.jobService.createJob(req.user.id, data.type, data); } + @Post('/hCaptcha') + public async createCaptchaJob( + @Request() req: RequestWithUser, + @Body() data: JobCaptchaDto, + ): Promise { + throw new UnauthorizedException('Hcaptcha jobs disabled temporally'); + return this.jobService.createJob( + req.user.id, + JobRequestType.HCAPTCHA, + data, + ); + } + @ApiOperation({ summary: 'Get a list of jobs', description: diff --git a/packages/apps/job-launcher/server/src/modules/job/job.dto.ts b/packages/apps/job-launcher/server/src/modules/job/job.dto.ts index 64c0015699..f1c36c9775 100644 --- a/packages/apps/job-launcher/server/src/modules/job/job.dto.ts +++ b/packages/apps/job-launcher/server/src/modules/job/job.dto.ts @@ -7,15 +7,30 @@ import { IsString, IsUrl, IsDate, + IsDateString, IsOptional, IsObject, IsNumberString, Min, + Max, IsNotEmpty, IsEthereumAddress, + ValidateNested, + IsDefined, + IsNotEmptyObject, + ArrayMinSize, } from 'class-validator'; +import { Type } from 'class-transformer'; import { ChainId } from '@human-protocol/sdk'; -import { JobRequestType, JobStatus } from '../../common/enums/job'; +import { + JobCaptchaRequestType, + JobCaptchaShapeType, + JobRequestType, + JobStatus, + WorkerBrowser, + WorkerLanguage, + WorkerLocation, +} from '../../common/enums/job'; import { EventType } from '../../common/enums/webhook'; import { BigNumber } from 'ethers'; export class JobCreateDto { @@ -64,6 +79,12 @@ export class JobDto { @IsEnum(ChainId) @IsOptional() public chainId?: ChainId; +} + +export class JobFortuneDto extends JobDto { + @ApiProperty() + @IsString() + public requesterTitle: string; @ApiProperty() @IsString() @@ -72,27 +93,26 @@ export class JobDto { @ApiProperty() @IsNumber() @IsPositive() - public fundAmount: number; -} - -export class JobFortuneDto extends JobDto { - @ApiProperty() - @IsString() - public requesterTitle: string; + public submissionsRequired: number; @ApiProperty() @IsNumber() @IsPositive() - public submissionsRequired: number; + public fundAmount: number; } export class JobCvatDto extends JobDto { + @ApiProperty() + @IsString() + public requesterDescription: string; + @ApiProperty() @IsUrl() public dataUrl: string; @ApiProperty() @IsArray() + @ArrayMinSize(1) public labels: string[]; @ApiProperty() @@ -110,7 +130,12 @@ export class JobCvatDto extends JobDto { @ApiProperty({ enum: JobRequestType }) @IsEnum(JobRequestType) - public type: JobRequestType; + type: JobRequestType; + + @ApiProperty() + @IsNumber() + @IsPositive() + public fundAmount: number; } export class JobCancelDto { @@ -465,3 +490,256 @@ export class EscrowCancelDto { @ApiProperty() public amountRefunded: BigNumber; } + +export class JobCaptchaAdvancedDto { + @ApiProperty({ + enum: WorkerLanguage, + }) + @IsEnum(WorkerLanguage) + @IsOptional() + workerLanguage?: WorkerLanguage; + + @ApiProperty({ + enum: WorkerLocation, + }) + @IsEnum(WorkerLocation) + @IsOptional() + workerLocation?: WorkerLocation; + + @ApiProperty({ + enum: WorkerBrowser, + }) + @IsEnum(WorkerBrowser) + @IsOptional() + targetBrowser?: WorkerBrowser; +} + +export class JobCaptchaAnnotationsDto { + @ApiProperty({ + enum: JobCaptchaShapeType, + }) + @IsEnum(JobCaptchaShapeType) + typeOfJob: JobCaptchaShapeType; + + @ApiProperty() + @IsNumber() + @IsPositive() + taskBidPrice: number; + + @ApiPropertyOptional() + @IsOptional() + @IsString() + label?: string; + + @ApiProperty() + @IsString() + labelingPrompt: string; + + @ApiProperty() + @IsString() + groundTruths: string; + + @ApiProperty() + @IsOptional() + @IsArray() + exampleImages?: string[]; +} + +export class JobCaptchaDto extends JobDto { + @ApiProperty() + @IsUrl() + dataUrl: string; + + @ApiProperty() + @IsNumber() + @IsPositive() + @Max(1) + accuracyTarget: number; + + @ApiProperty() + @IsDateString() + @IsOptional() + completionDate: Date; + + @ApiProperty() + @IsNumber() + @IsPositive() + @Max(100) + minRequests: number; + + @ApiProperty() + @IsNumber() + @IsPositive() + @Max(100) + maxRequests: number; + + @ApiProperty() + @IsDefined() + @IsNotEmptyObject() + @IsObject() + @ValidateNested() + @Type(() => JobCaptchaAdvancedDto) + advanced: JobCaptchaAdvancedDto; + + @ApiProperty() + @IsDefined() + @IsNotEmptyObject() + @IsObject() + @ValidateNested() + @Type(() => JobCaptchaAnnotationsDto) + annotations: JobCaptchaAnnotationsDto; +} + +export class RestrictedAudience { + @IsObject() + sitekey?: Record[]; + + @IsObject() + lang?: Record[]; + + @IsObject() + browser?: Record[]; + + @IsObject() + country?: Record[]; +} + +class RequesterRestrictedAnswer { + @IsString() + en?: string; + + @IsUrl() + answer_example_uri?: string; +} + +class RequestConfig { + @IsEnum(JobCaptchaShapeType) + shape_type?: JobCaptchaShapeType; + + @IsNumber() + @IsPositive() + min_shapes_per_image?: number; + + @IsNumber() + @IsPositive() + max_shapes_per_image?: number; + + @IsNumber() + @IsPositive() + min_points?: number; + + @IsNumber() + @IsPositive() + max_points?: number; + + @IsNumber() + @IsPositive() + minimum_selection_area_per_shape?: number; + + @IsNumber() + @IsPositive() + multiple_choice_max_choices?: number; + + @IsNumber() + @IsPositive() + multiple_choice_min_choices?: number; + + @IsString() + answer_type?: string; + + overlap_threshold?: any; + + @IsNumber() + @IsPositive() + max_length?: number; + + @IsNumber() + @IsPositive() + min_length?: number; +} + +export class HCaptchaManifestDto { + @IsString() + job_mode: string; + + @IsEnum(JobCaptchaRequestType) + request_type: JobCaptchaRequestType; + + @IsObject() + @ValidateNested() + request_config: RequestConfig; + + @IsNumber() + requester_accuracy_target: number; + + @IsNumber() + requester_max_repeats: number; + + @IsNumber() + requester_min_repeats: number; + + @IsArray() + @IsUrl({}, { each: true }) + @IsOptional() + requester_question_example?: string[]; + + @IsObject() + requester_question: Record; + + @IsUrl() + taskdata_uri: string; + + @IsNumber() + job_total_tasks: number; + + @IsNumber() + task_bid_price: number; + + @IsUrl() + groundtruth_uri?: string; + + public_results: boolean; + + @IsNumber() + oracle_stake: number; + + @IsString() + repo_uri: string; + + @IsString() + ro_uri: string; + + @IsObject() + @ValidateNested() + restricted_audience: RestrictedAudience; + + @IsObject() + @ValidateNested({ each: true }) + requester_restricted_answer_set: RequesterRestrictedAnswer; + + @IsOptional() + @IsArray() + @ValidateNested({ each: true }) + taskdata?: TaskData[]; +} + +class DatapointText { + @IsString() + en: string; +} + +class TaskData { + @IsString() + task_key: string; + + @IsOptional() + @IsString() + datapoint_uri?: string; + + @IsString() + datapoint_hash: string; + + @IsObject() + @IsOptional() + datapoint_text?: DatapointText; +} 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 1b60a36280..3971ce0bae 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 @@ -12,18 +12,13 @@ import { } from '@human-protocol/sdk'; import { HttpService } from '@nestjs/axios'; import { - BadGatewayException, BadRequestException, ConflictException, NotFoundException, } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { Test } from '@nestjs/testing'; -import { - ErrorBucket, - ErrorJob, - ErrorWeb3, -} from '../../common/constants/errors'; +import { ErrorJob, ErrorWeb3 } from '../../common/constants/errors'; import { PaymentSource, PaymentStatus, @@ -31,9 +26,15 @@ import { TokenId, } from '../../common/enums/payment'; import { + JobCaptchaMode, + JobCaptchaRequestType, + JobCaptchaShapeType, JobRequestType, JobStatus, JobStatusFilter, + WorkerBrowser, + WorkerLanguage, + WorkerLocation, } from '../../common/enums/job'; import { MOCK_ADDRESS, @@ -44,10 +45,13 @@ import { MOCK_ORACLE_FEE, MOCK_EXCHANGE_ORACLE_WEBHOOK_URL, MOCK_FILE_HASH, - MOCK_FILE_KEY, MOCK_FILE_URL, + MOCK_HCAPTCHA_ORACLE_ADDRESS, + MOCK_HCAPTCHA_PGP_PUBLIC_KEY, MOCK_JOB_ID, MOCK_JOB_LAUNCHER_FEE, + MOCK_PGP_PRIVATE_KEY, + MOCK_PGP_PUBLIC_KEY, MOCK_MANIFEST, MOCK_PRIVATE_KEY, MOCK_RECORDING_ORACLE_ADDRESS, @@ -57,16 +61,25 @@ import { MOCK_SUBMISSION_REQUIRED, MOCK_TRANSACTION_HASH, MOCK_USER_ID, + MOCK_CVAT_JOB_SIZE, + MOCK_CVAT_MAX_TIME, + MOCK_CVAT_VAL_SIZE, + MOCK_HCAPTCHA_SITE_KEY, + MOCK_HCAPTCHA_IMAGE_LABEL, + MOCK_HCAPTCHA_IMAGE_URL, + MOCK_HCAPTCHA_REPO_URI, + MOCK_HCAPTCHA_RO_URI, + MOCK_FILE_KEY, } from '../../../test/constants'; import { PaymentService } from '../payment/payment.service'; import { Web3Service } from '../web3/web3.service'; import { FortuneFinalResultDto, FortuneManifestDto, - CvatManifestDto, JobFortuneDto, JobCvatDto, JobDetailsDto, + JobCaptchaDto, } from './job.dto'; import { JobEntity } from './job.entity'; import { JobRepository } from './job.repository'; @@ -80,6 +93,12 @@ import { PaymentEntity } from '../payment/payment.entity'; import { BigNumber, ethers } from 'ethers'; import { HMToken__factory } from '@human-protocol/core/typechain-types'; import { StorageService } from '../storage/storage.service'; +import { + HCAPTCHA_MAX_SHAPES_PER_IMAGE, + HCAPTCHA_MINIMUM_SELECTION_AREA_PER_SHAPE, + HCAPTCHA_MIN_SHAPES_PER_IMAGE, + HCAPTCHA_NOT_PRESENTED_LABEL, +} from '../../common/constants'; import { WebhookService } from '../webhook/webhook.service'; const rate = 1.5; @@ -176,6 +195,26 @@ describe('JobService', () => { return MOCK_BUCKET_NAME; case 'CVAT_JOB_SIZE': return 1; + case 'PGP_PRIVATE_KEY': + return MOCK_PGP_PRIVATE_KEY; + case 'PGP_PUBLIC_KEY': + return MOCK_PGP_PUBLIC_KEY; + case 'HCAPTCHA_PGP_PUBLIC_KEY': + return MOCK_HCAPTCHA_PGP_PUBLIC_KEY; + case 'HCAPTCHA_ORACLE_ADDRESS': + return MOCK_HCAPTCHA_ORACLE_ADDRESS; + case 'HCAPTCHA_SITE_KEY': + return MOCK_HCAPTCHA_SITE_KEY; + case 'CVAT_JOB_SIZE': + return MOCK_CVAT_JOB_SIZE; + case 'CVAT_MAX_TIME': + return MOCK_CVAT_MAX_TIME; + case 'CVAT_VAL_SIZE': + return MOCK_CVAT_VAL_SIZE; + case 'HCAPTCHA_REPUTATION_ORACLE_URI': + return MOCK_HCAPTCHA_REPO_URI; + case 'HCAPTCHA_RECORDING_ORACLE_URI': + return MOCK_HCAPTCHA_RO_URI; } }), }; @@ -217,7 +256,7 @@ describe('JobService', () => { storageService = moduleRef.get(StorageService); webhookService = moduleRef.get(WebhookService); - storageService.uploadManifest = jest.fn().mockResolvedValue({ + storageService.uploadFile = jest.fn().mockResolvedValue({ url: MOCK_FILE_URL, hash: MOCK_FILE_HASH, }); @@ -347,6 +386,9 @@ describe('JobService', () => { .spyOn(paymentService, 'getUserBalance') .mockResolvedValue(userBalance); + storageService.listObjectsInBucket = jest + .fn() + .mockResolvedValue(MOCK_BUCKET_FILES); getUserBalanceMock.mockResolvedValue(userBalance); await expect( @@ -367,8 +409,439 @@ describe('JobService', () => { }); }); + describe('createCvatManifest', () => { + it('should create a valid CVAT manifest', async () => { + const jobBounty = '50'; + jest + .spyOn(jobService, 'calculateJobBounty') + .mockResolvedValueOnce(jobBounty); + + const dto = { + dataUrl: MOCK_FILE_URL, + labels: ['label1', 'label2'], + requesterDescription: MOCK_REQUESTER_DESCRIPTION, + userGuide: MOCK_FILE_URL, + minQuality: 0.8, + gtUrl: MOCK_FILE_URL, + type: JobRequestType.IMAGE_BOXES, + fundAmount: 10, + }; + + const requestType = JobRequestType.IMAGE_BOXES; + const tokenFundAmount = 100; + + const result = await jobService.createCvatManifest( + dto, + requestType, + tokenFundAmount, + ); + + expect(result).toEqual({ + data: { + data_url: MOCK_FILE_URL, + }, + annotation: { + labels: [{ name: 'label1' }, { name: 'label2' }], + description: MOCK_REQUESTER_DESCRIPTION, + user_guide: MOCK_FILE_URL, + type: requestType, + job_size: 1, + max_time: 300, + }, + validation: { + min_quality: 0.8, + val_size: 2, + gt_url: MOCK_FILE_URL, + }, + job_bounty: jobBounty, + }); + }); + }); + + describe('createHCaptchaManifest', () => { + const listObjectsInBucket = ['example1.jpg', 'example2.jpg']; + + beforeEach(() => { + jest + .spyOn(storageService, 'listObjectsInBucket') + .mockResolvedValueOnce(listObjectsInBucket); + jest + .spyOn(jobService, 'generateAndUploadTaskData') + .mockResolvedValueOnce(MOCK_FILE_URL); + }); + + it('should create a valid HCaptcha manifest for COMPARISON job type', async () => { + const fileContent = JSON.stringify({ + [MOCK_HCAPTCHA_IMAGE_URL]: [true, true, true], + }); + jest.spyOn(storageService, 'download').mockResolvedValueOnce(fileContent); + + const jobType = JobCaptchaShapeType.COMPARISON; + const jobDto = { + dataUrl: MOCK_FILE_URL, + accuracyTarget: 0.9, + minRequests: 1, + maxRequests: 10, + annotations: { + typeOfJob: jobType, + labelingPrompt: MOCK_REQUESTER_DESCRIPTION, + groundTruths: MOCK_FILE_URL, + exampleImages: listObjectsInBucket, + taskBidPrice: 0.5, + }, + completionDate: new Date(), + advanced: {}, + }; + + const result = await jobService.createHCaptchaManifest(jobType, jobDto); + + expect(result).toEqual({ + job_mode: JobCaptchaMode.BATCH, + requester_accuracy_target: 0.9, + request_config: {}, + restricted_audience: { + sitekey: [ + { + [MOCK_HCAPTCHA_SITE_KEY]: { + score: 1, + }, + }, + ], + }, + requester_max_repeats: 10, + requester_min_repeats: 1, + requester_question: { en: MOCK_REQUESTER_DESCRIPTION }, + job_total_tasks: 2, + task_bid_price: 0.5, + taskdata_uri: MOCK_FILE_URL, + public_results: true, + oracle_stake: 0.05, + repo_uri: MOCK_HCAPTCHA_REPO_URI, + ro_uri: MOCK_HCAPTCHA_RO_URI, + request_type: JobCaptchaRequestType.IMAGE_LABEL_BINARY, + groundtruth_uri: MOCK_FILE_URL, + requester_restricted_answer_set: {}, + requester_question_example: listObjectsInBucket, + }); + }); + + it('should create a valid HCaptcha manifest for CATEGORIZATION job type', async () => { + const fileContent = JSON.stringify({ + [MOCK_HCAPTCHA_IMAGE_URL]: [[MOCK_HCAPTCHA_IMAGE_LABEL]], + }); + jest.spyOn(storageService, 'download').mockResolvedValueOnce(fileContent); + + const jobType = JobCaptchaShapeType.CATEGORIZATION; + const jobDto = { + dataUrl: MOCK_FILE_URL, + accuracyTarget: 0.9, + minRequests: 1, + maxRequests: 10, + annotations: { + typeOfJob: jobType, + labelingPrompt: MOCK_REQUESTER_DESCRIPTION, + groundTruths: MOCK_FILE_URL, + exampleImages: listObjectsInBucket, + taskBidPrice: 0.5, + }, + completionDate: new Date(), + advanced: {}, + }; + + const result = await jobService.createHCaptchaManifest(jobType, jobDto); + + expect(result).toEqual({ + job_mode: JobCaptchaMode.BATCH, + requester_accuracy_target: 0.9, + request_config: {}, + restricted_audience: { + sitekey: [ + { + [MOCK_HCAPTCHA_SITE_KEY]: { + score: 1, + }, + }, + ], + }, + requester_max_repeats: 10, + requester_min_repeats: 1, + requester_question: { en: MOCK_REQUESTER_DESCRIPTION }, + job_total_tasks: 2, // Mocked length of objectsInBucket + task_bid_price: 0.5, + taskdata_uri: MOCK_FILE_URL, + public_results: true, + oracle_stake: 0.05, + repo_uri: MOCK_HCAPTCHA_REPO_URI, + ro_uri: MOCK_HCAPTCHA_RO_URI, + request_type: JobCaptchaRequestType.IMAGE_LABEL_MULTIPLE_CHOICE, + groundtruth_uri: MOCK_FILE_URL, + requester_restricted_answer_set: { + '0': { + en: HCAPTCHA_NOT_PRESENTED_LABEL, + }, + [MOCK_HCAPTCHA_IMAGE_LABEL]: { + answer_example_uri: MOCK_HCAPTCHA_IMAGE_URL, + en: MOCK_HCAPTCHA_IMAGE_LABEL, + }, + }, + }); + }); + + it('should create a valid HCaptcha manifest for POLYGON job type', async () => { + const fileContent = JSON.stringify({ + [MOCK_HCAPTCHA_IMAGE_URL]: [ + [ + { + entity_type: 'number', + entity_coords: [ + 97, 89, 105, 89, 105, 118, 112, 118, 112, 123, 89, 123, 89, 118, + 97, 118, 97, 95, 89, 100, 89, 94, + ], + entity_name: 0, + }, + ], + ], + }); + jest.spyOn(storageService, 'download').mockResolvedValueOnce(fileContent); + + const jobType = JobCaptchaShapeType.POLYGON; + const jobDto = { + dataUrl: MOCK_FILE_URL, + accuracyTarget: 0.9, + minRequests: 1, + maxRequests: 10, + annotations: { + typeOfJob: jobType, + labelingPrompt: MOCK_REQUESTER_DESCRIPTION, + groundTruths: MOCK_FILE_URL, + exampleImages: listObjectsInBucket, + taskBidPrice: 0.5, + label: MOCK_HCAPTCHA_IMAGE_LABEL, + }, + completionDate: new Date(), + advanced: {}, + }; + + const result = await jobService.createHCaptchaManifest(jobType, jobDto); + + expect(result).toEqual({ + job_mode: JobCaptchaMode.BATCH, + requester_accuracy_target: 0.9, + request_config: { + shape_type: jobType, + min_shapes_per_image: HCAPTCHA_MIN_SHAPES_PER_IMAGE, + max_shapes_per_image: HCAPTCHA_MAX_SHAPES_PER_IMAGE, + min_points: 4, + max_points: 4, + minimum_selection_area_per_shape: + HCAPTCHA_MINIMUM_SELECTION_AREA_PER_SHAPE, + }, + restricted_audience: { + sitekey: [ + { + [MOCK_HCAPTCHA_SITE_KEY]: { + score: 1, + }, + }, + ], + }, + requester_max_repeats: 10, + requester_min_repeats: 1, + requester_question: { en: MOCK_REQUESTER_DESCRIPTION }, + job_total_tasks: 2, // Mocked length of objectsInBucket + task_bid_price: 0.5, + taskdata_uri: MOCK_FILE_URL, + public_results: true, + oracle_stake: 0.05, + repo_uri: MOCK_HCAPTCHA_REPO_URI, + ro_uri: MOCK_HCAPTCHA_RO_URI, + request_type: JobCaptchaRequestType.IMAGE_LABEL_AREA_SELECT, + groundtruth_uri: MOCK_FILE_URL, + requester_restricted_answer_set: { + [MOCK_HCAPTCHA_IMAGE_LABEL]: { en: MOCK_HCAPTCHA_IMAGE_LABEL }, + }, + requester_question_example: listObjectsInBucket, + }); + }); + + it('should create a valid HCaptcha manifest for POINT job type', async () => { + const fileContent = JSON.stringify({ + [MOCK_HCAPTCHA_IMAGE_URL]: [ + [ + { + entity_type: 'number', + entity_coords: [124, 89], + entity_name: 0, + }, + ], + ], + }); + jest.spyOn(storageService, 'download').mockResolvedValueOnce(fileContent); + + const jobType = JobCaptchaShapeType.POINT; + const jobDto = { + dataUrl: MOCK_FILE_URL, + accuracyTarget: 0.9, + minRequests: 1, + maxRequests: 10, + annotations: { + typeOfJob: jobType, + labelingPrompt: MOCK_REQUESTER_DESCRIPTION, + groundTruths: MOCK_FILE_URL, + exampleImages: listObjectsInBucket, + taskBidPrice: 0.5, + label: MOCK_HCAPTCHA_IMAGE_LABEL, + }, + completionDate: new Date(), + advanced: {}, + }; + + const result = await jobService.createHCaptchaManifest(jobType, jobDto); + + expect(result).toEqual({ + job_mode: JobCaptchaMode.BATCH, + requester_accuracy_target: 0.9, + request_config: { + shape_type: JobCaptchaShapeType.POINT, + min_shapes_per_image: HCAPTCHA_MIN_SHAPES_PER_IMAGE, + max_shapes_per_image: HCAPTCHA_MAX_SHAPES_PER_IMAGE, + min_points: 1, + max_points: 8, + }, + restricted_audience: { + sitekey: [ + { + [MOCK_HCAPTCHA_SITE_KEY]: { + score: 1, + }, + }, + ], + }, + requester_max_repeats: 10, + requester_min_repeats: 1, + requester_question: { en: MOCK_REQUESTER_DESCRIPTION }, + job_total_tasks: 2, // Mocked length of objectsInBucket + task_bid_price: 0.5, + taskdata_uri: MOCK_FILE_URL, + public_results: true, + oracle_stake: 0.05, + repo_uri: MOCK_HCAPTCHA_REPO_URI, + ro_uri: MOCK_HCAPTCHA_RO_URI, + request_type: JobCaptchaRequestType.IMAGE_LABEL_AREA_SELECT, + groundtruth_uri: MOCK_FILE_URL, + requester_restricted_answer_set: { + [MOCK_HCAPTCHA_IMAGE_LABEL]: { en: MOCK_HCAPTCHA_IMAGE_LABEL }, + }, + requester_question_example: listObjectsInBucket, + }); + }); + + it('should create a valid HCaptcha manifest for BOUNDING_BOX job type', async () => { + const fileContent = JSON.stringify({ + [MOCK_HCAPTCHA_IMAGE_URL]: [ + [ + { + entity_type: 'number', + entity_coords: [74, 88, 126, 88, 126, 123, 74, 123], + entity_name: 0, + }, + ], + ], + }); + jest.spyOn(storageService, 'download').mockResolvedValueOnce(fileContent); + + const jobType = JobCaptchaShapeType.BOUNDING_BOX; + const jobDto = { + dataUrl: MOCK_FILE_URL, + accuracyTarget: 0.9, + minRequests: 1, + maxRequests: 10, + annotations: { + typeOfJob: jobType, + labelingPrompt: MOCK_REQUESTER_DESCRIPTION, + groundTruths: MOCK_FILE_URL, + exampleImages: listObjectsInBucket, + taskBidPrice: 0.5, + label: MOCK_HCAPTCHA_IMAGE_LABEL, + }, + completionDate: new Date(), + advanced: {}, + }; + + const result = await jobService.createHCaptchaManifest(jobType, jobDto); + + expect(result).toEqual({ + job_mode: JobCaptchaMode.BATCH, + requester_accuracy_target: 0.9, + request_config: { + shape_type: JobCaptchaShapeType.BOUNDING_BOX, + min_shapes_per_image: HCAPTCHA_MIN_SHAPES_PER_IMAGE, + max_shapes_per_image: HCAPTCHA_MAX_SHAPES_PER_IMAGE, + min_points: 4, + max_points: 4, + }, + restricted_audience: { + sitekey: [ + { + [MOCK_HCAPTCHA_SITE_KEY]: { + score: 1, + }, + }, + ], + }, + requester_max_repeats: 10, + requester_min_repeats: 1, + requester_question: { en: MOCK_REQUESTER_DESCRIPTION }, + job_total_tasks: 2, // Mocked length of objectsInBucket + task_bid_price: 0.5, + taskdata_uri: MOCK_FILE_URL, + public_results: true, + oracle_stake: 0.05, + repo_uri: MOCK_HCAPTCHA_REPO_URI, + ro_uri: MOCK_HCAPTCHA_RO_URI, + request_type: JobCaptchaRequestType.IMAGE_LABEL_AREA_SELECT, + groundtruth_uri: MOCK_FILE_URL, + requester_restricted_answer_set: { + [MOCK_HCAPTCHA_IMAGE_LABEL]: { en: MOCK_HCAPTCHA_IMAGE_LABEL }, + }, + requester_question_example: listObjectsInBucket, + }); + }); + + it('should throw BadRequestException for invalid POLYGON job type without label', async () => { + const fileContent = JSON.stringify({ + [MOCK_HCAPTCHA_IMAGE_URL]: [[MOCK_HCAPTCHA_IMAGE_LABEL]], + }); + jest.spyOn(storageService, 'download').mockResolvedValueOnce(fileContent); + + const jobType = JobCaptchaShapeType.POLYGON; + const jobDto = { + dataUrl: MOCK_FILE_URL, + accuracyTarget: 0.9, + minRequests: 1, + maxRequests: 10, + annotations: { + typeOfJob: jobType, + labelingPrompt: MOCK_REQUESTER_DESCRIPTION, + groundTruths: MOCK_FILE_URL, + exampleImages: listObjectsInBucket, + taskBidPrice: 0.5, + }, + completionDate: new Date(), + advanced: {}, + }; + + await expect( + jobService.createHCaptchaManifest(jobType, jobDto), + ).rejects.toThrowError(BadRequestException); + }); + }); + describe('calculateJobBounty', () => { it('should calculate the job bounty correctly', async () => { + storageService.listObjectsInBucket = jest + .fn() + .mockResolvedValue(MOCK_BUCKET_FILES); const tokenFundAmount = 0.013997056833333334; const result = await jobService['calculateJobBounty']( MOCK_FILE_URL, @@ -412,6 +885,9 @@ describe('JobService', () => { const userBalance = 25; getUserBalanceMock.mockResolvedValue(userBalance); + storageService.listObjectsInBucket = jest + .fn() + .mockResolvedValue(MOCK_BUCKET_FILES); const mockJobEntity: Partial = { id: jobId, @@ -457,12 +933,15 @@ describe('JobService', () => { }); }); - it('should create a fortune job successfully on network selected from round robin logic', async () => { + it('should create a job successfully on network selected from round robin logic', async () => { const fundAmount = imageLabelBinaryJobDto.fundAmount; const fee = (MOCK_JOB_LAUNCHER_FEE / 100) * fundAmount; const userBalance = 25; getUserBalanceMock.mockResolvedValue(userBalance); + storageService.listObjectsInBucket = jest + .fn() + .mockResolvedValue(MOCK_BUCKET_FILES); jest .spyOn(routingProtocolService, 'selectNetwork') @@ -522,6 +1001,9 @@ describe('JobService', () => { const userBalance = 100; getUserBalanceMock.mockResolvedValue(userBalance); + storageService.listObjectsInBucket = jest + .fn() + .mockResolvedValue(MOCK_BUCKET_FILES); jest.spyOn(jobRepository, 'create').mockResolvedValue(undefined!); @@ -535,6 +1017,165 @@ describe('JobService', () => { }); }); + describe('createJob with hCaptcha type', () => { + const userId = 1; + const jobId = 123; + + const hCaptchaJobDto: JobCaptchaDto = { + dataUrl: MOCK_FILE_URL, + accuracyTarget: 0.9, + completionDate: new Date(), + minRequests: 1, + maxRequests: 4, + advanced: { + workerLanguage: WorkerLanguage.EN, + workerLocation: WorkerLocation.FR, + targetBrowser: WorkerBrowser.DESKTOP, + }, + annotations: { + typeOfJob: JobCaptchaShapeType.COMPARISON, + taskBidPrice: 1, + labelingPrompt: 'Test description', + groundTruths: MOCK_FILE_URL, + exampleImages: [MOCK_FILE_URL], + }, + }; + + let getUserBalanceMock: any; + + beforeEach(() => { + getUserBalanceMock = jest.spyOn(paymentService, 'getUserBalance'); + createPaymentMock.mockResolvedValue(true); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('should create a job successfully', async () => { + const fundAmount = div( + hCaptchaJobDto.annotations.taskBidPrice * MOCK_BUCKET_FILES.length, + rate, + ); + const fee = (MOCK_JOB_LAUNCHER_FEE / 100) * fundAmount; + const userBalance = 25; + getUserBalanceMock.mockResolvedValue(userBalance); + + const mockJobEntity: Partial = { + id: jobId, + userId: userId, + chainId: ChainId.LOCALHOST, + manifestUrl: MOCK_FILE_URL, + manifestHash: MOCK_FILE_HASH, + escrowAddress: MOCK_ADDRESS, + fee, + fundAmount, + status: JobStatus.PENDING, + save: jest.fn().mockResolvedValue(true), + }; + + jobRepository.create = jest.fn().mockResolvedValue(mockJobEntity); + storageService.listObjectsInBucket = jest + .fn() + .mockResolvedValue(MOCK_BUCKET_FILES); + + await jobService.createJob( + userId, + JobRequestType.HCAPTCHA, + hCaptchaJobDto, + ); + + expect(paymentService.getUserBalance).toHaveBeenCalledWith(userId); + expect(paymentRepository.create).toHaveBeenCalledWith({ + userId, + jobId, + source: PaymentSource.BALANCE, + type: PaymentType.WITHDRAWAL, + currency: TokenId.HMT, + amount: -mul(fundAmount + fee, rate), + rate: div(1, rate), + status: PaymentStatus.SUCCEEDED, + }); + expect(jobRepository.create).toHaveBeenCalledWith({ + chainId: hCaptchaJobDto.chainId, + userId, + manifestUrl: expect.any(String), + manifestHash: expect.any(String), + fee: Number(mul(fee, rate).toFixed(3)), + fundAmount: mul(fundAmount, rate), + status: JobStatus.PENDING, + waitUntil: expect.any(Date), + }); + }); + + it('should create a job successfully on network selected from round robin logic', async () => { + const fundAmount = div( + hCaptchaJobDto.annotations.taskBidPrice * MOCK_BUCKET_FILES.length, + rate, + ); + const fee = (MOCK_JOB_LAUNCHER_FEE / 100) * fundAmount; + + const userBalance = 25; + getUserBalanceMock.mockResolvedValue(userBalance); + storageService.listObjectsInBucket = jest + .fn() + .mockResolvedValue(MOCK_BUCKET_FILES); + + jest + .spyOn(routingProtocolService, 'selectNetwork') + .mockReturnValue(ChainId.MOONBEAM); + + await jobService.createJob(userId, JobRequestType.HCAPTCHA, { + ...hCaptchaJobDto, + chainId: undefined, + }); + + expect(paymentService.getUserBalance).toHaveBeenCalledWith(userId); + expect(jobRepository.create).toHaveBeenCalledWith({ + chainId: ChainId.MOONBEAM, + userId, + manifestUrl: expect.any(String), + manifestHash: expect.any(String), + fee: mul(fee, rate), + fundAmount: mul(fundAmount, rate), + status: JobStatus.PENDING, + waitUntil: expect.any(Date), + }); + }); + + it('should throw an exception for insufficient user balance', async () => { + const userBalance = 1; + + jest + .spyOn(paymentService, 'getUserBalance') + .mockResolvedValue(userBalance); + + getUserBalanceMock.mockResolvedValue(userBalance); + storageService.listObjectsInBucket = jest + .fn() + .mockResolvedValue(MOCK_BUCKET_FILES); + + await expect( + jobService.createJob(userId, JobRequestType.HCAPTCHA, hCaptchaJobDto), + ).rejects.toThrowError(ErrorJob.NotEnoughFunds); + }); + + it('should throw an exception if job entity creation fails', async () => { + const userBalance = 100; + + getUserBalanceMock.mockResolvedValue(userBalance); + storageService.listObjectsInBucket = jest + .fn() + .mockResolvedValue(MOCK_BUCKET_FILES); + + jest.spyOn(jobRepository, 'create').mockResolvedValue(undefined!); + + await expect( + jobService.createJob(userId, JobRequestType.HCAPTCHA, hCaptchaJobDto), + ).rejects.toThrowError(ErrorJob.NotCreated); + }); + }); + describe('launchJob', () => { const chainId = ChainId.LOCALHOST; @@ -860,143 +1501,6 @@ describe('JobService', () => { }); }); - describe('saveManifest with fortune request type', () => { - const fortuneManifestParams = { - requestType: JobRequestType.FORTUNE, - submissionsRequired: MOCK_SUBMISSION_REQUIRED, - requesterDescription: MOCK_REQUESTER_DESCRIPTION, - fundAmount: 10, - requesterTitle: MOCK_REQUESTER_TITLE, - }; - - let uploadFilesMock: any; - - beforeEach(() => { - uploadFilesMock = storageService.uploadManifest; - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('should save the manifest and return the manifest URL and hash', async () => { - uploadFilesMock.mockResolvedValue({ - url: MOCK_FILE_URL, - hash: MOCK_FILE_HASH, - }); - - const result = await jobService.saveManifest(fortuneManifestParams); - - expect(result).toEqual({ - manifestUrl: MOCK_FILE_URL, - manifestHash: MOCK_FILE_HASH, - }); - expect(storageService.uploadManifest).toHaveBeenCalledWith( - fortuneManifestParams, - ); - }); - - it('should throw an error if the manifest file fails to upload', async () => { - const uploadError = new Error(ErrorBucket.UnableSaveFile); - - uploadFilesMock.mockRejectedValue(uploadError); - - await expect( - jobService.saveManifest(fortuneManifestParams), - ).rejects.toThrowError( - new BadGatewayException(ErrorBucket.UnableSaveFile), - ); - expect(storageService.uploadManifest).toHaveBeenCalledWith( - fortuneManifestParams, - ); - }); - - it('should rethrow any other errors encountered', async () => { - const errorMessage = 'Something went wrong'; - const uploadError = new Error(errorMessage); - - uploadFilesMock.mockRejectedValue(uploadError); - - await expect( - jobService.saveManifest(fortuneManifestParams), - ).rejects.toThrowError(new Error(errorMessage)); - - expect(storageService.uploadManifest).toHaveBeenCalledWith( - fortuneManifestParams, - ); - }); - }); - - describe('saveManifest with image label binary request type', () => { - const manifest: CvatManifestDto = { - data: { - data_url: MOCK_FILE_URL, - }, - annotation: { - labels: [{ name: 'label1' }], - description: MOCK_REQUESTER_DESCRIPTION, - user_guide: MOCK_FILE_URL, - type: JobRequestType.IMAGE_POINTS, - job_size: 10, - max_time: 300, - }, - validation: { - min_quality: 1, - val_size: 2, - gt_url: '', - }, - job_bounty: '1', - }; - - let uploadFilesMock: any; - - beforeEach(() => { - uploadFilesMock = storageService.uploadManifest; - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('should save the manifest and return the manifest URL and hash', async () => { - uploadFilesMock.mockResolvedValue({ - url: MOCK_FILE_URL, - hash: MOCK_FILE_HASH, - }); - - const result = await jobService.saveManifest(manifest); - - expect(result).toEqual({ - manifestUrl: MOCK_FILE_URL, - manifestHash: MOCK_FILE_HASH, - }); - expect(storageService.uploadManifest).toHaveBeenCalledWith(manifest); - }); - - it('should throw an error if the manifest file fails to upload', async () => { - const uploadError = new Error(ErrorBucket.UnableSaveFile); - - uploadFilesMock.mockRejectedValue(uploadError); - - await expect(jobService.saveManifest(manifest)).rejects.toThrowError( - new BadGatewayException(ErrorBucket.UnableSaveFile), - ); - expect(storageService.uploadManifest).toHaveBeenCalledWith(manifest); - }); - - it('should rethrow any other errors encountered', async () => { - const errorMessage = 'Something went wrong'; - const uploadError = new Error(errorMessage); - - uploadFilesMock.mockRejectedValue(uploadError); - - await expect(jobService.saveManifest(manifest)).rejects.toThrowError( - new Error(errorMessage), - ); - expect(storageService.uploadManifest).toHaveBeenCalledWith(manifest); - }); - }); - describe('getResult', () => { let downloadFileFromUrlMock: any; const jobEntityMock = { @@ -1392,9 +1896,9 @@ describe('JobService', () => { fundAmount: expect.any(Number), requesterAddress: MOCK_ADDRESS, requestType: JobRequestType.FORTUNE, - exchangeOracleAddress: undefined, - recordingOracleAddress: undefined, - reputationOracleAddress: undefined, + exchangeOracleAddress: ethers.constants.AddressZero, + recordingOracleAddress: ethers.constants.AddressZero, + reputationOracleAddress: ethers.constants.AddressZero, }, staking: { staker: expect.any(String), 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 a448a735df..ee96521cdd 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 @@ -9,7 +9,10 @@ import { StakingClient, StorageClient, KVStoreKeys, + Encryption, + EncryptionUtils, } from '@human-protocol/sdk'; +import { v4 as uuidv4 } from 'uuid'; import { BadGatewayException, BadRequestException, @@ -36,6 +39,9 @@ import { JobRequestType, JobStatus, JobStatusFilter, + JobCaptchaMode, + JobCaptchaRequestType, + JobCaptchaShapeType, } from '../../common/enums/job'; import { Currency, @@ -44,7 +50,12 @@ import { PaymentType, TokenId, } from '../../common/enums/payment'; -import { getRate, parseUrl } from '../../common/utils'; +import { + isPGPMessage, + getRate, + hashString, + isValidJSON, +} from '../../common/utils'; import { add, div, lt, mul } from '../../common/utils/decimal'; import { PaymentRepository } from '../payment/payment.repository'; import { PaymentService } from '../payment/payment.service'; @@ -59,7 +70,10 @@ import { JobDetailsDto, JobFortuneDto, JobListDto, - SaveManifestDto, + HCaptchaManifestDto, + JobCaptchaAdvancedDto, + JobCaptchaDto, + RestrictedAudience, } from './job.dto'; import { JobEntity } from './job.entity'; import { JobRepository } from './job.repository'; @@ -67,6 +81,19 @@ import { RoutingProtocolService } from './routing-protocol.service'; import { CANCEL_JOB_STATUSES, JOB_RETRIES_COUNT_THRESHOLD, + HCAPTCHA_BOUNDING_BOX_MAX_POINTS, + HCAPTCHA_BOUNDING_BOX_MIN_POINTS, + HCAPTCHA_IMMO_MAX_LENGTH, + HCAPTCHA_IMMO_MIN_LENGTH, + HCAPTCHA_LANDMARK_MAX_POINTS, + HCAPTCHA_LANDMARK_MIN_POINTS, + HCAPTCHA_MAX_SHAPES_PER_IMAGE, + HCAPTCHA_MINIMUM_SELECTION_AREA_PER_SHAPE, + HCAPTCHA_MIN_SHAPES_PER_IMAGE, + HCAPTCHA_NOT_PRESENTED_LABEL, + HCAPTCHA_ORACLE_STAKE, + HCAPTCHA_POLYGON_MAX_POINTS, + HCAPTCHA_POLYGON_MIN_POINTS, } from '../../common/constants'; import { SortDirection } from '../../common/enums/collection'; import { EventType, OracleType } from '../../common/enums/webhook'; @@ -78,8 +105,8 @@ import Decimal from 'decimal.js'; import { EscrowData } from '@human-protocol/sdk/dist/graphql'; import { filterToEscrowStatus } from '../../common/utils/status'; import { StorageService } from '../storage/storage.service'; -import { UploadedFile } from '../../common/interfaces/s3'; import { WebhookService } from '../webhook/webhook.service'; +import stringify from 'json-stable-stringify'; import { Cron, CronExpression } from '@nestjs/schedule'; @Injectable() @@ -98,22 +125,309 @@ export class JobService { private readonly webhookService: WebhookService, ) {} + public async createCvatManifest( + dto: JobCvatDto, + requestType: JobRequestType, + tokenFundAmount: number, + ): Promise { + return { + data: { + data_url: dto.dataUrl, + }, + annotation: { + labels: dto.labels.map((item) => ({ name: item })), + description: dto.requesterDescription, + user_guide: dto.userGuide, + type: requestType, + job_size: Number( + this.configService.get(ConfigNames.CVAT_JOB_SIZE)!, + ), + max_time: Number( + this.configService.get(ConfigNames.CVAT_MAX_TIME)!, + ), + }, + validation: { + min_quality: dto.minQuality, + val_size: Number( + this.configService.get(ConfigNames.CVAT_VAL_SIZE)!, + ), + gt_url: dto.gtUrl, + }, + job_bounty: await this.calculateJobBounty(dto.dataUrl, tokenFundAmount), + }; + } + + public async createHCaptchaManifest( + jobType: JobCaptchaShapeType, + jobDto: JobCaptchaDto, + ): Promise { + const objectsInBucket = await this.storageService.listObjectsInBucket( + jobDto.dataUrl, + ); + + const commonManifestProperties = { + job_mode: JobCaptchaMode.BATCH, + requester_accuracy_target: jobDto.accuracyTarget, + request_config: {}, + restricted_audience: this.buildHCaptchaRestrictedAudience( + jobDto.advanced, + ), + requester_max_repeats: jobDto.maxRequests, + requester_min_repeats: jobDto.minRequests, + requester_question: { en: jobDto.annotations.labelingPrompt }, + job_total_tasks: objectsInBucket.length, + task_bid_price: jobDto.annotations.taskBidPrice, + taskdata_uri: await this.generateAndUploadTaskData( + jobDto.dataUrl, + objectsInBucket, + ), + public_results: true, + oracle_stake: HCAPTCHA_ORACLE_STAKE, + repo_uri: this.configService.get( + ConfigNames.HCAPTCHA_REPUTATION_ORACLE_URI, + )!, + ro_uri: this.configService.get( + ConfigNames.HCAPTCHA_RECORDING_ORACLE_URI, + )!, + }; + + let groundTruthsData; + if (jobDto.annotations.groundTruths) { + groundTruthsData = await this.storageService.download( + jobDto.annotations.groundTruths, + ); + + if (isValidJSON(groundTruthsData)) { + groundTruthsData = JSON.parse(groundTruthsData); + } + } + + switch (jobType) { + case JobCaptchaShapeType.COMPARISON: + return { + ...commonManifestProperties, + request_type: JobCaptchaRequestType.IMAGE_LABEL_BINARY, + groundtruth_uri: jobDto.annotations.groundTruths, + requester_restricted_answer_set: {}, + requester_question_example: jobDto.annotations.exampleImages || [], + }; + + case JobCaptchaShapeType.CATEGORIZATION: + return { + ...commonManifestProperties, + request_type: JobCaptchaRequestType.IMAGE_LABEL_MULTIPLE_CHOICE, + groundtruth_uri: jobDto.annotations.groundTruths, + requester_restricted_answer_set: + this.buildHCaptchaRestrictedAnswerSet(groundTruthsData), + }; + + case JobCaptchaShapeType.POLYGON: + if (!jobDto.annotations.label) { + this.logger.log(ErrorJob.JobParamsValidationFailed, JobService.name); + throw new BadRequestException(ErrorJob.JobParamsValidationFailed); + } + + const polygonManifest = { + ...commonManifestProperties, + request_type: JobCaptchaRequestType.IMAGE_LABEL_AREA_SELECT, + request_config: { + shape_type: JobCaptchaShapeType.POLYGON, + min_shapes_per_image: HCAPTCHA_MIN_SHAPES_PER_IMAGE, + max_shapes_per_image: HCAPTCHA_MAX_SHAPES_PER_IMAGE, + min_points: HCAPTCHA_POLYGON_MIN_POINTS, + max_points: HCAPTCHA_POLYGON_MAX_POINTS, + minimum_selection_area_per_shape: + HCAPTCHA_MINIMUM_SELECTION_AREA_PER_SHAPE, + }, + groundtruth_uri: jobDto.annotations.groundTruths, + requester_restricted_answer_set: { + [jobDto.annotations.label!]: { en: jobDto.annotations.label }, + }, + requester_question_example: jobDto.annotations.exampleImages || [], + }; + + return polygonManifest; + + case JobCaptchaShapeType.POINT: + if (!jobDto.annotations.label) { + this.logger.log(ErrorJob.JobParamsValidationFailed, JobService.name); + throw new BadRequestException(ErrorJob.JobParamsValidationFailed); + } + + const pointManifest = { + ...commonManifestProperties, + request_type: JobCaptchaRequestType.IMAGE_LABEL_AREA_SELECT, + request_config: { + shape_type: jobType, + min_shapes_per_image: HCAPTCHA_MIN_SHAPES_PER_IMAGE, + max_shapes_per_image: HCAPTCHA_MAX_SHAPES_PER_IMAGE, + min_points: HCAPTCHA_LANDMARK_MIN_POINTS, + max_points: HCAPTCHA_LANDMARK_MAX_POINTS, + }, + groundtruth_uri: jobDto.annotations.groundTruths, + requester_restricted_answer_set: { + [jobDto.annotations.label!]: { en: jobDto.annotations.label }, + }, + requester_question_example: jobDto.annotations.exampleImages || [], + }; + + return pointManifest; + case JobCaptchaShapeType.BOUNDING_BOX: + if (!jobDto.annotations.label) { + this.logger.log(ErrorJob.JobParamsValidationFailed, JobService.name); + throw new BadRequestException(ErrorJob.JobParamsValidationFailed); + } + + const boundingBoxManifest = { + ...commonManifestProperties, + request_type: JobCaptchaRequestType.IMAGE_LABEL_AREA_SELECT, + request_config: { + shape_type: jobType, + min_shapes_per_image: HCAPTCHA_MIN_SHAPES_PER_IMAGE, + max_shapes_per_image: HCAPTCHA_MAX_SHAPES_PER_IMAGE, + min_points: HCAPTCHA_BOUNDING_BOX_MIN_POINTS, + max_points: HCAPTCHA_BOUNDING_BOX_MAX_POINTS, + }, + groundtruth_uri: jobDto.annotations.groundTruths, + requester_restricted_answer_set: { + [jobDto.annotations.label!]: { en: jobDto.annotations.label }, + }, + requester_question_example: jobDto.annotations.exampleImages || [], + }; + + return boundingBoxManifest; + case JobCaptchaShapeType.IMMO: + if (!jobDto.annotations.label) { + this.logger.log(ErrorJob.JobParamsValidationFailed, JobService.name); + throw new BadRequestException(ErrorJob.JobParamsValidationFailed); + } + + const immoManifest = { + ...commonManifestProperties, + request_type: JobCaptchaRequestType.TEXT_FREEE_NTRY, + request_config: { + multiple_choice_max_choices: 1, + multiple_choice_min_choices: 1, + overlap_threshold: null, + answer_type: 'str', + max_length: HCAPTCHA_IMMO_MAX_LENGTH, + min_length: HCAPTCHA_IMMO_MIN_LENGTH, + }, + requester_restricted_answer_set: { + [jobDto.annotations.label!]: { en: jobDto.annotations.label }, + }, + taskdata: [], + }; + + return immoManifest; + + default: + this.logger.log(ErrorJob.HCaptchaInvalidJobType, JobService.name); + throw new ConflictException(ErrorJob.HCaptchaInvalidJobType); + } + } + + private buildHCaptchaRestrictedAudience(advanced: JobCaptchaAdvancedDto) { + const restrictedAudience: RestrictedAudience = {}; + + restrictedAudience.sitekey = [ + { + [this.configService.get(ConfigNames.HCAPTCHA_SITE_KEY)!]: { + score: 1, + }, + }, + ]; + + if (advanced.workerLanguage) { + restrictedAudience.lang = [{ [advanced.workerLanguage]: { score: 1 } }]; + } + + if (advanced.workerLocation) { + restrictedAudience.country = [ + { [advanced.workerLocation]: { score: 1 } }, + ]; + } + + if (advanced.targetBrowser) { + restrictedAudience.browser = [{ [advanced.targetBrowser]: { score: 1 } }]; + } + + return restrictedAudience; + } + + private buildHCaptchaRestrictedAnswerSet(groundTruthsData: any) { + const maxElements = 3; + const outputObject: any = {}; + + let elementCount = 0; + + for (const key of Object.keys(groundTruthsData)) { + if (elementCount >= maxElements) { + break; + } + + const value = groundTruthsData[key][0][0]; + outputObject[value] = { en: value, answer_example_uri: key }; + elementCount++; + } + + // Default case + outputObject['0'] = { en: HCAPTCHA_NOT_PRESENTED_LABEL }; + + return outputObject; + } + + public async generateAndUploadTaskData( + dataUrl: string, + objectNames: string[], + ) { + const data = objectNames.map((objectName) => { + return { + datapoint_uri: `${dataUrl}/${objectName}`, + datapoint_hash: 'undefined-hash', + task_key: uuidv4(), + }; + }); + + const hash = hashString(stringify(data)); + const { url } = await this.storageService.uploadFile(data, hash); + return url; + } + public async createJob( userId: number, requestType: JobRequestType, - dto: JobFortuneDto | JobCvatDto, + dto: JobFortuneDto | JobCvatDto | JobCaptchaDto, ): Promise { - let manifestUrl, manifestHash; - const { chainId, fundAmount } = dto; + const { chainId } = dto; if (chainId) { this.web3Service.validateChainId(chainId); } - const userBalance = await this.paymentService.getUserBalance(userId); - + let manifestOrigin, manifestEncrypted, fundAmount; const rate = await getRate(Currency.USD, TokenId.HMT); + if (requestType === JobRequestType.HCAPTCHA) { + // hCaptcha + dto = dto as JobCaptchaDto; + const objectsInBucket = await this.storageService.listObjectsInBucket( + dto.dataUrl, + ); + fundAmount = div( + dto.annotations.taskBidPrice * objectsInBucket.length, + rate, + ); + } else if (requestType === JobRequestType.FORTUNE) { + // Fortune + dto = dto as JobFortuneDto; + fundAmount = dto.fundAmount; + } else { + // CVAT + dto = dto as JobCvatDto; + fundAmount = dto.fundAmount; + } + const userBalance = await this.paymentService.getUserBalance(userId); const feePercentage = this.configService.get( ConfigNames.JOB_LAUNCHER_FEE, )!; @@ -129,46 +443,47 @@ export class JobService { const tokenFee = mul(fee, rate); const tokenTotalAmount = add(tokenFundAmount, tokenFee); - if (requestType == JobRequestType.FORTUNE) { - ({ manifestUrl, manifestHash } = await this.saveManifest({ - ...(dto as JobFortuneDto), - requestType, - fundAmount: tokenFundAmount, - })); + if (requestType === JobRequestType.HCAPTCHA) { + // hCaptcha + dto = dto as JobCaptchaDto; + dto.dataUrl = dto.dataUrl.replace(/\/$/, ''); + manifestOrigin = await this.createHCaptchaManifest( + dto.annotations.typeOfJob, + dto, + ); + + manifestEncrypted = await EncryptionUtils.encrypt( + stringify(manifestOrigin), + [ + this.configService.get(ConfigNames.PGP_PUBLIC_KEY)!, + this.configService.get(ConfigNames.HCAPTCHA_PGP_PUBLIC_KEY)!, + ], + ); + } else if (requestType == JobRequestType.FORTUNE) { + // Fortune + dto = dto as JobFortuneDto; + manifestOrigin = { ...dto, requestType, fundAmount: tokenTotalAmount }; } else { + // CVAT dto = dto as JobCvatDto; - ({ manifestUrl, manifestHash } = await this.saveManifest({ - data: { - data_url: dto.dataUrl, - }, - annotation: { - labels: dto.labels.map((item) => ({ name: item })), - description: dto.requesterDescription, - user_guide: dto.userGuide, - type: requestType, - job_size: Number( - this.configService.get(ConfigNames.CVAT_JOB_SIZE)!, - ), - max_time: Number( - this.configService.get(ConfigNames.CVAT_MAX_TIME)!, - ), - }, - validation: { - min_quality: dto.minQuality, - val_size: Number( - this.configService.get(ConfigNames.CVAT_VAL_SIZE)!, - ), - gt_url: dto.gtUrl, - }, - job_bounty: await this.calculateJobBounty(dto.dataUrl, tokenFundAmount), - })); + dto.dataUrl = dto.dataUrl.replace(/\/$/, ''); + manifestOrigin = await this.createCvatManifest( + dto, + requestType, + tokenFundAmount, + ); } + const hash = hashString(stringify(manifestOrigin)); + const { url } = await this.storageService.uploadFile( + manifestEncrypted || manifestOrigin, + hash, + ); const jobEntity = await this.jobRepository.create({ chainId: chainId ?? this.routingProtocolService.selectNetwork(), userId, - manifestUrl, - manifestHash, + manifestUrl: url, + manifestHash: hash, fee: tokenFee, fundAmount: tokenFundAmount, status: JobStatus.PENDING, @@ -210,25 +525,13 @@ export class JobService { return jobEntity.id; } - private async calculateJobBounty( + public async calculateJobBounty( endpointUrl: string, fundAmount: number, ): Promise { - const storageData = parseUrl(endpointUrl); - - if (!storageData.bucket) { - throw new BadRequestException(ErrorBucket.NotExist); - } - - const storageClient = new StorageClient({ - endPoint: storageData.endPoint, - port: storageData.port, - useSSL: storageData.useSSL, - region: storageData.region, - }); - - const totalImages = (await storageClient.listObjects(storageData.bucket)) - .length; + const totalImages = ( + await this.storageService.listObjectsInBucket(endpointUrl) + ).length; const totalJobs = Math.ceil( div( @@ -247,17 +550,38 @@ export class JobService { const escrowClient = await EscrowClient.build(signer); - const manifest = await this.storageService.download(jobEntity.manifestUrl); + let manifest = await this.storageService.download(jobEntity.manifestUrl); + if (typeof manifest === 'string' && isPGPMessage(manifest)) { + const encription = await Encryption.build( + this.configService.get(ConfigNames.PGP_PRIVATE_KEY)!, + ); + manifest = await encription.decrypt(manifest as any); + } - const recordingOracleConfigKey = - (manifest as FortuneManifestDto).requestType === JobRequestType.FORTUNE - ? ConfigNames.FORTUNE_RECORDING_ORACLE_ADDRESS - : ConfigNames.CVAT_RECORDING_ORACLE_ADDRESS; + if (isValidJSON(manifest)) { + manifest = JSON.parse(manifest); + } + + await this.validateManifest(manifest); + + let recordingOracleConfigKey; + let exchangeOracleConfigKey; + let trustedHandlers; - const exchangeOracleConfigKey = + if ( (manifest as FortuneManifestDto).requestType === JobRequestType.FORTUNE - ? ConfigNames.FORTUNE_EXCHANGE_ORACLE_ADDRESS - : ConfigNames.CVAT_EXCHANGE_ORACLE_ADDRESS; + ) { + recordingOracleConfigKey = ConfigNames.FORTUNE_RECORDING_ORACLE_ADDRESS; + exchangeOracleConfigKey = ConfigNames.FORTUNE_EXCHANGE_ORACLE_ADDRESS; + } else if ( + (manifest as HCaptchaManifestDto).job_mode === JobCaptchaMode.BATCH + ) { + recordingOracleConfigKey = ConfigNames.HCAPTCHA_ORACLE_ADDRESS; + exchangeOracleConfigKey = ConfigNames.HCAPTCHA_ORACLE_ADDRESS; + } else { + recordingOracleConfigKey = ConfigNames.CVAT_RECORDING_ORACLE_ADDRESS; + exchangeOracleConfigKey = ConfigNames.CVAT_EXCHANGE_ORACLE_ADDRESS; + } const recordingOracleAddress = this.configService.get( recordingOracleConfigKey, @@ -369,29 +693,22 @@ export class JobService { await jobEntity.save(); } - public async saveManifest( - manifest: FortuneManifestDto | CvatManifestDto, - ): Promise { - const uploadedManifest: UploadedFile = - await this.storageService.uploadManifest(manifest); - - if (!uploadedManifest) { - this.logger.log(ErrorBucket.UnableSaveFile, JobService.name); - throw new BadGatewayException(ErrorBucket.UnableSaveFile); - } - - const manifestUrl = uploadedManifest.url; - - return { manifestUrl, manifestHash: uploadedManifest.hash }; - } - private async validateManifest( - manifest: FortuneManifestDto | CvatManifestDto, + manifest: FortuneManifestDto | CvatManifestDto | HCaptchaManifestDto, ): Promise { - const dtoCheck = - (manifest as FortuneManifestDto).requestType == JobRequestType.FORTUNE - ? new FortuneManifestDto() - : new CvatManifestDto(); + let dtoCheck; + + if ( + (manifest as FortuneManifestDto).requestType === JobRequestType.FORTUNE + ) { + dtoCheck = new FortuneManifestDto(); + } else if ( + (manifest as HCaptchaManifestDto).job_mode === JobCaptchaMode.BATCH + ) { + dtoCheck = new HCaptchaManifestDto(); + } else { + dtoCheck = new CvatManifestDto(); + } Object.assign(dtoCheck, manifest); @@ -624,7 +941,6 @@ export class JobService { const manifest = await this.storageService.download( jobEntity.manifestUrl, ); - await this.validateManifest(manifest); if (!jobEntity.escrowAddress && jobEntity.status === JobStatus.PAID) { jobEntity = await this.launchJob(jobEntity); @@ -687,22 +1003,39 @@ export class JobService { const manifest = await this.storageService.download(jobEntity.manifestUrl); - await this.webhookService.createWebhook({ - escrowAddress: jobEntity.escrowAddress, - chainId: jobEntity.chainId, - eventType: EventType.ESCROW_CANCELED, - oracleType: - (manifest as FortuneManifestDto).requestType === JobRequestType.FORTUNE - ? OracleType.FORTUNE - : OracleType.CVAT, - hasSignature: - (manifest as FortuneManifestDto).requestType === JobRequestType.FORTUNE, - }); + const oracleType = this.getOracleType(manifest); + if (oracleType !== OracleType.HCAPTCHA) { + await this.webhookService.createWebhook({ + escrowAddress: jobEntity.escrowAddress, + chainId: jobEntity.chainId, + eventType: EventType.ESCROW_CANCELED, + oracleType: this.getOracleType(manifest), + hasSignature: + (manifest as FortuneManifestDto).requestType === + JobRequestType.FORTUNE, + }); + } this.logger.log('Cancel jobs STOP'); return true; } + private getOracleType(manifest: any): OracleType { + if ( + (manifest as FortuneManifestDto).requestType === JobRequestType.FORTUNE + ) { + return OracleType.FORTUNE; + } else if ( + (manifest as CvatManifestDto)?.annotation?.type === + JobRequestType.IMAGE_BOXES || + (manifest as CvatManifestDto)?.annotation?.type === + JobRequestType.IMAGE_POINTS + ) { + return OracleType.CVAT; + } + return OracleType.HCAPTCHA; + } + public async processEscrowCancellation( jobEntity: JobEntity, ): Promise { @@ -782,42 +1115,76 @@ export class JobService { jobEntity = await this.updateCompletedStatus(jobEntity, escrow); } - const manifestData = await this.storageService.download(manifestUrl); + let manifestData = await this.storageService.download(manifestUrl); if (!manifestData) { throw new NotFoundException(ErrorJob.ManifestNotFound); } - const manifest = + let manifest; + if (typeof manifestData === 'string' && isPGPMessage(manifestData)) { + const encription = await Encryption.build( + this.configService.get(ConfigNames.PGP_PRIVATE_KEY)!, + ); + manifestData = await encription.decrypt(manifestData as any); + } + + if (isValidJSON(manifestData)) { + manifestData = JSON.parse(manifestData); + } + + if ( (manifestData as FortuneManifestDto).requestType === JobRequestType.FORTUNE - ? (manifestData as FortuneManifestDto) - : (manifestData as CvatManifestDto); + ) { + manifest = manifestData as FortuneManifestDto; + } else if ( + (manifestData as HCaptchaManifestDto)?.job_mode === JobCaptchaMode.BATCH + ) { + manifest = manifestData as HCaptchaManifestDto; + } else { + manifest = manifestData as CvatManifestDto; + } const baseManifestDetails = { chainId, tokenAddress: escrow ? escrow.token : ethers.constants.AddressZero, - fundAmount: escrow ? Number(escrow.totalFundedAmount) : 0, requesterAddress: signer.address, - exchangeOracleAddress: escrow?.exchangeOracle, - recordingOracleAddress: escrow?.recordingOracle, - reputationOracleAddress: escrow?.reputationOracle, + fundAmount: escrow ? Number(escrow.totalFundedAmount) : 0, + exchangeOracleAddress: + escrow?.exchangeOracle || ethers.constants.AddressZero, + recordingOracleAddress: + escrow?.recordingOracle || ethers.constants.AddressZero, + reputationOracleAddress: + escrow?.reputationOracle || ethers.constants.AddressZero, }; - const specificManifestDetails = + let specificManifestDetails; + if ( (manifest as FortuneManifestDto).requestType === JobRequestType.FORTUNE - ? { - title: (manifest as FortuneManifestDto).requesterTitle, - description: (manifest as FortuneManifestDto).requesterDescription, - requestType: JobRequestType.FORTUNE, - submissionsRequired: (manifest as FortuneManifestDto) - .submissionsRequired, - } - : { - requestType: (manifest as CvatManifestDto).annotation.type, - submissionsRequired: (manifest as CvatManifestDto).annotation - .job_size, - }; + ) { + manifest = manifest as FortuneManifestDto; + specificManifestDetails = { + title: manifest.requesterTitle, + description: manifest.requesterDescription, + requestType: JobRequestType.FORTUNE, + submissionsRequired: manifest.submissionsRequired, + }; + } else if ( + (manifest as HCaptchaManifestDto).job_mode === JobCaptchaMode.BATCH + ) { + manifest = manifest as HCaptchaManifestDto; + specificManifestDetails = { + requestType: JobRequestType.HCAPTCHA, + submissionsRequired: manifest.job_total_tasks, + }; + } else { + manifest = manifest as CvatManifestDto; + specificManifestDetails = { + requestType: manifest.annotation.type, + submissionsRequired: manifest.annotation.job_size, + }; + } const manifestDetails = { ...baseManifestDetails, diff --git a/packages/apps/job-launcher/server/src/modules/storage/storage.service.spec.ts b/packages/apps/job-launcher/server/src/modules/storage/storage.service.spec.ts index 746adc709b..807206f46c 100644 --- a/packages/apps/job-launcher/server/src/modules/storage/storage.service.spec.ts +++ b/packages/apps/job-launcher/server/src/modules/storage/storage.service.spec.ts @@ -2,6 +2,7 @@ import { StorageClient } from '@human-protocol/sdk'; import { ConfigModule, registerAs } from '@nestjs/config'; import { Test } from '@nestjs/testing'; import { + MOCK_FILE_HASH, MOCK_FILE_URL, MOCK_MANIFEST, MOCK_S3_ACCESS_KEY, @@ -12,9 +13,10 @@ import { MOCK_S3_USE_SSL, } from '../../../test/constants'; import { StorageService } from './storage.service'; -import crypto from 'crypto'; -import axios from 'axios'; -import stream from 'stream'; +import stringify from 'json-stable-stringify'; +import { ErrorBucket } from '../../common/constants/errors'; +import { hashString } from '../../common/utils'; +import { ContentType } from '../../common/enums/storage'; jest.mock('@human-protocol/sdk', () => ({ ...jest.requireActual('@human-protocol/sdk'), @@ -62,31 +64,25 @@ describe('Web3Service', () => { storageService = moduleRef.get(StorageService); }); - describe('uploadManifest', () => { + describe('uploadFile', () => { it('should upload the manifest correctly', async () => { storageService.minioClient.bucketExists = jest .fn() .mockResolvedValueOnce(true); - const hash = crypto - .createHash('sha1') - .update(JSON.stringify(MOCK_MANIFEST)) - .digest('hex'); + const hash = hashString(stringify(MOCK_MANIFEST)); - const fileData = await storageService.uploadManifest(MOCK_MANIFEST); + const fileData = await storageService.uploadFile(MOCK_MANIFEST, hash); expect(fileData).toEqual({ - url: `http://${MOCK_S3_ENDPOINT}:${MOCK_S3_PORT}/${MOCK_S3_BUCKET}/s3${hash}.json`, - hash: crypto - .createHash('sha1') - .update(JSON.stringify(MOCK_MANIFEST)) - .digest('hex'), + url: expect.any(String), + hash: expect.any(String), }); expect(storageService.minioClient.putObject).toHaveBeenCalledWith( MOCK_S3_BUCKET, - `s3${hash}.json`, + expect.any(String), expect.any(String), { - 'Content-Type': 'application/json', + 'Content-Type': ContentType.APPLICATION_JSON, 'Cache-Control': 'no-store', }, ); @@ -98,8 +94,8 @@ describe('Web3Service', () => { .mockResolvedValueOnce(false); await expect( - storageService.uploadManifest(MOCK_MANIFEST), - ).rejects.toThrow('Bucket not found'); + storageService.uploadFile(MOCK_MANIFEST, MOCK_FILE_HASH), + ).rejects.toThrow(ErrorBucket.NotExist); }); it('should fail if the file cannot be uploaded', async () => { @@ -111,7 +107,7 @@ describe('Web3Service', () => { .mockRejectedValueOnce('Network error'); await expect( - storageService.uploadManifest(MOCK_MANIFEST), + storageService.uploadFile(MOCK_MANIFEST, MOCK_FILE_HASH), ).rejects.toThrow('File not uploaded'); }); }); @@ -148,53 +144,4 @@ describe('Web3Service', () => { expect(solutionsFile).toStrictEqual([]); }); }); - - describe('copyFileFromURLToBucket', () => { - it('should copy a file from a valid URL to a bucket', async () => { - const streamResponseData = new stream.Readable(); - streamResponseData.push(JSON.stringify(MOCK_MANIFEST)); - streamResponseData.push(null); - (axios.get as any).mockResolvedValueOnce({ data: streamResponseData }); - - const uploadedFile = - await storageService.copyFileFromURLToBucket(MOCK_FILE_URL); - - expect( - uploadedFile.url.includes( - `http://${MOCK_S3_ENDPOINT}:${MOCK_S3_PORT}/${MOCK_S3_BUCKET}/`, - ), - ).toBeTruthy(); - expect(uploadedFile.hash).toBeDefined(); - expect(storageService.minioClient.putObject).toBeCalledWith( - MOCK_S3_BUCKET, - expect.any(String), - expect.any(stream), - { 'Cache-Control': 'no-store' }, - ); - }); - - it('should handle an invalid URL', async () => { - (axios.get as any).mockRejectedValue('Network error'); - - await expect( - storageService.copyFileFromURLToBucket(MOCK_FILE_URL), - ).rejects.toThrow('File not uploaded'); - }); - - it('should handle errors when copying the file', async () => { - const streamResponseData = new stream.Readable(); - streamResponseData.push(JSON.stringify(MOCK_MANIFEST)); - streamResponseData.push(null); - (axios.get as any).mockResolvedValueOnce({ data: streamResponseData }); - storageService.minioClient.putObject = jest - .fn() - .mockRejectedValue('Network error'); - - await expect( - storageService.copyFileFromURLToBucket( - 'https://example.com/archivo.zip', - ), - ).rejects.toThrow('File not uploaded'); - }); - }); }); diff --git a/packages/apps/job-launcher/server/src/modules/storage/storage.service.ts b/packages/apps/job-launcher/server/src/modules/storage/storage.service.ts index ab71767053..ec5486e5ee 100644 --- a/packages/apps/job-launcher/server/src/modules/storage/storage.service.ts +++ b/packages/apps/job-launcher/server/src/modules/storage/storage.service.ts @@ -2,13 +2,12 @@ import { StorageClient } from '@human-protocol/sdk'; import { BadRequestException, Inject, Injectable } from '@nestjs/common'; import * as Minio from 'minio'; import { S3ConfigType, s3ConfigKey } from '../../common/config'; -import crypto from 'crypto'; -import { UploadedFile } from '../../common/interfaces/s3'; -import { PassThrough } from 'stream'; import axios from 'axios'; -import { Logger } from '@nestjs/common'; -import { hashStream } from '../../common/utils'; -import { CvatManifestDto, FortuneManifestDto } from '../job/job.dto'; +import { ErrorBucket } from '../../common/constants/errors'; +import { parseString } from 'xml2js'; +import stringify from 'json-stable-stringify'; +import { ContentType, Extension } from '../../common/enums/storage'; +import { UploadedFile } from '../../common/interfaces'; @Injectable() export class StorageService { @@ -26,7 +25,7 @@ export class StorageService { useSSL: this.s3Config.useSSL, }); } - public getUrl(key: string): string { + public formatUrl(key: string): string { return `${this.s3Config.useSSL ? 'https' : 'http'}://${ this.s3Config.endPoint }:${this.s3Config.port}/${this.s3Config.bucket}/${key}`; @@ -40,63 +39,55 @@ export class StorageService { } } - public async uploadManifest( - manifest: FortuneManifestDto | CvatManifestDto | string, + public async uploadFile( + data: string | object, + hash: string, ): Promise { if (!(await this.minioClient.bucketExists(this.s3Config.bucket))) { - throw new BadRequestException('Bucket not found'); + throw new BadRequestException(ErrorBucket.NotExist); } - const isString = typeof manifest === 'string'; - - const contentType = isString ? 'text/plain' : 'application/json'; - - const content = isString ? manifest : JSON.stringify(manifest); - - const hash = crypto.createHash('sha1').update(content).digest('hex'); - const key = isString ? `s3${hash}` : `s3${hash}.json`; + const isStringData = typeof data === 'string'; + const contentType = isStringData + ? ContentType.TEXT_PLAIN + : ContentType.APPLICATION_JSON; + const content = isStringData ? data : stringify(data); + const key = `s3${hash}${isStringData ? '' : Extension.JSON}`; try { - const hash = crypto.createHash('sha1').update(content).digest('hex'); await this.minioClient.putObject(this.s3Config.bucket, key, content, { 'Content-Type': contentType, 'Cache-Control': 'no-store', }); - return { url: this.getUrl(key), hash }; + return { url: this.formatUrl(key), hash }; } catch (e) { throw new BadRequestException('File not uploaded'); } } - /** - * **Copy file from a URL to cloud storage** - * - * @param {string} url - URL of the source file - * @returns {Promise} - Uploaded file with key/hash - */ - public async copyFileFromURLToBucket(url: string): Promise { - try { - const { data } = await axios.get(url, { responseType: 'stream' }); - const stream = new PassThrough(); - data.pipe(stream); - - const hash = await hashStream(stream); - const key = `s3${hash}.zip`; + public async listObjectsInBucket(bucketUrl: string): Promise { + return new Promise(async (resolve, reject) => { + try { + const response = await axios.get(bucketUrl); - await this.minioClient.putObject(this.s3Config.bucket, key, stream, { - 'Cache-Control': 'no-store', - }); - - Logger.log(`File from ${url} copied to ${this.s3Config.bucket}/${key}`); + if (response.status === 200 && response.data) { + parseString(response.data, (err: any, result: any) => { + if (err) { + reject(err); + } - return { - url: this.getUrl(key), - hash, - }; - } catch (error) { - Logger.error('Error copying file:', error); - throw new Error('File not uploaded'); - } + const objectKeys = result.ListBucketResult.Contents.map( + (item: any) => item.Key, + ); + resolve(objectKeys.flat()); + }); + } else { + reject(ErrorBucket.FailedToFetchBucketContents); + } + } catch (err) { + reject(err); + } + }); } } diff --git a/packages/apps/job-launcher/server/test/constants.ts b/packages/apps/job-launcher/server/test/constants.ts index ea2b70dfe1..eb4cebf81d 100644 --- a/packages/apps/job-launcher/server/test/constants.ts +++ b/packages/apps/job-launcher/server/test/constants.ts @@ -63,4 +63,57 @@ export const MOCK_MANIFEST: FortuneManifestDto = { fundAmount: 10, requestType: JobRequestType.FORTUNE, }; +export const MOCK_ENCRYPTED_MANIFEST = 'encryptedManifest'; +export const MOCK_PGP_PRIVATE_KEY = `-----BEGIN PGP PRIVATE KEY BLOCK----- + +xVgEZTveLhYJKwYBBAHaRw8BAQdAD7pTrDk129glXoPH+rMofQwsQuyuo1j0 +Q1fXrBmoPwcAAP4qsdYoTBZ7fXhaP9sjJMhGtYLg2Ux7ODQ5Ix6Ws7/V4A/M +zRZldWdlbmUgPGV1Z2VuZUBobXQuYWk+wowEEBYKAD4FgmU73i4ECwkHCAmQ ++tuf/UifrmMDFQgKBBYAAgECGQECmwMCHgEWIQRihZ0fm6KM8/bPWt7625/9 +SJ+uYwAAq/0BAOJ3WYERhsp2xSbS45Gixp9QGsOCC1Ef2TiUlO2R3vDAAP9Q +1snuCth7bM1EdJBsIYJPuGP0CL0Nv4hzZm0KcNxtDsddBGU73i4SCisGAQQB +l1UBBQEBB0B5C9OzSIcuiWkhkzO5xyc5k4pp0JRqGLs81d7OCWUgSQMBCAcA +AP9wcvfUXUNks+0ggHgI7iZTy7q1slQT8YUh+oyxU6UheBGJwngEGBYIACoF +gmU73i4JkPrbn/1In65jApsMFiEEYoWdH5uijPP2z1re+tuf/UifrmMAAMZQ +AQDGveM6dh1co+7vkCsYs4vCoDBRQcSByD6FSbBmq8JJugD7Bpk0bm95Oi9M +UJgXNi74umGlLKtWNrbJIYiY1yHFSA4= +=27X/ +-----END PGP PRIVATE KEY BLOCK-----`; +export const MOCK_PGP_PUBLIC_KEY = `-----BEGIN PGP PUBLIC KEY BLOCK----- + +xjMEZTveLhYJKwYBBAHaRw8BAQdAD7pTrDk129glXoPH+rMofQwsQuyuo1j0 +Q1fXrBmoPwfNFmV1Z2VuZSA8ZXVnZW5lQGhtdC5haT7CjAQQFgoAPgWCZTve +LgQLCQcICZD625/9SJ+uYwMVCAoEFgACAQIZAQKbAwIeARYhBGKFnR+boozz +9s9a3vrbn/1In65jAACr/QEA4ndZgRGGynbFJtLjkaLGn1Aaw4ILUR/ZOJSU +7ZHe8MAA/1DWye4K2HtszUR0kGwhgk+4Y/QIvQ2/iHNmbQpw3G0OzjgEZTve +LhIKKwYBBAGXVQEFAQEHQHkL07NIhy6JaSGTM7nHJzmTimnQlGoYuzzV3s4J +ZSBJAwEIB8J4BBgWCAAqBYJlO94uCZD625/9SJ+uYwKbDBYhBGKFnR+boozz +9s9a3vrbn/1In65jAADGUAEAxr3jOnYdXKPu75ArGLOLwqAwUUHEgcg+hUmw +ZqvCSboA+waZNG5veTovTFCYFzYu+LphpSyrVja2ySGImNchxUgO +=uLQK +-----END PGP PUBLIC KEY BLOCK-----`; +export const MOCK_HCAPTCHA_PGP_PUBLIC_KEY = `-----BEGIN PGP PUBLIC KEY BLOCK----- + +xjMEZTFB7RYJKwYBBAHaRw8BAQdAEqnF7yvbnHaL5nM7uryCts/FAnBazgBA +ldusotlPEgnNGWFsaWR6bSA8cWFyejg5QGdtYWlsLmNvbT7CjAQQFgoAPgWC +ZTFB7QQLCQcICZCyJEVcrn3KbAMVCAoEFgACAQIZAQKbAwIeARYhBCWadFbn +oT02XD9wsbIkRVyufcpsAAA02AD/bTo/OX+PceOMfWgQlK4KrUTrrEFayWgL +RODAqZIVFXABAK/q1P1t54pSXmZs1p76LR9eLkzWpXzxs1UjYUlJPzEPzjgE +ZTFB7RIKKwYBBAGXVQEFAQEHQB8t/IZJLOiA0erKV7qyXWpvdiUegoDpdeDU +68nBw1tiAwEIB8J4BBgWCAAqBYJlMUHtCZCyJEVcrn3KbAKbDBYhBCWadFbn +oT02XD9wsbIkRVyufcpsAADvXAEAu9cf+VXbCe5Kj+7G3gRQnO+smX/gySHj +Cj8wO9Ii68YA/1EpYseshTKcNncCad8Npro313/PpE3SzsCP1b+58mkD +=XWPr +-----END PGP PUBLIC KEY BLOCK-----`; +export const MOCK_HCAPTCHA_ORACLE_ADDRESS = + '0xa62a1c18571b869e43eeabd217e233e7f0275af3'; +export const MOCK_CVAT_JOB_SIZE = '10'; +export const MOCK_CVAT_MAX_TIME = '300'; +export const MOCK_CVAT_VAL_SIZE = '2'; +export const MOCK_HCAPTCHA_SITE_KEY = '1234'; +export const MOCK_HCAPTCHA_IMAGE_URL = + 'http://mockedFileUrl.test/bucket/img_1.jpg'; +export const MOCK_HCAPTCHA_IMAGE_LABEL = 'cat'; +export const MOCK_HCAPTCHA_REPO_URI = 'http://recoracle:3000'; +export const MOCK_HCAPTCHA_RO_URI = 'http://recoracle:3000'; export const MOCK_MAX_RETRY_COUNT = 5; diff --git a/yarn.lock b/yarn.lock index be99c47db1..c3b29d5354 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1573,6 +1573,11 @@ resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz#d0fce5d07b0620caa282b5131c297bb60f9d87e6" integrity sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww== +"@esbuild/aix-ppc64@0.19.10": + version "0.19.10" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.19.10.tgz#fb3922a0183d27446de00cf60d4f7baaadf98d84" + integrity sha512-Q+mk96KJ+FZ30h9fsJl+67IjNJm3x2eX+GBWGmocAKgzp27cowCOOqSdscX80s0SpdFXZnIv/+1xD1EctFx96Q== + "@esbuild/android-arm64@0.16.17": version "0.16.17" resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.16.17.tgz#cf91e86df127aa3d141744edafcba0abdc577d23" @@ -1583,16 +1588,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz#984b4f9c8d0377443cc2dfcef266d02244593622" integrity sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ== +"@esbuild/android-arm64@0.19.10": + version "0.19.10" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.19.10.tgz#ef31015416dd79398082409b77aaaa2ade4d531a" + integrity sha512-1X4CClKhDgC3by7k8aOWZeBXQX8dHT5QAMCAQDArCLaYfkppoARvh0fit3X2Qs+MXDngKcHv6XXyQCpY0hkK1Q== + "@esbuild/android-arm64@0.19.2": version "0.19.2" resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.19.2.tgz#bc35990f412a749e948b792825eef7df0ce0e073" integrity sha512-lsB65vAbe90I/Qe10OjkmrdxSX4UJDjosDgb8sZUKcg3oefEuW2OT2Vozz8ef7wrJbMcmhvCC+hciF8jY/uAkw== -"@esbuild/android-arm64@0.19.9": - version "0.19.9" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.19.9.tgz#683794bdc3d27222d3eced7b74cad15979548031" - integrity sha512-q4cR+6ZD0938R19MyEW3jEsMzbb/1rulLXiNAJQADD/XYp7pT+rOS5JGxvpRW8dFDEfjW4wLgC/3FXIw4zYglQ== - "@esbuild/android-arm@0.16.17": version "0.16.17" resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.16.17.tgz#025b6246d3f68b7bbaa97069144fb5fb70f2fff2" @@ -1603,16 +1608,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.18.20.tgz#fedb265bc3a589c84cc11f810804f234947c3682" integrity sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw== +"@esbuild/android-arm@0.19.10": + version "0.19.10" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.19.10.tgz#1c23c7e75473aae9fb323be5d9db225142f47f52" + integrity sha512-7W0bK7qfkw1fc2viBfrtAEkDKHatYfHzr/jKAHNr9BvkYDXPcC6bodtm8AyLJNNuqClLNaeTLuwURt4PRT9d7w== + "@esbuild/android-arm@0.19.2": version "0.19.2" resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.19.2.tgz#edd1c8f23ba353c197f5b0337123c58ff2a56999" integrity sha512-tM8yLeYVe7pRyAu9VMi/Q7aunpLwD139EY1S99xbQkT4/q2qa6eA4ige/WJQYdJ8GBL1K33pPFhPfPdJ/WzT8Q== -"@esbuild/android-arm@0.19.9": - version "0.19.9" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.19.9.tgz#21a4de41f07b2af47401c601d64dfdefd056c595" - integrity sha512-jkYjjq7SdsWuNI6b5quymW0oC83NN5FdRPuCbs9HZ02mfVdAP8B8eeqLSYU3gb6OJEaY5CQabtTFbqBf26H3GA== - "@esbuild/android-x64@0.16.17": version "0.16.17" resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.16.17.tgz#c820e0fef982f99a85c4b8bfdd582835f04cd96e" @@ -1623,16 +1628,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.18.20.tgz#35cf419c4cfc8babe8893d296cd990e9e9f756f2" integrity sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg== +"@esbuild/android-x64@0.19.10": + version "0.19.10" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.19.10.tgz#df6a4e6d6eb8da5595cfce16d4e3f6bc24464707" + integrity sha512-O/nO/g+/7NlitUxETkUv/IvADKuZXyH4BHf/g/7laqKC4i/7whLpB0gvpPc2zpF0q9Q6FXS3TS75QHac9MvVWw== + "@esbuild/android-x64@0.19.2": version "0.19.2" resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.19.2.tgz#2dcdd6e6f1f2d82ea1b746abd8da5b284960f35a" integrity sha512-qK/TpmHt2M/Hg82WXHRc/W/2SGo/l1thtDHZWqFq7oi24AjZ4O/CpPSu6ZuYKFkEgmZlFoa7CooAyYmuvnaG8w== -"@esbuild/android-x64@0.19.9": - version "0.19.9" - resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.19.9.tgz#e2d7674bc025ddc8699f0cc76cb97823bb63c252" - integrity sha512-KOqoPntWAH6ZxDwx1D6mRntIgZh9KodzgNOy5Ebt9ghzffOk9X2c1sPwtM9P+0eXbefnDhqYfkh5PLP5ULtWFA== - "@esbuild/darwin-arm64@0.16.17": version "0.16.17" resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.16.17.tgz#edef4487af6b21afabba7be5132c26d22379b220" @@ -1643,16 +1648,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz#08172cbeccf95fbc383399a7f39cfbddaeb0d7c1" integrity sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA== +"@esbuild/darwin-arm64@0.19.10": + version "0.19.10" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.19.10.tgz#8462a55db07c1b2fad61c8244ce04469ef1043be" + integrity sha512-YSRRs2zOpwypck+6GL3wGXx2gNP7DXzetmo5pHXLrY/VIMsS59yKfjPizQ4lLt5vEI80M41gjm2BxrGZ5U+VMA== + "@esbuild/darwin-arm64@0.19.2": version "0.19.2" resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.19.2.tgz#55b36bc06d76f5c243987c1f93a11a80d8fc3b26" integrity sha512-Ora8JokrvrzEPEpZO18ZYXkH4asCdc1DLdcVy8TGf5eWtPO1Ie4WroEJzwI52ZGtpODy3+m0a2yEX9l+KUn0tA== -"@esbuild/darwin-arm64@0.19.9": - version "0.19.9" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.19.9.tgz#ae7a582289cc5c0bac15d4b9020a90cb7288f1e9" - integrity sha512-KBJ9S0AFyLVx2E5D8W0vExqRW01WqRtczUZ8NRu+Pi+87opZn5tL4Y0xT0mA4FtHctd0ZgwNoN639fUUGlNIWw== - "@esbuild/darwin-x64@0.16.17": version "0.16.17" resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.16.17.tgz#42829168730071c41ef0d028d8319eea0e2904b4" @@ -1663,16 +1668,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz#d70d5790d8bf475556b67d0f8b7c5bdff053d85d" integrity sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ== +"@esbuild/darwin-x64@0.19.10": + version "0.19.10" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.19.10.tgz#d1de20bfd41bb75b955ba86a6b1004539e8218c1" + integrity sha512-alfGtT+IEICKtNE54hbvPg13xGBe4GkVxyGWtzr+yHO7HIiRJppPDhOKq3zstTcVf8msXb/t4eavW3jCDpMSmA== + "@esbuild/darwin-x64@0.19.2": version "0.19.2" resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.19.2.tgz#982524af33a6424a3b5cb44bbd52559623ad719c" integrity sha512-tP+B5UuIbbFMj2hQaUr6EALlHOIOmlLM2FK7jeFBobPy2ERdohI4Ka6ZFjZ1ZYsrHE/hZimGuU90jusRE0pwDw== -"@esbuild/darwin-x64@0.19.9": - version "0.19.9" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.19.9.tgz#8a216c66dcf51addeeb843d8cfaeff712821d12b" - integrity sha512-vE0VotmNTQaTdX0Q9dOHmMTao6ObjyPm58CHZr1UK7qpNleQyxlFlNCaHsHx6Uqv86VgPmR4o2wdNq3dP1qyDQ== - "@esbuild/freebsd-arm64@0.16.17": version "0.16.17" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.16.17.tgz#1f4af488bfc7e9ced04207034d398e793b570a27" @@ -1683,16 +1688,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz#98755cd12707f93f210e2494d6a4b51b96977f54" integrity sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw== +"@esbuild/freebsd-arm64@0.19.10": + version "0.19.10" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.10.tgz#16904879e34c53a2e039d1284695d2db3e664d57" + integrity sha512-dMtk1wc7FSH8CCkE854GyGuNKCewlh+7heYP/sclpOG6Cectzk14qdUIY5CrKDbkA/OczXq9WesqnPl09mj5dg== + "@esbuild/freebsd-arm64@0.19.2": version "0.19.2" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.2.tgz#8e478a0856645265fe79eac4b31b52193011ee06" integrity sha512-YbPY2kc0acfzL1VPVK6EnAlig4f+l8xmq36OZkU0jzBVHcOTyQDhnKQaLzZudNJQyymd9OqQezeaBgkTGdTGeQ== -"@esbuild/freebsd-arm64@0.19.9": - version "0.19.9" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.9.tgz#63d4f603e421252c3cd836b18d01545be7c6c440" - integrity sha512-uFQyd/o1IjiEk3rUHSwUKkqZwqdvuD8GevWF065eqgYfexcVkxh+IJgwTaGZVu59XczZGcN/YMh9uF1fWD8j1g== - "@esbuild/freebsd-x64@0.16.17": version "0.16.17" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.16.17.tgz#636306f19e9bc981e06aa1d777302dad8fddaf72" @@ -1703,16 +1708,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz#c1eb2bff03915f87c29cece4c1a7fa1f423b066e" integrity sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ== +"@esbuild/freebsd-x64@0.19.10": + version "0.19.10" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.19.10.tgz#8ad9e5ca9786ca3f1ef1411bfd10b08dcd9d4cef" + integrity sha512-G5UPPspryHu1T3uX8WiOEUa6q6OlQh6gNl4CO4Iw5PS+Kg5bVggVFehzXBJY6X6RSOMS8iXDv2330VzaObm4Ag== + "@esbuild/freebsd-x64@0.19.2": version "0.19.2" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.19.2.tgz#01b96604f2540db023c73809bb8ae6cd1692d6f3" integrity sha512-nSO5uZT2clM6hosjWHAsS15hLrwCvIWx+b2e3lZ3MwbYSaXwvfO528OF+dLjas1g3bZonciivI8qKR/Hm7IWGw== -"@esbuild/freebsd-x64@0.19.9": - version "0.19.9" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.19.9.tgz#a3db52595be65360eae4de1d1fa3c1afd942e1e4" - integrity sha512-WMLgWAtkdTbTu1AWacY7uoj/YtHthgqrqhf1OaEWnZb7PQgpt8eaA/F3LkV0E6K/Lc0cUr/uaVP/49iE4M4asA== - "@esbuild/linux-arm64@0.16.17": version "0.16.17" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.16.17.tgz#a003f7ff237c501e095d4f3a09e58fc7b25a4aca" @@ -1723,16 +1728,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz#bad4238bd8f4fc25b5a021280c770ab5fc3a02a0" integrity sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA== +"@esbuild/linux-arm64@0.19.10": + version "0.19.10" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.19.10.tgz#d82cf2c590faece82d28bbf1cfbe36f22ae25bd2" + integrity sha512-QxaouHWZ+2KWEj7cGJmvTIHVALfhpGxo3WLmlYfJ+dA5fJB6lDEIg+oe/0//FuyVHuS3l79/wyBxbHr0NgtxJQ== + "@esbuild/linux-arm64@0.19.2": version "0.19.2" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.19.2.tgz#7e5d2c7864c5c83ec789b59c77cd9c20d2594916" integrity sha512-ig2P7GeG//zWlU0AggA3pV1h5gdix0MA3wgB+NsnBXViwiGgY77fuN9Wr5uoCrs2YzaYfogXgsWZbm+HGr09xg== -"@esbuild/linux-arm64@0.19.9": - version "0.19.9" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.19.9.tgz#4ae5811ce9f8d7df5eb9edd9765ea9401a534f13" - integrity sha512-PiPblfe1BjK7WDAKR1Cr9O7VVPqVNpwFcPWgfn4xu0eMemzRp442hXyzF/fSwgrufI66FpHOEJk0yYdPInsmyQ== - "@esbuild/linux-arm@0.16.17": version "0.16.17" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.16.17.tgz#b591e6a59d9c4fe0eeadd4874b157ab78cf5f196" @@ -1743,16 +1748,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz#3e617c61f33508a27150ee417543c8ab5acc73b0" integrity sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg== +"@esbuild/linux-arm@0.19.10": + version "0.19.10" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.19.10.tgz#477b8e7c7bcd34369717b04dd9ee6972c84f4029" + integrity sha512-j6gUW5aAaPgD416Hk9FHxn27On28H4eVI9rJ4az7oCGTFW48+LcgNDBN+9f8rKZz7EEowo889CPKyeaD0iw9Kg== + "@esbuild/linux-arm@0.19.2": version "0.19.2" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.19.2.tgz#c32ae97bc0246664a1cfbdb4a98e7b006d7db8ae" integrity sha512-Odalh8hICg7SOD7XCj0YLpYCEc+6mkoq63UnExDCiRA2wXEmGlK5JVrW50vZR9Qz4qkvqnHcpH+OFEggO3PgTg== -"@esbuild/linux-arm@0.19.9": - version "0.19.9" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.19.9.tgz#9807e92cfd335f46326394805ad488e646e506f2" - integrity sha512-C/ChPohUYoyUaqn1h17m/6yt6OB14hbXvT8EgM1ZWaiiTYz7nWZR0SYmMnB5BzQA4GXl3BgBO1l8MYqL/He3qw== - "@esbuild/linux-ia32@0.16.17": version "0.16.17" resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.16.17.tgz#24333a11027ef46a18f57019450a5188918e2a54" @@ -1763,16 +1768,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz#699391cccba9aee6019b7f9892eb99219f1570a7" integrity sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA== +"@esbuild/linux-ia32@0.19.10": + version "0.19.10" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.19.10.tgz#d55ff822cf5b0252a57112f86857ff23be6cab0e" + integrity sha512-4ub1YwXxYjj9h1UIZs2hYbnTZBtenPw5NfXCRgEkGb0b6OJ2gpkMvDqRDYIDRjRdWSe/TBiZltm3Y3Q8SN1xNg== + "@esbuild/linux-ia32@0.19.2": version "0.19.2" resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.19.2.tgz#3fc4f0fa026057fe885e4a180b3956e704f1ceaa" integrity sha512-mLfp0ziRPOLSTek0Gd9T5B8AtzKAkoZE70fneiiyPlSnUKKI4lp+mGEnQXcQEHLJAcIYDPSyBvsUbKUG2ri/XQ== -"@esbuild/linux-ia32@0.19.9": - version "0.19.9" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.19.9.tgz#18892c10f3106652b16f9da88a0362dc95ed46c7" - integrity sha512-f37i/0zE0MjDxijkPSQw1CO/7C27Eojqb+r3BbHVxMLkj8GCa78TrBZzvPyA/FNLUMzP3eyHCVkAopkKVja+6Q== - "@esbuild/linux-loong64@0.14.54": version "0.14.54" resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz#de2a4be678bd4d0d1ffbb86e6de779cde5999028" @@ -1788,16 +1793,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz#e6fccb7aac178dd2ffb9860465ac89d7f23b977d" integrity sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg== +"@esbuild/linux-loong64@0.19.10": + version "0.19.10" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.19.10.tgz#a9ad057d7e48d6c9f62ff50f6f208e331c4543c7" + integrity sha512-lo3I9k+mbEKoxtoIbM0yC/MZ1i2wM0cIeOejlVdZ3D86LAcFXFRdeuZmh91QJvUTW51bOK5W2BznGNIl4+mDaA== + "@esbuild/linux-loong64@0.19.2": version "0.19.2" resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.19.2.tgz#633bcaea443f3505fb0ed109ab840c99ad3451a4" integrity sha512-hn28+JNDTxxCpnYjdDYVMNTR3SKavyLlCHHkufHV91fkewpIyQchS1d8wSbmXhs1fiYDpNww8KTFlJ1dHsxeSw== -"@esbuild/linux-loong64@0.19.9": - version "0.19.9" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.19.9.tgz#dc2ebf9a125db0a1bba18c2bbfd4fbdcbcaf61c2" - integrity sha512-t6mN147pUIf3t6wUt3FeumoOTPfmv9Cc6DQlsVBpB7eCpLOqQDyWBP1ymXn1lDw4fNUSb/gBcKAmvTP49oIkaA== - "@esbuild/linux-mips64el@0.16.17": version "0.16.17" resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.16.17.tgz#4e5967a665c38360b0a8205594377d4dcf9c3726" @@ -1808,16 +1813,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz#eeff3a937de9c2310de30622a957ad1bd9183231" integrity sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ== +"@esbuild/linux-mips64el@0.19.10": + version "0.19.10" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.19.10.tgz#b011a96924773d60ebab396fbd7a08de66668179" + integrity sha512-J4gH3zhHNbdZN0Bcr1QUGVNkHTdpijgx5VMxeetSk6ntdt+vR1DqGmHxQYHRmNb77tP6GVvD+K0NyO4xjd7y4A== + "@esbuild/linux-mips64el@0.19.2": version "0.19.2" resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.19.2.tgz#e0bff2898c46f52be7d4dbbcca8b887890805823" integrity sha512-KbXaC0Sejt7vD2fEgPoIKb6nxkfYW9OmFUK9XQE4//PvGIxNIfPk1NmlHmMg6f25x57rpmEFrn1OotASYIAaTg== -"@esbuild/linux-mips64el@0.19.9": - version "0.19.9" - resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.19.9.tgz#4c2f7c5d901015e3faf1563c4a89a50776cb07fd" - integrity sha512-jg9fujJTNTQBuDXdmAg1eeJUL4Jds7BklOTkkH80ZgQIoCTdQrDaHYgbFZyeTq8zbY+axgptncko3v9p5hLZtw== - "@esbuild/linux-ppc64@0.16.17": version "0.16.17" resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.16.17.tgz#206443a02eb568f9fdf0b438fbd47d26e735afc8" @@ -1828,16 +1833,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz#2f7156bde20b01527993e6881435ad79ba9599fb" integrity sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA== +"@esbuild/linux-ppc64@0.19.10": + version "0.19.10" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.19.10.tgz#5d8b59929c029811e473f2544790ea11d588d4dd" + integrity sha512-tgT/7u+QhV6ge8wFMzaklOY7KqiyitgT1AUHMApau32ZlvTB/+efeCtMk4eXS+uEymYK249JsoiklZN64xt6oQ== + "@esbuild/linux-ppc64@0.19.2": version "0.19.2" resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.19.2.tgz#d75798da391f54a9674f8c143b9a52d1dbfbfdde" integrity sha512-dJ0kE8KTqbiHtA3Fc/zn7lCd7pqVr4JcT0JqOnbj4LLzYnp+7h8Qi4yjfq42ZlHfhOCM42rBh0EwHYLL6LEzcw== -"@esbuild/linux-ppc64@0.19.9": - version "0.19.9" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.19.9.tgz#8385332713b4e7812869622163784a5633f76fc4" - integrity sha512-tkV0xUX0pUUgY4ha7z5BbDS85uI7ABw3V1d0RNTii7E9lbmV8Z37Pup2tsLV46SQWzjOeyDi1Q7Wx2+QM8WaCQ== - "@esbuild/linux-riscv64@0.16.17": version "0.16.17" resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.16.17.tgz#c351e433d009bf256e798ad048152c8d76da2fc9" @@ -1848,16 +1853,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz#6628389f210123d8b4743045af8caa7d4ddfc7a6" integrity sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A== +"@esbuild/linux-riscv64@0.19.10": + version "0.19.10" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.19.10.tgz#292b06978375b271bd8bc0a554e0822957508d22" + integrity sha512-0f/spw0PfBMZBNqtKe5FLzBDGo0SKZKvMl5PHYQr3+eiSscfJ96XEknCe+JoOayybWUFQbcJTrk946i3j9uYZA== + "@esbuild/linux-riscv64@0.19.2": version "0.19.2" resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.19.2.tgz#012409bd489ed1bb9b775541d4a46c5ded8e6dd8" integrity sha512-7Z/jKNFufZ/bbu4INqqCN6DDlrmOTmdw6D0gH+6Y7auok2r02Ur661qPuXidPOJ+FSgbEeQnnAGgsVynfLuOEw== -"@esbuild/linux-riscv64@0.19.9": - version "0.19.9" - resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.19.9.tgz#23f1db24fa761be311874f32036c06249aa20cba" - integrity sha512-DfLp8dj91cufgPZDXr9p3FoR++m3ZJ6uIXsXrIvJdOjXVREtXuQCjfMfvmc3LScAVmLjcfloyVtpn43D56JFHg== - "@esbuild/linux-s390x@0.16.17": version "0.16.17" resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.16.17.tgz#661f271e5d59615b84b6801d1c2123ad13d9bd87" @@ -1868,16 +1873,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz#255e81fb289b101026131858ab99fba63dcf0071" integrity sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ== +"@esbuild/linux-s390x@0.19.10": + version "0.19.10" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.19.10.tgz#d30af63530f8d4fa96930374c9dd0d62bf59e069" + integrity sha512-pZFe0OeskMHzHa9U38g+z8Yx5FNCLFtUnJtQMpwhS+r4S566aK2ci3t4NCP4tjt6d5j5uo4h7tExZMjeKoehAA== + "@esbuild/linux-s390x@0.19.2": version "0.19.2" resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.19.2.tgz#ece3ed75c5a150de8a5c110f02e97d315761626b" integrity sha512-U+RinR6aXXABFCcAY4gSlv4CL1oOVvSSCdseQmGO66H+XyuQGZIUdhG56SZaDJQcLmrSfRmx5XZOWyCJPRqS7g== -"@esbuild/linux-s390x@0.19.9": - version "0.19.9" - resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.19.9.tgz#2dffe497726b897c9f0109e774006e25b33b4fd0" - integrity sha512-zHbglfEdC88KMgCWpOl/zc6dDYJvWGLiUtmPRsr1OgCViu3z5GncvNVdf+6/56O2Ca8jUU+t1BW261V6kp8qdw== - "@esbuild/linux-x64@0.16.17": version "0.16.17" resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.16.17.tgz#e4ba18e8b149a89c982351443a377c723762b85f" @@ -1888,16 +1893,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz#c7690b3417af318a9b6f96df3031a8865176d338" integrity sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w== +"@esbuild/linux-x64@0.19.10": + version "0.19.10" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.19.10.tgz#898c72eeb74d9f2fb43acf316125b475548b75ce" + integrity sha512-SpYNEqg/6pZYoc+1zLCjVOYvxfZVZj6w0KROZ3Fje/QrM3nfvT2llI+wmKSrWuX6wmZeTapbarvuNNK/qepSgA== + "@esbuild/linux-x64@0.19.2": version "0.19.2" resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.19.2.tgz#dea187019741602d57aaf189a80abba261fbd2aa" integrity sha512-oxzHTEv6VPm3XXNaHPyUTTte+3wGv7qVQtqaZCrgstI16gCuhNOtBXLEBkBREP57YTd68P0VgDgG73jSD8bwXQ== -"@esbuild/linux-x64@0.19.9": - version "0.19.9" - resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.19.9.tgz#ceb1d62cd830724ff5b218e5d3172a8bad59420e" - integrity sha512-JUjpystGFFmNrEHQnIVG8hKwvA2DN5o7RqiO1CVX8EN/F/gkCjkUMgVn6hzScpwnJtl2mPR6I9XV1oW8k9O+0A== - "@esbuild/netbsd-x64@0.16.17": version "0.16.17" resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.16.17.tgz#7d4f4041e30c5c07dd24ffa295c73f06038ec775" @@ -1908,16 +1913,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz#30e8cd8a3dded63975e2df2438ca109601ebe0d1" integrity sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A== +"@esbuild/netbsd-x64@0.19.10": + version "0.19.10" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.19.10.tgz#fd473a5ae261b43eab6dad4dbd5a3155906e6c91" + integrity sha512-ACbZ0vXy9zksNArWlk2c38NdKg25+L9pr/mVaj9SUq6lHZu/35nx2xnQVRGLrC1KKQqJKRIB0q8GspiHI3J80Q== + "@esbuild/netbsd-x64@0.19.2": version "0.19.2" resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.19.2.tgz#bbfd7cf9ab236a23ee3a41b26f0628c57623d92a" integrity sha512-WNa5zZk1XpTTwMDompZmvQLHszDDDN7lYjEHCUmAGB83Bgs20EMs7ICD+oKeT6xt4phV4NDdSi/8OfjPbSbZfQ== -"@esbuild/netbsd-x64@0.19.9": - version "0.19.9" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.19.9.tgz#0cbca65e9ef4d3fc41502d3e055e6f49479a8f18" - integrity sha512-GThgZPAwOBOsheA2RUlW5UeroRfESwMq/guy8uEe3wJlAOjpOXuSevLRd70NZ37ZrpO6RHGHgEHvPg1h3S1Jug== - "@esbuild/openbsd-x64@0.16.17": version "0.16.17" resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.16.17.tgz#970fa7f8470681f3e6b1db0cc421a4af8060ec35" @@ -1928,16 +1933,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz#7812af31b205055874c8082ea9cf9ab0da6217ae" integrity sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg== +"@esbuild/openbsd-x64@0.19.10": + version "0.19.10" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.19.10.tgz#96eb8992e526717b5272321eaad3e21f3a608e46" + integrity sha512-PxcgvjdSjtgPMiPQrM3pwSaG4kGphP+bLSb+cihuP0LYdZv1epbAIecHVl5sD3npkfYBZ0ZnOjR878I7MdJDFg== + "@esbuild/openbsd-x64@0.19.2": version "0.19.2" resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.19.2.tgz#fa5c4c6ee52a360618f00053652e2902e1d7b4a7" integrity sha512-S6kI1aT3S++Dedb7vxIuUOb3oAxqxk2Rh5rOXOTYnzN8JzW1VzBd+IqPiSpgitu45042SYD3HCoEyhLKQcDFDw== -"@esbuild/openbsd-x64@0.19.9": - version "0.19.9" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.19.9.tgz#1f57adfbee09c743292c6758a3642e875bcad1cf" - integrity sha512-Ki6PlzppaFVbLnD8PtlVQfsYw4S9n3eQl87cqgeIw+O3sRr9IghpfSKY62mggdt1yCSZ8QWvTZ9jo9fjDSg9uw== - "@esbuild/sunos-x64@0.16.17": version "0.16.17" resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.16.17.tgz#abc60e7c4abf8b89fb7a4fe69a1484132238022c" @@ -1948,16 +1953,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz#d5c275c3b4e73c9b0ecd38d1ca62c020f887ab9d" integrity sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ== +"@esbuild/sunos-x64@0.19.10": + version "0.19.10" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.19.10.tgz#c16ee1c167f903eaaa6acf7372bee42d5a89c9bc" + integrity sha512-ZkIOtrRL8SEJjr+VHjmW0znkPs+oJXhlJbNwfI37rvgeMtk3sxOQevXPXjmAPZPigVTncvFqLMd+uV0IBSEzqA== + "@esbuild/sunos-x64@0.19.2": version "0.19.2" resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.19.2.tgz#52a2ac8ac6284c02d25df22bb4cfde26fbddd68d" integrity sha512-VXSSMsmb+Z8LbsQGcBMiM+fYObDNRm8p7tkUDMPG/g4fhFX5DEFmjxIEa3N8Zr96SjsJ1woAhF0DUnS3MF3ARw== -"@esbuild/sunos-x64@0.19.9": - version "0.19.9" - resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.19.9.tgz#116be6adbd2c7479edeeb5f6ea0441002ab4cb9c" - integrity sha512-MLHj7k9hWh4y1ddkBpvRj2b9NCBhfgBt3VpWbHQnXRedVun/hC7sIyTGDGTfsGuXo4ebik2+3ShjcPbhtFwWDw== - "@esbuild/win32-arm64@0.16.17": version "0.16.17" resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.16.17.tgz#7b0ff9e8c3265537a7a7b1fd9a24e7bd39fcd87a" @@ -1968,16 +1973,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz#73bc7f5a9f8a77805f357fab97f290d0e4820ac9" integrity sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg== +"@esbuild/win32-arm64@0.19.10": + version "0.19.10" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.19.10.tgz#7e417d1971dbc7e469b4eceb6a5d1d667b5e3dcc" + integrity sha512-+Sa4oTDbpBfGpl3Hn3XiUe4f8TU2JF7aX8cOfqFYMMjXp6ma6NJDztl5FDG8Ezx0OjwGikIHw+iA54YLDNNVfw== + "@esbuild/win32-arm64@0.19.2": version "0.19.2" resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.19.2.tgz#719ed5870855de8537aef8149694a97d03486804" integrity sha512-5NayUlSAyb5PQYFAU9x3bHdsqB88RC3aM9lKDAz4X1mo/EchMIT1Q+pSeBXNgkfNmRecLXA0O8xP+x8V+g/LKg== -"@esbuild/win32-arm64@0.19.9": - version "0.19.9" - resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.19.9.tgz#2be22131ab18af4693fd737b161d1ef34de8ca9d" - integrity sha512-GQoa6OrQ8G08guMFgeXPH7yE/8Dt0IfOGWJSfSH4uafwdC7rWwrfE6P9N8AtPGIjUzdo2+7bN8Xo3qC578olhg== - "@esbuild/win32-ia32@0.16.17": version "0.16.17" resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.16.17.tgz#e90fe5267d71a7b7567afdc403dfd198c292eb09" @@ -1988,16 +1993,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz#ec93cbf0ef1085cc12e71e0d661d20569ff42102" integrity sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g== +"@esbuild/win32-ia32@0.19.10": + version "0.19.10" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.19.10.tgz#2b52dfec6cd061ecb36171c13bae554888b439e5" + integrity sha512-EOGVLK1oWMBXgfttJdPHDTiivYSjX6jDNaATeNOaCOFEVcfMjtbx7WVQwPSE1eIfCp/CaSF2nSrDtzc4I9f8TQ== + "@esbuild/win32-ia32@0.19.2": version "0.19.2" resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.19.2.tgz#24832223880b0f581962c8660f8fb8797a1e046a" integrity sha512-47gL/ek1v36iN0wL9L4Q2MFdujR0poLZMJwhO2/N3gA89jgHp4MR8DKCmwYtGNksbfJb9JoTtbkoe6sDhg2QTA== -"@esbuild/win32-ia32@0.19.9": - version "0.19.9" - resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.19.9.tgz#e10ead5a55789b167b4225d2469324538768af7c" - integrity sha512-UOozV7Ntykvr5tSOlGCrqU3NBr3d8JqPes0QWN2WOXfvkWVGRajC+Ym0/Wj88fUgecUCLDdJPDF0Nna2UK3Qtg== - "@esbuild/win32-x64@0.16.17": version "0.16.17" resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.16.17.tgz#c5a1a4bfe1b57f0c3e61b29883525c6da3e5c091" @@ -2008,16 +2013,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz#786c5f41f043b07afb1af37683d7c33668858f6d" integrity sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ== +"@esbuild/win32-x64@0.19.10": + version "0.19.10" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.19.10.tgz#bd123a74f243d2f3a1f046447bb9b363ee25d072" + integrity sha512-whqLG6Sc70AbU73fFYvuYzaE4MNMBIlR1Y/IrUeOXFrWHxBEjjbZaQ3IXIQS8wJdAzue2GwYZCjOrgrU1oUHoA== + "@esbuild/win32-x64@0.19.2": version "0.19.2" resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.19.2.tgz#1205014625790c7ff0e471644a878a65d1e34ab0" integrity sha512-tcuhV7ncXBqbt/Ybf0IyrMcwVOAPDckMK9rXNHtF17UTK18OKLpg08glminN06pt2WCoALhXdLfSPbVvK/6fxw== -"@esbuild/win32-x64@0.19.9": - version "0.19.9" - resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.19.9.tgz#b2da6219b603e3fa371a78f53f5361260d0c5585" - integrity sha512-oxoQgglOP7RH6iasDrhY+R/3cHrfwIDvRlT4CGChflq6twk8iENeVvMJjmvBb94Ik1Z+93iGO27err7w6l54GQ== - "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" @@ -2045,10 +2050,10 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@8.55.0": - version "8.55.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.55.0.tgz#b721d52060f369aa259cf97392403cb9ce892ec6" - integrity sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA== +"@eslint/js@8.56.0": + version "8.56.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.56.0.tgz#ef20350fec605a7f7035a01764731b2de0f3782b" + integrity sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A== "@ethereumjs/common@2.6.5", "@ethereumjs/common@^2.6.4": version "2.6.5" @@ -3360,9 +3365,9 @@ path-to-regexp "^6.1.0" "@ledgerhq/connect-kit-loader@^1.0.1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@ledgerhq/connect-kit-loader/-/connect-kit-loader-1.1.2.tgz#d550e3c1f046e4c796f32a75324b03606b7e226a" - integrity sha512-mscwGroSJQrCTjtNGBu+18FQbZYA4+q6Tyx6K7CXHl6AwgZKbWfZYdgP2F+fyZcRUdGRsMX8QtvU61VcGGtO1A== + version "1.1.8" + resolved "https://registry.yarnpkg.com/@ledgerhq/connect-kit-loader/-/connect-kit-loader-1.1.8.tgz#6cc32191660dd9d6e8f89047af09b0f201e30190" + integrity sha512-mDJsOucVW8m3Lk2fdQst+P74SgiKebvq1iBk4sXLbADQOwhL9bWGaArvO+tW7jPJZwEfSPWBdHcHoYi11XAwZw== "@lezer/common@^1.0.0", "@lezer/common@^1.1.0": version "1.1.2" @@ -3518,91 +3523,91 @@ "@motionone/dom" "^10.16.4" tslib "^2.3.1" -"@mui/base@5.0.0-beta.27", "@mui/base@^5.0.0-beta.22": - version "5.0.0-beta.27" - resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-beta.27.tgz#21a9c7d954a5f88f6706dfee630154c651ee73af" - integrity sha512-duL37qxihT1N0pW/gyXVezP7SttLkF+cLAs/y6g6ubEFmVadjbnZ45SeF12/vAiKzqwf5M0uFH1cczIPXFZygA== +"@mui/base@5.0.0-beta.28", "@mui/base@^5.0.0-beta.22": + version "5.0.0-beta.28" + resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-beta.28.tgz#f072e55c0530f456ee5cb5cde2af788fdda3bf05" + integrity sha512-KIoSc5sUFceeCaZTq5MQBapFzhHqMo4kj+4azWaCAjorduhcRQtN+BCgVHmo+gvEjix74bUfxwTqGifnu2fNTg== dependencies: "@babel/runtime" "^7.23.5" "@floating-ui/react-dom" "^2.0.4" "@mui/types" "^7.2.11" - "@mui/utils" "^5.15.0" + "@mui/utils" "^5.15.1" "@popperjs/core" "^2.11.8" clsx "^2.0.0" prop-types "^15.8.1" -"@mui/core-downloads-tracker@^5.15.0": - version "5.15.0" - resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.0.tgz#6b45d5bff38f305402d24d3bf86075b56c578909" - integrity sha512-NpGtlHwuyLfJtdrlERXb8qRqd279O0VnuGaZAor1ehdNhUJOD1bSxHDeXKZkbqNpvi50hasFj7lsbTpluworTQ== +"@mui/core-downloads-tracker@^5.15.1": + version "5.15.1" + resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.1.tgz#8aad47e2b198640244f05f6486a927ce362e814e" + integrity sha512-y/nUEsWHyBzaKYp9zLtqJKrLod/zMNEWpMj488FuQY9QTmqBiyUhI2uh7PVaLqLewXRtdmG6JV0b6T5exyuYRw== "@mui/icons-material@^5.14.14": - version "5.15.0" - resolved "https://registry.yarnpkg.com/@mui/icons-material/-/icons-material-5.15.0.tgz#fdc93611ca77ce3b79128be02fb6c1ae79b972b8" - integrity sha512-zHY6fOkaK7VfhWeyxO8MjO3IAjEYpYMXuqUhX7TkUZJ9+TSH/9dn4ClG4K2j6hdgBU5Yrq2Z/89Bo6BHHp7AdQ== + version "5.15.1" + resolved "https://registry.yarnpkg.com/@mui/icons-material/-/icons-material-5.15.1.tgz#38f51a88d224a107e753313b4d9815247caa5398" + integrity sha512-VPJdBSyap6uOxCb5BLbWbkvd6aeJCp1pQZm8DcZBITCH0NOSv8Mz9c8Zvo8xr4Od7+xyWHUAgvRSL4047pL2WQ== dependencies: "@babel/runtime" "^7.23.5" "@mui/lab@^5.0.0-alpha.141": - version "5.0.0-alpha.156" - resolved "https://registry.yarnpkg.com/@mui/lab/-/lab-5.0.0-alpha.156.tgz#9fee3a408ded367ab4fe84275789155b65dee7e7" - integrity sha512-OUAckFeqlAG6aIBG1Ud1fDCBqnU1wltWZYHsA7YCGzRBykNzQC/W/dYddp+RJLu0BgYpMiXwPXq2Hg0ERVtaog== + version "5.0.0-alpha.157" + resolved "https://registry.yarnpkg.com/@mui/lab/-/lab-5.0.0-alpha.157.tgz#cb248823ffd881153eef86c2e3786c35601d1986" + integrity sha512-gY7UM2kNSxiVLfsm0o6HG2G5rM2Vr47prJhDCazY+VG/NOSRc8CG7la6dpL9WDTJhotEZdWwfj1FOUxTonmuQA== dependencies: "@babel/runtime" "^7.23.5" - "@mui/base" "5.0.0-beta.27" - "@mui/system" "^5.15.0" + "@mui/base" "5.0.0-beta.28" + "@mui/system" "^5.15.1" "@mui/types" "^7.2.11" - "@mui/utils" "^5.15.0" + "@mui/utils" "^5.15.1" clsx "^2.0.0" prop-types "^15.8.1" "@mui/material@^5.14.14": - version "5.15.0" - resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.15.0.tgz#f801cf56d505cc52dca225438360ab9c62057285" - integrity sha512-60CDI/hQNwJv9a3vEZtFG7zz0USdQhVwpBd3fZqrzhuXSdiMdYMaZcCXeX/KMuNq0ZxQEAZd74Pv+gOb408QVA== + version "5.15.1" + resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.15.1.tgz#5fc15c6eb9efe4b62b0c30b13bf5fa042bda71a1" + integrity sha512-WA5DVyvacxDakVyAhNqu/rRT28ppuuUFFw1bLpmRzrCJ4uw/zLTATcd4WB3YbB+7MdZNEGG/SJNWTDLEIyn3xQ== dependencies: "@babel/runtime" "^7.23.5" - "@mui/base" "5.0.0-beta.27" - "@mui/core-downloads-tracker" "^5.15.0" - "@mui/system" "^5.15.0" + "@mui/base" "5.0.0-beta.28" + "@mui/core-downloads-tracker" "^5.15.1" + "@mui/system" "^5.15.1" "@mui/types" "^7.2.11" - "@mui/utils" "^5.15.0" - "@types/react-transition-group" "^4.4.9" + "@mui/utils" "^5.15.1" + "@types/react-transition-group" "^4.4.10" clsx "^2.0.0" csstype "^3.1.2" prop-types "^15.8.1" react-is "^18.2.0" react-transition-group "^4.4.5" -"@mui/private-theming@^5.15.0": - version "5.15.0" - resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.15.0.tgz#96de66ce097ba417e1b6b769cd67cbf516bd8876" - integrity sha512-7WxtIhXxNek0JjtsYy+ut2LtFSLpsUW5JSDehQO+jF7itJ8ehy7Bd9bSt2yIllbwGjCFowLfYpPk2Ykgvqm1tA== +"@mui/private-theming@^5.15.1": + version "5.15.1" + resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.15.1.tgz#58fd8da48295e105067fa7361734ee0b166d9cca" + integrity sha512-wTbzuy5KjSvCPE9UVJktWHJ0b/tD5biavY9wvF+OpYDLPpdXK52vc1hTDxSbdkHIFMkJExzrwO9GvpVAHZBnFQ== dependencies: "@babel/runtime" "^7.23.5" - "@mui/utils" "^5.15.0" + "@mui/utils" "^5.15.1" prop-types "^15.8.1" -"@mui/styled-engine@^5.15.0": - version "5.15.0" - resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-5.15.0.tgz#44e068dbb855699053b9e6e4e6d3ed5efe98b7d9" - integrity sha512-6NysIsHkuUS2lF+Lzv1jiK3UjBJk854/vKVcJQVGKlPiqNEVZJNlwaSpsaU5xYXxWEZYfbVFSAomLOS/LV/ovQ== +"@mui/styled-engine@^5.15.1": + version "5.15.1" + resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-5.15.1.tgz#00f179e51afe252022bf356f72354968f9c5bf25" + integrity sha512-7WDZTJLqGexWDjqE9oAgjU8ak6hEtUw2yQU7SIYID5kLVO2Nj/Wi/KicbLsXnTsJNvSqePIlUIWTBSXwWJCPZw== dependencies: "@babel/runtime" "^7.23.5" "@emotion/cache" "^11.11.0" csstype "^3.1.2" prop-types "^15.8.1" -"@mui/system@^5.15.0": - version "5.15.0" - resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.15.0.tgz#d4f6fd65ad8e404d4d7c7b56d755d2a63b0edddb" - integrity sha512-8TPjfTlYBNB7/zBJRL4QOD9kImwdZObbiYNh0+hxvhXr2koezGx8USwPXj8y/JynbzGCkIybkUztCdWlMZe6OQ== +"@mui/system@^5.15.1": + version "5.15.1" + resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.15.1.tgz#e2a79b5e188ca89a3e58aa4d27e3484edf9e24b0" + integrity sha512-LAnP0ls69rqW9eBgI29phIx/lppv+WDGI7b3EJN7VZIqw0RezA0GD7NRpV12BgEYJABEii6z5Q9B5tg7dsX0Iw== dependencies: "@babel/runtime" "^7.23.5" - "@mui/private-theming" "^5.15.0" - "@mui/styled-engine" "^5.15.0" + "@mui/private-theming" "^5.15.1" + "@mui/styled-engine" "^5.15.1" "@mui/types" "^7.2.11" - "@mui/utils" "^5.15.0" + "@mui/utils" "^5.15.1" clsx "^2.0.0" csstype "^3.1.2" prop-types "^15.8.1" @@ -3612,10 +3617,10 @@ resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.2.11.tgz#36b99a88f8010dc716128e568dc05681a69dc7ae" integrity sha512-KWe/QTEsFFlFSH+qRYf3zoFEj3z67s+qAuSnMMg+gFwbxG7P96Hm6g300inQL1Wy///gSRb8juX7Wafvp93m3w== -"@mui/utils@^5.14.16", "@mui/utils@^5.15.0": - version "5.15.0" - resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.15.0.tgz#87b4db92dd2ddf3e2676377427f50662124013b4" - integrity sha512-XSmTKStpKYamewxyJ256+srwEnsT3/6eNo6G7+WC1tj2Iq9GfUJ/6yUoB7YXjOD2jTZ3XobToZm4pVz1LBt6GA== +"@mui/utils@^5.14.16", "@mui/utils@^5.15.1": + version "5.15.1" + resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.15.1.tgz#71d69dc8c0f13a1fd6aca20b53ec496636e6b854" + integrity sha512-V1/d0E3Bju5YdB59HJf2G0tnHrFEvWLN+f8hAXp9+JSNy/LC2zKyqUfPPahflR6qsI681P8G9r4mEZte/SrrYA== dependencies: "@babel/runtime" "^7.23.5" "@types/prop-types" "^15.7.11" @@ -3677,9 +3682,9 @@ webpack-node-externals "3.0.0" "@nestjs/common@^10.2.7": - version "10.2.10" - resolved "https://registry.yarnpkg.com/@nestjs/common/-/common-10.2.10.tgz#22ba5c17962b54ffdf70a7bad690f1647995fe55" - integrity sha512-fwAk931rjW8CNH2Mgwawq/7HWHH1dxkOLdcgs7U52ddLk8CtHXjejm1cbNahewlSbNhvlOl7y1STLHutE6sUqw== + version "10.3.0" + resolved "https://registry.yarnpkg.com/@nestjs/common/-/common-10.3.0.tgz#d78f0ff2062d1d53c79c170a79c12a1548e2e598" + integrity sha512-DGv34UHsZBxCM3H5QGE2XE/+oLJzz5+714JQjBhjD9VccFlQs3LRxo/epso4l7nJIiNlZkPyIUC8WzfU/5RTsQ== dependencies: uid "2.0.2" iterare "1.2.1" @@ -3696,9 +3701,9 @@ uuid "9.0.0" "@nestjs/core@^10.2.8": - version "10.2.10" - resolved "https://registry.yarnpkg.com/@nestjs/core/-/core-10.2.10.tgz#96b6fda7785402b0a3b819c15103675990b75290" - integrity sha512-+ckOI6BPi2ZMHikT9MCG4ctHDc4OnjhoIytrn7f2AYMMXI4bnutJhqyQKc30VDka5x3Wq6QAD57pgSP7y+JjJg== + version "10.3.0" + resolved "https://registry.yarnpkg.com/@nestjs/core/-/core-10.3.0.tgz#d5c6b26d6d9280664910d5481153d25c5da4ec00" + integrity sha512-N06P5ncknW/Pm8bj964WvLIZn2gNhHliCBoAO1LeBvNImYkecqKcrmLbY49Fa1rmMfEM3MuBHeDys3edeuYAOA== dependencies: uid "2.0.2" "@nuxtjs/opencollective" "0.3.2" @@ -3726,9 +3731,9 @@ integrity sha512-znJ9Y4S8ZDVY+j4doWAJ8EuuVO7SkQN3yOBmzxbGaXbvcSwFDAdGJ+OMCg52NdzIO4tQoN4pYKx8W6M0ArfFRQ== "@nestjs/platform-express@^10.2.6": - version "10.2.10" - resolved "https://registry.yarnpkg.com/@nestjs/platform-express/-/platform-express-10.2.10.tgz#6ff94e5a3f3a15ff39d8044e966fd4ceecd8d068" - integrity sha512-U4KDgtMjH8TqEvt0RzC/POP8ABvL9bYoCScvlGtFSKgVGaMLBKkZ4+jHtbQx6qItYSlBBRUuz/dveMZCObfrkQ== + version "10.3.0" + resolved "https://registry.yarnpkg.com/@nestjs/platform-express/-/platform-express-10.3.0.tgz#ea69b048ef90b78b1001eb1c6b02d9d798f5f3af" + integrity sha512-E4hUW48bYv8OHbP9XQg6deefmXb0pDSSuE38SdhA0mJ37zGY7C5EqqBUdlQk4ttfD+OdnbIgJ1zOokT6dd2d7A== dependencies: body-parser "1.20.2" cors "2.8.5" @@ -4933,70 +4938,70 @@ estree-walker "^2.0.2" picomatch "^2.3.1" -"@rollup/rollup-android-arm-eabi@4.9.0": - version "4.9.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.0.tgz#0437b27edd7095d0b6d5db99d13af8157d7c58b0" - integrity sha512-+1ge/xmaJpm1KVBuIH38Z94zj9fBD+hp+/5WLaHgyY8XLq1ibxk/zj6dTXaqM2cAbYKq8jYlhHd6k05If1W5xA== - -"@rollup/rollup-android-arm64@4.9.0": - version "4.9.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.0.tgz#d4c14ef9e45d5c46b8d1f611ab8124a611d5be5b" - integrity sha512-im6hUEyQ7ZfoZdNvtwgEJvBWZYauC9KVKq1w58LG2Zfz6zMd8gRrbN+xCVoqA2hv/v6fm9lp5LFGJ3za8EQH3A== - -"@rollup/rollup-darwin-arm64@4.9.0": - version "4.9.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.0.tgz#6f3fdf5712db6b5e3d8f62a86a09cd659dd871f9" - integrity sha512-u7aTMskN6Dmg1lCT0QJ+tINRt+ntUrvVkhbPfFz4bCwRZvjItx2nJtwJnJRlKMMaQCHRjrNqHRDYvE4mBm3DlQ== - -"@rollup/rollup-darwin-x64@4.9.0": - version "4.9.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.0.tgz#626d7786fe7c10b2e8533ad981b4a791fd72b9d0" - integrity sha512-8FvEl3w2ExmpcOmX5RJD0yqXcVSOqAJJUJ29Lca29Ik+3zPS1yFimr2fr5JSZ4Z5gt8/d7WqycpgkX9nocijSw== - -"@rollup/rollup-linux-arm-gnueabihf@4.9.0": - version "4.9.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.0.tgz#57ece7bb1b7659a3ea2ace580a63b8f92b3161f1" - integrity sha512-lHoKYaRwd4gge+IpqJHCY+8Vc3hhdJfU6ukFnnrJasEBUvVlydP8PuwndbWfGkdgSvZhHfSEw6urrlBj0TSSfg== - -"@rollup/rollup-linux-arm64-gnu@4.9.0": - version "4.9.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.0.tgz#345b276b814a5377344adc5780c4dfb7cd0e8ba9" - integrity sha512-JbEPfhndYeWHfOSeh4DOFvNXrj7ls9S/2omijVsao+LBPTPayT1uKcK3dHW3MwDJ7KO11t9m2cVTqXnTKpeaiw== - -"@rollup/rollup-linux-arm64-musl@4.9.0": - version "4.9.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.0.tgz#61cc6516e6e92e2205ea1d0ac30326379b0563c8" - integrity sha512-ahqcSXLlcV2XUBM3/f/C6cRoh7NxYA/W7Yzuv4bDU1YscTFw7ay4LmD7l6OS8EMhTNvcrWGkEettL1Bhjf+B+w== - -"@rollup/rollup-linux-riscv64-gnu@4.9.0": - version "4.9.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.0.tgz#e9add70ddca7bd6f685ec447ae83eb3be552f211" - integrity sha512-uwvOYNtLw8gVtrExKhdFsYHA/kotURUmZYlinH2VcQxNCQJeJXnkmWgw2hI9Xgzhgu7J9QvWiq9TtTVwWMDa+w== - -"@rollup/rollup-linux-x64-gnu@4.9.0": - version "4.9.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.0.tgz#ece153613f0cf2c864dbfc2076c579da8abd51a9" - integrity sha512-m6pkSwcZZD2LCFHZX/zW2aLIISyzWLU3hrLLzQKMI12+OLEzgruTovAxY5sCZJkipklaZqPy/2bEEBNjp+Y7xg== - -"@rollup/rollup-linux-x64-musl@4.9.0": - version "4.9.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.0.tgz#2d2dbdf5fbf2c19d1f3d31b8a7850b57f5799037" - integrity sha512-VFAC1RDRSbU3iOF98X42KaVicAfKf0m0OvIu8dbnqhTe26Kh6Ym9JrDulz7Hbk7/9zGc41JkV02g+p3BivOdAg== - -"@rollup/rollup-win32-arm64-msvc@4.9.0": - version "4.9.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.0.tgz#bf2dbad350376e46cb77fab408bb398ad5f3648d" - integrity sha512-9jPgMvTKXARz4inw6jezMLA2ihDBvgIU9Ml01hjdVpOcMKyxFBJrn83KVQINnbeqDv0+HdO1c09hgZ8N0s820Q== - -"@rollup/rollup-win32-ia32-msvc@4.9.0": - version "4.9.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.0.tgz#5c26b07f74f4054f3ecf202550100496ed2e73f3" - integrity sha512-WE4pT2kTXQN2bAv40Uog0AsV7/s9nT9HBWXAou8+++MBCnY51QS02KYtm6dQxxosKi1VIz/wZIrTQO5UP2EW+Q== - -"@rollup/rollup-win32-x64-msvc@4.9.0": - version "4.9.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.0.tgz#4ea610e0c40a07a8afa2977cbf80507f41c2271c" - integrity sha512-aPP5Q5AqNGuT0tnuEkK/g4mnt3ZhheiXrDIiSVIHN9mcN21OyXDVbEMqmXPE7e2OplNLDkcvV+ZoGJa2ZImFgw== +"@rollup/rollup-android-arm-eabi@4.9.1": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.1.tgz#beaf518ee45a196448e294ad3f823d2d4576cf35" + integrity sha512-6vMdBZqtq1dVQ4CWdhFwhKZL6E4L1dV6jUjuBvsavvNJSppzi6dLBbuV+3+IyUREaj9ZFvQefnQm28v4OCXlig== + +"@rollup/rollup-android-arm64@4.9.1": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.1.tgz#6f76cfa759c2d0fdb92122ffe28217181a1664eb" + integrity sha512-Jto9Fl3YQ9OLsTDWtLFPtaIMSL2kwGyGoVCmPC8Gxvym9TCZm4Sie+cVeblPO66YZsYH8MhBKDMGZ2NDxuk/XQ== + +"@rollup/rollup-darwin-arm64@4.9.1": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.1.tgz#9aaefe33a5481d66322d1c62f368171c03eabe2b" + integrity sha512-LtYcLNM+bhsaKAIGwVkh5IOWhaZhjTfNOkGzGqdHvhiCUVuJDalvDxEdSnhFzAn+g23wgsycmZk1vbnaibZwwA== + +"@rollup/rollup-darwin-x64@4.9.1": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.1.tgz#707dcaadcdc6bd3fd6c69f55d9456cd4446306a3" + integrity sha512-KyP/byeXu9V+etKO6Lw3E4tW4QdcnzDG/ake031mg42lob5tN+5qfr+lkcT/SGZaH2PdW4Z1NX9GHEkZ8xV7og== + +"@rollup/rollup-linux-arm-gnueabihf@4.9.1": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.1.tgz#7a4dbbd1dd98731d88a55aefcef0ec4c578fa9c7" + integrity sha512-Yqz/Doumf3QTKplwGNrCHe/B2p9xqDghBZSlAY0/hU6ikuDVQuOUIpDP/YcmoT+447tsZTmirmjgG3znvSCR0Q== + +"@rollup/rollup-linux-arm64-gnu@4.9.1": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.1.tgz#967ba8e6f68a5f21bd00cd97773dcdd6107e94ed" + integrity sha512-u3XkZVvxcvlAOlQJ3UsD1rFvLWqu4Ef/Ggl40WAVCuogf4S1nJPHh5RTgqYFpCOvuGJ7H5yGHabjFKEZGExk5Q== + +"@rollup/rollup-linux-arm64-musl@4.9.1": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.1.tgz#d3a4e1c9f21eef3b9f4e4989f334a519a1341462" + integrity sha512-0XSYN/rfWShW+i+qjZ0phc6vZ7UWI8XWNz4E/l+6edFt+FxoEghrJHjX1EY/kcUGCnZzYYRCl31SNdfOi450Aw== + +"@rollup/rollup-linux-riscv64-gnu@4.9.1": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.1.tgz#415c0533bb752164effd05f5613858e8f6779bc9" + integrity sha512-LmYIO65oZVfFt9t6cpYkbC4d5lKHLYv5B4CSHRpnANq0VZUQXGcCPXHzbCXCz4RQnx7jvlYB1ISVNCE/omz5cw== + +"@rollup/rollup-linux-x64-gnu@4.9.1": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.1.tgz#0983385dd753a2e0ecaddea7a81dd37fea5114f5" + integrity sha512-kr8rEPQ6ns/Lmr/hiw8sEVj9aa07gh1/tQF2Y5HrNCCEPiCBGnBUt9tVusrcBBiJfIt1yNaXN6r1CCmpbFEDpg== + +"@rollup/rollup-linux-x64-musl@4.9.1": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.1.tgz#eb7494ebc5199cbd2e5c38c2b8acbe2603f35e03" + integrity sha512-t4QSR7gN+OEZLG0MiCgPqMWZGwmeHhsM4AkegJ0Kiy6TnJ9vZ8dEIwHw1LcZKhbHxTY32hp9eVCMdR3/I8MGRw== + +"@rollup/rollup-win32-arm64-msvc@4.9.1": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.1.tgz#5bebc66e3a7f82d4b9aa9ff448e7fc13a69656e9" + integrity sha512-7XI4ZCBN34cb+BH557FJPmh0kmNz2c25SCQeT9OiFWEgf8+dL6ZwJ8f9RnUIit+j01u07Yvrsuu1rZGxJCc51g== + +"@rollup/rollup-win32-ia32-msvc@4.9.1": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.1.tgz#34156ebf8b4de3b20e6497260fe519a30263f8cf" + integrity sha512-yE5c2j1lSWOH5jp+Q0qNL3Mdhr8WuqCNVjc6BxbVfS5cAS6zRmdiw7ktb8GNpDCEUJphILY6KACoFoRtKoqNQg== + +"@rollup/rollup-win32-x64-msvc@4.9.1": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.1.tgz#d146db7a5949e10837b323ce933ed882ac878262" + integrity sha512-PyJsSsafjmIhVgaI1Zdj7m8BB8mMckFah/xbpplObyHfiXzKcI5UOUXRyOdHW7nz4DpMCuzLnF7v5IWHenCwYA== "@rushstack/eslint-patch@^1.1.0": version "1.6.1" @@ -6115,9 +6120,9 @@ prop-types "^15.7.2" "@stripe/stripe-js@^2.2.1": - version "2.2.1" - resolved "https://registry.yarnpkg.com/@stripe/stripe-js/-/stripe-js-2.2.1.tgz#532bbb796b2be680308aca6becef9967bc19bef7" - integrity sha512-Wd81A0u8EwT3cf+Xv1mpMI18RbXVhgh19MtPcF9ojNTlG3kl36B1+XFe1KQfnJxD3WRnVfDuI0rNCK53mcGm6g== + version "2.2.2" + resolved "https://registry.yarnpkg.com/@stripe/stripe-js/-/stripe-js-2.2.2.tgz#9c1318a3e09a6df0a667d54d7f6edb917670109d" + integrity sha512-LvFZRZEBoMe6vXC6RoOAIbXWo/0JDdndq43ekL9M6affcM7PtF5KALmwt91BazW7q49sbSl0l7TunWhhSwEW4w== "@swc/helpers@^0.5.0": version "0.5.3" @@ -6294,9 +6299,9 @@ "@types/babel__traverse" "*" "@types/babel__generator@*": - version "7.6.7" - resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.7.tgz#a7aebf15c7bc0eb9abd638bdb5c0b8700399c9d0" - integrity sha512-6Sfsq+EaaLrw4RmdFWE9Onp63TOUue71AWb4Gpa6JxzgTYtimbM086WnYTy2U67AofR++QKCo08ZP6pwx8YFHQ== + version "7.6.8" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.8.tgz#f836c61f48b1346e7d2b0d93c6dacc5b9535d3ab" + integrity sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw== dependencies: "@babel/types" "^7.0.0" @@ -6688,6 +6693,11 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== +"@types/json-stable-stringify@^1.0.36": + version "1.0.36" + resolved "https://registry.yarnpkg.com/@types/json-stable-stringify/-/json-stable-stringify-1.0.36.tgz#fe6c6001a69ff8160a772da08779448a333c7ddd" + integrity sha512-b7bq23s4fgBB76n34m2b3RBf6M369B0Z9uRR8aHTMd8kZISRkmDEpPD8hhpYvDFzr3bJCPES96cm3Q6qRNDbQw== + "@types/json5@^0.0.29": version "0.0.29" resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" @@ -6771,10 +6781,10 @@ resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.34.tgz#10964ba0dee6ac4cd462e2795b6bebd407303433" integrity sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g== -"@types/node@*", "@types/node@20.10.4", "@types/node@>=12.0.0", "@types/node@>=13.7.0", "@types/node@>=8.1.0", "@types/node@^20.10.4": - version "20.10.4" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.10.4.tgz#b246fd84d55d5b1b71bf51f964bd514409347198" - integrity sha512-D08YG6rr8X90YB56tSIuBaddy/UXAA9RKJoFvrsnogAum/0pmjkgi4+2nx96A330FmioegBWmEYQ+syqCFaveg== +"@types/node@*", "@types/node@>=12.0.0", "@types/node@>=13.7.0", "@types/node@>=8.1.0", "@types/node@^20.10.4": + version "20.10.5" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.10.5.tgz#47ad460b514096b7ed63a1dae26fad0914ed3ab2" + integrity sha512-nNPsNE65wjMxEKI93yOP+NPGGBJz/PoN3kZsVLee0XMiJolxSekEVD8wRwBUBqkwc7UWop0edW50yrCQW4CyRw== dependencies: undici-types "~5.26.4" @@ -6783,6 +6793,13 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== +"@types/node@20.10.4": + version "20.10.4" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.10.4.tgz#b246fd84d55d5b1b71bf51f964bd514409347198" + integrity sha512-D08YG6rr8X90YB56tSIuBaddy/UXAA9RKJoFvrsnogAum/0pmjkgi4+2nx96A330FmioegBWmEYQ+syqCFaveg== + dependencies: + undici-types "~5.26.4" + "@types/node@^10.0.3": version "10.17.60" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b" @@ -6865,9 +6882,9 @@ integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== "@types/react-dom@^18.0.0", "@types/react-dom@^18.2.14": - version "18.2.17" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.17.tgz#375c55fab4ae671bd98448dcfa153268d01d6f64" - integrity sha512-rvrT/M7Df5eykWFxn6MYt5Pem/Dbyc1N8Y0S9Mrkw2WFCRiqUgw9P7ul2NpwsXCSM1DVdENzdG9J5SreqfAIWg== + version "18.2.18" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.18.tgz#16946e6cd43971256d874bc3d0a72074bb8571dd" + integrity sha512-TJxDm6OfAX2KJWJdMEVTwWke5Sc/E/RlnPGvGfS0W7+6ocy2xhDVQVh/KvC2Uf7kACs+gDytdusDSdWfWkaNzw== dependencies: "@types/react" "*" @@ -6878,7 +6895,7 @@ dependencies: "@types/react" "*" -"@types/react-transition-group@^4.4.0", "@types/react-transition-group@^4.4.8", "@types/react-transition-group@^4.4.9": +"@types/react-transition-group@^4.4.0", "@types/react-transition-group@^4.4.10", "@types/react-transition-group@^4.4.8": version "4.4.10" resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.10.tgz#6ee71127bdab1f18f11ad8fb3322c6da27c327ac" integrity sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q== @@ -7024,6 +7041,13 @@ dependencies: "@types/node" "*" +"@types/xml2js@0.4.13": + version "0.4.13" + resolved "https://registry.yarnpkg.com/@types/xml2js/-/xml2js-0.4.13.tgz#219134d550c21e2f1d7b0e2fbb9174144c6053ed" + integrity sha512-nuT42GzgoUa4zZgBoF4d+Zqc12/FlVxXCT4xU6j3RfqTFVQWrUAClI/0sNJ5ImM9Wv6KB42KMG2xsVMn4cSBzA== + dependencies: + "@types/node" "*" + "@types/xml2js@^0.4.14": version "0.4.14" resolved "https://registry.yarnpkg.com/@types/xml2js/-/xml2js-0.4.14.tgz#5d462a2a7330345e2309c6b549a183a376de8f9a" @@ -7096,13 +7120,13 @@ "@typescript-eslint/types" "5.62.0" "@typescript-eslint/visitor-keys" "5.62.0" -"@typescript-eslint/scope-manager@6.14.0": - version "6.14.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.14.0.tgz#53d24363fdb5ee0d1d8cda4ed5e5321272ab3d48" - integrity sha512-VT7CFWHbZipPncAZtuALr9y3EuzY1b1t1AEkIq2bTXUPKw+pHoXflGNG5L+Gv6nKul1cz1VH8fz16IThIU0tdg== +"@typescript-eslint/scope-manager@6.15.0": + version "6.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.15.0.tgz#40e5214a3e9e048aca55ce33381bc61b6b51c32a" + integrity sha512-+BdvxYBltqrmgCNu4Li+fGDIkW9n//NrruzG9X1vBzaNK+ExVXPoGB71kneaVw/Jp+4rH/vaMAGC6JfMbHstVg== dependencies: - "@typescript-eslint/types" "6.14.0" - "@typescript-eslint/visitor-keys" "6.14.0" + "@typescript-eslint/types" "6.15.0" + "@typescript-eslint/visitor-keys" "6.15.0" "@typescript-eslint/type-utils@5.62.0": version "5.62.0" @@ -7119,10 +7143,10 @@ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== -"@typescript-eslint/types@6.14.0": - version "6.14.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.14.0.tgz#935307f7a931016b7a5eb25d494ea3e1f613e929" - integrity sha512-uty9H2K4Xs8E47z3SnXEPRNDfsis8JO27amp2GNCnzGETEW3yTqEIVg5+AI7U276oGF/tw6ZA+UesxeQ104ceA== +"@typescript-eslint/types@6.15.0": + version "6.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.15.0.tgz#a9f7b006aee52b0948be6e03f521814bf435ddd5" + integrity sha512-yXjbt//E4T/ee8Ia1b5mGlbNj9fB9lJP4jqLbZualwpP2BCQ5is6BcWwxpIsY4XKAhmdv3hrW92GdtJbatC6dQ== "@typescript-eslint/typescript-estree@5.62.0": version "5.62.0" @@ -7137,13 +7161,13 @@ semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/typescript-estree@6.14.0": - version "6.14.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.14.0.tgz#90c7ddd45cd22139adf3d4577580d04c9189ac13" - integrity sha512-yPkaLwK0yH2mZKFE/bXkPAkkFgOv15GJAUzgUVonAbv0Hr4PK/N2yaA/4XQbTZQdygiDkpt5DkxPELqHguNvyw== +"@typescript-eslint/typescript-estree@6.15.0": + version "6.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.15.0.tgz#2f8a513df1ce5e6e1ba8e5c6aa52f392ae023fc5" + integrity sha512-7mVZJN7Hd15OmGuWrp2T9UvqR2Ecg+1j/Bp1jXUEY2GZKV6FXlOIoqVDmLpBiEiq3katvj/2n2mR0SDwtloCew== dependencies: - "@typescript-eslint/types" "6.14.0" - "@typescript-eslint/visitor-keys" "6.14.0" + "@typescript-eslint/types" "6.15.0" + "@typescript-eslint/visitor-keys" "6.15.0" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" @@ -7165,16 +7189,16 @@ semver "^7.3.7" "@typescript-eslint/utils@^6.10.0": - version "6.14.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.14.0.tgz#856a9e274367d99ffbd39c48128b93a86c4261e3" - integrity sha512-XwRTnbvRr7Ey9a1NT6jqdKX8y/atWG+8fAIu3z73HSP8h06i3r/ClMhmaF/RGWGW1tHJEwij1uEg2GbEmPYvYg== + version "6.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.15.0.tgz#f80dbb79f3b0f569077a8711dd44186a8933fa4c" + integrity sha512-eF82p0Wrrlt8fQSRL0bGXzK5nWPRV2dYQZdajcfzOD9+cQz9O7ugifrJxclB+xVOvWvagXfqS4Es7vpLP4augw== dependencies: "@eslint-community/eslint-utils" "^4.4.0" "@types/json-schema" "^7.0.12" "@types/semver" "^7.5.0" - "@typescript-eslint/scope-manager" "6.14.0" - "@typescript-eslint/types" "6.14.0" - "@typescript-eslint/typescript-estree" "6.14.0" + "@typescript-eslint/scope-manager" "6.15.0" + "@typescript-eslint/types" "6.15.0" + "@typescript-eslint/typescript-estree" "6.15.0" semver "^7.5.4" "@typescript-eslint/visitor-keys@5.62.0": @@ -7185,12 +7209,12 @@ "@typescript-eslint/types" "5.62.0" eslint-visitor-keys "^3.3.0" -"@typescript-eslint/visitor-keys@6.14.0": - version "6.14.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.14.0.tgz#1d1d486581819287de824a56c22f32543561138e" - integrity sha512-fB5cw6GRhJUz03MrROVuj5Zm/Q+XWlVdIsFj+Zb1Hvqouc8t+XP2H5y53QYU/MGtd2dPg6/vJJlhoX3xc2ehfw== +"@typescript-eslint/visitor-keys@6.15.0": + version "6.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.15.0.tgz#5baf97a7bfeec6f4894d400437055155a46b2330" + integrity sha512-1zvtdC1a9h5Tb5jU9x3ADNXO9yjP8rXlaoChu0DQX40vf5ACVpYIVIZhIMZ6d5sDXH7vq4dsZBT1fEGj8D2n2w== dependencies: - "@typescript-eslint/types" "6.14.0" + "@typescript-eslint/types" "6.15.0" eslint-visitor-keys "^3.4.1" "@ucast/core@^1.0.0", "@ucast/core@^1.4.1", "@ucast/core@^1.6.1": @@ -8710,9 +8734,9 @@ available-typed-arrays@^1.0.5: integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== aws-sdk@^2.1255.0: - version "2.1519.0" - resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1519.0.tgz#cda5288f152f388c60efe37b0163fe71f317e813" - integrity sha512-eBU0I7dIKM2b0OKT0tDOT+Bb+Z7T3saatt1TQ7C6xz0jET7WQ7zSZ+oizLLrNC0n0/Hzu873l+eU/ZwwdoyrDQ== + version "2.1521.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1521.0.tgz#6cf522addc2770c8fa18b9d7eb85d1b0dea37383" + integrity sha512-OOlQ0MFVz54sQESJlJ85HdXTS0sBQdcg5jnwX+rx8C70qSvgDJKesvCYt4ZUQ7tRC4xRSMaJO5tE8xvftdGjtA== dependencies: buffer "4.9.2" events "1.1.1" @@ -11879,9 +11903,9 @@ electron-fetch@^1.7.2: encoding "^0.1.13" electron-to-chromium@^1.4.601: - version "1.4.613" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.613.tgz#529e4fc65576ecfd055d7d4619fade4fac446af2" - integrity sha512-r4x5+FowKG6q+/Wj0W9nidx7QO31BJwmR2uEo+Qh3YLGQ8SbBAFuDFpTxzly/I2gsbrFwBuIjrMp423L3O5U3w== + version "1.4.615" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.615.tgz#b1c41839962d2e4e63dca05519da9040e34848c2" + integrity sha512-/bKPPcgZVUziECqDc+0HkT87+0zhaWSZHNXqF8FLd2lQcptpmUFwoCSWjCdOng9Gdq+afKArPdEg/0ZW461Eng== elliptic@6.5.4, elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3, elliptic@^6.5.4: version "6.5.4" @@ -12396,32 +12420,33 @@ esbuild@^0.18.10: "@esbuild/win32-x64" "0.18.20" esbuild@^0.19.3: - version "0.19.9" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.19.9.tgz#423a8f35153beb22c0b695da1cd1e6c0c8cdd490" - integrity sha512-U9CHtKSy+EpPsEBa+/A2gMs/h3ylBC0H0KSqIg7tpztHerLi6nrrcoUJAkNCEPumx8yJ+Byic4BVwHgRbN0TBg== + version "0.19.10" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.19.10.tgz#55e83e4a6b702e3498b9f872d84bfb4ebcb6d16e" + integrity sha512-S1Y27QGt/snkNYrRcswgRFqZjaTG5a5xM3EQo97uNBnH505pdzSNe/HLBq1v0RO7iK/ngdbhJB6mDAp0OK+iUA== optionalDependencies: - "@esbuild/android-arm" "0.19.9" - "@esbuild/android-arm64" "0.19.9" - "@esbuild/android-x64" "0.19.9" - "@esbuild/darwin-arm64" "0.19.9" - "@esbuild/darwin-x64" "0.19.9" - "@esbuild/freebsd-arm64" "0.19.9" - "@esbuild/freebsd-x64" "0.19.9" - "@esbuild/linux-arm" "0.19.9" - "@esbuild/linux-arm64" "0.19.9" - "@esbuild/linux-ia32" "0.19.9" - "@esbuild/linux-loong64" "0.19.9" - "@esbuild/linux-mips64el" "0.19.9" - "@esbuild/linux-ppc64" "0.19.9" - "@esbuild/linux-riscv64" "0.19.9" - "@esbuild/linux-s390x" "0.19.9" - "@esbuild/linux-x64" "0.19.9" - "@esbuild/netbsd-x64" "0.19.9" - "@esbuild/openbsd-x64" "0.19.9" - "@esbuild/sunos-x64" "0.19.9" - "@esbuild/win32-arm64" "0.19.9" - "@esbuild/win32-ia32" "0.19.9" - "@esbuild/win32-x64" "0.19.9" + "@esbuild/aix-ppc64" "0.19.10" + "@esbuild/android-arm" "0.19.10" + "@esbuild/android-arm64" "0.19.10" + "@esbuild/android-x64" "0.19.10" + "@esbuild/darwin-arm64" "0.19.10" + "@esbuild/darwin-x64" "0.19.10" + "@esbuild/freebsd-arm64" "0.19.10" + "@esbuild/freebsd-x64" "0.19.10" + "@esbuild/linux-arm" "0.19.10" + "@esbuild/linux-arm64" "0.19.10" + "@esbuild/linux-ia32" "0.19.10" + "@esbuild/linux-loong64" "0.19.10" + "@esbuild/linux-mips64el" "0.19.10" + "@esbuild/linux-ppc64" "0.19.10" + "@esbuild/linux-riscv64" "0.19.10" + "@esbuild/linux-s390x" "0.19.10" + "@esbuild/linux-x64" "0.19.10" + "@esbuild/netbsd-x64" "0.19.10" + "@esbuild/openbsd-x64" "0.19.10" + "@esbuild/sunos-x64" "0.19.10" + "@esbuild/win32-arm64" "0.19.10" + "@esbuild/win32-ia32" "0.19.10" + "@esbuild/win32-x64" "0.19.10" escalade@^3.1.1: version "3.1.1" @@ -12661,14 +12686,14 @@ eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4 integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== eslint@^8.24.0, eslint@^8.55.0: - version "8.55.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.55.0.tgz#078cb7b847d66f2c254ea1794fa395bf8e7e03f8" - integrity sha512-iyUUAM0PCKj5QpwGfmCAG9XXbZCWsqP/eWAWrG/W0umvjuLRBECwSFdt+rCntju0xEH7teIABPwXpahftIaTdA== + version "8.56.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.56.0.tgz#4957ce8da409dc0809f99ab07a1b94832ab74b15" + integrity sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@eslint-community/regexpp" "^4.6.1" "@eslint/eslintrc" "^2.1.4" - "@eslint/js" "8.55.0" + "@eslint/js" "8.56.0" "@humanwhocodes/config-array" "^0.11.13" "@humanwhocodes/module-importer" "^1.0.1" "@nodelib/fs.walk" "^1.2.8" @@ -13368,9 +13393,9 @@ fastest-levenshtein@^1.0.7: integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== fastq@^1.6.0: - version "1.15.0" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a" - integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== + version "1.16.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.16.0.tgz#83b9a9375692db77a822df081edb6a9cf6839320" + integrity sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA== dependencies: reusify "^1.0.4" @@ -14513,9 +14538,9 @@ hardhat-dependency-compiler@^1.1.3: integrity sha512-bCDqsOxGST6WkbMvj4lPchYWidNSSBm5CFnkyAex1T11cGmr9otZTGl81W6f9pmrtBXbKCvr3OSuNJ6Q394sAw== hardhat-deploy@^0.11.43: - version "0.11.44" - resolved "https://registry.yarnpkg.com/hardhat-deploy/-/hardhat-deploy-0.11.44.tgz#a7a771a675a3837ce4c321f2c18d4b6fa1ed03a0" - integrity sha512-kAkCvTYPpdIvnWCc3F0xg3AbVsa/SgH9aePPO7e7nHUOkg07LGKuknU/UaVAfWwHe7Zs57pQIAolAQyKI+mnXw== + version "0.11.45" + resolved "https://registry.yarnpkg.com/hardhat-deploy/-/hardhat-deploy-0.11.45.tgz#bed86118175a38a03bb58aba2ce1ed5e80a20bc8" + integrity sha512-aC8UNaq3JcORnEUIwV945iJuvBwi65tjHVDU3v6mOcqik7WAzHVCJ7cwmkkipsHrWysrB5YvGF1q9S1vIph83w== dependencies: "@ethersproject/abi" "^5.7.0" "@ethersproject/abstract-signer" "^5.7.0" @@ -14552,9 +14577,9 @@ hardhat-gas-reporter@^1.0.8, hardhat-gas-reporter@^1.0.9: sha1 "^1.1.1" hardhat@^2.11.0, hardhat@^2.18.3: - version "2.19.2" - resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.19.2.tgz#815819e4efd234941d495decb718b358d572e2c8" - integrity sha512-CRU3+0Cc8Qh9UpxKd8cLADDPes7ZDtKj4dTK+ERtLBomEzhRPLWklJn4VKOwjre9/k8GNd/e9DYxpfuzcxbXPQ== + version "2.19.3" + resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.19.3.tgz#fe3b28b889e34a074ea5b740c227e3c8d4ce56e8" + integrity sha512-zUvfILiu1O7W1a+t5E1nCJ6z1danRLNizQkSEQCCgDYcRx13AGXtH1MVZajKmdLmXIjKAPReTp/8JQQ4ZHaX3g== dependencies: "@ethersproject/abi" "^5.1.2" "@metamask/eth-sig-util" "^4.0.0" @@ -17323,6 +17348,16 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== +json-stable-stringify@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.1.0.tgz#43d39c7c8da34bfaf785a61a56808b0def9f747d" + integrity sha512-zfA+5SuwYN2VWqN1/5HZaDzQKLJHaBVMZIIM+wuYjdptkaQsqzDdqjqf+lZZJUuJq1aanHiY8LhH8LmH+qBYJA== + dependencies: + call-bind "^1.0.5" + isarray "^2.0.5" + jsonify "^0.0.1" + object-keys "^1.1.1" + json-stream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/json-stream/-/json-stream-1.0.0.tgz#1a3854e28d2bbeeab31cc7ddf683d2ddc5652708" @@ -17373,6 +17408,11 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" +jsonify@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.1.tgz#2aa3111dae3d34a0f151c63f3a45d995d9420978" + integrity sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg== + jsonparse@^1.2.0: version "1.3.1" resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" @@ -17795,9 +17835,9 @@ libmime@^2.0.3: libqp "1.1.0" libphonenumber-js@^1.10.14: - version "1.10.51" - resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.51.tgz#a3b8c15db2721c3e5f7fe6759e2a524712b578e6" - integrity sha512-vY2I+rQwrDQzoPds0JeTEpeWzbUJgqoV0O4v31PauHBb/e+1KCXKylHcDnBMgJZ9fH9mErsEbROJY3Z3JtqEmg== + version "1.10.52" + resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.52.tgz#786d7743e75aba1996824057b60003bb539f8daa" + integrity sha512-6vCuCHgem+OW1/VCAKgkasfegItCea8zIT7s9/CG/QxdCMIM7GfzbEBG5d7lGO3rzipjt5woOQL3DiHa8Fy78Q== libqp@1.1.0: version "1.1.0" @@ -21660,9 +21700,9 @@ regenerate@^1.4.2: integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== regenerator-runtime@^0.14.0: - version "0.14.0" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45" - integrity sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA== + version "0.14.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" + integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== regenerator-transform@^0.15.2: version "0.15.2" @@ -22106,29 +22146,29 @@ rollup@^3.27.1: fsevents "~2.3.2" rollup@^4.2.0: - version "4.9.0" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.9.0.tgz#94dff4070f106c1be6b2e88401a49b023c87fa88" - integrity sha512-bUHW/9N21z64gw8s6tP4c88P382Bq/L5uZDowHlHx6s/QWpjJXivIAbEw6LZthgSvlEizZBfLC4OAvWe7aoF7A== + version "4.9.1" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.9.1.tgz#351d6c03e4e6bcd7a0339df3618d2aeeb108b507" + integrity sha512-pgPO9DWzLoW/vIhlSoDByCzcpX92bKEorbgXuZrqxByte3JFk2xSW2JEeAcyLc9Ru9pqcNNW+Ob7ntsk2oT/Xw== optionalDependencies: - "@rollup/rollup-android-arm-eabi" "4.9.0" - "@rollup/rollup-android-arm64" "4.9.0" - "@rollup/rollup-darwin-arm64" "4.9.0" - "@rollup/rollup-darwin-x64" "4.9.0" - "@rollup/rollup-linux-arm-gnueabihf" "4.9.0" - "@rollup/rollup-linux-arm64-gnu" "4.9.0" - "@rollup/rollup-linux-arm64-musl" "4.9.0" - "@rollup/rollup-linux-riscv64-gnu" "4.9.0" - "@rollup/rollup-linux-x64-gnu" "4.9.0" - "@rollup/rollup-linux-x64-musl" "4.9.0" - "@rollup/rollup-win32-arm64-msvc" "4.9.0" - "@rollup/rollup-win32-ia32-msvc" "4.9.0" - "@rollup/rollup-win32-x64-msvc" "4.9.0" + "@rollup/rollup-android-arm-eabi" "4.9.1" + "@rollup/rollup-android-arm64" "4.9.1" + "@rollup/rollup-darwin-arm64" "4.9.1" + "@rollup/rollup-darwin-x64" "4.9.1" + "@rollup/rollup-linux-arm-gnueabihf" "4.9.1" + "@rollup/rollup-linux-arm64-gnu" "4.9.1" + "@rollup/rollup-linux-arm64-musl" "4.9.1" + "@rollup/rollup-linux-riscv64-gnu" "4.9.1" + "@rollup/rollup-linux-x64-gnu" "4.9.1" + "@rollup/rollup-linux-x64-musl" "4.9.1" + "@rollup/rollup-win32-arm64-msvc" "4.9.1" + "@rollup/rollup-win32-ia32-msvc" "4.9.1" + "@rollup/rollup-win32-x64-msvc" "4.9.1" fsevents "~2.3.2" rpc-websockets@^7.5.1: - version "7.8.0" - resolved "https://registry.yarnpkg.com/rpc-websockets/-/rpc-websockets-7.8.0.tgz#1bcf571f65c51803e81f0824e9540a0da35a5287" - integrity sha512-AStkq6KDvSAmA4WiwlK1pDvj/33BWmExTATUokC0v+NhWekXSTNzXS5OGXeYwq501/pj6lBZMofg/h4dx4/tCg== + version "7.9.0" + resolved "https://registry.yarnpkg.com/rpc-websockets/-/rpc-websockets-7.9.0.tgz#a3938e16d6f134a3999fdfac422a503731bf8973" + integrity sha512-DwKewQz1IUA5wfLvgM8wDpPRcr+nWSxuFxx5CbrI2z/MyyZ4nXLM86TvIA+cI1ZAdqC8JIBR1mZR55dzaLU+Hw== dependencies: "@babel/runtime" "^7.17.2" eventemitter3 "^4.0.7" @@ -22556,9 +22596,9 @@ shelljs@0.8.5, shelljs@^0.8.3: rechoir "^0.6.2" shiki@^0.14.1: - version "0.14.6" - resolved "https://registry.yarnpkg.com/shiki/-/shiki-0.14.6.tgz#908a9cd5439f7e87279c6623e7c60a3b0a2df85c" - integrity sha512-R4koBBlQP33cC8cpzX0hAoOURBHJILp4Aaduh2eYi+Vj8ZBqtK/5SWNEHBS3qwUMu8dqOtI/ftno3ESfNeVW9g== + version "0.14.7" + resolved "https://registry.yarnpkg.com/shiki/-/shiki-0.14.7.tgz#c3c9e1853e9737845f1d2ef81b31bcfb07056d4e" + integrity sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg== dependencies: ansi-sequence-parser "^1.1.0" jsonc-parser "^3.2.0" @@ -24927,9 +24967,9 @@ victory-vendor@^36.6.8: d3-timer "^3.0.1" viem@^1.16.6: - version "1.20.0" - resolved "https://registry.yarnpkg.com/viem/-/viem-1.20.0.tgz#25cb019831f46aeecf2c8c5cdbad41e4f79b2e32" - integrity sha512-yPjV9pJr10xi28C/9LEvs5zdZNEMiru3Kz7nufghVYABJAfeSkoZQXb6b23n7MscS7c55JO5nmUI3xKkd9g6Yg== + version "1.20.2" + resolved "https://registry.yarnpkg.com/viem/-/viem-1.20.2.tgz#42455f003bef012fcac7e51e510d50fa972d969a" + integrity sha512-f+duq2Gs38REUVsKaEmNq+JFcvXaYtaoZdw43QUAZEvm7D4iwQDkfDSMvsYSwCSUJC8rUUqX3JFcXZm03mYWlQ== dependencies: "@adraffy/ens-normalize" "1.10.0" "@noble/curves" "1.2.0" @@ -25027,9 +25067,9 @@ vite@^2.2.0: fsevents "~2.3.2" vite@^5.0.9: - version "5.0.9" - resolved "https://registry.yarnpkg.com/vite/-/vite-5.0.9.tgz#2ac49a37b5accba29d945fcee9a0b7b862423cd4" - integrity sha512-wVqMd5kp28QWGgfYPDfrj771VyHTJ4UDlCteLH7bJDGDEamaz5hV8IX6h1brSGgnnyf9lI2RnzXq/JmD0c2wwg== + version "5.0.10" + resolved "https://registry.yarnpkg.com/vite/-/vite-5.0.10.tgz#1e13ef5c3cf5aa4eed81f5df6d107b3c3f1f6356" + integrity sha512-2P8J7WWgmc355HUMlFrwofacvr98DAjoE52BfdbwQtyLH06XKwaL/FMnmKM2crF0iX4MpmMKoDlNCB1ok7zHCw== dependencies: esbuild "^0.19.3" postcss "^8.4.32" From f311e3fd085a3a8d861a1a9431cd838e18a23562 Mon Sep 17 00:00:00 2001 From: m00n620 <50647994+m00n620@users.noreply.github.com> Date: Tue, 19 Dec 2023 11:39:02 -0500 Subject: [PATCH 002/104] [Dashhboard] Fix admin build issue (#1366) * fix dashboard admin build issue * use single quote --- packages/apps/dashboard/admin/.eslintrc.json | 7 + packages/apps/dashboard/admin/.gitignore | 1 + packages/apps/dashboard/admin/.prettierrc | 1 + packages/apps/dashboard/admin/config/admin.ts | 8 +- .../apps/dashboard/admin/config/cron-tasks.ts | 36 +- .../apps/dashboard/admin/config/database.ts | 22 +- .../apps/dashboard/admin/config/server.ts | 10 +- packages/apps/dashboard/admin/package.json | 4 + .../dashboard/admin/src/admin/tsconfig.json | 12 +- .../controllers/monthly-task-summary.ts | 26 +- .../routes/01-custom-monthly-task-summary.ts | 6 +- .../controllers/network-data-item.ts | 2 +- .../api/news-item/controllers/news-item.ts | 2 +- .../admin/src/api/stats/controllers/stats.ts | 4 +- .../admin/src/api/stats/routes/stats.ts | 12 +- .../admin/src/api/stats/services/stats.ts | 32 +- .../admin/types/generated/components.d.ts | 5 + .../admin/types/generated/contentTypes.d.ts | 611 ++++++++++++++++++ yarn.lock | 4 +- 19 files changed, 700 insertions(+), 105 deletions(-) create mode 100644 packages/apps/dashboard/admin/.eslintrc.json create mode 100644 packages/apps/dashboard/admin/types/generated/components.d.ts create mode 100644 packages/apps/dashboard/admin/types/generated/contentTypes.d.ts diff --git a/packages/apps/dashboard/admin/.eslintrc.json b/packages/apps/dashboard/admin/.eslintrc.json new file mode 100644 index 0000000000..0a1aa4cdf7 --- /dev/null +++ b/packages/apps/dashboard/admin/.eslintrc.json @@ -0,0 +1,7 @@ +{ + "extends": "prettier", + "plugins": ["prettier"], + "rules": { + "prettier/prettier": "error" + } +} diff --git a/packages/apps/dashboard/admin/.gitignore b/packages/apps/dashboard/admin/.gitignore index b4e4505aba..3a18a7377e 100644 --- a/packages/apps/dashboard/admin/.gitignore +++ b/packages/apps/dashboard/admin/.gitignore @@ -113,6 +113,7 @@ exports dist build .strapi-updater.json +.strapi # docker db-data \ No newline at end of file diff --git a/packages/apps/dashboard/admin/.prettierrc b/packages/apps/dashboard/admin/.prettierrc index 8f02c18a8d..6417e42437 100644 --- a/packages/apps/dashboard/admin/.prettierrc +++ b/packages/apps/dashboard/admin/.prettierrc @@ -1,4 +1,5 @@ { + "singleQuote": true, "tabWidth": 2, "printWidth": 120 } diff --git a/packages/apps/dashboard/admin/config/admin.ts b/packages/apps/dashboard/admin/config/admin.ts index 2678faf032..0fe98ed1c4 100644 --- a/packages/apps/dashboard/admin/config/admin.ts +++ b/packages/apps/dashboard/admin/config/admin.ts @@ -1,14 +1,14 @@ export default ({ env }) => ({ auth: { - secret: env("ADMIN_JWT_SECRET"), + secret: env('ADMIN_JWT_SECRET'), }, apiToken: { - salt: env("API_TOKEN_SALT"), + salt: env('API_TOKEN_SALT'), }, transfer: { token: { - salt: env("TRANSFER_TOKEN_SALT"), + salt: env('TRANSFER_TOKEN_SALT'), }, }, - watchIgnoreFiles: ["**/db-data"], + watchIgnoreFiles: ['**/db-data'], }); diff --git a/packages/apps/dashboard/admin/config/cron-tasks.ts b/packages/apps/dashboard/admin/config/cron-tasks.ts index ea63795877..e05ff1ce2b 100644 --- a/packages/apps/dashboard/admin/config/cron-tasks.ts +++ b/packages/apps/dashboard/admin/config/cron-tasks.ts @@ -1,5 +1,5 @@ -import { ChainId, NETWORKS, StatisticsClient } from "@human-protocol/sdk"; -import { createPublicClient, http } from "viem"; +import { ChainId, NETWORKS, StatisticsClient } from '@human-protocol/sdk'; +import { createPublicClient, http } from 'viem'; import { bsc, bscTestnet, @@ -11,8 +11,8 @@ import { moonbaseAlpha, celo, celoAlfajores, -} from "viem/chains"; -import { formatUnits, parseUnits } from "viem/utils"; +} from 'viem/chains'; +import { formatUnits, parseUnits } from 'viem/utils'; const SUPPORTED_CHAINS = { [ChainId.MAINNET]: mainnet, @@ -43,7 +43,7 @@ const fetchData = async () => { const hmtStats = await client.getHMTStatistics(); const paymentStats = await client.getPaymentStatistics(); - const publicClient = createPublicClient({ + const publicClient: any = createPublicClient({ chain: SUPPORTED_CHAINS[chainId], transport: http(), }); @@ -52,19 +52,19 @@ const fetchData = async () => { abi: [ { inputs: [], - name: "totalSupply", + name: 'totalSupply', outputs: [ { - internalType: "uint256", - name: "", - type: "uint256", + internalType: 'uint256', + name: '', + type: 'uint256', }, ], - stateMutability: "view", - type: "function", + stateMutability: 'view', + type: 'function', }, ], - functionName: "totalSupply", + functionName: 'totalSupply', }); return { @@ -93,11 +93,11 @@ export default { syncDashboardData: { task: async ({ strapi }) => { try { - console.log("sync started..."); + console.log('sync started...'); const dataItems = await fetchData(); const allNetworkDataItem = { ...dataItems[0] }; - allNetworkDataItem.chainId = "-1"; + allNetworkDataItem.chainId = '-1'; for (let i = 1; i < dataItems.length; i++) { const dataItem = dataItems[i]; dataItem.dailyHMTData.forEach((hmtDayData) => { @@ -149,10 +149,10 @@ export default { ); const networkDataItems = [allNetworkDataItem, ...dataItems]; - const uid = "api::network-data-item.network-data-item"; + const uid = 'api::network-data-item.network-data-item'; for (const dataItem of networkDataItems) { const entries = await strapi.entityService.findMany(uid, { - fields: ["chainId"], + fields: ['chainId'], filters: { chainId: Number(dataItem.chainId) }, }); console.log(dataItem.chainId); @@ -164,14 +164,14 @@ export default { await strapi.entityService.create(uid, { data: dataItem }); } } - console.log("sync ended..."); + console.log('sync ended...'); } catch (err) { console.log(err); } }, options: { // Every 1 minute - rule: "*/1 * * * *", + rule: '*/1 * * * *', }, }, }; diff --git a/packages/apps/dashboard/admin/config/database.ts b/packages/apps/dashboard/admin/config/database.ts index baa13b193c..33827a99e1 100644 --- a/packages/apps/dashboard/admin/config/database.ts +++ b/packages/apps/dashboard/admin/config/database.ts @@ -18,10 +18,7 @@ export default ({ env }) => { ca: env('DATABASE_SSL_CA', undefined), capath: env('DATABASE_SSL_CAPATH', undefined), cipher: env('DATABASE_SSL_CIPHER', undefined), - rejectUnauthorized: env.bool( - 'DATABASE_SSL_REJECT_UNAUTHORIZED', - true - ), + rejectUnauthorized: env.bool('DATABASE_SSL_REJECT_UNAUTHORIZED', true), }, }, pool: { min: env.int('DATABASE_POOL_MIN', 2), max: env.int('DATABASE_POOL_MAX', 10) }, @@ -39,10 +36,7 @@ export default ({ env }) => { ca: env('DATABASE_SSL_CA', undefined), capath: env('DATABASE_SSL_CAPATH', undefined), cipher: env('DATABASE_SSL_CIPHER', undefined), - rejectUnauthorized: env.bool( - 'DATABASE_SSL_REJECT_UNAUTHORIZED', - true - ), + rejectUnauthorized: env.bool('DATABASE_SSL_REJECT_UNAUTHORIZED', true), }, }, pool: { min: env.int('DATABASE_POOL_MIN', 2), max: env.int('DATABASE_POOL_MAX', 10) }, @@ -61,10 +55,7 @@ export default ({ env }) => { ca: env('DATABASE_SSL_CA', undefined), capath: env('DATABASE_SSL_CAPATH', undefined), cipher: env('DATABASE_SSL_CIPHER', undefined), - rejectUnauthorized: env.bool( - 'DATABASE_SSL_REJECT_UNAUTHORIZED', - true - ), + rejectUnauthorized: env.bool('DATABASE_SSL_REJECT_UNAUTHORIZED', true), }, schema: env('DATABASE_SCHEMA', 'public'), }, @@ -72,12 +63,7 @@ export default ({ env }) => { }, sqlite: { connection: { - filename: path.join( - __dirname, - '..', - '..', - env('DATABASE_FILENAME', '.tmp/data.db') - ), + filename: path.join(__dirname, '..', '..', env('DATABASE_FILENAME', '.tmp/data.db')), }, useNullAsDefault: true, }, diff --git a/packages/apps/dashboard/admin/config/server.ts b/packages/apps/dashboard/admin/config/server.ts index 83a8413c10..6bdb49946c 100644 --- a/packages/apps/dashboard/admin/config/server.ts +++ b/packages/apps/dashboard/admin/config/server.ts @@ -1,13 +1,13 @@ -import cronTasks from "./cron-tasks"; +import cronTasks from './cron-tasks'; export default ({ env }) => ({ - host: env("HOST", "0.0.0.0"), - port: env.int("PORT", 1337), + host: env('HOST', '0.0.0.0'), + port: env.int('PORT', 1337), app: { - keys: env.array("APP_KEYS"), + keys: env.array('APP_KEYS'), }, webhooks: { - populateRelations: env.bool("WEBHOOKS_POPULATE_RELATIONS", false), + populateRelations: env.bool('WEBHOOKS_POPULATE_RELATIONS', false), }, cron: { enabled: true, diff --git a/packages/apps/dashboard/admin/package.json b/packages/apps/dashboard/admin/package.json index f809de35cd..293b2b5819 100644 --- a/packages/apps/dashboard/admin/package.json +++ b/packages/apps/dashboard/admin/package.json @@ -19,6 +19,10 @@ "dayjs": "^1.11.10", "graphql-request": "^6.1.0", "pg": "8.8.0", + "react": "^18.0.0", + "react-dom": "^18.0.0", + "react-router-dom": "5.3.4", + "styled-components": "5.3.3", "viem": "^1.16.6" }, "author": { diff --git a/packages/apps/dashboard/admin/src/admin/tsconfig.json b/packages/apps/dashboard/admin/src/admin/tsconfig.json index 9be5078552..2894cac8fb 100644 --- a/packages/apps/dashboard/admin/src/admin/tsconfig.json +++ b/packages/apps/dashboard/admin/src/admin/tsconfig.json @@ -1,13 +1,5 @@ { "extends": "@strapi/typescript-utils/tsconfigs/admin", - "include": [ - "../plugins/**/admin/src/**/*", - "./" - ], - "exclude": [ - "node_modules/", - "build/", - "dist/", - "**/*.test.ts" - ] + "include": ["../plugins/**/admin/src/**/*", "./"], + "exclude": ["node_modules/", "build/", "dist/", "**/*.test.ts"] } diff --git a/packages/apps/dashboard/admin/src/api/monthly-task-summary/controllers/monthly-task-summary.ts b/packages/apps/dashboard/admin/src/api/monthly-task-summary/controllers/monthly-task-summary.ts index 80a84a8490..3da4449ae2 100644 --- a/packages/apps/dashboard/admin/src/api/monthly-task-summary/controllers/monthly-task-summary.ts +++ b/packages/apps/dashboard/admin/src/api/monthly-task-summary/controllers/monthly-task-summary.ts @@ -2,37 +2,37 @@ * monthly-task-summary controller */ -import { factories } from "@strapi/strapi"; -import axios from "axios"; -import dayjs from "dayjs"; +import { factories } from '@strapi/strapi'; +import axios from 'axios'; +import dayjs from 'dayjs'; -export default factories.createCoreController("api::monthly-task-summary.monthly-task-summary", ({ strapi }) => ({ +export default factories.createCoreController('api::monthly-task-summary.monthly-task-summary', ({ strapi }) => ({ async syncTaskSummary(ctx) { - const uid = "api::monthly-task-summary.monthly-task-summary"; + const uid = 'api::monthly-task-summary.monthly-task-summary'; const entries = await strapi.entityService.findMany(uid); - let startDate = dayjs("2022-07-01"); - const currentDate = dayjs().subtract(1, "month").endOf("month"); + let startDate = dayjs('2022-07-01'); + const currentDate = dayjs().subtract(1, 'month').endOf('month'); const dates = []; while (startDate <= currentDate) { - const from = startDate.startOf("month").format("YYYY-MM-DD"); - const to = startDate.endOf("month").format("YYYY-MM-DD"); + const from = startDate.startOf('month').format('YYYY-MM-DD'); + const to = startDate.endOf('month').format('YYYY-MM-DD'); const entry = entries.find((e) => e.date === to); if (!entry) { dates.push({ from, to }); } - startDate = startDate.add(1, "month"); + startDate = startDate.add(1, 'month'); } const results = await Promise.all( dates.map(({ from, to }) => axios - .get("/support/summary-stats", { - baseURL: "https://foundation-accounts.hmt.ai", - method: "GET", + .get('/support/summary-stats', { + baseURL: 'https://foundation-accounts.hmt.ai', + method: 'GET', params: { start_date: from, end_date: to, diff --git a/packages/apps/dashboard/admin/src/api/monthly-task-summary/routes/01-custom-monthly-task-summary.ts b/packages/apps/dashboard/admin/src/api/monthly-task-summary/routes/01-custom-monthly-task-summary.ts index 79d0635f1f..b5e2d6ccac 100644 --- a/packages/apps/dashboard/admin/src/api/monthly-task-summary/routes/01-custom-monthly-task-summary.ts +++ b/packages/apps/dashboard/admin/src/api/monthly-task-summary/routes/01-custom-monthly-task-summary.ts @@ -1,9 +1,9 @@ module.exports = { routes: [ { - method: "POST", - path: "/monthly-task-summary/sync", - handler: "monthly-task-summary.syncTaskSummary", + method: 'POST', + path: '/monthly-task-summary/sync', + handler: 'monthly-task-summary.syncTaskSummary', }, ], }; diff --git a/packages/apps/dashboard/admin/src/api/network-data-item/controllers/network-data-item.ts b/packages/apps/dashboard/admin/src/api/network-data-item/controllers/network-data-item.ts index dd4b231d1d..b79a8bc923 100644 --- a/packages/apps/dashboard/admin/src/api/network-data-item/controllers/network-data-item.ts +++ b/packages/apps/dashboard/admin/src/api/network-data-item/controllers/network-data-item.ts @@ -2,6 +2,6 @@ * network-data-item controller */ -import { factories } from '@strapi/strapi' +import { factories } from '@strapi/strapi'; export default factories.createCoreController('api::network-data-item.network-data-item'); diff --git a/packages/apps/dashboard/admin/src/api/news-item/controllers/news-item.ts b/packages/apps/dashboard/admin/src/api/news-item/controllers/news-item.ts index e7d4a006b5..9a5d572b25 100644 --- a/packages/apps/dashboard/admin/src/api/news-item/controllers/news-item.ts +++ b/packages/apps/dashboard/admin/src/api/news-item/controllers/news-item.ts @@ -2,6 +2,6 @@ * news-item controller */ -import { factories } from '@strapi/strapi' +import { factories } from '@strapi/strapi'; export default factories.createCoreController('api::news-item.news-item'); diff --git a/packages/apps/dashboard/admin/src/api/stats/controllers/stats.ts b/packages/apps/dashboard/admin/src/api/stats/controllers/stats.ts index d35cde18e2..b2451a8073 100644 --- a/packages/apps/dashboard/admin/src/api/stats/controllers/stats.ts +++ b/packages/apps/dashboard/admin/src/api/stats/controllers/stats.ts @@ -5,7 +5,7 @@ export default { getTasksStats: async (ctx, next) => { try { - const service = strapi.service("api::stats.stats") as any; + const service = strapi.service('api::stats.stats') as any; const data = await service.getTasksStats(ctx.query); ctx.body = data; } catch (err) { @@ -14,7 +14,7 @@ export default { }, getWorkersStats: async (ctx, next) => { try { - const service = strapi.service("api::stats.stats") as any; + const service = strapi.service('api::stats.stats') as any; const data = await service.getWorkersStats(ctx.query); ctx.body = data; } catch (err) { diff --git a/packages/apps/dashboard/admin/src/api/stats/routes/stats.ts b/packages/apps/dashboard/admin/src/api/stats/routes/stats.ts index b8a3e8ca00..4251ee03a6 100644 --- a/packages/apps/dashboard/admin/src/api/stats/routes/stats.ts +++ b/packages/apps/dashboard/admin/src/api/stats/routes/stats.ts @@ -1,18 +1,18 @@ export default { routes: [ { - method: "GET", - path: "/stats/tasks", - handler: "stats.getTasksStats", + method: 'GET', + path: '/stats/tasks', + handler: 'stats.getTasksStats', config: { policies: [], middlewares: [], }, }, { - method: "GET", - path: "/stats/workers", - handler: "stats.getWorkersStats", + method: 'GET', + path: '/stats/workers', + handler: 'stats.getWorkersStats', config: { policies: [], middlewares: [], diff --git a/packages/apps/dashboard/admin/src/api/stats/services/stats.ts b/packages/apps/dashboard/admin/src/api/stats/services/stats.ts index 5bb15e2024..35f15bc305 100644 --- a/packages/apps/dashboard/admin/src/api/stats/services/stats.ts +++ b/packages/apps/dashboard/admin/src/api/stats/services/stats.ts @@ -2,23 +2,15 @@ * stats service */ -import { ChainId, IStatisticsParams, NETWORKS } from "@human-protocol/sdk"; -import { - GET_PAYOUTS_QUERY, - IMData, - PayoutData, -} from "@human-protocol/sdk/dist/graphql"; -import gqlFetch from "graphql-request"; -import axios from "axios"; +import { ChainId, IStatisticsParams, NETWORKS } from '@human-protocol/sdk'; +import { GET_PAYOUTS_QUERY, IMData, PayoutData } from '@human-protocol/sdk/dist/graphql'; +import gqlFetch from 'graphql-request'; +import axios from 'axios'; const getIMData = async (params: IStatisticsParams): Promise => { const now = new Date(); const today = new Date(now.getFullYear(), now.getMonth(), now.getDate()); - const defaultFromDate = new Date( - now.getFullYear(), - now.getMonth(), - now.getDate() - ); + const defaultFromDate = new Date(now.getFullYear(), now.getMonth(), now.getDate()); defaultFromDate.setDate(today.getDate() - 60); const from = params.from ? new Date(params.from) : defaultFromDate; @@ -38,9 +30,9 @@ const getIMData = async (params: IStatisticsParams): Promise => { return await Promise.all( chunks.map(({ from, to }) => axios - .get("/support/summary-stats", { - baseURL: "https://foundation-accounts.hmt.ai", - method: "GET", + .get('/support/summary-stats', { + baseURL: 'https://foundation-accounts.hmt.ai', + method: 'GET', params: { start_date: from.toISOString().slice(0, 10), end_date: to.toISOString().slice(0, 10), @@ -100,16 +92,12 @@ export default () => ({ } ); - const activeWorkers = new Set( - payouts.map(({ recipient }) => recipient) - ).size; + const activeWorkers = new Set(payouts.map(({ recipient }) => recipient)).size; return { timestamp, activeWorkers, - averageJobsSolved: activeWorkers - ? value.solved / activeWorkers - : 0, + averageJobsSolved: activeWorkers ? value.solved / activeWorkers : 0, }; }) ), diff --git a/packages/apps/dashboard/admin/types/generated/components.d.ts b/packages/apps/dashboard/admin/types/generated/components.d.ts new file mode 100644 index 0000000000..0cd76b43f0 --- /dev/null +++ b/packages/apps/dashboard/admin/types/generated/components.d.ts @@ -0,0 +1,5 @@ +import type { Schema, Attribute } from '@strapi/strapi'; + +declare module '@strapi/types' { + export module Shared {} +} diff --git a/packages/apps/dashboard/admin/types/generated/contentTypes.d.ts b/packages/apps/dashboard/admin/types/generated/contentTypes.d.ts new file mode 100644 index 0000000000..71c173abb9 --- /dev/null +++ b/packages/apps/dashboard/admin/types/generated/contentTypes.d.ts @@ -0,0 +1,611 @@ +import type { Schema, Attribute } from '@strapi/strapi'; + +export interface AdminPermission extends Schema.CollectionType { + collectionName: 'admin_permissions'; + info: { + name: 'Permission'; + description: ''; + singularName: 'permission'; + pluralName: 'permissions'; + displayName: 'Permission'; + }; + pluginOptions: { + 'content-manager': { + visible: false; + }; + 'content-type-builder': { + visible: false; + }; + }; + attributes: { + action: Attribute.String & + Attribute.Required & + Attribute.SetMinMaxLength<{ + minLength: 1; + }>; + actionParameters: Attribute.JSON & Attribute.DefaultTo<{}>; + subject: Attribute.String & + Attribute.SetMinMaxLength<{ + minLength: 1; + }>; + properties: Attribute.JSON & Attribute.DefaultTo<{}>; + conditions: Attribute.JSON & Attribute.DefaultTo<[]>; + role: Attribute.Relation<'admin::permission', 'manyToOne', 'admin::role'>; + createdAt: Attribute.DateTime; + updatedAt: Attribute.DateTime; + createdBy: Attribute.Relation<'admin::permission', 'oneToOne', 'admin::user'> & Attribute.Private; + updatedBy: Attribute.Relation<'admin::permission', 'oneToOne', 'admin::user'> & Attribute.Private; + }; +} + +export interface AdminUser extends Schema.CollectionType { + collectionName: 'admin_users'; + info: { + name: 'User'; + description: ''; + singularName: 'user'; + pluralName: 'users'; + displayName: 'User'; + }; + pluginOptions: { + 'content-manager': { + visible: false; + }; + 'content-type-builder': { + visible: false; + }; + }; + attributes: { + firstname: Attribute.String & + Attribute.SetMinMaxLength<{ + minLength: 1; + }>; + lastname: Attribute.String & + Attribute.SetMinMaxLength<{ + minLength: 1; + }>; + username: Attribute.String; + email: Attribute.Email & + Attribute.Required & + Attribute.Private & + Attribute.Unique & + Attribute.SetMinMaxLength<{ + minLength: 6; + }>; + password: Attribute.Password & + Attribute.Private & + Attribute.SetMinMaxLength<{ + minLength: 6; + }>; + resetPasswordToken: Attribute.String & Attribute.Private; + registrationToken: Attribute.String & Attribute.Private; + isActive: Attribute.Boolean & Attribute.Private & Attribute.DefaultTo; + roles: Attribute.Relation<'admin::user', 'manyToMany', 'admin::role'> & Attribute.Private; + blocked: Attribute.Boolean & Attribute.Private & Attribute.DefaultTo; + preferedLanguage: Attribute.String; + createdAt: Attribute.DateTime; + updatedAt: Attribute.DateTime; + createdBy: Attribute.Relation<'admin::user', 'oneToOne', 'admin::user'> & Attribute.Private; + updatedBy: Attribute.Relation<'admin::user', 'oneToOne', 'admin::user'> & Attribute.Private; + }; +} + +export interface AdminRole extends Schema.CollectionType { + collectionName: 'admin_roles'; + info: { + name: 'Role'; + description: ''; + singularName: 'role'; + pluralName: 'roles'; + displayName: 'Role'; + }; + pluginOptions: { + 'content-manager': { + visible: false; + }; + 'content-type-builder': { + visible: false; + }; + }; + attributes: { + name: Attribute.String & + Attribute.Required & + Attribute.Unique & + Attribute.SetMinMaxLength<{ + minLength: 1; + }>; + code: Attribute.String & + Attribute.Required & + Attribute.Unique & + Attribute.SetMinMaxLength<{ + minLength: 1; + }>; + description: Attribute.String; + users: Attribute.Relation<'admin::role', 'manyToMany', 'admin::user'>; + permissions: Attribute.Relation<'admin::role', 'oneToMany', 'admin::permission'>; + createdAt: Attribute.DateTime; + updatedAt: Attribute.DateTime; + createdBy: Attribute.Relation<'admin::role', 'oneToOne', 'admin::user'> & Attribute.Private; + updatedBy: Attribute.Relation<'admin::role', 'oneToOne', 'admin::user'> & Attribute.Private; + }; +} + +export interface AdminApiToken extends Schema.CollectionType { + collectionName: 'strapi_api_tokens'; + info: { + name: 'Api Token'; + singularName: 'api-token'; + pluralName: 'api-tokens'; + displayName: 'Api Token'; + description: ''; + }; + pluginOptions: { + 'content-manager': { + visible: false; + }; + 'content-type-builder': { + visible: false; + }; + }; + attributes: { + name: Attribute.String & + Attribute.Required & + Attribute.Unique & + Attribute.SetMinMaxLength<{ + minLength: 1; + }>; + description: Attribute.String & + Attribute.SetMinMaxLength<{ + minLength: 1; + }> & + Attribute.DefaultTo<''>; + type: Attribute.Enumeration<['read-only', 'full-access', 'custom']> & + Attribute.Required & + Attribute.DefaultTo<'read-only'>; + accessKey: Attribute.String & + Attribute.Required & + Attribute.SetMinMaxLength<{ + minLength: 1; + }>; + lastUsedAt: Attribute.DateTime; + permissions: Attribute.Relation<'admin::api-token', 'oneToMany', 'admin::api-token-permission'>; + expiresAt: Attribute.DateTime; + lifespan: Attribute.BigInteger; + createdAt: Attribute.DateTime; + updatedAt: Attribute.DateTime; + createdBy: Attribute.Relation<'admin::api-token', 'oneToOne', 'admin::user'> & Attribute.Private; + updatedBy: Attribute.Relation<'admin::api-token', 'oneToOne', 'admin::user'> & Attribute.Private; + }; +} + +export interface AdminApiTokenPermission extends Schema.CollectionType { + collectionName: 'strapi_api_token_permissions'; + info: { + name: 'API Token Permission'; + description: ''; + singularName: 'api-token-permission'; + pluralName: 'api-token-permissions'; + displayName: 'API Token Permission'; + }; + pluginOptions: { + 'content-manager': { + visible: false; + }; + 'content-type-builder': { + visible: false; + }; + }; + attributes: { + action: Attribute.String & + Attribute.Required & + Attribute.SetMinMaxLength<{ + minLength: 1; + }>; + token: Attribute.Relation<'admin::api-token-permission', 'manyToOne', 'admin::api-token'>; + createdAt: Attribute.DateTime; + updatedAt: Attribute.DateTime; + createdBy: Attribute.Relation<'admin::api-token-permission', 'oneToOne', 'admin::user'> & Attribute.Private; + updatedBy: Attribute.Relation<'admin::api-token-permission', 'oneToOne', 'admin::user'> & Attribute.Private; + }; +} + +export interface AdminTransferToken extends Schema.CollectionType { + collectionName: 'strapi_transfer_tokens'; + info: { + name: 'Transfer Token'; + singularName: 'transfer-token'; + pluralName: 'transfer-tokens'; + displayName: 'Transfer Token'; + description: ''; + }; + pluginOptions: { + 'content-manager': { + visible: false; + }; + 'content-type-builder': { + visible: false; + }; + }; + attributes: { + name: Attribute.String & + Attribute.Required & + Attribute.Unique & + Attribute.SetMinMaxLength<{ + minLength: 1; + }>; + description: Attribute.String & + Attribute.SetMinMaxLength<{ + minLength: 1; + }> & + Attribute.DefaultTo<''>; + accessKey: Attribute.String & + Attribute.Required & + Attribute.SetMinMaxLength<{ + minLength: 1; + }>; + lastUsedAt: Attribute.DateTime; + permissions: Attribute.Relation<'admin::transfer-token', 'oneToMany', 'admin::transfer-token-permission'>; + expiresAt: Attribute.DateTime; + lifespan: Attribute.BigInteger; + createdAt: Attribute.DateTime; + updatedAt: Attribute.DateTime; + createdBy: Attribute.Relation<'admin::transfer-token', 'oneToOne', 'admin::user'> & Attribute.Private; + updatedBy: Attribute.Relation<'admin::transfer-token', 'oneToOne', 'admin::user'> & Attribute.Private; + }; +} + +export interface AdminTransferTokenPermission extends Schema.CollectionType { + collectionName: 'strapi_transfer_token_permissions'; + info: { + name: 'Transfer Token Permission'; + description: ''; + singularName: 'transfer-token-permission'; + pluralName: 'transfer-token-permissions'; + displayName: 'Transfer Token Permission'; + }; + pluginOptions: { + 'content-manager': { + visible: false; + }; + 'content-type-builder': { + visible: false; + }; + }; + attributes: { + action: Attribute.String & + Attribute.Required & + Attribute.SetMinMaxLength<{ + minLength: 1; + }>; + token: Attribute.Relation<'admin::transfer-token-permission', 'manyToOne', 'admin::transfer-token'>; + createdAt: Attribute.DateTime; + updatedAt: Attribute.DateTime; + createdBy: Attribute.Relation<'admin::transfer-token-permission', 'oneToOne', 'admin::user'> & Attribute.Private; + updatedBy: Attribute.Relation<'admin::transfer-token-permission', 'oneToOne', 'admin::user'> & Attribute.Private; + }; +} + +export interface PluginUploadFile extends Schema.CollectionType { + collectionName: 'files'; + info: { + singularName: 'file'; + pluralName: 'files'; + displayName: 'File'; + description: ''; + }; + pluginOptions: { + 'content-manager': { + visible: false; + }; + 'content-type-builder': { + visible: false; + }; + }; + attributes: { + name: Attribute.String & Attribute.Required; + alternativeText: Attribute.String; + caption: Attribute.String; + width: Attribute.Integer; + height: Attribute.Integer; + formats: Attribute.JSON; + hash: Attribute.String & Attribute.Required; + ext: Attribute.String; + mime: Attribute.String & Attribute.Required; + size: Attribute.Decimal & Attribute.Required; + url: Attribute.String & Attribute.Required; + previewUrl: Attribute.String; + provider: Attribute.String & Attribute.Required; + provider_metadata: Attribute.JSON; + related: Attribute.Relation<'plugin::upload.file', 'morphToMany'>; + folder: Attribute.Relation<'plugin::upload.file', 'manyToOne', 'plugin::upload.folder'> & Attribute.Private; + folderPath: Attribute.String & + Attribute.Required & + Attribute.Private & + Attribute.SetMinMax<{ + min: 1; + }>; + createdAt: Attribute.DateTime; + updatedAt: Attribute.DateTime; + createdBy: Attribute.Relation<'plugin::upload.file', 'oneToOne', 'admin::user'> & Attribute.Private; + updatedBy: Attribute.Relation<'plugin::upload.file', 'oneToOne', 'admin::user'> & Attribute.Private; + }; +} + +export interface PluginUploadFolder extends Schema.CollectionType { + collectionName: 'upload_folders'; + info: { + singularName: 'folder'; + pluralName: 'folders'; + displayName: 'Folder'; + }; + pluginOptions: { + 'content-manager': { + visible: false; + }; + 'content-type-builder': { + visible: false; + }; + }; + attributes: { + name: Attribute.String & + Attribute.Required & + Attribute.SetMinMax<{ + min: 1; + }>; + pathId: Attribute.Integer & Attribute.Required & Attribute.Unique; + parent: Attribute.Relation<'plugin::upload.folder', 'manyToOne', 'plugin::upload.folder'>; + children: Attribute.Relation<'plugin::upload.folder', 'oneToMany', 'plugin::upload.folder'>; + files: Attribute.Relation<'plugin::upload.folder', 'oneToMany', 'plugin::upload.file'>; + path: Attribute.String & + Attribute.Required & + Attribute.SetMinMax<{ + min: 1; + }>; + createdAt: Attribute.DateTime; + updatedAt: Attribute.DateTime; + createdBy: Attribute.Relation<'plugin::upload.folder', 'oneToOne', 'admin::user'> & Attribute.Private; + updatedBy: Attribute.Relation<'plugin::upload.folder', 'oneToOne', 'admin::user'> & Attribute.Private; + }; +} + +export interface PluginI18NLocale extends Schema.CollectionType { + collectionName: 'i18n_locale'; + info: { + singularName: 'locale'; + pluralName: 'locales'; + collectionName: 'locales'; + displayName: 'Locale'; + description: ''; + }; + options: { + draftAndPublish: false; + }; + pluginOptions: { + 'content-manager': { + visible: false; + }; + 'content-type-builder': { + visible: false; + }; + }; + attributes: { + name: Attribute.String & + Attribute.SetMinMax<{ + min: 1; + max: 50; + }>; + code: Attribute.String & Attribute.Unique; + createdAt: Attribute.DateTime; + updatedAt: Attribute.DateTime; + createdBy: Attribute.Relation<'plugin::i18n.locale', 'oneToOne', 'admin::user'> & Attribute.Private; + updatedBy: Attribute.Relation<'plugin::i18n.locale', 'oneToOne', 'admin::user'> & Attribute.Private; + }; +} + +export interface PluginUsersPermissionsPermission extends Schema.CollectionType { + collectionName: 'up_permissions'; + info: { + name: 'permission'; + description: ''; + singularName: 'permission'; + pluralName: 'permissions'; + displayName: 'Permission'; + }; + pluginOptions: { + 'content-manager': { + visible: false; + }; + 'content-type-builder': { + visible: false; + }; + }; + attributes: { + action: Attribute.String & Attribute.Required; + role: Attribute.Relation<'plugin::users-permissions.permission', 'manyToOne', 'plugin::users-permissions.role'>; + createdAt: Attribute.DateTime; + updatedAt: Attribute.DateTime; + createdBy: Attribute.Relation<'plugin::users-permissions.permission', 'oneToOne', 'admin::user'> & + Attribute.Private; + updatedBy: Attribute.Relation<'plugin::users-permissions.permission', 'oneToOne', 'admin::user'> & + Attribute.Private; + }; +} + +export interface PluginUsersPermissionsRole extends Schema.CollectionType { + collectionName: 'up_roles'; + info: { + name: 'role'; + description: ''; + singularName: 'role'; + pluralName: 'roles'; + displayName: 'Role'; + }; + pluginOptions: { + 'content-manager': { + visible: false; + }; + 'content-type-builder': { + visible: false; + }; + }; + attributes: { + name: Attribute.String & + Attribute.Required & + Attribute.SetMinMaxLength<{ + minLength: 3; + }>; + description: Attribute.String; + type: Attribute.String & Attribute.Unique; + permissions: Attribute.Relation< + 'plugin::users-permissions.role', + 'oneToMany', + 'plugin::users-permissions.permission' + >; + users: Attribute.Relation<'plugin::users-permissions.role', 'oneToMany', 'plugin::users-permissions.user'>; + createdAt: Attribute.DateTime; + updatedAt: Attribute.DateTime; + createdBy: Attribute.Relation<'plugin::users-permissions.role', 'oneToOne', 'admin::user'> & Attribute.Private; + updatedBy: Attribute.Relation<'plugin::users-permissions.role', 'oneToOne', 'admin::user'> & Attribute.Private; + }; +} + +export interface PluginUsersPermissionsUser extends Schema.CollectionType { + collectionName: 'up_users'; + info: { + name: 'user'; + description: ''; + singularName: 'user'; + pluralName: 'users'; + displayName: 'User'; + }; + options: { + draftAndPublish: false; + timestamps: true; + }; + attributes: { + username: Attribute.String & + Attribute.Required & + Attribute.Unique & + Attribute.SetMinMaxLength<{ + minLength: 3; + }>; + email: Attribute.Email & + Attribute.Required & + Attribute.SetMinMaxLength<{ + minLength: 6; + }>; + provider: Attribute.String; + password: Attribute.Password & + Attribute.Private & + Attribute.SetMinMaxLength<{ + minLength: 6; + }>; + resetPasswordToken: Attribute.String & Attribute.Private; + confirmationToken: Attribute.String & Attribute.Private; + confirmed: Attribute.Boolean & Attribute.DefaultTo; + blocked: Attribute.Boolean & Attribute.DefaultTo; + role: Attribute.Relation<'plugin::users-permissions.user', 'manyToOne', 'plugin::users-permissions.role'>; + createdAt: Attribute.DateTime; + updatedAt: Attribute.DateTime; + createdBy: Attribute.Relation<'plugin::users-permissions.user', 'oneToOne', 'admin::user'> & Attribute.Private; + updatedBy: Attribute.Relation<'plugin::users-permissions.user', 'oneToOne', 'admin::user'> & Attribute.Private; + }; +} + +export interface ApiMonthlyTaskSummaryMonthlyTaskSummary extends Schema.CollectionType { + collectionName: 'monthly_task_summaries'; + info: { + singularName: 'monthly-task-summary'; + pluralName: 'monthly-task-summaries'; + displayName: 'MonthlyTaskSummary'; + }; + options: { + draftAndPublish: false; + }; + attributes: { + date: Attribute.String; + served_count: Attribute.Integer; + solved_count: Attribute.Integer; + createdAt: Attribute.DateTime; + updatedAt: Attribute.DateTime; + createdBy: Attribute.Relation<'api::monthly-task-summary.monthly-task-summary', 'oneToOne', 'admin::user'> & + Attribute.Private; + updatedBy: Attribute.Relation<'api::monthly-task-summary.monthly-task-summary', 'oneToOne', 'admin::user'> & + Attribute.Private; + }; +} + +export interface ApiNetworkDataItemNetworkDataItem extends Schema.CollectionType { + collectionName: 'network_data_items'; + info: { + singularName: 'network-data-item'; + pluralName: 'network-data-items'; + displayName: 'NetworkDataItem'; + }; + options: { + draftAndPublish: false; + comment: ''; + }; + attributes: { + chainId: Attribute.Integer; + dailyHMTData: Attribute.JSON; + dailyPaymentsData: Attribute.JSON; + totalTransferAmount: Attribute.String; + totalTransferCount: Attribute.Integer; + totalHolders: Attribute.Integer; + totalSupply: Attribute.String; + createdAt: Attribute.DateTime; + updatedAt: Attribute.DateTime; + createdBy: Attribute.Relation<'api::network-data-item.network-data-item', 'oneToOne', 'admin::user'> & + Attribute.Private; + updatedBy: Attribute.Relation<'api::network-data-item.network-data-item', 'oneToOne', 'admin::user'> & + Attribute.Private; + }; +} + +export interface ApiNewsItemNewsItem extends Schema.SingleType { + collectionName: 'news_items'; + info: { + singularName: 'news-item'; + pluralName: 'news-items'; + displayName: 'News'; + description: ''; + }; + options: { + draftAndPublish: true; + }; + attributes: { + title: Attribute.String; + description: Attribute.Text; + image: Attribute.Media; + link: Attribute.String; + createdAt: Attribute.DateTime; + updatedAt: Attribute.DateTime; + publishedAt: Attribute.DateTime; + createdBy: Attribute.Relation<'api::news-item.news-item', 'oneToOne', 'admin::user'> & Attribute.Private; + updatedBy: Attribute.Relation<'api::news-item.news-item', 'oneToOne', 'admin::user'> & Attribute.Private; + }; +} + +declare module '@strapi/types' { + export module Shared { + export interface ContentTypes { + 'admin::permission': AdminPermission; + 'admin::user': AdminUser; + 'admin::role': AdminRole; + 'admin::api-token': AdminApiToken; + 'admin::api-token-permission': AdminApiTokenPermission; + 'admin::transfer-token': AdminTransferToken; + 'admin::transfer-token-permission': AdminTransferTokenPermission; + 'plugin::upload.file': PluginUploadFile; + 'plugin::upload.folder': PluginUploadFolder; + 'plugin::i18n.locale': PluginI18NLocale; + 'plugin::users-permissions.permission': PluginUsersPermissionsPermission; + 'plugin::users-permissions.role': PluginUsersPermissionsRole; + 'plugin::users-permissions.user': PluginUsersPermissionsUser; + 'api::monthly-task-summary.monthly-task-summary': ApiMonthlyTaskSummaryMonthlyTaskSummary; + 'api::network-data-item.network-data-item': ApiNetworkDataItemNetworkDataItem; + 'api::news-item.news-item': ApiNewsItemNewsItem; + } + } +} diff --git a/yarn.lock b/yarn.lock index c3b29d5354..b833eb47d8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -21209,7 +21209,7 @@ react-dnd@15.1.2: fast-deep-equal "^3.1.3" hoist-non-react-statics "^3.3.2" -react-dom@^18.2.0: +react-dom@^18.0.0, react-dom@^18.2.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== @@ -21492,7 +21492,7 @@ react-window@1.8.8: "@babel/runtime" "^7.0.0" memoize-one ">=3.1.1 <6" -react@^18.2.0: +react@^18.0.0, react@^18.2.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== From 2b9372ecda237d0eb4733dcf82966d36548ffc1c Mon Sep 17 00:00:00 2001 From: portuu3 <61605646+portuu3@users.noreply.github.com> Date: Thu, 21 Dec 2023 12:49:28 +0100 Subject: [PATCH 003/104] Job Launcher - CVAT S3 bucket limitations (#1379) * add storage params cvat dto * disable GCS and fix tests * Add error message and test * Improve error checks and messages * use storage data class for hcaptcha --- .../server/src/common/constants/errors.ts | 4 + .../server/src/common/enums/storage.ts | 82 ++++++++ .../server/src/common/utils/storage.ts | 34 ++++ .../server/src/modules/job/job.dto.ts | 27 ++- .../src/modules/job/job.service.spec.ts | 186 +++++++++++++++--- .../server/src/modules/job/job.service.ts | 21 +- .../job-launcher/server/test/constants.ts | 11 +- 7 files changed, 326 insertions(+), 39 deletions(-) create mode 100644 packages/apps/job-launcher/server/src/common/utils/storage.ts diff --git a/packages/apps/job-launcher/server/src/common/constants/errors.ts b/packages/apps/job-launcher/server/src/common/constants/errors.ts index 1e7c6d6b80..6ffa0ef1f0 100644 --- a/packages/apps/job-launcher/server/src/common/constants/errors.ts +++ b/packages/apps/job-launcher/server/src/common/constants/errors.ts @@ -103,6 +103,10 @@ export enum ErrorBucket { NotExist = 'Bucket does not exist', NotPublic = 'Bucket is not public', UnableSaveFile = 'Unable to save file', + InvalidProvider = 'Invalid storage provider', + EmptyRegion = 'Region cannot be empty for this storage provider', + InvalidRegion = 'Invalid region for the storage provider', + EmptyBucket = 'bucketName cannot be empty', FailedToFetchBucketContents = 'Failed to fetch bucket contents', } diff --git a/packages/apps/job-launcher/server/src/common/enums/storage.ts b/packages/apps/job-launcher/server/src/common/enums/storage.ts index c2bf37d34c..01c24ed303 100644 --- a/packages/apps/job-launcher/server/src/common/enums/storage.ts +++ b/packages/apps/job-launcher/server/src/common/enums/storage.ts @@ -1,3 +1,85 @@ +export enum StorageProviders { + AWS = 'AWS', + GCS = 'GCS', +} + +export enum AWSRegions { + AF_SOUTH_1 = 'af-south-1', + AP_EAST_1 = 'ap-east-1', + AP_NORTHEAST_1 = 'ap-northeast-1', + AP_NORTHEAST_2 = 'ap-northeast-2', + AP_NORTHEAST_3 = 'ap-northeast-3', + AP_SOUTH_1 = 'ap-south-1', + AP_SOUTH_2 = 'ap-south-2', + AP_SOUTHEAST_1 = 'ap-southeast-1', + AP_SOUTHEAST_2 = 'ap-southeast-2', + AP_SOUTHEAST_3 = 'ap-southeast-3', + AP_SOUTHEAST_4 = 'ap-southeast-4', + CA_CENTRAL_1 = 'ca-central-1', + CN_NORTH_1 = 'cn-north-1', + CN_NORTHWEST_1 = 'cn-northwest-1', + EU_CENTRAL_1 = 'eu-central-1', + EU_CENTRAL_2 = 'eu-central-2', + EU_NORTH_1 = 'eu-north-1', + EU_SOUTH_1 = 'eu-south-1', + EU_SOUTH_2 = 'eu-south-2', + EU_WEST_1 = 'eu-west-1', + EU_WEST_2 = 'eu-west-2', + EU_WEST_3 = 'eu-west-3', + IL_CENTRAL_1 = 'il-central-1', + ME_CENTRAL_1 = 'me-central-1', + ME_SOUTH_1 = 'me-south-1', + SA_EAST_1 = 'sa-east-1', + US_EAST_1 = 'us-east-1', + US_EAST_2 = 'us-east-2', + US_GOV_EAST_1 = 'us-gov-east-1', + US_GOV_WEST_1 = 'us-gov-west-1', + US_WEST_1 = 'us-west-1', + US_WEST_2 = 'us-west-2', +} + +export enum GCSRegions { + ASIA_EAST1 = 'asia-east1', // Taiwan + ASIA_EAST2 = 'asia-east2', // Hong Kong + ASIA_NORTHEAST1 = 'asia-northeast1', // Tokyo + ASIA_NORTHEAST2 = 'asia-northeast2', // Osaka + ASIA_NORTHEAST3 = 'asia-northeast3', // Seoul + ASIA_SOUTH1 = 'asia-south1', // Bombay + ASIA_SOUTH2 = 'asia-south2', // Delhi + ASIA_SOUTHEAST1 = 'asia-southeast1', // Singapore + ASIA_SOUTHEAST2 = 'asia-southeast2', // Jakarta + AUSTRALIA_SOUTHEAST1 = 'australia-southeast1', // Sydney + AUSTRALIA_SOUTHEAST2 = 'australia-southeast2', // Melbourne + EUROPE_CENTRAL2 = 'europe-central2', // Warsaw + EUROPE_NORTH1 = 'europe-north1', // Finland (Low CO2 footprint) + EUROPE_SOUTHWEST1 = 'europe-southwest1', // Madrid + EUROPE_WEST1 = 'europe-west1', // Belgium (Low CO2 footprint) + EUROPE_WEST2 = 'europe-west2', // London (Low CO2 footprint) + EUROPE_WEST3 = 'europe-west3', // Frankfurt (Low CO2 footprint) + EUROPE_WEST4 = 'europe-west4', // Netherlands + EUROPE_WEST6 = 'europe-west6', // Zurich (Low CO2 footprint) + EUROPE_WEST8 = 'europe-west8', // Milan + EUROPE_WEST9 = 'europe-west9', // Paris (Low CO2 footprint) + EUROPE_WEST10 = 'europe-west10', // Berlin + EUROPE_WEST12 = 'europe-west12', // Turin + ME_CENTRAL1 = 'me-central1', // Doha + ME_CENTRAL2 = 'me-central2', // Dammam, Saudi Arabia + ME_WEST1 = 'me-west1', // Tel Aviv + NORTHAMERICA_NORTHEAST1 = 'northamerica-northeast1', // Montreal (Low CO2 footprint) + NORTHAMERICA_NORTHEAST2 = 'northamerica-northeast2', // Toronto (Low CO2 footprint) + SOUTHAMERICA_EAST1 = 'southamerica-east1', // São Paulo (Low CO2 footprint) + SOUTHAMERICA_WEST1 = 'southamerica-west1', // Santiago (Low CO2 footprint) + US_CENTRAL1 = 'us-central1', // Iowa (Low CO2 footprint) + US_EAST1 = 'us-east1', // South Carolina + US_EAST4 = 'us-east4', // Northern Virginia + US_EAST5 = 'us-east5', // Columbus + US_SOUTH1 = 'us-south1', // Dallas + US_WEST1 = 'us-west1', // Oregon (Low CO2 footprint) + US_WEST2 = 'us-west2', // Los Angeles + US_WEST3 = 'us-west3', // Salt Lake City + US_WEST4 = 'us-west4', // Las Vegas +} + export enum ContentType { TEXT_PLAIN = 'text/plain', APPLICATION_JSON = 'application/json', diff --git a/packages/apps/job-launcher/server/src/common/utils/storage.ts b/packages/apps/job-launcher/server/src/common/utils/storage.ts new file mode 100644 index 0000000000..af6fe231f6 --- /dev/null +++ b/packages/apps/job-launcher/server/src/common/utils/storage.ts @@ -0,0 +1,34 @@ +import { BadRequestException } from '@nestjs/common'; +import { StorageDataDto } from '../../modules/job/job.dto'; +import { AWSRegions, StorageProviders } from '../enums/storage'; +import { ErrorBucket } from '../constants/errors'; + +export function generateBucketUrl(storageData: StorageDataDto): string { + if (!storageData.bucketName) { + throw new BadRequestException(ErrorBucket.EmptyBucket); + } + switch (storageData.provider) { + case StorageProviders.AWS: + if (!storageData.region) { + throw new BadRequestException(ErrorBucket.EmptyRegion); + } + if (!isRegion(storageData.region)) { + throw new BadRequestException(ErrorBucket.InvalidRegion); + } + return `https://${storageData.bucketName}.s3.${ + storageData.region + }.amazonaws.com${ + storageData.path ? `/${storageData.path.replace(/\/$/, '')}` : '' + }`; + // case StorageProviders.GCS: + // return `https://${s3Data.bucketName}.storage.googleapis.com${ + // s3Data.path ? `/${s3Data.path}` : '' + // }`; + default: + throw new BadRequestException(ErrorBucket.InvalidProvider); + } +} + +function isRegion(value: string): value is AWSRegions { + return Object.values(AWSRegions).includes(value as AWSRegions); +} diff --git a/packages/apps/job-launcher/server/src/modules/job/job.dto.ts b/packages/apps/job-launcher/server/src/modules/job/job.dto.ts index f1c36c9775..64881853f3 100644 --- a/packages/apps/job-launcher/server/src/modules/job/job.dto.ts +++ b/packages/apps/job-launcher/server/src/modules/job/job.dto.ts @@ -33,6 +33,7 @@ import { } from '../../common/enums/job'; import { EventType } from '../../common/enums/webhook'; import { BigNumber } from 'ethers'; +import { AWSRegions, StorageProviders } from '../../common/enums/storage'; export class JobCreateDto { @ApiProperty({ enum: ChainId }) @IsEnum(ChainId) @@ -101,14 +102,30 @@ export class JobFortuneDto extends JobDto { public fundAmount: number; } +export class StorageDataDto { + @ApiProperty({ enum: StorageProviders }) + @IsEnum(StorageProviders) + public provider: StorageProviders; + @ApiProperty({ enum: AWSRegions }) + @IsEnum(AWSRegions) + public region: AWSRegions | null; + @ApiProperty() + @IsString() + public bucketName: string; + @ApiProperty() + @IsOptional() + @IsString() + public path: string; +} + export class JobCvatDto extends JobDto { @ApiProperty() @IsString() public requesterDescription: string; @ApiProperty() - @IsUrl() - public dataUrl: string; + @IsObject() + public data: StorageDataDto; @ApiProperty() @IsArray() @@ -121,8 +138,8 @@ export class JobCvatDto extends JobDto { public minQuality: number; @ApiProperty() - @IsString() - public gtUrl: string; + @IsObject() + public groundTruth: StorageDataDto; @ApiProperty() @IsUrl() @@ -548,7 +565,7 @@ export class JobCaptchaAnnotationsDto { export class JobCaptchaDto extends JobDto { @ApiProperty() @IsUrl() - dataUrl: string; + data: StorageDataDto; @ApiProperty() @IsNumber() 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 3971ce0bae..88e17784c0 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 @@ -18,7 +18,11 @@ import { } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { Test } from '@nestjs/testing'; -import { ErrorJob, ErrorWeb3 } from '../../common/constants/errors'; +import { + ErrorBucket, + ErrorJob, + ErrorWeb3, +} from '../../common/constants/errors'; import { PaymentSource, PaymentStatus, @@ -61,6 +65,7 @@ import { MOCK_SUBMISSION_REQUIRED, MOCK_TRANSACTION_HASH, MOCK_USER_ID, + MOCK_STORAGE_DATA, MOCK_CVAT_JOB_SIZE, MOCK_CVAT_MAX_TIME, MOCK_CVAT_VAL_SIZE, @@ -70,6 +75,7 @@ import { MOCK_HCAPTCHA_REPO_URI, MOCK_HCAPTCHA_RO_URI, MOCK_FILE_KEY, + MOCK_BUCKET_FILE, } from '../../../test/constants'; import { PaymentService } from '../payment/payment.service'; import { Web3Service } from '../web3/web3.service'; @@ -79,6 +85,7 @@ import { JobFortuneDto, JobCvatDto, JobDetailsDto, + StorageDataDto, JobCaptchaDto, } from './job.dto'; import { JobEntity } from './job.entity'; @@ -100,6 +107,7 @@ import { HCAPTCHA_NOT_PRESENTED_LABEL, } from '../../common/constants'; import { WebhookService } from '../webhook/webhook.service'; +import { AWSRegions, StorageProviders } from '../../common/enums/storage'; const rate = 1.5; jest.mock('@human-protocol/sdk', () => ({ @@ -416,13 +424,13 @@ describe('JobService', () => { .spyOn(jobService, 'calculateJobBounty') .mockResolvedValueOnce(jobBounty); - const dto = { - dataUrl: MOCK_FILE_URL, + const dto: JobCvatDto = { + data: MOCK_STORAGE_DATA, labels: ['label1', 'label2'], requesterDescription: MOCK_REQUESTER_DESCRIPTION, userGuide: MOCK_FILE_URL, minQuality: 0.8, - gtUrl: MOCK_FILE_URL, + groundTruth: MOCK_STORAGE_DATA, type: JobRequestType.IMAGE_BOXES, fundAmount: 10, }; @@ -438,7 +446,7 @@ describe('JobService', () => { expect(result).toEqual({ data: { - data_url: MOCK_FILE_URL, + data_url: MOCK_BUCKET_FILE, }, annotation: { labels: [{ name: 'label1' }, { name: 'label2' }], @@ -451,7 +459,7 @@ describe('JobService', () => { validation: { min_quality: 0.8, val_size: 2, - gt_url: MOCK_FILE_URL, + gt_url: MOCK_BUCKET_FILE, }, job_bounty: jobBounty, }); @@ -477,8 +485,8 @@ describe('JobService', () => { jest.spyOn(storageService, 'download').mockResolvedValueOnce(fileContent); const jobType = JobCaptchaShapeType.COMPARISON; - const jobDto = { - dataUrl: MOCK_FILE_URL, + const jobDto: JobCaptchaDto = { + data: MOCK_STORAGE_DATA, accuracyTarget: 0.9, minRequests: 1, maxRequests: 10, @@ -532,8 +540,8 @@ describe('JobService', () => { jest.spyOn(storageService, 'download').mockResolvedValueOnce(fileContent); const jobType = JobCaptchaShapeType.CATEGORIZATION; - const jobDto = { - dataUrl: MOCK_FILE_URL, + const jobDto: JobCaptchaDto = { + data: MOCK_STORAGE_DATA, accuracyTarget: 0.9, minRequests: 1, maxRequests: 10, @@ -605,8 +613,8 @@ describe('JobService', () => { jest.spyOn(storageService, 'download').mockResolvedValueOnce(fileContent); const jobType = JobCaptchaShapeType.POLYGON; - const jobDto = { - dataUrl: MOCK_FILE_URL, + const jobDto: JobCaptchaDto = { + data: MOCK_STORAGE_DATA, accuracyTarget: 0.9, minRequests: 1, maxRequests: 10, @@ -679,8 +687,8 @@ describe('JobService', () => { jest.spyOn(storageService, 'download').mockResolvedValueOnce(fileContent); const jobType = JobCaptchaShapeType.POINT; - const jobDto = { - dataUrl: MOCK_FILE_URL, + const jobDto: JobCaptchaDto = { + data: MOCK_STORAGE_DATA, accuracyTarget: 0.9, minRequests: 1, maxRequests: 10, @@ -751,8 +759,8 @@ describe('JobService', () => { jest.spyOn(storageService, 'download').mockResolvedValueOnce(fileContent); const jobType = JobCaptchaShapeType.BOUNDING_BOX; - const jobDto = { - dataUrl: MOCK_FILE_URL, + const jobDto: JobCaptchaDto = { + data: MOCK_STORAGE_DATA, accuracyTarget: 0.9, minRequests: 1, maxRequests: 10, @@ -815,8 +823,8 @@ describe('JobService', () => { jest.spyOn(storageService, 'download').mockResolvedValueOnce(fileContent); const jobType = JobCaptchaShapeType.POLYGON; - const jobDto = { - dataUrl: MOCK_FILE_URL, + const jobDto: JobCaptchaDto = { + data: MOCK_STORAGE_DATA, accuracyTarget: 0.9, minRequests: 1, maxRequests: 10, @@ -858,12 +866,12 @@ describe('JobService', () => { const imageLabelBinaryJobDto: JobCvatDto = { chainId: MOCK_CHAIN_ID, - dataUrl: MOCK_FILE_URL, + data: MOCK_STORAGE_DATA, labels: ['cat', 'dog'], requesterDescription: MOCK_REQUESTER_DESCRIPTION, minQuality: 0.95, fundAmount: 10, - gtUrl: '', + groundTruth: MOCK_STORAGE_DATA, userGuide: MOCK_FILE_URL, type: JobRequestType.IMAGE_POINTS, }; @@ -933,7 +941,141 @@ describe('JobService', () => { }); }); - it('should create a job successfully on network selected from round robin logic', async () => { + it('should throw an error for invalid storage provider', async () => { + const userBalance = 25; + getUserBalanceMock.mockResolvedValue(userBalance); + + const storageDataMock: StorageDataDto = { + provider: StorageProviders.GCS, + region: AWSRegions.EU_CENTRAL_1, + bucketName: 'bucket', + path: 'folder/test', + }; + + const imageLabelBinaryJobDto: JobCvatDto = { + chainId: MOCK_CHAIN_ID, + data: storageDataMock, + labels: ['cat', 'dog'], + requesterDescription: MOCK_REQUESTER_DESCRIPTION, + minQuality: 0.95, + fundAmount: 10, + groundTruth: storageDataMock, + userGuide: MOCK_FILE_URL, + type: JobRequestType.IMAGE_POINTS, + }; + + await expect( + jobService.createJob( + userId, + JobRequestType.IMAGE_POINTS, + imageLabelBinaryJobDto, + ), + ).rejects.toThrowError(ErrorBucket.InvalidProvider); + + expect(paymentService.getUserBalance).toHaveBeenCalledWith(userId); + }); + + it('should throw an error for invalid region', async () => { + const userBalance = 25; + getUserBalanceMock.mockResolvedValue(userBalance); + + const storageDataMock: any = { + provider: StorageProviders.AWS, + region: 'test-region', + bucketName: 'bucket', + path: 'folder/test', + }; + + const imageLabelBinaryJobDto: JobCvatDto = { + chainId: MOCK_CHAIN_ID, + data: storageDataMock, + labels: ['cat', 'dog'], + requesterDescription: MOCK_REQUESTER_DESCRIPTION, + minQuality: 0.95, + fundAmount: 10, + groundTruth: storageDataMock, + userGuide: MOCK_FILE_URL, + type: JobRequestType.IMAGE_POINTS, + }; + + await expect( + jobService.createJob( + userId, + JobRequestType.IMAGE_POINTS, + imageLabelBinaryJobDto, + ), + ).rejects.toThrowError(ErrorBucket.InvalidRegion); + + expect(paymentService.getUserBalance).toHaveBeenCalledWith(userId); + }); + + it('should throw an error for empty region', async () => { + const userBalance = 25; + getUserBalanceMock.mockResolvedValue(userBalance); + + const storageDataMock: any = { + provider: StorageProviders.AWS, + bucketName: 'bucket', + path: 'folder/test', + }; + + const imageLabelBinaryJobDto: JobCvatDto = { + chainId: MOCK_CHAIN_ID, + data: storageDataMock, + labels: ['cat', 'dog'], + requesterDescription: MOCK_REQUESTER_DESCRIPTION, + minQuality: 0.95, + fundAmount: 10, + groundTruth: storageDataMock, + userGuide: MOCK_FILE_URL, + type: JobRequestType.IMAGE_POINTS, + }; + + await expect( + jobService.createJob( + userId, + JobRequestType.IMAGE_POINTS, + imageLabelBinaryJobDto, + ), + ).rejects.toThrowError(ErrorBucket.EmptyRegion); + + expect(paymentService.getUserBalance).toHaveBeenCalledWith(userId); + }); + + it('should throw an error for empty bucket', async () => { + const userBalance = 25; + getUserBalanceMock.mockResolvedValue(userBalance); + + const storageDataMock: any = { + provider: StorageProviders.AWS, + region: AWSRegions.EU_CENTRAL_1, + path: 'folder/test', + }; + + const imageLabelBinaryJobDto: JobCvatDto = { + chainId: MOCK_CHAIN_ID, + data: storageDataMock, + labels: ['cat', 'dog'], + requesterDescription: MOCK_REQUESTER_DESCRIPTION, + minQuality: 0.95, + fundAmount: 10, + groundTruth: storageDataMock, + userGuide: MOCK_FILE_URL, + type: JobRequestType.IMAGE_POINTS, + }; + + await expect( + jobService.createJob( + userId, + JobRequestType.IMAGE_POINTS, + imageLabelBinaryJobDto, + ), + ).rejects.toThrowError(ErrorBucket.EmptyBucket); + + expect(paymentService.getUserBalance).toHaveBeenCalledWith(userId); + }); + + it('should create a fortune job successfully on network selected from round robin logic', async () => { const fundAmount = imageLabelBinaryJobDto.fundAmount; const fee = (MOCK_JOB_LAUNCHER_FEE / 100) * fundAmount; @@ -1022,7 +1164,7 @@ describe('JobService', () => { const jobId = 123; const hCaptchaJobDto: JobCaptchaDto = { - dataUrl: MOCK_FILE_URL, + data: MOCK_STORAGE_DATA, accuracyTarget: 0.9, completionDate: new Date(), minRequests: 1, 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 ee96521cdd..d7217cfed2 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 @@ -108,6 +108,7 @@ import { StorageService } from '../storage/storage.service'; import { WebhookService } from '../webhook/webhook.service'; import stringify from 'json-stable-stringify'; import { Cron, CronExpression } from '@nestjs/schedule'; +import { generateBucketUrl } from '../../common/utils/storage'; @Injectable() export class JobService { @@ -130,9 +131,10 @@ export class JobService { requestType: JobRequestType, tokenFundAmount: number, ): Promise { + const data_url = generateBucketUrl(dto.data); return { data: { - data_url: dto.dataUrl, + data_url, }, annotation: { labels: dto.labels.map((item) => ({ name: item })), @@ -151,9 +153,9 @@ export class JobService { val_size: Number( this.configService.get(ConfigNames.CVAT_VAL_SIZE)!, ), - gt_url: dto.gtUrl, + gt_url: generateBucketUrl(dto.groundTruth), }, - job_bounty: await this.calculateJobBounty(dto.dataUrl, tokenFundAmount), + job_bounty: await this.calculateJobBounty(data_url, tokenFundAmount), }; } @@ -161,9 +163,9 @@ export class JobService { jobType: JobCaptchaShapeType, jobDto: JobCaptchaDto, ): Promise { - const objectsInBucket = await this.storageService.listObjectsInBucket( - jobDto.dataUrl, - ); + const dataUrl = generateBucketUrl(jobDto.data); + const objectsInBucket = + await this.storageService.listObjectsInBucket(dataUrl); const commonManifestProperties = { job_mode: JobCaptchaMode.BATCH, @@ -178,7 +180,7 @@ export class JobService { job_total_tasks: objectsInBucket.length, task_bid_price: jobDto.annotations.taskBidPrice, taskdata_uri: await this.generateAndUploadTaskData( - jobDto.dataUrl, + dataUrl, objectsInBucket, ), public_results: true, @@ -412,7 +414,7 @@ export class JobService { // hCaptcha dto = dto as JobCaptchaDto; const objectsInBucket = await this.storageService.listObjectsInBucket( - dto.dataUrl, + generateBucketUrl(dto.data), ); fundAmount = div( dto.annotations.taskBidPrice * objectsInBucket.length, @@ -446,7 +448,6 @@ export class JobService { if (requestType === JobRequestType.HCAPTCHA) { // hCaptcha dto = dto as JobCaptchaDto; - dto.dataUrl = dto.dataUrl.replace(/\/$/, ''); manifestOrigin = await this.createHCaptchaManifest( dto.annotations.typeOfJob, dto, @@ -466,7 +467,6 @@ export class JobService { } else { // CVAT dto = dto as JobCvatDto; - dto.dataUrl = dto.dataUrl.replace(/\/$/, ''); manifestOrigin = await this.createCvatManifest( dto, requestType, @@ -566,7 +566,6 @@ export class JobService { let recordingOracleConfigKey; let exchangeOracleConfigKey; - let trustedHandlers; if ( (manifest as FortuneManifestDto).requestType === JobRequestType.FORTUNE diff --git a/packages/apps/job-launcher/server/test/constants.ts b/packages/apps/job-launcher/server/test/constants.ts index eb4cebf81d..fad767305b 100644 --- a/packages/apps/job-launcher/server/test/constants.ts +++ b/packages/apps/job-launcher/server/test/constants.ts @@ -1,5 +1,6 @@ +import { AWSRegions, StorageProviders } from '../src/common/enums/storage'; import { JobRequestType } from '../src/common/enums/job'; -import { FortuneManifestDto } from '../src/modules/job/job.dto'; +import { FortuneManifestDto, StorageDataDto } from '../src/modules/job/job.dto'; export const MOCK_REQUESTER_TITLE = 'Mock job title'; export const MOCK_REQUESTER_DESCRIPTION = 'Mock job description'; @@ -117,3 +118,11 @@ export const MOCK_HCAPTCHA_IMAGE_LABEL = 'cat'; export const MOCK_HCAPTCHA_REPO_URI = 'http://recoracle:3000'; export const MOCK_HCAPTCHA_RO_URI = 'http://recoracle:3000'; export const MOCK_MAX_RETRY_COUNT = 5; +export const MOCK_STORAGE_DATA: StorageDataDto = { + provider: StorageProviders.AWS, + region: AWSRegions.EU_CENTRAL_1, + bucketName: 'bucket', + path: 'folder/test', +}; +export const MOCK_BUCKET_FILE = + 'https://bucket.s3.eu-central-1.amazonaws.com/folder/test'; From 269fd31e63d536c3c1b43eb40ebb8cb585131ecb Mon Sep 17 00:00:00 2001 From: Dzeranov Date: Fri, 22 Dec 2023 12:21:57 +0300 Subject: [PATCH 004/104] [Reputation Oracle] Separate streams for hashing and uploading a file (#1388) * Using two separate streams for calculating file hash and uploading to s3 bucket * `copyFileFromURLToBucket` testcase updated to match `copyFileFromURLToBucket` method --- .../src/modules/storage/storage.service.spec.ts | 13 +++++++++---- .../server/src/modules/storage/storage.service.ts | 14 +++++++++----- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/packages/apps/reputation-oracle/server/src/modules/storage/storage.service.spec.ts b/packages/apps/reputation-oracle/server/src/modules/storage/storage.service.spec.ts index c60f3a8783..11dc6fece0 100644 --- a/packages/apps/reputation-oracle/server/src/modules/storage/storage.service.spec.ts +++ b/packages/apps/reputation-oracle/server/src/modules/storage/storage.service.spec.ts @@ -222,10 +222,15 @@ describe('StorageService', () => { describe('copyFileFromURLToBucket', () => { it('should copy a file from a valid URL to a bucket', async () => { - const streamResponseData = new stream.Readable(); - streamResponseData.push(JSON.stringify(MOCK_MANIFEST)); - streamResponseData.push(null); - (axios.get as any).mockResolvedValueOnce({ data: streamResponseData }); + const hashStreamData = new stream.Readable(); + hashStreamData.push(JSON.stringify(MOCK_MANIFEST)); + hashStreamData.push(null); + const uploadStreamData = new stream.Readable(); + uploadStreamData.push(JSON.stringify(MOCK_MANIFEST)); + uploadStreamData.push(null); + (axios.get as any) + .mockResolvedValueOnce({ data: hashStreamData }) + .mockResolvedValueOnce({ data: uploadStreamData }); const uploadedFile = await storageService.copyFileFromURLToBucket(MOCK_FILE_URL); diff --git a/packages/apps/reputation-oracle/server/src/modules/storage/storage.service.ts b/packages/apps/reputation-oracle/server/src/modules/storage/storage.service.ts index 3683164e63..ac4b2245aa 100644 --- a/packages/apps/reputation-oracle/server/src/modules/storage/storage.service.ts +++ b/packages/apps/reputation-oracle/server/src/modules/storage/storage.service.ts @@ -95,14 +95,18 @@ export class StorageService { */ public async copyFileFromURLToBucket(url: string): Promise { try { - const { data } = await axios.get(url, { responseType: 'stream' }); - const stream = new PassThrough(); - data.pipe(stream); + const { data: hStream } = await axios.get(url, { + responseType: 'stream', + }); + const hash = await hashStream(hStream); - const hash = await hashStream(stream); const key = `s3${hash}.zip`; - await this.minioClient.putObject(this.s3Config.bucket, key, stream, { + // Creating a second readable stream for uploading a file to the bucket + const { data: uStream } = await axios.get(url, { + responseType: 'stream', + }); + await this.minioClient.putObject(this.s3Config.bucket, key, uStream, { 'Cache-Control': 'no-store', }); From 68930877afd2eb42386d744e20f86eeaec468193 Mon Sep 17 00:00:00 2001 From: m00n620 <50647994+m00n620@users.noreply.github.com> Date: Fri, 22 Dec 2023 06:01:14 -0500 Subject: [PATCH 005/104] [Dashboard UI] Add google tag manager (#1389) * add google tag manager * fix dependency error --- packages/apps/dashboard/ui/package.json | 11 +++++++++++ packages/apps/dashboard/ui/src/main.tsx | 5 +++++ yarn.lock | 10 ++++++++++ 3 files changed, 26 insertions(+) diff --git a/packages/apps/dashboard/ui/package.json b/packages/apps/dashboard/ui/package.json index 3ae3bad3ad..b8b2bbf8c2 100644 --- a/packages/apps/dashboard/ui/package.json +++ b/packages/apps/dashboard/ui/package.json @@ -24,6 +24,7 @@ "openpgp": "^5.10.2", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-gtm-module": "^2.0.11", "react-loading-skeleton": "^3.3.1", "react-redux": "^8.0.5", "react-router-dom": "^6.4.3", @@ -43,6 +44,7 @@ "@types/numeral": "^2.0.2", "@types/react": "^18.2.43", "@types/react-dom": "^18.2.14", + "@types/react-gtm-module": "^2.0.3", "@types/react-test-renderer": "^18.0.0", "@vitejs/plugin-react": "^3.1.0", "crypto-js": "^4.2.0", @@ -91,5 +93,14 @@ "prettier --write", "eslint --fix" ] + }, + "resolutions": { + "ejs": "^3.1.8", + "gluegun": "^5.0.0", + "mocha": "^10.0.0", + "node-fetch": "^2.6.7", + "node-forge": "^1.0.0", + "qrcode": "^1.5.0", + "semver": "^7.5.2" } } diff --git a/packages/apps/dashboard/ui/src/main.tsx b/packages/apps/dashboard/ui/src/main.tsx index 6f4b898a71..d5f49b63e4 100644 --- a/packages/apps/dashboard/ui/src/main.tsx +++ b/packages/apps/dashboard/ui/src/main.tsx @@ -1,5 +1,6 @@ import React from 'react'; import { createRoot } from 'react-dom/client'; +import TagManager from 'react-gtm-module'; import { Provider } from 'react-redux'; import './index.css'; import { App } from './components'; @@ -8,6 +9,10 @@ import { store } from './state'; import 'react-loading-skeleton/dist/skeleton.css'; +TagManager.initialize({ + gtmId: 'G-GQBK13YSRS', +}); + const container = document.getElementById('root'); const root = createRoot(container!); diff --git a/yarn.lock b/yarn.lock index b833eb47d8..b5dcc77be9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6888,6 +6888,11 @@ dependencies: "@types/react" "*" +"@types/react-gtm-module@^2.0.3": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/react-gtm-module/-/react-gtm-module-2.0.3.tgz#bc4b68abc7112a658100266948e24e88330d2600" + integrity sha512-fL2zKdDFN5LckSsVBXEhhm9M4tFTM9oHJfGcfZJzktQkzpOTGtDM8oXIP9d9UBDxO4xLNZhS22dlgRVv6wgK9w== + "@types/react-test-renderer@^18.0.0": version "18.0.7" resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-18.0.7.tgz#2cfe657adb3688cdf543995eceb2e062b5a68728" @@ -21234,6 +21239,11 @@ react-fast-compare@^3.1.1: resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.2.tgz#929a97a532304ce9fee4bcae44234f1ce2c21d49" integrity sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ== +react-gtm-module@^2.0.11: + version "2.0.11" + resolved "https://registry.yarnpkg.com/react-gtm-module/-/react-gtm-module-2.0.11.tgz#14484dac8257acd93614e347c32da9c5ac524206" + integrity sha512-8gyj4TTxeP7eEyc2QKawEuQoAZdjKvMY4pgWfycGmqGByhs17fR+zEBs0JUDq4US/l+vbTl+6zvUIx27iDo/Vw== + react-helmet@6.1.0, react-helmet@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/react-helmet/-/react-helmet-6.1.0.tgz#a750d5165cb13cf213e44747502652e794468726" From ecfb0bfb5740a70958b67df430713f7991ff5e44 Mon Sep 17 00:00:00 2001 From: portuu3 <61605646+portuu3@users.noreply.github.com> Date: Fri, 22 Dec 2023 12:25:44 +0100 Subject: [PATCH 006/104] list objects in bucket improvements (#1391) --- .../server/src/common/utils/storage.spec.ts | 146 ++++++++++++++++++ .../server/src/common/utils/storage.ts | 76 ++++++++- .../src/modules/job/job.service.spec.ts | 72 +++------ .../server/src/modules/job/job.service.ts | 39 ++--- .../modules/storage/storage.service.spec.ts | 2 +- .../src/modules/storage/storage.service.ts | 28 +--- 6 files changed, 261 insertions(+), 102 deletions(-) create mode 100644 packages/apps/job-launcher/server/src/common/utils/storage.spec.ts diff --git a/packages/apps/job-launcher/server/src/common/utils/storage.spec.ts b/packages/apps/job-launcher/server/src/common/utils/storage.spec.ts new file mode 100644 index 0000000000..a56762f9d5 --- /dev/null +++ b/packages/apps/job-launcher/server/src/common/utils/storage.spec.ts @@ -0,0 +1,146 @@ +import { AWSRegions, StorageProviders } from '../enums/storage'; +import { JobRequestType } from '../enums/job'; +import axios from 'axios'; +import { StorageDataDto } from '../../modules/job/job.dto'; +import { listObjectsInBucket } from './storage'; + +jest.mock('axios'); + +describe('Storage utils', () => { + describe('listObjectsInBucket', () => { + it('should return an array of object keys when given valid storageData and jobType', async () => { + const storageData: StorageDataDto = { + provider: StorageProviders.AWS, + region: AWSRegions.US_EAST_1, + bucketName: 'my-bucket', + path: 'my-folder', + }; + const jobType: JobRequestType = JobRequestType.IMAGE_POINTS; + const objects = ['object1', 'object2']; + const response = { + status: 200, + data: ` + + + object1 + + + object2 + + + `, + }; + axios.get = jest.fn().mockResolvedValueOnce(response); + + const result = await listObjectsInBucket(storageData, jobType); + + expect(result).toEqual(objects); + }); + + it('should handle pagination and return all object keys when NextContinuationToken exists', async () => { + const storageData: StorageDataDto = { + provider: StorageProviders.AWS, + region: AWSRegions.US_EAST_1, + bucketName: 'my-bucket', + path: 'my-folder', + }; + const jobType: JobRequestType = JobRequestType.IMAGE_POINTS; + const objects = Array.from({ length: 4 }, (_, i) => `object${i + 1}`); + const response1 = { + status: 200, + data: ` + + token1 + + object1 + + + object2 + + + `, + }; + const response2 = { + status: 200, + data: ` + + + object3 + + + object4 + + + `, + }; + axios.get = jest + .fn() + .mockResolvedValueOnce(response1) + .mockResolvedValueOnce(response2); + + const result = await listObjectsInBucket(storageData, jobType); + + expect(result).toEqual(objects); + }); + + it('should return an empty array when there are no objects in the bucket', async () => { + const storageData: StorageDataDto = { + provider: StorageProviders.AWS, + region: AWSRegions.US_EAST_1, + bucketName: 'my-bucket', + path: 'my-folder', + }; + const jobType: JobRequestType = JobRequestType.IMAGE_POINTS; + const response = { + status: 200, + data: ` + + + `, + }; + axios.get = jest.fn().mockResolvedValueOnce(response); + + const result = await listObjectsInBucket(storageData, jobType); + + expect(result).toEqual([]); + }); + + it('should reject with an error when the bucket does not exist', async () => { + const storageData: StorageDataDto = { + provider: StorageProviders.AWS, + region: AWSRegions.US_EAST_1, + bucketName: 'non-existent-bucket', + path: 'my-folder', + }; + const jobType: JobRequestType = JobRequestType.IMAGE_POINTS; + const response = { + status: 404, + data: 'Bucket not found', + }; + axios.get = jest.fn().mockResolvedValueOnce(response); + + await expect(listObjectsInBucket(storageData, jobType)).rejects.toEqual( + 'Failed to fetch bucket contents', + ); + }); + + it('should reject with an error when the bucket is not public', async () => { + const storageData: StorageDataDto = { + provider: StorageProviders.AWS, + region: AWSRegions.US_EAST_1, + bucketName: 'private-bucket', + path: 'my-folder', + }; + const jobType: JobRequestType = JobRequestType.IMAGE_POINTS; + const response = { + status: 403, + data: 'Access denied', + }; + axios.get = jest.fn().mockResolvedValueOnce(response); + + await expect(listObjectsInBucket(storageData, jobType)).rejects.toEqual( + 'Failed to fetch bucket contents', + ); + }); + }); +}); 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 af6fe231f6..eb58a61949 100644 --- a/packages/apps/job-launcher/server/src/common/utils/storage.ts +++ b/packages/apps/job-launcher/server/src/common/utils/storage.ts @@ -2,8 +2,22 @@ import { BadRequestException } from '@nestjs/common'; import { StorageDataDto } from '../../modules/job/job.dto'; import { AWSRegions, StorageProviders } from '../enums/storage'; import { ErrorBucket } from '../constants/errors'; +import { JobRequestType } from '../enums/job'; +import axios from 'axios'; +import { parseString } from 'xml2js'; -export function generateBucketUrl(storageData: StorageDataDto): string { +export function generateBucketUrl( + storageData: StorageDataDto, + jobType: JobRequestType, + addPath = true, +): string { + if ( + (jobType === JobRequestType.IMAGE_BOXES || + jobType === JobRequestType.IMAGE_POINTS) && + storageData.provider != StorageProviders.AWS + ) { + throw new BadRequestException(ErrorBucket.InvalidProvider); + } if (!storageData.bucketName) { throw new BadRequestException(ErrorBucket.EmptyBucket); } @@ -18,12 +32,14 @@ export function generateBucketUrl(storageData: StorageDataDto): string { return `https://${storageData.bucketName}.s3.${ storageData.region }.amazonaws.com${ - storageData.path ? `/${storageData.path.replace(/\/$/, '')}` : '' + storageData.path && addPath + ? `/${storageData.path.replace(/\/$/, '')}` + : '' + }`; + case StorageProviders.GCS: + return `https://${storageData.bucketName}.storage.googleapis.com${ + storageData.path && addPath ? `/${storageData.path}` : '' }`; - // case StorageProviders.GCS: - // return `https://${s3Data.bucketName}.storage.googleapis.com${ - // s3Data.path ? `/${s3Data.path}` : '' - // }`; default: throw new BadRequestException(ErrorBucket.InvalidProvider); } @@ -32,3 +48,51 @@ export function generateBucketUrl(storageData: StorageDataDto): string { function isRegion(value: string): value is AWSRegions { return Object.values(AWSRegions).includes(value as AWSRegions); } + +export async function listObjectsInBucket( + storageData: StorageDataDto, + jobType: JobRequestType, +): Promise { + return new Promise(async (resolve, reject) => { + try { + let objects: string[] = []; + const url = generateBucketUrl(storageData, jobType, false); + let nextContinuationToken: string | undefined; + + do { + const response = await axios.get( + `${url}?list-type=2${ + nextContinuationToken + ? `&continuation-token=${encodeURIComponent( + nextContinuationToken, + )}` + : '' + }${storageData.path ? `&prefix=${storageData.path}` : ''}`, + ); + + if (response.status === 200 && response.data) { + parseString(response.data, (err: any, result: any) => { + if (err) { + reject(err); + } + nextContinuationToken = result.ListBucketResult + .NextContinuationToken + ? result.ListBucketResult.NextContinuationToken[0] + : undefined; + + const objectKeys = result.ListBucketResult.Contents?.map( + (item: any) => item.Key, + ); + + objects = objects.concat(objectKeys?.flat()); + }); + } else { + reject(ErrorBucket.FailedToFetchBucketContents); + } + } while (nextContinuationToken); + resolve(objects); + } catch (err) { + reject(err); + } + }); +} 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 88e17784c0..902aa36636 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 @@ -155,6 +155,11 @@ jest.mock('../../common/utils', () => ({ }), })); +jest.mock('../../common/utils/storage', () => ({ + ...jest.requireActual('../../common/utils/storage'), + listObjectsInBucket: jest.fn().mockImplementation(() => MOCK_BUCKET_FILES), +})); + describe('JobService', () => { let jobService: JobService, jobRepository: JobRepository, @@ -394,9 +399,6 @@ describe('JobService', () => { .spyOn(paymentService, 'getUserBalance') .mockResolvedValue(userBalance); - storageService.listObjectsInBucket = jest - .fn() - .mockResolvedValue(MOCK_BUCKET_FILES); getUserBalanceMock.mockResolvedValue(userBalance); await expect( @@ -467,12 +469,7 @@ describe('JobService', () => { }); describe('createHCaptchaManifest', () => { - const listObjectsInBucket = ['example1.jpg', 'example2.jpg']; - beforeEach(() => { - jest - .spyOn(storageService, 'listObjectsInBucket') - .mockResolvedValueOnce(listObjectsInBucket); jest .spyOn(jobService, 'generateAndUploadTaskData') .mockResolvedValueOnce(MOCK_FILE_URL); @@ -494,7 +491,7 @@ describe('JobService', () => { typeOfJob: jobType, labelingPrompt: MOCK_REQUESTER_DESCRIPTION, groundTruths: MOCK_FILE_URL, - exampleImages: listObjectsInBucket, + exampleImages: MOCK_BUCKET_FILES, taskBidPrice: 0.5, }, completionDate: new Date(), @@ -519,7 +516,7 @@ describe('JobService', () => { requester_max_repeats: 10, requester_min_repeats: 1, requester_question: { en: MOCK_REQUESTER_DESCRIPTION }, - job_total_tasks: 2, + job_total_tasks: 6, task_bid_price: 0.5, taskdata_uri: MOCK_FILE_URL, public_results: true, @@ -529,7 +526,7 @@ describe('JobService', () => { request_type: JobCaptchaRequestType.IMAGE_LABEL_BINARY, groundtruth_uri: MOCK_FILE_URL, requester_restricted_answer_set: {}, - requester_question_example: listObjectsInBucket, + requester_question_example: MOCK_BUCKET_FILES, }); }); @@ -549,7 +546,7 @@ describe('JobService', () => { typeOfJob: jobType, labelingPrompt: MOCK_REQUESTER_DESCRIPTION, groundTruths: MOCK_FILE_URL, - exampleImages: listObjectsInBucket, + exampleImages: MOCK_BUCKET_FILES, taskBidPrice: 0.5, }, completionDate: new Date(), @@ -574,7 +571,7 @@ describe('JobService', () => { requester_max_repeats: 10, requester_min_repeats: 1, requester_question: { en: MOCK_REQUESTER_DESCRIPTION }, - job_total_tasks: 2, // Mocked length of objectsInBucket + job_total_tasks: 6, // Mocked length task_bid_price: 0.5, taskdata_uri: MOCK_FILE_URL, public_results: true, @@ -622,7 +619,7 @@ describe('JobService', () => { typeOfJob: jobType, labelingPrompt: MOCK_REQUESTER_DESCRIPTION, groundTruths: MOCK_FILE_URL, - exampleImages: listObjectsInBucket, + exampleImages: MOCK_BUCKET_FILES, taskBidPrice: 0.5, label: MOCK_HCAPTCHA_IMAGE_LABEL, }, @@ -656,7 +653,7 @@ describe('JobService', () => { requester_max_repeats: 10, requester_min_repeats: 1, requester_question: { en: MOCK_REQUESTER_DESCRIPTION }, - job_total_tasks: 2, // Mocked length of objectsInBucket + job_total_tasks: 6, // Mocked length task_bid_price: 0.5, taskdata_uri: MOCK_FILE_URL, public_results: true, @@ -668,7 +665,7 @@ describe('JobService', () => { requester_restricted_answer_set: { [MOCK_HCAPTCHA_IMAGE_LABEL]: { en: MOCK_HCAPTCHA_IMAGE_LABEL }, }, - requester_question_example: listObjectsInBucket, + requester_question_example: MOCK_BUCKET_FILES, }); }); @@ -696,7 +693,7 @@ describe('JobService', () => { typeOfJob: jobType, labelingPrompt: MOCK_REQUESTER_DESCRIPTION, groundTruths: MOCK_FILE_URL, - exampleImages: listObjectsInBucket, + exampleImages: MOCK_BUCKET_FILES, taskBidPrice: 0.5, label: MOCK_HCAPTCHA_IMAGE_LABEL, }, @@ -728,7 +725,7 @@ describe('JobService', () => { requester_max_repeats: 10, requester_min_repeats: 1, requester_question: { en: MOCK_REQUESTER_DESCRIPTION }, - job_total_tasks: 2, // Mocked length of objectsInBucket + job_total_tasks: 6, // Mocked length task_bid_price: 0.5, taskdata_uri: MOCK_FILE_URL, public_results: true, @@ -740,7 +737,7 @@ describe('JobService', () => { requester_restricted_answer_set: { [MOCK_HCAPTCHA_IMAGE_LABEL]: { en: MOCK_HCAPTCHA_IMAGE_LABEL }, }, - requester_question_example: listObjectsInBucket, + requester_question_example: MOCK_BUCKET_FILES, }); }); @@ -768,7 +765,7 @@ describe('JobService', () => { typeOfJob: jobType, labelingPrompt: MOCK_REQUESTER_DESCRIPTION, groundTruths: MOCK_FILE_URL, - exampleImages: listObjectsInBucket, + exampleImages: MOCK_BUCKET_FILES, taskBidPrice: 0.5, label: MOCK_HCAPTCHA_IMAGE_LABEL, }, @@ -800,7 +797,7 @@ describe('JobService', () => { requester_max_repeats: 10, requester_min_repeats: 1, requester_question: { en: MOCK_REQUESTER_DESCRIPTION }, - job_total_tasks: 2, // Mocked length of objectsInBucket + job_total_tasks: 6, // Mocked length task_bid_price: 0.5, taskdata_uri: MOCK_FILE_URL, public_results: true, @@ -812,7 +809,7 @@ describe('JobService', () => { requester_restricted_answer_set: { [MOCK_HCAPTCHA_IMAGE_LABEL]: { en: MOCK_HCAPTCHA_IMAGE_LABEL }, }, - requester_question_example: listObjectsInBucket, + requester_question_example: MOCK_BUCKET_FILES, }); }); @@ -832,7 +829,7 @@ describe('JobService', () => { typeOfJob: jobType, labelingPrompt: MOCK_REQUESTER_DESCRIPTION, groundTruths: MOCK_FILE_URL, - exampleImages: listObjectsInBucket, + exampleImages: MOCK_BUCKET_FILES, taskBidPrice: 0.5, }, completionDate: new Date(), @@ -847,14 +844,8 @@ describe('JobService', () => { describe('calculateJobBounty', () => { it('should calculate the job bounty correctly', async () => { - storageService.listObjectsInBucket = jest - .fn() - .mockResolvedValue(MOCK_BUCKET_FILES); const tokenFundAmount = 0.013997056833333334; - const result = await jobService['calculateJobBounty']( - MOCK_FILE_URL, - tokenFundAmount, - ); + const result = await jobService['calculateJobBounty'](6, tokenFundAmount); expect(result).toEqual('0.002332842805555555'); }); @@ -893,9 +884,6 @@ describe('JobService', () => { const userBalance = 25; getUserBalanceMock.mockResolvedValue(userBalance); - storageService.listObjectsInBucket = jest - .fn() - .mockResolvedValue(MOCK_BUCKET_FILES); const mockJobEntity: Partial = { id: jobId, @@ -1081,9 +1069,6 @@ describe('JobService', () => { const userBalance = 25; getUserBalanceMock.mockResolvedValue(userBalance); - storageService.listObjectsInBucket = jest - .fn() - .mockResolvedValue(MOCK_BUCKET_FILES); jest .spyOn(routingProtocolService, 'selectNetwork') @@ -1143,9 +1128,6 @@ describe('JobService', () => { const userBalance = 100; getUserBalanceMock.mockResolvedValue(userBalance); - storageService.listObjectsInBucket = jest - .fn() - .mockResolvedValue(MOCK_BUCKET_FILES); jest.spyOn(jobRepository, 'create').mockResolvedValue(undefined!); @@ -1217,9 +1199,6 @@ describe('JobService', () => { }; jobRepository.create = jest.fn().mockResolvedValue(mockJobEntity); - storageService.listObjectsInBucket = jest - .fn() - .mockResolvedValue(MOCK_BUCKET_FILES); await jobService.createJob( userId, @@ -1259,9 +1238,6 @@ describe('JobService', () => { const userBalance = 25; getUserBalanceMock.mockResolvedValue(userBalance); - storageService.listObjectsInBucket = jest - .fn() - .mockResolvedValue(MOCK_BUCKET_FILES); jest .spyOn(routingProtocolService, 'selectNetwork') @@ -1293,9 +1269,6 @@ describe('JobService', () => { .mockResolvedValue(userBalance); getUserBalanceMock.mockResolvedValue(userBalance); - storageService.listObjectsInBucket = jest - .fn() - .mockResolvedValue(MOCK_BUCKET_FILES); await expect( jobService.createJob(userId, JobRequestType.HCAPTCHA, hCaptchaJobDto), @@ -1306,9 +1279,6 @@ describe('JobService', () => { const userBalance = 100; getUserBalanceMock.mockResolvedValue(userBalance); - storageService.listObjectsInBucket = jest - .fn() - .mockResolvedValue(MOCK_BUCKET_FILES); jest.spyOn(jobRepository, 'create').mockResolvedValue(undefined!); 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 d7217cfed2..4fa086f54d 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 @@ -7,14 +7,12 @@ import { EscrowUtils, NETWORKS, StakingClient, - StorageClient, KVStoreKeys, Encryption, EncryptionUtils, } from '@human-protocol/sdk'; import { v4 as uuidv4 } from 'uuid'; import { - BadGatewayException, BadRequestException, ConflictException, Inject, @@ -29,7 +27,6 @@ import { BigNumber, ethers } from 'ethers'; import { IsNull, LessThanOrEqual, Not, QueryFailedError } from 'typeorm'; import { ConfigNames } from '../../common/config'; import { - ErrorBucket, ErrorEscrow, ErrorJob, ErrorPayment, @@ -74,6 +71,7 @@ import { JobCaptchaAdvancedDto, JobCaptchaDto, RestrictedAudience, + StorageDataDto, } from './job.dto'; import { JobEntity } from './job.entity'; import { JobRepository } from './job.repository'; @@ -108,7 +106,10 @@ import { StorageService } from '../storage/storage.service'; import { WebhookService } from '../webhook/webhook.service'; import stringify from 'json-stable-stringify'; import { Cron, CronExpression } from '@nestjs/schedule'; -import { generateBucketUrl } from '../../common/utils/storage'; +import { + generateBucketUrl, + listObjectsInBucket, +} from '../../common/utils/storage'; @Injectable() export class JobService { @@ -131,10 +132,11 @@ export class JobService { requestType: JobRequestType, tokenFundAmount: number, ): Promise { - const data_url = generateBucketUrl(dto.data); + const elementsCount = (await listObjectsInBucket(dto.data, requestType)) + .length; return { data: { - data_url, + data_url: generateBucketUrl(dto.data, requestType), }, annotation: { labels: dto.labels.map((item) => ({ name: item })), @@ -153,9 +155,9 @@ export class JobService { val_size: Number( this.configService.get(ConfigNames.CVAT_VAL_SIZE)!, ), - gt_url: generateBucketUrl(dto.groundTruth), + gt_url: generateBucketUrl(dto.groundTruth, requestType), }, - job_bounty: await this.calculateJobBounty(data_url, tokenFundAmount), + job_bounty: await this.calculateJobBounty(elementsCount, tokenFundAmount), }; } @@ -163,9 +165,11 @@ export class JobService { jobType: JobCaptchaShapeType, jobDto: JobCaptchaDto, ): Promise { - const dataUrl = generateBucketUrl(jobDto.data); - const objectsInBucket = - await this.storageService.listObjectsInBucket(dataUrl); + const dataUrl = generateBucketUrl(jobDto.data, JobRequestType.HCAPTCHA); + const objectsInBucket = await listObjectsInBucket( + jobDto.data, + JobRequestType.HCAPTCHA, + ); const commonManifestProperties = { job_mode: JobCaptchaMode.BATCH, @@ -413,8 +417,9 @@ export class JobService { if (requestType === JobRequestType.HCAPTCHA) { // hCaptcha dto = dto as JobCaptchaDto; - const objectsInBucket = await this.storageService.listObjectsInBucket( - generateBucketUrl(dto.data), + const objectsInBucket = await listObjectsInBucket( + dto.data, + JobRequestType.HCAPTCHA, ); fundAmount = div( dto.annotations.taskBidPrice * objectsInBucket.length, @@ -526,16 +531,12 @@ export class JobService { } public async calculateJobBounty( - endpointUrl: string, + elementsCount: number, fundAmount: number, ): Promise { - const totalImages = ( - await this.storageService.listObjectsInBucket(endpointUrl) - ).length; - const totalJobs = Math.ceil( div( - totalImages, + elementsCount, Number(this.configService.get(ConfigNames.CVAT_JOB_SIZE)!), ), ); diff --git a/packages/apps/job-launcher/server/src/modules/storage/storage.service.spec.ts b/packages/apps/job-launcher/server/src/modules/storage/storage.service.spec.ts index 807206f46c..7e677be357 100644 --- a/packages/apps/job-launcher/server/src/modules/storage/storage.service.spec.ts +++ b/packages/apps/job-launcher/server/src/modules/storage/storage.service.spec.ts @@ -41,7 +41,7 @@ jest.mock('minio', () => { jest.mock('axios'); -describe('Web3Service', () => { +describe('StorageService', () => { let storageService: StorageService; beforeAll(async () => { diff --git a/packages/apps/job-launcher/server/src/modules/storage/storage.service.ts b/packages/apps/job-launcher/server/src/modules/storage/storage.service.ts index ec5486e5ee..9f7628cd9f 100644 --- a/packages/apps/job-launcher/server/src/modules/storage/storage.service.ts +++ b/packages/apps/job-launcher/server/src/modules/storage/storage.service.ts @@ -8,6 +8,9 @@ import { parseString } from 'xml2js'; import stringify from 'json-stable-stringify'; import { ContentType, Extension } from '../../common/enums/storage'; import { UploadedFile } from '../../common/interfaces'; +import { generateBucketUrl } from '../../common/utils/storage'; +import { StorageDataDto } from '../job/job.dto'; +import { JobRequestType } from '../../common/enums/job'; @Injectable() export class StorageService { @@ -65,29 +68,4 @@ export class StorageService { throw new BadRequestException('File not uploaded'); } } - - public async listObjectsInBucket(bucketUrl: string): Promise { - return new Promise(async (resolve, reject) => { - try { - const response = await axios.get(bucketUrl); - - if (response.status === 200 && response.data) { - parseString(response.data, (err: any, result: any) => { - if (err) { - reject(err); - } - - const objectKeys = result.ListBucketResult.Contents.map( - (item: any) => item.Key, - ); - resolve(objectKeys.flat()); - }); - } else { - reject(ErrorBucket.FailedToFetchBucketContents); - } - } catch (err) { - reject(err); - } - }); - } } From ddd67e7d88cf3a68e6e33922bbfb60961a3552ed Mon Sep 17 00:00:00 2001 From: m00n620 <50647994+m00n620@users.noreply.github.com> Date: Fri, 22 Dec 2023 06:48:19 -0500 Subject: [PATCH 007/104] [Dashboard UI] fix ui bugs and tasks data on dashboard ui (#1385) * fix ui bugs and tasks data on dashboard ui * fix tooltip title --- .../components/HumanAppData/HumanAppDataView.tsx | 4 ++-- .../src/components/HumanAppData/views/Payments.tsx | 3 ++- .../ui/src/components/HumanAppData/views/Tasks.tsx | 13 +++++++------ .../components/HumanAppData/views/Transactions.tsx | 3 ++- .../src/components/HumanAppData/views/Workers.tsx | 3 ++- .../dashboard/ui/src/components/Token/TokenView.tsx | 4 ++-- .../apps/dashboard/ui/src/constants/tooltips.ts | 2 +- .../apps/dashboard/ui/src/hooks/useTaskStats.ts | 2 +- 8 files changed, 19 insertions(+), 15 deletions(-) diff --git a/packages/apps/dashboard/ui/src/components/HumanAppData/HumanAppDataView.tsx b/packages/apps/dashboard/ui/src/components/HumanAppData/HumanAppDataView.tsx index 95b8487f15..ce3a02e232 100644 --- a/packages/apps/dashboard/ui/src/components/HumanAppData/HumanAppDataView.tsx +++ b/packages/apps/dashboard/ui/src/components/HumanAppData/HumanAppDataView.tsx @@ -81,7 +81,7 @@ export const HumanAppDataView: FC = () => { const getTooltipTitle = (button: ViewButton) => { switch (button) { case ViewButton.Tasks: - return TOOLTIPS.TASKS; + return TOOLTIPS.SOLVED_TASKS; case ViewButton.Payments: return TOOLTIPS.PAYMENTS; case ViewButton.Transactions: @@ -136,7 +136,7 @@ export const HumanAppDataView: FC = () => { ))} - + {viewButton === ViewButton.Transactions && ( )} diff --git a/packages/apps/dashboard/ui/src/components/HumanAppData/views/Payments.tsx b/packages/apps/dashboard/ui/src/components/HumanAppData/views/Payments.tsx index e0944012b1..c0df7afd2e 100644 --- a/packages/apps/dashboard/ui/src/components/HumanAppData/views/Payments.tsx +++ b/packages/apps/dashboard/ui/src/components/HumanAppData/views/Payments.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { ChartContainer } from './Container'; import { TooltipIcon } from 'src/components/TooltipIcon'; +import { TOOLTIPS } from 'src/constants/tooltips'; export const PaymentsView = ({ isLoading, @@ -12,7 +13,7 @@ export const PaymentsView = ({ }) => { return ( - + ); }; diff --git a/packages/apps/dashboard/ui/src/components/HumanAppData/views/Tasks.tsx b/packages/apps/dashboard/ui/src/components/HumanAppData/views/Tasks.tsx index 1e3f5607a5..49bc9832e8 100644 --- a/packages/apps/dashboard/ui/src/components/HumanAppData/views/Tasks.tsx +++ b/packages/apps/dashboard/ui/src/components/HumanAppData/views/Tasks.tsx @@ -3,20 +3,21 @@ import React, { useMemo } from 'react'; import { ChartContainer } from './Container'; import { TooltipIcon } from 'src/components/TooltipIcon'; -import { useWorkerStats } from 'src/hooks/useWorkerStats'; +import { TOOLTIPS } from 'src/constants/tooltips'; +import { useTaskStats } from 'src/hooks/useTaskStats'; import { useChainId, useDays } from 'src/state/humanAppData/hooks'; export const TasksView = () => { const days = useDays(); const chainId = useChainId(); - const { data, isLoading } = useWorkerStats(); + const { data, isLoading } = useTaskStats(); const seriesData = useMemo(() => { if (data) { - const cumulativeDailyWorkersData = [...data.dailyWorkersData] + const cumulativeDailyTasksData = [...data.dailyTasksData] .map((d) => ({ date: d.timestamp, - value: Number(d.averageJobsSolved), + value: Number(d.tasksSolved), })) .reduce((acc, d) => { acc.push({ @@ -26,7 +27,7 @@ export const TasksView = () => { return acc; }, [] as any[]); - return cumulativeDailyWorkersData.slice(-days); + return cumulativeDailyTasksData.slice(-days); } return []; }, [data, days]); @@ -40,7 +41,7 @@ export const TasksView = () => { } title="Tasks" > - + ); }; diff --git a/packages/apps/dashboard/ui/src/components/HumanAppData/views/Transactions.tsx b/packages/apps/dashboard/ui/src/components/HumanAppData/views/Transactions.tsx index 83d15b1e54..482013d159 100644 --- a/packages/apps/dashboard/ui/src/components/HumanAppData/views/Transactions.tsx +++ b/packages/apps/dashboard/ui/src/components/HumanAppData/views/Transactions.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { ChartContainer } from './Container'; import { TooltipIcon } from 'src/components/TooltipIcon'; +import { TOOLTIPS } from 'src/constants/tooltips'; export const TransactionsView = ({ isLoading, @@ -12,7 +13,7 @@ export const TransactionsView = ({ }) => { return ( - + ); }; diff --git a/packages/apps/dashboard/ui/src/components/HumanAppData/views/Workers.tsx b/packages/apps/dashboard/ui/src/components/HumanAppData/views/Workers.tsx index f76d0c25fb..563b238572 100644 --- a/packages/apps/dashboard/ui/src/components/HumanAppData/views/Workers.tsx +++ b/packages/apps/dashboard/ui/src/components/HumanAppData/views/Workers.tsx @@ -3,6 +3,7 @@ import React, { useMemo } from 'react'; import { ChartContainer } from './Container'; import { TooltipIcon } from 'src/components/TooltipIcon'; +import { TOOLTIPS } from 'src/constants/tooltips'; import { useWorkerStats } from 'src/hooks/useWorkerStats'; import { useChainId, useDays } from 'src/state/humanAppData/hooks'; @@ -28,7 +29,7 @@ export const WorkersView = () => { title="Workers" isNotSupportedChain={chainId !== ChainId.POLYGON} > - + ); }; diff --git a/packages/apps/dashboard/ui/src/components/Token/TokenView.tsx b/packages/apps/dashboard/ui/src/components/Token/TokenView.tsx index b71396c3b5..b9f1242da3 100644 --- a/packages/apps/dashboard/ui/src/components/Token/TokenView.tsx +++ b/packages/apps/dashboard/ui/src/components/Token/TokenView.tsx @@ -117,9 +117,9 @@ export const TokenView: FC = () => { diff --git a/packages/apps/dashboard/ui/src/constants/tooltips.ts b/packages/apps/dashboard/ui/src/constants/tooltips.ts index c0f3cc76a3..dbda5c3324 100644 --- a/packages/apps/dashboard/ui/src/constants/tooltips.ts +++ b/packages/apps/dashboard/ui/src/constants/tooltips.ts @@ -4,7 +4,7 @@ export const TOOLTIPS = { PAYMENTS: 'All bulk paid out HMT amount', TRANSACTIONS: 'Daily on-chain HMT transaction count', SOLVED_TASKS: 'Number of tasks solved by workers', - TOTAL_TRANSFERS: 'Total HMT transfer count', + TOTAL_TRANSACTIONS: 'Total HMT transaction count', HOLDERS: 'Number of HMT holders, this number is all time number and not for the time period selected', TOTAL_SUPPLY: 'Total supply of HMT', diff --git a/packages/apps/dashboard/ui/src/hooks/useTaskStats.ts b/packages/apps/dashboard/ui/src/hooks/useTaskStats.ts index b32ae276ab..e9a7c386dc 100644 --- a/packages/apps/dashboard/ui/src/hooks/useTaskStats.ts +++ b/packages/apps/dashboard/ui/src/hooks/useTaskStats.ts @@ -7,7 +7,7 @@ import { useChainId } from 'src/state/humanAppData/hooks'; export function useTaskStats() { const chainId = useChainId(); return useSWR(`human-protocol-dashboard-task-stats-${chainId}`, async () => { - if (chainId !== ChainId.POLYGON) return null; + if (chainId !== ChainId.POLYGON && chainId !== ChainId.ALL) return null; const apiURL = import.meta.env.VITE_APP_ADMIN_API_URL; const to = dayjs().format('YYYY-MM-DD'); From 95a63af758019d89bed68db4b7f657d39295c1b8 Mon Sep 17 00:00:00 2001 From: m00n620 <50647994+m00n620@users.noreply.github.com> Date: Fri, 22 Dec 2023 08:29:31 -0500 Subject: [PATCH 008/104] use multiplier (#1392) --- .../dashboard/ui/src/components/HumanAppData/views/Tasks.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/apps/dashboard/ui/src/components/HumanAppData/views/Tasks.tsx b/packages/apps/dashboard/ui/src/components/HumanAppData/views/Tasks.tsx index 49bc9832e8..8a82032f95 100644 --- a/packages/apps/dashboard/ui/src/components/HumanAppData/views/Tasks.tsx +++ b/packages/apps/dashboard/ui/src/components/HumanAppData/views/Tasks.tsx @@ -14,10 +14,11 @@ export const TasksView = () => { const seriesData = useMemo(() => { if (data) { + const multiplier = 9; const cumulativeDailyTasksData = [...data.dailyTasksData] .map((d) => ({ date: d.timestamp, - value: Number(d.tasksSolved), + value: Number(d.tasksSolved) * multiplier, })) .reduce((acc, d) => { acc.push({ From c2d7bd8756ceb5656d5e2970405a45611a8b3e4b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jan 2024 19:02:31 +0800 Subject: [PATCH 009/104] Bump aws-sdk from 2.1521.0 to 2.1528.0 (#1427) Bumps [aws-sdk](https://github.com/aws/aws-sdk-js) from 2.1521.0 to 2.1528.0. - [Release notes](https://github.com/aws/aws-sdk-js/releases) - [Commits](https://github.com/aws/aws-sdk-js/compare/v2.1521.0...v2.1528.0) --- updated-dependencies: - dependency-name: aws-sdk dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- packages/sdk/typescript/human-protocol-sdk/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/sdk/typescript/human-protocol-sdk/package.json b/packages/sdk/typescript/human-protocol-sdk/package.json index 0c363f137b..02bcdcdc84 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": "*", - "aws-sdk": "^2.1255.0", + "aws-sdk": "^2.1528.0", "axios": "^1.4.0", "crypto": "^1.0.1", "ethers": "^5.7.2", diff --git a/yarn.lock b/yarn.lock index b5dcc77be9..c5d667f52b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8738,10 +8738,10 @@ available-typed-arrays@^1.0.5: resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== -aws-sdk@^2.1255.0: - version "2.1521.0" - resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1521.0.tgz#6cf522addc2770c8fa18b9d7eb85d1b0dea37383" - integrity sha512-OOlQ0MFVz54sQESJlJ85HdXTS0sBQdcg5jnwX+rx8C70qSvgDJKesvCYt4ZUQ7tRC4xRSMaJO5tE8xvftdGjtA== +aws-sdk@^2.1528.0: + version "2.1528.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1528.0.tgz#56dd74e3732fc8ba9dad652d1550816389fa540b" + integrity sha512-QyV8fTJJAqnBAbAGkRKgXfI/NvxAoeJHjEFVXDo77hv13cJZKOdBTe9dV56ztS4R1twDJxHibXdDi7IeBrag2w== dependencies: buffer "4.9.2" events "1.1.1" From 72d27dd741cd2a3b8e553b279019da54cc31792d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jan 2024 19:03:10 +0800 Subject: [PATCH 010/104] Bump @types/node from 20.10.4 to 20.10.6 (#1426) Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 20.10.4 to 20.10.6. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- .../fortune/exchange-oracle/server/package.json | 2 +- .../apps/hufi/exchange-oracle/server/package.json | 2 +- .../apps/hufi/job-launcher/server/package.json | 2 +- .../hufi/reputation-oracle/server/package.json | 2 +- packages/apps/job-launcher/server/package.json | 2 +- .../apps/reputation-oracle/server/package.json | 2 +- yarn.lock | 15 ++++----------- 8 files changed, 11 insertions(+), 18 deletions(-) diff --git a/package.json b/package.json index 708d892e16..be7ac1821d 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "@babel/preset-typescript": "^7.18.6", "@jest/globals": "^29.3.1", "@types/jest": "^29.2.3", - "@types/node": "^20.10.4", + "@types/node": "^20.10.6", "@typescript-eslint/eslint-plugin": "^5.43.0", "@typescript-eslint/parser": "^5.43.0", "@typescript-eslint/utils": "^6.10.0", diff --git a/packages/apps/fortune/exchange-oracle/server/package.json b/packages/apps/fortune/exchange-oracle/server/package.json index 8feae8bfcc..e09e1d2c7b 100644 --- a/packages/apps/fortune/exchange-oracle/server/package.json +++ b/packages/apps/fortune/exchange-oracle/server/package.json @@ -37,7 +37,7 @@ "@nestjs/testing": "^9.4.3", "@types/express": "^4.17.13", "@types/jest": "29.5.1", - "@types/node": "20.10.4", + "@types/node": "20.10.6", "@types/supertest": "^2.0.15", "@typescript-eslint/eslint-plugin": "^5.0.0", "@typescript-eslint/parser": "^5.0.0", diff --git a/packages/apps/hufi/exchange-oracle/server/package.json b/packages/apps/hufi/exchange-oracle/server/package.json index b695e12093..3d7a1bf121 100644 --- a/packages/apps/hufi/exchange-oracle/server/package.json +++ b/packages/apps/hufi/exchange-oracle/server/package.json @@ -38,7 +38,7 @@ "@nestjs/testing": "^9.4.3", "@types/express": "^4.17.13", "@types/jest": "29.5.1", - "@types/node": "20.10.4", + "@types/node": "20.10.6", "@types/supertest": "^2.0.15", "@typescript-eslint/eslint-plugin": "^5.0.0", "@typescript-eslint/parser": "^5.0.0", diff --git a/packages/apps/hufi/job-launcher/server/package.json b/packages/apps/hufi/job-launcher/server/package.json index 41f5efc79d..0649a7439d 100644 --- a/packages/apps/hufi/job-launcher/server/package.json +++ b/packages/apps/hufi/job-launcher/server/package.json @@ -70,7 +70,7 @@ "@types/bcrypt": "^5.0.2", "@types/express": "^4.17.13", "@types/jest": "29.5.1", - "@types/node": "20.10.4", + "@types/node": "20.10.6", "@types/supertest": "^2.0.15", "@types/zxcvbn": "4.4.1", "@typescript-eslint/eslint-plugin": "^5.0.0", diff --git a/packages/apps/hufi/reputation-oracle/server/package.json b/packages/apps/hufi/reputation-oracle/server/package.json index f0e561ebc4..2f0cb17cc5 100644 --- a/packages/apps/hufi/reputation-oracle/server/package.json +++ b/packages/apps/hufi/reputation-oracle/server/package.json @@ -67,7 +67,7 @@ "@nestjs/testing": "^9.4.3", "@types/express": "^4.17.13", "@types/jest": "29.5.1", - "@types/node": "20.10.4", + "@types/node": "20.10.6", "@types/supertest": "^2.0.15", "@types/zxcvbn": "4.4.1", "@types/cookie-parser": "^1.4.3", diff --git a/packages/apps/job-launcher/server/package.json b/packages/apps/job-launcher/server/package.json index 53c17327f7..1ffe0e368b 100644 --- a/packages/apps/job-launcher/server/package.json +++ b/packages/apps/job-launcher/server/package.json @@ -73,7 +73,7 @@ "@types/express": "^4.17.13", "@types/jest": "29.5.1", "@types/json-stable-stringify": "^1.0.36", - "@types/node": "20.10.4", + "@types/node": "20.10.6", "@types/supertest": "^2.0.15", "@types/zxcvbn": "4.4.1", "@types/xml2js": "0.4.13", diff --git a/packages/apps/reputation-oracle/server/package.json b/packages/apps/reputation-oracle/server/package.json index 4ded156f05..c0fdb95e85 100644 --- a/packages/apps/reputation-oracle/server/package.json +++ b/packages/apps/reputation-oracle/server/package.json @@ -70,7 +70,7 @@ "@types/express": "^4.17.13", "@types/express-session": "^1.17.10", "@types/jest": "29.5.1", - "@types/node": "20.10.4", + "@types/node": "20.10.6", "@types/supertest": "^2.0.15", "@types/uuid": "^9.0.6", "@types/zxcvbn": "4.4.1", diff --git a/yarn.lock b/yarn.lock index c5d667f52b..e25e550b81 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6781,10 +6781,10 @@ resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.34.tgz#10964ba0dee6ac4cd462e2795b6bebd407303433" integrity sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g== -"@types/node@*", "@types/node@>=12.0.0", "@types/node@>=13.7.0", "@types/node@>=8.1.0", "@types/node@^20.10.4": - version "20.10.5" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.10.5.tgz#47ad460b514096b7ed63a1dae26fad0914ed3ab2" - integrity sha512-nNPsNE65wjMxEKI93yOP+NPGGBJz/PoN3kZsVLee0XMiJolxSekEVD8wRwBUBqkwc7UWop0edW50yrCQW4CyRw== +"@types/node@*", "@types/node@20.10.6", "@types/node@>=12.0.0", "@types/node@>=13.7.0", "@types/node@>=8.1.0", "@types/node@^20.10.6": + version "20.10.6" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.10.6.tgz#a3ec84c22965802bf763da55b2394424f22bfbb5" + integrity sha512-Vac8H+NlRNNlAmDfGUP7b5h/KA+AtWIzuXy0E6OyP8f1tCLYAtPvKRRDJjAPqhpCb0t6U2j7/xqAuLEebW2kiw== dependencies: undici-types "~5.26.4" @@ -6793,13 +6793,6 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== -"@types/node@20.10.4": - version "20.10.4" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.10.4.tgz#b246fd84d55d5b1b71bf51f964bd514409347198" - integrity sha512-D08YG6rr8X90YB56tSIuBaddy/UXAA9RKJoFvrsnogAum/0pmjkgi4+2nx96A330FmioegBWmEYQ+syqCFaveg== - dependencies: - undici-types "~5.26.4" - "@types/node@^10.0.3": version "10.17.60" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b" From db62b05313ce3ddf5468ee93aecb8bafd78e4c37 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jan 2024 19:03:51 +0800 Subject: [PATCH 011/104] Bump react-redux from 8.1.3 to 9.0.4 (#1397) Bumps [react-redux](https://github.com/reduxjs/react-redux) from 8.1.3 to 9.0.4. - [Release notes](https://github.com/reduxjs/react-redux/releases) - [Changelog](https://github.com/reduxjs/react-redux/blob/master/CHANGELOG.md) - [Commits](https://github.com/reduxjs/react-redux/compare/v8.1.3...v9.0.4) --- updated-dependencies: - dependency-name: react-redux dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- packages/apps/dashboard/ui/package.json | 2 +- packages/apps/job-launcher/client/package.json | 2 +- yarn.lock | 12 ++++-------- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/packages/apps/dashboard/ui/package.json b/packages/apps/dashboard/ui/package.json index b8b2bbf8c2..cfec2c902e 100644 --- a/packages/apps/dashboard/ui/package.json +++ b/packages/apps/dashboard/ui/package.json @@ -26,7 +26,7 @@ "react-dom": "^18.2.0", "react-gtm-module": "^2.0.11", "react-loading-skeleton": "^3.3.1", - "react-redux": "^8.0.5", + "react-redux": "^9.0.4", "react-router-dom": "^6.4.3", "react-test-renderer": "^18.2.0", "recharts": "2.9.0", diff --git a/packages/apps/job-launcher/client/package.json b/packages/apps/job-launcher/client/package.json index dc752a2c78..c1876fcae5 100644 --- a/packages/apps/job-launcher/client/package.json +++ b/packages/apps/job-launcher/client/package.json @@ -20,7 +20,7 @@ "jwt-decode": "^4.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-redux": "^8.1.1", + "react-redux": "^9.0.4", "react-router-dom": "^6.14.1", "recharts": "^2.7.2", "swr": "^2.2.4", diff --git a/yarn.lock b/yarn.lock index e25e550b81..0def0a3f8a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -21316,16 +21316,12 @@ react-redux@8.1.1: react-is "^18.0.0" use-sync-external-store "^1.0.0" -react-redux@^8.0.5, react-redux@^8.1.1: - version "8.1.3" - resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-8.1.3.tgz#4fdc0462d0acb59af29a13c27ffef6f49ab4df46" - integrity sha512-n0ZrutD7DaX/j9VscF+uTALI3oUPa/pO4Z3soOBIjuRn/FzVu6aehhysxZCLi6y7duMf52WNZGMl7CtuK5EnRw== +react-redux@^9.0.4: + version "9.0.4" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-9.0.4.tgz#6892d465f086507a517d4b53eb589876e6bc8344" + integrity sha512-9J1xh8sWO0vYq2sCxK2My/QO7MzUMRi3rpiILP/+tDr8krBHixC6JMM17fMK88+Oh3e4Ae6/sHIhNBgkUivwFA== dependencies: - "@babel/runtime" "^7.12.1" - "@types/hoist-non-react-statics" "^3.3.1" "@types/use-sync-external-store" "^0.0.3" - hoist-non-react-statics "^3.3.2" - react-is "^18.0.0" use-sync-external-store "^1.0.0" react-refresh@0.14.0, react-refresh@^0.14.0: From 08f9b21483608ecea17cc1ffa9dc7b608d861a03 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jan 2024 19:04:12 +0800 Subject: [PATCH 012/104] Bump @mui/x-date-pickers from 6.18.5 to 6.18.6 (#1395) Bumps [@mui/x-date-pickers](https://github.com/mui/mui-x/tree/HEAD/packages/x-date-pickers) from 6.18.5 to 6.18.6. - [Release notes](https://github.com/mui/mui-x/releases) - [Changelog](https://github.com/mui/mui-x/blob/next/CHANGELOG.md) - [Commits](https://github.com/mui/mui-x/commits/v6.18.6/packages/x-date-pickers) --- updated-dependencies: - dependency-name: "@mui/x-date-pickers" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- packages/apps/job-launcher/client/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/apps/job-launcher/client/package.json b/packages/apps/job-launcher/client/package.json index c1876fcae5..116ac9a2a7 100644 --- a/packages/apps/job-launcher/client/package.json +++ b/packages/apps/job-launcher/client/package.json @@ -9,7 +9,7 @@ "@human-protocol/sdk": "*", "@mui/lab": "^5.0.0-alpha.141", "@mui/material": "^5.14.14", - "@mui/x-date-pickers": "^6.18.0", + "@mui/x-date-pickers": "^6.18.6", "@reduxjs/toolkit": "^1.9.5", "@stripe/react-stripe-js": "^2.4.0", "@stripe/stripe-js": "^2.2.1", diff --git a/yarn.lock b/yarn.lock index 0def0a3f8a..4943f789dd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3627,10 +3627,10 @@ prop-types "^15.8.1" react-is "^18.2.0" -"@mui/x-date-pickers@^6.18.0": - version "6.18.5" - resolved "https://registry.yarnpkg.com/@mui/x-date-pickers/-/x-date-pickers-6.18.5.tgz#a2a70ce2926f0acb8ee414b546ba1f7daf399c21" - integrity sha512-3jImYIWP2Xgi608yzm/Sz1v0MTjQQYdZSQOEIi3dWBfSAU9B06KXDpqlXfRSpTV+rtsnfYIIyiWlz6Ltk7sUWw== +"@mui/x-date-pickers@^6.18.6": + version "6.18.6" + resolved "https://registry.yarnpkg.com/@mui/x-date-pickers/-/x-date-pickers-6.18.6.tgz#416e0b83dd2774547e3c864c89bedf2f4ca3e05a" + integrity sha512-pqOrGPUDVY/1xXrM1hofqwgquno/SB9aG9CVS1m2Rs8hKF1VWRC+jYlEa1Qk08xKmvkia5g7NsdV/BBb+tHUZw== dependencies: "@babel/runtime" "^7.23.2" "@mui/base" "^5.0.0-beta.22" From 6615c6e34eec915797ac8a033804c76479e70a03 Mon Sep 17 00:00:00 2001 From: Eric Lee <98655210+leric7@users.noreply.github.com> Date: Sun, 7 Jan 2024 18:39:51 +0800 Subject: [PATCH 013/104] [Job Launcher] Split cron jobs to launch and lock while running. (#1383) * split cron job and lock * fix retries and failure handling * fix cron job service * remove created at property * remove unnecessary CREATING status * fix job status and cron job check * refactor remaining cron jobs * fix cron job type enum and running check --------- Co-authored-by: portuu3 --- .../job-launcher/server/src/app.module.ts | 2 + .../server/src/common/constants/errors.ts | 9 + .../server/src/common/constants/index.ts | 2 +- .../server/src/common/enums/cron-job.ts | 7 + .../server/src/common/enums/job.ts | 4 +- .../server/src/common/interfaces/cron-job.ts | 8 + .../server/src/database/database.module.ts | 2 + ...updateJobStatusAndWebhookOracleTypeEnum.ts | 89 +++ .../1703266371801-addCronJobTable.ts | 43 ++ .../src/modules/cron-job/cron-job.entity.ts | 22 + .../src/modules/cron-job/cron-job.module.ts | 14 + .../modules/cron-job/cron-job.repository.ts | 34 + .../modules/cron-job/cron-job.service.spec.ts | 172 +++++ .../src/modules/cron-job/cron-job.service.ts | 55 ++ .../server/src/modules/job/job.controller.ts | 50 +- .../src/modules/job/job.service.spec.ts | 679 ++++++++++++++++-- .../server/src/modules/job/job.service.ts | 371 +++++++--- .../modules/webhook/webhook.service.spec.ts | 150 +++- .../src/modules/webhook/webhook.service.ts | 76 +- 19 files changed, 1546 insertions(+), 243 deletions(-) create mode 100644 packages/apps/job-launcher/server/src/common/enums/cron-job.ts create mode 100644 packages/apps/job-launcher/server/src/common/interfaces/cron-job.ts create mode 100644 packages/apps/job-launcher/server/src/database/migrations/1703041584469-updateJobStatusAndWebhookOracleTypeEnum.ts create mode 100644 packages/apps/job-launcher/server/src/database/migrations/1703266371801-addCronJobTable.ts create mode 100644 packages/apps/job-launcher/server/src/modules/cron-job/cron-job.entity.ts create mode 100644 packages/apps/job-launcher/server/src/modules/cron-job/cron-job.module.ts create mode 100644 packages/apps/job-launcher/server/src/modules/cron-job/cron-job.repository.ts create mode 100644 packages/apps/job-launcher/server/src/modules/cron-job/cron-job.service.spec.ts create mode 100644 packages/apps/job-launcher/server/src/modules/cron-job/cron-job.service.ts diff --git a/packages/apps/job-launcher/server/src/app.module.ts b/packages/apps/job-launcher/server/src/app.module.ts index 11c84b0006..058816fd11 100644 --- a/packages/apps/job-launcher/server/src/app.module.ts +++ b/packages/apps/job-launcher/server/src/app.module.ts @@ -16,6 +16,7 @@ import { envValidator } from './common/config'; import { ServeStaticModule } from '@nestjs/serve-static'; import { join } from 'path'; import { StorageModule } from './modules/storage/storage.module'; +import { CronJobModule } from './modules/cron-job/cron-job.module'; @Module({ providers: [ @@ -51,6 +52,7 @@ import { StorageModule } from './modules/storage/storage.module'; 'node_modules/swagger-ui-dist', ), }), + CronJobModule, ], controllers: [AppController], }) diff --git a/packages/apps/job-launcher/server/src/common/constants/errors.ts b/packages/apps/job-launcher/server/src/common/constants/errors.ts index 6ffa0ef1f0..cbaae92364 100644 --- a/packages/apps/job-launcher/server/src/common/constants/errors.ts +++ b/packages/apps/job-launcher/server/src/common/constants/errors.ts @@ -140,3 +140,12 @@ export enum ErrorSignature { export enum ErrorPostgres { NumericFieldOverflow = 'Numeric field overflow', } + +/** + * Represents error messages associated with a cron job. + */ +export enum ErrorCronJob { + NotCreated = 'Cron job has not been created', + NotCompleted = 'Cron job is not completed', + Completed = 'Cron job is completed', +} diff --git a/packages/apps/job-launcher/server/src/common/constants/index.ts b/packages/apps/job-launcher/server/src/common/constants/index.ts index 9822c8ae9b..837806e2b2 100644 --- a/packages/apps/job-launcher/server/src/common/constants/index.ts +++ b/packages/apps/job-launcher/server/src/common/constants/index.ts @@ -5,7 +5,7 @@ export const SERVICE_NAME = 'Job Launcher'; export const NS = 'hmt'; export const COINGECKO_API_URL = 'https://api.coingecko.com/api/v3/simple/price'; -export const JOB_RETRIES_COUNT_THRESHOLD = 3; +export const DEFAULT_MAX_RETRY_COUNT = 3; export const TX_CONFIRMATION_TRESHOLD = 1; export const JWT_PREFIX = 'bearer '; diff --git a/packages/apps/job-launcher/server/src/common/enums/cron-job.ts b/packages/apps/job-launcher/server/src/common/enums/cron-job.ts new file mode 100644 index 0000000000..196e7fa00f --- /dev/null +++ b/packages/apps/job-launcher/server/src/common/enums/cron-job.ts @@ -0,0 +1,7 @@ +export enum CronJobType { + CreateEscrow = 'create-escrow', + SetupEscrow = 'setup-escrow', + FundEscrow = 'fund-escrow', + CancelEscrow = 'cancel-escrow', + ProcessPendingWebhook = 'process-pending-webhook', +} 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 559ccba4bf..4775e03b79 100644 --- a/packages/apps/job-launcher/server/src/common/enums/job.ts +++ b/packages/apps/job-launcher/server/src/common/enums/job.ts @@ -1,8 +1,8 @@ export enum JobStatus { PENDING = 'PENDING', PAID = 'PAID', - LAUNCHING = 'LAUNCHING', - FUNDING = 'FUNDING', + CREATED = 'CREATED', + SET_UP = 'SET_UP', LAUNCHED = 'LAUNCHED', COMPLETED = 'COMPLETED', FAILED = 'FAILED', diff --git a/packages/apps/job-launcher/server/src/common/interfaces/cron-job.ts b/packages/apps/job-launcher/server/src/common/interfaces/cron-job.ts new file mode 100644 index 0000000000..1335866ba9 --- /dev/null +++ b/packages/apps/job-launcher/server/src/common/interfaces/cron-job.ts @@ -0,0 +1,8 @@ +import { CronJobType } from '../enums/cron-job'; +import { IBase } from './base'; + +export interface ICronJob extends IBase { + cronJobType: CronJobType; + startedAt: Date; + completedAt?: Date | null; +} diff --git a/packages/apps/job-launcher/server/src/database/database.module.ts b/packages/apps/job-launcher/server/src/database/database.module.ts index 82d571cf79..eacbf888bf 100644 --- a/packages/apps/job-launcher/server/src/database/database.module.ts +++ b/packages/apps/job-launcher/server/src/database/database.module.ts @@ -15,6 +15,7 @@ import { ConfigNames } from '../common/config'; import { ApiKeyEntity } from '../modules/auth/apikey.entity'; import { WebhookEntity } from '../modules/webhook/webhook.entity'; import { LoggerOptions } from 'typeorm'; +import { CronJobEntity } from '../modules/cron-job/cron-job.entity'; @Module({ imports: [ @@ -44,6 +45,7 @@ import { LoggerOptions } from 'typeorm'; JobEntity, PaymentEntity, WebhookEntity, + CronJobEntity, ], // We are using migrations, synchronize should be set to false. synchronize: false, diff --git a/packages/apps/job-launcher/server/src/database/migrations/1703041584469-updateJobStatusAndWebhookOracleTypeEnum.ts b/packages/apps/job-launcher/server/src/database/migrations/1703041584469-updateJobStatusAndWebhookOracleTypeEnum.ts new file mode 100644 index 0000000000..6a879a1fc7 --- /dev/null +++ b/packages/apps/job-launcher/server/src/database/migrations/1703041584469-updateJobStatusAndWebhookOracleTypeEnum.ts @@ -0,0 +1,89 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class UpdateJobStatusAndWebhookOracleTypeEnum1703041584469 + implements MigrationInterface +{ + name = 'UpdateJobStatusAndWebhookOracleTypeEnum1703041584469'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TYPE "hmt"."jobs_status_enum" + RENAME TO "jobs_status_enum_old" + `); + await queryRunner.query(` + CREATE TYPE "hmt"."jobs_status_enum" AS ENUM( + 'PENDING', + 'PAID', + 'CREATED', + 'SET_UP', + 'LAUNCHED', + 'COMPLETED', + 'FAILED', + 'TO_CANCEL', + 'CANCELED' + ) + `); + await queryRunner.query(` + ALTER TABLE "hmt"."jobs" + ALTER COLUMN "status" TYPE "hmt"."jobs_status_enum" USING "status"::"text"::"hmt"."jobs_status_enum" + `); + await queryRunner.query(` + DROP TYPE "hmt"."jobs_status_enum_old" + `); + await queryRunner.query(` + ALTER TYPE "hmt"."webhook_oracle_type_enum" + RENAME TO "webhook_oracle_type_enum_old" + `); + await queryRunner.query(` + CREATE TYPE "hmt"."webhook_oracle_type_enum" AS ENUM('fortune', 'cvat', 'hcaptcha') + `); + await queryRunner.query(` + ALTER TABLE "hmt"."webhook" + ALTER COLUMN "oracle_type" TYPE "hmt"."webhook_oracle_type_enum" USING "oracle_type"::"text"::"hmt"."webhook_oracle_type_enum" + `); + await queryRunner.query(` + DROP TYPE "hmt"."webhook_oracle_type_enum_old" + `); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + CREATE TYPE "hmt"."webhook_oracle_type_enum_old" AS ENUM('fortune', 'cvat') + `); + await queryRunner.query(` + ALTER TABLE "hmt"."webhook" + ALTER COLUMN "oracle_type" TYPE "hmt"."webhook_oracle_type_enum_old" USING "oracle_type"::"text"::"hmt"."webhook_oracle_type_enum_old" + `); + await queryRunner.query(` + DROP TYPE "hmt"."webhook_oracle_type_enum" + `); + await queryRunner.query(` + ALTER TYPE "hmt"."webhook_oracle_type_enum_old" + RENAME TO "webhook_oracle_type_enum" + `); + await queryRunner.query(` + CREATE TYPE "hmt"."jobs_status_enum_old" AS ENUM( + 'PENDING', + 'PAID', + 'LAUNCHING', + 'FUNDING', + 'LAUNCHED', + 'COMPLETED', + 'FAILED', + 'TO_CANCEL', + 'CANCELED' + ) + `); + await queryRunner.query(` + ALTER TABLE "hmt"."jobs" + ALTER COLUMN "status" TYPE "hmt"."jobs_status_enum_old" USING "status"::"text"::"hmt"."jobs_status_enum_old" + `); + await queryRunner.query(` + DROP TYPE "hmt"."jobs_status_enum" + `); + await queryRunner.query(` + ALTER TYPE "hmt"."jobs_status_enum_old" + RENAME TO "jobs_status_enum" + `); + } +} diff --git a/packages/apps/job-launcher/server/src/database/migrations/1703266371801-addCronJobTable.ts b/packages/apps/job-launcher/server/src/database/migrations/1703266371801-addCronJobTable.ts new file mode 100644 index 0000000000..041167ff72 --- /dev/null +++ b/packages/apps/job-launcher/server/src/database/migrations/1703266371801-addCronJobTable.ts @@ -0,0 +1,43 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddCronJobTable1703266371801 implements MigrationInterface { + name = 'AddCronJobTable1703266371801'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + CREATE TYPE "hmt"."cron-jobs_cron_job_type_enum" AS ENUM( + 'create-escrow', + 'setup-escrow', + 'fund-escrow', + 'cancel-escrow', + 'process-pending-webhook' + ) + `); + await queryRunner.query(` + CREATE TABLE "hmt"."cron-jobs" ( + "id" SERIAL NOT NULL, + "created_at" TIMESTAMP WITH TIME ZONE NOT NULL, + "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL, + "cron_job_type" "hmt"."cron-jobs_cron_job_type_enum" NOT NULL, + "started_at" TIMESTAMP WITH TIME ZONE NOT NULL, + "completed_at" TIMESTAMP WITH TIME ZONE, + CONSTRAINT "PK_268498ac0d3e7472960fb0faeb1" PRIMARY KEY ("id") + ) + `); + await queryRunner.query(` + CREATE UNIQUE INDEX "IDX_0dafd70b737e71d21490ad0126" ON "hmt"."cron-jobs" ("cron_job_type") + `); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + DROP INDEX "hmt"."IDX_0dafd70b737e71d21490ad0126" + `); + await queryRunner.query(` + DROP TABLE "hmt"."cron-jobs" + `); + await queryRunner.query(` + DROP TYPE "hmt"."cron-jobs_cron_job_type_enum" + `); + } +} diff --git a/packages/apps/job-launcher/server/src/modules/cron-job/cron-job.entity.ts b/packages/apps/job-launcher/server/src/modules/cron-job/cron-job.entity.ts new file mode 100644 index 0000000000..707e0d53be --- /dev/null +++ b/packages/apps/job-launcher/server/src/modules/cron-job/cron-job.entity.ts @@ -0,0 +1,22 @@ +import { Column, Entity, Index } from 'typeorm'; + +import { NS } from '../../common/constants'; +import { BaseEntity } from '../../database/base.entity'; +import { ICronJob } from '../../common/interfaces/cron-job'; +import { CronJobType } from '../../common/enums/cron-job'; + +@Entity({ schema: NS, name: 'cron-jobs' }) +@Index(['cronJobType'], { unique: true }) +export class CronJobEntity extends BaseEntity implements ICronJob { + @Column({ + type: 'enum', + enum: CronJobType, + }) + public cronJobType: CronJobType; + + @Column({ type: 'timestamptz' }) + public startedAt: Date; + + @Column({ type: 'timestamptz', nullable: true }) + public completedAt?: Date | null; +} diff --git a/packages/apps/job-launcher/server/src/modules/cron-job/cron-job.module.ts b/packages/apps/job-launcher/server/src/modules/cron-job/cron-job.module.ts new file mode 100644 index 0000000000..554e49ca8e --- /dev/null +++ b/packages/apps/job-launcher/server/src/modules/cron-job/cron-job.module.ts @@ -0,0 +1,14 @@ +import { Global, Module } from '@nestjs/common'; + +import { CronJobService } from './cron-job.service'; +import { CronJobRepository } from './cron-job.repository'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { CronJobEntity } from './cron-job.entity'; + +@Global() +@Module({ + imports: [TypeOrmModule.forFeature([CronJobEntity])], + providers: [CronJobService, CronJobRepository], + exports: [CronJobService], +}) +export class CronJobModule {} diff --git a/packages/apps/job-launcher/server/src/modules/cron-job/cron-job.repository.ts b/packages/apps/job-launcher/server/src/modules/cron-job/cron-job.repository.ts new file mode 100644 index 0000000000..0beec5b731 --- /dev/null +++ b/packages/apps/job-launcher/server/src/modules/cron-job/cron-job.repository.ts @@ -0,0 +1,34 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { FindOneOptions, FindOptionsWhere, Repository } from 'typeorm'; +import { CronJobEntity } from './cron-job.entity'; +import { CronJobType } from '../../common/enums/cron-job'; + +@Injectable() +export class CronJobRepository { + private readonly logger = new Logger(CronJobRepository.name); + + constructor( + @InjectRepository(CronJobEntity) + private readonly cronJobEntityRepository: Repository, + ) {} + + public async create(cronJobType: CronJobType): Promise { + return this.cronJobEntityRepository + .create({ + cronJobType, + startedAt: new Date(), + }) + .save(); + } + + public async findOne( + where: FindOptionsWhere, + options?: FindOneOptions, + ): Promise { + return this.cronJobEntityRepository.findOne({ + where, + ...options, + }); + } +} 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 new file mode 100644 index 0000000000..6f099a9e2f --- /dev/null +++ b/packages/apps/job-launcher/server/src/modules/cron-job/cron-job.service.spec.ts @@ -0,0 +1,172 @@ +import { Test, TestingModule } from '@nestjs/testing'; + +import { CronJobType } from '../../common/enums/cron-job'; + +import { CronJobService } from './cron-job.service'; +import { CronJobRepository } from './cron-job.repository'; +import { CronJobEntity } from './cron-job.entity'; +import { createMock } from '@golevelup/ts-jest'; + +describe('CronJobService', () => { + let service: CronJobService; + let repository: CronJobRepository; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + CronJobService, + { + provide: CronJobRepository, + useValue: createMock(), + }, + ], + }).compile(); + + service = module.get(CronJobService); + repository = module.get(CronJobRepository); + }); + + describe('startCronJob', () => { + it('should create a cron job if not exists', async () => { + const cronJobType = CronJobType.CreateEscrow; + + const cronJobEntity = new CronJobEntity(); + cronJobEntity.cronJobType = cronJobType; + cronJobEntity.startedAt = new Date(); + + jest.spyOn(repository, 'findOne').mockResolvedValue(null); + + const createSpy = jest + .spyOn(repository, 'create') + .mockResolvedValue(cronJobEntity); + + const result = await service.startCronJob(cronJobType); + + expect(createSpy).toHaveBeenCalledWith(cronJobType); + expect(result).toEqual(cronJobEntity); + }); + + it('should start a cron job if exists', async () => { + const cronJobType = CronJobType.CreateEscrow; + const cronJobEntity = new CronJobEntity(); + cronJobEntity.cronJobType = cronJobType; + cronJobEntity.startedAt = new Date(); + cronJobEntity.completedAt = new Date(); + + const mockDate = new Date(2023, 12, 23); + jest.useFakeTimers(); + jest.setSystemTime(mockDate); + + const findOneSpy = jest + .spyOn(repository, 'findOne') + .mockResolvedValue(cronJobEntity); + + const saveSpy = jest + .spyOn(cronJobEntity, 'save') + .mockResolvedValue(cronJobEntity); + + const result = await service.startCronJob(cronJobType); + + expect(findOneSpy).toHaveBeenCalledWith({ + cronJobType, + }); + expect(saveSpy).toHaveBeenCalled(); + cronJobEntity.startedAt = mockDate; + expect(result).toEqual(cronJobEntity); + + jest.useRealTimers(); + }); + }); + + describe('isCronJobRunning', () => { + it('should return false if no cron job is running', async () => { + const cronJobType = CronJobType.CreateEscrow; + const cronJobEntity = new CronJobEntity(); + cronJobEntity.cronJobType = cronJobType; + + const findOneSpy = jest + .spyOn(repository, 'findOne') + .mockResolvedValue(null); + + const result = await service.isCronJobRunning(cronJobType); + + expect(findOneSpy).toHaveBeenCalledWith({ + cronJobType, + }); + expect(result).toEqual(false); + }); + + it('should return false if last cron job is completed', async () => { + const cronJobType = CronJobType.CreateEscrow; + const cronJobEntity = new CronJobEntity(); + cronJobEntity.cronJobType = cronJobType; + cronJobEntity.completedAt = new Date(); + + const findOneSpy = jest + .spyOn(repository, 'findOne') + .mockResolvedValue(cronJobEntity); + + const result = await service.isCronJobRunning(cronJobType); + + expect(findOneSpy).toHaveBeenCalledWith({ + cronJobType, + }); + expect(result).toEqual(false); + }); + + it('should return true if last cron job is not completed', async () => { + const cronJobType = CronJobType.CreateEscrow; + const cronJobEntity = new CronJobEntity(); + cronJobEntity.cronJobType = cronJobType; + + const findOneSpy = jest + .spyOn(repository, 'findOne') + .mockResolvedValue(cronJobEntity); + + const result = await service.isCronJobRunning(cronJobType); + expect(findOneSpy).toHaveBeenCalledWith({ + cronJobType, + }); + expect(result).toEqual(true); + }); + }); + + describe('completeCronJob', () => { + it('should complete a cron job', async () => { + const cronJobType = CronJobType.CreateEscrow; + const cronJobEntity = new CronJobEntity(); + cronJobEntity.cronJobType = cronJobType; + + const mockDate = new Date(2023, 12, 23); + + jest.useFakeTimers(); + jest.setSystemTime(mockDate); + + const saveSpy = jest + .spyOn(cronJobEntity, 'save') + .mockResolvedValue(cronJobEntity); + + const result = await service.completeCronJob(cronJobEntity); + + expect(saveSpy).toHaveBeenCalled(); + expect(cronJobEntity.completedAt).toEqual(mockDate); + expect(result).toEqual(cronJobEntity); + + jest.useRealTimers(); + }); + + it('should throw an error if cron job is already completed', async () => { + const cronJobType = CronJobType.CreateEscrow; + const cronJobEntity = new CronJobEntity(); + cronJobEntity.cronJobType = cronJobType; + cronJobEntity.completedAt = new Date(); + + const saveSpy = jest + .spyOn(cronJobEntity, 'save') + .mockResolvedValue(cronJobEntity); + + await expect(service.completeCronJob(cronJobEntity)).rejects.toThrow(); + expect(saveSpy).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/packages/apps/job-launcher/server/src/modules/cron-job/cron-job.service.ts b/packages/apps/job-launcher/server/src/modules/cron-job/cron-job.service.ts new file mode 100644 index 0000000000..302b382e5d --- /dev/null +++ b/packages/apps/job-launcher/server/src/modules/cron-job/cron-job.service.ts @@ -0,0 +1,55 @@ +import { BadRequestException, Injectable, Logger } from '@nestjs/common'; + +import { CronJobType } from '../../common/enums/cron-job'; +import { ErrorCronJob } from '../../common/constants/errors'; + +import { CronJobEntity } from './cron-job.entity'; +import { CronJobRepository } from './cron-job.repository'; + +@Injectable() +export class CronJobService { + private readonly logger = new Logger(CronJobService.name); + + constructor(private readonly cronJobRepository: CronJobRepository) {} + + public async startCronJob(cronJobType: CronJobType): Promise { + let cronJob = await this.cronJobRepository.findOne({ + cronJobType, + }); + + if (!cronJob) { + cronJob = await this.cronJobRepository.create(cronJobType); + } else { + cronJob.startedAt = new Date(); + cronJob.completedAt = null; + await cronJob.save(); + } + + return cronJob; + } + + public async isCronJobRunning(cronJobType: CronJobType): Promise { + const lastCronJob = await this.cronJobRepository.findOne({ + cronJobType, + }); + + if (!lastCronJob || lastCronJob.completedAt) { + return false; + } + + this.logger.log('Previous cron job is not completed yet'); + return true; + } + + public async completeCronJob( + cronJobEntity: CronJobEntity, + ): Promise { + if (cronJobEntity.completedAt) { + this.logger.error(ErrorCronJob.Completed, CronJobService.name); + throw new BadRequestException(ErrorCronJob.Completed); + } + + cronJobEntity.completedAt = new Date(); + return cronJobEntity.save(); + } +} diff --git a/packages/apps/job-launcher/server/src/modules/job/job.controller.ts b/packages/apps/job-launcher/server/src/modules/job/job.controller.ts index b7fb34dd97..1041373988 100644 --- a/packages/apps/job-launcher/server/src/modules/job/job.controller.ts +++ b/packages/apps/job-launcher/server/src/modules/job/job.controller.ts @@ -186,21 +186,59 @@ export class JobController { } @ApiOperation({ - summary: 'Launch a cron job', - description: 'Endpoint to launch a cron job.', + summary: 'Launch the cron job to create escrows', + description: 'Endpoint to launch the cron job to create escrows.', }) @Public() @ApiResponse({ status: 200, - description: 'Cron job launched successfully.', + description: 'Cron job to create escrows launched successfully.', + }) + @ApiResponse({ + status: 404, + description: 'Not Found. Could not find the requested content.', + }) + @Post('/cron/create-escrow') + public async launchCreateEscrowCronJob(): Promise { + await this.jobService.createEscrowCronJob(); + return; + } + + @ApiOperation({ + summary: 'Launch the cron job to setup escrows', + description: 'Endpoint to launch the cron job to setup escrows.', + }) + @Public() + @ApiResponse({ + status: 200, + description: 'Cron job to setup escrows launched successfully.', + }) + @ApiResponse({ + status: 404, + description: 'Not Found. Could not find the requested content.', + }) + @Post('/cron/setup-escrow') + public async launchSetupEscrowCronJob(): Promise { + await this.jobService.setupEscrowCronJob(); + return; + } + + @ApiOperation({ + summary: 'Launch the cron job to fund escrows', + description: 'Endpoint to launch the cron job to fund escrows.', + }) + @Public() + @ApiResponse({ + status: 200, + description: 'Cron job to fund escrows launched successfully.', }) @ApiResponse({ status: 404, description: 'Not Found. Could not find the requested content.', }) - @Get('/cron/launch') - public async launchCronJob(): Promise { - await this.jobService.launchCronJob(); + @Post('/cron/fund-escrow') + public async launchFundEscrowCronJob(): Promise { + await this.jobService.fundEscrowCronJob(); return; } 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 902aa36636..4e90495ea8 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 @@ -76,6 +76,7 @@ import { MOCK_HCAPTCHA_RO_URI, MOCK_FILE_KEY, MOCK_BUCKET_FILE, + MOCK_MAX_RETRY_COUNT, } from '../../../test/constants'; import { PaymentService } from '../payment/payment.service'; import { Web3Service } from '../web3/web3.service'; @@ -87,6 +88,7 @@ import { JobDetailsDto, StorageDataDto, JobCaptchaDto, + CvatManifestDto, } from './job.dto'; import { JobEntity } from './job.entity'; import { JobRepository } from './job.repository'; @@ -96,7 +98,6 @@ import { div, mul } from '../../common/utils/decimal'; import { PaymentRepository } from '../payment/payment.repository'; import { RoutingProtocolService } from './routing-protocol.service'; import { EventType } from '../../common/enums/webhook'; -import { PaymentEntity } from '../payment/payment.entity'; import { BigNumber, ethers } from 'ethers'; import { HMToken__factory } from '@human-protocol/core/typechain-types'; import { StorageService } from '../storage/storage.service'; @@ -107,6 +108,10 @@ import { HCAPTCHA_NOT_PRESENTED_LABEL, } from '../../common/constants'; import { WebhookService } from '../webhook/webhook.service'; +import { CronJobService } from '../cron-job/cron-job.service'; +import { CronJobEntity } from '../cron-job/cron-job.entity'; +import { CronJobType } from '../../common/enums/cron-job'; +import { DeepPartial } from 'typeorm'; import { AWSRegions, StorageProviders } from '../../common/enums/storage'; const rate = 1.5; @@ -116,6 +121,7 @@ jest.mock('@human-protocol/sdk', () => ({ build: jest.fn().mockImplementation(() => ({ createEscrow: jest.fn().mockResolvedValue(MOCK_ADDRESS), setup: jest.fn().mockResolvedValue(null), + fund: jest.fn().mockResolvedValue(null), })), }, EscrowUtils: { @@ -169,7 +175,8 @@ describe('JobService', () => { routingProtocolService: RoutingProtocolService, web3Service: Web3Service, storageService: StorageService, - webhookService: WebhookService; + webhookService: WebhookService, + cronJobService: CronJobService; const signerMock = { address: MOCK_ADDRESS, @@ -228,6 +235,8 @@ describe('JobService', () => { return MOCK_HCAPTCHA_REPO_URI; case 'HCAPTCHA_RECORDING_ORACLE_URI': return MOCK_HCAPTCHA_RO_URI; + case 'MAX_RETRY_COUNT': + return MOCK_MAX_RETRY_COUNT; } }), }; @@ -256,6 +265,10 @@ describe('JobService', () => { provide: RoutingProtocolService, useValue: createMock(), }, + { + provide: CronJobService, + useValue: createMock(), + }, ], }).compile(); @@ -268,6 +281,7 @@ describe('JobService', () => { web3Service = moduleRef.get(Web3Service); storageService = moduleRef.get(StorageService); webhookService = moduleRef.get(WebhookService); + cronJobService = moduleRef.get(CronJobService); storageService.uploadFile = jest.fn().mockResolvedValue({ url: MOCK_FILE_URL, @@ -1288,10 +1302,10 @@ describe('JobService', () => { }); }); - describe('launchJob', () => { + describe('createEscrow', () => { const chainId = ChainId.LOCALHOST; - it('should launch a job successfully', async () => { + it('should create an escrow successfully', async () => { const fundAmount = 10; const fee = (MOCK_JOB_LAUNCHER_FEE / 100) * fundAmount; @@ -1301,7 +1315,54 @@ describe('JobService', () => { manifestHash: MOCK_FILE_HASH, fee, fundAmount, - status: JobStatus.PENDING, + status: JobStatus.PAID, + save: jest.fn().mockResolvedValue(true), + userId: 1, + }; + + const jobEntityResult = await jobService.createEscrow( + mockJobEntity as JobEntity, + ); + + mockJobEntity.status = JobStatus.CREATED; + mockJobEntity.escrowAddress = MOCK_ADDRESS; + expect(jobEntityResult).toMatchObject(mockJobEntity); + expect(mockJobEntity.save).toHaveBeenCalled(); + }); + + it('should handle error during job creation', async () => { + (EscrowClient.build as any).mockImplementationOnce(() => ({ + createEscrow: jest.fn().mockRejectedValue(new Error()), + })); + + const mockJobEntity: Partial = { + chainId: 1, + manifestUrl: MOCK_FILE_URL, + manifestHash: MOCK_FILE_HASH, + status: JobStatus.PAID, + save: jest.fn().mockResolvedValue(true), + }; + + await expect( + jobService.createEscrow(mockJobEntity as JobEntity), + ).rejects.toThrow(); + }); + }); + + describe('setupEscrow', () => { + const chainId = ChainId.LOCALHOST; + + it('should setup escrow and update the status to funding', async () => { + const fundAmount = 10; + const fee = (MOCK_JOB_LAUNCHER_FEE / 100) * fundAmount; + + const mockJobEntity: Partial = { + chainId, + manifestUrl: MOCK_FILE_URL, + manifestHash: MOCK_FILE_HASH, + fee, + fundAmount, + status: JobStatus.CREATED, save: jest.fn().mockResolvedValue(true), userId: 1, }; @@ -1316,42 +1377,78 @@ describe('JobService', () => { storageService.download = jest.fn().mockReturnValue(manifest); - const jobEntityResult = await jobService.launchJob( + const jobEntityResult = await jobService.setupEscrow( mockJobEntity as JobEntity, ); - mockJobEntity.escrowAddress = MOCK_ADDRESS; - expect(jobEntityResult).toMatchObject(mockJobEntity); + mockJobEntity.status = JobStatus.SET_UP; expect(mockJobEntity.save).toHaveBeenCalled(); + expect(jobEntityResult).toMatchObject(mockJobEntity); }); - it('should handle error during job launch', async () => { - (EscrowClient.build as any).mockImplementation(() => ({ - createAndSetupEscrow: jest.fn().mockRejectedValue(new Error()), + it('should validate manifest before setup', async () => { + const fundAmount = 10; + const fee = (MOCK_JOB_LAUNCHER_FEE / 100) * fundAmount; + + const mockJobEntity: Partial = { + chainId, + manifestUrl: MOCK_FILE_URL, + manifestHash: MOCK_FILE_HASH, + fee, + fundAmount, + status: JobStatus.CREATED, + save: jest.fn().mockResolvedValue(true), + userId: 1, + }; + + const manifest: Partial = { + submissionsRequired: 10, + requesterTitle: MOCK_REQUESTER_TITLE, + requesterDescription: MOCK_REQUESTER_DESCRIPTION, + requestType: JobRequestType.FORTUNE, + }; + + storageService.download = jest.fn().mockReturnValue(manifest); + + await expect( + jobService.setupEscrow(mockJobEntity as JobEntity), + ).rejects.toThrow(); + }); + + it('should handle error during job setup', async () => { + (EscrowClient.build as any).mockImplementationOnce(() => ({ + setup: jest.fn().mockRejectedValue(new Error()), })); const mockJobEntity: Partial = { chainId: 1, manifestUrl: MOCK_FILE_URL, manifestHash: MOCK_FILE_HASH, - status: JobStatus.PENDING, + status: JobStatus.CREATED, save: jest.fn().mockResolvedValue(true), }; + const fundAmount = 10; + const manifest: FortuneManifestDto = { + submissionsRequired: 10, + requesterTitle: MOCK_REQUESTER_TITLE, + requesterDescription: MOCK_REQUESTER_DESCRIPTION, + fundAmount, + requestType: JobRequestType.FORTUNE, + }; + + storageService.download = jest.fn().mockReturnValue(manifest); + await expect( - jobService.launchJob(mockJobEntity as JobEntity), + jobService.setupEscrow(mockJobEntity as JobEntity), ).rejects.toThrow(); }); }); - describe('fundJob', () => { + describe('fundEscrow', () => { const chainId = ChainId.LOCALHOST; - it('should fund a job successfully', async () => { - (EscrowClient.build as any).mockImplementation(() => ({ - fund: jest.fn(), - })); - + it('should fund escrow and update the status to launched', async () => { const fundAmount = 10; const fee = (MOCK_JOB_LAUNCHER_FEE / 100) * fundAmount; @@ -1361,22 +1458,22 @@ describe('JobService', () => { manifestHash: MOCK_FILE_HASH, fee, fundAmount, - status: JobStatus.PAID, + status: JobStatus.SET_UP, save: jest.fn().mockResolvedValue(true), + userId: 1, }; - const jobEntityResult = await jobService.fundJob( + const jobEntityResult = await jobService.fundEscrow( mockJobEntity as JobEntity, ); - mockJobEntity.escrowAddress = MOCK_ADDRESS; - expect(jobEntityResult).toMatchObject(mockJobEntity); - expect(jobEntityResult.status).toBe(JobStatus.LAUNCHED); + mockJobEntity.status = JobStatus.LAUNCHED; expect(mockJobEntity.save).toHaveBeenCalled(); + expect(jobEntityResult).toMatchObject(mockJobEntity); }); it('should handle error during job fund', async () => { - (EscrowClient.build as any).mockImplementation(() => ({ + (EscrowClient.build as any).mockImplementationOnce(() => ({ fund: jest.fn().mockRejectedValue(new Error()), })); @@ -1384,12 +1481,12 @@ describe('JobService', () => { chainId: 1, manifestUrl: MOCK_FILE_URL, manifestHash: MOCK_FILE_HASH, - status: JobStatus.PENDING, + status: JobStatus.SET_UP, save: jest.fn().mockResolvedValue(true), }; await expect( - jobService.fundJob(mockJobEntity as JobEntity), + jobService.fundEscrow(mockJobEntity as JobEntity), ).rejects.toThrow(); }); }); @@ -1472,58 +1569,405 @@ describe('JobService', () => { }); }); - describe('cancelCronJob', () => { - let findOneJobMock: any, - findOnePaymentMock: any, - jobEntityMock: Partial, - paymentEntityMock: Partial; + describe('createEscrowCronJob', () => { + let createEscrowMock: any; + let cronJobEntityMock: Partial; + let jobEntityMock1: Partial, jobEntityMock2: Partial; beforeEach(() => { - jobEntityMock = { - status: JobStatus.TO_CANCEL, + cronJobEntityMock = { + cronJobType: CronJobType.CreateEscrow, + startedAt: new Date(), + }; + + jobEntityMock1 = { + status: JobStatus.PAID, fundAmount: 100, userId: 1, id: 1, manifestUrl: MOCK_FILE_URL, + manifestHash: MOCK_FILE_HASH, escrowAddress: MOCK_ADDRESS, chainId: ChainId.LOCALHOST, + retriesCount: 1, save: jest.fn(), }; - paymentEntityMock = { - chainId: 1, - jobId: jobEntityMock.id, - status: PaymentStatus.SUCCEEDED, + jobEntityMock2 = { + status: JobStatus.PAID, + fundAmount: 100, + userId: 1, + id: 1, + manifestUrl: MOCK_FILE_URL, + manifestHash: MOCK_FILE_HASH, + escrowAddress: MOCK_ADDRESS, + chainId: ChainId.LOCALHOST, + retriesCount: 1, save: jest.fn(), }; - findOneJobMock = jest.spyOn(jobRepository, 'findOne'); - findOnePaymentMock = jest.spyOn(paymentRepository, 'findOne'); - findOnePaymentMock.mockResolvedValueOnce( - paymentEntityMock as PaymentEntity, + + jest + .spyOn(jobRepository, 'find') + .mockResolvedValue([jobEntityMock1 as any, jobEntityMock2 as any]); + + createEscrowMock = jest.spyOn(jobService, 'createEscrow'); + createEscrowMock.mockResolvedValue(true); + + jest.spyOn(cronJobService, 'isCronJobRunning').mockResolvedValue(false); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('should not run if the cron job is already running', async () => { + jest + .spyOn(cronJobService, 'isCronJobRunning') + .mockResolvedValueOnce(true); + + await jobService.createEscrowCronJob(); + + expect(createEscrowMock).not.toHaveBeenCalled(); + }); + + it('should create cron job entity on database to lock', async () => { + jest + .spyOn(cronJobService, 'startCronJob') + .mockResolvedValueOnce(cronJobEntityMock as any); + + await jobService.createEscrowCronJob(); + + expect(cronJobService.startCronJob).toHaveBeenCalledWith( + CronJobType.CreateEscrow, ); }); + it('should run createEscrow for all of the jobs with status PAID', async () => { + await jobService.createEscrowCronJob(); + + expect(createEscrowMock).toHaveBeenCalledTimes(2); + }); + + it('should increase retriesCount by 1, if the job creation fails', async () => { + createEscrowMock.mockRejectedValueOnce(new Error('creation failed')); + + await jobService.createEscrowCronJob(); + + expect(createEscrowMock).toHaveBeenCalledTimes(2); + expect(jobEntityMock1.retriesCount).toBe(2); + expect(jobEntityMock2.retriesCount).toBe(1); + }); + + it('should mark job as failed if the job creation fails more than max retries count', async () => { + createEscrowMock.mockRejectedValueOnce(new Error('creation failed')); + jobEntityMock1.retriesCount = MOCK_MAX_RETRY_COUNT; + + await jobService.createEscrowCronJob(); + + expect(createEscrowMock).toHaveBeenCalledTimes(2); + expect(jobEntityMock1.status).toBe(JobStatus.FAILED); + expect(jobEntityMock2.status).toBe(JobStatus.PAID); + }); + + it('should complete the cron job entity on database to unlock', async () => { + jest + .spyOn(cronJobService, 'completeCronJob') + .mockResolvedValueOnce(cronJobEntityMock as any); + + await jobService.createEscrowCronJob(); + + expect(cronJobService.completeCronJob).toHaveBeenCalledWith( + CronJobType.CreateEscrow, + ); + }); + }); + + describe('setupEscrowCronJob', () => { + let setupEscrowMock: any; + let cronJobEntityMock: Partial; + let jobEntityMock1: Partial, jobEntityMock2: Partial; + + beforeEach(() => { + cronJobEntityMock = { + cronJobType: CronJobType.SetupEscrow, + createdAt: new Date(), + }; + + jobEntityMock1 = { + status: JobStatus.CREATED, + fundAmount: 100, + userId: 1, + id: 1, + manifestUrl: MOCK_FILE_URL, + manifestHash: MOCK_FILE_HASH, + escrowAddress: MOCK_ADDRESS, + chainId: ChainId.LOCALHOST, + retriesCount: 1, + save: jest.fn(), + }; + + jobEntityMock2 = { + status: JobStatus.CREATED, + fundAmount: 100, + userId: 1, + id: 1, + manifestUrl: MOCK_FILE_URL, + manifestHash: MOCK_FILE_HASH, + escrowAddress: MOCK_ADDRESS, + chainId: ChainId.LOCALHOST, + retriesCount: 1, + save: jest.fn(), + }; + + jest + .spyOn(jobRepository, 'find') + .mockResolvedValue([jobEntityMock1 as any, jobEntityMock2 as any]); + + setupEscrowMock = jest.spyOn(jobService, 'setupEscrow'); + setupEscrowMock.mockResolvedValue(true); + + jest.spyOn(cronJobService, 'isCronJobRunning').mockResolvedValue(false); + }); + afterEach(() => { - jest.clearAllMocks(); + jest.restoreAllMocks(); }); - it('should return undefined when no job entity is found', async () => { - findOneJobMock.mockResolvedValue(null); + it('should not run if the cron job is already running', async () => { + jest + .spyOn(cronJobService, 'isCronJobRunning') + .mockResolvedValueOnce(true); - const result = await jobService.cancelCronJob(); + await jobService.setupEscrowCronJob(); + + expect(setupEscrowMock).not.toHaveBeenCalled(); + }); + + it('should create cron job entity on database to lock', async () => { + jest + .spyOn(cronJobService, 'startCronJob') + .mockResolvedValueOnce(cronJobEntityMock as any); - expect(result).toBeUndefined(); + await jobService.setupEscrowCronJob(); + + expect(cronJobService.startCronJob).toHaveBeenCalledWith( + CronJobType.SetupEscrow, + ); + }); + + it('should run setupEscrow for all of the jobs with status LAUNCHING', async () => { + await jobService.setupEscrowCronJob(); + + expect(setupEscrowMock).toHaveBeenCalledTimes(2); + }); + + it('should increase retriesCount by 1, if the job setup fails', async () => { + setupEscrowMock.mockRejectedValueOnce(new Error('setup failed')); + + await jobService.setupEscrowCronJob(); + + expect(setupEscrowMock).toHaveBeenCalledTimes(2); + expect(jobEntityMock1.retriesCount).toBe(2); + expect(jobEntityMock2.retriesCount).toBe(1); }); - it('should return true when the job is successfully canceled', async () => { - findOneJobMock.mockResolvedValue(jobEntityMock as any); + it('should mark job as failed if the job setup fails more than max retries count', async () => { + setupEscrowMock.mockRejectedValueOnce(new Error('setup failed')); + jobEntityMock1.retriesCount = MOCK_MAX_RETRY_COUNT; + + await jobService.setupEscrowCronJob(); + + expect(setupEscrowMock).toHaveBeenCalledTimes(2); + expect(jobEntityMock1.status).toBe(JobStatus.FAILED); + expect(jobEntityMock2.status).toBe(JobStatus.CREATED); + }); + it('should complete the cron job entity on database to unlock', async () => { jest - .spyOn(jobService, 'processEscrowCancellation') - .mockResolvedValueOnce({ - txHash: MOCK_TRANSACTION_HASH, - amountRefunded: BigNumber.from(1), - }); + .spyOn(cronJobService, 'completeCronJob') + .mockResolvedValueOnce(cronJobEntityMock as any); + + await jobService.setupEscrowCronJob(); + + expect(cronJobService.completeCronJob).toHaveBeenCalledWith( + CronJobType.SetupEscrow, + ); + }); + }); + + describe('fundEscrowCronJob', () => { + let fundEscrowMock: any; + let cronJobEntityMock: Partial; + let jobEntityMock1: Partial, jobEntityMock2: Partial; + let createWebhookMock: any; + + beforeEach(() => { + cronJobEntityMock = { + cronJobType: CronJobType.FundEscrow, + createdAt: new Date(), + }; + + jobEntityMock1 = { + status: JobStatus.SET_UP, + fundAmount: 100, + userId: 1, + id: 1, + manifestUrl: MOCK_FILE_URL, + manifestHash: MOCK_FILE_HASH, + escrowAddress: MOCK_ADDRESS, + chainId: ChainId.LOCALHOST, + retriesCount: 1, + save: jest.fn(), + }; + + jobEntityMock2 = { + status: JobStatus.SET_UP, + fundAmount: 100, + userId: 1, + id: 1, + manifestUrl: MOCK_FILE_URL, + manifestHash: MOCK_FILE_HASH, + escrowAddress: MOCK_ADDRESS, + chainId: ChainId.LOCALHOST, + retriesCount: 1, + save: jest.fn(), + }; + + jest + .spyOn(jobRepository, 'find') + .mockResolvedValue([jobEntityMock1 as any, jobEntityMock2 as any]); + + fundEscrowMock = jest.spyOn(jobService, 'fundEscrow'); + fundEscrowMock.mockResolvedValue(true); + + jest.spyOn(cronJobService, 'isCronJobRunning').mockResolvedValue(false); + + createWebhookMock = jest.spyOn(webhookService, 'createWebhook'); + + const cvatManifestMock: DeepPartial = { + data: { + data_url: MOCK_FILE_URL, + }, + annotation: { + type: JobRequestType.IMAGE_POINTS, + }, + }; + jest + .spyOn(storageService, 'download') + .mockResolvedValue(cvatManifestMock); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('should not run if the cron job is already running', async () => { + jest + .spyOn(cronJobService, 'isCronJobRunning') + .mockResolvedValueOnce(true); + + await jobService.fundEscrowCronJob(); + + expect(fundEscrowMock).not.toHaveBeenCalled(); + }); + + it('should create cron job entity on database to lock', async () => { + jest + .spyOn(cronJobService, 'startCronJob') + .mockResolvedValueOnce(cronJobEntityMock as any); + + await jobService.fundEscrowCronJob(); + + expect(cronJobService.startCronJob).toHaveBeenCalledWith( + CronJobType.FundEscrow, + ); + }); + + it('should run fundEscrow for all of the jobs with status FUNDING, and trigger webhook', async () => { + await jobService.fundEscrowCronJob(); + + expect(fundEscrowMock).toHaveBeenCalledTimes(2); + expect(createWebhookMock).toHaveBeenCalledTimes(2); + }); + + it('should increase retriesCount by 1, if the job fund fails', async () => { + fundEscrowMock.mockRejectedValueOnce(new Error('fund failed')); + + await jobService.fundEscrowCronJob(); + + expect(fundEscrowMock).toHaveBeenCalledTimes(2); + expect(jobEntityMock1.retriesCount).toBe(2); + expect(jobEntityMock2.retriesCount).toBe(1); + + expect(createWebhookMock).toHaveBeenCalledTimes(1); + }); + + it('should mark job as failed if the job fund fails more than max retries count', async () => { + fundEscrowMock.mockRejectedValueOnce(new Error('fund failed')); + jobEntityMock1.retriesCount = MOCK_MAX_RETRY_COUNT; + + await jobService.fundEscrowCronJob(); + + expect(fundEscrowMock).toHaveBeenCalledTimes(2); + expect(jobEntityMock1.status).toBe(JobStatus.FAILED); + expect(jobEntityMock2.status).toBe(JobStatus.SET_UP); + + expect(createWebhookMock).toHaveBeenCalledTimes(1); + }); + + it('should complete the cron job entity on database to unlock', async () => { + jest + .spyOn(cronJobService, 'completeCronJob') + .mockResolvedValueOnce(cronJobEntityMock as any); + + await jobService.fundEscrowCronJob(); + + expect(cronJobService.completeCronJob).toHaveBeenCalledWith( + CronJobType.FundEscrow, + ); + }); + }); + + describe('cancelCronJob', () => { + let findJobMock: any, + jobEntityMock1: Partial, + jobEntityMock2: Partial; + + beforeEach(() => { + jobEntityMock1 = { + status: JobStatus.TO_CANCEL, + fundAmount: 100, + userId: 1, + id: 1, + manifestUrl: MOCK_FILE_URL, + escrowAddress: MOCK_ADDRESS, + chainId: ChainId.LOCALHOST, + save: jest.fn(), + retriesCount: 0, + }; + + jobEntityMock2 = { + status: JobStatus.TO_CANCEL, + fundAmount: 100, + userId: 1, + id: 2, + manifestUrl: MOCK_FILE_URL, + escrowAddress: MOCK_ADDRESS, + chainId: ChainId.LOCALHOST, + save: jest.fn(), + retriesCount: 0, + }; + + findJobMock = jest + .spyOn(jobRepository, 'find') + .mockResolvedValue([jobEntityMock1 as any, jobEntityMock2 as any]); + + jest.spyOn(cronJobService, 'isCronJobRunning').mockResolvedValue(false); + + jest.spyOn(jobService, 'processEscrowCancellation').mockResolvedValue({ + txHash: MOCK_TRANSACTION_HASH, + amountRefunded: BigNumber.from(1), + }); (EscrowClient.build as any).mockImplementation(() => ({ getExchangeOracleAddress: jest @@ -1539,38 +1983,145 @@ describe('JobService', () => { requestType: JobRequestType.FORTUNE, }; storageService.download = jest.fn().mockResolvedValue(manifestMock); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should not run if cron job is already running', async () => { + jest + .spyOn(cronJobService, 'isCronJobRunning') + .mockResolvedValueOnce(true); + + await jobService.cancelCronJob(); + expect(findJobMock).not.toHaveBeenCalled(); + }); + + it('should create cron job entity on database to lock', async () => { + jest + .spyOn(cronJobService, 'startCronJob') + .mockResolvedValueOnce({} as any); + + await jobService.cancelCronJob(); + + expect(cronJobService.startCronJob).toHaveBeenCalledWith( + CronJobType.CancelEscrow, + ); + }); + + it('should cancel all of the jobs with status TO_CANCEL', async () => { const result = await jobService.cancelCronJob(); expect(result).toBeTruthy(); expect(jobService.processEscrowCancellation).toHaveBeenCalledWith( - jobEntityMock, + jobEntityMock1, + ); + expect(jobEntityMock1.save).toHaveBeenCalled(); + expect(jobService.processEscrowCancellation).toHaveBeenCalledWith( + jobEntityMock2, ); - expect(jobEntityMock.save).toHaveBeenCalled(); - expect(webhookService.createWebhook).toHaveBeenCalled(); + expect(jobEntityMock2.save).toHaveBeenCalled(); + expect(webhookService.createWebhook).toHaveBeenCalledTimes(2); }); it('should not call process escrow cancellation when escrowAddress is not present', async () => { const jobEntityWithoutEscrow = { - ...jobEntityMock, + ...jobEntityMock1, escrowAddress: undefined, }; jest - .spyOn(jobRepository, 'findOne') - .mockResolvedValueOnce(jobEntityWithoutEscrow as any); + .spyOn(jobRepository, 'find') + .mockResolvedValueOnce([jobEntityWithoutEscrow as any]); jest .spyOn(jobService, 'processEscrowCancellation') .mockResolvedValueOnce(undefined as any); - const manifestMock = { - requestType: JobRequestType.FORTUNE, - }; - storageService.download = jest.fn().mockResolvedValue(manifestMock); expect(await jobService.cancelCronJob()).toBe(true); expect(jobService.processEscrowCancellation).toHaveBeenCalledTimes(0); }); + it('should increase retriesCount by 1 if the job cancellation fails', async () => { + jest + .spyOn(jobService, 'processEscrowCancellation') + .mockRejectedValueOnce(new Error('cancellation failed')); + + expect(jobEntityMock1.retriesCount).toBe(0); + expect(jobEntityMock2.retriesCount).toBe(0); + + await jobService.cancelCronJob(); + + expect(jobEntityMock1.retriesCount).toBe(1); + expect(jobEntityMock2.retriesCount).toBe(0); + }); + + it('should mark job as failed if the job cancellation fails more than max retries count', async () => { + jest + .spyOn(jobService, 'processEscrowCancellation') + .mockRejectedValueOnce(new Error('cancellation failed')); + jobEntityMock1.retriesCount = MOCK_MAX_RETRY_COUNT; + + await jobService.cancelCronJob(); + + expect(jobService.processEscrowCancellation).toHaveBeenCalledTimes(2); + expect(jobEntityMock1.status).toBe(JobStatus.FAILED); + expect(jobEntityMock2.status).toBe(JobStatus.CANCELED); + }); + + it('should complete the cron job entity on database to unlock', async () => { + jest + .spyOn(cronJobService, 'completeCronJob') + .mockResolvedValueOnce({} as any); + + await jobService.cancelCronJob(); + + expect(cronJobService.completeCronJob).toHaveBeenCalledWith( + CronJobType.CancelEscrow, + ); + }); + }); + + describe('processEscrowCancellation', () => { + const jobEntityMock = { + status: JobStatus.TO_CANCEL, + fundAmount: 100, + userId: 1, + id: 1, + manifestUrl: MOCK_FILE_URL, + manifestHash: MOCK_FILE_HASH, + escrowAddress: MOCK_ADDRESS, + chainId: ChainId.LOCALHOST, + retriesCount: 1, + save: jest.fn(), + }; + + it('should cancel escrow', async () => { + const fundedAmount = BigNumber.from(1); + + const escrowClientMock = { + getStatus: jest.fn().mockResolvedValue(EscrowStatus.Launched), + getBalance: jest.fn().mockResolvedValue(fundedAmount), + cancel: jest.fn().mockResolvedValue({ + amountRefunded: fundedAmount, + txHash: MOCK_TRANSACTION_HASH, + }), + }; + + (EscrowClient.build as any).mockImplementation(() => escrowClientMock); + + const result = await jobService.processEscrowCancellation( + jobEntityMock as any, + ); + + expect(result).toEqual({ + amountRefunded: fundedAmount, + txHash: MOCK_TRANSACTION_HASH, + }); + expect(escrowClientMock.cancel).toHaveBeenCalled(); + }); + it('should throw bad request exception if escrowStatus is Complete', async () => { (EscrowClient.build as any).mockImplementation(() => ({ getStatus: jest.fn().mockResolvedValue(EscrowStatus.Complete), 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 4fa086f54d..7cf9b6fd9c 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 @@ -24,7 +24,7 @@ import { import { ConfigService } from '@nestjs/config'; import { validate } from 'class-validator'; import { BigNumber, ethers } from 'ethers'; -import { IsNull, LessThanOrEqual, Not, QueryFailedError } from 'typeorm'; +import { In, LessThanOrEqual, QueryFailedError } from 'typeorm'; import { ConfigNames } from '../../common/config'; import { ErrorEscrow, @@ -71,14 +71,13 @@ import { JobCaptchaAdvancedDto, JobCaptchaDto, RestrictedAudience, - StorageDataDto, } from './job.dto'; import { JobEntity } from './job.entity'; import { JobRepository } from './job.repository'; import { RoutingProtocolService } from './routing-protocol.service'; import { CANCEL_JOB_STATUSES, - JOB_RETRIES_COUNT_THRESHOLD, + DEFAULT_MAX_RETRY_COUNT, HCAPTCHA_BOUNDING_BOX_MAX_POINTS, HCAPTCHA_BOUNDING_BOX_MIN_POINTS, HCAPTCHA_IMMO_MAX_LENGTH, @@ -106,6 +105,8 @@ import { StorageService } from '../storage/storage.service'; import { WebhookService } from '../webhook/webhook.service'; import stringify from 'json-stable-stringify'; import { Cron, CronExpression } from '@nestjs/schedule'; +import { CronJobService } from '../cron-job/cron-job.service'; +import { CronJobType } from '../../common/enums/cron-job'; import { generateBucketUrl, listObjectsInBucket, @@ -125,6 +126,7 @@ export class JobService { private readonly routingProtocolService: RoutingProtocolService, private readonly storageService: StorageService, private readonly webhookService: WebhookService, + private readonly cronJobService: CronJobService, ) {} public async createCvatManifest( @@ -546,7 +548,33 @@ export class JobService { ); } - public async launchJob(jobEntity: JobEntity): Promise { + public async createEscrow(jobEntity: JobEntity): Promise { + const signer = this.web3Service.getSigner(jobEntity.chainId); + + const escrowClient = await EscrowClient.build(signer); + + const escrowAddress = await escrowClient.createEscrow( + NETWORKS[jobEntity.chainId as ChainId]!.hmtAddress, + [], + jobEntity.userId.toString(), + { + gasPrice: await this.web3Service.calculateGasPrice(jobEntity.chainId), + }, + ); + + if (!escrowAddress) { + this.logger.log(ErrorEscrow.NotCreated, JobService.name); + throw new NotFoundException(ErrorEscrow.NotCreated); + } + + jobEntity.status = JobStatus.CREATED; + jobEntity.escrowAddress = escrowAddress; + await jobEntity.save(); + + return jobEntity; + } + + public async setupEscrow(jobEntity: JobEntity): Promise { const signer = this.web3Service.getSigner(jobEntity.chainId); const escrowClient = await EscrowClient.build(signer); @@ -614,37 +642,17 @@ export class JobService { manifestHash: jobEntity.manifestHash, }; - jobEntity.status = JobStatus.LAUNCHING; - await jobEntity.save(); - - const escrowAddress = await escrowClient.createEscrow( - NETWORKS[jobEntity.chainId as ChainId]!.hmtAddress, - [], - jobEntity.userId.toString(), - { - gasPrice: await this.web3Service.calculateGasPrice(jobEntity.chainId), - }, - ); - - await escrowClient.setup(escrowAddress, escrowConfig, { + await escrowClient.setup(jobEntity.escrowAddress, escrowConfig, { gasPrice: await this.web3Service.calculateGasPrice(jobEntity.chainId), }); - if (!escrowAddress) { - this.logger.log(ErrorEscrow.NotCreated, JobService.name); - throw new NotFoundException(ErrorEscrow.NotCreated); - } - - jobEntity.escrowAddress = escrowAddress; + jobEntity.status = JobStatus.SET_UP; await jobEntity.save(); return jobEntity; } - public async fundJob(jobEntity: JobEntity): Promise { - jobEntity.status = JobStatus.FUNDING; - await jobEntity.save(); - + public async fundEscrow(jobEntity: JobEntity): Promise { const signer = this.web3Service.getSigner(jobEntity.chainId); const escrowClient = await EscrowClient.build(signer); @@ -901,18 +909,31 @@ export class JobService { } @Cron(CronExpression.EVERY_10_MINUTES) - public async launchCronJob() { - this.logger.log('Launch jobs START'); - try { - // TODO: Add retry policy and process failure requests https://github.com/humanprotocol/human-protocol/issues/334 - let jobEntity; + public async createEscrowCronJob() { + const isCronJobRunning = await this.cronJobService.isCronJobRunning( + CronJobType.CreateEscrow, + ); + + if (isCronJobRunning) { + return; + } + + this.logger.log('Create escrow START'); + const cronJob = await this.cronJobService.startCronJob( + CronJobType.CreateEscrow, + ); - jobEntity = await this.jobRepository.findOne( + try { + const jobEntities = await this.jobRepository.find( { - status: JobStatus.LAUNCHING, - retriesCount: LessThanOrEqual(JOB_RETRIES_COUNT_THRESHOLD), + status: In([JobStatus.PAID]), + retriesCount: LessThanOrEqual( + this.configService.get( + ConfigNames.MAX_RETRY_COUNT, + DEFAULT_MAX_RETRY_COUNT, + ), + ), waitUntil: LessThanOrEqual(new Date()), - escrowAddress: Not(IsNull()), }, { order: { @@ -921,105 +942,235 @@ export class JobService { }, ); - if (!jobEntity) { - jobEntity = await this.jobRepository.findOne( - { - status: JobStatus.PAID, - retriesCount: LessThanOrEqual(JOB_RETRIES_COUNT_THRESHOLD), - waitUntil: LessThanOrEqual(new Date()), - }, - { - order: { - waitUntil: SortDirection.ASC, - }, - }, - ); + for (const jobEntity of jobEntities) { + try { + await this.createEscrow(jobEntity); + } catch (err) { + this.logger.error(`Error creating escrow: ${err.message}`); + await this.handleProcessJobFailure(jobEntity); + } } + } catch (e) { + this.logger.error(e); + } - if (!jobEntity) return; + this.logger.log('Create escrow STOP'); + await this.cronJobService.completeCronJob(cronJob); + } - const manifest = await this.storageService.download( - jobEntity.manifestUrl, + @Cron(CronExpression.EVERY_10_MINUTES) + public async setupEscrowCronJob() { + const isCronJobRunning = await this.cronJobService.isCronJobRunning( + CronJobType.SetupEscrow, + ); + + if (isCronJobRunning) { + return; + } + + this.logger.log('Setup escrow START'); + const cronJob = await this.cronJobService.startCronJob( + CronJobType.SetupEscrow, + ); + + try { + const jobEntities = await this.jobRepository.find( + { + status: JobStatus.CREATED, + retriesCount: LessThanOrEqual( + this.configService.get( + ConfigNames.MAX_RETRY_COUNT, + DEFAULT_MAX_RETRY_COUNT, + ), + ), + waitUntil: LessThanOrEqual(new Date()), + }, + { + order: { + waitUntil: SortDirection.ASC, + }, + }, ); - if (!jobEntity.escrowAddress && jobEntity.status === JobStatus.PAID) { - jobEntity = await this.launchJob(jobEntity); - } - if (jobEntity.escrowAddress && jobEntity.status === JobStatus.LAUNCHING) { - jobEntity = await this.fundJob(jobEntity); - } - if (jobEntity.escrowAddress && jobEntity.status === JobStatus.LAUNCHED) { - if ((manifest as CvatManifestDto)?.annotation?.type) { - await this.webhookService.createWebhook({ - escrowAddress: jobEntity.escrowAddress, - chainId: jobEntity.chainId, - eventType: EventType.ESCROW_CREATED, - oracleType: OracleType.CVAT, - hasSignature: false, - }); + for (const jobEntity of jobEntities) { + try { + await this.setupEscrow(jobEntity); + } catch (err) { + this.logger.error(`Error setting up escrow: ${err.message}`); + await this.handleProcessJobFailure(jobEntity); } } } catch (e) { this.logger.error(e); + } + + this.logger.log('Setup escrow STOP'); + await this.cronJobService.completeCronJob(cronJob); + } + + @Cron(CronExpression.EVERY_10_MINUTES) + public async fundEscrowCronJob() { + const isCronJobRunning = await this.cronJobService.isCronJobRunning( + CronJobType.FundEscrow, + ); + + if (isCronJobRunning) { return; } - this.logger.log('Launch jobs STOP'); + + this.logger.log('Fund escrow START'); + const cronJob = await this.cronJobService.startCronJob( + CronJobType.FundEscrow, + ); + + try { + const jobEntities = await this.jobRepository.find( + { + status: JobStatus.SET_UP, + retriesCount: LessThanOrEqual( + this.configService.get( + ConfigNames.MAX_RETRY_COUNT, + DEFAULT_MAX_RETRY_COUNT, + ), + ), + waitUntil: LessThanOrEqual(new Date()), + }, + { + order: { + waitUntil: SortDirection.ASC, + }, + }, + ); + + for (const jobEntity of jobEntities) { + try { + await this.fundEscrow(jobEntity); + + const manifest = await this.storageService.download( + jobEntity.manifestUrl, + ); + + if ((manifest as CvatManifestDto)?.annotation?.type) { + await this.webhookService.createWebhook({ + escrowAddress: jobEntity.escrowAddress, + chainId: jobEntity.chainId, + eventType: EventType.ESCROW_CREATED, + oracleType: OracleType.CVAT, + hasSignature: false, + }); + } + } catch (err) { + this.logger.error(`Error funding escrow: ${err.message}`); + await this.handleProcessJobFailure(jobEntity); + } + } + } catch (e) { + this.logger.error(e); + } + + this.logger.log('Fund escrow STOP'); + await this.cronJobService.completeCronJob(cronJob); } @Cron(CronExpression.EVERY_10_MINUTES) public async cancelCronJob() { - this.logger.log('Cancel jobs START'); - const jobEntity = await this.jobRepository.findOne( - { - status: JobStatus.TO_CANCEL, - retriesCount: LessThanOrEqual(JOB_RETRIES_COUNT_THRESHOLD), - waitUntil: LessThanOrEqual(new Date()), - }, - { - order: { - waitUntil: SortDirection.ASC, - }, - }, + const isCronJobRunning = await this.cronJobService.isCronJobRunning( + CronJobType.FundEscrow, ); - if (!jobEntity) return; - if (jobEntity.escrowAddress) { - const { amountRefunded } = - await this.processEscrowCancellation(jobEntity); - await this.paymentService.createRefundPayment({ - refundAmount: Number(ethers.utils.formatEther(amountRefunded)), - userId: jobEntity.userId, - jobId: jobEntity.id, - }); - } else { - await this.paymentService.createRefundPayment({ - refundAmount: jobEntity.fundAmount, - userId: jobEntity.userId, - jobId: jobEntity.id, - }); + if (isCronJobRunning) { + return; } - jobEntity.status = JobStatus.CANCELED; - await jobEntity.save(); - const manifest = await this.storageService.download(jobEntity.manifestUrl); + this.logger.log('Cancel jobs START'); + const cronJob = await this.cronJobService.startCronJob( + CronJobType.CancelEscrow, + ); - const oracleType = this.getOracleType(manifest); - if (oracleType !== OracleType.HCAPTCHA) { - await this.webhookService.createWebhook({ - escrowAddress: jobEntity.escrowAddress, - chainId: jobEntity.chainId, - eventType: EventType.ESCROW_CANCELED, - oracleType: this.getOracleType(manifest), - hasSignature: - (manifest as FortuneManifestDto).requestType === - JobRequestType.FORTUNE, - }); - } + try { + const jobEntities = await this.jobRepository.find( + { + status: JobStatus.TO_CANCEL, + retriesCount: LessThanOrEqual( + this.configService.get( + ConfigNames.MAX_RETRY_COUNT, + DEFAULT_MAX_RETRY_COUNT, + ), + ), + waitUntil: LessThanOrEqual(new Date()), + }, + { + order: { + waitUntil: SortDirection.ASC, + }, + }, + ); + for (const jobEntity of jobEntities) { + try { + if (jobEntity.escrowAddress) { + const { amountRefunded } = + await this.processEscrowCancellation(jobEntity); + await this.paymentService.createRefundPayment({ + refundAmount: Number(ethers.utils.formatEther(amountRefunded)), + userId: jobEntity.userId, + jobId: jobEntity.id, + }); + } else { + await this.paymentService.createRefundPayment({ + refundAmount: jobEntity.fundAmount, + userId: jobEntity.userId, + jobId: jobEntity.id, + }); + } + jobEntity.status = JobStatus.CANCELED; + await jobEntity.save(); + + const manifest = await this.storageService.download( + jobEntity.manifestUrl, + ); + + const oracleType = this.getOracleType(manifest); + if (oracleType !== OracleType.HCAPTCHA) { + await this.webhookService.createWebhook({ + escrowAddress: jobEntity.escrowAddress, + chainId: jobEntity.chainId, + eventType: EventType.ESCROW_CANCELED, + oracleType: this.getOracleType(manifest), + hasSignature: + (manifest as FortuneManifestDto).requestType === + JobRequestType.FORTUNE, + }); + } + } catch (err) { + this.logger.error(`Error canceling escrow: ${err.message}`); + await this.handleProcessJobFailure(jobEntity); + } + } + } catch (e) { + this.logger.error(e); + } + await this.cronJobService.completeCronJob(cronJob); this.logger.log('Cancel jobs STOP'); return true; } + private handleProcessJobFailure = async (jobEntity: JobEntity) => { + if ( + jobEntity.retriesCount < + this.configService.get( + ConfigNames.MAX_RETRY_COUNT, + DEFAULT_MAX_RETRY_COUNT, + ) + ) { + jobEntity.retriesCount += 1; + } else { + jobEntity.status = JobStatus.FAILED; + } + + await jobEntity.save(); + }; + private getOracleType(manifest: any): OracleType { if ( (manifest as FortuneManifestDto).requestType === JobRequestType.FORTUNE diff --git a/packages/apps/job-launcher/server/src/modules/webhook/webhook.service.spec.ts b/packages/apps/job-launcher/server/src/modules/webhook/webhook.service.spec.ts index c46e4a3a63..d53bf8b5f9 100644 --- a/packages/apps/job-launcher/server/src/modules/webhook/webhook.service.spec.ts +++ b/packages/apps/job-launcher/server/src/modules/webhook/webhook.service.spec.ts @@ -23,6 +23,9 @@ import { WebhookRepository } from './webhook.repository'; import { WebhookService } from './webhook.service'; import { of } from 'rxjs'; import { HEADER_SIGNATURE_KEY } from '../../common/constants'; +import { CronJobService } from '../cron-job/cron-job.service'; +import { CronJobEntity } from '../cron-job/cron-job.entity'; +import { CronJobType } from '../../common/enums/cron-job'; jest.mock('@human-protocol/sdk', () => ({ ...jest.requireActual('@human-protocol/sdk'), @@ -38,7 +41,8 @@ describe('WebhookService', () => { let webhookService: WebhookService, webhookRepository: WebhookRepository, web3Service: Web3Service, - httpService: HttpService; + httpService: HttpService, + cronJobService: CronJobService; const signerMock = { address: MOCK_ADDRESS, @@ -76,6 +80,10 @@ describe('WebhookService', () => { }, { provide: ConfigService, useValue: mockConfigService }, { provide: HttpService, useValue: createMock() }, + { + provide: CronJobService, + useValue: createMock(), + }, ], }).compile(); @@ -83,6 +91,7 @@ describe('WebhookService', () => { webhookRepository = moduleRef.get(WebhookRepository); web3Service = moduleRef.get(Web3Service); httpService = moduleRef.get(HttpService); + cronJobService = moduleRef.get(CronJobService); }); afterEach(() => { @@ -285,52 +294,129 @@ describe('WebhookService', () => { }); describe('processPendingCronJob', () => { - const webhookEntity: Partial = { - id: 1, - chainId: ChainId.LOCALHOST, - escrowAddress: MOCK_ADDRESS, - status: WebhookStatus.PENDING, - waitUntil: new Date(), - }; + let sendWebhookMock: any; + let cronJobEntityMock: Partial; + let webhookEntity1: Partial, + webhookEntity2: Partial; + + beforeEach(() => { + cronJobEntityMock = { + cronJobType: CronJobType.ProcessPendingWebhook, + startedAt: new Date(), + }; - it('should not execute anything if no pending webhook is found', async () => { - webhookRepository.findOne = jest.fn().mockReturnValue(null); - jest.spyOn(webhookService as any, 'sendWebhook'); - expect(await webhookService.processPendingCronJob()).toBe(undefined); - expect((webhookService as any).sendWebhook).not.toBeCalled(); - }); + webhookEntity1 = { + id: 1, + chainId: ChainId.LOCALHOST, + escrowAddress: MOCK_ADDRESS, + status: WebhookStatus.PENDING, + waitUntil: new Date(), + retriesCount: 0, + }; - it('should handle error if any exception is thrown', async () => { - webhookRepository.findOne = jest.fn().mockReturnValue(webhookEntity); + webhookEntity2 = { + id: 2, + chainId: ChainId.LOCALHOST, + escrowAddress: MOCK_ADDRESS, + status: WebhookStatus.PENDING, + waitUntil: new Date(), + retriesCount: 0, + }; + + jest + .spyOn(webhookRepository, 'find') + .mockResolvedValue([webhookEntity1 as any, webhookEntity2 as any]); - jest.spyOn(webhookService as any, 'handleWebhookError'); + sendWebhookMock = jest.spyOn(webhookService as any, 'sendWebhook'); + sendWebhookMock.mockResolvedValue(true); + jest.spyOn(cronJobService, 'isCronJobRunning').mockResolvedValue(false); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('should not run if cron job is already running', async () => { jest - .spyOn(webhookService as any, 'sendWebhook') - .mockImplementation(() => { - throw new Error(); - }); + .spyOn(cronJobService, 'isCronJobRunning') + .mockResolvedValueOnce(true); - expect(await webhookService.processPendingCronJob()).toBe(undefined); - expect((webhookService as any).handleWebhookError).toBeCalled(); + const startCronJobMock = jest.spyOn(cronJobService, 'startCronJob'); + + await (webhookService as any).processPendingCronJob(); + + expect(startCronJobMock).not.toHaveBeenCalled(); }); - it('should successfully process a webhook', async () => { - webhookRepository.findOne = jest.fn().mockReturnValue(webhookEntity); + it('should create cron job entity to lock the process', async () => { jest - .spyOn(webhookService as any, 'sendWebhook') - .mockResolvedValue(undefined); + .spyOn(cronJobService, 'startCronJob') + .mockResolvedValueOnce(cronJobEntityMock as any); + + await (webhookService as any).processPendingCronJob(); - expect(await webhookService.processPendingCronJob()).toBe(undefined); - expect((webhookService as any).sendWebhook).toBeCalled(); + expect(cronJobService.startCronJob).toHaveBeenCalledWith( + CronJobType.ProcessPendingWebhook, + ); + }); + + it('should send webhook for all of the pending webhooks', async () => { + await (webhookService as any).processPendingCronJob(); + + expect(sendWebhookMock).toHaveBeenCalledTimes(2); + expect(sendWebhookMock).toHaveBeenCalledWith(webhookEntity1); + expect(sendWebhookMock).toHaveBeenCalledWith(webhookEntity2); + + expect(webhookRepository.updateOne).toHaveBeenCalledTimes(2); expect(webhookRepository.updateOne).toHaveBeenCalledWith( - { id: 1 }, + { id: webhookEntity1.id }, + { status: WebhookStatus.COMPLETED }, + ); + expect(webhookRepository.updateOne).toHaveBeenCalledWith( + { id: webhookEntity2.id }, + { status: WebhookStatus.COMPLETED }, + ); + }); + + it('should increase retriesCount by 1 if sending webhook fails', async () => { + sendWebhookMock.mockRejectedValueOnce(new Error()); + + await (webhookService as any).processPendingCronJob(); + + expect(webhookRepository.updateOne).toHaveBeenCalledWith( + { id: webhookEntity1.id }, { - status: WebhookStatus.COMPLETED, - retriesCount: 0, + retriesCount: 1, + waitUntil: expect.any(Date), }, ); }); + + it('should mark webhook as failed if retriesCount exceeds threshold', async () => { + sendWebhookMock.mockRejectedValueOnce(new Error()); + + webhookEntity1.retriesCount = MOCK_MAX_RETRY_COUNT; + + await (webhookService as any).processPendingCronJob(); + + expect(webhookRepository.updateOne).toHaveBeenCalledWith( + { id: webhookEntity1.id }, + { status: WebhookStatus.FAILED }, + ); + }); + + it('should complete the cron job entity to unlock', async () => { + jest + .spyOn(cronJobService, 'completeCronJob') + .mockResolvedValueOnce(cronJobEntityMock as any); + + await (webhookService as any).processPendingCronJob(); + + expect(cronJobService.completeCronJob).toHaveBeenCalledWith( + cronJobEntityMock as any, + ); + }); }); describe('getExchangeOracleWebhookUrl', () => { diff --git a/packages/apps/job-launcher/server/src/modules/webhook/webhook.service.ts b/packages/apps/job-launcher/server/src/modules/webhook/webhook.service.ts index 4c4c49de22..dbb38febe8 100644 --- a/packages/apps/job-launcher/server/src/modules/webhook/webhook.service.ts +++ b/packages/apps/job-launcher/server/src/modules/webhook/webhook.service.ts @@ -14,14 +14,18 @@ import { signMessage } from '../../common/utils/signature'; import { WebhookRepository } from './webhook.repository'; import { CVATWebhookDto, FortuneWebhookDto } from '../job/job.dto'; import { firstValueFrom } from 'rxjs'; -import { HEADER_SIGNATURE_KEY } from '../../common/constants'; +import { + DEFAULT_MAX_RETRY_COUNT, + HEADER_SIGNATURE_KEY, +} from '../../common/constants'; import { HttpService } from '@nestjs/axios'; import { Web3Service } from '../web3/web3.service'; import { OracleType, WebhookStatus } from '../../common/enums/webhook'; import { ErrorWebhook } from '../../common/constants/errors'; -import { SortDirection } from '../../common/enums/collection'; import { WebhookEntity } from './webhook.entity'; import { WebhookDto } from './webhook.dto'; +import { CronJobService } from '../cron-job/cron-job.service'; +import { CronJobType } from '../../common/enums/cron-job'; @Injectable() export class WebhookService { private readonly logger = new Logger(WebhookService.name); @@ -32,6 +36,7 @@ export class WebhookService { private readonly webhookRepository: WebhookRepository, public readonly configService: ConfigService, public readonly httpService: HttpService, + private readonly cronJobService: CronJobService, ) {} /** @@ -156,39 +161,51 @@ export class WebhookService { */ @Cron(CronExpression.EVERY_10_MINUTES) public async processPendingCronJob(): Promise { + const isCronJobRunning = await this.cronJobService.isCronJobRunning( + CronJobType.ProcessPendingWebhook, + ); + + if (isCronJobRunning) { + return; + } + this.logger.log('Pending webhooks START'); - const webhookEntity = await this.webhookRepository.findOne( - { + const cronJob = await this.cronJobService.startCronJob( + CronJobType.ProcessPendingWebhook, + ); + + try { + const webhookEntities = await this.webhookRepository.find({ status: WebhookStatus.PENDING, retriesCount: LessThanOrEqual( - this.configService.get(ConfigNames.MAX_RETRY_COUNT)!, + this.configService.get( + ConfigNames.MAX_RETRY_COUNT, + DEFAULT_MAX_RETRY_COUNT, + ), ), waitUntil: LessThanOrEqual(new Date()), - }, - { - order: { - waitUntil: SortDirection.ASC, - }, - }, - ); + }); - if (!webhookEntity) { - this.logger.log('Pending webhooks STOP'); - return; - } - try { - await this.sendWebhook(webhookEntity); - await this.webhookRepository.updateOne( - { id: webhookEntity.id }, - { - status: WebhookStatus.COMPLETED, - retriesCount: 0, - }, - ); - this.logger.log('Pending webhooks STOP'); + for (const webhookEntity of webhookEntities) { + try { + await this.sendWebhook(webhookEntity); + await this.webhookRepository.updateOne( + { id: webhookEntity.id }, + { + status: WebhookStatus.COMPLETED, + }, + ); + } catch (err) { + this.logger.error(`Error sending webhook: ${err.message}`); + await this.handleWebhookError(webhookEntity, err); + } + } } catch (e) { - await this.handleWebhookError(webhookEntity, e); + this.logger.error(e); } + + this.logger.log('Pending webhooks STOP'); + await this.cronJobService.completeCronJob(cronJob); } /** @@ -204,7 +221,10 @@ export class WebhookService { ): Promise { if ( webhookEntity.retriesCount >= - this.configService.get(ConfigNames.MAX_RETRY_COUNT)! + this.configService.get( + ConfigNames.MAX_RETRY_COUNT, + DEFAULT_MAX_RETRY_COUNT, + ) ) { await this.webhookRepository.updateOne( { id: webhookEntity.id }, From 7e4763c29bd16ccd9b23769a84a6e885e28f7f05 Mon Sep 17 00:00:00 2001 From: eugenvoronov <104138627+eugenvoronov@users.noreply.github.com> Date: Mon, 8 Jan 2024 16:58:29 +0800 Subject: [PATCH 014/104] [Job Launcher] Fix password validation regex (#1369) * Updated regex * Added regex to error message * Added validation pipe for password * use relative paths --------- Co-authored-by: portuu3 --- .../server/src/common/constants/errors.ts | 1 + .../server/src/common/pipes/validation.ts | 20 +++++++++++++++++++ .../src/modules/auth/auth.controller.ts | 3 +++ .../server/src/modules/auth/auth.dto.ts | 4 ---- 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/packages/apps/job-launcher/server/src/common/constants/errors.ts b/packages/apps/job-launcher/server/src/common/constants/errors.ts index cbaae92364..cb2e13e0a0 100644 --- a/packages/apps/job-launcher/server/src/common/constants/errors.ts +++ b/packages/apps/job-launcher/server/src/common/constants/errors.ts @@ -60,6 +60,7 @@ export enum ErrorAuth { UserNotActive = 'User not active', ApiKeyCouldNotBeCreatedOrUpdated = 'API key could not be created or updated', ApiKeyNotFound = 'API key not found', + PasswordIsNotStrongEnough = 'Password is not strong enough. Password must be at least eight characters long and contain 1 upper, 1 lowercase, 1 number and 1 special character. (!@#$%^&*()_+={}|\'"/`[]:;<>,.?~-])', } /** diff --git a/packages/apps/job-launcher/server/src/common/pipes/validation.ts b/packages/apps/job-launcher/server/src/common/pipes/validation.ts index f46e742df2..265d67f6cf 100644 --- a/packages/apps/job-launcher/server/src/common/pipes/validation.ts +++ b/packages/apps/job-launcher/server/src/common/pipes/validation.ts @@ -1,10 +1,13 @@ import { BadRequestException, Injectable, + PipeTransform, ValidationError, ValidationPipe, ValidationPipeOptions, } from '@nestjs/common'; +import { ValidatePasswordDto } from '../../modules/auth/auth.dto'; +import { ErrorAuth } from '../constants/errors'; @Injectable() export class HttpValidationPipe extends ValidationPipe { @@ -20,3 +23,20 @@ export class HttpValidationPipe extends ValidationPipe { }); } } + +@Injectable() +export class PasswordValidationPipe implements PipeTransform { + transform(value: ValidatePasswordDto) { + if (!this.isValidPassword(value.password)) { + throw new BadRequestException(ErrorAuth.PasswordIsNotStrongEnough); + } + + return value; + } + + private isValidPassword(password: string): boolean { + const regex = + /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*()_+={}\|'"/`[\]:;<>,.?~\\-]).*$/; + return regex.test(password); + } +} diff --git a/packages/apps/job-launcher/server/src/modules/auth/auth.controller.ts b/packages/apps/job-launcher/server/src/modules/auth/auth.controller.ts index 695776634c..29deae668e 100644 --- a/packages/apps/job-launcher/server/src/modules/auth/auth.controller.ts +++ b/packages/apps/job-launcher/server/src/modules/auth/auth.controller.ts @@ -9,6 +9,7 @@ import { Request, UnprocessableEntityException, Logger, + UsePipes, } from '@nestjs/common'; import { @@ -33,6 +34,7 @@ import { AuthService } from './auth.service'; import { JwtAuthGuard } from '../../common/guards'; import { RequestWithUser } from '../../common/types'; import { ErrorAuth } from '../../common/constants/errors'; +import { PasswordValidationPipe } from '../../common/pipes'; @ApiTags('Auth') @Controller('/auth') @@ -41,6 +43,7 @@ export class AuthJwtController { constructor(private readonly authService: AuthService) {} + @UsePipes(new PasswordValidationPipe()) @Public() @Post('/signup') @UseInterceptors(ClassSerializerInterceptor) diff --git a/packages/apps/job-launcher/server/src/modules/auth/auth.dto.ts b/packages/apps/job-launcher/server/src/modules/auth/auth.dto.ts index 46a887a923..c51e130baa 100644 --- a/packages/apps/job-launcher/server/src/modules/auth/auth.dto.ts +++ b/packages/apps/job-launcher/server/src/modules/auth/auth.dto.ts @@ -24,10 +24,6 @@ export class SignInDto { } export class ValidatePasswordDto { - @Matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*+])(?=.{8,})/, { - message: - 'Password is not strong enough. Password must be at least eight characters long and contain 1 upper, 1 lowercase, 1 number and 1 special character.', - }) @ApiProperty() @IsPassword() public password: string; From be71b8c344a66c3e3f097b41aaf9dfc28f71e37c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jan 2024 22:27:24 +0800 Subject: [PATCH 015/104] Bump @graphprotocol/graph-cli from 0.62.0 to 0.64.1 (#1434) Bumps @graphprotocol/graph-cli from 0.62.0 to 0.64.1. --- updated-dependencies: - dependency-name: "@graphprotocol/graph-cli" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- packages/sdk/typescript/subgraph/package.json | 2 +- yarn.lock | 25 +++++++++++-------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/packages/sdk/typescript/subgraph/package.json b/packages/sdk/typescript/subgraph/package.json index c53bff6366..7205b47ce9 100644 --- a/packages/sdk/typescript/subgraph/package.json +++ b/packages/sdk/typescript/subgraph/package.json @@ -38,7 +38,7 @@ ], "license": "MIT", "devDependencies": { - "@graphprotocol/graph-cli": "^0.62.0", + "@graphprotocol/graph-cli": "^0.64.1", "@graphprotocol/graph-ts": "^0.31.0", "@graphql-eslint/eslint-plugin": "^3.19.1", "@human-protocol/core": "*", diff --git a/yarn.lock b/yarn.lock index 4943f789dd..31e7101751 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2559,10 +2559,10 @@ resolved "https://registry.yarnpkg.com/@golevelup/ts-jest/-/ts-jest-0.4.0.tgz#e36551ecbb37fcf3e4143a1ba9f78a649649dc91" integrity sha512-ehgllV/xU8PC+yVyEUtTzhiSQKsr7k5Jz74B6dtCaVJz7/Vo7JiaACsCLvD7/iATlJUAEqvBson0OHewD3JDzQ== -"@graphprotocol/graph-cli@^0.62.0": - version "0.62.0" - resolved "https://registry.yarnpkg.com/@graphprotocol/graph-cli/-/graph-cli-0.62.0.tgz#948d3861d9ff027728097deac5d8751924ec0c8a" - integrity sha512-dyjhkCSXbdcul5h1wY2CmorjM4k+tjjzEFs5GFBSSdLC2VodKXEWJo9RzrpFSmRBJzwc7ywDf8Pbmy1cJTCXRA== +"@graphprotocol/graph-cli@^0.64.1": + version "0.64.1" + resolved "https://registry.yarnpkg.com/@graphprotocol/graph-cli/-/graph-cli-0.64.1.tgz#c644d7b387b8959e637b94896721251d45008e16" + integrity sha512-BUjWLiEvZUPxuJ/PpARXMBeVLBGjJ/AKP+Orqzlb09b/6HAyQwYpDsaz5R9rW05LYPRqMikuCmktY727rbIFuA== dependencies: "@float-capital/float-subgraph-uncrashable" "^0.0.0-alpha.4" "@oclif/core" "2.8.6" @@ -2584,7 +2584,7 @@ ipfs-http-client "55.0.0" jayson "4.0.0" js-yaml "3.14.1" - prettier "1.19.1" + prettier "3.0.3" request "2.88.2" semver "7.4.0" sync-request "6.1.0" @@ -20772,16 +20772,21 @@ prettier-plugin-solidity@^1.2.0: semver "^7.5.4" solidity-comments-extractor "^0.0.7" -prettier@1.19.1, prettier@^1.19.1: - version "1.19.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" - integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== - prettier@2.8.4: version "2.8.4" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.4.tgz#34dd2595629bfbb79d344ac4a91ff948694463c3" integrity sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw== +prettier@3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.0.3.tgz#432a51f7ba422d1469096c0fdc28e235db8f9643" + integrity sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg== + +prettier@^1.19.1: + version "1.19.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" + integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== + prettier@^2.3.1: version "2.8.8" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" From 76fac8022865409e1b136449a36e5eabb2110692 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jan 2024 22:28:16 +0800 Subject: [PATCH 016/104] Bump web-vitals from 3.5.0 to 3.5.1 (#1435) Bumps [web-vitals](https://github.com/GoogleChrome/web-vitals) from 3.5.0 to 3.5.1. - [Changelog](https://github.com/GoogleChrome/web-vitals/blob/main/CHANGELOG.md) - [Commits](https://github.com/GoogleChrome/web-vitals/compare/v3.5.0...v3.5.1) --- updated-dependencies: - dependency-name: web-vitals dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- packages/apps/dashboard/ui/package.json | 2 +- packages/apps/job-launcher/client/package.json | 2 +- yarn.lock | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/apps/dashboard/ui/package.json b/packages/apps/dashboard/ui/package.json index cfec2c902e..bfdba54247 100644 --- a/packages/apps/dashboard/ui/package.json +++ b/packages/apps/dashboard/ui/package.json @@ -33,7 +33,7 @@ "serve": "^14.1.1", "swr": "^2.2.4", "wagmi": "^0.12.2", - "web-vitals": "^3.5.0" + "web-vitals": "^3.5.1" }, "devDependencies": { "@testing-library/jest-dom": "^6.1.3", diff --git a/packages/apps/job-launcher/client/package.json b/packages/apps/job-launcher/client/package.json index 116ac9a2a7..7b741b74e6 100644 --- a/packages/apps/job-launcher/client/package.json +++ b/packages/apps/job-launcher/client/package.json @@ -26,7 +26,7 @@ "swr": "^2.2.4", "typescript": "^4.9.3", "wagmi": "^0.12.2", - "web-vitals": "^3.5.0", + "web-vitals": "^3.5.1", "xml2js": "^0.6.2", "yup": "^1.2.0" }, diff --git a/yarn.lock b/yarn.lock index 31e7101751..475491a35e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -25207,10 +25207,10 @@ web-streams-polyfill@^3.1.1, web-streams-polyfill@^3.2.1: resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz#71c2718c52b45fd49dbeee88634b3a60ceab42a6" integrity sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q== -web-vitals@^3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/web-vitals/-/web-vitals-3.5.0.tgz#3a5571f00743ecd059394b61e0adceec7fac2634" - integrity sha512-f5YnCHVG9Y6uLCePD4tY8bO/Ge15NPEQWtvm3tPzDKygloiqtb4SVqRHBcrIAqo2ztqX5XueqDn97zHF0LdT6w== +web-vitals@^3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/web-vitals/-/web-vitals-3.5.1.tgz#af7a9dc60708b81007922ab55a23d963676ba30a" + integrity sha512-xQ9lvIpfLxUj0eSmT79ZjRoU5wIRfIr7pNukL7ZE4EcWZSmfZQqOlhuAGfkVa3EFmzPHZhWhXfm2i5ys+THVPg== web3-bzz@1.10.3: version "1.10.3" From 6ce6ab7a112bbf1fbfa2d8fc6d899627e3e8561c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jan 2024 22:29:44 +0800 Subject: [PATCH 017/104] Bump chai from 4.3.10 to 4.4.0 (#1436) Bumps [chai](https://github.com/chaijs/chai) from 4.3.10 to 4.4.0. - [Release notes](https://github.com/chaijs/chai/releases) - [Changelog](https://github.com/chaijs/chai/blob/main/History.md) - [Commits](https://github.com/chaijs/chai/compare/v4.3.10...v4.4.0) --- updated-dependencies: - dependency-name: chai dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- packages/core/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/core/package.json b/packages/core/package.json index 4b86869828..da5532d8a2 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -60,7 +60,7 @@ "@typechain/hardhat": "^6.1.4", "@types/chai": "^4.3.3", "@types/mocha": "^10.0.2", - "chai": "^4.3.7", + "chai": "^4.4.0", "ethers": "^5.0.0", "hardhat": "^2.18.3", "hardhat-abi-exporter": "^2.10.1", diff --git a/yarn.lock b/yarn.lock index 475491a35e..7dc94b5357 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9776,10 +9776,10 @@ chai-as-promised@^7.1.1: dependencies: check-error "^1.0.2" -chai@^4.2.0, chai@^4.3.7: - version "4.3.10" - resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.10.tgz#d784cec635e3b7e2ffb66446a63b4e33bd390384" - integrity sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g== +chai@^4.2.0, chai@^4.3.7, chai@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.4.0.tgz#f9ac79f26726a867ac9d90a9b382120479d5f55b" + integrity sha512-x9cHNq1uvkCdU+5xTkNh5WtgD4e4yDFCsp9jVc7N7qVeKeftv3gO/ZrviX5d+3ZfxdYnZXZYujjRInu1RogU6A== dependencies: assertion-error "^1.1.0" check-error "^1.0.3" From dea9efc4088babd7f44ff62d9f42a5185bb42939 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jan 2024 22:30:45 +0800 Subject: [PATCH 018/104] Bump @strapi/plugin-i18n from 4.15.5 to 4.16.2 (#1437) Bumps [@strapi/plugin-i18n](https://github.com/strapi/strapi/tree/HEAD/packages/plugins/i18n) from 4.15.5 to 4.16.2. - [Release notes](https://github.com/strapi/strapi/releases) - [Commits](https://github.com/strapi/strapi/commits/v4.16.2/packages/plugins/i18n) --- updated-dependencies: - dependency-name: "@strapi/plugin-i18n" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- packages/apps/dashboard/admin/package.json | 2 +- yarn.lock | 88 ++++++++++++++++++++-- 2 files changed, 82 insertions(+), 8 deletions(-) diff --git a/packages/apps/dashboard/admin/package.json b/packages/apps/dashboard/admin/package.json index 293b2b5819..9074466592 100644 --- a/packages/apps/dashboard/admin/package.json +++ b/packages/apps/dashboard/admin/package.json @@ -12,7 +12,7 @@ "devDependencies": {}, "dependencies": { "@human-protocol/sdk": "*", - "@strapi/plugin-i18n": "4.15.5", + "@strapi/plugin-i18n": "4.16.2", "@strapi/plugin-users-permissions": "4.15.5", "@strapi/strapi": "4.15.5", "axios": "^1.5.1", diff --git a/yarn.lock b/yarn.lock index 7dc94b5357..4c6ad36ff4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5734,6 +5734,25 @@ prop-types "^15.8.1" react-remove-scroll "^2.5.7" +"@strapi/design-system@1.13.1": + version "1.13.1" + resolved "https://registry.yarnpkg.com/@strapi/design-system/-/design-system-1.13.1.tgz#42d1c7b054e3d0f19ba707b5f2e10cc0d2eba280" + integrity sha512-mtfONSTZmOKd0Mvb1NGEYx5qaA4uDPqx5q3/67kDSK2mkfjEe3j69SoQ6ONt/glvsidclWtez6R1aJmF5cojRg== + dependencies: + "@codemirror/lang-json" "^6.0.1" + "@floating-ui/react-dom" "^2.0.2" + "@internationalized/date" "^3.5.0" + "@internationalized/number" "^3.3.0" + "@radix-ui/react-dismissable-layer" "^1.0.5" + "@radix-ui/react-dropdown-menu" "^2.0.6" + "@radix-ui/react-focus-scope" "1.0.4" + "@strapi/ui-primitives" "^1.13.1" + "@uiw/react-codemirror" "^4.21.20" + aria-hidden "^1.2.3" + compute-scroll-into-view "^3.1.0" + prop-types "^15.8.1" + react-remove-scroll "^2.5.7" + "@strapi/generate-new@4.15.5": version "4.15.5" resolved "https://registry.yarnpkg.com/@strapi/generate-new/-/generate-new-4.15.5.tgz#a5b71e5ac032aa2d7f466d2f9dd6a70746cad1ba" @@ -5782,6 +5801,22 @@ react-query "3.39.3" react-select "5.7.0" +"@strapi/helper-plugin@4.16.2": + version "4.16.2" + resolved "https://registry.yarnpkg.com/@strapi/helper-plugin/-/helper-plugin-4.16.2.tgz#c9ed341965c1aba51b5adb7e413c91cac00b869e" + integrity sha512-O9ZWm/qHa2gZByuG4+YePvy9wfHykh4vUw7wufIWn4aTdv3AQdCIBmN33iiDXKd9Fxud+o7XIubXe2ZRTZbySQ== + dependencies: + axios "1.6.0" + date-fns "2.30.0" + formik "2.4.0" + immer "9.0.19" + lodash "4.17.21" + qs "6.11.1" + react-helmet "6.1.0" + react-intl "6.4.1" + react-query "3.39.3" + react-select "5.7.0" + "@strapi/icons@1.13.0": version "1.13.0" resolved "https://registry.yarnpkg.com/@strapi/icons/-/icons-1.13.0.tgz#0020560b5bed008ddc39aa177b264cb3bebe8b67" @@ -5881,16 +5916,16 @@ react-query "3.39.3" yup "0.32.9" -"@strapi/plugin-i18n@4.15.5": - version "4.15.5" - resolved "https://registry.yarnpkg.com/@strapi/plugin-i18n/-/plugin-i18n-4.15.5.tgz#3ab70971c4cfda86c0244548063f8e198c98ded6" - integrity sha512-yXHONAqsYMbKtq80DmFdFQlyARS/LNFcx130SHT6CE3h7IRnzEtRZMjOuRLypcF+vW90QyUDykXkZ5Nd4i2dKA== +"@strapi/plugin-i18n@4.16.2": + version "4.16.2" + resolved "https://registry.yarnpkg.com/@strapi/plugin-i18n/-/plugin-i18n-4.16.2.tgz#7f326515650e210885e644cf7558a19e50690efc" + integrity sha512-wHhgN4qWQ39L6Xn+02JZ2URojYlsZmDofAiRjYtTEcTrN5iTH2Gt9yC4OY4VHTOwwz3QMpZzYlY2lv9zvFvFJg== dependencies: "@reduxjs/toolkit" "1.9.7" - "@strapi/design-system" "1.13.0" - "@strapi/helper-plugin" "4.15.5" + "@strapi/design-system" "1.13.1" + "@strapi/helper-plugin" "4.16.2" "@strapi/icons" "1.13.0" - "@strapi/utils" "4.15.5" + "@strapi/utils" "4.16.2" formik "2.4.0" immer "9.0.19" lodash "4.17.21" @@ -6100,6 +6135,33 @@ aria-hidden "^1.2.3" react-remove-scroll "^2.5.7" +"@strapi/ui-primitives@^1.13.1": + version "1.14.0" + resolved "https://registry.yarnpkg.com/@strapi/ui-primitives/-/ui-primitives-1.14.0.tgz#98ff668701b4100bee8bc0212b392158c88744f7" + integrity sha512-M5RhM7/qVuu4gPvHWiSTOdI7bVDWK68aB+XyB/g1hGPqXL2Umsz8Iwn9bJPQk6YwCaHdrobaoB7lEDlfrlUAVA== + dependencies: + "@radix-ui/number" "^1.0.1" + "@radix-ui/primitive" "^1.0.1" + "@radix-ui/react-collection" "1.0.3" + "@radix-ui/react-compose-refs" "^1.0.1" + "@radix-ui/react-context" "^1.0.1" + "@radix-ui/react-direction" "1.0.1" + "@radix-ui/react-dismissable-layer" "^1.0.5" + "@radix-ui/react-focus-guards" "1.0.1" + "@radix-ui/react-focus-scope" "1.0.4" + "@radix-ui/react-id" "^1.0.1" + "@radix-ui/react-popper" "^1.1.3" + "@radix-ui/react-portal" "^1.0.4" + "@radix-ui/react-primitive" "^1.0.3" + "@radix-ui/react-slot" "^1.0.2" + "@radix-ui/react-use-callback-ref" "^1.0.1" + "@radix-ui/react-use-controllable-state" "^1.0.1" + "@radix-ui/react-use-layout-effect" "1.0.1" + "@radix-ui/react-use-previous" "^1.0.1" + "@radix-ui/react-visually-hidden" "^1.0.3" + aria-hidden "^1.2.3" + react-remove-scroll "^2.5.7" + "@strapi/utils@4.15.5": version "4.15.5" resolved "https://registry.yarnpkg.com/@strapi/utils/-/utils-4.15.5.tgz#ffb39f545bb809273a46bc5a7e11f09ddc474517" @@ -6112,6 +6174,18 @@ p-map "4.0.0" yup "0.32.9" +"@strapi/utils@4.16.2": + version "4.16.2" + resolved "https://registry.yarnpkg.com/@strapi/utils/-/utils-4.16.2.tgz#278b953872645e37ab46369f508bbe8c11ef0f02" + integrity sha512-CuAXBXXke4j0OTVuQswZ8c/Mpeb9W1DhcBhYQpc8Cwn9NeII5VGE57TrIkXL3FCU8q4fcL3IrSLaftmL6/e3uQ== + dependencies: + "@sindresorhus/slugify" "1.1.0" + date-fns "2.30.0" + http-errors "1.8.1" + lodash "4.17.21" + p-map "4.0.0" + yup "0.32.9" + "@stripe/react-stripe-js@^2.4.0": version "2.4.0" resolved "https://registry.yarnpkg.com/@stripe/react-stripe-js/-/react-stripe-js-2.4.0.tgz#b69d383a2b13e1104c5766695e7a865899cc84fb" From 8278fa79c3827e6607932e3a9fb674a348753e10 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jan 2024 22:30:58 +0800 Subject: [PATCH 019/104] Bump lint-staged from 14.0.1 to 15.2.0 (#1439) Bumps [lint-staged](https://github.com/okonet/lint-staged) from 14.0.1 to 15.2.0. - [Release notes](https://github.com/okonet/lint-staged/releases) - [Changelog](https://github.com/lint-staged/lint-staged/blob/master/CHANGELOG.md) - [Commits](https://github.com/okonet/lint-staged/compare/v14.0.1...v15.2.0) --- updated-dependencies: - dependency-name: lint-staged dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 197 +++++++++++++++++++++++++++++++++++---------------- 2 files changed, 136 insertions(+), 63 deletions(-) diff --git a/package.json b/package.json index be7ac1821d..fb015c006d 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "husky": "^8.0.2", "jest": "^29.3.1", "jest-environment-jsdom": "^29.7.0", - "lint-staged": "^14.0.1", + "lint-staged": "^15.2.0", "prettier": "^3.1.1", "ts-jest": "^29.1.1", "ts-node": "^10.9.2", diff --git a/yarn.lock b/yarn.lock index 4c6ad36ff4..fa3b3c6868 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8315,12 +8315,12 @@ ansi-escapes@^4.2.1, ansi-escapes@^4.3.0, ansi-escapes@^4.3.2: dependencies: type-fest "^0.21.3" -ansi-escapes@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-5.0.0.tgz#b6a0caf0eef0c41af190e9a749e0c00ec04bb2a6" - integrity sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA== +ansi-escapes@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-6.2.0.tgz#8a13ce75286f417f1963487d86ba9f90dccf9947" + integrity sha512-kzRaCqXnpzWs+3z5ABPQiVke+iq0KXkHo8xiWV4RPTi5Yli0l97BEQuhXV1s7+aSU/fu1kUuxgS4MsQ0fRuygw== dependencies: - type-fest "^1.0.2" + type-fest "^3.0.0" ansi-html-community@0.0.8, ansi-html-community@^0.0.8: version "0.0.8" @@ -8381,7 +8381,7 @@ ansi-styles@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== -ansi-styles@^6.0.0, ansi-styles@^6.1.0: +ansi-styles@^6.0.0, ansi-styles@^6.1.0, ansi-styles@^6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== @@ -10225,13 +10225,13 @@ cli-table3@^0.5.0: optionalDependencies: colors "^1.1.2" -cli-truncate@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-3.1.0.tgz#3f23ab12535e3d73e839bb43e73c9de487db1389" - integrity sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA== +cli-truncate@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-4.0.0.tgz#6cc28a2924fee9e25ce91e973db56c7066e6172a" + integrity sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA== dependencies: slice-ansi "^5.0.0" - string-width "^5.0.0" + string-width "^7.0.0" cli-width@^3.0.0: version "3.0.0" @@ -10470,10 +10470,10 @@ command-line-usage@^6.1.0: table-layout "^1.0.2" typical "^5.2.0" -commander@11.0.0: - version "11.0.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-11.0.0.tgz#43e19c25dbedc8256203538e8d7e9346877a6f67" - integrity sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ== +commander@11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-11.1.0.tgz#62fdce76006a68e5c1ab3314dc92e800eb83d906" + integrity sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ== commander@3.0.2: version "3.0.2" @@ -12007,6 +12007,11 @@ emittery@^0.8.1: resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860" integrity sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg== +emoji-regex@^10.3.0: + version "10.3.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.3.0.tgz#76998b9268409eb3dae3de989254d456e70cfe23" + integrity sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw== + emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" @@ -13158,19 +13163,19 @@ execa@5.1.1, execa@^5.0.0, execa@^5.1.1: signal-exit "^3.0.3" strip-final-newline "^2.0.0" -execa@7.2.0, execa@^7.1.1: - version "7.2.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-7.2.0.tgz#657e75ba984f42a70f38928cedc87d6f2d4fe4e9" - integrity sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA== +execa@8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-8.0.1.tgz#51f6a5943b580f963c3ca9c6321796db8cc39b8c" + integrity sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg== dependencies: cross-spawn "^7.0.3" - get-stream "^6.0.1" - human-signals "^4.3.0" + get-stream "^8.0.1" + human-signals "^5.0.0" is-stream "^3.0.0" merge-stream "^2.0.0" npm-run-path "^5.1.0" onetime "^6.0.0" - signal-exit "^3.0.7" + signal-exit "^4.1.0" strip-final-newline "^3.0.0" execa@^4.0.2: @@ -13188,6 +13193,21 @@ execa@^4.0.2: signal-exit "^3.0.2" strip-final-newline "^2.0.0" +execa@^7.1.1: + version "7.2.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-7.2.0.tgz#657e75ba984f42a70f38928cedc87d6f2d4fe4e9" + integrity sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.1" + human-signals "^4.3.0" + is-stream "^3.0.0" + merge-stream "^2.0.0" + npm-run-path "^5.1.0" + onetime "^6.0.0" + signal-exit "^3.0.7" + strip-final-newline "^3.0.0" + exit@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" @@ -13981,6 +14001,11 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== +get-east-asian-width@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz#5e6ebd9baee6fb8b7b6bd505221065f0cd91f64e" + integrity sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA== + get-func-name@^2.0.1, get-func-name@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41" @@ -14064,6 +14089,11 @@ get-stream@^6.0.0, get-stream@^6.0.1: resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== +get-stream@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-8.0.1.tgz#def9dfd71742cd7754a7761ed43749a27d02eca2" + integrity sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA== + get-symbol-description@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" @@ -15119,6 +15149,11 @@ human-signals@^4.3.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-4.3.1.tgz#ab7f811e851fca97ffbd2c1fe9a958964de321b2" integrity sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ== +human-signals@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28" + integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ== + humanize-ms@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" @@ -15840,6 +15875,13 @@ is-fullwidth-code-point@^4.0.0: resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz#fae3167c729e7463f8461ce512b080a49268aa88" integrity sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ== +is-fullwidth-code-point@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz#9609efced7c2f97da7b60145ef481c787c7ba704" + integrity sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA== + dependencies: + get-east-asian-width "^1.0.0" + is-function@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.2.tgz#4f097f30abf6efadac9833b17ca5dc03f8144e08" @@ -17937,10 +17979,10 @@ liftoff@^2.5.0: rechoir "^0.6.2" resolve "^1.1.7" -lilconfig@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52" - integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ== +lilconfig@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.0.0.tgz#f8067feb033b5b74dab4602a5f5029420be749bc" + integrity sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g== lines-and-columns@^1.1.6: version "1.2.4" @@ -17954,21 +17996,21 @@ linkify-it@^3.0.1: dependencies: uc.micro "^1.0.1" -lint-staged@^14.0.1: - version "14.0.1" - resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-14.0.1.tgz#57dfa3013a3d60762d9af5d9c83bdb51291a6232" - integrity sha512-Mw0cL6HXnHN1ag0mN/Dg4g6sr8uf8sn98w2Oc1ECtFto9tvRF7nkXGJRbx8gPlHyoR0pLyBr2lQHbWwmUHe1Sw== +lint-staged@^15.2.0: + version "15.2.0" + resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-15.2.0.tgz#3111534ca58096a3c8f70b044b6e7fe21b36f859" + integrity sha512-TFZzUEV00f+2YLaVPWBWGAMq7So6yQx+GG8YRMDeOEIf95Zn5RyiLMsEiX4KTNl9vq/w+NqRJkLA1kPIo15ufQ== dependencies: chalk "5.3.0" - commander "11.0.0" + commander "11.1.0" debug "4.3.4" - execa "7.2.0" - lilconfig "2.1.0" - listr2 "6.6.1" + execa "8.0.1" + lilconfig "3.0.0" + listr2 "8.0.0" micromatch "4.0.5" pidtree "0.6.0" string-argv "0.3.2" - yaml "2.3.1" + yaml "2.3.4" listhen@^1.5.5: version "1.5.5" @@ -17993,17 +18035,17 @@ listhen@^1.5.5: untun "^0.1.2" uqr "^0.1.2" -listr2@6.6.1: - version "6.6.1" - resolved "https://registry.yarnpkg.com/listr2/-/listr2-6.6.1.tgz#08b2329e7e8ba6298481464937099f4a2cd7f95d" - integrity sha512-+rAXGHh0fkEWdXBmX+L6mmfmXmXvDGEKzkjxO+8mP3+nI/r/CWznVBvsibXdxda9Zz0OW2e2ikphN3OwCT/jSg== +listr2@8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/listr2/-/listr2-8.0.0.tgz#aa7c230995f8ce378585f7c96c0c6d1cefa4700d" + integrity sha512-u8cusxAcyqAiQ2RhYvV7kRKNLgUvtObIbhOX2NCXqvp1UU32xIg5CT22ykS2TPKJXZWJwtK3IKLiqAGlGNE+Zg== dependencies: - cli-truncate "^3.1.0" + cli-truncate "^4.0.0" colorette "^2.0.20" eventemitter3 "^5.0.1" - log-update "^5.0.1" + log-update "^6.0.0" rfdc "^1.3.0" - wrap-ansi "^8.1.0" + wrap-ansi "^9.0.0" lit-element@^3.3.0: version "3.3.3" @@ -18276,16 +18318,16 @@ log-symbols@^3.0.0: dependencies: chalk "^2.4.2" -log-update@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/log-update/-/log-update-5.0.1.tgz#9e928bf70cb183c1f0c9e91d9e6b7115d597ce09" - integrity sha512-5UtUDQ/6edw4ofyljDNcOVJQ4c7OjDro4h3y8e1GQL5iYElYclVHJ3zeWchylvMaKnDbDilC8irOVyexnA/Slw== +log-update@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/log-update/-/log-update-6.0.0.tgz#0ddeb7ac6ad658c944c1de902993fce7c33f5e59" + integrity sha512-niTvB4gqvtof056rRIrTZvjNYE4rCUzO6X/X+kYjd7WFxXeJ0NwEFnRxX6ehkvv3jTwrXnNdtAak5XYZuIyPFw== dependencies: - ansi-escapes "^5.0.0" + ansi-escapes "^6.2.0" cli-cursor "^4.0.0" - slice-ansi "^5.0.0" - strip-ansi "^7.0.1" - wrap-ansi "^8.0.1" + slice-ansi "^7.0.0" + strip-ansi "^7.1.0" + wrap-ansi "^9.0.0" logform@^2.3.2, logform@^2.4.0: version "2.6.0" @@ -22707,6 +22749,11 @@ signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +signal-exit@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + simple-concat@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" @@ -22843,6 +22890,14 @@ slice-ansi@^5.0.0: ansi-styles "^6.0.0" is-fullwidth-code-point "^4.0.0" +slice-ansi@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-7.1.0.tgz#cd6b4655e298a8d1bdeb04250a433094b347b9a9" + integrity sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg== + dependencies: + ansi-styles "^6.2.1" + is-fullwidth-code-point "^5.0.0" + snake-case@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-2.1.0.tgz#41bdb1b73f30ec66a04d4e2cad1b76387d4d6d9f" @@ -23294,7 +23349,7 @@ string-width@^2.1.1: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" -string-width@^5.0.0, string-width@^5.0.1, string-width@^5.1.2: +string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== @@ -23303,6 +23358,15 @@ string-width@^5.0.0, string-width@^5.0.1, string-width@^5.1.2: emoji-regex "^9.2.2" strip-ansi "^7.0.1" +string-width@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-7.0.0.tgz#14aa1b7aaa126d5b64fa79d3c894da8a9650ba06" + integrity sha512-GPQHj7row82Hjo9hKZieKcHIhaAIKOJvFSIZXuCU9OASVZrMNUaZuz++SPVrBjnLsnk4k+z9f2EIypgxf2vNFw== + dependencies: + emoji-regex "^10.3.0" + get-east-asian-width "^1.0.0" + strip-ansi "^7.1.0" + string.prototype.matchall@^4.0.8: version "4.0.10" resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz#a1553eb532221d4180c51581d6072cd65d1ee100" @@ -23392,7 +23456,7 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" -strip-ansi@^7.0.1: +strip-ansi@^7.0.1, strip-ansi@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== @@ -24374,16 +24438,16 @@ type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== -type-fest@^1.0.2: - version "1.4.0" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-1.4.0.tgz#e9fb813fe3bf1744ec359d55d1affefa76f14be1" - integrity sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA== - type-fest@^2.13.0, type-fest@^2.18.0, type-fest@^2.19.0: version "2.19.0" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b" integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA== +type-fest@^3.0.0: + version "3.13.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-3.13.1.tgz#bb744c1f0678bea7543a2d1ec24e83e68e8c8706" + integrity sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g== + type-is@^1.6.14, type-is@^1.6.16, type-is@^1.6.4, type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" @@ -25953,7 +26017,7 @@ wrap-ansi@^7.0.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^8.0.1, wrap-ansi@^8.1.0: +wrap-ansi@^8.0.1: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== @@ -25962,6 +26026,15 @@ wrap-ansi@^8.0.1, wrap-ansi@^8.1.0: string-width "^5.0.1" strip-ansi "^7.0.1" +wrap-ansi@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-9.0.0.tgz#1a3dc8b70d85eeb8398ddfb1e4a02cd186e58b3e" + integrity sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q== + dependencies: + ansi-styles "^6.2.1" + string-width "^7.0.0" + strip-ansi "^7.1.0" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -26135,10 +26208,10 @@ yaml@1.10.2, yaml@^1.10.0, yaml@^1.10.2, yaml@^1.7.2: resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== -yaml@2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.1.tgz#02fe0975d23cd441242aa7204e09fc28ac2ac33b" - integrity sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ== +yaml@2.3.4: + version "2.3.4" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.4.tgz#53fc1d514be80aabf386dc6001eb29bf3b7523b2" + integrity sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA== yargs-parser@20.2.4: version "20.2.4" From fdfad7f5281c209666bd4b955231d03139ee7d65 Mon Sep 17 00:00:00 2001 From: m00n620 <50647994+m00n620@users.noreply.github.com> Date: Mon, 8 Jan 2024 10:36:54 -0500 Subject: [PATCH 020/104] [Job Launcher] CVAT S3 buckets (#1384) * support s3 providers on cvat * disable GCS --------- Co-authored-by: portuu3 --- .../client/src/components/Accordion/index.tsx | 37 ++ .../Jobs/Create/CvatJobRequestForm.tsx | 563 +++++++++++++----- .../Jobs/Create/FortuneJobRequestForm.tsx | 149 +++-- .../Jobs/Create/HCaptchaJobRequestForm.tsx | 47 +- .../src/components/Jobs/Create/schema.ts | 12 +- .../job-launcher/client/src/services/job.ts | 4 +- .../job-launcher/client/src/types/index.ts | 97 ++- 7 files changed, 641 insertions(+), 268 deletions(-) create mode 100644 packages/apps/job-launcher/client/src/components/Accordion/index.tsx diff --git a/packages/apps/job-launcher/client/src/components/Accordion/index.tsx b/packages/apps/job-launcher/client/src/components/Accordion/index.tsx new file mode 100644 index 0000000000..46e4c640b7 --- /dev/null +++ b/packages/apps/job-launcher/client/src/components/Accordion/index.tsx @@ -0,0 +1,37 @@ +import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'; +import MuiAccordion, { AccordionProps } from '@mui/material/Accordion'; +import MuiAccordionDetails from '@mui/material/AccordionDetails'; +import MuiAccordionSummary, { + AccordionSummaryProps, +} from '@mui/material/AccordionSummary'; +import { styled } from '@mui/material/styles'; + +export const Accordion = styled((props: AccordionProps) => ( + +))(({ theme }) => ({ + marginBottom: '42px', + '&:not(:last-child)': { + borderBottom: 0, + }, + '&:before': { + display: 'none', + }, +})); + +export const AccordionSummary = styled((props: AccordionSummaryProps) => ( + } + {...props} + /> +))(({ theme }) => ({ + backgroundColor: `rgba(20, 6, 178, 0.08)`, + borderRadius: '12px', + padding: '8px 32px', + '& .MuiAccordionSummary-expandIconWrapper.Mui-expanded': { + transform: 'rotate(180deg)', + }, +})); + +export const AccordionDetails = styled(MuiAccordionDetails)(({ theme }) => ({ + padding: '42px 0px 22px', +})); diff --git a/packages/apps/job-launcher/client/src/components/Jobs/Create/CvatJobRequestForm.tsx b/packages/apps/job-launcher/client/src/components/Jobs/Create/CvatJobRequestForm.tsx index 451829039d..63cf3a9b4c 100644 --- a/packages/apps/job-launcher/client/src/components/Jobs/Create/CvatJobRequestForm.tsx +++ b/packages/apps/job-launcher/client/src/components/Jobs/Create/CvatJobRequestForm.tsx @@ -13,35 +13,65 @@ import { Select, TextField, Tooltip, + Typography, } from '@mui/material'; import { useFormik } from 'formik'; -import React, { useEffect } from 'react'; +import React, { useEffect, useState } from 'react'; import { useSearchParams } from 'react-router-dom'; +import { + Accordion, + AccordionSummary, + AccordionDetails, +} from '../../../components/Accordion'; +import { CollectionsFilledIcon } from '../../../components/Icons/CollectionsFilledIcon'; import { useCreateJobPageUI } from '../../../providers/CreateJobPageUIProvider'; -import { CvatJobType } from '../../../types'; +import { + AWSRegions, + CvatJobType, + GCSRegions, + StorageProviders, +} from '../../../types'; import { CvatJobRequestValidationSchema } from './schema'; export const CvatJobRequestForm = () => { const { jobRequest, updateJobRequest, goToPrevStep, goToNextStep } = useCreateJobPageUI(); const [searchParams] = useSearchParams(); + const [expanded, setExpanded] = useState('panel1'); const initialValues = { labels: [], type: CvatJobType.IMAGE_BOXES, description: '', - dataUrl: '', - groundTruthUrl: '', userGuide: '', accuracyTarget: 80, + dataProvider: StorageProviders.AWS, + dataRegion: '', + dataBucketName: '', + dataPath: '', + gtProvider: StorageProviders.AWS, + gtRegion: '', + gtBucketName: '', + gtPath: '', }; + const handleChange = + (panel: string) => (event: React.SyntheticEvent, newExpanded: boolean) => { + setExpanded(newExpanded ? panel : false); + }; + const handleNext = ({ labels, type, description, - dataUrl, - groundTruthUrl, + dataProvider, + dataRegion, + dataBucketName, + dataPath, + gtProvider, + gtRegion, + gtBucketName, + gtPath, userGuide, accuracyTarget, }: any) => { @@ -51,8 +81,18 @@ export const CvatJobRequestForm = () => { labels, type, description, - dataUrl, - groundTruthUrl, + data: { + provider: dataProvider, + region: dataRegion, + bucketName: dataBucketName, + path: dataPath, + }, + groundTruth: { + provider: gtProvider, + region: gtRegion, + bucketName: gtBucketName, + path: gtPath, + }, userGuide, accuracyTarget, }, @@ -83,161 +123,362 @@ export const CvatJobRequestForm = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + const dataRegions = + values.dataProvider === StorageProviders.AWS ? AWSRegions : GCSRegions; + + const gtRegions = + values.gtProvider === StorageProviders.AWS ? AWSRegions : GCSRegions; + return ( - +
- - - - - Type of job - - - - - setFieldValue('description', e.target.value)} - onBlur={handleBlur} - placeholder="Description" - label="Description" - error={touched.description && Boolean(errors.description)} - helperText={errors.description} - multiline - rows={11} - /> - - - - - - value.map((option, index) => ( - - )) - } - renderInput={(params) => ( - - )} - onChange={(e, value) => setFieldValue('labels', value)} - onBlur={handleBlur} - placeholder="Labels" - /> - {errors.labels && ( - - {errors.labels} - - )} - - - setFieldValue('dataUrl', e.target.value)} - onBlur={handleBlur} - placeholder="Data URL" - label="Data URL" - error={touched.dataUrl && Boolean(errors.dataUrl)} - helperText={errors.dataUrl} - /> - - - - setFieldValue('groundTruthUrl', e.target.value) - } - onBlur={handleBlur} - placeholder="Reference data for annotation accuracy" - label="Ground Truth URL" - error={touched.groundTruthUrl && Boolean(errors.groundTruthUrl)} - helperText={errors.groundTruthUrl} - InputProps={{ - endAdornment: ( - - - - - - ), - }} - /> - - - setFieldValue('userGuide', e.target.value)} - onBlur={handleBlur} - placeholder="Annotator's guideline for data labeling" - label="User Guide URL" - error={touched.userGuide && Boolean(errors.userGuide)} - helperText={errors.userGuide} - InputProps={{ - endAdornment: ( - - - - - - ), - }} - /> - - - - setFieldValue('accuracyTarget', e.target.value) - } - onBlur={handleBlur} - placeholder="Accuracy target %" - label="Accuracy target %" - error={touched.accuracyTarget && Boolean(errors.accuracyTarget)} - helperText={errors.accuracyTarget} - /> - - - + + + + + General + + + + + + + + Type of job + + + + + + + + value.map((option, index) => ( + + )) + } + renderInput={(params) => ( + + )} + onChange={(e, value) => setFieldValue('labels', value)} + onBlur={handleBlur} + /> + {errors.labels && ( + + {errors.labels} + + )} + + + + + + setFieldValue('description', e.target.value) + } + onBlur={handleBlur} + placeholder="Description" + label="Description" + error={touched.description && Boolean(errors.description)} + helperText={errors.description} + multiline + rows={4} + /> + + + + + + + + + + Job annotation details + + + + + + + + Datasets + + + + + + Storage Provider + + + + + + + + Region + + + {errors.dataRegion && ( + + {errors.dataRegion} + + )} + + + + + + setFieldValue('dataBucketName', e.target.value) + } + error={ + touched.dataBucketName && Boolean(errors.dataBucketName) + } + helperText={errors.dataBucketName} + /> + + + + + + setFieldValue('dataPath', e.target.value) + } + error={touched.dataPath && Boolean(errors.dataPath)} + helperText={errors.dataPath} + /> + + + + + + + Ground truth + + + + + + Storage Provider + + + + + + + + Region + + + {errors.dataRegion && ( + + {errors.dataRegion} + + )} + + + + + + setFieldValue('gtBucketName', e.target.value) + } + error={ + touched.gtBucketName && Boolean(errors.gtBucketName) + } + helperText={errors.gtBucketName} + /> + + + + + setFieldValue('gtPath', e.target.value)} + onBlur={handleBlur} + error={touched.gtPath && Boolean(errors.gtPath)} + InputProps={{ + endAdornment: ( + + + + + + ), + }} + /> + + + + + + + + setFieldValue('userGuide', e.target.value) + } + onBlur={handleBlur} + placeholder="Annotator's guideline for data labeling" + label="User Guide URL" + error={touched.userGuide && Boolean(errors.userGuide)} + helperText={errors.userGuide} + InputProps={{ + endAdornment: ( + + + + + + ), + }} + /> + + + + + + setFieldValue('accuracyTarget', e.target.value) + } + onBlur={handleBlur} + error={ + touched.accuracyTarget && Boolean(errors.accuracyTarget) + } + helperText={errors.accuracyTarget} + InputProps={{ + endAdornment: ( + + + + + + ), + }} + /> + + + + + + { const { jobRequest, updateJobRequest, goToPrevStep, goToNextStep } = useCreateJobPageUI(); + const [expanded, setExpanded] = useState('panel1'); const initialValues = { title: '', @@ -14,6 +28,11 @@ export const FortuneJobRequestForm = () => { description: '', }; + const handleChange = + (panel: string) => (event: React.SyntheticEvent, newExpanded: boolean) => { + setExpanded(newExpanded ? panel : false); + }; + const handleNext = ({ title, fortunesRequested, description }: any) => { updateJobRequest?.({ ...jobRequest, @@ -27,7 +46,7 @@ export const FortuneJobRequestForm = () => { }; return ( - + { setFieldValue, }) => ( - - - - setFieldValue('title', e.target.value)} - onBlur={handleBlur} - placeholder="Title" - label="Title" - error={touched.title && Boolean(errors.title)} - helperText={errors.title} - /> - - - - - - setFieldValue('fortunesRequested', e.target.value) - } - onBlur={handleBlur} - placeholder="Fortunes Requested" - label="Fortunes Requested" - type="number" - inputProps={{ min: 0, step: 1 }} - error={ - touched.fortunesRequested && - Boolean(errors.fortunesRequested) - } - helperText={errors.fortunesRequested} - /> - - - - - - setFieldValue('description', e.target.value) - } - onBlur={handleBlur} - placeholder="Description" - label="Description" - error={touched.description && Boolean(errors.description)} - helperText={errors.description} - /> - - - + + + + + General + + + + + + + setFieldValue('title', e.target.value)} + onBlur={handleBlur} + placeholder="Title" + label="Title" + error={touched.title && Boolean(errors.title)} + helperText={errors.title} + /> + + + + + + setFieldValue('fortunesRequested', e.target.value) + } + onBlur={handleBlur} + placeholder="Fortunes Requested" + label="Fortunes Requested" + type="number" + inputProps={{ min: 0, step: 1 }} + error={ + touched.fortunesRequested && + Boolean(errors.fortunesRequested) + } + helperText={errors.fortunesRequested} + /> + + + + + + setFieldValue('description', e.target.value) + } + onBlur={handleBlur} + placeholder="Description" + label="Description" + error={ + touched.description && Boolean(errors.description) + } + helperText={errors.description} + /> + + + + + ( - -))(({ theme }) => ({ - marginBottom: '42px', - '&:not(:last-child)': { - borderBottom: 0, - }, - '&:before': { - display: 'none', - }, -})); - -const AccordionSummary = styled((props: AccordionSummaryProps) => ( - } - {...props} - /> -))(({ theme }) => ({ - backgroundColor: `rgba(20, 6, 178, 0.08)`, - borderRadius: '12px', - padding: '8px 32px', - '& .MuiAccordionSummary-expandIconWrapper.Mui-expanded': { - transform: 'rotate(180deg)', - }, -})); - -const AccordionDetails = styled(MuiAccordionDetails)(({ theme }) => ({ - padding: '42px 0px 22px', -})); - export const HCaptchaJobRequestForm = () => { const { jobRequest, updateJobRequest, goToPrevStep, goToNextStep } = useCreateJobPageUI(); @@ -309,10 +277,7 @@ export const HCaptchaJobRequestForm = () => { expanded={expanded === 'panel2'} onChange={handleChange('panel2')} > - + Job annotation details diff --git a/packages/apps/job-launcher/client/src/components/Jobs/Create/schema.ts b/packages/apps/job-launcher/client/src/components/Jobs/Create/schema.ts index f7bd3ed1b7..2c08d25625 100644 --- a/packages/apps/job-launcher/client/src/components/Jobs/Create/schema.ts +++ b/packages/apps/job-launcher/client/src/components/Jobs/Create/schema.ts @@ -3,10 +3,14 @@ import * as Yup from 'yup'; export const CvatJobRequestValidationSchema = Yup.object().shape({ labels: Yup.array().of(Yup.string()).min(1, 'At least one label is required'), description: Yup.string().required('Description is required'), - dataUrl: Yup.string().required('Data URL is required').url('Invalid URL'), - groundTruthUrl: Yup.string() - .required('Ground Truth URL is required') - .url('Invalid URL'), + dataProvider: Yup.string().required('Data provider is required'), + dataRegion: Yup.string().required('Data region is required'), + dataBucketName: Yup.string().required('Data bucket name is required'), + dataPath: Yup.string().required('Data path is required'), + gtProvider: Yup.string().required('Ground truth provider is required'), + gtRegion: Yup.string().required('Ground truth region is required'), + gtBucketName: Yup.string().required('Ground truth bucket name is required'), + gtPath: Yup.string().required('Ground truth path is required'), userGuide: Yup.string() .required('User Guide URL is required') .url('Invalid URL'), diff --git a/packages/apps/job-launcher/client/src/services/job.ts b/packages/apps/job-launcher/client/src/services/job.ts index e44976f6ec..3ba1d0f4a7 100644 --- a/packages/apps/job-launcher/client/src/services/job.ts +++ b/packages/apps/job-launcher/client/src/services/job.ts @@ -36,10 +36,10 @@ export const createCvatJob = async ( chainId, requesterDescription: data.description, fundAmount: Number(amount), - dataUrl: data.dataUrl, + data: data.data, labels: data.labels, minQuality: Number(data.accuracyTarget) / 100, - gtUrl: data.groundTruthUrl, + groundTruth: data.groundTruth, userGuide: data.userGuide, type: data.type, }; diff --git a/packages/apps/job-launcher/client/src/types/index.ts b/packages/apps/job-launcher/client/src/types/index.ts index 3783b708d1..1bb50b9619 100644 --- a/packages/apps/job-launcher/client/src/types/index.ts +++ b/packages/apps/job-launcher/client/src/types/index.ts @@ -44,10 +44,10 @@ export type CreateCvatJobRequest = { chainId: number; requesterDescription: string; fundAmount: number; - dataUrl: string; + data: CvatDataSource; labels: string[]; minQuality: number; - gtUrl: string; + groundTruth: CvatDataSource; userGuide: string; type: CvatJobType; }; @@ -89,12 +89,101 @@ export type FortuneRequest = { description: string; }; +export enum StorageProviders { + AWS = 'AWS', + GCS = 'GCS', +} + +export enum AWSRegions { + AF_SOUTH_1 = 'af-south-1', + AP_EAST_1 = 'ap-east-1', + AP_NORTHEAST_1 = 'ap-northeast-1', + AP_NORTHEAST_2 = 'ap-northeast-2', + AP_NORTHEAST_3 = 'ap-northeast-3', + AP_SOUTH_1 = 'ap-south-1', + AP_SOUTH_2 = 'ap-south-2', + AP_SOUTHEAST_1 = 'ap-southeast-1', + AP_SOUTHEAST_2 = 'ap-southeast-2', + AP_SOUTHEAST_3 = 'ap-southeast-3', + AP_SOUTHEAST_4 = 'ap-southeast-4', + CA_CENTRAL_1 = 'ca-central-1', + CN_NORTH_1 = 'cn-north-1', + CN_NORTHWEST_1 = 'cn-northwest-1', + EU_CENTRAL_1 = 'eu-central-1', + EU_CENTRAL_2 = 'eu-central-2', + EU_NORTH_1 = 'eu-north-1', + EU_SOUTH_1 = 'eu-south-1', + EU_SOUTH_2 = 'eu-south-2', + EU_WEST_1 = 'eu-west-1', + EU_WEST_2 = 'eu-west-2', + EU_WEST_3 = 'eu-west-3', + IL_CENTRAL_1 = 'il-central-1', + ME_CENTRAL_1 = 'me-central-1', + ME_SOUTH_1 = 'me-south-1', + SA_EAST_1 = 'sa-east-1', + US_EAST_1 = 'us-east-1', + US_EAST_2 = 'us-east-2', + US_GOV_EAST_1 = 'us-gov-east-1', + US_GOV_WEST_1 = 'us-gov-west-1', + US_WEST_1 = 'us-west-1', + US_WEST_2 = 'us-west-2', +} + +export enum GCSRegions { + ASIA_EAST1 = 'asia-east1', // Taiwan + ASIA_EAST2 = 'asia-east2', // Hong Kong + ASIA_NORTHEAST1 = 'asia-northeast1', // Tokyo + ASIA_NORTHEAST2 = 'asia-northeast2', // Osaka + ASIA_NORTHEAST3 = 'asia-northeast3', // Seoul + ASIA_SOUTH1 = 'asia-south1', // Bombay + ASIA_SOUTH2 = 'asia-south2', // Delhi + ASIA_SOUTHEAST1 = 'asia-southeast1', // Singapore + ASIA_SOUTHEAST2 = 'asia-southeast2', // Jakarta + AUSTRALIA_SOUTHEAST1 = 'australia-southeast1', // Sydney + AUSTRALIA_SOUTHEAST2 = 'australia-southeast2', // Melbourne + EUROPE_CENTRAL2 = 'europe-central2', // Warsaw + EUROPE_NORTH1 = 'europe-north1', // Finland (Low CO2 footprint) + EUROPE_SOUTHWEST1 = 'europe-southwest1', // Madrid + EUROPE_WEST1 = 'europe-west1', // Belgium (Low CO2 footprint) + EUROPE_WEST2 = 'europe-west2', // London (Low CO2 footprint) + EUROPE_WEST3 = 'europe-west3', // Frankfurt (Low CO2 footprint) + EUROPE_WEST4 = 'europe-west4', // Netherlands + EUROPE_WEST6 = 'europe-west6', // Zurich (Low CO2 footprint) + EUROPE_WEST8 = 'europe-west8', // Milan + EUROPE_WEST9 = 'europe-west9', // Paris (Low CO2 footprint) + EUROPE_WEST10 = 'europe-west10', // Berlin + EUROPE_WEST12 = 'europe-west12', // Turin + ME_CENTRAL1 = 'me-central1', // Doha + ME_CENTRAL2 = 'me-central2', // Dammam, Saudi Arabia + ME_WEST1 = 'me-west1', // Tel Aviv + NORTHAMERICA_NORTHEAST1 = 'northamerica-northeast1', // Montreal (Low CO2 footprint) + NORTHAMERICA_NORTHEAST2 = 'northamerica-northeast2', // Toronto (Low CO2 footprint) + SOUTHAMERICA_EAST1 = 'southamerica-east1', // São Paulo (Low CO2 footprint) + SOUTHAMERICA_WEST1 = 'southamerica-west1', // Santiago (Low CO2 footprint) + US_CENTRAL1 = 'us-central1', // Iowa (Low CO2 footprint) + US_EAST1 = 'us-east1', // South Carolina + US_EAST4 = 'us-east4', // Northern Virginia + US_EAST5 = 'us-east5', // Columbus + US_SOUTH1 = 'us-south1', // Dallas + US_WEST1 = 'us-west1', // Oregon (Low CO2 footprint) + US_WEST2 = 'us-west2', // Los Angeles + US_WEST3 = 'us-west3', // Salt Lake City + US_WEST4 = 'us-west4', // Las Vegas +} + +type CvatDataSource = { + provider: StorageProviders; + region: AWSRegions | GCSRegions; + bucketName: string; + path: string; +}; + export type CvatRequest = { labels: string[]; type: CvatJobType; description: string; - dataUrl: string; - groundTruthUrl: string; + data: CvatDataSource; + groundTruth: CvatDataSource; userGuide: string; accuracyTarget: number; }; From 8ee620794d6f662b9604e0edfb869e7f43905571 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20L=C3=B3pez?= <50665615+flopez7@users.noreply.github.com> Date: Tue, 9 Jan 2024 11:25:12 +0100 Subject: [PATCH 021/104] Ethers v6 (#1428) Update ethers to v6 and all its references --- .../classes/base.BaseEthersClient.md | 16 +- .../classes/encryption.Encryption.md | 12 +- .../classes/encryption.EncryptionUtils.md | 10 +- .../typescript/classes/escrow.EscrowClient.md | 102 +- .../typescript/classes/escrow.EscrowUtils.md | 4 +- .../classes/kvstore.KVStoreClient.md | 42 +- .../classes/staking.StakingClient.md | 84 +- .../classes/statistics.StatisticsClient.md | 12 +- .../classes/storage.StorageClient.md | 16 +- package.json | 3 +- .../exchange-oracle/server/package.json | 12 +- .../server/src/common/utils/signature.ts | 2 +- .../server/src/common/validators/ethers.ts | 2 +- .../src/modules/web3/web3.service.spec.ts | 1 - .../server/src/modules/web3/web3.service.ts | 4 +- .../fortune/recording-oracle/package.json | 3 +- .../src/common/utils/signature.ts | 2 +- .../src/common/validators/ethers.ts | 2 +- .../src/modules/job/job.service.ts | 3 +- .../src/modules/web3/web3.service.spec.ts | 1 - .../src/modules/web3/web3.service.ts | 4 +- .../hufi/exchange-oracle/server/package.json | 1 - .../server/src/common/utils/signature.ts | 2 +- .../server/src/common/validators/ethers.ts | 2 +- .../src/modules/web3/web3.service.spec.ts | 1 - .../server/src/modules/web3/web3.service.ts | 4 +- .../hufi/job-launcher/server/package.json | 1 - .../server/src/common/utils/signature.ts | 2 +- .../server/src/modules/job/job.dto.ts | 5 +- .../src/modules/job/job.service.spec.ts | 4 +- .../server/src/modules/job/job.service.ts | 50 +- .../modules/payment/payment.service.spec.ts | 28 +- .../src/modules/payment/payment.service.ts | 55 +- .../server/src/modules/web3/Web3Service.ts | 57 + .../src/modules/web3/web3.controller.ts | 2 +- .../server/src/modules/web3/web3.module.ts | 2 +- .../src/modules/web3/web3.service.spec.ts | 2 +- .../server/src/modules/web3/web3.service.ts | 4 +- .../hufi/job-launcher/server/tsconfig.json | 4 +- .../apps/hufi/recording-oracle/package.json | 1 - .../src/common/utils/signature.ts | 2 +- .../src/common/validators/ethers.ts | 2 +- .../modules/liquidity/liquidity.service.ts | 107 +- .../src/modules/web3/web3.service.ts | 4 +- .../reputation-oracle/server/package.json | 45 +- .../server/src/common/utils/signature.ts | 2 +- .../server/src/modules/web3/web3.service.ts | 4 +- .../src/modules/webhook/webhook.service.ts | 21 +- .../reputation-oracle/server/tsconfig.json | 2 +- .../apps/job-launcher/server/package.json | 3 +- .../server/src/common/utils/signature.ts | 2 +- .../server/src/modules/job/job.dto.ts | 3 +- .../src/modules/job/job.service.spec.ts | 40 +- .../server/src/modules/job/job.service.ts | 54 +- .../modules/payment/payment.service.spec.ts | 47 +- .../src/modules/payment/payment.service.ts | 19 +- .../server/src/modules/web3/web3.service.ts | 15 +- .../apps/job-launcher/server/tsconfig.json | 4 +- .../reputation-oracle/server/package.json | 1 - .../server/src/common/utils/signature.ts | 4 +- .../src/modules/web3/web3.service.spec.ts | 1 - .../server/src/modules/web3/web3.service.ts | 13 +- .../server/src/modules/webhook/webhook.dto.ts | 3 +- .../modules/webhook/webhook.service.spec.ts | 11 +- .../src/modules/webhook/webhook.service.ts | 20 +- .../reputation-oracle/server/tsconfig.json | 2 +- packages/core/hardhat.config.ts | 3 - packages/core/package.json | 24 +- packages/core/scripts/deploy-proxies.ts | 31 +- packages/core/scripts/deploy.ts | 50 +- packages/core/scripts/upgrade-proxies.ts | 17 +- packages/core/test/Escrow.ts | 88 +- packages/core/test/EscrowFactory.ts | 76 +- packages/core/test/KVStore.ts | 4 +- packages/core/test/RewardPool.ts | 70 +- packages/core/test/Staking.ts | 136 +- .../human-protocol-sdk/package.json | 3 +- .../typescript/human-protocol-sdk/src/base.ts | 11 +- .../human-protocol-sdk/src/decorators.ts | 5 +- .../human-protocol-sdk/src/error.ts | 12 - .../human-protocol-sdk/src/escrow.ts | 215 +-- .../human-protocol-sdk/src/graphql/types.ts | 12 +- .../human-protocol-sdk/src/interfaces.ts | 35 +- .../human-protocol-sdk/src/kvstore.ts | 58 +- .../human-protocol-sdk/src/staking.ts | 110 +- .../human-protocol-sdk/src/statistics.ts | 17 +- .../human-protocol-sdk/src/types.ts | 4 +- .../human-protocol-sdk/src/utils.ts | 18 +- .../human-protocol-sdk/test/escrow.test.ts | 677 ++++--- .../human-protocol-sdk/test/kvstore.test.ts | 39 +- .../human-protocol-sdk/test/staking.test.ts | 273 ++- .../test/statistics.test.ts | 12 +- yarn.lock | 1662 +++++++++-------- 93 files changed, 2346 insertions(+), 2311 deletions(-) create mode 100644 packages/apps/hufi/job-launcher/server/src/modules/web3/Web3Service.ts diff --git a/docs/sdk/typescript/classes/base.BaseEthersClient.md b/docs/sdk/typescript/classes/base.BaseEthersClient.md index 4a76a23c9c..c4476f952b 100644 --- a/docs/sdk/typescript/classes/base.BaseEthersClient.md +++ b/docs/sdk/typescript/classes/base.BaseEthersClient.md @@ -27,13 +27,13 @@ This class is used as a base class for other clients making on-chain calls. ### Properties - [networkData](base.BaseEthersClient.md#networkdata) -- [signerOrProvider](base.BaseEthersClient.md#signerorprovider) +- [runner](base.BaseEthersClient.md#runner) ## Constructors ### constructor -• **new BaseEthersClient**(`signerOrProvider`, `networkData`): [`BaseEthersClient`](base.BaseEthersClient.md) +• **new BaseEthersClient**(`runner`, `networkData`): [`BaseEthersClient`](base.BaseEthersClient.md) **BaseClient constructor** @@ -41,7 +41,7 @@ This class is used as a base class for other clients making on-chain calls. | Name | Type | Description | | :------ | :------ | :------ | -| `signerOrProvider` | `Signer` \| `Provider` | The Signer or Provider object to interact with the Ethereum network | +| `runner` | `ContractRunner` | The Signer or Provider object to interact with the Ethereum network | | `networkData` | `NetworkData` | The network information required to connect to the contracts | #### Returns @@ -50,7 +50,7 @@ This class is used as a base class for other clients making on-chain calls. #### Defined in -[base.ts:21](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/base.ts#L21) +[base.ts:20](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/base.ts#L20) ## Properties @@ -60,14 +60,14 @@ This class is used as a base class for other clients making on-chain calls. #### Defined in -[base.ts:13](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/base.ts#L13) +[base.ts:12](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/base.ts#L12) ___ -### signerOrProvider +### runner -• `Protected` **signerOrProvider**: `Signer` \| `Provider` +• `Protected` **runner**: `ContractRunner` #### Defined in -[base.ts:12](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/base.ts#L12) +[base.ts:11](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/base.ts#L11) diff --git a/docs/sdk/typescript/classes/encryption.Encryption.md b/docs/sdk/typescript/classes/encryption.Encryption.md index 5b6fc3dcc8..7ac03b5e67 100644 --- a/docs/sdk/typescript/classes/encryption.Encryption.md +++ b/docs/sdk/typescript/classes/encryption.Encryption.md @@ -81,7 +81,7 @@ Constructor for the Encryption class. #### Defined in -[encryption.ts:53](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/encryption.ts#L53) +[encryption.ts:53](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/encryption.ts#L53) ## Properties @@ -91,7 +91,7 @@ Constructor for the Encryption class. #### Defined in -[encryption.ts:46](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/encryption.ts#L46) +[encryption.ts:46](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/encryption.ts#L46) ## Methods @@ -140,7 +140,7 @@ const resultMessage = await encription.decrypt('message'); #### Defined in -[encryption.ts:180](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/encryption.ts#L180) +[encryption.ts:180](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/encryption.ts#L180) ___ @@ -176,7 +176,7 @@ const resultMessage = await encription.sign('message'); #### Defined in -[encryption.ts:217](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/encryption.ts#L217) +[encryption.ts:217](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/encryption.ts#L217) ___ @@ -238,7 +238,7 @@ const resultMessage = await encription.signAndEncrypt('message', publicKeys); #### Defined in -[encryption.ts:129](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/encryption.ts#L129) +[encryption.ts:129](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/encryption.ts#L129) ___ @@ -263,4 +263,4 @@ Builds an Encryption instance by decrypting the private key from an encrypted pr #### Defined in -[encryption.ts:64](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/encryption.ts#L64) +[encryption.ts:64](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/encryption.ts#L64) diff --git a/docs/sdk/typescript/classes/encryption.EncryptionUtils.md b/docs/sdk/typescript/classes/encryption.EncryptionUtils.md index cc5a1c1bbe..c07321c1f5 100644 --- a/docs/sdk/typescript/classes/encryption.EncryptionUtils.md +++ b/docs/sdk/typescript/classes/encryption.EncryptionUtils.md @@ -108,7 +108,7 @@ const result = await EncriptionUtils.encrypt('message', publicKeys); #### Defined in -[encryption.ts:422](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/encryption.ts#L422) +[encryption.ts:422](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/encryption.ts#L422) ___ @@ -157,7 +157,7 @@ const result = await EncriptionUtils.generateKeyPair(name, email, passphrase); #### Defined in -[encryption.ts:360](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/encryption.ts#L360) +[encryption.ts:360](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/encryption.ts#L360) ___ @@ -189,7 +189,7 @@ const signedData = await EncriptionUtils.getSignedData('message'); #### Defined in -[encryption.ts:317](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/encryption.ts#L317) +[encryption.ts:317](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/encryption.ts#L317) ___ @@ -237,7 +237,7 @@ if (isEncrypted) { #### Defined in -[encryption.ts:471](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/encryption.ts#L471) +[encryption.ts:471](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/encryption.ts#L471) ___ @@ -282,4 +282,4 @@ const result = await EncriptionUtils.verify('message', publicKey); #### Defined in -[encryption.ts:284](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/encryption.ts#L284) +[encryption.ts:284](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/encryption.ts#L284) diff --git a/docs/sdk/typescript/classes/escrow.EscrowClient.md b/docs/sdk/typescript/classes/escrow.EscrowClient.md index 26f0b3cea6..4a443333ca 100644 --- a/docs/sdk/typescript/classes/escrow.EscrowClient.md +++ b/docs/sdk/typescript/classes/escrow.EscrowClient.md @@ -8,11 +8,11 @@ This client enables to perform actions on Escrow contracts and obtain information from both the contracts and subgraph. -Internally, the SDK will use one network or another according to the network ID of the `signerOrProvider`. +Internally, the SDK will use one network or another according to the network ID of the `runner`. To use this client, it is recommended to initialize it using the static `build` method. ```ts -static async build(signerOrProvider: Signer | Provider); +static async build(runner: ContractRunner); ``` A `Signer` or a `Provider` should be passed depending on the use case of this module: @@ -88,7 +88,7 @@ const escrowClient = await EscrowClient.build(provider); - [escrowFactoryContract](escrow.EscrowClient.md#escrowfactorycontract) - [networkData](escrow.EscrowClient.md#networkdata) -- [signerOrProvider](escrow.EscrowClient.md#signerorprovider) +- [runner](escrow.EscrowClient.md#runner) ### Methods @@ -121,7 +121,7 @@ const escrowClient = await EscrowClient.build(provider); ### constructor -• **new EscrowClient**(`signerOrProvider`, `networkData`): [`EscrowClient`](escrow.EscrowClient.md) +• **new EscrowClient**(`runner`, `networkData`): [`EscrowClient`](escrow.EscrowClient.md) **EscrowClient constructor** @@ -129,7 +129,7 @@ const escrowClient = await EscrowClient.build(provider); | Name | Type | Description | | :------ | :------ | :------ | -| `signerOrProvider` | `Signer` \| `Provider` | The Signer or Provider object to interact with the Ethereum network | +| `runner` | `ContractRunner` | The Runner object to interact with the Ethereum network | | `networkData` | `NetworkData` | - | #### Returns @@ -142,7 +142,7 @@ const escrowClient = await EscrowClient.build(provider); #### Defined in -[escrow.ts:129](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L129) +[escrow.ts:127](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L127) ## Properties @@ -152,7 +152,7 @@ const escrowClient = await EscrowClient.build(provider); #### Defined in -[escrow.ts:121](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L121) +[escrow.ts:119](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L119) ___ @@ -166,21 +166,21 @@ ___ #### Defined in -[base.ts:13](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/base.ts#L13) +[base.ts:12](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/base.ts#L12) ___ -### signerOrProvider +### runner -• `Protected` **signerOrProvider**: `Signer` \| `Provider` +• `Protected` **runner**: `ContractRunner` #### Inherited from -[BaseEthersClient](base.BaseEthersClient.md).[signerOrProvider](base.BaseEthersClient.md#signerorprovider) +[BaseEthersClient](base.BaseEthersClient.md).[runner](base.BaseEthersClient.md#runner) #### Defined in -[base.ts:12](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/base.ts#L12) +[base.ts:11](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/base.ts#L11) ## Methods @@ -223,7 +223,7 @@ await escrowClient.abort('0x62dD51230A30401C455c8398d06F85e4EaB6309f'); #### Defined in -[escrow.ts:839](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L839) +[escrow.ts:835](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L835) ___ @@ -268,7 +268,7 @@ await escrowClient.addTrustedHandlers('0x62dD51230A30401C455c8398d06F85e4EaB6309 #### Defined in -[escrow.ts:887](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L887) +[escrow.ts:883](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L883) ___ @@ -284,7 +284,7 @@ This function pays out the amounts specified to the workers and sets the URL of | :------ | :------ | :------ | | `escrowAddress` | `string` | Escrow address to payout. | | `recipients` | `string`[] | Array of recipient addresses. | -| `amounts` | `BigNumber`[] | Array of amounts the recipients will receive. | +| `amounts` | `bigint`[] | Array of amounts the recipients will receive. | | `finalResultsUrl` | `string` | Final results file url. | | `finalResultsHash` | `string` | Final results file hash. | | `txOptions?` | `Overrides` | Additional transaction parameters (optional, defaults to an empty object). | @@ -311,7 +311,7 @@ const signer = new Wallet(privateKey, provider); const escrowClient = await EscrowClient.build(signer); const recipients = ['0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266']; -const amounts = [ethers.utils.parseUnits(5, 'ether'), ethers.utils.parseUnits(10, 'ether')]; +const amounts = [ethers.parseUnits(5, 'ether'), ethers.parseUnits(10, 'ether')]; const resultsUrl = 'http://localhost/results.json'; const resultsHash'b5dad76bf6772c0f07fd5e048f6e75a5f86ee079'; @@ -320,7 +320,7 @@ await escrowClient.bulkPayOut('0x62dD51230A30401C455c8398d06F85e4EaB6309f', reci #### Defined in -[escrow.ts:655](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L655) +[escrow.ts:648](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L648) ___ @@ -363,7 +363,7 @@ await escrowClient.cancel('0x62dD51230A30401C455c8398d06F85e4EaB6309f'); #### Defined in -[escrow.ts:758](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L758) +[escrow.ts:751](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L751) ___ @@ -406,7 +406,7 @@ await escrowClient.complete('0x62dD51230A30401C455c8398d06F85e4EaB6309f'); #### Defined in -[escrow.ts:597](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L597) +[escrow.ts:590](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L590) ___ @@ -452,9 +452,9 @@ const escrowConfig = { recordingOracle: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', reputationOracle: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', exchangeOracle: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', - recordingOracleFee: BigNumber.from('10'), - reputationOracleFee: BigNumber.from('10'), - exchangeOracleFee: BigNumber.from('10'), + recordingOracleFee: bigint.from('10'), + reputationOracleFee: bigint.from('10'), + exchangeOracleFee: bigint.from('10'), manifestUrl: 'htttp://localhost/manifest.json', manifestHash: 'b5dad76bf6772c0f07fd5e048f6e75a5f86ee079', }; @@ -464,7 +464,7 @@ const escrowAddress = await escrowClient.createAndSetupEscrow(tokenAddress, trus #### Defined in -[escrow.ts:420](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L420) +[escrow.ts:413](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L413) ___ @@ -512,7 +512,7 @@ const escrowAddress = await escrowClient.createEscrow(tokenAddress, trustedHandl #### Defined in -[escrow.ts:214](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L214) +[escrow.ts:207](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L207) ___ @@ -527,7 +527,7 @@ This function adds funds of the chosen token to the escrow. | Name | Type | Description | | :------ | :------ | :------ | | `escrowAddress` | `string` | Address of the escrow to fund. | -| `amount` | `BigNumber` | Amount to be added as funds. | +| `amount` | `bigint` | Amount to be added as funds. | | `txOptions?` | `Overrides` | Additional transaction parameters (optional, defaults to an empty object). | #### Returns @@ -549,19 +549,19 @@ const provider = new providers.JsonRpcProvider(rpcUrl); const signer = new Wallet(privateKey, provider); const escrowClient = await EscrowClient.build(signer); -const amount = ethers.utils.parseUnits(5, 'ether'); //convert from ETH to WEI +const amount = ethers.parseUnits(5, 'ether'); //convert from ETH to WEI await escrowClient.fund('0x62dD51230A30401C455c8398d06F85e4EaB6309f', amount); ``` #### Defined in -[escrow.ts:468](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L468) +[escrow.ts:461](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L461) ___ ### getBalance -▸ **getBalance**(`escrowAddress`): `Promise`\<`BigNumber`\> +▸ **getBalance**(`escrowAddress`): `Promise`\<`bigint`\> This function returns the balance for a specified escrow address. @@ -573,7 +573,7 @@ This function returns the balance for a specified escrow address. #### Returns -`Promise`\<`BigNumber`\> +`Promise`\<`bigint`\> Balance of the escrow in the token used to fund it. @@ -593,7 +593,7 @@ const balance = await escrowClient.getBalance('0x62dD51230A30401C455c8398d06F85e #### Defined in -[escrow.ts:942](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L942) +[escrow.ts:938](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L938) ___ @@ -615,7 +615,7 @@ Connects to the escrow contract #### Defined in -[escrow.ts:174](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L174) +[escrow.ts:167](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L167) ___ @@ -653,7 +653,7 @@ const oracleAddress = await escrowClient.getExchangeOracleAddress('0x62dD51230A3 #### Defined in -[escrow.ts:1322](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L1322) +[escrow.ts:1318](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L1318) ___ @@ -691,7 +691,7 @@ const factoryAddress = await escrowClient.getFactoryAddress('0x62dD51230A30401C4 #### Defined in -[escrow.ts:1360](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L1360) +[escrow.ts:1356](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L1356) ___ @@ -729,7 +729,7 @@ const intemediateResultsUrl = await escrowClient.getIntermediateResultsUrl('0x62 #### Defined in -[escrow.ts:1094](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L1094) +[escrow.ts:1090](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L1090) ___ @@ -767,7 +767,7 @@ const jobLauncherAddress = await escrowClient.getJobLauncherAddress('0x62dD51230 #### Defined in -[escrow.ts:1246](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L1246) +[escrow.ts:1242](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L1242) ___ @@ -805,7 +805,7 @@ const manifestHash = await escrowClient.getManifestHash('0x62dD51230A30401C455c8 #### Defined in -[escrow.ts:980](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L980) +[escrow.ts:976](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L976) ___ @@ -843,7 +843,7 @@ const manifestUrl = await escrowClient.getManifestUrl('0x62dD51230A30401C455c839 #### Defined in -[escrow.ts:1018](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L1018) +[escrow.ts:1014](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L1014) ___ @@ -881,7 +881,7 @@ const oracleAddress = await escrowClient.getRecordingOracleAddress('0x62dD51230A #### Defined in -[escrow.ts:1208](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L1208) +[escrow.ts:1204](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L1204) ___ @@ -919,7 +919,7 @@ const oracleAddress = await escrowClient.getReputationOracleAddress('0x62dD51230 #### Defined in -[escrow.ts:1284](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L1284) +[escrow.ts:1280](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L1280) ___ @@ -957,7 +957,7 @@ const resultsUrl = await escrowClient.getResultsUrl('0x62dD51230A30401C455c8398d #### Defined in -[escrow.ts:1056](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L1056) +[escrow.ts:1052](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L1052) ___ @@ -995,7 +995,7 @@ const status = await escrowClient.getStatus('0x62dD51230A30401C455c8398d06F85e4E #### Defined in -[escrow.ts:1170](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L1170) +[escrow.ts:1166](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L1166) ___ @@ -1033,7 +1033,7 @@ const tokenAddress = await escrowClient.getTokenAddress('0x62dD51230A30401C455c8 #### Defined in -[escrow.ts:1132](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L1132) +[escrow.ts:1128](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L1128) ___ @@ -1077,9 +1077,9 @@ const escrowConfig = { recordingOracle: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', reputationOracle: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', exchangeOracle: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', - recordingOracleFee: BigNumber.from('10'), - reputationOracleFee: BigNumber.from('10'), - exchangeOracleFee: BigNumber.from('10'), + recordingOracleFee: bigint.from('10'), + reputationOracleFee: bigint.from('10'), + exchangeOracleFee: bigint.from('10'), manifestUrl: 'htttp://localhost/manifest.json', manifestHash: 'b5dad76bf6772c0f07fd5e048f6e75a5f86ee079', }; @@ -1088,7 +1088,7 @@ await escrowClient.setup(escrowAddress, escrowConfig); #### Defined in -[escrow.ts:293](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L293) +[escrow.ts:288](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L288) ___ @@ -1133,21 +1133,21 @@ await storeResults.storeResults('0x62dD51230A30401C455c8398d06F85e4EaB6309f', 'h #### Defined in -[escrow.ts:533](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L533) +[escrow.ts:526](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L526) ___ ### build -▸ **build**(`signerOrProvider`): `Promise`\<[`EscrowClient`](escrow.EscrowClient.md)\> +▸ **build**(`runner`): `Promise`\<[`EscrowClient`](escrow.EscrowClient.md)\> -Creates an instance of EscrowClient from a Signer or Provider. +Creates an instance of EscrowClient from a Runner. #### Parameters | Name | Type | Description | | :------ | :------ | :------ | -| `signerOrProvider` | `Signer` \| `Provider` | The Signer or Provider object to interact with the Ethereum network | +| `runner` | `ContractRunner` | The Runner object to interact with the Ethereum network | #### Returns @@ -1165,4 +1165,4 @@ Thrown if the network's chainId is not supported #### Defined in -[escrow.ts:147](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L147) +[escrow.ts:145](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L145) diff --git a/docs/sdk/typescript/classes/escrow.EscrowUtils.md b/docs/sdk/typescript/classes/escrow.EscrowUtils.md index d476ff2a80..bbdc767578 100644 --- a/docs/sdk/typescript/classes/escrow.EscrowUtils.md +++ b/docs/sdk/typescript/classes/escrow.EscrowUtils.md @@ -138,7 +138,7 @@ const escrowData = new EscrowUtils.getEscrow(ChainId.POLYGON_MUMBAI, "0x12345678 #### Defined in -[escrow.ts:1645](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L1645) +[escrow.ts:1632](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L1632) ___ @@ -251,4 +251,4 @@ const escrowDatas = await EscrowUtils.getEscrows(filters); #### Defined in -[escrow.ts:1508](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L1508) +[escrow.ts:1504](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts#L1504) diff --git a/docs/sdk/typescript/classes/kvstore.KVStoreClient.md b/docs/sdk/typescript/classes/kvstore.KVStoreClient.md index 037fd4d3db..61d9912a8b 100644 --- a/docs/sdk/typescript/classes/kvstore.KVStoreClient.md +++ b/docs/sdk/typescript/classes/kvstore.KVStoreClient.md @@ -8,11 +8,11 @@ This client enables to perform actions on KVStore contract and obtain information from both the contracts and subgraph. -Internally, the SDK will use one network or another according to the network ID of the `signerOrProvider`. +Internally, the SDK will use one network or another according to the network ID of the `runner`. To use this client, it is recommended to initialize it using the static `build` method. ```ts -static async build(signerOrProvider: Signer | Provider); +static async build(runner: ContractRunner); ``` A `Signer` or a `Provider` should be passed depending on the use case of this module: @@ -88,7 +88,7 @@ const kvstoreClient = await KVStoreClient.build(signer); - [contract](kvstore.KVStoreClient.md#contract) - [networkData](kvstore.KVStoreClient.md#networkdata) -- [signerOrProvider](kvstore.KVStoreClient.md#signerorprovider) +- [runner](kvstore.KVStoreClient.md#runner) ### Methods @@ -103,7 +103,7 @@ const kvstoreClient = await KVStoreClient.build(signer); ### constructor -• **new KVStoreClient**(`signerOrProvider`, `networkData`): [`KVStoreClient`](kvstore.KVStoreClient.md) +• **new KVStoreClient**(`runner`, `networkData`): [`KVStoreClient`](kvstore.KVStoreClient.md) **KVStoreClient constructor** @@ -111,7 +111,7 @@ const kvstoreClient = await KVStoreClient.build(signer); | Name | Type | Description | | :------ | :------ | :------ | -| `signerOrProvider` | `Signer` \| `Provider` | The Signer or Provider object to interact with the Ethereum network | +| `runner` | `ContractRunner` | The Runner object to interact with the Ethereum network | | `networkData` | `NetworkData` | - | #### Returns @@ -124,7 +124,7 @@ const kvstoreClient = await KVStoreClient.build(signer); #### Defined in -[kvstore.ts:103](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/kvstore.ts#L103) +[kvstore.ts:100](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/kvstore.ts#L100) ## Properties @@ -134,7 +134,7 @@ const kvstoreClient = await KVStoreClient.build(signer); #### Defined in -[kvstore.ts:95](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/kvstore.ts#L95) +[kvstore.ts:92](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/kvstore.ts#L92) ___ @@ -148,21 +148,21 @@ ___ #### Defined in -[base.ts:13](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/base.ts#L13) +[base.ts:12](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/base.ts#L12) ___ -### signerOrProvider +### runner -• `Protected` **signerOrProvider**: `Signer` \| `Provider` +• `Protected` **runner**: `ContractRunner` #### Inherited from -[BaseEthersClient](base.BaseEthersClient.md).[signerOrProvider](base.BaseEthersClient.md#signerorprovider) +[BaseEthersClient](base.BaseEthersClient.md).[runner](base.BaseEthersClient.md#runner) #### Defined in -[base.ts:12](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/base.ts#L12) +[base.ts:11](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/base.ts#L11) ## Methods @@ -203,7 +203,7 @@ const value = await kvstoreClient.get('0xf39Fd6e51aad88F6F4ce6aB8827279cffFb9226 #### Defined in -[kvstore.ts:317](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/kvstore.ts#L317) +[kvstore.ts:301](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/kvstore.ts#L301) ___ @@ -246,7 +246,7 @@ const linkedinUrl = await kvstoreClient.getURL( #### Defined in -[kvstore.ts:356](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/kvstore.ts#L356) +[kvstore.ts:340](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/kvstore.ts#L340) ___ @@ -290,7 +290,7 @@ await kvstoreClient.set('Role', 'RecordingOracle'); #### Defined in -[kvstore.ts:171](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/kvstore.ts#L171) +[kvstore.ts:163](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/kvstore.ts#L163) ___ @@ -336,7 +336,7 @@ await kvstoreClient.set(keys, values); #### Defined in -[kvstore.ts:215](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/kvstore.ts#L215) +[kvstore.ts:206](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/kvstore.ts#L206) ___ @@ -379,21 +379,21 @@ await kvstoreClient.setURL('linkedin.com/example', 'linkedinUrl); #### Defined in -[kvstore.ts:259](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/kvstore.ts#L259) +[kvstore.ts:249](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/kvstore.ts#L249) ___ ### build -▸ **build**(`signerOrProvider`): `Promise`\<[`KVStoreClient`](kvstore.KVStoreClient.md)\> +▸ **build**(`runner`): `Promise`\<[`KVStoreClient`](kvstore.KVStoreClient.md)\> -Creates an instance of KVStoreClient from a Signer or Provider. +Creates an instance of KVStoreClient from a runner. #### Parameters | Name | Type | Description | | :------ | :------ | :------ | -| `signerOrProvider` | `Signer` \| `Provider` | The Signer or Provider object to interact with the Ethereum network | +| `runner` | `ContractRunner` | The Runner object to interact with the Ethereum network | #### Returns @@ -411,4 +411,4 @@ Creates an instance of KVStoreClient from a Signer or Provider. #### Defined in -[kvstore.ts:121](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/kvstore.ts#L121) +[kvstore.ts:118](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/kvstore.ts#L118) diff --git a/docs/sdk/typescript/classes/staking.StakingClient.md b/docs/sdk/typescript/classes/staking.StakingClient.md index e5278ddf08..6f3d6311f6 100644 --- a/docs/sdk/typescript/classes/staking.StakingClient.md +++ b/docs/sdk/typescript/classes/staking.StakingClient.md @@ -8,11 +8,11 @@ This client enables to perform actions on staking contracts and obtain staking information from both the contracts and subgraph. -Internally, the SDK will use one network or another according to the network ID of the `signerOrProvider`. +Internally, the SDK will use one network or another according to the network ID of the `runner`. To use this client, it is recommended to initialize it using the static `build` method. ```ts -static async build(signerOrProvider: Signer | Provider); +static async build(runner: ContractRunner); ``` A `Signer` or a `Provider` should be passed depending on the use case of this module: @@ -89,7 +89,7 @@ const stakingClient = await StakingClient.build(provider); - [escrowFactoryContract](staking.StakingClient.md#escrowfactorycontract) - [networkData](staking.StakingClient.md#networkdata) - [rewardPoolContract](staking.StakingClient.md#rewardpoolcontract) -- [signerOrProvider](staking.StakingClient.md#signerorprovider) +- [runner](staking.StakingClient.md#runner) - [stakingContract](staking.StakingClient.md#stakingcontract) - [tokenContract](staking.StakingClient.md#tokencontract) @@ -114,7 +114,7 @@ const stakingClient = await StakingClient.build(provider); ### constructor -• **new StakingClient**(`signerOrProvider`, `networkData`): [`StakingClient`](staking.StakingClient.md) +• **new StakingClient**(`runner`, `networkData`): [`StakingClient`](staking.StakingClient.md) **StakingClient constructor** @@ -122,7 +122,7 @@ const stakingClient = await StakingClient.build(provider); | Name | Type | Description | | :------ | :------ | :------ | -| `signerOrProvider` | `Signer` \| `Provider` | The Signer or Provider object to interact with the Ethereum network | +| `runner` | `ContractRunner` | The Runner object to interact with the Ethereum network | | `networkData` | `NetworkData` | - | #### Returns @@ -135,7 +135,7 @@ const stakingClient = await StakingClient.build(provider); #### Defined in -[staking.ts:118](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/staking.ts#L118) +[staking.ts:116](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/staking.ts#L116) ## Properties @@ -145,7 +145,7 @@ const stakingClient = await StakingClient.build(provider); #### Defined in -[staking.ts:109](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/staking.ts#L109) +[staking.ts:107](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/staking.ts#L107) ___ @@ -159,7 +159,7 @@ ___ #### Defined in -[base.ts:13](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/base.ts#L13) +[base.ts:12](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/base.ts#L12) ___ @@ -169,21 +169,21 @@ ___ #### Defined in -[staking.ts:110](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/staking.ts#L110) +[staking.ts:108](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/staking.ts#L108) ___ -### signerOrProvider +### runner -• `Protected` **signerOrProvider**: `Signer` \| `Provider` +• `Protected` **runner**: `ContractRunner` #### Inherited from -[BaseEthersClient](base.BaseEthersClient.md).[signerOrProvider](base.BaseEthersClient.md#signerorprovider) +[BaseEthersClient](base.BaseEthersClient.md).[runner](base.BaseEthersClient.md#runner) #### Defined in -[base.ts:12](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/base.ts#L12) +[base.ts:11](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/base.ts#L11) ___ @@ -193,7 +193,7 @@ ___ #### Defined in -[staking.ts:108](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/staking.ts#L108) +[staking.ts:106](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/staking.ts#L106) ___ @@ -203,7 +203,7 @@ ___ #### Defined in -[staking.ts:107](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/staking.ts#L107) +[staking.ts:105](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/staking.ts#L105) ## Methods @@ -220,7 +220,7 @@ This function allocates a portion of the staked tokens to a specific escrow. | Name | Type | Description | | :------ | :------ | :------ | | `escrowAddress` | `string` | Address of the escrow contract to allocate in. | -| `amount` | `BigNumber` | Amount in WEI of tokens to allocate. | +| `amount` | `bigint` | Amount in WEI of tokens to allocate. | | `txOptions?` | `Overrides` | Additional transaction parameters (optional, defaults to an empty object). | #### Returns @@ -242,13 +242,13 @@ const provider = new providers.JsonRpcProvider(rpcUrl); const signer = new Wallet(privateKey, provider); const stakingClient = await StakingClient.build(signer); -const amount = ethers.utils.parseUnits(5, 'ether'); //convert from ETH to WEI +const amount = ethers.parseUnits(5, 'ether'); //convert from ETH to WEI await stakingClient.allocate('0x62dD51230A30401C455c8398d06F85e4EaB6309f', amount); ``` #### Defined in -[staking.ts:473](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/staking.ts#L473) +[staking.ts:463](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/staking.ts#L463) ___ @@ -262,7 +262,7 @@ This function approves the staking contract to transfer a specified amount of to | Name | Type | Description | | :------ | :------ | :------ | -| `amount` | `BigNumber` | Amount in WEI of tokens to approve for stake. | +| `amount` | `bigint` | Amount in WEI of tokens to approve for stake. | | `txOptions?` | `Overrides` | Additional transaction parameters (optional, defaults to an empty object). | #### Returns @@ -284,13 +284,13 @@ const provider = new providers.JsonRpcProvider(rpcUrl); const signer = new Wallet(privateKey, provider); const stakingClient = await StakingClient.build(signer); -const amount = ethers.utils.parseUnits(5, 'ether'); //convert from ETH to WEI +const amount = ethers.parseUnits(5, 'ether'); //convert from ETH to WEI await stakingClient.approveStake(amount); ``` #### Defined in -[staking.ts:215](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/staking.ts#L215) +[staking.ts:208](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/staking.ts#L208) ___ @@ -312,7 +312,7 @@ Check if escrow exists #### Defined in -[staking.ts:179](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/staking.ts#L179) +[staking.ts:172](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/staking.ts#L172) ___ @@ -356,7 +356,7 @@ await stakingClient.closeAllocation('0x62dD51230A30401C455c8398d06F85e4EaB6309f' #### Defined in -[staking.ts:526](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/staking.ts#L526) +[staking.ts:516](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/staking.ts#L516) ___ @@ -399,7 +399,7 @@ await stakingClient.distributeReward('0x62dD51230A30401C455c8398d06F85e4EaB6309f #### Defined in -[staking.ts:569](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/staking.ts#L569) +[staking.ts:559](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/staking.ts#L559) ___ @@ -437,7 +437,7 @@ const allocationInfo = await stakingClient.getAllocation('0x62dD51230A30401C455c #### Defined in -[staking.ts:680](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/staking.ts#L680) +[staking.ts:670](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/staking.ts#L670) ___ @@ -475,7 +475,7 @@ const leaders = await stakingClient.getLeaders(); #### Defined in -[staking.ts:606](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/staking.ts#L606) +[staking.ts:596](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/staking.ts#L596) ___ @@ -513,7 +513,7 @@ const leader = await stakingClient.getLeader('0x62dD51230A30401C455c8398d06F85e4 #### Defined in -[staking.ts:645](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/staking.ts#L645) +[staking.ts:635](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/staking.ts#L635) ___ @@ -551,7 +551,7 @@ const rewards = await stakingClient.getRewards('0x62dD51230A30401C455c8398d06F85 #### Defined in -[staking.ts:712](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/staking.ts#L712) +[staking.ts:702](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/staking.ts#L702) ___ @@ -568,7 +568,7 @@ This function reduces the allocated amount by an staker in an escrow and transfe | `slasher` | `string` | Wallet address from who requested the slash | | `staker` | `string` | Wallet address from who is going to be slashed | | `escrowAddress` | `string` | Address of the escrow which allocation will be slashed | -| `amount` | `BigNumber` | Amount in WEI of tokens to unstake. | +| `amount` | `bigint` | Amount in WEI of tokens to unstake. | | `txOptions?` | `Overrides` | Additional transaction parameters (optional, defaults to an empty object). | #### Returns @@ -590,13 +590,13 @@ const provider = new providers.JsonRpcProvider(rpcUrl); const signer = new Wallet(privateKey, provider); const stakingClient = await StakingClient.build(signer); -const amount = ethers.utils.parseUnits(5, 'ether'); //convert from ETH to WEI +const amount = ethers.parseUnits(5, 'ether'); //convert from ETH to WEI await stakingClient.slash('0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', '0x62dD51230A30401C455c8398d06F85e4EaB6309f', amount); ``` #### Defined in -[staking.ts:402](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/staking.ts#L402) +[staking.ts:392](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/staking.ts#L392) ___ @@ -612,7 +612,7 @@ This function stakes a specified amount of tokens on a specific network. | Name | Type | Description | | :------ | :------ | :------ | -| `amount` | `BigNumber` | Amount in WEI of tokens to stake. | +| `amount` | `bigint` | Amount in WEI of tokens to stake. | | `txOptions?` | `Overrides` | Additional transaction parameters (optional, defaults to an empty object). | #### Returns @@ -634,14 +634,14 @@ const provider = new providers.JsonRpcProvider(rpcUrl); const signer = new Wallet(privateKey, provider); const stakingClient = await StakingClient.build(signer); -const amount = ethers.utils.parseUnits(5, 'ether'); //convert from ETH to WEI +const amount = ethers.parseUnits(5, 'ether'); //convert from ETH to WEI await stakingClient.approveStake(amount); // if it was already approved before, this is not necessary await stakingClient.approveStake(amount); ``` #### Defined in -[staking.ts:270](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/staking.ts#L270) +[staking.ts:263](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/staking.ts#L263) ___ @@ -657,7 +657,7 @@ This function unstakes tokens from staking contract. The unstaked tokens stay lo | Name | Type | Description | | :------ | :------ | :------ | -| `amount` | `BigNumber` | Amount in WEI of tokens to unstake. | +| `amount` | `bigint` | Amount in WEI of tokens to unstake. | | `txOptions?` | `Overrides` | Additional transaction parameters (optional, defaults to an empty object). | #### Returns @@ -679,13 +679,13 @@ const provider = new providers.JsonRpcProvider(rpcUrl); const signer = new Wallet(privateKey, provider); const stakingClient = await StakingClient.build(signer); -const amount = ethers.utils.parseUnits(5, 'ether'); //convert from ETH to WEI +const amount = ethers.parseUnits(5, 'ether'); //convert from ETH to WEI await stakingClient.unstake(amount); ``` #### Defined in -[staking.ts:318](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/staking.ts#L318) +[staking.ts:308](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/staking.ts#L308) ___ @@ -727,21 +727,21 @@ await stakingClient.withdraw(); #### Defined in -[staking.ts:364](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/staking.ts#L364) +[staking.ts:354](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/staking.ts#L354) ___ ### build -▸ **build**(`signerOrProvider`): `Promise`\<[`StakingClient`](staking.StakingClient.md)\> +▸ **build**(`runner`): `Promise`\<[`StakingClient`](staking.StakingClient.md)\> -Creates an instance of StakingClient from a Signer or Provider. +Creates an instance of StakingClient from a Runner. #### Parameters | Name | Type | Description | | :------ | :------ | :------ | -| `signerOrProvider` | `Signer` \| `Provider` | The Signer or Provider object to interact with the Ethereum network | +| `runner` | `ContractRunner` | The Runner object to interact with the Ethereum network | #### Returns @@ -759,4 +759,4 @@ Creates an instance of StakingClient from a Signer or Provider. #### Defined in -[staking.ts:152](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/staking.ts#L152) +[staking.ts:150](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/staking.ts#L150) diff --git a/docs/sdk/typescript/classes/statistics.StatisticsClient.md b/docs/sdk/typescript/classes/statistics.StatisticsClient.md index 7e3d008874..5630d2c15b 100644 --- a/docs/sdk/typescript/classes/statistics.StatisticsClient.md +++ b/docs/sdk/typescript/classes/statistics.StatisticsClient.md @@ -77,7 +77,7 @@ const statisticsClient = new StatisticsClient(NETWORKS[ChainId.POLYGON_MUMBAI]); #### Defined in -[statistics.ts:68](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/statistics.ts#L68) +[statistics.ts:68](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/statistics.ts#L68) ## Properties @@ -87,7 +87,7 @@ const statisticsClient = new StatisticsClient(NETWORKS[ChainId.POLYGON_MUMBAI]); #### Defined in -[statistics.ts:61](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/statistics.ts#L61) +[statistics.ts:61](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/statistics.ts#L61) ## Methods @@ -151,7 +151,7 @@ const escrowStatisticsApril = await statisticsClient.getEscrowStatistics({ #### Defined in -[statistics.ts:121](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/statistics.ts#L121) +[statistics.ts:121](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/statistics.ts#L121) ___ @@ -247,7 +247,7 @@ console.log('HMT statistics from 5/8 - 6/8:', { #### Defined in -[statistics.ts:395](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/statistics.ts#L395) +[statistics.ts:394](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/statistics.ts#L394) ___ @@ -329,7 +329,7 @@ console.log( #### Defined in -[statistics.ts:285](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/statistics.ts#L285) +[statistics.ts:285](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/statistics.ts#L285) ___ @@ -388,4 +388,4 @@ const workerStatisticsApril = await statisticsClient.getWorkerStatistics({ #### Defined in -[statistics.ts:196](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/statistics.ts#L196) +[statistics.ts:196](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/statistics.ts#L196) diff --git a/docs/sdk/typescript/classes/storage.StorageClient.md b/docs/sdk/typescript/classes/storage.StorageClient.md index 9a8e23ab00..ddd88c1eed 100644 --- a/docs/sdk/typescript/classes/storage.StorageClient.md +++ b/docs/sdk/typescript/classes/storage.StorageClient.md @@ -91,7 +91,7 @@ const storageClient = new StorageClient(params, credentials); #### Defined in -[storage.ts:73](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/storage.ts#L73) +[storage.ts:73](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/storage.ts#L73) ## Properties @@ -101,7 +101,7 @@ const storageClient = new StorageClient(params, credentials); #### Defined in -[storage.ts:64](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/storage.ts#L64) +[storage.ts:64](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/storage.ts#L64) ___ @@ -111,7 +111,7 @@ ___ #### Defined in -[storage.ts:65](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/storage.ts#L65) +[storage.ts:65](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/storage.ts#L65) ## Methods @@ -155,7 +155,7 @@ const exists = await storageClient.bucketExists('bucket-name'); #### Defined in -[storage.ts:266](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/storage.ts#L266) +[storage.ts:266](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/storage.ts#L266) ___ @@ -198,7 +198,7 @@ const files = await storageClient.downloadFiles(keys, 'bucket-name'); #### Defined in -[storage.ts:113](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/storage.ts#L113) +[storage.ts:113](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/storage.ts#L113) ___ @@ -242,7 +242,7 @@ const fileNames = await storageClient.listObjects('bucket-name'); #### Defined in -[storage.ts:297](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/storage.ts#L297) +[storage.ts:297](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/storage.ts#L297) ___ @@ -290,7 +290,7 @@ const uploadedFiles = await storageClient.uploadFiles(files, 'bucket-name'); #### Defined in -[storage.ts:201](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/storage.ts#L201) +[storage.ts:201](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/storage.ts#L201) ___ @@ -322,4 +322,4 @@ const file = await storageClient.downloadFileFromUrl('http://localhost/file.json #### Defined in -[storage.ts:148](https://github.com/humanprotocol/human-protocol/blob/47ca9511/packages/sdk/typescript/human-protocol-sdk/src/storage.ts#L148) +[storage.ts:148](https://github.com/humanprotocol/human-protocol/blob/9bc762a5/packages/sdk/typescript/human-protocol-sdk/src/storage.ts#L148) diff --git a/package.json b/package.json index fb015c006d..c0f2107cbb 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,8 @@ "prettier": "^3.1.1", "ts-jest": "^29.1.1", "ts-node": "^10.9.2", - "typescript": "^4.9.3" + "typescript": "^4.9.3", + "ethers": "^6.9.1" }, "resolutions": { "ejs": "^3.1.8", diff --git a/packages/apps/fortune/exchange-oracle/server/package.json b/packages/apps/fortune/exchange-oracle/server/package.json index e09e1d2c7b..58a425463e 100644 --- a/packages/apps/fortune/exchange-oracle/server/package.json +++ b/packages/apps/fortune/exchange-oracle/server/package.json @@ -1,10 +1,9 @@ { - "name": "server", - "version": "0.0.1", - "description": "", - "author": "", - "private": true, - "license": "UNLICENSED", + "name": "@human-protocol/fortune-exchange-oracle-server", + "version": "1.0.0", + "description": "Fortune Exchange Oracle", + "author": "Human Protocol", + "license": "MIT", "scripts": { "build": "nest build", "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", @@ -25,7 +24,6 @@ "@nestjs/common": "^10.2.7", "@nestjs/core": "^10.2.8", "@nestjs/platform-express": "^10.2.6", - "ethers": "^5.7.2", "joi": "^17.9.2", "reflect-metadata": "^0.1.13", "rxjs": "^7.2.0" diff --git a/packages/apps/fortune/exchange-oracle/server/src/common/utils/signature.ts b/packages/apps/fortune/exchange-oracle/server/src/common/utils/signature.ts index 9741950d9e..20cc25b726 100644 --- a/packages/apps/fortune/exchange-oracle/server/src/common/utils/signature.ts +++ b/packages/apps/fortune/exchange-oracle/server/src/common/utils/signature.ts @@ -40,7 +40,7 @@ export function recoverSigner( } try { - return ethers.utils.verifyMessage(message, signature); + return ethers.verifyMessage(message, signature); } catch (e) { throw new ConflictException('Invalid signature'); } diff --git a/packages/apps/fortune/exchange-oracle/server/src/common/validators/ethers.ts b/packages/apps/fortune/exchange-oracle/server/src/common/validators/ethers.ts index 982b4aa3ba..55bc380197 100644 --- a/packages/apps/fortune/exchange-oracle/server/src/common/validators/ethers.ts +++ b/packages/apps/fortune/exchange-oracle/server/src/common/validators/ethers.ts @@ -11,7 +11,7 @@ import { ethers } from 'ethers'; @Injectable() class ValidateEthereumAddress implements ValidatorConstraintInterface { public validate(value: string): boolean { - return ethers.utils.isAddress(value); + return ethers.isAddress(value); } public defaultMessage(): string { diff --git a/packages/apps/fortune/exchange-oracle/server/src/modules/web3/web3.service.spec.ts b/packages/apps/fortune/exchange-oracle/server/src/modules/web3/web3.service.spec.ts index 2a5e5011ce..bb8aaa99bf 100644 --- a/packages/apps/fortune/exchange-oracle/server/src/modules/web3/web3.service.spec.ts +++ b/packages/apps/fortune/exchange-oracle/server/src/modules/web3/web3.service.spec.ts @@ -34,7 +34,6 @@ describe('Web3Service', () => { const signer = web3Service.getSigner(network.chainId); expect(signer).toBeDefined(); - expect((signer.provider as any).connection.url).toBe(network.rpcUrl); } }); diff --git a/packages/apps/fortune/exchange-oracle/server/src/modules/web3/web3.service.ts b/packages/apps/fortune/exchange-oracle/server/src/modules/web3/web3.service.ts index d11e1555d8..a2c5bd6384 100644 --- a/packages/apps/fortune/exchange-oracle/server/src/modules/web3/web3.service.ts +++ b/packages/apps/fortune/exchange-oracle/server/src/modules/web3/web3.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; -import { Wallet, providers } from 'ethers'; +import { Wallet, ethers } from 'ethers'; import { ConfigNames, networkMap } from '../../common/config'; @Injectable() @@ -11,7 +11,7 @@ export class Web3Service { const privateKey = this.configService.get(ConfigNames.WEB3_PRIVATE_KEY); for (const networkKey of Object.keys(networkMap)) { const network = networkMap[networkKey]; - const provider = new providers.JsonRpcProvider(network.rpcUrl); + const provider = new ethers.JsonRpcProvider(network.rpcUrl); this.signers[network.chainId] = new Wallet(privateKey, provider); } } diff --git a/packages/apps/fortune/recording-oracle/package.json b/packages/apps/fortune/recording-oracle/package.json index 6e75d9fc15..11e4d66c62 100644 --- a/packages/apps/fortune/recording-oracle/package.json +++ b/packages/apps/fortune/recording-oracle/package.json @@ -1,5 +1,5 @@ { - "name": "@human-protocol/fortune-v3-recording-oracle", + "name": "@human-protocol/fortune-recording-oracle", "version": "1.0.0", "description": "Fortune Recording Oracle", "author": "Human Protocol", @@ -32,7 +32,6 @@ "body-parser": "^1.20.2", "class-validator": "^0.14.0", "cookie-parser": "^1.4.6", - "ethers": "^5.7.2", "helmet": "^7.1.0", "reflect-metadata": "^0.1.13" }, diff --git a/packages/apps/fortune/recording-oracle/src/common/utils/signature.ts b/packages/apps/fortune/recording-oracle/src/common/utils/signature.ts index 9741950d9e..20cc25b726 100644 --- a/packages/apps/fortune/recording-oracle/src/common/utils/signature.ts +++ b/packages/apps/fortune/recording-oracle/src/common/utils/signature.ts @@ -40,7 +40,7 @@ export function recoverSigner( } try { - return ethers.utils.verifyMessage(message, signature); + return ethers.verifyMessage(message, signature); } catch (e) { throw new ConflictException('Invalid signature'); } diff --git a/packages/apps/fortune/recording-oracle/src/common/validators/ethers.ts b/packages/apps/fortune/recording-oracle/src/common/validators/ethers.ts index 982b4aa3ba..55bc380197 100644 --- a/packages/apps/fortune/recording-oracle/src/common/validators/ethers.ts +++ b/packages/apps/fortune/recording-oracle/src/common/validators/ethers.ts @@ -11,7 +11,7 @@ import { ethers } from 'ethers'; @Injectable() class ValidateEthereumAddress implements ValidatorConstraintInterface { public validate(value: string): boolean { - return ethers.utils.isAddress(value); + return ethers.isAddress(value); } public defaultMessage(): string { diff --git a/packages/apps/fortune/recording-oracle/src/modules/job/job.service.ts b/packages/apps/fortune/recording-oracle/src/modules/job/job.service.ts index 0caf927b13..63fe0cfbbd 100644 --- a/packages/apps/fortune/recording-oracle/src/modules/job/job.service.ts +++ b/packages/apps/fortune/recording-oracle/src/modules/job/job.service.ts @@ -104,8 +104,7 @@ export class JobService { jobSolution.escrowAddress, ); if ( - ethers.utils.getAddress(recordingOracleAddress) !== - (await signer.getAddress()) + ethers.getAddress(recordingOracleAddress) !== (await signer.getAddress()) ) { this.logger.log(ErrorJob.AddressMismatches, JobService.name); throw new BadRequestException(ErrorJob.AddressMismatches); diff --git a/packages/apps/fortune/recording-oracle/src/modules/web3/web3.service.spec.ts b/packages/apps/fortune/recording-oracle/src/modules/web3/web3.service.spec.ts index 099cea8ede..72047ba267 100644 --- a/packages/apps/fortune/recording-oracle/src/modules/web3/web3.service.spec.ts +++ b/packages/apps/fortune/recording-oracle/src/modules/web3/web3.service.spec.ts @@ -30,7 +30,6 @@ describe('Web3Service', () => { const signer = web3Service.getSigner(network.chainId); expect(signer).toBeDefined(); - expect((signer.provider as any).connection.url).toBe(network.rpcUrl); } }); diff --git a/packages/apps/fortune/recording-oracle/src/modules/web3/web3.service.ts b/packages/apps/fortune/recording-oracle/src/modules/web3/web3.service.ts index abb789c2e2..70b97fe0c6 100644 --- a/packages/apps/fortune/recording-oracle/src/modules/web3/web3.service.ts +++ b/packages/apps/fortune/recording-oracle/src/modules/web3/web3.service.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { Wallet, providers } from 'ethers'; +import { Wallet, ethers } from 'ethers'; import { Web3ConfigType, web3ConfigKey } from '../../common/config'; import { networkMap } from '../../common/constants/networks'; @@ -15,7 +15,7 @@ export class Web3Service { for (const networkKey of Object.keys(networkMap)) { const network = networkMap[networkKey]; - const provider = new providers.JsonRpcProvider(network.rpcUrl); + const provider = new ethers.JsonRpcProvider(network.rpcUrl); this.signers[network.chainId] = new Wallet(privateKey, provider); } } diff --git a/packages/apps/hufi/exchange-oracle/server/package.json b/packages/apps/hufi/exchange-oracle/server/package.json index 3d7a1bf121..6479cb7c4d 100644 --- a/packages/apps/hufi/exchange-oracle/server/package.json +++ b/packages/apps/hufi/exchange-oracle/server/package.json @@ -26,7 +26,6 @@ "@nestjs/common": "^10.2.7", "@nestjs/core": "^10.2.8", "@nestjs/platform-express": "^10.2.6", - "ethers": "^5.7.2", "joi": "^17.9.2", "reflect-metadata": "^0.1.13", "rxjs": "^7.2.0" diff --git a/packages/apps/hufi/exchange-oracle/server/src/common/utils/signature.ts b/packages/apps/hufi/exchange-oracle/server/src/common/utils/signature.ts index 9741950d9e..20cc25b726 100644 --- a/packages/apps/hufi/exchange-oracle/server/src/common/utils/signature.ts +++ b/packages/apps/hufi/exchange-oracle/server/src/common/utils/signature.ts @@ -40,7 +40,7 @@ export function recoverSigner( } try { - return ethers.utils.verifyMessage(message, signature); + return ethers.verifyMessage(message, signature); } catch (e) { throw new ConflictException('Invalid signature'); } diff --git a/packages/apps/hufi/exchange-oracle/server/src/common/validators/ethers.ts b/packages/apps/hufi/exchange-oracle/server/src/common/validators/ethers.ts index 982b4aa3ba..55bc380197 100644 --- a/packages/apps/hufi/exchange-oracle/server/src/common/validators/ethers.ts +++ b/packages/apps/hufi/exchange-oracle/server/src/common/validators/ethers.ts @@ -11,7 +11,7 @@ import { ethers } from 'ethers'; @Injectable() class ValidateEthereumAddress implements ValidatorConstraintInterface { public validate(value: string): boolean { - return ethers.utils.isAddress(value); + return ethers.isAddress(value); } public defaultMessage(): string { diff --git a/packages/apps/hufi/exchange-oracle/server/src/modules/web3/web3.service.spec.ts b/packages/apps/hufi/exchange-oracle/server/src/modules/web3/web3.service.spec.ts index 2a5e5011ce..bb8aaa99bf 100644 --- a/packages/apps/hufi/exchange-oracle/server/src/modules/web3/web3.service.spec.ts +++ b/packages/apps/hufi/exchange-oracle/server/src/modules/web3/web3.service.spec.ts @@ -34,7 +34,6 @@ describe('Web3Service', () => { const signer = web3Service.getSigner(network.chainId); expect(signer).toBeDefined(); - expect((signer.provider as any).connection.url).toBe(network.rpcUrl); } }); diff --git a/packages/apps/hufi/exchange-oracle/server/src/modules/web3/web3.service.ts b/packages/apps/hufi/exchange-oracle/server/src/modules/web3/web3.service.ts index 5d55ab38b8..d931380f52 100644 --- a/packages/apps/hufi/exchange-oracle/server/src/modules/web3/web3.service.ts +++ b/packages/apps/hufi/exchange-oracle/server/src/modules/web3/web3.service.ts @@ -1,6 +1,6 @@ import { BadRequestException, Injectable, Logger } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; -import { Wallet, providers } from 'ethers'; +import { Wallet, ethers } from 'ethers'; import { ConfigNames, networks } from '../../common/config'; import { ChainId } from '@human-protocol/sdk'; import { MAINNET_CHAIN_IDS, TESTNET_CHAIN_IDS } from '../../common/constant'; @@ -20,7 +20,7 @@ export class Web3Service { validChains.includes(network.chainId), ); for (const network of validNetworks) { - const provider = new providers.JsonRpcProvider(network.rpcUrl); + const provider = new ethers.JsonRpcProvider(network.rpcUrl); this.signers[network.chainId] = new Wallet(privateKey, provider); } this.signerAddress = this.signers[validChains[0]].address; diff --git a/packages/apps/hufi/job-launcher/server/package.json b/packages/apps/hufi/job-launcher/server/package.json index 0649a7439d..668c97e26d 100644 --- a/packages/apps/hufi/job-launcher/server/package.json +++ b/packages/apps/hufi/job-launcher/server/package.json @@ -49,7 +49,6 @@ "class-transformer": "^0.5.1", "cookie-parser": "^1.4.6", "decimal.js": "^10.4.3", - "ethers": "^5.7.2", "express-session": "^1.17.3", "joi": "^17.9.2", "nestjs-minio-client": "^2.2.0", diff --git a/packages/apps/hufi/job-launcher/server/src/common/utils/signature.ts b/packages/apps/hufi/job-launcher/server/src/common/utils/signature.ts index e1bf475a57..c619886dcb 100644 --- a/packages/apps/hufi/job-launcher/server/src/common/utils/signature.ts +++ b/packages/apps/hufi/job-launcher/server/src/common/utils/signature.ts @@ -41,7 +41,7 @@ export function recoverSigner( } try { - return ethers.utils.verifyMessage(message, signature); + return ethers.verifyMessage(message, signature); } catch (e) { throw new ConflictException(ErrorSignature.InvalidSignature); } diff --git a/packages/apps/hufi/job-launcher/server/src/modules/job/job.dto.ts b/packages/apps/hufi/job-launcher/server/src/modules/job/job.dto.ts index 3e5bfb3957..93dfa306f8 100644 --- a/packages/apps/hufi/job-launcher/server/src/modules/job/job.dto.ts +++ b/packages/apps/hufi/job-launcher/server/src/modules/job/job.dto.ts @@ -16,7 +16,6 @@ import { import { ChainId } from '@human-protocol/sdk'; import { JobRequestType, JobStatus } from '../../common/enums/job'; import { EventType } from '../../common/enums/webhook'; -import { BigNumber } from 'ethers'; import { Exchange } from '../../common/enums/exchange'; export class JobCreateDto { @@ -303,7 +302,7 @@ export class EscrowFailedWebhookDto { export class EscrowCancelDto { txHash: string; - amountRefunded: BigNumber; + amountRefunded: bigint; } export class CampaignFinalResultDto { @@ -315,4 +314,4 @@ export class CampaignFinalResultDto { @IsString() liquidityScore: string; -} \ No newline at end of file +} diff --git a/packages/apps/hufi/job-launcher/server/src/modules/job/job.service.spec.ts b/packages/apps/hufi/job-launcher/server/src/modules/job/job.service.spec.ts index af1ea57cce..3d0d57567c 100644 --- a/packages/apps/hufi/job-launcher/server/src/modules/job/job.service.spec.ts +++ b/packages/apps/hufi/job-launcher/server/src/modules/job/job.service.spec.ts @@ -60,7 +60,7 @@ import { MOCK_USER_ID, } from '../../../test/constants'; import { PaymentService } from '../payment/payment.service'; -import { Web3Service } from '../web3/web3.service'; +import { Web3Service } from '../web3/Web3Service'; import { FortuneFinalResultDto, FortuneManifestDto, @@ -1477,7 +1477,7 @@ describe('JobService', () => { describe('getPaidOutAmount', () => { it('should calculate the paid out amount', async () => { const chainId = ChainId.LOCALHOST; - const amount = ethers.utils.parseEther('1.5'); + const amount = ethers.parseEther('1.5'); const mockLogs = [ { data: 'mockData', diff --git a/packages/apps/hufi/job-launcher/server/src/modules/job/job.service.ts b/packages/apps/hufi/job-launcher/server/src/modules/job/job.service.ts index ab335f822d..4f7dd8e124 100644 --- a/packages/apps/hufi/job-launcher/server/src/modules/job/job.service.ts +++ b/packages/apps/hufi/job-launcher/server/src/modules/job/job.service.ts @@ -24,7 +24,7 @@ import { } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { validate } from 'class-validator'; -import { BigNumber, ethers } from 'ethers'; +import { ethers } from 'ethers'; import { firstValueFrom } from 'rxjs'; import { LessThanOrEqual, QueryFailedError } from 'typeorm'; import { ConfigNames } from '../../common/config'; @@ -51,7 +51,7 @@ import { getRate } from '../../common/utils'; import { add, div, lt, mul } from '../../common/utils/decimal'; import { PaymentRepository } from '../payment/payment.repository'; import { PaymentService } from '../payment/payment.service'; -import { Web3Service } from '../web3/web3.service'; +import { Web3Service } from '../web3/Web3Service'; import { CampaignFinalResultDto, CampaignManifestDto, @@ -213,13 +213,13 @@ export class JobService { exchangeOracle: this.configService.get( ConfigNames.EXCHANGE_ORACLE_ADDRESS, )!, - recordingOracleFee: BigNumber.from( + recordingOracleFee: BigInt( this.configService.get(ConfigNames.RECORDING_ORACLE_FEE)!, ), - reputationOracleFee: BigNumber.from( + reputationOracleFee: BigInt( this.configService.get(ConfigNames.REPUTATION_ORACLE_FEE)!, ), - exchangeOracleFee: BigNumber.from( + exchangeOracleFee: BigInt( this.configService.get(ConfigNames.EXCHANGE_ORACLE_FEE)!, ), manifestUrl: jobEntity.manifestUrl, @@ -249,7 +249,7 @@ export class JobService { const escrowClient = await EscrowClient.build(signer); - const weiAmount = ethers.utils.parseUnits( + const weiAmount = ethers.parseUnits( jobEntity.fundAmount.toString(), 'ether', ); @@ -388,7 +388,7 @@ export class JobService { limit, ); const escrowAddresses = escrows.map((escrow) => - ethers.utils.getAddress(escrow.address), + ethers.getAddress(escrow.address), ); jobs = await this.jobRepository.findJobsByEscrowAddresses( @@ -570,11 +570,10 @@ export class JobService { if (!jobEntity) return; if (jobEntity.escrowAddress) { - const { amountRefunded } = await this.processEscrowCancellation( - jobEntity, - ); + const { amountRefunded } = + await this.processEscrowCancellation(jobEntity); await this.paymentService.createRefundPayment({ - refundAmount: Number(ethers.utils.formatEther(amountRefunded)), + refundAmount: Number(ethers.formatEther(amountRefunded)), userId: jobEntity.userId, jobId: jobEntity.id, }); @@ -620,7 +619,7 @@ export class JobService { } const balance = await escrowClient.getBalance(escrowAddress); - if (balance.eq(0)) { + if (balance === 0n) { this.logger.log(ErrorEscrow.InvalidBalanceCancellation, JobService.name); throw new BadRequestException(ErrorEscrow.InvalidBalanceCancellation); } @@ -691,7 +690,7 @@ export class JobService { const baseManifestDetails = { chainId, - tokenAddress: escrow ? escrow.token : ethers.constants.AddressZero, + tokenAddress: escrow ? escrow.token : ethers.ZeroAddress, fundAmount: escrow ? Number(escrow.totalFundedAmount) : 0, requesterAddress: signer.address, exchangeOracleAddress: escrow?.exchangeOracle, @@ -707,7 +706,7 @@ export class JobService { if (!escrowAddress) { return { details: { - escrowAddress: ethers.constants.AddressZero, + escrowAddress: ethers.ZeroAddress, manifestUrl, manifestHash, balance: 0, @@ -716,7 +715,7 @@ export class JobService { }, manifest: manifestDetails, staking: { - staker: ethers.constants.AddressZero, + staker: ethers.ZeroAddress, allocated: 0, slashed: 0, }, @@ -728,14 +727,14 @@ export class JobService { escrowAddress, manifestUrl, manifestHash, - balance: Number(ethers.utils.formatEther(escrow?.balance || 0)), + balance: Number(ethers.formatEther(escrow?.balance || 0)), paidOut: Number(escrow?.amountPaid || 0), status, }, manifest: manifestDetails, staking: { staker: allocation?.staker as string, - allocated: allocation?.tokens.toNumber() as number, + allocated: Number(allocation?.tokens), slashed: 0, // TODO: Retrieve slash tokens }, }; @@ -750,12 +749,12 @@ export class JobService { const signer = this.web3Service.getSigner(chainId); const filter = { address: tokenAddress, - topics: [ethers.utils.id('Transfer(address,address,uint256)')], + topics: [ethers.id('Transfer(address,address,uint256)')], fromBlock: fromBlock, toBlock: toBlock, }; - return signer.provider.getLogs(filter); + return signer.provider?.getLogs(filter); } public async getPaidOutAmount( @@ -772,13 +771,16 @@ export class JobService { const logs = await this.getTransferLogs(chainId, tokenAddress, 0, 'latest'); let paidOutAmount = new Decimal(0); - logs.forEach((log) => { - const parsedLog = tokenContract.interface.parseLog(log); - const from = parsedLog.args[0]; - const amount = parsedLog.args[2]; + logs?.forEach((log) => { + const parsedLog = tokenContract.interface.parseLog({ + topics: log.topics as string[], + data: log.data, + }); + const from = parsedLog?.args[0]; + const amount = parsedLog?.args[2]; if (from === escrowAddress) { - paidOutAmount = paidOutAmount.add(ethers.utils.formatEther(amount)); + paidOutAmount = paidOutAmount.add(ethers.formatEther(amount)); } }); diff --git a/packages/apps/hufi/job-launcher/server/src/modules/payment/payment.service.spec.ts b/packages/apps/hufi/job-launcher/server/src/modules/payment/payment.service.spec.ts index 260447a419..14ede8d24d 100644 --- a/packages/apps/hufi/job-launcher/server/src/modules/payment/payment.service.spec.ts +++ b/packages/apps/hufi/job-launcher/server/src/modules/payment/payment.service.spec.ts @@ -6,7 +6,11 @@ import { PaymentRepository } from './payment.repository'; import { ConfigService } from '@nestjs/config'; import { HttpService } from '@nestjs/axios'; import { createMock } from '@golevelup/ts-jest'; -import { ErrorPayment, ErrorPostgres, ErrorSignature } from '../../common/constants/errors'; +import { + ErrorPayment, + ErrorPostgres, + ErrorSignature, +} from '../../common/constants/errors'; import { TransactionReceipt } from '@ethersproject/abstract-provider'; import { Currency, @@ -23,7 +27,7 @@ import { MOCK_SIGNATURE, MOCK_TRANSACTION_HASH, } from '../../../test/constants'; -import { Web3Service } from '../web3/web3.service'; +import { Web3Service } from '../web3/Web3Service'; import { HMToken__factory } from '@human-protocol/core/typechain-types'; import { ChainId, NETWORKS } from '@human-protocol/sdk'; import { PaymentEntity } from './payment.entity'; @@ -666,9 +670,13 @@ describe('PaymentService', () => { }; it('should successfully create a refund payment', async () => { - jest.spyOn(paymentRepository, 'create').mockResolvedValueOnce(undefined as any); + jest + .spyOn(paymentRepository, 'create') + .mockResolvedValueOnce(undefined as any); - await expect(paymentService.createRefundPayment(mockPaymentRefundCreateDto)).resolves.not.toThrow(); + await expect( + paymentService.createRefundPayment(mockPaymentRefundCreateDto), + ).resolves.not.toThrow(); }); it('should throw IncorrectAmount error when overflow occurs', async () => { @@ -676,13 +684,19 @@ describe('PaymentService', () => { mockError.message = ErrorPostgres.NumericFieldOverflow.toLowerCase(); jest.spyOn(paymentRepository, 'create').mockRejectedValueOnce(mockError); - await expect(paymentService.createRefundPayment(mockPaymentRefundCreateDto)).rejects.toThrow(ConflictException); + await expect( + paymentService.createRefundPayment(mockPaymentRefundCreateDto), + ).rejects.toThrow(ConflictException); }); it('should throw NotSuccess error on other database errors', async () => { - jest.spyOn(paymentRepository, 'create').mockRejectedValueOnce(new Error()); + jest + .spyOn(paymentRepository, 'create') + .mockRejectedValueOnce(new Error()); - await expect(paymentService.createRefundPayment(mockPaymentRefundCreateDto)).rejects.toThrow(ConflictException); + await expect( + paymentService.createRefundPayment(mockPaymentRefundCreateDto), + ).rejects.toThrow(ConflictException); }); }); }); diff --git a/packages/apps/hufi/job-launcher/server/src/modules/payment/payment.service.ts b/packages/apps/hufi/job-launcher/server/src/modules/payment/payment.service.ts index 87657d31c8..3e0d58eec4 100644 --- a/packages/apps/hufi/job-launcher/server/src/modules/payment/payment.service.ts +++ b/packages/apps/hufi/job-launcher/server/src/modules/payment/payment.service.ts @@ -7,7 +7,7 @@ import { } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import Stripe from 'stripe'; -import { ethers, providers } from 'ethers'; +import { ethers } from 'ethers'; import { ErrorPayment, ErrorPostgres } from '../../common/constants/errors'; import { PaymentRepository } from './payment.repository'; import { @@ -30,7 +30,7 @@ import { HMToken, HMToken__factory, } from '@human-protocol/core/typechain-types'; -import { Web3Service } from '../web3/web3.service'; +import { Web3Service } from '../web3/Web3Service'; import { CoingeckoTokenId } from '../../common/constants/payment'; import { getRate } from '../../common/utils'; import { add, div, mul } from '../../common/utils/decimal'; @@ -170,7 +170,7 @@ export class PaymentService { const network = Object.values(networkMap).find( (item) => item.chainId === dto.chainId, ); - const provider = new providers.JsonRpcProvider(network?.rpcUrl); + const provider = new ethers.JsonRpcProvider(network?.rpcUrl); const transaction = await provider.getTransactionReceipt( dto.transactionHash, @@ -188,7 +188,7 @@ export class PaymentService { throw new NotFoundException(ErrorPayment.InvalidTransactionData); } - if (transaction.confirmations < TX_CONFIRMATION_TRESHOLD) { + if ((await transaction.confirmations()) < TX_CONFIRMATION_TRESHOLD) { this.logger.error( `Transaction has ${transaction.confirmations} confirmations instead of ${TX_CONFIRMATION_TRESHOLD}`, ); @@ -200,15 +200,14 @@ export class PaymentService { const signer = this.web3Service.getSigner(dto.chainId); const recipientAddress = transaction.logs[0].topics.some( - (topic) => - ethers.utils.hexValue(topic) === ethers.utils.hexValue(signer.address), + (topic) => ethers.hexlify(topic) === ethers.hexlify(signer.address), ); if (!recipientAddress) { this.logger.error(ErrorPayment.InvalidRecipient); throw new ConflictException(ErrorPayment.InvalidRecipient); } - const amount = Number(ethers.utils.formatEther(transaction.logs[0].data)); + const amount = Number(ethers.formatEther(transaction.logs[0].data)); const tokenAddress = transaction.logs[0].address; const tokenContract: HMToken = HMToken__factory.connect( @@ -226,7 +225,7 @@ export class PaymentService { } const paymentEntity = await this.paymentRepository.findOne({ - transaction: transaction.transactionHash, + transaction: transaction.hash, chainId: dto.chainId, }); @@ -272,24 +271,30 @@ export class PaymentService { const rate = await getRate(TokenId.HMT, Currency.USD); try { - await this.paymentRepository.create({ - userId: dto.userId, - jobId: dto.jobId, - source: PaymentSource.BALANCE, - type: PaymentType.REFUND, - amount: dto.refundAmount, - currency: TokenId.HMT, - rate, - status: PaymentStatus.SUCCEEDED, - }); + await this.paymentRepository.create({ + userId: dto.userId, + jobId: dto.jobId, + source: PaymentSource.BALANCE, + type: PaymentType.REFUND, + amount: dto.refundAmount, + currency: TokenId.HMT, + rate, + status: PaymentStatus.SUCCEEDED, + }); } catch (error) { - if (error instanceof QueryFailedError && error.message.includes(ErrorPostgres.NumericFieldOverflow.toLowerCase())) { - this.logger.log(ErrorPostgres.NumericFieldOverflow, PaymentService.name); - throw new ConflictException(ErrorPayment.IncorrectAmount); - } else { - this.logger.log(error, PaymentService.name); - throw new ConflictException(ErrorPayment.NotSuccess); - } + if ( + error instanceof QueryFailedError && + error.message.includes(ErrorPostgres.NumericFieldOverflow.toLowerCase()) + ) { + this.logger.log( + ErrorPostgres.NumericFieldOverflow, + PaymentService.name, + ); + throw new ConflictException(ErrorPayment.IncorrectAmount); + } else { + this.logger.log(error, PaymentService.name); + throw new ConflictException(ErrorPayment.NotSuccess); + } } } } diff --git a/packages/apps/hufi/job-launcher/server/src/modules/web3/Web3Service.ts b/packages/apps/hufi/job-launcher/server/src/modules/web3/Web3Service.ts new file mode 100644 index 0000000000..c76962ec48 --- /dev/null +++ b/packages/apps/hufi/job-launcher/server/src/modules/web3/Web3Service.ts @@ -0,0 +1,57 @@ +import { BadRequestException, Injectable, Logger } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { Wallet, ethers } from 'ethers'; +import { ConfigNames, networks } from '../../common/config'; +import { Web3Env } from '../../common/enums/web3'; +import { MAINNET_CHAIN_IDS, TESTNET_CHAIN_IDS } from '../../common/constants'; +import { ErrorWeb3 } from '../../common/constants/errors'; +import { ChainId } from '@human-protocol/sdk'; + +@Injectable() +export class Web3Service { + private signers: { [key: number]: Wallet } = {}; + public readonly logger = new Logger(Web3Service.name); + public readonly signerAddress: string; + + constructor(private readonly configService: ConfigService) { + const privateKey = this.configService.get(ConfigNames.WEB3_PRIVATE_KEY); + const validChains = this.getValidChains(); + const validNetworks = networks.filter((network) => + validChains.includes(network.chainId), + ); + for (const network of validNetworks) { + const provider = new ethers.JsonRpcProvider(network.rpcUrl); + this.signers[network.chainId] = new Wallet(privateKey, provider); + } + this.signerAddress = this.signers[validChains[0]].address; + } + + public getSigner(chainId: number): Wallet { + this.validateChainId(chainId); + return this.signers[chainId]; + } + + public validateChainId(chainId: number): void { + const currentWeb3Env = this.configService.get(ConfigNames.WEB3_ENV); + const validChainIds = this.getValidChains(); + + if (!validChainIds.includes(chainId)) { + const errorType = + currentWeb3Env === Web3Env.MAINNET + ? ErrorWeb3.InvalidMainnetChainId + : ErrorWeb3.InvalidTestnetChainId; + this.logger.log(errorType, Web3Service.name); + throw new BadRequestException(errorType); + } + } + + public getValidChains(): ChainId[] { + const currentWeb3Env = this.configService.get(ConfigNames.WEB3_ENV); + const validChainIds = + currentWeb3Env === Web3Env.MAINNET + ? MAINNET_CHAIN_IDS + : TESTNET_CHAIN_IDS; + + return validChainIds; + } +} diff --git a/packages/apps/hufi/job-launcher/server/src/modules/web3/web3.controller.ts b/packages/apps/hufi/job-launcher/server/src/modules/web3/web3.controller.ts index a03027c364..58c2d116e3 100644 --- a/packages/apps/hufi/job-launcher/server/src/modules/web3/web3.controller.ts +++ b/packages/apps/hufi/job-launcher/server/src/modules/web3/web3.controller.ts @@ -1,7 +1,7 @@ import { Controller, Get } from '@nestjs/common'; import { Public } from '../../common/decorators'; import { ApiTags } from '@nestjs/swagger'; -import { Web3Service } from './web3.service'; +import { Web3Service } from './Web3Service'; import { ChainId } from '@human-protocol/sdk'; @ApiTags('Web3') diff --git a/packages/apps/hufi/job-launcher/server/src/modules/web3/web3.module.ts b/packages/apps/hufi/job-launcher/server/src/modules/web3/web3.module.ts index acd61d7ac6..1cb936f905 100644 --- a/packages/apps/hufi/job-launcher/server/src/modules/web3/web3.module.ts +++ b/packages/apps/hufi/job-launcher/server/src/modules/web3/web3.module.ts @@ -1,5 +1,5 @@ import { Logger, Module } from '@nestjs/common'; -import { Web3Service } from './web3.service'; +import { Web3Service } from './Web3Service'; import { ConfigModule } from '@nestjs/config'; import { Web3Controller } from './web3.controller'; diff --git a/packages/apps/hufi/job-launcher/server/src/modules/web3/web3.service.spec.ts b/packages/apps/hufi/job-launcher/server/src/modules/web3/web3.service.spec.ts index 26aecc09ce..af1e57ef82 100644 --- a/packages/apps/hufi/job-launcher/server/src/modules/web3/web3.service.spec.ts +++ b/packages/apps/hufi/job-launcher/server/src/modules/web3/web3.service.spec.ts @@ -4,7 +4,7 @@ import { Test } from '@nestjs/testing'; import { MAINNET_CHAIN_IDS, TESTNET_CHAIN_IDS } from '../../common/constants'; import { ErrorWeb3 } from '../../common/constants/errors'; import { Web3Env } from '../../common/enums/web3'; -import { Web3Service } from './web3.service'; +import { Web3Service } from './Web3Service'; describe('Web3Service', () => { let mockConfigService: Partial; diff --git a/packages/apps/hufi/job-launcher/server/src/modules/web3/web3.service.ts b/packages/apps/hufi/job-launcher/server/src/modules/web3/web3.service.ts index 1a612d52bb..c76962ec48 100644 --- a/packages/apps/hufi/job-launcher/server/src/modules/web3/web3.service.ts +++ b/packages/apps/hufi/job-launcher/server/src/modules/web3/web3.service.ts @@ -1,6 +1,6 @@ import { BadRequestException, Injectable, Logger } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; -import { Wallet, providers } from 'ethers'; +import { Wallet, ethers } from 'ethers'; import { ConfigNames, networks } from '../../common/config'; import { Web3Env } from '../../common/enums/web3'; import { MAINNET_CHAIN_IDS, TESTNET_CHAIN_IDS } from '../../common/constants'; @@ -20,7 +20,7 @@ export class Web3Service { validChains.includes(network.chainId), ); for (const network of validNetworks) { - const provider = new providers.JsonRpcProvider(network.rpcUrl); + const provider = new ethers.JsonRpcProvider(network.rpcUrl); this.signers[network.chainId] = new Wallet(privateKey, provider); } this.signerAddress = this.signers[validChains[0]].address; diff --git a/packages/apps/hufi/job-launcher/server/tsconfig.json b/packages/apps/hufi/job-launcher/server/tsconfig.json index 7252f8ae99..fb6e941176 100644 --- a/packages/apps/hufi/job-launcher/server/tsconfig.json +++ b/packages/apps/hufi/job-launcher/server/tsconfig.json @@ -7,7 +7,7 @@ "experimentalDecorators": true, "allowSyntheticDefaultImports": true, "allowJs": true, - "target": "es2017", + "target": "ES2020", "sourceMap": true, "outDir": "./dist", "baseUrl": "./", @@ -18,6 +18,6 @@ "strictBindCallApply": true, "forceConsistentCasingInFileNames": true, "noFallthroughCasesInSwitch": true, - "esModuleInterop": true, + "esModuleInterop": true } } diff --git a/packages/apps/hufi/recording-oracle/package.json b/packages/apps/hufi/recording-oracle/package.json index 864be86bbb..73a7edeebb 100644 --- a/packages/apps/hufi/recording-oracle/package.json +++ b/packages/apps/hufi/recording-oracle/package.json @@ -34,7 +34,6 @@ "body-parser": "^1.20.2", "class-validator": "^0.14.0", "cookie-parser": "^1.4.6", - "ethers": "^5.7.2", "graphql-request": "^6.1.0", "helmet": "^7.1.0", "reflect-metadata": "^0.1.13" diff --git a/packages/apps/hufi/recording-oracle/src/common/utils/signature.ts b/packages/apps/hufi/recording-oracle/src/common/utils/signature.ts index 9741950d9e..20cc25b726 100644 --- a/packages/apps/hufi/recording-oracle/src/common/utils/signature.ts +++ b/packages/apps/hufi/recording-oracle/src/common/utils/signature.ts @@ -40,7 +40,7 @@ export function recoverSigner( } try { - return ethers.utils.verifyMessage(message, signature); + return ethers.verifyMessage(message, signature); } catch (e) { throw new ConflictException('Invalid signature'); } diff --git a/packages/apps/hufi/recording-oracle/src/common/validators/ethers.ts b/packages/apps/hufi/recording-oracle/src/common/validators/ethers.ts index 982b4aa3ba..55bc380197 100644 --- a/packages/apps/hufi/recording-oracle/src/common/validators/ethers.ts +++ b/packages/apps/hufi/recording-oracle/src/common/validators/ethers.ts @@ -11,7 +11,7 @@ import { ethers } from 'ethers'; @Injectable() class ValidateEthereumAddress implements ValidatorConstraintInterface { public validate(value: string): boolean { - return ethers.utils.isAddress(value); + return ethers.isAddress(value); } public defaultMessage(): string { diff --git a/packages/apps/hufi/recording-oracle/src/modules/liquidity/liquidity.service.ts b/packages/apps/hufi/recording-oracle/src/modules/liquidity/liquidity.service.ts index 4c8e0e49c2..90552ad865 100644 --- a/packages/apps/hufi/recording-oracle/src/modules/liquidity/liquidity.service.ts +++ b/packages/apps/hufi/recording-oracle/src/modules/liquidity/liquidity.service.ts @@ -12,7 +12,7 @@ import { Logger, NotFoundException, } from '@nestjs/common'; -import { BigNumber, ethers } from 'ethers'; +import { ethers } from 'ethers'; import * as Minio from 'minio'; import { @@ -36,7 +36,7 @@ import { ConfigService } from '@nestjs/config'; import { signMessage } from '../../common/utils/signature'; import { HEADER_SIGNATURE_KEY } from '../../common/constants'; import { GraphQLClient, gql } from 'graphql-request'; -import crypto from "crypto"; +import crypto from 'crypto'; import { CEX, DEX } from '../../common/constants/exchange'; @Injectable() @@ -121,7 +121,7 @@ export class LiquidityService { await this.httpService.post(url, body, { headers: { [HEADER_SIGNATURE_KEY]: signedBody }, }); - } + } public async getLiquidityScore( liquidityRequest: liquidityRequestDto, @@ -191,16 +191,16 @@ export class LiquidityService { await escrowClient.getRecordingOracleAddress( liquidityRequest.escrowAddress, ); - + if ( - ethers.utils.getAddress(recordingOracleAddress) !== + ethers.getAddress(recordingOracleAddress) !== (await signer.getAddress()) ) { this.logger.log(ErrorJob.AddressMismatches, LiquidityService.name); throw new BadRequestException(ErrorJob.AddressMismatches); } - let [exchange, chain] = manifest.exchangeName.split('-'); + const [exchange, chain] = manifest.exchangeName.split('-'); const variables = { user: liquidityRequest.liquidityProvider, startTime: manifest.startBlock, @@ -211,9 +211,11 @@ export class LiquidityService { chain, }; - if (CEX.includes(manifest.exchangeName)) - { - if (!liquidityRequest.liquidityProviderAPISecret || !liquidityRequest.liquidityProviderAPIKEY) { + if (CEX.includes(manifest.exchangeName)) { + if ( + !liquidityRequest.liquidityProviderAPISecret || + !liquidityRequest.liquidityProviderAPIKEY + ) { throw new Error('Empty API keys'); } const queryString = `symbol=${manifest.tokenA}${ @@ -255,12 +257,14 @@ export class LiquidityService { } return liquidityScore; } - throw new Error('No data received from server'); - } - else { - const client = this.getGraphQLClient(variables.chain, variables.exchange); + throw new Error('No data received from server'); + } else { + const client = this.getGraphQLClient( + variables.chain, + variables.exchange, + ); const result: any = await client.request(UniswapQuery, variables); - let positionSnapshots = result?.positionSnapshots; + const positionSnapshots = result?.positionSnapshots; const filteredSnapshots = this.filterObjectsByInputTokenSymbol( positionSnapshots, @@ -287,7 +291,7 @@ export class LiquidityService { liquidityProvider: liquidityRequest.liquidityProvider, }; return response; - } + } } catch (error: any) { console.error(`Error in getLiquidityScore: ${error.message}`); throw error; @@ -295,7 +299,7 @@ export class LiquidityService { } public calculateLiquidityScore(snapshots: any[]): string { - let totalScore = BigNumber.from(0); + let totalScore = 0n; // Check for no snapshots if (snapshots.length === 0) { @@ -321,27 +325,25 @@ export class LiquidityService { * Calculate score for a single snapshot. * * @param snapshot - The liquidity snapshot. - * @returns The liquidity score as a BigNumber. + * @returns The liquidity score as a BigInt. */ - private calculateSingleSnapshotScore(snapshot: any): BigNumber { - const totalLiquidity = BigNumber.from( - snapshot.position.pool.totalLiquidity, - ); - const liquidityAmount = BigNumber.from(snapshot.position.liquidity); - const currentTime = BigNumber.from(Math.floor(Date.now() / 1000)); - const timeWithheld = currentTime.sub(snapshot.timestamp); + private calculateSingleSnapshotScore(snapshot: any): bigint { + const totalLiquidity = BigInt(snapshot.position.pool.totalLiquidity); + const liquidityAmount = BigInt(snapshot.position.liquidity); + const currentTime = BigInt(Math.floor(Date.now() / 1000)); + const timeWithheld = currentTime - snapshot.timestamp; - return liquidityAmount.mul(timeWithheld).div(totalLiquidity); + return (liquidityAmount * timeWithheld) / totalLiquidity; } /** * Calculate score for multiple snapshots. * * @param snapshots - Array of liquidity snapshots. - * @returns The cumulative liquidity score as a BigNumber. + * @returns The cumulative liquidity score as a bigint. */ - private calculateMultipleSnapshotsScore(snapshots: any[]): BigNumber { - let totalScore = BigNumber.from(0); + private calculateMultipleSnapshotsScore(snapshots: any[]): bigint { + let totalScore = 0n; for (let i = 1; i < snapshots.length; i++) { const snapshot = snapshots[i]; @@ -353,19 +355,15 @@ export class LiquidityService { continue; } - const totalLiquidity = BigNumber.from( - snapshot.position.pool.totalLiquidity, - ); - const liquidityAmount = BigNumber.from(snapshot.position.liquidity); - const timeWithheld = BigNumber.from(snapshot.timestamp).sub( - prevSnapshot.timestamp, - ); + const totalLiquidity = BigInt(snapshot.position.pool.totalLiquidity); + const liquidityAmount = BigInt(snapshot.position.liquidity); + const timeWithheld = BigInt(snapshot.timestamp) - prevSnapshot.timestamp; - const PRECISION = BigNumber.from((10 ** 3).toString()); // Let's try with a higher precision to keep more decimals + const PRECISION = BigInt((10 ** 3).toString()); // Let's try with a higher precision to keep more decimals - const adjustedLiquidity = liquidityAmount.mul(PRECISION); - const multipliedLiquidity = adjustedLiquidity.mul(timeWithheld); - const rawScore = multipliedLiquidity.div(totalLiquidity); + const adjustedLiquidity = liquidityAmount * PRECISION; + const multipliedLiquidity = adjustedLiquidity * timeWithheld; + const rawScore = multipliedLiquidity / totalLiquidity; const snapshotScore = rawScore; console.log(snapshot); // Log all steps to find out what's going wrong @@ -379,28 +377,31 @@ export class LiquidityService { console.log('Liquidity Amount:', liquidityAmount.toString()); console.log('Time Withheld:', timeWithheld.toString()); - totalScore = totalScore.add(snapshotScore); + totalScore = totalScore + snapshotScore; console.log('total score', snapshotScore.toString()); } return totalScore; } - public calculateCentralizedLiquidityScore(orders: { cummulativeQuoteQty: number; time: number; updateTime: number; }[]): string { + public calculateCentralizedLiquidityScore( + orders: { cummulativeQuoteQty: number; time: number; updateTime: number }[], + ): string { if (orders.length === 0) { return '0'; } - + const totalScore = orders.reduce((acc, order) => { console.log('Order: ', order); const liquidityAmount = order.cummulativeQuoteQty; - const timeWithheld = order.time === order.updateTime ? 1 : order.updateTime - order.time; + const timeWithheld = + order.time === order.updateTime ? 1 : order.updateTime - order.time; const score = liquidityAmount * timeWithheld; acc += score; console.log('Total Score: ', acc); return acc; }, 0); - + return totalScore.toString(); } @@ -429,11 +430,16 @@ export class LiquidityService { const escrowClient = await EscrowClient.build(signer); let liquidities: liquidityDto[]; - const existingLiquiditiesURL: string = await escrowClient.getIntermediateResultsUrl(escrowAddress); + const existingLiquiditiesURL: string = + await escrowClient.getIntermediateResultsUrl(escrowAddress); if (existingLiquiditiesURL) { - liquidities = JSON.parse(await this.storageService.download(existingLiquiditiesURL)); + liquidities = JSON.parse( + await this.storageService.download(existingLiquiditiesURL), + ); if (liquidities) { - const exisitingLiquidity = liquidities.find((liq) => liq.liquidityProvider === liquidityProvider); + const exisitingLiquidity = liquidities.find( + (liq) => liq.liquidityProvider === liquidityProvider, + ); if (exisitingLiquidity) { exisitingLiquidity.liquidityScore = score; } else { @@ -446,15 +452,14 @@ export class LiquidityService { } else { throw new NotFoundException(ErrorJob.NotFoundIntermediateResults); } - } else { liquidities = [ { chainId, liquidityProvider, liquidityScore: score, - } - ] + }, + ]; } const saveLiquidityResult = await this.storageService.uploadLiquidities( @@ -462,7 +467,7 @@ export class LiquidityService { chainId, liquidities, ); - + if (!existingLiquiditiesURL) { await escrowClient.storeResults( escrowAddress, diff --git a/packages/apps/hufi/recording-oracle/src/modules/web3/web3.service.ts b/packages/apps/hufi/recording-oracle/src/modules/web3/web3.service.ts index abb789c2e2..70b97fe0c6 100644 --- a/packages/apps/hufi/recording-oracle/src/modules/web3/web3.service.ts +++ b/packages/apps/hufi/recording-oracle/src/modules/web3/web3.service.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { Wallet, providers } from 'ethers'; +import { Wallet, ethers } from 'ethers'; import { Web3ConfigType, web3ConfigKey } from '../../common/config'; import { networkMap } from '../../common/constants/networks'; @@ -15,7 +15,7 @@ export class Web3Service { for (const networkKey of Object.keys(networkMap)) { const network = networkMap[networkKey]; - const provider = new providers.JsonRpcProvider(network.rpcUrl); + const provider = new ethers.JsonRpcProvider(network.rpcUrl); this.signers[network.chainId] = new Wallet(privateKey, provider); } } diff --git a/packages/apps/hufi/reputation-oracle/server/package.json b/packages/apps/hufi/reputation-oracle/server/package.json index 2f0cb17cc5..270e6bf9e9 100644 --- a/packages/apps/hufi/reputation-oracle/server/package.json +++ b/packages/apps/hufi/reputation-oracle/server/package.json @@ -31,48 +31,48 @@ "@human-protocol/core": "*", "@human-protocol/sdk": "*", "@nestjs/axios": "^2.0.0", - "@nestjs/jwt": "^10.2.0", "@nestjs/common": "^10.2.7", + "@nestjs/config": "^3.1.1", "@nestjs/core": "^10.2.8", + "@nestjs/jwt": "^10.2.0", + "@nestjs/passport": "^10.0.0", "@nestjs/platform-express": "^10.2.6", - "passport": "^0.6.0", - "@types/passport-jwt": "^3.0.10", - "passport-jwt": "^4.0.1", - "@nestjs/terminus": "^10.2.0", "@nestjs/schedule": "^3.0.1", - "typeorm-naming-strategies": "^4.1.0", - "zxcvbn": "^4.4.2", - "@nestjs/passport": "^10.0.0", - "@nestjs/typeorm": "^10.0.1", - "ethers": "^5.7.2", - "joi": "^17.9.2", - "reflect-metadata": "^0.1.13", - "rxjs": "^7.2.0", - "nestjs-minio-client": "^2.2.0", "@nestjs/swagger": "^7.1.13", - "class-transformer": "^0.5.1", + "@nestjs/terminus": "^10.2.0", + "@nestjs/typeorm": "^10.0.1", + "@types/passport-jwt": "^3.0.10", "bcrypt": "^5.1.1", - "@nestjs/config": "^3.1.1", - "express-session": "^1.17.3", + "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "cookie-parser": "^1.4.6", + "express-session": "^1.17.3", "helmet": "^7.1.0", + "joi": "^17.9.2", + "nestjs-minio-client": "^2.2.0", + "passport": "^0.6.0", + "passport-jwt": "^4.0.1", "pg": "8.11.0", - "typeorm": "^0.3.16" + "reflect-metadata": "^0.1.13", + "rxjs": "^7.2.0", + "typeorm": "^0.3.16", + "typeorm-naming-strategies": "^4.1.0", + "zxcvbn": "^4.4.2" }, "devDependencies": { "@golevelup/ts-jest": "^0.4.0", "@nestjs/cli": "^9.4.3", "@nestjs/schematics": "^9.2.0", "@nestjs/testing": "^9.4.3", + "@types/bcrypt": "^5.0.2", + "@types/cookie-parser": "^1.4.3", "@types/express": "^4.17.13", + "@types/express-session": "^1.17.10", "@types/jest": "29.5.1", "@types/node": "20.10.6", "@types/supertest": "^2.0.15", - "@types/zxcvbn": "4.4.1", - "@types/cookie-parser": "^1.4.3", - "@types/express-session": "^1.17.10", "@types/uuid": "^9.0.6", + "@types/zxcvbn": "4.4.1", "@typescript-eslint/eslint-plugin": "^5.0.0", "@typescript-eslint/parser": "^5.0.0", "eslint": "^8.55.0", @@ -87,7 +87,6 @@ "ts-loader": "^9.2.3", "ts-node": "^10.9.2", "tsconfig-paths": "4.2.0", - "typescript": "^5.0.0", - "@types/bcrypt": "^5.0.2" + "typescript": "^5.0.0" } } diff --git a/packages/apps/hufi/reputation-oracle/server/src/common/utils/signature.ts b/packages/apps/hufi/reputation-oracle/server/src/common/utils/signature.ts index e1bf475a57..c619886dcb 100644 --- a/packages/apps/hufi/reputation-oracle/server/src/common/utils/signature.ts +++ b/packages/apps/hufi/reputation-oracle/server/src/common/utils/signature.ts @@ -41,7 +41,7 @@ export function recoverSigner( } try { - return ethers.utils.verifyMessage(message, signature); + return ethers.verifyMessage(message, signature); } catch (e) { throw new ConflictException(ErrorSignature.InvalidSignature); } diff --git a/packages/apps/hufi/reputation-oracle/server/src/modules/web3/web3.service.ts b/packages/apps/hufi/reputation-oracle/server/src/modules/web3/web3.service.ts index 8b981db854..be2b8857a7 100644 --- a/packages/apps/hufi/reputation-oracle/server/src/modules/web3/web3.service.ts +++ b/packages/apps/hufi/reputation-oracle/server/src/modules/web3/web3.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; -import { Wallet, providers } from 'ethers'; +import { Wallet, ethers } from 'ethers'; import { ConfigNames, networkMap } from '../../common/config'; @Injectable() @@ -11,7 +11,7 @@ export class Web3Service { const privateKey = this.configService.get(ConfigNames.WEB3_PRIVATE_KEY); for (const networkKey of Object.keys(networkMap)) { const network = networkMap[networkKey]; - const provider = new providers.JsonRpcProvider(network.rpcUrl); + const provider = new ethers.JsonRpcProvider(network.rpcUrl); this.signers[network.chainId] = new Wallet(privateKey, provider); } } diff --git a/packages/apps/hufi/reputation-oracle/server/src/modules/webhook/webhook.service.ts b/packages/apps/hufi/reputation-oracle/server/src/modules/webhook/webhook.service.ts index b277b2899d..d8bf361b86 100644 --- a/packages/apps/hufi/reputation-oracle/server/src/modules/webhook/webhook.service.ts +++ b/packages/apps/hufi/reputation-oracle/server/src/modules/webhook/webhook.service.ts @@ -12,7 +12,6 @@ import { WebhookIncomingDto, liquidityDto } from './webhook.dto'; import { ErrorWebhook } from '../../common/constants/errors'; import { WebhookRepository } from './webhook.repository'; import { RETRIES_COUNT_THRESHOLD } from '../../common/constants'; -import { BigNumber } from 'ethers'; import { Web3Service } from '../web3/web3.service'; import { EventType, @@ -160,26 +159,26 @@ export class WebhookService { } public calculateCampaignPayoutAmounts( - totalAmount: BigNumber, + totalAmount: bigint, results: liquidityDto[], - ): BigNumber[] { - // Convert the liquidity scores to BigNumber for precision in calculations. + ): bigint[] { + // Convert the liquidity scores to bigint for precision in calculations. const bigNumberResults = results.map((result) => ({ ...result, - liquidityScore: BigNumber.from(result.liquidityScore), + liquidityScore: BigInt(result.liquidityScore), })); // Calculate the total liquidity score as a BigNumber. const totalLiquidityScore = bigNumberResults.reduce( - (total, item) => total.add(item.liquidityScore), - BigNumber.from(0), + (total, item) => total + item.liquidityScore, + 0n, ); // Map through each result, calculate each recipient's payout, and return the array. const payouts = bigNumberResults.map((result) => { const participantScore = result.liquidityScore; - const participantPercentage = participantScore.div(totalLiquidityScore); - return totalAmount.mul(participantPercentage); + const participantPercentage = participantScore / totalLiquidityScore; + return totalAmount * participantPercentage; }); return payouts; @@ -283,8 +282,8 @@ export class WebhookService { ReputationEntityType.REPUTATION_ORACLE, ); - const balance = BigNumber.from(escrow.balance); - if (balance.isZero()) { + const balance = BigInt(escrow.balance); + if (balance === 0n) { await escrowClient.complete(webhookEntity.escrowAddress); } await this.webhookRepository.updateOne( diff --git a/packages/apps/hufi/reputation-oracle/server/tsconfig.json b/packages/apps/hufi/reputation-oracle/server/tsconfig.json index 5393cfe8d6..fb6e941176 100644 --- a/packages/apps/hufi/reputation-oracle/server/tsconfig.json +++ b/packages/apps/hufi/reputation-oracle/server/tsconfig.json @@ -7,7 +7,7 @@ "experimentalDecorators": true, "allowSyntheticDefaultImports": true, "allowJs": true, - "target": "es2017", + "target": "ES2020", "sourceMap": true, "outDir": "./dist", "baseUrl": "./", diff --git a/packages/apps/job-launcher/server/package.json b/packages/apps/job-launcher/server/package.json index 1ffe0e368b..a2af88670b 100644 --- a/packages/apps/job-launcher/server/package.json +++ b/packages/apps/job-launcher/server/package.json @@ -49,7 +49,6 @@ "class-transformer": "^0.5.1", "cookie-parser": "^1.4.6", "decimal.js": "^10.4.3", - "ethers": "^5.7.2", "express-session": "^1.17.3", "joi": "^17.9.2", "json-stable-stringify": "^1.0.2", @@ -75,8 +74,8 @@ "@types/json-stable-stringify": "^1.0.36", "@types/node": "20.10.6", "@types/supertest": "^2.0.15", - "@types/zxcvbn": "4.4.1", "@types/xml2js": "0.4.13", + "@types/zxcvbn": "4.4.1", "@typescript-eslint/eslint-plugin": "^5.0.0", "@typescript-eslint/parser": "^5.0.0", "eslint": "^8.55.0", diff --git a/packages/apps/job-launcher/server/src/common/utils/signature.ts b/packages/apps/job-launcher/server/src/common/utils/signature.ts index e1bf475a57..c619886dcb 100644 --- a/packages/apps/job-launcher/server/src/common/utils/signature.ts +++ b/packages/apps/job-launcher/server/src/common/utils/signature.ts @@ -41,7 +41,7 @@ export function recoverSigner( } try { - return ethers.utils.verifyMessage(message, signature); + return ethers.verifyMessage(message, signature); } catch (e) { throw new ConflictException(ErrorSignature.InvalidSignature); } diff --git a/packages/apps/job-launcher/server/src/modules/job/job.dto.ts b/packages/apps/job-launcher/server/src/modules/job/job.dto.ts index 64881853f3..b70ce7f442 100644 --- a/packages/apps/job-launcher/server/src/modules/job/job.dto.ts +++ b/packages/apps/job-launcher/server/src/modules/job/job.dto.ts @@ -32,7 +32,6 @@ import { WorkerLocation, } from '../../common/enums/job'; import { EventType } from '../../common/enums/webhook'; -import { BigNumber } from 'ethers'; import { AWSRegions, StorageProviders } from '../../common/enums/storage'; export class JobCreateDto { @ApiProperty({ enum: ChainId }) @@ -505,7 +504,7 @@ export class EscrowCancelDto { public txHash: string; @ApiProperty() - public amountRefunded: BigNumber; + public amountRefunded: bigint; } export class JobCaptchaAdvancedDto { 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 4e90495ea8..cf8435dee0 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 @@ -98,7 +98,7 @@ import { div, mul } from '../../common/utils/decimal'; import { PaymentRepository } from '../payment/payment.repository'; import { RoutingProtocolService } from './routing-protocol.service'; import { EventType } from '../../common/enums/webhook'; -import { BigNumber, ethers } from 'ethers'; +import { ethers } from 'ethers'; import { HMToken__factory } from '@human-protocol/core/typechain-types'; import { StorageService } from '../storage/storage.service'; import { @@ -290,9 +290,7 @@ describe('JobService', () => { storageService.download = jest.fn(); - web3Service.calculateGasPrice = jest - .fn() - .mockReturnValue(BigNumber.from(1000)); + web3Service.calculateGasPrice = jest.fn().mockReturnValue(1000n); }); describe('createJob', () => { @@ -1966,7 +1964,7 @@ describe('JobService', () => { jest.spyOn(jobService, 'processEscrowCancellation').mockResolvedValue({ txHash: MOCK_TRANSACTION_HASH, - amountRefunded: BigNumber.from(1), + amountRefunded: 1n, }); (EscrowClient.build as any).mockImplementation(() => ({ @@ -2098,7 +2096,7 @@ describe('JobService', () => { }; it('should cancel escrow', async () => { - const fundedAmount = BigNumber.from(1); + const fundedAmount = 1n; const escrowClientMock = { getStatus: jest.fn().mockResolvedValue(EscrowStatus.Launched), @@ -2155,7 +2153,7 @@ describe('JobService', () => { it('should throw bad request exception if escrow balance is zero', async () => { (EscrowClient.build as any).mockImplementation(() => ({ getStatus: jest.fn().mockResolvedValue(EscrowStatus.Launched), - getBalance: jest.fn().mockResolvedValue({ eq: () => true }), + getBalance: jest.fn().mockResolvedValue(0n), })); await expect( @@ -2442,11 +2440,11 @@ describe('JobService', () => { it('should return job details with escrow address successfully', async () => { const balance = '1'; const allocationMock: IAllocation = { - escrowAddress: ethers.constants.AddressZero, - staker: ethers.constants.AddressZero, - tokens: BigNumber.from('1'), - createdAt: BigNumber.from('1'), - closedAt: BigNumber.from('1'), + escrowAddress: ethers.ZeroAddress, + staker: ethers.ZeroAddress, + tokens: 1n, + createdAt: 1n, + closedAt: 1n, }; const manifestMock: FortuneManifestDto = { @@ -2543,7 +2541,7 @@ describe('JobService', () => { const expectedJobDetailsDto: JobDetailsDto = { details: { - escrowAddress: ethers.constants.AddressZero, + escrowAddress: ethers.ZeroAddress, manifestUrl: MOCK_FILE_URL, manifestHash: MOCK_FILE_HASH, balance: 0, @@ -2555,13 +2553,13 @@ describe('JobService', () => { title: MOCK_REQUESTER_TITLE, description: MOCK_REQUESTER_DESCRIPTION, submissionsRequired: expect.any(Number), - tokenAddress: ethers.constants.AddressZero, + tokenAddress: ethers.ZeroAddress, fundAmount: expect.any(Number), requesterAddress: MOCK_ADDRESS, requestType: JobRequestType.FORTUNE, - exchangeOracleAddress: ethers.constants.AddressZero, - recordingOracleAddress: ethers.constants.AddressZero, - reputationOracleAddress: ethers.constants.AddressZero, + exchangeOracleAddress: ethers.ZeroAddress, + recordingOracleAddress: ethers.ZeroAddress, + reputationOracleAddress: ethers.ZeroAddress, }, staking: { staker: expect.any(String), @@ -2600,7 +2598,7 @@ describe('JobService', () => { await jobService.getTransferLogs(chainId, MOCK_ADDRESS, 0, 'latest'); expect( - web3Service.getSigner(chainId).provider.getLogs, + web3Service.getSigner(chainId).provider?.getLogs, ).toHaveBeenCalled(); }); }); @@ -2608,7 +2606,7 @@ describe('JobService', () => { describe('getPaidOutAmount', () => { it('should calculate the paid out amount', async () => { const chainId = ChainId.LOCALHOST; - const amount = ethers.utils.parseEther('1.5'); + const amount = ethers.parseEther('1.5'); const mockLogs = [ { data: 'mockData', @@ -2671,8 +2669,8 @@ describe('JobService', () => { ChainId.LOCALHOST, ); - expect(result.toNumber()).toBe(MOCK_ORACLE_FEE); - expect(result).toBeInstanceOf(BigNumber); + expect(Number(result)).toBe(MOCK_ORACLE_FEE); + expect(typeof result).toBe('bigint'); }); }); }); 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 7cf9b6fd9c..d19c9e63e3 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 @@ -23,7 +23,7 @@ import { } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { validate } from 'class-validator'; -import { BigNumber, ethers } from 'ethers'; +import { ethers } from 'ethers'; import { In, LessThanOrEqual, QueryFailedError } from 'typeorm'; import { ConfigNames } from '../../common/config'; import { @@ -543,8 +543,8 @@ export class JobService { ), ); - return ethers.utils.formatEther( - ethers.utils.parseUnits(fundAmount.toString(), 'ether').div(totalJobs), + return ethers.formatEther( + ethers.parseUnits(fundAmount.toString(), 'ether') / BigInt(totalJobs), ); } @@ -657,7 +657,7 @@ export class JobService { const escrowClient = await EscrowClient.build(signer); - const weiAmount = ethers.utils.parseUnits( + const weiAmount = ethers.parseUnits( jobEntity.fundAmount.toString(), 'ether', ); @@ -770,7 +770,7 @@ export class JobService { limit, ); const escrowAddresses = escrows.map((escrow) => - ethers.utils.getAddress(escrow.address), + ethers.getAddress(escrow.address), ); jobs = await this.jobRepository.findJobsByEscrowAddresses( @@ -1112,7 +1112,7 @@ export class JobService { const { amountRefunded } = await this.processEscrowCancellation(jobEntity); await this.paymentService.createRefundPayment({ - refundAmount: Number(ethers.utils.formatEther(amountRefunded)), + refundAmount: Number(ethers.formatEther(amountRefunded)), userId: jobEntity.userId, jobId: jobEntity.id, }); @@ -1206,7 +1206,7 @@ export class JobService { } const balance = await escrowClient.getBalance(escrowAddress); - if (balance.eq(0)) { + if (balance === 0n) { this.logger.log(ErrorEscrow.InvalidBalanceCancellation, JobService.name); throw new BadRequestException(ErrorEscrow.InvalidBalanceCancellation); } @@ -1299,15 +1299,12 @@ export class JobService { const baseManifestDetails = { chainId, - tokenAddress: escrow ? escrow.token : ethers.constants.AddressZero, + tokenAddress: escrow ? escrow.token : ethers.ZeroAddress, requesterAddress: signer.address, fundAmount: escrow ? Number(escrow.totalFundedAmount) : 0, - exchangeOracleAddress: - escrow?.exchangeOracle || ethers.constants.AddressZero, - recordingOracleAddress: - escrow?.recordingOracle || ethers.constants.AddressZero, - reputationOracleAddress: - escrow?.reputationOracle || ethers.constants.AddressZero, + exchangeOracleAddress: escrow?.exchangeOracle || ethers.ZeroAddress, + recordingOracleAddress: escrow?.recordingOracle || ethers.ZeroAddress, + reputationOracleAddress: escrow?.reputationOracle || ethers.ZeroAddress, }; let specificManifestDetails; @@ -1345,7 +1342,7 @@ export class JobService { if (!escrowAddress) { return { details: { - escrowAddress: ethers.constants.AddressZero, + escrowAddress: ethers.ZeroAddress, manifestUrl, manifestHash, balance: 0, @@ -1354,7 +1351,7 @@ export class JobService { }, manifest: manifestDetails, staking: { - staker: ethers.constants.AddressZero, + staker: ethers.ZeroAddress, allocated: 0, slashed: 0, }, @@ -1366,14 +1363,14 @@ export class JobService { escrowAddress, manifestUrl, manifestHash, - balance: Number(ethers.utils.formatEther(escrow?.balance || 0)), + balance: Number(ethers.formatEther(escrow?.balance || 0)), paidOut: Number(escrow?.amountPaid || 0), status: jobEntity.status, }, manifest: manifestDetails, staking: { staker: allocation?.staker as string, - allocated: allocation?.tokens.toNumber() as number, + allocated: Number(allocation?.tokens), slashed: 0, // TODO: Retrieve slash tokens }, }; @@ -1388,12 +1385,12 @@ export class JobService { const signer = this.web3Service.getSigner(chainId); const filter = { address: tokenAddress, - topics: [ethers.utils.id('Transfer(address,address,uint256)')], + topics: [ethers.id('Transfer(address,address,uint256)')], fromBlock: fromBlock, toBlock: toBlock, }; - return signer.provider.getLogs(filter); + return signer.provider?.getLogs(filter); } public async getPaidOutAmount( @@ -1410,13 +1407,16 @@ export class JobService { const logs = await this.getTransferLogs(chainId, tokenAddress, 0, 'latest'); let paidOutAmount = new Decimal(0); - logs.forEach((log) => { - const parsedLog = tokenContract.interface.parseLog(log); - const from = parsedLog.args[0]; - const amount = parsedLog.args[2]; + logs?.forEach((log) => { + const parsedLog = tokenContract.interface.parseLog({ + topics: log.topics as string[], + data: log.data, + }); + const from = parsedLog?.args[0]; + const amount = parsedLog?.args[2]; if (from === escrowAddress) { - paidOutAmount = paidOutAmount.add(ethers.utils.formatEther(amount)); + paidOutAmount = paidOutAmount.add(ethers.formatEther(amount)); } }); @@ -1426,14 +1426,14 @@ export class JobService { private async getOracleFee( oracleAddress: string, chainId: ChainId, - ): Promise { + ): Promise { const signer = this.web3Service.getSigner(chainId); const kvStoreClient = await KVStoreClient.build(signer); const feeValue = await kvStoreClient.get(oracleAddress, KVStoreKeys.fee); - return BigNumber.from(feeValue ? feeValue : 1); + return BigInt(feeValue ? feeValue : 1); } private async updateCompletedStatus( diff --git a/packages/apps/job-launcher/server/src/modules/payment/payment.service.spec.ts b/packages/apps/job-launcher/server/src/modules/payment/payment.service.spec.ts index 898be5e2c3..ac3ffe3e5a 100644 --- a/packages/apps/job-launcher/server/src/modules/payment/payment.service.spec.ts +++ b/packages/apps/job-launcher/server/src/modules/payment/payment.service.spec.ts @@ -11,7 +11,6 @@ import { ErrorPostgres, ErrorSignature, } from '../../common/constants/errors'; -import { TransactionReceipt } from '@ethersproject/abstract-provider'; import { Currency, PaymentSource, @@ -322,7 +321,7 @@ describe('PaymentService', () => { }; jest - .spyOn(ethers.providers, 'JsonRpcProvider') + .spyOn(ethers, 'JsonRpcProvider') .mockReturnValue(jsonRpcProviderMock as any); jest @@ -347,11 +346,11 @@ describe('PaymentService', () => { const token = 'hmt'; - const transactionReceipt: Partial = { + const transactionReceipt = { from: MOCK_ADDRESS, logs: [ { - data: ethers.utils.parseUnits('10').toString(), + data: ethers.parseUnits('10').toString(), blockNumber: 123, blockHash: '123', transactionIndex: 123, @@ -362,8 +361,8 @@ describe('PaymentService', () => { logIndex: 123, }, ], - transactionHash: MOCK_TRANSACTION_HASH, - confirmations: TX_CONFIRMATION_TRESHOLD, + hash: MOCK_TRANSACTION_HASH, + confirmations: jest.fn().mockResolvedValue(TX_CONFIRMATION_TRESHOLD), }; jsonRpcProviderMock.getTransactionReceipt.mockResolvedValue( @@ -407,11 +406,11 @@ describe('PaymentService', () => { const token = 'hmt'; - const transactionReceipt: Partial = { + const transactionReceipt = { from: MOCK_ADDRESS, logs: [ { - data: ethers.utils.parseUnits('10').toString(), + data: ethers.parseUnits('10').toString(), blockNumber: 123, blockHash: '123', transactionIndex: 123, @@ -423,7 +422,7 @@ describe('PaymentService', () => { }, ], transactionHash: MOCK_TRANSACTION_HASH, - confirmations: TX_CONFIRMATION_TRESHOLD, + confirmations: jest.fn().mockResolvedValue(TX_CONFIRMATION_TRESHOLD), }; jsonRpcProviderMock.getTransactionReceipt.mockResolvedValue( @@ -446,11 +445,11 @@ describe('PaymentService', () => { const unsupportedToken = 'doge'; - const transactionReceipt: Partial = { + const transactionReceipt = { from: MOCK_ADDRESS, logs: [ { - data: ethers.utils.parseUnits('10').toString(), + data: ethers.parseUnits('10').toString(), blockNumber: 123, blockHash: '123', transactionIndex: 123, @@ -462,7 +461,7 @@ describe('PaymentService', () => { }, ], transactionHash: MOCK_TRANSACTION_HASH, - confirmations: TX_CONFIRMATION_TRESHOLD, + confirmations: jest.fn().mockResolvedValue(TX_CONFIRMATION_TRESHOLD), }; jsonRpcProviderMock.getTransactionReceipt.mockResolvedValue( @@ -486,10 +485,10 @@ describe('PaymentService', () => { throw new ConflictException(ErrorSignature.SignatureNotVerified); }); - const transactionReceipt: Partial = { + const transactionReceipt = { from: MOCK_ADDRESS, logs: [], - confirmations: TX_CONFIRMATION_TRESHOLD, + confirmations: jest.fn().mockResolvedValue(TX_CONFIRMATION_TRESHOLD), }; jsonRpcProviderMock.getTransactionReceipt.mockResolvedValue( @@ -522,10 +521,10 @@ describe('PaymentService', () => { transactionHash: MOCK_TRANSACTION_HASH, }; - const transactionReceipt: Partial = { + const transactionReceipt = { from: MOCK_ADDRESS, logs: [], - confirmations: TX_CONFIRMATION_TRESHOLD, + confirmations: jest.fn().mockResolvedValue(TX_CONFIRMATION_TRESHOLD), }; jsonRpcProviderMock.getTransactionReceipt.mockResolvedValue( @@ -544,11 +543,11 @@ describe('PaymentService', () => { transactionHash: MOCK_TRANSACTION_HASH, }; - const transactionReceipt: Partial = { + const transactionReceipt = { from: MOCK_ADDRESS, logs: [ { - data: ethers.utils.parseUnits('10').toString(), + data: ethers.parseUnits('10').toString(), blockNumber: 123, blockHash: '123', transactionIndex: 123, @@ -560,7 +559,9 @@ describe('PaymentService', () => { }, ], transactionHash: MOCK_TRANSACTION_HASH, - confirmations: TX_CONFIRMATION_TRESHOLD - 1, + confirmations: jest + .fn() + .mockResolvedValue(TX_CONFIRMATION_TRESHOLD - 1), }; jsonRpcProviderMock.getTransactionReceipt.mockResolvedValue( @@ -583,11 +584,11 @@ describe('PaymentService', () => { const token = 'hmt'; - const transactionReceipt: Partial = { + const transactionReceipt = { from: MOCK_ADDRESS, logs: [ { - data: ethers.utils.parseUnits('10').toString(), + data: ethers.parseUnits('10').toString(), blockNumber: 123, blockHash: '123', transactionIndex: 123, @@ -599,7 +600,7 @@ describe('PaymentService', () => { }, ], transactionHash: MOCK_TRANSACTION_HASH, - confirmations: TX_CONFIRMATION_TRESHOLD, + confirmations: jest.fn().mockResolvedValue(TX_CONFIRMATION_TRESHOLD), }; jsonRpcProviderMock.getTransactionReceipt.mockResolvedValue( @@ -683,7 +684,7 @@ describe('PaymentService', () => { }); it('should throw IncorrectAmount error when overflow occurs', async () => { - const mockError = new QueryFailedError('', [], ''); + const mockError = new QueryFailedError('', [], new Error('')); mockError.message = ErrorPostgres.NumericFieldOverflow.toLowerCase(); jest.spyOn(paymentRepository, 'create').mockRejectedValueOnce(mockError); diff --git a/packages/apps/job-launcher/server/src/modules/payment/payment.service.ts b/packages/apps/job-launcher/server/src/modules/payment/payment.service.ts index 3e409de96d..0e68e0c34b 100644 --- a/packages/apps/job-launcher/server/src/modules/payment/payment.service.ts +++ b/packages/apps/job-launcher/server/src/modules/payment/payment.service.ts @@ -7,7 +7,7 @@ import { } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import Stripe from 'stripe'; -import { ethers, providers } from 'ethers'; +import { ethers } from 'ethers'; import { ErrorPayment, ErrorPostgres } from '../../common/constants/errors'; import { PaymentRepository } from './payment.repository'; import { @@ -174,7 +174,7 @@ export class PaymentService { const network = Object.values(networkMap).find( (item) => item.chainId === dto.chainId, ); - const provider = new providers.JsonRpcProvider(network?.rpcUrl); + const provider = new ethers.JsonRpcProvider(network?.rpcUrl); const transaction = await provider.getTransactionReceipt( dto.transactionHash, @@ -192,7 +192,7 @@ export class PaymentService { throw new NotFoundException(ErrorPayment.InvalidTransactionData); } - if (transaction.confirmations < TX_CONFIRMATION_TRESHOLD) { + if ((await transaction.confirmations()) < TX_CONFIRMATION_TRESHOLD) { this.logger.error( `Transaction has ${transaction.confirmations} confirmations instead of ${TX_CONFIRMATION_TRESHOLD}`, ); @@ -210,16 +210,19 @@ export class PaymentService { ); if ( - ethers.utils.hexValue( - tokenContract.interface.parseLog(transaction.logs[0]).args['_to'], - ) !== ethers.utils.hexValue(signer.address) + ethers.hexlify( + tokenContract.interface.parseLog({ + topics: transaction.logs[0].topics as string[], + data: transaction.logs[0].data, + })?.args['_to'], + ) !== ethers.hexlify(signer.address) ) { this.logger.error(ErrorPayment.InvalidRecipient); throw new ConflictException(ErrorPayment.InvalidRecipient); } const tokenId = (await tokenContract.symbol()).toLowerCase(); - const amount = Number(ethers.utils.formatEther(transaction.logs[0].data)); + const amount = Number(ethers.formatEther(transaction.logs[0].data)); if ( network?.tokens[tokenId] != tokenAddress || @@ -230,7 +233,7 @@ export class PaymentService { } const paymentEntity = await this.paymentRepository.findOne({ - transaction: transaction.transactionHash, + transaction: transaction.hash, chainId: dto.chainId, }); diff --git a/packages/apps/job-launcher/server/src/modules/web3/web3.service.ts b/packages/apps/job-launcher/server/src/modules/web3/web3.service.ts index cb407d50e2..336445142a 100644 --- a/packages/apps/job-launcher/server/src/modules/web3/web3.service.ts +++ b/packages/apps/job-launcher/server/src/modules/web3/web3.service.ts @@ -1,6 +1,6 @@ import { BadRequestException, Injectable, Logger } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; -import { BigNumberish, Wallet, providers } from 'ethers'; +import { Wallet, ethers } from 'ethers'; import { ConfigNames, networks } from '../../common/config'; import { Web3Env } from '../../common/enums/web3'; import { MAINNET_CHAIN_IDS, TESTNET_CHAIN_IDS } from '../../common/constants'; @@ -20,7 +20,7 @@ export class Web3Service { validChains.includes(network.chainId), ); for (const network of validNetworks) { - const provider = new providers.JsonRpcProvider(network.rpcUrl); + const provider = new ethers.JsonRpcProvider(network.rpcUrl); this.signers[network.chainId] = new Wallet(privateKey, provider); } this.signerAddress = this.signers[validChains[0]].address; @@ -55,15 +55,18 @@ export class Web3Service { return validChainIds; } - public async calculateGasPrice(chainId: number): Promise { + public async calculateGasPrice(chainId: number): Promise { const signer = this.getSigner(chainId); const multiplier = this.configService.get( ConfigNames.GAS_PRICE_MULTIPLIER, ); - if (multiplier) { - return (await signer.provider.getGasPrice()).mul(multiplier); + if (multiplier && signer.provider) { + return ( + ((await signer.provider.getFeeData())?.gasPrice || 1n) * + BigInt(multiplier) + ); } - return 1; + return 1n; } } diff --git a/packages/apps/job-launcher/server/tsconfig.json b/packages/apps/job-launcher/server/tsconfig.json index 7252f8ae99..fb6e941176 100644 --- a/packages/apps/job-launcher/server/tsconfig.json +++ b/packages/apps/job-launcher/server/tsconfig.json @@ -7,7 +7,7 @@ "experimentalDecorators": true, "allowSyntheticDefaultImports": true, "allowJs": true, - "target": "es2017", + "target": "ES2020", "sourceMap": true, "outDir": "./dist", "baseUrl": "./", @@ -18,6 +18,6 @@ "strictBindCallApply": true, "forceConsistentCasingInFileNames": true, "noFallthroughCasesInSwitch": true, - "esModuleInterop": true, + "esModuleInterop": true } } diff --git a/packages/apps/reputation-oracle/server/package.json b/packages/apps/reputation-oracle/server/package.json index c0fdb95e85..48fb714bcb 100644 --- a/packages/apps/reputation-oracle/server/package.json +++ b/packages/apps/reputation-oracle/server/package.json @@ -46,7 +46,6 @@ "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "cookie-parser": "^1.4.6", - "ethers": "^5.7.2", "express-session": "^1.17.3", "helmet": "^7.1.0", "joi": "^17.9.2", diff --git a/packages/apps/reputation-oracle/server/src/common/utils/signature.ts b/packages/apps/reputation-oracle/server/src/common/utils/signature.ts index b7f0aab223..57aa4bdc66 100644 --- a/packages/apps/reputation-oracle/server/src/common/utils/signature.ts +++ b/packages/apps/reputation-oracle/server/src/common/utils/signature.ts @@ -41,12 +41,12 @@ export function recoverSigner( } try { - return ethers.utils.verifyMessage(message, signature); + return ethers.verifyMessage(message, signature); } catch (e) { throw new ConflictException(ErrorSignature.InvalidSignature); } } export function getNonce(): string { - return Buffer.from(ethers.utils.randomBytes(16)).toString('hex'); + return Buffer.from(ethers.randomBytes(16)).toString('hex'); } diff --git a/packages/apps/reputation-oracle/server/src/modules/web3/web3.service.spec.ts b/packages/apps/reputation-oracle/server/src/modules/web3/web3.service.spec.ts index 2a5e5011ce..bb8aaa99bf 100644 --- a/packages/apps/reputation-oracle/server/src/modules/web3/web3.service.spec.ts +++ b/packages/apps/reputation-oracle/server/src/modules/web3/web3.service.spec.ts @@ -34,7 +34,6 @@ describe('Web3Service', () => { const signer = web3Service.getSigner(network.chainId); expect(signer).toBeDefined(); - expect((signer.provider as any).connection.url).toBe(network.rpcUrl); } }); diff --git a/packages/apps/reputation-oracle/server/src/modules/web3/web3.service.ts b/packages/apps/reputation-oracle/server/src/modules/web3/web3.service.ts index 45f130c210..b024cc565b 100644 --- a/packages/apps/reputation-oracle/server/src/modules/web3/web3.service.ts +++ b/packages/apps/reputation-oracle/server/src/modules/web3/web3.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; -import { BigNumberish, Wallet, providers } from 'ethers'; +import { Wallet, ethers } from 'ethers'; import { ConfigNames, networkMap } from '../../common/config'; @Injectable() @@ -11,7 +11,7 @@ export class Web3Service { const privateKey = this.configService.get(ConfigNames.WEB3_PRIVATE_KEY); for (const networkKey of Object.keys(networkMap)) { const network = networkMap[networkKey]; - const provider = new providers.JsonRpcProvider(network.rpcUrl); + const provider = new ethers.JsonRpcProvider(network.rpcUrl); this.signers[network.chainId] = new Wallet(privateKey, provider); } } @@ -20,15 +20,18 @@ export class Web3Service { return this.signers[chainId]; } - public async calculateGasPrice(chainId: number): Promise { + public async calculateGasPrice(chainId: number): Promise { const signer = this.getSigner(chainId); const multiplier = this.configService.get( ConfigNames.GAS_PRICE_MULTIPLIER, ); if (multiplier) { - return (await signer.provider.getGasPrice()).mul(multiplier); + return ( + ((await signer.provider?.getFeeData())?.gasPrice || 1n) * + BigInt(multiplier) + ); } - return 1; + return 1n; } } diff --git a/packages/apps/reputation-oracle/server/src/modules/webhook/webhook.dto.ts b/packages/apps/reputation-oracle/server/src/modules/webhook/webhook.dto.ts index 199d2330ff..abe35d6bb9 100644 --- a/packages/apps/reputation-oracle/server/src/modules/webhook/webhook.dto.ts +++ b/packages/apps/reputation-oracle/server/src/modules/webhook/webhook.dto.ts @@ -13,7 +13,6 @@ import { import { EventType, SolutionError, WebhookStatus } from '../../common/enums'; import { ChainId } from '@human-protocol/sdk'; import { JobRequestType } from '../../common/enums'; -import { BigNumber } from 'ethers'; export class WebhookIncomingDto { @ApiProperty() @@ -162,7 +161,7 @@ export class ProcessingResultDto { /** * Corresponding amounts to be paid out to recipients. */ - amounts: BigNumber[]; + amounts: bigint[]; /** * URL to the stored results. diff --git a/packages/apps/reputation-oracle/server/src/modules/webhook/webhook.service.spec.ts b/packages/apps/reputation-oracle/server/src/modules/webhook/webhook.service.spec.ts index b8e53e8c4b..69470c2f85 100644 --- a/packages/apps/reputation-oracle/server/src/modules/webhook/webhook.service.spec.ts +++ b/packages/apps/reputation-oracle/server/src/modules/webhook/webhook.service.spec.ts @@ -14,7 +14,6 @@ import { } from './webhook.dto'; import { ChainId, EscrowClient, StorageClient } from '@human-protocol/sdk'; import { WebhookIncomingEntity } from './webhook-incoming.entity'; -import { BigNumber } from 'ethers'; import { ReputationRepository } from '../reputation/reputation.repository'; import { ErrorResults, ErrorWebhook } from '../../common/constants/errors'; import { @@ -54,7 +53,7 @@ jest.mock('@human-protocol/sdk', () => ({ getManifestUrl: jest.fn().mockResolvedValue(MOCK_FILE_URL), getResultsUrl: jest.fn().mockResolvedValue(MOCK_FILE_URL), bulkPayOut: jest.fn().mockResolvedValue(true), - getBalance: jest.fn().mockResolvedValue(BigNumber.from(10)), + getBalance: jest.fn().mockResolvedValue(10n), })), }, StorageClient: jest.fn().mockImplementation(() => ({ @@ -148,9 +147,7 @@ describe('WebhookService', () => { reputationService = moduleRef.get(ReputationService); storageService = moduleRef.get(StorageService); web3Service = moduleRef.get(Web3Service); - web3Service.calculateGasPrice = jest - .fn() - .mockReturnValue(BigNumber.from(1000)); + web3Service.calculateGasPrice = jest.fn().mockReturnValue(1000n); }); afterEach(() => { @@ -247,7 +244,7 @@ describe('WebhookService', () => { const results: ProcessingResultDto = { recipients: [MOCK_ADDRESS], - amounts: [BigNumber.from(10)], + amounts: [10n], url: MOCK_FILE_URL, hash: MOCK_FILE_HASH, checkPassed: true, @@ -314,7 +311,7 @@ describe('WebhookService', () => { getManifestUrl: jest.fn().mockResolvedValue(MOCK_FILE_URL), getResultsUrl: jest.fn().mockResolvedValue(MOCK_FILE_URL), bulkPayOut: jest.fn().mockResolvedValue(true), - getBalance: jest.fn().mockResolvedValue(BigNumber.from(0)), + getBalance: jest.fn().mockResolvedValue(0n), })); expect(await webhookService.processPendingCronJob()).toBeUndefined(); diff --git a/packages/apps/reputation-oracle/server/src/modules/webhook/webhook.service.ts b/packages/apps/reputation-oracle/server/src/modules/webhook/webhook.service.ts index c58f1c0302..3073243b1e 100644 --- a/packages/apps/reputation-oracle/server/src/modules/webhook/webhook.service.ts +++ b/packages/apps/reputation-oracle/server/src/modules/webhook/webhook.service.ts @@ -30,7 +30,7 @@ import { RETRIES_COUNT_THRESHOLD, } from '../../common/constants'; import { ReputationService } from '../reputation/reputation.service'; -import { BigNumber, ethers } from 'ethers'; +import { ethers } from 'ethers'; import { Web3Service } from '../web3/web3.service'; import { EventType, @@ -133,7 +133,7 @@ export class WebhookService { let results: { recipients: string[]; - amounts: BigNumber[]; + amounts: bigint[]; url: string; hash: string; checkPassed: boolean; @@ -228,9 +228,9 @@ export class WebhookService { const recipients = intermediateResults .filter((result) => !result.error) .map((item) => item.workerAddress); - const payoutAmount = BigNumber.from( - ethers.utils.parseUnits(manifest.fundAmount.toString(), 'ether'), - ).div(recipients.length); + const payoutAmount = + BigInt(ethers.parseUnits(manifest.fundAmount.toString(), 'ether')) / + BigInt(recipients.length); const amounts = new Array(recipients.length).fill(payoutAmount); return { recipients, amounts, url, hash, checkPassed: true }; // Assuming checkPassed is true for this case @@ -259,15 +259,11 @@ export class WebhookService { `${intermediateResultsUrl}/${CVAT_VALIDATION_META_FILENAME}`, ); - const bountyValue = ethers.utils.parseUnits(manifest.job_bounty, 18); + const bountyValue = ethers.parseUnits(manifest.job_bounty, 18); const accumulatedBounties = annotations.results.reduce((accMap, curr) => { if (curr.annotation_quality >= manifest.validation.min_quality) { - const existingValue = - accMap.get(curr.annotator_wallet_address) || BigNumber.from(0); - accMap.set( - curr.annotator_wallet_address, - existingValue.add(bountyValue), - ); + const existingValue = accMap.get(curr.annotator_wallet_address) || 0n; + accMap.set(curr.annotator_wallet_address, existingValue + bountyValue); } return accMap; }, new Map()); diff --git a/packages/apps/reputation-oracle/server/tsconfig.json b/packages/apps/reputation-oracle/server/tsconfig.json index 5393cfe8d6..fb6e941176 100644 --- a/packages/apps/reputation-oracle/server/tsconfig.json +++ b/packages/apps/reputation-oracle/server/tsconfig.json @@ -7,7 +7,7 @@ "experimentalDecorators": true, "allowSyntheticDefaultImports": true, "allowJs": true, - "target": "es2017", + "target": "ES2020", "sourceMap": true, "outDir": "./dist", "baseUrl": "./", diff --git a/packages/core/hardhat.config.ts b/packages/core/hardhat.config.ts index 6155f44a52..188446ad65 100644 --- a/packages/core/hardhat.config.ts +++ b/packages/core/hardhat.config.ts @@ -1,9 +1,7 @@ import * as dotenv from 'dotenv'; import { HardhatUserConfig, task } from 'hardhat/config'; -import '@nomiclabs/hardhat-ethers'; import '@nomicfoundation/hardhat-chai-matchers'; -import '@nomiclabs/hardhat-etherscan'; import '@typechain/hardhat'; import 'xdeployer'; import 'hardhat-gas-reporter'; @@ -13,7 +11,6 @@ import 'hardhat-abi-exporter'; import '@nomicfoundation/hardhat-toolbox'; import '@openzeppelin/hardhat-upgrades'; import 'hardhat-dependency-compiler'; -import 'hardhat-celo'; dotenv.config(); diff --git a/packages/core/package.json b/packages/core/package.json index da5532d8a2..62a6d016be 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -45,26 +45,22 @@ ], "license": "MIT", "devDependencies": { - "@ethersproject/abi": "^5.7.0", - "@ethersproject/providers": "^5.7.2", - "@nomicfoundation/hardhat-chai-matchers": "^1.0.5", - "@nomicfoundation/hardhat-network-helpers": "^1.0.7", - "@nomicfoundation/hardhat-toolbox": "^2.0.1", - "@nomiclabs/hardhat-ethers": "^2.2.2", - "@nomiclabs/hardhat-etherscan": "^3.1.2", + "@nomicfoundation/hardhat-chai-matchers": "^2.0.3", + "@nomicfoundation/hardhat-ethers": "^3.0.5", + "@nomicfoundation/hardhat-network-helpers": "^1.0.10", + "@nomicfoundation/hardhat-toolbox": "^4.0.0", + "@nomicfoundation/hardhat-verify": "^2.0.3", "@openzeppelin/contracts": "^4.9.2", "@openzeppelin/contracts-upgradeable": "^4.9.2", - "@openzeppelin/hardhat-upgrades": "^1.22.0", + "@openzeppelin/hardhat-upgrades": "^3.0.1", "@tenderly/hardhat-tenderly": "^2.0.1", - "@typechain/ethers-v5": "^10.1.1", - "@typechain/hardhat": "^6.1.4", + "@typechain/ethers-v6": "^0.5.1", + "@typechain/hardhat": "^9.1.0", "@types/chai": "^4.3.3", "@types/mocha": "^10.0.2", "chai": "^4.4.0", - "ethers": "^5.0.0", "hardhat": "^2.18.3", "hardhat-abi-exporter": "^2.10.1", - "hardhat-celo": "^0.0.7", "hardhat-contract-sizer": "^2.6.1", "hardhat-dependency-compiler": "^1.1.3", "hardhat-gas-reporter": "^1.0.9", @@ -72,8 +68,8 @@ "prettier-plugin-solidity": "^1.2.0", "solidity-coverage": "^0.8.2", "tenderly": "^0.7.0", - "typechain": "^8.1.1", - "xdeployer": "1.2.7" + "typechain": "^8.3.2", + "xdeployer": "3.0.0" }, "lint-staged": { "*.sol": [ diff --git a/packages/core/scripts/deploy-proxies.ts b/packages/core/scripts/deploy-proxies.ts index a183beec25..9c9c6606ef 100644 --- a/packages/core/scripts/deploy-proxies.ts +++ b/packages/core/scripts/deploy-proxies.ts @@ -15,10 +15,12 @@ async function main() { { initializer: 'initialize', kind: 'uups' } ); await stakingContract.deployed(); - console.log('Staking Proxy Address: ', stakingContract.address); + console.log('Staking Proxy Address: ', await stakingContract.getAddress()); console.log( 'Staking Implementation Address: ', - await upgrades.erc1967.getImplementationAddress(stakingContract.address) + await upgrades.erc1967.getImplementationAddress( + await stakingContract.getAddress() + ) ); const EscrowFactory = await ethers.getContractFactory( @@ -26,39 +28,46 @@ async function main() { ); const escrowFactoryContract = await upgrades.deployProxy( EscrowFactory, - [stakingContract.address], + [await stakingContract.getAddress()], { initializer: 'initialize', kind: 'uups' } ); await escrowFactoryContract.deployed(); - console.log('Escrow Factory Proxy Address: ', escrowFactoryContract.address); + console.log( + 'Escrow Factory Proxy Address: ', + await escrowFactoryContract.getAddress() + ); console.log( 'Escrow Factory Implementation Address: ', await upgrades.erc1967.getImplementationAddress( - escrowFactoryContract.address + await escrowFactoryContract.getAddress() ) ); const KVStore = await ethers.getContractFactory('KVStore'); const kvStoreContract = await KVStore.deploy(); - await kvStoreContract.deployed(); - console.log('KVStore Address: ', kvStoreContract.address); + console.log('KVStore Address: ', await kvStoreContract.getAddress()); const RewardPool = await ethers.getContractFactory('RewardPool'); const rewardPoolContract = await upgrades.deployProxy( RewardPool, - [hmtAddress, stakingContract.address, 1], + [hmtAddress, await stakingContract.getAddress(), 1], { initializer: 'initialize', kind: 'uups' } ); await rewardPoolContract.deployed(); - console.log('Reward Pool Proxy Address: ', rewardPoolContract.address); + console.log( + 'Reward Pool Proxy Address: ', + await rewardPoolContract.getAddress() + ); console.log( 'Reward Pool Implementation Address: ', - await upgrades.erc1967.getImplementationAddress(rewardPoolContract.address) + await upgrades.erc1967.getImplementationAddress( + await rewardPoolContract.getAddress() + ) ); // Configure RewardPool in Staking - await stakingContract.setRewardPool(rewardPoolContract.address); + await stakingContract.setRewardPool(await rewardPoolContract.getAddress()); } main().catch((error) => { diff --git a/packages/core/scripts/deploy.ts b/packages/core/scripts/deploy.ts index cfe335cd60..a4f788845c 100644 --- a/packages/core/scripts/deploy.ts +++ b/packages/core/scripts/deploy.ts @@ -1,5 +1,6 @@ /* eslint-disable no-console */ import { ethers, upgrades } from 'hardhat'; +import { HMToken } from 'typechain-types'; async function main() { const [, ...accounts] = await ethers.getSigners(); @@ -12,20 +13,23 @@ async function main() { 18, 'HMT' ); - await HMTokenContract.deployed(); - console.log('HMToken Address: ', HMTokenContract.address); + await HMTokenContract.waitForDeployment(); + console.log('HMToken Address: ', await HMTokenContract.getAddress()); const Staking = await ethers.getContractFactory('Staking'); const stakingContract = await upgrades.deployProxy( Staking, - [HMTokenContract.address, 1, 10], + [await HMTokenContract.getAddress(), 1, 10], { initializer: 'initialize', kind: 'uups' } ); - await stakingContract.deployed(); - console.log('Staking Proxy Address: ', stakingContract.address); + + await stakingContract.waitForDeployment(); + console.log('Staking Proxy Address: ', await stakingContract.getAddress()); console.log( 'Staking Implementation Address: ', - await upgrades.erc1967.getImplementationAddress(stakingContract.address) + await upgrades.erc1967.getImplementationAddress( + await stakingContract.getAddress() + ) ); const EscrowFactory = await ethers.getContractFactory( @@ -33,44 +37,52 @@ async function main() { ); const escrowFactoryContract = await upgrades.deployProxy( EscrowFactory, - [stakingContract.address], + [await stakingContract.getAddress()], { initializer: 'initialize', kind: 'uups' } ); - await escrowFactoryContract.deployed(); - console.log('Escrow Factory Proxy Address: ', escrowFactoryContract.address); + await escrowFactoryContract.waitForDeployment(); + console.log( + 'Escrow Factory Proxy Address: ', + await escrowFactoryContract.getAddress() + ); console.log( 'Escrow Factory Implementation Address: ', await upgrades.erc1967.getImplementationAddress( - escrowFactoryContract.address + await escrowFactoryContract.getAddress() ) ); const KVStore = await ethers.getContractFactory('KVStore'); const kvStoreContract = await KVStore.deploy(); - await kvStoreContract.deployed(); + await kvStoreContract.waitForDeployment(); - console.log('KVStore Address: ', kvStoreContract.address); + console.log('KVStore Address: ', await kvStoreContract.getAddress()); const RewardPool = await ethers.getContractFactory('RewardPool'); const rewardPoolContract = await upgrades.deployProxy( RewardPool, - [HMTokenContract.address, stakingContract.address, 1], + [await HMTokenContract.getAddress(), await stakingContract.getAddress(), 1], { initializer: 'initialize', kind: 'uups' } ); - await rewardPoolContract.deployed(); - console.log('Reward Pool Proxy Address: ', rewardPoolContract.address); + await rewardPoolContract.waitForDeployment(); + console.log( + 'Reward Pool Proxy Address: ', + await rewardPoolContract.getAddress() + ); console.log( 'Reward Pool Implementation Address: ', - await upgrades.erc1967.getImplementationAddress(rewardPoolContract.address) + await upgrades.erc1967.getImplementationAddress( + await rewardPoolContract.getAddress() + ) ); // Configure RewardPool in Staking - await stakingContract.setRewardPool(rewardPoolContract.address); + await stakingContract.setRewardPool(await rewardPoolContract.getAddress()); for (const account of accounts) { - await HMTokenContract.transfer( + await (HMTokenContract as HMToken).transfer( account.address, - ethers.utils.parseEther('1000') + ethers.parseEther('1000') ); } } diff --git a/packages/core/scripts/upgrade-proxies.ts b/packages/core/scripts/upgrade-proxies.ts index bd8d80ee2e..19f14bf2ad 100644 --- a/packages/core/scripts/upgrade-proxies.ts +++ b/packages/core/scripts/upgrade-proxies.ts @@ -30,12 +30,12 @@ async function main() { console.log( 'Escrow Factory Proxy Address: ', - escrowFactoryContract.address + await escrowFactoryContract.getAddress() ); console.log( 'New Escrow Factory Implementation Address: ', await upgrades.erc1967.getImplementationAddress( - escrowFactoryContract.address + await escrowFactoryContract.getAddress() ) ); } @@ -52,10 +52,12 @@ async function main() { contract.deployTransaction.hash ); - console.log('Staking Proxy Address: ', stakingContract.address); + console.log('Staking Proxy Address: ', await stakingContract.getAddress()); console.log( 'New Staking Implementation Address: ', - await upgrades.erc1967.getImplementationAddress(stakingContract.address) + await upgrades.erc1967.getImplementationAddress( + await stakingContract.getAddress() + ) ); } @@ -71,11 +73,14 @@ async function main() { contract.deployTransaction.hash ); - console.log('Reward Pool Proxy Address: ', rewardPoolContract.address); + console.log( + 'Reward Pool Proxy Address: ', + await rewardPoolContract.getAddress() + ); console.log( 'New Reward Pool Implementation Address: ', await upgrades.erc1967.getImplementationAddress( - rewardPoolContract.address + await rewardPoolContract.getAddress() ) ); } diff --git a/packages/core/test/Escrow.ts b/packages/core/test/Escrow.ts index 99329e355e..4f8de1fbb2 100644 --- a/packages/core/test/Escrow.ts +++ b/packages/core/test/Escrow.ts @@ -1,7 +1,8 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import { anyValue } from '@nomicfoundation/hardhat-chai-matchers/withArgs'; import { expect } from 'chai'; import { ethers } from 'hardhat'; -import { Signer } from 'ethers'; +import { EventLog, Signer } from 'ethers'; import { Escrow, HMToken } from '../typechain-types'; const MOCK_URL = 'http://google.com/fake'; @@ -32,7 +33,7 @@ async function deployEscrow() { // Deploy Escrow Contract const Escrow = await ethers.getContractFactory('contracts/Escrow.sol:Escrow'); escrow = (await Escrow.deploy( - token.address, + await token.getAddress(), await launcher.getAddress(), await owner.getAddress(), 100, @@ -59,7 +60,7 @@ async function setupEscrow() { async function fundEscrow() { const amount = 100; - await token.connect(owner).transfer(escrow.address, amount); + await token.connect(owner).transfer(escrow.getAddress(), amount); } describe('Escrow', function () { @@ -95,7 +96,7 @@ describe('Escrow', function () { it('Should set the right token address', async () => { const result = await escrow.token(); - expect(result).to.equal(token.address); + expect(result).to.equal(await token.getAddress()); }); it('Should set the right launched status', async () => { @@ -120,7 +121,7 @@ describe('Escrow', function () { it('Should topup and return the right escrow balance', async () => { const amount = 1000; - await token.connect(owner).transfer(escrow.address, amount); + await token.connect(owner).transfer(escrow.getAddress(), amount); const result = await escrow.connect(launcher).getBalance(); expect(result).to.equal(amount.toString()); @@ -161,23 +162,23 @@ describe('Escrow', function () { it('Should transfer tokens to owner if contract funded when abort is called', async function () { const amount = 100; - await token.connect(owner).transfer(escrow.address, amount); + await token.connect(owner).transfer(escrow.getAddress(), amount); await escrow.connect(owner).abort(); expect( - (await token.connect(owner).balanceOf(escrow.address)).toString() + (await token.connect(owner).balanceOf(escrow.getAddress())).toString() ).to.equal('0', 'Escrow has not been properly aborted'); }); it('Should transfer tokens to owner if contract funded when abort is called from trusted handler', async function () { const amount = 100; - await token.connect(owner).transfer(escrow.address, amount); + await token.connect(owner).transfer(escrow.getAddress(), amount); await escrow.connect(trustedHandlers[0]).abort(); expect( - (await token.connect(owner).balanceOf(escrow.address)).toString() + (await token.connect(owner).balanceOf(escrow.getAddress())).toString() ).to.equal('0', 'Escrow has not been properly aborted'); }); }); @@ -227,10 +228,8 @@ describe('Escrow', function () { .storeResults(MOCK_URL, MOCK_HASH) ).wait(); - expect(result.events?.[0].event).to.equal( - 'IntermediateStorage', - 'IntermediateStorage event was not emitted' - ); + expect((result?.logs[0] as EventLog).args).to.contain(MOCK_URL); + expect((result?.logs[0] as EventLog).args).to.contain(MOCK_HASH); }); it('Should succeed when add a new trusted handler from trusted handler and a trusted handler stores results', async () => { @@ -244,10 +243,8 @@ describe('Escrow', function () { .storeResults(MOCK_URL, MOCK_HASH) ).wait(); - expect(result.events?.[0].event).to.equal( - 'IntermediateStorage', - 'IntermediateStorage event was not emitted' - ); + expect((result?.logs[0] as EventLog).args).to.contain(MOCK_URL); + expect((result?.logs[0] as EventLog).args).to.contain(MOCK_HASH); }); }); }); @@ -307,10 +304,8 @@ describe('Escrow', function () { .storeResults(MOCK_URL, MOCK_HASH) ).wait(); - expect(result.events?.[0].event).to.equal( - 'IntermediateStorage', - 'IntermediateStorage event was not emitted' - ); + expect((result?.logs[0] as EventLog).args).to.contain(MOCK_URL); + expect((result?.logs[0] as EventLog).args).to.contain(MOCK_HASH); }); it('Should succeed when a trusted handler stores results', async () => { @@ -320,10 +315,8 @@ describe('Escrow', function () { .storeResults(MOCK_URL, MOCK_HASH) ).wait(); - expect(result.events?.[0].event).to.equal( - 'IntermediateStorage', - 'IntermediateStorage event was not emitted' - ); + expect((result?.logs[0] as EventLog).args).to.contain(MOCK_URL); + expect((result?.logs[0] as EventLog).args).to.contain(MOCK_HASH); }); }); }); @@ -356,7 +349,7 @@ describe('Escrow', function () { escrow .connect(owner) .setup( - ethers.constants.AddressZero, + ethers.ZeroAddress, await recordingOracle.getAddress(), await exchangeOracle.getAddress(), 10, @@ -374,7 +367,7 @@ describe('Escrow', function () { .connect(owner) .setup( await reputationOracle.getAddress(), - ethers.constants.AddressZero, + ethers.ZeroAddress, await exchangeOracle.getAddress(), 10, 10, @@ -392,7 +385,7 @@ describe('Escrow', function () { .setup( await reputationOracle.getAddress(), await reputationOracle.getAddress(), - ethers.constants.AddressZero, + ethers.ZeroAddress, 10, 10, 10, @@ -558,10 +551,9 @@ describe('Escrow', function () { const ststus = await escrow.status(); expect(ststus).to.equal(Status.Cancelled); - expect(await token.connect(owner).balanceOf(escrow.address)).to.equal( - '0', - 'Escrow has not been properly canceled' - ); + expect( + await token.connect(owner).balanceOf(escrow.getAddress()) + ).to.equal('0', 'Escrow has not been properly canceled'); }); it('Should succeed when the contract was canceled by trusted handler', async () => { @@ -569,10 +561,9 @@ describe('Escrow', function () { const ststus = await escrow.status(); expect(ststus).to.equal(Status.Cancelled); - expect(await token.connect(owner).balanceOf(escrow.address)).to.equal( - '0', - 'Escrow has not been properly canceled' - ); + expect( + await token.connect(owner).balanceOf(escrow.getAddress()) + ).to.equal('0', 'Escrow has not been properly canceled'); }); }); }); @@ -639,7 +630,7 @@ describe('Escrow', function () { it('Should revert with the right error if too many recipients', async function () { const recepients = Array.from( new Array(BULK_MAX_COUNT + 1), - () => ethers.constants.AddressZero + () => ethers.ZeroAddress ); const amounts = Array.from({ length: BULK_MAX_COUNT + 1 }, () => 1); @@ -730,38 +721,27 @@ describe('Escrow', function () { .balanceOf(await exchangeOracle.getAddress()); expect( - ( - finalBalanceAccount1.toNumber() - initialBalanceAccount1.toNumber() - ).toString() + (finalBalanceAccount1 - initialBalanceAccount1).toString() ).to.equal('7'); expect( - ( - finalBalanceAccount2.toNumber() - initialBalanceAccount2.toNumber() - ).toString() + (finalBalanceAccount2 - initialBalanceAccount2).toString() ).to.equal('14'); expect( - ( - finalBalanceAccount3.toNumber() - initialBalanceAccount3.toNumber() - ).toString() + (finalBalanceAccount3 - initialBalanceAccount3).toString() ).to.equal('21'); expect( ( - finalBalanceRecordingOracle.toNumber() - - initialBalanceRecordingOracle.toNumber() + finalBalanceRecordingOracle - initialBalanceRecordingOracle ).toString() ).to.equal('6'); expect( ( - finalBalanceReputationOracle.toNumber() - - initialBalanceReputationOracle.toNumber() + finalBalanceReputationOracle - initialBalanceReputationOracle ).toString() ).to.equal('6'); expect( - ( - finalBalanceExchangeOracle.toNumber() - - initialBalanceExchangeOracle.toNumber() - ).toString() + (finalBalanceExchangeOracle - initialBalanceExchangeOracle).toString() ).to.equal('6'); }); diff --git a/packages/core/test/EscrowFactory.ts b/packages/core/test/EscrowFactory.ts index bb6fd5b758..cae53b2637 100644 --- a/packages/core/test/EscrowFactory.ts +++ b/packages/core/test/EscrowFactory.ts @@ -1,6 +1,6 @@ import { anyValue } from '@nomicfoundation/hardhat-chai-matchers/withArgs'; import { assert, expect } from 'chai'; -import { Signer } from 'ethers'; +import { EventLog, Signer } from 'ethers'; import { ethers, upgrades } from 'hardhat'; import { EscrowFactory, HMToken, Staking } from '../typechain-types'; @@ -23,10 +23,12 @@ describe('EscrowFactory', function () { const result = await ( await escrowFactory .connect(operator) - .createEscrow(token.address, trustedHandlers, jobRequesterId) + .createEscrow(await token.getAddress(), trustedHandlers, jobRequesterId) ).wait(); - const event = result.events?.find(({ topics }) => - topics.includes(ethers.utils.id('LaunchedV2(address,address,string)')) + const event = ( + result?.logs?.find(({ topics }) => + topics.includes(ethers.id('LaunchedV2(address,address,string)')) + ) as EventLog )?.args; return event; @@ -67,12 +69,12 @@ describe('EscrowFactory', function () { const Staking = await ethers.getContractFactory('Staking'); staking = (await upgrades.deployProxy( Staking, - [token.address, minimumStake, lockPeriod], + [await token.getAddress(), minimumStake, lockPeriod], { kind: 'uups', initializer: 'initialize' } - )) as Staking; + )) as unknown as Staking; // Approve spend HMT tokens staking contract - await token.connect(operator).approve(staking.address, 1000); + await token.connect(operator).approve(await staking.getAddress(), 1000); // Deploy Escrow Factory Contract const EscrowFactory = await ethers.getContractFactory( @@ -81,9 +83,9 @@ describe('EscrowFactory', function () { escrowFactory = (await upgrades.deployProxy( EscrowFactory, - [staking.address], + [await staking.getAddress()], { kind: 'uups', initializer: 'initialize' } - )) as EscrowFactory; + )) as unknown as EscrowFactory; }); describe('deployment', () => { @@ -98,7 +100,7 @@ describe('EscrowFactory', function () { escrowFactory .connect(operator) .createEscrow( - token.address, + await token.getAddress(), [await reputationOracle.getAddress()], jobRequesterId ) @@ -108,7 +110,10 @@ describe('EscrowFactory', function () { it('Operator should be able to create an escrow after staking', async () => { const event = await stakeAndCreateEscrow(staking); - expect(event?.token).to.equal(token.address, 'token address is correct'); + expect(event?.token).to.equal( + await token.getAddress(), + 'token address is correct' + ); expect(event?.escrow).to.not.be.null; }); @@ -118,10 +123,10 @@ describe('EscrowFactory', function () { await expect( escrowFactory .connect(operator) - .createEscrow(token.address, trustedHandlers, jobRequesterId) + .createEscrow(await token.getAddress(), trustedHandlers, jobRequesterId) ) .to.emit(escrowFactory, 'LaunchedV2') - .withArgs(token.address, anyValue, jobRequesterId); + .withArgs(await token.getAddress(), anyValue, jobRequesterId); }); it('Should find the newly created escrow from deployed escrow', async () => { @@ -144,7 +149,10 @@ describe('EscrowFactory', function () { const event = await createEscrow(); - expect(event?.token).to.equal(token.address, 'token address is correct'); + expect(event?.token).to.equal( + await token.getAddress(), + 'token address is correct' + ); expect(event?.escrow).to.not.be.null; }); @@ -158,7 +166,7 @@ describe('EscrowFactory', function () { escrowFactory .connect(operator) .createEscrow( - token.address, + await token.getAddress(), [await reputationOracle.getAddress()], jobRequesterId ) @@ -173,7 +181,10 @@ describe('EscrowFactory', function () { const event = await stakeAndCreateEscrow(staking); - expect(event?.token).to.equal(token.address, 'token address is correct'); + expect(event?.token).to.equal( + await token.getAddress(), + 'token address is correct' + ); expect(event?.escrow).to.not.be.null; }); @@ -185,7 +196,7 @@ describe('EscrowFactory', function () { ); await expect( - upgrades.upgradeProxy(escrowFactory.address, EscrowFactoryV0) + upgrades.upgradeProxy(await escrowFactory.getAddress(), EscrowFactoryV0) ).to.be.revertedWith('Ownable: caller is not the owner'); }); @@ -193,17 +204,27 @@ describe('EscrowFactory', function () { const EscrowFactoryV0 = await ethers.getContractFactory('EscrowFactoryV0'); const oldImplementationAddress = - await upgrades.erc1967.getImplementationAddress(escrowFactory.address); + await upgrades.erc1967.getImplementationAddress( + await escrowFactory.getAddress() + ); - await upgrades.upgradeProxy(escrowFactory.address, EscrowFactoryV0); + await upgrades.upgradeProxy( + await escrowFactory.getAddress(), + EscrowFactoryV0 + ); expect( - await upgrades.erc1967.getImplementationAddress(escrowFactory.address) + await upgrades.erc1967.getImplementationAddress( + await escrowFactory.getAddress() + ) ).to.not.be.equal(oldImplementationAddress); const event = await stakeAndCreateEscrow(staking); - expect(event?.token).to.equal(token.address, 'token address is correct'); + expect(event?.token).to.equal( + await token.getAddress(), + 'token address is correct' + ); expect(event?.escrow).to.not.be.null; try { @@ -221,14 +242,21 @@ describe('EscrowFactory', function () { const oldLastEscrow = await escrowFactory.lastEscrow(); const oldImplementationAddress = - await upgrades.erc1967.getImplementationAddress(escrowFactory.address); + await upgrades.erc1967.getImplementationAddress( + await escrowFactory.getAddress() + ); const EscrowFactoryV0 = await ethers.getContractFactory('EscrowFactoryV0'); - await upgrades.upgradeProxy(escrowFactory.address, EscrowFactoryV0); + await upgrades.upgradeProxy( + await escrowFactory.getAddress(), + EscrowFactoryV0 + ); expect( - await upgrades.erc1967.getImplementationAddress(escrowFactory.address) + await upgrades.erc1967.getImplementationAddress( + await escrowFactory.getAddress() + ) ).to.not.be.equal(oldImplementationAddress); expect(await escrowFactory.lastEscrow()).to.equal(oldLastEscrow); diff --git a/packages/core/test/KVStore.ts b/packages/core/test/KVStore.ts index 1804b76a33..dd8300233c 100644 --- a/packages/core/test/KVStore.ts +++ b/packages/core/test/KVStore.ts @@ -51,8 +51,8 @@ describe('KVStore', function () { }); it('outputs an address on deployment', async () => { - expect(typeof kvStore.address).equal('string'); - expect(kvStore.address.length).greaterThanOrEqual(10); + expect(typeof (await kvStore.getAddress())).equal('string'); + expect((await kvStore.getAddress()).length).greaterThanOrEqual(10); }); it('store public key and its ipfs hash on smart contract', async () => { diff --git a/packages/core/test/RewardPool.ts b/packages/core/test/RewardPool.ts index 5659d2e8ef..eedcfd09c7 100644 --- a/packages/core/test/RewardPool.ts +++ b/packages/core/test/RewardPool.ts @@ -1,5 +1,5 @@ import { ethers, upgrades } from 'hardhat'; -import { Signer } from 'ethers'; +import { EventLog, Signer } from 'ethers'; import { EscrowFactory, HMToken, @@ -54,9 +54,9 @@ describe('RewardPool', function () { const Staking = await ethers.getContractFactory('Staking'); staking = (await upgrades.deployProxy( Staking, - [token.address, minimumStake, lockPeriod], + [await token.getAddress(), minimumStake, lockPeriod], { kind: 'uups', initializer: 'initialize' } - )) as Staking; + )) as unknown as Staking; // Deploy Escrow Factory Contract const EscrowFactory = await ethers.getContractFactory( @@ -65,9 +65,9 @@ describe('RewardPool', function () { escrowFactory = (await upgrades.deployProxy( EscrowFactory, - [staking.address], + [await staking.getAddress()], { kind: 'uups', initializer: 'initialize' } - )) as EscrowFactory; + )) as unknown as EscrowFactory; }); this.beforeEach(async () => { @@ -95,12 +95,12 @@ describe('RewardPool', function () { const RewardPool = await ethers.getContractFactory('RewardPool'); rewardPool = (await upgrades.deployProxy( RewardPool, - [token.address, staking.address, rewardFee], + [await token.getAddress(), await staking.getAddress(), rewardFee], { kind: 'uups', initializer: 'initialize' } - )) as RewardPool; + )) as unknown as RewardPool; // Configure RewardPool in Staking - await staking.setRewardPool(rewardPool.address); + await staking.setRewardPool(await rewardPool.getAddress()); // Approve spend HMT tokens staking contract [ @@ -112,12 +112,12 @@ describe('RewardPool', function () { recordingOracle, externalAccount, ].map(async (account) => { - await token.connect(account).approve(staking.address, 1000); + await token.connect(account).approve(await staking.getAddress(), 1000); }); }); it('Should set token address given to constructor', async () => { - expect(await rewardPool.token()).to.equal(token.address); + expect(await rewardPool.token()).to.equal(await token.getAddress()); }); it('Should set fee given to constructor', async () => { @@ -138,16 +138,21 @@ describe('RewardPool', function () { await escrowFactory .connect(operator) .createEscrow( - token.address, + await token.getAddress(), [await validator.getAddress()], jobRequesterId ) ).wait(); - const event = result.events?.find(({ topics }) => - topics.includes(ethers.utils.id('LaunchedV2(address,address,string)')) + const event = ( + result?.logs?.find(({ topics }) => + topics.includes(ethers.id('LaunchedV2(address,address,string)')) + ) as EventLog )?.args; - expect(event?.token).to.equal(token.address, 'token address is correct'); + expect(event?.token).to.equal( + await token.getAddress(), + 'token address is correct' + ); expect(event?.escrow).to.not.be.null; escrowAddress = event?.escrow; @@ -160,9 +165,9 @@ describe('RewardPool', function () { rewardPool .connect(operator) .addReward( - ethers.constants.AddressZero, - ethers.constants.AddressZero, - ethers.constants.AddressZero, + ethers.ZeroAddress, + ethers.ZeroAddress, + ethers.ZeroAddress, 1 ) ).to.be.revertedWith('Caller is not staking contract'); @@ -180,7 +185,9 @@ describe('RewardPool', function () { slashedTokens ); - expect(await token.balanceOf(rewardPool.address)).to.equal(slashedTokens); + expect(await token.balanceOf(await rewardPool.getAddress())).to.equal( + slashedTokens + ); const rewards = await rewardPool.getRewards(escrowAddress); expect(rewards.length).to.equal(0); @@ -206,7 +213,9 @@ describe('RewardPool', function () { slashedTokens - rewardFee ); - expect(await token.balanceOf(rewardPool.address)).to.equal(slashedTokens); + expect(await token.balanceOf(await rewardPool.getAddress())).to.equal( + slashedTokens + ); const rewards = await rewardPool.getRewards(escrowAddress); expect(rewards.length).to.equal(1); @@ -232,16 +241,21 @@ describe('RewardPool', function () { await escrowFactory .connect(operator) .createEscrow( - token.address, + await token.getAddress(), [await validator.getAddress()], jobRequesterId ) ).wait(); - const event = result.events?.find(({ topics }) => - topics.includes(ethers.utils.id('LaunchedV2(address,address,string)')) + const event = ( + result?.logs?.find(({ topics }) => + topics.includes(ethers.id('LaunchedV2(address,address,string)')) + ) as EventLog )?.args; - expect(event?.token).to.equal(token.address, 'token address is correct'); + expect(event?.token).to.equal( + await token.getAddress(), + 'token address is correct' + ); expect(event?.escrow).to.not.be.null; escrowAddress = event?.escrow; @@ -285,14 +299,16 @@ describe('RewardPool', function () { await rewardPool.distributeReward(escrowAddress); expect(await token.balanceOf(await validator.getAddress())).to.equal( - vBalanceBefore.add(vSlashAmount - rewardFee) + vBalanceBefore + BigInt(vSlashAmount - rewardFee) ); expect(await token.balanceOf(await validator2.getAddress())).to.equal( - v2BalanceBefore.add(v2SlashAmount - rewardFee) + v2BalanceBefore + BigInt(v2SlashAmount - rewardFee) ); - expect(await token.balanceOf(rewardPool.address)).to.equal(rewardFee * 2); + expect(await token.balanceOf(await rewardPool.getAddress())).to.equal( + rewardFee * 2 + ); }); it('Should withdraw the reward', async () => { @@ -320,7 +336,7 @@ describe('RewardPool', function () { await rewardPool.withdraw(await owner.getAddress()); expect(await token.balanceOf(await owner.getAddress())).to.equal( - oBalanceBefore.add(rewardFee * 2) + oBalanceBefore + BigInt(rewardFee * 2) ); }); }); diff --git a/packages/core/test/Staking.ts b/packages/core/test/Staking.ts index 3324bea6b6..0521dd1977 100644 --- a/packages/core/test/Staking.ts +++ b/packages/core/test/Staking.ts @@ -1,7 +1,7 @@ import { anyValue } from '@nomicfoundation/hardhat-chai-matchers/withArgs'; import { expect } from 'chai'; import { ethers, upgrades } from 'hardhat'; -import { Signer } from 'ethers'; +import { EventLog, Signer } from 'ethers'; import { Escrow, EscrowFactory, @@ -94,9 +94,9 @@ describe('Staking', function () { const Staking = await ethers.getContractFactory('Staking'); staking = (await upgrades.deployProxy( Staking, - [token.address, minimumStake, lockPeriod], + [await token.getAddress(), minimumStake, lockPeriod], { kind: 'uups', initializer: 'initialize' } - )) as Staking; + )) as unknown as Staking; // Deploy Escrow Factory Contract const EscrowFactory = await ethers.getContractFactory( @@ -105,20 +105,20 @@ describe('Staking', function () { escrowFactory = (await upgrades.deployProxy( EscrowFactory, - [staking.address], + [await staking.getAddress()], { kind: 'uups', initializer: 'initialize' } - )) as EscrowFactory; + )) as unknown as EscrowFactory; // Deploy Reward Pool Conract const RewardPool = await ethers.getContractFactory('RewardPool'); rewardPool = (await upgrades.deployProxy( RewardPool, - [token.address, staking.address, rewardFee], + [await token.getAddress(), await staking.getAddress(), rewardFee], { kind: 'uups', initializer: 'initialize' } - )) as RewardPool; + )) as unknown as RewardPool; // Topup staking address - await token.connect(owner).transfer(staking.address, 1000); + await token.connect(owner).transfer(await staking.getAddress(), 1000); // Approve spend HMT tokens staking contract [ @@ -130,14 +130,14 @@ describe('Staking', function () { reputationOracle, recordingOracle, ].map(async (account) => { - await token.connect(account).approve(staking.address, 1000); + await token.connect(account).approve(await staking.getAddress(), 1000); }); }); describe('deployment', () => { it('Should set the right token address', async () => { const result = await staking.token(); - expect(result).to.equal(token.address); + expect(result).to.equal(await token.getAddress()); }); it('Should set the minimum stake', async () => { @@ -168,7 +168,7 @@ describe('Staking', function () { describe('Events', function () { it('Should emit an event on stake deposited', async function () { - await token.connect(operator).approve(staking.address, 100); //! + await token.connect(operator).approve(await staking.getAddress(), 100); //! await expect(await staking.connect(operator).stake(2)) .to.emit(staking, 'StakeDeposited') @@ -254,16 +254,21 @@ describe('Staking', function () { await escrowFactory .connect(operator) .createEscrow( - token.address, + await token.getAddress(), [await validator.getAddress()], jobRequesterId ) ).wait(); - const event = result.events?.find(({ topics }) => - topics.includes(ethers.utils.id('LaunchedV2(address,address,string)')) + const event = ( + result?.logs?.find(({ topics }) => + topics.includes(ethers.id('LaunchedV2(address,address,string)')) + ) as EventLog )?.args; - expect(event?.token).to.equal(token.address, 'token address is correct'); + expect(event?.token).to.equal( + await token.getAddress(), + 'token address is correct' + ); expect(event?.escrow).to.not.be.null; escrowAddress = event?.escrow; @@ -274,9 +279,7 @@ describe('Staking', function () { const amount = 5; await expect( - staking - .connect(operator) - .allocate(ethers.constants.AddressZero, amount) + staking.connect(operator).allocate(ethers.ZeroAddress, amount) ).to.be.revertedWith('Must be a valid address'); }); @@ -356,16 +359,21 @@ describe('Staking', function () { await escrowFactory .connect(operator) .createEscrow( - token.address, + await token.getAddress(), [await validator.getAddress()], jobRequesterId ) ).wait(); - const event = result.events?.find(({ topics }) => - topics.includes(ethers.utils.id('LaunchedV2(address,address,string)')) + const event = ( + result?.logs?.find(({ topics }) => + topics.includes(ethers.id('LaunchedV2(address,address,string)')) + ) as EventLog )?.args; - expect(event?.token).to.equal(token.address, 'token address is correct'); + expect(event?.token).to.equal( + await token.getAddress(), + 'token address is correct' + ); expect(event?.escrow).to.not.be.null; }); @@ -511,13 +519,13 @@ describe('Staking', function () { describe('Validations', function () { it('Should revert with the right error if caller is not an owner', async function () { await expect( - staking.connect(operator).setRewardPool(rewardPool.address) + staking.connect(operator).setRewardPool(await rewardPool.getAddress()) ).to.be.revertedWith('Ownable: caller is not the owner'); }); it('Should revert with the right error if not a positive number', async function () { await expect( - staking.connect(owner).setRewardPool(ethers.constants.AddressZero) + staking.connect(owner).setRewardPool(ethers.ZeroAddress) ).to.be.revertedWith('Must be a valid address'); }); }); @@ -525,17 +533,23 @@ describe('Staking', function () { describe('Events', function () { it('Should emit an event on set reward pool', async function () { await expect( - await staking.connect(owner).setRewardPool(rewardPool.address) + await staking + .connect(owner) + .setRewardPool(await rewardPool.getAddress()) ) .to.emit(staking, 'SetRewardPool') - .withArgs(rewardPool.address); + .withArgs(await rewardPool.getAddress()); }); }); describe('Set minimum stake', function () { it('Should assign a value to minimum stake variable', async function () { - await staking.connect(owner).setRewardPool(rewardPool.address); - await expect(await staking.rewardPool()).to.equal(rewardPool.address); + await staking + .connect(owner) + .setRewardPool(await rewardPool.getAddress()); + await expect(await staking.rewardPool()).to.equal( + await rewardPool.getAddress() + ); }); }); }); @@ -552,17 +566,19 @@ describe('Staking', function () { await escrowFactory .connect(operator) .createEscrow( - token.address, + await token.getAddress(), [await validator.getAddress()], jobRequesterId ) ).wait(); - const event = result.events?.find(({ topics }) => - topics.includes(ethers.utils.id('LaunchedV2(address,address,string)')) + const event = ( + result?.logs?.find(({ topics }) => + topics.includes(ethers.id('LaunchedV2(address,address,string)')) + ) as EventLog )?.args; expect(event?.token).to.equal( - token.address, + await token.getAddress(), 'token address is correct' ); expect(event?.escrow).to.not.be.null; @@ -622,17 +638,19 @@ describe('Staking', function () { await escrowFactory .connect(operator) .createEscrow( - token.address, + await token.getAddress(), [await validator.getAddress()], jobRequesterId ) ).wait(); - const event = result.events?.find(({ topics }) => - topics.includes(ethers.utils.id('LaunchedV2(address,address,string)')) + const event = ( + result?.logs?.find(({ topics }) => + topics.includes(ethers.id('LaunchedV2(address,address,string)')) + ) as EventLog )?.args; expect(event?.token).to.equal( - token.address, + await token.getAddress(), 'token address is correct' ); expect(event?.escrow).to.not.be.null; @@ -647,10 +665,10 @@ describe('Staking', function () { it('Should return a null allocation by escrow address', async function () { const allocation = await staking .connect(operator) - .getAllocation(ethers.constants.AddressZero); + .getAllocation(ethers.ZeroAddress); - expect(allocation.escrowAddress).to.equal(ethers.constants.AddressZero); - expect(allocation.staker).to.equal(ethers.constants.AddressZero); + expect(allocation.escrowAddress).to.equal(ethers.ZeroAddress); + expect(allocation.staker).to.equal(ethers.ZeroAddress); expect(allocation.tokens).to.equal(0); // Tokens allocated to a escrowAddress expect(allocation.createdAt).to.equal(0); // Time when allocation was created expect(allocation.closedAt).to.equal(0); // Time when allocation was closed @@ -675,7 +693,7 @@ describe('Staking', function () { const slashedTokens = 2; this.beforeEach(async () => { - await staking.connect(owner).setRewardPool(rewardPool.address); + await staking.connect(owner).setRewardPool(await rewardPool.getAddress()); await staking.connect(validator).stake(stakedTokens); @@ -685,16 +703,21 @@ describe('Staking', function () { await escrowFactory .connect(operator) .createEscrow( - token.address, + await token.getAddress(), [await validator.getAddress()], jobRequesterId ) ).wait(); - const event = result.events?.find(({ topics }) => - topics.includes(ethers.utils.id('LaunchedV2(address,address,string)')) + const event = ( + result?.logs?.find(({ topics }) => + topics.includes(ethers.id('LaunchedV2(address,address,string)')) + ) as EventLog )?.args; - expect(event?.token).to.equal(token.address, 'token address is correct'); + expect(event?.token).to.equal( + await token.getAddress(), + 'token address is correct' + ); expect(event?.escrow).to.not.be.null; escrowAddress = event?.escrow; @@ -723,7 +746,7 @@ describe('Staking', function () { .slash( await validator.getAddress(), await operator.getAddress(), - ethers.constants.AddressZero, + ethers.ZeroAddress, slashedTokens ) ).to.be.revertedWith('Must be a valid address'); @@ -816,9 +839,9 @@ describe('Staking', function () { stakedTokens - slashedTokens ); - await expect(await token.balanceOf(rewardPool.address)).to.equal( - slashedTokens - ); + await expect( + await token.balanceOf(await rewardPool.getAddress()) + ).to.equal(slashedTokens); }); }); }); @@ -879,7 +902,7 @@ describe('Staking', function () { await escrowFactory .connect(operator) .createEscrow( - token.address, + await token.getAddress(), [ await validator.getAddress(), await reputationOracle.getAddress(), @@ -888,11 +911,16 @@ describe('Staking', function () { jobRequesterId ) ).wait(); - const event = result.events?.find(({ topics }) => - topics.includes(ethers.utils.id('LaunchedV2(address,address,string)')) + const event = ( + result?.logs?.find(({ topics }) => + topics.includes(ethers.id('LaunchedV2(address,address,string)')) + ) as EventLog )?.args; - expect(event?.token).to.equal(token.address, 'token address is correct'); + expect(event?.token).to.equal( + await token.getAddress(), + 'token address is correct' + ); expect(event?.escrow).to.not.be.null; escrowAddress = event?.escrow; @@ -927,9 +955,7 @@ describe('Staking', function () { describe('Validations', function () { it('Should revert with the right error if not a valid address', async function () { await expect( - staking - .connect(operator) - .closeAllocation(ethers.constants.AddressZero) + staking.connect(operator).closeAllocation(ethers.ZeroAddress) ).to.be.revertedWith('Must be a valid address'); }); diff --git a/packages/sdk/typescript/human-protocol-sdk/package.json b/packages/sdk/typescript/human-protocol-sdk/package.json index 02bcdcdc84..c1cb753507 100644 --- a/packages/sdk/typescript/human-protocol-sdk/package.json +++ b/packages/sdk/typescript/human-protocol-sdk/package.json @@ -43,8 +43,7 @@ "aws-sdk": "^2.1528.0", "axios": "^1.4.0", "crypto": "^1.0.1", - "ethers": "^5.7.2", - "graphql": "^16.7.1", + "graphql": "^16.8.1", "graphql-request": "^6.1.0", "graphql-tag": "^2.12.6", "minio": "^7.0.32", diff --git a/packages/sdk/typescript/human-protocol-sdk/src/base.ts b/packages/sdk/typescript/human-protocol-sdk/src/base.ts index d17e9d9d06..a0f3083da7 100644 --- a/packages/sdk/typescript/human-protocol-sdk/src/base.ts +++ b/packages/sdk/typescript/human-protocol-sdk/src/base.ts @@ -1,5 +1,4 @@ -import { Provider } from '@ethersproject/abstract-provider'; -import { Signer } from 'ethers'; +import { ContractRunner } from 'ethers'; import { NetworkData } from './types'; /** @@ -9,17 +8,17 @@ import { NetworkData } from './types'; * */ export abstract class BaseEthersClient { - protected signerOrProvider: Signer | Provider; + protected runner: ContractRunner; public networkData: NetworkData; /** * **BaseClient constructor** * - * @param {Signer | Provider} signerOrProvider The Signer or Provider object to interact with the Ethereum network + * @param {ContractRunner} runner The Signer or Provider object to interact with the Ethereum network * @param {NetworkData} networkData The network information required to connect to the contracts */ - constructor(signerOrProvider: Signer | Provider, networkData: NetworkData) { + constructor(runner: ContractRunner, networkData: NetworkData) { this.networkData = networkData; - this.signerOrProvider = signerOrProvider; + this.runner = runner; } } diff --git a/packages/sdk/typescript/human-protocol-sdk/src/decorators.ts b/packages/sdk/typescript/human-protocol-sdk/src/decorators.ts index c3c783012e..d5cb059f44 100644 --- a/packages/sdk/typescript/human-protocol-sdk/src/decorators.ts +++ b/packages/sdk/typescript/human-protocol-sdk/src/decorators.ts @@ -1,5 +1,4 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { Signer } from 'ethers'; import { ErrorSigner } from './error'; export function requiresSigner( @@ -10,7 +9,9 @@ export function requiresSigner( const originalMethod = descriptor.value; descriptor.value = async function (this: any, ...args: any[]) { - if (!Signer.isSigner(this.signerOrProvider)) { + try { + !this.runner.getAddress(); + } catch { throw ErrorSigner; } diff --git a/packages/sdk/typescript/human-protocol-sdk/src/error.ts b/packages/sdk/typescript/human-protocol-sdk/src/error.ts index b433dddd58..8b14e80c79 100644 --- a/packages/sdk/typescript/human-protocol-sdk/src/error.ts +++ b/packages/sdk/typescript/human-protocol-sdk/src/error.ts @@ -283,18 +283,6 @@ export class InvalidArgumentError extends EthereumError { } } -export class OutOfGasError extends EthereumError { - constructor(message: string) { - super(`Out of gas: ${message}`); - } -} - -export class UnpredictableGasLimit extends EthereumError { - constructor(message: string) { - super(`Unpredictable gas limit: ${message}`); - } -} - export class ReplacementUnderpriced extends EthereumError { constructor(message: string) { super(`Replacement underpriced: ${message}`); diff --git a/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts b/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts index 7201a6a87d..f9b65a93cc 100644 --- a/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts +++ b/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts @@ -1,6 +1,4 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { Provider } from '@ethersproject/abstract-provider'; -import { Network } from '@ethersproject/networks'; import { Escrow, EscrowFactory, @@ -9,7 +7,7 @@ import { HMToken, HMToken__factory, } from '@human-protocol/core/typechain-types'; -import { BigNumber, ContractReceipt, Overrides, Signer, ethers } from 'ethers'; +import { ContractRunner, EventLog, Overrides, ethers } from 'ethers'; import gqlFetch from 'graphql-request'; import { BaseEthersClient } from './base'; import { DEFAULT_TX_ID, NETWORKS } from './constants'; @@ -21,43 +19,43 @@ import { ErrorEscrowAddressIsNotProvidedByFactory, ErrorEscrowDoesNotHaveEnoughBalance, ErrorHashIsEmptyString, - ErrorProviderDoesNotExist, - ErrorUnsupportedChainID, ErrorInvalidAddress, ErrorInvalidEscrowAddressProvided, + ErrorInvalidExchangeOracleAddressProvided, ErrorInvalidRecordingOracleAddressProvided, ErrorInvalidReputationOracleAddressProvided, ErrorInvalidTokenAddress, ErrorInvalidUrl, ErrorLaunchedEventIsNotEmitted, ErrorListOfHandlersCannotBeEmpty, + ErrorProviderDoesNotExist, ErrorRecipientAndAmountsMustBeSameLength, ErrorRecipientCannotBeEmptyArray, ErrorTotalFeeMustBeLessThanHundred, + ErrorTransferEventNotFoundInTransactionLogs, + ErrorUnsupportedChainID, ErrorUrlIsEmptyString, InvalidEthereumAddressError, - ErrorInvalidExchangeOracleAddressProvided, - ErrorTransferEventNotFoundInTransactionLogs, } from './error'; -import { IEscrowConfig, IEscrowsFilter } from './interfaces'; -import { EscrowCancel, EscrowStatus, NetworkData } from './types'; -import { isValidUrl, throwError } from './utils'; import { EscrowData, GET_ESCROWS_QUERY, GET_ESCROW_BY_ADDRESS_QUERY, } from './graphql'; +import { IEscrowConfig, IEscrowsFilter } from './interfaces'; +import { EscrowCancel, EscrowStatus, NetworkData } from './types'; +import { isValidUrl, throwError } from './utils'; /** * ## Introduction * * This client enables to perform actions on Escrow contracts and obtain information from both the contracts and subgraph. * - * Internally, the SDK will use one network or another according to the network ID of the `signerOrProvider`. + * Internally, the SDK will use one network or another according to the network ID of the `runner`. * To use this client, it is recommended to initialize it using the static `build` method. * * ```ts - * static async build(signerOrProvider: Signer | Provider); + * static async build(runner: ContractRunner); * ``` * * A `Signer` or a `Provider` should be passed depending on the use case of this module: @@ -123,47 +121,42 @@ export class EscrowClient extends BaseEthersClient { /** * **EscrowClient constructor** * - * @param {Signer | Provider} signerOrProvider The Signer or Provider object to interact with the Ethereum network + * @param {ContractRunner} runner The Runner object to interact with the Ethereum network * @param {NetworkData} network The network information required to connect to the Escrow contract */ - constructor(signerOrProvider: Signer | Provider, networkData: NetworkData) { - super(signerOrProvider, networkData); + constructor(runner: ContractRunner, networkData: NetworkData) { + super(runner, networkData); this.escrowFactoryContract = EscrowFactory__factory.connect( networkData.factoryAddress, - signerOrProvider + runner ); } /** - * Creates an instance of EscrowClient from a Signer or Provider. + * Creates an instance of EscrowClient from a Runner. * - * @param {Signer | Provider} signerOrProvider The Signer or Provider object to interact with the Ethereum network + * @param {ContractRunner} runner The Runner object to interact with the Ethereum network * * @returns {Promise} An instance of EscrowClient * @throws {ErrorProviderDoesNotExist} Thrown if the provider does not exist for the provided Signer * @throws {ErrorUnsupportedChainID} Thrown if the network's chainId is not supported */ - public static async build(signerOrProvider: Signer | Provider) { - let network: Network; - if (Signer.isSigner(signerOrProvider)) { - if (!signerOrProvider.provider) { - throw ErrorProviderDoesNotExist; - } - - network = await signerOrProvider.provider.getNetwork(); - } else { - network = await signerOrProvider.getNetwork(); + public static async build(runner: ContractRunner) { + if (!runner.provider) { + throw ErrorProviderDoesNotExist; } - const chainId: ChainId = network.chainId; + const network = await runner.provider?.getNetwork(); + + const chainId: ChainId = Number(network?.chainId); const networkData = NETWORKS[chainId]; if (!networkData) { throw ErrorUnsupportedChainID; } - return new EscrowClient(signerOrProvider, networkData); + return new EscrowClient(runner, networkData); } /** @@ -173,7 +166,7 @@ export class EscrowClient extends BaseEthersClient { */ private getEscrowContract(escrowAddress: string): Escrow { try { - return Escrow__factory.connect(escrowAddress, this.signerOrProvider); + return Escrow__factory.connect(escrowAddress, this.runner); } catch (e) { return throwError(e); } @@ -217,18 +210,18 @@ export class EscrowClient extends BaseEthersClient { jobRequesterId: string, txOptions: Overrides = {} ): Promise { - if (!ethers.utils.isAddress(tokenAddress)) { + if (!ethers.isAddress(tokenAddress)) { throw ErrorInvalidTokenAddress; } trustedHandlers.forEach((trustedHandler) => { - if (!ethers.utils.isAddress(trustedHandler)) { + if (!ethers.isAddress(trustedHandler)) { throw new InvalidEthereumAddressError(trustedHandler); } }); try { - const result: ContractReceipt = await ( + const result = await ( await this.escrowFactoryContract.createEscrow( tokenAddress, trustedHandlers, @@ -237,8 +230,10 @@ export class EscrowClient extends BaseEthersClient { ) ).wait(); - const event = result.events?.find(({ topics }) => - topics.includes(ethers.utils.id('LaunchedV2(address,address,string)')) + const event = ( + result?.logs?.find(({ topics }) => + topics.includes(ethers.id('LaunchedV2(address,address,string)')) + ) as EventLog )?.args; if (!event) { @@ -280,9 +275,9 @@ export class EscrowClient extends BaseEthersClient { * recordingOracle: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', * reputationOracle: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', * exchangeOracle: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', - * recordingOracleFee: BigNumber.from('10'), - * reputationOracleFee: BigNumber.from('10'), - * exchangeOracleFee: BigNumber.from('10'), + * recordingOracleFee: bigint.from('10'), + * reputationOracleFee: bigint.from('10'), + * exchangeOracleFee: bigint.from('10'), * manifestUrl: 'htttp://localhost/manifest.json', * manifestHash: 'b5dad76bf6772c0f07fd5e048f6e75a5f86ee079', * }; @@ -306,33 +301,31 @@ export class EscrowClient extends BaseEthersClient { manifestHash, } = escrowConfig; - if (!ethers.utils.isAddress(recordingOracle)) { + if (!ethers.isAddress(recordingOracle)) { throw ErrorInvalidRecordingOracleAddressProvided; } - if (!ethers.utils.isAddress(reputationOracle)) { + if (!ethers.isAddress(reputationOracle)) { throw ErrorInvalidReputationOracleAddressProvided; } - if (!ethers.utils.isAddress(exchangeOracle)) { + if (!ethers.isAddress(exchangeOracle)) { throw ErrorInvalidExchangeOracleAddressProvided; } - if (!ethers.utils.isAddress(escrowAddress)) { + if (!ethers.isAddress(escrowAddress)) { throw ErrorInvalidEscrowAddressProvided; } if ( - recordingOracleFee.lte(0) || - reputationOracleFee.lte(0) || - exchangeOracleFee.lte(0) + recordingOracleFee <= 0 || + reputationOracleFee <= 0 || + exchangeOracleFee <= 0 ) { throw ErrorAmountMustBeGreaterThanZero; } - if ( - recordingOracleFee.add(reputationOracleFee).add(exchangeOracleFee).gt(100) - ) { + if (recordingOracleFee + reputationOracleFee + exchangeOracleFee > 100) { throw ErrorTotalFeeMustBeLessThanHundred; } @@ -406,9 +399,9 @@ export class EscrowClient extends BaseEthersClient { * recordingOracle: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', * reputationOracle: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', * exchangeOracle: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', - * recordingOracleFee: BigNumber.from('10'), - * reputationOracleFee: BigNumber.from('10'), - * exchangeOracleFee: BigNumber.from('10'), + * recordingOracleFee: bigint.from('10'), + * reputationOracleFee: bigint.from('10'), + * exchangeOracleFee: bigint.from('10'), * manifestUrl: 'htttp://localhost/manifest.json', * manifestHash: 'b5dad76bf6772c0f07fd5e048f6e75a5f86ee079', * }; @@ -442,7 +435,7 @@ export class EscrowClient extends BaseEthersClient { * This function adds funds of the chosen token to the escrow. * * @param {string} escrowAddress Address of the escrow to fund. - * @param {BigNumber} amount Amount to be added as funds. + * @param {bigint} amount Amount to be added as funds. * @param {Overrides} [txOptions] - Additional transaction parameters (optional, defaults to an empty object). * @returns Returns void if successful. Throws error if any. * @@ -460,21 +453,21 @@ export class EscrowClient extends BaseEthersClient { * const signer = new Wallet(privateKey, provider); * const escrowClient = await EscrowClient.build(signer); * - * const amount = ethers.utils.parseUnits(5, 'ether'); //convert from ETH to WEI + * const amount = ethers.parseUnits(5, 'ether'); //convert from ETH to WEI * await escrowClient.fund('0x62dD51230A30401C455c8398d06F85e4EaB6309f', amount); * ``` */ @requiresSigner async fund( escrowAddress: string, - amount: BigNumber, + amount: bigint, txOptions: Overrides = {} ): Promise { - if (!ethers.utils.isAddress(escrowAddress)) { + if (!ethers.isAddress(escrowAddress)) { throw ErrorInvalidEscrowAddressProvided; } - if (amount.lte(0)) { + if (amount <= 0n) { throw ErrorAmountMustBeGreaterThanZero; } @@ -489,7 +482,7 @@ export class EscrowClient extends BaseEthersClient { const tokenContract: HMToken = HMToken__factory.connect( tokenAddress, - this.signerOrProvider + this.runner ); await ( await tokenContract.transfer(escrowAddress, amount, txOptions) @@ -536,7 +529,7 @@ export class EscrowClient extends BaseEthersClient { hash: string, txOptions: Overrides = {} ): Promise { - if (!ethers.utils.isAddress(escrowAddress)) { + if (!ethers.isAddress(escrowAddress)) { throw ErrorInvalidEscrowAddressProvided; } @@ -598,7 +591,7 @@ export class EscrowClient extends BaseEthersClient { escrowAddress: string, txOptions: Overrides = {} ): Promise { - if (!ethers.utils.isAddress(escrowAddress)) { + if (!ethers.isAddress(escrowAddress)) { throw ErrorInvalidEscrowAddressProvided; } @@ -621,7 +614,7 @@ export class EscrowClient extends BaseEthersClient { * * @param {string} escrowAddress Escrow address to payout. * @param {string[]} recipients Array of recipient addresses. - * @param {BigNumber[]} amounts Array of amounts the recipients will receive. + * @param {bigint[]} amounts Array of amounts the recipients will receive. * @param {string} finalResultsUrl Final results file url. * @param {string} finalResultsHash Final results file hash. * @param {Overrides} [txOptions] - Additional transaction parameters (optional, defaults to an empty object). @@ -644,7 +637,7 @@ export class EscrowClient extends BaseEthersClient { * const escrowClient = await EscrowClient.build(signer); * * const recipients = ['0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266']; - * const amounts = [ethers.utils.parseUnits(5, 'ether'), ethers.utils.parseUnits(10, 'ether')]; + * const amounts = [ethers.parseUnits(5, 'ether'), ethers.parseUnits(10, 'ether')]; * const resultsUrl = 'http://localhost/results.json'; * const resultsHash'b5dad76bf6772c0f07fd5e048f6e75a5f86ee079'; * @@ -655,12 +648,12 @@ export class EscrowClient extends BaseEthersClient { async bulkPayOut( escrowAddress: string, recipients: string[], - amounts: BigNumber[], + amounts: bigint[], finalResultsUrl: string, finalResultsHash: string, txOptions: Overrides = {} ): Promise { - if (!ethers.utils.isAddress(escrowAddress)) { + if (!ethers.isAddress(escrowAddress)) { throw ErrorInvalidEscrowAddressProvided; } @@ -677,7 +670,7 @@ export class EscrowClient extends BaseEthersClient { } recipients.forEach((recipient) => { - if (!ethers.utils.isAddress(recipient)) { + if (!ethers.isAddress(recipient)) { throw new InvalidEthereumAddressError(recipient); } }); @@ -696,12 +689,12 @@ export class EscrowClient extends BaseEthersClient { const balance = await this.getBalance(escrowAddress); - let totalAmount = BigNumber.from(0); + let totalAmount = 0n; amounts.forEach((amount) => { - totalAmount = totalAmount.add(amount); + totalAmount = totalAmount + amount; }); - if (balance.lt(totalAmount)) { + if (balance < totalAmount) { throw ErrorEscrowDoesNotHaveEnoughBalance; } @@ -759,7 +752,7 @@ export class EscrowClient extends BaseEthersClient { escrowAddress: string, txOptions: Overrides = {} ): Promise { - if (!ethers.utils.isAddress(escrowAddress)) { + if (!ethers.isAddress(escrowAddress)) { throw ErrorInvalidEscrowAddressProvided; } @@ -774,32 +767,35 @@ export class EscrowClient extends BaseEthersClient { await escrowContract.cancel(txOptions) ).wait(); - let amountTransferred: BigNumber | undefined = undefined; + let amountTransferred: bigint | undefined = undefined; const tokenAddress = await escrowContract.token(); const tokenContract: HMToken = HMToken__factory.connect( tokenAddress, - this.signerOrProvider + this.runner ); - - for (const log of transactionReceipt.logs) { - if (log.address === tokenAddress) { - const parsedLog = tokenContract.interface.parseLog(log); - - const from = parsedLog.args[0]; - if (parsedLog.name === 'Transfer' && from === escrowAddress) { - amountTransferred = parsedLog.args[2]; - break; + if (transactionReceipt) + for (const log of transactionReceipt.logs) { + if (log.address === tokenAddress) { + const parsedLog = tokenContract.interface.parseLog({ + topics: log.topics as string[], + data: log.data, + }); + + const from = parsedLog?.args[0]; + if (parsedLog?.name === 'Transfer' && from === escrowAddress) { + amountTransferred = parsedLog?.args[2]; + break; + } } } - } if (amountTransferred === undefined) { throw ErrorTransferEventNotFoundInTransactionLogs; } const escrowCancelData: EscrowCancel = { - txHash: transactionReceipt.transactionHash, + txHash: transactionReceipt?.hash || '', amountRefunded: amountTransferred, }; @@ -837,7 +833,7 @@ export class EscrowClient extends BaseEthersClient { */ @requiresSigner async abort(escrowAddress: string, txOptions: Overrides = {}): Promise { - if (!ethers.utils.isAddress(escrowAddress)) { + if (!ethers.isAddress(escrowAddress)) { throw ErrorInvalidEscrowAddressProvided; } @@ -889,7 +885,7 @@ export class EscrowClient extends BaseEthersClient { trustedHandlers: string[], txOptions: Overrides = {} ): Promise { - if (!ethers.utils.isAddress(escrowAddress)) { + if (!ethers.isAddress(escrowAddress)) { throw ErrorInvalidEscrowAddressProvided; } @@ -898,7 +894,7 @@ export class EscrowClient extends BaseEthersClient { } trustedHandlers.forEach((trustedHandler) => { - if (!ethers.utils.isAddress(trustedHandler)) { + if (!ethers.isAddress(trustedHandler)) { throw new InvalidEthereumAddressError(trustedHandler); } }); @@ -923,7 +919,7 @@ export class EscrowClient extends BaseEthersClient { * This function returns the balance for a specified escrow address. * * @param {string} escrowAddress Address of the escrow. - * @returns {BigNumber} Balance of the escrow in the token used to fund it. + * @returns {bigint} Balance of the escrow in the token used to fund it. * * **Code example** * @@ -939,8 +935,8 @@ export class EscrowClient extends BaseEthersClient { * const balance = await escrowClient.getBalance('0x62dD51230A30401C455c8398d06F85e4EaB6309f'); * ``` */ - async getBalance(escrowAddress: string): Promise { - if (!ethers.utils.isAddress(escrowAddress)) { + async getBalance(escrowAddress: string): Promise { + if (!ethers.isAddress(escrowAddress)) { throw ErrorInvalidEscrowAddressProvided; } @@ -978,7 +974,7 @@ export class EscrowClient extends BaseEthersClient { * ``` */ async getManifestHash(escrowAddress: string): Promise { - if (!ethers.utils.isAddress(escrowAddress)) { + if (!ethers.isAddress(escrowAddress)) { throw ErrorInvalidEscrowAddressProvided; } @@ -1016,7 +1012,7 @@ export class EscrowClient extends BaseEthersClient { * ``` */ async getManifestUrl(escrowAddress: string): Promise { - if (!ethers.utils.isAddress(escrowAddress)) { + if (!ethers.isAddress(escrowAddress)) { throw ErrorInvalidEscrowAddressProvided; } @@ -1054,7 +1050,7 @@ export class EscrowClient extends BaseEthersClient { * ``` */ async getResultsUrl(escrowAddress: string): Promise { - if (!ethers.utils.isAddress(escrowAddress)) { + if (!ethers.isAddress(escrowAddress)) { throw ErrorInvalidEscrowAddressProvided; } @@ -1092,7 +1088,7 @@ export class EscrowClient extends BaseEthersClient { * ``` */ async getIntermediateResultsUrl(escrowAddress: string): Promise { - if (!ethers.utils.isAddress(escrowAddress)) { + if (!ethers.isAddress(escrowAddress)) { throw ErrorInvalidEscrowAddressProvided; } @@ -1130,7 +1126,7 @@ export class EscrowClient extends BaseEthersClient { * ``` */ async getTokenAddress(escrowAddress: string): Promise { - if (!ethers.utils.isAddress(escrowAddress)) { + if (!ethers.isAddress(escrowAddress)) { throw ErrorInvalidEscrowAddressProvided; } @@ -1168,7 +1164,7 @@ export class EscrowClient extends BaseEthersClient { * ``` */ async getStatus(escrowAddress: string): Promise { - if (!ethers.utils.isAddress(escrowAddress)) { + if (!ethers.isAddress(escrowAddress)) { throw ErrorInvalidEscrowAddressProvided; } @@ -1179,7 +1175,7 @@ export class EscrowClient extends BaseEthersClient { try { const escrowContract = this.getEscrowContract(escrowAddress); - return escrowContract.status(); + return Number(await escrowContract.status()); } catch (e) { return throwError(e); } @@ -1206,7 +1202,7 @@ export class EscrowClient extends BaseEthersClient { * ``` */ async getRecordingOracleAddress(escrowAddress: string): Promise { - if (!ethers.utils.isAddress(escrowAddress)) { + if (!ethers.isAddress(escrowAddress)) { throw ErrorInvalidEscrowAddressProvided; } @@ -1244,7 +1240,7 @@ export class EscrowClient extends BaseEthersClient { * ``` */ async getJobLauncherAddress(escrowAddress: string): Promise { - if (!ethers.utils.isAddress(escrowAddress)) { + if (!ethers.isAddress(escrowAddress)) { throw ErrorInvalidEscrowAddressProvided; } @@ -1282,7 +1278,7 @@ export class EscrowClient extends BaseEthersClient { * ``` */ async getReputationOracleAddress(escrowAddress: string): Promise { - if (!ethers.utils.isAddress(escrowAddress)) { + if (!ethers.isAddress(escrowAddress)) { throw ErrorInvalidEscrowAddressProvided; } @@ -1320,7 +1316,7 @@ export class EscrowClient extends BaseEthersClient { * ``` */ async getExchangeOracleAddress(escrowAddress: string): Promise { - if (!ethers.utils.isAddress(escrowAddress)) { + if (!ethers.isAddress(escrowAddress)) { throw ErrorInvalidEscrowAddressProvided; } @@ -1358,7 +1354,7 @@ export class EscrowClient extends BaseEthersClient { * ``` */ async getFactoryAddress(escrowAddress: string): Promise { - if (!ethers.utils.isAddress(escrowAddress)) { + if (!ethers.isAddress(escrowAddress)) { throw ErrorInvalidEscrowAddressProvided; } @@ -1511,28 +1507,19 @@ export class EscrowUtils { if (!filter?.networks?.length) { throw ErrorUnsupportedChainID; } - if (filter.launcher && !ethers.utils.isAddress(filter.launcher)) { + if (filter.launcher && !ethers.isAddress(filter.launcher)) { throw ErrorInvalidAddress; } - if ( - filter.recordingOracle && - !ethers.utils.isAddress(filter.recordingOracle) - ) { + if (filter.recordingOracle && !ethers.isAddress(filter.recordingOracle)) { throw ErrorInvalidAddress; } - if ( - filter.reputationOracle && - !ethers.utils.isAddress(filter.reputationOracle) - ) { + if (filter.reputationOracle && !ethers.isAddress(filter.reputationOracle)) { throw ErrorInvalidAddress; } - if ( - filter.exchangeOracle && - !ethers.utils.isAddress(filter.exchangeOracle) - ) { + if (filter.exchangeOracle && !ethers.isAddress(filter.exchangeOracle)) { throw ErrorInvalidAddress; } @@ -1652,7 +1639,7 @@ export class EscrowUtils { throw ErrorUnsupportedChainID; } - if (escrowAddress && !ethers.utils.isAddress(escrowAddress)) { + if (escrowAddress && !ethers.isAddress(escrowAddress)) { throw ErrorInvalidAddress; } diff --git a/packages/sdk/typescript/human-protocol-sdk/src/graphql/types.ts b/packages/sdk/typescript/human-protocol-sdk/src/graphql/types.ts index e23165f5bf..251c86a467 100644 --- a/packages/sdk/typescript/human-protocol-sdk/src/graphql/types.ts +++ b/packages/sdk/typescript/human-protocol-sdk/src/graphql/types.ts @@ -1,5 +1,3 @@ -import { BigNumber } from 'ethers'; - export type EscrowData = { id: string; address: string; @@ -108,9 +106,9 @@ export type WorkerStatistics = { export type DailyPaymentData = { timestamp: Date; - totalAmountPaid: BigNumber; + totalAmountPaid: bigint; totalCount: number; - averageAmountPerWorker: BigNumber; + averageAmountPerWorker: bigint; }; export type PaymentStatistics = { @@ -124,17 +122,17 @@ export type HMTHolderData = { export type HMTHolder = { address: string; - balance: BigNumber; + balance: bigint; }; export type DailyHMTData = { timestamp: Date; - totalTransactionAmount: BigNumber; + totalTransactionAmount: bigint; totalTransactionCount: number; }; export type HMTStatistics = { - totalTransferAmount: BigNumber; + totalTransferAmount: bigint; totalTransferCount: number; totalHolders: number; holders: HMTHolder[]; diff --git a/packages/sdk/typescript/human-protocol-sdk/src/interfaces.ts b/packages/sdk/typescript/human-protocol-sdk/src/interfaces.ts index 3e204b8ad5..917a764fb4 100644 --- a/packages/sdk/typescript/human-protocol-sdk/src/interfaces.ts +++ b/packages/sdk/typescript/human-protocol-sdk/src/interfaces.ts @@ -1,34 +1,33 @@ -import { BigNumber } from 'ethers'; import { EscrowStatus } from './types'; import { ChainId } from './enums'; export interface IAllocation { escrowAddress: string; staker: string; - tokens: BigNumber; - createdAt: BigNumber; - closedAt: BigNumber; + tokens: bigint; + createdAt: bigint; + closedAt: bigint; } export interface IReward { escrowAddress: string; - amount: BigNumber; + amount: bigint; } export interface ILeader { id: string; address: string; - amountStaked: BigNumber; - amountAllocated: BigNumber; - amountLocked: BigNumber; - lockedUntilTimestamp: BigNumber; - amountWithdrawn: BigNumber; - amountSlashed: BigNumber; - reputation: BigNumber; - reward: BigNumber; - amountJobsLaunched: BigNumber; + amountStaked: bigint; + amountAllocated: bigint; + amountLocked: bigint; + lockedUntilTimestamp: bigint; + amountWithdrawn: bigint; + amountSlashed: bigint; + reputation: bigint; + reward: bigint; + amountJobsLaunched: bigint; role?: string; - fee?: BigNumber; + fee?: bigint; publicKey?: string; webhookUrl?: string; url?: string; @@ -54,9 +53,9 @@ export interface IEscrowConfig { recordingOracle: string; reputationOracle: string; exchangeOracle: string; - recordingOracleFee: BigNumber; - reputationOracleFee: BigNumber; - exchangeOracleFee: BigNumber; + recordingOracleFee: bigint; + reputationOracleFee: bigint; + exchangeOracleFee: bigint; manifestUrl: string; manifestHash: string; } diff --git a/packages/sdk/typescript/human-protocol-sdk/src/kvstore.ts b/packages/sdk/typescript/human-protocol-sdk/src/kvstore.ts index 461620d543..c9c989699b 100644 --- a/packages/sdk/typescript/human-protocol-sdk/src/kvstore.ts +++ b/packages/sdk/typescript/human-protocol-sdk/src/kvstore.ts @@ -1,10 +1,8 @@ -import { Provider } from '@ethersproject/abstract-provider'; -import { Network } from '@ethersproject/networks'; import { KVStore, KVStore__factory, } from '@human-protocol/core/typechain-types'; -import { Overrides, Signer, ethers } from 'ethers'; +import { ContractRunner, Overrides, ethers } from 'ethers'; import { BaseEthersClient } from './base'; import { NETWORKS } from './constants'; import { requiresSigner } from './decorators'; @@ -16,7 +14,6 @@ import { ErrorKVStoreArrayLength, ErrorKVStoreEmptyKey, ErrorProviderDoesNotExist, - ErrorSigner, ErrorUnsupportedChainID, } from './error'; import { NetworkData } from './types'; @@ -27,11 +24,11 @@ import { isValidUrl } from './utils'; * * This client enables to perform actions on KVStore contract and obtain information from both the contracts and subgraph. * - * Internally, the SDK will use one network or another according to the network ID of the `signerOrProvider`. + * Internally, the SDK will use one network or another according to the network ID of the `runner`. * To use this client, it is recommended to initialize it using the static `build` method. * * ```ts - * static async build(signerOrProvider: Signer | Provider); + * static async build(runner: ContractRunner); * ``` * * A `Signer` or a `Provider` should be passed depending on the use case of this module: @@ -97,47 +94,42 @@ export class KVStoreClient extends BaseEthersClient { /** * **KVStoreClient constructor** * - * @param {Signer | Provider} signerOrProvider - The Signer or Provider object to interact with the Ethereum network + * @param {ContractRunner} runner - The Runner object to interact with the Ethereum network * @param {NetworkData} network - The network information required to connect to the KVStore contract */ - constructor(signerOrProvider: Signer | Provider, networkData: NetworkData) { - super(signerOrProvider, networkData); + constructor(runner: ContractRunner, networkData: NetworkData) { + super(runner, networkData); this.contract = KVStore__factory.connect( networkData.kvstoreAddress, - signerOrProvider + runner ); } /** - * Creates an instance of KVStoreClient from a Signer or Provider. + * Creates an instance of KVStoreClient from a runner. * - * @param {Signer | Provider} signerOrProvider - The Signer or Provider object to interact with the Ethereum network + * @param {ContractRunner} runner - The Runner object to interact with the Ethereum network * * @returns {Promise} - An instance of KVStoreClient * @throws {ErrorProviderDoesNotExist} - Thrown if the provider does not exist for the provided Signer * @throws {ErrorUnsupportedChainID} - Thrown if the network's chainId is not supported */ - public static async build(signerOrProvider: Signer | Provider) { - let network: Network; - if (Signer.isSigner(signerOrProvider)) { - if (!signerOrProvider.provider) { - throw ErrorProviderDoesNotExist; - } - - network = await signerOrProvider.provider.getNetwork(); - } else { - network = await signerOrProvider.getNetwork(); + public static async build(runner: ContractRunner) { + if (!runner.provider) { + throw ErrorProviderDoesNotExist; } - const chainId: ChainId = network.chainId; + const network = await runner.provider?.getNetwork(); + + const chainId: ChainId = Number(network?.chainId); const networkData = NETWORKS[chainId]; if (!networkData) { throw ErrorUnsupportedChainID; } - return new KVStoreClient(signerOrProvider, networkData); + return new KVStoreClient(runner, networkData); } /** @@ -173,7 +165,6 @@ export class KVStoreClient extends BaseEthersClient { value: string, txOptions: Overrides = {} ): Promise { - if (!Signer.isSigner(this.signerOrProvider)) throw ErrorSigner; if (key === '') throw ErrorKVStoreEmptyKey; try { await (await this.contract.set(key, value, txOptions)).wait(); @@ -217,7 +208,6 @@ export class KVStoreClient extends BaseEthersClient { values: string[], txOptions: Overrides = {} ): Promise { - if (!Signer.isSigner(this.signerOrProvider)) throw ErrorSigner; if (keys.length !== values.length) throw ErrorKVStoreArrayLength; if (keys.includes('')) throw ErrorKVStoreEmptyKey; @@ -261,18 +251,12 @@ export class KVStoreClient extends BaseEthersClient { urlKey = 'url', txOptions: Overrides = {} ): Promise { - if (!Signer.isSigner(this.signerOrProvider)) { - throw ErrorSigner; - } - if (!isValidUrl(url)) { throw ErrorInvalidUrl; } const content = await fetch(url).then((res) => res.text()); - const contentHash = ethers.utils.keccak256( - ethers.utils.toUtf8Bytes(content) - ); + const contentHash = ethers.keccak256(ethers.toUtf8Bytes(content)); const hashKey = urlKey + 'Hash'; @@ -316,7 +300,7 @@ export class KVStoreClient extends BaseEthersClient { */ public async get(address: string, key: string): Promise { if (key === '') throw ErrorKVStoreEmptyKey; - if (!ethers.utils.isAddress(address)) throw ErrorInvalidAddress; + if (!ethers.isAddress(address)) throw ErrorInvalidAddress; try { const result = await this.contract?.get(address, key); @@ -354,7 +338,7 @@ export class KVStoreClient extends BaseEthersClient { * ``` */ public async getURL(address: string, urlKey = 'url'): Promise { - if (!ethers.utils.isAddress(address)) throw ErrorInvalidAddress; + if (!ethers.isAddress(address)) throw ErrorInvalidAddress; const hashKey = urlKey + 'Hash'; let url = '', @@ -378,9 +362,7 @@ export class KVStoreClient extends BaseEthersClient { } const content = await fetch(url).then((res) => res.text()); - const contentHash = ethers.utils.keccak256( - ethers.utils.toUtf8Bytes(content) - ); + const contentHash = ethers.keccak256(ethers.toUtf8Bytes(content)); if (hash !== contentHash) { throw ErrorInvalidHash; diff --git a/packages/sdk/typescript/human-protocol-sdk/src/staking.ts b/packages/sdk/typescript/human-protocol-sdk/src/staking.ts index 35033ebdd1..ef6c81160c 100644 --- a/packages/sdk/typescript/human-protocol-sdk/src/staking.ts +++ b/packages/sdk/typescript/human-protocol-sdk/src/staking.ts @@ -1,6 +1,4 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { Provider } from '@ethersproject/abstract-provider'; -import { Network } from '@ethersproject/networks'; import { EscrowFactory, EscrowFactory__factory, @@ -11,7 +9,7 @@ import { Staking, Staking__factory, } from '@human-protocol/core/typechain-types'; -import { BigNumber, Overrides, Signer, ethers } from 'ethers'; +import { ContractRunner, Overrides, ethers } from 'ethers'; import gqlFetch from 'graphql-request'; import { BaseEthersClient } from './base'; import { NETWORKS } from './constants'; @@ -39,11 +37,11 @@ import { GET_LEADER_QUERY, GET_LEADERS_QUERY } from './graphql/queries/staking'; * * This client enables to perform actions on staking contracts and obtain staking information from both the contracts and subgraph. * - * Internally, the SDK will use one network or another according to the network ID of the `signerOrProvider`. + * Internally, the SDK will use one network or another according to the network ID of the `runner`. * To use this client, it is recommended to initialize it using the static `build` method. * * ```ts - * static async build(signerOrProvider: Signer | Provider); + * static async build(runner: ContractRunner); * ``` * * A `Signer` or a `Provider` should be passed depending on the use case of this module: @@ -112,63 +110,58 @@ export class StakingClient extends BaseEthersClient { /** * **StakingClient constructor** * - * @param {Signer | Provider} signerOrProvider - The Signer or Provider object to interact with the Ethereum network + * @param {ContractRunner} runner - The Runner object to interact with the Ethereum network * @param {NetworkData} network - The network information required to connect to the Staking contract */ - constructor(signerOrProvider: Signer | Provider, networkData: NetworkData) { - super(signerOrProvider, networkData); + constructor(runner: ContractRunner, networkData: NetworkData) { + super(runner, networkData); this.stakingContract = Staking__factory.connect( networkData.stakingAddress, - signerOrProvider + runner ); this.escrowFactoryContract = EscrowFactory__factory.connect( networkData.factoryAddress, - signerOrProvider + runner ); this.tokenContract = HMToken__factory.connect( networkData.hmtAddress, - signerOrProvider + runner ); this.rewardPoolContract = RewardPool__factory.connect( networkData.rewardPoolAddress, - this.signerOrProvider + this.runner ); } /** - * Creates an instance of StakingClient from a Signer or Provider. + * Creates an instance of StakingClient from a Runner. * - * @param {Signer | Provider} signerOrProvider - The Signer or Provider object to interact with the Ethereum network + * @param {ContractRunner} runner - The Runner object to interact with the Ethereum network * @param {number | undefined} gasPriceMultiplier - The multiplier to apply to the gas price * * @returns {Promise} - An instance of StakingClient * @throws {ErrorProviderDoesNotExist} - Thrown if the provider does not exist for the provided Signer * @throws {ErrorUnsupportedChainID} - Thrown if the network's chainId is not supported */ - public static async build(signerOrProvider: Signer | Provider) { - let network: Network; - if (Signer.isSigner(signerOrProvider)) { - if (!signerOrProvider.provider) { - throw ErrorProviderDoesNotExist; - } - - network = await signerOrProvider.provider.getNetwork(); - } else { - network = await signerOrProvider.getNetwork(); + public static async build(runner: ContractRunner) { + if (!runner.provider) { + throw ErrorProviderDoesNotExist; } - const chainId: ChainId = network.chainId; + const network = await runner.provider?.getNetwork(); + + const chainId: ChainId = Number(network?.chainId); const networkData = NETWORKS[chainId]; if (!networkData) { throw ErrorUnsupportedChainID; } - return new StakingClient(signerOrProvider, networkData); + return new StakingClient(runner, networkData); } /** @@ -177,7 +170,7 @@ export class StakingClient extends BaseEthersClient { * @param escrowAddress Escrow address to check against */ private async checkValidEscrow(escrowAddress: string) { - if (!ethers.utils.isAddress(escrowAddress)) { + if (!ethers.isAddress(escrowAddress)) { throw ErrorInvalidEscrowAddressProvided; } @@ -189,7 +182,7 @@ export class StakingClient extends BaseEthersClient { /** * This function approves the staking contract to transfer a specified amount of tokens when the user stakes. It increases the allowance for the staking contract. * - * @param {BigNumber} amount Amount in WEI of tokens to approve for stake. + * @param {bigint} amount Amount in WEI of tokens to approve for stake. * @param {Overrides} [txOptions] - Additional transaction parameters (optional, defaults to an empty object). * @returns Returns void if successful. Throws error if any. * @@ -207,27 +200,27 @@ export class StakingClient extends BaseEthersClient { * const signer = new Wallet(privateKey, provider); * const stakingClient = await StakingClient.build(signer); * - * const amount = ethers.utils.parseUnits(5, 'ether'); //convert from ETH to WEI + * const amount = ethers.parseUnits(5, 'ether'); //convert from ETH to WEI * await stakingClient.approveStake(amount); * ``` */ @requiresSigner public async approveStake( - amount: BigNumber, + amount: bigint, txOptions: Overrides = {} ): Promise { - if (!BigNumber.isBigNumber(amount)) { + if (typeof amount !== 'bigint') { throw ErrorInvalidStakingValueType; } - if (amount.isNegative()) { + if (amount < 0n) { throw ErrorInvalidStakingValueSign; } try { await ( await this.tokenContract.approve( - this.stakingContract.address, + await this.stakingContract.getAddress(), amount, txOptions ) @@ -243,7 +236,7 @@ export class StakingClient extends BaseEthersClient { * * > `approveStake` must be called before * - * @param {BigNumber} amount Amount in WEI of tokens to stake. + * @param {bigint} amount Amount in WEI of tokens to stake. * @param {Overrides} [txOptions] - Additional transaction parameters (optional, defaults to an empty object). * @returns Returns void if successful. Throws error if any. * @@ -261,21 +254,18 @@ export class StakingClient extends BaseEthersClient { * const signer = new Wallet(privateKey, provider); * const stakingClient = await StakingClient.build(signer); * - * const amount = ethers.utils.parseUnits(5, 'ether'); //convert from ETH to WEI + * const amount = ethers.parseUnits(5, 'ether'); //convert from ETH to WEI * await stakingClient.approveStake(amount); // if it was already approved before, this is not necessary * await stakingClient.approveStake(amount); * ``` */ @requiresSigner - public async stake( - amount: BigNumber, - txOptions: Overrides = {} - ): Promise { - if (!BigNumber.isBigNumber(amount)) { + public async stake(amount: bigint, txOptions: Overrides = {}): Promise { + if (typeof amount !== 'bigint') { throw ErrorInvalidStakingValueType; } - if (amount.isNegative()) { + if (amount < 0n) { throw ErrorInvalidStakingValueSign; } @@ -292,7 +282,7 @@ export class StakingClient extends BaseEthersClient { * * > Must have tokens available to unstake * - * @param {BigNumber} amount Amount in WEI of tokens to unstake. + * @param {bigint} amount Amount in WEI of tokens to unstake. * @param {Overrides} [txOptions] - Additional transaction parameters (optional, defaults to an empty object). * @returns Returns void if successful. Throws error if any. * @@ -310,20 +300,20 @@ export class StakingClient extends BaseEthersClient { * const signer = new Wallet(privateKey, provider); * const stakingClient = await StakingClient.build(signer); * - * const amount = ethers.utils.parseUnits(5, 'ether'); //convert from ETH to WEI + * const amount = ethers.parseUnits(5, 'ether'); //convert from ETH to WEI * await stakingClient.unstake(amount); * ``` */ @requiresSigner public async unstake( - amount: BigNumber, + amount: bigint, txOptions: Overrides = {} ): Promise { - if (!BigNumber.isBigNumber(amount)) { + if (typeof amount !== 'bigint') { throw ErrorInvalidStakingValueType; } - if (amount.isNegative()) { + if (amount < 0n) { throw ErrorInvalidStakingValueSign; } @@ -377,7 +367,7 @@ export class StakingClient extends BaseEthersClient { * @param {string} staker Wallet address from who is going to be slashed * @param {string} escrowAddress Address of the escrow which allocation will be slashed * @param {Overrides} [txOptions] - Additional transaction parameters (optional, defaults to an empty object). - * @param {BigNumber} amount Amount in WEI of tokens to unstake. + * @param {bigint} amount Amount in WEI of tokens to unstake. * @returns Returns void if successful. Throws error if any. * * @@ -394,7 +384,7 @@ export class StakingClient extends BaseEthersClient { * const signer = new Wallet(privateKey, provider); * const stakingClient = await StakingClient.build(signer); * - * const amount = ethers.utils.parseUnits(5, 'ether'); //convert from ETH to WEI + * const amount = ethers.parseUnits(5, 'ether'); //convert from ETH to WEI * await stakingClient.slash('0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', '0x62dD51230A30401C455c8398d06F85e4EaB6309f', amount); * ``` */ @@ -403,22 +393,22 @@ export class StakingClient extends BaseEthersClient { slasher: string, staker: string, escrowAddress: string, - amount: BigNumber, + amount: bigint, txOptions: Overrides = {} ): Promise { - if (!BigNumber.isBigNumber(amount)) { + if (typeof amount !== 'bigint') { throw ErrorInvalidStakingValueType; } - if (amount.isNegative()) { + if (amount < 0n) { throw ErrorInvalidStakingValueSign; } - if (!ethers.utils.isAddress(slasher)) { + if (!ethers.isAddress(slasher)) { throw ErrorInvalidSlasherAddressProvided; } - if (!ethers.utils.isAddress(staker)) { + if (!ethers.isAddress(staker)) { throw ErrorInvalidStakerAddressProvided; } @@ -448,7 +438,7 @@ export class StakingClient extends BaseEthersClient { * * @param {string} escrowAddress Address of the escrow contract to allocate in. * @param {Overrides} [txOptions] - Additional transaction parameters (optional, defaults to an empty object). - * @param {BigNumber} amount Amount in WEI of tokens to allocate. + * @param {bigint} amount Amount in WEI of tokens to allocate. * @returns Returns void if successful. Throws error if any. * * @@ -465,21 +455,21 @@ export class StakingClient extends BaseEthersClient { * const signer = new Wallet(privateKey, provider); * const stakingClient = await StakingClient.build(signer); * - * const amount = ethers.utils.parseUnits(5, 'ether'); //convert from ETH to WEI + * const amount = ethers.parseUnits(5, 'ether'); //convert from ETH to WEI * await stakingClient.allocate('0x62dD51230A30401C455c8398d06F85e4EaB6309f', amount); * ``` */ @requiresSigner public async allocate( escrowAddress: string, - amount: BigNumber, + amount: bigint, txOptions: Overrides = {} ): Promise { - if (!BigNumber.isBigNumber(amount)) { + if (typeof amount !== 'bigint') { throw ErrorInvalidStakingValueType; } - if (amount.isNegative()) { + if (amount < 0n) { throw ErrorInvalidStakingValueSign; } @@ -604,7 +594,7 @@ export class StakingClient extends BaseEthersClient { * ``` */ public async getLeader(address: string): Promise { - if (!ethers.utils.isAddress(address)) { + if (!ethers.isAddress(address)) { throw ErrorInvalidStakerAddressProvided; } @@ -710,7 +700,7 @@ export class StakingClient extends BaseEthersClient { * ``` */ public async getRewards(slasherAddress: string): Promise { - if (!ethers.utils.isAddress(slasherAddress)) { + if (!ethers.isAddress(slasherAddress)) { throw ErrorInvalidSlasherAddressProvided; } diff --git a/packages/sdk/typescript/human-protocol-sdk/src/statistics.ts b/packages/sdk/typescript/human-protocol-sdk/src/statistics.ts index ce9fc01240..8e8d316449 100644 --- a/packages/sdk/typescript/human-protocol-sdk/src/statistics.ts +++ b/packages/sdk/typescript/human-protocol-sdk/src/statistics.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { BigNumber } from 'ethers'; +import { ethers } from 'ethers'; import gqlFetch from 'graphql-request'; import { @@ -296,14 +296,13 @@ export class StatisticsClient { return { dailyPaymentsData: eventDayDatas.map((eventDayData) => ({ timestamp: new Date(+eventDayData.timestamp * 1000), - totalAmountPaid: BigNumber.from(eventDayData.dailyPayoutAmount), + totalAmountPaid: ethers.toBigInt(eventDayData.dailyPayoutAmount), totalCount: +eventDayData.dailyPayoutCount, averageAmountPerWorker: eventDayData.dailyWorkerCount === '0' - ? BigNumber.from(0) - : BigNumber.from(eventDayData.dailyPayoutAmount).div( - eventDayData.dailyWorkerCount - ), + ? ethers.toBigInt(0) + : ethers.toBigInt(eventDayData.dailyPayoutAmount) / + ethers.toBigInt(eventDayData.dailyWorkerCount), })), }; } catch (e: any) { @@ -412,18 +411,18 @@ export class StatisticsClient { }); return { - totalTransferAmount: BigNumber.from( + totalTransferAmount: ethers.toBigInt( hmtokenStatistics.totalValueTransfered ), totalTransferCount: Number(hmtokenStatistics.totalTransferEventCount), totalHolders: +hmtokenStatistics.holders, holders: holders.map((holder) => ({ address: holder.address, - balance: BigNumber.from(holder.balance), + balance: ethers.toBigInt(holder.balance), })), dailyHMTData: eventDayDatas.map((eventDayData) => ({ timestamp: new Date(+eventDayData.timestamp * 1000), - totalTransactionAmount: BigNumber.from( + totalTransactionAmount: ethers.toBigInt( eventDayData.dailyHMTTransferAmount ), totalTransactionCount: +eventDayData.dailyHMTTransferCount, diff --git a/packages/sdk/typescript/human-protocol-sdk/src/types.ts b/packages/sdk/typescript/human-protocol-sdk/src/types.ts index 62a79bdb73..05dc8d5658 100644 --- a/packages/sdk/typescript/human-protocol-sdk/src/types.ts +++ b/packages/sdk/typescript/human-protocol-sdk/src/types.ts @@ -1,5 +1,3 @@ -import { BigNumber } from 'ethers'; - /** * Enum for escrow statuses. * @readonly @@ -150,5 +148,5 @@ export type EscrowCancel = { /** * The amount refunded in the escrow cancellation. */ - amountRefunded: BigNumber; + amountRefunded: bigint; }; diff --git a/packages/sdk/typescript/human-protocol-sdk/src/utils.ts b/packages/sdk/typescript/human-protocol-sdk/src/utils.ts index 977a55e41a..7838390123 100644 --- a/packages/sdk/typescript/human-protocol-sdk/src/utils.ts +++ b/packages/sdk/typescript/human-protocol-sdk/src/utils.ts @@ -7,10 +7,8 @@ import { InvalidArgumentError, NonceExpired, NumericFault, - OutOfGasError, ReplacementUnderpriced, TransactionReplaced, - UnpredictableGasLimit, } from './error'; /** @@ -35,22 +33,18 @@ export const getRevertReason = (error: any): string => { * @returns */ export const throwError = (e: any) => { - if (e.code === ethers.utils.Logger.errors.INVALID_ARGUMENT) { + if (ethers.isError(e, 'INVALID_ARGUMENT')) { throw new InvalidArgumentError(e.message); - } else if (e.code === 'OUT_OF_GAS') { - throw new OutOfGasError(e.message); - } else if (e.code === ethers.utils.Logger.errors.CALL_EXCEPTION) { + } else if (ethers.isError(e, 'CALL_EXCEPTION')) { const reason = getRevertReason(e.data); throw new ContractExecutionError(reason); - } else if (e.code === ethers.utils.Logger.errors.UNPREDICTABLE_GAS_LIMIT) { - throw new UnpredictableGasLimit(e.message); - } else if (e.code === ethers.utils.Logger.errors.TRANSACTION_REPLACED) { + } else if (ethers.isError(e, 'TRANSACTION_REPLACED')) { throw new TransactionReplaced(e.message); - } else if (e.code === ethers.utils.Logger.errors.REPLACEMENT_UNDERPRICED) { + } else if (ethers.isError(e, 'REPLACEMENT_UNDERPRICED')) { throw new ReplacementUnderpriced(e.message); - } else if (e.code === ethers.utils.Logger.errors.NUMERIC_FAULT) { + } else if (ethers.isError(e, 'NUMERIC_FAULT')) { throw new NumericFault(e.message); - } else if (e.code === ethers.utils.Logger.errors.NONCE_EXPIRED) { + } else if (ethers.isError(e, 'NONCE_EXPIRED')) { throw new NonceExpired(e.message); } else { throw new EthereumError(e.message); diff --git a/packages/sdk/typescript/human-protocol-sdk/test/escrow.test.ts b/packages/sdk/typescript/human-protocol-sdk/test/escrow.test.ts index f0b83de564..f071c33d70 100644 --- a/packages/sdk/typescript/human-protocol-sdk/test/escrow.test.ts +++ b/packages/sdk/typescript/human-protocol-sdk/test/escrow.test.ts @@ -4,7 +4,7 @@ import { Escrow__factory, HMToken__factory, } from '@human-protocol/core/typechain-types'; -import { BigNumber, Overrides, ethers } from 'ethers'; +import { Overrides, ethers } from 'ethers'; import * as gqlFetch from 'graphql-request'; import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; import { DEFAULT_TX_ID, NETWORKS } from '../src/constants'; @@ -51,7 +51,6 @@ vi.mock('graphql-request', () => { vi.mock('../src/init'); describe('EscrowClient', () => { - const provider = new ethers.providers.JsonRpcProvider(); let escrowClient: any, mockProvider: any, mockSigner: any, @@ -62,15 +61,13 @@ describe('EscrowClient', () => { beforeEach(async () => { mockProvider = { - ...provider, - getNetwork: vi.fn().mockReturnValue({ chainId: ChainId.LOCALHOST }), - }; - mockSigner = { - ...provider.getSigner(), provider: { - ...mockProvider, + getNetwork: vi.fn().mockResolvedValue({ chainId: ChainId.LOCALHOST }), }, - getAddress: vi.fn().mockReturnValue(ethers.constants.AddressZero), + }; + mockSigner = { + provider: mockProvider.provider, + getAddress: vi.fn().mockResolvedValue(ethers.ZeroAddress), }; mockEscrowContract = { @@ -92,7 +89,7 @@ describe('EscrowClient', () => { status: vi.fn(), getEscrow: vi.fn(), getEscrows: vi.fn(), - address: ethers.constants.AddressZero, + address: ethers.ZeroAddress, canceler: vi.fn(), recordingOracle: vi.fn(), reputationOracle: vi.fn(), @@ -147,9 +144,7 @@ describe('EscrowClient', () => { }); test('should create a new instance of EscrowClient with a Provider', async () => { - const provider = ethers.getDefaultProvider(); - - const escrowClient = await EscrowClient.build(provider); + const escrowClient = await EscrowClient.build(mockProvider); expect(escrowClient).toBeInstanceOf(EscrowClient); }); @@ -163,7 +158,7 @@ describe('EscrowClient', () => { }); test('should throw an error if the chain ID is unsupported', async () => { - const provider = ethers.getDefaultProvider(); + const provider = new ethers.JsonRpcProvider(); vi.spyOn(provider, 'getNetwork').mockResolvedValue({ chainId: 1337, @@ -180,32 +175,30 @@ describe('EscrowClient', () => { const invalidAddress = FAKE_ADDRESS; await expect( - escrowClient.createEscrow(invalidAddress, [ - ethers.constants.AddressZero, - ]) + escrowClient.createEscrow(invalidAddress, [ethers.ZeroAddress]) ).rejects.toThrow(ErrorInvalidTokenAddress); }); test('should throw an error if trustedHandlers contains an invalid address', async () => { await expect( - escrowClient.createEscrow(ethers.constants.AddressZero, [FAKE_ADDRESS]) + escrowClient.createEscrow(ethers.ZeroAddress, [FAKE_ADDRESS]) ).rejects.toThrow(new InvalidEthereumAddressError(FAKE_ADDRESS)); }); test('should create an escrow and return its address', async () => { - const tokenAddress = ethers.constants.AddressZero; - const trustedHandlers = [ethers.constants.AddressZero]; + const tokenAddress = ethers.ZeroAddress; + const trustedHandlers = [ethers.ZeroAddress]; const jobRequesterId = 'job-requester'; - const expectedEscrowAddress = ethers.constants.AddressZero; + const expectedEscrowAddress = ethers.ZeroAddress; // Create a spy object for the createEscrow method const createEscrowSpy = vi .spyOn(escrowClient.escrowFactoryContract, 'createEscrow') .mockImplementation(() => ({ wait: async () => ({ - events: [ + logs: [ { - topics: [ethers.utils.id('LaunchedV2(address,address,string)')], + topics: [ethers.id('LaunchedV2(address,address,string)')], args: { escrow: expectedEscrowAddress, }, @@ -230,8 +223,8 @@ describe('EscrowClient', () => { }); test('should throw an error if the create an escrow fails', async () => { - const tokenAddress = ethers.constants.AddressZero; - const trustedHandlers = [ethers.constants.AddressZero]; + const tokenAddress = ethers.ZeroAddress; + const trustedHandlers = [ethers.ZeroAddress]; const jobRequesterId = 'job-requester'; escrowClient.escrowFactoryContract.createEscrow.mockRejectedValueOnce( @@ -248,19 +241,19 @@ describe('EscrowClient', () => { }); test('should create an escrow and return its address with transaction options', async () => { - const tokenAddress = ethers.constants.AddressZero; - const trustedHandlers = [ethers.constants.AddressZero]; + const tokenAddress = ethers.ZeroAddress; + const trustedHandlers = [ethers.ZeroAddress]; const jobRequesterId = 'job-requester'; - const expectedEscrowAddress = ethers.constants.AddressZero; + const expectedEscrowAddress = ethers.ZeroAddress; // Create a spy object for the createEscrow method const createEscrowSpy = vi .spyOn(escrowClient.escrowFactoryContract, 'createEscrow') .mockImplementation(() => ({ wait: async () => ({ - events: [ + logs: [ { - topics: [ethers.utils.id('LaunchedV2(address,address,string)')], + topics: [ethers.id('LaunchedV2(address,address,string)')], args: { escrow: expectedEscrowAddress, }, @@ -292,62 +285,62 @@ describe('EscrowClient', () => { test('should throw an error if recordingOracle is an invalid address', async () => { const escrowConfig = { recordingOracle: FAKE_ADDRESS, - reputationOracle: ethers.constants.AddressZero, - exchangeOracle: ethers.constants.AddressZero, - recordingOracleFee: BigNumber.from(10), - reputationOracleFee: BigNumber.from(10), - exchangeOracleFee: BigNumber.from(10), + reputationOracle: ethers.ZeroAddress, + exchangeOracle: ethers.ZeroAddress, + recordingOracleFee: 10n, + reputationOracleFee: 10n, + exchangeOracleFee: 10n, manifestUrl: VALID_URL, hash: FAKE_HASH, }; await expect( - escrowClient.setup(ethers.constants.AddressZero, escrowConfig) + escrowClient.setup(ethers.ZeroAddress, escrowConfig) ).rejects.toThrow(ErrorInvalidRecordingOracleAddressProvided); }); test('should throw an error if reputationOracle is an invalid address', async () => { const escrowConfig = { - recordingOracle: ethers.constants.AddressZero, + recordingOracle: ethers.ZeroAddress, reputationOracle: FAKE_ADDRESS, - exchangeOracle: ethers.constants.AddressZero, - recordingOracleFee: BigNumber.from(10), - reputationOracleFee: BigNumber.from(10), - exchangeOracleFee: BigNumber.from(10), + exchangeOracle: ethers.ZeroAddress, + recordingOracleFee: 10n, + reputationOracleFee: 10n, + exchangeOracleFee: 10n, manifestUrl: VALID_URL, hash: FAKE_HASH, }; await expect( - escrowClient.setup(ethers.constants.AddressZero, escrowConfig) + escrowClient.setup(ethers.ZeroAddress, escrowConfig) ).rejects.toThrow(ErrorInvalidReputationOracleAddressProvided); }); test('should throw an error if exchangeOracle is an invalid address', async () => { const escrowConfig = { - recordingOracle: ethers.constants.AddressZero, - reputationOracle: ethers.constants.AddressZero, + recordingOracle: ethers.ZeroAddress, + reputationOracle: ethers.ZeroAddress, exchangeOracle: FAKE_ADDRESS, - recordingOracleFee: BigNumber.from(10), - reputationOracleFee: BigNumber.from(10), - exchangeOracleFee: BigNumber.from(10), + recordingOracleFee: 10n, + reputationOracleFee: 10n, + exchangeOracleFee: 10n, manifestUrl: VALID_URL, hash: FAKE_HASH, }; await expect( - escrowClient.setup(ethers.constants.AddressZero, escrowConfig) + escrowClient.setup(ethers.ZeroAddress, escrowConfig) ).rejects.toThrow(ErrorInvalidExchangeOracleAddressProvided); }); test('should throw an error if escrowAddress is an invalid address', async () => { const escrowConfig = { - recordingOracle: ethers.constants.AddressZero, - reputationOracle: ethers.constants.AddressZero, - exchangeOracle: ethers.constants.AddressZero, - recordingOracleFee: BigNumber.from(10), - reputationOracleFee: BigNumber.from(10), - exchangeOracleFee: BigNumber.from(10), + recordingOracle: ethers.ZeroAddress, + reputationOracle: ethers.ZeroAddress, + exchangeOracle: ethers.ZeroAddress, + recordingOracleFee: 10n, + reputationOracleFee: 10n, + exchangeOracleFee: 10n, manifestUrl: VALID_URL, hash: FAKE_HASH, }; @@ -359,12 +352,12 @@ describe('EscrowClient', () => { test('should throw an error if hasEscrow returns false', async () => { const escrowConfig = { - recordingOracle: ethers.constants.AddressZero, - reputationOracle: ethers.constants.AddressZero, - exchangeOracle: ethers.constants.AddressZero, - recordingOracleFee: BigNumber.from(10), - reputationOracleFee: BigNumber.from(10), - exchangeOracleFee: BigNumber.from(10), + recordingOracle: ethers.ZeroAddress, + reputationOracle: ethers.ZeroAddress, + exchangeOracle: ethers.ZeroAddress, + recordingOracleFee: 10n, + reputationOracleFee: 10n, + exchangeOracleFee: 10n, manifestUrl: VALID_URL, manifestHash: FAKE_HASH, }; @@ -372,18 +365,18 @@ describe('EscrowClient', () => { escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(false); await expect( - escrowClient.setup(ethers.constants.AddressZero, escrowConfig) + escrowClient.setup(ethers.ZeroAddress, escrowConfig) ).rejects.toThrow(ErrorEscrowAddressIsNotProvidedByFactory); }); test('should throw an error if 0 <= recordingOracleFee', async () => { const escrowConfig = { - recordingOracle: ethers.constants.AddressZero, - reputationOracle: ethers.constants.AddressZero, - exchangeOracle: ethers.constants.AddressZero, - recordingOracleFee: BigNumber.from(0), - reputationOracleFee: BigNumber.from(10), - exchangeOracleFee: BigNumber.from(10), + recordingOracle: ethers.ZeroAddress, + reputationOracle: ethers.ZeroAddress, + exchangeOracle: ethers.ZeroAddress, + recordingOracleFee: 0n, + reputationOracleFee: 10n, + exchangeOracleFee: 10n, manifestUrl: VALID_URL, hash: FAKE_HASH, }; @@ -391,18 +384,18 @@ describe('EscrowClient', () => { escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); await expect( - escrowClient.setup(ethers.constants.AddressZero, escrowConfig) + escrowClient.setup(ethers.ZeroAddress, escrowConfig) ).rejects.toThrow(ErrorAmountMustBeGreaterThanZero); }); test('should throw an error if 0 <= reputationOracleFee', async () => { const escrowConfig = { - recordingOracle: ethers.constants.AddressZero, - reputationOracle: ethers.constants.AddressZero, - exchangeOracle: ethers.constants.AddressZero, - recordingOracleFee: BigNumber.from(10), - reputationOracleFee: BigNumber.from(0), - exchangeOracleFee: BigNumber.from(10), + recordingOracle: ethers.ZeroAddress, + reputationOracle: ethers.ZeroAddress, + exchangeOracle: ethers.ZeroAddress, + recordingOracleFee: 10n, + reputationOracleFee: 0n, + exchangeOracleFee: 10n, manifestUrl: VALID_URL, hash: FAKE_HASH, }; @@ -410,18 +403,18 @@ describe('EscrowClient', () => { escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); await expect( - escrowClient.setup(ethers.constants.AddressZero, escrowConfig) + escrowClient.setup(ethers.ZeroAddress, escrowConfig) ).rejects.toThrow(ErrorAmountMustBeGreaterThanZero); }); test('should throw an error if 0 <= exchangeOracleFee', async () => { const escrowConfig = { - recordingOracle: ethers.constants.AddressZero, - reputationOracle: ethers.constants.AddressZero, - exchangeOracle: ethers.constants.AddressZero, - recordingOracleFee: BigNumber.from(10), - reputationOracleFee: BigNumber.from(10), - exchangeOracleFee: BigNumber.from(0), + recordingOracle: ethers.ZeroAddress, + reputationOracle: ethers.ZeroAddress, + exchangeOracle: ethers.ZeroAddress, + recordingOracleFee: 10n, + reputationOracleFee: 10n, + exchangeOracleFee: 0n, manifestUrl: VALID_URL, hash: FAKE_HASH, }; @@ -429,18 +422,18 @@ describe('EscrowClient', () => { escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); await expect( - escrowClient.setup(ethers.constants.AddressZero, escrowConfig) + escrowClient.setup(ethers.ZeroAddress, escrowConfig) ).rejects.toThrow(ErrorAmountMustBeGreaterThanZero); }); test('should throw an error if total fee is greater than 100', async () => { const escrowConfig = { - recordingOracle: ethers.constants.AddressZero, - reputationOracle: ethers.constants.AddressZero, - exchangeOracle: ethers.constants.AddressZero, - recordingOracleFee: BigNumber.from(40), - reputationOracleFee: BigNumber.from(40), - exchangeOracleFee: BigNumber.from(40), + recordingOracle: ethers.ZeroAddress, + reputationOracle: ethers.ZeroAddress, + exchangeOracle: ethers.ZeroAddress, + recordingOracleFee: 40n, + reputationOracleFee: 40n, + exchangeOracleFee: 40n, manifestUrl: VALID_URL, hash: FAKE_HASH, }; @@ -448,18 +441,18 @@ describe('EscrowClient', () => { escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); await expect( - escrowClient.setup(ethers.constants.AddressZero, escrowConfig) + escrowClient.setup(ethers.ZeroAddress, escrowConfig) ).rejects.toThrow(ErrorTotalFeeMustBeLessThanHundred); }); test('should throw an error if manifestUrl is an empty string', async () => { const escrowConfig = { - recordingOracle: ethers.constants.AddressZero, - reputationOracle: ethers.constants.AddressZero, - exchangeOracle: ethers.constants.AddressZero, - recordingOracleFee: BigNumber.from(10), - reputationOracleFee: BigNumber.from(10), - exchangeOracleFee: BigNumber.from(10), + recordingOracle: ethers.ZeroAddress, + reputationOracle: ethers.ZeroAddress, + exchangeOracle: ethers.ZeroAddress, + recordingOracleFee: 10n, + reputationOracleFee: 10n, + exchangeOracleFee: 10n, manifestUrl: '', hash: FAKE_HASH, }; @@ -467,18 +460,18 @@ describe('EscrowClient', () => { escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); await expect( - escrowClient.setup(ethers.constants.AddressZero, escrowConfig) + escrowClient.setup(ethers.ZeroAddress, escrowConfig) ).rejects.toThrow(ErrorUrlIsEmptyString); }); test('should throw an error if manifestUrl is an invalid url', async () => { const escrowConfig = { - recordingOracle: ethers.constants.AddressZero, - reputationOracle: ethers.constants.AddressZero, - exchangeOracle: ethers.constants.AddressZero, - recordingOracleFee: BigNumber.from(10), - reputationOracleFee: BigNumber.from(10), - exchangeOracleFee: BigNumber.from(10), + recordingOracle: ethers.ZeroAddress, + reputationOracle: ethers.ZeroAddress, + exchangeOracle: ethers.ZeroAddress, + recordingOracleFee: 10n, + reputationOracleFee: 10n, + exchangeOracleFee: 10n, manifestUrl: FAKE_URL, hash: FAKE_HASH, }; @@ -486,18 +479,18 @@ describe('EscrowClient', () => { escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); await expect( - escrowClient.setup(ethers.constants.AddressZero, escrowConfig) + escrowClient.setup(ethers.ZeroAddress, escrowConfig) ).rejects.toThrow(ErrorInvalidUrl); }); test('should throw an error if hash is an empty string', async () => { const escrowConfig = { - recordingOracle: ethers.constants.AddressZero, - reputationOracle: ethers.constants.AddressZero, - exchangeOracle: ethers.constants.AddressZero, - recordingOracleFee: BigNumber.from(10), - reputationOracleFee: BigNumber.from(10), - exchangeOracleFee: BigNumber.from(10), + recordingOracle: ethers.ZeroAddress, + reputationOracle: ethers.ZeroAddress, + exchangeOracle: ethers.ZeroAddress, + recordingOracleFee: 10n, + reputationOracleFee: 10n, + exchangeOracleFee: 10n, manifestUrl: VALID_URL, hash: '', }; @@ -505,18 +498,18 @@ describe('EscrowClient', () => { escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); await expect( - escrowClient.setup(ethers.constants.AddressZero, escrowConfig) + escrowClient.setup(ethers.ZeroAddress, escrowConfig) ).rejects.toThrow(ErrorHashIsEmptyString); }); test('should successfully setup escrow', async () => { const escrowConfig = { - recordingOracle: ethers.constants.AddressZero, - reputationOracle: ethers.constants.AddressZero, - exchangeOracle: ethers.constants.AddressZero, - recordingOracleFee: BigNumber.from(10), - reputationOracleFee: BigNumber.from(10), - exchangeOracleFee: BigNumber.from(10), + recordingOracle: ethers.ZeroAddress, + reputationOracle: ethers.ZeroAddress, + exchangeOracle: ethers.ZeroAddress, + recordingOracleFee: 10n, + reputationOracleFee: 10n, + exchangeOracleFee: 10n, manifestUrl: VALID_URL, manifestHash: FAKE_HASH, }; @@ -528,15 +521,15 @@ describe('EscrowClient', () => { wait: vi.fn().mockResolvedValue(true), })); - await escrowClient.setup(ethers.constants.AddressZero, escrowConfig); + await escrowClient.setup(ethers.ZeroAddress, escrowConfig); expect(setupSpy).toHaveBeenCalledWith( - ethers.constants.AddressZero, - ethers.constants.AddressZero, - ethers.constants.AddressZero, - BigNumber.from(10), - BigNumber.from(10), - BigNumber.from(10), + ethers.ZeroAddress, + ethers.ZeroAddress, + ethers.ZeroAddress, + 10n, + 10n, + 10n, VALID_URL, FAKE_HASH, {} @@ -545,12 +538,12 @@ describe('EscrowClient', () => { test('should throw an error if setup escrow fails', async () => { const escrowConfig = { - recordingOracle: ethers.constants.AddressZero, - reputationOracle: ethers.constants.AddressZero, - exchangeOracle: ethers.constants.AddressZero, - recordingOracleFee: BigNumber.from(10), - reputationOracleFee: BigNumber.from(10), - exchangeOracleFee: BigNumber.from(10), + recordingOracle: ethers.ZeroAddress, + reputationOracle: ethers.ZeroAddress, + exchangeOracle: ethers.ZeroAddress, + recordingOracleFee: 10n, + reputationOracleFee: 10n, + exchangeOracleFee: 10n, manifestUrl: VALID_URL, manifestHash: FAKE_HASH, }; @@ -559,16 +552,16 @@ describe('EscrowClient', () => { escrowClient.escrowContract.setup.mockRejectedValueOnce(new Error()); await expect( - escrowClient.setup(ethers.constants.AddressZero, escrowConfig) + escrowClient.setup(ethers.ZeroAddress, escrowConfig) ).rejects.toThrow(); expect(escrowClient.escrowContract.setup).toHaveBeenCalledWith( - ethers.constants.AddressZero, - ethers.constants.AddressZero, - ethers.constants.AddressZero, - BigNumber.from(10), - BigNumber.from(10), - BigNumber.from(10), + ethers.ZeroAddress, + ethers.ZeroAddress, + ethers.ZeroAddress, + 10n, + 10n, + 10n, VALID_URL, FAKE_HASH, {} @@ -577,12 +570,12 @@ describe('EscrowClient', () => { test('should successfully setup escrow with transaction options', async () => { const escrowConfig = { - recordingOracle: ethers.constants.AddressZero, - reputationOracle: ethers.constants.AddressZero, - exchangeOracle: ethers.constants.AddressZero, - recordingOracleFee: BigNumber.from(10), - reputationOracleFee: BigNumber.from(10), - exchangeOracleFee: BigNumber.from(10), + recordingOracle: ethers.ZeroAddress, + reputationOracle: ethers.ZeroAddress, + exchangeOracle: ethers.ZeroAddress, + recordingOracleFee: 10n, + reputationOracleFee: 10n, + exchangeOracleFee: 10n, manifestUrl: VALID_URL, manifestHash: FAKE_HASH, }; @@ -596,19 +589,15 @@ describe('EscrowClient', () => { const txOptions: Overrides = { gasLimit: 45000 }; - await escrowClient.setup( - ethers.constants.AddressZero, - escrowConfig, - txOptions - ); + await escrowClient.setup(ethers.ZeroAddress, escrowConfig, txOptions); expect(setupSpy).toHaveBeenCalledWith( - ethers.constants.AddressZero, - ethers.constants.AddressZero, - ethers.constants.AddressZero, - BigNumber.from(10), - BigNumber.from(10), - BigNumber.from(10), + ethers.ZeroAddress, + ethers.ZeroAddress, + ethers.ZeroAddress, + 10n, + 10n, + 10n, VALID_URL, FAKE_HASH, txOptions @@ -618,18 +607,18 @@ describe('EscrowClient', () => { describe('createAndSetupEscrow', () => { test('should successfully create and setup escrow', async () => { - const escrowAddress = ethers.constants.AddressZero; - const tokenAddress = ethers.constants.AddressZero; - const trustedHandlers = [ethers.constants.AddressZero]; + const escrowAddress = ethers.ZeroAddress; + const tokenAddress = ethers.ZeroAddress; + const trustedHandlers = [ethers.ZeroAddress]; const jobRequesterId = 'job-requester'; const escrowConfig = { - recordingOracle: ethers.constants.AddressZero, - reputationOracle: ethers.constants.AddressZero, - exchangeOracle: ethers.constants.AddressZero, - recordingOracleFee: BigNumber.from(10), - reputationOracleFee: BigNumber.from(10), - exchangeOracleFee: BigNumber.from(10), + recordingOracle: ethers.ZeroAddress, + reputationOracle: ethers.ZeroAddress, + exchangeOracle: ethers.ZeroAddress, + recordingOracleFee: 10n, + reputationOracleFee: 10n, + exchangeOracleFee: 10n, manifestUrl: VALID_URL, manifestHash: FAKE_HASH, }; @@ -655,12 +644,12 @@ describe('EscrowClient', () => { jobRequesterId ); expect(setupSpy).toHaveBeenCalledWith( - ethers.constants.AddressZero, - ethers.constants.AddressZero, - ethers.constants.AddressZero, - BigNumber.from(10), - BigNumber.from(10), - BigNumber.from(10), + ethers.ZeroAddress, + ethers.ZeroAddress, + ethers.ZeroAddress, + 10n, + 10n, + 10n, VALID_URL, FAKE_HASH, {} @@ -669,12 +658,12 @@ describe('EscrowClient', () => { test('should throw an error if setup escrow fails', async () => { const escrowConfig = { - recordingOracle: ethers.constants.AddressZero, - reputationOracle: ethers.constants.AddressZero, - exchangeOracle: ethers.constants.AddressZero, - recordingOracleFee: BigNumber.from(10), - reputationOracleFee: BigNumber.from(10), - exchangeOracleFee: BigNumber.from(10), + recordingOracle: ethers.ZeroAddress, + reputationOracle: ethers.ZeroAddress, + exchangeOracle: ethers.ZeroAddress, + recordingOracleFee: 10n, + reputationOracleFee: 10n, + exchangeOracleFee: 10n, manifestUrl: VALID_URL, manifestHash: FAKE_HASH, }; @@ -683,16 +672,16 @@ describe('EscrowClient', () => { escrowClient.escrowContract.setup.mockRejectedValueOnce(new Error()); await expect( - escrowClient.setup(ethers.constants.AddressZero, escrowConfig) + escrowClient.setup(ethers.ZeroAddress, escrowConfig) ).rejects.toThrow(); expect(escrowClient.escrowContract.setup).toHaveBeenCalledWith( - ethers.constants.AddressZero, - ethers.constants.AddressZero, - ethers.constants.AddressZero, - BigNumber.from(10), - BigNumber.from(10), - BigNumber.from(10), + ethers.ZeroAddress, + ethers.ZeroAddress, + ethers.ZeroAddress, + 10n, + 10n, + 10n, VALID_URL, FAKE_HASH, {} @@ -703,7 +692,7 @@ describe('EscrowClient', () => { describe('fund', () => { test('should throw an error if escrowAddress is an invalid address', async () => { const invalidAddress = FAKE_ADDRESS; - const amount = BigNumber.from(10); + const amount = 10n; await expect(escrowClient.fund(invalidAddress, amount)).rejects.toThrow( ErrorInvalidEscrowAddressProvided @@ -711,8 +700,8 @@ describe('EscrowClient', () => { }); test('should throw an error if hasEscrow returns false', async () => { - const escrowAddress = ethers.constants.AddressZero; - const amount = BigNumber.from(10); + const escrowAddress = ethers.ZeroAddress; + const amount = 10n; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(false); @@ -722,8 +711,8 @@ describe('EscrowClient', () => { }); test('should throw an error if 0 <= amount', async () => { - const escrowAddress = ethers.constants.AddressZero; - const invalidAmount = BigNumber.from(0); + const escrowAddress = ethers.ZeroAddress; + const invalidAmount = 0n; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); @@ -733,9 +722,9 @@ describe('EscrowClient', () => { }); test('should successfully fund escrow', async () => { - const tokenAddress = ethers.constants.AddressZero; - const escrowAddress = ethers.constants.AddressZero; - const amount = BigNumber.from(10); + const tokenAddress = ethers.ZeroAddress; + const escrowAddress = ethers.ZeroAddress; + const amount = 10n; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); escrowClient.escrowContract.token.mockReturnValue(tokenAddress); @@ -752,9 +741,9 @@ describe('EscrowClient', () => { }); test('should throw an error if transfer fails', async () => { - const tokenAddress = ethers.constants.AddressZero; - const escrowAddress = ethers.constants.AddressZero; - const amount = BigNumber.from(10); + const tokenAddress = ethers.ZeroAddress; + const escrowAddress = ethers.ZeroAddress; + const amount = 10n; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); escrowClient.escrowContract.token.mockReturnValue(tokenAddress); @@ -766,9 +755,9 @@ describe('EscrowClient', () => { }); test('should successfully fund escrow with transaction options', async () => { - const tokenAddress = ethers.constants.AddressZero; - const escrowAddress = ethers.constants.AddressZero; - const amount = BigNumber.from(10); + const tokenAddress = ethers.ZeroAddress; + const escrowAddress = ethers.ZeroAddress; + const amount = 10n; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); escrowClient.escrowContract.token.mockReturnValue(tokenAddress); @@ -802,7 +791,7 @@ describe('EscrowClient', () => { }); test('should throw an error if hasEscrow returns false', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; const url = VALID_URL; const hash = FAKE_HASH; @@ -814,7 +803,7 @@ describe('EscrowClient', () => { }); test('should throw an error if url is an empty string', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; const url = ''; const hash = FAKE_HASH; @@ -826,7 +815,7 @@ describe('EscrowClient', () => { }); test('should throw an error if results url is invalid url', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; const url = FAKE_URL; const hash = FAKE_HASH; @@ -838,7 +827,7 @@ describe('EscrowClient', () => { }); test('should throw an error if hash is an empty string', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; const url = VALID_URL; const hash = ''; @@ -850,7 +839,7 @@ describe('EscrowClient', () => { }); test('should successfully store results', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; const url = VALID_URL; const hash = FAKE_HASH; @@ -867,7 +856,7 @@ describe('EscrowClient', () => { }); test('should throw an error if the store results fails', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; const url = VALID_URL; const hash = FAKE_HASH; @@ -888,7 +877,7 @@ describe('EscrowClient', () => { }); test('should successfully store results with transaction options', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; const url = VALID_URL; const hash = FAKE_HASH; @@ -916,7 +905,7 @@ describe('EscrowClient', () => { }); test('should throw an error if hasEscrow returns false', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(false); @@ -926,7 +915,7 @@ describe('EscrowClient', () => { }); test('should successfully complete escrow', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); @@ -942,7 +931,7 @@ describe('EscrowClient', () => { }); test('should throw an error if the complete fails', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); escrowClient.escrowContract.complete.mockRejectedValueOnce(new Error()); @@ -953,7 +942,7 @@ describe('EscrowClient', () => { }); test('should successfully complete escrow with transaction options', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); @@ -973,8 +962,8 @@ describe('EscrowClient', () => { describe('bulkPayOut', () => { test('should throw an error if escrowAddress is an invalid address', async () => { const invalidAddress = FAKE_ADDRESS; - const recipients = [ethers.constants.AddressZero]; - const amounts = [BigNumber.from(100)]; + const recipients = [ethers.ZeroAddress]; + const amounts = [100n]; const finalResultsUrl = VALID_URL; const finalResultsHash = FAKE_HASH; @@ -990,9 +979,9 @@ describe('EscrowClient', () => { }); test('should throw an error if hasEscrow returns false', async () => { - const escrowAddress = ethers.constants.AddressZero; - const recipients = [ethers.constants.AddressZero]; - const amounts = [BigNumber.from(100)]; + const escrowAddress = ethers.ZeroAddress; + const recipients = [ethers.ZeroAddress]; + const amounts = [100n]; const finalResultsUrl = VALID_URL; const finalResultsHash = FAKE_HASH; @@ -1010,9 +999,9 @@ describe('EscrowClient', () => { }); test('should throw an error if recipients length is equal to 0', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; const recipients: string[] = []; - const amounts = [BigNumber.from(100)]; + const amounts = [100n]; const finalResultsUrl = VALID_URL; const finalResultsHash = FAKE_HASH; @@ -1030,8 +1019,8 @@ describe('EscrowClient', () => { }); test('should throw an error if amounts length is equal to 0', async () => { - const escrowAddress = ethers.constants.AddressZero; - const recipients = [ethers.constants.AddressZero]; + const escrowAddress = ethers.ZeroAddress; + const recipients = [ethers.ZeroAddress]; const amounts: number[] = []; const finalResultsUrl = VALID_URL; const finalResultsHash = FAKE_HASH; @@ -1050,13 +1039,9 @@ describe('EscrowClient', () => { }); test('should throw an error if recipients and amounts do not have the same length', async () => { - const escrowAddress = ethers.constants.AddressZero; - const recipients = [ethers.constants.AddressZero]; - const amounts = [ - BigNumber.from(100), - BigNumber.from(100), - BigNumber.from(100), - ]; + const escrowAddress = ethers.ZeroAddress; + const recipients = [ethers.ZeroAddress]; + const amounts = [100n, 100n, 100n]; const finalResultsUrl = VALID_URL; const finalResultsHash = FAKE_HASH; @@ -1074,9 +1059,9 @@ describe('EscrowClient', () => { }); test('should throw an error if recipients contains invalid addresses', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; const recipients = [FAKE_ADDRESS]; - const amounts = [BigNumber.from(100)]; + const amounts = [100n]; const finalResultsUrl = VALID_URL; const finalResultsHash = FAKE_HASH; @@ -1094,9 +1079,9 @@ describe('EscrowClient', () => { }); test('should throw an error if url is an empty string', async () => { - const escrowAddress = ethers.constants.AddressZero; - const recipients = [ethers.constants.AddressZero]; - const amounts = [BigNumber.from(100)]; + const escrowAddress = ethers.ZeroAddress; + const recipients = [ethers.ZeroAddress]; + const amounts = [100n]; const finalResultsUrl = ''; const finalResultsHash = FAKE_HASH; @@ -1114,9 +1099,9 @@ describe('EscrowClient', () => { }); test('should throw an error if final results url is an invalid url', async () => { - const escrowAddress = ethers.constants.AddressZero; - const recipients = [ethers.constants.AddressZero]; - const amounts = [BigNumber.from(100)]; + const escrowAddress = ethers.ZeroAddress; + const recipients = [ethers.ZeroAddress]; + const amounts = [100n]; const finalResultsUrl = FAKE_URL; const finalResultsHash = FAKE_HASH; @@ -1134,9 +1119,9 @@ describe('EscrowClient', () => { }); test('should throw an error if hash is an empty string', async () => { - const escrowAddress = ethers.constants.AddressZero; - const recipients = [ethers.constants.AddressZero]; - const amounts = [BigNumber.from(100)]; + const escrowAddress = ethers.ZeroAddress; + const recipients = [ethers.ZeroAddress]; + const amounts = [100n]; const finalResultsUrl = VALID_URL; const finalResultsHash = ''; @@ -1154,17 +1139,14 @@ describe('EscrowClient', () => { }); test('should throw an error if escrow does not have enough balance', async () => { - const escrowAddress = ethers.constants.AddressZero; - const recipients = [ - ethers.constants.AddressZero, - ethers.constants.AddressZero, - ]; - const amounts = [BigNumber.from(90), BigNumber.from(20)]; + const escrowAddress = ethers.ZeroAddress; + const recipients = [ethers.ZeroAddress, ethers.ZeroAddress]; + const amounts = [90n, 20n]; const finalResultsUrl = VALID_URL; const finalResultsHash = FAKE_HASH; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); - escrowClient.getBalance = vi.fn().mockReturnValue(BigNumber.from(50)); + escrowClient.getBalance = vi.fn().mockReturnValue(50n); await expect( escrowClient.bulkPayOut( @@ -1178,17 +1160,14 @@ describe('EscrowClient', () => { }); test('should successfully bulkPayOut escrow', async () => { - const escrowAddress = ethers.constants.AddressZero; - const recipients = [ - ethers.constants.AddressZero, - ethers.constants.AddressZero, - ]; - const amounts = [BigNumber.from(10), BigNumber.from(10)]; + const escrowAddress = ethers.ZeroAddress; + const recipients = [ethers.ZeroAddress, ethers.ZeroAddress]; + const amounts = [10n, 10n]; const finalResultsUrl = VALID_URL; const finalResultsHash = FAKE_HASH; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); - escrowClient.getBalance = vi.fn().mockReturnValue(BigNumber.from(100)); + escrowClient.getBalance = vi.fn().mockReturnValue(100n); const bulkPayOutSpy = vi .spyOn(escrowClient.escrowContract, 'bulkPayOut') @@ -1215,7 +1194,7 @@ describe('EscrowClient', () => { }); test('should throw an error if bulkPayOut fails', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); escrowClient.escrowContract.abort.mockRejectedValueOnce(new Error()); @@ -1226,17 +1205,14 @@ describe('EscrowClient', () => { }); test('should successfully bulkPayOut escrow with transaction options', async () => { - const escrowAddress = ethers.constants.AddressZero; - const recipients = [ - ethers.constants.AddressZero, - ethers.constants.AddressZero, - ]; - const amounts = [BigNumber.from(10), BigNumber.from(10)]; + const escrowAddress = ethers.ZeroAddress; + const recipients = [ethers.ZeroAddress, ethers.ZeroAddress]; + const amounts = [10n, 10n]; const finalResultsUrl = VALID_URL; const finalResultsHash = FAKE_HASH; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); - escrowClient.getBalance = vi.fn().mockReturnValue(BigNumber.from(100)); + escrowClient.getBalance = vi.fn().mockReturnValue(100n); const bulkPayOutSpy = vi .spyOn(escrowClient.escrowContract, 'bulkPayOut') @@ -1275,7 +1251,7 @@ describe('EscrowClient', () => { }); test('should throw an error if hasEscrow returns false', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(false); @@ -1285,24 +1261,20 @@ describe('EscrowClient', () => { }); test('should successfully cancel escrow', async () => { - const escrowAddress = ethers.constants.AddressZero; - const amountRefunded = BigNumber.from(1); + const escrowAddress = ethers.ZeroAddress; + const amountRefunded = 1n; escrowClient.escrowContract.token.mockResolvedValueOnce( - ethers.constants.AddressZero + ethers.ZeroAddress ); const log = { - address: ethers.constants.AddressZero, + address: ethers.ZeroAddress, name: 'Transfer', - args: [ - ethers.constants.AddressZero, - ethers.constants.AddressZero, - amountRefunded, - ], + args: [ethers.ZeroAddress, ethers.ZeroAddress, amountRefunded], }; mockTx.wait.mockResolvedValueOnce({ - transactionHash: FAKE_HASH, + hash: FAKE_HASH, logs: [log], }); @@ -1329,7 +1301,7 @@ describe('EscrowClient', () => { }); test('should throw an error if the cancel fails', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); escrowClient.escrowContract.cancel.mockRejectedValueOnce(new Error()); @@ -1340,7 +1312,7 @@ describe('EscrowClient', () => { }); test('should throw an error if the wait fails', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; mockTx.wait.mockRejectedValueOnce(new Error()); escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); escrowClient.escrowContract.cancel.mockResolvedValueOnce(mockTx); @@ -1351,18 +1323,14 @@ describe('EscrowClient', () => { }); test('should throw an error if transfer event not found in transaction logs', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; mockTx.wait.mockResolvedValueOnce({ transactionHash: FAKE_HASH, logs: [ { - address: ethers.constants.AddressZero, + address: ethers.ZeroAddress, name: 'NotTransfer', - args: [ - ethers.constants.AddressZero, - ethers.constants.AddressZero, - undefined, - ], + args: [ethers.ZeroAddress, ethers.ZeroAddress, undefined], }, ], }); @@ -1385,24 +1353,20 @@ describe('EscrowClient', () => { }); test('should successfully cancel escrow with transaction options', async () => { - const escrowAddress = ethers.constants.AddressZero; - const amountRefunded = BigNumber.from(1); + const escrowAddress = ethers.ZeroAddress; + const amountRefunded = 1n; escrowClient.escrowContract.token.mockResolvedValueOnce( - ethers.constants.AddressZero + ethers.ZeroAddress ); const log = { - address: ethers.constants.AddressZero, + address: ethers.ZeroAddress, name: 'Transfer', - args: [ - ethers.constants.AddressZero, - ethers.constants.AddressZero, - amountRefunded, - ], + args: [ethers.ZeroAddress, ethers.ZeroAddress, amountRefunded], }; mockTx.wait.mockResolvedValueOnce({ - transactionHash: FAKE_HASH, + hash: FAKE_HASH, logs: [log], }); @@ -1442,7 +1406,7 @@ describe('EscrowClient', () => { }); test('should throw an error if hasEscrow returns false', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(false); @@ -1452,7 +1416,7 @@ describe('EscrowClient', () => { }); test('should successfully abort escrow', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); const abortSpy = vi @@ -1467,7 +1431,7 @@ describe('EscrowClient', () => { }); test('should throw an error if abort fails', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); escrowClient.escrowContract.abort.mockRejectedValueOnce(new Error()); @@ -1478,7 +1442,7 @@ describe('EscrowClient', () => { }); test('should successfully abort escrow with transaction options', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); const abortSpy = vi @@ -1497,7 +1461,7 @@ describe('EscrowClient', () => { describe('addTrustedHandlers', () => { test('should throw an error if escrowAddress is an invalid address', async () => { const escrowAddress = FAKE_ADDRESS; - const trustedHandlers = [ethers.constants.AddressZero]; + const trustedHandlers = [ethers.ZeroAddress]; await expect( escrowClient.addTrustedHandlers(escrowAddress, trustedHandlers) @@ -1505,8 +1469,8 @@ describe('EscrowClient', () => { }); test('should throw an error if hasEscrow returns false', async () => { - const escrowAddress = ethers.constants.AddressZero; - const trustedHandlers = [ethers.constants.AddressZero]; + const escrowAddress = ethers.ZeroAddress; + const trustedHandlers = [ethers.ZeroAddress]; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(false); @@ -1516,7 +1480,7 @@ describe('EscrowClient', () => { }); test('should throw an error if trusted handlers length is equal to 0', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; const trustedHandlers: string[] = []; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); @@ -1527,7 +1491,7 @@ describe('EscrowClient', () => { }); test('should throw an error if trusted handlers contains invalid addresses', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; const trustedHandlers = [FAKE_ADDRESS]; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); @@ -1538,8 +1502,8 @@ describe('EscrowClient', () => { }); test('should successfully addTrustedHandlers', async () => { - const escrowAddress = ethers.constants.AddressZero; - const trustedHandlers = [ethers.constants.AddressZero]; + const escrowAddress = ethers.ZeroAddress; + const trustedHandlers = [ethers.ZeroAddress]; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); const addTrustedHandlersSpy = vi @@ -1554,8 +1518,8 @@ describe('EscrowClient', () => { }); test('should throw an error if addTrustedHandlers fails', async () => { - const escrowAddress = ethers.constants.AddressZero; - const trustedHandlers = [ethers.constants.AddressZero]; + const escrowAddress = ethers.ZeroAddress; + const trustedHandlers = [ethers.ZeroAddress]; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); escrowClient.escrowContract.addTrustedHandlers.mockRejectedValueOnce( @@ -1572,8 +1536,8 @@ describe('EscrowClient', () => { }); test('should successfully addTrustedHandlers with transaction options', async () => { - const escrowAddress = ethers.constants.AddressZero; - const trustedHandlers = [ethers.constants.AddressZero]; + const escrowAddress = ethers.ZeroAddress; + const trustedHandlers = [ethers.ZeroAddress]; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); const addTrustedHandlersSpy = vi @@ -1606,7 +1570,7 @@ describe('EscrowClient', () => { }); test('should throw an error if hasEscrow returns false', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(false); @@ -1616,8 +1580,8 @@ describe('EscrowClient', () => { }); test('should successfully getBalance escrow', async () => { - const escrowAddress = ethers.constants.AddressZero; - const amount = BigNumber.from(100); + const escrowAddress = ethers.ZeroAddress; + const amount = 100n; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); escrowClient.escrowContract.getBalance.mockReturnValue(amount); @@ -1629,7 +1593,7 @@ describe('EscrowClient', () => { }); test('should throw an error if the getBalance fails', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); escrowClient.escrowContract.getBalance.mockRejectedValueOnce(new Error()); @@ -1650,7 +1614,7 @@ describe('EscrowClient', () => { }); test('should throw an error if hasEscrow returns false', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(false); @@ -1660,7 +1624,7 @@ describe('EscrowClient', () => { }); test('should successfully getManifestHash', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; const hash = FAKE_HASH; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); @@ -1673,7 +1637,7 @@ describe('EscrowClient', () => { }); test('should throw an error if getManifestHash fails', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); escrowClient.escrowContract.manifestHash.mockRejectedValueOnce( @@ -1698,7 +1662,7 @@ describe('EscrowClient', () => { }); test('should throw an error if hasEscrow returns false', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(false); @@ -1708,7 +1672,7 @@ describe('EscrowClient', () => { }); test('should successfully getManifestUrl', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; const url = FAKE_URL; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); @@ -1721,7 +1685,7 @@ describe('EscrowClient', () => { }); test('should throw an error if getManifestUrl fails', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); escrowClient.escrowContract.manifestUrl.mockRejectedValueOnce( @@ -1746,7 +1710,7 @@ describe('EscrowClient', () => { }); test('should throw an error if hasEscrow returns false', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(false); @@ -1756,7 +1720,7 @@ describe('EscrowClient', () => { }); test('should successfully getResultsUrl', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; const url = FAKE_URL; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); @@ -1771,7 +1735,7 @@ describe('EscrowClient', () => { }); test('should throw an error if getResultsUrl fails', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); escrowClient.escrowContract.finalResultsUrl.mockRejectedValueOnce( @@ -1796,7 +1760,7 @@ describe('EscrowClient', () => { }); test('should throw an error if hasEscrow returns false', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(false); @@ -1806,7 +1770,7 @@ describe('EscrowClient', () => { }); test('should successfully getIntermediateResultsUrl', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; const url = FAKE_URL; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); @@ -1822,7 +1786,7 @@ describe('EscrowClient', () => { }); test('should throw an error if intermediateResultsUrl fails', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); escrowClient.escrowContract.intermediateResultsUrl.mockRejectedValueOnce( @@ -1849,7 +1813,7 @@ describe('EscrowClient', () => { }); test('should throw an error if hasEscrow returns false', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(false); @@ -1859,21 +1823,19 @@ describe('EscrowClient', () => { }); test('should successfully getTokenAddress', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); - escrowClient.escrowContract.token.mockReturnValue( - ethers.constants.AddressZero - ); + escrowClient.escrowContract.token.mockReturnValue(ethers.ZeroAddress); const tokenAddress = await escrowClient.getTokenAddress(escrowAddress); - expect(tokenAddress).toEqual(ethers.constants.AddressZero); + expect(tokenAddress).toEqual(ethers.ZeroAddress); expect(escrowClient.escrowContract.token).toHaveBeenCalledWith(); }); test('should throw an error if getTokenAddress fails', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); escrowClient.escrowContract.token.mockRejectedValueOnce(new Error()); @@ -1896,7 +1858,7 @@ describe('EscrowClient', () => { }); test('should throw an error if hasEscrow returns false', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(false); @@ -1906,7 +1868,7 @@ describe('EscrowClient', () => { }); test('should successfully getStatus', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); escrowClient.escrowContract.status.mockReturnValue(EscrowStatus.Complete); @@ -1918,7 +1880,7 @@ describe('EscrowClient', () => { }); test('should throw an error if getStatus fails', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); escrowClient.escrowContract.status.mockRejectedValueOnce(new Error()); @@ -1939,7 +1901,7 @@ describe('EscrowClient', () => { }); test('should throw an error if hasEscrow returns false', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(false); @@ -1949,24 +1911,24 @@ describe('EscrowClient', () => { }); test('should successfully getRecordingOracleAddress', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); escrowClient.escrowContract.recordingOracle.mockReturnValue( - ethers.constants.AddressZero + ethers.ZeroAddress ); const recordingOracleAddress = await escrowClient.getRecordingOracleAddress(escrowAddress); - expect(recordingOracleAddress).toEqual(ethers.constants.AddressZero); + expect(recordingOracleAddress).toEqual(ethers.ZeroAddress); expect( escrowClient.escrowContract.recordingOracle ).toHaveBeenCalledWith(); }); test('should throw an error if getRecordingOracleAddress fails', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); escrowClient.escrowContract.recordingOracle.mockRejectedValueOnce( @@ -1993,7 +1955,7 @@ describe('EscrowClient', () => { }); test('should throw an error if hasEscrow returns false', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(false); @@ -2003,24 +1965,24 @@ describe('EscrowClient', () => { }); test('should successfully getReputationOracleAddress', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); escrowClient.escrowContract.reputationOracle.mockReturnValue( - ethers.constants.AddressZero + ethers.ZeroAddress ); const reputationOracleAddress = await escrowClient.getReputationOracleAddress(escrowAddress); - expect(reputationOracleAddress).toEqual(ethers.constants.AddressZero); + expect(reputationOracleAddress).toEqual(ethers.ZeroAddress); expect( escrowClient.escrowContract.reputationOracle ).toHaveBeenCalledWith(); }); test('should throw an error if getReputationOracleAddress fails', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); escrowClient.escrowContract.reputationOracle.mockRejectedValueOnce( @@ -2047,7 +2009,7 @@ describe('EscrowClient', () => { }); test('should throw an error if hasEscrow returns false', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(false); @@ -2057,22 +2019,22 @@ describe('EscrowClient', () => { }); test('should successfully getExchangeOracleAddress', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); escrowClient.escrowContract.exchangeOracle.mockReturnValue( - ethers.constants.AddressZero + ethers.ZeroAddress ); const exchangeOracleAddress = await escrowClient.getExchangeOracleAddress(escrowAddress); - expect(exchangeOracleAddress).toEqual(ethers.constants.AddressZero); + expect(exchangeOracleAddress).toEqual(ethers.ZeroAddress); expect(escrowClient.escrowContract.exchangeOracle).toHaveBeenCalledWith(); }); test('should throw an error if getExchangeOracleAddress fails', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); escrowClient.escrowContract.exchangeOracle.mockRejectedValueOnce( @@ -2097,7 +2059,7 @@ describe('EscrowClient', () => { }); test('should throw an error if hasEscrow returns false', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(false); @@ -2107,22 +2069,20 @@ describe('EscrowClient', () => { }); test('should successfully get the job launcher address', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); - escrowClient.escrowContract.launcher.mockReturnValue( - ethers.constants.AddressZero - ); + escrowClient.escrowContract.launcher.mockReturnValue(ethers.ZeroAddress); const jobLauncherAddress = await escrowClient.getJobLauncherAddress(escrowAddress); - expect(jobLauncherAddress).toEqual(ethers.constants.AddressZero); + expect(jobLauncherAddress).toEqual(ethers.ZeroAddress); expect(escrowClient.escrowContract.launcher).toHaveBeenCalledWith(); }); test('should throw an error if getJobLauncherAddress fails', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); escrowClient.escrowContract.launcher.mockRejectedValueOnce(new Error()); @@ -2145,7 +2105,7 @@ describe('EscrowClient', () => { }); test('should throw an error if hasEscrow returns false', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(false); @@ -2155,22 +2115,22 @@ describe('EscrowClient', () => { }); test('should successfully get the escrow factory address', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); escrowClient.escrowContract.escrowFactory.mockReturnValue( - ethers.constants.AddressZero + ethers.ZeroAddress ); const escrowFactoryAddress = await escrowClient.getFactoryAddress(escrowAddress); - expect(escrowFactoryAddress).toEqual(ethers.constants.AddressZero); + expect(escrowFactoryAddress).toEqual(ethers.ZeroAddress); expect(escrowClient.escrowContract.escrowFactory).toHaveBeenCalledWith(); }); test('should throw an error if getFactoryAddress fails', async () => { - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; escrowClient.escrowFactoryContract.hasEscrow.mockReturnValue(true); escrowClient.escrowContract.escrowFactory.mockRejectedValueOnce( @@ -2297,7 +2257,7 @@ describe('EscrowUtils', () => { const result = await EscrowUtils.getEscrows({ networks: [ChainId.POLYGON_MUMBAI], - launcher: ethers.constants.AddressZero, + launcher: ethers.ZeroAddress, }); expect(result).toEqual(escrows); @@ -2384,7 +2344,7 @@ describe('EscrowUtils', () => { describe('getEscrow', () => { test('should throw an error if chain id is an unsupported id', async () => { const chainId = -1; - const escrowAddress = ethers.constants.AddressZero; + const escrowAddress = ethers.ZeroAddress; await expect( EscrowUtils.getEscrow(chainId, escrowAddress) @@ -2404,7 +2364,7 @@ describe('EscrowUtils', () => { const chainId = ChainId.LOCALHOST; const escrow = { id: '1', - address: ethers.constants.AddressZero, + address: ethers.ZeroAddress, amountPaid: '3', balance: '0', count: '1', @@ -2418,10 +2378,7 @@ describe('EscrowUtils', () => { .spyOn(gqlFetch, 'default') .mockResolvedValue({ escrow }); - const result = await EscrowUtils.getEscrow( - chainId, - ethers.constants.AddressZero - ); + const result = await EscrowUtils.getEscrow(chainId, ethers.ZeroAddress); expect(result).toEqual(escrow); expect(gqlFetchSpy).toHaveBeenCalledWith( diff --git a/packages/sdk/typescript/human-protocol-sdk/test/kvstore.test.ts b/packages/sdk/typescript/human-protocol-sdk/test/kvstore.test.ts index ae231d5b99..60abfc3c4c 100644 --- a/packages/sdk/typescript/human-protocol-sdk/test/kvstore.test.ts +++ b/packages/sdk/typescript/human-protocol-sdk/test/kvstore.test.ts @@ -31,17 +31,14 @@ describe('KVStoreClient', () => { mockKVStoreContract: any; beforeAll(async () => { - const provider = new ethers.providers.JsonRpcProvider(); mockProvider = { - ...provider, - getNetwork: vi.fn().mockReturnValue({ chainId: ChainId.LOCALHOST }), - }; - mockSigner = { - ...provider.getSigner(), provider: { - ...mockProvider, + getNetwork: vi.fn().mockReturnValue({ chainId: ChainId.LOCALHOST }), }, - getAddress: vi.fn().mockReturnValue(ethers.constants.AddressZero), + }; + mockSigner = { + provider: mockProvider.provider, + getAddress: vi.fn().mockReturnValue(ethers.ZeroAddress), }; network = NETWORKS[ChainId.LOCALHOST]; }); @@ -66,9 +63,7 @@ describe('KVStoreClient', () => { }); test('should create a new instance of KVStoreClient with a Provider', async () => { - const provider = ethers.getDefaultProvider(); - - const kvStoreClient = await KVStoreClient.build(provider); + const kvStoreClient = await KVStoreClient.build(mockProvider); expect(kvStoreClient).toBeInstanceOf(KVStoreClient); }); @@ -82,7 +77,7 @@ describe('KVStoreClient', () => { }); test('should throw an error if the chain ID is unsupported', async () => { - const provider = ethers.getDefaultProvider(); + const provider = new ethers.JsonRpcProvider(); vi.spyOn(provider, 'getNetwork').mockResolvedValue({ chainId: 1337, @@ -168,7 +163,7 @@ describe('KVStoreClient', () => { ['url', 'urlHash'], [ 'https://example.com', - ethers.utils.keccak256(ethers.utils.toUtf8Bytes('example')), + ethers.keccak256(ethers.toUtf8Bytes('example')), ], {} ); @@ -190,7 +185,7 @@ describe('KVStoreClient', () => { ['linkedinUrl', 'linkedinUrlHash'], [ 'https://example.com', - ethers.utils.keccak256(ethers.utils.toUtf8Bytes('example')), + ethers.keccak256(ethers.toUtf8Bytes('example')), ], {} ); @@ -223,7 +218,7 @@ describe('KVStoreClient', () => { ['url', 'urlHash'], [ 'https://example.com', - ethers.utils.keccak256(ethers.utils.toUtf8Bytes('example')), + ethers.keccak256(ethers.toUtf8Bytes('example')), ], {} ); @@ -251,7 +246,7 @@ describe('KVStoreClient', () => { ['linkedinUrl', 'linkedinUrlHash'], [ 'https://example.com', - ethers.utils.keccak256(ethers.utils.toUtf8Bytes('example')), + ethers.keccak256(ethers.toUtf8Bytes('example')), ], txOptions ); @@ -424,9 +419,7 @@ describe('KVStoreClient', () => { }); test('should return URL if the content is valid', async () => { - const validHash = ethers.utils.keccak256( - ethers.utils.toUtf8Bytes('example') - ); + const validHash = ethers.keccak256(ethers.toUtf8Bytes('example')); mockKVStoreContract.get.mockResolvedValueOnce('example.com'); mockKVStoreContract.get.mockResolvedValueOnce(validHash); @@ -447,9 +440,7 @@ describe('KVStoreClient', () => { }); test('should return URL for the given URL key if the content is valid', async () => { - const validHash = ethers.utils.keccak256( - ethers.utils.toUtf8Bytes('example') - ); + const validHash = ethers.keccak256(ethers.toUtf8Bytes('example')); mockKVStoreContract.get.mockResolvedValueOnce('example.com'); mockKVStoreContract.get.mockResolvedValueOnce(validHash); @@ -471,8 +462,8 @@ describe('KVStoreClient', () => { }); test('should throw an error if the content is not valid', async () => { - const invalidHash = ethers.utils.keccak256( - ethers.utils.toUtf8Bytes('invalid-example') + const invalidHash = ethers.keccak256( + ethers.toUtf8Bytes('invalid-example') ); mockKVStoreContract.get.mockResolvedValueOnce('example.com'); diff --git a/packages/sdk/typescript/human-protocol-sdk/test/staking.test.ts b/packages/sdk/typescript/human-protocol-sdk/test/staking.test.ts index 3b8597c175..affeaf0e51 100644 --- a/packages/sdk/typescript/human-protocol-sdk/test/staking.test.ts +++ b/packages/sdk/typescript/human-protocol-sdk/test/staking.test.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import * as gqlFetch from 'graphql-request'; -import { BigNumber, Overrides, Signer, ethers } from 'ethers'; +import { Overrides, Signer, ethers } from 'ethers'; import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; import { ChainId } from '../src/enums'; import { @@ -33,7 +33,6 @@ vi.mock('graphql-request', () => { vi.mock('../src/init'); describe('StakingClient', () => { - const provider = new ethers.providers.JsonRpcProvider(); let stakingClient: any, mockProvider: any, mockSigner: any, @@ -44,15 +43,13 @@ describe('StakingClient', () => { beforeEach(async () => { mockProvider = { - ...provider, - getNetwork: vi.fn().mockReturnValue({ chainId: ChainId.MAINNET }), - }; - mockSigner = { - ...provider.getSigner(), provider: { - ...mockProvider, + getNetwork: vi.fn().mockResolvedValue({ chainId: ChainId.LOCALHOST }), }, - getAddress: vi.fn().mockReturnValue(ethers.constants.AddressZero), + }; + mockSigner = { + provider: mockProvider.provider, + getAddress: vi.fn().mockResolvedValue(ethers.ZeroAddress), }; mockStakingContract = { @@ -67,7 +64,7 @@ describe('StakingClient', () => { getStaker: vi.fn(), getListOfStakers: vi.fn(), getAllocation: vi.fn(), - address: ethers.constants.AddressZero, + getAddress: vi.fn().mockResolvedValue(ethers.ZeroAddress), }; mockEscrowFactoryContract = { @@ -102,9 +99,7 @@ describe('StakingClient', () => { }); test('should create a new instance of StakingClient with a Provider', async () => { - const provider = ethers.getDefaultProvider(); - - const stakingClient = await StakingClient.build(provider); + const stakingClient = await StakingClient.build(mockProvider); expect(stakingClient).toBeInstanceOf(StakingClient); }); @@ -118,7 +113,7 @@ describe('StakingClient', () => { }); test('should throw an error if the chain ID is unsupported', async () => { - const provider = ethers.getDefaultProvider(); + const provider = new ethers.JsonRpcProvider(); vi.spyOn(provider, 'getNetwork').mockResolvedValue({ chainId: 1337, @@ -131,10 +126,10 @@ describe('StakingClient', () => { }); describe('approveStake', () => { - const amount = BigNumber.from(FAKE_AMOUNT); - const negativeAmount = BigNumber.from(FAKE_NEGATIVE_AMOUNT); + const amount = ethers.toBigInt(FAKE_AMOUNT); + const negativeAmount = ethers.toBigInt(FAKE_NEGATIVE_AMOUNT); - test('should throw an error if the amount is not a BigNumber', async () => { + test('should throw an error if the amount is not a bigint', async () => { await expect(stakingClient.approveStake('foo')).rejects.toThrow( ErrorInvalidStakingValueType ); @@ -158,11 +153,7 @@ describe('StakingClient', () => { })); await expect(await stakingClient.approveStake(amount)).toBeUndefined(); - expect(approveSpy).toBeCalledWith( - ethers.constants.AddressZero, - amount, - {} - ); + expect(approveSpy).toBeCalledWith(ethers.ZeroAddress, amount, {}); expect(mockTokenContract.approve).toHaveBeenCalledTimes(1); }); @@ -180,11 +171,7 @@ describe('StakingClient', () => { await expect( await stakingClient.approveStake(amount, txOptions) ).toBeUndefined(); - expect(approveSpy).toBeCalledWith( - ethers.constants.AddressZero, - amount, - txOptions - ); + expect(approveSpy).toBeCalledWith(ethers.ZeroAddress, amount, txOptions); expect(mockTokenContract.approve).toHaveBeenCalledTimes(1); }); @@ -195,7 +182,7 @@ describe('StakingClient', () => { await expect(stakingClient.approveStake(amount)).rejects.toThrow(); expect(mockTokenContract.approve).toBeCalledWith( - ethers.constants.AddressZero, + ethers.ZeroAddress, amount, {} ); @@ -204,10 +191,10 @@ describe('StakingClient', () => { }); describe('stake', () => { - const amount = BigNumber.from(FAKE_AMOUNT); - const negativeAmount = BigNumber.from(FAKE_NEGATIVE_AMOUNT); + const amount = ethers.toBigInt(FAKE_AMOUNT); + const negativeAmount = ethers.toBigInt(FAKE_NEGATIVE_AMOUNT); - test('should throw an error if amount is not a BigNumber', async () => { + test('should throw an error if amount is not a bigint', async () => { await expect(stakingClient.stake('foo')).rejects.toThrow( ErrorInvalidStakingValueType ); @@ -263,10 +250,10 @@ describe('StakingClient', () => { }); describe('unstake', () => { - const amount = BigNumber.from(FAKE_AMOUNT); - const negativeAmount = BigNumber.from(FAKE_NEGATIVE_AMOUNT); + const amount = ethers.toBigInt(FAKE_AMOUNT); + const negativeAmount = ethers.toBigInt(FAKE_NEGATIVE_AMOUNT); - test('should throw an error if amount is not a BigNumber', async () => { + test('should throw an error if amount is not a bigint', async () => { await expect(stakingClient.unstake('foo')).rejects.toThrow( ErrorInvalidStakingValueType ); @@ -356,16 +343,16 @@ describe('StakingClient', () => { }); describe('slash', () => { - const amount = BigNumber.from(FAKE_AMOUNT); - const negativeAmount = BigNumber.from(FAKE_NEGATIVE_AMOUNT); + const amount = ethers.toBigInt(FAKE_AMOUNT); + const negativeAmount = ethers.toBigInt(FAKE_NEGATIVE_AMOUNT); const invalidAddress = 'InvalidAddress'; - test('throws an error if amount is not a BigNumber', async () => { + test('throws an error if amount is not a bigint', async () => { await expect( stakingClient.slash( - ethers.constants.AddressZero, - ethers.constants.AddressZero, - ethers.constants.AddressZero, + ethers.ZeroAddress, + ethers.ZeroAddress, + ethers.ZeroAddress, 'foo' ) ).rejects.toThrow(ErrorInvalidStakingValueType); @@ -375,9 +362,9 @@ describe('StakingClient', () => { test('throws an error if amount is negative', async () => { await expect( stakingClient.slash( - ethers.constants.AddressZero, - ethers.constants.AddressZero, - ethers.constants.AddressZero, + ethers.ZeroAddress, + ethers.ZeroAddress, + ethers.ZeroAddress, negativeAmount ) ).rejects.toThrow(ErrorInvalidStakingValueSign); @@ -388,8 +375,8 @@ describe('StakingClient', () => { await expect( stakingClient.slash( invalidAddress, - ethers.constants.AddressZero, - ethers.constants.AddressZero, + ethers.ZeroAddress, + ethers.ZeroAddress, amount ) ).rejects.toThrow(ErrorInvalidSlasherAddressProvided); @@ -399,9 +386,9 @@ describe('StakingClient', () => { test('throws an error if staker address is invalid', async () => { await expect( stakingClient.slash( - ethers.constants.AddressZero, + ethers.ZeroAddress, invalidAddress, - ethers.constants.AddressZero, + ethers.ZeroAddress, amount ) ).rejects.toThrow(ErrorInvalidStakerAddressProvided); @@ -411,8 +398,8 @@ describe('StakingClient', () => { test('throws an error if escrow address is invalid', async () => { await expect( stakingClient.slash( - ethers.constants.AddressZero, - ethers.constants.AddressZero, + ethers.ZeroAddress, + ethers.ZeroAddress, invalidAddress, amount ) @@ -426,8 +413,8 @@ describe('StakingClient', () => { await expect( stakingClient.slash( invalidAddress, - ethers.constants.AddressZero, - ethers.constants.AddressZero, + ethers.ZeroAddress, + ethers.ZeroAddress, amount ) ).rejects.toThrow(ErrorInvalidSlasherAddressProvided); @@ -440,17 +427,17 @@ describe('StakingClient', () => { await expect( stakingClient.slash( - ethers.constants.AddressZero, - ethers.constants.AddressZero, - ethers.constants.AddressZero, + ethers.ZeroAddress, + ethers.ZeroAddress, + ethers.ZeroAddress, amount ) ).rejects.toThrow(); expect(mockStakingContract.slash).toHaveBeenCalledWith( - ethers.constants.AddressZero, - ethers.constants.AddressZero, - ethers.constants.AddressZero, + ethers.ZeroAddress, + ethers.ZeroAddress, + ethers.ZeroAddress, amount, {} ); @@ -466,16 +453,16 @@ describe('StakingClient', () => { })); await stakingClient.slash( - ethers.constants.AddressZero, - ethers.constants.AddressZero, - ethers.constants.AddressZero, + ethers.ZeroAddress, + ethers.ZeroAddress, + ethers.ZeroAddress, amount ); expect(slashSpy).toHaveBeenCalledWith( - ethers.constants.AddressZero, - ethers.constants.AddressZero, - ethers.constants.AddressZero, + ethers.ZeroAddress, + ethers.ZeroAddress, + ethers.ZeroAddress, amount, {} ); @@ -493,17 +480,17 @@ describe('StakingClient', () => { const txOptions: Overrides = { gasLimit: 45000 }; await stakingClient.slash( - ethers.constants.AddressZero, - ethers.constants.AddressZero, - ethers.constants.AddressZero, + ethers.ZeroAddress, + ethers.ZeroAddress, + ethers.ZeroAddress, amount, txOptions ); expect(slashSpy).toHaveBeenCalledWith( - ethers.constants.AddressZero, - ethers.constants.AddressZero, - ethers.constants.AddressZero, + ethers.ZeroAddress, + ethers.ZeroAddress, + ethers.ZeroAddress, amount, txOptions ); @@ -512,8 +499,8 @@ describe('StakingClient', () => { }); describe('allocate', () => { - const amount = BigNumber.from(FAKE_AMOUNT); - const negativeAmount = BigNumber.from(FAKE_NEGATIVE_AMOUNT); + const amount = ethers.toBigInt(FAKE_AMOUNT); + const negativeAmount = ethers.toBigInt(FAKE_NEGATIVE_AMOUNT); const invalidAddress = 'InvalidAddress'; test('throws an error if escrow address is invalid', async () => { @@ -523,16 +510,16 @@ describe('StakingClient', () => { expect(mockStakingContract.allocate).toHaveBeenCalledTimes(0); }); - test('throws an error if amount is not a BigNumber', async () => { + test('throws an error if amount is not a bigint', async () => { await expect( - stakingClient.allocate(ethers.constants.AddressZero, 'foo') + stakingClient.allocate(ethers.ZeroAddress, 'foo') ).rejects.toThrow(ErrorInvalidStakingValueType); expect(mockStakingContract.allocate).toHaveBeenCalledTimes(0); }); test('throws an error if amount is negative', async () => { await expect( - stakingClient.allocate(ethers.constants.AddressZero, negativeAmount) + stakingClient.allocate(ethers.ZeroAddress, negativeAmount) ).rejects.toThrow(ErrorInvalidStakingValueSign); expect(mockStakingContract.allocate).toHaveBeenCalledTimes(0); }); @@ -541,7 +528,7 @@ describe('StakingClient', () => { mockEscrowFactoryContract.hasEscrow.mockRejectedValueOnce(new Error()); await expect( - stakingClient.allocate(ethers.constants.AddressZero, amount) + stakingClient.allocate(ethers.ZeroAddress, amount) ).rejects.toThrow(); expect(mockStakingContract.allocate).toHaveBeenCalledTimes(0); }); @@ -553,13 +540,9 @@ describe('StakingClient', () => { .mockImplementation(() => ({ wait: vi.fn().mockResolvedValue(true), })); - await stakingClient.allocate(ethers.constants.AddressZero, amount); + await stakingClient.allocate(ethers.ZeroAddress, amount); - expect(allocateSpy).toHaveBeenCalledWith( - ethers.constants.AddressZero, - amount, - {} - ); + expect(allocateSpy).toHaveBeenCalledWith(ethers.ZeroAddress, amount, {}); expect(allocateSpy).toHaveBeenCalledTimes(1); }); @@ -573,14 +556,10 @@ describe('StakingClient', () => { const txOptions: Overrides = { gasLimit: 45000 }; - await stakingClient.allocate( - ethers.constants.AddressZero, - amount, - txOptions - ); + await stakingClient.allocate(ethers.ZeroAddress, amount, txOptions); expect(allocateSpy).toHaveBeenCalledWith( - ethers.constants.AddressZero, + ethers.ZeroAddress, amount, txOptions ); @@ -592,10 +571,10 @@ describe('StakingClient', () => { mockStakingContract.allocate.mockRejectedValueOnce(new Error()); await expect( - stakingClient.allocate(ethers.constants.AddressZero, amount) + stakingClient.allocate(ethers.ZeroAddress, amount) ).rejects.toThrow(); expect(mockStakingContract.allocate).toHaveBeenCalledWith( - ethers.constants.AddressZero, + ethers.ZeroAddress, amount, {} ); @@ -617,7 +596,7 @@ describe('StakingClient', () => { mockEscrowFactoryContract.hasEscrow.mockRejectedValueOnce(new Error()); await expect( - stakingClient.closeAllocation(ethers.constants.AddressZero) + stakingClient.closeAllocation(ethers.ZeroAddress) ).rejects.toThrow(); expect(mockStakingContract.closeAllocation).toHaveBeenCalledTimes(0); }); @@ -627,11 +606,11 @@ describe('StakingClient', () => { mockStakingContract.closeAllocation.mockRejectedValueOnce(new Error()); await expect( - stakingClient.closeAllocation(ethers.constants.AddressZero) + stakingClient.closeAllocation(ethers.ZeroAddress) ).rejects.toThrow(); expect(mockStakingContract.closeAllocation).toHaveBeenCalledWith( - ethers.constants.AddressZero, + ethers.ZeroAddress, {} ); expect(mockStakingContract.closeAllocation).toHaveBeenCalledTimes(1); @@ -645,12 +624,9 @@ describe('StakingClient', () => { wait: vi.fn().mockResolvedValue(true), })); - await stakingClient.closeAllocation(ethers.constants.AddressZero); + await stakingClient.closeAllocation(ethers.ZeroAddress); - expect(closeAllocationSpy).toHaveBeenCalledWith( - ethers.constants.AddressZero, - {} - ); + expect(closeAllocationSpy).toHaveBeenCalledWith(ethers.ZeroAddress, {}); expect(closeAllocationSpy).toHaveBeenCalledTimes(1); }); @@ -664,13 +640,10 @@ describe('StakingClient', () => { const txOptions: Overrides = { gasLimit: 45000 }; - await stakingClient.closeAllocation( - ethers.constants.AddressZero, - txOptions - ); + await stakingClient.closeAllocation(ethers.ZeroAddress, txOptions); expect(closeAllocationSpy).toHaveBeenCalledWith( - ethers.constants.AddressZero, + ethers.ZeroAddress, txOptions ); expect(closeAllocationSpy).toHaveBeenCalledTimes(1); @@ -691,7 +664,7 @@ describe('StakingClient', () => { mockEscrowFactoryContract.hasEscrow.mockRejectedValueOnce(new Error()); await expect( - stakingClient.distributeReward(ethers.constants.AddressZero) + stakingClient.distributeReward(ethers.ZeroAddress) ).rejects.toThrow(); expect(mockRewardPoolContract.distributeReward).toHaveBeenCalledTimes(0); }); @@ -704,12 +677,9 @@ describe('StakingClient', () => { wait: vi.fn().mockResolvedValue(true), })); - await stakingClient.distributeReward(ethers.constants.AddressZero); + await stakingClient.distributeReward(ethers.ZeroAddress); - expect(distributeRewardSpy).toHaveBeenCalledWith( - ethers.constants.AddressZero, - {} - ); + expect(distributeRewardSpy).toHaveBeenCalledWith(ethers.ZeroAddress, {}); expect(distributeRewardSpy).toHaveBeenCalledTimes(1); }); @@ -723,13 +693,10 @@ describe('StakingClient', () => { const txOptions: Overrides = { gasLimit: 45000 }; - await stakingClient.distributeReward( - ethers.constants.AddressZero, - txOptions - ); + await stakingClient.distributeReward(ethers.ZeroAddress, txOptions); expect(distributeRewardSpy).toHaveBeenCalledWith( - ethers.constants.AddressZero, + ethers.ZeroAddress, txOptions ); expect(distributeRewardSpy).toHaveBeenCalledTimes(1); @@ -737,21 +704,21 @@ describe('StakingClient', () => { }); describe('getLeader', () => { - const stakerAddress = ethers.constants.AddressZero; + const stakerAddress = ethers.ZeroAddress; const invalidAddress = 'InvalidAddress'; const mockLeader: ILeader = { id: stakerAddress, address: stakerAddress, - amountStaked: ethers.utils.parseEther('100'), - amountAllocated: ethers.utils.parseEther('50'), - amountLocked: ethers.utils.parseEther('25'), - lockedUntilTimestamp: ethers.BigNumber.from(0), - amountWithdrawn: ethers.utils.parseEther('25'), - amountSlashed: ethers.utils.parseEther('25'), - reputation: ethers.utils.parseEther('25'), - reward: ethers.utils.parseEther('25'), - amountJobsLaunched: ethers.utils.parseEther('25'), + amountStaked: ethers.parseEther('100'), + amountAllocated: ethers.parseEther('50'), + amountLocked: ethers.parseEther('25'), + lockedUntilTimestamp: ethers.toBigInt(0), + amountWithdrawn: ethers.parseEther('25'), + amountSlashed: ethers.parseEther('25'), + reputation: ethers.parseEther('25'), + reward: ethers.parseEther('25'), + amountJobsLaunched: ethers.parseEther('25'), }; test('should return staker information', async () => { @@ -761,13 +728,9 @@ describe('StakingClient', () => { const result = await stakingClient.getLeader(stakerAddress); - expect(gqlFetchSpy).toHaveBeenCalledWith( - 'https://api.thegraph.com/subgraphs/name/humanprotocol/mainnet-v2', - GET_LEADER_QUERY, - { - address: stakerAddress, - } - ); + expect(gqlFetchSpy).toHaveBeenCalledWith('', GET_LEADER_QUERY, { + address: stakerAddress, + }); expect(result).toEqual(mockLeader); }); @@ -789,20 +752,20 @@ describe('StakingClient', () => { }); describe('getLeaders', () => { - const stakerAddress = ethers.constants.AddressZero; + const stakerAddress = ethers.ZeroAddress; const mockLeader: ILeader = { id: stakerAddress, address: stakerAddress, - amountStaked: ethers.utils.parseEther('100'), - amountAllocated: ethers.utils.parseEther('50'), - amountLocked: ethers.utils.parseEther('25'), - lockedUntilTimestamp: ethers.BigNumber.from(0), - amountWithdrawn: ethers.utils.parseEther('25'), - amountSlashed: ethers.utils.parseEther('25'), - reputation: ethers.utils.parseEther('25'), - reward: ethers.utils.parseEther('25'), - amountJobsLaunched: ethers.utils.parseEther('25'), + amountStaked: ethers.parseEther('100'), + amountAllocated: ethers.parseEther('50'), + amountLocked: ethers.parseEther('25'), + lockedUntilTimestamp: ethers.toBigInt(0), + amountWithdrawn: ethers.parseEther('25'), + amountSlashed: ethers.parseEther('25'), + reputation: ethers.parseEther('25'), + reward: ethers.parseEther('25'), + amountJobsLaunched: ethers.parseEther('25'), }; test('should return an array of stakers', async () => { @@ -814,7 +777,7 @@ describe('StakingClient', () => { const result = await stakingClient.getLeaders(filter); expect(gqlFetchSpy).toHaveBeenCalledWith( - 'https://api.thegraph.com/subgraphs/name/humanprotocol/mainnet-v2', + '', GET_LEADERS_QUERY(filter), filter ); @@ -845,28 +808,26 @@ describe('StakingClient', () => { mockEscrowFactoryContract.hasEscrow.mockRejectedValueOnce(new Error()); await expect( - stakingClient.getAllocation(ethers.constants.AddressZero) + stakingClient.getAllocation(ethers.ZeroAddress) ).rejects.toThrow(); expect(mockStakingContract.getAllocation).toHaveBeenCalledTimes(0); }); test('should return allocation information', async () => { const mockAllocation: IAllocation = { - escrowAddress: ethers.constants.AddressZero, - staker: ethers.constants.AddressZero, - tokens: ethers.utils.parseEther('100'), - createdAt: ethers.utils.parseEther('100'), - closedAt: ethers.utils.parseEther('100'), + escrowAddress: ethers.ZeroAddress, + staker: ethers.ZeroAddress, + tokens: ethers.parseEther('100'), + createdAt: ethers.parseEther('100'), + closedAt: ethers.parseEther('100'), }; mockEscrowFactoryContract.hasEscrow.mockResolvedValueOnce(true); mockStakingContract.getAllocation.mockResolvedValueOnce(mockAllocation); - const result = await stakingClient.getAllocation( - ethers.constants.AddressZero - ); + const result = await stakingClient.getAllocation(ethers.ZeroAddress); expect(result).toEqual(mockAllocation); expect(mockStakingContract.getAllocation).toHaveBeenCalledWith( - ethers.constants.AddressZero + ethers.ZeroAddress ); expect(mockStakingContract.getAllocation).toHaveBeenCalledTimes(1); }); @@ -875,7 +836,7 @@ describe('StakingClient', () => { mockEscrowFactoryContract.hasEscrow.mockResolvedValueOnce(true); mockStakingContract.getAllocation.mockRejectedValue(new Error()); await expect( - stakingClient.getAllocation(ethers.constants.AddressZero) + stakingClient.getAllocation(ethers.ZeroAddress) ).rejects.toThrow(); }); }); @@ -884,8 +845,8 @@ describe('StakingClient', () => { const invalidAddress = 'InvalidAddress'; const mockReward: IReward = { - escrowAddress: ethers.constants.AddressZero, - amount: ethers.utils.parseEther('100'), + escrowAddress: ethers.ZeroAddress, + amount: ethers.parseEther('100'), }; test('should throw an error if an invalid escrow address is provided', async () => { @@ -900,9 +861,7 @@ describe('StakingClient', () => { Promise.resolve([mockReward, mockReward]) ); - const results = await stakingClient.getRewards( - ethers.constants.AddressZero - ); + const results = await stakingClient.getRewards(ethers.ZeroAddress); expect(results).toEqual([mockReward, mockReward]); }); diff --git a/packages/sdk/typescript/human-protocol-sdk/test/statistics.test.ts b/packages/sdk/typescript/human-protocol-sdk/test/statistics.test.ts index 84b8f4d9e1..1a15f73c60 100644 --- a/packages/sdk/typescript/human-protocol-sdk/test/statistics.test.ts +++ b/packages/sdk/typescript/human-protocol-sdk/test/statistics.test.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { BigNumber } from 'ethers'; +import { ethers } from 'ethers'; import * as gqlFetch from 'graphql-request'; import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; import { NETWORKS } from '../src/constants'; @@ -192,9 +192,9 @@ describe('StatisticsClient', () => { dailyPaymentsData: [ { timestamp: new Date(1000), - totalAmountPaid: BigNumber.from(100), + totalAmountPaid: ethers.toBigInt(100), totalCount: 4, - averageAmountPerWorker: BigNumber.from(25), + averageAmountPerWorker: ethers.toBigInt(25), }, ], }); @@ -263,19 +263,19 @@ describe('StatisticsClient', () => { ); expect(result).toEqual({ - totalTransferAmount: BigNumber.from(100), + totalTransferAmount: ethers.toBigInt(100), totalTransferCount: 4, totalHolders: 2, holders: [ { address: '0x123', - balance: BigNumber.from(10), + balance: ethers.toBigInt(10), }, ], dailyHMTData: [ { timestamp: new Date(1000), - totalTransactionAmount: BigNumber.from(100), + totalTransactionAmount: ethers.toBigInt(100), totalTransactionCount: 4, }, ], diff --git a/yarn.lock b/yarn.lock index fa3b3c6868..b5a2e01e8f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,7 +7,7 @@ resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== -"@adobe/css-tools@^4.3.1": +"@adobe/css-tools@^4.3.2": version "4.3.2" resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.3.2.tgz#a6abc715fb6884851fca9dad37fc34739a04fd11" integrity sha512-DA5a1C0gD/pLOvhv33YMrbf2FK3oUzwNl9oOJqE4XVjuEtt6XIakRcsd7eLiOSPkp1kTRQGICTA8cKra/vFbjw== @@ -108,11 +108,11 @@ tslib "^1.11.1" "@aws-sdk/types@^3.1.0": - version "3.468.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.468.0.tgz#f97b34fc92a800d1d8b866f47693ae8f3d46517b" - integrity sha512-rx/9uHI4inRbp2tw3Y4Ih4PNZkVj32h7WneSg3MVgVjAoVD5Zti9KhS5hkvsBxfgmQmg0AQbE+b1sy5WGAgntA== + version "3.485.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.485.0.tgz#9ffebb602bba4b6b75e2b037ee93a8735c06da3e" + integrity sha512-+QW32YQdvZRDOwrAQPo/qCyXoSjgXB6RwJwCwkd8ebJXRXw6tmGKIHaZqYHt/LtBymvnaBgBBADNa4+qFvlOFw== dependencies: - "@smithy/types" "^2.7.0" + "@smithy/types" "^2.8.0" tslib "^2.5.0" "@aws-sdk/util-utf8-browser@^3.0.0": @@ -136,19 +136,19 @@ integrity sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw== "@babel/core@^7.1.0", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.16.0", "@babel/core@^7.20.12", "@babel/core@^7.22.20", "@babel/core@^7.23.5", "@babel/core@^7.7.2", "@babel/core@^7.8.0": - version "7.23.6" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.23.6.tgz#8be77cd77c55baadcc1eae1c33df90ab6d2151d4" - integrity sha512-FxpRyGjrMJXh7X3wGLGhNDCRiwpWEF74sKjTLDJSG5Kyvow3QZaG0Adbqzi9ZrVjTWpsX+2cxWXD71NMg93kdw== + version "7.23.7" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.23.7.tgz#4d8016e06a14b5f92530a13ed0561730b5c6483f" + integrity sha512-+UpDgowcmqe36d4NwqvKsyPMlOLNGMsfMmQ5WGCu+siCe3t3dfe9njrzGfdN4qq+bcNUt0+Vw6haRxBOycs4dw== dependencies: "@ampproject/remapping" "^2.2.0" "@babel/code-frame" "^7.23.5" "@babel/generator" "^7.23.6" "@babel/helper-compilation-targets" "^7.23.6" "@babel/helper-module-transforms" "^7.23.3" - "@babel/helpers" "^7.23.6" + "@babel/helpers" "^7.23.7" "@babel/parser" "^7.23.6" "@babel/template" "^7.22.15" - "@babel/traverse" "^7.23.6" + "@babel/traverse" "^7.23.7" "@babel/types" "^7.23.6" convert-source-map "^2.0.0" debug "^4.1.0" @@ -200,10 +200,10 @@ lru-cache "^5.1.1" semver "^6.3.1" -"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.22.15", "@babel/helper-create-class-features-plugin@^7.23.6": - version "7.23.6" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.6.tgz#b04d915ce92ce363666f816a884cdcfc9be04953" - integrity sha512-cBXU1vZni/CpGF29iTu4YRbOZt3Wat6zCoMDxRF1MayiEc4URxOj31tT65HUM0CRpMowA3HCJaAOVOUnMf96cw== +"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.22.15", "@babel/helper-create-class-features-plugin@^7.23.6", "@babel/helper-create-class-features-plugin@^7.23.7": + version "7.23.7" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.7.tgz#b2e6826e0e20d337143655198b79d58fdc9bd43d" + integrity sha512-xCoqR/8+BoNnXOY7RVSgv6X+o7pmT5q1d+gGcRlXYkI+9B31glE4jeejhKVpA04O1AtzOt7OSQ6VYKP5FcRl9g== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" "@babel/helper-environment-visitor" "^7.22.20" @@ -355,13 +355,13 @@ "@babel/template" "^7.22.15" "@babel/types" "^7.22.19" -"@babel/helpers@^7.23.6": - version "7.23.6" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.23.6.tgz#d03af2ee5fb34691eec0cda90f5ecbb4d4da145a" - integrity sha512-wCfsbN4nBidDRhpDhvcKlzHWCTlgJYUUdSJfzXb2NuBssDSIjc3xcb+znA7l+zYsFljAcGM0aFkN40cR3lXiGA== +"@babel/helpers@^7.23.7": + version "7.23.8" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.23.8.tgz#fc6b2d65b16847fd50adddbd4232c76378959e34" + integrity sha512-KDqYz4PiOWvDFrdHLPhKtCThtIcKVy6avWD2oG4GEvyQ+XDZwHD4YQd+H2vNMnq2rkdxsDkU82T+Vk8U/WXHRQ== dependencies: "@babel/template" "^7.22.15" - "@babel/traverse" "^7.23.6" + "@babel/traverse" "^7.23.7" "@babel/types" "^7.23.6" "@babel/highlight@^7.23.4": @@ -394,10 +394,10 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" "@babel/plugin-transform-optional-chaining" "^7.23.3" -"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.23.3.tgz#20c60d4639d18f7da8602548512e9d3a4c8d7098" - integrity sha512-XaJak1qcityzrX0/IU5nKHb34VaibwP3saKqG6a/tppelgllOH13LUann4ZCIBcVOeE6H18K4Vx9QKkVww3z/w== +"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@^7.23.7": + version "7.23.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.23.7.tgz#516462a95d10a9618f197d39ad291a9b47ae1d7b" + integrity sha512-LlRT7HgaifEpQA1ZgLVOIJZZFVPWN5iReq/7/JixwBtwcoeVGDBD53ZV28rrsLYOZs1Y/EHhA8N/Z6aazHR8cw== dependencies: "@babel/helper-environment-visitor" "^7.22.20" "@babel/helper-plugin-utils" "^7.22.5" @@ -411,15 +411,12 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-proposal-decorators@^7.16.4": - version "7.23.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.23.6.tgz#b34e9837c4fb0277c6d571581c76595521cf2db4" - integrity sha512-D7Ccq9LfkBFnow3azZGJvZYgcfeqAw3I1e5LoTpj6UKIFQilh8yqXsIGcRIqbBdsPWIz+Ze7ZZfggSj62Qp+Fg== + version "7.23.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.23.7.tgz#1d827902cbd3d9054e54fb2f2056cdd1eaa0e368" + integrity sha512-b1s5JyeMvqj7d9m9KhJNHKc18gEJiSyVzVX3bwbiPalQBQpuvfPh6lA9F7Kk/dWH0TIiXRpB9yicwijY6buPng== dependencies: - "@babel/helper-create-class-features-plugin" "^7.23.6" + "@babel/helper-create-class-features-plugin" "^7.23.7" "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-replace-supers" "^7.22.20" - "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.6" "@babel/plugin-syntax-decorators" "^7.23.3" "@babel/plugin-proposal-nullish-coalescing-operator@^7.16.0": @@ -629,10 +626,10 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-async-generator-functions@^7.23.4": - version "7.23.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.4.tgz#93ac8e3531f347fba519b4703f9ff2a75c6ae27a" - integrity sha512-efdkfPhHYTtn0G6n2ddrESE91fgXxjlqLsnUtPWnJs4a4mZIbUaK7ffqKIIUKXSHwcDvaCVX6GXkaJJFqtX7jw== +"@babel/plugin-transform-async-generator-functions@^7.23.7": + version "7.23.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.7.tgz#3aa0b4f2fa3788b5226ef9346cf6d16ec61f99cd" + integrity sha512-PdxEpL71bJp1byMG0va5gwQcXHxuEYC/BgI/e88mGTtohbZN28O5Yit0Plkkm/dBzCF/BxmbNcses1RH1T+urA== dependencies: "@babel/helper-environment-visitor" "^7.22.20" "@babel/helper-plugin-utils" "^7.22.5" @@ -679,16 +676,15 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-class-static-block" "^7.14.5" -"@babel/plugin-transform-classes@^7.23.5": - version "7.23.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.5.tgz#e7a75f815e0c534cc4c9a39c56636c84fc0d64f2" - integrity sha512-jvOTR4nicqYC9yzOHIhXG5emiFEOpappSJAl73SDSEDcybD+Puuze8Tnpb9p9qEyYup24tq891gkaygIFvWDqg== +"@babel/plugin-transform-classes@^7.23.8": + version "7.23.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.8.tgz#d08ae096c240347badd68cdf1b6d1624a6435d92" + integrity sha512-yAYslGsY1bX6Knmg46RjiCiNSwJKv2IUC8qOdYKqMMr0491SXFhcHqOdRDeCRohOOIzwN/90C6mQ9qAKgrP7dg== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-compilation-targets" "^7.22.15" + "@babel/helper-compilation-targets" "^7.23.6" "@babel/helper-environment-visitor" "^7.22.20" "@babel/helper-function-name" "^7.23.0" - "@babel/helper-optimise-call-expression" "^7.22.5" "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-replace-supers" "^7.22.20" "@babel/helper-split-export-declaration" "^7.22.6" @@ -1000,15 +996,15 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-runtime@^7.16.4": - version "7.23.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.23.6.tgz#bf853cd0a675c16ee33e6ba2a63b536e75e5d754" - integrity sha512-kF1Zg62aPseQ11orDhFRw+aPG/eynNQtI+TyY+m33qJa2cJ5EEvza2P2BNTIA9E5MyqFABHEyY6CPHwgdy9aNg== + version "7.23.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.23.7.tgz#52bbd20054855beb9deae3bee9ceb05289c343e6" + integrity sha512-fa0hnfmiXc9fq/weK34MUV0drz2pOL/vfKWvN7Qw127hiUPabFCUMgAbYWcchRzMJit4o5ARsK/s+5h0249pLw== dependencies: "@babel/helper-module-imports" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" - babel-plugin-polyfill-corejs2 "^0.4.6" - babel-plugin-polyfill-corejs3 "^0.8.5" - babel-plugin-polyfill-regenerator "^0.5.3" + babel-plugin-polyfill-corejs2 "^0.4.7" + babel-plugin-polyfill-corejs3 "^0.8.7" + babel-plugin-polyfill-regenerator "^0.5.4" semver "^6.3.1" "@babel/plugin-transform-shorthand-properties@^7.23.3": @@ -1089,9 +1085,9 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/preset-env@^7.16.4", "@babel/preset-env@^7.23.5": - version "7.23.6" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.23.6.tgz#ad0ea799d5a3c07db5b9a172819bbd444092187a" - integrity sha512-2XPn/BqKkZCpzYhUUNZ1ssXw7DcXfKQEjv/uXZUXgaebCMYmkEsfZ2yY+vv+xtXv50WmL5SGhyB6/xsWxIvvOQ== + version "7.23.8" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.23.8.tgz#7d6f8171ea7c221ecd28059e65ad37c20e441e3e" + integrity sha512-lFlpmkApLkEP6woIKprO6DO60RImpatTQKtz4sUcDjVcK8M8mQ4sZsuxaTMNOZf0sqAq/ReYW1ZBHnOQwKpLWA== dependencies: "@babel/compat-data" "^7.23.5" "@babel/helper-compilation-targets" "^7.23.6" @@ -1099,7 +1095,7 @@ "@babel/helper-validator-option" "^7.23.5" "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.23.3" "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.23.3" - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly" "^7.23.3" + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly" "^7.23.7" "@babel/plugin-proposal-private-property-in-object" "7.21.0-placeholder-for-preset-env.2" "@babel/plugin-syntax-async-generators" "^7.8.4" "@babel/plugin-syntax-class-properties" "^7.12.13" @@ -1120,13 +1116,13 @@ "@babel/plugin-syntax-top-level-await" "^7.14.5" "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6" "@babel/plugin-transform-arrow-functions" "^7.23.3" - "@babel/plugin-transform-async-generator-functions" "^7.23.4" + "@babel/plugin-transform-async-generator-functions" "^7.23.7" "@babel/plugin-transform-async-to-generator" "^7.23.3" "@babel/plugin-transform-block-scoped-functions" "^7.23.3" "@babel/plugin-transform-block-scoping" "^7.23.4" "@babel/plugin-transform-class-properties" "^7.23.3" "@babel/plugin-transform-class-static-block" "^7.23.4" - "@babel/plugin-transform-classes" "^7.23.5" + "@babel/plugin-transform-classes" "^7.23.8" "@babel/plugin-transform-computed-properties" "^7.23.3" "@babel/plugin-transform-destructuring" "^7.23.3" "@babel/plugin-transform-dotall-regex" "^7.23.3" @@ -1168,9 +1164,9 @@ "@babel/plugin-transform-unicode-regex" "^7.23.3" "@babel/plugin-transform-unicode-sets-regex" "^7.23.3" "@babel/preset-modules" "0.1.6-no-external-plugins" - babel-plugin-polyfill-corejs2 "^0.4.6" - babel-plugin-polyfill-corejs3 "^0.8.5" - babel-plugin-polyfill-regenerator "^0.5.3" + babel-plugin-polyfill-corejs2 "^0.4.7" + babel-plugin-polyfill-corejs3 "^0.8.7" + babel-plugin-polyfill-regenerator "^0.5.4" core-js-compat "^3.31.0" semver "^6.3.1" @@ -1212,17 +1208,17 @@ integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== "@babel/runtime-corejs3@^7.9.2": - version "7.23.6" - resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.23.6.tgz#c25dd662fc205a03fdaefd122066eb9d4533ccf9" - integrity sha512-Djs/ZTAnpyj0nyg7p1J6oiE/tZ9G2stqAFlLGZynrW+F3k2w2jGK2mLOBxzYIOcZYA89+c3d3wXKpYLcpwcU6w== + version "7.23.8" + resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.23.8.tgz#b8aa3d47570bdd08fed77fdfd69542118af0df26" + integrity sha512-2ZzmcDugdm0/YQKFVYsXiwUN7USPX8PM7cytpb4PFl87fM+qYPSvTZX//8tyeJB1j0YDmafBJEbl5f8NfLyuKw== dependencies: core-js-pure "^3.30.2" regenerator-runtime "^0.14.0" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.5", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.16.3", "@babel/runtime@^7.17.2", "@babel/runtime@^7.17.9", "@babel/runtime@^7.18.3", "@babel/runtime@^7.18.6", "@babel/runtime@^7.21.0", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": - version "7.23.6" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.6.tgz#c05e610dc228855dc92ef1b53d07389ed8ab521d" - integrity sha512-zHd0eUrf5GZoOWVCXp6koAKQTfZV07eit6bGPmJgnZdnSAvvZee6zniW2XMF7Cmc4ISOOnPy3QaSiIJGJkVEDQ== +"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.5", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.16.3", "@babel/runtime@^7.17.2", "@babel/runtime@^7.17.9", "@babel/runtime@^7.18.3", "@babel/runtime@^7.18.6", "@babel/runtime@^7.21.0", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.4", "@babel/runtime@^7.23.6", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": + version "7.23.8" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.8.tgz#8ee6fe1ac47add7122902f257b8ddf55c898f650" + integrity sha512-Y7KbAP984rn1VGMbGqKmBLio9V7y5Je9GvU4rQPCPinCyNfUcToxIXl06d59URp/F3LwinvODxab5N/G6qggkw== dependencies: regenerator-runtime "^0.14.0" @@ -1235,10 +1231,10 @@ "@babel/parser" "^7.22.15" "@babel/types" "^7.22.15" -"@babel/traverse@^7.16.8", "@babel/traverse@^7.23.6", "@babel/traverse@^7.4.5", "@babel/traverse@^7.7.2": - version "7.23.6" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.6.tgz#b53526a2367a0dd6edc423637f3d2d0f2521abc5" - integrity sha512-czastdK1e8YByZqezMPFiZ8ahwVMh/ESl9vPgvgdB9AmFMGP5jfpFax74AQgl5zj4XHzqeYAg2l8PuUeRS1MgQ== +"@babel/traverse@^7.16.8", "@babel/traverse@^7.23.7", "@babel/traverse@^7.4.5", "@babel/traverse@^7.7.2": + version "7.23.7" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.7.tgz#9a7bf285c928cb99b5ead19c3b1ce5b310c9c305" + integrity sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg== dependencies: "@babel/code-frame" "^7.23.5" "@babel/generator" "^7.23.6" @@ -1319,12 +1315,12 @@ "@lezer/common" "^1.0.0" "@codemirror/commands@^6.0.0", "@codemirror/commands@^6.1.0": - version "6.3.2" - resolved "https://registry.yarnpkg.com/@codemirror/commands/-/commands-6.3.2.tgz#9fa47ccdacbea52fcddc6845089dfbf5be03f126" - integrity sha512-tjoi4MCWDNxgIpoLZ7+tezdS9OEB6pkiDKhfKx9ReJ/XBcs2G2RXIu+/FxXBlWsPTsz6C9q/r4gjzrsxpcnqCQ== + version "6.3.3" + resolved "https://registry.yarnpkg.com/@codemirror/commands/-/commands-6.3.3.tgz#03face5bf5f3de0fc4e09b177b3c91eda2ceb7e9" + integrity sha512-dO4hcF0fGT9tu1Pj1D2PvGvxjeGkbC6RGcZw6Qs74TH+Ed1gw98jmUgd2axWvIZEqTeTuFrg1lEB1KV6cK9h1A== dependencies: "@codemirror/language" "^6.0.0" - "@codemirror/state" "^6.2.0" + "@codemirror/state" "^6.4.0" "@codemirror/view" "^6.0.0" "@lezer/common" "^1.1.0" @@ -1337,12 +1333,12 @@ "@lezer/json" "^1.0.0" "@codemirror/language@^6.0.0": - version "6.9.3" - resolved "https://registry.yarnpkg.com/@codemirror/language/-/language-6.9.3.tgz#1c127dc43e025d4c9b1ba1b79f4b1ba081d5aeaa" - integrity sha512-qq48pYzoi6ldYWV/52+Z9Ou6QouVI+8YwvxFbUypI33NbjG2UeRHKENRyhwljTTiOqjQ33FjyZj6EREQ9apAOQ== + version "6.10.0" + resolved "https://registry.yarnpkg.com/@codemirror/language/-/language-6.10.0.tgz#2d0e818716825ee2ed0dacd04595eaa61bae8f23" + integrity sha512-2vaNn9aPGCRFKWcHPFksctzJ8yS5p7YoaT+jHpc0UGKzNuAIx4qy6R5wiqbP+heEEdyaABA582mNqSHzSoYdmg== dependencies: "@codemirror/state" "^6.0.0" - "@codemirror/view" "^6.0.0" + "@codemirror/view" "^6.23.0" "@lezer/common" "^1.1.0" "@lezer/highlight" "^1.0.0" "@lezer/lr" "^1.0.0" @@ -1366,10 +1362,10 @@ "@codemirror/view" "^6.0.0" crelt "^1.0.5" -"@codemirror/state@^6.0.0", "@codemirror/state@^6.1.1", "@codemirror/state@^6.1.4", "@codemirror/state@^6.2.0": - version "6.3.3" - resolved "https://registry.yarnpkg.com/@codemirror/state/-/state-6.3.3.tgz#6a647c2fa62b68604187152de497e91aabf43f82" - integrity sha512-0wufKcTw2dEwEaADajjHf6hBy1sh3M6V0e+q4JKIhLuiMSe5td5HOWpUdvKth1fT1M9VYOboajoBHpkCd7PG7A== +"@codemirror/state@^6.0.0", "@codemirror/state@^6.1.1", "@codemirror/state@^6.4.0": + version "6.4.0" + resolved "https://registry.yarnpkg.com/@codemirror/state/-/state-6.4.0.tgz#8bc3e096c84360b34525a84696a84f86b305363a" + integrity sha512-hm8XshYj5Fo30Bb922QX9hXB/bxOAVH+qaqHBzw5TKa72vOeslyGwd4X8M0c1dJ9JqxlaMceOQ8RsL9tC7gU0A== "@codemirror/theme-one-dark@^6.0.0": version "6.1.2" @@ -1381,12 +1377,12 @@ "@codemirror/view" "^6.0.0" "@lezer/highlight" "^1.0.0" -"@codemirror/view@^6.0.0", "@codemirror/view@^6.17.0": - version "6.22.3" - resolved "https://registry.yarnpkg.com/@codemirror/view/-/view-6.22.3.tgz#22514a0256d0fbd3e9079d7c49cb97f35593156c" - integrity sha512-rqnq+Zospwoi3x1vZ8BGV1MlRsaGljX+6qiGYmIpJ++M+LCC+wjfDaPklhwpWSgv7pr/qx29KiAKQBH5+DOn4w== +"@codemirror/view@^6.0.0", "@codemirror/view@^6.17.0", "@codemirror/view@^6.23.0": + version "6.23.0" + resolved "https://registry.yarnpkg.com/@codemirror/view/-/view-6.23.0.tgz#8054a2043273abad7f1587d15accb0623e1960ed" + integrity sha512-/51px9N4uW8NpuWkyUX+iam5+PM6io2fm+QmRnzwqBy5v/pwGg9T0kILFtYeum8hjuvENtgsGNKluOfqIICmeQ== dependencies: - "@codemirror/state" "^6.1.4" + "@codemirror/state" "^6.4.0" style-mod "^4.1.0" w3c-keyname "^2.2.4" @@ -1502,23 +1498,23 @@ integrity sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA== "@emotion/react@^11.10.5", "@emotion/react@^11.8.1": - version "11.11.1" - resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.11.1.tgz#b2c36afac95b184f73b08da8c214fdf861fa4157" - integrity sha512-5mlW1DquU5HaxjLkfkGN1GA/fvVGdyHURRiX/0FHl2cfIfRxSOfmxEH5YS43edp0OldZrZ+dkBKbngxcNCdZvA== + version "11.11.3" + resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.11.3.tgz#96b855dc40a2a55f52a72f518a41db4f69c31a25" + integrity sha512-Cnn0kuq4DoONOMcnoVsTOR8E+AdnKFf//6kUWc4LCdnxj31pZWn7rIULd6Y7/Js1PiPHzn7SKCM9vB/jBni8eA== dependencies: "@babel/runtime" "^7.18.3" "@emotion/babel-plugin" "^11.11.0" "@emotion/cache" "^11.11.0" - "@emotion/serialize" "^1.1.2" + "@emotion/serialize" "^1.1.3" "@emotion/use-insertion-effect-with-fallbacks" "^1.0.1" "@emotion/utils" "^1.2.1" "@emotion/weak-memoize" "^0.3.1" hoist-non-react-statics "^3.3.1" -"@emotion/serialize@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.1.2.tgz#017a6e4c9b8a803bd576ff3d52a0ea6fa5a62b51" - integrity sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA== +"@emotion/serialize@^1.1.2", "@emotion/serialize@^1.1.3": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.1.3.tgz#84b77bfcfe3b7bb47d326602f640ccfcacd5ffb0" + integrity sha512-iD4D6QVZFDhcbH0RAG1uVu1CwVLMWUkCvAqqlewO/rxf8+87yIBAlt4+AxMiiKPLs5hFc0owNk/sLLAOROw3cA== dependencies: "@emotion/hash" "^0.9.1" "@emotion/memoize" "^0.8.1" @@ -1573,10 +1569,10 @@ resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz#d0fce5d07b0620caa282b5131c297bb60f9d87e6" integrity sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww== -"@esbuild/aix-ppc64@0.19.10": - version "0.19.10" - resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.19.10.tgz#fb3922a0183d27446de00cf60d4f7baaadf98d84" - integrity sha512-Q+mk96KJ+FZ30h9fsJl+67IjNJm3x2eX+GBWGmocAKgzp27cowCOOqSdscX80s0SpdFXZnIv/+1xD1EctFx96Q== +"@esbuild/aix-ppc64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.19.11.tgz#2acd20be6d4f0458bc8c784103495ff24f13b1d3" + integrity sha512-FnzU0LyE3ySQk7UntJO4+qIiQgI7KoODnZg5xzXIrFJlKd2P2gwHsHY4927xj9y5PJmJSzULiUCWmv7iWnNa7g== "@esbuild/android-arm64@0.16.17": version "0.16.17" @@ -1588,10 +1584,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz#984b4f9c8d0377443cc2dfcef266d02244593622" integrity sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ== -"@esbuild/android-arm64@0.19.10": - version "0.19.10" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.19.10.tgz#ef31015416dd79398082409b77aaaa2ade4d531a" - integrity sha512-1X4CClKhDgC3by7k8aOWZeBXQX8dHT5QAMCAQDArCLaYfkppoARvh0fit3X2Qs+MXDngKcHv6XXyQCpY0hkK1Q== +"@esbuild/android-arm64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.19.11.tgz#b45d000017385c9051a4f03e17078abb935be220" + integrity sha512-aiu7K/5JnLj//KOnOfEZ0D90obUkRzDMyqd/wNAUQ34m4YUPVhRZpnqKV9uqDGxT7cToSDnIHsGooyIczu9T+Q== "@esbuild/android-arm64@0.19.2": version "0.19.2" @@ -1608,10 +1604,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.18.20.tgz#fedb265bc3a589c84cc11f810804f234947c3682" integrity sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw== -"@esbuild/android-arm@0.19.10": - version "0.19.10" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.19.10.tgz#1c23c7e75473aae9fb323be5d9db225142f47f52" - integrity sha512-7W0bK7qfkw1fc2viBfrtAEkDKHatYfHzr/jKAHNr9BvkYDXPcC6bodtm8AyLJNNuqClLNaeTLuwURt4PRT9d7w== +"@esbuild/android-arm@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.19.11.tgz#f46f55414e1c3614ac682b29977792131238164c" + integrity sha512-5OVapq0ClabvKvQ58Bws8+wkLCV+Rxg7tUVbo9xu034Nm536QTII4YzhaFriQ7rMrorfnFKUsArD2lqKbFY4vw== "@esbuild/android-arm@0.19.2": version "0.19.2" @@ -1628,10 +1624,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.18.20.tgz#35cf419c4cfc8babe8893d296cd990e9e9f756f2" integrity sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg== -"@esbuild/android-x64@0.19.10": - version "0.19.10" - resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.19.10.tgz#df6a4e6d6eb8da5595cfce16d4e3f6bc24464707" - integrity sha512-O/nO/g+/7NlitUxETkUv/IvADKuZXyH4BHf/g/7laqKC4i/7whLpB0gvpPc2zpF0q9Q6FXS3TS75QHac9MvVWw== +"@esbuild/android-x64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.19.11.tgz#bfc01e91740b82011ef503c48f548950824922b2" + integrity sha512-eccxjlfGw43WYoY9QgB82SgGgDbibcqyDTlk3l3C0jOVHKxrjdc9CTwDUQd0vkvYg5um0OH+GpxYvp39r+IPOg== "@esbuild/android-x64@0.19.2": version "0.19.2" @@ -1648,10 +1644,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz#08172cbeccf95fbc383399a7f39cfbddaeb0d7c1" integrity sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA== -"@esbuild/darwin-arm64@0.19.10": - version "0.19.10" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.19.10.tgz#8462a55db07c1b2fad61c8244ce04469ef1043be" - integrity sha512-YSRRs2zOpwypck+6GL3wGXx2gNP7DXzetmo5pHXLrY/VIMsS59yKfjPizQ4lLt5vEI80M41gjm2BxrGZ5U+VMA== +"@esbuild/darwin-arm64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.19.11.tgz#533fb7f5a08c37121d82c66198263dcc1bed29bf" + integrity sha512-ETp87DRWuSt9KdDVkqSoKoLFHYTrkyz2+65fj9nfXsaV3bMhTCjtQfw3y+um88vGRKRiF7erPrh/ZuIdLUIVxQ== "@esbuild/darwin-arm64@0.19.2": version "0.19.2" @@ -1668,10 +1664,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz#d70d5790d8bf475556b67d0f8b7c5bdff053d85d" integrity sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ== -"@esbuild/darwin-x64@0.19.10": - version "0.19.10" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.19.10.tgz#d1de20bfd41bb75b955ba86a6b1004539e8218c1" - integrity sha512-alfGtT+IEICKtNE54hbvPg13xGBe4GkVxyGWtzr+yHO7HIiRJppPDhOKq3zstTcVf8msXb/t4eavW3jCDpMSmA== +"@esbuild/darwin-x64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.19.11.tgz#62f3819eff7e4ddc656b7c6815a31cf9a1e7d98e" + integrity sha512-fkFUiS6IUK9WYUO/+22omwetaSNl5/A8giXvQlcinLIjVkxwTLSktbF5f/kJMftM2MJp9+fXqZ5ezS7+SALp4g== "@esbuild/darwin-x64@0.19.2": version "0.19.2" @@ -1688,10 +1684,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz#98755cd12707f93f210e2494d6a4b51b96977f54" integrity sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw== -"@esbuild/freebsd-arm64@0.19.10": - version "0.19.10" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.10.tgz#16904879e34c53a2e039d1284695d2db3e664d57" - integrity sha512-dMtk1wc7FSH8CCkE854GyGuNKCewlh+7heYP/sclpOG6Cectzk14qdUIY5CrKDbkA/OczXq9WesqnPl09mj5dg== +"@esbuild/freebsd-arm64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.11.tgz#d478b4195aa3ca44160272dab85ef8baf4175b4a" + integrity sha512-lhoSp5K6bxKRNdXUtHoNc5HhbXVCS8V0iZmDvyWvYq9S5WSfTIHU2UGjcGt7UeS6iEYp9eeymIl5mJBn0yiuxA== "@esbuild/freebsd-arm64@0.19.2": version "0.19.2" @@ -1708,10 +1704,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz#c1eb2bff03915f87c29cece4c1a7fa1f423b066e" integrity sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ== -"@esbuild/freebsd-x64@0.19.10": - version "0.19.10" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.19.10.tgz#8ad9e5ca9786ca3f1ef1411bfd10b08dcd9d4cef" - integrity sha512-G5UPPspryHu1T3uX8WiOEUa6q6OlQh6gNl4CO4Iw5PS+Kg5bVggVFehzXBJY6X6RSOMS8iXDv2330VzaObm4Ag== +"@esbuild/freebsd-x64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.19.11.tgz#7bdcc1917409178257ca6a1a27fe06e797ec18a2" + integrity sha512-JkUqn44AffGXitVI6/AbQdoYAq0TEullFdqcMY/PCUZ36xJ9ZJRtQabzMA+Vi7r78+25ZIBosLTOKnUXBSi1Kw== "@esbuild/freebsd-x64@0.19.2": version "0.19.2" @@ -1728,10 +1724,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz#bad4238bd8f4fc25b5a021280c770ab5fc3a02a0" integrity sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA== -"@esbuild/linux-arm64@0.19.10": - version "0.19.10" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.19.10.tgz#d82cf2c590faece82d28bbf1cfbe36f22ae25bd2" - integrity sha512-QxaouHWZ+2KWEj7cGJmvTIHVALfhpGxo3WLmlYfJ+dA5fJB6lDEIg+oe/0//FuyVHuS3l79/wyBxbHr0NgtxJQ== +"@esbuild/linux-arm64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.19.11.tgz#58ad4ff11685fcc735d7ff4ca759ab18fcfe4545" + integrity sha512-LneLg3ypEeveBSMuoa0kwMpCGmpu8XQUh+mL8XXwoYZ6Be2qBnVtcDI5azSvh7vioMDhoJFZzp9GWp9IWpYoUg== "@esbuild/linux-arm64@0.19.2": version "0.19.2" @@ -1748,10 +1744,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz#3e617c61f33508a27150ee417543c8ab5acc73b0" integrity sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg== -"@esbuild/linux-arm@0.19.10": - version "0.19.10" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.19.10.tgz#477b8e7c7bcd34369717b04dd9ee6972c84f4029" - integrity sha512-j6gUW5aAaPgD416Hk9FHxn27On28H4eVI9rJ4az7oCGTFW48+LcgNDBN+9f8rKZz7EEowo889CPKyeaD0iw9Kg== +"@esbuild/linux-arm@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.19.11.tgz#ce82246d873b5534d34de1e5c1b33026f35e60e3" + integrity sha512-3CRkr9+vCV2XJbjwgzjPtO8T0SZUmRZla+UL1jw+XqHZPkPgZiyWvbDvl9rqAN8Zl7qJF0O/9ycMtjU67HN9/Q== "@esbuild/linux-arm@0.19.2": version "0.19.2" @@ -1768,10 +1764,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz#699391cccba9aee6019b7f9892eb99219f1570a7" integrity sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA== -"@esbuild/linux-ia32@0.19.10": - version "0.19.10" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.19.10.tgz#d55ff822cf5b0252a57112f86857ff23be6cab0e" - integrity sha512-4ub1YwXxYjj9h1UIZs2hYbnTZBtenPw5NfXCRgEkGb0b6OJ2gpkMvDqRDYIDRjRdWSe/TBiZltm3Y3Q8SN1xNg== +"@esbuild/linux-ia32@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.19.11.tgz#cbae1f313209affc74b80f4390c4c35c6ab83fa4" + integrity sha512-caHy++CsD8Bgq2V5CodbJjFPEiDPq8JJmBdeyZ8GWVQMjRD0sU548nNdwPNvKjVpamYYVL40AORekgfIubwHoA== "@esbuild/linux-ia32@0.19.2": version "0.19.2" @@ -1793,10 +1789,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz#e6fccb7aac178dd2ffb9860465ac89d7f23b977d" integrity sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg== -"@esbuild/linux-loong64@0.19.10": - version "0.19.10" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.19.10.tgz#a9ad057d7e48d6c9f62ff50f6f208e331c4543c7" - integrity sha512-lo3I9k+mbEKoxtoIbM0yC/MZ1i2wM0cIeOejlVdZ3D86LAcFXFRdeuZmh91QJvUTW51bOK5W2BznGNIl4+mDaA== +"@esbuild/linux-loong64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.19.11.tgz#5f32aead1c3ec8f4cccdb7ed08b166224d4e9121" + integrity sha512-ppZSSLVpPrwHccvC6nQVZaSHlFsvCQyjnvirnVjbKSHuE5N24Yl8F3UwYUUR1UEPaFObGD2tSvVKbvR+uT1Nrg== "@esbuild/linux-loong64@0.19.2": version "0.19.2" @@ -1813,10 +1809,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz#eeff3a937de9c2310de30622a957ad1bd9183231" integrity sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ== -"@esbuild/linux-mips64el@0.19.10": - version "0.19.10" - resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.19.10.tgz#b011a96924773d60ebab396fbd7a08de66668179" - integrity sha512-J4gH3zhHNbdZN0Bcr1QUGVNkHTdpijgx5VMxeetSk6ntdt+vR1DqGmHxQYHRmNb77tP6GVvD+K0NyO4xjd7y4A== +"@esbuild/linux-mips64el@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.19.11.tgz#38eecf1cbb8c36a616261de858b3c10d03419af9" + integrity sha512-B5x9j0OgjG+v1dF2DkH34lr+7Gmv0kzX6/V0afF41FkPMMqaQ77pH7CrhWeR22aEeHKaeZVtZ6yFwlxOKPVFyg== "@esbuild/linux-mips64el@0.19.2": version "0.19.2" @@ -1833,10 +1829,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz#2f7156bde20b01527993e6881435ad79ba9599fb" integrity sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA== -"@esbuild/linux-ppc64@0.19.10": - version "0.19.10" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.19.10.tgz#5d8b59929c029811e473f2544790ea11d588d4dd" - integrity sha512-tgT/7u+QhV6ge8wFMzaklOY7KqiyitgT1AUHMApau32ZlvTB/+efeCtMk4eXS+uEymYK249JsoiklZN64xt6oQ== +"@esbuild/linux-ppc64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.19.11.tgz#9c5725a94e6ec15b93195e5a6afb821628afd912" + integrity sha512-MHrZYLeCG8vXblMetWyttkdVRjQlQUb/oMgBNurVEnhj4YWOr4G5lmBfZjHYQHHN0g6yDmCAQRR8MUHldvvRDA== "@esbuild/linux-ppc64@0.19.2": version "0.19.2" @@ -1853,10 +1849,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz#6628389f210123d8b4743045af8caa7d4ddfc7a6" integrity sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A== -"@esbuild/linux-riscv64@0.19.10": - version "0.19.10" - resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.19.10.tgz#292b06978375b271bd8bc0a554e0822957508d22" - integrity sha512-0f/spw0PfBMZBNqtKe5FLzBDGo0SKZKvMl5PHYQr3+eiSscfJ96XEknCe+JoOayybWUFQbcJTrk946i3j9uYZA== +"@esbuild/linux-riscv64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.19.11.tgz#2dc4486d474a2a62bbe5870522a9a600e2acb916" + integrity sha512-f3DY++t94uVg141dozDu4CCUkYW+09rWtaWfnb3bqe4w5NqmZd6nPVBm+qbz7WaHZCoqXqHz5p6CM6qv3qnSSQ== "@esbuild/linux-riscv64@0.19.2": version "0.19.2" @@ -1873,10 +1869,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz#255e81fb289b101026131858ab99fba63dcf0071" integrity sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ== -"@esbuild/linux-s390x@0.19.10": - version "0.19.10" - resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.19.10.tgz#d30af63530f8d4fa96930374c9dd0d62bf59e069" - integrity sha512-pZFe0OeskMHzHa9U38g+z8Yx5FNCLFtUnJtQMpwhS+r4S566aK2ci3t4NCP4tjt6d5j5uo4h7tExZMjeKoehAA== +"@esbuild/linux-s390x@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.19.11.tgz#4ad8567df48f7dd4c71ec5b1753b6f37561a65a8" + integrity sha512-A5xdUoyWJHMMlcSMcPGVLzYzpcY8QP1RtYzX5/bS4dvjBGVxdhuiYyFwp7z74ocV7WDc0n1harxmpq2ePOjI0Q== "@esbuild/linux-s390x@0.19.2": version "0.19.2" @@ -1893,10 +1889,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz#c7690b3417af318a9b6f96df3031a8865176d338" integrity sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w== -"@esbuild/linux-x64@0.19.10": - version "0.19.10" - resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.19.10.tgz#898c72eeb74d9f2fb43acf316125b475548b75ce" - integrity sha512-SpYNEqg/6pZYoc+1zLCjVOYvxfZVZj6w0KROZ3Fje/QrM3nfvT2llI+wmKSrWuX6wmZeTapbarvuNNK/qepSgA== +"@esbuild/linux-x64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.19.11.tgz#b7390c4d5184f203ebe7ddaedf073df82a658766" + integrity sha512-grbyMlVCvJSfxFQUndw5mCtWs5LO1gUlwP4CDi4iJBbVpZcqLVT29FxgGuBJGSzyOxotFG4LoO5X+M1350zmPA== "@esbuild/linux-x64@0.19.2": version "0.19.2" @@ -1913,10 +1909,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz#30e8cd8a3dded63975e2df2438ca109601ebe0d1" integrity sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A== -"@esbuild/netbsd-x64@0.19.10": - version "0.19.10" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.19.10.tgz#fd473a5ae261b43eab6dad4dbd5a3155906e6c91" - integrity sha512-ACbZ0vXy9zksNArWlk2c38NdKg25+L9pr/mVaj9SUq6lHZu/35nx2xnQVRGLrC1KKQqJKRIB0q8GspiHI3J80Q== +"@esbuild/netbsd-x64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.19.11.tgz#d633c09492a1721377f3bccedb2d821b911e813d" + integrity sha512-13jvrQZJc3P230OhU8xgwUnDeuC/9egsjTkXN49b3GcS5BKvJqZn86aGM8W9pd14Kd+u7HuFBMVtrNGhh6fHEQ== "@esbuild/netbsd-x64@0.19.2": version "0.19.2" @@ -1933,10 +1929,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz#7812af31b205055874c8082ea9cf9ab0da6217ae" integrity sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg== -"@esbuild/openbsd-x64@0.19.10": - version "0.19.10" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.19.10.tgz#96eb8992e526717b5272321eaad3e21f3a608e46" - integrity sha512-PxcgvjdSjtgPMiPQrM3pwSaG4kGphP+bLSb+cihuP0LYdZv1epbAIecHVl5sD3npkfYBZ0ZnOjR878I7MdJDFg== +"@esbuild/openbsd-x64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.19.11.tgz#17388c76e2f01125bf831a68c03a7ffccb65d1a2" + integrity sha512-ysyOGZuTp6SNKPE11INDUeFVVQFrhcNDVUgSQVDzqsqX38DjhPEPATpid04LCoUr2WXhQTEZ8ct/EgJCUDpyNw== "@esbuild/openbsd-x64@0.19.2": version "0.19.2" @@ -1953,10 +1949,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz#d5c275c3b4e73c9b0ecd38d1ca62c020f887ab9d" integrity sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ== -"@esbuild/sunos-x64@0.19.10": - version "0.19.10" - resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.19.10.tgz#c16ee1c167f903eaaa6acf7372bee42d5a89c9bc" - integrity sha512-ZkIOtrRL8SEJjr+VHjmW0znkPs+oJXhlJbNwfI37rvgeMtk3sxOQevXPXjmAPZPigVTncvFqLMd+uV0IBSEzqA== +"@esbuild/sunos-x64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.19.11.tgz#e320636f00bb9f4fdf3a80e548cb743370d41767" + integrity sha512-Hf+Sad9nVwvtxy4DXCZQqLpgmRTQqyFyhT3bZ4F2XlJCjxGmRFF0Shwn9rzhOYRB61w9VMXUkxlBy56dk9JJiQ== "@esbuild/sunos-x64@0.19.2": version "0.19.2" @@ -1973,10 +1969,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz#73bc7f5a9f8a77805f357fab97f290d0e4820ac9" integrity sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg== -"@esbuild/win32-arm64@0.19.10": - version "0.19.10" - resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.19.10.tgz#7e417d1971dbc7e469b4eceb6a5d1d667b5e3dcc" - integrity sha512-+Sa4oTDbpBfGpl3Hn3XiUe4f8TU2JF7aX8cOfqFYMMjXp6ma6NJDztl5FDG8Ezx0OjwGikIHw+iA54YLDNNVfw== +"@esbuild/win32-arm64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.19.11.tgz#c778b45a496e90b6fc373e2a2bb072f1441fe0ee" + integrity sha512-0P58Sbi0LctOMOQbpEOvOL44Ne0sqbS0XWHMvvrg6NE5jQ1xguCSSw9jQeUk2lfrXYsKDdOe6K+oZiwKPilYPQ== "@esbuild/win32-arm64@0.19.2": version "0.19.2" @@ -1993,10 +1989,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz#ec93cbf0ef1085cc12e71e0d661d20569ff42102" integrity sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g== -"@esbuild/win32-ia32@0.19.10": - version "0.19.10" - resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.19.10.tgz#2b52dfec6cd061ecb36171c13bae554888b439e5" - integrity sha512-EOGVLK1oWMBXgfttJdPHDTiivYSjX6jDNaATeNOaCOFEVcfMjtbx7WVQwPSE1eIfCp/CaSF2nSrDtzc4I9f8TQ== +"@esbuild/win32-ia32@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.19.11.tgz#481a65fee2e5cce74ec44823e6b09ecedcc5194c" + integrity sha512-6YOrWS+sDJDmshdBIQU+Uoyh7pQKrdykdefC1avn76ss5c+RN6gut3LZA4E2cH5xUEp5/cA0+YxRaVtRAb0xBg== "@esbuild/win32-ia32@0.19.2": version "0.19.2" @@ -2013,10 +2009,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz#786c5f41f043b07afb1af37683d7c33668858f6d" integrity sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ== -"@esbuild/win32-x64@0.19.10": - version "0.19.10" - resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.19.10.tgz#bd123a74f243d2f3a1f046447bb9b363ee25d072" - integrity sha512-whqLG6Sc70AbU73fFYvuYzaE4MNMBIlR1Y/IrUeOXFrWHxBEjjbZaQ3IXIQS8wJdAzue2GwYZCjOrgrU1oUHoA== +"@esbuild/win32-x64@0.19.11": + version "0.19.11" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.19.11.tgz#a5d300008960bb39677c46bf16f53ec70d8dee04" + integrity sha512-vfkhltrjCAb603XaFhqhAF4LGDi2M4OrCRrFusyQ+iTLQ/o60QQXxc9cZC/FFpihBI9N1Grn6SMKVJ4KP7Fuiw== "@esbuild/win32-x64@0.19.2": version "0.19.2" @@ -2100,7 +2096,7 @@ "@ethersproject/properties" "^5.0.3" "@ethersproject/strings" "^5.0.4" -"@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.0.9", "@ethersproject/abi@^5.1.2", "@ethersproject/abi@^5.4.7", "@ethersproject/abi@^5.5.0", "@ethersproject/abi@^5.6.3", "@ethersproject/abi@^5.7.0": +"@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.0.9", "@ethersproject/abi@^5.1.2", "@ethersproject/abi@^5.5.0", "@ethersproject/abi@^5.6.3", "@ethersproject/abi@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449" integrity sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA== @@ -2291,7 +2287,7 @@ dependencies: "@ethersproject/logger" "^5.7.0" -"@ethersproject/providers@5.7.2", "@ethersproject/providers@^5.4.7", "@ethersproject/providers@^5.7.1", "@ethersproject/providers@^5.7.2": +"@ethersproject/providers@5.7.2", "@ethersproject/providers@^5.7.1", "@ethersproject/providers@^5.7.2": version "5.7.2" resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.7.2.tgz#f8b1a4f275d7ce58cf0a2eec222269a08beb18cb" integrity sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg== @@ -2457,32 +2453,32 @@ graphql-import-node "^0.0.5" js-yaml "^4.1.0" -"@floating-ui/core@^1.4.2": - version "1.5.2" - resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.5.2.tgz#53a0f7a98c550e63134d504f26804f6b83dbc071" - integrity sha512-Ii3MrfY/GAIN3OhXNzpCKaLxHQfJF9qvwq/kEJYdqDxeIHa01K8sldugal6TmeeXl+WMvhv9cnVzUTaFFJF09A== +"@floating-ui/core@^1.5.3": + version "1.5.3" + resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.5.3.tgz#b6aa0827708d70971c8679a16cf680a515b8a52a" + integrity sha512-O0WKDOo0yhJuugCx6trZQj5jVJ9yR0ystG2JaNAemYUWce+pmM6WUEFIibnWyEJKdrDxhm75NoSRME35FNaM/Q== dependencies: - "@floating-ui/utils" "^0.1.3" + "@floating-ui/utils" "^0.2.0" -"@floating-ui/dom@^1.0.1", "@floating-ui/dom@^1.5.1": - version "1.5.3" - resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.5.3.tgz#54e50efcb432c06c23cd33de2b575102005436fa" - integrity sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA== +"@floating-ui/dom@^1.0.1", "@floating-ui/dom@^1.5.4": + version "1.5.4" + resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.5.4.tgz#28df1e1cb373884224a463235c218dcbd81a16bb" + integrity sha512-jByEsHIY+eEdCjnTVu+E3ephzTOzkQ8hgUfGwos+bg7NlH33Zc5uO+QHz1mrQUOgIKKDD1RtS201P9NvAfq3XQ== dependencies: - "@floating-ui/core" "^1.4.2" - "@floating-ui/utils" "^0.1.3" + "@floating-ui/core" "^1.5.3" + "@floating-ui/utils" "^0.2.0" "@floating-ui/react-dom@^2.0.0", "@floating-ui/react-dom@^2.0.2", "@floating-ui/react-dom@^2.0.4": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.0.4.tgz#b076fafbdfeb881e1d86ae748b7ff95150e9f3ec" - integrity sha512-CF8k2rgKeh/49UrnIBs4BdxPUV6vize/Db1d/YbCLyp9GiVZ0BEwf5AiDSxJRCr6yOkGqTFHtmrULxkEfYZ7dQ== + version "2.0.5" + resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.0.5.tgz#851522899c34e3e2be1e29f3294f150834936e28" + integrity sha512-UsBK30Bg+s6+nsgblXtZmwHhgS2vmbuQK22qgt2pTQM6M3X6H1+cQcLXqgRY3ihVLcZJE6IvqDQozhsnIVqK/Q== dependencies: - "@floating-ui/dom" "^1.5.1" + "@floating-ui/dom" "^1.5.4" -"@floating-ui/utils@^0.1.3": - version "0.1.6" - resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.1.6.tgz#22958c042e10b67463997bd6ea7115fe28cbcaf9" - integrity sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A== +"@floating-ui/utils@^0.2.0": + version "0.2.1" + resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.1.tgz#16308cea045f0fc777b6ff20a9f25474dd8293d2" + integrity sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q== "@formatjs/ecma402-abstract@1.14.3": version "1.14.3" @@ -2858,16 +2854,16 @@ integrity sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw== "@internationalized/date@^3.5.0": - version "3.5.0" - resolved "https://registry.yarnpkg.com/@internationalized/date/-/date-3.5.0.tgz#67f1dd62355f05140cc80e324842e9bfb4553abe" - integrity sha512-nw0Q+oRkizBWMioseI8+2TeUPEyopJVz5YxoYVzR0W1v+2YytiYah7s/ot35F149q/xAg4F1gT/6eTd+tsUpFQ== + version "3.5.1" + resolved "https://registry.yarnpkg.com/@internationalized/date/-/date-3.5.1.tgz#14401139f70c1ef14b845d3cac8912e82e82adcc" + integrity sha512-LUQIfwU9e+Fmutc/DpRTGXSdgYZLBegi4wygCWDSVmUdLTaMHsQyASDiJtREwanwKuQLq0hY76fCJ9J/9I2xOQ== dependencies: "@swc/helpers" "^0.5.0" "@internationalized/number@^3.3.0": - version "3.4.0" - resolved "https://registry.yarnpkg.com/@internationalized/number/-/number-3.4.0.tgz#1c3ebf6ac40ce649d3d97bb835ff0559957f2e1f" - integrity sha512-8TvotW3qVDHC4uv/BVoN6Qx0Dm8clHY1/vpH+dh+XRiPW/9NVpKn1P8d1A+WLphWrMwyqyWXI7uWehJPviaeIw== + version "3.5.0" + resolved "https://registry.yarnpkg.com/@internationalized/number/-/number-3.5.0.tgz#9de6018424b441a6545f209afa286ad7df4a2906" + integrity sha512-ZY1BW8HT9WKYvaubbuqXbbDdHhOUMfE2zHHFJeTppid0S+pc8HtdIxFxaYMsGjCb4UsF+MEJ4n2TfU7iHnUK8w== dependencies: "@swc/helpers" "^0.5.0" @@ -2916,6 +2912,18 @@ dependencies: multiformats "^9.5.4" +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" @@ -3333,7 +3341,7 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.9": +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.9": version "0.3.20" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz#72e45707cf240fa6b081d0366f8265b0cd10197f" integrity sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q== @@ -3369,10 +3377,10 @@ resolved "https://registry.yarnpkg.com/@ledgerhq/connect-kit-loader/-/connect-kit-loader-1.1.8.tgz#6cc32191660dd9d6e8f89047af09b0f201e30190" integrity sha512-mDJsOucVW8m3Lk2fdQst+P74SgiKebvq1iBk4sXLbADQOwhL9bWGaArvO+tW7jPJZwEfSPWBdHcHoYi11XAwZw== -"@lezer/common@^1.0.0", "@lezer/common@^1.1.0": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@lezer/common/-/common-1.1.2.tgz#2fc5cd6788094ffc816b539ab2bc55bafacd2abc" - integrity sha512-V+GqBsga5+cQJMfM0GdnHmg4DgWvLzgMWjbldBg0+jC3k9Gu6nJNZDLJxXEBT1Xj8KhRN4jmbC5CY7SIL++sVw== +"@lezer/common@^1.0.0", "@lezer/common@^1.1.0", "@lezer/common@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@lezer/common/-/common-1.2.0.tgz#f10493d12c4a196a02ff5fcf5695a516a4039aae" + integrity sha512-Wmvlm4q6tRpwiy20TnB3yyLTZim38Tkc50dPY8biQRwqE+ati/wD84rm3N15hikvdT4uSg9phs9ubjvcLmkpKg== "@lezer/highlight@^1.0.0": version "1.2.0" @@ -3382,10 +3390,11 @@ "@lezer/common" "^1.0.0" "@lezer/json@^1.0.0": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@lezer/json/-/json-1.0.1.tgz#3bf5641f3d1408ec31a5f9b29e4e96c6e3a232e6" - integrity sha512-nkVC27qiEZEjySbi6gQRuMwa2sDu2PtfjSgz0A4QF81QyRGm3kb2YRzLcOPcTEtmcwvrX/cej7mlhbwViA4WJw== + version "1.0.2" + resolved "https://registry.yarnpkg.com/@lezer/json/-/json-1.0.2.tgz#bdc849e174113e2d9a569a5e6fb1a27e2f703eaf" + integrity sha512-xHT2P4S5eeCYECyKNPhr4cbEL9tc8w83SPwRC373o9uEdrvGKTZoJVAGxpOsZckMlEh9W23Pc72ew918RWQOBQ== dependencies: + "@lezer/common" "^1.2.0" "@lezer/highlight" "^1.0.0" "@lezer/lr" "^1.0.0" @@ -3523,55 +3532,55 @@ "@motionone/dom" "^10.16.4" tslib "^2.3.1" -"@mui/base@5.0.0-beta.28", "@mui/base@^5.0.0-beta.22": - version "5.0.0-beta.28" - resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-beta.28.tgz#f072e55c0530f456ee5cb5cde2af788fdda3bf05" - integrity sha512-KIoSc5sUFceeCaZTq5MQBapFzhHqMo4kj+4azWaCAjorduhcRQtN+BCgVHmo+gvEjix74bUfxwTqGifnu2fNTg== +"@mui/base@5.0.0-beta.30", "@mui/base@^5.0.0-beta.22": + version "5.0.0-beta.30" + resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-beta.30.tgz#8feca6b70f2b9cd4d5cb97799ae9fcb5376c7f83" + integrity sha512-dc38W4W3K42atE9nSaOeoJ7/x9wGIfawdwC/UmMxMLlZ1iSsITQ8dQJaTATCbn98YvYPINK/EH541YA5enQIPQ== dependencies: - "@babel/runtime" "^7.23.5" + "@babel/runtime" "^7.23.6" "@floating-ui/react-dom" "^2.0.4" - "@mui/types" "^7.2.11" - "@mui/utils" "^5.15.1" + "@mui/types" "^7.2.12" + "@mui/utils" "^5.15.3" "@popperjs/core" "^2.11.8" clsx "^2.0.0" prop-types "^15.8.1" -"@mui/core-downloads-tracker@^5.15.1": - version "5.15.1" - resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.1.tgz#8aad47e2b198640244f05f6486a927ce362e814e" - integrity sha512-y/nUEsWHyBzaKYp9zLtqJKrLod/zMNEWpMj488FuQY9QTmqBiyUhI2uh7PVaLqLewXRtdmG6JV0b6T5exyuYRw== +"@mui/core-downloads-tracker@^5.15.3": + version "5.15.3" + resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.3.tgz#40fc854d7cf5505a182a4e121149dfe21cd277ef" + integrity sha512-sWeihiVyxdJjpLkp8SHkTy9kt2M/o11M60G1MzwljGL2BXdM3Ktzqv5QaQHdi00y7Y1ulvtI3GOSxP2xU8mQJw== "@mui/icons-material@^5.14.14": - version "5.15.1" - resolved "https://registry.yarnpkg.com/@mui/icons-material/-/icons-material-5.15.1.tgz#38f51a88d224a107e753313b4d9815247caa5398" - integrity sha512-VPJdBSyap6uOxCb5BLbWbkvd6aeJCp1pQZm8DcZBITCH0NOSv8Mz9c8Zvo8xr4Od7+xyWHUAgvRSL4047pL2WQ== + version "5.15.3" + resolved "https://registry.yarnpkg.com/@mui/icons-material/-/icons-material-5.15.3.tgz#eee07582ea3ad913982b7843ff1612d0fad21278" + integrity sha512-7LEs8AnO2Se/XYH+CcJndRsGAE+M8KAExiiQHf0V11poqmPVGcbbY82Ry2IUYf9+rOilCVnWI18ErghZ625BPQ== dependencies: - "@babel/runtime" "^7.23.5" + "@babel/runtime" "^7.23.6" "@mui/lab@^5.0.0-alpha.141": - version "5.0.0-alpha.157" - resolved "https://registry.yarnpkg.com/@mui/lab/-/lab-5.0.0-alpha.157.tgz#cb248823ffd881153eef86c2e3786c35601d1986" - integrity sha512-gY7UM2kNSxiVLfsm0o6HG2G5rM2Vr47prJhDCazY+VG/NOSRc8CG7la6dpL9WDTJhotEZdWwfj1FOUxTonmuQA== - dependencies: - "@babel/runtime" "^7.23.5" - "@mui/base" "5.0.0-beta.28" - "@mui/system" "^5.15.1" - "@mui/types" "^7.2.11" - "@mui/utils" "^5.15.1" + version "5.0.0-alpha.159" + resolved "https://registry.yarnpkg.com/@mui/lab/-/lab-5.0.0-alpha.159.tgz#d2b97b3e6b1b51d11d85f46096618490fe6d4e67" + integrity sha512-42Y8nf2/mDgYSLOw6PhOfHNV6P7tPcQkQEL0DTbY7a+gc+hXDsyVEzBMYST1MrV64EHTH68msfQm+k3CvLON/g== + dependencies: + "@babel/runtime" "^7.23.6" + "@mui/base" "5.0.0-beta.30" + "@mui/system" "^5.15.3" + "@mui/types" "^7.2.12" + "@mui/utils" "^5.15.3" clsx "^2.0.0" prop-types "^15.8.1" "@mui/material@^5.14.14": - version "5.15.1" - resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.15.1.tgz#5fc15c6eb9efe4b62b0c30b13bf5fa042bda71a1" - integrity sha512-WA5DVyvacxDakVyAhNqu/rRT28ppuuUFFw1bLpmRzrCJ4uw/zLTATcd4WB3YbB+7MdZNEGG/SJNWTDLEIyn3xQ== - dependencies: - "@babel/runtime" "^7.23.5" - "@mui/base" "5.0.0-beta.28" - "@mui/core-downloads-tracker" "^5.15.1" - "@mui/system" "^5.15.1" - "@mui/types" "^7.2.11" - "@mui/utils" "^5.15.1" + version "5.15.3" + resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.15.3.tgz#b77f1ac1275e5bf13b735e8224bdd301aab918c4" + integrity sha512-DODBBMouyq1B5f3YkEWL9vO8pGCxuEGqtfpltF6peMJzz/78tJFyLQsDas9MNLC/8AdFu2BQdkK7wox5UBPTAA== + dependencies: + "@babel/runtime" "^7.23.6" + "@mui/base" "5.0.0-beta.30" + "@mui/core-downloads-tracker" "^5.15.3" + "@mui/system" "^5.15.3" + "@mui/types" "^7.2.12" + "@mui/utils" "^5.15.3" "@types/react-transition-group" "^4.4.10" clsx "^2.0.0" csstype "^3.1.2" @@ -3579,58 +3588,58 @@ react-is "^18.2.0" react-transition-group "^4.4.5" -"@mui/private-theming@^5.15.1": - version "5.15.1" - resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.15.1.tgz#58fd8da48295e105067fa7361734ee0b166d9cca" - integrity sha512-wTbzuy5KjSvCPE9UVJktWHJ0b/tD5biavY9wvF+OpYDLPpdXK52vc1hTDxSbdkHIFMkJExzrwO9GvpVAHZBnFQ== +"@mui/private-theming@^5.15.3": + version "5.15.3" + resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.15.3.tgz#2db0177d847dc6b28721d93308ed05d434a77c53" + integrity sha512-Q79MhVMmywC1l5bMsMZq5PsIudr1MNPJnx9/EqdMP0vpz5iNvFpnLmxsD7d8/hqTWgFAljI+LH3jX8MxlZH9Gw== dependencies: - "@babel/runtime" "^7.23.5" - "@mui/utils" "^5.15.1" + "@babel/runtime" "^7.23.6" + "@mui/utils" "^5.15.3" prop-types "^15.8.1" -"@mui/styled-engine@^5.15.1": - version "5.15.1" - resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-5.15.1.tgz#00f179e51afe252022bf356f72354968f9c5bf25" - integrity sha512-7WDZTJLqGexWDjqE9oAgjU8ak6hEtUw2yQU7SIYID5kLVO2Nj/Wi/KicbLsXnTsJNvSqePIlUIWTBSXwWJCPZw== +"@mui/styled-engine@^5.15.3": + version "5.15.3" + resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-5.15.3.tgz#85cb294d701b1a3f197bfc90e87ec0685a0943b2" + integrity sha512-+d5XZCTeemOO/vBfWGEeHgTm8fjU1Psdgm+xAw+uegycO2EnoA/EfGSaG5UwZ6g3b66y48Mkxi35AggShMr88w== dependencies: - "@babel/runtime" "^7.23.5" + "@babel/runtime" "^7.23.6" "@emotion/cache" "^11.11.0" csstype "^3.1.2" prop-types "^15.8.1" -"@mui/system@^5.15.1": - version "5.15.1" - resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.15.1.tgz#e2a79b5e188ca89a3e58aa4d27e3484edf9e24b0" - integrity sha512-LAnP0ls69rqW9eBgI29phIx/lppv+WDGI7b3EJN7VZIqw0RezA0GD7NRpV12BgEYJABEii6z5Q9B5tg7dsX0Iw== +"@mui/system@^5.15.3": + version "5.15.3" + resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.15.3.tgz#062d0d6b5259c3dc0e1d4026b85ffcc3acf8637b" + integrity sha512-ewVU4eRgo4VfNMGpO61cKlfWmH7l9s6rA8EknRzuMX3DbSLfmtW2WJJg6qPwragvpPIir0Pp/AdWVSDhyNy5Tw== dependencies: - "@babel/runtime" "^7.23.5" - "@mui/private-theming" "^5.15.1" - "@mui/styled-engine" "^5.15.1" - "@mui/types" "^7.2.11" - "@mui/utils" "^5.15.1" + "@babel/runtime" "^7.23.6" + "@mui/private-theming" "^5.15.3" + "@mui/styled-engine" "^5.15.3" + "@mui/types" "^7.2.12" + "@mui/utils" "^5.15.3" clsx "^2.0.0" csstype "^3.1.2" prop-types "^15.8.1" -"@mui/types@^7.2.11": - version "7.2.11" - resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.2.11.tgz#36b99a88f8010dc716128e568dc05681a69dc7ae" - integrity sha512-KWe/QTEsFFlFSH+qRYf3zoFEj3z67s+qAuSnMMg+gFwbxG7P96Hm6g300inQL1Wy///gSRb8juX7Wafvp93m3w== +"@mui/types@^7.2.12": + version "7.2.12" + resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.2.12.tgz#602acbb5aa3eb56a31f569a19f87f75d33de5c01" + integrity sha512-3kaHiNm9khCAo0pVe0RenketDSFoZGAlVZ4zDjB/QNZV0XiCj+sh1zkX0VVhQPgYJDlBEzAag+MHJ1tU3vf0Zw== -"@mui/utils@^5.14.16", "@mui/utils@^5.15.1": - version "5.15.1" - resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.15.1.tgz#71d69dc8c0f13a1fd6aca20b53ec496636e6b854" - integrity sha512-V1/d0E3Bju5YdB59HJf2G0tnHrFEvWLN+f8hAXp9+JSNy/LC2zKyqUfPPahflR6qsI681P8G9r4mEZte/SrrYA== +"@mui/utils@^5.14.16", "@mui/utils@^5.15.3": + version "5.15.3" + resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.15.3.tgz#421043be5279d31ca9b221a6398feb7c9d61209b" + integrity sha512-mT3LiSt9tZWCdx1pl7q4Q5tNo6gdZbvJel286ZHGuj6LQQXjWNAh8qiF9d+LogvNUI+D7eLkTnj605d1zoazfg== dependencies: - "@babel/runtime" "^7.23.5" + "@babel/runtime" "^7.23.6" "@types/prop-types" "^15.7.11" prop-types "^15.8.1" react-is "^18.2.0" "@mui/x-date-pickers@^6.18.6": - version "6.18.6" - resolved "https://registry.yarnpkg.com/@mui/x-date-pickers/-/x-date-pickers-6.18.6.tgz#416e0b83dd2774547e3c864c89bedf2f4ca3e05a" - integrity sha512-pqOrGPUDVY/1xXrM1hofqwgquno/SB9aG9CVS1m2Rs8hKF1VWRC+jYlEa1Qk08xKmvkia5g7NsdV/BBb+tHUZw== + version "6.18.7" + resolved "https://registry.yarnpkg.com/@mui/x-date-pickers/-/x-date-pickers-6.18.7.tgz#6b00163c77dc450c11b44a479baf62541e6f8b36" + integrity sha512-4NoapaCT3jvEk2cuAUjG0ReZvTEk1i4dGDz94Gt1Oc08GuC1AuzYRwCR1/1tdmbDynwkR8ilkKL6AyS3NL1H4A== dependencies: "@babel/runtime" "^7.23.2" "@mui/base" "^5.0.0-beta.22" @@ -3850,7 +3859,7 @@ resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39" integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ== -"@noble/hashes@1.3.3", "@noble/hashes@^1.3.1", "@noble/hashes@~1.3.0", "@noble/hashes@~1.3.1", "@noble/hashes@~1.3.2": +"@noble/hashes@1.3.3", "@noble/hashes@^1.3.2", "@noble/hashes@~1.3.0", "@noble/hashes@~1.3.1", "@noble/hashes@~1.3.2": version "1.3.3" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.3.tgz#39908da56a4adc270147bb07968bf3b16cfe1699" integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA== @@ -4015,18 +4024,17 @@ mcl-wasm "^0.7.1" rustbn.js "~0.2.0" -"@nomicfoundation/hardhat-chai-matchers@^1.0.0", "@nomicfoundation/hardhat-chai-matchers@^1.0.5": - version "1.0.6" - resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-chai-matchers/-/hardhat-chai-matchers-1.0.6.tgz#72a2e312e1504ee5dd73fe302932736432ba96bc" - integrity sha512-f5ZMNmabZeZegEfuxn/0kW+mm7+yV7VNDxLpMOMGXWFJ2l/Ct3QShujzDRF9cOkK9Ui/hbDeOWGZqyQALDXVCQ== +"@nomicfoundation/hardhat-chai-matchers@^2.0.3": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-chai-matchers/-/hardhat-chai-matchers-2.0.3.tgz#f4c074d39b74bd283c99e2c2bf143e3cef51ae18" + integrity sha512-A40s7EAK4Acr8UP1Yudgi9GGD9Cca/K3LHt3DzmRIje14lBfHtg9atGQ7qK56vdPcTwKmeaGn30FzxMUfPGEMw== dependencies: - "@ethersproject/abi" "^5.1.2" "@types/chai-as-promised" "^7.1.3" chai-as-promised "^7.1.1" deep-eql "^4.0.1" ordinal "^1.0.3" -"@nomicfoundation/hardhat-ethers@^3.0.4": +"@nomicfoundation/hardhat-ethers@^3.0.4", "@nomicfoundation/hardhat-ethers@^3.0.5": version "3.0.5" resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-ethers/-/hardhat-ethers-3.0.5.tgz#0422c2123dec7c42e7fb2be8e1691f1d9708db56" integrity sha512-RNFe8OtbZK6Ila9kIlHp0+S80/0Bu/3p41HUpaRIoHLm6X3WekTd83vob3rE54Duufu1edCiBDxspBzi2rxHHw== @@ -4034,17 +4042,32 @@ debug "^4.1.1" lodash.isequal "^4.5.0" -"@nomicfoundation/hardhat-network-helpers@^1.0.0", "@nomicfoundation/hardhat-network-helpers@^1.0.7": +"@nomicfoundation/hardhat-network-helpers@^1.0.10": version "1.0.10" resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-network-helpers/-/hardhat-network-helpers-1.0.10.tgz#c61042ceb104fdd6c10017859fdef6529c1d6585" integrity sha512-R35/BMBlx7tWN5V6d/8/19QCwEmIdbnA4ZrsuXgvs8i2qFx5i7h6mH5pBS4Pwi4WigLH+upl6faYusrNPuzMrQ== dependencies: ethereumjs-util "^7.1.4" -"@nomicfoundation/hardhat-toolbox@^2.0.1", "@nomicfoundation/hardhat-toolbox@^2.0.2": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-toolbox/-/hardhat-toolbox-2.0.2.tgz#ec95f23b53cb4e71a1a7091380fa223aad18f156" - integrity sha512-vnN1AzxbvpSx9pfdRHbUzTRIXpMLPXnUlkW855VaDk6N1pwRaQ2gNzEmFAABk4lWf11E00PKwFd/q27HuwYrYg== +"@nomicfoundation/hardhat-toolbox@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-toolbox/-/hardhat-toolbox-4.0.0.tgz#eb1f619218dd1414fa161dfec92d3e5e53a2f407" + integrity sha512-jhcWHp0aHaL0aDYj8IJl80v4SZXWMS1A2XxXa1CA6pBiFfJKuZinCkO6wb+POAt0LIfXB3gA3AgdcOccrcwBwA== + +"@nomicfoundation/hardhat-verify@^2.0.3": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-verify/-/hardhat-verify-2.0.3.tgz#173557f8cfa53c8c9da23a326f54d24fe459ae68" + integrity sha512-ESbRu9by53wu6VvgwtMtm108RSmuNsVqXtzg061D+/4R7jaWh/Wl/8ve+p6SdDX7vA1Z3L02hDO1Q3BY4luLXQ== + dependencies: + "@ethersproject/abi" "^5.1.2" + "@ethersproject/address" "^5.0.2" + cbor "^8.1.0" + chalk "^2.4.2" + debug "^4.1.1" + lodash.clonedeep "^4.5.0" + semver "^6.3.0" + table "^6.8.0" + undici "^5.14.0" "@nomicfoundation/solidity-analyzer-darwin-arm64@0.1.1": version "0.1.1" @@ -4112,27 +4135,6 @@ "@nomicfoundation/solidity-analyzer-win32-ia32-msvc" "0.1.1" "@nomicfoundation/solidity-analyzer-win32-x64-msvc" "0.1.1" -"@nomiclabs/hardhat-ethers@^2.0.0", "@nomiclabs/hardhat-ethers@^2.2.2": - version "2.2.3" - resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-ethers/-/hardhat-ethers-2.2.3.tgz#b41053e360c31a32c2640c9a45ee981a7e603fe0" - integrity sha512-YhzPdzb612X591FOe68q+qXVXGG2ANZRvDo0RRUtimev85rCrAlv/TLMEZw5c+kq9AbzocLTVX/h2jVIFPL9Xg== - -"@nomiclabs/hardhat-etherscan@^3.0.0", "@nomiclabs/hardhat-etherscan@^3.1.2": - version "3.1.8" - resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-etherscan/-/hardhat-etherscan-3.1.8.tgz#3c12ee90b3733e0775e05111146ef9418d4f5a38" - integrity sha512-v5F6IzQhrsjHh6kQz4uNrym49brK9K5bYCq2zQZ729RYRaifI9hHbtmK+KkIVevfhut7huQFEQ77JLRMAzWYjQ== - dependencies: - "@ethersproject/abi" "^5.1.2" - "@ethersproject/address" "^5.0.2" - cbor "^8.1.0" - chalk "^2.4.2" - debug "^4.1.1" - fs-extra "^7.0.1" - lodash "^4.17.11" - semver "^6.3.0" - table "^6.8.0" - undici "^5.14.0" - "@nuxtjs/opencollective@0.3.2": version "0.3.2" resolved "https://registry.yarnpkg.com/@nuxtjs/opencollective/-/opencollective-0.3.2.tgz#620ce1044f7ac77185e825e1936115bb38e2681c" @@ -4239,7 +4241,18 @@ resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.9.5.tgz#1eed23d4844c861a1835b5d33507c1017fa98de8" integrity sha512-ZK+W5mVhRppff9BE6YdR8CC52C8zAvsVAiWhEtQ5+oNxFE6h1WdeWo+FJSF8KKvtxxVYZ7MTP/5KoVpAU3aSWg== -"@openzeppelin/defender-base-client@^1.46.0": +"@openzeppelin/defender-admin-client@^1.52.0": + version "1.54.1" + resolved "https://registry.yarnpkg.com/@openzeppelin/defender-admin-client/-/defender-admin-client-1.54.1.tgz#b877972992b95a0dc3787f2ade2f044586621357" + integrity sha512-kRpSUdTsnSqntp4FOXIm95t+6VKHc8CUY2Si71VDuxs0q7HSPZkdpRPSntcolwEzWy9L4a8NS/QMwDF5NJ4X1g== + dependencies: + "@openzeppelin/defender-base-client" "1.54.1" + axios "^1.4.0" + ethers "^5.7.2" + lodash "^4.17.19" + node-fetch "^2.6.0" + +"@openzeppelin/defender-base-client@1.54.1", "@openzeppelin/defender-base-client@^1.52.0": version "1.54.1" resolved "https://registry.yarnpkg.com/@openzeppelin/defender-base-client/-/defender-base-client-1.54.1.tgz#ed777ae56908d5a920e1f72ac735c63694e65b30" integrity sha512-DRGz/7KN3ZQwu28YWMOaojrC7jjPkz/uCwkC8/C8B11qwZhA5qIVvyhYHhhFOCl0J84+E3TNdvkPD2q3p2WaJw== @@ -4250,33 +4263,44 @@ lodash "^4.17.19" node-fetch "^2.6.0" -"@openzeppelin/hardhat-upgrades@^1.22.0": - version "1.28.0" - resolved "https://registry.yarnpkg.com/@openzeppelin/hardhat-upgrades/-/hardhat-upgrades-1.28.0.tgz#6361f313a8a879d8a08a5e395acf0933bc190950" - integrity sha512-7sb/Jf+X+uIufOBnmHR0FJVWuxEs2lpxjJnLNN6eCJCP8nD0v+Ot5lTOW2Qb/GFnh+fLvJtEkhkowz4ZQ57+zQ== +"@openzeppelin/defender-sdk-base-client@^1.8.0": + version "1.8.0" + resolved "https://registry.yarnpkg.com/@openzeppelin/defender-sdk-base-client/-/defender-sdk-base-client-1.8.0.tgz#2209a060ce61b4dfc44c7ac0c2b1d86e18b69f7d" + integrity sha512-XIJat6BW2CTM74AwG5IL0Q/aE6RXj8x7smnVKmBql4wMvmirVW+njfwzZCLhUTiBXg9AlHxIInEF14SabfIisg== dependencies: - "@openzeppelin/defender-base-client" "^1.46.0" - "@openzeppelin/platform-deploy-client" "^0.8.0" - "@openzeppelin/upgrades-core" "^1.27.0" + amazon-cognito-identity-js "^6.3.6" + async-retry "^1.3.3" + +"@openzeppelin/defender-sdk-deploy-client@^1.8.0": + version "1.8.0" + resolved "https://registry.yarnpkg.com/@openzeppelin/defender-sdk-deploy-client/-/defender-sdk-deploy-client-1.8.0.tgz#1e186d2b3ff176c6a4c03e8207bad8022528975f" + integrity sha512-/tNS2EnHuA5l095wzMbIkGMDNHZLcZQ2eLUP8z+AeKaAUeR2z4qzZ1ul21kR3EJURAyoy8aULFZanLggoBWHrA== + dependencies: + "@ethersproject/abi" "^5.7.0" + "@openzeppelin/defender-sdk-base-client" "^1.8.0" + axios "^1.4.0" + lodash "^4.17.21" + +"@openzeppelin/hardhat-upgrades@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@openzeppelin/hardhat-upgrades/-/hardhat-upgrades-3.0.1.tgz#f1a9c5e817ddb9163da0e79cf50a13dfe7e14856" + integrity sha512-NtD2/n2PKNqHBafQy3AM6KCvsDZD0w97po7fFa4wctl0fg/8QwGAg3fB8InkBFEhGn17+IgshRI8G94hUrFPcQ== + dependencies: + "@openzeppelin/defender-admin-client" "^1.52.0" + "@openzeppelin/defender-base-client" "^1.52.0" + "@openzeppelin/defender-sdk-base-client" "^1.8.0" + "@openzeppelin/defender-sdk-deploy-client" "^1.8.0" + "@openzeppelin/upgrades-core" "^1.32.0" chalk "^4.1.0" debug "^4.1.1" + ethereumjs-util "^7.1.5" proper-lockfile "^4.1.1" + undici "^5.28.2" -"@openzeppelin/platform-deploy-client@^0.8.0": - version "0.8.0" - resolved "https://registry.yarnpkg.com/@openzeppelin/platform-deploy-client/-/platform-deploy-client-0.8.0.tgz#af6596275a19c283d6145f0128cc1247d18223c1" - integrity sha512-POx3AsnKwKSV/ZLOU/gheksj0Lq7Is1q2F3pKmcFjGZiibf+4kjGxr4eSMrT+2qgKYZQH1ZLQZ+SkbguD8fTvA== - dependencies: - "@ethersproject/abi" "^5.6.3" - "@openzeppelin/defender-base-client" "^1.46.0" - axios "^0.21.2" - lodash "^4.17.19" - node-fetch "^2.6.0" - -"@openzeppelin/upgrades-core@^1.27.0": - version "1.32.1" - resolved "https://registry.yarnpkg.com/@openzeppelin/upgrades-core/-/upgrades-core-1.32.1.tgz#77d5590df6c3a4c8c9b6c47355a6f246ec47e1f5" - integrity sha512-LlaWp2UX5BYT6BXs6OxJc1eSGMR0pz7DOltj6WCAJ038NdqdEIoItP5p9VOrw7ZdUdvUYHKHY4IinLY+ITxaig== +"@openzeppelin/upgrades-core@^1.32.0": + version "1.32.2" + resolved "https://registry.yarnpkg.com/@openzeppelin/upgrades-core/-/upgrades-core-1.32.2.tgz#4313bd0a547090a350817cf798af60e0eb0728e8" + integrity sha512-EkXriOHZfn6u00Tbq0zUuhHDeTQB9WyAZKZo3UeYdqULb7E3vqxZgxgXmWJwEzAb6E77DvprzQ4gwCAjMV/S4Q== dependencies: cbor "^9.0.0" chalk "^4.1.0" @@ -4406,7 +4430,17 @@ tslib "^2.5.0" webcrypto-core "^1.7.7" -"@pkgr/utils@^2.3.1", "@pkgr/utils@^2.4.2": +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + +"@pkgr/core@^0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.1.0.tgz#7d8dacb7fdef0e4387caf7396cbd77f179867d06" + integrity sha512-Zwq5OCzuwJC2jwqmpEQt7Ds1DTi6BWSwoGkbb1n9pO3hzb35BoJELx7c0T23iDkBGkh2e7tvOtjF3tr3OaQHDQ== + +"@pkgr/utils@^2.3.1": version "2.4.2" resolved "https://registry.yarnpkg.com/@pkgr/utils/-/utils-2.4.2.tgz#9e638bbe9a6a6f165580dc943f138fd3309a2cbc" integrity sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw== @@ -4454,7 +4488,7 @@ "@pnpm/network.ca-file" "^1.0.1" config-chain "^1.1.11" -"@polka/url@^1.0.0-next.20": +"@polka/url@^1.0.0-next.24": version "1.0.0-next.24" resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.24.tgz#58601079e11784d20f82d0585865bb42305c4df3" integrity sha512-2LuNTFBIO0m7kKIQvvPHN6UE63VjpmL9rnEEaOOaiSPbZK+zUOYIzBAWcED+3XYzhYsd/0mD57VdxAEqqV52CQ== @@ -4852,10 +4886,10 @@ redux-thunk "^2.4.2" reselect "^4.1.8" -"@remix-run/router@1.14.0": - version "1.14.0" - resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.14.0.tgz#9bc39a5a3a71b81bdb310eba6def5bc3966695b7" - integrity sha512-WOHih+ClN7N8oHk9N4JUiMxQJmRVaOxcg8w7F/oHUXzJt920ekASLI/7cYX8XkntDWRhLZtsk6LbGrkgOAvi5A== +"@remix-run/router@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.14.1.tgz#6d2dd03d52e604279c38911afc1079d58c50a755" + integrity sha512-Qg4DMQsfPNAs88rb2xkdk03N3bjK4jgX5fR24eHCTR9q6PrhZQZ4UJBPzCHJkIpTRN1UKxx2DzjZmnC+7Lj0Ow== "@repeaterjs/repeater@3.0.4": version "3.0.4" @@ -4938,70 +4972,70 @@ estree-walker "^2.0.2" picomatch "^2.3.1" -"@rollup/rollup-android-arm-eabi@4.9.1": - version "4.9.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.1.tgz#beaf518ee45a196448e294ad3f823d2d4576cf35" - integrity sha512-6vMdBZqtq1dVQ4CWdhFwhKZL6E4L1dV6jUjuBvsavvNJSppzi6dLBbuV+3+IyUREaj9ZFvQefnQm28v4OCXlig== - -"@rollup/rollup-android-arm64@4.9.1": - version "4.9.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.1.tgz#6f76cfa759c2d0fdb92122ffe28217181a1664eb" - integrity sha512-Jto9Fl3YQ9OLsTDWtLFPtaIMSL2kwGyGoVCmPC8Gxvym9TCZm4Sie+cVeblPO66YZsYH8MhBKDMGZ2NDxuk/XQ== - -"@rollup/rollup-darwin-arm64@4.9.1": - version "4.9.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.1.tgz#9aaefe33a5481d66322d1c62f368171c03eabe2b" - integrity sha512-LtYcLNM+bhsaKAIGwVkh5IOWhaZhjTfNOkGzGqdHvhiCUVuJDalvDxEdSnhFzAn+g23wgsycmZk1vbnaibZwwA== - -"@rollup/rollup-darwin-x64@4.9.1": - version "4.9.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.1.tgz#707dcaadcdc6bd3fd6c69f55d9456cd4446306a3" - integrity sha512-KyP/byeXu9V+etKO6Lw3E4tW4QdcnzDG/ake031mg42lob5tN+5qfr+lkcT/SGZaH2PdW4Z1NX9GHEkZ8xV7og== - -"@rollup/rollup-linux-arm-gnueabihf@4.9.1": - version "4.9.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.1.tgz#7a4dbbd1dd98731d88a55aefcef0ec4c578fa9c7" - integrity sha512-Yqz/Doumf3QTKplwGNrCHe/B2p9xqDghBZSlAY0/hU6ikuDVQuOUIpDP/YcmoT+447tsZTmirmjgG3znvSCR0Q== - -"@rollup/rollup-linux-arm64-gnu@4.9.1": - version "4.9.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.1.tgz#967ba8e6f68a5f21bd00cd97773dcdd6107e94ed" - integrity sha512-u3XkZVvxcvlAOlQJ3UsD1rFvLWqu4Ef/Ggl40WAVCuogf4S1nJPHh5RTgqYFpCOvuGJ7H5yGHabjFKEZGExk5Q== - -"@rollup/rollup-linux-arm64-musl@4.9.1": - version "4.9.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.1.tgz#d3a4e1c9f21eef3b9f4e4989f334a519a1341462" - integrity sha512-0XSYN/rfWShW+i+qjZ0phc6vZ7UWI8XWNz4E/l+6edFt+FxoEghrJHjX1EY/kcUGCnZzYYRCl31SNdfOi450Aw== - -"@rollup/rollup-linux-riscv64-gnu@4.9.1": - version "4.9.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.1.tgz#415c0533bb752164effd05f5613858e8f6779bc9" - integrity sha512-LmYIO65oZVfFt9t6cpYkbC4d5lKHLYv5B4CSHRpnANq0VZUQXGcCPXHzbCXCz4RQnx7jvlYB1ISVNCE/omz5cw== - -"@rollup/rollup-linux-x64-gnu@4.9.1": - version "4.9.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.1.tgz#0983385dd753a2e0ecaddea7a81dd37fea5114f5" - integrity sha512-kr8rEPQ6ns/Lmr/hiw8sEVj9aa07gh1/tQF2Y5HrNCCEPiCBGnBUt9tVusrcBBiJfIt1yNaXN6r1CCmpbFEDpg== - -"@rollup/rollup-linux-x64-musl@4.9.1": - version "4.9.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.1.tgz#eb7494ebc5199cbd2e5c38c2b8acbe2603f35e03" - integrity sha512-t4QSR7gN+OEZLG0MiCgPqMWZGwmeHhsM4AkegJ0Kiy6TnJ9vZ8dEIwHw1LcZKhbHxTY32hp9eVCMdR3/I8MGRw== - -"@rollup/rollup-win32-arm64-msvc@4.9.1": - version "4.9.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.1.tgz#5bebc66e3a7f82d4b9aa9ff448e7fc13a69656e9" - integrity sha512-7XI4ZCBN34cb+BH557FJPmh0kmNz2c25SCQeT9OiFWEgf8+dL6ZwJ8f9RnUIit+j01u07Yvrsuu1rZGxJCc51g== - -"@rollup/rollup-win32-ia32-msvc@4.9.1": - version "4.9.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.1.tgz#34156ebf8b4de3b20e6497260fe519a30263f8cf" - integrity sha512-yE5c2j1lSWOH5jp+Q0qNL3Mdhr8WuqCNVjc6BxbVfS5cAS6zRmdiw7ktb8GNpDCEUJphILY6KACoFoRtKoqNQg== - -"@rollup/rollup-win32-x64-msvc@4.9.1": - version "4.9.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.1.tgz#d146db7a5949e10837b323ce933ed882ac878262" - integrity sha512-PyJsSsafjmIhVgaI1Zdj7m8BB8mMckFah/xbpplObyHfiXzKcI5UOUXRyOdHW7nz4DpMCuzLnF7v5IWHenCwYA== +"@rollup/rollup-android-arm-eabi@4.9.4": + version "4.9.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.4.tgz#b1094962742c1a0349587040bc06185e2a667c9b" + integrity sha512-ub/SN3yWqIv5CWiAZPHVS1DloyZsJbtXmX4HxUTIpS0BHm9pW5iYBo2mIZi+hE3AeiTzHz33blwSnhdUo+9NpA== + +"@rollup/rollup-android-arm64@4.9.4": + version "4.9.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.4.tgz#96eb86fb549e05b187f2ad06f51d191a23cb385a" + integrity sha512-ehcBrOR5XTl0W0t2WxfTyHCR/3Cq2jfb+I4W+Ch8Y9b5G+vbAecVv0Fx/J1QKktOrgUYsIKxWAKgIpvw56IFNA== + +"@rollup/rollup-darwin-arm64@4.9.4": + version "4.9.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.4.tgz#2456630c007cc5905cb368acb9ff9fc04b2d37be" + integrity sha512-1fzh1lWExwSTWy8vJPnNbNM02WZDS8AW3McEOb7wW+nPChLKf3WG2aG7fhaUmfX5FKw9zhsF5+MBwArGyNM7NA== + +"@rollup/rollup-darwin-x64@4.9.4": + version "4.9.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.4.tgz#97742214fc7dfd47a0f74efba6f5ae264e29c70c" + integrity sha512-Gc6cukkF38RcYQ6uPdiXi70JB0f29CwcQ7+r4QpfNpQFVHXRd0DfWFidoGxjSx1DwOETM97JPz1RXL5ISSB0pA== + +"@rollup/rollup-linux-arm-gnueabihf@4.9.4": + version "4.9.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.4.tgz#cd933e61d6f689c9cdefde424beafbd92cfe58e2" + integrity sha512-g21RTeFzoTl8GxosHbnQZ0/JkuFIB13C3T7Y0HtKzOXmoHhewLbVTFBQZu+z5m9STH6FZ7L/oPgU4Nm5ErN2fw== + +"@rollup/rollup-linux-arm64-gnu@4.9.4": + version "4.9.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.4.tgz#33b09bf462f1837afc1e02a1b352af6b510c78a6" + integrity sha512-TVYVWD/SYwWzGGnbfTkrNpdE4HON46orgMNHCivlXmlsSGQOx/OHHYiQcMIOx38/GWgwr/po2LBn7wypkWw/Mg== + +"@rollup/rollup-linux-arm64-musl@4.9.4": + version "4.9.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.4.tgz#50257fb248832c2308064e3764a16273b6ee4615" + integrity sha512-XcKvuendwizYYhFxpvQ3xVpzje2HHImzg33wL9zvxtj77HvPStbSGI9czrdbfrf8DGMcNNReH9pVZv8qejAQ5A== + +"@rollup/rollup-linux-riscv64-gnu@4.9.4": + version "4.9.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.4.tgz#09589e4e1a073cf56f6249b77eb6c9a8e9b613a8" + integrity sha512-LFHS/8Q+I9YA0yVETyjonMJ3UA+DczeBd/MqNEzsGSTdNvSJa1OJZcSH8GiXLvcizgp9AlHs2walqRcqzjOi3A== + +"@rollup/rollup-linux-x64-gnu@4.9.4": + version "4.9.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.4.tgz#bd312bb5b5f02e54d15488605d15cfd3f90dda7c" + integrity sha512-dIYgo+j1+yfy81i0YVU5KnQrIJZE8ERomx17ReU4GREjGtDW4X+nvkBak2xAUpyqLs4eleDSj3RrV72fQos7zw== + +"@rollup/rollup-linux-x64-musl@4.9.4": + version "4.9.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.4.tgz#25b3bede85d86438ce28cc642842d10d867d40e9" + integrity sha512-RoaYxjdHQ5TPjaPrLsfKqR3pakMr3JGqZ+jZM0zP2IkDtsGa4CqYaWSfQmZVgFUCgLrTnzX+cnHS3nfl+kB6ZQ== + +"@rollup/rollup-win32-arm64-msvc@4.9.4": + version "4.9.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.4.tgz#95957067eb107f571da1d81939f017d37b4958d3" + integrity sha512-T8Q3XHV+Jjf5e49B4EAaLKV74BbX7/qYBRQ8Wop/+TyyU0k+vSjiLVSHNWdVd1goMjZcbhDmYZUYW5RFqkBNHQ== + +"@rollup/rollup-win32-ia32-msvc@4.9.4": + version "4.9.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.4.tgz#71b6facad976db527863f698692c6964c0b6e10e" + integrity sha512-z+JQ7JirDUHAsMecVydnBPWLwJjbppU+7LZjffGf+Jvrxq+dVjIE7By163Sc9DKc3ADSU50qPVw0KonBS+a+HQ== + +"@rollup/rollup-win32-x64-msvc@4.9.4": + version "4.9.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.4.tgz#16295ccae354707c9bc6842906bdeaad4f3ba7a5" + integrity sha512-LfdGXCV9rdEify1oxlN9eamvDSjv9md9ZVMAbNHA87xqIfFCxImxan9qZ8+Un54iK2nnqPlbnSi4R54ONtbWBw== "@rushstack/eslint-patch@^1.1.0": version "1.6.1" @@ -5383,14 +5417,14 @@ bn.js "^5.2.1" web3-utils "^1.8.1" -"@smithy/types@^2.7.0": - version "2.7.0" - resolved "https://registry.yarnpkg.com/@smithy/types/-/types-2.7.0.tgz#6ed9ba5bff7c4d28c980cff967e6d8456840a4f3" - integrity sha512-1OIFyhK+vOkMbu4aN2HZz/MomREkrAC/HqY5mlJMUJfGrPRwijJDTeiN8Rnj9zUaB8ogXAfIOtZrrgqZ4w7Wnw== +"@smithy/types@^2.8.0": + version "2.8.0" + resolved "https://registry.yarnpkg.com/@smithy/types/-/types-2.8.0.tgz#bdbaa0a54c9c3538d6c763c6f32d3e4f76fe0df9" + integrity sha512-h9sz24cFgt/W1Re22OlhQKmUZkNh244ApgRsUDYinqF8R+QgcsBIX344u2j61TPshsTz3CvL6HYU1DnQdsSrHA== dependencies: tslib "^2.5.0" -"@solana/buffer-layout@^4.0.0": +"@solana/buffer-layout@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@solana/buffer-layout/-/buffer-layout-4.0.1.tgz#b996235eaec15b1e0b5092a8ed6028df77fa6c15" integrity sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA== @@ -5398,15 +5432,15 @@ buffer "~6.0.3" "@solana/web3.js@^1.70.1": - version "1.87.6" - resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.87.6.tgz#6744cfc5f4fc81e0f58241c0a92648a7320bb3bf" - integrity sha512-LkqsEBgTZztFiccZZXnawWa8qNCATEqE97/d0vIwjTclmVlc8pBpD1DmjfVHtZ1HS5fZorFlVhXfpwnCNDZfyg== + version "1.88.0" + resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.88.0.tgz#24e1482f63ac54914430b4ce5ab36eaf433ecdb8" + integrity sha512-E4BdfB0HZpb66OPFhIzPApNE2tG75Mc6XKIoeymUkx/IV+USSYuxDX29sjgE/KGNYxggrOf4YuYnRMI6UiPL8w== dependencies: - "@babel/runtime" "^7.23.2" + "@babel/runtime" "^7.23.4" "@noble/curves" "^1.2.0" - "@noble/hashes" "^1.3.1" - "@solana/buffer-layout" "^4.0.0" - agentkeepalive "^4.3.0" + "@noble/hashes" "^1.3.2" + "@solana/buffer-layout" "^4.0.1" + agentkeepalive "^4.5.0" bigint-buffer "^1.1.5" bn.js "^5.2.1" borsh "^0.7.0" @@ -5414,7 +5448,7 @@ buffer "6.0.3" fast-stable-stringify "^1.0.0" jayson "^4.1.0" - node-fetch "^2.6.12" + node-fetch "^2.7.0" rpc-websockets "^7.5.1" superstruct "^0.14.2" @@ -5425,13 +5459,18 @@ dependencies: antlr4ts "^0.5.0-alpha.4" -"@solidity-parser/parser@^0.16.0", "@solidity-parser/parser@^0.16.2": +"@solidity-parser/parser@^0.16.0": version "0.16.2" resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.16.2.tgz#42cb1e3d88b3e8029b0c9befff00b634cd92d2fa" integrity sha512-PI9NfoA3P8XK2VBkK5oIfRgKDsicwDZfkVq9ZTBCQYGOP1N2owgY2dyLGyU5/J/hQs8KRk55kdmvTLjy3Mu3vg== dependencies: antlr4ts "^0.5.0-alpha.4" +"@solidity-parser/parser@^0.17.0": + version "0.17.0" + resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.17.0.tgz#52a2fcc97ff609f72011014e4c5b485ec52243ef" + integrity sha512-Nko8R0/kUo391jsEHHxrGM07QFdnPGvlmox4rmH0kNiNAashItAilhy4Mv4pK5gQmW5f4sXAF58fwJbmlkGcVw== + "@sqltools/formatter@^1.2.5": version "1.2.5" resolved "https://registry.yarnpkg.com/@sqltools/formatter/-/formatter-1.2.5.tgz#3abc203c79b8c3e90fd6c156a0c62d5403520e12" @@ -6108,37 +6147,10 @@ prettier "2.8.4" typescript "5.2.2" -"@strapi/ui-primitives@^1.13.0": - version "1.13.2" - resolved "https://registry.yarnpkg.com/@strapi/ui-primitives/-/ui-primitives-1.13.2.tgz#8e510bc56944d8bf93581fdc7934f77a76d17e3a" - integrity sha512-Koaun6riXoTOvvlKNgFy44BVUfLfchgpabRhrNH/KfmBQQlhLCqJDQu023FcFQDQQ4XFO3DpvzDk8pCbmva7oQ== - dependencies: - "@radix-ui/number" "^1.0.1" - "@radix-ui/primitive" "^1.0.1" - "@radix-ui/react-collection" "1.0.3" - "@radix-ui/react-compose-refs" "^1.0.1" - "@radix-ui/react-context" "^1.0.1" - "@radix-ui/react-direction" "1.0.1" - "@radix-ui/react-dismissable-layer" "^1.0.5" - "@radix-ui/react-focus-guards" "1.0.1" - "@radix-ui/react-focus-scope" "1.0.4" - "@radix-ui/react-id" "^1.0.1" - "@radix-ui/react-popper" "^1.1.3" - "@radix-ui/react-portal" "^1.0.4" - "@radix-ui/react-primitive" "^1.0.3" - "@radix-ui/react-slot" "^1.0.2" - "@radix-ui/react-use-callback-ref" "^1.0.1" - "@radix-ui/react-use-controllable-state" "^1.0.1" - "@radix-ui/react-use-layout-effect" "1.0.1" - "@radix-ui/react-use-previous" "^1.0.1" - "@radix-ui/react-visually-hidden" "^1.0.3" - aria-hidden "^1.2.3" - react-remove-scroll "^2.5.7" - -"@strapi/ui-primitives@^1.13.1": - version "1.14.0" - resolved "https://registry.yarnpkg.com/@strapi/ui-primitives/-/ui-primitives-1.14.0.tgz#98ff668701b4100bee8bc0212b392158c88744f7" - integrity sha512-M5RhM7/qVuu4gPvHWiSTOdI7bVDWK68aB+XyB/g1hGPqXL2Umsz8Iwn9bJPQk6YwCaHdrobaoB7lEDlfrlUAVA== +"@strapi/ui-primitives@^1.13.0", "@strapi/ui-primitives@^1.13.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@strapi/ui-primitives/-/ui-primitives-1.14.1.tgz#f4100f68874754088322bdb6da98a206fa9fbc2d" + integrity sha512-AmwyfZuazN7J1AgVf7i7oly+zwcJdWFqh/UCd3uPtoonnPmdCIRjkK8aBWlU9M+k3277FGIaCHOwNHiMSiBbbA== dependencies: "@radix-ui/number" "^1.0.1" "@radix-ui/primitive" "^1.0.1" @@ -6194,9 +6206,9 @@ prop-types "^15.7.2" "@stripe/stripe-js@^2.2.1": - version "2.2.2" - resolved "https://registry.yarnpkg.com/@stripe/stripe-js/-/stripe-js-2.2.2.tgz#9c1318a3e09a6df0a667d54d7f6edb917670109d" - integrity sha512-LvFZRZEBoMe6vXC6RoOAIbXWo/0JDdndq43ekL9M6affcM7PtF5KALmwt91BazW7q49sbSl0l7TunWhhSwEW4w== + version "2.3.0" + resolved "https://registry.yarnpkg.com/@stripe/stripe-js/-/stripe-js-2.3.0.tgz#607f91b75990eb660b9a7a4a8a7c282f5babd3fc" + integrity sha512-iTwzjw1ORUR+1pH21+C/M05w+Jh5hAuE4QUei7Gnku65N7QpEaHtyVszYMYDBs6iNyLrD1tfQTSrjD6NkOA/ww== "@swc/helpers@^0.5.0": version "0.5.3" @@ -6254,9 +6266,9 @@ use-sync-external-store "^1.2.0" "@tenderly/hardhat-tenderly@^2.0.1": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@tenderly/hardhat-tenderly/-/hardhat-tenderly-2.0.1.tgz#a5e8a9aaeacd973ee006ed3b857f7808e71d4247" - integrity sha512-MaVq7UuWvsyqstya5wVwDc5ml5xo1pAS9PRQQZWhg/f6ats3KFboi9BP18Yhypjh4bPw2NPZOBip9u1lV0oaGw== + version "2.1.0" + resolved "https://registry.yarnpkg.com/@tenderly/hardhat-tenderly/-/hardhat-tenderly-2.1.0.tgz#20036426da8f11a5d8860703ef64cdb9086cc98d" + integrity sha512-wy6WnvrT4fxqTsln5DH3MgT+lvUV7AyqHVtSyGJgQh6NX0Q59ZXKoqedB8Hi3IkYMOhbjbPFlR0Z/zr8sYGEzQ== dependencies: "@ethersproject/bignumber" "^5.7.0" "@nomicfoundation/hardhat-ethers" "^3.0.4" @@ -6264,15 +6276,15 @@ ethers "^6.8.1" fs-extra "^10.1.0" hardhat-deploy "^0.11.43" - tenderly "^0.7.0" + tenderly "^0.8.0" ts-node "^10.9.1" tslog "^4.3.1" typescript "^5.2.2" "@testing-library/dom@^9.0.0": - version "9.3.3" - resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-9.3.3.tgz#108c23a5b0ef51121c26ae92eb3179416b0434f5" - integrity sha512-fB0R+fa3AUqbLHWyxXa2kGVtf1Fe1ZZFr0Zp6AIbIAzXb2mKbEXl+PCQNUOaq5lbTab5tfctfXRNsWXxa2f7Aw== + version "9.3.4" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-9.3.4.tgz#50696ec28376926fec0a1bf87d9dbac5e27f60ce" + integrity sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ== dependencies: "@babel/code-frame" "^7.10.4" "@babel/runtime" "^7.12.5" @@ -6284,16 +6296,16 @@ pretty-format "^27.0.2" "@testing-library/jest-dom@^6.1.3": - version "6.1.5" - resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-6.1.5.tgz#0a635d0ad4a1a880089d967299d94e9cfc81fbe1" - integrity sha512-3y04JLW+EceVPy2Em3VwNr95dOKqA8DhR0RJHhHKDZNYXcVXnEK7WIrpj4eYU8SVt/qYZ2aRWt/WgQ+grNES8g== + version "6.2.0" + resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-6.2.0.tgz#b572bd5cd6b29314487bac7ba393188e4987b4f7" + integrity sha512-+BVQlJ9cmEn5RDMUS8c2+TU6giLvzaHZ8sU/x0Jj7fk+6/46wPdwlgOPcpxS17CjcanBi/3VmGMqVr2rmbUmNw== dependencies: - "@adobe/css-tools" "^4.3.1" + "@adobe/css-tools" "^4.3.2" "@babel/runtime" "^7.9.2" aria-query "^5.0.0" chalk "^3.0.0" css.escape "^1.5.1" - dom-accessibility-api "^0.5.6" + dom-accessibility-api "^0.6.3" lodash "^4.17.15" redent "^3.0.0" @@ -6336,18 +6348,18 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== -"@typechain/ethers-v5@^10.1.0", "@typechain/ethers-v5@^10.1.1": - version "10.2.1" - resolved "https://registry.yarnpkg.com/@typechain/ethers-v5/-/ethers-v5-10.2.1.tgz#50241e6957683281ecfa03fb5a6724d8a3ce2391" - integrity sha512-n3tQmCZjRE6IU4h6lqUGiQ1j866n5MTCBJreNEHHVWXa2u9GJTaeYyU1/k+1qLutkyw+sS6VAN+AbeiTqsxd/A== +"@typechain/ethers-v6@^0.5.1": + version "0.5.1" + resolved "https://registry.yarnpkg.com/@typechain/ethers-v6/-/ethers-v6-0.5.1.tgz#42fe214a19a8b687086c93189b301e2b878797ea" + integrity sha512-F+GklO8jBWlsaVV+9oHaPh5NJdd6rAKN4tklGfInX1Q7h0xPgVLP39Jl3eCulPB5qexI71ZFHwbljx4ZXNfouA== dependencies: lodash "^4.17.15" ts-essentials "^7.0.1" -"@typechain/hardhat@^6.1.2", "@typechain/hardhat@^6.1.4": - version "6.1.6" - resolved "https://registry.yarnpkg.com/@typechain/hardhat/-/hardhat-6.1.6.tgz#1a749eb35e5054c80df531cf440819cb347c62ea" - integrity sha512-BiVnegSs+ZHVymyidtK472syodx1sXYlYJJixZfRstHVGYTi8V1O7QG4nsjyb0PC/LORcq7sfBUcHto1y6UgJA== +"@typechain/hardhat@^9.1.0": + version "9.1.0" + resolved "https://registry.yarnpkg.com/@typechain/hardhat/-/hardhat-9.1.0.tgz#6985015f01dfb37ef2ca8a29c742d05890351ddc" + integrity sha512-mtaUlzLlkqTlfPwB3FORdejqBskSnh+Jl8AIJGjXNAQfRQ4ofHADPl1+oU7Z3pAJzmZbUXII8MhOLQltcHgKnA== dependencies: fs-extra "^9.1.0" @@ -6388,9 +6400,9 @@ "@babel/types" "^7.0.0" "@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": - version "7.20.4" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.4.tgz#ec2c06fed6549df8bc0eb4615b683749a4a92e1b" - integrity sha512-mSM/iKUk5fDDrEV/e83qY+Cr3I1+Q3qqTuEn++HAWYjEa1+NxZr6CNrcJGf2ZTnq4HoFGC3zaTPZTobCzCFukA== + version "7.20.5" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.5.tgz#7b7502be0aa80cc4ef22978846b983edaafcd4dd" + integrity sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ== dependencies: "@babel/types" "^7.20.7" @@ -6447,7 +6459,7 @@ dependencies: "@types/chai" "*" -"@types/chai@*", "@types/chai@^4.2.0", "@types/chai@^4.3.3", "@types/chai@^4.3.4": +"@types/chai@*", "@types/chai@^4.3.3", "@types/chai@^4.3.4": version "4.3.11" resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.11.tgz#e95050bf79a932cb7305dd130254ccdf9bde671c" integrity sha512-qQR1dr2rGIHYlJulmr8Ioq3De0Le9E4MJ5AiaeAETJJpndT1uUNHsGFK3L/UIu+rbkQSdj8J/w2bCsBZc/Y5fQ== @@ -6488,7 +6500,7 @@ dependencies: "@types/express" "*" -"@types/cookiejar@*": +"@types/cookiejar@^2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@types/cookiejar/-/cookiejar-2.1.5.tgz#14a3e83fa641beb169a2dd8422d91c3c345a9a78" integrity sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q== @@ -6572,14 +6584,14 @@ "@types/estree" "*" "@types/eslint@*": - version "8.44.9" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.44.9.tgz#5799663009645637bd1c45b2e1a7c8f4caf89534" - integrity sha512-6yBxcvwnnYoYT1Uk2d+jvIfsuP4mb2EdIxFnrPABj5a/838qe5bGkNLFOiipX4ULQ7XVQvTxOh7jO+BTAiqsEw== + version "8.56.1" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.56.1.tgz#988cabb39c973e9200f35fdbb29d17992965bb08" + integrity sha512-18PLWRzhy9glDQp3+wOgfLYRWlhgX0azxgJ63rdpoUHyrC9z0f5CkFburjQx4uD7ZCruw85ZtMt6K+L+R8fLJQ== dependencies: "@types/estree" "*" "@types/json-schema" "*" -"@types/estree@*", "@types/estree@^1.0.0": +"@types/estree@*", "@types/estree@1.0.5", "@types/estree@^1.0.0": version "1.0.5" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== @@ -6816,9 +6828,14 @@ integrity sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw== "@types/luxon@~3.3.0": - version "3.3.7" - resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.3.7.tgz#043d413b6492a012de47503907bdf3ec4f827933" - integrity sha512-gKc9P2d4g5uYwmy4s/MO/yOVPmvHyvzka1YH6i5dM03UrFofHSmgc0D0ymbDRStFWHusk6cwwF6nhLm/ckBbbQ== + version "3.3.8" + resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.3.8.tgz#84dbf2d020a9209a272058725e168f21d331a67e" + integrity sha512-jYvz8UMLDgy3a5SkGJne8H7VA7zPV2Lwohjx0V8V31+SqAjNmurWMkk9cQhfvlcnXWudBpK9xPM1n4rljOcHYQ== + +"@types/methods@^1.1.4": + version "1.1.4" + resolved "https://registry.yarnpkg.com/@types/methods/-/methods-1.1.4.tgz#d3b7ac30ac47c91054ea951ce9eed07b1051e547" + integrity sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ== "@types/mime@*": version "3.0.4" @@ -6845,7 +6862,7 @@ resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.5.tgz#ec10755e871497bcd83efe927e43ec46e8c0747e" integrity sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag== -"@types/mocha@>=9.1.0", "@types/mocha@^10.0.2": +"@types/mocha@^10.0.2": version "10.0.6" resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.6.tgz#818551d39113081048bdddbef96701b4e8bb9d1b" integrity sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg== @@ -6855,10 +6872,10 @@ resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.34.tgz#10964ba0dee6ac4cd462e2795b6bebd407303433" integrity sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g== -"@types/node@*", "@types/node@20.10.6", "@types/node@>=12.0.0", "@types/node@>=13.7.0", "@types/node@>=8.1.0", "@types/node@^20.10.6": - version "20.10.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.10.6.tgz#a3ec84c22965802bf763da55b2394424f22bfbb5" - integrity sha512-Vac8H+NlRNNlAmDfGUP7b5h/KA+AtWIzuXy0E6OyP8f1tCLYAtPvKRRDJjAPqhpCb0t6U2j7/xqAuLEebW2kiw== +"@types/node@*", "@types/node@>=13.7.0", "@types/node@>=8.1.0", "@types/node@^20.10.6": + version "20.10.7" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.10.7.tgz#40fe8faf25418a75de9fe68a8775546732a3a901" + integrity sha512-fRbIKb8C/Y2lXxB5eVMj4IU7xpdox0Lh8bUPEdtLysaylsml1hOOx1+STloRs/B9nf7C6kPRmmg/V7aQW7usNg== dependencies: undici-types "~5.26.4" @@ -6867,6 +6884,13 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== +"@types/node@20.10.6": + version "20.10.6" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.10.6.tgz#a3ec84c22965802bf763da55b2394424f22bfbb5" + integrity sha512-Vac8H+NlRNNlAmDfGUP7b5h/KA+AtWIzuXy0E6OyP8f1tCLYAtPvKRRDJjAPqhpCb0t6U2j7/xqAuLEebW2kiw== + dependencies: + undici-types "~5.26.4" + "@types/node@^10.0.3": version "10.17.60" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b" @@ -6939,9 +6963,9 @@ integrity sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng== "@types/qs@*", "@types/qs@^6.2.31", "@types/qs@^6.9.7": - version "6.9.10" - resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.10.tgz#0af26845b5067e1c9a622658a51f60a3934d51e8" - integrity sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw== + version "6.9.11" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.11.tgz#208d8a30bc507bd82e03ada29e4732ea46a6bbda" + integrity sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ== "@types/range-parser@*": version "1.2.7" @@ -6975,9 +6999,9 @@ "@types/react" "*" "@types/react@*", "@types/react@16 || 17 || 18", "@types/react@^18.2.43": - version "18.2.45" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.45.tgz#253f4fac288e7e751ab3dc542000fb687422c15c" - integrity sha512-TtAxCNrlrBp8GoeEp1npd5g+d/OejJHFxS3OWmrPBMFaVQMSN0OFySozJio5BHxTuTeug00AVXVAjfDSfk+lUg== + version "18.2.47" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.47.tgz#85074b27ab563df01fbc3f68dc64bf7050b0af40" + integrity sha512-xquNkkOirwyCgoClNk85BjP+aqnIS+ckAJ8i37gAbDs14jfW/J23f2GItAf33oiUPQnqNMALiFeoM9Y5mbjpVQ== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -7048,11 +7072,12 @@ integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== "@types/superagent@*": - version "4.1.24" - resolved "https://registry.yarnpkg.com/@types/superagent/-/superagent-4.1.24.tgz#e1f9ad3b66a21ed13c047e8529009735732dde0a" - integrity sha512-mEafCgyKiMFin24SDzWN7yAADt4gt6YawFiNMp0QS5ZPboORfyxFt0s3VzJKhTaKg9py/4FUmrHLTNfJKt9Rbw== + version "8.1.1" + resolved "https://registry.yarnpkg.com/@types/superagent/-/superagent-8.1.1.tgz#dbc620c5df3770b0c3092f947d6d5e808adae2bc" + integrity sha512-YQyEXA4PgCl7EVOoSAS3o0fyPFU6erv5mMixztQYe1bqbWmmn8c+IrqoxjQeZe4MgwXikgcaZPiI/DsbmOVlzA== dependencies: - "@types/cookiejar" "*" + "@types/cookiejar" "^2.1.5" + "@types/methods" "^1.1.4" "@types/node" "*" "@types/supertest@^2.0.15": @@ -7192,13 +7217,13 @@ "@typescript-eslint/types" "5.62.0" "@typescript-eslint/visitor-keys" "5.62.0" -"@typescript-eslint/scope-manager@6.15.0": - version "6.15.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.15.0.tgz#40e5214a3e9e048aca55ce33381bc61b6b51c32a" - integrity sha512-+BdvxYBltqrmgCNu4Li+fGDIkW9n//NrruzG9X1vBzaNK+ExVXPoGB71kneaVw/Jp+4rH/vaMAGC6JfMbHstVg== +"@typescript-eslint/scope-manager@6.18.1": + version "6.18.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.18.1.tgz#28c31c60f6e5827996aa3560a538693cb4bd3848" + integrity sha512-BgdBwXPFmZzaZUuw6wKiHKIovms97a7eTImjkXCZE04TGHysG+0hDQPmygyvgtkoB/aOQwSM/nWv3LzrOIQOBw== dependencies: - "@typescript-eslint/types" "6.15.0" - "@typescript-eslint/visitor-keys" "6.15.0" + "@typescript-eslint/types" "6.18.1" + "@typescript-eslint/visitor-keys" "6.18.1" "@typescript-eslint/type-utils@5.62.0": version "5.62.0" @@ -7215,10 +7240,10 @@ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== -"@typescript-eslint/types@6.15.0": - version "6.15.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.15.0.tgz#a9f7b006aee52b0948be6e03f521814bf435ddd5" - integrity sha512-yXjbt//E4T/ee8Ia1b5mGlbNj9fB9lJP4jqLbZualwpP2BCQ5is6BcWwxpIsY4XKAhmdv3hrW92GdtJbatC6dQ== +"@typescript-eslint/types@6.18.1": + version "6.18.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.18.1.tgz#91617d8080bcd99ac355d9157079970d1d49fefc" + integrity sha512-4TuMAe+tc5oA7wwfqMtB0Y5OrREPF1GeJBAjqwgZh1lEMH5PJQgWgHGfYufVB51LtjD+peZylmeyxUXPfENLCw== "@typescript-eslint/typescript-estree@5.62.0": version "5.62.0" @@ -7233,16 +7258,17 @@ semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/typescript-estree@6.15.0": - version "6.15.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.15.0.tgz#2f8a513df1ce5e6e1ba8e5c6aa52f392ae023fc5" - integrity sha512-7mVZJN7Hd15OmGuWrp2T9UvqR2Ecg+1j/Bp1jXUEY2GZKV6FXlOIoqVDmLpBiEiq3katvj/2n2mR0SDwtloCew== +"@typescript-eslint/typescript-estree@6.18.1": + version "6.18.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.18.1.tgz#a12b6440175b4cbc9d09ab3c4966c6b245215ab4" + integrity sha512-fv9B94UAhywPRhUeeV/v+3SBDvcPiLxRZJw/xZeeGgRLQZ6rLMG+8krrJUyIf6s1ecWTzlsbp0rlw7n9sjufHA== dependencies: - "@typescript-eslint/types" "6.15.0" - "@typescript-eslint/visitor-keys" "6.15.0" + "@typescript-eslint/types" "6.18.1" + "@typescript-eslint/visitor-keys" "6.18.1" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" + minimatch "9.0.3" semver "^7.5.4" ts-api-utils "^1.0.1" @@ -7261,16 +7287,16 @@ semver "^7.3.7" "@typescript-eslint/utils@^6.10.0": - version "6.15.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.15.0.tgz#f80dbb79f3b0f569077a8711dd44186a8933fa4c" - integrity sha512-eF82p0Wrrlt8fQSRL0bGXzK5nWPRV2dYQZdajcfzOD9+cQz9O7ugifrJxclB+xVOvWvagXfqS4Es7vpLP4augw== + version "6.18.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.18.1.tgz#3451cfe2e56babb6ac657e10b6703393d4b82955" + integrity sha512-zZmTuVZvD1wpoceHvoQpOiewmWu3uP9FuTWo8vqpy2ffsmfCE8mklRPi+vmnIYAIk9t/4kOThri2QCDgor+OpQ== dependencies: "@eslint-community/eslint-utils" "^4.4.0" "@types/json-schema" "^7.0.12" "@types/semver" "^7.5.0" - "@typescript-eslint/scope-manager" "6.15.0" - "@typescript-eslint/types" "6.15.0" - "@typescript-eslint/typescript-estree" "6.15.0" + "@typescript-eslint/scope-manager" "6.18.1" + "@typescript-eslint/types" "6.18.1" + "@typescript-eslint/typescript-estree" "6.18.1" semver "^7.5.4" "@typescript-eslint/visitor-keys@5.62.0": @@ -7281,12 +7307,12 @@ "@typescript-eslint/types" "5.62.0" eslint-visitor-keys "^3.3.0" -"@typescript-eslint/visitor-keys@6.15.0": - version "6.15.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.15.0.tgz#5baf97a7bfeec6f4894d400437055155a46b2330" - integrity sha512-1zvtdC1a9h5Tb5jU9x3ADNXO9yjP8rXlaoChu0DQX40vf5ACVpYIVIZhIMZ6d5sDXH7vq4dsZBT1fEGj8D2n2w== +"@typescript-eslint/visitor-keys@6.18.1": + version "6.18.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.18.1.tgz#704d789bda2565a15475e7d22f145b8fe77443f4" + integrity sha512-/kvt0C5lRqGoCfsbmm7/CwMqoSkY3zzHLIjdhHZQW3VFrnz7ATecOHR7nb7V+xn4286MBxfnQfQhAmCI0u+bJA== dependencies: - "@typescript-eslint/types" "6.15.0" + "@typescript-eslint/types" "6.18.1" eslint-visitor-keys "^3.4.1" "@ucast/core@^1.0.0", "@ucast/core@^1.4.1", "@ucast/core@^1.6.1": @@ -8170,9 +8196,9 @@ acorn@^7.1.1: integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== acorn@^8.0.4, acorn@^8.1.0, acorn@^8.10.0, acorn@^8.2.4, acorn@^8.4.1, acorn@^8.7.1, acorn@^8.8.1, acorn@^8.8.2, acorn@^8.9.0: - version "8.11.2" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.2.tgz#ca0d78b51895be5390a5903c5b3bdcdaf78ae40b" - integrity sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w== + version "8.11.3" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" + integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== address@^1.0.1: version "1.2.2" @@ -8211,7 +8237,7 @@ agent-base@6: dependencies: debug "4" -agentkeepalive@^4.3.0: +agentkeepalive@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923" integrity sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew== @@ -8275,7 +8301,7 @@ ajv@^6.11.0, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -amazon-cognito-identity-js@^6.0.1: +amazon-cognito-identity-js@^6.0.1, amazon-cognito-identity-js@^6.3.6: version "6.3.7" resolved "https://registry.yarnpkg.com/amazon-cognito-identity-js/-/amazon-cognito-identity-js-6.3.7.tgz#65c3d7ee4e0c0a1ffea01927248989c5bd1d1868" integrity sha512-tSjnM7KyAeOZ7UMah+oOZ6cW4Gf64FFcc7BE2l7MTcp7ekAPrXaCbpcW2xEpH1EiDS4cPcAouHzmCuc2tr72vQ== @@ -8806,9 +8832,9 @@ available-typed-arrays@^1.0.5: integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== aws-sdk@^2.1528.0: - version "2.1528.0" - resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1528.0.tgz#56dd74e3732fc8ba9dad652d1550816389fa540b" - integrity sha512-QyV8fTJJAqnBAbAGkRKgXfI/NvxAoeJHjEFVXDo77hv13cJZKOdBTe9dV56ztS4R1twDJxHibXdDi7IeBrag2w== + version "2.1532.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1532.0.tgz#7c78054e53584ce6396406e87d3edfbadb0ffa81" + integrity sha512-4QVQs01LEAxo7UpSHlq/HaO+SJ1WrYF8W1otO2WhKpVRYXkSxXIgZgfYaK+sQ762XTtB6tSuD2ZS2HGsKNXVLw== dependencies: buffer "4.9.2" events "1.1.1" @@ -8845,7 +8871,7 @@ axios@1.6.0: form-data "^4.0.0" proxy-from-env "^1.1.0" -axios@^0.21.1, axios@^0.21.2, axios@^0.21.4: +axios@^0.21.1, axios@^0.21.4: version "0.21.4" resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== @@ -8868,11 +8894,11 @@ axios@^0.27.2: form-data "^4.0.0" axios@^1.1.3, axios@^1.2.3, axios@^1.3.4, axios@^1.4.0, axios@^1.5.1: - version "1.6.2" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.2.tgz#de67d42c755b571d3e698df1b6504cde9b0ee9f2" - integrity sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A== + version "1.6.5" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.5.tgz#2c090da14aeeab3770ad30c3a1461bc970fb0cd8" + integrity sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg== dependencies: - follow-redirects "^1.15.0" + follow-redirects "^1.15.4" form-data "^4.0.0" proxy-from-env "^1.1.0" @@ -8955,7 +8981,7 @@ babel-plugin-macros@^3.1.0: cosmiconfig "^7.0.0" resolve "^1.19.0" -babel-plugin-polyfill-corejs2@^0.4.6: +babel-plugin-polyfill-corejs2@^0.4.7: version "0.4.7" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.7.tgz#679d1b94bf3360f7682e11f2cb2708828a24fe8c" integrity sha512-LidDk/tEGDfuHW2DWh/Hgo4rmnw3cduK6ZkOI1NPFceSK3n/yAGeOsNT7FLnSGHkXj3RHGSEVkN3FsCTY6w2CQ== @@ -8964,7 +8990,7 @@ babel-plugin-polyfill-corejs2@^0.4.6: "@babel/helper-define-polyfill-provider" "^0.4.4" semver "^6.3.1" -babel-plugin-polyfill-corejs3@^0.8.5: +babel-plugin-polyfill-corejs3@^0.8.7: version "0.8.7" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.7.tgz#941855aa7fdaac06ed24c730a93450d2b2b76d04" integrity sha512-KyDvZYxAzkC0Aj2dAPyDzi2Ym15e5JKZSK+maI7NAwSqofvuFglbSsxE7wUOvTg9oFVnHMzVzBKcqEb4PJgtOA== @@ -8972,7 +8998,7 @@ babel-plugin-polyfill-corejs3@^0.8.5: "@babel/helper-define-polyfill-provider" "^0.4.4" core-js-compat "^3.33.1" -babel-plugin-polyfill-regenerator@^0.5.3: +babel-plugin-polyfill-regenerator@^0.5.4: version "0.5.4" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.4.tgz#c6fc8eab610d3a11eb475391e52584bacfc020f4" integrity sha512-S/x2iOCvDaCASLYsOOgWOq4bCfKYVqvO/uxjkaYyZ3rVsVE3CeAI/c84NpyuBBymEgNvHgjEot3a9/Z/kXvqsg== @@ -9787,9 +9813,9 @@ camelize@^1.0.0: integrity sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ== caniuse-lite@^1.0.30001565: - version "1.0.30001570" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001570.tgz#b4e5c1fa786f733ab78fc70f592df6b3f23244ca" - integrity sha512-+3e0ASu4sw1SWaoCtvPeyXp+5PsjigkSt8OXZbF9StH5pQWbxEjLAZE3n8Aup5udop1uRiKA7a4utUk/uoSpUw== + version "1.0.30001576" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001576.tgz#893be772cf8ee6056d6c1e2d07df365b9ec0a5c4" + integrity sha512-ff5BdakGe2P3SQsMsiqmt1Lc8221NR1VzHj5jXN5vBny9A6fpze94HiVV/n7XRosOlsShJcvMv5mdnpjOGCEgg== carbites@^1.0.6: version "1.0.6" @@ -9850,7 +9876,7 @@ chai-as-promised@^7.1.1: dependencies: check-error "^1.0.2" -chai@^4.2.0, chai@^4.3.7, chai@^4.4.0: +chai@^4.3.7, chai@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/chai/-/chai-4.4.0.tgz#f9ac79f26726a867ac9d90a9b382120479d5f55b" integrity sha512-x9cHNq1uvkCdU+5xTkNh5WtgD4e4yDFCsp9jVc7N7qVeKeftv3gO/ZrviX5d+3ZfxdYnZXZYujjRInu1RogU6A== @@ -10109,9 +10135,9 @@ classic-level@^1.2.0: node-gyp-build "^4.3.0" classnames@^2.2.5: - version "2.3.2" - resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924" - integrity sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw== + version "2.5.1" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b" + integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow== clean-css@^5.2.2: version "5.3.3" @@ -10302,9 +10328,9 @@ clsx@^1.1.0: integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== clsx@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.0.0.tgz#12658f3fd98fafe62075595a5c30e43d18f3d00b" - integrity sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q== + version "2.1.0" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.0.tgz#e851283bcb5c80ee7608db18487433f7b23f77cb" + integrity sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg== cluster-key-slot@^1.1.0: version "1.1.2" @@ -10775,16 +10801,16 @@ copyfiles@2.4.1: yargs "^16.1.0" core-js-compat@^3.31.0, core-js-compat@^3.33.1: - version "3.34.0" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.34.0.tgz#61a4931a13c52f8f08d924522bba65f8c94a5f17" - integrity sha512-4ZIyeNbW/Cn1wkMMDy+mvrRUxrwFNjKwbhCfQpDd+eLgYipDqp8oGFGtLmhh18EDPKA0g3VUBYOxQGGwvWLVpA== + version "3.35.0" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.35.0.tgz#c149a3d1ab51e743bc1da61e39cb51f461a41873" + integrity sha512-5blwFAddknKeNgsjBzilkdQ0+YK8L1PfqPYq40NOYMYFSS38qj+hpTcLLWwpIwA2A5bje/x5jmVn2tzUMg9IVw== dependencies: browserslist "^4.22.2" core-js-pure@^3.23.3, core-js-pure@^3.30.2: - version "3.34.0" - resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.34.0.tgz#981e462500708664c91b827a75b011f04a8134a0" - integrity sha512-pmhivkYXkymswFfbXsANmBAewXx86UBfmagP+w0wkK06kLsLlTK5oQmsURPivzMkIBQiYq2cjamcZExIwlFQIg== + version "3.35.0" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.35.0.tgz#4660033304a050215ae82e476bd2513a419fbb34" + integrity sha512-f+eRYmkou59uh7BPcyJ8MC76DiGhspj1KMxVIcF24tzP8NA9HVa1uC7BTW2tgx7E1QVCzDzsgp7kArrzhlz8Ew== core-util-is@1.0.2: version "1.0.2" @@ -11236,7 +11262,7 @@ dataloader@^2.2.2: resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-2.2.2.tgz#216dc509b5abe39d43a9b9d97e6e5e473dfbe3e0" integrity sha512-8YnDaaf7N3k/q5HnTJVuzSyLETjoZjVmHc4AeKAzOvKHEFQKcn64OKBfzHYtE9zGjctNM7V9I0MfnUVLpi7M5g== -date-fns@2.30.0, date-fns@^2.29.1, date-fns@^2.29.3: +date-fns@2.30.0, date-fns@^2.29.1: version "2.30.0" resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.30.0.tgz#f367e644839ff57894ec6ac480de40cae4b0f4d0" integrity sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw== @@ -11487,9 +11513,9 @@ define-property@^2.0.2: isobject "^3.0.1" defu@^6.1.2, defu@^6.1.3: - version "6.1.3" - resolved "https://registry.yarnpkg.com/defu/-/defu-6.1.3.tgz#6d7f56bc61668e844f9f593ace66fd67ef1205fd" - integrity sha512-Vy2wmG3NTkmHNg/kzpuvHhkqeIx3ODWqasgCRbKtbXEN0G+HpEEv9BtJLp7ZG1CZloFaC41Ah3ZFbq7aqCqMeQ== + version "6.1.4" + resolved "https://registry.yarnpkg.com/defu/-/defu-6.1.4.tgz#4e0c9cf9ff68fe5f3d7f2765cc1a012dfdcb0479" + integrity sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg== del@^5.1.0: version "5.1.0" @@ -11757,11 +11783,16 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" -dom-accessibility-api@^0.5.6, dom-accessibility-api@^0.5.9: +dom-accessibility-api@^0.5.9: version "0.5.16" resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453" integrity sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg== +dom-accessibility-api@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz#993e925cc1d73f2c662e7d75dd5a5445259a8fd8" + integrity sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w== + dom-converter@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" @@ -11975,9 +12006,9 @@ electron-fetch@^1.7.2: encoding "^0.1.13" electron-to-chromium@^1.4.601: - version "1.4.615" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.615.tgz#b1c41839962d2e4e63dca05519da9040e34848c2" - integrity sha512-/bKPPcgZVUziECqDc+0HkT87+0zhaWSZHNXqF8FLd2lQcptpmUFwoCSWjCdOng9Gdq+afKArPdEg/0ZW461Eng== + version "1.4.625" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.625.tgz#a9a1d18ee911f9074a9c42d9e84b1c79b29f4059" + integrity sha512-DENMhh3MFgaPDoXWrVIqSPInQoLImywfCwrSmVl3cf9QHzoZSiutHwGaB/Ql3VkqcQV30rzgdM+BjKqBAJxo5Q== elliptic@6.5.4, elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3, elliptic@^6.5.4: version "6.5.4" @@ -12497,33 +12528,33 @@ esbuild@^0.18.10: "@esbuild/win32-x64" "0.18.20" esbuild@^0.19.3: - version "0.19.10" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.19.10.tgz#55e83e4a6b702e3498b9f872d84bfb4ebcb6d16e" - integrity sha512-S1Y27QGt/snkNYrRcswgRFqZjaTG5a5xM3EQo97uNBnH505pdzSNe/HLBq1v0RO7iK/ngdbhJB6mDAp0OK+iUA== + version "0.19.11" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.19.11.tgz#4a02dca031e768b5556606e1b468fe72e3325d96" + integrity sha512-HJ96Hev2hX/6i5cDVwcqiJBBtuo9+FeIJOtZ9W1kA5M6AMJRHUZlpYZ1/SbEwtO0ioNAW8rUooVpC/WehY2SfA== optionalDependencies: - "@esbuild/aix-ppc64" "0.19.10" - "@esbuild/android-arm" "0.19.10" - "@esbuild/android-arm64" "0.19.10" - "@esbuild/android-x64" "0.19.10" - "@esbuild/darwin-arm64" "0.19.10" - "@esbuild/darwin-x64" "0.19.10" - "@esbuild/freebsd-arm64" "0.19.10" - "@esbuild/freebsd-x64" "0.19.10" - "@esbuild/linux-arm" "0.19.10" - "@esbuild/linux-arm64" "0.19.10" - "@esbuild/linux-ia32" "0.19.10" - "@esbuild/linux-loong64" "0.19.10" - "@esbuild/linux-mips64el" "0.19.10" - "@esbuild/linux-ppc64" "0.19.10" - "@esbuild/linux-riscv64" "0.19.10" - "@esbuild/linux-s390x" "0.19.10" - "@esbuild/linux-x64" "0.19.10" - "@esbuild/netbsd-x64" "0.19.10" - "@esbuild/openbsd-x64" "0.19.10" - "@esbuild/sunos-x64" "0.19.10" - "@esbuild/win32-arm64" "0.19.10" - "@esbuild/win32-ia32" "0.19.10" - "@esbuild/win32-x64" "0.19.10" + "@esbuild/aix-ppc64" "0.19.11" + "@esbuild/android-arm" "0.19.11" + "@esbuild/android-arm64" "0.19.11" + "@esbuild/android-x64" "0.19.11" + "@esbuild/darwin-arm64" "0.19.11" + "@esbuild/darwin-x64" "0.19.11" + "@esbuild/freebsd-arm64" "0.19.11" + "@esbuild/freebsd-x64" "0.19.11" + "@esbuild/linux-arm" "0.19.11" + "@esbuild/linux-arm64" "0.19.11" + "@esbuild/linux-ia32" "0.19.11" + "@esbuild/linux-loong64" "0.19.11" + "@esbuild/linux-mips64el" "0.19.11" + "@esbuild/linux-ppc64" "0.19.11" + "@esbuild/linux-riscv64" "0.19.11" + "@esbuild/linux-s390x" "0.19.11" + "@esbuild/linux-x64" "0.19.11" + "@esbuild/netbsd-x64" "0.19.11" + "@esbuild/openbsd-x64" "0.19.11" + "@esbuild/sunos-x64" "0.19.11" + "@esbuild/win32-arm64" "0.19.11" + "@esbuild/win32-ia32" "0.19.11" + "@esbuild/win32-x64" "0.19.11" escalade@^3.1.1: version "3.1.1" @@ -12666,9 +12697,9 @@ eslint-plugin-jest@^25.3.0: "@typescript-eslint/experimental-utils" "^5.0.0" eslint-plugin-jest@^27.1.5: - version "27.6.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-27.6.0.tgz#e5c0cf735b3c8cad0ef9db5b565b2fc99f5e55ed" - integrity sha512-MTlusnnDMChbElsszJvrwD1dN3x6nZl//s4JD23BxB6MgR66TZlL064su24xEIS3VACfAoHV1vgyMgPw8nkdng== + version "27.6.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-27.6.1.tgz#5e43b07f3ca48d72e4b4fa243531e5153d9ca1dc" + integrity sha512-WEYkyVXD9NlmFBKvrkmzrC+C9yZoz5pAml2hO19PlS3spJtoiwj4p2u8spd/7zx5IvRsZsCmsoImaAvBB9X93Q== dependencies: "@typescript-eslint/utils" "^5.10.0" @@ -12695,12 +12726,12 @@ eslint-plugin-jsx-a11y@^6.5.1: object.fromentries "^2.0.7" eslint-plugin-prettier@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.1.tgz#a3b399f04378f79f066379f544e42d6b73f11515" - integrity sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg== + version "5.1.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.2.tgz#584c94d4bf31329b2d4cbeb10fd600d17d6de742" + integrity sha512-dhlpWc9vOwohcWmClFcA+HjlvUpuyynYs0Rf+L/P6/0iQE6vlHW9l5bkfzN62/Stm9fbq8ku46qzde76T1xlSg== dependencies: prettier-linter-helpers "^1.0.0" - synckit "^0.8.5" + synckit "^0.8.6" eslint-plugin-react-hooks@^4.3.0, eslint-plugin-react-hooks@^4.6.0: version "4.6.0" @@ -13045,7 +13076,7 @@ ethereumjs-util@^7.0.3, ethereumjs-util@^7.1.0, ethereumjs-util@^7.1.4, ethereum ethereum-cryptography "^0.1.3" rlp "^2.2.4" -ethers@^5.0.0, ethers@^5.4.7, ethers@^5.7.0, ethers@^5.7.1, ethers@^5.7.2: +ethers@^5.7.0, ethers@^5.7.1, ethers@^5.7.2: version "5.7.2" resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.7.2.tgz#3a7deeabbb8c030d4126b24f84e525466145872e" integrity sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg== @@ -13081,10 +13112,10 @@ ethers@^5.0.0, ethers@^5.4.7, ethers@^5.7.0, ethers@^5.7.1, ethers@^5.7.2: "@ethersproject/web" "5.7.1" "@ethersproject/wordlists" "5.7.0" -ethers@^6.8.1: - version "6.9.0" - resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.9.0.tgz#a4534bdcdfde306aee94ef32f3d5c70d7e33fcb9" - integrity sha512-pmfNyQzc2mseLe91FnT2vmNaTt8dDzhxZ/xItAV7uGsF4dI4ek2ufMu3rAkgQETL/TIs0GS5A+U05g9QyWnv3Q== +ethers@^6.8.1, ethers@^6.9.1: + version "6.9.2" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.9.2.tgz#6f4632f62e2350fa8354ff28624027a175ef85a4" + integrity sha512-YpkrtILnMQz5jSEsJQRTpduaGT/CXuLnUIuOYzHA0v/7c8IX91m2J48wSKjzGL5L9J/Us3tLoUdb+OwE3U+FFQ== dependencies: "@adraffy/ens-normalize" "1.10.0" "@noble/curves" "1.2.0" @@ -13668,10 +13699,10 @@ fn.name@1.x.x: resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== -follow-redirects@^1.12.1, follow-redirects@^1.14.0, follow-redirects@^1.14.8, follow-redirects@^1.14.9, follow-redirects@^1.15.0, follow-redirects@^1.15.2: - version "1.15.3" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a" - integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q== +follow-redirects@^1.12.1, follow-redirects@^1.14.0, follow-redirects@^1.14.8, follow-redirects@^1.14.9, follow-redirects@^1.15.0, follow-redirects@^1.15.2, follow-redirects@^1.15.4: + version "1.15.4" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.4.tgz#cdc7d308bf6493126b17ea2191ea0ccf3e535adf" + integrity sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw== for-each@^0.3.3: version "0.3.3" @@ -13692,6 +13723,14 @@ for-own@^1.0.0: dependencies: for-in "^1.0.1" +foreground-child@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.1.1.tgz#1d173e776d75d2772fed08efe4a0de1ea1b12d0d" + integrity sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" + forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -14224,6 +14263,17 @@ glob@9.3.5, glob@^9.2.0: minipass "^4.2.4" path-scurry "^1.6.1" +glob@^10.3.10: + version "10.3.10" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.10.tgz#0351ebb809fd187fe421ab96af83d3a70715df4b" + integrity sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g== + dependencies: + foreground-child "^3.1.0" + jackspeak "^2.3.5" + minimatch "^9.0.1" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + path-scurry "^1.10.1" + glob@^5.0.15: version "5.0.15" resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" @@ -14235,7 +14285,7 @@ glob@^5.0.15: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^8.0.3, glob@^8.1.0: +glob@^8.0.3: version "8.1.0" resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== @@ -14515,7 +14565,7 @@ graphql@15.5.0: resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.5.0.tgz#39d19494dbe69d1ea719915b578bf920344a69d5" integrity sha512-OmaM7y0kaK31NKG31q4YbD2beNYa6jBBKtMFT6gLYJljHLJr42IqJ8KX08u3Li/0ifzTU5HjmoOOrwa5BRLeDA== -graphql@^16.6.0, graphql@^16.7.1: +graphql@^16.6.0, graphql@^16.8.1: version "16.8.1" resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.8.1.tgz#1930a965bef1170603702acdb68aedd3f3cf6f07" integrity sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw== @@ -14528,9 +14578,9 @@ gzip-size@^6.0.0: duplexer "^0.1.2" h3@^1.8.1, h3@^1.8.2: - version "1.9.0" - resolved "https://registry.yarnpkg.com/h3/-/h3-1.9.0.tgz#c5f512a93026df9837db6f30c9ef51135dd46752" - integrity sha512-+F3ZqrNV/CFXXfZ2lXBINHi+rM4Xw3CDC5z2CDK3NMPocjonKipGLLDSkrqY9DOrioZNPTIdDMWfQKm//3X2DA== + version "1.10.0" + resolved "https://registry.yarnpkg.com/h3/-/h3-1.10.0.tgz#55ac36deb6e250ada5ff1940b6324bc6acc4085f" + integrity sha512-Tw1kcIC+AeimwRmviiObaD5EB430Yt+lTgOxLJxNr96Vd/fGRu04EF7aKfOAcpwKCI+U2JlbxOLhycD86p3Ciw== dependencies: cookie-es "^1.0.0" defu "^6.1.3" @@ -14539,7 +14589,7 @@ h3@^1.8.1, h3@^1.8.2: radix3 "^1.1.0" ufo "^1.3.2" uncrypto "^0.1.3" - unenv "^1.7.4" + unenv "^1.8.0" hamt-sharding@^2.0.0: version "2.0.1" @@ -14599,32 +14649,6 @@ hardhat-abi-exporter@^2.10.1: "@ethersproject/abi" "^5.5.0" delete-empty "^3.0.0" -hardhat-celo@^0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/hardhat-celo/-/hardhat-celo-0.0.7.tgz#fb08fe6afcb95487977ca10cd614a12d1f6c3c7a" - integrity sha512-//LB0KD9HMRUMO9MLHnr6ODLJCAbJK2e3OWIhQvJr8PTzms+1uELW/skAi5wUcO+gOASV4PTo45g/yipgHfzeA== - dependencies: - "@ethersproject/abi" "^5.4.7" - "@ethersproject/providers" "^5.4.7" - "@nomicfoundation/hardhat-chai-matchers" "^1.0.0" - "@nomicfoundation/hardhat-network-helpers" "^1.0.0" - "@nomicfoundation/hardhat-toolbox" "^2.0.2" - "@nomiclabs/hardhat-ethers" "^2.0.0" - "@nomiclabs/hardhat-etherscan" "^3.0.0" - "@typechain/ethers-v5" "^10.1.0" - "@typechain/hardhat" "^6.1.2" - "@types/chai" "^4.2.0" - "@types/mocha" ">=9.1.0" - "@types/node" ">=12.0.0" - chai "^4.2.0" - ethers "^5.4.7" - hardhat "^2.11.0" - hardhat-gas-reporter "^1.0.8" - solidity-coverage "^0.8.1" - ts-node ">=8.0.0" - typechain "^8.1.0" - typescript ">=4.5.0" - hardhat-contract-sizer@^2.6.1: version "2.10.0" resolved "https://registry.yarnpkg.com/hardhat-contract-sizer/-/hardhat-contract-sizer-2.10.0.tgz#72646f43bfe50e9a5702c9720c9bc3e77d93a2c9" @@ -14669,7 +14693,7 @@ hardhat-deploy@^0.11.43: qs "^6.9.4" zksync-web3 "^0.14.3" -hardhat-gas-reporter@^1.0.8, hardhat-gas-reporter@^1.0.9: +hardhat-gas-reporter@^1.0.9: version "1.0.9" resolved "https://registry.yarnpkg.com/hardhat-gas-reporter/-/hardhat-gas-reporter-1.0.9.tgz#9a2afb354bc3b6346aab55b1c02ca556d0e16450" integrity sha512-INN26G3EW43adGKBNzYWOlI3+rlLnasXTwW79YNnUhXPDa+yHESgt639dJEs37gCjhkbNKcRRJnomXEuMFBXJg== @@ -14678,10 +14702,10 @@ hardhat-gas-reporter@^1.0.8, hardhat-gas-reporter@^1.0.9: eth-gas-reporter "^0.2.25" sha1 "^1.1.1" -hardhat@^2.11.0, hardhat@^2.18.3: - version "2.19.3" - resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.19.3.tgz#fe3b28b889e34a074ea5b740c227e3c8d4ce56e8" - integrity sha512-zUvfILiu1O7W1a+t5E1nCJ6z1danRLNizQkSEQCCgDYcRx13AGXtH1MVZajKmdLmXIjKAPReTp/8JQQ4ZHaX3g== +hardhat@^2.18.3: + version "2.19.4" + resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.19.4.tgz#5112c30295d8be2e18e55d847373c50483ed1902" + integrity sha512-fTQJpqSt3Xo9Mn/WrdblNGAfcANM6XC3tAEi6YogB4s02DmTf93A8QsGb8uR0KR8TFcpcS8lgiW4ugAIYpnbrQ== dependencies: "@ethersproject/abi" "^5.1.2" "@metamask/eth-sig-util" "^4.0.0" @@ -16406,6 +16430,15 @@ iterator.prototype@^1.1.2: reflect.getprototypeof "^1.0.4" set-function-name "^2.0.1" +jackspeak@^2.3.5: + version "2.3.6" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.3.6.tgz#647ecc472238aee4b06ac0e461acc21a8c505ca8" + integrity sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + jake@^10.8.5: version "10.8.7" resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.7.tgz#63a32821177940c33f356e0ba44ff9d34e1c7d8f" @@ -17949,9 +17982,9 @@ libmime@^2.0.3: libqp "1.1.0" libphonenumber-js@^1.10.14: - version "1.10.52" - resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.52.tgz#786d7743e75aba1996824057b60003bb539f8daa" - integrity sha512-6vCuCHgem+OW1/VCAKgkasfegItCea8zIT7s9/CG/QxdCMIM7GfzbEBG5d7lGO3rzipjt5woOQL3DiHa8Fy78Q== + version "1.10.53" + resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.53.tgz#8dbfe1355ef1a3d8e13b8d92849f7db7ebddc98f" + integrity sha512-sDTnnqlWK4vH4AlDQuswz3n4Hx7bIQWTpIcScJX+Sp7St3LXHmfiax/ZFfyYxHmkdCvydOLSuvtAO/XpXiSySw== libqp@1.1.0: version "1.1.0" @@ -18131,6 +18164,11 @@ lodash.camelcase@^4.3.0: resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== +lodash.clonedeep@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + integrity sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ== + lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" @@ -18937,6 +18975,13 @@ minimatch@5.0.1: dependencies: brace-expansion "^2.0.1" +minimatch@9.0.3, minimatch@^9.0.1, minimatch@^9.0.3: + version "9.0.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" + integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== + dependencies: + brace-expansion "^2.0.1" + minimatch@^5.0.1: version "5.1.6" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" @@ -18951,13 +18996,6 @@ minimatch@^8.0.2: dependencies: brace-expansion "^2.0.1" -minimatch@^9.0.3: - version "9.0.3" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" - integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== - dependencies: - brace-expansion "^2.0.1" - minimist-options@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" @@ -19162,6 +19200,11 @@ mrmime@^1.0.0: resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-1.0.1.tgz#5f90c825fad4bdd41dc914eff5d1a8cfdaf24f27" integrity sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw== +mrmime@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-2.0.0.tgz#151082a6e06e59a9a39b46b3e14d5cfe92b3abb4" + integrity sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw== + ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -19460,9 +19503,9 @@ no-case@^3.0.4: tslib "^2.0.3" node-abi@^3.3.0: - version "3.52.0" - resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.52.0.tgz#ffba0a85f54e552547e5849015f40f9514d5ba7c" - integrity sha512-JJ98b02z16ILv7859irtXn4oUaFWADtvkzy2c0IAatNVX2Mc9Yoh8z6hZInn3QwvMEYhHuQloYi+TTQy67SIdQ== + version "3.54.0" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.54.0.tgz#f6386f7548817acac6434c6cba02999c9aebcc69" + integrity sha512-p7eGEiQil0YUV3ItH4/tBb781L5impVmmx2E9FRKF7d18XXzp4PGT2tdYMFY6wQqgxD0IwNZOiSJ0/K0fSi/OA== dependencies: semver "^7.3.5" @@ -19505,12 +19548,12 @@ node-emoji@1.11.0, node-emoji@^1.10.0: dependencies: lodash "^4.17.21" -node-fetch-native@^1.4.0, node-fetch-native@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/node-fetch-native/-/node-fetch-native-1.4.1.tgz#5a336e55b4e1b1e72b9927da09fecd2b374c9be5" - integrity sha512-NsXBU0UgBxo2rQLOeWNZqS3fvflWePMECr8CoSWoSTqCqGbVVsvl9vZu1HfQicYN0g5piV9Gh8RTEvo/uP752w== +node-fetch-native@^1.4.0, node-fetch-native@^1.4.1, node-fetch-native@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/node-fetch-native/-/node-fetch-native-1.6.1.tgz#f95c74917d3cebc794cdae0cd2a9c7594aad0cb4" + integrity sha512-bW9T/uJDPAJB2YNYEpWzE54U5O3MQidXsOyTfnbKYtTtFexRvGzb1waphBN4ZwP6EcIvYYEOwW0b72BpAqydTw== -node-fetch@2.7.0, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.12, node-fetch@^2.6.7, node-fetch@^2.6.8: +node-fetch@2.7.0, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.12, node-fetch@^2.6.7, node-fetch@^2.6.8, node-fetch@^2.7.0: version "2.7.0" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== @@ -19523,9 +19566,9 @@ node-forge@^1.0.0, node-forge@^1.3.1: integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== node-gyp-build@^4.2.0, node-gyp-build@^4.3.0: - version "4.7.1" - resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.7.1.tgz#cd7d2eb48e594874053150a9418ac85af83ca8f7" - integrity sha512-wTSrZ+8lsRRa3I3H8Xr65dLWSgCvY2l4AOnaeKdPA9TB/WYMPaTcrzf3rXvFoVvjKNVnu0CcWSx54qq9GKRUYg== + version "4.8.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.0.tgz#3fee9c1731df4581a3f9ead74664369ff00d26dd" + integrity sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og== node-int64@^0.4.0: version "0.4.0" @@ -19685,9 +19728,9 @@ npm-run-path@^4.0.0, npm-run-path@^4.0.1: path-key "^3.0.0" npm-run-path@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.1.0.tgz#bc62f7f3f6952d9894bd08944ba011a6ee7b7e00" - integrity sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q== + version "5.2.0" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.2.0.tgz#224cdd22c755560253dd71b83a1ef2f758b2e955" + integrity sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg== dependencies: path-key "^4.0.0" @@ -20473,7 +20516,7 @@ path-root@^0.1.1: dependencies: path-root-regex "^0.1.0" -path-scurry@^1.6.1: +path-scurry@^1.10.1, path-scurry@^1.6.1: version "1.10.1" resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.10.1.tgz#9ba6bf5aa8500fe9fd67df4f0d9483b2b0bfc698" integrity sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ== @@ -20774,9 +20817,9 @@ postcss-modules-local-by-default@^4.0.3: postcss-value-parser "^4.1.0" postcss-modules-scope@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz#9ef3151456d3bbfa120ca44898dfca6f2fa01f06" - integrity sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg== + version "3.1.0" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.1.0.tgz#fbfddfda93a31f310f1d152c2bb4d3f3c5592ee0" + integrity sha512-SaIbK8XW+MZbd0xHPf7kdfA/3eOt7vxJ72IRecn3EzuZVLr1r0orzf0MX/pN8m+NMDoo6X/SQd8oeKqGZd8PXg== dependencies: postcss-selector-parser "^6.0.4" @@ -20788,9 +20831,9 @@ postcss-modules-values@^4.0.0: icss-utils "^5.0.0" postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4: - version "6.0.13" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz#d05d8d76b1e8e173257ef9d60b706a8e5e99bf1b" - integrity sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ== + version "6.0.15" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz#11cc2b21eebc0b99ea374ffb9887174855a01535" + integrity sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw== dependencies: cssesc "^3.0.0" util-deprecate "^1.0.2" @@ -20801,9 +20844,9 @@ postcss-value-parser@^4.0.2, postcss-value-parser@^4.1.0, postcss-value-parser@^ integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== postcss@^8.3.11, postcss@^8.4.13, postcss@^8.4.21, postcss@^8.4.27, postcss@^8.4.32: - version "8.4.32" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.32.tgz#1dac6ac51ab19adb21b8b34fd2d93a86440ef6c9" - integrity sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw== + version "8.4.33" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.33.tgz#1378e859c9f69bf6f638b990a0212f43e2aaa742" + integrity sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg== dependencies: nanoid "^3.3.7" picocolors "^1.0.0" @@ -20880,13 +20923,13 @@ prettier-plugin-packagejson@2.4.5: synckit "0.8.5" prettier-plugin-solidity@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/prettier-plugin-solidity/-/prettier-plugin-solidity-1.2.0.tgz#dc620b4fc7708a60687a87cdc803e57a1856b6fd" - integrity sha512-fgxcUZpVAP+LlRfy5JI5oaAkXGkmsje2VJ5krv/YMm+rcTZbIUwFguSw5f+WFuttMjpDm6wB4UL7WVkArEfiVA== + version "1.3.1" + resolved "https://registry.yarnpkg.com/prettier-plugin-solidity/-/prettier-plugin-solidity-1.3.1.tgz#59944d3155b249f7f234dee29f433524b9a4abcf" + integrity sha512-MN4OP5I2gHAzHZG1wcuJl0FsLS3c4Cc5494bbg+6oQWBPuEamjwDvmGfFMZ6NFzsh3Efd9UUxeT7ImgjNH4ozA== dependencies: - "@solidity-parser/parser" "^0.16.2" + "@solidity-parser/parser" "^0.17.0" semver "^7.5.4" - solidity-comments-extractor "^0.0.7" + solidity-comments-extractor "^0.0.8" prettier@2.8.4: version "2.8.4" @@ -21501,12 +21544,12 @@ react-router-dom@5.3.4: tiny-warning "^1.0.0" react-router-dom@^6.14.1, react-router-dom@^6.4.3: - version "6.21.0" - resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.21.0.tgz#aa4c6bc046a8e8723095bc09b3c0ab2254532712" - integrity sha512-1dUdVj3cwc1npzJaf23gulB562ESNvxf7E4x8upNJycqyUm5BRRZ6dd3LrlzhtLaMrwOCO8R0zoiYxdaJx4LlQ== + version "6.21.1" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.21.1.tgz#58b459d2fe1841388c95bb068f85128c45e27349" + integrity sha512-QCNrtjtDPwHDO+AO21MJd7yIcr41UetYt5jzaB9Y1UYaPTCnVuJq6S748g1dE11OQlCFIQg+RtAA1SEZIyiBeA== dependencies: - "@remix-run/router" "1.14.0" - react-router "6.21.0" + "@remix-run/router" "1.14.1" + react-router "6.21.1" react-router@5.3.4: version "5.3.4" @@ -21523,12 +21566,12 @@ react-router@5.3.4: tiny-invariant "^1.0.2" tiny-warning "^1.0.0" -react-router@6.21.0: - version "6.21.0" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.21.0.tgz#6fe3e59877aca3dccceec1801d26991ddf42d12b" - integrity sha512-hGZ0HXbwz3zw52pLZV3j3+ec+m/PQ9cTpBvqjFQmy2XVUWGn5MD+31oXHb6dVTxYzmAeaiUBYjkoNz66n3RGCg== +react-router@6.21.1: + version "6.21.1" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.21.1.tgz#8db7ee8d7cfc36513c9a66b44e0897208c33be34" + integrity sha512-W0l13YlMTm1YrpVIOpjCADJqEUpz1vm+CMo47RuFX4Ftegwm6KOYsL5G3eiE52jnJpKvzm6uB/vTKTPKM8dmkA== dependencies: - "@remix-run/router" "1.14.0" + "@remix-run/router" "1.14.1" react-select@5.7.0: version "5.7.0" @@ -22266,23 +22309,25 @@ rollup@^3.27.1: fsevents "~2.3.2" rollup@^4.2.0: - version "4.9.1" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.9.1.tgz#351d6c03e4e6bcd7a0339df3618d2aeeb108b507" - integrity sha512-pgPO9DWzLoW/vIhlSoDByCzcpX92bKEorbgXuZrqxByte3JFk2xSW2JEeAcyLc9Ru9pqcNNW+Ob7ntsk2oT/Xw== + version "4.9.4" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.9.4.tgz#37bc0c09ae6b4538a9c974f4d045bb64b2e7c27c" + integrity sha512-2ztU7pY/lrQyXSCnnoU4ICjT/tCG9cdH3/G25ERqE3Lst6vl2BCM5hL2Nw+sslAvAf+ccKsAq1SkKQALyqhR7g== + dependencies: + "@types/estree" "1.0.5" optionalDependencies: - "@rollup/rollup-android-arm-eabi" "4.9.1" - "@rollup/rollup-android-arm64" "4.9.1" - "@rollup/rollup-darwin-arm64" "4.9.1" - "@rollup/rollup-darwin-x64" "4.9.1" - "@rollup/rollup-linux-arm-gnueabihf" "4.9.1" - "@rollup/rollup-linux-arm64-gnu" "4.9.1" - "@rollup/rollup-linux-arm64-musl" "4.9.1" - "@rollup/rollup-linux-riscv64-gnu" "4.9.1" - "@rollup/rollup-linux-x64-gnu" "4.9.1" - "@rollup/rollup-linux-x64-musl" "4.9.1" - "@rollup/rollup-win32-arm64-msvc" "4.9.1" - "@rollup/rollup-win32-ia32-msvc" "4.9.1" - "@rollup/rollup-win32-x64-msvc" "4.9.1" + "@rollup/rollup-android-arm-eabi" "4.9.4" + "@rollup/rollup-android-arm64" "4.9.4" + "@rollup/rollup-darwin-arm64" "4.9.4" + "@rollup/rollup-darwin-x64" "4.9.4" + "@rollup/rollup-linux-arm-gnueabihf" "4.9.4" + "@rollup/rollup-linux-arm64-gnu" "4.9.4" + "@rollup/rollup-linux-arm64-musl" "4.9.4" + "@rollup/rollup-linux-riscv64-gnu" "4.9.4" + "@rollup/rollup-linux-x64-gnu" "4.9.4" + "@rollup/rollup-linux-x64-musl" "4.9.4" + "@rollup/rollup-win32-arm64-msvc" "4.9.4" + "@rollup/rollup-win32-ia32-msvc" "4.9.4" + "@rollup/rollup-win32-x64-msvc" "4.9.4" fsevents "~2.3.2" rpc-websockets@^7.5.1: @@ -22547,9 +22592,9 @@ serialize-javascript@6.0.0: randombytes "^2.1.0" serialize-javascript@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.1.tgz#b206efb27c3da0b0ab6b52f48d170b7996458e5c" - integrity sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w== + version "6.0.2" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" + integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== dependencies: randombytes "^2.1.0" @@ -22715,7 +22760,7 @@ shelljs@0.8.5, shelljs@^0.8.3: interpret "^1.0.0" rechoir "^0.6.2" -shiki@^0.14.1: +shiki@^0.14.7: version "0.14.7" resolved "https://registry.yarnpkg.com/shiki/-/shiki-0.14.7.tgz#c3c9e1853e9737845f1d2ef81b31bcfb07056d4e" integrity sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg== @@ -22749,7 +22794,7 @@ signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== -signal-exit@^4.1.0: +signal-exit@^4.0.1, signal-exit@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== @@ -22819,12 +22864,12 @@ sinon@^7.2.3: supports-color "^5.5.0" sirv@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/sirv/-/sirv-2.0.3.tgz#ca5868b87205a74bef62a469ed0296abceccd446" - integrity sha512-O9jm9BsID1P+0HOi81VpXPoDxYP374pkOLzACAoyUQ/3OUVndNpsz6wMnY2z+yOxzbllCKZrM+9QrWsv4THnyA== + version "2.0.4" + resolved "https://registry.yarnpkg.com/sirv/-/sirv-2.0.4.tgz#5dd9a725c578e34e449f332703eb2a74e46a29b0" + integrity sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ== dependencies: - "@polka/url" "^1.0.0-next.20" - mrmime "^1.0.0" + "@polka/url" "^1.0.0-next.24" + mrmime "^2.0.0" totalist "^3.0.0" sisteransi@^1.0.5: @@ -22957,12 +23002,12 @@ solidity-ast@^0.4.51: dependencies: array.prototype.findlast "^1.2.2" -solidity-comments-extractor@^0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/solidity-comments-extractor/-/solidity-comments-extractor-0.0.7.tgz#99d8f1361438f84019795d928b931f4e5c39ca19" - integrity sha512-wciNMLg/Irp8OKGrh3S2tfvZiZ0NEyILfcRCXCD4mp7SgK/i9gzLfhY2hY7VMCQJ3kH9UB9BzNdibIVMchzyYw== +solidity-comments-extractor@^0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/solidity-comments-extractor/-/solidity-comments-extractor-0.0.8.tgz#f6e148ab0c49f30c1abcbecb8b8df01ed8e879f8" + integrity sha512-htM7Vn6LhHreR+EglVMd2s+sZhcXAirB1Zlyrv5zBuTxieCvjfnRpd7iZk75m/u6NOlEyQ94C6TWbBn2cY7w8g== -solidity-coverage@^0.8.1, solidity-coverage@^0.8.2: +solidity-coverage@^0.8.2: version "0.8.5" resolved "https://registry.yarnpkg.com/solidity-coverage/-/solidity-coverage-0.8.5.tgz#64071c3a0c06a0cecf9a7776c35f49edc961e875" integrity sha512-6C6N6OV2O8FQA0FWA95FdzVH+L16HU94iFgg5wAFZ29UpLFkgNI/DRR2HotG1bC0F4gAc/OMs2BJI44Q/DYlKQ== @@ -23218,9 +23263,9 @@ statuses@2.0.1, statuses@^2.0.1: integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== std-env@^3.3.2, std-env@^3.4.3: - version "3.6.0" - resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.6.0.tgz#94807562bddc68fa90f2e02c5fd5b6865bb4e98e" - integrity sha512-aFZ19IgVmhdB2uX599ve2kE6BIE3YMnQ6Gp6BURhW/oIzpXGKr878TQfAQZn1+i0Flcc/UKUy1gOlcfaUBCryg== + version "3.7.0" + resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.7.0.tgz#c9f7386ced6ecf13360b6c6c55b8aaa4ef7481d2" + integrity sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg== stop-iteration-iterator@^1.0.0: version "1.0.0" @@ -23332,7 +23377,7 @@ string-natural-compare@^3.0.1: resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4" integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw== -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -23428,6 +23473,13 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" @@ -23449,13 +23501,6 @@ strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.1, strip-ansi@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -23515,9 +23560,9 @@ strip-literal@^1.0.1: acorn "^8.10.0" stripe@^14.8.0: - version "14.9.0" - resolved "https://registry.yarnpkg.com/stripe/-/stripe-14.9.0.tgz#e814a3da09f47258bec87e24516c3f3e33041148" - integrity sha512-t2XdpNbRH4x3MYEoxNWhwUPl9D80aUd5OHds0zhDiwRYPZ0H7MrUI/dj9wOSYlzycD3xdvjn0q7pWeFWljtMUQ== + version "14.11.0" + resolved "https://registry.yarnpkg.com/stripe/-/stripe-14.11.0.tgz#1df63c31bcff3b136457c2b7584f917509e8030c" + integrity sha512-NmFEkDC0PldP7CQtdPgKs5dVZA/pF+IepldbmB+Kk9B4d7EBkWnbANp0y+/zJcbRGul48s8hmQzeqNWUlWW0wg== dependencies: "@types/node" ">=8.1.0" qs "^6.11.0" @@ -23710,12 +23755,12 @@ synckit@0.8.5: "@pkgr/utils" "^2.3.1" tslib "^2.5.0" -synckit@^0.8.5: - version "0.8.6" - resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.8.6.tgz#b69b7fbce3917c2673cbdc0d87fb324db4a5b409" - integrity sha512-laHF2savN6sMeHCjLRkheIU4wo3Zg9Ln5YOjOo7sZ5dVQW8yF5pPE5SIw1dsPhq3TRp1jisKRCdPhfs/1WMqDA== +synckit@^0.8.6: + version "0.8.8" + resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.8.8.tgz#fe7fe446518e3d3d49f5e429f443cf08b6edfcd7" + integrity sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ== dependencies: - "@pkgr/utils" "^2.4.2" + "@pkgr/core" "^0.1.0" tslib "^2.6.2" table-layout@^1.0.2: @@ -23861,6 +23906,19 @@ tenderly@^0.7.0: prompts "^2.4.2" tslog "^4.4.0" +tenderly@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/tenderly/-/tenderly-0.8.0.tgz#ffda1f40597b91470e728900e3bdfe6a4f152ec5" + integrity sha512-4Faw9jkwMuBOva82lAtvhTa9isc503GkWwVWSsR8ONm+i3SeFatv7hNyYPZIifQBeuU9GOVNkWHCAXon6NE/aw== + dependencies: + axios "^0.27.2" + cli-table3 "^0.6.2" + commander "^9.4.0" + js-yaml "^4.1.0" + open "^8.4.0" + prompts "^2.4.2" + tslog "^4.4.0" + terminal-link@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" @@ -23870,17 +23928,17 @@ terminal-link@^2.0.0: supports-hyperlinks "^2.0.0" terser-webpack-plugin@^5.3.7: - version "5.3.9" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz#832536999c51b46d468067f9e37662a3b96adfe1" - integrity sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA== + version "5.3.10" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz#904f4c9193c6fd2a03f693a2150c62a92f40d199" + integrity sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w== dependencies: - "@jridgewell/trace-mapping" "^0.3.17" + "@jridgewell/trace-mapping" "^0.3.20" jest-worker "^27.4.5" schema-utils "^3.1.1" serialize-javascript "^6.0.1" - terser "^5.16.8" + terser "^5.26.0" -terser@^5.10.0, terser@^5.16.8: +terser@^5.10.0, terser@^5.26.0: version "5.26.0" resolved "https://registry.yarnpkg.com/terser/-/terser-5.26.0.tgz#ee9f05d929f4189a9c28a0feb889d96d50126fe1" integrity sha512-dytTGoE2oHgbNV9nTzgBEPaqAWvcJNl66VZ0BkJqlvp71IjO8CxdBx/ykCNb47cLnCmCvRZ6ZR0tLkqvZCdVBQ== @@ -24273,7 +24331,7 @@ ts-loader@^9.2.3: semver "^7.3.4" source-map "^0.7.4" -ts-node@>=8.0.0, ts-node@^10.9.1, ts-node@^10.9.2: +ts-node@^10.9.1, ts-node@^10.9.2: version "10.9.2" resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== @@ -24466,7 +24524,7 @@ type@^2.7.2: resolved "https://registry.yarnpkg.com/type/-/type-2.7.2.tgz#2376a15a3a28b1efa0f5350dcf72d24df6ef98d0" integrity sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw== -typechain@^8.1.0, typechain@^8.1.1: +typechain@^8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/typechain/-/typechain-8.3.2.tgz#1090dd8d9c57b6ef2aed3640a516bdbf01b00d73" integrity sha512-x/sQYr5w9K7yv3es7jo4KTX05CLxOf7TRWwoHlrjRh8H82G64g+k7VuWPJlgMo6qrjfCulOdfBjiaDtmhFYD/Q== @@ -24541,14 +24599,14 @@ typedoc-plugin-markdown@^3.16.0: handlebars "^4.7.7" typedoc@^0.25.1: - version "0.25.4" - resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.25.4.tgz#5c2c0677881f504e41985f29d9aef0dbdb6f1e6f" - integrity sha512-Du9ImmpBCw54bX275yJrxPVnjdIyJO/84co0/L9mwe0R3G4FSR6rQ09AlXVRvZEGMUg09+z/usc8mgygQ1aidA== + version "0.25.7" + resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.25.7.tgz#11e3f527ca80ca3c029cb8e15f362e6d9f715e25" + integrity sha512-m6A6JjQRg39p2ZVRIN3NKXgrN8vzlHhOS+r9ymUYtcUP/TIQPvWSq7YgE5ZjASfv5Vd5BW5xrir6Gm2XNNcOow== dependencies: lunr "^2.3.9" marked "^4.3.0" minimatch "^9.0.3" - shiki "^0.14.1" + shiki "^0.14.7" typeorm-naming-strategies@^4.1.0: version "4.1.0" @@ -24556,19 +24614,19 @@ typeorm-naming-strategies@^4.1.0: integrity sha512-vPekJXzZOTZrdDvTl1YoM+w+sUIfQHG4kZTpbFYoTsufyv9NIBRe4Q+PdzhEAFA2std3D9LZHEb1EjE9zhRpiQ== typeorm@^0.3.16, typeorm@^0.3.17: - version "0.3.17" - resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.3.17.tgz#a73c121a52e4fbe419b596b244777be4e4b57949" - integrity sha512-UDjUEwIQalO9tWw9O2A4GU+sT3oyoUXheHJy4ft+RFdnRdQctdQ34L9SqE2p7LdwzafHx1maxT+bqXON+Qnmig== + version "0.3.19" + resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.3.19.tgz#a985ce8ae36d266018e44fed5e27a4a5da34ad2a" + integrity sha512-OGelrY5qEoAU80mR1iyvmUHiKCPUydL6xp6bebXzS7jyv/X70Gp/jBWRAfF4qGOfy2A7orMiGRfwsBUNbEL65g== dependencies: "@sqltools/formatter" "^1.2.5" app-root-path "^3.1.0" buffer "^6.0.3" chalk "^4.1.2" cli-highlight "^2.1.11" - date-fns "^2.29.3" + dayjs "^1.11.9" debug "^4.3.4" dotenv "^16.0.3" - glob "^8.1.0" + glob "^10.3.10" mkdirp "^2.1.3" reflect-metadata "^0.1.13" sha.js "^2.4.11" @@ -24586,7 +24644,7 @@ typescript@5.2.2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.2.tgz#5ebb5e5a5b75f085f22bc3f8460fba308310fa78" integrity sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w== -typescript@>=4.5.0, typescript@^5.0.0, typescript@^5.2.2: +typescript@^5.0.0, typescript@^5.2.2: version "5.3.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.3.tgz#b3ce6ba258e72e6305ba66f5c9b452aaee3ffe37" integrity sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw== @@ -24679,22 +24737,22 @@ undici-types@~5.26.4: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== -undici@^5.14.0: +undici@^5.14.0, undici@^5.28.2: version "5.28.2" resolved "https://registry.yarnpkg.com/undici/-/undici-5.28.2.tgz#fea200eac65fc7ecaff80a023d1a0543423b4c91" integrity sha512-wh1pHJHnUeQV5Xa8/kyQhO7WFa8M34l026L5P/+2TYiakvGy5Rdc8jWZVyG7ieht/0WgJLEd3kcU5gKx+6GC8w== dependencies: "@fastify/busboy" "^2.0.0" -unenv@^1.7.4: - version "1.8.0" - resolved "https://registry.yarnpkg.com/unenv/-/unenv-1.8.0.tgz#0f860d5278405700bd95d47b23bc01f3a735d68c" - integrity sha512-uIGbdCWZfhRRmyKj1UioCepQ0jpq638j/Cf0xFTn4zD1nGJ2lSdzYHLzfdXN791oo/0juUiSWW1fBklXMTsuqg== +unenv@^1.8.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/unenv/-/unenv-1.9.0.tgz#469502ae85be1bd3a6aa60f810972b1a904ca312" + integrity sha512-QKnFNznRxmbOF1hDgzpqrlIf6NC5sbZ2OJ+5Wl3OX8uM+LUJXbj4TXvLJCtwbPTmbMHCLIz6JLKNinNsMShK9g== dependencies: consola "^3.2.3" defu "^6.1.3" mime "^3.0.0" - node-fetch-native "^1.4.1" + node-fetch-native "^1.6.1" pathe "^1.1.1" unfetch@^4.2.0: @@ -24906,9 +24964,9 @@ urlpattern-polyfill@^8.0.0: integrity sha512-Qp95D4TPJl1kC9SKigDcqgyM2VDVO4RiJc2d4qe5GrYm+zbIQCWWKAFaJNQ4BhdFeDGwBmAxqJBwWSJDb9T3BQ== use-callback-ref@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/use-callback-ref/-/use-callback-ref-1.3.0.tgz#772199899b9c9a50526fedc4993fc7fa1f7e32d5" - integrity sha512-3FT9PRuRdbB9HfXhEq35u4oZkvpJ5kuYbpqhCfmiZyReuRgpnhDlbr2ZEnnuS0RrJAPn6l23xjFg9kpDM+Ms7w== + version "1.3.1" + resolved "https://registry.yarnpkg.com/use-callback-ref/-/use-callback-ref-1.3.1.tgz#9be64c3902cbd72b07fe55e56408ae3a26036fd0" + integrity sha512-Lg4Vx1XZQauB42Hw3kK7JM6yjVjgFmFC5/Ab797s79aARomD2nEErc4mCgM8EZrARLmmbWpi5DGCadmK50DcAQ== dependencies: tslib "^2.0.0" @@ -25109,9 +25167,9 @@ victory-vendor@^36.6.8: d3-timer "^3.0.1" viem@^1.16.6: - version "1.20.2" - resolved "https://registry.yarnpkg.com/viem/-/viem-1.20.2.tgz#42455f003bef012fcac7e51e510d50fa972d969a" - integrity sha512-f+duq2Gs38REUVsKaEmNq+JFcvXaYtaoZdw43QUAZEvm7D4iwQDkfDSMvsYSwCSUJC8rUUqX3JFcXZm03mYWlQ== + version "1.21.4" + resolved "https://registry.yarnpkg.com/viem/-/viem-1.21.4.tgz#883760e9222540a5a7e0339809202b45fe6a842d" + integrity sha512-BNVYdSaUjeS2zKQgPs+49e5JKocfo60Ib2yiXOWBT6LuVxY1I/6fFX3waEtpXvL1Xn4qu+BVitVtMh9lyThyhQ== dependencies: "@adraffy/ens-normalize" "1.10.0" "@noble/curves" "1.2.0" @@ -25209,9 +25267,9 @@ vite@^2.2.0: fsevents "~2.3.2" vite@^5.0.9: - version "5.0.10" - resolved "https://registry.yarnpkg.com/vite/-/vite-5.0.10.tgz#1e13ef5c3cf5aa4eed81f5df6d107b3c3f1f6356" - integrity sha512-2P8J7WWgmc355HUMlFrwofacvr98DAjoE52BfdbwQtyLH06XKwaL/FMnmKM2crF0iX4MpmMKoDlNCB1ok7zHCw== + version "5.0.11" + resolved "https://registry.yarnpkg.com/vite/-/vite-5.0.11.tgz#31562e41e004cb68e1d51f5d2c641ab313b289e4" + integrity sha512-XBMnDjZcNAw/G1gEiskiM1v6yzM4GE5aMGvhWTlHAYYhxb7S3/V1s3m2LDHa8Vh6yIWYYB0iJwsEaS523c4oYA== dependencies: esbuild "^0.19.3" postcss "^8.4.32" @@ -25341,9 +25399,9 @@ web-encoding@1.1.5, web-encoding@^1.1.5: "@zxing/text-encoding" "0.9.0" web-streams-polyfill@^3.1.1, web-streams-polyfill@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz#71c2718c52b45fd49dbeee88634b3a60ceab42a6" - integrity sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q== + version "3.3.2" + resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.3.2.tgz#32e26522e05128203a7de59519be3c648004343b" + integrity sha512-3pRGuxRF5gpuZc0W+EpwQRmCD7gRqcDOMt688KmdlDAgAyaB1XlN0zq2njfDNm44XVdIouE7pZ6GzbdyH47uIQ== web-vitals@^3.5.1: version "3.5.1" @@ -25999,25 +26057,25 @@ workerpool@6.2.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== -wrap-ansi@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" - integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: ansi-styles "^4.0.0" string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== dependencies: ansi-styles "^4.0.0" string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^8.0.1: +wrap-ansi@^8.0.1, wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== @@ -26088,14 +26146,14 @@ ws@^7.3.1, ws@^7.4.5, ws@^7.4.6, ws@^7.5.1: integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== ws@^8.11.0, ws@^8.12.0, ws@^8.13.0, ws@^8.5.0: - version "8.15.1" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.15.1.tgz#271ba33a45ca0cc477940f7f200cd7fba7ee1997" - integrity sha512-W5OZiCjXEmk0yZ66ZN82beM5Sz7l7coYxpRkzS+p9PP+ToQry8szKh+61eNktr7EA9DOwvFGhfC605jDHbP6QQ== + version "8.16.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.16.0.tgz#d1cd774f36fbc07165066a60e40323eab6446fd4" + integrity sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ== -xdeployer@1.2.7: - version "1.2.7" - resolved "https://registry.yarnpkg.com/xdeployer/-/xdeployer-1.2.7.tgz#371af982147e57f9712d120cedce529b4f43a951" - integrity sha512-953WaHUeQ1Rtigq0R5/8Rm4utYxvgHFqANbHG3DF/C2MRWyA8Jg/769lDNVP6mC8tG+S1P19cw6070j7Guo4Ew== +xdeployer@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/xdeployer/-/xdeployer-3.0.0.tgz#f39753c52fbeea10877652f99dc70cce47e814e5" + integrity sha512-YG5YsprDGDF3tXw9zDizsp2EdoEy8FYtl1ta72yaOnmCgbkP6DWAvwhavr59xfapWJg8cLqGasxa9h5YwL1TOA== xdg-basedir@^4.0.0: version "4.0.0" From ffc05f8ff39c153bcce0374f57681202ccc97604 Mon Sep 17 00:00:00 2001 From: Eric Lee <98655210+leric7@users.noreply.github.com> Date: Tue, 9 Jan 2024 19:58:19 +0800 Subject: [PATCH 022/104] feat: update web3 version (#1430) * update web3 version * fix yarn lock --- packages/apps/faucet-server/package.json | 2 +- .../faucet-server/src/services/web3.test.ts | 62 +- .../apps/faucet-server/src/services/web3.ts | 63 +- yarn.lock | 980 +++++------------- 4 files changed, 313 insertions(+), 794 deletions(-) diff --git a/packages/apps/faucet-server/package.json b/packages/apps/faucet-server/package.json index a4aa306c3a..db06c13100 100644 --- a/packages/apps/faucet-server/package.json +++ b/packages/apps/faucet-server/package.json @@ -21,7 +21,7 @@ "express": "^4.18.1", "express-rate-limit": "^7.1.3", "node-cache": "^5.1.2", - "web3": "^1.10.2" + "web3": "^4.3.0" }, "devDependencies": { "@types/cors": "^2.8.17", diff --git a/packages/apps/faucet-server/src/services/web3.test.ts b/packages/apps/faucet-server/src/services/web3.test.ts index 38b5913aa3..33ad996b4e 100644 --- a/packages/apps/faucet-server/src/services/web3.test.ts +++ b/packages/apps/faucet-server/src/services/web3.test.ts @@ -1,12 +1,20 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +/** + * TODO: Remove eslint rule for explict any after the PR is merged + * Known issue for web3 v4 unknown ABI + * https://github.com/web3/web3.js/pull/6636 + */ + import dotenv from 'dotenv'; dotenv.config({ path: `.env.development` }); -import HMToken from '@human-protocol/core/artifacts/contracts/HMToken.sol//HMToken.json'; +import HMToken from '@human-protocol/core/artifacts/contracts/HMToken.sol/HMToken.json'; import { describe, expect, it } from '@jest/globals'; import { checkFaucetBalance, getFaucetBalance, sendFunds } from './web3'; import { Contract } from 'web3-eth-contract'; import Web3 from 'web3'; -let token: Contract; +let token: Contract; const web3 = new Web3('http://127.0.0.1:8549'); const owner = web3.eth.accounts.privateKeyToAccount( @@ -17,20 +25,18 @@ const externalUser = '0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f'; describe('Faucet', () => { beforeEach(async () => { - const tokenContract = new web3.eth.Contract(HMToken.abi as []); - token = await tokenContract - .deploy({ - data: HMToken.bytecode, - arguments: [ - web3.utils.toWei('100000', 'ether'), - 'Human Token', - 18, - 'HMT', - ], - }) - .send({ - from: owner.address, - }); + const tokenContract = new web3.eth.Contract(HMToken.abi); + token = await (tokenContract.deploy as any)({ + data: HMToken.bytecode, + arguments: [ + web3.utils.toWei('100000', 'ether'), + 'Human Token', + 18, + 'HMT', + ], + }).send({ + from: owner.address, + }); }); it('Check faucet balance before send', async () => { @@ -44,7 +50,9 @@ describe('Faucet', () => { web3, token.options.address ); - const oldUserBalance = await token.methods.balanceOf(externalUser).call(); + const oldUserBalance = await (token.methods.balanceOf as any)( + externalUser + ).call(); await sendFunds(web3, token.options.address, externalUser); @@ -52,26 +60,24 @@ describe('Faucet', () => { web3, token.options.address ); - const newUserBalance = await token.methods.balanceOf(externalUser).call(); + const newUserBalance = await (token.methods.balanceOf as any)( + externalUser + ).call(); expect(Number(newFaucetBalance)).toBe(Number(oldFaucetBalance) - 10); expect(oldUserBalance).toBe( - web3.utils - .toBN(newUserBalance) - .sub(web3.utils.toBN(web3.utils.toWei('10', 'ether'))) - .toString() + web3.utils.toBigInt(newUserBalance) - + web3.utils.toBigInt(web3.utils.toWei('10', 'ether')) ); }); it('Check balance', async () => { expect(await checkFaucetBalance(web3, token.options.address)).toBeTruthy(); - await token.methods - .transfer( - externalUser, - await token.methods.balanceOf(owner.address).call() - ) - .send({ from: owner.address }); + await (token.methods.transfer as any)( + externalUser, + await (token.methods.balanceOf as any)(owner.address).call() + ).send({ from: owner.address }); expect(await checkFaucetBalance(web3, token.options.address)).toBeFalsy(); }); diff --git a/packages/apps/faucet-server/src/services/web3.ts b/packages/apps/faucet-server/src/services/web3.ts index 22dae4f3a3..296306c8ee 100644 --- a/packages/apps/faucet-server/src/services/web3.ts +++ b/packages/apps/faucet-server/src/services/web3.ts @@ -1,8 +1,15 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +/** + * TODO: Remove eslint rule for explict any after the PR is merged + * Known issue for web3 v4 unknown ABI + * https://github.com/web3/web3.js/pull/6636 + */ + import hmtAbi from '@human-protocol/core/abis/HMToken.json'; import Web3 from 'web3'; -import { HttpProvider } from 'web3-core'; -import { ChainId } from '../constants/networks'; +import { ChainId, FAUCET_NETWORKS } from '../constants/networks'; import { AnonymousParams, AnonymousPoW, @@ -29,9 +36,9 @@ export const getFaucetBalance = async ( faucetAddress ) return await web3.eth.getBalance(faucetAddress); - const HMT = new web3.eth.Contract(hmtAbi as [], hmtAddress); + const HMT = new web3.eth.Contract(hmtAbi, hmtAddress); const balance = web3.utils.fromWei( - await HMT.methods.balanceOf(web3.eth.defaultAccount).call(), + await (HMT.methods.balanceOf as any)(web3.eth.defaultAccount).call(), 'ether' ); return balance; @@ -43,16 +50,15 @@ export const sendFunds = async ( toAddress: string, faucetAddress?: string ): Promise => { - const HMT = new web3.eth.Contract(hmtAbi as [], hmtAddress); + const HMT = new web3.eth.Contract(hmtAbi, hmtAddress); let txHash = ''; try { if ( (await web3.eth.getChainId()).toString() === ChainId.SKALE.toString() && faucetAddress ) { - const currentProvider = web3.currentProvider as HttpProvider; const skalePOW = new AnonymousPoW({ - rpcUrl: currentProvider.host, + rpcUrl: FAUCET_NETWORKS[ChainId.SKALE].rpcUrl, } as AnonymousParams); const txParams = { to: faucetAddress, @@ -61,19 +67,19 @@ export const sendFunds = async ( const receipt = await (await skalePOW.send(txParams)).wait(); txHash = receipt.transactionHash; } else { - const gasNeeded = await HMT.methods - .transfer( - toAddress, - Web3.utils.toWei(process.env.DAILY_LIMIT as string, 'ether') - ) - .estimateGas({ from: web3.eth.defaultAccount }); + const gasNeeded = await (HMT.methods.transfer as any)( + toAddress, + Web3.utils.toWei(process.env.DAILY_LIMIT as string, 'ether') + ).estimateGas({ from: web3.eth.defaultAccount }); const gasPrice = await web3.eth.getGasPrice(); - const receipt = await HMT.methods - .transfer( - toAddress, - Web3.utils.toWei(process.env.DAILY_LIMIT as string, 'ether') - ) - .send({ from: web3.eth.defaultAccount, gas: gasNeeded, gasPrice }); + const receipt = await (HMT.methods.transfer as any)( + toAddress, + Web3.utils.toWei(process.env.DAILY_LIMIT as string, 'ether') + ).send({ + from: web3.eth.defaultAccount, + gas: gasNeeded.toString(), + gasPrice: gasPrice.toString(), + }); txHash = receipt.transactionHash; } } catch (err) { @@ -102,22 +108,15 @@ export const checkFaucetBalance = async ( ) return true; - const HMT = new web3.eth.Contract(hmtAbi as [], hmtAddress); - const gasNeeded = await HMT.methods - .transfer( - web3.eth.defaultAccount, - Web3.utils.toWei(process.env.DAILY_LIMIT as string, 'ether') - ) - .estimateGas({ from: web3.eth.defaultAccount }); + const HMT = new web3.eth.Contract(hmtAbi, hmtAddress); + const gasNeeded = await (HMT.methods.transfer as any)( + web3.eth.defaultAccount, + Web3.utils.toWei(process.env.DAILY_LIMIT as string, 'ether') + ).estimateGas({ from: web3.eth.defaultAccount }); const gasPrice = await web3.eth.getGasPrice(); const balance = await web3.eth.getBalance(web3.eth.defaultAccount); - if ( - web3.utils - .toBN(balance) - .cmp(web3.utils.toBN(gasNeeded).mul(web3.utils.toBN(gasPrice))) === -1 - ) - return false; + if (balance < web3.utils.toBigInt(gasNeeded) * gasPrice) return false; return true; }; diff --git a/yarn.lock b/yarn.lock index b5a2e01e8f..2a217dfffd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12,7 +12,7 @@ resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.3.2.tgz#a6abc715fb6884851fca9dad37fc34739a04fd11" integrity sha512-DA5a1C0gD/pLOvhv33YMrbf2FK3oUzwNl9oOJqE4XVjuEtt6XIakRcsd7eLiOSPkp1kTRQGICTA8cKra/vFbjw== -"@adraffy/ens-normalize@1.10.0": +"@adraffy/ens-normalize@1.10.0", "@adraffy/ens-normalize@^1.8.8": version "1.10.0" resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.10.0.tgz#d2a39395c587e092d77cbbc80acf956a54f38bf7" integrity sha512-nA9XHtlAkYfJxY7bce8DcN7eKxWWCWkU+1GR9d+U6MbNpfwQp8TI7vqOsBsMcHoT4mBu2kypKoSKnghEzOOq5Q== @@ -2051,27 +2051,11 @@ resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.56.0.tgz#ef20350fec605a7f7035a01764731b2de0f3782b" integrity sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A== -"@ethereumjs/common@2.6.5", "@ethereumjs/common@^2.6.4": - version "2.6.5" - resolved "https://registry.yarnpkg.com/@ethereumjs/common/-/common-2.6.5.tgz#0a75a22a046272579d91919cb12d84f2756e8d30" - integrity sha512-lRyVQOeCDaIVtgfbowla32pzeDv2Obr8oR8Put5RdUBNRGr1VGPGQNGP6elWIpgK3YdpzqTOh4GyUGOureVeeA== - dependencies: - crc-32 "^1.2.0" - ethereumjs-util "^7.1.5" - "@ethereumjs/rlp@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@ethereumjs/rlp/-/rlp-4.0.1.tgz#626fabfd9081baab3d0a3074b0c7ecaf674aaa41" integrity sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw== -"@ethereumjs/tx@3.5.2": - version "3.5.2" - resolved "https://registry.yarnpkg.com/@ethereumjs/tx/-/tx-3.5.2.tgz#197b9b6299582ad84f9527ca961466fce2296c1c" - integrity sha512-gQDNJWKrSDGu2w7w0PzVXVBNMzb7wwdDOmOqczmhNjqFxFuIbhVJDwiGEnxFNC2/b8ifcZzY7MLcluizohRzNw== - dependencies: - "@ethereumjs/common" "^2.6.4" - ethereumjs-util "^7.1.5" - "@ethereumjs/util@^8.1.0": version "8.1.0" resolved "https://registry.yarnpkg.com/@ethereumjs/util/-/util-8.1.0.tgz#299df97fb6b034e0577ce9f94c7d9d1004409ed4" @@ -2096,7 +2080,7 @@ "@ethersproject/properties" "^5.0.3" "@ethersproject/strings" "^5.0.4" -"@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.0.9", "@ethersproject/abi@^5.1.2", "@ethersproject/abi@^5.5.0", "@ethersproject/abi@^5.6.3", "@ethersproject/abi@^5.7.0": +"@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.0.9", "@ethersproject/abi@^5.1.2", "@ethersproject/abi@^5.5.0", "@ethersproject/abi@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449" integrity sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA== @@ -2371,7 +2355,7 @@ "@ethersproject/constants" "^5.7.0" "@ethersproject/logger" "^5.7.0" -"@ethersproject/transactions@5.7.0", "@ethersproject/transactions@^5.6.2", "@ethersproject/transactions@^5.7.0": +"@ethersproject/transactions@5.7.0", "@ethersproject/transactions@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.7.0.tgz#91318fc24063e057885a6af13fdb703e1f993d3b" integrity sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ== @@ -5303,7 +5287,7 @@ resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== -"@sindresorhus/is@^4.0.0", "@sindresorhus/is@^4.6.0": +"@sindresorhus/is@^4.0.0": version "4.6.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== @@ -6224,13 +6208,6 @@ dependencies: defer-to-connect "^2.0.0" -"@szmarczak/http-timer@^5.0.1": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-5.0.1.tgz#c7c1bf1141cdd4751b0399c8fc7b8b664cd5be3a" - integrity sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw== - dependencies: - defer-to-connect "^2.0.1" - "@tanstack/query-core@4.36.1": version "4.36.1" resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-4.36.1.tgz#79f8c1a539d47c83104210be2388813a7af2e524" @@ -6420,7 +6397,7 @@ dependencies: "@types/node" "*" -"@types/bn.js@^5.1.0", "@types/bn.js@^5.1.1": +"@types/bn.js@^5.1.0": version "5.1.5" resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.5.tgz#2e0dacdcce2c0f16b905d20ff87aedbc6f7b4bf0" integrity sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A== @@ -6435,7 +6412,7 @@ "@types/connect" "*" "@types/node" "*" -"@types/cacheable-request@^6.0.1", "@types/cacheable-request@^6.0.2": +"@types/cacheable-request@^6.0.1": version "6.0.3" resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.3.tgz#a430b3260466ca7b5ca5bfd735693b36e7a9d183" integrity sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw== @@ -6896,7 +6873,7 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b" integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw== -"@types/node@^12.12.54", "@types/node@^12.12.6": +"@types/node@^12.12.54": version "12.20.55" resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240" integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ== @@ -7124,6 +7101,13 @@ resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.11.7.tgz#99e19760297667ae46b7069ec8b96cbfe0a08b98" integrity sha512-q0JomTsJ2I5Mv7dhHhQLGjMvX0JJm5dyZ1DXQySIUzU1UlwzB8bt+R6+LODUbz0UDIOvEzGc28tk27gBJw2N8Q== +"@types/ws@8.5.3": + version "8.5.3" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.3.tgz#7d25a1ffbecd3c4f2d35068d0b283c037003274d" + integrity sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w== + dependencies: + "@types/node" "*" + "@types/ws@^7.4.4": version "7.4.7" resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.7.tgz#f7c390a36f7a0679aa69de2d501319f4f8d9b702" @@ -8111,6 +8095,11 @@ abbrev@1.0.x: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" integrity sha512-LEyx4aLEC3x6T0UguF6YILf+ntvmOaWsVfENmIW0E9H09vKlLDGelMjjSm0jkDHALj8A8quZ/HapKNigzwge+Q== +abitype@0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/abitype/-/abitype-0.7.1.tgz#16db20abe67de80f6183cf75f3de1ff86453b745" + integrity sha512-VBkRHTDZf9Myaek/dO3yMmOzB/y2s3Zo6nVU7yaw1G+TvCHAjwaJzNGN9yo4K5D8bU/VZXKP1EJpRhFr862PlQ== + abitype@0.9.8: version "0.9.8" resolved "https://registry.yarnpkg.com/abitype/-/abitype-0.9.8.tgz#1f120b6b717459deafd213dfbf3a3dd1bf10ae8c" @@ -8128,11 +8117,6 @@ abort-controller@^3.0.0: dependencies: event-target-shim "^5.0.0" -abortcontroller-polyfill@^1.7.5: - version "1.7.5" - resolved "https://registry.yarnpkg.com/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.5.tgz#6738495f4e901fbb57b6c0611d0c75f76c485bed" - integrity sha512-JMJ5soJWP18htbbxJjG7bG6yuI6pRhgJ0scHHTfkUjf6wjP912xZWvM+A4sJK3gqd9E8fcPbDnOefbA9Th/FIQ== - abstract-level@^1.0.0, abstract-level@^1.0.2, abstract-level@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/abstract-level/-/abstract-level-1.0.3.tgz#78a67d3d84da55ee15201486ab44c09560070741" @@ -8770,11 +8754,6 @@ astral-regex@^2.0.0: resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== -async-limiter@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" - integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== - async-mutex@^0.2.6: version "0.2.6" resolved "https://registry.yarnpkg.com/async-mutex/-/async-mutex-0.2.6.tgz#0d7a3deb978bc2b984d5908a2038e1ae2e54ff40" @@ -9082,7 +9061,7 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -base-x@^3.0.2, base-x@^3.0.8: +base-x@^3.0.2: version "3.0.9" resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.9.tgz#6349aaabb58526332de9f60995e548a53fe21320" integrity sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ== @@ -9154,7 +9133,7 @@ bigint-crypto-utils@^3.0.23: resolved "https://registry.yarnpkg.com/bigint-crypto-utils/-/bigint-crypto-utils-3.3.0.tgz#72ad00ae91062cf07f2b1def9594006c279c1d77" integrity sha512-jOTSb+drvEDxEq6OuUybOAv/xxoh3cuYRUIPyu8sSHQNKM303UQ2R1DAo45o1AkcIXw6fzbaFI1+xGGdaXs2lg== -bignumber.js@^9.0.0, bignumber.js@^9.0.1, bignumber.js@^9.1.0: +bignumber.js@^9.0.1, bignumber.js@^9.1.0: version "9.1.2" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.2.tgz#b7c4242259c008903b13707983b5f4bbd31eda0c" integrity sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug== @@ -9254,11 +9233,6 @@ blockstore-core@^1.0.2: it-take "^1.0.1" multiformats "^9.4.7" -bluebird@^3.5.0: - version "3.7.2" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" - integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== - blueimp-md5@^2.10.0: version "2.19.0" resolved "https://registry.yarnpkg.com/blueimp-md5/-/blueimp-md5-2.19.0.tgz#b53feea5498dcb53dc6ec4b823adb84b729c4af0" @@ -9269,7 +9243,7 @@ bn.js@4.11.6: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215" integrity sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA== -bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.0, bn.js@^4.11.6, bn.js@^4.11.8, bn.js@^4.11.9: +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.0, bn.js@^4.11.8, bn.js@^4.11.9: version "4.12.0" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== @@ -9297,7 +9271,7 @@ body-parser@1.20.1: type-is "~1.6.18" unpipe "1.0.0" -body-parser@1.20.2, body-parser@^1.16.0, body-parser@^1.20.0, body-parser@^1.20.2: +body-parser@1.20.2, body-parser@^1.20.0, body-parser@^1.20.2: version "1.20.2" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd" integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA== @@ -9604,11 +9578,6 @@ buffer-reverse@^1.0.1: resolved "https://registry.yarnpkg.com/buffer-reverse/-/buffer-reverse-1.0.1.tgz#49283c8efa6f901bc01fa3304d06027971ae2f60" integrity sha512-M87YIUBsZ6N924W57vDwT/aOu8hw7ZgdByz6ijksLjmHJELBASmYTTlNHRgjE+pTsT9oJXGaDSgqqwfdHotDUg== -buffer-to-arraybuffer@^0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz#6064a40fa76eb43c723aba9ef8f6e1216d10511a" - integrity sha512-3dthu5CYiVB1DEJp61FtApNnNndTckcqe4pFcLdvHtrpG+kcyekCJKg4MRiDcFW7A6AODnXB9U4dwQiCW5kzJQ== - buffer-writer@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/buffer-writer/-/buffer-writer-2.0.0.tgz#ce7eb81a38f7829db09c873f2fbb792c0c98ec04" @@ -9636,7 +9605,7 @@ buffer@6.0.3, buffer@^6.0.1, buffer@^6.0.3, buffer@~6.0.3: base64-js "^1.3.1" ieee754 "^1.2.1" -buffer@^5.0.5, buffer@^5.1.0, buffer@^5.5.0, buffer@^5.6.0, buffer@^5.7.1: +buffer@^5.1.0, buffer@^5.5.0, buffer@^5.7.1: version "5.7.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== @@ -9735,11 +9704,6 @@ cacheable-lookup@^5.0.3: resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== -cacheable-lookup@^6.0.4: - version "6.1.0" - resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-6.1.0.tgz#0330a543471c61faa4e9035db583aad753b36385" - integrity sha512-KJ/Dmo1lDDhmW2XDPMo+9oiy/CeqosPguPCrgcVzKyZrL6pM1gU2GmPY/xo6OQPTUaA/c0kwHuywB4E6nmT9ww== - cacheable-request@^7.0.2: version "7.0.4" resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.4.tgz#7a33ebf08613178b403635be7b899d3e69bbe817" @@ -10033,7 +9997,7 @@ chokidar@3.5.3, chokidar@^3.4.0, chokidar@^3.5.2, chokidar@^3.5.3: optionalDependencies: fsevents "~2.3.2" -chownr@^1.0.1, chownr@^1.1.1, chownr@^1.1.4: +chownr@^1.0.1, chownr@^1.1.1: version "1.1.4" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== @@ -10063,17 +10027,6 @@ ci-info@^3.2.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== -cids@^0.7.1: - version "0.7.5" - resolved "https://registry.yarnpkg.com/cids/-/cids-0.7.5.tgz#60a08138a99bfb69b6be4ceb63bfef7a396b28b2" - integrity sha512-zT7mPeghoWAu+ppn8+BS1tQ5qGmbMfB4AregnQjA/qHY3GC1m1ptI9GkWNlgeu38r7CuRdXB47uY2XgAYt6QVA== - dependencies: - buffer "^5.5.0" - class-is "^1.1.0" - multibase "~0.6.0" - multicodec "^1.0.0" - multihashes "~0.4.15" - cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" @@ -10094,11 +10047,6 @@ cjs-module-lexer@^1.0.0: resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz#6c370ab19f8a3394e318fe682686ec0ac684d107" integrity sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ== -class-is@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/class-is/-/class-is-1.1.0.tgz#9d3c0fba0440d211d843cec3dedfa48055005825" - integrity sha512-rhjH9AG1fvabIDoGRVH587413LPjTZgmDF9fOFCbFJQV4yuocX1mHxxvXI4g3cGwbVY9wAYIoKlg1N79frJKQw== - class-transformer@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/class-transformer/-/class-transformer-0.5.1.tgz#24147d5dffd2a6cea930a3250a677addf96ab336" @@ -10700,15 +10648,6 @@ content-disposition@0.5.4, content-disposition@~0.5.2: dependencies: safe-buffer "5.2.1" -content-hash@^2.5.2: - version "2.5.2" - resolved "https://registry.yarnpkg.com/content-hash/-/content-hash-2.5.2.tgz#bbc2655e7c21f14fd3bfc7b7d4bfe6e454c9e211" - integrity sha512-FvIQKy0S1JaWV10sMsA7TRx8bpU+pqPkhbsfvOJAdjRXvYxEckAwQWGwtRjiaJfh+E0DvcWUGqcdjwMGFjsSdw== - dependencies: - cids "^0.7.1" - multicodec "^0.5.5" - multihashes "^0.4.15" - content-type@^1.0.4, content-type@~1.0.4, content-type@~1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" @@ -10822,7 +10761,7 @@ core-util-is@^1.0.2, core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== -cors@2.8.5, cors@^2.8.1, cors@^2.8.5: +cors@2.8.5, cors@^2.8.5: version "2.8.5" resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== @@ -10873,7 +10812,7 @@ cosmiconfig@^7.0.0, cosmiconfig@^7.0.1: path-type "^4.0.0" yaml "^1.10.0" -crc-32@^1.2.0: +crc-32@^1.2.0, crc-32@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff" integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ== @@ -11205,14 +11144,6 @@ d3-timer@^3.0.1: resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-3.0.1.tgz#6284d2a2708285b1abb7e201eda4380af35e63b0" integrity sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA== -d@1, d@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" - integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== - dependencies: - es5-ext "^0.10.50" - type "^1.0.1" - damerau-levenshtein@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" @@ -11345,13 +11276,6 @@ decode-uri-component@^0.2.0, decode-uri-component@^0.2.2: resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== -decompress-response@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" - integrity sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA== - dependencies: - mimic-response "^1.0.0" - decompress-response@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" @@ -11457,7 +11381,7 @@ defaults@^1.0.3: dependencies: clone "^1.0.2" -defer-to-connect@^2.0.0, defer-to-connect@^2.0.1: +defer-to-connect@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== @@ -11833,11 +11757,6 @@ dom-serializer@^2.0.0: domhandler "^5.0.2" entities "^4.2.0" -dom-walk@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84" - integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w== - domain-browser@^4.22.0: version "4.23.0" resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-4.23.0.tgz#427ebb91efcb070f05cffdfb8a4e9a6c25f8c94b" @@ -12010,7 +11929,7 @@ electron-to-chromium@^1.4.601: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.625.tgz#a9a1d18ee911f9074a9c42d9e84b1c79b29f4059" integrity sha512-DENMhh3MFgaPDoXWrVIqSPInQoLImywfCwrSmVl3cf9QHzoZSiutHwGaB/Ql3VkqcQV30rzgdM+BjKqBAJxo5Q== -elliptic@6.5.4, elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3, elliptic@^6.5.4: +elliptic@6.5.4, elliptic@^6.5.2, elliptic@^6.5.3, elliptic@^6.5.4: version "6.5.4" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== @@ -12259,25 +12178,7 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" -es5-ext@^0.10.35, es5-ext@^0.10.50: - version "0.10.62" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.62.tgz#5e6adc19a6da524bf3d1e02bbc8960e5eb49a9a5" - integrity sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA== - dependencies: - es6-iterator "^2.0.3" - es6-symbol "^3.1.3" - next-tick "^1.1.0" - -es6-iterator@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" - integrity sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g== - dependencies: - d "1" - es5-ext "^0.10.35" - es6-symbol "^3.1.1" - -es6-promise@^4.0.3, es6-promise@^4.2.8: +es6-promise@^4.0.3: version "4.2.8" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== @@ -12289,14 +12190,6 @@ es6-promisify@^5.0.0: dependencies: es6-promise "^4.0.3" -es6-symbol@^3.1.1, es6-symbol@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" - integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== - dependencies: - d "^1.0.1" - ext "^1.1.2" - esbuild-android-64@0.14.54: version "0.14.54" resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz#505f41832884313bbaffb27704b8bcaa2d8616be" @@ -12915,14 +12808,6 @@ eth-block-tracker@6.1.0: json-rpc-random-id "^1.0.1" pify "^3.0.0" -eth-ens-namehash@2.0.8: - version "2.0.8" - resolved "https://registry.yarnpkg.com/eth-ens-namehash/-/eth-ens-namehash-2.0.8.tgz#229ac46eca86d52e0c991e7cb2aef83ff0f68bcf" - integrity sha512-VWEI1+KJfz4Km//dadyvBBoBeSQ0MHTXPvr8UIXiLW6IanxvAV+DmlZAijZwAyggqGUfwQBeHf7tc9wzc1piSw== - dependencies: - idna-uts46-hx "^2.3.1" - js-sha3 "^0.5.7" - eth-gas-reporter@^0.2.25: version "0.2.27" resolved "https://registry.yarnpkg.com/eth-gas-reporter/-/eth-gas-reporter-0.2.27.tgz#928de8548a674ed64c7ba0bf5795e63079150d4e" @@ -12953,27 +12838,6 @@ eth-json-rpc-filters@5.1.0: json-rpc-engine "^6.1.0" pify "^5.0.0" -eth-lib@0.2.8: - version "0.2.8" - resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.2.8.tgz#b194058bef4b220ad12ea497431d6cb6aa0623c8" - integrity sha512-ArJ7x1WcWOlSpzdoTBX8vkwlkSQ85CjjifSZtV4co64vWxSV8geWfPI9x4SVYu3DSxnX4yWFVTtGL+j9DUFLNw== - dependencies: - bn.js "^4.11.6" - elliptic "^6.4.0" - xhr-request-promise "^0.1.2" - -eth-lib@^0.1.26: - version "0.1.29" - resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.1.29.tgz#0c11f5060d42da9f931eab6199084734f4dbd1d9" - integrity sha512-bfttrr3/7gG4E02HoWTDUcDDslN003OlOoBxk9virpAZQ1ja/jDgwkWB8QfJF7ojuEowrqy+lzp9VcJG7/k5bQ== - dependencies: - bn.js "^4.11.6" - elliptic "^6.4.0" - nano-json-stream-parser "^0.1.2" - servify "^0.1.12" - ws "^3.0.0" - xhr-request-promise "^0.1.2" - eth-query@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/eth-query/-/eth-query-2.1.2.tgz#d6741d9000106b51510c72db92d6365456a6da5e" @@ -13146,11 +13010,6 @@ event-target-shim@^5.0.0: resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== -eventemitter3@4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.4.tgz#b5463ace635a083d018bdc7c917b4c5f10a85384" - integrity sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ== - eventemitter3@^4.0.1, eventemitter3@^4.0.7: version "4.0.7" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" @@ -13309,7 +13168,7 @@ express-session@^1.17.3: safe-buffer "5.2.1" uid-safe "~2.1.5" -express@4.18.2, express@^4.14.0, express@^4.18.1: +express@4.18.2, express@^4.18.1: version "4.18.2" resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== @@ -13346,13 +13205,6 @@ express@4.18.2, express@^4.14.0, express@^4.18.1: utils-merge "1.0.1" vary "~1.1.2" -ext@^1.1.2: - version "1.7.0" - resolved "https://registry.yarnpkg.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f" - integrity sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw== - dependencies: - type "^2.7.2" - extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" @@ -13754,11 +13606,6 @@ fork-ts-checker-webpack-plugin@8.0.0: semver "^7.3.5" tapable "^2.2.1" -form-data-encoder@1.7.1: - version "1.7.1" - resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-1.7.1.tgz#ac80660e4f87ee0d3d3c3638b7da8278ddb8ec96" - integrity sha512-EFRDrsMm/kyqbTQocNvRXMLjc7Es2Vk+IQFx/YW7hkUH1eBl4J1fqiP34l74Yt0pFLCNpc06fkbVk00008mzjg== - form-data@^2.2.0: version "2.5.1" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4" @@ -13921,15 +13768,6 @@ fs-extra@^10.0.0, fs-extra@^10.1.0: jsonfile "^6.0.1" universalify "^2.0.0" -fs-extra@^4.0.2: - version "4.0.3" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94" - integrity sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg== - dependencies: - graceful-fs "^4.1.2" - jsonfile "^4.0.0" - universalify "^0.1.0" - fs-extra@^7.0.0, fs-extra@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" @@ -13956,13 +13794,6 @@ fs-jetpack@4.3.1, fs-jetpack@^4.3.1: minimatch "^3.0.2" rimraf "^2.6.3" -fs-minipass@^1.2.7: - version "1.2.7" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" - integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== - dependencies: - minipass "^2.6.0" - fs-minipass@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" @@ -14332,14 +14163,6 @@ global-prefix@^3.0.0: kind-of "^6.0.2" which "^1.3.1" -global@~4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406" - integrity sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w== - dependencies: - min-document "^2.19.0" - process "^0.11.10" - globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" @@ -14439,26 +14262,7 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" -got@12.1.0: - version "12.1.0" - resolved "https://registry.yarnpkg.com/got/-/got-12.1.0.tgz#099f3815305c682be4fd6b0ee0726d8e4c6b0af4" - integrity sha512-hBv2ty9QN2RdbJJMK3hesmSkFTjVIHyIDDbssCKnSmq62edGgImJWD10Eb1k77TiV1bxloxqcFAVK8+9pkhOig== - dependencies: - "@sindresorhus/is" "^4.6.0" - "@szmarczak/http-timer" "^5.0.1" - "@types/cacheable-request" "^6.0.2" - "@types/responselike" "^1.0.0" - cacheable-lookup "^6.0.4" - cacheable-request "^7.0.2" - decompress-response "^6.0.0" - form-data-encoder "1.7.1" - get-stream "^6.0.1" - http2-wrapper "^2.1.10" - lowercase-keys "^3.0.0" - p-cancelable "^3.0.0" - responselike "^2.0.0" - -got@^11.8.2, got@^11.8.5: +got@^11.8.2: version "11.8.6" resolved "https://registry.yarnpkg.com/got/-/got-11.8.6.tgz#276e827ead8772eddbcfc97170590b841823233a" integrity sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g== @@ -15085,11 +14889,6 @@ http-errors@~1.6.2: setprototypeof "1.1.0" statuses ">= 1.4.0 < 2" -http-https@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/http-https/-/http-https-1.0.0.tgz#2f908dd5f1db4068c058cd6e6d4ce392c913389b" - integrity sha512-o0PWwVCSp3O0wS6FvNr6xfBCHgt0m1tvPLFOCc2iFDKTRAXhB7m8klDf7ErowFH8POa6dVdGatKU5I1YYwzUyg== - http-proxy-agent@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" @@ -15137,14 +14936,6 @@ http2-wrapper@^1.0.0-beta.5.2: quick-lru "^5.1.1" resolve-alpn "^1.0.0" -http2-wrapper@^2.1.10: - version "2.2.1" - resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-2.2.1.tgz#310968153dcdedb160d8b72114363ef5fce1f64a" - integrity sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ== - dependencies: - quick-lru "^5.1.1" - resolve-alpn "^1.2.0" - https-browserify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" @@ -15236,13 +15027,6 @@ identity-obj-proxy@^3.0.0: dependencies: harmony-reflect "^1.4.6" -idna-uts46-hx@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/idna-uts46-hx/-/idna-uts46-hx-2.3.1.tgz#a1dc5c4df37eee522bf66d969cc980e00e8711f9" - integrity sha512-PWoF9Keq6laYdIRwwCdhTPl60xRqAloYNMQLiyUnG42VjT53oW07BXIRM+NK7eQjzXjAk2gUvX9caRxlnF9TAA== - dependencies: - punycode "2.1.0" - ieee754@1.1.13: version "1.1.13" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" @@ -15906,11 +15690,6 @@ is-fullwidth-code-point@^5.0.0: dependencies: get-east-asian-width "^1.0.0" -is-function@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.2.tgz#4f097f30abf6efadac9833b17ca5dc03f8144e08" - integrity sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ== - is-generator-fn@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" @@ -17313,11 +17092,6 @@ js-sha3@0.8.0, js-sha3@^0.8.0: resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== -js-sha3@^0.5.7: - version "0.5.7" - resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7" - integrity sha512-GII20kjaPX0zJ8wzkTbNDYMY7msuZcTWk8S5UOh6806Jq/wz1J8/bnr8uGU0DAUmYDjj2Mr4X1cW8v/GLYnR+g== - js-string-escape@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/js-string-escape/-/js-string-escape-1.0.1.tgz#e2625badbc0d67c7533e9edc1068c587ae4137ef" @@ -18444,11 +18218,6 @@ lowercase-keys@^2.0.0: resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== -lowercase-keys@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-3.0.0.tgz#c5e7d442e37ead247ae9db117a9d0a467c89d4f2" - integrity sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ== - lru-cache@^10.0.2, "lru-cache@^9.1.1 || ^10.0.0": version "10.1.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.1.0.tgz#2098d41c2dc56500e6c88584aa656c84de7d0484" @@ -18873,7 +18642,7 @@ mime-types@2.1.18: dependencies: mime-db "~1.33.0" -mime-types@2.1.35, mime-types@^2.1.12, mime-types@^2.1.16, mime-types@^2.1.18, mime-types@^2.1.27, mime-types@^2.1.28, mime-types@^2.1.31, mime-types@^2.1.35, mime-types@~2.1.19, mime-types@~2.1.24, mime-types@~2.1.34: +mime-types@2.1.35, mime-types@^2.1.12, mime-types@^2.1.18, mime-types@^2.1.27, mime-types@^2.1.28, mime-types@^2.1.31, mime-types@^2.1.35, mime-types@~2.1.19, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== @@ -18925,13 +18694,6 @@ mimic-response@^3.1.0: resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== -min-document@^2.19.0: - version "2.19.0" - resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" - integrity sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ== - dependencies: - dom-walk "^0.1.0" - min-indent@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" @@ -19030,14 +18792,6 @@ minio@^7.0.32, minio@^7.1.3: xml "^1.0.1" xml2js "^0.5.0" -minipass@^2.6.0, minipass@^2.9.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" - integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== - dependencies: - safe-buffer "^5.1.2" - yallist "^3.0.0" - minipass@^3.0.0: version "3.3.6" resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" @@ -19060,13 +18814,6 @@ minipass@^5.0.0: resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.4.tgz#dbce03740f50a4786ba994c1fb908844d27b038c" integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ== -minizlib@^1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" - integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== - dependencies: - minipass "^2.9.0" - minizlib@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" @@ -19088,19 +18835,7 @@ mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== -mkdirp-promise@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz#e9b8f68e552c68a9c1713b84883f7a1dd039b8a1" - integrity sha512-Hepn5kb1lJPtVW84RFT40YG1OddBNTOVUZR2bzQUHc+Z03en8/3uX0+060JDhcEzyO08HmipsN9DcnFMxhIL9w== - dependencies: - mkdirp "*" - -mkdirp@*: - version "3.0.1" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-3.0.1.tgz#e44e4c5607fb279c168241713cc6e0fea9adcb50" - integrity sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg== - -mkdirp@0.5.x, mkdirp@^0.5.1, mkdirp@^0.5.4, mkdirp@^0.5.5: +mkdirp@0.5.x, mkdirp@^0.5.1, mkdirp@^0.5.4: version "0.5.6" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== @@ -19161,11 +18896,6 @@ mocha@10.2.0, mocha@^10.0.0, mocha@^10.2.0: yargs-parser "20.2.4" yargs-unparser "2.0.0" -mock-fs@^4.1.0: - version "4.14.0" - resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.14.0.tgz#ce5124d2c601421255985e6e94da80a7357b1b18" - integrity sha512-qYvlv/exQ4+svI3UOvPUpLDF0OMX5euvUH0Ny4N5QyRyhNdgAgUrVH3iUINSzEPLvx0kbo/Bp28GJKIqvE7URw== - module-error@^1.0.1, module-error@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/module-error/-/module-error-1.0.2.tgz#8d1a48897ca883f47a45816d4fb3e3c6ba404d86" @@ -19252,51 +18982,11 @@ multiaddr@^10.0.0: uint8arrays "^3.0.0" varint "^6.0.0" -multibase@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/multibase/-/multibase-0.7.0.tgz#1adfc1c50abe05eefeb5091ac0c2728d6b84581b" - integrity sha512-TW8q03O0f6PNFTQDvh3xxH03c8CjGaaYrjkl9UQPG6rz53TQzzxJVCIWVjzcbN/Q5Y53Zd0IBQBMVktVgNx4Fg== - dependencies: - base-x "^3.0.8" - buffer "^5.5.0" - -multibase@~0.6.0: - version "0.6.1" - resolved "https://registry.yarnpkg.com/multibase/-/multibase-0.6.1.tgz#b76df6298536cc17b9f6a6db53ec88f85f8cc12b" - integrity sha512-pFfAwyTjbbQgNc3G7D48JkJxWtoJoBMaR4xQUOuB8RnCgRqaYmWNFeJTTvrJ2w51bjLq2zTby6Rqj9TQ9elSUw== - dependencies: - base-x "^3.0.8" - buffer "^5.5.0" - -multicodec@^0.5.5: - version "0.5.7" - resolved "https://registry.yarnpkg.com/multicodec/-/multicodec-0.5.7.tgz#1fb3f9dd866a10a55d226e194abba2dcc1ee9ffd" - integrity sha512-PscoRxm3f+88fAtELwUnZxGDkduE2HD9Q6GHUOywQLjOGT/HAdhjLDYNZ1e7VR0s0TP0EwZ16LNUTFpoBGivOA== - dependencies: - varint "^5.0.0" - -multicodec@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/multicodec/-/multicodec-1.0.4.tgz#46ac064657c40380c28367c90304d8ed175a714f" - integrity sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg== - dependencies: - buffer "^5.6.0" - varint "^5.0.0" - multiformats@^9.0.4, multiformats@^9.4.13, multiformats@^9.4.2, multiformats@^9.4.5, multiformats@^9.4.7, multiformats@^9.5.4, multiformats@^9.6.3, multiformats@^9.6.4: version "9.9.0" resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.9.0.tgz#c68354e7d21037a8f1f8833c8ccd68618e8f1d37" integrity sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg== -multihashes@^0.4.15, multihashes@~0.4.15: - version "0.4.21" - resolved "https://registry.yarnpkg.com/multihashes/-/multihashes-0.4.21.tgz#dc02d525579f334a7909ade8a122dabb58ccfcb5" - integrity sha512-uVSvmeCWf36pU2nB4/1kzYZjsXD9vofZKpgudqkceYY5g2aZZXJ5r9lxuzoRLl1OAp28XljXsEJ/X/85ZsKmKw== - dependencies: - buffer "^5.5.0" - multibase "^0.7.0" - varint "^5.0.0" - multistream@^4.0.1: version "4.1.0" resolved "https://registry.yarnpkg.com/multistream/-/multistream-4.1.0.tgz#7bf00dfd119556fbc153cff3de4c6d477909f5a8" @@ -19338,11 +19028,6 @@ mz@^2.4.0, mz@^2.7.0: object-assign "^4.0.1" thenify-all "^1.0.0" -nano-json-stream-parser@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz#0cc8f6d0e2b622b479c40d499c46d64b755c6f5f" - integrity sha512-9MqxMH/BSJC7dnLsEMPyfN5Dvoo49IsPFYMcHw3Bcfc2kN0lpHRBSzlMSVx4HGyJ7s9B31CyBTVehWJoQ8Ctew== - nano-time@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/nano-time/-/nano-time-1.0.0.tgz#b0554f69ad89e22d0907f7a12b0993a5d96137ef" @@ -19441,11 +19126,6 @@ nestjs-minio-client@^2.2.0: reflect-metadata "^0.1.13" rxjs "^7.8.1" -next-tick@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" - integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== - nft.storage@^7.0.0: version "7.1.1" resolved "https://registry.yarnpkg.com/nft.storage/-/nft.storage-7.1.1.tgz#1a4e0896436c67f5eb9d4120f509d6db4b7f1df0" @@ -19908,13 +19588,6 @@ oblivious-set@1.0.0: resolved "https://registry.yarnpkg.com/oblivious-set/-/oblivious-set-1.0.0.tgz#c8316f2c2fb6ff7b11b6158db3234c49f733c566" integrity sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw== -oboe@2.1.5: - version "2.1.5" - resolved "https://registry.yarnpkg.com/oboe/-/oboe-2.1.5.tgz#5554284c543a2266d7a38f17e073821fbde393cd" - integrity sha512-zRFWiF+FoicxEs3jNI/WYUrVEgA7DeET/InK0XQuudGHRg8iIob3cNPrJTKaz4004uaA9Pbe+Dwa8iluhjLZWA== - dependencies: - http-https "^1.0.0" - ofetch@^1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/ofetch/-/ofetch-1.3.3.tgz#588cb806a28e5c66c2c47dd8994f9059a036d8c0" @@ -20135,11 +19808,6 @@ p-cancelable@^2.0.0: resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf" integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg== -p-cancelable@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-3.0.0.tgz#63826694b54d61ca1c20ebcb6d3ecf5e14cd8050" - integrity sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw== - p-defer@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" @@ -20323,7 +19991,7 @@ parse-filepath@^1.0.1: map-cache "^0.2.0" path-root "^0.1.1" -parse-headers@^2.0.0, parse-headers@^2.0.5: +parse-headers@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.5.tgz#069793f9356a54008571eb7f9761153e6c770da9" integrity sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA== @@ -21128,11 +20796,6 @@ punycode@1.3.2: resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" integrity sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw== -punycode@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.0.tgz#5f863edc89b96db09074bad7947bf09056ca4e7d" - integrity sha512-Yxz2kRwT90aPiWEMHVYnEf4+rhwF1tBmmZ4KepCP+Wkium9JxtWnUm1nqGwpiAHr/tnTSeHqr3wb++jgSkXjhA== - punycode@^1.3.2, punycode@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" @@ -21217,15 +20880,6 @@ query-string@7.1.3, query-string@^7.1.3: split-on-first "^1.0.0" strict-uri-encode "^2.0.0" -query-string@^5.0.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb" - integrity sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw== - dependencies: - decode-uri-component "^0.2.0" - object-assign "^4.1.0" - strict-uri-encode "^1.0.0" - query-string@^6.13.5: version "6.14.1" resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.14.1.tgz#7ac2dca46da7f309449ba0f86b1fd28255b0c86a" @@ -22026,7 +21680,7 @@ request-oauth@^1.0.1: qs "^6.9.6" uuid "^8.3.2" -request@2.88.2, request@^2.79.0: +request@2.88.2: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== @@ -22082,7 +21736,7 @@ resize-observer-polyfill@^1.5.1: resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== -resolve-alpn@^1.0.0, resolve-alpn@^1.2.0: +resolve-alpn@^1.0.0: version "1.2.1" resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== @@ -22528,7 +22182,7 @@ scroll-into-view-if-needed@^2.2.20: dependencies: compute-scroll-into-view "^1.0.20" -scrypt-js@3.0.1, scrypt-js@^3.0.0, scrypt-js@^3.0.1: +scrypt-js@3.0.1, scrypt-js@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== @@ -22639,17 +22293,6 @@ serve@^14.1.1: serve-handler "6.1.5" update-check "1.5.4" -servify@^0.1.12: - version "0.1.12" - resolved "https://registry.yarnpkg.com/servify/-/servify-0.1.12.tgz#142ab7bee1f1d033b66d0707086085b17c06db95" - integrity sha512-/xE6GvsKKqyo1BAY+KxOWXcLpPsUUyji7Qg3bVD7hh1eRze5bR1uYiuDA/k3Gof1s9BTzQZEJK8sNcNGFIzeWw== - dependencies: - body-parser "^1.16.0" - cors "^2.8.1" - express "^4.14.0" - request "^2.79.0" - xhr "^2.3.3" - set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" @@ -22804,15 +22447,6 @@ simple-concat@^1.0.0: resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== -simple-get@^2.7.0: - version "2.8.2" - resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-2.8.2.tgz#5708fb0919d440657326cd5fe7d2599d07705019" - integrity sha512-Ijd/rV5o+mSBBs4F/x9oDPtTx9Zb6X9brmnXvMW4J7IR15ngi9q5xxqWBKU744jTZiaXtxaPL7uHG6vtN8kUkw== - dependencies: - decompress-response "^3.3.0" - once "^1.3.1" - simple-concat "^1.0.0" - simple-get@^4.0.0, simple-get@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-4.0.1.tgz#4a39db549287c979d352112fa03fd99fd6bc3543" @@ -23339,11 +22973,6 @@ streamx@^2.15.0: fast-fifo "^1.1.0" queue-tick "^1.0.1" -strict-uri-encode@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" - integrity sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ== - strict-uri-encode@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" @@ -23696,23 +23325,6 @@ swap-case@^1.1.0: lower-case "^1.1.1" upper-case "^1.1.1" -swarm-js@^0.1.40: - version "0.1.42" - resolved "https://registry.yarnpkg.com/swarm-js/-/swarm-js-0.1.42.tgz#497995c62df6696f6e22372f457120e43e727979" - integrity sha512-BV7c/dVlA3R6ya1lMlSSNPLYrntt0LUq4YMgy3iwpCIc6rZnS5W2wUoctarZ5pXlpKtxDDf9hNziEkcfrxdhqQ== - dependencies: - bluebird "^3.5.0" - buffer "^5.0.5" - eth-lib "^0.1.26" - fs-extra "^4.0.2" - got "^11.8.5" - mime-types "^2.1.16" - mkdirp-promise "^5.0.1" - mock-fs "^4.1.0" - setimmediate "^1.0.5" - tar "^4.0.2" - xhr-request "^1.0.1" - swr@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/swr/-/swr-2.2.4.tgz#03ec4c56019902fbdc904d78544bd7a9a6fa3f07" @@ -23863,19 +23475,6 @@ tar@6.1.13: mkdirp "^1.0.3" yallist "^4.0.0" -tar@^4.0.2: - version "4.4.19" - resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.19.tgz#2e4d7263df26f2b914dee10c825ab132123742f3" - integrity sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA== - dependencies: - chownr "^1.1.4" - fs-minipass "^1.2.7" - minipass "^2.9.0" - minizlib "^1.3.3" - mkdirp "^0.5.5" - safe-buffer "^5.2.1" - yallist "^3.1.1" - tar@^6.1.0, tar@^6.1.11: version "6.2.0" resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.0.tgz#b14ce49a79cb1cd23bc9b016302dea5474493f73" @@ -24050,11 +23649,6 @@ time-zone@^1.0.0: resolved "https://registry.yarnpkg.com/time-zone/-/time-zone-1.0.0.tgz#99c5bf55958966af6d06d83bdf3800dc82faec5d" integrity sha512-TIsDdtKo6+XrPtiTm1ssmMngN1sAhyKnTO2kunQWqNPWIVvCm15Wmw4SWInwTVgJ5u/Tr04+8Ei9TNcw4x4ONA== -timed-out@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" - integrity sha512-G7r3AhovYtr5YKOWQkta8RKAPb+J9IsO4uVmzjl8AZwfhs8UcUwTiD6gcJYSgOtzyjvQKrKYn41syHbUWMkafA== - timeout-abort-controller@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/timeout-abort-controller/-/timeout-abort-controller-1.1.1.tgz#2c3c3c66f13c783237987673c276cbd7a9762f29" @@ -24514,16 +24108,6 @@ type-is@^1.6.14, type-is@^1.6.16, type-is@^1.6.4, type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" -type@^1.0.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" - integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== - -type@^2.7.2: - version "2.7.2" - resolved "https://registry.yarnpkg.com/type/-/type-2.7.2.tgz#2376a15a3a28b1efa0f5350dcf72d24df6ef98d0" - integrity sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw== - typechain@^8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/typechain/-/typechain-8.3.2.tgz#1090dd8d9c57b6ef2aed3640a516bdbf01b00d73" @@ -24695,11 +24279,6 @@ uint8arrays@^3.0.0, uint8arrays@^3.1.0: dependencies: multiformats "^9.4.2" -ultron@~1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" - integrity sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og== - umzug@3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/umzug/-/umzug-3.2.1.tgz#01c3a109efb037a10a317d4191be22810c37b195" @@ -24937,11 +24516,6 @@ url-parse@^1.5.3: querystringify "^2.1.1" requires-port "^1.0.0" -url-set-query@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/url-set-query/-/url-set-query-1.0.0.tgz#016e8cfd7c20ee05cafe7795e892bd0702faa339" - integrity sha512-3AChu4NiXquPfeckE5R5cGdiHCMWJx1dwCWOmWIL4KHAziJNOFIYJlpGFeKDvwLPHovZRCxK3cYlwzqI9Vp+Gg== - url@0.10.3: version "0.10.3" resolved "https://registry.yarnpkg.com/url/-/url-0.10.3.tgz#021e4d9c7705f21bbf37d03ceb58767402774c64" @@ -25122,11 +24696,6 @@ value-or-promise@^1.0.11, value-or-promise@^1.0.12: resolved "https://registry.yarnpkg.com/value-or-promise/-/value-or-promise-1.0.12.tgz#0e5abfeec70148c78460a849f6b003ea7986f15c" integrity sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q== -varint@^5.0.0: - version "5.0.2" - resolved "https://registry.yarnpkg.com/varint/-/varint-5.0.2.tgz#5b47f8a947eb668b848e034dcfa87d0ff8a7f7a4" - integrity sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow== - varint@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/varint/-/varint-6.0.0.tgz#9881eb0ce8feaea6512439d19ddf84bf551661d0" @@ -25408,80 +24977,28 @@ web-vitals@^3.5.1: resolved "https://registry.yarnpkg.com/web-vitals/-/web-vitals-3.5.1.tgz#af7a9dc60708b81007922ab55a23d963676ba30a" integrity sha512-xQ9lvIpfLxUj0eSmT79ZjRoU5wIRfIr7pNukL7ZE4EcWZSmfZQqOlhuAGfkVa3EFmzPHZhWhXfm2i5ys+THVPg== -web3-bzz@1.10.3: - version "1.10.3" - resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.10.3.tgz#13942b37757eb850f3500a8e08bf605448b67566" - integrity sha512-XDIRsTwekdBXtFytMpHBuun4cK4x0ZMIDXSoo1UVYp+oMyZj07c7gf7tNQY5qZ/sN+CJIas4ilhN25VJcjSijQ== - dependencies: - "@types/node" "^12.12.6" - got "12.1.0" - swarm-js "^0.1.40" - -web3-core-helpers@1.10.3: - version "1.10.3" - resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.10.3.tgz#f2db40ea57e888795e46f229b06113b60bcd671c" - integrity sha512-Yv7dQC3B9ipOc5sWm3VAz1ys70Izfzb8n9rSiQYIPjpqtJM+3V4EeK6ghzNR6CO2es0+Yu9CtCkw0h8gQhrTxA== - dependencies: - web3-eth-iban "1.10.3" - web3-utils "1.10.3" - -web3-core-method@1.10.3: - version "1.10.3" - resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.10.3.tgz#63f16310ccab4eec8eca0a337d534565c2ba8d33" - integrity sha512-VZ/Dmml4NBmb0ep5PTSg9oqKoBtG0/YoMPei/bq/tUdlhB2dMB79sbeJPwx592uaV0Vpk7VltrrrBv5hTM1y4Q== - dependencies: - "@ethersproject/transactions" "^5.6.2" - web3-core-helpers "1.10.3" - web3-core-promievent "1.10.3" - web3-core-subscriptions "1.10.3" - web3-utils "1.10.3" - -web3-core-promievent@1.10.3: - version "1.10.3" - resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.10.3.tgz#9765dd42ce6cf2dc0a08eaffee607b855644f290" - integrity sha512-HgjY+TkuLm5uTwUtaAfkTgRx/NzMxvVradCi02gy17NxDVdg/p6svBHcp037vcNpkuGeFznFJgULP+s2hdVgUQ== - dependencies: - eventemitter3 "4.0.4" - -web3-core-requestmanager@1.10.3: - version "1.10.3" - resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.10.3.tgz#c34ca8e998a18d6ca3fa7f7a11d4391da401c987" - integrity sha512-VT9sKJfgM2yBOIxOXeXiDuFMP4pxzF6FT+y8KTLqhDFHkbG3XRe42Vm97mB/IvLQCJOmokEjl3ps8yP1kbggyw== - dependencies: - util "^0.12.5" - web3-core-helpers "1.10.3" - web3-providers-http "1.10.3" - web3-providers-ipc "1.10.3" - web3-providers-ws "1.10.3" - -web3-core-subscriptions@1.10.3: - version "1.10.3" - resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.10.3.tgz#58768cd72a9313252ef05dc52c09536f009a9479" - integrity sha512-KW0Mc8sgn70WadZu7RjQ4H5sNDJ5Lx8JMI3BWos+f2rW0foegOCyWhRu33W1s6ntXnqeBUw5rRCXZRlA3z+HNA== - dependencies: - eventemitter3 "4.0.4" - web3-core-helpers "1.10.3" +web3-core@^4.3.0, web3-core@^4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-4.3.2.tgz#f24b11d6a57dee527de8d42c89de2a439f0c4bed" + integrity sha512-uIMVd/j4BgOnwfpY8ZT+QKubOyM4xohEhFZXz9xB8wimXWMMlYVlIK/TbfHqFolS9uOerdSGhsMbcK9lETae8g== + dependencies: + web3-errors "^1.1.4" + web3-eth-accounts "^4.1.0" + web3-eth-iban "^4.0.7" + web3-providers-http "^4.1.0" + web3-providers-ws "^4.0.7" + web3-types "^1.3.1" + web3-utils "^4.1.0" + web3-validator "^2.0.3" + optionalDependencies: + web3-providers-ipc "^4.0.7" -web3-core@1.10.3: - version "1.10.3" - resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.10.3.tgz#4aeb8f4b0cb5775d9fa4edf1127864743f1c3ae3" - integrity sha512-Vbk0/vUNZxJlz3RFjAhNNt7qTpX8yE3dn3uFxfX5OHbuon5u65YEOd3civ/aQNW745N0vGUlHFNxxmn+sG9DIw== - dependencies: - "@types/bn.js" "^5.1.1" - "@types/node" "^12.12.6" - bignumber.js "^9.0.0" - web3-core-helpers "1.10.3" - web3-core-method "1.10.3" - web3-core-requestmanager "1.10.3" - web3-utils "1.10.3" - -web3-eth-abi@1.10.3: - version "1.10.3" - resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.10.3.tgz#7decfffa8fed26410f32cfefdc32d3e76f717ca2" - integrity sha512-O8EvV67uhq0OiCMekqYsDtb6FzfYzMXT7VMHowF8HV6qLZXCGTdB/NH4nJrEh2mFtEwVdS6AmLFJAQd2kVyoMQ== +web3-errors@^1.1.3, web3-errors@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/web3-errors/-/web3-errors-1.1.4.tgz#5667a0a5f66fc936e101ef32032ccc1e8ca4d5a1" + integrity sha512-WahtszSqILez+83AxGecVroyZsMuuRT+KmQp4Si5P4Rnqbczno1k748PCrZTS1J4UCPmXMG2/Vt+0Bz2zwXkwQ== dependencies: - "@ethersproject/abi" "^5.6.3" - web3-utils "1.10.3" + web3-types "^1.3.1" web3-eth-abi@1.7.0: version "1.7.0" @@ -25491,147 +25008,151 @@ web3-eth-abi@1.7.0: "@ethersproject/abi" "5.0.7" web3-utils "1.7.0" -web3-eth-accounts@1.10.3: - version "1.10.3" - resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.10.3.tgz#9ecb816b81cd97333988bfcd0afaee5d13bbb198" - integrity sha512-8MipGgwusDVgn7NwKOmpeo3gxzzd+SmwcWeBdpXknuyDiZSQy9tXe+E9LeFGrmys/8mLLYP79n3jSbiTyv+6pQ== +web3-eth-abi@^4.1.4: + version "4.1.4" + resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-4.1.4.tgz#56ae7ebb1385db1a948e69fb35f4057bff6743af" + integrity sha512-YLOBVVxxxLYKXjaiwZjEWYEnkMmmrm0nswZsvzSsINy/UgbWbzfoiZU+zn4YNWIEhORhx1p37iS3u/dP6VyC2w== dependencies: - "@ethereumjs/common" "2.6.5" - "@ethereumjs/tx" "3.5.2" - "@ethereumjs/util" "^8.1.0" - eth-lib "0.2.8" - scrypt-js "^3.0.1" - uuid "^9.0.0" - web3-core "1.10.3" - web3-core-helpers "1.10.3" - web3-core-method "1.10.3" - web3-utils "1.10.3" + abitype "0.7.1" + web3-errors "^1.1.3" + web3-types "^1.3.0" + web3-utils "^4.0.7" + web3-validator "^2.0.3" -web3-eth-contract@1.10.3: - version "1.10.3" - resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.10.3.tgz#8880468e2ba7d8a4791cf714f67d5e1ec1591275" - integrity sha512-Y2CW61dCCyY4IoUMD4JsEQWrILX4FJWDWC/Txx/pr3K/+fGsBGvS9kWQN5EsVXOp4g7HoFOfVh9Lf7BmVVSRmg== - dependencies: - "@types/bn.js" "^5.1.1" - web3-core "1.10.3" - web3-core-helpers "1.10.3" - web3-core-method "1.10.3" - web3-core-promievent "1.10.3" - web3-core-subscriptions "1.10.3" - web3-eth-abi "1.10.3" - web3-utils "1.10.3" - -web3-eth-ens@1.10.3: - version "1.10.3" - resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-1.10.3.tgz#ae5b49bcb9823027e0b28aa6b1de58d726cbaafa" - integrity sha512-hR+odRDXGqKemw1GFniKBEXpjYwLgttTES+bc7BfTeoUyUZXbyDHe5ifC+h+vpzxh4oS0TnfcIoarK0Z9tFSiQ== - dependencies: - content-hash "^2.5.2" - eth-ens-namehash "2.0.8" - web3-core "1.10.3" - web3-core-helpers "1.10.3" - web3-core-promievent "1.10.3" - web3-eth-abi "1.10.3" - web3-eth-contract "1.10.3" - web3-utils "1.10.3" - -web3-eth-iban@1.10.3: - version "1.10.3" - resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.10.3.tgz#91d458e5400195edc883a0d4383bf1cecd17240d" - integrity sha512-ZCfOjYKAjaX2TGI8uif5ah+J3BYFuo+47JOIV1RIz2l7kD9VfnxvRH5UiQDRyMALQC7KFd2hUqIEtHklapNyKA== +web3-eth-accounts@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-4.1.0.tgz#5b5e6c60d457e7b829ec590021fc87ada8585920" + integrity sha512-UFtAsOANsvihTQ6SSvOKguupmQkResyR9M9JNuOxYpKh7+3W+sTnbLXw2UbOSYIsKlc1mpqqW9bVr1SjqHDpUQ== dependencies: - bn.js "^5.2.1" - web3-utils "1.10.3" + "@ethereumjs/rlp" "^4.0.1" + crc-32 "^1.2.2" + ethereum-cryptography "^2.0.0" + web3-errors "^1.1.3" + web3-types "^1.3.0" + web3-utils "^4.0.7" + web3-validator "^2.0.3" -web3-eth-personal@1.10.3: - version "1.10.3" - resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.10.3.tgz#4e72008aa211327ccc3bfa7671c510e623368457" - integrity sha512-avrQ6yWdADIvuNQcFZXmGLCEzulQa76hUOuVywN7O3cklB4nFc/Gp3yTvD3bOAaE7DhjLQfhUTCzXL7WMxVTsw== +web3-eth-contract@^4.1.2, web3-eth-contract@^4.1.4: + version "4.1.4" + resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-4.1.4.tgz#513720fc674e42180ec1b88384dc2ce31715488e" + integrity sha512-tJ4z6QLgtu8EQu2sXnLA7g427oxmngnbAUh+9kJKbP6Yep/oe+z79PqJv7H3MwqwUNW9T+/FeB2PnSQSyxz6ig== + dependencies: + web3-core "^4.3.2" + web3-errors "^1.1.4" + web3-eth "^4.3.1" + web3-eth-abi "^4.1.4" + web3-types "^1.3.1" + web3-utils "^4.1.0" + web3-validator "^2.0.3" + +web3-eth-ens@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-4.0.8.tgz#f4e0a018ce6cc69e37007ee92063867feb5994f0" + integrity sha512-nj0JfeD45BbzVJcVYpUJnSo8iwDcY9CQ7CZhhIVVOFjvpMAPw0zEwjTvZEIQyCW61OoDG9xcBzwxe2tZoYhMRw== + dependencies: + "@adraffy/ens-normalize" "^1.8.8" + web3-core "^4.3.0" + web3-errors "^1.1.3" + web3-eth "^4.3.1" + web3-eth-contract "^4.1.2" + web3-net "^4.0.7" + web3-types "^1.3.0" + web3-utils "^4.0.7" + web3-validator "^2.0.3" + +web3-eth-iban@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-4.0.7.tgz#ee504f845d7b6315f0be78fcf070ccd5d38e4aaf" + integrity sha512-8weKLa9KuKRzibC87vNLdkinpUE30gn0IGY027F8doeJdcPUfsa4IlBgNC4k4HLBembBB2CTU0Kr/HAOqMeYVQ== dependencies: - "@types/node" "^12.12.6" - web3-core "1.10.3" - web3-core-helpers "1.10.3" - web3-core-method "1.10.3" - web3-net "1.10.3" - web3-utils "1.10.3" + web3-errors "^1.1.3" + web3-types "^1.3.0" + web3-utils "^4.0.7" + web3-validator "^2.0.3" -web3-eth@1.10.3: - version "1.10.3" - resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.10.3.tgz#b8c6f37f1aac52422583a5a9c29130983a3fb3b1" - integrity sha512-Uk1U2qGiif2mIG8iKu23/EQJ2ksB1BQXy3wF3RvFuyxt8Ft9OEpmGlO7wOtAyJdoKzD5vcul19bJpPcWSAYZhA== - dependencies: - web3-core "1.10.3" - web3-core-helpers "1.10.3" - web3-core-method "1.10.3" - web3-core-subscriptions "1.10.3" - web3-eth-abi "1.10.3" - web3-eth-accounts "1.10.3" - web3-eth-contract "1.10.3" - web3-eth-ens "1.10.3" - web3-eth-iban "1.10.3" - web3-eth-personal "1.10.3" - web3-net "1.10.3" - web3-utils "1.10.3" - -web3-net@1.10.3: - version "1.10.3" - resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.10.3.tgz#9486c2fe51452cb958e11915db6f90bd6caa5482" - integrity sha512-IoSr33235qVoI1vtKssPUigJU9Fc/Ph0T9CgRi15sx+itysmvtlmXMNoyd6Xrgm9LuM4CIhxz7yDzH93B79IFg== +web3-eth-personal@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-4.0.8.tgz#b51628c560de550ca8b354fa784f9556aae6065c" + integrity sha512-sXeyLKJ7ddQdMxz1BZkAwImjqh7OmKxhXoBNF3isDmD4QDpMIwv/t237S3q4Z0sZQamPa/pHebJRWVuvP8jZdw== dependencies: - web3-core "1.10.3" - web3-core-method "1.10.3" - web3-utils "1.10.3" + web3-core "^4.3.0" + web3-eth "^4.3.1" + web3-rpc-methods "^1.1.3" + web3-types "^1.3.0" + web3-utils "^4.0.7" + web3-validator "^2.0.3" -web3-providers-http@1.10.3: - version "1.10.3" - resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.10.3.tgz#d8166ee89db82d37281ea9e15c5882a2d7928755" - integrity sha512-6dAgsHR3MxJ0Qyu3QLFlQEelTapVfWNTu5F45FYh8t7Y03T1/o+YAkVxsbY5AdmD+y5bXG/XPJ4q8tjL6MgZHw== +web3-eth@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-4.3.1.tgz#cb4224356716da71e694091aa3fbf10bcb813fb5" + integrity sha512-zJir3GOXooHQT85JB8SrufE+Voo5TtXdjhf1D8IGXmxM8MrhI8AT+Pgt4siBTupJcu5hF17iGmTP/Nj2XnaibQ== dependencies: - abortcontroller-polyfill "^1.7.5" - cross-fetch "^4.0.0" - es6-promise "^4.2.8" - web3-core-helpers "1.10.3" + setimmediate "^1.0.5" + web3-core "^4.3.0" + web3-errors "^1.1.3" + web3-eth-abi "^4.1.4" + web3-eth-accounts "^4.1.0" + web3-net "^4.0.7" + web3-providers-ws "^4.0.7" + web3-rpc-methods "^1.1.3" + web3-types "^1.3.0" + web3-utils "^4.0.7" + web3-validator "^2.0.3" + +web3-net@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-4.0.7.tgz#ed2c1bd700cf94be93a6dbd8bd8aa413d8681942" + integrity sha512-SzEaXFrBjY25iQGk5myaOfO9ZyfTwQEa4l4Ps4HDNVMibgZji3WPzpjq8zomVHMwi8bRp6VV7YS71eEsX7zLow== + dependencies: + web3-core "^4.3.0" + web3-rpc-methods "^1.1.3" + web3-types "^1.3.0" + web3-utils "^4.0.7" -web3-providers-ipc@1.10.3: - version "1.10.3" - resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.10.3.tgz#a7e015957fc037d8a87bd4b6ae3561c1b1ad1f46" - integrity sha512-vP5WIGT8FLnGRfswTxNs9rMfS1vCbMezj/zHbBe/zB9GauBRTYVrUo2H/hVrhLg8Ut7AbsKZ+tCJ4mAwpKi2hA== +web3-providers-http@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-4.1.0.tgz#8d7afda67d1d8542ca85b30f60a3d1fe1993b561" + integrity sha512-6qRUGAhJfVQM41E5t+re5IHYmb5hSaLc02BE2MaRQsz2xKA6RjmHpOA5h/+ojJxEpI9NI2CrfDKOAgtJfoUJQg== dependencies: - oboe "2.1.5" - web3-core-helpers "1.10.3" + cross-fetch "^4.0.0" + web3-errors "^1.1.3" + web3-types "^1.3.0" + web3-utils "^4.0.7" -web3-providers-ws@1.10.3: - version "1.10.3" - resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.10.3.tgz#03c84958f9da251349cd26fd7a4ae567e3af6caa" - integrity sha512-/filBXRl48INxsh6AuCcsy4v5ndnTZ/p6bl67kmO9aK1wffv7CT++DrtclDtVMeDGCgB3van+hEf9xTAVXur7Q== +web3-providers-ipc@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-4.0.7.tgz#9ec4c8565053af005a5170ba80cddeb40ff3e3d3" + integrity sha512-YbNqY4zUvIaK2MHr1lQFE53/8t/ejHtJchrWn9zVbFMGXlTsOAbNoIoZWROrg1v+hCBvT2c9z8xt7e/+uz5p1g== dependencies: - eventemitter3 "4.0.4" - web3-core-helpers "1.10.3" - websocket "^1.0.32" + web3-errors "^1.1.3" + web3-types "^1.3.0" + web3-utils "^4.0.7" -web3-shh@1.10.3: - version "1.10.3" - resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.10.3.tgz#ee44f760598a65a290d611c443838aac854ee858" - integrity sha512-cAZ60CPvs9azdwMSQ/PSUdyV4PEtaW5edAZhu3rCXf6XxQRliBboic+AvwUvB6j3eswY50VGa5FygfVmJ1JVng== +web3-providers-ws@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-4.0.7.tgz#7a78a0dcf077e0e802da524fbb37d080b356c14b" + integrity sha512-n4Dal9/rQWjS7d6LjyEPM2R458V8blRm0eLJupDEJOOIBhGYlxw5/4FthZZ/cqB7y/sLVi7K09DdYx2MeRtU5w== dependencies: - web3-core "1.10.3" - web3-core-method "1.10.3" - web3-core-subscriptions "1.10.3" - web3-net "1.10.3" + "@types/ws" "8.5.3" + isomorphic-ws "^5.0.0" + web3-errors "^1.1.3" + web3-types "^1.3.0" + web3-utils "^4.0.7" + ws "^8.8.1" -web3-utils@1.10.3, web3-utils@^1.3.4, web3-utils@^1.3.6, web3-utils@^1.8.1: - version "1.10.3" - resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.10.3.tgz#f1db99c82549c7d9f8348f04ffe4e0188b449714" - integrity sha512-OqcUrEE16fDBbGoQtZXWdavsPzbGIDc5v3VrRTZ0XrIpefC/viZ1ZU9bGEemazyS0catk/3rkOOxpzTfY+XsyQ== +web3-rpc-methods@^1.1.3, web3-rpc-methods@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/web3-rpc-methods/-/web3-rpc-methods-1.1.4.tgz#0b478e38231d3f3260ac504307c6dc4059af6fda" + integrity sha512-LTFNg4LFaeU8K9ecuT8fHDp/LOXyxCneeZjCrRYIW1u82Ly52SrY55FIzMIISGoG/iT5Wh7UiHOB3CQsWLBmbQ== dependencies: - "@ethereumjs/util" "^8.1.0" - bn.js "^5.2.1" - ethereum-bloom-filters "^1.0.6" - ethereum-cryptography "^2.1.2" - ethjs-unit "0.1.6" - number-to-bn "1.7.0" - randombytes "^2.1.0" - utf8 "3.0.0" + web3-core "^4.3.2" + web3-types "^1.3.1" + web3-validator "^2.0.3" + +web3-types@^1.3.0, web3-types@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/web3-types/-/web3-types-1.3.1.tgz#cf6148ad46b68c5c89714613380b270d31e297be" + integrity sha512-8fXi7h/t95VKRtgU4sxprLPZpsTh3jYDfSghshIDBgUD/OoGe5S+syP24SUzBZYllZ/L+hMr2gdp/0bGJa8pYQ== web3-utils@1.7.0: version "1.7.0" @@ -25646,18 +25167,62 @@ web3-utils@1.7.0: randombytes "^2.1.0" utf8 "3.0.0" -web3@^1.10.2: +web3-utils@^1.3.4, web3-utils@^1.3.6, web3-utils@^1.8.1: version "1.10.3" - resolved "https://registry.yarnpkg.com/web3/-/web3-1.10.3.tgz#5e80ac532dc432b09fde668d570b0ad4e6710897" - integrity sha512-DgUdOOqC/gTqW+VQl1EdPxrVRPB66xVNtuZ5KD4adVBtko87hkgM8BTZ0lZ8IbUfnQk6DyjcDujMiH3oszllAw== + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.10.3.tgz#f1db99c82549c7d9f8348f04ffe4e0188b449714" + integrity sha512-OqcUrEE16fDBbGoQtZXWdavsPzbGIDc5v3VrRTZ0XrIpefC/viZ1ZU9bGEemazyS0catk/3rkOOxpzTfY+XsyQ== dependencies: - web3-bzz "1.10.3" - web3-core "1.10.3" - web3-eth "1.10.3" - web3-eth-personal "1.10.3" - web3-net "1.10.3" - web3-shh "1.10.3" - web3-utils "1.10.3" + "@ethereumjs/util" "^8.1.0" + bn.js "^5.2.1" + ethereum-bloom-filters "^1.0.6" + ethereum-cryptography "^2.1.2" + ethjs-unit "0.1.6" + number-to-bn "1.7.0" + randombytes "^2.1.0" + utf8 "3.0.0" + +web3-utils@^4.0.7, web3-utils@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-4.1.0.tgz#3fcb77261fe1e4d02c46564fdee26f690f58a76a" + integrity sha512-+VJWR6FtCsgwuJr5tvSvQlSEG06586df8h2CxGc9tcNtIDyJKNkSDDWJkdNPvyDhhXFzQYFh8QOGymD1CIP6fw== + dependencies: + ethereum-cryptography "^2.0.0" + web3-errors "^1.1.4" + web3-types "^1.3.1" + web3-validator "^2.0.3" + +web3-validator@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/web3-validator/-/web3-validator-2.0.3.tgz#e5dcd4b4902612cff21b7f8817dd233393999d97" + integrity sha512-fJbAQh+9LSNWy+l5Ze6HABreml8fra98o5+vS073T35jUcLbRZ0IOjF/ZPJhJNbJDt+jP1vseZsc3z3uX9mxxQ== + dependencies: + ethereum-cryptography "^2.0.0" + util "^0.12.5" + web3-errors "^1.1.3" + web3-types "^1.3.0" + zod "^3.21.4" + +web3@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/web3/-/web3-4.3.0.tgz#3f1d5e878295ef8bc7d2195f0f42aa2f7b8224cf" + integrity sha512-YiLCsb5wmgJlSxRLzt7Z7H+CmlVVIKD8VaUQaZ+xKVG3Q7CpsO5Z6jmeKnlr6M9c6fDDsDnRM6G8g+nchZehbA== + dependencies: + web3-core "^4.3.2" + web3-errors "^1.1.4" + web3-eth "^4.3.1" + web3-eth-abi "^4.1.4" + web3-eth-accounts "^4.1.0" + web3-eth-contract "^4.1.4" + web3-eth-ens "^4.0.8" + web3-eth-iban "^4.0.7" + web3-eth-personal "^4.0.8" + web3-net "^4.0.7" + web3-providers-http "^4.1.0" + web3-providers-ws "^4.0.7" + web3-rpc-methods "^1.1.4" + web3-types "^1.3.1" + web3-utils "^4.1.0" + web3-validator "^2.0.3" webcrypto-core@^1.7.7: version "1.7.7" @@ -25812,18 +25377,6 @@ webpack@^5.88.1: watchpack "^2.4.0" webpack-sources "^3.2.3" -websocket@^1.0.32: - version "1.0.34" - resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.34.tgz#2bdc2602c08bf2c82253b730655c0ef7dcab3111" - integrity sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ== - dependencies: - bufferutil "^4.0.1" - debug "^2.2.0" - es5-ext "^0.10.50" - typedarray-to-buffer "^3.1.5" - utf-8-validate "^5.0.2" - yaeti "^0.0.6" - well-known-symbols@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/well-known-symbols/-/well-known-symbols-2.0.0.tgz#e9c7c07dbd132b7b84212c8174391ec1f9871ba5" @@ -26131,21 +25684,12 @@ ws@8.5.0: resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f" integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg== -ws@^3.0.0: - version "3.3.3" - resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" - integrity sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA== - dependencies: - async-limiter "~1.0.0" - safe-buffer "~5.1.0" - ultron "~1.1.0" - ws@^7.3.1, ws@^7.4.5, ws@^7.4.6, ws@^7.5.1: version "7.5.9" resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== -ws@^8.11.0, ws@^8.12.0, ws@^8.13.0, ws@^8.5.0: +ws@^8.11.0, ws@^8.12.0, ws@^8.13.0, ws@^8.5.0, ws@^8.8.1: version "8.16.0" resolved "https://registry.yarnpkg.com/ws/-/ws-8.16.0.tgz#d1cd774f36fbc07165066a60e40323eab6446fd4" integrity sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ== @@ -26160,36 +25704,6 @@ xdg-basedir@^4.0.0: resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q== -xhr-request-promise@^0.1.2: - version "0.1.3" - resolved "https://registry.yarnpkg.com/xhr-request-promise/-/xhr-request-promise-0.1.3.tgz#2d5f4b16d8c6c893be97f1a62b0ed4cf3ca5f96c" - integrity sha512-YUBytBsuwgitWtdRzXDDkWAXzhdGB8bYm0sSzMPZT7Z2MBjMSTHFsyCT1yCRATY+XC69DUrQraRAEgcoCRaIPg== - dependencies: - xhr-request "^1.1.0" - -xhr-request@^1.0.1, xhr-request@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/xhr-request/-/xhr-request-1.1.0.tgz#f4a7c1868b9f198723444d82dcae317643f2e2ed" - integrity sha512-Y7qzEaR3FDtL3fP30k9wO/e+FBnBByZeybKOhASsGP30NIkRAAkKD/sCnLvgEfAIEC1rcmK7YG8f4oEnIrrWzA== - dependencies: - buffer-to-arraybuffer "^0.0.5" - object-assign "^4.1.1" - query-string "^5.0.1" - simple-get "^2.7.0" - timed-out "^4.0.1" - url-set-query "^1.0.0" - xhr "^2.0.4" - -xhr@^2.0.4, xhr@^2.3.3: - version "2.6.0" - resolved "https://registry.yarnpkg.com/xhr/-/xhr-2.6.0.tgz#b69d4395e792b4173d6b7df077f0fc5e4e2b249d" - integrity sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA== - dependencies: - global "~4.4.0" - is-function "^1.0.1" - parse-headers "^2.0.0" - xtend "^4.0.0" - xml-name-validator@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" @@ -26246,12 +25760,7 @@ y18n@^5.0.5: resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== -yaeti@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577" - integrity sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug== - -yallist@^3.0.0, yallist@^3.0.2, yallist@^3.1.1: +yallist@^3.0.2: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== @@ -26407,6 +25916,11 @@ zksync-web3@^0.14.3: resolved "https://registry.yarnpkg.com/zksync-web3/-/zksync-web3-0.14.4.tgz#0b70a7e1a9d45cc57c0971736079185746d46b1f" integrity sha512-kYehMD/S6Uhe1g434UnaMN+sBr9nQm23Ywn0EUP5BfQCsbjcr3ORuS68PosZw8xUTu3pac7G6YMSnNHk+fwzvg== +zod@^3.21.4: + version "3.22.4" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.22.4.tgz#f31c3a9386f61b1f228af56faa9255e845cf3fff" + integrity sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg== + zustand@^4.3.1: version "4.4.7" resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.4.7.tgz#355406be6b11ab335f59a66d2cf9815e8f24038c" From f957962e927fc1d2548df32f90433625171a79c2 Mon Sep 17 00:00:00 2001 From: portuu3 <61605646+portuu3@users.noreply.github.com> Date: Tue, 9 Jan 2024 16:08:29 +0100 Subject: [PATCH 023/104] add new cron jobs and change cron expression (#1441) --- .../src/modules/webhook/webhook.controller.ts | 6 +++--- .../modules/webhook/webhook.service.spec.ts | 12 ++++++------ .../src/modules/webhook/webhook.service.ts | 2 +- packages/apps/job-launcher/server/vercel.json | 18 +++++++++++++++--- 4 files changed, 25 insertions(+), 13 deletions(-) diff --git a/packages/apps/job-launcher/server/src/modules/webhook/webhook.controller.ts b/packages/apps/job-launcher/server/src/modules/webhook/webhook.controller.ts index 2ad54457d5..618873ad99 100644 --- a/packages/apps/job-launcher/server/src/modules/webhook/webhook.controller.ts +++ b/packages/apps/job-launcher/server/src/modules/webhook/webhook.controller.ts @@ -9,9 +9,9 @@ import { WebhookService } from './webhook.service'; export class WebhookController { constructor(private readonly webhookService: WebhookService) {} @Public() - @Get('/cron/launch') - public async processPendingCronJob(): Promise { - await this.webhookService.processPendingCronJob(); + @Get('/cron') + public async processPendingWebhooks(): Promise { + await this.webhookService.processPendingWebhooks(); return; } } diff --git a/packages/apps/job-launcher/server/src/modules/webhook/webhook.service.spec.ts b/packages/apps/job-launcher/server/src/modules/webhook/webhook.service.spec.ts index d53bf8b5f9..45ab102464 100644 --- a/packages/apps/job-launcher/server/src/modules/webhook/webhook.service.spec.ts +++ b/packages/apps/job-launcher/server/src/modules/webhook/webhook.service.spec.ts @@ -344,7 +344,7 @@ describe('WebhookService', () => { const startCronJobMock = jest.spyOn(cronJobService, 'startCronJob'); - await (webhookService as any).processPendingCronJob(); + await (webhookService as any).processPendingWebhooks(); expect(startCronJobMock).not.toHaveBeenCalled(); }); @@ -354,7 +354,7 @@ describe('WebhookService', () => { .spyOn(cronJobService, 'startCronJob') .mockResolvedValueOnce(cronJobEntityMock as any); - await (webhookService as any).processPendingCronJob(); + await (webhookService as any).processPendingWebhooks(); expect(cronJobService.startCronJob).toHaveBeenCalledWith( CronJobType.ProcessPendingWebhook, @@ -362,7 +362,7 @@ describe('WebhookService', () => { }); it('should send webhook for all of the pending webhooks', async () => { - await (webhookService as any).processPendingCronJob(); + await (webhookService as any).processPendingWebhooks(); expect(sendWebhookMock).toHaveBeenCalledTimes(2); expect(sendWebhookMock).toHaveBeenCalledWith(webhookEntity1); @@ -382,7 +382,7 @@ describe('WebhookService', () => { it('should increase retriesCount by 1 if sending webhook fails', async () => { sendWebhookMock.mockRejectedValueOnce(new Error()); - await (webhookService as any).processPendingCronJob(); + await (webhookService as any).processPendingWebhooks(); expect(webhookRepository.updateOne).toHaveBeenCalledWith( { id: webhookEntity1.id }, @@ -398,7 +398,7 @@ describe('WebhookService', () => { webhookEntity1.retriesCount = MOCK_MAX_RETRY_COUNT; - await (webhookService as any).processPendingCronJob(); + await (webhookService as any).processPendingWebhooks(); expect(webhookRepository.updateOne).toHaveBeenCalledWith( { id: webhookEntity1.id }, @@ -411,7 +411,7 @@ describe('WebhookService', () => { .spyOn(cronJobService, 'completeCronJob') .mockResolvedValueOnce(cronJobEntityMock as any); - await (webhookService as any).processPendingCronJob(); + await (webhookService as any).processPendingWebhooks(); expect(cronJobService.completeCronJob).toHaveBeenCalledWith( cronJobEntityMock as any, diff --git a/packages/apps/job-launcher/server/src/modules/webhook/webhook.service.ts b/packages/apps/job-launcher/server/src/modules/webhook/webhook.service.ts index dbb38febe8..607a298b8f 100644 --- a/packages/apps/job-launcher/server/src/modules/webhook/webhook.service.ts +++ b/packages/apps/job-launcher/server/src/modules/webhook/webhook.service.ts @@ -160,7 +160,7 @@ export class WebhookService { * @returns {Promise} - Returns a promise that resolves when the operation is complete. */ @Cron(CronExpression.EVERY_10_MINUTES) - public async processPendingCronJob(): Promise { + public async processPendingWebhooks(): Promise { const isCronJobRunning = await this.cronJobService.isCronJobRunning( CronJobType.ProcessPendingWebhook, ); diff --git a/packages/apps/job-launcher/server/vercel.json b/packages/apps/job-launcher/server/vercel.json index db11d9fdd8..a43e293568 100644 --- a/packages/apps/job-launcher/server/vercel.json +++ b/packages/apps/job-launcher/server/vercel.json @@ -19,12 +19,24 @@ ], "crons": [ { - "path": "/job/cron/launch", - "schedule": "*/1 * * * *" + "path": "/job/cron/create-escrow", + "schedule": "*/10 * * * *" + }, + { + "path": "/job/cron/setup-escrow", + "schedule": "*/10 * * * *" + }, + { + "path": "/job/cron/fund-escrow", + "schedule": "*/10 * * * *" }, { "path": "/job/cron/cancel", - "schedule": "*/1 * * * *" + "schedule": "*/10 * * * *" + }, + { + "path": "/webhook/cron", + "schedule": "*/10 * * * *" } ], "ignoreCommand": "git diff HEAD^ HEAD --quiet ." From af03ba8761876ab0bdeb0e62f94ae30cd2110cd7 Mon Sep 17 00:00:00 2001 From: eugenvoronov <104138627+eugenvoronov@users.noreply.github.com> Date: Thu, 11 Jan 2024 00:08:20 +0800 Subject: [PATCH 024/104] [SDK] [Python] Updated cancel method (#960) * Updated cancel method * Updated cancel method logic * Removed old implementation of cancel method * fix test and generate doc --------- Co-authored-by: Eric Lee --- ...human_protocol_sdk.escrow.escrow_client.md | 24 ++++- docs/sdk/python/human_protocol_sdk.escrow.md | 2 + docs/sdk/python/human_protocol_sdk.md | 1 + .../human_protocol_sdk/agreement/measures.py | 12 +-- .../human_protocol_sdk/constants.py | 68 ++++++++++---- .../escrow/escrow_client.py | 54 ++++++++++- .../human_protocol_sdk/escrow/escrow_utils.py | 78 +++++++++------- .../human_protocol_sdk/gql/escrow.py | 28 +++--- .../human_protocol_sdk/gql/hmtoken.py | 4 +- .../human_protocol_sdk/gql/payout.py | 6 +- .../human_protocol_sdk/gql/reward.py | 4 +- .../human_protocol_sdk/gql/statistics.py | 8 +- .../statistics/statistics_client.py | 12 +-- .../escrow/test_escrow_client.py | 90 ++++++++++++++++++- .../kvstore/test_kvstore_client.py | 9 +- 15 files changed, 294 insertions(+), 106 deletions(-) diff --git a/docs/sdk/python/human_protocol_sdk.escrow.escrow_client.md b/docs/sdk/python/human_protocol_sdk.escrow.escrow_client.md index 76879c9f8a..c876b9468b 100644 --- a/docs/sdk/python/human_protocol_sdk.escrow.escrow_client.md +++ b/docs/sdk/python/human_protocol_sdk.escrow.escrow_client.md @@ -48,6 +48,18 @@ escrow_client = EscrowClient(w3) ## Module +### *class* human_protocol_sdk.escrow.escrow_client.EscrowCancel(tx_hash, amount_refunded) + +Bases: `object` + +#### \_\_init_\_(tx_hash, amount_refunded) + +Represents the result of an escrow cancellation transaction. +Args: + +> tx_hash (str): The hash of the transaction that cancelled the escrow. +> amount_refunded (Any): The amount refunded during the escrow cancellation. + ### *class* human_protocol_sdk.escrow.escrow_client.EscrowClient(web3) Bases: `object` @@ -215,11 +227,15 @@ Cancels the specified escrow and sends the balance to the canceler. * **escrow_address** (`str`) – Address of the escrow to cancel * **tx_options** (`Optional`[`TxParams`]) – (Optional) Additional transaction parameters * **Return type:** - `None` + [`EscrowCancel`](#human_protocol_sdk.escrow.escrow_client.EscrowCancel) * **Returns:** - None + EscrowCancel: + An instance of the EscrowCancel class containing details of the cancellation transaction, + including the transaction hash and the amount refunded. * **Raises:** - [**EscrowClientError**](#human_protocol_sdk.escrow.escrow_client.EscrowClientError) – If an error occurs while checking the parameters + * [**EscrowClientError**](#human_protocol_sdk.escrow.escrow_client.EscrowClientError) – If an error occurs while checking the parameters + * [**EscrowClientError**](#human_protocol_sdk.escrow.escrow_client.EscrowClientError) – If the transfer event associated with the cancellation + is not found in the transaction logs * **Example:** ```python from eth_typing import URI @@ -242,7 +258,7 @@ Cancels the specified escrow and sends the balance to the canceler. (w3, gas_payer) = get_w3_with_priv_key('YOUR_PRIVATE_KEY') escrow_client = EscrowClient(w3) - transaction_hash = escrow_client.cancel( + escrow_cancel_data = escrow_client.cancel( "0x62dD51230A30401C455c8398d06F85e4EaB6309f" ) ``` diff --git a/docs/sdk/python/human_protocol_sdk.escrow.md b/docs/sdk/python/human_protocol_sdk.escrow.md index fd20e4b026..d5f4da28c0 100644 --- a/docs/sdk/python/human_protocol_sdk.escrow.md +++ b/docs/sdk/python/human_protocol_sdk.escrow.md @@ -8,6 +8,8 @@ obtain information from both the contracts and subgraph. * [human_protocol_sdk.escrow.escrow_client module](human_protocol_sdk.escrow.escrow_client.md) * [Code Example](human_protocol_sdk.escrow.escrow_client.md#code-example) * [Module](human_protocol_sdk.escrow.escrow_client.md#module) + * [`EscrowCancel`](human_protocol_sdk.escrow.escrow_client.md#human_protocol_sdk.escrow.escrow_client.EscrowCancel) + * [`EscrowCancel.__init__()`](human_protocol_sdk.escrow.escrow_client.md#human_protocol_sdk.escrow.escrow_client.EscrowCancel.__init__) * [`EscrowClient`](human_protocol_sdk.escrow.escrow_client.md#human_protocol_sdk.escrow.escrow_client.EscrowClient) * [`EscrowClient.__init__()`](human_protocol_sdk.escrow.escrow_client.md#human_protocol_sdk.escrow.escrow_client.EscrowClient.__init__) * [`EscrowClient.abort()`](human_protocol_sdk.escrow.escrow_client.md#human_protocol_sdk.escrow.escrow_client.EscrowClient.abort) diff --git a/docs/sdk/python/human_protocol_sdk.md b/docs/sdk/python/human_protocol_sdk.md index b42a16d2be..c7ee11f32c 100644 --- a/docs/sdk/python/human_protocol_sdk.md +++ b/docs/sdk/python/human_protocol_sdk.md @@ -36,6 +36,7 @@ * [human_protocol_sdk.escrow.escrow_client module](human_protocol_sdk.escrow.escrow_client.md) * [Code Example](human_protocol_sdk.escrow.escrow_client.md#code-example) * [Module](human_protocol_sdk.escrow.escrow_client.md#module) + * [`EscrowCancel`](human_protocol_sdk.escrow.escrow_client.md#human_protocol_sdk.escrow.escrow_client.EscrowCancel) * [`EscrowClient`](human_protocol_sdk.escrow.escrow_client.md#human_protocol_sdk.escrow.escrow_client.EscrowClient) * [`EscrowClientError`](human_protocol_sdk.escrow.escrow_client.md#human_protocol_sdk.escrow.escrow_client.EscrowClientError) * [`EscrowConfig`](human_protocol_sdk.escrow.escrow_client.md#human_protocol_sdk.escrow.escrow_client.EscrowConfig) diff --git a/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/agreement/measures.py b/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/agreement/measures.py index b0ccf3a7a0..a4b938ca89 100644 --- a/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/agreement/measures.py +++ b/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/agreement/measures.py @@ -185,13 +185,11 @@ def _percentage_from_label_counts(label_counts): max_item_agreements = (n_raters * (n_raters - 1)).sum() if max_item_agreements == 0: - warn( - """ + warn(""" All annotations were made by a single annotator, check your data to ensure this is not an error. Returning 1.0 - """ - ) + """) return 1.0 return item_agreements / max_item_agreements @@ -199,13 +197,11 @@ def _percentage_from_label_counts(label_counts): def _kappa(agreement_observed, agreement_expected): if agreement_expected == 1.0: - warn( - """ + warn(""" Annotations contained only a single value, check your data to ensure this is not an error. Returning 1.0. - """ - ) + """) return 1.0 return (agreement_observed - agreement_expected) / (1 - agreement_expected) diff --git a/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/constants.py b/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/constants.py index 2c2e799f71..2be1266488 100644 --- a/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/constants.py +++ b/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/constants.py @@ -26,7 +26,9 @@ class ChainId(Enum): ChainId.MAINNET: { "title": "Ethereum", "scan_url": "https://etherscan.io", - "subgraph_url": "https://api.thegraph.com/subgraphs/name/humanprotocol/mainnet-v2", + "subgraph_url": ( + "https://api.thegraph.com/subgraphs/name/humanprotocol/mainnet-v2" + ), "hmt_address": "0xd1ba9BAC957322D6e8c07a160a3A8dA11A0d2867", "factory_address": "0xD9c75a1Aa4237BB72a41E5E26bd8384f10c1f55a", "staking_address": "0x05398211bA2046E296fBc9a9D3EB49e3F15C3123", @@ -38,13 +40,17 @@ class ChainId(Enum): ChainId.GOERLI: { "title": "Ethereum Goerli", "scan_url": "https://goerli.etherscan.io", - "subgraph_url": "https://api.thegraph.com/subgraphs/name/humanprotocol/goerli-v2", + "subgraph_url": ( + "https://api.thegraph.com/subgraphs/name/humanprotocol/goerli-v2" + ), "hmt_address": "0xd3A31D57FDD790725d0F6B78095F62E8CD4ab317", "factory_address": "0x87469B4f2Fcf37cBd34E54244c0BD4Fa0603664c", "staking_address": "0xf46B45Df3d956369726d8Bd93Ba33963Ab692920", "reward_pool_address": "0x0376D26246Eb35FF4F9924cF13E6C05fd0bD7Fb4", "kvstore_address": "0x19Fc3e859C1813ac9427a7a78BeB9ae102CE96d3", - "old_subgraph_url": "https://api.thegraph.com/subgraphs/name/humanprotocol/goerli", + "old_subgraph_url": ( + "https://api.thegraph.com/subgraphs/name/humanprotocol/goerli" + ), "old_factory_address": "0xaAe6a2646C1F88763E62e0cD08aD050Ea66AC46F", }, ChainId.BSC_MAINNET: { @@ -62,55 +68,73 @@ class ChainId(Enum): ChainId.BSC_TESTNET: { "title": "Binance Smart Chain (Testnet)", "scan_url": "https://testnet.bscscan.com", - "subgraph_url": "https://api.thegraph.com/subgraphs/name/humanprotocol/bsctest-v2", + "subgraph_url": ( + "https://api.thegraph.com/subgraphs/name/humanprotocol/bsctest-v2" + ), "hmt_address": "0xE3D74BBFa45B4bCa69FF28891fBE392f4B4d4e4d", "factory_address": "0x2bfA592DBDaF434DDcbb893B1916120d181DAD18", "staking_address": "0x5517fE916Fe9F8dB15B0DDc76ebDf0BdDCd4ed18", "reward_pool_address": "0xB0A0500103eCEc431b73F6BAd923F0a2774E6e29", "kvstore_address": "0x32e27177BA6Ea91cf28dfd91a0Da9822A4b74EcF", - "old_subgraph_url": "https://api.thegraph.com/subgraphs/name/humanprotocol/bsctest", + "old_subgraph_url": ( + "https://api.thegraph.com/subgraphs/name/humanprotocol/bsctest" + ), "old_factory_address": "0xaae6a2646c1f88763e62e0cd08ad050ea66ac46f", }, ChainId.POLYGON: { "title": "Polygon", "scan_url": "https://polygonscan.com", - "subgraph_url": "https://api.thegraph.com/subgraphs/name/humanprotocol/polygon-v2", + "subgraph_url": ( + "https://api.thegraph.com/subgraphs/name/humanprotocol/polygon-v2" + ), "hmt_address": "0xc748B2A084F8eFc47E086ccdDD9b7e67aEb571BF", "factory_address": "0xBDBfD2cC708199C5640C6ECdf3B0F4A4C67AdfcB", "staking_address": "0xcbAd56bE3f504E98bd70875823d3CC0242B7bB29", "reward_pool_address": "0xa8e32d777a3839440cc7c24D591A64B9481753B3", "kvstore_address": "0xbcB28672F826a50B03EE91B28145EAbddA73B2eD", - "old_subgraph_url": "https://api.thegraph.com/subgraphs/name/humanprotocol/polygon", + "old_subgraph_url": ( + "https://api.thegraph.com/subgraphs/name/humanprotocol/polygon" + ), "old_factory_address": "0x45eBc3eAE6DA485097054ae10BA1A0f8e8c7f794", }, ChainId.POLYGON_MUMBAI: { "title": "Polygon Mumbai", "scan_url": "https://mumbai.polygonscan.com", - "subgraph_url": "https://api.thegraph.com/subgraphs/name/humanprotocol/mumbai-v2", + "subgraph_url": ( + "https://api.thegraph.com/subgraphs/name/humanprotocol/mumbai-v2" + ), "hmt_address": "0x0376D26246Eb35FF4F9924cF13E6C05fd0bD7Fb4", "factory_address": "0xA8D927C4DA17A6b71675d2D49dFda4E9eBE58f2d", "staking_address": "0x7Fd3dF914E7b6Bd96B4c744Df32183b51368Bfac", "reward_pool_address": "0xf0145eD99AC3c4f877aDa7dA4D1E059ec9116BAE", "kvstore_address": "0xD96158c7267Ea658a4688F4aEf1c85659851625d", - "old_subgraph_url": "https://api.thegraph.com/subgraphs/name/humanprotocol/mumbai", + "old_subgraph_url": ( + "https://api.thegraph.com/subgraphs/name/humanprotocol/mumbai" + ), "old_factory_address": "0x558cd800f9F0B02f3B149667bDe003284c867E94", }, ChainId.MOONBEAM: { "title": "Moonbeam", "scan_url": "https://moonbeam.moonscan.io", - "subgraph_url": "https://api.thegraph.com/subgraphs/name/humanprotocol/moonbeam-v2", + "subgraph_url": ( + "https://api.thegraph.com/subgraphs/name/humanprotocol/moonbeam-v2" + ), "hmt_address": "0x3b25BC1dC591D24d60560d0135D6750A561D4764", "factory_address": "0xD9c75a1Aa4237BB72a41E5E26bd8384f10c1f55a", "staking_address": "0x05398211bA2046E296fBc9a9D3EB49e3F15C3123", "reward_pool_address": "0x4A5963Dd6792692e9147EdC7659936b96251917a", "kvstore_address": "0x2B95bEcb6EBC4589f64CB000dFCF716b4aeF8aA6", - "old_subgraph_url": "https://api.thegraph.com/subgraphs/name/humanprotocol/moonbeam", + "old_subgraph_url": ( + "https://api.thegraph.com/subgraphs/name/humanprotocol/moonbeam" + ), "old_factory_address": "0x98108c28B7767a52BE38B4860832dd4e11A7ecad", }, ChainId.MOONBASE_ALPHA: { "title": "Moonbase Alpha", "scan_url": "https://moonbase.moonscan.io/", - "subgraph_url": "https://api.thegraph.com/subgraphs/name/humanprotocol/moonbase-alpha-v2", + "subgraph_url": ( + "https://api.thegraph.com/subgraphs/name/humanprotocol/moonbase-alpha-v2" + ), "hmt_address": "0x2dd72db2bBA65cE663e476bA8b84A1aAF802A8e3", "factory_address": "0x5e622FF522D81aa426f082bDD95210BC25fCA7Ed", "staking_address": "0xBFC7009F3371F93F3B54DdC8caCd02914a37495c", @@ -122,13 +146,17 @@ class ChainId(Enum): ChainId.AVALANCHE: { "title": "Avalanche C-Chain Mainnet", "scan_url": "https://snowtrace.io", - "subgraph_url": "https://api.thegraph.com/subgraphs/name/humanprotocol/avalanche-v2", + "subgraph_url": ( + "https://api.thegraph.com/subgraphs/name/humanprotocol/avalanche-v2" + ), "hmt_address": "0x12365293cb6477d4fc2686e46BB97E3Fb64f1550", "factory_address": "0xD9c75a1Aa4237BB72a41E5E26bd8384f10c1f55a", "staking_address": "0x05398211bA2046E296fBc9a9D3EB49e3F15C3123", "reward_pool_address": "0x4A5963Dd6792692e9147EdC7659936b96251917a", "kvstore_address": "0x9Bc7bff35B2Be2413708d48c3B0aEF5c43646728", - "old_subgraph_url": "https://api.thegraph.com/subgraphs/name/humanprotocol/avalanche", + "old_subgraph_url": ( + "https://api.thegraph.com/subgraphs/name/humanprotocol/avalanche" + ), "old_factory_address": "0x9767a578ba7a5FA1563c8229943cB01cd8446BB4", }, ChainId.AVALANCHE_TESTNET: { @@ -140,7 +168,9 @@ class ChainId(Enum): "staking_address": "0x9890473B0b93E24d6D1a8Dfb739D577C6f25FFd3", "reward_pool_address": "0x5517fE916Fe9F8dB15B0DDc76ebDf0BdDCd4ed18", "kvstore_address": "0x3aD4B091E054f192a822D1406f4535eAd38580e4", - "old_subgraph_url": "https://api.thegraph.com/subgraphs/name/humanprotocol/fuji", + "old_subgraph_url": ( + "https://api.thegraph.com/subgraphs/name/humanprotocol/fuji" + ), "old_factory_address": "0xfb4469201951C3B9a7F1996c477cb7BDBEcE0A88", }, ChainId.CELO: { @@ -158,7 +188,9 @@ class ChainId(Enum): ChainId.CELO_ALFAJORES: { "title": "Celo", "scan_url": "https://alfajores.celoscan.io/", - "subgraph_url": "https://api.thegraph.com/subgraphs/name/humanprotocol/celo-alfajores", + "subgraph_url": ( + "https://api.thegraph.com/subgraphs/name/humanprotocol/celo-alfajores" + ), "hmt_address": "0x2736B33455A872dC478E1E004106D04c35472468", "factory_address": "0x86Af9f6Cd34B69Db1B202223C6d6D109f2491569", "staking_address": "0x003548Df34be8836cF0F9673403a1E40ba449a0F", @@ -170,7 +202,9 @@ class ChainId(Enum): ChainId.SKALE: { "title": "SKALE Human Protocol Chain", "scan_url": "https://wan-red-ain.explorer.mainnet.skalenodes.com/", - "subgraph_url": "https://graph-skale.humanprotocol.org/subgraphs/name/skale-human", + "subgraph_url": ( + "https://graph-skale.humanprotocol.org/subgraphs/name/skale-human" + ), "hmt_address": "0x6E5FF61Ea88270F6142E0E0eC8cbe9d67476CbCd", "factory_address": "0x319070b49C8d1cC015915D1E7Eb5fd8e22833885", "staking_address": "0x79F37FB9C210910733c16228AC4D14a8e32C11BD", diff --git a/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/escrow/escrow_client.py b/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/escrow/escrow_client.py index 9f4a848552..0cbef4fa60 100644 --- a/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/escrow/escrow_client.py +++ b/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/escrow/escrow_client.py @@ -63,14 +63,28 @@ def get_w3_with_priv_key(priv_key: str): handle_transaction, ) from web3 import Web3, contract +from web3 import eth from web3.middleware import geth_poa_middleware from web3.types import TxParams +from eth_utils import abi from human_protocol_sdk.utils import validate_url LOG = logging.getLogger("human_protocol_sdk.escrow") +class EscrowCancel: + def __init__(self, tx_hash: str, amount_refunded: any): + """ + Represents the result of an escrow cancellation transaction. + Args: + tx_hash (str): The hash of the transaction that cancelled the escrow. + amount_refunded (Any): The amount refunded during the escrow cancellation. + """ + self.txHash = tx_hash + self.amountRefunded = amount_refunded + + class EscrowClientError(Exception): """ Raises when some error happens when interacting with escrow. @@ -681,15 +695,20 @@ def get_w3_with_priv_key(priv_key: str): def cancel( self, escrow_address: str, tx_options: Optional[TxParams] = None - ) -> None: + ) -> EscrowCancel: """Cancels the specified escrow and sends the balance to the canceler. :param escrow_address: Address of the escrow to cancel :param tx_options: (Optional) Additional transaction parameters - :return: None + :return: EscrowCancel: + An instance of the EscrowCancel class containing details of the cancellation transaction, + including the transaction hash and the amount refunded. :raise EscrowClientError: If an error occurs while checking the parameters + :raise EscrowClientError: If the transfer event associated with the cancellation + is not found in the transaction logs + :example: .. code-block:: python @@ -714,7 +733,7 @@ def get_w3_with_priv_key(priv_key: str): (w3, gas_payer) = get_w3_with_priv_key('YOUR_PRIVATE_KEY') escrow_client = EscrowClient(w3) - transaction_hash = escrow_client.cancel( + escrow_cancel_data = escrow_client.cancel( "0x62dD51230A30401C455c8398d06F85e4EaB6309f" ) """ @@ -722,7 +741,7 @@ def get_w3_with_priv_key(priv_key: str): if not Web3.is_address(escrow_address): raise EscrowClientError(f"Invalid escrow address: {escrow_address}") - handle_transaction( + transaction_receipt = handle_transaction( self.w3, "Cancel", self._get_escrow_contract(escrow_address).functions.cancel(), @@ -730,6 +749,33 @@ def get_w3_with_priv_key(priv_key: str): tx_options, ) + amount_transferred = None + token_address = self.get_token_address(escrow_address) + + erc20_interface = get_erc20_interface() + token_contract = self.w3.eth.contract(token_address, abi=erc20_interface["abi"]) + + for log in transaction_receipt["logs"]: + if log["address"] == token_address: + processed_log = token_contract.events.Transfer().process_log(log) + + if ( + processed_log["event"] == "Transfer" + and processed_log["args"]["from"] == escrow_address + ): + amount_transferred = processed_log["args"]["value"] + break + + if amount_transferred is None: + raise EscrowClientError("Transfer Event Not Found in Transaction Logs") + + escrow_cancel_data = EscrowCancel( + tx_hash=transaction_receipt["transactionHash"].hex(), + amount_refunded=amount_transferred, + ) + + return escrow_cancel_data + def abort(self, escrow_address: str, tx_options: Optional[TxParams] = None) -> None: """Cancels the specified escrow, sends the balance to the canceler and selfdestructs the escrow contract. diff --git a/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/escrow/escrow_utils.py b/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/escrow/escrow_utils.py index 40548d5c6e..1157508a12 100644 --- a/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/escrow/escrow_utils.py +++ b/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/escrow/escrow_utils.py @@ -165,20 +165,26 @@ def get_escrows( query=get_escrows_query(filter), params={ "launcher": filter.launcher.lower() if filter.launcher else None, - "reputationOracle": filter.reputation_oracle.lower() - if filter.reputation_oracle - else None, - "recordingOracle": filter.recording_oracle.lower() - if filter.recording_oracle - else None, - "exchangeOracle": filter.exchange_oracle.lower() - if filter.exchange_oracle - else None, + "reputationOracle": ( + filter.reputation_oracle.lower() + if filter.reputation_oracle + else None + ), + "recordingOracle": ( + filter.recording_oracle.lower() + if filter.recording_oracle + else None + ), + "exchangeOracle": ( + filter.exchange_oracle.lower() + if filter.exchange_oracle + else None + ), "jobRequesterId": filter.job_requester_id, "status": filter.status.name if filter.status else None, - "from": int(filter.date_from.timestamp()) - if filter.date_from - else None, + "from": ( + int(filter.date_from.timestamp()) if filter.date_from else None + ), "to": int(filter.date_to.timestamp()) if filter.date_to else None, }, ) @@ -208,17 +214,23 @@ def get_escrows( manifest_hash=escrow.get("manifestHash", None), manifest_url=escrow.get("manifestUrl", None), recording_oracle=escrow.get("recordingOracle", None), - recording_oracle_fee=int(escrow.get("recordingOracleFee")) - if escrow.get("recordingOracleFee", None) - else None, + recording_oracle_fee=( + int(escrow.get("recordingOracleFee")) + if escrow.get("recordingOracleFee", None) + else None + ), reputation_oracle=escrow.get("reputationOracle", None), - reputation_oracle_fee=int(escrow.get("reputationOracleFee")) - if escrow.get("reputationOracleFee", None) - else None, + reputation_oracle_fee=( + int(escrow.get("reputationOracleFee")) + if escrow.get("reputationOracleFee", None) + else None + ), exchange_oracle=escrow.get("exchangeOracle", None), - exchange_oracle_fee=int(escrow.get("exchangeOracleFee")) - if escrow.get("exchangeOracleFee", None) - else None, + exchange_oracle_fee=( + int(escrow.get("exchangeOracleFee")) + if escrow.get("exchangeOracleFee", None) + else None + ), ) for escrow in escrows_raw ] @@ -294,15 +306,21 @@ def get_escrow( manifest_hash=escrow.get("manifestHash", None), manifest_url=escrow.get("manifestUrl", None), recording_oracle=escrow.get("recordingOracle", None), - recording_oracle_fee=int(escrow.get("recordingOracleFee")) - if escrow.get("recordingOracleFee", None) - else None, + recording_oracle_fee=( + int(escrow.get("recordingOracleFee")) + if escrow.get("recordingOracleFee", None) + else None + ), reputation_oracle=escrow.get("reputationOracle", None), - reputation_oracle_fee=int(escrow.get("reputationOracleFee")) - if escrow.get("reputationOracleFee", None) - else None, + reputation_oracle_fee=( + int(escrow.get("reputationOracleFee")) + if escrow.get("reputationOracleFee", None) + else None + ), exchange_oracle=escrow.get("exchangeOracle", None), - exchange_oracle_fee=int(escrow.get("exchangeOracleFee")) - if escrow.get("exchangeOracleFee", None) - else None, + exchange_oracle_fee=( + int(escrow.get("exchangeOracleFee")) + if escrow.get("exchangeOracleFee", None) + else None + ), ) diff --git a/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/gql/escrow.py b/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/gql/escrow.py index 3d8abdaebc..168c0dbca9 100644 --- a/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/gql/escrow.py +++ b/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/gql/escrow.py @@ -59,18 +59,18 @@ def get_escrows_query(filter: EscrowFilter): """.format( escrow_fragment=escrow_fragment, launcher_clause="launcher: $launcher" if filter.launcher else "", - reputation_oracle_clause="reputationOracle: $reputationOracle" - if filter.reputation_oracle - else "", - recording_oracle_clause="recordingOracle: $recordingOracle" - if filter.recording_oracle - else "", - exchange_oracle_clause="exchangeOracle: $exchangeOracle" - if filter.exchange_oracle - else "", - job_requester_clause="jobRequesterId: $jobRequesterId" - if filter.job_requester_id - else "", + reputation_oracle_clause=( + "reputationOracle: $reputationOracle" if filter.reputation_oracle else "" + ), + recording_oracle_clause=( + "recordingOracle: $recordingOracle" if filter.recording_oracle else "" + ), + exchange_oracle_clause=( + "exchangeOracle: $exchangeOracle" if filter.exchange_oracle else "" + ), + job_requester_clause=( + "jobRequesterId: $jobRequesterId" if filter.job_requester_id else "" + ), status_clause="status: $status" if filter.status else "", from_clause="createdAt_gte: $from" if filter.date_from else "", to_clause="createdAt_lte: $to" if filter.date_from else "", @@ -87,6 +87,4 @@ def get_escrow_query(): }} }} {escrow_fragment} -""".format( - escrow_fragment=escrow_fragment - ) +""".format(escrow_fragment=escrow_fragment) diff --git a/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/gql/hmtoken.py b/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/gql/hmtoken.py index a9bd32c28e..04f2096ad7 100644 --- a/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/gql/hmtoken.py +++ b/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/gql/hmtoken.py @@ -12,6 +12,4 @@ }} }} {holder_fragment} -""".format( - holder_fragment=holder_fragment -) +""".format(holder_fragment=holder_fragment) diff --git a/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/gql/payout.py b/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/gql/payout.py index 17bcf3c8a4..59db6df906 100644 --- a/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/gql/payout.py +++ b/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/gql/payout.py @@ -33,9 +33,9 @@ def get_payouts_query(filter: PayoutFilter): {payout_fragment} """.format( payout_fragment=payout_fragment, - escrow_address_clause="escrowAddress: $escrowAddress" - if filter.escrow_address - else "", + escrow_address_clause=( + "escrowAddress: $escrowAddress" if filter.escrow_address else "" + ), recipient_clause="recipient: $recipient" if filter.recipient else "", from_clause="createdAt_gte: $from" if filter.date_from else "", to_clause="createdAt_lte: $to" if filter.date_from else "", diff --git a/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/gql/reward.py b/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/gql/reward.py index 3f43bbff9e..09c06278ae 100644 --- a/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/gql/reward.py +++ b/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/gql/reward.py @@ -14,6 +14,4 @@ }} }} {reward_added_event_fragment} -""".format( - reward_added_event_fragment=reward_added_event_fragment -) +""".format(reward_added_event_fragment=reward_added_event_fragment) diff --git a/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/gql/statistics.py b/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/gql/statistics.py index 74da8d6815..4e787baa9a 100644 --- a/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/gql/statistics.py +++ b/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/gql/statistics.py @@ -56,9 +56,7 @@ }} }} {hmtoken_statistics_fragment} -""".format( - hmtoken_statistics_fragment=hmtoken_statistics_fragment -) +""".format(hmtoken_statistics_fragment=hmtoken_statistics_fragment) get_escrow_statistics_query = """ query GetEscrowStatistics {{ @@ -67,9 +65,7 @@ }} }} {escrow_statistics_fragment} -""".format( - escrow_statistics_fragment=escrow_statistics_fragment -) +""".format(escrow_statistics_fragment=escrow_statistics_fragment) def get_event_day_data_query(param: StatisticsParam): diff --git a/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/statistics/statistics_client.py b/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/statistics/statistics_client.py index eb59b0a01f..0321154d95 100644 --- a/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/statistics/statistics_client.py +++ b/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/statistics/statistics_client.py @@ -470,12 +470,12 @@ def get_payment_statistics( ), total_amount_paid=int(event_day_data.get("dailyPayoutAmount", 0)), total_count=int(event_day_data.get("dailyPayoutCount", 0)), - average_amount_per_worker=int( - event_day_data.get("dailyPayoutAmount", 0) - ) - / int(event_day_data.get("dailyWorkerCount")) - if event_day_data.get("dailyWorkerCount", "0") != "0" - else 0, + average_amount_per_worker=( + int(event_day_data.get("dailyPayoutAmount", 0)) + / int(event_day_data.get("dailyWorkerCount")) + if event_day_data.get("dailyWorkerCount", "0") != "0" + else 0 + ), ) for event_day_data in event_day_datas ], diff --git a/packages/sdk/python/human-protocol-sdk/test/human_protocol_sdk/escrow/test_escrow_client.py b/packages/sdk/python/human-protocol-sdk/test/human_protocol_sdk/escrow/test_escrow_client.py index 34b302e8c3..14d3be5587 100644 --- a/packages/sdk/python/human-protocol-sdk/test/human_protocol_sdk/escrow/test_escrow_client.py +++ b/packages/sdk/python/human-protocol-sdk/test/human_protocol_sdk/escrow/test_escrow_client.py @@ -8,6 +8,7 @@ from human_protocol_sdk.escrow import EscrowClient, EscrowClientError, EscrowConfig from human_protocol_sdk.filter import EscrowFilter, FilterError from web3 import Web3 +from web3.constants import ADDRESS_ZERO from web3.middleware import construct_sign_and_send_raw_middleware from web3.providers.rpc import HTTPProvider @@ -1478,12 +1479,49 @@ def test_cancel(self): mock_contract = MagicMock() mock_contract.functions.cancel = MagicMock() self.escrow._get_escrow_contract = MagicMock(return_value=mock_contract) - escrow_address = "0x1234567890123456789012345678901234567890" + escrow_address = "0xa76507AbFE3B67cB25F16DbC75a883D4190B7e46" + token_address = "0x0376D26246Eb35FF4F9924cF13E6C05fd0bD7Fb4" + + self.escrow.get_token_address = MagicMock(return_value=token_address) with patch( "human_protocol_sdk.escrow.escrow_client.handle_transaction" ) as mock_function: - self.escrow.cancel(escrow_address) + tx_hash = bytes.fromhex( + "01682095d5abb0270d11a31139b9a1f410b363c84add467004e728ec831bd529" + ) + amount_refunded = 187744067287473730 + mock_function.return_value = { + "transactionHash": tx_hash, + "logs": [ + { + "logIndex": 0, + "transactionIndex": 0, + "transactionHash": tx_hash, + "blockHash": bytes.fromhex( + "92abf9325a3959a911a2581e9ea36cba3060d8b293b50e5738ff959feb95258a" + ), + "blockNumber": 5, + "address": token_address, + "data": bytes.fromhex( + "000000000000000000000000000000000000000000000000029b003c075b5e42" + ), + "topics": [ + bytes.fromhex( + "ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" + ), + bytes.fromhex( + "000000000000000000000000a76507abfe3b67cb25f16dbc75a883d4190b7e46" + ), + bytes.fromhex( + "0000000000000000000000005607acf0828e238099aa1784541a5abd7f975c76" + ), + ], + } + ], + } + + escrow_cancel_data = self.escrow.cancel(escrow_address) self.escrow._get_escrow_contract.assert_called_once_with(escrow_address) mock_contract.functions.cancel.assert_called_once_with() @@ -1495,6 +1533,9 @@ def test_cancel(self): None, ) + self.assertEqual(escrow_cancel_data.txHash, tx_hash.hex()) + self.assertEqual(escrow_cancel_data.amountRefunded, amount_refunded) + def test_cancel_invalid_address(self): escrow_address = "invalid_address" @@ -1560,13 +1601,51 @@ def test_cancel_with_tx_options(self): mock_contract = MagicMock() mock_contract.functions.cancel = MagicMock() self.escrow._get_escrow_contract = MagicMock(return_value=mock_contract) - escrow_address = "0x1234567890123456789012345678901234567890" tx_options = {"gas": 50000} + escrow_address = "0xa76507AbFE3B67cB25F16DbC75a883D4190B7e46" + token_address = "0x0376D26246Eb35FF4F9924cF13E6C05fd0bD7Fb4" + + self.escrow.get_token_address = MagicMock(return_value=token_address) + with patch( "human_protocol_sdk.escrow.escrow_client.handle_transaction" ) as mock_function: - self.escrow.cancel(escrow_address, tx_options) + tx_hash = bytes.fromhex( + "01682095d5abb0270d11a31139b9a1f410b363c84add467004e728ec831bd529" + ) + amount_refunded = 187744067287473730 + mock_function.return_value = { + "transactionHash": tx_hash, + "logs": [ + { + "logIndex": 0, + "transactionIndex": 0, + "transactionHash": tx_hash, + "blockHash": bytes.fromhex( + "92abf9325a3959a911a2581e9ea36cba3060d8b293b50e5738ff959feb95258a" + ), + "blockNumber": 5, + "address": token_address, + "data": bytes.fromhex( + "000000000000000000000000000000000000000000000000029b003c075b5e42" + ), + "topics": [ + bytes.fromhex( + "ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" + ), + bytes.fromhex( + "000000000000000000000000a76507abfe3b67cb25f16dbc75a883d4190b7e46" + ), + bytes.fromhex( + "0000000000000000000000005607acf0828e238099aa1784541a5abd7f975c76" + ), + ], + } + ], + } + + escrow_cancel_data = self.escrow.cancel(escrow_address, tx_options) self.escrow._get_escrow_contract.assert_called_once_with(escrow_address) mock_contract.functions.cancel.assert_called_once_with() @@ -1578,6 +1657,9 @@ def test_cancel_with_tx_options(self): tx_options, ) + self.assertEqual(escrow_cancel_data.txHash, tx_hash.hex()) + self.assertEqual(escrow_cancel_data.amountRefunded, amount_refunded) + def test_abort(self): mock_contract = MagicMock() mock_contract.functions.abort = MagicMock() diff --git a/packages/sdk/python/human-protocol-sdk/test/human_protocol_sdk/kvstore/test_kvstore_client.py b/packages/sdk/python/human-protocol-sdk/test/human_protocol_sdk/kvstore/test_kvstore_client.py index e5238ea181..7937a84700 100644 --- a/packages/sdk/python/human-protocol-sdk/test/human_protocol_sdk/kvstore/test_kvstore_client.py +++ b/packages/sdk/python/human-protocol-sdk/test/human_protocol_sdk/kvstore/test_kvstore_client.py @@ -241,9 +241,12 @@ def test_set_url_with_key(self): content = "example" content_hash = self.w3.keccak(text=content).hex() - with patch( - "human_protocol_sdk.kvstore.kvstore_client.handle_transaction" - ) as mock_handle_transaction, patch("requests.get") as mock_get: + with ( + patch( + "human_protocol_sdk.kvstore.kvstore_client.handle_transaction" + ) as mock_handle_transaction, + patch("requests.get") as mock_get, + ): mock_response = mock_get.return_value mock_response.text = content From fa803de0638e8513f6c9d43f4c25ad62d68dabc4 Mon Sep 17 00:00:00 2001 From: m00n620 <50647994+m00n620@users.noreply.github.com> Date: Thu, 11 Jan 2024 03:32:13 -0500 Subject: [PATCH 025/104] fix date sorting for solved tasks chart (#1447) --- .../apps/dashboard/ui/src/hooks/useMonthlyTaskSummaries.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/apps/dashboard/ui/src/hooks/useMonthlyTaskSummaries.ts b/packages/apps/dashboard/ui/src/hooks/useMonthlyTaskSummaries.ts index 5cfdde5ea2..39bcd8ef43 100644 --- a/packages/apps/dashboard/ui/src/hooks/useMonthlyTaskSummaries.ts +++ b/packages/apps/dashboard/ui/src/hooks/useMonthlyTaskSummaries.ts @@ -9,7 +9,9 @@ export function useMonthlyTaskSummaries() { const from = dayjs().startOf('month').format('YYYY-MM-DD'); const to = dayjs().endOf('month').format('YYYY-MM-DD'); const [cachedData, thisMonthData] = await Promise.all([ - axios.get(`${apiURL}/monthly-task-summaries`).then((res) => res.data), + axios + .get(`${apiURL}/monthly-task-summaries?sort=id`) + .then((res) => res.data), axios .get(`${apiURL}/stats/tasks?to=${to}&from=${from}`) .then((res) => res.data), From 60fa171f068f18ea2563ee0dbb1ad3f96cf3ef5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20L=C3=B3pez?= <50665615+flopez7@users.noreply.github.com> Date: Mon, 15 Jan 2024 11:52:28 +0100 Subject: [PATCH 026/104] Fix error messages when reverting transactions (#1449) --- .../typescript/human-protocol-sdk/src/utils.ts | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/packages/sdk/typescript/human-protocol-sdk/src/utils.ts b/packages/sdk/typescript/human-protocol-sdk/src/utils.ts index 7838390123..b1ade8a602 100644 --- a/packages/sdk/typescript/human-protocol-sdk/src/utils.ts +++ b/packages/sdk/typescript/human-protocol-sdk/src/utils.ts @@ -11,21 +11,6 @@ import { TransactionReplaced, } from './error'; -/** - * **Get specific error text.* - * - * @param {any} error - An error message. - * @returns - */ -export const getRevertReason = (error: any): string => { - const prefix = "reverted with reason string '"; - const suffix = "'"; - const message = error.data.substring( - error.data.indexOf(prefix) + prefix.length - ); - return message.substring(0, message.indexOf(suffix)); -}; - /** * **Handle and throw the error.* * @@ -36,8 +21,7 @@ export const throwError = (e: any) => { if (ethers.isError(e, 'INVALID_ARGUMENT')) { throw new InvalidArgumentError(e.message); } else if (ethers.isError(e, 'CALL_EXCEPTION')) { - const reason = getRevertReason(e.data); - throw new ContractExecutionError(reason); + throw new ContractExecutionError(e.reason as string); } else if (ethers.isError(e, 'TRANSACTION_REPLACED')) { throw new TransactionReplaced(e.message); } else if (ethers.isError(e, 'REPLACEMENT_UNDERPRICED')) { From b93a2f77a1b94b0ef64dc40c81704a9e7b2d22c9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Jan 2024 23:22:52 +0800 Subject: [PATCH 027/104] Bump @nestjs/schedule from 3.0.4 to 4.0.0 (#1459) Bumps [@nestjs/schedule](https://github.com/nestjs/schedule) from 3.0.4 to 4.0.0. - [Release notes](https://github.com/nestjs/schedule/releases) - [Changelog](https://github.com/nestjs/schedule/blob/master/.release-it.json) - [Commits](https://github.com/nestjs/schedule/compare/3.0.4...4.0.0) --- updated-dependencies: - dependency-name: "@nestjs/schedule" dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .../hufi/job-launcher/server/package.json | 2 +- .../reputation-oracle/server/package.json | 2 +- .../apps/job-launcher/server/package.json | 2 +- yarn.lock | 21 ------------------- 4 files changed, 3 insertions(+), 24 deletions(-) diff --git a/packages/apps/hufi/job-launcher/server/package.json b/packages/apps/hufi/job-launcher/server/package.json index 668c97e26d..7dac99d74b 100644 --- a/packages/apps/hufi/job-launcher/server/package.json +++ b/packages/apps/hufi/job-launcher/server/package.json @@ -36,7 +36,7 @@ "@nestjs/jwt": "^10.2.0", "@nestjs/passport": "^10.0.0", "@nestjs/platform-express": "^10.2.6", - "@nestjs/schedule": "^3.0.1", + "@nestjs/schedule": "^4.0.0", "@nestjs/serve-static": "^4.0.0", "@nestjs/swagger": "^7.1.13", "@nestjs/terminus": "^10.2.0", diff --git a/packages/apps/hufi/reputation-oracle/server/package.json b/packages/apps/hufi/reputation-oracle/server/package.json index 270e6bf9e9..db07fb57db 100644 --- a/packages/apps/hufi/reputation-oracle/server/package.json +++ b/packages/apps/hufi/reputation-oracle/server/package.json @@ -37,7 +37,7 @@ "@nestjs/jwt": "^10.2.0", "@nestjs/passport": "^10.0.0", "@nestjs/platform-express": "^10.2.6", - "@nestjs/schedule": "^3.0.1", + "@nestjs/schedule": "^4.0.0", "@nestjs/swagger": "^7.1.13", "@nestjs/terminus": "^10.2.0", "@nestjs/typeorm": "^10.0.1", diff --git a/packages/apps/job-launcher/server/package.json b/packages/apps/job-launcher/server/package.json index a2af88670b..04f5aa4c3e 100644 --- a/packages/apps/job-launcher/server/package.json +++ b/packages/apps/job-launcher/server/package.json @@ -36,7 +36,7 @@ "@nestjs/jwt": "^10.2.0", "@nestjs/passport": "^10.0.0", "@nestjs/platform-express": "^10.2.6", - "@nestjs/schedule": "^3.0.1", + "@nestjs/schedule": "^4.0.0", "@nestjs/serve-static": "^4.0.0", "@nestjs/swagger": "^7.1.13", "@nestjs/terminus": "^10.2.0", diff --git a/yarn.lock b/yarn.lock index 2a217dfffd..8017990499 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3734,14 +3734,6 @@ multer "1.4.4-lts.1" tslib "2.6.2" -"@nestjs/schedule@^3.0.1": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@nestjs/schedule/-/schedule-3.0.4.tgz#c2a3d96bb2cb7d562349f030823080b8513b34c3" - integrity sha512-uFJpuZsXfpvgx2y7/KrIZW9e1L68TLiwRodZ6+Gc8xqQiHSUzAVn+9F4YMxWFlHITZvvkjWziUFgRNCitDcTZQ== - dependencies: - cron "2.4.3" - uuid "9.0.1" - "@nestjs/schedule@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@nestjs/schedule/-/schedule-4.0.0.tgz#522fa0c79a2b44a66aab16a46bdf4c11ae73f3c3" @@ -10886,14 +10878,6 @@ cron-parser@^3.5.0: is-nan "^1.3.2" luxon "^1.26.0" -cron@2.4.3: - version "2.4.3" - resolved "https://registry.yarnpkg.com/cron/-/cron-2.4.3.tgz#4e43d8d9a6373b8f28d876c4e9a47c14422d8652" - integrity sha512-YBvExkQYF7w0PxyeFLRyr817YVDhGxaCi5/uRRMqa4aWD3IFKRd+uNbpW1VWMdqQy8PZ7CElc+accXJcauPKzQ== - dependencies: - "@types/luxon" "~3.3.0" - luxon "~3.3.0" - cron@3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/cron/-/cron-3.1.3.tgz#4eac8f6691ce7e24c8e89b5317b8097d6f2d0053" @@ -18252,11 +18236,6 @@ luxon@^1.26.0: resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.28.1.tgz#528cdf3624a54506d710290a2341aa8e6e6c61b0" integrity sha512-gYHAa180mKrNIUJCbwpmD0aTu9kV0dREDrwNnuyFAsO1Wt0EVYSZelPnJlbj9HplzXX/YWXHFTL45kvZ53M0pw== -luxon@~3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.3.0.tgz#d73ab5b5d2b49a461c47cedbc7e73309b4805b48" - integrity sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg== - luxon@~3.4.0: version "3.4.4" resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.4.4.tgz#cf20dc27dc532ba41a169c43fdcc0063601577af" From bf73c0b8f4cc57cc4a79c6dddd14a2242690dd2f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Jan 2024 23:27:58 +0800 Subject: [PATCH 028/104] Bump supertest and @types/supertest (#1460) Bumps [supertest](https://github.com/ladjs/supertest) and [@types/supertest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/supertest). These dependencies needed to be updated together. Updates `supertest` from 6.3.3 to 6.3.4 - [Release notes](https://github.com/ladjs/supertest/releases) - [Commits](https://github.com/ladjs/supertest/compare/v6.3.3...v6.3.4) Updates `@types/supertest` from 2.0.16 to 6.0.2 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/supertest) --- updated-dependencies: - dependency-name: supertest dependency-type: direct:development update-type: version-update:semver-patch - dependency-name: "@types/supertest" dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .../exchange-oracle/server/package.json | 4 +-- .../hufi/exchange-oracle/server/package.json | 4 +-- .../hufi/job-launcher/server/package.json | 4 +-- .../reputation-oracle/server/package.json | 4 +-- .../apps/job-launcher/server/package.json | 4 +-- .../reputation-oracle/server/package.json | 4 +-- yarn.lock | 25 ++++++++++--------- 7 files changed, 25 insertions(+), 24 deletions(-) diff --git a/packages/apps/fortune/exchange-oracle/server/package.json b/packages/apps/fortune/exchange-oracle/server/package.json index 58a425463e..ccdb6f57c1 100644 --- a/packages/apps/fortune/exchange-oracle/server/package.json +++ b/packages/apps/fortune/exchange-oracle/server/package.json @@ -36,7 +36,7 @@ "@types/express": "^4.17.13", "@types/jest": "29.5.1", "@types/node": "20.10.6", - "@types/supertest": "^2.0.15", + "@types/supertest": "^6.0.2", "@typescript-eslint/eslint-plugin": "^5.0.0", "@typescript-eslint/parser": "^5.0.0", "eslint": "^8.55.0", @@ -45,7 +45,7 @@ "jest": "29.5.0", "prettier": "^3.1.1", "source-map-support": "^0.5.20", - "supertest": "^6.1.3", + "supertest": "^6.3.4", "ts-jest": "29.1.1", "ts-loader": "^9.2.3", "ts-node": "^10.9.2", diff --git a/packages/apps/hufi/exchange-oracle/server/package.json b/packages/apps/hufi/exchange-oracle/server/package.json index 6479cb7c4d..349c6165ec 100644 --- a/packages/apps/hufi/exchange-oracle/server/package.json +++ b/packages/apps/hufi/exchange-oracle/server/package.json @@ -38,7 +38,7 @@ "@types/express": "^4.17.13", "@types/jest": "29.5.1", "@types/node": "20.10.6", - "@types/supertest": "^2.0.15", + "@types/supertest": "^6.0.2", "@typescript-eslint/eslint-plugin": "^5.0.0", "@typescript-eslint/parser": "^5.0.0", "eslint": "^8.55.0", @@ -47,7 +47,7 @@ "jest": "29.5.0", "prettier": "^3.1.1", "source-map-support": "^0.5.20", - "supertest": "^6.1.3", + "supertest": "^6.3.4", "ts-jest": "29.1.1", "ts-loader": "^9.2.3", "ts-node": "^10.9.2", diff --git a/packages/apps/hufi/job-launcher/server/package.json b/packages/apps/hufi/job-launcher/server/package.json index 7dac99d74b..95b5827c9a 100644 --- a/packages/apps/hufi/job-launcher/server/package.json +++ b/packages/apps/hufi/job-launcher/server/package.json @@ -70,7 +70,7 @@ "@types/express": "^4.17.13", "@types/jest": "29.5.1", "@types/node": "20.10.6", - "@types/supertest": "^2.0.15", + "@types/supertest": "^6.0.2", "@types/zxcvbn": "4.4.1", "@typescript-eslint/eslint-plugin": "^5.0.0", "@typescript-eslint/parser": "^5.0.0", @@ -80,7 +80,7 @@ "jest": "29.5.0", "prettier": "^3.1.1", "source-map-support": "^0.5.20", - "supertest": "^6.1.3", + "supertest": "^6.3.4", "ts-jest": "29.1.1", "ts-loader": "^9.2.3", "ts-node": "^10.9.2", diff --git a/packages/apps/hufi/reputation-oracle/server/package.json b/packages/apps/hufi/reputation-oracle/server/package.json index db07fb57db..bf5bcf1be9 100644 --- a/packages/apps/hufi/reputation-oracle/server/package.json +++ b/packages/apps/hufi/reputation-oracle/server/package.json @@ -70,7 +70,7 @@ "@types/express-session": "^1.17.10", "@types/jest": "29.5.1", "@types/node": "20.10.6", - "@types/supertest": "^2.0.15", + "@types/supertest": "^6.0.2", "@types/uuid": "^9.0.6", "@types/zxcvbn": "4.4.1", "@typescript-eslint/eslint-plugin": "^5.0.0", @@ -82,7 +82,7 @@ "jest": "29.5.0", "prettier": "^3.1.1", "source-map-support": "^0.5.20", - "supertest": "^6.1.3", + "supertest": "^6.3.4", "ts-jest": "29.1.1", "ts-loader": "^9.2.3", "ts-node": "^10.9.2", diff --git a/packages/apps/job-launcher/server/package.json b/packages/apps/job-launcher/server/package.json index 04f5aa4c3e..af20521927 100644 --- a/packages/apps/job-launcher/server/package.json +++ b/packages/apps/job-launcher/server/package.json @@ -73,7 +73,7 @@ "@types/jest": "29.5.1", "@types/json-stable-stringify": "^1.0.36", "@types/node": "20.10.6", - "@types/supertest": "^2.0.15", + "@types/supertest": "^6.0.2", "@types/xml2js": "0.4.13", "@types/zxcvbn": "4.4.1", "@typescript-eslint/eslint-plugin": "^5.0.0", @@ -84,7 +84,7 @@ "jest": "29.5.0", "prettier": "^3.1.1", "source-map-support": "^0.5.20", - "supertest": "^6.1.3", + "supertest": "^6.3.4", "ts-jest": "29.1.1", "ts-loader": "^9.2.3", "ts-node": "^10.9.2", diff --git a/packages/apps/reputation-oracle/server/package.json b/packages/apps/reputation-oracle/server/package.json index 48fb714bcb..da969ed810 100644 --- a/packages/apps/reputation-oracle/server/package.json +++ b/packages/apps/reputation-oracle/server/package.json @@ -70,7 +70,7 @@ "@types/express-session": "^1.17.10", "@types/jest": "29.5.1", "@types/node": "20.10.6", - "@types/supertest": "^2.0.15", + "@types/supertest": "^6.0.2", "@types/uuid": "^9.0.6", "@types/zxcvbn": "4.4.1", "@typescript-eslint/eslint-plugin": "^5.0.0", @@ -82,7 +82,7 @@ "jest": "29.5.0", "prettier": "^3.1.1", "source-map-support": "^0.5.20", - "supertest": "^6.1.3", + "supertest": "^6.3.4", "ts-jest": "29.1.1", "ts-loader": "^9.2.3", "ts-node": "^10.9.2", diff --git a/yarn.lock b/yarn.lock index 8017990499..be1bb9abaa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7040,7 +7040,7 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== -"@types/superagent@*": +"@types/superagent@^8.1.0": version "8.1.1" resolved "https://registry.yarnpkg.com/@types/superagent/-/superagent-8.1.1.tgz#dbc620c5df3770b0c3092f947d6d5e808adae2bc" integrity sha512-YQyEXA4PgCl7EVOoSAS3o0fyPFU6erv5mMixztQYe1bqbWmmn8c+IrqoxjQeZe4MgwXikgcaZPiI/DsbmOVlzA== @@ -7049,12 +7049,13 @@ "@types/methods" "^1.1.4" "@types/node" "*" -"@types/supertest@^2.0.15": - version "2.0.16" - resolved "https://registry.yarnpkg.com/@types/supertest/-/supertest-2.0.16.tgz#7a1294edebecb960d957bbe9b26002a2b7f21cd7" - integrity sha512-6c2ogktZ06tr2ENoZivgm7YnprnhYE4ZoXGMY+oA7IuAf17M8FWvujXZGmxLv8y0PTyts4x5A+erSwVUFA8XSg== +"@types/supertest@^6.0.2": + version "6.0.2" + resolved "https://registry.yarnpkg.com/@types/supertest/-/supertest-6.0.2.tgz#2af1c466456aaf82c7c6106c6b5cbd73a5e86588" + integrity sha512-137ypx2lk/wTQbW6An6safu9hXmajAifU/s7szAHLN/FeIm5w7yR0Wkl9fdJMRSHwOn4HLAI0DaB2TOORuhPDg== dependencies: - "@types/superagent" "*" + "@types/methods" "^1.1.4" + "@types/superagent" "^8.1.0" "@types/through@*": version "0.0.33" @@ -23211,7 +23212,7 @@ stylis@4.2.0: resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.2.0.tgz#79daee0208964c8fe695a42fcffcac633a211a51" integrity sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw== -superagent@^8.0.5: +superagent@^8.1.2: version "8.1.2" resolved "https://registry.yarnpkg.com/superagent/-/superagent-8.1.2.tgz#03cb7da3ec8b32472c9d20f6c2a57c7f3765f30b" integrity sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA== @@ -23237,13 +23238,13 @@ superstruct@^1.0.3: resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-1.0.3.tgz#de626a5b49c6641ff4d37da3c7598e7a87697046" integrity sha512-8iTn3oSS8nRGn+C2pgXSKPI3jmpm6FExNazNpjvqS6ZUJQCej3PUXEKM8NjHBOs54ExM+LPW/FBRhymrdcCiSg== -supertest@^6.1.3: - version "6.3.3" - resolved "https://registry.yarnpkg.com/supertest/-/supertest-6.3.3.tgz#42f4da199fee656106fd422c094cf6c9578141db" - integrity sha512-EMCG6G8gDu5qEqRQ3JjjPs6+FYT1a7Hv5ApHvtSghmOFJYtsU5S+pSb6Y2EUeCEY3CmEL3mmQ8YWlPOzQomabA== +supertest@^6.3.4: + version "6.3.4" + resolved "https://registry.yarnpkg.com/supertest/-/supertest-6.3.4.tgz#2145c250570c2ea5d337db3552dbfb78a2286218" + integrity sha512-erY3HFDG0dPnhw4U+udPfrzXa4xhSG+n4rxfRuZWCUvjFWwKl+OxWf/7zk50s84/fAAs7vf5QAb9uRa0cCykxw== dependencies: methods "^1.1.2" - superagent "^8.0.5" + superagent "^8.1.2" supports-color@8.1.1, supports-color@^8.0.0, supports-color@^8.1.0, supports-color@^8.1.1: version "8.1.1" From 7b73a0e43c55de21f07483bb9c9d725449aea68d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Jan 2024 23:30:09 +0800 Subject: [PATCH 029/104] Bump pg from 8.8.0 to 8.11.3 (#1461) Bumps [pg](https://github.com/brianc/node-postgres/tree/HEAD/packages/pg) from 8.8.0 to 8.11.3. - [Changelog](https://github.com/brianc/node-postgres/blob/master/CHANGELOG.md) - [Commits](https://github.com/brianc/node-postgres/commits/pg@8.11.3/packages/pg) --- updated-dependencies: - dependency-name: pg dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- packages/apps/dashboard/admin/package.json | 2 +- .../hufi/job-launcher/server/package.json | 2 +- .../reputation-oracle/server/package.json | 2 +- .../apps/job-launcher/server/package.json | 2 +- .../reputation-oracle/server/package.json | 2 +- yarn.lock | 35 ++++++------------- 6 files changed, 16 insertions(+), 29 deletions(-) diff --git a/packages/apps/dashboard/admin/package.json b/packages/apps/dashboard/admin/package.json index 9074466592..cf2eb98a46 100644 --- a/packages/apps/dashboard/admin/package.json +++ b/packages/apps/dashboard/admin/package.json @@ -18,7 +18,7 @@ "axios": "^1.5.1", "dayjs": "^1.11.10", "graphql-request": "^6.1.0", - "pg": "8.8.0", + "pg": "8.11.3", "react": "^18.0.0", "react-dom": "^18.0.0", "react-router-dom": "5.3.4", diff --git a/packages/apps/hufi/job-launcher/server/package.json b/packages/apps/hufi/job-launcher/server/package.json index 95b5827c9a..6a05a0a807 100644 --- a/packages/apps/hufi/job-launcher/server/package.json +++ b/packages/apps/hufi/job-launcher/server/package.json @@ -54,7 +54,7 @@ "nestjs-minio-client": "^2.2.0", "passport": "^0.6.0", "passport-jwt": "^4.0.1", - "pg": "8.11.0", + "pg": "8.11.3", "reflect-metadata": "^0.1.13", "rxjs": "^7.2.0", "typeorm": "^0.3.17", diff --git a/packages/apps/hufi/reputation-oracle/server/package.json b/packages/apps/hufi/reputation-oracle/server/package.json index bf5bcf1be9..adc417a1f9 100644 --- a/packages/apps/hufi/reputation-oracle/server/package.json +++ b/packages/apps/hufi/reputation-oracle/server/package.json @@ -52,7 +52,7 @@ "nestjs-minio-client": "^2.2.0", "passport": "^0.6.0", "passport-jwt": "^4.0.1", - "pg": "8.11.0", + "pg": "8.11.3", "reflect-metadata": "^0.1.13", "rxjs": "^7.2.0", "typeorm": "^0.3.16", diff --git a/packages/apps/job-launcher/server/package.json b/packages/apps/job-launcher/server/package.json index af20521927..e4080606bf 100644 --- a/packages/apps/job-launcher/server/package.json +++ b/packages/apps/job-launcher/server/package.json @@ -55,7 +55,7 @@ "nestjs-minio-client": "^2.2.0", "passport": "^0.6.0", "passport-jwt": "^4.0.1", - "pg": "8.11.0", + "pg": "8.11.3", "reflect-metadata": "^0.1.13", "rxjs": "^7.2.0", "stripe": "^14.8.0", diff --git a/packages/apps/reputation-oracle/server/package.json b/packages/apps/reputation-oracle/server/package.json index da969ed810..0511500e8a 100644 --- a/packages/apps/reputation-oracle/server/package.json +++ b/packages/apps/reputation-oracle/server/package.json @@ -52,7 +52,7 @@ "nestjs-minio-client": "^2.2.0", "passport": "^0.6.0", "passport-jwt": "^4.0.1", - "pg": "8.11.0", + "pg": "8.11.3", "reflect-metadata": "^0.1.13", "rxjs": "^7.2.0", "typeorm": "^0.3.16", diff --git a/yarn.lock b/yarn.lock index be1bb9abaa..b207e00a0c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20245,7 +20245,7 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== -pg-cloudflare@^1.1.0: +pg-cloudflare@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz#e6d5833015b170e23ae819e8c5d7eaedb472ca98" integrity sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q== @@ -20255,7 +20255,7 @@ pg-connection-string@2.6.1: resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.6.1.tgz#78c23c21a35dd116f48e12e23c0965e8d9e2cbfb" integrity sha512-w6ZzNu6oMmIzEAYVw+RLK0+nqHPt8K3ZnknKi+g48Ak2pr3dtljJW3o+D/n2zzCG07Zoe9VOX3aiKpj+BN0pjg== -pg-connection-string@^2.5.0, pg-connection-string@^2.6.0: +pg-connection-string@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.6.2.tgz#713d82053de4e2bd166fab70cd4f26ad36aab475" integrity sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA== @@ -20265,12 +20265,12 @@ pg-int8@1.0.1: resolved "https://registry.yarnpkg.com/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c" integrity sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw== -pg-pool@^3.5.2, pg-pool@^3.6.0: +pg-pool@^3.6.1: version "3.6.1" resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-3.6.1.tgz#5a902eda79a8d7e3c928b77abf776b3cb7d351f7" integrity sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og== -pg-protocol@^1.5.0, pg-protocol@^1.6.0: +pg-protocol@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/pg-protocol/-/pg-protocol-1.6.0.tgz#4c91613c0315349363af2084608db843502f8833" integrity sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q== @@ -20286,33 +20286,20 @@ pg-types@^2.1.0: postgres-date "~1.0.4" postgres-interval "^1.1.0" -pg@8.11.0: - version "8.11.0" - resolved "https://registry.yarnpkg.com/pg/-/pg-8.11.0.tgz#a37e534e94b57a7ed811e926f23a7c56385f55d9" - integrity sha512-meLUVPn2TWgJyLmy7el3fQQVwft4gU5NGyvV0XbD41iU9Jbg8lCH4zexhIkihDzVHJStlt6r088G6/fWeNjhXA== +pg@8.11.3: + version "8.11.3" + resolved "https://registry.yarnpkg.com/pg/-/pg-8.11.3.tgz#d7db6e3fe268fcedd65b8e4599cda0b8b4bf76cb" + integrity sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g== dependencies: buffer-writer "2.0.0" packet-reader "1.0.0" - pg-connection-string "^2.6.0" - pg-pool "^3.6.0" + pg-connection-string "^2.6.2" + pg-pool "^3.6.1" pg-protocol "^1.6.0" pg-types "^2.1.0" pgpass "1.x" optionalDependencies: - pg-cloudflare "^1.1.0" - -pg@8.8.0: - version "8.8.0" - resolved "https://registry.yarnpkg.com/pg/-/pg-8.8.0.tgz#a77f41f9d9ede7009abfca54667c775a240da686" - integrity sha512-UXYN0ziKj+AeNNP7VDMwrehpACThH7LUl/p8TDFpEUuSejCUIwGSfxpHsPvtM6/WXFy6SU4E5RG4IJV/TZAGjw== - dependencies: - buffer-writer "2.0.0" - packet-reader "1.0.0" - pg-connection-string "^2.5.0" - pg-pool "^3.5.2" - pg-protocol "^1.5.0" - pg-types "^2.1.0" - pgpass "1.x" + pg-cloudflare "^1.1.1" pgpass@1.x: version "1.0.5" From e46c32c580527c2df92bc52de68be85b6e99df9a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Jan 2024 23:30:41 +0800 Subject: [PATCH 030/104] Bump jest and @types/jest (#1462) Bumps [jest](https://github.com/jestjs/jest/tree/HEAD/packages/jest) and [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest). These dependencies needed to be updated together. Updates `jest` from 27.5.1 to 29.7.0 - [Release notes](https://github.com/jestjs/jest/releases) - [Changelog](https://github.com/jestjs/jest/blob/main/CHANGELOG.md) - [Commits](https://github.com/jestjs/jest/commits/v29.7.0/packages/jest) Updates `@types/jest` from 29.5.1 to 29.5.11 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest) --- updated-dependencies: - dependency-name: jest dependency-type: direct:development update-type: version-update:semver-major - dependency-name: "@types/jest" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 4 +- .../exchange-oracle/server/package.json | 4 +- .../hufi/exchange-oracle/server/package.json | 4 +- .../hufi/job-launcher/server/package.json | 4 +- .../reputation-oracle/server/package.json | 4 +- .../apps/job-launcher/server/package.json | 4 +- packages/apps/meta-code-verify/package.json | 4 +- .../reputation-oracle/server/package.json | 4 +- yarn.lock | 907 +----------------- 9 files changed, 46 insertions(+), 893 deletions(-) diff --git a/package.json b/package.json index c0f2107cbb..63400d3f19 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "@babel/preset-react": "^7.18.6", "@babel/preset-typescript": "^7.18.6", "@jest/globals": "^29.3.1", - "@types/jest": "^29.2.3", + "@types/jest": "^29.5.11", "@types/node": "^20.10.6", "@typescript-eslint/eslint-plugin": "^5.43.0", "@typescript-eslint/parser": "^5.43.0", @@ -57,7 +57,7 @@ "eslint-plugin-jest": "^27.1.5", "eslint-plugin-prettier": "^5.0.0", "husky": "^8.0.2", - "jest": "^29.3.1", + "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", "lint-staged": "^15.2.0", "prettier": "^3.1.1", diff --git a/packages/apps/fortune/exchange-oracle/server/package.json b/packages/apps/fortune/exchange-oracle/server/package.json index ccdb6f57c1..410dc75267 100644 --- a/packages/apps/fortune/exchange-oracle/server/package.json +++ b/packages/apps/fortune/exchange-oracle/server/package.json @@ -34,7 +34,7 @@ "@nestjs/schematics": "^9.2.0", "@nestjs/testing": "^9.4.3", "@types/express": "^4.17.13", - "@types/jest": "29.5.1", + "@types/jest": "29.5.11", "@types/node": "20.10.6", "@types/supertest": "^6.0.2", "@typescript-eslint/eslint-plugin": "^5.0.0", @@ -42,7 +42,7 @@ "eslint": "^8.55.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.0.0", - "jest": "29.5.0", + "jest": "29.7.0", "prettier": "^3.1.1", "source-map-support": "^0.5.20", "supertest": "^6.3.4", diff --git a/packages/apps/hufi/exchange-oracle/server/package.json b/packages/apps/hufi/exchange-oracle/server/package.json index 349c6165ec..8cb599742d 100644 --- a/packages/apps/hufi/exchange-oracle/server/package.json +++ b/packages/apps/hufi/exchange-oracle/server/package.json @@ -36,7 +36,7 @@ "@nestjs/schematics": "^9.2.0", "@nestjs/testing": "^9.4.3", "@types/express": "^4.17.13", - "@types/jest": "29.5.1", + "@types/jest": "29.5.11", "@types/node": "20.10.6", "@types/supertest": "^6.0.2", "@typescript-eslint/eslint-plugin": "^5.0.0", @@ -44,7 +44,7 @@ "eslint": "^8.55.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.0.0", - "jest": "29.5.0", + "jest": "29.7.0", "prettier": "^3.1.1", "source-map-support": "^0.5.20", "supertest": "^6.3.4", diff --git a/packages/apps/hufi/job-launcher/server/package.json b/packages/apps/hufi/job-launcher/server/package.json index 6a05a0a807..828aad1d92 100644 --- a/packages/apps/hufi/job-launcher/server/package.json +++ b/packages/apps/hufi/job-launcher/server/package.json @@ -68,7 +68,7 @@ "@nestjs/testing": "^9.4.3", "@types/bcrypt": "^5.0.2", "@types/express": "^4.17.13", - "@types/jest": "29.5.1", + "@types/jest": "29.5.11", "@types/node": "20.10.6", "@types/supertest": "^6.0.2", "@types/zxcvbn": "4.4.1", @@ -77,7 +77,7 @@ "eslint": "^8.55.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.0.0", - "jest": "29.5.0", + "jest": "29.7.0", "prettier": "^3.1.1", "source-map-support": "^0.5.20", "supertest": "^6.3.4", diff --git a/packages/apps/hufi/reputation-oracle/server/package.json b/packages/apps/hufi/reputation-oracle/server/package.json index adc417a1f9..d8e81bbf47 100644 --- a/packages/apps/hufi/reputation-oracle/server/package.json +++ b/packages/apps/hufi/reputation-oracle/server/package.json @@ -68,7 +68,7 @@ "@types/cookie-parser": "^1.4.3", "@types/express": "^4.17.13", "@types/express-session": "^1.17.10", - "@types/jest": "29.5.1", + "@types/jest": "29.5.11", "@types/node": "20.10.6", "@types/supertest": "^6.0.2", "@types/uuid": "^9.0.6", @@ -79,7 +79,7 @@ "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.0.0", "express-session": "^1.17.3", - "jest": "29.5.0", + "jest": "29.7.0", "prettier": "^3.1.1", "source-map-support": "^0.5.20", "supertest": "^6.3.4", diff --git a/packages/apps/job-launcher/server/package.json b/packages/apps/job-launcher/server/package.json index e4080606bf..dda2bb317a 100644 --- a/packages/apps/job-launcher/server/package.json +++ b/packages/apps/job-launcher/server/package.json @@ -70,7 +70,7 @@ "@nestjs/testing": "^9.4.3", "@types/bcrypt": "^5.0.2", "@types/express": "^4.17.13", - "@types/jest": "29.5.1", + "@types/jest": "29.5.11", "@types/json-stable-stringify": "^1.0.36", "@types/node": "20.10.6", "@types/supertest": "^6.0.2", @@ -81,7 +81,7 @@ "eslint": "^8.55.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.0.0", - "jest": "29.5.0", + "jest": "29.7.0", "prettier": "^3.1.1", "source-map-support": "^0.5.20", "supertest": "^6.3.4", diff --git a/packages/apps/meta-code-verify/package.json b/packages/apps/meta-code-verify/package.json index 84538908d3..bbabe9ca73 100644 --- a/packages/apps/meta-code-verify/package.json +++ b/packages/apps/meta-code-verify/package.json @@ -26,12 +26,12 @@ "@rollup/plugin-replace": "^5.0.2", "@rollup/plugin-typescript": "^11.0.0", "@types/chrome": "^0.0.246", - "@types/jest": "^29.4.0", + "@types/jest": "^29.5.11", "@typescript-eslint/eslint-plugin": "^5.52.0", "@typescript-eslint/parser": "^5.52.0", "assert": "^2.0.0", "eslint": "^8.55.0", - "jest": "^27.1.0", + "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", "prettier": "^3.1.1", "rollup": "^2.56.3", diff --git a/packages/apps/reputation-oracle/server/package.json b/packages/apps/reputation-oracle/server/package.json index 0511500e8a..e4f6d4d1de 100644 --- a/packages/apps/reputation-oracle/server/package.json +++ b/packages/apps/reputation-oracle/server/package.json @@ -68,7 +68,7 @@ "@types/cookie-parser": "^1.4.3", "@types/express": "^4.17.13", "@types/express-session": "^1.17.10", - "@types/jest": "29.5.1", + "@types/jest": "29.5.11", "@types/node": "20.10.6", "@types/supertest": "^6.0.2", "@types/uuid": "^9.0.6", @@ -79,7 +79,7 @@ "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.0.0", "express-session": "^1.17.3", - "jest": "29.5.0", + "jest": "29.7.0", "prettier": "^3.1.1", "source-map-support": "^0.5.20", "supertest": "^6.3.4", diff --git a/yarn.lock b/yarn.lock index b207e00a0c..4dd921e154 100644 --- a/yarn.lock +++ b/yarn.lock @@ -135,7 +135,7 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.23.5.tgz#ffb878728bb6bdcb6f4510aa51b1be9afb8cfd98" integrity sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw== -"@babel/core@^7.1.0", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.16.0", "@babel/core@^7.20.12", "@babel/core@^7.22.20", "@babel/core@^7.23.5", "@babel/core@^7.7.2", "@babel/core@^7.8.0": +"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.16.0", "@babel/core@^7.20.12", "@babel/core@^7.22.20", "@babel/core@^7.23.5": version "7.23.7" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.23.7.tgz#4d8016e06a14b5f92530a13ed0561730b5c6483f" integrity sha512-+UpDgowcmqe36d4NwqvKsyPMlOLNGMsfMmQ5WGCu+siCe3t3dfe9njrzGfdN4qq+bcNUt0+Vw6haRxBOycs4dw== @@ -1231,7 +1231,7 @@ "@babel/parser" "^7.22.15" "@babel/types" "^7.22.15" -"@babel/traverse@^7.16.8", "@babel/traverse@^7.23.7", "@babel/traverse@^7.4.5", "@babel/traverse@^7.7.2": +"@babel/traverse@^7.16.8", "@babel/traverse@^7.23.7", "@babel/traverse@^7.4.5": version "7.23.7" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.7.tgz#9a7bf285c928cb99b5ead19c3b1ce5b310c9c305" integrity sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg== @@ -2924,18 +2924,6 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== -"@jest/console@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-27.5.1.tgz#260fe7239602fe5130a94f1aa386eff54b014bba" - integrity sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg== - dependencies: - "@jest/types" "^27.5.1" - "@types/node" "*" - chalk "^4.0.0" - jest-message-util "^27.5.1" - jest-util "^27.5.1" - slash "^3.0.0" - "@jest/console@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.7.0.tgz#cd4822dbdb84529265c5a2bdb529a3c9cc950ffc" @@ -2948,41 +2936,7 @@ jest-util "^29.7.0" slash "^3.0.0" -"@jest/core@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.5.1.tgz#267ac5f704e09dc52de2922cbf3af9edcd64b626" - integrity sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ== - dependencies: - "@jest/console" "^27.5.1" - "@jest/reporters" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/node" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" - emittery "^0.8.1" - exit "^0.1.2" - graceful-fs "^4.2.9" - jest-changed-files "^27.5.1" - jest-config "^27.5.1" - jest-haste-map "^27.5.1" - jest-message-util "^27.5.1" - jest-regex-util "^27.5.1" - jest-resolve "^27.5.1" - jest-resolve-dependencies "^27.5.1" - jest-runner "^27.5.1" - jest-runtime "^27.5.1" - jest-snapshot "^27.5.1" - jest-util "^27.5.1" - jest-validate "^27.5.1" - jest-watcher "^27.5.1" - micromatch "^4.0.4" - rimraf "^3.0.0" - slash "^3.0.0" - strip-ansi "^6.0.0" - -"@jest/core@^29.5.0", "@jest/core@^29.7.0": +"@jest/core@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.7.0.tgz#b6cccc239f30ff36609658c5a5e2291757ce448f" integrity sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg== @@ -3016,16 +2970,6 @@ slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/environment@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.5.1.tgz#d7425820511fe7158abbecc010140c3fd3be9c74" - integrity sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA== - dependencies: - "@jest/fake-timers" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/node" "*" - jest-mock "^27.5.1" - "@jest/environment@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.7.0.tgz#24d61f54ff1f786f3cd4073b4b94416383baf2a7" @@ -3051,18 +2995,6 @@ expect "^29.7.0" jest-snapshot "^29.7.0" -"@jest/fake-timers@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.5.1.tgz#76979745ce0579c8a94a4678af7a748eda8ada74" - integrity sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ== - dependencies: - "@jest/types" "^27.5.1" - "@sinonjs/fake-timers" "^8.0.1" - "@types/node" "*" - jest-message-util "^27.5.1" - jest-mock "^27.5.1" - jest-util "^27.5.1" - "@jest/fake-timers@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.7.0.tgz#fd91bf1fffb16d7d0d24a426ab1a47a49881a565" @@ -3075,15 +3007,6 @@ jest-mock "^29.7.0" jest-util "^29.7.0" -"@jest/globals@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.5.1.tgz#7ac06ce57ab966566c7963431cef458434601b2b" - integrity sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q== - dependencies: - "@jest/environment" "^27.5.1" - "@jest/types" "^27.5.1" - expect "^27.5.1" - "@jest/globals@^29.3.1", "@jest/globals@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.7.0.tgz#8d9290f9ec47ff772607fa864ca1d5a2efae1d4d" @@ -3094,37 +3017,6 @@ "@jest/types" "^29.6.3" jest-mock "^29.7.0" -"@jest/reporters@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.5.1.tgz#ceda7be96170b03c923c37987b64015812ffec04" - integrity sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw== - dependencies: - "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/node" "*" - chalk "^4.0.0" - collect-v8-coverage "^1.0.0" - exit "^0.1.2" - glob "^7.1.2" - graceful-fs "^4.2.9" - istanbul-lib-coverage "^3.0.0" - istanbul-lib-instrument "^5.1.0" - istanbul-lib-report "^3.0.0" - istanbul-lib-source-maps "^4.0.0" - istanbul-reports "^3.1.3" - jest-haste-map "^27.5.1" - jest-resolve "^27.5.1" - jest-util "^27.5.1" - jest-worker "^27.5.1" - slash "^3.0.0" - source-map "^0.6.0" - string-length "^4.0.1" - terminal-link "^2.0.0" - v8-to-istanbul "^8.1.0" - "@jest/reporters@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.7.0.tgz#04b262ecb3b8faa83b0b3d321623972393e8f4c7" @@ -3162,15 +3054,6 @@ dependencies: "@sinclair/typebox" "^0.27.8" -"@jest/source-map@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-27.5.1.tgz#6608391e465add4205eae073b55e7f279e04e8cf" - integrity sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg== - dependencies: - callsites "^3.0.0" - graceful-fs "^4.2.9" - source-map "^0.6.0" - "@jest/source-map@^29.6.3": version "29.6.3" resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.6.3.tgz#d90ba772095cf37a34a5eb9413f1b562a08554c4" @@ -3180,16 +3063,6 @@ callsites "^3.0.0" graceful-fs "^4.2.9" -"@jest/test-result@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.5.1.tgz#56a6585fa80f7cdab72b8c5fc2e871d03832f5bb" - integrity sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag== - dependencies: - "@jest/console" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/istanbul-lib-coverage" "^2.0.0" - collect-v8-coverage "^1.0.0" - "@jest/test-result@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.7.0.tgz#8db9a80aa1a097bb2262572686734baed9b1657c" @@ -3200,16 +3073,6 @@ "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-sequencer@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz#4057e0e9cea4439e544c6353c6affe58d095745b" - integrity sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ== - dependencies: - "@jest/test-result" "^27.5.1" - graceful-fs "^4.2.9" - jest-haste-map "^27.5.1" - jest-runtime "^27.5.1" - "@jest/test-sequencer@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz#6cef977ce1d39834a3aea887a1726628a6f072ce" @@ -3220,27 +3083,6 @@ jest-haste-map "^29.7.0" slash "^3.0.0" -"@jest/transform@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.5.1.tgz#6c3501dcc00c4c08915f292a600ece5ecfe1f409" - integrity sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw== - dependencies: - "@babel/core" "^7.1.0" - "@jest/types" "^27.5.1" - babel-plugin-istanbul "^6.1.1" - chalk "^4.0.0" - convert-source-map "^1.4.0" - fast-json-stable-stringify "^2.0.0" - graceful-fs "^4.2.9" - jest-haste-map "^27.5.1" - jest-regex-util "^27.5.1" - jest-util "^27.5.1" - micromatch "^4.0.4" - pirates "^4.0.4" - slash "^3.0.0" - source-map "^0.6.1" - write-file-atomic "^3.0.0" - "@jest/transform@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.7.0.tgz#df2dd9c346c7d7768b8a06639994640c642e284c" @@ -3262,18 +3104,7 @@ slash "^3.0.0" write-file-atomic "^4.0.2" -"@jest/types@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.5.1.tgz#3c79ec4a8ba61c170bf937bcf9e98a9df175ec80" - integrity sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw== - dependencies: - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" - "@types/node" "*" - "@types/yargs" "^16.0.0" - chalk "^4.0.0" - -"@jest/types@^29.5.0", "@jest/types@^29.6.3": +"@jest/types@^29.6.3": version "29.6.3" resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== @@ -5328,13 +5159,6 @@ dependencies: "@sinonjs/commons" "^3.0.0" -"@sinonjs/fake-timers@^8.0.1": - version "8.1.0" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz#3fdc2b6cb58935b21bfb8d1625eb1300484316e7" - integrity sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg== - dependencies: - "@sinonjs/commons" "^1.7.0" - "@sinonjs/formatio@^3.2.1": version "3.2.2" resolved "https://registry.yarnpkg.com/@sinonjs/formatio/-/formatio-3.2.2.tgz#771c60dfa75ea7f2d68e3b94c7e888a78781372c" @@ -6287,11 +6111,6 @@ "@testing-library/dom" "^9.0.0" "@types/react-dom" "^18.0.0" -"@tootallnate/once@1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" - integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== - "@tootallnate/once@2": version "2.0.0" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" @@ -6342,7 +6161,7 @@ resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.4.tgz#1a31c3d378850d2778dabb6374d036dcba4ba708" integrity sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw== -"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14", "@types/babel__core@^7.20.2": +"@types/babel__core@^7.1.14", "@types/babel__core@^7.20.2": version "7.20.5" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== @@ -6368,7 +6187,7 @@ "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" -"@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": version "7.20.5" resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.5.tgz#7b7502be0aa80cc4ef22978846b983edaafcd4dd" integrity sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ== @@ -6644,7 +6463,7 @@ "@types/minimatch" "^5.1.2" "@types/node" "*" -"@types/graceful-fs@^4.1.2", "@types/graceful-fs@^4.1.3": +"@types/graceful-fs@^4.1.3": version "4.1.9" resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.9.tgz#2a06bc0f68a20ab37b3e36aa238be6abdf49e8b4" integrity sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ== @@ -6718,15 +6537,7 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@29.5.1": - version "29.5.1" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.1.tgz#83c818aa9a87da27d6da85d3378e5a34d2f31a47" - integrity sha512-tEuVcHrpaixS36w7hpsfLBLpjtMRJUE09/MHXn923LOVojDwyC14cWcfc0rDs0VEfUyYmt/+iX1kxxp+gZMcaQ== - dependencies: - expect "^29.0.0" - pretty-format "^29.0.0" - -"@types/jest@^29.2.3", "@types/jest@^29.4.0": +"@types/jest@29.5.11", "@types/jest@^29.5.11": version "29.5.11" resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.11.tgz#0c13aa0da7d0929f078ab080ae5d4ced80fa2f2c" integrity sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ== @@ -6921,7 +6732,7 @@ dependencies: "@types/node" "*" -"@types/prettier@^2.1.1", "@types/prettier@^2.1.5": +"@types/prettier@^2.1.1": version "2.7.3" resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.3.tgz#3e51a17e291d01d17d3fc61422015a933af7a08f" integrity sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA== @@ -7134,13 +6945,6 @@ resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== -"@types/yargs@^16.0.0": - version "16.0.9" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.9.tgz#ba506215e45f7707e6cbcaf386981155b7ab956e" - integrity sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA== - dependencies: - "@types/yargs-parser" "*" - "@types/yargs@^17.0.8": version "17.0.32" resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.32.tgz#030774723a2f7faafebf645f4e5a48371dca6229" @@ -8073,7 +7877,7 @@ JSONStream@^1.3.5: jsonparse "^1.2.0" through ">=2.2.7 <3" -abab@^2.0.3, abab@^2.0.5, abab@^2.0.6: +abab@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== @@ -8131,14 +7935,6 @@ accepts@^1.3.5, accepts@~1.3.5, accepts@~1.3.8: mime-types "~2.1.34" negotiator "0.6.3" -acorn-globals@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" - integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg== - dependencies: - acorn "^7.1.1" - acorn-walk "^7.1.1" - acorn-globals@^7.0.0: version "7.0.1" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-7.0.1.tgz#0dbf05c44fa7c94332914c02066d5beff62c40c3" @@ -8157,22 +7953,12 @@ acorn-jsx@^5.3.2: resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -acorn-walk@^7.1.1: - version "7.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" - integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== - acorn-walk@^8.0.0, acorn-walk@^8.0.2, acorn-walk@^8.1.1, acorn-walk@^8.2.0: version "8.3.1" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.1.tgz#2f10f5b69329d90ae18c58bf1fa8fccd8b959a43" integrity sha512-TgUZgYvqZprrl7YldZNoa9OciCAyZR+Ejm9eXzKCmjsF5IKp/wgQ7Z/ZpjpGTIUPwrHQIcYeI8qDh4PsEwxMbw== -acorn@^7.1.1: - version "7.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" - integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== - -acorn@^8.0.4, acorn@^8.1.0, acorn@^8.10.0, acorn@^8.2.4, acorn@^8.4.1, acorn@^8.7.1, acorn@^8.8.1, acorn@^8.8.2, acorn@^8.9.0: +acorn@^8.0.4, acorn@^8.1.0, acorn@^8.10.0, acorn@^8.4.1, acorn@^8.7.1, acorn@^8.8.1, acorn@^8.8.2, acorn@^8.9.0: version "8.11.3" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== @@ -8886,20 +8672,6 @@ b4a@^1.6.4: resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.6.4.tgz#ef1c1422cae5ce6535ec191baeed7567443f36c9" integrity sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw== -babel-jest@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.5.1.tgz#a1bf8d61928edfefd21da27eb86a695bfd691444" - integrity sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg== - dependencies: - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/babel__core" "^7.1.14" - babel-plugin-istanbul "^6.1.1" - babel-preset-jest "^27.5.1" - chalk "^4.0.0" - graceful-fs "^4.2.9" - slash "^3.0.0" - babel-jest@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5" @@ -8924,16 +8696,6 @@ babel-plugin-istanbul@^6.1.1: istanbul-lib-instrument "^5.0.4" test-exclude "^6.0.0" -babel-plugin-jest-hoist@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz#9be98ecf28c331eb9f5df9c72d6f89deb8181c2e" - integrity sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ== - dependencies: - "@babel/template" "^7.3.3" - "@babel/types" "^7.3.3" - "@types/babel__core" "^7.0.0" - "@types/babel__traverse" "^7.0.6" - babel-plugin-jest-hoist@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz#aadbe943464182a8922c3c927c3067ff40d24626" @@ -9011,14 +8773,6 @@ babel-preset-current-node-syntax@^1.0.0: "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-syntax-top-level-await" "^7.8.3" -babel-preset-jest@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz#91f10f58034cb7989cb4f962b69fa6eef6a6bc81" - integrity sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag== - dependencies: - babel-plugin-jest-hoist "^27.5.1" - babel-preset-current-node-syntax "^1.0.0" - babel-preset-jest@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz#fa05fa510e7d493896d7b0dd2033601c840f171c" @@ -9403,11 +9157,6 @@ browser-or-node@^2.1.1: resolved "https://registry.yarnpkg.com/browser-or-node/-/browser-or-node-2.1.1.tgz#738790b3a86a8fc020193fa581273fbe65eaea0f" integrity sha512-8CVjaLJGuSKMVTxJ2DpBl5XnlNDiT4cQFeuCJJrvJmts9YrTZDizTX7PjC2s6W4x+MBGZeEY6dGMrF04/6Hgqg== -browser-process-hrtime@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" - integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== - browser-readablestream-to-it@^1.0.0, browser-readablestream-to-it@^1.0.1, browser-readablestream-to-it@^1.0.2, browser-readablestream-to-it@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/browser-readablestream-to-it/-/browser-readablestream-to-it-1.0.3.tgz#ac3e406c7ee6cdf0a502dd55db33bab97f7fba76" @@ -10646,7 +10395,7 @@ content-type@^1.0.4, content-type@~1.0.4, content-type@~1.0.5: resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== -convert-source-map@^1.4.0, convert-source-map@^1.5.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: +convert-source-map@^1.5.0, convert-source-map@^1.7.0: version "1.9.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== @@ -11024,11 +10773,6 @@ cssesc@^3.0.0: resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== -cssom@^0.4.4: - version "0.4.4" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" - integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== - cssom@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.5.0.tgz#d254fa92cd8b6fbd83811b9fbaed34663cc17c36" @@ -11146,15 +10890,6 @@ data-uri-to-buffer@^3.0.1: resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz#594b8973938c5bc2c33046535785341abc4f3636" integrity sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og== -data-urls@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" - integrity sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ== - dependencies: - abab "^2.0.3" - whatwg-mimetype "^2.3.0" - whatwg-url "^8.0.0" - data-urls@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-3.0.2.tgz#9cf24a477ae22bcef5cd5f6f0bfbc1d2d3be9143" @@ -11251,7 +10986,7 @@ decimal.js-light@^2.4.1: resolved "https://registry.yarnpkg.com/decimal.js-light/-/decimal.js-light-2.5.1.tgz#134fd32508f19e208f4fb2f8dac0d2626a867934" integrity sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg== -decimal.js@^10.2.1, decimal.js@^10.4.2, decimal.js@^10.4.3: +decimal.js@^10.4.2, decimal.js@^10.4.3: version "10.4.3" resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== @@ -11275,11 +11010,6 @@ decompress-response@^7.0.0: dependencies: mimic-response "^3.1.0" -dedent@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" - integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== - dedent@^1.0.0: version "1.5.1" resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.1.tgz#4f3fc94c8b711e9bb2800d185cd6ad20f2a90aff" @@ -11564,11 +11294,6 @@ dezalgo@^1.0.4: asap "^2.0.0" wrappy "1" -diff-sequences@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327" - integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ== - diff-sequences@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" @@ -11752,13 +11477,6 @@ domelementtype@^2.0.1, domelementtype@^2.2.0, domelementtype@^2.3.0: resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== -domexception@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" - integrity sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg== - dependencies: - webidl-conversions "^5.0.0" - domexception@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/domexception/-/domexception-4.0.0.tgz#4ad1be56ccadc86fc76d033353999a8037d03673" @@ -11937,11 +11655,6 @@ emittery@^0.13.1: resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== -emittery@^0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860" - integrity sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg== - emoji-regex@^10.3.0: version "10.3.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.3.0.tgz#76998b9268409eb3dae3de989254d456e70cfe23" @@ -13113,16 +12826,6 @@ expand-tilde@^2.0.0, expand-tilde@^2.0.2: dependencies: homedir-polyfill "^1.0.1" -expect@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/expect/-/expect-27.5.1.tgz#83ce59f1e5bdf5f9d2b94b61d2050db48f3fef74" - integrity sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw== - dependencies: - "@jest/types" "^27.5.1" - jest-get-type "^27.5.1" - jest-matcher-utils "^27.5.1" - jest-message-util "^27.5.1" - expect@^29.0.0, expect@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" @@ -13600,15 +13303,6 @@ form-data@^2.2.0: combined-stream "^1.0.6" mime-types "^2.1.12" -form-data@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" - integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - form-data@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" @@ -14057,7 +13751,7 @@ glob@7.2.0: once "^1.3.0" path-is-absolute "^1.0.0" -glob@7.2.3, glob@^7.0.0, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: +glob@7.2.3, glob@^7.0.0, glob@^7.0.5, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -14751,13 +14445,6 @@ hosted-git-info@^4.0.1: dependencies: lru-cache "^6.0.0" -html-encoding-sniffer@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" - integrity sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ== - dependencies: - whatwg-encoding "^1.0.5" - html-encoding-sniffer@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz#2cb1a8cf0db52414776e5b2a7a04d5dd98158de9" @@ -14874,15 +14561,6 @@ http-errors@~1.6.2: setprototypeof "1.1.0" statuses ">= 1.4.0 < 2" -http-proxy-agent@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" - integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== - dependencies: - "@tootallnate/once" "1" - agent-base "6" - debug "4" - http-proxy-agent@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" @@ -16053,7 +15731,7 @@ istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== -istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: +istanbul-lib-instrument@^5.0.4: version "5.2.1" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d" integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== @@ -16249,15 +15927,6 @@ jayson@^4.1.0: uuid "^8.3.2" ws "^7.4.5" -jest-changed-files@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.5.1.tgz#a348aed00ec9bf671cc58a66fcbe7c3dfd6a68f5" - integrity sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw== - dependencies: - "@jest/types" "^27.5.1" - execa "^5.0.0" - throat "^6.0.1" - jest-changed-files@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a" @@ -16267,31 +15936,6 @@ jest-changed-files@^29.7.0: jest-util "^29.7.0" p-limit "^3.1.0" -jest-circus@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.5.1.tgz#37a5a4459b7bf4406e53d637b49d22c65d125ecc" - integrity sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw== - dependencies: - "@jest/environment" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/node" "*" - chalk "^4.0.0" - co "^4.6.0" - dedent "^0.7.0" - expect "^27.5.1" - is-generator-fn "^2.0.0" - jest-each "^27.5.1" - jest-matcher-utils "^27.5.1" - jest-message-util "^27.5.1" - jest-runtime "^27.5.1" - jest-snapshot "^27.5.1" - jest-util "^27.5.1" - pretty-format "^27.5.1" - slash "^3.0.0" - stack-utils "^2.0.3" - throat "^6.0.1" - jest-circus@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.7.0.tgz#b6817a45fcc835d8b16d5962d0c026473ee3668a" @@ -16318,25 +15962,7 @@ jest-circus@^29.7.0: slash "^3.0.0" stack-utils "^2.0.3" -jest-cli@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.5.1.tgz#278794a6e6458ea8029547e6c6cbf673bd30b145" - integrity sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw== - dependencies: - "@jest/core" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/types" "^27.5.1" - chalk "^4.0.0" - exit "^0.1.2" - graceful-fs "^4.2.9" - import-local "^3.0.2" - jest-config "^27.5.1" - jest-util "^27.5.1" - jest-validate "^27.5.1" - prompts "^2.0.1" - yargs "^16.2.0" - -jest-cli@^29.5.0, jest-cli@^29.7.0: +jest-cli@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.7.0.tgz#5592c940798e0cae677eec169264f2d839a37995" integrity sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg== @@ -16353,36 +15979,6 @@ jest-cli@^29.5.0, jest-cli@^29.7.0: jest-validate "^29.7.0" yargs "^17.3.1" -jest-config@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.5.1.tgz#5c387de33dca3f99ad6357ddeccd91bf3a0e4a41" - integrity sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA== - dependencies: - "@babel/core" "^7.8.0" - "@jest/test-sequencer" "^27.5.1" - "@jest/types" "^27.5.1" - babel-jest "^27.5.1" - chalk "^4.0.0" - ci-info "^3.2.0" - deepmerge "^4.2.2" - glob "^7.1.1" - graceful-fs "^4.2.9" - jest-circus "^27.5.1" - jest-environment-jsdom "^27.5.1" - jest-environment-node "^27.5.1" - jest-get-type "^27.5.1" - jest-jasmine2 "^27.5.1" - jest-regex-util "^27.5.1" - jest-resolve "^27.5.1" - jest-runner "^27.5.1" - jest-util "^27.5.1" - jest-validate "^27.5.1" - micromatch "^4.0.4" - parse-json "^5.2.0" - pretty-format "^27.5.1" - slash "^3.0.0" - strip-json-comments "^3.1.1" - jest-config@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.7.0.tgz#bcbda8806dbcc01b1e316a46bb74085a84b0245f" @@ -16411,16 +16007,6 @@ jest-config@^29.7.0: slash "^3.0.0" strip-json-comments "^3.1.1" -jest-diff@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.5.1.tgz#a07f5011ac9e6643cf8a95a462b7b1ecf6680def" - integrity sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw== - dependencies: - chalk "^4.0.0" - diff-sequences "^27.5.1" - jest-get-type "^27.5.1" - pretty-format "^27.5.1" - jest-diff@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" @@ -16431,13 +16017,6 @@ jest-diff@^29.7.0: jest-get-type "^29.6.3" pretty-format "^29.7.0" -jest-docblock@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.5.1.tgz#14092f364a42c6108d42c33c8cf30e058e25f6c0" - integrity sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ== - dependencies: - detect-newline "^3.0.0" - jest-docblock@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.7.0.tgz#8fddb6adc3cdc955c93e2a87f61cfd350d5d119a" @@ -16445,17 +16024,6 @@ jest-docblock@^29.7.0: dependencies: detect-newline "^3.0.0" -jest-each@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.5.1.tgz#5bc87016f45ed9507fed6e4702a5b468a5b2c44e" - integrity sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ== - dependencies: - "@jest/types" "^27.5.1" - chalk "^4.0.0" - jest-get-type "^27.5.1" - jest-util "^27.5.1" - pretty-format "^27.5.1" - jest-each@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.7.0.tgz#162a9b3f2328bdd991beaabffbb74745e56577d1" @@ -16467,19 +16035,6 @@ jest-each@^29.7.0: jest-util "^29.7.0" pretty-format "^29.7.0" -jest-environment-jsdom@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz#ea9ccd1fc610209655a77898f86b2b559516a546" - integrity sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw== - dependencies: - "@jest/environment" "^27.5.1" - "@jest/fake-timers" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/node" "*" - jest-mock "^27.5.1" - jest-util "^27.5.1" - jsdom "^16.6.0" - jest-environment-jsdom@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz#d206fa3551933c3fd519e5dfdb58a0f5139a837f" @@ -16494,18 +16049,6 @@ jest-environment-jsdom@^29.7.0: jest-util "^29.7.0" jsdom "^20.0.0" -jest-environment-node@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.5.1.tgz#dedc2cfe52fab6b8f5714b4808aefa85357a365e" - integrity sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw== - dependencies: - "@jest/environment" "^27.5.1" - "@jest/fake-timers" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/node" "*" - jest-mock "^27.5.1" - jest-util "^27.5.1" - jest-environment-node@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376" @@ -16518,36 +16061,11 @@ jest-environment-node@^29.7.0: jest-mock "^29.7.0" jest-util "^29.7.0" -jest-get-type@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1" - integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw== - jest-get-type@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== -jest-haste-map@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.5.1.tgz#9fd8bd7e7b4fa502d9c6164c5640512b4e811e7f" - integrity sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng== - dependencies: - "@jest/types" "^27.5.1" - "@types/graceful-fs" "^4.1.2" - "@types/node" "*" - anymatch "^3.0.3" - fb-watchman "^2.0.0" - graceful-fs "^4.2.9" - jest-regex-util "^27.5.1" - jest-serializer "^27.5.1" - jest-util "^27.5.1" - jest-worker "^27.5.1" - micromatch "^4.0.4" - walker "^1.0.7" - optionalDependencies: - fsevents "^2.3.2" - jest-haste-map@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.7.0.tgz#3c2396524482f5a0506376e6c858c3bbcc17b104" @@ -16567,37 +16085,6 @@ jest-haste-map@^29.7.0: optionalDependencies: fsevents "^2.3.2" -jest-jasmine2@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz#a037b0034ef49a9f3d71c4375a796f3b230d1ac4" - integrity sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ== - dependencies: - "@jest/environment" "^27.5.1" - "@jest/source-map" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/node" "*" - chalk "^4.0.0" - co "^4.6.0" - expect "^27.5.1" - is-generator-fn "^2.0.0" - jest-each "^27.5.1" - jest-matcher-utils "^27.5.1" - jest-message-util "^27.5.1" - jest-runtime "^27.5.1" - jest-snapshot "^27.5.1" - jest-util "^27.5.1" - pretty-format "^27.5.1" - throat "^6.0.1" - -jest-leak-detector@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz#6ec9d54c3579dd6e3e66d70e3498adf80fde3fb8" - integrity sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ== - dependencies: - jest-get-type "^27.5.1" - pretty-format "^27.5.1" - jest-leak-detector@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz#5b7ec0dadfdfec0ca383dc9aa016d36b5ea4c728" @@ -16606,16 +16093,6 @@ jest-leak-detector@^29.7.0: jest-get-type "^29.6.3" pretty-format "^29.7.0" -jest-matcher-utils@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab" - integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw== - dependencies: - chalk "^4.0.0" - jest-diff "^27.5.1" - jest-get-type "^27.5.1" - pretty-format "^27.5.1" - jest-matcher-utils@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" @@ -16626,21 +16103,6 @@ jest-matcher-utils@^29.7.0: jest-get-type "^29.6.3" pretty-format "^29.7.0" -jest-message-util@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.5.1.tgz#bdda72806da10d9ed6425e12afff38cd1458b6cf" - integrity sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g== - dependencies: - "@babel/code-frame" "^7.12.13" - "@jest/types" "^27.5.1" - "@types/stack-utils" "^2.0.0" - chalk "^4.0.0" - graceful-fs "^4.2.9" - micromatch "^4.0.4" - pretty-format "^27.5.1" - slash "^3.0.0" - stack-utils "^2.0.3" - jest-message-util@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" @@ -16656,14 +16118,6 @@ jest-message-util@^29.7.0: slash "^3.0.0" stack-utils "^2.0.3" -jest-mock@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.5.1.tgz#19948336d49ef4d9c52021d34ac7b5f36ff967d6" - integrity sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og== - dependencies: - "@jest/types" "^27.5.1" - "@types/node" "*" - jest-mock@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347" @@ -16678,25 +16132,11 @@ jest-pnp-resolver@^1.2.2: resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e" integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== -jest-regex-util@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.5.1.tgz#4da143f7e9fd1e542d4aa69617b38e4a78365b95" - integrity sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg== - jest-regex-util@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52" integrity sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg== -jest-resolve-dependencies@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz#d811ecc8305e731cc86dd79741ee98fed06f1da8" - integrity sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg== - dependencies: - "@jest/types" "^27.5.1" - jest-regex-util "^27.5.1" - jest-snapshot "^27.5.1" - jest-resolve-dependencies@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz#1b04f2c095f37fc776ff40803dc92921b1e88428" @@ -16705,22 +16145,6 @@ jest-resolve-dependencies@^29.7.0: jest-regex-util "^29.6.3" jest-snapshot "^29.7.0" -jest-resolve@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.5.1.tgz#a2f1c5a0796ec18fe9eb1536ac3814c23617b384" - integrity sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw== - dependencies: - "@jest/types" "^27.5.1" - chalk "^4.0.0" - graceful-fs "^4.2.9" - jest-haste-map "^27.5.1" - jest-pnp-resolver "^1.2.2" - jest-util "^27.5.1" - jest-validate "^27.5.1" - resolve "^1.20.0" - resolve.exports "^1.1.0" - slash "^3.0.0" - jest-resolve@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.7.0.tgz#64d6a8992dd26f635ab0c01e5eef4399c6bcbc30" @@ -16736,33 +16160,6 @@ jest-resolve@^29.7.0: resolve.exports "^2.0.0" slash "^3.0.0" -jest-runner@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.5.1.tgz#071b27c1fa30d90540805c5645a0ec167c7b62e5" - integrity sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ== - dependencies: - "@jest/console" "^27.5.1" - "@jest/environment" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/node" "*" - chalk "^4.0.0" - emittery "^0.8.1" - graceful-fs "^4.2.9" - jest-docblock "^27.5.1" - jest-environment-jsdom "^27.5.1" - jest-environment-node "^27.5.1" - jest-haste-map "^27.5.1" - jest-leak-detector "^27.5.1" - jest-message-util "^27.5.1" - jest-resolve "^27.5.1" - jest-runtime "^27.5.1" - jest-util "^27.5.1" - jest-worker "^27.5.1" - source-map-support "^0.5.6" - throat "^6.0.1" - jest-runner@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.7.0.tgz#809af072d408a53dcfd2e849a4c976d3132f718e" @@ -16790,34 +16187,6 @@ jest-runner@^29.7.0: p-limit "^3.1.0" source-map-support "0.5.13" -jest-runtime@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.5.1.tgz#4896003d7a334f7e8e4a53ba93fb9bcd3db0a1af" - integrity sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A== - dependencies: - "@jest/environment" "^27.5.1" - "@jest/fake-timers" "^27.5.1" - "@jest/globals" "^27.5.1" - "@jest/source-map" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" - chalk "^4.0.0" - cjs-module-lexer "^1.0.0" - collect-v8-coverage "^1.0.0" - execa "^5.0.0" - glob "^7.1.3" - graceful-fs "^4.2.9" - jest-haste-map "^27.5.1" - jest-message-util "^27.5.1" - jest-mock "^27.5.1" - jest-regex-util "^27.5.1" - jest-resolve "^27.5.1" - jest-snapshot "^27.5.1" - jest-util "^27.5.1" - slash "^3.0.0" - strip-bom "^4.0.0" - jest-runtime@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.7.0.tgz#efecb3141cf7d3767a3a0cc8f7c9990587d3d817" @@ -16846,42 +16215,6 @@ jest-runtime@^29.7.0: slash "^3.0.0" strip-bom "^4.0.0" -jest-serializer@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-27.5.1.tgz#81438410a30ea66fd57ff730835123dea1fb1f64" - integrity sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w== - dependencies: - "@types/node" "*" - graceful-fs "^4.2.9" - -jest-snapshot@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.5.1.tgz#b668d50d23d38054a51b42c4039cab59ae6eb6a1" - integrity sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA== - dependencies: - "@babel/core" "^7.7.2" - "@babel/generator" "^7.7.2" - "@babel/plugin-syntax-typescript" "^7.7.2" - "@babel/traverse" "^7.7.2" - "@babel/types" "^7.0.0" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/babel__traverse" "^7.0.4" - "@types/prettier" "^2.1.5" - babel-preset-current-node-syntax "^1.0.0" - chalk "^4.0.0" - expect "^27.5.1" - graceful-fs "^4.2.9" - jest-diff "^27.5.1" - jest-get-type "^27.5.1" - jest-haste-map "^27.5.1" - jest-matcher-utils "^27.5.1" - jest-message-util "^27.5.1" - jest-util "^27.5.1" - natural-compare "^1.4.0" - pretty-format "^27.5.1" - semver "^7.3.2" - jest-snapshot@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.7.0.tgz#c2c574c3f51865da1bb329036778a69bf88a6be5" @@ -16908,18 +16241,6 @@ jest-snapshot@^29.7.0: pretty-format "^29.7.0" semver "^7.5.3" -jest-util@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.5.1.tgz#3ba9771e8e31a0b85da48fe0b0891fb86c01c2f9" - integrity sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw== - dependencies: - "@jest/types" "^27.5.1" - "@types/node" "*" - chalk "^4.0.0" - ci-info "^3.2.0" - graceful-fs "^4.2.9" - picomatch "^2.2.3" - jest-util@^29.0.0, jest-util@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" @@ -16932,18 +16253,6 @@ jest-util@^29.0.0, jest-util@^29.7.0: graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-validate@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.5.1.tgz#9197d54dc0bdb52260b8db40b46ae668e04df067" - integrity sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ== - dependencies: - "@jest/types" "^27.5.1" - camelcase "^6.2.0" - chalk "^4.0.0" - jest-get-type "^27.5.1" - leven "^3.1.0" - pretty-format "^27.5.1" - jest-validate@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.7.0.tgz#7bf705511c64da591d46b15fce41400d52147d9c" @@ -16956,19 +16265,6 @@ jest-validate@^29.7.0: leven "^3.1.0" pretty-format "^29.7.0" -jest-watcher@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-27.5.1.tgz#71bd85fb9bde3a2c2ec4dc353437971c43c642a2" - integrity sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw== - dependencies: - "@jest/test-result" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/node" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" - jest-util "^27.5.1" - string-length "^4.0.1" - jest-watcher@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.7.0.tgz#7810d30d619c3a62093223ce6bb359ca1b28a2f2" @@ -16983,7 +16279,7 @@ jest-watcher@^29.7.0: jest-util "^29.7.0" string-length "^4.0.1" -jest-worker@^27.4.5, jest-worker@^27.5.1: +jest-worker@^27.4.5: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== @@ -17002,26 +16298,7 @@ jest-worker@^29.7.0: merge-stream "^2.0.0" supports-color "^8.0.0" -jest@29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest/-/jest-29.5.0.tgz#f75157622f5ce7ad53028f2f8888ab53e1f1f24e" - integrity sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ== - dependencies: - "@jest/core" "^29.5.0" - "@jest/types" "^29.5.0" - import-local "^3.0.2" - jest-cli "^29.5.0" - -jest@^27.1.0: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest/-/jest-27.5.1.tgz#dadf33ba70a779be7a6fc33015843b51494f63fc" - integrity sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ== - dependencies: - "@jest/core" "^27.5.1" - import-local "^3.0.2" - jest-cli "^27.5.1" - -jest@^29.3.1: +jest@29.7.0, jest@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613" integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw== @@ -17107,39 +16384,6 @@ jsbn@~0.1.0: resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg== -jsdom@^16.6.0: - version "16.7.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710" - integrity sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw== - dependencies: - abab "^2.0.5" - acorn "^8.2.4" - acorn-globals "^6.0.0" - cssom "^0.4.4" - cssstyle "^2.3.0" - data-urls "^2.0.0" - decimal.js "^10.2.1" - domexception "^2.0.1" - escodegen "^2.0.0" - form-data "^3.0.0" - html-encoding-sniffer "^2.0.1" - http-proxy-agent "^4.0.1" - https-proxy-agent "^5.0.0" - is-potential-custom-element-name "^1.0.1" - nwsapi "^2.2.0" - parse5 "6.0.1" - saxes "^5.0.1" - symbol-tree "^3.2.4" - tough-cookie "^4.0.0" - w3c-hr-time "^1.0.2" - w3c-xmlserializer "^2.0.0" - webidl-conversions "^6.1.0" - whatwg-encoding "^1.0.5" - whatwg-mimetype "^2.3.0" - whatwg-url "^8.5.0" - ws "^7.4.6" - xml-name-validator "^3.0.0" - jsdom@^20.0.0: version "20.0.3" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-20.0.3.tgz#886a41ba1d4726f67a8858028c99489fed6ad4db" @@ -18088,7 +17332,7 @@ lodash.upperfirst@^4.3.1: resolved "https://registry.yarnpkg.com/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz#1365edf431480481ef0d1c68957a5ed99d49f7ce" integrity sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg== -lodash@4.17.21, lodash@^4.16.3, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.7.0: +lodash@4.17.21, lodash@^4.16.3, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -19424,7 +18668,7 @@ numeral@^2.0.6: resolved "https://registry.yarnpkg.com/numeral/-/numeral-2.0.6.tgz#4ad080936d443c2561aed9f2197efffe25f4e506" integrity sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA== -nwsapi@^2.2.0, nwsapi@^2.2.2, nwsapi@^2.2.4: +nwsapi@^2.2.2, nwsapi@^2.2.4: version "2.2.7" resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.7.tgz#738e0707d3128cb750dddcfe90e4610482df0f30" integrity sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ== @@ -20025,16 +19269,16 @@ parse5-htmlparser2-tree-adapter@^7.0.0: domhandler "^5.0.2" parse5 "^7.0.0" -parse5@6.0.1, parse5@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" - integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== - parse5@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178" integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug== +parse5@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== + parse5@^7.0.0, parse5@^7.1.1, parse5@^7.1.2: version "7.1.2" resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32" @@ -21761,11 +21005,6 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg== -resolve.exports@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.1.tgz#05cfd5b3edf641571fd46fa608b610dda9ead999" - integrity sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ== - resolve.exports@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" @@ -22082,13 +21321,6 @@ sax@>=0.6.0: resolved "https://registry.yarnpkg.com/sax/-/sax-1.3.0.tgz#a5dbe77db3be05c9d1ee7785dbd3ea9de51593d0" integrity sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA== -saxes@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" - integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== - dependencies: - xmlchars "^2.2.0" - saxes@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5" @@ -22693,7 +21925,7 @@ source-map-support@0.5.13: buffer-from "^1.0.0" source-map "^0.6.0" -source-map-support@0.5.21, source-map-support@^0.5.13, source-map-support@^0.5.20, source-map-support@^0.5.6, source-map-support@~0.5.20: +source-map-support@0.5.21, source-map-support@^0.5.13, source-map-support@^0.5.20, source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== @@ -23266,7 +22498,7 @@ supports-color@^7.0.0, supports-color@^7.1.0, supports-color@^7.2.0: dependencies: has-flag "^4.0.0" -supports-hyperlinks@^2.0.0, supports-hyperlinks@^2.2.0: +supports-hyperlinks@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz#3943544347c1ff90b15effb03fc14ae45ec10624" integrity sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA== @@ -23485,14 +22717,6 @@ tenderly@^0.8.0: prompts "^2.4.2" tslog "^4.4.0" -terminal-link@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" - integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== - dependencies: - ansi-escapes "^4.2.1" - supports-hyperlinks "^2.0.0" - terser-webpack-plugin@^5.3.7: version "5.3.10" resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz#904f4c9193c6fd2a03f693a2150c62a92f40d199" @@ -23576,11 +22800,6 @@ thread-stream@^0.15.1: dependencies: real-require "^0.1.0" -throat@^6.0.1: - version "6.0.2" - resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.2.tgz#51a3fbb5e11ae72e2cf74861ed5c8020f89f29fe" - integrity sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ== - throttled-queue@^2.1.2: version "2.1.4" resolved "https://registry.yarnpkg.com/throttled-queue/-/throttled-queue-2.1.4.tgz#4e2008c73ab3f72ba1bb09496c3cc9c5b745dbee" @@ -23776,7 +22995,7 @@ totalist@^3.0.0: resolved "https://registry.yarnpkg.com/totalist/-/totalist-3.0.1.tgz#ba3a3d600c915b1a97872348f79c127475f6acf8" integrity sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ== -tough-cookie@^4.0.0, tough-cookie@^4.1.2: +tough-cookie@^4.1.2: version "4.1.3" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.3.tgz#97b9adb0728b42280aa3d814b6b999b2ff0318bf" integrity sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw== @@ -23794,13 +23013,6 @@ tough-cookie@~2.5.0: psl "^1.1.28" punycode "^2.1.1" -tr46@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240" - integrity sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw== - dependencies: - punycode "^2.1.1" - tr46@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9" @@ -24607,15 +23819,6 @@ v8-compile-cache-lib@^3.0.1: resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== -v8-to-istanbul@^8.1.0: - version "8.1.1" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz#77b752fd3975e31bbcef938f85e9bd1c7a8d60ed" - integrity sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w== - dependencies: - "@types/istanbul-lib-coverage" "^2.0.1" - convert-source-map "^1.6.0" - source-map "^0.7.3" - v8-to-istanbul@^9.0.1: version "9.2.0" resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz#2ed7644a245cddd83d4e087b9b33b3e62dfd10ad" @@ -24860,25 +24063,11 @@ vscode-textmate@^8.0.0: resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-8.0.0.tgz#2c7a3b1163ef0441097e0b5d6389cd5504b59e5d" integrity sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg== -w3c-hr-time@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" - integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== - dependencies: - browser-process-hrtime "^1.0.0" - w3c-keyname@^2.2.4: version "2.2.8" resolved "https://registry.yarnpkg.com/w3c-keyname/-/w3c-keyname-2.2.8.tgz#7b17c8c6883d4e8b86ac8aba79d39e880f8869c5" integrity sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ== -w3c-xmlserializer@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a" - integrity sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA== - dependencies: - xml-name-validator "^3.0.0" - w3c-xmlserializer@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz#aebdc84920d806222936e3cdce408e32488a3073" @@ -24903,7 +24092,7 @@ wagmi@^0.12.2: abitype "^0.3.0" use-sync-external-store "^1.2.0" -walker@^1.0.7, walker@^1.0.8: +walker@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== @@ -25212,16 +24401,6 @@ webidl-conversions@^3.0.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== -webidl-conversions@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" - integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== - -webidl-conversions@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" - integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== - webidl-conversions@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" @@ -25349,13 +24528,6 @@ well-known-symbols@^2.0.0: resolved "https://registry.yarnpkg.com/well-known-symbols/-/well-known-symbols-2.0.0.tgz#e9c7c07dbd132b7b84212c8174391ec1f9871ba5" integrity sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q== -whatwg-encoding@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" - integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== - dependencies: - iconv-lite "0.4.24" - whatwg-encoding@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz#e7635f597fd87020858626805a2729fa7698ac53" @@ -25363,11 +24535,6 @@ whatwg-encoding@^2.0.0: dependencies: iconv-lite "0.6.3" -whatwg-mimetype@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" - integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== - whatwg-mimetype@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7" @@ -25397,15 +24564,6 @@ whatwg-url@^5.0.0: tr46 "~0.0.3" webidl-conversions "^3.0.0" -whatwg-url@^8.0.0, whatwg-url@^8.5.0: - version "8.7.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77" - integrity sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg== - dependencies: - lodash "^4.7.0" - tr46 "^2.1.0" - webidl-conversions "^6.1.0" - which-boxed-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" @@ -25671,11 +24829,6 @@ xdg-basedir@^4.0.0: resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q== -xml-name-validator@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" - integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== - xml-name-validator@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835" @@ -25780,7 +24933,7 @@ yargs-unparser@2.0.0: flat "^5.0.2" is-plain-obj "^2.1.0" -yargs@16.2.0, yargs@^16.0.0, yargs@^16.1.0, yargs@^16.2.0: +yargs@16.2.0, yargs@^16.0.0, yargs@^16.1.0: version "16.2.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== From 2662e1af9b9d6c469845e72aab850a697b8f62b6 Mon Sep 17 00:00:00 2001 From: Eric Lee <98655210+leric7@users.noreply.github.com> Date: Tue, 16 Jan 2024 22:16:52 +0800 Subject: [PATCH 031/104] remove rust contracts (#1463) --- .github/workflows/ci-mx-contracts.yaml | 50 - .github/workflows/ci-test-core.yaml | 1 - .github/workflows/ci-test-dashboard-ui.yaml | 1 - .github/workflows/ci-test-faucet-server.yaml | 1 - .github/workflows/ci-test-fortune-v3.yaml | 1 - .github/workflows/ci-test-job-launcher.yaml | 1 - .../workflows/ci-test-meta-code-verify.yaml | 1 - .github/workflows/ci-test-node-sdk.yaml | 1 - .github/workflows/ci-test-python-sdk.yaml | 1 - .../workflows/ci-test-reputation-oracle.yaml | 1 - .github/workflows/ci-test-subgraph.yaml | 1 - packages/core/mx/.gitignore | 8 - packages/core/mx/Cargo.lock | 2498 ----------------- packages/core/mx/Cargo.toml | 39 - packages/core/mx/README.md | 71 - .../mx/contracts/common-structs/Cargo.toml | 21 - .../mx/contracts/common-structs/src/escrow.rs | 11 - .../mx/contracts/common-structs/src/lib.rs | 4 - .../mx/contracts/common-structs/src/stakes.rs | 138 - .../mx/contracts/escrow-factory/Cargo.toml | 28 - .../interactions/testnet.snippets.sh | 48 - .../contracts/escrow-factory/meta/Cargo.toml | 14 - .../contracts/escrow-factory/meta/src/main.rs | 3 - .../contracts/escrow-factory/multiversx.json | 3 - .../mx/contracts/escrow-factory/src/lib.rs | 91 - .../mx/contracts/escrow-factory/src/proxy.rs | 30 - .../escrow-factory/tests/interactions.rs | 119 - .../tests/test_escrow_factory.rs | 31 - .../contracts/escrow-factory/wasm/Cargo.toml | 27 - .../contracts/escrow-factory/wasm/src/lib.rs | 32 - .../core/mx/contracts/escrow-mock/.gitignore | 7 - .../core/mx/contracts/escrow-mock/Cargo.toml | 23 - .../mx/contracts/escrow-mock/meta/Cargo.toml | 14 - .../mx/contracts/escrow-mock/meta/src/main.rs | 3 - .../mx/contracts/escrow-mock/multiversx.json | 3 - .../core/mx/contracts/escrow-mock/src/lib.rs | 25 - .../mx/contracts/escrow-mock/wasm/Cargo.toml | 27 - .../mx/contracts/escrow-mock/wasm/src/lib.rs | 26 - packages/core/mx/contracts/escrow/Cargo.toml | 24 - .../escrow/interactions/testnet.snippets.sh | 23 - .../core/mx/contracts/escrow/meta/Cargo.toml | 14 - .../core/mx/contracts/escrow/meta/src/main.rs | 3 - .../core/mx/contracts/escrow/multiversx.json | 3 - .../core/mx/contracts/escrow/src/constants.rs | 38 - packages/core/mx/contracts/escrow/src/lib.rs | 359 --- .../mx/contracts/escrow/tests/interactions.rs | 232 -- .../mx/contracts/escrow/tests/test_escrow.rs | 319 --- .../core/mx/contracts/escrow/wasm/Cargo.toml | 27 - .../core/mx/contracts/escrow/wasm/src/lib.rs | 37 - .../core/mx/contracts/kv-store/.gitignore | 7 - .../core/mx/contracts/kv-store/Cargo.toml | 20 - .../mx/contracts/kv-store/meta/Cargo.toml | 14 - .../mx/contracts/kv-store/meta/src/main.rs | 3 - .../mx/contracts/kv-store/multiversx.json | 3 - .../core/mx/contracts/kv-store/src/lib.rs | 34 - .../kv-store/tests/contract_interactions.rs | 66 - .../contracts/kv-store/tests/test_kv_store.rs | 22 - .../mx/contracts/kv-store/wasm/Cargo.toml | 27 - .../mx/contracts/kv-store/wasm/src/lib.rs | 26 - .../mx/contracts/rewards-pool-mock/.gitignore | 7 - .../mx/contracts/rewards-pool-mock/Cargo.toml | 20 - .../rewards-pool-mock/meta/Cargo.toml | 14 - .../rewards-pool-mock/meta/src/main.rs | 3 - .../rewards-pool-mock/multiversx.json | 3 - .../mx/contracts/rewards-pool-mock/src/lib.rs | 27 - .../rewards-pool-mock/wasm/Cargo.toml | 27 - .../rewards-pool-mock/wasm/src/lib.rs | 26 - .../core/mx/contracts/rewards-pool/.gitignore | 7 - .../core/mx/contracts/rewards-pool/Cargo.toml | 20 - .../mx/contracts/rewards-pool/meta/Cargo.toml | 14 - .../contracts/rewards-pool/meta/src/main.rs | 3 - .../mx/contracts/rewards-pool/multiversx.json | 3 - .../contracts/rewards-pool/src/constants.rs | 9 - .../core/mx/contracts/rewards-pool/src/lib.rs | 125 - .../tests/contract_interactions.rs | 147 - .../rewards-pool/tests/test_rewards_pool.rs | 93 - .../mx/contracts/rewards-pool/wasm/Cargo.toml | 27 - .../mx/contracts/rewards-pool/wasm/src/lib.rs | 33 - .../core/mx/contracts/staking-mock/.gitignore | 7 - .../core/mx/contracts/staking-mock/Cargo.toml | 23 - .../mx/contracts/staking-mock/meta/Cargo.toml | 14 - .../contracts/staking-mock/meta/src/main.rs | 3 - .../mx/contracts/staking-mock/multiversx.json | 3 - .../core/mx/contracts/staking-mock/src/lib.rs | 30 - .../mx/contracts/staking-mock/wasm/Cargo.toml | 27 - .../mx/contracts/staking-mock/wasm/src/lib.rs | 26 - packages/core/mx/contracts/staking/.gitignore | 7 - packages/core/mx/contracts/staking/Cargo.toml | 29 - .../core/mx/contracts/staking/meta/Cargo.toml | 14 - .../mx/contracts/staking/meta/src/main.rs | 3 - .../core/mx/contracts/staking/multiversx.json | 3 - .../mx/contracts/staking/src/escrow_proxy.rs | 33 - .../core/mx/contracts/staking/src/events.rs | 118 - packages/core/mx/contracts/staking/src/lib.rs | 355 --- .../staking/src/rewards_pool_proxy.rs | 40 - .../staking/tests/contract_interactions.rs | 269 -- .../contracts/staking/tests/test_staking.rs | 446 --- .../core/mx/contracts/staking/wasm/Cargo.toml | 27 - .../core/mx/contracts/staking/wasm/src/lib.rs | 42 - packages/core/mx/multiversx.workspace.json | 1 - .../core/mx/scripts/build-mx-contracts.sh | 20 - .../core/mx/scripts/clean-mx-contracts.sh | 21 - 102 files changed, 6874 deletions(-) delete mode 100644 .github/workflows/ci-mx-contracts.yaml delete mode 100644 packages/core/mx/.gitignore delete mode 100644 packages/core/mx/Cargo.lock delete mode 100644 packages/core/mx/Cargo.toml delete mode 100644 packages/core/mx/README.md delete mode 100644 packages/core/mx/contracts/common-structs/Cargo.toml delete mode 100644 packages/core/mx/contracts/common-structs/src/escrow.rs delete mode 100644 packages/core/mx/contracts/common-structs/src/lib.rs delete mode 100644 packages/core/mx/contracts/common-structs/src/stakes.rs delete mode 100644 packages/core/mx/contracts/escrow-factory/Cargo.toml delete mode 100644 packages/core/mx/contracts/escrow-factory/interactions/testnet.snippets.sh delete mode 100644 packages/core/mx/contracts/escrow-factory/meta/Cargo.toml delete mode 100644 packages/core/mx/contracts/escrow-factory/meta/src/main.rs delete mode 100644 packages/core/mx/contracts/escrow-factory/multiversx.json delete mode 100644 packages/core/mx/contracts/escrow-factory/src/lib.rs delete mode 100644 packages/core/mx/contracts/escrow-factory/src/proxy.rs delete mode 100644 packages/core/mx/contracts/escrow-factory/tests/interactions.rs delete mode 100644 packages/core/mx/contracts/escrow-factory/tests/test_escrow_factory.rs delete mode 100644 packages/core/mx/contracts/escrow-factory/wasm/Cargo.toml delete mode 100644 packages/core/mx/contracts/escrow-factory/wasm/src/lib.rs delete mode 100644 packages/core/mx/contracts/escrow-mock/.gitignore delete mode 100644 packages/core/mx/contracts/escrow-mock/Cargo.toml delete mode 100644 packages/core/mx/contracts/escrow-mock/meta/Cargo.toml delete mode 100644 packages/core/mx/contracts/escrow-mock/meta/src/main.rs delete mode 100644 packages/core/mx/contracts/escrow-mock/multiversx.json delete mode 100644 packages/core/mx/contracts/escrow-mock/src/lib.rs delete mode 100644 packages/core/mx/contracts/escrow-mock/wasm/Cargo.toml delete mode 100644 packages/core/mx/contracts/escrow-mock/wasm/src/lib.rs delete mode 100644 packages/core/mx/contracts/escrow/Cargo.toml delete mode 100644 packages/core/mx/contracts/escrow/interactions/testnet.snippets.sh delete mode 100644 packages/core/mx/contracts/escrow/meta/Cargo.toml delete mode 100644 packages/core/mx/contracts/escrow/meta/src/main.rs delete mode 100644 packages/core/mx/contracts/escrow/multiversx.json delete mode 100644 packages/core/mx/contracts/escrow/src/constants.rs delete mode 100644 packages/core/mx/contracts/escrow/src/lib.rs delete mode 100644 packages/core/mx/contracts/escrow/tests/interactions.rs delete mode 100644 packages/core/mx/contracts/escrow/tests/test_escrow.rs delete mode 100644 packages/core/mx/contracts/escrow/wasm/Cargo.toml delete mode 100644 packages/core/mx/contracts/escrow/wasm/src/lib.rs delete mode 100644 packages/core/mx/contracts/kv-store/.gitignore delete mode 100644 packages/core/mx/contracts/kv-store/Cargo.toml delete mode 100644 packages/core/mx/contracts/kv-store/meta/Cargo.toml delete mode 100644 packages/core/mx/contracts/kv-store/meta/src/main.rs delete mode 100644 packages/core/mx/contracts/kv-store/multiversx.json delete mode 100644 packages/core/mx/contracts/kv-store/src/lib.rs delete mode 100644 packages/core/mx/contracts/kv-store/tests/contract_interactions.rs delete mode 100644 packages/core/mx/contracts/kv-store/tests/test_kv_store.rs delete mode 100644 packages/core/mx/contracts/kv-store/wasm/Cargo.toml delete mode 100644 packages/core/mx/contracts/kv-store/wasm/src/lib.rs delete mode 100644 packages/core/mx/contracts/rewards-pool-mock/.gitignore delete mode 100644 packages/core/mx/contracts/rewards-pool-mock/Cargo.toml delete mode 100644 packages/core/mx/contracts/rewards-pool-mock/meta/Cargo.toml delete mode 100644 packages/core/mx/contracts/rewards-pool-mock/meta/src/main.rs delete mode 100644 packages/core/mx/contracts/rewards-pool-mock/multiversx.json delete mode 100644 packages/core/mx/contracts/rewards-pool-mock/src/lib.rs delete mode 100644 packages/core/mx/contracts/rewards-pool-mock/wasm/Cargo.toml delete mode 100644 packages/core/mx/contracts/rewards-pool-mock/wasm/src/lib.rs delete mode 100644 packages/core/mx/contracts/rewards-pool/.gitignore delete mode 100644 packages/core/mx/contracts/rewards-pool/Cargo.toml delete mode 100644 packages/core/mx/contracts/rewards-pool/meta/Cargo.toml delete mode 100644 packages/core/mx/contracts/rewards-pool/meta/src/main.rs delete mode 100644 packages/core/mx/contracts/rewards-pool/multiversx.json delete mode 100644 packages/core/mx/contracts/rewards-pool/src/constants.rs delete mode 100644 packages/core/mx/contracts/rewards-pool/src/lib.rs delete mode 100644 packages/core/mx/contracts/rewards-pool/tests/contract_interactions.rs delete mode 100644 packages/core/mx/contracts/rewards-pool/tests/test_rewards_pool.rs delete mode 100644 packages/core/mx/contracts/rewards-pool/wasm/Cargo.toml delete mode 100644 packages/core/mx/contracts/rewards-pool/wasm/src/lib.rs delete mode 100644 packages/core/mx/contracts/staking-mock/.gitignore delete mode 100644 packages/core/mx/contracts/staking-mock/Cargo.toml delete mode 100644 packages/core/mx/contracts/staking-mock/meta/Cargo.toml delete mode 100644 packages/core/mx/contracts/staking-mock/meta/src/main.rs delete mode 100644 packages/core/mx/contracts/staking-mock/multiversx.json delete mode 100644 packages/core/mx/contracts/staking-mock/src/lib.rs delete mode 100644 packages/core/mx/contracts/staking-mock/wasm/Cargo.toml delete mode 100644 packages/core/mx/contracts/staking-mock/wasm/src/lib.rs delete mode 100644 packages/core/mx/contracts/staking/.gitignore delete mode 100644 packages/core/mx/contracts/staking/Cargo.toml delete mode 100644 packages/core/mx/contracts/staking/meta/Cargo.toml delete mode 100644 packages/core/mx/contracts/staking/meta/src/main.rs delete mode 100644 packages/core/mx/contracts/staking/multiversx.json delete mode 100644 packages/core/mx/contracts/staking/src/escrow_proxy.rs delete mode 100644 packages/core/mx/contracts/staking/src/events.rs delete mode 100644 packages/core/mx/contracts/staking/src/lib.rs delete mode 100644 packages/core/mx/contracts/staking/src/rewards_pool_proxy.rs delete mode 100644 packages/core/mx/contracts/staking/tests/contract_interactions.rs delete mode 100644 packages/core/mx/contracts/staking/tests/test_staking.rs delete mode 100644 packages/core/mx/contracts/staking/wasm/Cargo.toml delete mode 100644 packages/core/mx/contracts/staking/wasm/src/lib.rs delete mode 100644 packages/core/mx/multiversx.workspace.json delete mode 100755 packages/core/mx/scripts/build-mx-contracts.sh delete mode 100755 packages/core/mx/scripts/clean-mx-contracts.sh diff --git a/.github/workflows/ci-mx-contracts.yaml b/.github/workflows/ci-mx-contracts.yaml deleted file mode 100644 index 807411df1a..0000000000 --- a/.github/workflows/ci-mx-contracts.yaml +++ /dev/null @@ -1,50 +0,0 @@ -name: CI MX Contracts - -on: - push: - branches: - - 'main' - pull_request: - paths: - - 'packages/core/mx' - -env: - RUST_TOOLCHAIN: nightly-2023-05-26 - VMTOOLS_VERSION: v1.4.60 - PIP_MXPY_ARGS: multiversx-sdk-cli==v8.1.5 - CLIPPY_ARGS: --all-targets --all-features - -defaults: - run: - working-directory: ./packages/core/mx/ - -jobs: - rust_test: - name: Rust tests - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions-rust-lang/setup-rust-toolchain@v1 - with: - toolchain: ${{ env.RUST_TOOLCHAIN }} - - - name: Run the rust tests - env: - RUSTFLAGS: '' - run: cargo test - - clippy_check: - name: Clippy linter check - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions-rust-lang/setup-rust-toolchain@v1 - with: - toolchain: ${{ env.RUST_TOOLCHAIN }} - components: clippy - - uses: giraffate/clippy-action@v1 - env: - RUSTFLAGS: '' - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - clippy_flags: ${{ env.CLIPPY_ARGS}} diff --git a/.github/workflows/ci-test-core.yaml b/.github/workflows/ci-test-core.yaml index a0bfcd8943..d4e44d99e1 100644 --- a/.github/workflows/ci-test-core.yaml +++ b/.github/workflows/ci-test-core.yaml @@ -7,7 +7,6 @@ on: pull_request: paths: - 'packages/core/**' - - '!packages/core/mx/**' workflow_dispatch: jobs: diff --git a/.github/workflows/ci-test-dashboard-ui.yaml b/.github/workflows/ci-test-dashboard-ui.yaml index ab8495c46e..7fa612d636 100644 --- a/.github/workflows/ci-test-dashboard-ui.yaml +++ b/.github/workflows/ci-test-dashboard-ui.yaml @@ -7,7 +7,6 @@ on: pull_request: paths: - 'packages/core/**' - - '!packages/core/mx/**' - 'packages/sdk/typescript/human-protocol-sdk/**' - 'packages/sdk/typescript/subgraph/**' - 'packages/apps/dashboard/ui/**' diff --git a/.github/workflows/ci-test-faucet-server.yaml b/.github/workflows/ci-test-faucet-server.yaml index 74c371a660..39bf3b1698 100644 --- a/.github/workflows/ci-test-faucet-server.yaml +++ b/.github/workflows/ci-test-faucet-server.yaml @@ -7,7 +7,6 @@ on: pull_request: paths: - 'packages/core/**' - - '!packages/core/mx/**' - 'packages/sdk/typescript/human-protocol-sdk/**' - 'packages/apps/faucet-server/**' workflow_dispatch: diff --git a/.github/workflows/ci-test-fortune-v3.yaml b/.github/workflows/ci-test-fortune-v3.yaml index ae5eed8425..f281777048 100644 --- a/.github/workflows/ci-test-fortune-v3.yaml +++ b/.github/workflows/ci-test-fortune-v3.yaml @@ -7,7 +7,6 @@ on: pull_request: paths: - 'packages/core/**' - - '!packages/core/mx/**' - 'packages/sdk/typescript/human-protocol-sdk/**' - 'packages/app/fortune/**' workflow_dispatch: diff --git a/.github/workflows/ci-test-job-launcher.yaml b/.github/workflows/ci-test-job-launcher.yaml index 5ef4b78eef..0910c1aaad 100644 --- a/.github/workflows/ci-test-job-launcher.yaml +++ b/.github/workflows/ci-test-job-launcher.yaml @@ -7,7 +7,6 @@ on: pull_request: paths: - "packages/core/**" - - "!packages/core/mx/**" - "packages/sdk/typescript/human-protocol-sdk/**" - "packages/apps/job-launcher/**" workflow_dispatch: diff --git a/.github/workflows/ci-test-meta-code-verify.yaml b/.github/workflows/ci-test-meta-code-verify.yaml index 60d0f4d04f..aaff64c591 100644 --- a/.github/workflows/ci-test-meta-code-verify.yaml +++ b/.github/workflows/ci-test-meta-code-verify.yaml @@ -7,7 +7,6 @@ on: pull_request: paths: - 'packages/core/**' - - '!packages/core/mx/**' - 'packages/sdk/typescript/human-protocol-sdk/**' - 'packages/apps/meta-code-verify/**' workflow_dispatch: diff --git a/.github/workflows/ci-test-node-sdk.yaml b/.github/workflows/ci-test-node-sdk.yaml index 40dc338a78..ef8412023d 100644 --- a/.github/workflows/ci-test-node-sdk.yaml +++ b/.github/workflows/ci-test-node-sdk.yaml @@ -7,7 +7,6 @@ on: pull_request: paths: - 'packages/core/**' - - '!packages/core/mx/**' - 'packages/sdk/typescript/human-protocol-sdk/**' workflow_dispatch: diff --git a/.github/workflows/ci-test-python-sdk.yaml b/.github/workflows/ci-test-python-sdk.yaml index 4b54a27262..1094c03b05 100644 --- a/.github/workflows/ci-test-python-sdk.yaml +++ b/.github/workflows/ci-test-python-sdk.yaml @@ -7,7 +7,6 @@ on: pull_request: paths: - 'packages/core/**' - - '!packages/core/mx/**' - 'packages/sdk/python/human-protocol-sdk/**' workflow_dispatch: diff --git a/.github/workflows/ci-test-reputation-oracle.yaml b/.github/workflows/ci-test-reputation-oracle.yaml index f13334eca8..7fd5466824 100644 --- a/.github/workflows/ci-test-reputation-oracle.yaml +++ b/.github/workflows/ci-test-reputation-oracle.yaml @@ -7,7 +7,6 @@ on: pull_request: paths: - "packages/core/**" - - "!packages/core/mx/**" - "packages/sdk/typescript/human-protocol-sdk/**" - "packages/apps/reputation-oracle/**" workflow_dispatch: diff --git a/.github/workflows/ci-test-subgraph.yaml b/.github/workflows/ci-test-subgraph.yaml index d8d3c7a5ec..b7285f2944 100644 --- a/.github/workflows/ci-test-subgraph.yaml +++ b/.github/workflows/ci-test-subgraph.yaml @@ -7,7 +7,6 @@ on: pull_request: paths: - 'packages/core/**' - - '!packages/core/mx/**' - 'packages/sdk/typescript/subgraph/**' workflow_dispatch: diff --git a/packages/core/mx/.gitignore b/packages/core/mx/.gitignore deleted file mode 100644 index 8d04b63b06..0000000000 --- a/packages/core/mx/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Contract builds -output/ - -# Rust targets -target/ - -# Interactions Results -*.interaction.json \ No newline at end of file diff --git a/packages/core/mx/Cargo.lock b/packages/core/mx/Cargo.lock deleted file mode 100644 index a7151950c5..0000000000 --- a/packages/core/mx/Cargo.lock +++ /dev/null @@ -1,2498 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "Inflector" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" -dependencies = [ - "lazy_static", - "regex", -] - -[[package]] -name = "addr2line" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "ahash" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy", -] - -[[package]] -name = "aho-corasick" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" -dependencies = [ - "memchr", -] - -[[package]] -name = "allocator-api2" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" - -[[package]] -name = "anstream" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" - -[[package]] -name = "anstyle-parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" -dependencies = [ - "windows-sys", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" -dependencies = [ - "anstyle", - "windows-sys", -] - -[[package]] -name = "anyhow" -version = "1.0.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" - -[[package]] -name = "arrayvec" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "backtrace" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "base64" -version = "0.21.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" - -[[package]] -name = "base64ct" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" - -[[package]] -name = "bech32" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" - -[[package]] -name = "bip39" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f2635620bf0b9d4576eb7bb9a38a55df78bd1205d26fa994b25911a69f212f" -dependencies = [ - "bitcoin_hashes", - "rand", - "rand_core", - "serde", - "unicode-normalization", -] - -[[package]] -name = "bitcoin_hashes" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90064b8dee6815a6470d60bad07bbbaee885c0e12d04177138fa3291a01b7bc4" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" - -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "block-padding", - "generic-array", -] - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "block-padding" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" - -[[package]] -name = "bstr" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c79ad7fb2dd38f3dabd76b09c6a5a20c038fc0213ef1e9afd30eb777f120f019" -dependencies = [ - "memchr", - "serde", -] - -[[package]] -name = "bumpalo" -version = "3.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "bytes" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" - -[[package]] -name = "cc" -version = "1.0.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "libc", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "clap" -version = "4.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap_builder" -version = "4.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_derive" -version = "4.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.39", -] - -[[package]] -name = "clap_lex" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" - -[[package]] -name = "colorchoice" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" - -[[package]] -name = "colored" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6" -dependencies = [ - "is-terminal", - "lazy_static", - "windows-sys", -] - -[[package]] -name = "common-path" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2382f75942f4b3be3690fe4f86365e9c853c1587d6ee58212cebf6e2a9ccd101" - -[[package]] -name = "common-structs" -version = "0.1.0" -dependencies = [ - "multiversx-sc", - "multiversx-sc-scenario", - "num-bigint", -] - -[[package]] -name = "const-oid" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" - -[[package]] -name = "convert_case" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "copy_dir" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "543d1dd138ef086e2ff05e3a48cf9da045da2033d16f8538fd76b86cd49b2ca3" -dependencies = [ - "walkdir", -] - -[[package]] -name = "core-foundation" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" - -[[package]] -name = "cpufeatures" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" -dependencies = [ - "libc", -] - -[[package]] -name = "crc32fast" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "crypto-mac" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" -dependencies = [ - "generic-array", - "subtle", -] - -[[package]] -name = "curve25519-dalek" -version = "4.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c" -dependencies = [ - "cfg-if", - "cpufeatures", - "curve25519-dalek-derive", - "digest 0.10.7", - "fiat-crypto", - "platforms", - "rustc_version", - "subtle", - "zeroize", -] - -[[package]] -name = "curve25519-dalek-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.39", -] - -[[package]] -name = "der" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" -dependencies = [ - "const-oid", - "zeroize", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer 0.10.4", - "crypto-common", -] - -[[package]] -name = "ed25519" -version = "1.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" -dependencies = [ - "signature 1.6.4", -] - -[[package]] -name = "ed25519" -version = "2.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" -dependencies = [ - "pkcs8", - "signature 2.1.0", -] - -[[package]] -name = "ed25519-dalek" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7277392b266383ef8396db7fdeb1e77b6c52fed775f5df15bb24f35b72156980" -dependencies = [ - "curve25519-dalek", - "ed25519 2.2.3", - "serde", - "sha2 0.10.8", - "zeroize", -] - -[[package]] -name = "either" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" - -[[package]] -name = "encoding_rs" -version = "0.8.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "endian-type" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" - -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - -[[package]] -name = "errno" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c18ee0ed65a5f1f81cac6b1d213b69c35fa47d4252ad41f1486dbd8226fe36e" -dependencies = [ - "libc", - "windows-sys", -] - -[[package]] -name = "escrow" -version = "0.1.0" -dependencies = [ - "common-structs", - "multiversx-sc", - "multiversx-sc-scenario", - "num-bigint", -] - -[[package]] -name = "escrow-factory" -version = "0.1.0" -dependencies = [ - "escrow", - "multiversx-sc", - "multiversx-sc-scenario", - "num-bigint", - "staking-mock", -] - -[[package]] -name = "escrow-factory-meta" -version = "0.0.0" -dependencies = [ - "escrow-factory", - "multiversx-sc-meta", -] - -[[package]] -name = "escrow-meta" -version = "0.0.0" -dependencies = [ - "escrow", - "multiversx-sc-meta", -] - -[[package]] -name = "escrow-mock" -version = "0.1.0" -dependencies = [ - "common-structs", - "multiversx-sc", - "multiversx-sc-scenario", - "num-bigint", -] - -[[package]] -name = "escrow-mock-meta" -version = "0.0.0" -dependencies = [ - "escrow-mock", - "multiversx-sc-meta", -] - -[[package]] -name = "fastrand" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" - -[[package]] -name = "fiat-crypto" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a481586acf778f1b1455424c343f71124b048ffa5f4fc3f8f6ae9dc432dcb3c7" - -[[package]] -name = "flate2" -version = "1.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "form_urlencoded" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "futures-channel" -version = "0.3.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" -dependencies = [ - "futures-core", -] - -[[package]] -name = "futures-core" -version = "0.3.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" - -[[package]] -name = "futures-io" -version = "0.3.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" - -[[package]] -name = "futures-sink" -version = "0.3.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" - -[[package]] -name = "futures-task" -version = "0.3.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" - -[[package]] -name = "futures-util" -version = "0.3.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" -dependencies = [ - "futures-core", - "futures-io", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "gimli" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" - -[[package]] -name = "globset" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759c97c1e17c55525b57192c06a267cda0ac5210b222d6b82189a2338fa1c13d" -dependencies = [ - "aho-corasick", - "bstr", - "fnv", - "log", - "regex", -] - -[[package]] -name = "h2" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap 1.9.3", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hashbrown" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" -dependencies = [ - "ahash", - "allocator-api2", -] - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "hermit-abi" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hex-literal" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" - -[[package]] -name = "hmac" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" -dependencies = [ - "crypto-mac", - "digest 0.9.0", -] - -[[package]] -name = "http" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" -dependencies = [ - "bytes", - "http", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" - -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - -[[package]] -name = "hyper" -version = "0.14.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2 0.4.10", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper", - "native-tls", - "tokio", - "tokio-native-tls", -] - -[[package]] -name = "idna" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "ignore" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbe7873dab538a9a44ad79ede1faf5f30d49f9a5c883ddbab48bce81b64b7492" -dependencies = [ - "globset", - "lazy_static", - "log", - "memchr", - "regex", - "same-file", - "thread_local", - "walkdir", - "winapi-util", -] - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", -] - -[[package]] -name = "indexmap" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" -dependencies = [ - "equivalent", - "hashbrown 0.14.2", -] - -[[package]] -name = "ipnet" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" - -[[package]] -name = "is-terminal" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" -dependencies = [ - "hermit-abi", - "rustix", - "windows-sys", -] - -[[package]] -name = "itertools" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" - -[[package]] -name = "js-sys" -version = "0.3.65" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "keccak" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" -dependencies = [ - "cpufeatures", -] - -[[package]] -name = "kv-store" -version = "0.1.0" -dependencies = [ - "multiversx-sc", - "multiversx-sc-scenario", - "num-bigint", -] - -[[package]] -name = "kv-store-meta" -version = "0.0.0" -dependencies = [ - "kv-store", - "multiversx-sc-meta", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.150" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" - -[[package]] -name = "linux-raw-sys" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" - -[[package]] -name = "lock_api" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" - -[[package]] -name = "memchr" -version = "2.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "miniz_oxide" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" -dependencies = [ - "adler", -] - -[[package]] -name = "mio" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" -dependencies = [ - "libc", - "wasi", - "windows-sys", -] - -[[package]] -name = "multiversx-chain-scenario-format" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c57b56954b133e76bfe77e1f20a903983f25fd24d53b28fe9cc9c9e54f374d2f" -dependencies = [ - "bech32", - "hex", - "num-bigint", - "num-traits", - "serde", - "serde_json", - "sha3 0.9.1", -] - -[[package]] -name = "multiversx-chain-vm" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d3c6d36157be3a413daef246de14c8c9465bea53b985941593aa741423722ce" -dependencies = [ - "bitflags 1.3.2", - "ed25519-dalek", - "hex", - "hex-literal", - "itertools", - "multiversx-chain-vm-executor", - "num-bigint", - "num-traits", - "rand", - "rand_seeder", - "sha2 0.10.8", - "sha3 0.10.8", -] - -[[package]] -name = "multiversx-chain-vm-executor" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b59072fa0624b55ae5ae3fa6bfa91515bbeb4ac440214bc4a509e2c8806d6e9f" - -[[package]] -name = "multiversx-sc" -version = "0.45.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f11b51d6a2ab0218585ed58e100c5ac2218de09490fbfc4243b5ecd0ca7f5cbc" -dependencies = [ - "bitflags 1.3.2", - "hashbrown 0.14.2", - "hex-literal", - "multiversx-sc-codec", - "multiversx-sc-derive", - "num-traits", -] - -[[package]] -name = "multiversx-sc-codec" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327b0ad1c3477cab0d7c84391439ba302238f738ef3c6e1fcd18e247fba84875" -dependencies = [ - "arrayvec", - "multiversx-sc-codec-derive", - "num-bigint", -] - -[[package]] -name = "multiversx-sc-codec-derive" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f444038e0043b8eda816b26952479c2aca3c4a643580f4337f71fb362a586db5" -dependencies = [ - "hex", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "multiversx-sc-derive" -version = "0.45.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784f0fc505806102fe6f808b368e12fcb13096dd73c5678f09c5663230724d6f" -dependencies = [ - "hex", - "proc-macro2", - "quote", - "radix_trie", - "syn 1.0.109", -] - -[[package]] -name = "multiversx-sc-meta" -version = "0.45.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b3fad2491d11c6edf3fe131696be31743dc568457c013b2c256e56376da294b" -dependencies = [ - "clap", - "colored", - "common-path", - "convert_case", - "copy_dir", - "hex", - "lazy_static", - "multiversx-sc", - "pathdiff", - "reqwest", - "ruplacer", - "rustc_version", - "serde", - "serde_json", - "toml", - "wasmparser", - "wasmprinter", - "zip", -] - -[[package]] -name = "multiversx-sc-scenario" -version = "0.45.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "285a6021a379c069cce06f5b6ba6b562ea63fb1c1fd252b031993e5f32e9e440" -dependencies = [ - "base64 0.13.1", - "bech32", - "clap", - "colored", - "hex", - "itertools", - "log", - "multiversx-chain-scenario-format", - "multiversx-chain-vm", - "multiversx-chain-vm-executor", - "multiversx-sc", - "multiversx-sc-meta", - "multiversx-sdk", - "num-bigint", - "num-traits", - "pathdiff", - "serde", - "serde_json", - "sha2 0.10.8", - "tokio", -] - -[[package]] -name = "multiversx-sdk" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baedd8e9f73ddee5a21964cde188397363c50bd43f1c12ee3dde20d15307cff" -dependencies = [ - "anyhow", - "base64 0.13.1", - "bech32", - "bip39", - "ed25519 1.5.3", - "hex", - "hmac", - "itertools", - "pbkdf2", - "pem", - "rand", - "reqwest", - "serde", - "serde_json", - "serde_repr", - "sha2 0.9.9", - "sha3 0.9.1", - "tokio", - "zeroize", -] - -[[package]] -name = "native-tls" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" -dependencies = [ - "lazy_static", - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - -[[package]] -name = "nibble_vec" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" -dependencies = [ - "smallvec", -] - -[[package]] -name = "num-bigint" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" -dependencies = [ - "autocfg", -] - -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "object" -version = "0.32.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" - -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - -[[package]] -name = "openssl" -version = "0.10.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79a4c6c3a2b158f7f8f2a2fc5a969fa3a068df6fc9dbb4a43845436e3af7c800" -dependencies = [ - "bitflags 2.4.1", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.39", -] - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-sys" -version = "0.9.96" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3812c071ba60da8b5677cc12bcb1d42989a65553772897a7e0355545a819838f" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets", -] - -[[package]] -name = "pathdiff" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" - -[[package]] -name = "pbkdf2" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05894bce6a1ba4be299d0c5f29563e08af2bc18bb7d48313113bed71e904739" -dependencies = [ - "crypto-mac", -] - -[[package]] -name = "pem" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" -dependencies = [ - "base64 0.13.1", -] - -[[package]] -name = "percent-encoding" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" - -[[package]] -name = "pin-project-lite" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der", - "spki", -] - -[[package]] -name = "pkg-config" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" - -[[package]] -name = "platforms" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14e6ab3f592e6fb464fc9712d8d6e6912de6473954635fd76a589d832cffcbb0" - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "proc-macro2" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "radix_trie" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" -dependencies = [ - "endian-type", - "nibble_vec", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rand_seeder" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf2890aaef0aa82719a50e808de264f9484b74b442e1a3a0e5ee38243ac40bdb" -dependencies = [ - "rand_core", -] - -[[package]] -name = "redox_syscall" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "regex" -version = "1.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" - -[[package]] -name = "reqwest" -version = "0.11.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" -dependencies = [ - "base64 0.21.5", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "hyper", - "hyper-tls", - "ipnet", - "js-sys", - "log", - "mime", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite", - "serde", - "serde_json", - "serde_urlencoded", - "system-configuration", - "tokio", - "tokio-native-tls", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "winreg", -] - -[[package]] -name = "rewards-pool" -version = "0.1.0" -dependencies = [ - "multiversx-sc", - "multiversx-sc-scenario", - "num-bigint", -] - -[[package]] -name = "rewards-pool-meta" -version = "0.0.0" -dependencies = [ - "multiversx-sc-meta", - "rewards-pool", -] - -[[package]] -name = "rewards-pool-mock" -version = "0.1.0" -dependencies = [ - "multiversx-sc", - "multiversx-sc-scenario", - "num-bigint", -] - -[[package]] -name = "rewards-pool-mock-meta" -version = "0.0.0" -dependencies = [ - "multiversx-sc-meta", - "rewards-pool-mock", -] - -[[package]] -name = "ruplacer" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58a26a1b15ff113d31d139357f7422708312978ed69cd5dd47e36d1b80b7eaf3" -dependencies = [ - "Inflector", - "anyhow", - "clap", - "colored", - "ignore", - "regex", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" - -[[package]] -name = "rustc_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver", -] - -[[package]] -name = "rustix" -version = "0.38.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" -dependencies = [ - "bitflags 2.4.1", - "errno", - "libc", - "linux-raw-sys", - "windows-sys", -] - -[[package]] -name = "ryu" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "schannel" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" -dependencies = [ - "windows-sys", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "security-framework" -version = "2.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "semver" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" - -[[package]] -name = "serde" -version = "1.0.192" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.192" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.39", -] - -[[package]] -name = "serde_json" -version = "1.0.108" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" -dependencies = [ - "indexmap 2.1.0", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_repr" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3081f5ffbb02284dda55132aa26daecedd7372a42417bbbab6f14ab7d6bb9145" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.39", -] - -[[package]] -name = "serde_spanned" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "sha2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", -] - -[[package]] -name = "sha2" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest 0.10.7", -] - -[[package]] -name = "sha3" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" -dependencies = [ - "block-buffer 0.9.0", - "digest 0.9.0", - "keccak", - "opaque-debug", -] - -[[package]] -name = "sha3" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" -dependencies = [ - "digest 0.10.7", - "keccak", -] - -[[package]] -name = "signal-hook-registry" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" -dependencies = [ - "libc", -] - -[[package]] -name = "signature" -version = "1.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" - -[[package]] -name = "signature" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" - -[[package]] -name = "slab" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] - -[[package]] -name = "smallvec" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" - -[[package]] -name = "socket2" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "socket2" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" -dependencies = [ - "libc", - "windows-sys", -] - -[[package]] -name = "spki" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" -dependencies = [ - "base64ct", - "der", -] - -[[package]] -name = "staking" -version = "0.1.0" -dependencies = [ - "common-structs", - "escrow-mock", - "multiversx-sc", - "multiversx-sc-scenario", - "num-bigint", - "rewards-pool-mock", -] - -[[package]] -name = "staking-meta" -version = "0.0.0" -dependencies = [ - "multiversx-sc-meta", - "staking", -] - -[[package]] -name = "staking-mock" -version = "0.1.0" -dependencies = [ - "common-structs", - "multiversx-sc", - "multiversx-sc-scenario", - "num-bigint", -] - -[[package]] -name = "staking-mock-meta" -version = "0.0.0" -dependencies = [ - "multiversx-sc-meta", - "staking-mock", -] - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "subtle" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "system-configuration" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "tempfile" -version = "3.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" -dependencies = [ - "cfg-if", - "fastrand", - "redox_syscall", - "rustix", - "windows-sys", -] - -[[package]] -name = "thread_local" -version = "1.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" -dependencies = [ - "cfg-if", - "once_cell", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653" -dependencies = [ - "backtrace", - "bytes", - "libc", - "mio", - "num_cpus", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "socket2 0.5.5", - "tokio-macros", - "windows-sys", -] - -[[package]] -name = "tokio-macros" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.39", -] - -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", - "tracing", -] - -[[package]] -name = "toml" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" -dependencies = [ - "indexmap 2.1.0", - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", -] - -[[package]] -name = "toml_datetime" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" -dependencies = [ - "indexmap 2.1.0", - "serde", - "serde_spanned", - "toml_datetime", - "winnow", -] - -[[package]] -name = "tower-service" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" - -[[package]] -name = "tracing" -version = "0.1.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" -dependencies = [ - "pin-project-lite", - "tracing-core", -] - -[[package]] -name = "tracing-core" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" -dependencies = [ - "once_cell", -] - -[[package]] -name = "try-lock" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" - -[[package]] -name = "typenum" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" - -[[package]] -name = "unicode-bidi" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-segmentation" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" - -[[package]] -name = "url" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", -] - -[[package]] -name = "utf8parse" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "walkdir" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasm-bindgen" -version = "0.2.88" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.88" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.39", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afec9963e3d0994cac82455b2b3502b81a7f40f9a0d32181f7528d9f4b43e02" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.88" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.88" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.39", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.88" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" - -[[package]] -name = "wasmparser" -version = "0.116.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a58e28b80dd8340cb07b8242ae654756161f6fc8d0038123d679b7b99964fa50" -dependencies = [ - "indexmap 2.1.0", - "semver", -] - -[[package]] -name = "wasmprinter" -version = "0.2.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aff4df0cdf1906ec040e97d78c3fc8fd26d3f8d70adaac81f07f80957b63b54" -dependencies = [ - "anyhow", - "wasmparser", -] - -[[package]] -name = "web-sys" -version = "0.3.65" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5db499c5f66323272151db0e666cd34f78617522fb0c1604d31a27c50c206a85" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "winnow" -version = "0.5.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "829846f3e3db426d4cee4510841b71a8e58aa2a76b1132579487ae430ccd9c7b" -dependencies = [ - "memchr", -] - -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys", -] - -[[package]] -name = "zerocopy" -version = "0.7.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c4061bedbb353041c12f413700357bec76df2c7e2ca8e4df8bac24c6bf68e3d" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3c129550b3e6de3fd0ba67ba5c81818f9805e58b8d7fee80a3a59d2c9fc601a" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.39", -] - -[[package]] -name = "zeroize" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" - -[[package]] -name = "zip" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" -dependencies = [ - "byteorder", - "crc32fast", - "crossbeam-utils", - "flate2", -] diff --git a/packages/core/mx/Cargo.toml b/packages/core/mx/Cargo.toml deleted file mode 100644 index 1e8929ebde..0000000000 --- a/packages/core/mx/Cargo.toml +++ /dev/null @@ -1,39 +0,0 @@ -[workspace] - -members = [ - - # Common contract paths - "contracts/common-structs", - - # Escrow contract paths - "contracts/escrow", - "contracts/escrow/meta", - - # Escrow factory contract paths - "contracts/escrow-factory", - "contracts/escrow-factory/meta", - - # KV Store contract paths - "contracts/kv-store", - "contracts/kv-store/meta", - - # Rewards pool contract paths - "contracts/rewards-pool", - "contracts/rewards-pool/meta", - - # Staking contract paths - "contracts/staking", - "contracts/staking/meta", - - # Staking mock contract paths - "contracts/staking-mock", - "contracts/staking-mock/meta", - - # Rewards pool mock contract paths - "contracts/rewards-pool-mock", - "contracts/rewards-pool-mock/meta", - - # Escrow mock contract paths - "contracts/escrow-mock", - "contracts/escrow-mock/meta", -] \ No newline at end of file diff --git a/packages/core/mx/README.md b/packages/core/mx/README.md deleted file mode 100644 index 90c4407d92..0000000000 --- a/packages/core/mx/README.md +++ /dev/null @@ -1,71 +0,0 @@ -# Human Protocol MultiversX Smart Contracts - -## Contracts - -Available Contracts: - -- EscrowContract -- EscrowFactoryContract -- KVStoreContract -- RewardsPoolContract -- StakingContract - -## Development Setup - -This entire directory of `mx` is a cargo workspace that contains all the contracts and the tests for them. To add a new -contract to the workspace, after adding it to the `contracts` directory, add it to the `members` array in the -`Cargo.toml` file from root of `mx`. - -In order to write contracts and be able to build/test/deploy them there are some small prerequisites that need to be -installed. There is a tutorial section on MultiversX Docs that can be followed to install everything necessary right -here [MultiversX Docs](https://docs.multiversx.com/developers/tutorials/your-first-dapp#software-prerequisites) - -For a simple fast install on Debian environments run the following: - -**Install python 3.8, pip and libncurses5** - -```bash -sudo apt-get update -sudo apt install libncurses5 build-essential python3-pip nodejs npm python3.8-venv -``` - -**Install mxpy** - -```bash -wget -O mxpy-up.py https://raw.githubusercontent.com/multiversx/mx-sdk-py-cli/main/mxpy-up.py -python3 mxpy-up.py -``` - -**Install MultiversX IDE VSCode extension** - -[MultiversX IDE VSCode](https://marketplace.visualstudio.com/items?itemName=Elrond.vscode-elrond-ide) - -## Building contracts - -You can use a multitude of ways for building the contracts: - -- in the MultiversX VSCode extension right click on contract and choose build -- directly right click on the contract directory in the source tree and choose build -- in a console inside the contract directory type `mxpy contract build` - -For building all the contracts inside the `contracts` directory there is a script called `build-mx-contracts.sh`. -Run the script from the `mx` root directory and will automatically compile and build all the contracts. - -The same principle goes for cleaning the `output` directories of the contracts. Run the script `clean-mx-contracts.sh` -from the `mx` root directory and will clean all contracts. - -## Test contracts - -To run all tests from the `mx` directory, run from the root of this directory: - -```bash -cargo test -``` - -If you want to run tests for only one contract you can run the same command as above but from the contract root -directory. - -### Troubleshooting - -If with `cargo test` the test are not able to run, you can try to clean your contract output and build it once again, -or in other cases just run build once again. diff --git a/packages/core/mx/contracts/common-structs/Cargo.toml b/packages/core/mx/contracts/common-structs/Cargo.toml deleted file mode 100644 index 358227b4f3..0000000000 --- a/packages/core/mx/contracts/common-structs/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ - -[package] -name = "common-structs" -version = "0.1.0" -authors = [ - "Vlad Nedelcu " -] -edition = "2018" -publish = false - -[lib] -path = "src/lib.rs" - -[dev-dependencies] -num-bigint = "0.4.2" - -[dependencies.multiversx-sc] -version = "0.45.1" - -[dev-dependencies.multiversx-sc-scenario] -version = "0.45.1" diff --git a/packages/core/mx/contracts/common-structs/src/escrow.rs b/packages/core/mx/contracts/common-structs/src/escrow.rs deleted file mode 100644 index bef4389729..0000000000 --- a/packages/core/mx/contracts/common-structs/src/escrow.rs +++ /dev/null @@ -1,11 +0,0 @@ -multiversx_sc::derive_imports!(); - -#[derive(TopEncode, TopDecode, PartialEq, Eq, TypeAbi, Debug)] -pub enum EscrowStatus { - Launched, - Pending, - Partial, - Paid, - Complete, - Cancelled, -} diff --git a/packages/core/mx/contracts/common-structs/src/lib.rs b/packages/core/mx/contracts/common-structs/src/lib.rs deleted file mode 100644 index 1522162aaa..0000000000 --- a/packages/core/mx/contracts/common-structs/src/lib.rs +++ /dev/null @@ -1,4 +0,0 @@ -#![no_std] - -pub mod stakes; -pub mod escrow; \ No newline at end of file diff --git a/packages/core/mx/contracts/common-structs/src/stakes.rs b/packages/core/mx/contracts/common-structs/src/stakes.rs deleted file mode 100644 index 23271aec02..0000000000 --- a/packages/core/mx/contracts/common-structs/src/stakes.rs +++ /dev/null @@ -1,138 +0,0 @@ -multiversx_sc::imports!(); -multiversx_sc::derive_imports!(); - -#[derive(Debug, TypeAbi, TopEncode, TopDecode)] -pub struct Staker { - pub token_staked: BigUint, - pub tokens_allocated: BigUint, - pub tokens_locked: BigUint, - pub tokens_locked_until: u64, -} - -impl Staker { - pub fn new() -> Self { - Self { - token_staked: BigUint::zero(), - tokens_allocated: BigUint::zero(), - tokens_locked: BigUint::zero(), - tokens_locked_until: 0 - } - } - - pub fn deposit(&mut self, tokens: &BigUint) { - self.token_staked += tokens; - } - - pub fn tokens_used(&self) -> BigUint { - &self.tokens_allocated + &self.tokens_locked - } - - pub fn tokens_available(&self) -> BigUint { - &self.token_staked - &self.tokens_used() - } - - pub fn tokens_secure_stake(&self) -> BigUint { - &self.token_staked - &self.tokens_locked - } - - pub fn tokens_withdrawable(&self, block_number: u64) -> BigUint { - if self.tokens_locked_until == 0 || block_number < self.tokens_locked_until { - return BigUint::zero(); - } - - self.tokens_locked.clone() - } - - /// Return all tokens available for withdrawal - pub fn withdraw_tokens(&mut self, block_number: u64) -> BigUint { - let tokens_to_withdraw = self.tokens_withdrawable(block_number); - - if tokens_to_withdraw > 0 { - self.unlock_tokens(&tokens_to_withdraw); - self.withdraw(&tokens_to_withdraw); - } - - return tokens_to_withdraw - } - - pub fn lock_tokens(&mut self, tokens: &BigUint, period: u64, block_number: u64) { - let mut locking_period = period; - if self.tokens_locked > 0 { - let blocks_diff = if self.tokens_locked_until > block_number { - BigUint::from(self.tokens_locked_until - block_number) - } else { - BigUint::zero() - }; - - locking_period = self.weighted_average( - &blocks_diff, - &self.tokens_locked, - &BigUint::from(period), - tokens - ); - } - - self.tokens_locked = &self.tokens_locked + tokens; - self.tokens_locked_until = block_number + locking_period; - } - - fn unlock_tokens(&mut self, tokens: &BigUint) { - self.tokens_locked = &self.tokens_locked - tokens; - if self.tokens_locked == 0 { - self.tokens_locked_until = 0; - } - } - - fn withdraw(&mut self, tokens: &BigUint) { - self.token_staked = &self.token_staked - tokens; - } - - fn weighted_average( - &self, - value_a: &BigUint, - weight_a: &BigUint, - value_b: &BigUint, - weight_b: &BigUint, - ) -> u64 { - let avg = value_a * weight_a + value_b * weight_b / (weight_a + weight_b); - if avg > BigUint::from(u64::MAX) { - panic!("weighted_average overflow"); - } - - avg.to_u64().unwrap() - } - - pub fn unallocate(&mut self, tokens: &BigUint) { - self.tokens_allocated -= tokens; - } - - pub fn release(&mut self, tokens: &BigUint) { - self.token_staked -= tokens; - } - - pub fn allocate(&mut self, tokens: &BigUint) { - self.tokens_allocated += tokens; - } - - pub fn is_empty(&self) -> bool { - self.token_staked == 0 && self.tokens_allocated == 0 && self.tokens_locked == 0 - } -} - -#[derive(Debug, TypeAbi, TopEncode, TopDecode)] -pub struct Allocation { - pub escrow_address: ManagedAddress, - pub staker: ManagedAddress, - pub tokens: BigUint, - pub created_at: u64, - pub closed_at: u64, -} - -#[derive(Debug, TypeAbi, TopEncode, TopDecode, PartialEq, Eq)] -pub enum AllocationState { - Null, - Pending, - Active, - Closed, - Completed -} \ No newline at end of file diff --git a/packages/core/mx/contracts/escrow-factory/Cargo.toml b/packages/core/mx/contracts/escrow-factory/Cargo.toml deleted file mode 100644 index fc0cf3d5cf..0000000000 --- a/packages/core/mx/contracts/escrow-factory/Cargo.toml +++ /dev/null @@ -1,28 +0,0 @@ -[package] -name = "escrow-factory" -version = "0.1.0" -authors = [ - "Claudiu-Marcel Bruda ", - "Vlad Nedelcu " -] -edition = "2018" -publish = false - -[lib] -path = "src/lib.rs" - -[dev-dependencies] -num-bigint = "0.4.2" - -[dependencies.multiversx-sc] -version = "0.45.1" - -[dependencies.escrow] -path = "../escrow" - -[dependencies.staking-mock] -path = "../staking-mock" - -[dev-dependencies.multiversx-sc-scenario] -version = "0.45.1" - diff --git a/packages/core/mx/contracts/escrow-factory/interactions/testnet.snippets.sh b/packages/core/mx/contracts/escrow-factory/interactions/testnet.snippets.sh deleted file mode 100644 index 0d5989dc77..0000000000 --- a/packages/core/mx/contracts/escrow-factory/interactions/testnet.snippets.sh +++ /dev/null @@ -1,48 +0,0 @@ -WALLET_PEM="~/erd-wallets/HUMAN.pem" -PROXY="https://testnet-gateway.elrond.com" -CHAIN_ID="T" - -# UPDATE THIS AFTER EACH DEPLOY -CONTRACT_ADDRESS="erd1qqqqqqqqqqqqqpgqlucetxjl3c2yxrn4dhy8xkm6kh6ptac99cus2jsw3j" - -deploy() { - mxpy --verbose contract deploy --recall-nonce \ - --metadata-payable --metadata-payable-by-sc\ - --pem=${WALLET_PEM} \ - --gas-limit=100000000 \ - --proxy=${PROXY} --chain=${CHAIN_ID} \ - --bytecode="output/escrow-factory.wasm" \ - --outfile="deploy.interaction.json" --send || return -} - -upgrade() { - mxpy --verbose contract upgrade ${CONTRACT_ADDRESS} --recall-nonce \ - --metadata-payable \ - --pem=${WALLET_PEM} \ - --gas-limit=100000000 \ - --proxy=${PROXY} --chain=${CHAIN_ID} \ - --bytecode="output/escrow-factory.wasm" \ - --outfile="deploy.interaction.json" --send || return -} - -setTemplateAddress() { - template_contract_address=$1 - mxpy --verbose contract call ${CONTRACT_ADDRESS} --recall-nonce \ - --pem=${WALLET_PEM} \ - --gas-limit=10000000 \ - --proxy=${PROXY} --chain=${CHAIN_ID} \ - --function=setTemplateAddress \ - --arguments $template_contract_address \ - --send || return -} - -setToken() { - token_id=str:$1 - mxpy --verbose contract call ${CONTRACT_ADDRESS} --recall-nonce \ - --pem=${WALLET_PEM} \ - --gas-limit=10000000 \ - --proxy=${PROXY} --chain=${CHAIN_ID} \ - --function=setToken \ - --arguments $token_id \ - --send || return -} \ No newline at end of file diff --git a/packages/core/mx/contracts/escrow-factory/meta/Cargo.toml b/packages/core/mx/contracts/escrow-factory/meta/Cargo.toml deleted file mode 100644 index 7851533bf3..0000000000 --- a/packages/core/mx/contracts/escrow-factory/meta/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "escrow-factory-meta" -version = "0.0.0" -edition = "2018" -publish = false -authors = [ "you",] - -[dev-dependencies] - -[dependencies.escrow-factory] -path = ".." - -[dependencies.multiversx-sc-meta] -version = "0.45.1" diff --git a/packages/core/mx/contracts/escrow-factory/meta/src/main.rs b/packages/core/mx/contracts/escrow-factory/meta/src/main.rs deleted file mode 100644 index 979cf3bca4..0000000000 --- a/packages/core/mx/contracts/escrow-factory/meta/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - multiversx_sc_meta::cli_main::(); -} diff --git a/packages/core/mx/contracts/escrow-factory/multiversx.json b/packages/core/mx/contracts/escrow-factory/multiversx.json deleted file mode 100644 index 7365539625..0000000000 --- a/packages/core/mx/contracts/escrow-factory/multiversx.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "language": "rust" -} \ No newline at end of file diff --git a/packages/core/mx/contracts/escrow-factory/src/lib.rs b/packages/core/mx/contracts/escrow-factory/src/lib.rs deleted file mode 100644 index 88fc6819b6..0000000000 --- a/packages/core/mx/contracts/escrow-factory/src/lib.rs +++ /dev/null @@ -1,91 +0,0 @@ -#![no_std] -pub mod proxy; - -multiversx_sc::imports!(); - -pub const STANDARD_DURATION: u64 = 8640000; - -#[multiversx_sc::contract] -pub trait EscrowFactoryContract: proxy::StakingProxyModule { - - #[init] - fn init(&self, staking: ManagedAddress) { - self.staking_contract_address().set(&staking); - } - - #[endpoint(createEscrow)] - fn create_escrow( - &self, - token: TokenIdentifier, - token_bulk_max_value: BigUint, - trusted_handlers: MultiValueEncoded, - ) -> ManagedAddress { - let caller = self.blockchain().get_caller(); - let has_available_stake = self.has_available_stake(&caller); - require!(has_available_stake, "Needs to stake HMT tokens to create an escrow"); - - let mut arguments = ManagedArgBuffer::new(); - arguments.push_arg(token.clone()); - arguments.push_arg(caller); - arguments.push_arg(STANDARD_DURATION); - arguments.push_arg(token_bulk_max_value); - for handler in trusted_handlers.into_iter() { - arguments.push_arg(handler); - } - - let (escrow_address, _) = self.send_raw() - .deploy_from_source_contract( - self.blockchain().get_gas_left(), - &BigUint::zero(), - &self.escrow_template_address().get(), - CodeMetadata::UPGRADEABLE | CodeMetadata::READABLE | CodeMetadata::PAYABLE, - &arguments - ); - - let counter = self.counter().get() + 1; - self.escrow_counter(&escrow_address).set(counter); - self.counter().set(counter); - - self.escrow_launched_event(token, escrow_address.clone()); - - escrow_address - } - - #[view(isChild)] - fn is_child(&self, address: ManagedAddress) -> bool { - self.escrow_counter(&address).get() == self.counter().get() - } - - #[view(hasEscrow)] - fn has_escrow(&self, address: ManagedAddress) -> bool { - !self.escrow_counter(&address).is_empty() - } - - #[only_owner] - #[endpoint(setTemplateAddress)] - fn set_template_address(&self, address: ManagedAddress) { - self.escrow_template_address().set(&address); - } - - #[view(getTemplateAddress)] - #[storage_mapper("escrow_template_address")] - fn escrow_template_address(&self) -> SingleValueMapper; - - #[view(getEscrowCounter)] - #[storage_mapper("escrow_counter")] - fn escrow_counter(&self, escrow_address: &ManagedAddress) -> SingleValueMapper; - - #[view(getCounter)] - #[storage_mapper("counter")] - fn counter(&self) -> SingleValueMapper; - - #[storage_mapper("last_escrow")] - fn last_escrow(&self) -> SingleValueMapper; - - #[event("escrow_launched")] - fn escrow_launched_event( - &self, - #[indexed] token: TokenIdentifier, - #[indexed] escrow_address: ManagedAddress - ); -} diff --git a/packages/core/mx/contracts/escrow-factory/src/proxy.rs b/packages/core/mx/contracts/escrow-factory/src/proxy.rs deleted file mode 100644 index 0c9aa506fe..0000000000 --- a/packages/core/mx/contracts/escrow-factory/src/proxy.rs +++ /dev/null @@ -1,30 +0,0 @@ -multiversx_sc::imports!(); - -mod staking_proxy { - - multiversx_sc::imports!(); - - #[multiversx_sc::proxy] - pub trait StakingContract { - - #[view(hasAvailableStake)] - fn has_available_stake(&self, staker: ManagedAddress) -> bool; - } -} - -#[multiversx_sc::module] -pub trait StakingProxyModule { - - fn has_available_stake(&self, address: &ManagedAddress) -> bool { - self.staking_proxy(self.staking_contract_address().get()) - .has_available_stake(address.clone()) - .execute_on_dest_context() - } - - #[view(getStakingContractAddress)] - #[storage_mapper("staking_contract_address")] - fn staking_contract_address(&self) -> SingleValueMapper; - - #[proxy] - fn staking_proxy(&self, to: ManagedAddress) -> staking_proxy::Proxy; -} \ No newline at end of file diff --git a/packages/core/mx/contracts/escrow-factory/tests/interactions.rs b/packages/core/mx/contracts/escrow-factory/tests/interactions.rs deleted file mode 100644 index 59b97bc6d6..0000000000 --- a/packages/core/mx/contracts/escrow-factory/tests/interactions.rs +++ /dev/null @@ -1,119 +0,0 @@ -use multiversx_sc::types::{Address, MultiValueEncoded}; - -use multiversx_sc_scenario::{DebugApi, rust_biguint, managed_address, managed_token_id, managed_biguint}; -use multiversx_sc_scenario::testing_framework::{BlockchainStateWrapper, ContractObjWrapper}; - -pub const FACTORY_WASM_PATH: &'static str = "../output/escrow-factory.wasm"; -pub const ESCROW_WASM_PATH: &'static str = "../../job/output/escrow.wasm"; -pub const STAKING_MOCK_WASM_PATH: &str = "../../staking-mock/output/staking-mock.wasm"; - -const HMT_TOKEN: &[u8] = b"HMT-j18xl0"; - -use escrow_factory::EscrowFactoryContract; - -pub struct EscrowFactorySetup -where - EscrowFactoryBuilder: 'static + Copy + Fn() -> escrow_factory::ContractObj, - EscrowBuilder: 'static + Copy + Fn() -> escrow::ContractObj, - StakingMockBuilder: 'static + Copy + Fn() -> staking_mock::ContractObj -{ - pub b_wrapper: BlockchainStateWrapper, - pub c_wrapper: ContractObjWrapper, EscrowFactoryBuilder>, - pub escrow_wrapper: ContractObjWrapper, EscrowBuilder>, - pub staking_mock_wrapper: ContractObjWrapper, StakingMockBuilder>, - pub owner: Address, -} - -impl EscrowFactorySetup -where - EscrowFactoryBuilder: 'static + Copy + Fn() -> escrow_factory::ContractObj, - EscrowBuilder: 'static + Copy + Fn() -> escrow::ContractObj, - StakingMockBuilder: 'static + Copy + Fn() -> staking_mock::ContractObj -{ - - pub fn new( - factory_builder: EscrowFactoryBuilder, - escrow_builder: EscrowBuilder, - staking_mock_builder: StakingMockBuilder, - ) -> Self { - let rust_zero = rust_biguint!(0u64); - let mut b_wrapper = BlockchainStateWrapper::new(); - let owner_address = b_wrapper.create_user_account(&rust_zero); - - // Create escrow factory wrapper - let factory_wrapper = b_wrapper.create_sc_account( - &rust_zero, - Some(&owner_address), - factory_builder, - FACTORY_WASM_PATH, - ); - - // Create escrow wrapper - let escrow_wrapper = b_wrapper.create_sc_account( - &rust_zero, - Some(&owner_address), - escrow_builder, - ESCROW_WASM_PATH - ); - - let staking_mock_wrapper = b_wrapper.create_sc_account( - &rust_zero, - Some(&owner_address), - staking_mock_builder, - STAKING_MOCK_WASM_PATH - ); - - b_wrapper - .execute_tx(&owner_address,&factory_wrapper,&rust_zero, |sc| { - sc.init(managed_address!(staking_mock_wrapper.address_ref())); - }) - .assert_ok(); - - Self { - b_wrapper, - c_wrapper: factory_wrapper, - escrow_wrapper, - staking_mock_wrapper, - owner: owner_address, - } - } - - pub fn set_template_address(&mut self) { - let tempate_address = self.escrow_wrapper.address_ref(); - self.b_wrapper - .execute_tx(&self.owner, &self.c_wrapper, &rust_biguint!(0u64), |sc| { - sc.set_template_address(managed_address!(tempate_address)); - }) - .assert_ok(); - } - - pub fn create_escrow(&mut self, trusted_handlers: Vec
) { - self.b_wrapper - .execute_tx(&self.owner, &self.c_wrapper, &rust_biguint!(0u64), |sc| { - let mut trusted_handlers_wrapper = MultiValueEncoded::new(); - for handler in trusted_handlers { - trusted_handlers_wrapper.push(managed_address!(&handler)); - } - sc.create_escrow( - managed_token_id!(HMT_TOKEN), - managed_biguint!(100_000_000u64), - trusted_handlers_wrapper - ); - }) - .assert_ok(); - } - - pub fn create_user(&mut self) -> Address { - self.b_wrapper.create_user_account(&rust_biguint!(0u64)) - } - - pub fn prepare_deploy_job(&mut self) { - self.b_wrapper - .prepare_deploy_from_sc( - &self.c_wrapper.address_ref(), - escrow::contract_obj - ); - } -} - - diff --git a/packages/core/mx/contracts/escrow-factory/tests/test_escrow_factory.rs b/packages/core/mx/contracts/escrow-factory/tests/test_escrow_factory.rs deleted file mode 100644 index 7487c08cea..0000000000 --- a/packages/core/mx/contracts/escrow-factory/tests/test_escrow_factory.rs +++ /dev/null @@ -1,31 +0,0 @@ -mod interactions; - -use interactions::*; - -use escrow; -use escrow_factory; - -#[test] -fn test_deploy_factory() { - let _ = EscrowFactorySetup::new( - escrow_factory::contract_obj, - escrow::contract_obj, - staking_mock::contract_obj - ); -} - -#[test] -fn bulk_max_count() { - let mut setup = EscrowFactorySetup::new( - escrow_factory::contract_obj, - escrow::contract_obj, - staking_mock::contract_obj - ); - setup.set_template_address(); - - let oracle1 = setup.create_user(); - let oracle2 = setup.create_user(); - - setup.prepare_deploy_job(); - setup.create_escrow(vec![oracle1, oracle2]); -} \ No newline at end of file diff --git a/packages/core/mx/contracts/escrow-factory/wasm/Cargo.toml b/packages/core/mx/contracts/escrow-factory/wasm/Cargo.toml deleted file mode 100644 index 46e4365601..0000000000 --- a/packages/core/mx/contracts/escrow-factory/wasm/Cargo.toml +++ /dev/null @@ -1,27 +0,0 @@ -[package] -name = "escrow-factory-wasm" -version = "0.0.0" -edition = "2018" -publish = false -authors = [ "you",] - -[lib] -crate-type = [ "cdylib",] - -[workspace] -members = [ ".",] - -[dev-dependencies] - -[profile.release] -codegen-units = 1 -opt-level = "z" -lto = true -debug = false -panic = "abort" - -[dependencies.escrow-factory] -path = ".." - -[dependencies.multiversx-sc-wasm-adapter] -version = "0.45.1" diff --git a/packages/core/mx/contracts/escrow-factory/wasm/src/lib.rs b/packages/core/mx/contracts/escrow-factory/wasm/src/lib.rs deleted file mode 100644 index 195015a4fd..0000000000 --- a/packages/core/mx/contracts/escrow-factory/wasm/src/lib.rs +++ /dev/null @@ -1,32 +0,0 @@ -// Code generated by the multiversx-sc multi-contract system. DO NOT EDIT. - -//////////////////////////////////////////////////// -////////////////// AUTO-GENERATED ////////////////// -//////////////////////////////////////////////////// - -// Init: 1 -// Endpoints: 8 -// Async Callback (empty): 1 -// Total number of exported functions: 10 - -#![no_std] -#![feature(alloc_error_handler, lang_items)] - -multiversx_sc_wasm_adapter::allocator!(); -multiversx_sc_wasm_adapter::panic_handler!(); - -multiversx_sc_wasm_adapter::endpoints! { - escrow_factory - ( - createEscrow - isChild - hasEscrow - setTemplateAddress - getTemplateAddress - getEscrowCounter - getCounter - getStakingContractAddress - ) -} - -multiversx_sc_wasm_adapter::empty_callback! {} diff --git a/packages/core/mx/contracts/escrow-mock/.gitignore b/packages/core/mx/contracts/escrow-mock/.gitignore deleted file mode 100644 index 920d759a24..0000000000 --- a/packages/core/mx/contracts/escrow-mock/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -# Generated by Cargo -# will have compiled files and executables -/target/ -*/target/ - -# The erdpy output -output* diff --git a/packages/core/mx/contracts/escrow-mock/Cargo.toml b/packages/core/mx/contracts/escrow-mock/Cargo.toml deleted file mode 100644 index ac6f3146d8..0000000000 --- a/packages/core/mx/contracts/escrow-mock/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "escrow-mock" -version = "0.1.0" -authors = [ - "Vlad Nedelcu " -] -edition = "2018" -publish = false - -[lib] -path = "src/lib.rs" - -[dev-dependencies] -num-bigint = "0.4.2" - -[dependencies.multiversx-sc] -version = "0.45.1" - -[dependencies.common-structs] -path = "../common-structs" - -[dev-dependencies.multiversx-sc-scenario] -version = "0.45.1" diff --git a/packages/core/mx/contracts/escrow-mock/meta/Cargo.toml b/packages/core/mx/contracts/escrow-mock/meta/Cargo.toml deleted file mode 100644 index 29024e5323..0000000000 --- a/packages/core/mx/contracts/escrow-mock/meta/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "escrow-mock-meta" -version = "0.0.0" -edition = "2018" -publish = false -authors = [ "you",] - -[dev-dependencies] - -[dependencies.escrow-mock] -path = ".." - -[dependencies.multiversx-sc-meta] -version = "0.45.1" diff --git a/packages/core/mx/contracts/escrow-mock/meta/src/main.rs b/packages/core/mx/contracts/escrow-mock/meta/src/main.rs deleted file mode 100644 index 3d3b37bb00..0000000000 --- a/packages/core/mx/contracts/escrow-mock/meta/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - multiversx_sc_meta::cli_main::(); -} diff --git a/packages/core/mx/contracts/escrow-mock/multiversx.json b/packages/core/mx/contracts/escrow-mock/multiversx.json deleted file mode 100644 index 7365539625..0000000000 --- a/packages/core/mx/contracts/escrow-mock/multiversx.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "language": "rust" -} \ No newline at end of file diff --git a/packages/core/mx/contracts/escrow-mock/src/lib.rs b/packages/core/mx/contracts/escrow-mock/src/lib.rs deleted file mode 100644 index 0a4d562922..0000000000 --- a/packages/core/mx/contracts/escrow-mock/src/lib.rs +++ /dev/null @@ -1,25 +0,0 @@ -#![no_std] - -multiversx_sc::imports!(); -use common_structs::escrow::EscrowStatus; - - -#[multiversx_sc::contract] -pub trait EscrowMockContract { - - #[init] - fn init(&self) {} - - #[view(getStatus)] - fn get_status(&self) -> EscrowStatus { - self.status().get() - } - - #[endpoint(setStatus)] - fn set_status(&self, status: EscrowStatus) { - self.status().set(&status); - } - - #[storage_mapper("status")] - fn status(&self) -> SingleValueMapper; -} diff --git a/packages/core/mx/contracts/escrow-mock/wasm/Cargo.toml b/packages/core/mx/contracts/escrow-mock/wasm/Cargo.toml deleted file mode 100644 index a3935af03b..0000000000 --- a/packages/core/mx/contracts/escrow-mock/wasm/Cargo.toml +++ /dev/null @@ -1,27 +0,0 @@ -[package] -name = "escrow-mock-wasm" -version = "0.0.0" -edition = "2018" -publish = false -authors = [ "you",] - -[lib] -crate-type = [ "cdylib",] - -[workspace] -members = [ ".",] - -[dev-dependencies] - -[profile.release] -codegen-units = 1 -opt-level = "z" -lto = true -debug = false -panic = "abort" - -[dependencies.escrow-mock] -path = ".." - -[dependencies.multiversx-sc-wasm-adapter] -version = "0.45.1" diff --git a/packages/core/mx/contracts/escrow-mock/wasm/src/lib.rs b/packages/core/mx/contracts/escrow-mock/wasm/src/lib.rs deleted file mode 100644 index 79475439f7..0000000000 --- a/packages/core/mx/contracts/escrow-mock/wasm/src/lib.rs +++ /dev/null @@ -1,26 +0,0 @@ -// Code generated by the multiversx-sc multi-contract system. DO NOT EDIT. - -//////////////////////////////////////////////////// -////////////////// AUTO-GENERATED ////////////////// -//////////////////////////////////////////////////// - -// Init: 1 -// Endpoints: 2 -// Async Callback (empty): 1 -// Total number of exported functions: 4 - -#![no_std] -#![feature(alloc_error_handler, lang_items)] - -multiversx_sc_wasm_adapter::allocator!(); -multiversx_sc_wasm_adapter::panic_handler!(); - -multiversx_sc_wasm_adapter::endpoints! { - escrow_mock - ( - getStatus - setStatus - ) -} - -multiversx_sc_wasm_adapter::empty_callback! {} diff --git a/packages/core/mx/contracts/escrow/Cargo.toml b/packages/core/mx/contracts/escrow/Cargo.toml deleted file mode 100644 index ae7a2d450a..0000000000 --- a/packages/core/mx/contracts/escrow/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -name = "escrow" -version = "0.1.0" -authors = [ - "Claudiu-Marcel Bruda ", - "Vlad Nedelcu " -] -edition = "2018" -publish = false - -[lib] -path = "src/lib.rs" - -[dependencies.common-structs] -path = "../common-structs" - -[dependencies.multiversx-sc] -version = "0.45.1" - -[dev-dependencies.multiversx-sc-scenario] -version = "0.45.1" - -[dev-dependencies] -num-bigint = "0.4.2" diff --git a/packages/core/mx/contracts/escrow/interactions/testnet.snippets.sh b/packages/core/mx/contracts/escrow/interactions/testnet.snippets.sh deleted file mode 100644 index fba323b9e1..0000000000 --- a/packages/core/mx/contracts/escrow/interactions/testnet.snippets.sh +++ /dev/null @@ -1,23 +0,0 @@ -WALLET_PEM="~/erd-wallets/HUMAN.pem" -PROXY="https://testnet-gateway.elrond.com" -CHAIN_ID="T" - -# UPDATE THIS AFTER EACH DEPLOY -CONTRACT_ADDRESS="erd1qqqqqqqqqqqqqpgq2l2tlv00q6t36gkpskan75s3sfqldw439cusr7l5f7" - -RECORDING_ORALCE="erd1w73dll00g2q96rqvj7gms00uey5s94z9fqjjj9ecgx2tpeyh8hxqpzgryr" -REPUTATION_ORACLE="erd17rw0ugxew767mwluxwu75gqg3m500qu7ktxfufn8tsf5dxxh6dds3nyt8w" - -deploy() { - token_id=str:HMTX-ab4369 - canceler=$(mxpy wallet pem-address ${WALLET_PEM}) - duration=2629743 - mxpy --verbose contract deploy --recall-nonce \ - --metadata-payable --metadata-payable-by-sc\ - --pem=${WALLET_PEM} \ - --gas-limit=100000000 \ - --proxy=${PROXY} --chain=${CHAIN_ID} \ - --bytecode="output/escrow.wasm" \ - --arguments $token_id $canceler $duration $RECORDING_ORALCE $REPUTATION_ORACLE \ - --outfile="deploy.interaction.json" --send || return -} diff --git a/packages/core/mx/contracts/escrow/meta/Cargo.toml b/packages/core/mx/contracts/escrow/meta/Cargo.toml deleted file mode 100644 index dc5188cbfe..0000000000 --- a/packages/core/mx/contracts/escrow/meta/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "escrow-meta" -version = "0.0.0" -edition = "2018" -publish = false -authors = [ "you",] - -[dev-dependencies] - -[dependencies.escrow] -path = ".." - -[dependencies.multiversx-sc-meta] -version = "0.45.1" diff --git a/packages/core/mx/contracts/escrow/meta/src/main.rs b/packages/core/mx/contracts/escrow/meta/src/main.rs deleted file mode 100644 index 1360bd6575..0000000000 --- a/packages/core/mx/contracts/escrow/meta/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - multiversx_sc_meta::cli_main::(); -} diff --git a/packages/core/mx/contracts/escrow/multiversx.json b/packages/core/mx/contracts/escrow/multiversx.json deleted file mode 100644 index 7365539625..0000000000 --- a/packages/core/mx/contracts/escrow/multiversx.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "language": "rust" -} \ No newline at end of file diff --git a/packages/core/mx/contracts/escrow/src/constants.rs b/packages/core/mx/contracts/escrow/src/constants.rs deleted file mode 100644 index 68e5c8fc85..0000000000 --- a/packages/core/mx/contracts/escrow/src/constants.rs +++ /dev/null @@ -1,38 +0,0 @@ -multiversx_sc::imports!(); -multiversx_sc::derive_imports!(); - -pub const BULK_MAX_COUNT: usize = 100; -pub const MAX_STAKE_PERCENTAGE: u64 = 100; - - -#[derive(TopEncode, TopDecode, NestedEncode, NestedDecode, TypeAbi, Clone)] -pub struct Oracle { - pub address: ManagedAddress, - pub stake: BigUint, -} - -impl Oracle { - pub fn new(address: ManagedAddress, stake: BigUint) -> Self { - Self { address, stake } - } -} - -#[derive(TopEncode, TopDecode, TypeAbi)] -pub struct OraclePair { - pub reputation: Oracle, - pub recording: Oracle, -} - -impl OraclePair { - pub fn new( - reputation_address: &ManagedAddress, - reputation_stake: BigUint, - recording_address: &ManagedAddress, - recording_stake: BigUint - ) -> Self { - Self { - reputation: Oracle::new(reputation_address.clone(), reputation_stake), - recording: Oracle::new(recording_address.clone(), recording_stake) - } - } -} \ No newline at end of file diff --git a/packages/core/mx/contracts/escrow/src/lib.rs b/packages/core/mx/contracts/escrow/src/lib.rs deleted file mode 100644 index 9671f2b1de..0000000000 --- a/packages/core/mx/contracts/escrow/src/lib.rs +++ /dev/null @@ -1,359 +0,0 @@ -#![no_std] -multiversx_sc::imports!(); - -pub mod constants; - -use common_structs::escrow::EscrowStatus; - -use crate::constants::{BULK_MAX_COUNT, MAX_STAKE_PERCENTAGE}; - -#[multiversx_sc::contract] -pub trait EscrowContract { - - #[init] - fn init( - &self, - token: TokenIdentifier, - canceler: ManagedAddress, - duration: u64, - bulk_max_value: BigUint, - trusted_callers: MultiValueEncoded, - ) { - self.token().set_if_empty(token); - self.status().set_if_empty(EscrowStatus::Launched); - let expiration = self.blockchain().get_block_timestamp() + duration; - self.duration().set_if_empty(expiration); - self.launcher().set_if_empty(self.blockchain().get_caller()); - self.canceler().set_if_empty(&canceler); - self.bulk_max_value().set_if_empty(bulk_max_value); - if self.trusted_callers().is_empty() { - for caller in trusted_callers { - self.trusted_callers().insert(caller.clone()); - } - self.trusted_callers().insert(self.blockchain().get_caller()); - self.trusted_callers().insert(canceler); - } - } - - #[view(getBalance)] - fn get_balance(&self) -> BigUint { - let contract_token = self.token().get(); - - self.blockchain().get_sc_balance(&EgldOrEsdtTokenIdentifier::esdt(contract_token), 0) - } - - #[endpoint(addTrustedHandlers)] - fn add_trusted_handlers(&self, trusted_handlers: MultiValueEncoded) { - self.require_trusted(); - for caller in trusted_handlers { - self.trusted_callers().insert(caller.clone()); - } - } - - #[endpoint(setup)] - fn setup( - &self, - reputation_oracle: ManagedAddress, - recording_oracle: ManagedAddress, - reputation_oracle_stake: u64, - recording_oracle_stake: u64, - url: ManagedBuffer, - hash: ManagedBuffer, - solution_requested: u64 - ) { - self.require_trusted(); - self.require_not_expired(); - require!(self.status().get() == EscrowStatus::Launched, "Contract is not launched"); - require!(solution_requested > 0, "Invalid or missing solutions"); - - let total_stake = &reputation_oracle_stake + &recording_oracle_stake; - require!(total_stake <= MAX_STAKE_PERCENTAGE, "Stake out of bounds"); - - self.reputation_oracle_address().set(&reputation_oracle); - self.recording_oracle_address().set(&recording_oracle); - self.reputation_oracle_stake().set(reputation_oracle_stake); - self.recording_oracle_stake().set(recording_oracle_stake); - - self.trusted_callers().insert(recording_oracle); - self.trusted_callers().insert(reputation_oracle); - - self.manifest_hash().set(&hash); - self.manifest_url().set(&url); - - self.remaining_solutions().set(solution_requested); - - self.status().set(EscrowStatus::Pending); - self.pending_event(url, hash); - } - - #[endpoint(abort)] - fn abort(&self) { - self.require_trusted(); - self.require_status(&[EscrowStatus::Launched, EscrowStatus::Pending, EscrowStatus::Partial]); - let balance: BigUint = self.get_balance(); - if balance != 0 { - self.cancel() - } else { - self.status().set(EscrowStatus::Cancelled) - } - } - - #[endpoint(cancel)] - fn cancel(&self) { - self.require_trusted(); - self.require_status(&[EscrowStatus::Launched, EscrowStatus::Pending, EscrowStatus::Partial]); - self.require_not_broke(); - - let balance: BigUint = self.get_balance(); - self.send().direct_esdt(&self.canceler().get(), &self.token().get(), 0, &balance); - - self.status().set(EscrowStatus::Cancelled) - } - - #[endpoint(complete)] - fn complete(&self) { - self.require_trusted(); - self.require_not_expired(); - self.require_status(&[EscrowStatus::Paid]); - self.status().set(EscrowStatus::Complete); - self.completed_event(); - } - - #[endpoint(storeResults)] - fn store_results_endpoint(&self, url: ManagedBuffer, hash: ManagedBuffer) { - self.require_trusted(); - self.require_not_expired(); - let status = self.status().get(); - require!( - status == EscrowStatus::Pending || status == EscrowStatus::Partial, - "Escrow not in Pending or Partial status state" - ); - - self.store_results(&url, &hash); - self.intermediate_storage_event(url, hash); - } - - #[endpoint(bulkPayOut)] - fn bulk_payout( - &self, - recipients: MultiValueEncoded, - amounts: MultiValueEncoded, - url: ManagedBuffer, - hash: ManagedBuffer, - tx_id: u64, - ) -> bool { - self.require_trusted(); - self.require_not_broke(); - self.require_not_expired(); - self.require_status(&[EscrowStatus::Pending, EscrowStatus::Partial]); - self.require_max_recipients(recipients.len()); - - require!(recipients.len() == amounts.len(), "Recipients and amounts length mismatch"); - - let mut balance = self.get_balance(); - let mut aggregate_bulk_amount = BigUint::zero(); - for amount in amounts.clone().into_iter() { - aggregate_bulk_amount += amount; - } - - require!(aggregate_bulk_amount < self.bulk_max_value().get(), "Bulk value too high"); - if balance < aggregate_bulk_amount { - return false - } - - self.store_results(&url, &hash); - - let (reputation_oracle_fee, recording_oracle_fee, final_amounts) = self.finalize_payouts(&amounts); - - for (i, recipient) in recipients.clone().into_iter().enumerate() { - let amount = final_amounts.get(i); - if *amount == 0 { - continue - } - self.send().direct_esdt(&recipient, &self.token().get(), 0, &amount); - } - - self.send().direct_esdt(&self.reputation_oracle_address().get(), &self.token().get(), 0, &reputation_oracle_fee); - self.send().direct_esdt(&self.recording_oracle_address().get(), &self.token().get(), 0, &recording_oracle_fee); - - balance = self.get_balance(); - let mut status = self.get_status(); - - if status == EscrowStatus::Pending { - self.remaining_solutions().update(|x| *x -= recipients.len() as u64); - status = EscrowStatus::Partial; - } - - if balance > 0 && status == EscrowStatus::Partial && self.remaining_solutions().get() == 0 { - self.send().direct_esdt(&self.canceler().get(), &self.token().get(), 0, &balance); - status = EscrowStatus::Paid; - } - - if balance == 0 && status == EscrowStatus::Partial { - status = EscrowStatus::Paid; - } - - self.status().set(status); - - - self.bulk_transfer_event(tx_id, recipients.len() as u64); - true - } - - fn finalize_payouts(&self, amounts: &MultiValueEncoded) -> (BigUint, BigUint, ManagedVec){ - let mut reputation_oracle_fee = BigUint::zero(); - let mut recording_oracle_fee = BigUint::zero(); - let reputation_oracle_stake = self.reputation_oracle_stake().get(); - let recording_oracle_stake = self.recording_oracle_stake().get(); - - let mut final_amounts: ManagedVec = ManagedVec::new(); - - for given_amount in amounts.clone().into_iter() { - let single_reputation_oracle_fee = &given_amount * reputation_oracle_stake / BigUint::from(MAX_STAKE_PERCENTAGE); - let single_recording_oracle_fee = &given_amount * recording_oracle_stake / BigUint::from(MAX_STAKE_PERCENTAGE); - let amount = given_amount - &single_reputation_oracle_fee - &single_recording_oracle_fee; - reputation_oracle_fee = &reputation_oracle_fee + &single_reputation_oracle_fee; - recording_oracle_fee = &recording_oracle_fee + &single_recording_oracle_fee; - - final_amounts.push(amount); - } - - (reputation_oracle_fee, recording_oracle_fee, final_amounts) - } - - fn store_results(&self, url: &ManagedBuffer, hash: &ManagedBuffer) { - let write_on_chain = url.len() != 0 || hash.len() != 0; - if write_on_chain { - self.final_results_url().set(url); - self.final_results_hash().set(hash); - } - } - - fn require_not_broke(&self) { - let balance: BigUint = self.get_balance(); - require!(balance != 0, "Contract out of funds") - } - - fn require_trusted(&self) { - let current_caller = self.blockchain().get_caller(); - let is_launcher = self.launcher().get() == current_caller; - let is_trusted_handler = self.trusted_callers().contains(¤t_caller); - - require!(is_launcher || is_trusted_handler, "Caller is not trusted") - } - - fn require_not_expired(&self) { - require!(self.duration().get() > self.blockchain().get_block_timestamp(), "Contract expired"); - } - - fn require_status(&self, allowed_status: &[EscrowStatus]) { - let current_status = self.status().get(); - require!( - allowed_status - .iter() - .any(|status| current_status == *status), - "Wrong status" - ); - } - - fn require_sufficient_balance(&self, payments_total: &BigUint){ - let current_balance = self.get_balance(); - - require!(payments_total <= ¤t_balance, "Not enough funds for payment"); - } - - fn require_payments_not_zero(&self, payments_total: &BigUint) { - require!(payments_total > &BigUint::zero(), "Cannot process payments with 0 amount") - } - - fn require_max_recipients(&self, recipients: usize) { - require!(recipients < BULK_MAX_COUNT, "Too many recipients"); - } - - #[payable("*")] - #[endpoint(deposit)] - fn deposit(&self){ - self.require_trusted(); - self.require_not_expired(); - self.require_status(&[EscrowStatus::Launched, EscrowStatus::Pending, EscrowStatus::Partial]); - - let payment = self.call_value().single_esdt(); - require!(payment.token_identifier == self.token().get(), "Wrong payment token"); - } - - #[view(getStatus)] - fn get_status(&self) -> EscrowStatus { - self.status().get() - } - - #[event("pending")] - fn pending_event(&self, #[indexed] url: ManagedBuffer, #[indexed] hash: ManagedBuffer); - - #[view(getRemainingSolutions)] - #[storage_mapper("remaining_solutions")] - fn remaining_solutions(&self) -> SingleValueMapper; - - #[view(getToken)] - #[storage_mapper("token")] - fn token(&self) -> SingleValueMapper; - - #[storage_mapper("status")] - fn status(&self) -> SingleValueMapper; - - #[view(getDuration)] - #[storage_mapper("duration")] - fn duration(&self) -> SingleValueMapper; - - #[storage_mapper("trusted_callers")] - fn trusted_callers(&self) -> UnorderedSetMapper; - - #[storage_mapper("manifest_url")] - fn manifest_url(&self) -> SingleValueMapper; - - #[storage_mapper("manifest_hash")] - fn manifest_hash(&self) -> SingleValueMapper; - - #[storage_mapper("final_results_url")] - fn final_results_url(&self) -> SingleValueMapper; - - #[storage_mapper("final_results_hash")] - fn final_results_hash(&self) -> SingleValueMapper; - - #[storage_mapper("launcher")] - fn launcher(&self) -> SingleValueMapper; - - #[storage_mapper("canceler")] - fn canceler(&self) -> SingleValueMapper; - - #[storage_mapper("reputation_oracle_address")] - fn reputation_oracle_address(&self) -> SingleValueMapper; - - #[storage_mapper("reputation_oracle_stake")] - fn reputation_oracle_stake(&self) -> SingleValueMapper; - - #[storage_mapper("recording_oracle_address")] - fn recording_oracle_address(&self) -> SingleValueMapper; - - #[storage_mapper("recording_oracle_stake")] - fn recording_oracle_stake(&self) -> SingleValueMapper; - - #[storage_mapper("bulk_max_value")] - fn bulk_max_value(&self) -> SingleValueMapper; - - #[event("intermediate_storage")] - fn intermediate_storage_event( - &self, - #[indexed] url: ManagedBuffer, - #[indexed] hash: ManagedBuffer - ); - - #[event("bulk_transfer")] - fn bulk_transfer_event( - &self, - #[indexed] tx_id: u64, - bulk_count: u64 - ); - - #[event("completed")] - fn completed_event(&self); -} diff --git a/packages/core/mx/contracts/escrow/tests/interactions.rs b/packages/core/mx/contracts/escrow/tests/interactions.rs deleted file mode 100644 index 5564cfa758..0000000000 --- a/packages/core/mx/contracts/escrow/tests/interactions.rs +++ /dev/null @@ -1,232 +0,0 @@ -use common_structs::escrow::EscrowStatus; -use escrow::EscrowContract; -use multiversx_sc::types::{Address, MultiValueEncoded}; -use multiversx_sc_scenario::{DebugApi, rust_biguint, managed_token_id, managed_address, managed_biguint, managed_buffer}; -use multiversx_sc_scenario::testing_framework::{BlockchainStateWrapper, ContractObjWrapper}; - -pub const HMT_TOKEN: &[u8] = b"HMT-sj19xl"; -pub const HMT_DECIMALS: u64 = 6; // Only used for tests purposes as 18 decimal places would overflow u64 -pub const WASM_PATH: &'static str = "../output/escrow.wasm"; - -pub struct EscrowSetup -where - Builder: 'static + Copy + Fn() -> escrow::ContractObj -{ - pub b_wrapper: BlockchainStateWrapper, - pub c_wrapper: ContractObjWrapper, Builder>, - pub owner: Address -} - -impl EscrowSetup -where - Builder: 'static + Copy + Fn() -> escrow::ContractObj -{ - pub fn new(builder: Builder) -> Self { - let rust_zero = rust_biguint!(0u64); - let mut blockchain_wrapper = BlockchainStateWrapper::new(); - let owner_address = blockchain_wrapper.create_user_account(&rust_zero); - - let contract_wrapper = blockchain_wrapper.create_sc_account( - &rust_zero, - Some(&owner_address), - builder, - WASM_PATH, - ); - - let bulk_max_count: u64 = ( - rust_biguint!(100u64) * rust_biguint!(10u64).pow(HMT_DECIMALS as u32) - ).to_string().parse().unwrap(); - blockchain_wrapper - .execute_tx(&owner_address, &contract_wrapper, &rust_zero, |sc| { - let mut trusted_handlers = MultiValueEncoded::new(); - trusted_handlers.push(managed_address!(&owner_address)); - sc.init( - managed_token_id!(HMT_TOKEN), - managed_address!(&owner_address), - 10000u64, - managed_biguint!(bulk_max_count), - trusted_handlers, - ) - }) - .assert_ok(); - - Self { - b_wrapper: blockchain_wrapper, - c_wrapper: contract_wrapper, - owner: owner_address - } - } - - pub fn add_trusted_handler_as_owner(&mut self, handler: &Address) { - self.b_wrapper - .execute_tx(&self.owner, &self.c_wrapper, &rust_biguint!(0u64), |sc| { - let mut trusted_handlers = MultiValueEncoded::new(); - trusted_handlers.push(managed_address!(handler)); - sc.add_trusted_handlers(trusted_handlers) - }) - .assert_ok(); - } - - pub fn add_trusted_handler_as_caller(&mut self, handler: &Address, caller: &Address) { - self.b_wrapper - .execute_tx(&caller, &self.c_wrapper, &rust_biguint!(0u64), |sc| { - let mut trusted_handlers = MultiValueEncoded::new(); - trusted_handlers.push(managed_address!(handler)); - sc.add_trusted_handlers(trusted_handlers) - }) - .assert_ok(); - } - - pub fn check_trusted_handler(&mut self, handler: &Address) { - self.b_wrapper - .execute_query(&self.c_wrapper, |sc| { - assert!(sc.trusted_callers().contains(&managed_address!(handler))); - }) - .assert_ok(); - } - - pub fn create_user_account(&mut self) -> Address { - self.b_wrapper.create_user_account(&rust_biguint!(0u64)) - } - - pub fn escrow_setup( - &mut self, - caller: &Address, - reputation_oracle: &Address, - recording_oracle: &Address, - reputation_oracle_stake: u64, - recording_oracle_stake: u64, - url: &[u8], - hash: &[u8], - solution_requested: u64, - expected_err: Option<&str> - ) { - let tx = self.b_wrapper - .execute_tx(&caller, &self.c_wrapper, &rust_biguint!(0u64), |sc| { - sc.setup( - managed_address!(reputation_oracle), - managed_address!(recording_oracle), - reputation_oracle_stake, - recording_oracle_stake, - managed_buffer!(url), - managed_buffer!(hash), - solution_requested - ); - }); - - match expected_err { - Some(err) => tx.assert_error(4, err), - None => tx.assert_ok() - } - } - - pub fn check_status(&mut self, expected_status: EscrowStatus) { - self.b_wrapper - .execute_query(&self.c_wrapper, |sc| { - assert_eq!(sc.status().get(), expected_status); - }) - .assert_ok(); - } - - pub fn set_contract_balance(&mut self, balance: u64) { - self.b_wrapper.set_esdt_balance(&self.c_wrapper.address_ref(), HMT_TOKEN, &rust_biguint!(balance)); - } - - pub fn set_balance(&mut self, handler: &Address, balance: u64) { - self.b_wrapper.set_esdt_balance(handler, HMT_TOKEN, &rust_biguint!(balance)); - } - - pub fn cancel(&mut self) { - self.b_wrapper - .execute_tx(&self.owner, &self.c_wrapper, &rust_biguint!(0u64), |sc| { - sc.cancel(); - }) - .assert_ok(); - } - - pub fn check_owner_balance(&mut self, expected_balance: u64) { - self.b_wrapper.check_esdt_balance(&self.owner, HMT_TOKEN, &rust_biguint!(expected_balance)); - } - - pub fn check_balance(&mut self, address: &Address, expected_balance: u64) { - self.b_wrapper.check_esdt_balance(&address, HMT_TOKEN, &rust_biguint!(expected_balance)); - } - - pub fn store_results(&mut self, caller: &Address, url: &[u8], hash: &[u8]) { - self.b_wrapper - .execute_tx(&caller, &self.c_wrapper, &rust_biguint!(0), |sc| { - sc.store_results_endpoint(managed_buffer!(url), managed_buffer!(hash)); - }) - .assert_ok(); - } - - pub fn check_final_solutions(&mut self, expected_url: &[u8], expected_hash: &[u8]) { - self.b_wrapper - .execute_query(&self.c_wrapper, |sc| { - assert_eq!(sc.final_results_url().get(), managed_buffer!(expected_url)); - assert_eq!(sc.final_results_hash().get(), managed_buffer!(expected_hash)); - }) - .assert_ok(); - } - - pub fn complete(&mut self, expected_err: Option<&str>) { - let tx = self.b_wrapper - .execute_tx(&self.owner, &self.c_wrapper, &rust_biguint!(0u64), |sc| { - sc.complete(); - }); - - match expected_err { - Some(err) => tx.assert_error(4, err), - None => tx.assert_ok() - } - } - - pub fn deposit(&mut self, caller: &Address, amount: u64) { - self.b_wrapper - .execute_esdt_transfer(&caller, &self.c_wrapper, HMT_TOKEN, 0, &rust_biguint!(amount), |sc| { - sc.deposit(); - }) - .assert_ok(); - } - - pub fn get_token_amount(&self, amount: u64) -> u64{ - (rust_biguint!(amount) * rust_biguint!(10).pow(HMT_DECIMALS as u32)).to_string().parse().unwrap() - } - - pub fn bulk_payout( - &mut self, - recipients: &Vec
, - amounts: &Vec, - url: &[u8], - hash: &[u8], - tx_id: u64, - ) { - self.b_wrapper - .execute_tx(&self.owner, &self.c_wrapper, &rust_biguint!(0), |sc| { - let mut recipients_encoded = MultiValueEncoded::new(); - let mut amounts_encoded = MultiValueEncoded::new(); - for recipient in recipients { - recipients_encoded.push(managed_address!(recipient)); - } - for amount in amounts { - amounts_encoded.push(managed_biguint!(*amount)); - } - sc.bulk_payout( - recipients_encoded, - amounts_encoded, - managed_buffer!(url), - managed_buffer!(hash), - tx_id, - ); - }) - .assert_ok(); - } - - pub fn abort(&mut self) { - self.b_wrapper - .execute_tx(&self.owner, &self.c_wrapper, &rust_biguint!(0), |sc| { - sc.abort(); - }) - .assert_ok(); - } -} \ No newline at end of file diff --git a/packages/core/mx/contracts/escrow/tests/test_escrow.rs b/packages/core/mx/contracts/escrow/tests/test_escrow.rs deleted file mode 100644 index 05d0cd6622..0000000000 --- a/packages/core/mx/contracts/escrow/tests/test_escrow.rs +++ /dev/null @@ -1,319 +0,0 @@ -mod interactions; -use interactions::*; - - -#[test] -fn test_add_trusted_handler() { - let mut setup = EscrowSetup::new(escrow::contract_obj); - let handler = setup.create_user_account(); - - setup.add_trusted_handler_as_owner(&handler); - setup.check_trusted_handler(&handler); - - let another_handler = setup.create_user_account(); - setup.add_trusted_handler_as_caller(&another_handler, &handler); -} - -#[test] -fn test_escrow_setup_stake_out_of_bounds() { - let mut setup = EscrowSetup::new(escrow::contract_obj); - - let handler = setup.create_user_account(); - setup.add_trusted_handler_as_owner(&handler); - setup.check_trusted_handler(&handler); - - let reputation_oracle = setup.create_user_account(); - let recording_oracle = setup.create_user_account(); - - setup.escrow_setup( - &handler, - &reputation_oracle, - &recording_oracle, - 80u64, - 90u64, - b"https://url.test/", - b"test-hash", - 2, - - Some("Stake out of bounds") - ); -} - -#[test] -fn test_escrow_setup() { - let mut setup = EscrowSetup::new(escrow::contract_obj); - - let handler = setup.create_user_account(); - setup.add_trusted_handler_as_owner(&handler); - setup.check_trusted_handler(&handler); - - let reputation_oracle = setup.create_user_account(); - let recording_oracle = setup.create_user_account(); - - setup.check_status(common_structs::escrow::EscrowStatus::Launched); - setup.escrow_setup( - &handler, - &reputation_oracle, - &recording_oracle, - 10u64, - 10u64, - b"https://url.test/", - b"test-hash", - 2, - None - ); - setup.check_status(common_structs::escrow::EscrowStatus::Pending); -} - -#[test] -fn test_escrow_cancel() { - let mut setup = EscrowSetup::new(escrow::contract_obj); - - let handler = setup.create_user_account(); - setup.add_trusted_handler_as_owner(&handler); - setup.check_trusted_handler(&handler); - - let reputation_oracle = setup.create_user_account(); - let recording_oracle = setup.create_user_account(); - - setup.check_status(common_structs::escrow::EscrowStatus::Launched); - setup.escrow_setup( - &handler, - &reputation_oracle, - &recording_oracle, - 10u64, - 10u64, - b"https://url.test/", - b"test-hash", - 2, - None - ); - - setup.check_owner_balance(0); - setup.check_status(common_structs::escrow::EscrowStatus::Pending); - setup.set_contract_balance(100); - - setup.cancel(); - - setup.check_status(common_structs::escrow::EscrowStatus::Cancelled); - setup.check_owner_balance(100); -} - -#[test] -fn test_store_results() { - let mut setup = EscrowSetup::new(escrow::contract_obj); - - let handler = setup.create_user_account(); - setup.add_trusted_handler_as_owner(&handler); - setup.check_trusted_handler(&handler); - - let reputation_oracle = setup.create_user_account(); - let recording_oracle = setup.create_user_account(); - - setup.check_status(common_structs::escrow::EscrowStatus::Launched); - setup.escrow_setup( - &handler, - &reputation_oracle, - &recording_oracle, - 10u64, - 10u64, - b"https://url.test/", - b"test-hash", - 2, - None - ); - - let solution = b"https://test-solution.com/"; - let solution_hash = b"test-hash"; - setup.store_results(&handler, solution, solution_hash); - setup.check_final_solutions(solution, solution_hash); -} - -#[test] -fn test_bulk_payout_partial() { - let mut setup = EscrowSetup::new(escrow::contract_obj); - - let handler = setup.create_user_account(); - setup.add_trusted_handler_as_owner(&handler); - setup.check_trusted_handler(&handler); - - let reputation_oracle = setup.create_user_account(); - let recording_oracle = setup.create_user_account(); - - setup.check_status(common_structs::escrow::EscrowStatus::Launched); - setup.escrow_setup( - &handler, - &reputation_oracle, - &recording_oracle, - 10u64, - 10u64, - b"https://url.test/", - b"test-hash", - 2, - None - ); - - setup.check_owner_balance(0); - setup.check_status(common_structs::escrow::EscrowStatus::Pending); - let balance = setup.get_token_amount(8); - let deposit = setup.get_token_amount(7); - setup.set_balance(&handler, balance); - setup.deposit(&handler, deposit); - - let account1 = setup.create_user_account(); - - let recipients = vec![account1.clone()]; - let solution = b"https://test-solution.com/"; - let solution_hash = b"test-hash"; - - setup.bulk_payout( - &recipients, - &vec![setup.get_token_amount(4)], - solution, - solution_hash, - 0 - ); - setup.check_status(common_structs::escrow::EscrowStatus::Partial); - setup.check_balance(&reputation_oracle, 400_000); - setup.check_balance(&recording_oracle, 400_000); - setup.check_balance(&account1, setup.get_token_amount(4) - 400_000 - 400_000); -} - -#[test] -fn test_bulk_payout_complete() { - let mut setup = EscrowSetup::new(escrow::contract_obj); - - let handler = setup.create_user_account(); - setup.add_trusted_handler_as_owner(&handler); - setup.check_trusted_handler(&handler); - - let reputation_oracle = setup.create_user_account(); - let recording_oracle = setup.create_user_account(); - - setup.check_status(common_structs::escrow::EscrowStatus::Launched); - setup.escrow_setup( - &handler, - &reputation_oracle, - &recording_oracle, - 10u64, - 10u64, - b"https://url.test/", - b"test-hash", - 2, - None - ); - - setup.check_owner_balance(0); - setup.check_status(common_structs::escrow::EscrowStatus::Pending); - let balance = setup.get_token_amount(8); - let deposit = setup.get_token_amount(7); - setup.set_balance(&handler, balance); - setup.deposit(&handler, deposit); - - let account1 = setup.create_user_account(); - let account2 = setup.create_user_account(); - - let recipients = vec![account1.clone(), account2.clone()]; - let solution = b"https://test-solution.com/"; - let solution_hash = b"test-hash"; - - setup.bulk_payout( - &recipients, - &vec![setup.get_token_amount(4), setup.get_token_amount(3)], - solution, - solution_hash, - 0 - ); - setup.check_status(common_structs::escrow::EscrowStatus::Paid); - setup.check_balance(&reputation_oracle, 700_000); - setup.check_balance(&recording_oracle, 700_000); - setup.check_balance(&account1, setup.get_token_amount(4) - 400_000 - 400_000); - setup.check_balance(&account2, setup.get_token_amount(3) - 300_000 - 300_000); - -} - - -#[test] -fn test_complete_escrow() { - let mut setup = EscrowSetup::new(escrow::contract_obj); - - let handler = setup.create_user_account(); - setup.add_trusted_handler_as_owner(&handler); - setup.check_trusted_handler(&handler); - - let reputation_oracle = setup.create_user_account(); - let recording_oracle = setup.create_user_account(); - - setup.check_status(common_structs::escrow::EscrowStatus::Launched); - setup.escrow_setup( - &handler, - &reputation_oracle, - &recording_oracle, - 10u64, - 10u64, - b"https://url.test/", - b"test-hash", - 2, - None - ); - - setup.check_owner_balance(0); - setup.check_status(common_structs::escrow::EscrowStatus::Pending); - let balance = setup.get_token_amount(8); - let deposit = setup.get_token_amount(7); - setup.set_balance(&handler, balance); - setup.deposit(&handler, deposit); - - let account1 = setup.create_user_account(); - let account2 = setup.create_user_account(); - - let recipients = vec![account1.clone(), account2.clone()]; - let solution = b"https://test-solution.com/"; - let solution_hash = b"test-hash"; - - setup.bulk_payout( - &recipients, - &vec![setup.get_token_amount(4), setup.get_token_amount(3)], - solution, - solution_hash, - 0 - ); - - setup.complete(None); -} - -#[test] -fn test_job_abort() { - let mut setup = EscrowSetup::new(escrow::contract_obj); - - let handler = setup.create_user_account(); - setup.add_trusted_handler_as_owner(&handler); - setup.check_trusted_handler(&handler); - - let reputation_oracle = setup.create_user_account(); - let recording_oracle = setup.create_user_account(); - - setup.check_status(common_structs::escrow::EscrowStatus::Launched); - setup.escrow_setup( - &handler, - &reputation_oracle, - &recording_oracle, - 10u64, - 10u64, - b"https://url.test/", - b"test-hash", - 2, - None - ); - - setup.check_owner_balance(0); - setup.check_status(common_structs::escrow::EscrowStatus::Pending); - let balance = setup.get_token_amount(8); - let deposit = setup.get_token_amount(7); - setup.set_balance(&handler, balance); - setup.deposit(&handler, deposit); - - setup.abort(); - - setup.check_owner_balance(deposit); -} \ No newline at end of file diff --git a/packages/core/mx/contracts/escrow/wasm/Cargo.toml b/packages/core/mx/contracts/escrow/wasm/Cargo.toml deleted file mode 100644 index 2b11f98f1e..0000000000 --- a/packages/core/mx/contracts/escrow/wasm/Cargo.toml +++ /dev/null @@ -1,27 +0,0 @@ -[package] -name = "escrow-wasm" -version = "0.0.0" -edition = "2018" -publish = false -authors = [ "you",] - -[lib] -crate-type = [ "cdylib",] - -[workspace] -members = [ ".",] - -[dev-dependencies] - -[profile.release] -codegen-units = 1 -opt-level = "z" -lto = true -debug = false -panic = "abort" - -[dependencies.escrow] -path = ".." - -[dependencies.multiversx-sc-wasm-adapter] -version = "0.45.1" diff --git a/packages/core/mx/contracts/escrow/wasm/src/lib.rs b/packages/core/mx/contracts/escrow/wasm/src/lib.rs deleted file mode 100644 index 776487770c..0000000000 --- a/packages/core/mx/contracts/escrow/wasm/src/lib.rs +++ /dev/null @@ -1,37 +0,0 @@ -// Code generated by the multiversx-sc multi-contract system. DO NOT EDIT. - -//////////////////////////////////////////////////// -////////////////// AUTO-GENERATED ////////////////// -//////////////////////////////////////////////////// - -// Init: 1 -// Endpoints: 13 -// Async Callback (empty): 1 -// Total number of exported functions: 15 - -#![no_std] -#![feature(alloc_error_handler, lang_items)] - -multiversx_sc_wasm_adapter::allocator!(); -multiversx_sc_wasm_adapter::panic_handler!(); - -multiversx_sc_wasm_adapter::endpoints! { - escrow - ( - getBalance - addTrustedHandlers - setup - abort - cancel - complete - storeResults - bulkPayOut - deposit - getStatus - getRemainingSolutions - getToken - getDuration - ) -} - -multiversx_sc_wasm_adapter::empty_callback! {} diff --git a/packages/core/mx/contracts/kv-store/.gitignore b/packages/core/mx/contracts/kv-store/.gitignore deleted file mode 100644 index 920d759a24..0000000000 --- a/packages/core/mx/contracts/kv-store/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -# Generated by Cargo -# will have compiled files and executables -/target/ -*/target/ - -# The erdpy output -output* diff --git a/packages/core/mx/contracts/kv-store/Cargo.toml b/packages/core/mx/contracts/kv-store/Cargo.toml deleted file mode 100644 index 158fa7265d..0000000000 --- a/packages/core/mx/contracts/kv-store/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "kv-store" -version = "0.1.0" -authors = [ - "Vlad Nedelcu " -] -edition = "2018" -publish = false - -[lib] -path = "src/lib.rs" - -[dev-dependencies] -num-bigint = "0.4.2" - -[dependencies.multiversx-sc] -version = "0.45.1" - -[dev-dependencies.multiversx-sc-scenario] -version = "0.45.1" diff --git a/packages/core/mx/contracts/kv-store/meta/Cargo.toml b/packages/core/mx/contracts/kv-store/meta/Cargo.toml deleted file mode 100644 index 0c9601db90..0000000000 --- a/packages/core/mx/contracts/kv-store/meta/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "kv-store-meta" -version = "0.0.0" -edition = "2018" -publish = false -authors = [ "you",] - -[dev-dependencies] - -[dependencies.kv-store] -path = ".." - -[dependencies.multiversx-sc-meta] -version = "0.45.1" diff --git a/packages/core/mx/contracts/kv-store/meta/src/main.rs b/packages/core/mx/contracts/kv-store/meta/src/main.rs deleted file mode 100644 index df37a1b8e0..0000000000 --- a/packages/core/mx/contracts/kv-store/meta/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - multiversx_sc_meta::cli_main::(); -} diff --git a/packages/core/mx/contracts/kv-store/multiversx.json b/packages/core/mx/contracts/kv-store/multiversx.json deleted file mode 100644 index 7365539625..0000000000 --- a/packages/core/mx/contracts/kv-store/multiversx.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "language": "rust" -} \ No newline at end of file diff --git a/packages/core/mx/contracts/kv-store/src/lib.rs b/packages/core/mx/contracts/kv-store/src/lib.rs deleted file mode 100644 index 13acb1491c..0000000000 --- a/packages/core/mx/contracts/kv-store/src/lib.rs +++ /dev/null @@ -1,34 +0,0 @@ -#![no_std] - -multiversx_sc::imports!(); - -#[multiversx_sc::contract] -pub trait KVStoreContract { - - #[init] - fn init(&self) {} - - #[endpoint(get)] - fn get(&self, key: ManagedBuffer) -> ManagedBuffer { - let sender = self.blockchain().get_caller(); - self.store(&sender, &key).get() - } - - #[endpoint(set)] - fn set(&self, key: ManagedBuffer, value: ManagedBuffer) { - let sender = self.blockchain().get_caller(); - self.store(&sender, &key).set(&value); - self.data_saved(sender, key, value); - } - - #[storage_mapper("store")] - fn store(&self, sender: &ManagedAddress, key: &ManagedBuffer) -> SingleValueMapper; - - #[event("data_saved")] - fn data_saved( - &self, - #[indexed] sender: ManagedAddress, - #[indexed] key: ManagedBuffer, - value: ManagedBuffer - ); -} diff --git a/packages/core/mx/contracts/kv-store/tests/contract_interactions.rs b/packages/core/mx/contracts/kv-store/tests/contract_interactions.rs deleted file mode 100644 index 20f5d1cc22..0000000000 --- a/packages/core/mx/contracts/kv-store/tests/contract_interactions.rs +++ /dev/null @@ -1,66 +0,0 @@ -use kv_store::KVStoreContract; -use multiversx_sc::types::Address; -use multiversx_sc_scenario::{DebugApi, rust_biguint, managed_buffer}; -use multiversx_sc_scenario::testing_framework::{BlockchainStateWrapper, ContractObjWrapper}; - -pub const WASM_PATH: &'static str = "../output/kv-store.wasm"; - -pub struct KVSetup -where - Builder: 'static + Copy + Fn() -> kv_store::ContractObj -{ - pub b_wrapper: BlockchainStateWrapper, - pub c_wrapper: ContractObjWrapper, Builder>, - pub owner: Address -} - -impl KVSetup -where - Builder: 'static + Copy + Fn() -> kv_store::ContractObj -{ - pub fn new(builder: Builder) -> Self { - let rust_zero = rust_biguint!(0u64); - let mut blockchain_wrapper = BlockchainStateWrapper::new(); - let owner_address = blockchain_wrapper.create_user_account(&rust_zero); - - let contract_wrapper = blockchain_wrapper.create_sc_account( - &rust_zero, - Some(&owner_address), - builder, - WASM_PATH, - ); - - blockchain_wrapper - .execute_tx(&owner_address, &contract_wrapper, &rust_zero, |sc| { - sc.init() - }) - .assert_ok(); - - Self { - b_wrapper: blockchain_wrapper, - c_wrapper: contract_wrapper, - owner: owner_address - } - } - - pub fn get(&mut self, key: &[u8], expected: &[u8], caller: &Address) { - self.b_wrapper - .execute_tx(&caller, &self.c_wrapper, &rust_biguint!(0u64), |sc| { - let value = sc.get(managed_buffer!(key)); - assert_eq!(value, managed_buffer!(expected)); - }) - .assert_ok() - } - - pub fn set(&mut self, key: &[u8], value: &[u8], caller: &Address) { - self.b_wrapper - .execute_tx(&caller, &self.c_wrapper, &rust_biguint!(0u64), |sc| { - sc.set(managed_buffer!(key), managed_buffer!(value)); - }) - .assert_ok() - } - - pub fn create_user(&mut self) -> Address{ - self.b_wrapper.create_user_account(&rust_biguint!(0u64)) - } -} diff --git a/packages/core/mx/contracts/kv-store/tests/test_kv_store.rs b/packages/core/mx/contracts/kv-store/tests/test_kv_store.rs deleted file mode 100644 index 84305f3940..0000000000 --- a/packages/core/mx/contracts/kv-store/tests/test_kv_store.rs +++ /dev/null @@ -1,22 +0,0 @@ -mod contract_interactions; -use contract_interactions::*; - -#[test] -fn test_set_key() { - let mut setup = KVSetup::new(kv_store::contract_obj); - let user = setup.create_user(); - - setup.set(b"key", b"value", &user); - setup.get(b"key", b"value", &user); -} - -#[test] -fn test_set_key_two_users() { - let mut setup = KVSetup::new(kv_store::contract_obj); - let user = setup.create_user(); - let user2 = setup.create_user(); - - setup.set(b"key", b"value", &user); - setup.set(b"key", b"value2", &user2); - setup.get(b"key", b"value", &user); -} \ No newline at end of file diff --git a/packages/core/mx/contracts/kv-store/wasm/Cargo.toml b/packages/core/mx/contracts/kv-store/wasm/Cargo.toml deleted file mode 100644 index bd3609ddd4..0000000000 --- a/packages/core/mx/contracts/kv-store/wasm/Cargo.toml +++ /dev/null @@ -1,27 +0,0 @@ -[package] -name = "kv-store-wasm" -version = "0.0.0" -edition = "2018" -publish = false -authors = [ "you",] - -[lib] -crate-type = [ "cdylib",] - -[workspace] -members = [ ".",] - -[dev-dependencies] - -[profile.release] -codegen-units = 1 -opt-level = "z" -lto = true -debug = false -panic = "abort" - -[dependencies.kv-store] -path = ".." - -[dependencies.multiversx-sc-wasm-adapter] -version = "0.45.1" diff --git a/packages/core/mx/contracts/kv-store/wasm/src/lib.rs b/packages/core/mx/contracts/kv-store/wasm/src/lib.rs deleted file mode 100644 index fa7708ff6b..0000000000 --- a/packages/core/mx/contracts/kv-store/wasm/src/lib.rs +++ /dev/null @@ -1,26 +0,0 @@ -// Code generated by the multiversx-sc multi-contract system. DO NOT EDIT. - -//////////////////////////////////////////////////// -////////////////// AUTO-GENERATED ////////////////// -//////////////////////////////////////////////////// - -// Init: 1 -// Endpoints: 2 -// Async Callback (empty): 1 -// Total number of exported functions: 4 - -#![no_std] -#![feature(alloc_error_handler, lang_items)] - -multiversx_sc_wasm_adapter::allocator!(); -multiversx_sc_wasm_adapter::panic_handler!(); - -multiversx_sc_wasm_adapter::endpoints! { - kv_store - ( - get - set - ) -} - -multiversx_sc_wasm_adapter::empty_callback! {} diff --git a/packages/core/mx/contracts/rewards-pool-mock/.gitignore b/packages/core/mx/contracts/rewards-pool-mock/.gitignore deleted file mode 100644 index 920d759a24..0000000000 --- a/packages/core/mx/contracts/rewards-pool-mock/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -# Generated by Cargo -# will have compiled files and executables -/target/ -*/target/ - -# The erdpy output -output* diff --git a/packages/core/mx/contracts/rewards-pool-mock/Cargo.toml b/packages/core/mx/contracts/rewards-pool-mock/Cargo.toml deleted file mode 100644 index b7c6ad5b79..0000000000 --- a/packages/core/mx/contracts/rewards-pool-mock/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "rewards-pool-mock" -version = "0.1.0" -authors = [ - "Vlad Nedelcu " -] -edition = "2018" -publish = false - -[lib] -path = "src/lib.rs" - -[dev-dependencies] -num-bigint = "0.4.2" - -[dependencies.multiversx-sc] -version = "0.45.1" - -[dev-dependencies.multiversx-sc-scenario] -version = "0.45.1" diff --git a/packages/core/mx/contracts/rewards-pool-mock/meta/Cargo.toml b/packages/core/mx/contracts/rewards-pool-mock/meta/Cargo.toml deleted file mode 100644 index 6829810b8d..0000000000 --- a/packages/core/mx/contracts/rewards-pool-mock/meta/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "rewards-pool-mock-meta" -version = "0.0.0" -edition = "2018" -publish = false -authors = [ "you",] - -[dev-dependencies] - -[dependencies.rewards-pool-mock] -path = ".." - -[dependencies.multiversx-sc-meta] -version = "0.45.1" diff --git a/packages/core/mx/contracts/rewards-pool-mock/meta/src/main.rs b/packages/core/mx/contracts/rewards-pool-mock/meta/src/main.rs deleted file mode 100644 index e92616483d..0000000000 --- a/packages/core/mx/contracts/rewards-pool-mock/meta/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - multiversx_sc_meta::cli_main::(); -} diff --git a/packages/core/mx/contracts/rewards-pool-mock/multiversx.json b/packages/core/mx/contracts/rewards-pool-mock/multiversx.json deleted file mode 100644 index 7365539625..0000000000 --- a/packages/core/mx/contracts/rewards-pool-mock/multiversx.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "language": "rust" -} \ No newline at end of file diff --git a/packages/core/mx/contracts/rewards-pool-mock/src/lib.rs b/packages/core/mx/contracts/rewards-pool-mock/src/lib.rs deleted file mode 100644 index e83b2885b9..0000000000 --- a/packages/core/mx/contracts/rewards-pool-mock/src/lib.rs +++ /dev/null @@ -1,27 +0,0 @@ -#![no_std] - -multiversx_sc::imports!(); - - -#[multiversx_sc::contract] -pub trait RewardsPoolMockContract { - - #[init] - fn init(&self) { - - } - - #[endpoint(addReward)] - fn add_reward(&self, _escrow_address: ManagedAddress, _slasher: ManagedAddress, _tokens: BigUint) { - require!(self.blockchain().get_caller() == self.staking_contract_address().get(), "Only staking contract can call this function"); - require!(_tokens > 0, "Amount must be greater than 0"); - } - - #[endpoint(setStakingContractAddress)] - fn set_staking_contract_address(&self, staking_contract_address: ManagedAddress) { - self.staking_contract_address().set(&staking_contract_address); - } - - #[storage_mapper("staking_contract_address")] - fn staking_contract_address(&self) -> SingleValueMapper; -} diff --git a/packages/core/mx/contracts/rewards-pool-mock/wasm/Cargo.toml b/packages/core/mx/contracts/rewards-pool-mock/wasm/Cargo.toml deleted file mode 100644 index 1e77c1cb11..0000000000 --- a/packages/core/mx/contracts/rewards-pool-mock/wasm/Cargo.toml +++ /dev/null @@ -1,27 +0,0 @@ -[package] -name = "rewards-pool-mock-wasm" -version = "0.0.0" -edition = "2018" -publish = false -authors = [ "you",] - -[lib] -crate-type = [ "cdylib",] - -[workspace] -members = [ ".",] - -[dev-dependencies] - -[profile.release] -codegen-units = 1 -opt-level = "z" -lto = true -debug = false -panic = "abort" - -[dependencies.rewards-pool-mock] -path = ".." - -[dependencies.multiversx-sc-wasm-adapter] -version = "0.45.1" diff --git a/packages/core/mx/contracts/rewards-pool-mock/wasm/src/lib.rs b/packages/core/mx/contracts/rewards-pool-mock/wasm/src/lib.rs deleted file mode 100644 index 4a6fba87cf..0000000000 --- a/packages/core/mx/contracts/rewards-pool-mock/wasm/src/lib.rs +++ /dev/null @@ -1,26 +0,0 @@ -// Code generated by the multiversx-sc multi-contract system. DO NOT EDIT. - -//////////////////////////////////////////////////// -////////////////// AUTO-GENERATED ////////////////// -//////////////////////////////////////////////////// - -// Init: 1 -// Endpoints: 2 -// Async Callback (empty): 1 -// Total number of exported functions: 4 - -#![no_std] -#![feature(alloc_error_handler, lang_items)] - -multiversx_sc_wasm_adapter::allocator!(); -multiversx_sc_wasm_adapter::panic_handler!(); - -multiversx_sc_wasm_adapter::endpoints! { - rewards_pool_mock - ( - addReward - setStakingContractAddress - ) -} - -multiversx_sc_wasm_adapter::empty_callback! {} diff --git a/packages/core/mx/contracts/rewards-pool/.gitignore b/packages/core/mx/contracts/rewards-pool/.gitignore deleted file mode 100644 index 920d759a24..0000000000 --- a/packages/core/mx/contracts/rewards-pool/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -# Generated by Cargo -# will have compiled files and executables -/target/ -*/target/ - -# The erdpy output -output* diff --git a/packages/core/mx/contracts/rewards-pool/Cargo.toml b/packages/core/mx/contracts/rewards-pool/Cargo.toml deleted file mode 100644 index af1d4ffff0..0000000000 --- a/packages/core/mx/contracts/rewards-pool/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "rewards-pool" -version = "0.1.0" -authors = [ - "Vlad Nedelcu " -] -edition = "2018" -publish = false - -[lib] -path = "src/lib.rs" - -[dev-dependencies] -num-bigint = "0.4.2" - -[dependencies.multiversx-sc] -version = "0.45.1" - -[dev-dependencies.multiversx-sc-scenario] -version = "0.45.1" diff --git a/packages/core/mx/contracts/rewards-pool/meta/Cargo.toml b/packages/core/mx/contracts/rewards-pool/meta/Cargo.toml deleted file mode 100644 index ed55dd155d..0000000000 --- a/packages/core/mx/contracts/rewards-pool/meta/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "rewards-pool-meta" -version = "0.0.0" -edition = "2018" -publish = false -authors = [ "you",] - -[dev-dependencies] - -[dependencies.rewards-pool] -path = ".." - -[dependencies.multiversx-sc-meta] -version = "0.45.1" diff --git a/packages/core/mx/contracts/rewards-pool/meta/src/main.rs b/packages/core/mx/contracts/rewards-pool/meta/src/main.rs deleted file mode 100644 index d6084ef606..0000000000 --- a/packages/core/mx/contracts/rewards-pool/meta/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - multiversx_sc_meta::cli_main::(); -} diff --git a/packages/core/mx/contracts/rewards-pool/multiversx.json b/packages/core/mx/contracts/rewards-pool/multiversx.json deleted file mode 100644 index 7365539625..0000000000 --- a/packages/core/mx/contracts/rewards-pool/multiversx.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "language": "rust" -} \ No newline at end of file diff --git a/packages/core/mx/contracts/rewards-pool/src/constants.rs b/packages/core/mx/contracts/rewards-pool/src/constants.rs deleted file mode 100644 index f1caed81b0..0000000000 --- a/packages/core/mx/contracts/rewards-pool/src/constants.rs +++ /dev/null @@ -1,9 +0,0 @@ -multiversx_sc::imports!(); -multiversx_sc::derive_imports!(); - -#[derive(NestedEncode, NestedDecode, TopEncode, TopDecode, TypeAbi, PartialEq, Clone, ManagedVecItem)] -pub struct Reward { - pub escrow_address: ManagedAddress, - pub slasher: ManagedAddress, - pub tokens: BigUint, -} \ No newline at end of file diff --git a/packages/core/mx/contracts/rewards-pool/src/lib.rs b/packages/core/mx/contracts/rewards-pool/src/lib.rs deleted file mode 100644 index da64942fb9..0000000000 --- a/packages/core/mx/contracts/rewards-pool/src/lib.rs +++ /dev/null @@ -1,125 +0,0 @@ -#![no_std] - -pub mod constants; - -use constants::Reward; -multiversx_sc::imports!(); - -#[multiversx_sc::contract] -pub trait RewardsPoolContract { - - #[init] - fn init( - &self, - rewards_token: TokenIdentifier, - staking_contract_address: ManagedAddress, - protocol_fee: BigUint, - ) { - self.rewards_token().set_if_empty(rewards_token); - self.protocol_fee().set_if_empty(protocol_fee); - self.staking_contract_address().set_if_empty(staking_contract_address); - } - - /// Add rewards record - /// Protocol fee is deduced from the rewards amount - #[endpoint(addReward)] - fn add_reward( - &self, - escrow_address: ManagedAddress, - slasher: ManagedAddress, - tokens: BigUint - ) { - self.require_only_staking(); - - let protocol_fee = self.protocol_fee().get(); - if tokens < protocol_fee { - self.total_fee().update(|total_fee| *total_fee += tokens); - return - } - - let rewards_after_fee = tokens - &protocol_fee; - let new_reward_entry = Reward { - escrow_address: escrow_address.clone(), - slasher: slasher.clone(), - tokens: rewards_after_fee.clone(), - }; - - self.rewards(&escrow_address).push(&new_reward_entry); - - self.total_fee().update(|total_fee| *total_fee += &protocol_fee); - self.rewards_added_event(escrow_address, slasher, rewards_after_fee); - } - - /// Distribute rewards for allocation - #[endpoint(distributeRewards)] - fn distribute_rewards(&self, escrow_address: ManagedAddress) { - let rewards_token = self.rewards_token().get(); - for reward in self.rewards(&escrow_address).iter() { - self.send().direct_esdt(&reward.slasher, &rewards_token, 0, &reward.tokens); - } - self.rewards(&escrow_address).clear(); - } - - #[only_owner] - #[endpoint(withdraw)] - fn withdraw(&self) { - let total_fee = self.total_fee().take(); - let rewards_token = self.rewards_token().get(); - let caller = self.blockchain().get_caller(); - self.send().direct_esdt(&caller, &rewards_token, 0, &total_fee); - self.total_fee().clear(); - } - - fn require_only_staking(&self) { - let caller = self.blockchain().get_caller(); - require!(caller == self.staking_contract_address().get(), "Caller is not staking contract"); - } - - #[view(getRewards)] - fn get_rewards(&self, escrow_address: ManagedAddress) -> MultiValueEncoded> { - let mut results = MultiValueEncoded::new(); - for reward in self.rewards(&escrow_address).iter() { - results.push(reward); - } - - results - } - - #[only_owner] - #[endpoint(setRewardsToken)] - fn set_rewards_token(&self, rewards_token: TokenIdentifier) { - let total_fee = self.total_fee().get(); - if total_fee > 0 { - self.withdraw(); - } - - self.rewards_token().set(&rewards_token); - } - - #[view(getRewardsToken)] - #[storage_mapper("rewards_token")] - fn rewards_token(&self) -> SingleValueMapper; - - #[view(getProtocolFee)] - #[storage_mapper("protocol_fee")] - fn protocol_fee(&self) -> SingleValueMapper; - - #[view(getTotalFee)] - #[storage_mapper("total_fee")] - fn total_fee(&self) -> SingleValueMapper; - - #[storage_mapper("rewards")] - fn rewards(&self, escrow_address: &ManagedAddress) -> VecMapper>; - - #[view(getStakingContractAddress)] - #[storage_mapper("staking_contract_address")] - fn staking_contract_address(&self) -> SingleValueMapper; - - #[event("rewards_added")] - fn rewards_added_event( - &self, - #[indexed] escrow_address: ManagedAddress, - #[indexed] slasher: ManagedAddress, - tokens: BigUint - ); -} diff --git a/packages/core/mx/contracts/rewards-pool/tests/contract_interactions.rs b/packages/core/mx/contracts/rewards-pool/tests/contract_interactions.rs deleted file mode 100644 index 5ed0df521f..0000000000 --- a/packages/core/mx/contracts/rewards-pool/tests/contract_interactions.rs +++ /dev/null @@ -1,147 +0,0 @@ -use multiversx_sc::types::Address; -use multiversx_sc_scenario::{DebugApi, rust_biguint, managed_token_id, managed_biguint, managed_address}; -use multiversx_sc_scenario::testing_framework::{BlockchainStateWrapper, ContractObjWrapper}; -use rewards_pool::RewardsPoolContract; - -pub const WASM_PATH: &'static str = "../output/rewards-pool.wasm"; -pub const HMT_TOKEN: &[u8] = b"HMT-a9k3l0"; -pub const HMT_TOKEN_DECIMALS: u64 = 6; -pub const PROTOCOL_FEE: u64 = 1; // 1 HMT(digits) * 10^6(decimals) = 1_000_000 - -pub struct RewardsPoolSetup -where - Builder: 'static + Copy + Fn() -> rewards_pool::ContractObj -{ - pub b_wrapper: BlockchainStateWrapper, - pub c_wrapper: ContractObjWrapper, Builder>, - pub mock_wrapper: Address, - pub owner: Address -} - -impl RewardsPoolSetup -where - Builder: 'static + Copy + Fn() -> rewards_pool::ContractObj -{ - pub fn new(builder: Builder) -> Self { - let rust_zero = rust_biguint!(0u64); - let mut blockchain_wrapper = BlockchainStateWrapper::new(); - let owner_address = blockchain_wrapper.create_user_account(&rust_zero); - - let contract_wrapper = blockchain_wrapper.create_sc_account( - &rust_zero, - Some(&owner_address), - builder, - WASM_PATH, - ); - - let mock_staking_contract = blockchain_wrapper.create_sc_account( - &rust_zero, - Some(&owner_address), - builder, - WASM_PATH, - ); - - blockchain_wrapper - .execute_tx(&owner_address, &contract_wrapper, &rust_zero, |sc| { - sc.init( - managed_token_id!(HMT_TOKEN), - managed_address!(mock_staking_contract.address_ref()), - managed_biguint!(PROTOCOL_FEE) * managed_biguint!(10u64).pow(HMT_TOKEN_DECIMALS as u32), - ) - }) - .assert_ok(); - - Self { - b_wrapper: blockchain_wrapper, - c_wrapper: contract_wrapper, - mock_wrapper: mock_staking_contract.address_ref().clone(), - owner: owner_address - } - } - - pub fn create_address(&mut self) -> Address { - self.b_wrapper.create_user_account(&rust_biguint!(0u64)) - } - - pub fn set_mock_staker_balance(&mut self, balance: u64) { - let balance_biguint = rust_biguint!(balance) * rust_biguint!(10u64).pow(HMT_TOKEN_DECIMALS as u32); - self.b_wrapper.set_esdt_balance(&self.mock_wrapper, HMT_TOKEN, &balance_biguint); - } - - pub fn check_total_fee(&mut self, expected_fee: u64) { - self.b_wrapper - .execute_query(&self.c_wrapper, |sc| { - let total_fee = sc.total_fee().get(); - assert_eq!(total_fee, managed_biguint!(expected_fee)); - }) - .assert_ok(); - } - - pub fn check_rewards_entry_exists(&mut self, escrow_address: &Address, slasher: &Address, expected_rewards: u64) { - self.b_wrapper - .execute_query(&self.c_wrapper, |sc| { - for reward in sc.rewards(&managed_address!(escrow_address)).iter() { - if reward.slasher == managed_address!(slasher) { - assert_eq!(reward.tokens, managed_biguint!(expected_rewards)); - return; - } - } - }) - .assert_ok(); - } - - pub fn add_rewards_by_staker( - &mut self, - escrow_address: &Address, - slasher: &Address, - tokens: u64, - expected_err: Option<&str>, - ) { - let tx = self.b_wrapper - .execute_tx(&self.mock_wrapper, &self.c_wrapper, &rust_biguint!(0), |sc| { - sc.add_reward( - managed_address!(escrow_address), - managed_address!(slasher), - managed_biguint!(tokens) - ); - }); - - match expected_err { - Some(err) => tx.assert_error(4, err), - None => tx.assert_ok(), - } - } - - pub fn distribute_rewards(&mut self, escrow_address: &Address) { - self.b_wrapper - .execute_tx(&self.owner, &self.c_wrapper, &rust_biguint!(0u64), |sc| { - sc.distribute_rewards(managed_address!(escrow_address)); - }) - .assert_ok(); - } - - pub fn check_owner_balance(&mut self, balance: u64) { - self.b_wrapper.check_esdt_balance(&self.owner, HMT_TOKEN, &rust_biguint!(balance)); - } - - pub fn withdraw(&mut self) { - self.b_wrapper - .execute_tx(&self.owner, &self.c_wrapper, &rust_biguint!(0u64), |sc| { - sc.withdraw(); - }) - .assert_ok(); - } - - pub fn make_reward_payment(&mut self, tokens: u64) { - self.b_wrapper - .execute_esdt_transfer( - &self.mock_wrapper, - &self.c_wrapper, - &HMT_TOKEN, - 0, - &rust_biguint!(tokens), |_| {} - ) - .assert_ok(); - } - -} \ No newline at end of file diff --git a/packages/core/mx/contracts/rewards-pool/tests/test_rewards_pool.rs b/packages/core/mx/contracts/rewards-pool/tests/test_rewards_pool.rs deleted file mode 100644 index 0dd763a9c1..0000000000 --- a/packages/core/mx/contracts/rewards-pool/tests/test_rewards_pool.rs +++ /dev/null @@ -1,93 +0,0 @@ -mod contract_interactions; -use contract_interactions::*; -use multiversx_sc_scenario::rust_biguint; - - -#[test] -fn test_add_rewards_smaller_than_protocol_fee() { - let mut setup = RewardsPoolSetup::new(rewards_pool::contract_obj); - setup.set_mock_staker_balance(1); - - let escrow1 = setup.create_address(); - let slasher1 = setup.create_address(); - let payment_amount = 500_000; // 0.5 HMT - - setup.add_rewards_by_staker(&escrow1, &slasher1, payment_amount, None); - setup.check_total_fee(payment_amount); -} - -#[test] -fn test_add_rewards_new_entry_rewards_after_fee_0() { - let mut setup = RewardsPoolSetup::new(rewards_pool::contract_obj); - setup.set_mock_staker_balance(1); - - let escrow1 = setup.create_address(); - let slasher1 = setup.create_address(); - let tokens = 1_000_000; // 1 HMT - - setup.make_reward_payment(tokens); - setup.add_rewards_by_staker(&escrow1, &slasher1, tokens, None); - setup.check_total_fee(tokens); -} - -#[test] -fn test_add_rewards_new_entry() { - let mut setup = RewardsPoolSetup::new(rewards_pool::contract_obj); - setup.set_mock_staker_balance(3); - - let escrow1 = setup.create_address(); - let slasher1 = setup.create_address(); - let payment_amount = 3_000_000; // 3 HMT - - setup.add_rewards_by_staker(&escrow1, &slasher1, payment_amount, None); - setup.check_total_fee(1_000_000); - setup.check_rewards_entry_exists(&escrow1, &slasher1, 2_000_000); -} - -#[test] -fn test_distribute_rewards() { - let mut setup = RewardsPoolSetup::new(rewards_pool::contract_obj); - setup.set_mock_staker_balance(8); - - let escrow1 = setup.create_address(); - let slasher1 = setup.create_address(); - let slasher2 = setup.create_address(); - let slasher1_payment = 5_000_000; // 5 HMT - let slasher2_payment = 3_000_000; // 3 HMT - - setup.make_reward_payment(slasher1_payment); - setup.add_rewards_by_staker(&escrow1, &slasher1, slasher1_payment, None); - setup.make_reward_payment(slasher2_payment); - setup.add_rewards_by_staker(&escrow1, &slasher2, slasher2_payment, None); - - setup.check_total_fee(2_000_000); - setup.b_wrapper.check_esdt_balance(&slasher1, HMT_TOKEN, &rust_biguint!(0)); - setup.b_wrapper.check_esdt_balance(&slasher2, HMT_TOKEN, &rust_biguint!(0)); - - setup.distribute_rewards(&escrow1); - setup.b_wrapper.check_esdt_balance(&slasher1, HMT_TOKEN, &rust_biguint!(4_000_000)); - setup.b_wrapper.check_esdt_balance(&slasher2, HMT_TOKEN, &rust_biguint!(2_000_000)); -} - -#[test] -fn test_withdraw_fees() { - let mut setup = RewardsPoolSetup::new(rewards_pool::contract_obj); - setup.set_mock_staker_balance(8); - - let escrow1 = setup.create_address(); - let slasher1 = setup.create_address(); - let slasher2 = setup.create_address(); - let slasher1_payment = 5_000_000; // 5 HMT - let slasher2_payment = 3_000_000; // 3 HMT - - setup.make_reward_payment(slasher1_payment); - setup.add_rewards_by_staker(&escrow1, &slasher1, slasher1_payment, None); - setup.make_reward_payment(slasher2_payment); - setup.add_rewards_by_staker(&escrow1, &slasher2, slasher2_payment, None); - setup.check_total_fee(2_000_000); - setup.check_owner_balance(0); - - setup.withdraw(); - setup.check_owner_balance(2_000_000); - setup.check_total_fee(0); -} \ No newline at end of file diff --git a/packages/core/mx/contracts/rewards-pool/wasm/Cargo.toml b/packages/core/mx/contracts/rewards-pool/wasm/Cargo.toml deleted file mode 100644 index 00c8d91f8d..0000000000 --- a/packages/core/mx/contracts/rewards-pool/wasm/Cargo.toml +++ /dev/null @@ -1,27 +0,0 @@ -[package] -name = "rewards-pool-wasm" -version = "0.0.0" -edition = "2018" -publish = false -authors = [ "you",] - -[lib] -crate-type = [ "cdylib",] - -[workspace] -members = [ ".",] - -[dev-dependencies] - -[profile.release] -codegen-units = 1 -opt-level = "z" -lto = true -debug = false -panic = "abort" - -[dependencies.rewards-pool] -path = ".." - -[dependencies.multiversx-sc-wasm-adapter] -version = "0.45.1" diff --git a/packages/core/mx/contracts/rewards-pool/wasm/src/lib.rs b/packages/core/mx/contracts/rewards-pool/wasm/src/lib.rs deleted file mode 100644 index 574698272f..0000000000 --- a/packages/core/mx/contracts/rewards-pool/wasm/src/lib.rs +++ /dev/null @@ -1,33 +0,0 @@ -// Code generated by the multiversx-sc multi-contract system. DO NOT EDIT. - -//////////////////////////////////////////////////// -////////////////// AUTO-GENERATED ////////////////// -//////////////////////////////////////////////////// - -// Init: 1 -// Endpoints: 9 -// Async Callback (empty): 1 -// Total number of exported functions: 11 - -#![no_std] -#![feature(alloc_error_handler, lang_items)] - -multiversx_sc_wasm_adapter::allocator!(); -multiversx_sc_wasm_adapter::panic_handler!(); - -multiversx_sc_wasm_adapter::endpoints! { - rewards_pool - ( - addReward - distributeRewards - withdraw - getRewards - setRewardsToken - getRewardsToken - getProtocolFee - getTotalFee - getStakingContractAddress - ) -} - -multiversx_sc_wasm_adapter::empty_callback! {} diff --git a/packages/core/mx/contracts/staking-mock/.gitignore b/packages/core/mx/contracts/staking-mock/.gitignore deleted file mode 100644 index 920d759a24..0000000000 --- a/packages/core/mx/contracts/staking-mock/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -# Generated by Cargo -# will have compiled files and executables -/target/ -*/target/ - -# The erdpy output -output* diff --git a/packages/core/mx/contracts/staking-mock/Cargo.toml b/packages/core/mx/contracts/staking-mock/Cargo.toml deleted file mode 100644 index e8646a29f7..0000000000 --- a/packages/core/mx/contracts/staking-mock/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "staking-mock" -version = "0.1.0" -authors = [ - "Vlad Nedelcu " -] -edition = "2018" -publish = false - -[lib] -path = "src/lib.rs" - -[dev-dependencies] -num-bigint = "0.4.2" - -[dependencies.common-structs] -path = "../common-structs" - -[dependencies.multiversx-sc] -version = "0.45.1" - -[dev-dependencies.multiversx-sc-scenario] -version = "0.45.1" diff --git a/packages/core/mx/contracts/staking-mock/meta/Cargo.toml b/packages/core/mx/contracts/staking-mock/meta/Cargo.toml deleted file mode 100644 index 5c1c9e22be..0000000000 --- a/packages/core/mx/contracts/staking-mock/meta/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "staking-mock-meta" -version = "0.0.0" -edition = "2018" -publish = false -authors = [ "you",] - -[dev-dependencies] - -[dependencies.staking-mock] -path = ".." - -[dependencies.multiversx-sc-meta] -version = "0.45.1" diff --git a/packages/core/mx/contracts/staking-mock/meta/src/main.rs b/packages/core/mx/contracts/staking-mock/meta/src/main.rs deleted file mode 100644 index 5dc6fd1ab5..0000000000 --- a/packages/core/mx/contracts/staking-mock/meta/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - multiversx_sc_meta::cli_main::(); -} diff --git a/packages/core/mx/contracts/staking-mock/multiversx.json b/packages/core/mx/contracts/staking-mock/multiversx.json deleted file mode 100644 index 7365539625..0000000000 --- a/packages/core/mx/contracts/staking-mock/multiversx.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "language": "rust" -} \ No newline at end of file diff --git a/packages/core/mx/contracts/staking-mock/src/lib.rs b/packages/core/mx/contracts/staking-mock/src/lib.rs deleted file mode 100644 index c3ef275e18..0000000000 --- a/packages/core/mx/contracts/staking-mock/src/lib.rs +++ /dev/null @@ -1,30 +0,0 @@ -#![no_std] - -use common_structs::stakes::Staker; - -multiversx_sc::imports!(); - -pub const MINIMUM_STAKE: u64 = 1_000_000; - - -#[multiversx_sc::contract] -pub trait StakingMockContract { - - #[init] - fn init(&self) {} - - #[endpoint(getStaker)] - fn get_staker(&self, _address: ManagedAddress) -> Staker { - Staker { - token_staked: BigUint::from(MINIMUM_STAKE).mul(2 as u64), - tokens_allocated: BigUint::zero(), - tokens_locked: BigUint::zero(), - tokens_locked_until: 0 - } - } - - #[view(hasAvailableStake)] - fn has_available_stake(&self, _address: ManagedAddress) -> bool { - true - } -} diff --git a/packages/core/mx/contracts/staking-mock/wasm/Cargo.toml b/packages/core/mx/contracts/staking-mock/wasm/Cargo.toml deleted file mode 100644 index 802daeab9e..0000000000 --- a/packages/core/mx/contracts/staking-mock/wasm/Cargo.toml +++ /dev/null @@ -1,27 +0,0 @@ -[package] -name = "staking-mock-wasm" -version = "0.0.0" -edition = "2018" -publish = false -authors = [ "you",] - -[lib] -crate-type = [ "cdylib",] - -[workspace] -members = [ ".",] - -[dev-dependencies] - -[profile.release] -codegen-units = 1 -opt-level = "z" -lto = true -debug = false -panic = "abort" - -[dependencies.staking-mock] -path = ".." - -[dependencies.multiversx-sc-wasm-adapter] -version = "0.45.1" diff --git a/packages/core/mx/contracts/staking-mock/wasm/src/lib.rs b/packages/core/mx/contracts/staking-mock/wasm/src/lib.rs deleted file mode 100644 index 319bd38366..0000000000 --- a/packages/core/mx/contracts/staking-mock/wasm/src/lib.rs +++ /dev/null @@ -1,26 +0,0 @@ -// Code generated by the multiversx-sc multi-contract system. DO NOT EDIT. - -//////////////////////////////////////////////////// -////////////////// AUTO-GENERATED ////////////////// -//////////////////////////////////////////////////// - -// Init: 1 -// Endpoints: 2 -// Async Callback (empty): 1 -// Total number of exported functions: 4 - -#![no_std] -#![feature(alloc_error_handler, lang_items)] - -multiversx_sc_wasm_adapter::allocator!(); -multiversx_sc_wasm_adapter::panic_handler!(); - -multiversx_sc_wasm_adapter::endpoints! { - staking_mock - ( - getStaker - hasAvailableStake - ) -} - -multiversx_sc_wasm_adapter::empty_callback! {} diff --git a/packages/core/mx/contracts/staking/.gitignore b/packages/core/mx/contracts/staking/.gitignore deleted file mode 100644 index 920d759a24..0000000000 --- a/packages/core/mx/contracts/staking/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -# Generated by Cargo -# will have compiled files and executables -/target/ -*/target/ - -# The erdpy output -output* diff --git a/packages/core/mx/contracts/staking/Cargo.toml b/packages/core/mx/contracts/staking/Cargo.toml deleted file mode 100644 index 65b7f23127..0000000000 --- a/packages/core/mx/contracts/staking/Cargo.toml +++ /dev/null @@ -1,29 +0,0 @@ -[package] -name = "staking" -version = "0.1.0" -authors = [ - "Vlad Nedelcu " -] -edition = "2018" -publish = false - -[lib] -path = "src/lib.rs" - -[dependencies.multiversx-sc] -version = "0.45.1" - -[dependencies.common-structs] -path = "../common-structs" - -[dev-dependencies.multiversx-sc-scenario] -version = "0.45.1" - -[dev-dependencies.num-bigint] -version = "0.4.2" - -[dev-dependencies.rewards-pool-mock] -path = "../rewards-pool-mock" - -[dev-dependencies.escrow-mock] -path = "../escrow-mock" diff --git a/packages/core/mx/contracts/staking/meta/Cargo.toml b/packages/core/mx/contracts/staking/meta/Cargo.toml deleted file mode 100644 index 6af2a3257b..0000000000 --- a/packages/core/mx/contracts/staking/meta/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "staking-meta" -version = "0.0.0" -edition = "2018" -publish = false -authors = [ "you",] - -[dev-dependencies] - -[dependencies.staking] -path = ".." - -[dependencies.multiversx-sc-meta] -version = "0.45.1" diff --git a/packages/core/mx/contracts/staking/meta/src/main.rs b/packages/core/mx/contracts/staking/meta/src/main.rs deleted file mode 100644 index 93e4c3f63a..0000000000 --- a/packages/core/mx/contracts/staking/meta/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - multiversx_sc_meta::cli_main::(); -} diff --git a/packages/core/mx/contracts/staking/multiversx.json b/packages/core/mx/contracts/staking/multiversx.json deleted file mode 100644 index 7365539625..0000000000 --- a/packages/core/mx/contracts/staking/multiversx.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "language": "rust" -} \ No newline at end of file diff --git a/packages/core/mx/contracts/staking/src/escrow_proxy.rs b/packages/core/mx/contracts/staking/src/escrow_proxy.rs deleted file mode 100644 index a32889c4d7..0000000000 --- a/packages/core/mx/contracts/staking/src/escrow_proxy.rs +++ /dev/null @@ -1,33 +0,0 @@ -use common_structs::escrow::EscrowStatus; - -multiversx_sc::imports!(); -multiversx_sc::derive_imports!(); - - -mod escrow_proxy { - use common_structs::escrow::EscrowStatus; - - multiversx_sc::imports!(); - multiversx_sc::derive_imports!(); - - #[multiversx_sc::proxy] - pub trait EscrowContract { - - #[view(getStatus)] - fn get_status(&self) -> EscrowStatus; - - } -} - -#[multiversx_sc::module] -pub trait EscrowProxyModule { - - fn get_status(&self, escrow_address: &ManagedAddress) -> EscrowStatus { - self.staking_proxy(escrow_address.clone()) - .get_status() - .execute_on_dest_context() - } - - #[proxy] - fn staking_proxy(&self, to: ManagedAddress) -> escrow_proxy::Proxy; -} \ No newline at end of file diff --git a/packages/core/mx/contracts/staking/src/events.rs b/packages/core/mx/contracts/staking/src/events.rs deleted file mode 100644 index 48d799f035..0000000000 --- a/packages/core/mx/contracts/staking/src/events.rs +++ /dev/null @@ -1,118 +0,0 @@ -multiversx_sc::imports!(); -multiversx_sc::derive_imports!(); - -#[derive(Debug, TypeAbi, TopEncode, TopDecode)] -pub struct StakeAllocatedData { - pub tokens: BigUint, - pub created_at: u64, -} - -#[derive(Debug, TypeAbi, TopEncode, TopDecode)] -pub struct StakeSlashedData { - pub tokens: BigUint, - pub slasher: ManagedAddress, -} - -#[derive(Debug, TypeAbi, TopEncode, TopDecode)] -pub struct AllocationClosedData { - pub tokens: BigUint, - pub closed_at: u64, -} - -#[multiversx_sc::module] -pub trait EventsModule { - - /// Emitted when 'staker' stake tokens amount - #[event("stake_deposited")] - fn stake_deposited_event( - &self, - #[indexed] staker: &ManagedAddress, - amount: &BigUint, - ); - - /// Emitted when `staker` unstaked and locked `tokens` amount `until` block. - #[event("stake_locked")] - fn stake_locked_event( - &self, - #[indexed] staker: &ManagedAddress, - #[indexed] until: &u64, - amount: &BigUint, - ); - - fn emit_stake_slashed_event( - &self, - staker: &ManagedAddress, - escrow_address: &ManagedAddress, - slasher: &ManagedAddress, - tokens: &BigUint, - ) { - let stake_slashed_data = StakeSlashedData { - tokens: tokens.clone(), - slasher: slasher.clone(), - }; - self.stake_slashed_event(staker, escrow_address, &stake_slashed_data); - } - - /// Emitted when `staker` was slashed for a total of `tokens` amount. - #[event("stake_slashed")] - fn stake_slashed_event( - &self, - #[indexed] staker: &ManagedAddress, - #[indexed] escrow_address: &ManagedAddress, - data: &StakeSlashedData, - ); - - /// Emitted when `staker` withdraws `tokens` staked. - #[event("stake_withdrawn")] - fn stake_withdrawn_event( - &self, - #[indexed] staker: &ManagedAddress, - amount: &BigUint, - ); - - fn emit_stake_allocated_event( - &self, - staker: &ManagedAddress, - escrow_address: &ManagedAddress, - tokens: &BigUint, - created_at: u64 - ) { - let stake_allocated_data = StakeAllocatedData { - tokens: tokens.clone(), - created_at, - }; - self.stake_allocated_event(staker, escrow_address, &stake_allocated_data); - } - - /// Emitted when `staker` allocated `tokens` amount to `escrowAddress`. - #[event("stake_allocated")] - fn stake_allocated_event( - &self, - #[indexed] staker: &ManagedAddress, - #[indexed] escrow_address: &ManagedAddress, - data: &StakeAllocatedData, - ); - - fn emit_allocation_closed_event( - &self, - staker: &ManagedAddress, - escrow_address: &ManagedAddress, - tokens: &BigUint, - closed_at: u64 - ) { - let allocation_closed_data = AllocationClosedData { - tokens: tokens.clone(), - closed_at, - }; - self.allocation_closed_event(staker, escrow_address, &allocation_closed_data); - } - - /// Emitted when `staker` close an allocation `escrowAddress`. - #[event("allocation_closed")] - fn allocation_closed_event( - &self, - #[indexed] staker: &ManagedAddress, - #[indexed] escrow_address: &ManagedAddress, - data: &AllocationClosedData, - ); -} \ No newline at end of file diff --git a/packages/core/mx/contracts/staking/src/lib.rs b/packages/core/mx/contracts/staking/src/lib.rs deleted file mode 100644 index c2df3fd435..0000000000 --- a/packages/core/mx/contracts/staking/src/lib.rs +++ /dev/null @@ -1,355 +0,0 @@ -#![no_std] - -pub mod escrow_proxy; -pub mod rewards_pool_proxy; -pub mod events; - -use common_structs::{ - stakes::{Staker, Allocation, AllocationState}, - escrow::EscrowStatus -}; - -multiversx_sc::imports!(); - -#[multiversx_sc::contract] -pub trait StakingContract: - escrow_proxy::EscrowProxyModule - + rewards_pool_proxy::RewardsPoolProxyModule - + events::EventsModule -{ - - #[init] - fn init( - &self, - staking_token: TokenIdentifier, - minimum_stake: BigUint, - lock_period: u64, - ) { - self.staking_token().set(&staking_token); - self.set_minimum_stake(minimum_stake); - self.set_lock_period(lock_period); - } - - #[only_owner] - #[endpoint(setMinimumStake)] - fn set_minimum_stake(&self, minimum_stake: BigUint) { - require!(minimum_stake > 0, "Minimum stake must be greater than 0"); - self.minimum_stake().set(&minimum_stake); - } - - #[only_owner] - #[endpoint(setLockPeriod)] - fn set_lock_period(&self, lock_period: u64) { - require!(lock_period > 0, "Must be a positive number"); - self.lock_period().set(&lock_period); - } - - #[only_owner] - #[endpoint(setRewardsPool)] - fn set_rewards_pool_address(&self, rewards_pool_address: ManagedAddress) { - self.rewards_pool_contract_address().set(&rewards_pool_address); - } - - #[payable("*")] - #[endpoint(stake)] - fn stake(&self) { - let payment = self.call_value().single_esdt(); - require!(payment.token_identifier == self.staking_token().get(), "Invalid token"); - require!(payment.token_nonce == 0, "Invalid token nonce"); - - let caller = self.blockchain().get_caller(); - let mut staker = self.get_or_create_staker(&caller); - - let total_stake = staker.tokens_secure_stake() + &payment.amount; - require!(total_stake >= self.minimum_stake().get(), "Total stake is below the minimum threshold"); - - staker.deposit(&payment.amount); - self.stakes(&caller).set(&staker); - - let mut stakers_handler = self.stakers(); - if !stakers_handler.contains(&caller) { - stakers_handler.insert(caller.clone()); - } - self.stake_deposited_event(&caller, &payment.amount); - } - - #[endpoint(unstake)] - fn unstake(&self, tokens: BigUint) { - let caller = self.blockchain().get_caller(); - require!(!self.stakes(&caller).is_empty(), "Caller is not a staker"); - - let mut staker = self.stakes(&caller).get(); - require!(staker.token_staked > 0, "Must have tokens staked"); - require!(tokens > 0, "Unstake amount must be greater than 0"); - require!(staker.tokens_available() >= tokens, "Insufficient amount to unstake"); - - let new_stake = staker.tokens_secure_stake() - &tokens; - require!(new_stake == 0 || new_stake >= self.minimum_stake().get(), "Total stake is below the minimum threshold"); - - let block_number = self.blockchain().get_block_nonce(); - let tokens_to_withdraw = staker.tokens_withdrawable(block_number); - if tokens_to_withdraw > 0 { - self.withdraw(&mut staker, &caller); - } - - staker.lock_tokens(&tokens, self.lock_period().get(), block_number); - self.stakes(&caller).set(&staker); - - self.stake_locked_event(&caller, &staker.tokens_locked_until, &tokens); - } - - #[endpoint(withdraw)] - fn withdraw_endpoint(&self) { - let caller = self.blockchain().get_caller(); - let stakes_handler = self.stakes(&caller); - require!(!stakes_handler.is_empty(), "Caller is not a staker"); - - let mut staker = stakes_handler.get(); - self.withdraw(&mut staker, &caller); - - if staker.is_empty() { - let mut stakers_handler = self.stakers(); - for staker in stakers_handler.iter() { - if staker == caller { - stakers_handler.swap_remove(&staker); - self.stakes(&caller).clear(); - return - } - } - } - self.stakes(&caller).set(staker); - } - - #[only_owner] - #[endpoint(slash)] - fn slash( - &self, - slasher: ManagedAddress, - staker_address: ManagedAddress, - escrow_address: ManagedAddress, - tokens: BigUint, - ) { - let stakes_handler = self.stakes(&staker_address); - require!(!stakes_handler.is_empty(), "Invalid address for staker"); - let mut staker = stakes_handler.get(); - - require!(!self.allocations(&escrow_address).is_empty(), "Invalid address for escrow"); - let mut allocation = self.allocations(&escrow_address).get(); - - require!(allocation.tokens > 0, "Allocation must have tokens"); - require!(allocation.tokens > tokens, "Allocation must have more tokens than the amount to slash"); - require!(staker.tokens_allocated >= tokens, "Staker must have more tokens than the amount to slash"); - require!(staker.token_staked >= tokens, "Staker must have more tokens than the amount to slash"); - - staker.unallocate(&tokens); - staker.release(&tokens); - allocation.tokens = allocation.tokens - &tokens; - - stakes_handler.set(&staker); - self.allocations(&escrow_address).set(&allocation); - - let rewards_pool_contract_address = self.rewards_pool_contract_address().get(); - self.send().direct_esdt(&rewards_pool_contract_address, &self.staking_token().get(), 0, &tokens); - - self.add_reward(escrow_address.clone(), slasher.clone(), tokens.clone(), rewards_pool_contract_address); - self.emit_stake_slashed_event(&staker_address, &escrow_address, &slasher, &tokens); - } - - #[endpoint(allocate)] - fn allocate(&self, escrow_address: ManagedAddress, tokens: BigUint) { - let caller = self.blockchain().get_caller(); - let stakes_handler = self.stakes(&caller); - require!(!stakes_handler.is_empty(), "Caller is not a staker"); - let mut staker = stakes_handler.get(); - - require!(tokens > 0, "Allocation tokens must be greater than 0"); - require!(staker.tokens_available() >= tokens, "Insufficient amount of tokens in the stake"); - - let allocation_state = self.get_escrow_allocation_state(&escrow_address); - require!(allocation_state == AllocationState::Null, "Allocation already exists"); - - let block_nonce = self.blockchain().get_block_nonce(); - let new_allocation = Allocation { - escrow_address: escrow_address.clone(), - staker: caller.clone(), - tokens: tokens.clone(), - created_at: block_nonce, - closed_at: 0 - }; - - self.allocations(&escrow_address).set(&new_allocation); - staker.allocate(&tokens); - stakes_handler.set(&staker); - self.emit_stake_allocated_event(&caller, &escrow_address, &tokens, block_nonce); - } - - #[endpoint(closeAllocation)] - fn close_allocation(&self, escrow_address: ManagedAddress) { - self.require_only_staker(); - - let allocation_state = self.get_escrow_allocation_state(&escrow_address); - require!(allocation_state == AllocationState::Completed, "Allocation has no completed state"); - require!(!self.allocations(&escrow_address).is_empty(), "Allocation does not exist on this escrow"); - - let mut allocation = self.allocations(&escrow_address).get(); - allocation.closed_at = self.blockchain().get_block_nonce(); - let diff_in_blocks = if allocation.closed_at > allocation.created_at { - allocation.closed_at - allocation.created_at - } else { - 0 - }; - - require!(diff_in_blocks > 0, "Allocation cannot be closed so early"); - - self.stakes(&allocation.staker).update(|s| s.unallocate(&allocation.tokens)); - self.allocations(&escrow_address).set(&allocation); - self.emit_allocation_closed_event(&allocation.staker, &escrow_address, &allocation.tokens, allocation.closed_at); - } - - /// Getter that returns if an staker has any stake. - #[view(hasStake)] - fn has_stake(&self, staker: ManagedAddress) -> bool { - let stakes_handler = self.stakes(&staker); - if stakes_handler.is_empty() { - return false - } - - let stake = stakes_handler.get(); - stake.token_staked > 0 - } - - #[view(hasAvailableStake)] - fn has_available_stake(&self, staker: ManagedAddress) -> bool { - let stakes_handler = self.stakes(&staker); - if stakes_handler.is_empty() { - return false - } - - let stake = stakes_handler.get(); - stake.tokens_available() > 0 - } - - /// Return boolean if escrow address is use for allocation - #[view(isAllocation)] - fn is_allocation(&self, escrow_address: ManagedAddress) -> bool { - let allocation_state = self.get_escrow_allocation_state(&escrow_address); - - match allocation_state { - AllocationState::Null => false, - _ => true - } - } - - #[view(getAllocationState)] - fn get_allocation_state(&self, escrow_address: ManagedAddress) -> AllocationState { - self.get_escrow_allocation_state(&escrow_address) - } - - #[view(getStakedTokens)] - fn get_staked_tokens(&self, staker: ManagedAddress) -> BigUint { - let stake = self.stakes(&staker).get(); - stake.token_staked - } - - #[view(getStaker)] - fn get_staker(&self, staker: ManagedAddress) -> Staker { - self.stakes(&staker).get() - } - - #[view(getListOfStakers)] - fn get_list_of_stakers(&self) -> MultiValue2, MultiValueEncoded>> { - let stakers_handler = self.stakers(); - if stakers_handler.len() == 0 { - return MultiValue2((MultiValueEncoded::new(), MultiValueEncoded::new())); - } - - let mut stakers_list: MultiValueEncoded> = MultiValueEncoded::new(); - let mut stakers_addresses: MultiValueEncoded = MultiValueEncoded::new(); - for staker in stakers_handler.iter(){ - let stake = self.stakes(&staker).get(); - stakers_list.push(stake); - stakers_addresses.push(staker); - } - - MultiValue2((stakers_addresses, stakers_list)) - } - - /// Withdraw staker tokens once the lock period has passed - fn withdraw(&self, staker: &mut Staker, staker_address: &ManagedAddress) { - let block_number = self.blockchain().get_block_nonce(); - let tokens_to_withdraw = staker.withdraw_tokens(block_number); - require!(tokens_to_withdraw > 0, "Stake has no available tokens for withdrawal"); - - self.send().direct_esdt(staker_address, &self.staking_token().get(), 0, &tokens_to_withdraw); - self.stake_withdrawn_event(staker_address, &tokens_to_withdraw); - } - - fn get_or_create_staker(&self, staker_address: &ManagedAddress) -> Staker { - if self.stakes(staker_address).is_empty() { - let new_staker = Staker::new(); - self.stakes(staker_address).set(&new_staker); - - return new_staker; - } - - self.stakes(staker_address).get() - } - - fn get_escrow_allocation_state(&self, escrow_address: &ManagedAddress) -> AllocationState { - if self.allocations(escrow_address).is_empty() { - return AllocationState::Null; - } - - let allocation = self.allocations(&escrow_address).get(); - let escrow_status = self.get_status(&escrow_address); - - if allocation.created_at != 0 && allocation.tokens > 0 && escrow_status == EscrowStatus::Pending { - return AllocationState::Pending; - } - - if allocation.closed_at == 0 { - match escrow_status { - EscrowStatus::Launched => return AllocationState::Active, - EscrowStatus::Complete => return AllocationState::Completed, - _ => return AllocationState::Closed, - } - } - - AllocationState::Closed - } - - fn require_only_staker(&self){ - let caller = self.blockchain().get_caller(); - require!(self.stakers().contains(&caller), "Only stakers can call this function"); - } - - - /// Staking token id - #[storage_mapper("staking_token")] - fn staking_token(&self) -> SingleValueMapper; - - /// Minimum amount of tokens a staker needs to stake - #[storage_mapper("minimum_stake")] - fn minimum_stake(&self) -> SingleValueMapper; - - /// Time in blocks to unstake - #[storage_mapper("lock_period")] - fn lock_period(&self) -> SingleValueMapper; - - /// List of stakers - #[storage_mapper("stakers")] - fn stakers(&self) -> UnorderedSetMapper; - - /// Allocations - #[view(getAllocation)] - #[storage_mapper("allocations")] - fn allocations(&self, escrow_address: &ManagedAddress) -> SingleValueMapper>; - - #[storage_mapper("stakes")] - fn stakes(&self, staker: &ManagedAddress) -> SingleValueMapper>; - - #[view(getRewardsPoolContractAddress)] - #[storage_mapper("rewards_pool_contract_address")] - fn rewards_pool_contract_address(&self) -> SingleValueMapper; - -} diff --git a/packages/core/mx/contracts/staking/src/rewards_pool_proxy.rs b/packages/core/mx/contracts/staking/src/rewards_pool_proxy.rs deleted file mode 100644 index 29ff8e9c91..0000000000 --- a/packages/core/mx/contracts/staking/src/rewards_pool_proxy.rs +++ /dev/null @@ -1,40 +0,0 @@ -multiversx_sc::imports!(); -multiversx_sc::derive_imports!(); - - -mod reward_pool_proxy { - multiversx_sc::imports!(); - multiversx_sc::derive_imports!(); - - #[multiversx_sc::proxy] - pub trait RewardPoolContract { - - #[endpoint(addReward)] - fn add_reward( - &self, - escrow_address: ManagedAddress, - slasher: ManagedAddress, - tokens: BigUint - ); - } -} - -#[multiversx_sc::module] -pub trait RewardsPoolProxyModule { - - fn add_reward( - &self, - escrow_address: ManagedAddress, - slasher: ManagedAddress, - tokens: BigUint, - rewards_pool_contract_address: ManagedAddress - ) { - - self.reward_pool_proxy(rewards_pool_contract_address) - .add_reward(escrow_address, slasher, tokens) - .execute_on_dest_context() - } - - #[proxy] - fn reward_pool_proxy(&self, to: ManagedAddress) -> reward_pool_proxy::Proxy; -} \ No newline at end of file diff --git a/packages/core/mx/contracts/staking/tests/contract_interactions.rs b/packages/core/mx/contracts/staking/tests/contract_interactions.rs deleted file mode 100644 index 5914840760..0000000000 --- a/packages/core/mx/contracts/staking/tests/contract_interactions.rs +++ /dev/null @@ -1,269 +0,0 @@ -use common_structs::escrow::EscrowStatus; -use multiversx_sc::types::Address; -use multiversx_sc_scenario::{DebugApi, rust_biguint, managed_token_id, managed_biguint, managed_address}; -use multiversx_sc_scenario::testing_framework::{ContractObjWrapper, BlockchainStateWrapper}; -use rewards_pool_mock::RewardsPoolMockContract; -use escrow_mock::EscrowMockContract; - -use staking::StakingContract; - -pub const WASM_PATH: &str = "../output/staking.wasm"; -pub const ESCROW_MOCK_WASM_PATH: &str = "../../escrow-mock/output/escrow-mock.wasm"; -pub const REWARDS_MOCK_WASM_PATH: &str = "../../rewards-pool-mock/output/rewards-pool-mock.wasm"; -pub const HMT_TOKEN: &[u8] = b"HMT-a9k3l0"; -pub const HMT_TOKEN_DECIMALS: u64 = 6; -pub const MINIMUM_STAKE: u64 = 1; // 1 HMT(digits) * 10^6(decimals) = 1_000_000 -pub const LOCKING_PERIOD: u64 = 5; - -pub struct StakerTest { - pub token_staked: u64, - pub tokens_allocated: u64, - pub tokens_locked: u64, - pub tokens_locked_until: u64, -} - -pub struct AllocationTest { - pub escrow_address: Address, - pub staker: Address, - pub tokens: u64, - pub created_at: u64, - pub closed_at: u64, -} - - -pub struct StakingSetup -where - Builder: 'static + Copy + Fn() -> staking::ContractObj, - MockEscrowBuilder: 'static + Copy + Fn() -> escrow_mock::ContractObj, - MockRewardsPoolBuilder: 'static + Copy + Fn() -> rewards_pool_mock::ContractObj -{ - pub b_wrapper: BlockchainStateWrapper, - pub c_wrapper: ContractObjWrapper, Builder>, - pub escrow_mock_wrapper: ContractObjWrapper, MockEscrowBuilder>, - pub rewards_mock_wrapper: ContractObjWrapper, MockRewardsPoolBuilder>, - pub owner: Address -} - -impl StakingSetup -where - Builder: 'static + Copy + Fn() -> staking::ContractObj, - MockEscrowBuilder: 'static + Copy + Fn() -> escrow_mock::ContractObj, - MockRewardsPoolBuilder: 'static + Copy + Fn() -> rewards_pool_mock::ContractObj -{ - pub fn new( - builder: Builder, - escrow_mock_builder: MockEscrowBuilder, - rewards_mock_builder: MockRewardsPoolBuilder - ) -> Self { - - let rust_zero = rust_biguint!(0u64); - let mut blockchain_wrapper = BlockchainStateWrapper::new(); - let owner_address = blockchain_wrapper.create_user_account(&rust_zero); - - let contract_wrapper = blockchain_wrapper.create_sc_account( - &rust_zero, - Some(&owner_address), - builder, - WASM_PATH, - ); - - let escrow_mock_wrapper = blockchain_wrapper.create_sc_account( - &rust_zero, - Some(&owner_address), - escrow_mock_builder, - ESCROW_MOCK_WASM_PATH, - ); - - let rewards_mock_wrapper = blockchain_wrapper.create_sc_account( - &rust_zero, - Some(&owner_address), - rewards_mock_builder, - REWARDS_MOCK_WASM_PATH, - ); - - blockchain_wrapper - .execute_tx(&owner_address, &contract_wrapper, &rust_zero, |sc| { - sc.init( - managed_token_id!(HMT_TOKEN), - managed_biguint!(MINIMUM_STAKE) * managed_biguint!(10u64).pow(HMT_TOKEN_DECIMALS as u32), - LOCKING_PERIOD - ); - }) - .assert_ok(); - - blockchain_wrapper - .execute_tx(&owner_address, &contract_wrapper, &rust_zero, |sc| { - sc.set_rewards_pool_address(managed_address!(rewards_mock_wrapper.address_ref())); - }) - .assert_ok(); - - blockchain_wrapper - .execute_tx(&owner_address, &rewards_mock_wrapper, &rust_zero, |sc| { - sc.set_staking_contract_address(managed_address!(contract_wrapper.address_ref())); - }) - .assert_ok(); - - blockchain_wrapper - .execute_tx(&owner_address, &escrow_mock_wrapper, &rust_zero, |sc| { - sc.set_status(EscrowStatus::Launched); - }) - .assert_ok(); - - Self { - b_wrapper: blockchain_wrapper, - c_wrapper: contract_wrapper, - escrow_mock_wrapper, - rewards_mock_wrapper, - owner: owner_address - } - } - - pub fn create_address(&mut self) -> Address { - self.b_wrapper.create_user_account(&rust_biguint!(0u64)) - } - - pub fn set_staker_balance(&mut self, balance: u64, address: &Address) { - let balance_biguint = rust_biguint!(balance) * rust_biguint!(10u64).pow(HMT_TOKEN_DECIMALS as u32); - self.b_wrapper.set_esdt_balance(address, HMT_TOKEN, &balance_biguint); - } - - pub fn stake( - &mut self, - caller: &Address, - amount: u64, - expected_err: Option<&str> - ) { - let tx = self.b_wrapper - .execute_esdt_transfer(&caller, &self.c_wrapper, HMT_TOKEN, 0, &rust_biguint!(amount), |sc| { - sc.stake(); - }); - - match expected_err { - Some(err) => tx.assert_error(4, err), - None => tx.assert_ok() - } - } - - pub fn check_staker_entry(&mut self, address: &Address, staker: &StakerTest) { - self.b_wrapper.execute_query(&self.c_wrapper, |sc| { - let managed_staker = sc.stakes(&managed_address!(address)).get(); - assert_eq!(managed_staker.token_staked, managed_biguint!(staker.token_staked)); - assert_eq!(managed_staker.tokens_allocated, managed_biguint!(staker.tokens_allocated)); - assert_eq!(managed_staker.tokens_locked, managed_biguint!(staker.tokens_locked)); - assert_eq!(managed_staker.tokens_locked_until, staker.tokens_locked_until); - }) - .assert_ok(); - } - - pub fn check_staker_entry_removed(&mut self, address: &Address) { - self.b_wrapper.execute_query(&self.c_wrapper, |sc| { - assert!(sc.stakes(&managed_address!(address)).is_empty()); - }) - .assert_ok(); - } - - pub fn unstake( - &mut self, - caller: &Address, - amount: u64, - expected_err: Option<&str> - ) { - let tx = self.b_wrapper - .execute_tx(&caller, &self.c_wrapper, &rust_biguint!(0), |sc| { - sc.unstake(managed_biguint!(amount)); - }); - - match expected_err { - Some(err) => tx.assert_error(4, err), - None => tx.assert_ok() - } - } - - pub fn withdraw( - &mut self, - caller: &Address, - expected_err: Option<&str> - ) { - let tx = self.b_wrapper - .execute_tx(&caller, &self.c_wrapper, &rust_biguint!(0), |sc| { - sc.withdraw_endpoint(); - }); - - match expected_err { - Some(err) => tx.assert_error(4, err), - None => tx.assert_ok() - } - } - - pub fn slash( - &mut self, - slasher: &Address, - staker_address: &Address, - tokens: u64, - expected_err: Option<&str> - ) { - let escrow_address = self.escrow_mock_wrapper.address_ref().clone(); - let tx = self.b_wrapper - .execute_tx(&self.owner, &self.c_wrapper, &rust_biguint!(0), |sc| { - sc.slash( - managed_address!(slasher), - managed_address!(staker_address), - managed_address!(&escrow_address), - managed_biguint!(tokens) - ); - }); - - match expected_err { - Some(err) => tx.assert_error(4, err), - None => tx.assert_ok() - } - } - - pub fn allocate(&mut self, caller: &Address, tokens: u64, expected_err: Option<&str>) { - let escrow_address = self.escrow_mock_wrapper.address_ref().clone(); - let tx = self.b_wrapper - .execute_tx(&caller, &self.c_wrapper, &rust_biguint!(0), |sc| { - sc.allocate( - managed_address!(&escrow_address), - managed_biguint!(tokens) - ); - }); - - match expected_err { - Some(err) => tx.assert_error(4, err), - None => tx.assert_ok() - } - } - - pub fn check_allocation(&mut self, allocation: &AllocationTest) { - let escrow_address = self.escrow_mock_wrapper.address_ref().clone(); - self.b_wrapper - .execute_query(&self.c_wrapper, |sc| { - let managed_allocation = sc.allocations(&managed_address!(&escrow_address)).get(); - assert_eq!(managed_allocation.tokens, managed_biguint!(allocation.tokens)); - }) - .assert_ok(); - } - - pub fn set_escrow_mock_status(&mut self, status: EscrowStatus) { - self.b_wrapper - .execute_tx(&self.owner, &self.escrow_mock_wrapper, &rust_biguint!(0), |sc| { - sc.set_status(status); - }) - .assert_ok(); - } - - pub fn close_allocation(&mut self, caller: &Address, expected_err: Option<&str>) { - let escrow_address = self.escrow_mock_wrapper.address_ref().clone(); - let tx = self.b_wrapper - .execute_tx(caller, &self.c_wrapper, &rust_biguint!(0), |sc| { - sc.close_allocation(managed_address!(&escrow_address)); - }); - - match expected_err { - Some(err) => tx.assert_error(4, err), - None => tx.assert_ok() - } - } - -} \ No newline at end of file diff --git a/packages/core/mx/contracts/staking/tests/test_staking.rs b/packages/core/mx/contracts/staking/tests/test_staking.rs deleted file mode 100644 index ef0437907f..0000000000 --- a/packages/core/mx/contracts/staking/tests/test_staking.rs +++ /dev/null @@ -1,446 +0,0 @@ -mod contract_interactions; -use common_structs::escrow::EscrowStatus; -use contract_interactions::*; -use multiversx_sc::types::Address; -use multiversx_sc_scenario::rust_biguint; - -#[test] -fn test_stake() { - let mut setup = StakingSetup::new( - staking::contract_obj, - escrow_mock::contract_obj, - rewards_pool_mock::contract_obj - ); - - let staker1 = setup.create_address(); - setup.set_staker_balance(3, &staker1); - setup.stake(&staker1, 3_000_000, None); - - let expected_staker = StakerTest { - token_staked: 3_000_000, - tokens_locked: 0, - tokens_locked_until: 0, - tokens_allocated: 0 - }; - - setup.check_staker_entry(&staker1, &expected_staker); -} - -#[test] -fn test_stake_not_minimum_amount() { - let mut setup = StakingSetup::new( - staking::contract_obj, - escrow_mock::contract_obj, - rewards_pool_mock::contract_obj - ); - - let staker1 = setup.create_address(); - setup.set_staker_balance(1, &staker1); - setup.stake(&staker1, 500_000, Some("Total stake is below the minimum threshold")); -} - -#[test] -fn test_stake_twice() { - let mut setup = StakingSetup::new( - staking::contract_obj, - escrow_mock::contract_obj, - rewards_pool_mock::contract_obj - ); - - let staker1 = setup.create_address(); - setup.set_staker_balance(6, &staker1); - setup.stake(&staker1, 3_000_000, None); - - let expected_staker = StakerTest { - token_staked: 3_000_000, - tokens_locked: 0, - tokens_locked_until: 0, - tokens_allocated: 0 - }; - - setup.check_staker_entry(&staker1, &expected_staker); - setup.stake(&staker1, 2_000_000, None); - - let expected_staker2 = StakerTest { - token_staked: 5_000_000, - tokens_locked: 0, - tokens_locked_until: 0, - tokens_allocated: 0 - }; - setup.check_staker_entry(&staker1, &expected_staker2); -} - -#[test] -fn test_unstake_more_than_minimum() { - let mut setup = StakingSetup::new( - staking::contract_obj, - escrow_mock::contract_obj, - rewards_pool_mock::contract_obj - ); - - let staker1 = setup.create_address(); - setup.set_staker_balance(6, &staker1); - setup.stake(&staker1, 3_000_000, None); - setup.unstake(&staker1, 2_700_000, Some("Total stake is below the minimum threshold")); -} - -#[test] -fn test_unstake_random_caller() { - let mut setup = StakingSetup::new( - staking::contract_obj, - escrow_mock::contract_obj, - rewards_pool_mock::contract_obj - ); - let staker1 = setup.create_address(); - setup.set_staker_balance(6, &staker1); - setup.unstake(&staker1, 2_700_000, Some("Caller is not a staker")); -} - -#[test] -fn test_unstake_and_withdraw_unlocked_tokens() { - let mut setup = StakingSetup::new( - staking::contract_obj, - escrow_mock::contract_obj, - rewards_pool_mock::contract_obj - ); - let staker1 = setup.create_address(); - setup.set_staker_balance(3, &staker1); - setup.stake(&staker1, 3_000_000, None); - setup.b_wrapper.check_esdt_balance(&staker1, HMT_TOKEN, &rust_biguint!(0)); - - let expected_staker = StakerTest { - token_staked: 3_000_000, - tokens_locked: 0, - tokens_locked_until: 0, - tokens_allocated: 0 - }; - setup.check_staker_entry(&staker1, &expected_staker); - - // First unstake and lock 1_000_000 token - setup.unstake(&staker1, 1_000_000, None); - let expected_staker = StakerTest { - token_staked: 3_000_000, - tokens_locked: 1_000_000, - tokens_locked_until: 5, - tokens_allocated: 0 - }; - setup.check_staker_entry(&staker1, &expected_staker); - setup.b_wrapper.check_esdt_balance(&staker1, HMT_TOKEN, &rust_biguint!(0)); - setup.b_wrapper.set_block_nonce(10); - - // Second unstake and withdraw 1_000_000 token - setup.unstake(&staker1, 1_000_000, None); - let expected_staker = StakerTest { - token_staked: 2_000_000, - tokens_locked: 1_000_000, - tokens_locked_until: 10 + 5, - tokens_allocated: 0 - }; - setup.check_staker_entry(&staker1, &expected_staker); - setup.b_wrapper.check_esdt_balance(&staker1, HMT_TOKEN, &rust_biguint!(1_000_000)); -} - -#[test] -fn test_stake_and_unstake_all() { - let mut setup = StakingSetup::new( - staking::contract_obj, - escrow_mock::contract_obj, - rewards_pool_mock::contract_obj - ); - let staker1 = setup.create_address(); - setup.set_staker_balance(3, &staker1); - setup.stake(&staker1, 3_000_000, None); - setup.b_wrapper.check_esdt_balance(&staker1, HMT_TOKEN, &rust_biguint!(0)); - - let expected_staker = StakerTest { - token_staked: 3_000_000, - tokens_locked: 0, - tokens_locked_until: 0, - tokens_allocated: 0 - }; - setup.check_staker_entry(&staker1, &expected_staker); - setup.unstake(&staker1, 3_000_000, None); - let expected_staker = StakerTest { - token_staked: 3_000_000, - tokens_locked: 3_000_000, - tokens_locked_until: 5, - tokens_allocated: 0 - }; - setup.check_staker_entry(&staker1, &expected_staker); - - setup.b_wrapper.set_block_nonce(100); - setup.withdraw(&staker1, None); - setup.check_staker_entry_removed(&staker1); -} - -#[test] -fn test_withdraw_with_no_tokens() { - let mut setup = StakingSetup::new( - staking::contract_obj, - escrow_mock::contract_obj, - rewards_pool_mock::contract_obj - ); - let staker1 = setup.create_address(); - setup.set_staker_balance(3, &staker1); - setup.stake(&staker1, 3_000_000, None); - - setup.withdraw(&staker1, Some("Stake has no available tokens for withdrawal")); -} - -#[test] -fn test_withdraw_with_locked_tokens() { - let mut setup = StakingSetup::new( - staking::contract_obj, - escrow_mock::contract_obj, - rewards_pool_mock::contract_obj - ); - let staker1 = setup.create_address(); - setup.set_staker_balance(3, &staker1); - setup.stake(&staker1, 3_000_000, None); - setup.b_wrapper.check_esdt_balance(&staker1, HMT_TOKEN, &rust_biguint!(0)); - - let expected_staker = StakerTest { - token_staked: 3_000_000, - tokens_locked: 0, - tokens_locked_until: 0, - tokens_allocated: 0 - }; - setup.check_staker_entry(&staker1, &expected_staker); - - // First unstake and lock 1_000_000 token - setup.unstake(&staker1, 1_000_000, None); - let expected_staker = StakerTest { - token_staked: 3_000_000, - tokens_locked: 1_000_000, - tokens_locked_until: 5, - tokens_allocated: 0 - }; - setup.check_staker_entry(&staker1, &expected_staker); - setup.b_wrapper.check_esdt_balance(&staker1, HMT_TOKEN, &rust_biguint!(0)); - - setup.withdraw(&staker1, Some("Stake has no available tokens for withdrawal")); -} - -#[test] -fn test_withdraw() { - let mut setup = StakingSetup::new( - staking::contract_obj, - escrow_mock::contract_obj, - rewards_pool_mock::contract_obj - ); - let staker1 = setup.create_address(); - setup.set_staker_balance(3, &staker1); - setup.stake(&staker1, 3_000_000, None); - setup.b_wrapper.check_esdt_balance(&staker1, HMT_TOKEN, &rust_biguint!(0)); - - let expected_staker = StakerTest { - token_staked: 3_000_000, - tokens_locked: 0, - tokens_locked_until: 0, - tokens_allocated: 0 - }; - setup.check_staker_entry(&staker1, &expected_staker); - - // First unstake and lock 1_000_000 token - setup.unstake(&staker1, 1_000_000, None); - let expected_staker = StakerTest { - token_staked: 3_000_000, - tokens_locked: 1_000_000, - tokens_locked_until: 5, - tokens_allocated: 0 - }; - setup.check_staker_entry(&staker1, &expected_staker); - setup.b_wrapper.check_esdt_balance(&staker1, HMT_TOKEN, &rust_biguint!(0)); - setup.b_wrapper.set_block_nonce(10); - - setup.withdraw(&staker1, None); - setup.b_wrapper.check_esdt_balance(&staker1, HMT_TOKEN, &rust_biguint!(1_000_000)); -} - -#[test] -fn test_allocate_with_tokens_locked() { - let mut setup = StakingSetup::new( - staking::contract_obj, - escrow_mock::contract_obj, - rewards_pool_mock::contract_obj - ); - let staker1 = setup.create_address(); - setup.set_staker_balance(3, &staker1); - setup.stake(&staker1, 3_000_000, None); - setup.unstake(&staker1, 2_000_000, None); - - setup.allocate(&staker1, 2_000_000, Some("Insufficient amount of tokens in the stake")); -} - -#[test] -fn test_allocate_success() { - let mut setup = StakingSetup::new( - staking::contract_obj, - escrow_mock::contract_obj, - rewards_pool_mock::contract_obj - ); - let staker1 = setup.create_address(); - setup.set_staker_balance(3, &staker1); - setup.stake(&staker1, 3_000_000, None); - setup.allocate(&staker1, 2_000_000, None); - let expected_staker = StakerTest { - token_staked: 3_000_000, - tokens_locked: 0, - tokens_locked_until: 0, - tokens_allocated: 2_000_000 - }; - setup.check_staker_entry(&staker1, &expected_staker); -} - -#[test] -fn test_allocation_exists() { - let mut setup = StakingSetup::new( - staking::contract_obj, - escrow_mock::contract_obj, - rewards_pool_mock::contract_obj - ); - let staker1 = setup.create_address(); - setup.set_staker_balance(3, &staker1); - setup.stake(&staker1, 3_000_000, None); - setup.allocate(&staker1, 1_000_000, None); - setup.allocate(&staker1, 1_000_000, Some("Allocation already exists")); -} - -#[test] -fn test_slash_not_enough_allocated_tokens() { - let mut setup = StakingSetup::new( - staking::contract_obj, - escrow_mock::contract_obj, - rewards_pool_mock::contract_obj - ); - let staker1 = setup.create_address(); - let slasher = setup.create_address(); - setup.set_staker_balance(3, &staker1); - setup.stake(&staker1, 3_000_000, None); - setup.allocate(&staker1, 1_000_000, None); - setup.slash(&slasher, &staker1, 2_000_000, Some("Allocation must have more tokens than the amount to slash")); -} - -#[test] -fn test_slash_and_add_rewards() { - let mut setup = StakingSetup::new( - staking::contract_obj, - escrow_mock::contract_obj, - rewards_pool_mock::contract_obj - ); - let staker1 = setup.create_address(); - let slasher = setup.create_address(); - setup.set_staker_balance(3, &staker1); - setup.stake(&staker1, 3_000_000, None); - setup.allocate(&staker1, 1_000_000, None); - let expected_staker = StakerTest { - token_staked: 3_000_000, - tokens_locked: 0, - tokens_locked_until: 0, - tokens_allocated: 1_000_000 - }; - setup.check_staker_entry(&staker1, &expected_staker); - let expected_allocation = AllocationTest { - escrow_address: Address::zero(), - staker: staker1.clone(), - tokens: 1_000_000, - created_at: 0, - closed_at: 0, - }; - setup.check_allocation(&expected_allocation); - setup.slash(&slasher, &staker1, 500_000, None); - let expected_staker = StakerTest { - token_staked: 2_500_000, - tokens_locked: 0, - tokens_locked_until: 0, - tokens_allocated: 500_000 - }; - setup.check_staker_entry(&staker1, &expected_staker); - let expected_allocation = AllocationTest { - escrow_address: Address::zero(), - staker: staker1.clone(), - tokens: 500_000, - created_at: 0, - closed_at: 0, - }; - setup.check_allocation(&expected_allocation); -} - -#[test] -fn test_close_allocation_on_incomplete_escrow() { - let mut setup = StakingSetup::new( - staking::contract_obj, - escrow_mock::contract_obj, - rewards_pool_mock::contract_obj - ); - let staker1 = setup.create_address(); - setup.set_staker_balance(3, &staker1); - setup.stake(&staker1, 3_000_000, None); - setup.allocate(&staker1, 1_000_000, None); - - setup.close_allocation(&staker1, Some("Allocation has no completed state")); -} - -#[test] -fn test_close_allocation_too_early() { - let mut setup = StakingSetup::new( - staking::contract_obj, - escrow_mock::contract_obj, - rewards_pool_mock::contract_obj - ); - let staker1 = setup.create_address(); - setup.set_staker_balance(3, &staker1); - setup.stake(&staker1, 3_000_000, None); - setup.allocate(&staker1, 1_000_000, None); - setup.set_escrow_mock_status(EscrowStatus::Complete); - - setup.close_allocation(&staker1, Some("Allocation cannot be closed so early")); -} - -#[test] -fn test_close_allocation() { - let mut setup = StakingSetup::new( - staking::contract_obj, - escrow_mock::contract_obj, - rewards_pool_mock::contract_obj - ); - let staker1 = setup.create_address(); - setup.set_staker_balance(3, &staker1); - setup.stake(&staker1, 3_000_000, None); - setup.allocate(&staker1, 1_000_000, None); - setup.set_escrow_mock_status(EscrowStatus::Complete); - let expected_staker = StakerTest { - token_staked: 3_000_000, - tokens_locked: 0, - tokens_locked_until: 0, - tokens_allocated: 1_000_000 - }; - setup.check_staker_entry(&staker1, &expected_staker); - let expected_allocation = AllocationTest { - escrow_address: Address::zero(), - staker: staker1.clone(), - tokens: 1_000_000, - created_at: 0, - closed_at: 0, - }; - setup.check_allocation(&expected_allocation); - setup.b_wrapper.set_block_nonce(10); - - setup.close_allocation(&staker1, None); - - let expected_staker = StakerTest { - token_staked: 3_000_000, - tokens_locked: 0, - tokens_locked_until: 0, - tokens_allocated: 0 - }; - setup.check_staker_entry(&staker1, &expected_staker); - let expected_allocation = AllocationTest { - escrow_address: Address::zero(), - staker: staker1.clone(), - tokens: 1_000_000, - created_at: 0, - closed_at: 10, - }; - setup.check_allocation(&expected_allocation); -} \ No newline at end of file diff --git a/packages/core/mx/contracts/staking/wasm/Cargo.toml b/packages/core/mx/contracts/staking/wasm/Cargo.toml deleted file mode 100644 index e2688111fa..0000000000 --- a/packages/core/mx/contracts/staking/wasm/Cargo.toml +++ /dev/null @@ -1,27 +0,0 @@ -[package] -name = "staking-wasm" -version = "0.0.0" -edition = "2018" -publish = false -authors = [ "you",] - -[lib] -crate-type = [ "cdylib",] - -[workspace] -members = [ ".",] - -[dev-dependencies] - -[profile.release] -codegen-units = 1 -opt-level = "z" -lto = true -debug = false -panic = "abort" - -[dependencies.staking] -path = ".." - -[dependencies.multiversx-sc-wasm-adapter] -version = "0.45.1" diff --git a/packages/core/mx/contracts/staking/wasm/src/lib.rs b/packages/core/mx/contracts/staking/wasm/src/lib.rs deleted file mode 100644 index 8c71df27c1..0000000000 --- a/packages/core/mx/contracts/staking/wasm/src/lib.rs +++ /dev/null @@ -1,42 +0,0 @@ -// Code generated by the multiversx-sc multi-contract system. DO NOT EDIT. - -//////////////////////////////////////////////////// -////////////////// AUTO-GENERATED ////////////////// -//////////////////////////////////////////////////// - -// Init: 1 -// Endpoints: 18 -// Async Callback (empty): 1 -// Total number of exported functions: 20 - -#![no_std] -#![feature(alloc_error_handler, lang_items)] - -multiversx_sc_wasm_adapter::allocator!(); -multiversx_sc_wasm_adapter::panic_handler!(); - -multiversx_sc_wasm_adapter::endpoints! { - staking - ( - setMinimumStake - setLockPeriod - setRewardsPool - stake - unstake - withdraw - slash - allocate - closeAllocation - hasStake - hasAvailableStake - isAllocation - getAllocationState - getStakedTokens - getStaker - getListOfStakers - getAllocation - getRewardsPoolContractAddress - ) -} - -multiversx_sc_wasm_adapter::empty_callback! {} diff --git a/packages/core/mx/multiversx.workspace.json b/packages/core/mx/multiversx.workspace.json deleted file mode 100644 index 9e26dfeeb6..0000000000 --- a/packages/core/mx/multiversx.workspace.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/packages/core/mx/scripts/build-mx-contracts.sh b/packages/core/mx/scripts/build-mx-contracts.sh deleted file mode 100755 index 3d50b01ce6..0000000000 --- a/packages/core/mx/scripts/build-mx-contracts.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash - -set -e - -CURR_DIR=$(basename $(pwd)) -if [ "$CURR_DIR" != "mx" ]; then - echo "Please run this script from the packages/mx folder" - exit 1 -fi - - -SMART_CONTRACT_JSONS=$(find . -name "multiversx.json") -for smart_contract_json in $SMART_CONTRACT_JSONS -do - smart_contract_folder=$(dirname $smart_contract_json) - echo "> Building $smart_contract_folder" - (set -x; mxpy --verbose contract build $smart_contract_folder) - echo "> Done" - echo "" -done diff --git a/packages/core/mx/scripts/clean-mx-contracts.sh b/packages/core/mx/scripts/clean-mx-contracts.sh deleted file mode 100755 index 5498697f80..0000000000 --- a/packages/core/mx/scripts/clean-mx-contracts.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -set -e -CURR_DIR=$(basename $(pwd)) -if [ "$CURR_DIR" != "mx" ]; then - echo "Please run this script from the packages/mx folder" - exit 1 -fi - - -# cleans all wasm targets -SMART_CONTRACT_JSONS=$(find . -name "multiversx.json") -for smart_contract_json in $SMART_CONTRACT_JSONS -do - smart_contract_folder=$(dirname $smart_contract_json) - echo "" - (set -x; mxpy --verbose contract clean $smart_contract_folder) -done - -# not wasm, but worth cleaning from time to time -cargo clean From 80eaf0c3c167e6c39ce89989f318264a26d26b2d Mon Sep 17 00:00:00 2001 From: portuu3 <61605646+portuu3@users.noreply.github.com> Date: Tue, 16 Jan 2024 15:34:42 +0100 Subject: [PATCH 032/104] Fix endpoint methods and setup function (#1468) --- .../job-launcher/server/src/modules/job/job.controller.ts | 6 +++--- .../apps/job-launcher/server/src/modules/job/job.service.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/apps/job-launcher/server/src/modules/job/job.controller.ts b/packages/apps/job-launcher/server/src/modules/job/job.controller.ts index 1041373988..d1a87010b3 100644 --- a/packages/apps/job-launcher/server/src/modules/job/job.controller.ts +++ b/packages/apps/job-launcher/server/src/modules/job/job.controller.ts @@ -198,7 +198,7 @@ export class JobController { status: 404, description: 'Not Found. Could not find the requested content.', }) - @Post('/cron/create-escrow') + @Get('/cron/create-escrow') public async launchCreateEscrowCronJob(): Promise { await this.jobService.createEscrowCronJob(); return; @@ -217,7 +217,7 @@ export class JobController { status: 404, description: 'Not Found. Could not find the requested content.', }) - @Post('/cron/setup-escrow') + @Get('/cron/setup-escrow') public async launchSetupEscrowCronJob(): Promise { await this.jobService.setupEscrowCronJob(); return; @@ -236,7 +236,7 @@ export class JobController { status: 404, description: 'Not Found. Could not find the requested content.', }) - @Post('/cron/fund-escrow') + @Get('/cron/fund-escrow') public async launchFundEscrowCronJob(): Promise { await this.jobService.fundEscrowCronJob(); return; 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 d19c9e63e3..78c19fb83f 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 @@ -624,7 +624,7 @@ export class JobService { )!; const escrowConfig = { recordingOracle: recordingOracleAddress, - reputationOracle: recordingOracleAddress, + reputationOracle: reputationOracleAddress, exchangeOracle: exchangeOracleAddress, recordingOracleFee: await this.getOracleFee( recordingOracleAddress, From cb818764191fbc06773ce6be685a6efc93b72614 Mon Sep 17 00:00:00 2001 From: Eric Lee <98655210+leric7@users.noreply.github.com> Date: Wed, 17 Jan 2024 07:12:25 +0800 Subject: [PATCH 033/104] use snake case for reputation oracle API req/res (#1470) --- .../server/src/app.module.ts | 7 ++- .../src/common/interceptors/snake-case.ts | 55 +++++++++++++++++++ .../server/src/modules/auth/auth.dto.ts | 4 +- .../reputation/reputation.controller.ts | 4 +- .../src/modules/reputation/reputation.dto.ts | 11 ++-- .../server/src/modules/user/user.dto.ts | 2 +- .../server/src/modules/webhook/webhook.dto.ts | 6 +- 7 files changed, 75 insertions(+), 14 deletions(-) create mode 100644 packages/apps/reputation-oracle/server/src/common/interceptors/snake-case.ts diff --git a/packages/apps/reputation-oracle/server/src/app.module.ts b/packages/apps/reputation-oracle/server/src/app.module.ts index d09283bbdb..9832d3370e 100644 --- a/packages/apps/reputation-oracle/server/src/app.module.ts +++ b/packages/apps/reputation-oracle/server/src/app.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { APP_PIPE } from '@nestjs/core'; +import { APP_INTERCEPTOR, APP_PIPE } from '@nestjs/core'; import { ConfigModule } from '@nestjs/config'; import { ScheduleModule } from '@nestjs/schedule'; import { AppController } from './app.controller'; @@ -13,6 +13,7 @@ import { envValidator } from './common/config'; import { AuthModule } from './modules/auth/auth.module'; import { ServeStaticModule } from '@nestjs/serve-static'; import { join } from 'path'; +import { SnakeCaseInterceptor } from './common/interceptors/snake-case'; @Module({ providers: [ @@ -20,6 +21,10 @@ import { join } from 'path'; provide: APP_PIPE, useClass: HttpValidationPipe, }, + { + provide: APP_INTERCEPTOR, + useClass: SnakeCaseInterceptor, + }, ], imports: [ ScheduleModule.forRoot(), diff --git a/packages/apps/reputation-oracle/server/src/common/interceptors/snake-case.ts b/packages/apps/reputation-oracle/server/src/common/interceptors/snake-case.ts new file mode 100644 index 0000000000..a991c1f0e2 --- /dev/null +++ b/packages/apps/reputation-oracle/server/src/common/interceptors/snake-case.ts @@ -0,0 +1,55 @@ +import { + CallHandler, + ExecutionContext, + Injectable, + NestInterceptor, +} from '@nestjs/common'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; + +@Injectable() +export class SnakeCaseInterceptor implements NestInterceptor { + intercept(context: ExecutionContext, next: CallHandler): Observable { + const request = context.switchToHttp().getRequest(); + + if (request.body) { + request.body = this.transformToCamelCase(request.body); + } + + if (request.query) { + request.query = this.transformToCamelCase(request.query); + } + + return next.handle().pipe(map((data) => this.transformToSnakeCase(data))); + } + + private transformToCamelCase(obj: Record): Record { + const result: Record = {}; + for (const key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + const camelCaseKey = key.replace(/_([a-z])/g, (g) => + g[1].toUpperCase(), + ); + result[camelCaseKey] = obj[key]; + } + } + return result; + } + + private transformToSnakeCase(obj: any): any { + if (Array.isArray(obj)) { + return obj.map((item) => this.transformToSnakeCase(item)); + } else if (typeof obj === 'object' && obj !== null) { + return Object.keys(obj).reduce( + (acc: Record, key: string) => { + const snakeCaseKey = key.replace(/([A-Z])/g, '_$1').toLowerCase(); + acc[snakeCaseKey] = this.transformToSnakeCase(obj[key]); + return acc; + }, + {}, + ); + } else { + return obj; + } + } +} diff --git a/packages/apps/reputation-oracle/server/src/modules/auth/auth.dto.ts b/packages/apps/reputation-oracle/server/src/modules/auth/auth.dto.ts index 2e6a96946f..768d808c76 100644 --- a/packages/apps/reputation-oracle/server/src/modules/auth/auth.dto.ts +++ b/packages/apps/reputation-oracle/server/src/modules/auth/auth.dto.ts @@ -58,11 +58,11 @@ export class VerifyEmailDto { } export class AuthDto { - @ApiProperty() + @ApiProperty({ name: 'refresh_token' }) @IsString() public refreshToken: string; - @ApiProperty() + @ApiProperty({ name: 'access_token' }) @IsString() public accessToken: string; } diff --git a/packages/apps/reputation-oracle/server/src/modules/reputation/reputation.controller.ts b/packages/apps/reputation-oracle/server/src/modules/reputation/reputation.controller.ts index d66b2f9da1..88bd489e75 100644 --- a/packages/apps/reputation-oracle/server/src/modules/reputation/reputation.controller.ts +++ b/packages/apps/reputation-oracle/server/src/modules/reputation/reputation.controller.ts @@ -27,7 +27,7 @@ export class ReputationController { description: 'Endpoint to get all reputations.', }) @ApiQuery({ - name: 'chainId', + name: 'chain_id', description: 'Chain ID for filtering reputations.', type: Number, required: true, @@ -57,7 +57,7 @@ export class ReputationController { required: true, }) @ApiQuery({ - name: 'chainId', + name: 'chain_id', description: 'Chain ID for filtering the reputation.', type: Number, required: true, diff --git a/packages/apps/reputation-oracle/server/src/modules/reputation/reputation.dto.ts b/packages/apps/reputation-oracle/server/src/modules/reputation/reputation.dto.ts index c40691c814..fa5a4b097b 100644 --- a/packages/apps/reputation-oracle/server/src/modules/reputation/reputation.dto.ts +++ b/packages/apps/reputation-oracle/server/src/modules/reputation/reputation.dto.ts @@ -11,7 +11,7 @@ import { ReputationEntityType, ReputationLevel } from '../../common/enums'; import { Transform } from 'class-transformer'; export class ReputationCreateDto { - @ApiProperty() + @ApiProperty({ name: 'chain_id' }) @IsEnum(ChainId) public chainId: ChainId; @@ -19,7 +19,7 @@ export class ReputationCreateDto { @IsString() public address: string; - @ApiProperty() + @ApiProperty({ name: 'reputation_points' }) @IsNumber() public reputationPoints: number; @@ -29,7 +29,7 @@ export class ReputationCreateDto { } export class ReputationUpdateDto { - @ApiProperty() + @ApiProperty({ name: 'reputation_points' }) @IsNumber() public reputationPoints: number; } @@ -37,6 +37,7 @@ export class ReputationUpdateDto { export class ReputationGetAllQueryDto { @ApiPropertyOptional({ enum: ChainId, + name: 'chain_id', }) @IsEnum(ChainId) @IsOptional() @@ -52,14 +53,14 @@ export class ReputationGetParamsDto { } export class ReputationGetQueryDto { - @ApiProperty({ enum: ChainId }) + @ApiProperty({ enum: ChainId, name: 'chain_id' }) @IsEnum(ChainId) @Transform(({ value }) => Number(value)) public chainId: ChainId; } export class ReputationDto { - @ApiProperty({ enum: ChainId }) + @ApiProperty({ enum: ChainId, name: 'chain_id' }) @IsEnum(ChainId) @Transform(({ value }) => Number(value)) chainId: ChainId; 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 f2473a79ea..5fc9229727 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 @@ -22,7 +22,7 @@ export class UserDto extends UserCreateDto { } export class Web3UserCreateDto { - @ApiProperty() + @ApiProperty({ name: 'evm_address' }) @IsString() public evmAddress: string; diff --git a/packages/apps/reputation-oracle/server/src/modules/webhook/webhook.dto.ts b/packages/apps/reputation-oracle/server/src/modules/webhook/webhook.dto.ts index abe35d6bb9..764361e842 100644 --- a/packages/apps/reputation-oracle/server/src/modules/webhook/webhook.dto.ts +++ b/packages/apps/reputation-oracle/server/src/modules/webhook/webhook.dto.ts @@ -15,15 +15,15 @@ import { ChainId } from '@human-protocol/sdk'; import { JobRequestType } from '../../common/enums'; export class WebhookIncomingDto { - @ApiProperty() + @ApiProperty({ name: 'chain_id' }) @IsEnum(ChainId) public chainId: ChainId; - @ApiProperty() + @ApiProperty({ name: 'event_type' }) @IsEnum(EventType) public eventType: EventType; - @ApiProperty() + @ApiProperty({ name: 'escrow_address' }) @IsString() public escrowAddress: string; } From 0a5675c81ef1e54d11e1b0a0e837ee7100276729 Mon Sep 17 00:00:00 2001 From: portuu3 <61605646+portuu3@users.noreply.github.com> Date: Wed, 17 Jan 2024 09:23:31 +0100 Subject: [PATCH 034/104] Make buckets path optional (#1475) --- .../job-launcher/client/src/components/Jobs/Create/schema.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/apps/job-launcher/client/src/components/Jobs/Create/schema.ts b/packages/apps/job-launcher/client/src/components/Jobs/Create/schema.ts index 2c08d25625..ef77d47cf5 100644 --- a/packages/apps/job-launcher/client/src/components/Jobs/Create/schema.ts +++ b/packages/apps/job-launcher/client/src/components/Jobs/Create/schema.ts @@ -6,11 +6,11 @@ export const CvatJobRequestValidationSchema = Yup.object().shape({ dataProvider: Yup.string().required('Data provider is required'), dataRegion: Yup.string().required('Data region is required'), dataBucketName: Yup.string().required('Data bucket name is required'), - dataPath: Yup.string().required('Data path is required'), + dataPath: Yup.string().optional(), gtProvider: Yup.string().required('Ground truth provider is required'), gtRegion: Yup.string().required('Ground truth region is required'), gtBucketName: Yup.string().required('Ground truth bucket name is required'), - gtPath: Yup.string().required('Ground truth path is required'), + gtPath: Yup.string().optional(), userGuide: Yup.string() .required('User Guide URL is required') .url('Invalid URL'), From 574e83adcd6c46abfe2ba074e0d933bc41741d52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20L=C3=B3pez?= <50665615+flopez7@users.noreply.github.com> Date: Wed, 17 Jan 2024 09:46:15 +0100 Subject: [PATCH 035/104] [Job Launcher][Client] Disable job types (#1457) * Disable job types in Mainnet * Update imports order --- .../client/src/components/Jobs/Create/CreateJob.tsx | 12 ++++++------ .../src/providers/CreateJobPageUIProvider.tsx | 13 ++++++++++++- 2 files changed, 18 insertions(+), 7 deletions(-) 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 3314a03819..e4e712ecc4 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 @@ -61,13 +61,13 @@ export const CreateJob = () => { }) } > - - Fortune - + {!IS_MAINNET && ( + Fortune + )} CVAT - - hCaptcha - + {/* {!IS_MAINNET && ( + hCaptcha + )} */} { + const { chain } = useNetwork(); const [step, setStep] = useState(CreateJobStep.FundingMethod); const [payMethod, setPayMethod] = useState(PayMethod.Crypto); const [jobRequest, setJobRequest] = useState({ jobType: IS_MAINNET ? JobType.CVAT : JobType.Fortune, + chainId: + chain?.id && SUPPORTED_CHAIN_IDS.includes(chain?.id) + ? chain?.id + : !IS_MAINNET + ? !chain?.id + ? ChainId.POLYGON_MUMBAI + : undefined + : undefined, }); const goToPrevStep = () => { From 1f108055e1ec425e020a54528199bbcc5543f928 Mon Sep 17 00:00:00 2001 From: portuu3 <61605646+portuu3@users.noreply.github.com> Date: Wed, 17 Jan 2024 10:14:06 +0100 Subject: [PATCH 036/104] update SDK changelog (#1474) --- docs/sdk/changelog.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/docs/sdk/changelog.md b/docs/sdk/changelog.md index cda09090c8..403fadd1e9 100644 --- a/docs/sdk/changelog.md +++ b/docs/sdk/changelog.md @@ -2,17 +2,14 @@ ### Added -- __Add transaction options:__ passing transaction options when a transaction is sent allows the user to set custom parameters like gas limit, gas price, etc. -- __Add gas estimation:__ add gas estimation as mechanism to verify that the transaction won't fail after being sent. Previously the transaction was sent and fees spent, but it failed if it didn't pass all requirements of the contract call. ### Changed -- __KVStore contract addresses:__ new contracts deployed to include setBulk function. +- __Update ethers version:__ update ethers to version 6. +- __Update cancel escrow method:__ return transaction hash and refunded amount. ### Deprecated ### Removed -- __Default value for gas limit in Python:__ gas limit should not be the same for all transactions. -- __Gas price multiplier:__ gas price multiplier has been removed, but now the gas price can be passed as a parameter when the transaction is sent. ### Fixed From f06478f1175ea868e7795f6e0ceb9798ddd5f4c5 Mon Sep 17 00:00:00 2001 From: Eric Lee <98655210+leric7@users.noreply.github.com> Date: Wed, 17 Jan 2024 21:22:41 +0800 Subject: [PATCH 037/104] remove hufi from monorepo (#1476) --- .../hufi/exchange-oracle/server/.env.example | 13 - .../hufi/exchange-oracle/server/.eslintrc.js | 27 - .../hufi/exchange-oracle/server/.gitignore | 36 - .../apps/hufi/exchange-oracle/server/.huskyrc | 5 - .../hufi/exchange-oracle/server/.prettierrc | 4 - .../hufi/exchange-oracle/server/README.md | 54 - .../exchange-oracle/server/docker-compose.yml | 32 - .../exchange-oracle/server/jest.config.ts | 14 - .../hufi/exchange-oracle/server/nest-cli.json | 8 - .../hufi/exchange-oracle/server/package.json | 57 - .../server/src/app.controller.spec.ts | 15 - .../server/src/app.controller.ts | 14 - .../exchange-oracle/server/src/app.module.ts | 28 - .../server/src/common/config/env.ts | 28 - .../server/src/common/config/index.ts | 3 - .../server/src/common/config/networks.ts | 39 - .../server/src/common/config/s3.ts | 13 - .../server/src/common/constant/errors.ts | 7 - .../server/src/common/constant/index.ts | 15 - .../server/src/common/decorators/index.ts | 1 - .../server/src/common/decorators/public.ts | 4 - .../server/src/common/enums/exchange.ts | 6 - .../server/src/common/enums/jobs.ts | 3 - .../server/src/common/enums/role.ts | 5 - .../server/src/common/enums/web3.ts | 4 - .../server/src/common/enums/webhook.ts | 5 - .../server/src/common/guards/index.ts | 1 - .../src/common/guards/signature.auth.spec.ts | 90 - .../src/common/guards/signature.auth.ts | 45 - .../server/src/common/utils/signature.spec.ts | 112 -- .../server/src/common/utils/signature.ts | 47 - .../src/common/validators/ethers.spec.ts | 27 - .../server/src/common/validators/ethers.ts | 33 - .../server/src/common/validators/index.ts | 1 - .../hufi/exchange-oracle/server/src/main.ts | 55 - .../src/modules/job/job.controller.spec.ts | 173 -- .../server/src/modules/job/job.controller.ts | 43 - .../server/src/modules/job/job.dto.ts | 75 - .../server/src/modules/job/job.module.ts | 19 - .../src/modules/job/job.service.spec.ts | 509 ------ .../server/src/modules/job/job.service.ts | 188 -- .../server/src/modules/web3/web3.module.ts | 10 - .../src/modules/web3/web3.service.spec.ts | 48 - .../server/src/modules/web3/web3.service.ts | 55 - .../server/test/app.e2e-spec.ts | 24 - .../exchange-oracle/server/test/constants.ts | 13 - .../exchange-oracle/server/test/jest-e2e.json | 9 - .../server/tsconfig.build.json | 4 - .../hufi/exchange-oracle/server/tsconfig.json | 22 - .../hufi/exchange-oracle/server/vercel.json | 21 - .../hufi/job-launcher/server/.env.example | 50 - .../hufi/job-launcher/server/.eslintrc.js | 26 - .../apps/hufi/job-launcher/server/.gitignore | 41 - .../apps/hufi/job-launcher/server/.huskyrc | 5 - .../apps/hufi/job-launcher/server/.prettierrc | 4 - .../apps/hufi/job-launcher/server/README.md | 117 -- .../job-launcher/server/docker-compose.yml | 49 - .../hufi/job-launcher/server/jest.config.ts | 15 - .../hufi/job-launcher/server/nest-cli.json | 8 - .../hufi/job-launcher/server/package.json | 90 - .../job-launcher/server/src/app.controller.ts | 13 - .../job-launcher/server/src/app.module.ts | 55 - .../server/src/common/config/env.ts | 93 - .../server/src/common/config/index.ts | 2 - .../server/src/common/config/networks.ts | 66 - .../server/src/common/constants/errors.ts | 122 -- .../server/src/common/constants/index.ts | 38 - .../server/src/common/constants/payment.ts | 6 - .../server/src/common/decorators/index.ts | 1 - .../server/src/common/decorators/public.ts | 4 - .../server/src/common/enums/collection.ts | 4 - .../server/src/common/enums/exchange.ts | 6 - .../server/src/common/enums/job.ts | 21 - .../server/src/common/enums/payment.ts | 77 - .../server/src/common/enums/role.ts | 5 - .../server/src/common/enums/user.ts | 10 - .../server/src/common/enums/web3.ts | 4 - .../server/src/common/enums/webhook.ts | 5 - .../server/src/common/guards/index.ts | 2 - .../server/src/common/guards/jwt.auth.ts | 33 - .../src/common/guards/signature.auth.spec.ts | 95 - .../src/common/guards/signature.auth.ts | 45 - .../server/src/common/interfaces/base.ts | 5 - .../server/src/common/interfaces/index.ts | 4 - .../server/src/common/interfaces/job.ts | 16 - .../server/src/common/interfaces/payments.ts | 3 - .../server/src/common/interfaces/user.ts | 15 - .../server/src/common/pipes/index.ts | 1 - .../server/src/common/pipes/validation.ts | 22 - .../server/src/common/types/index.ts | 1 - .../server/src/common/types/request.ts | 3 - .../server/src/common/utils/decimal.ts | 62 - .../server/src/common/utils/index.ts | 77 - .../server/src/common/utils/signature.spec.ts | 113 -- .../server/src/common/utils/signature.ts | 48 - .../server/src/common/utils/status.ts | 15 - .../server/src/common/validators/confirm.ts | 67 - .../server/src/common/validators/index.ts | 2 - .../server/src/common/validators/password.ts | 66 - .../server/src/database/base.entity.ts | 31 - .../server/src/database/database.module.ts | 81 - .../1691485394906-InitialMigration.ts | 214 --- .../1696331060184-AddFailedReasonToJobs.ts | 19 - ...onstraintAndAddToRefundToJobsStatusEnum.ts | 13 - .../server/src/database/typeorm/index.ts | 2 - .../database/typeorm/typeorm-logger.module.ts | 9 - .../typeorm/typeorm-logger.service.ts | 97 -- .../apps/hufi/job-launcher/server/src/main.ts | 78 - .../src/modules/auth/auth.controller.ts | 92 - .../server/src/modules/auth/auth.dto.ts | 78 - .../server/src/modules/auth/auth.entity.ts | 30 - .../server/src/modules/auth/auth.module.ts | 41 - .../src/modules/auth/auth.repository.ts | 64 - .../src/modules/auth/auth.service.spec.ts | 589 ------- .../server/src/modules/auth/auth.service.ts | 266 --- .../server/src/modules/auth/strategy/index.ts | 1 - .../src/modules/auth/strategy/jwt.http.ts | 69 - .../server/src/modules/auth/token.entity.ts | 36 - .../src/modules/auth/token.repository.ts | 31 - .../src/modules/health/health.controller.ts | 31 - .../src/modules/health/health.module.ts | 11 - .../server/src/modules/job/job.controller.ts | 120 -- .../server/src/modules/job/job.dto.ts | 317 ---- .../server/src/modules/job/job.entity.ts | 54 - .../server/src/modules/job/job.module.ts | 26 - .../server/src/modules/job/job.repository.ts | 101 -- .../src/modules/job/job.service.spec.ts | 1523 ----------------- .../server/src/modules/job/job.service.ts | 789 --------- .../job/routing-protocol.service.spec.ts | 60 - .../modules/job/routing-protocol.service.ts | 34 - .../src/modules/payment/payment.controller.ts | 69 - .../server/src/modules/payment/payment.dto.ts | 74 - .../src/modules/payment/payment.entity.ts | 68 - .../src/modules/payment/payment.module.ts | 37 - .../src/modules/payment/payment.repository.ts | 49 - .../modules/payment/payment.service.spec.ts | 702 -------- .../src/modules/payment/payment.service.ts | 300 ---- .../src/modules/sendgrid/sendgrid.module.ts | 13 - .../modules/sendgrid/sendgrid.service.spec.ts | 136 -- .../src/modules/sendgrid/sendgrid.service.ts | 61 - .../src/modules/user/user.controller.ts | 41 - .../server/src/modules/user/user.dto.ts | 31 - .../server/src/modules/user/user.entity.ts | 42 - .../server/src/modules/user/user.module.ts | 21 - .../src/modules/user/user.repository.ts | 66 - .../src/modules/user/user.service.spec.ts | 182 -- .../server/src/modules/user/user.service.ts | 103 -- .../server/src/modules/web3/Web3Service.ts | 57 - .../src/modules/web3/web3.controller.ts | 17 - .../server/src/modules/web3/web3.module.ts | 12 - .../src/modules/web3/web3.service.spec.ts | 72 - .../server/src/modules/web3/web3.service.ts | 57 - .../job-launcher/server/test/app.e2e-spec.ts | 24 - .../job-launcher/server/test/constants.ts | 51 - .../job-launcher/server/test/jest-e2e.json | 9 - .../job-launcher/server/tsconfig.build.json | 4 - .../hufi/job-launcher/server/tsconfig.json | 23 - .../job-launcher/server/typeorm.config.ts | 25 - .../apps/hufi/job-launcher/server/vercel.json | 31 - .../apps/hufi/recording-oracle/.env.example | 18 - .../apps/hufi/recording-oracle/.eslintrc.js | 26 - .../apps/hufi/recording-oracle/.gitignore | 46 - packages/apps/hufi/recording-oracle/.huskyrc | 5 - .../apps/hufi/recording-oracle/.prettierrc | 4 - packages/apps/hufi/recording-oracle/README.md | 33 - .../hufi/recording-oracle/docker-compose.yml | 34 - .../apps/hufi/recording-oracle/jest.config.js | 15 - .../apps/hufi/recording-oracle/nest-cli.json | 8 - .../apps/hufi/recording-oracle/package.json | 47 - .../src/app.controller.spec.ts | 15 - .../recording-oracle/src/app.controller.ts | 13 - .../hufi/recording-oracle/src/app.module.ts | 43 - .../src/common/config/index.ts | 4 - .../recording-oracle/src/common/config/s3.ts | 13 - .../src/common/config/server.ts | 9 - .../src/common/config/validation.ts | 40 - .../src/common/config/web3.ts | 8 - .../src/common/constants/errors.ts | 22 - .../src/common/constants/exchange.ts | 11 - .../src/common/constants/index.ts | 2 - .../src/common/constants/networks.ts | 32 - .../src/common/decorators/index.ts | 1 - .../src/common/decorators/public.ts | 5 - .../src/common/enums/exchange.ts | 6 - .../recording-oracle/src/common/enums/job.ts | 3 - .../recording-oracle/src/common/enums/role.ts | 5 - .../common/filter/global-exceptions.filter.ts | 30 - .../src/common/filter/index.ts | 1 - .../src/common/guards/index.ts | 1 - .../src/common/guards/signature.auth.spec.ts | 94 - .../src/common/guards/signature.auth.ts | 45 - .../src/common/interfaces/liquidity.ts | 7 - .../src/common/interfaces/network.ts | 6 - .../src/common/pipes/index.ts | 1 - .../src/common/pipes/validation.ts | 22 - .../src/common/utils/signature.spec.ts | 115 -- .../src/common/utils/signature.ts | 47 - .../src/common/utils/webhook.ts | 28 - .../src/common/validators/ethers.spec.ts | 27 - .../src/common/validators/ethers.ts | 33 - .../src/common/validators/index.ts | 1 - .../apps/hufi/recording-oracle/src/main.ts | 62 - .../modules/liquidity/liquidity.controller.ts | 22 - .../src/modules/liquidity/liquidity.dto.ts | 86 - .../src/modules/liquidity/liquidity.module.ts | 22 - .../modules/liquidity/liquidity.service.ts | 481 ------ .../src/modules/storage/storage.module.ts | 11 - .../modules/storage/storage.service.spec.ts | 91 - .../src/modules/storage/storage.service.ts | 81 - .../src/modules/web3/web3.module.ts | 11 - .../src/modules/web3/web3.service.spec.ts | 57 - .../src/modules/web3/web3.service.ts | 26 - .../recording-oracle/test/app.e2e-spec.ts | 24 - .../hufi/recording-oracle/test/constants.ts | 21 - .../hufi/recording-oracle/test/jest-e2e.json | 9 - .../hufi/recording-oracle/tsconfig.build.json | 4 - .../apps/hufi/recording-oracle/tsconfig.json | 30 - .../apps/hufi/recording-oracle/vercel.json | 21 - .../reputation-oracle/server/.env.example | 31 - .../reputation-oracle/server/.eslintrc.js | 25 - .../hufi/reputation-oracle/server/.gitignore | 38 - .../hufi/reputation-oracle/server/.prettierrc | 4 - .../hufi/reputation-oracle/server/README.md | 107 -- .../server/docker-compose.yml | 50 - .../reputation-oracle/server/jest.config.ts | 15 - .../reputation-oracle/server/nest-cli.json | 8 - .../reputation-oracle/server/package.json | 92 - .../server/src/app.controller.spec.ts | 15 - .../server/src/app.controller.ts | 14 - .../server/src/app.module.ts | 46 - .../server/src/common/config/env.ts | 62 - .../server/src/common/config/index.ts | 3 - .../server/src/common/config/networks.ts | 39 - .../server/src/common/config/s3.ts | 13 - .../server/src/common/constants/errors.ts | 43 - .../server/src/common/constants/index.ts | 9 - .../server/src/common/decorators/index.ts | 1 - .../server/src/common/decorators/public.ts | 4 - .../server/src/common/enums/collection.ts | 4 - .../server/src/common/enums/exchange.ts | 6 - .../server/src/common/enums/index.ts | 4 - .../server/src/common/enums/job.ts | 3 - .../server/src/common/enums/reputation.ts | 13 - .../server/src/common/enums/role.ts | 5 - .../server/src/common/enums/user.ts | 12 - .../server/src/common/enums/webhook.ts | 15 - .../server/src/common/guards/index.ts | 2 - .../server/src/common/guards/jwt.auth.ts | 33 - .../src/common/guards/signature.auth.spec.ts | 95 - .../src/common/guards/signature.auth.ts | 45 - .../server/src/common/interceptors/index.ts | 1 - .../src/common/interceptors/not-found.ts | 22 - .../server/src/common/interfaces/base.ts | 5 - .../server/src/common/interfaces/index.ts | 3 - .../server/src/common/interfaces/manifest.ts | 23 - .../src/common/interfaces/reputation.ts | 7 - .../server/src/common/interfaces/s3.ts | 4 - .../server/src/common/interfaces/user.ts | 9 - .../server/src/common/pipes/index.ts | 1 - .../server/src/common/pipes/validation.ts | 22 - .../server/src/common/types/index.ts | 1 - .../server/src/common/types/request.ts | 3 - .../server/src/common/utils/index.ts | 20 - .../server/src/common/utils/signature.spec.ts | 113 -- .../server/src/common/utils/signature.ts | 48 - .../server/src/common/validators/confirm.ts | 67 - .../server/src/common/validators/index.ts | 2 - .../server/src/common/validators/password.ts | 66 - .../server/src/database/base.entity.ts | 31 - .../server/src/database/database.module.ts | 62 - .../1698914497531-InitialMigration.ts | 30 - .../server/src/database/typeorm/index.ts | 2 - .../database/typeorm/typeorm-logger.module.ts | 9 - .../typeorm/typeorm-logger.service.ts | 97 -- .../hufi/reputation-oracle/server/src/main.ts | 63 - .../src/modules/health/health.controller.ts | 31 - .../src/modules/health/health.module.ts | 11 - .../reputation/reputation.controller.spec.ts | 103 -- .../reputation/reputation.controller.ts | 36 - .../src/modules/reputation/reputation.dto.ts | 59 - .../modules/reputation/reputation.entity.ts | 23 - .../modules/reputation/reputation.module.ts | 21 - .../reputation/reputation.repository.ts | 68 - .../reputation/reputation.service.spec.ts | 210 --- .../modules/reputation/reputation.service.ts | 132 -- .../server/src/modules/storage/storage.dto.ts | 4 - .../src/modules/storage/storage.module.ts | 11 - .../modules/storage/storage.service.spec.ts | 97 -- .../src/modules/storage/storage.service.ts | 69 - .../server/src/modules/web3/web3.module.ts | 10 - .../src/modules/web3/web3.service.spec.ts | 49 - .../server/src/modules/web3/web3.service.ts | 22 - .../webhook/webhook-incoming.entity.ts | 36 - .../src/modules/webhook/webhook.controller.ts | 43 - .../server/src/modules/webhook/webhook.dto.ts | 94 - .../src/modules/webhook/webhook.module.ts | 25 - .../src/modules/webhook/webhook.repository.ts | 76 - .../src/modules/webhook/webhook.service.ts | 302 ---- .../server/test/app.e2e-spec.ts | 24 - .../server/test/constants.ts | 33 - .../server/test/jest-e2e.json | 9 - .../server/tsconfig.build.json | 4 - .../reputation-oracle/server/tsconfig.json | 23 - .../server/typeorm.config.ts | 25 - .../hufi/reputation-oracle/server/vercel.json | 31 - 305 files changed, 16213 deletions(-) delete mode 100644 packages/apps/hufi/exchange-oracle/server/.env.example delete mode 100644 packages/apps/hufi/exchange-oracle/server/.eslintrc.js delete mode 100644 packages/apps/hufi/exchange-oracle/server/.gitignore delete mode 100644 packages/apps/hufi/exchange-oracle/server/.huskyrc delete mode 100644 packages/apps/hufi/exchange-oracle/server/.prettierrc delete mode 100644 packages/apps/hufi/exchange-oracle/server/README.md delete mode 100644 packages/apps/hufi/exchange-oracle/server/docker-compose.yml delete mode 100644 packages/apps/hufi/exchange-oracle/server/jest.config.ts delete mode 100644 packages/apps/hufi/exchange-oracle/server/nest-cli.json delete mode 100644 packages/apps/hufi/exchange-oracle/server/package.json delete mode 100644 packages/apps/hufi/exchange-oracle/server/src/app.controller.spec.ts delete mode 100644 packages/apps/hufi/exchange-oracle/server/src/app.controller.ts delete mode 100644 packages/apps/hufi/exchange-oracle/server/src/app.module.ts delete mode 100644 packages/apps/hufi/exchange-oracle/server/src/common/config/env.ts delete mode 100644 packages/apps/hufi/exchange-oracle/server/src/common/config/index.ts delete mode 100644 packages/apps/hufi/exchange-oracle/server/src/common/config/networks.ts delete mode 100644 packages/apps/hufi/exchange-oracle/server/src/common/config/s3.ts delete mode 100644 packages/apps/hufi/exchange-oracle/server/src/common/constant/errors.ts delete mode 100644 packages/apps/hufi/exchange-oracle/server/src/common/constant/index.ts delete mode 100644 packages/apps/hufi/exchange-oracle/server/src/common/decorators/index.ts delete mode 100644 packages/apps/hufi/exchange-oracle/server/src/common/decorators/public.ts delete mode 100644 packages/apps/hufi/exchange-oracle/server/src/common/enums/exchange.ts delete mode 100644 packages/apps/hufi/exchange-oracle/server/src/common/enums/jobs.ts delete mode 100644 packages/apps/hufi/exchange-oracle/server/src/common/enums/role.ts delete mode 100644 packages/apps/hufi/exchange-oracle/server/src/common/enums/web3.ts delete mode 100644 packages/apps/hufi/exchange-oracle/server/src/common/enums/webhook.ts delete mode 100644 packages/apps/hufi/exchange-oracle/server/src/common/guards/index.ts delete mode 100644 packages/apps/hufi/exchange-oracle/server/src/common/guards/signature.auth.spec.ts delete mode 100644 packages/apps/hufi/exchange-oracle/server/src/common/guards/signature.auth.ts delete mode 100644 packages/apps/hufi/exchange-oracle/server/src/common/utils/signature.spec.ts delete mode 100644 packages/apps/hufi/exchange-oracle/server/src/common/utils/signature.ts delete mode 100644 packages/apps/hufi/exchange-oracle/server/src/common/validators/ethers.spec.ts delete mode 100644 packages/apps/hufi/exchange-oracle/server/src/common/validators/ethers.ts delete mode 100644 packages/apps/hufi/exchange-oracle/server/src/common/validators/index.ts delete mode 100644 packages/apps/hufi/exchange-oracle/server/src/main.ts delete mode 100644 packages/apps/hufi/exchange-oracle/server/src/modules/job/job.controller.spec.ts delete mode 100644 packages/apps/hufi/exchange-oracle/server/src/modules/job/job.controller.ts delete mode 100644 packages/apps/hufi/exchange-oracle/server/src/modules/job/job.dto.ts delete mode 100644 packages/apps/hufi/exchange-oracle/server/src/modules/job/job.module.ts delete mode 100644 packages/apps/hufi/exchange-oracle/server/src/modules/job/job.service.spec.ts delete mode 100644 packages/apps/hufi/exchange-oracle/server/src/modules/job/job.service.ts delete mode 100644 packages/apps/hufi/exchange-oracle/server/src/modules/web3/web3.module.ts delete mode 100644 packages/apps/hufi/exchange-oracle/server/src/modules/web3/web3.service.spec.ts delete mode 100644 packages/apps/hufi/exchange-oracle/server/src/modules/web3/web3.service.ts delete mode 100644 packages/apps/hufi/exchange-oracle/server/test/app.e2e-spec.ts delete mode 100644 packages/apps/hufi/exchange-oracle/server/test/constants.ts delete mode 100644 packages/apps/hufi/exchange-oracle/server/test/jest-e2e.json delete mode 100644 packages/apps/hufi/exchange-oracle/server/tsconfig.build.json delete mode 100644 packages/apps/hufi/exchange-oracle/server/tsconfig.json delete mode 100644 packages/apps/hufi/exchange-oracle/server/vercel.json delete mode 100644 packages/apps/hufi/job-launcher/server/.env.example delete mode 100644 packages/apps/hufi/job-launcher/server/.eslintrc.js delete mode 100644 packages/apps/hufi/job-launcher/server/.gitignore delete mode 100644 packages/apps/hufi/job-launcher/server/.huskyrc delete mode 100644 packages/apps/hufi/job-launcher/server/.prettierrc delete mode 100644 packages/apps/hufi/job-launcher/server/README.md delete mode 100644 packages/apps/hufi/job-launcher/server/docker-compose.yml delete mode 100644 packages/apps/hufi/job-launcher/server/jest.config.ts delete mode 100644 packages/apps/hufi/job-launcher/server/nest-cli.json delete mode 100644 packages/apps/hufi/job-launcher/server/package.json delete mode 100644 packages/apps/hufi/job-launcher/server/src/app.controller.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/app.module.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/common/config/env.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/common/config/index.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/common/config/networks.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/common/constants/errors.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/common/constants/index.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/common/constants/payment.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/common/decorators/index.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/common/decorators/public.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/common/enums/collection.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/common/enums/exchange.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/common/enums/job.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/common/enums/payment.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/common/enums/role.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/common/enums/user.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/common/enums/web3.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/common/enums/webhook.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/common/guards/index.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/common/guards/jwt.auth.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/common/guards/signature.auth.spec.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/common/guards/signature.auth.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/common/interfaces/base.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/common/interfaces/index.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/common/interfaces/job.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/common/interfaces/payments.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/common/interfaces/user.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/common/pipes/index.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/common/pipes/validation.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/common/types/index.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/common/types/request.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/common/utils/decimal.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/common/utils/index.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/common/utils/signature.spec.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/common/utils/signature.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/common/utils/status.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/common/validators/confirm.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/common/validators/index.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/common/validators/password.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/database/base.entity.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/database/database.module.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/database/migrations/1691485394906-InitialMigration.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/database/migrations/1696331060184-AddFailedReasonToJobs.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/database/migrations/1696413951581-DropUniqueConstraintAndAddToRefundToJobsStatusEnum.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/database/typeorm/index.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/database/typeorm/typeorm-logger.module.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/database/typeorm/typeorm-logger.service.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/main.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/auth/auth.controller.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/auth/auth.dto.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/auth/auth.entity.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/auth/auth.module.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/auth/auth.repository.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/auth/auth.service.spec.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/auth/auth.service.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/auth/strategy/index.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/auth/strategy/jwt.http.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/auth/token.entity.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/auth/token.repository.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/health/health.controller.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/health/health.module.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/job/job.controller.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/job/job.dto.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/job/job.entity.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/job/job.module.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/job/job.repository.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/job/job.service.spec.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/job/job.service.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/job/routing-protocol.service.spec.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/job/routing-protocol.service.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/payment/payment.controller.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/payment/payment.dto.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/payment/payment.entity.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/payment/payment.module.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/payment/payment.repository.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/payment/payment.service.spec.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/payment/payment.service.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/sendgrid/sendgrid.module.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/sendgrid/sendgrid.service.spec.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/sendgrid/sendgrid.service.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/user/user.controller.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/user/user.dto.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/user/user.entity.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/user/user.module.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/user/user.repository.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/user/user.service.spec.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/user/user.service.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/web3/Web3Service.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/web3/web3.controller.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/web3/web3.module.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/web3/web3.service.spec.ts delete mode 100644 packages/apps/hufi/job-launcher/server/src/modules/web3/web3.service.ts delete mode 100644 packages/apps/hufi/job-launcher/server/test/app.e2e-spec.ts delete mode 100644 packages/apps/hufi/job-launcher/server/test/constants.ts delete mode 100644 packages/apps/hufi/job-launcher/server/test/jest-e2e.json delete mode 100644 packages/apps/hufi/job-launcher/server/tsconfig.build.json delete mode 100644 packages/apps/hufi/job-launcher/server/tsconfig.json delete mode 100644 packages/apps/hufi/job-launcher/server/typeorm.config.ts delete mode 100644 packages/apps/hufi/job-launcher/server/vercel.json delete mode 100644 packages/apps/hufi/recording-oracle/.env.example delete mode 100644 packages/apps/hufi/recording-oracle/.eslintrc.js delete mode 100644 packages/apps/hufi/recording-oracle/.gitignore delete mode 100644 packages/apps/hufi/recording-oracle/.huskyrc delete mode 100644 packages/apps/hufi/recording-oracle/.prettierrc delete mode 100644 packages/apps/hufi/recording-oracle/README.md delete mode 100644 packages/apps/hufi/recording-oracle/docker-compose.yml delete mode 100644 packages/apps/hufi/recording-oracle/jest.config.js delete mode 100644 packages/apps/hufi/recording-oracle/nest-cli.json delete mode 100644 packages/apps/hufi/recording-oracle/package.json delete mode 100644 packages/apps/hufi/recording-oracle/src/app.controller.spec.ts delete mode 100644 packages/apps/hufi/recording-oracle/src/app.controller.ts delete mode 100644 packages/apps/hufi/recording-oracle/src/app.module.ts delete mode 100644 packages/apps/hufi/recording-oracle/src/common/config/index.ts delete mode 100644 packages/apps/hufi/recording-oracle/src/common/config/s3.ts delete mode 100644 packages/apps/hufi/recording-oracle/src/common/config/server.ts delete mode 100644 packages/apps/hufi/recording-oracle/src/common/config/validation.ts delete mode 100644 packages/apps/hufi/recording-oracle/src/common/config/web3.ts delete mode 100644 packages/apps/hufi/recording-oracle/src/common/constants/errors.ts delete mode 100644 packages/apps/hufi/recording-oracle/src/common/constants/exchange.ts delete mode 100644 packages/apps/hufi/recording-oracle/src/common/constants/index.ts delete mode 100644 packages/apps/hufi/recording-oracle/src/common/constants/networks.ts delete mode 100644 packages/apps/hufi/recording-oracle/src/common/decorators/index.ts delete mode 100644 packages/apps/hufi/recording-oracle/src/common/decorators/public.ts delete mode 100644 packages/apps/hufi/recording-oracle/src/common/enums/exchange.ts delete mode 100644 packages/apps/hufi/recording-oracle/src/common/enums/job.ts delete mode 100644 packages/apps/hufi/recording-oracle/src/common/enums/role.ts delete mode 100644 packages/apps/hufi/recording-oracle/src/common/filter/global-exceptions.filter.ts delete mode 100644 packages/apps/hufi/recording-oracle/src/common/filter/index.ts delete mode 100644 packages/apps/hufi/recording-oracle/src/common/guards/index.ts delete mode 100644 packages/apps/hufi/recording-oracle/src/common/guards/signature.auth.spec.ts delete mode 100644 packages/apps/hufi/recording-oracle/src/common/guards/signature.auth.ts delete mode 100644 packages/apps/hufi/recording-oracle/src/common/interfaces/liquidity.ts delete mode 100644 packages/apps/hufi/recording-oracle/src/common/interfaces/network.ts delete mode 100644 packages/apps/hufi/recording-oracle/src/common/pipes/index.ts delete mode 100644 packages/apps/hufi/recording-oracle/src/common/pipes/validation.ts delete mode 100644 packages/apps/hufi/recording-oracle/src/common/utils/signature.spec.ts delete mode 100644 packages/apps/hufi/recording-oracle/src/common/utils/signature.ts delete mode 100644 packages/apps/hufi/recording-oracle/src/common/utils/webhook.ts delete mode 100644 packages/apps/hufi/recording-oracle/src/common/validators/ethers.spec.ts delete mode 100644 packages/apps/hufi/recording-oracle/src/common/validators/ethers.ts delete mode 100644 packages/apps/hufi/recording-oracle/src/common/validators/index.ts delete mode 100644 packages/apps/hufi/recording-oracle/src/main.ts delete mode 100644 packages/apps/hufi/recording-oracle/src/modules/liquidity/liquidity.controller.ts delete mode 100644 packages/apps/hufi/recording-oracle/src/modules/liquidity/liquidity.dto.ts delete mode 100644 packages/apps/hufi/recording-oracle/src/modules/liquidity/liquidity.module.ts delete mode 100644 packages/apps/hufi/recording-oracle/src/modules/liquidity/liquidity.service.ts delete mode 100644 packages/apps/hufi/recording-oracle/src/modules/storage/storage.module.ts delete mode 100644 packages/apps/hufi/recording-oracle/src/modules/storage/storage.service.spec.ts delete mode 100644 packages/apps/hufi/recording-oracle/src/modules/storage/storage.service.ts delete mode 100644 packages/apps/hufi/recording-oracle/src/modules/web3/web3.module.ts delete mode 100644 packages/apps/hufi/recording-oracle/src/modules/web3/web3.service.spec.ts delete mode 100644 packages/apps/hufi/recording-oracle/src/modules/web3/web3.service.ts delete mode 100644 packages/apps/hufi/recording-oracle/test/app.e2e-spec.ts delete mode 100644 packages/apps/hufi/recording-oracle/test/constants.ts delete mode 100644 packages/apps/hufi/recording-oracle/test/jest-e2e.json delete mode 100644 packages/apps/hufi/recording-oracle/tsconfig.build.json delete mode 100644 packages/apps/hufi/recording-oracle/tsconfig.json delete mode 100644 packages/apps/hufi/recording-oracle/vercel.json delete mode 100644 packages/apps/hufi/reputation-oracle/server/.env.example delete mode 100644 packages/apps/hufi/reputation-oracle/server/.eslintrc.js delete mode 100644 packages/apps/hufi/reputation-oracle/server/.gitignore delete mode 100644 packages/apps/hufi/reputation-oracle/server/.prettierrc delete mode 100644 packages/apps/hufi/reputation-oracle/server/README.md delete mode 100644 packages/apps/hufi/reputation-oracle/server/docker-compose.yml delete mode 100644 packages/apps/hufi/reputation-oracle/server/jest.config.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/nest-cli.json delete mode 100644 packages/apps/hufi/reputation-oracle/server/package.json delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/app.controller.spec.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/app.controller.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/app.module.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/common/config/env.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/common/config/index.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/common/config/networks.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/common/config/s3.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/common/constants/errors.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/common/constants/index.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/common/decorators/index.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/common/decorators/public.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/common/enums/collection.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/common/enums/exchange.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/common/enums/index.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/common/enums/job.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/common/enums/reputation.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/common/enums/role.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/common/enums/user.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/common/enums/webhook.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/common/guards/index.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/common/guards/jwt.auth.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/common/guards/signature.auth.spec.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/common/guards/signature.auth.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/common/interceptors/index.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/common/interceptors/not-found.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/common/interfaces/base.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/common/interfaces/index.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/common/interfaces/manifest.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/common/interfaces/reputation.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/common/interfaces/s3.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/common/interfaces/user.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/common/pipes/index.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/common/pipes/validation.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/common/types/index.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/common/types/request.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/common/utils/index.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/common/utils/signature.spec.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/common/utils/signature.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/common/validators/confirm.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/common/validators/index.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/common/validators/password.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/database/base.entity.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/database/database.module.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/database/migrations/1698914497531-InitialMigration.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/database/typeorm/index.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/database/typeorm/typeorm-logger.module.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/database/typeorm/typeorm-logger.service.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/main.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/modules/health/health.controller.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/modules/health/health.module.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/modules/reputation/reputation.controller.spec.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/modules/reputation/reputation.controller.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/modules/reputation/reputation.dto.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/modules/reputation/reputation.entity.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/modules/reputation/reputation.module.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/modules/reputation/reputation.repository.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/modules/reputation/reputation.service.spec.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/modules/reputation/reputation.service.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/modules/storage/storage.dto.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/modules/storage/storage.module.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/modules/storage/storage.service.spec.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/modules/storage/storage.service.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/modules/web3/web3.module.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/modules/web3/web3.service.spec.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/modules/web3/web3.service.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/modules/webhook/webhook-incoming.entity.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/modules/webhook/webhook.controller.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/modules/webhook/webhook.dto.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/modules/webhook/webhook.module.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/modules/webhook/webhook.repository.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/src/modules/webhook/webhook.service.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/test/app.e2e-spec.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/test/constants.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/test/jest-e2e.json delete mode 100644 packages/apps/hufi/reputation-oracle/server/tsconfig.build.json delete mode 100644 packages/apps/hufi/reputation-oracle/server/tsconfig.json delete mode 100644 packages/apps/hufi/reputation-oracle/server/typeorm.config.ts delete mode 100644 packages/apps/hufi/reputation-oracle/server/vercel.json diff --git a/packages/apps/hufi/exchange-oracle/server/.env.example b/packages/apps/hufi/exchange-oracle/server/.env.example deleted file mode 100644 index d5a776744a..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/.env.example +++ /dev/null @@ -1,13 +0,0 @@ -# General -NODE_ENV=development -PORT=3001 - -S3_ENDPOINT=localhost -S3_PORT=9000 -S3_ACCESS_KEY=access-key -S3_SECRET_KEY=secret-key -S3_BUCKET=liquidity -S3_USE_SSL=false - -WEB3_ENV=testnet -WEB3_PRIVATE_KEY= diff --git a/packages/apps/hufi/exchange-oracle/server/.eslintrc.js b/packages/apps/hufi/exchange-oracle/server/.eslintrc.js deleted file mode 100644 index 7bbc5f2c77..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/.eslintrc.js +++ /dev/null @@ -1,27 +0,0 @@ -module.exports = { - parser: '@typescript-eslint/parser', - parserOptions: { - project: 'tsconfig.json', - tsconfigRootDir: __dirname, - sourceType: 'module', - }, - plugins: ['@typescript-eslint/eslint-plugin'], - extends: [ - 'plugin:@typescript-eslint/recommended', - 'plugin:prettier/recommended', - ], - root: true, - env: { - node: true, - jest: true, - }, - ignorePatterns: ['.eslintrc.js'], - rules: { - '@typescript-eslint/interface-name-prefix': 'off', - '@typescript-eslint/explicit-function-return-type': 'off', - '@typescript-eslint/explicit-module-boundary-types': 'off', - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-empty-function': 'off', - '@typescript-eslint/no-non-null-assertion': 'off', - }, -}; diff --git a/packages/apps/hufi/exchange-oracle/server/.gitignore b/packages/apps/hufi/exchange-oracle/server/.gitignore deleted file mode 100644 index d9749b200b..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/.gitignore +++ /dev/null @@ -1,36 +0,0 @@ -# compiled output -/dist -/node_modules - -# Logs -logs -*.log -npm-debug.log* -pnpm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* - -# OS -.DS_Store -.env.development - -# Tests -/coverage -/.nyc_output - -# IDEs and editors -/.idea -.project -.classpath -.c9/ -*.launch -.settings/ -*.sublime-workspace - -# IDE - VSCode -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json \ No newline at end of file diff --git a/packages/apps/hufi/exchange-oracle/server/.huskyrc b/packages/apps/hufi/exchange-oracle/server/.huskyrc deleted file mode 100644 index 4d077c8292..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/.huskyrc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "hooks": { - "pre-commit": "lint-staged" - } -} diff --git a/packages/apps/hufi/exchange-oracle/server/.prettierrc b/packages/apps/hufi/exchange-oracle/server/.prettierrc deleted file mode 100644 index dcb72794f5..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/.prettierrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "singleQuote": true, - "trailingComma": "all" -} \ No newline at end of file diff --git a/packages/apps/hufi/exchange-oracle/server/README.md b/packages/apps/hufi/exchange-oracle/server/README.md deleted file mode 100644 index 9884f05c0c..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/README.md +++ /dev/null @@ -1,54 +0,0 @@ -# Exchange Oracle Server - -Exchange Oracle Server is an API built with Nest in TypeScript that allows Human Protocol users to interact with created jobs. It provides endpoints to retrieve a list of jobs, get the job details -and submit a solution using the worker address, escrow addres, chainId, and solution field. - -## Endpoints - -### `GET /jobs` - -Returns a list of similar jobs in JSON format. - -### `POST /jobs/solutions` - -Receives job parameters in the request body and add a new to solution to the job based on address, chainId, and solution fields. Returns a JSON response with a boolean value indicating whether the job exists or not. - -## Installation - -```bash -$ yarn install -``` - -## Running the app - -```bash -# development -$ yarn run start - -# watch mode -$ yarn run start:dev - -# production mode -$ yarn run start:prod -``` - -## Test - -```bash -# unit tests -$ yarn run test - -# e2e tests -$ yarn run test:e2e - -# test coverage -$ yarn run test:cov -``` - -## Contributing - -Contributions are welcome! Please create a pull request or open an issue for any improvements or bug fixes. - -## License - -This project is licensed under the [MIT License](LICENSE). diff --git a/packages/apps/hufi/exchange-oracle/server/docker-compose.yml b/packages/apps/hufi/exchange-oracle/server/docker-compose.yml deleted file mode 100644 index f9dc699676..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/docker-compose.yml +++ /dev/null @@ -1,32 +0,0 @@ -version: '3.7' - -services: - minio: - container_name: minio - image: minio/minio:RELEASE.2022-05-26T05-48-41Z - ports: - - 9001:9001 - - 9000:9000 - environment: - MINIO_ROOT_USER: dev - MINIO_ROOT_PASSWORD: devdevdev - entrypoint: 'sh' - command: - -c "minio server /data --console-address ':9001'" - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"] - interval: 5s - timeout: 5s - retries: 3 - minio-mc: - container_name: minio-mc - image: minio/mc - depends_on: - minio: - condition: service_healthy - entrypoint: > - /bin/sh -c " - /usr/bin/mc config host add myminio http://minio:9000 dev devdevdev; - /usr/bin/mc mb myminio/solution; - /usr/bin/mc anonymous set public myminio/solution; - " diff --git a/packages/apps/hufi/exchange-oracle/server/jest.config.ts b/packages/apps/hufi/exchange-oracle/server/jest.config.ts deleted file mode 100644 index 43fd9c5a46..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/jest.config.ts +++ /dev/null @@ -1,14 +0,0 @@ -module.exports = { - coverageDirectory: '../coverage', - collectCoverageFrom: ['**/*.(t|j)s'], - moduleFileExtensions: ['js', 'json', 'ts'], - rootDir: 'src', - testEnvironment: 'node', - testRegex: '.*\\.spec\\.ts$', - transform: { - '^.+\\.(t|j)s$': 'ts-jest', - }, - moduleNameMapper: { - '^uuid$': require.resolve('uuid'), - }, -}; diff --git a/packages/apps/hufi/exchange-oracle/server/nest-cli.json b/packages/apps/hufi/exchange-oracle/server/nest-cli.json deleted file mode 100644 index f9aa683b1a..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/nest-cli.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/nest-cli", - "collection": "@nestjs/schematics", - "sourceRoot": "src", - "compilerOptions": { - "deleteOutDir": true - } -} diff --git a/packages/apps/hufi/exchange-oracle/server/package.json b/packages/apps/hufi/exchange-oracle/server/package.json deleted file mode 100644 index 8cb599742d..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/package.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "name": "hufi-exchange-oracle-server", - "version": "0.0.1", - "description": "Hufi Exchange Oracle Server", - "author": "Human Protocol", - "private": true, - "license": "UNLICENSED", - "scripts": { - "build": "nest build", - "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", - "start": "nest start", - "start:dev": "NODE_ENV=development nest start --watch", - "start:debug": "nest start --debug --watch", - "start:prod": "node dist/main", - "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", - "test": "jest", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "jest --config ./test/jest-e2e.json", - "vercel-build": "yarn workspace @human-protocol/sdk build" - }, - "dependencies": { - "@human-protocol/sdk": "*", - "@nestjs/axios": "^2.0.0", - "@nestjs/common": "^10.2.7", - "@nestjs/core": "^10.2.8", - "@nestjs/platform-express": "^10.2.6", - "joi": "^17.9.2", - "reflect-metadata": "^0.1.13", - "rxjs": "^7.2.0" - }, - "devDependencies": { - "@golevelup/ts-jest": "^0.4.0", - "@nestjs/cli": "^9.4.3", - "@nestjs/schematics": "^9.2.0", - "@nestjs/testing": "^9.4.3", - "@types/express": "^4.17.13", - "@types/jest": "29.5.11", - "@types/node": "20.10.6", - "@types/supertest": "^6.0.2", - "@typescript-eslint/eslint-plugin": "^5.0.0", - "@typescript-eslint/parser": "^5.0.0", - "eslint": "^8.55.0", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-prettier": "^5.0.0", - "jest": "29.7.0", - "prettier": "^3.1.1", - "source-map-support": "^0.5.20", - "supertest": "^6.3.4", - "ts-jest": "29.1.1", - "ts-loader": "^9.2.3", - "ts-node": "^10.9.2", - "tsconfig-paths": "4.2.0", - "typescript": "^5.0.0" - } -} diff --git a/packages/apps/hufi/exchange-oracle/server/src/app.controller.spec.ts b/packages/apps/hufi/exchange-oracle/server/src/app.controller.spec.ts deleted file mode 100644 index 741a42c324..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/src/app.controller.spec.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { AppController } from './app.controller'; - -describe('AppController', () => { - let appController: AppController; - - beforeEach(() => { - appController = new AppController(); - }); - - describe('Health Check', () => { - it('should return OK', async () => { - expect(await appController.health()).toBe('OK'); - }); - }); -}); diff --git a/packages/apps/hufi/exchange-oracle/server/src/app.controller.ts b/packages/apps/hufi/exchange-oracle/server/src/app.controller.ts deleted file mode 100644 index f82b076b7e..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/src/app.controller.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Controller, Get, Redirect } from '@nestjs/common'; -import { Public } from './common/decorators'; -import { ApiExcludeController } from '@nestjs/swagger'; - -@Controller('/') -@ApiExcludeController() -export class AppController { - @Public() - @Get('/') - @Redirect('/swagger', 301) - public health(): string { - return 'OK'; - } -} diff --git a/packages/apps/hufi/exchange-oracle/server/src/app.module.ts b/packages/apps/hufi/exchange-oracle/server/src/app.module.ts deleted file mode 100644 index 0c3ca8bb21..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/src/app.module.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Module } from '@nestjs/common'; -import { AppController } from './app.controller'; -import { JobModule } from './modules/job/job.module'; -import { ConfigModule } from '@nestjs/config'; -import { envValidator } from './common/config'; -import { join } from 'path'; -import { ServeStaticModule } from '@nestjs/serve-static'; - -@Module({ - imports: [ - JobModule, - ConfigModule.forRoot({ - envFilePath: process.env.NODE_ENV - ? `.env.${process.env.NODE_ENV as string}` - : '.env', - validationSchema: envValidator, - }), - ServeStaticModule.forRoot({ - rootPath: join( - __dirname, - '../../../../../../', - 'node_modules/swagger-ui-dist', - ), - }), - ], - controllers: [AppController], -}) -export class AppModule {} diff --git a/packages/apps/hufi/exchange-oracle/server/src/common/config/env.ts b/packages/apps/hufi/exchange-oracle/server/src/common/config/env.ts deleted file mode 100644 index f532320503..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/src/common/config/env.ts +++ /dev/null @@ -1,28 +0,0 @@ -import * as Joi from 'joi'; - -export const ConfigNames = { - HOST: 'HOST', - PORT: 'PORT', - WEB3_ENV: 'WEB3_ENV', - WEB3_PRIVATE_KEY: 'WEB3_PRIVATE_KEY', - S3_ENDPOINT: 'S3_ENDPOINT', - S3_PORT: 'S3_PORT', - S3_ACCESS_KEY: 'S3_ACCESS_KEY', - S3_SECRET_KEY: 'S3_SECRET_KEY', - S3_BUCKET: 'S3_BUCKET', - S3_USE_SSL: 'S3_USE_SSL', -}; - -export const envValidator = Joi.object({ - HOST: Joi.string().default('localhost'), - PORT: Joi.string().default(3002), - WEB3_ENV: Joi.string().default('testnet'), - WEB3_PRIVATE_KEY: Joi.string().required(), - // S3 - S3_ENDPOINT: Joi.string().default('127.0.0.1'), - S3_PORT: Joi.string().default(9000), - S3_ACCESS_KEY: Joi.string().required(), - S3_SECRET_KEY: Joi.string().required(), - S3_BUCKET: Joi.string().default('solution'), - S3_USE_SSL: Joi.string().default(false), -}); diff --git a/packages/apps/hufi/exchange-oracle/server/src/common/config/index.ts b/packages/apps/hufi/exchange-oracle/server/src/common/config/index.ts deleted file mode 100644 index 20c54299ce..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/src/common/config/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './env'; -export * from './networks'; -export * from './s3'; diff --git a/packages/apps/hufi/exchange-oracle/server/src/common/config/networks.ts b/packages/apps/hufi/exchange-oracle/server/src/common/config/networks.ts deleted file mode 100644 index 779c41ac20..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/src/common/config/networks.ts +++ /dev/null @@ -1,39 +0,0 @@ -export interface NetworkDto { - chainId: number; - rpcUrl: string; -} - -interface NetworkMapDto { - [key: string]: NetworkDto; -} - -export const networkMap: NetworkMapDto = { - polygon: { - chainId: 137, - rpcUrl: - 'https://polygon-mainnet.g.alchemy.com/v2/0Lorh5KRkGl5FsRwy2epTg8fEFFoqUfY', - }, - bsc: { - chainId: 56, - rpcUrl: 'https://bsc-dataseed1.binance.org/', - }, - mumbai: { - chainId: 80001, - rpcUrl: - 'https://polygon-mumbai.g.alchemy.com/v2/vKNSJzJf6SW2sdW-05bgFwoyFxUrMzii', - }, - goerli: { - chainId: 5, - rpcUrl: 'https://goerli.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161', - }, - moonbeam: { - chainId: 1284, - rpcUrl: 'https://rpc.api.moonbeam.network', - }, - bsctest: { - chainId: 97, - rpcUrl: 'https://data-seed-prebsc-1-s1.binance.org:8545/', - }, -}; - -export const networks = Object.values(networkMap).map((network) => network); diff --git a/packages/apps/hufi/exchange-oracle/server/src/common/config/s3.ts b/packages/apps/hufi/exchange-oracle/server/src/common/config/s3.ts deleted file mode 100644 index b2d4e3731b..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/src/common/config/s3.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { ConfigType, registerAs } from '@nestjs/config'; - -export const s3Config = registerAs('s3', () => ({ - endPoint: process.env.S3_ENDPOINT!, - port: +process.env.S3_PORT!, - accessKey: process.env.S3_ACCESS_KEY!, - secretKey: process.env.S3_SECRET_KEY!, - bucket: process.env.S3_BUCKET!, - useSSL: process.env.S3_USE_SSL === 'true', -})); - -export const s3ConfigKey = s3Config.KEY; -export type S3ConfigType = ConfigType; diff --git a/packages/apps/hufi/exchange-oracle/server/src/common/constant/errors.ts b/packages/apps/hufi/exchange-oracle/server/src/common/constant/errors.ts deleted file mode 100644 index 0342ff49cd..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/src/common/constant/errors.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Represents error messages related to web3. - */ -export enum ErrorWeb3 { - InvalidTestnetChainId = 'Invalid chain id provided for the testnet environment', - InvalidMainnetChainId = 'Invalid chain id provided for the mainnet environment', -} diff --git a/packages/apps/hufi/exchange-oracle/server/src/common/constant/index.ts b/packages/apps/hufi/exchange-oracle/server/src/common/constant/index.ts deleted file mode 100644 index cb7262669d..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/src/common/constant/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { ChainId } from "@human-protocol/sdk"; - -export const HEADER_SIGNATURE_KEY = 'human-signature'; -export const ESCROW_FAILED_ENDPOINT = '/job/escrow-failed-webhook'; - -export const TESTNET_CHAIN_IDS = [ - ChainId.BSC_TESTNET, - ChainId.POLYGON_MUMBAI, - ChainId.GOERLI, -]; -export const MAINNET_CHAIN_IDS = [ - ChainId.BSC_MAINNET, - ChainId.POLYGON, - ChainId.MOONBEAM, -]; diff --git a/packages/apps/hufi/exchange-oracle/server/src/common/decorators/index.ts b/packages/apps/hufi/exchange-oracle/server/src/common/decorators/index.ts deleted file mode 100644 index b7e8b7187d..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/src/common/decorators/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './public'; diff --git a/packages/apps/hufi/exchange-oracle/server/src/common/decorators/public.ts b/packages/apps/hufi/exchange-oracle/server/src/common/decorators/public.ts deleted file mode 100644 index a12eaafa09..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/src/common/decorators/public.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { SetMetadata } from '@nestjs/common'; - -export const Public = (): ((target: any, key?: any, descriptor?: any) => any) => - SetMetadata('isPublic', true); diff --git a/packages/apps/hufi/exchange-oracle/server/src/common/enums/exchange.ts b/packages/apps/hufi/exchange-oracle/server/src/common/enums/exchange.ts deleted file mode 100644 index 3ea9700c8f..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/src/common/enums/exchange.ts +++ /dev/null @@ -1,6 +0,0 @@ -export enum Exchange { - UNISWAP_ETHEREUM = 'unispwap-ethereum', - UNISWAP_POLYGON = 'uniswap-polygon', - PANCAKESWAP_BSC = 'pancakeswap-bsc', - BINANCE = 'binance', -} diff --git a/packages/apps/hufi/exchange-oracle/server/src/common/enums/jobs.ts b/packages/apps/hufi/exchange-oracle/server/src/common/enums/jobs.ts deleted file mode 100644 index 9f7022705a..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/src/common/enums/jobs.ts +++ /dev/null @@ -1,3 +0,0 @@ -export enum JobRequestType { - CAMPAIGN = 'CAMPAIGN', -} diff --git a/packages/apps/hufi/exchange-oracle/server/src/common/enums/role.ts b/packages/apps/hufi/exchange-oracle/server/src/common/enums/role.ts deleted file mode 100644 index 1a36187f02..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/src/common/enums/role.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum Role { - JobLaucher = 'job_launcher', - Recording = 'recording', - Reputation = 'reputation', -} diff --git a/packages/apps/hufi/exchange-oracle/server/src/common/enums/web3.ts b/packages/apps/hufi/exchange-oracle/server/src/common/enums/web3.ts deleted file mode 100644 index d2bd10a96c..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/src/common/enums/web3.ts +++ /dev/null @@ -1,4 +0,0 @@ -export enum Web3Env { - TESTNET = 'testnet', - MAINNET = 'mainnet', -} diff --git a/packages/apps/hufi/exchange-oracle/server/src/common/enums/webhook.ts b/packages/apps/hufi/exchange-oracle/server/src/common/enums/webhook.ts deleted file mode 100644 index 4773912237..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/src/common/enums/webhook.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum EventType { - ESCROW_CREATED = 'escrow_created', - ESCROW_CANCELED = 'escrow_canceled', - TASK_CREATION_FAILED = 'task_creation_failed', -} diff --git a/packages/apps/hufi/exchange-oracle/server/src/common/guards/index.ts b/packages/apps/hufi/exchange-oracle/server/src/common/guards/index.ts deleted file mode 100644 index cd53cfcc27..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/src/common/guards/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './signature.auth'; diff --git a/packages/apps/hufi/exchange-oracle/server/src/common/guards/signature.auth.spec.ts b/packages/apps/hufi/exchange-oracle/server/src/common/guards/signature.auth.spec.ts deleted file mode 100644 index 2c89dd5fea..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/src/common/guards/signature.auth.spec.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { ExecutionContext, UnauthorizedException } from '@nestjs/common'; -import { SignatureAuthGuard } from './signature.auth'; -import { verifySignature } from '../utils/signature'; -import { ChainId, EscrowUtils } from '@human-protocol/sdk'; -import { MOCK_ADDRESS } from '../../../test/constants'; -import { Role } from '../enums/role'; - -jest.mock('../../common/utils/signature'); - -jest.mock('@human-protocol/sdk', () => ({ - ...jest.requireActual('@human-protocol/sdk'), - EscrowUtils: { - getEscrow: jest.fn().mockResolvedValue({ - launcher: '0x1234567890123456789012345678901234567891', - recordingOracle: '0x1234567890123456789012345678901234567892', - }), - }, -})); - -describe('SignatureAuthGuard', () => { - let guard: SignatureAuthGuard; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - { - provide: SignatureAuthGuard, - useValue: new SignatureAuthGuard([Role.JobLaucher, Role.Recording]), - }, - ], - }).compile(); - - guard = module.get(SignatureAuthGuard); - }); - - it('should be defined', () => { - expect(guard).toBeDefined(); - }); - - describe('canActivate', () => { - let context: ExecutionContext; - let mockRequest: any; - - beforeEach(() => { - mockRequest = { - switchToHttp: jest.fn().mockReturnThis(), - getRequest: jest.fn().mockReturnThis(), - headers: {}, - body: {}, - originalUrl: '', - }; - context = { - switchToHttp: jest.fn().mockReturnThis(), - getRequest: jest.fn(() => mockRequest), - } as any as ExecutionContext; - }); - - it('should return true if signature is verified', async () => { - mockRequest.headers['header-signature-key'] = 'validSignature'; - mockRequest.body = { - escrowAddress: MOCK_ADDRESS, - chainId: ChainId.LOCALHOST, - }; - (verifySignature as jest.Mock).mockReturnValue(true); - - const result = await guard.canActivate(context as any); - expect(result).toBeTruthy(); - expect(EscrowUtils.getEscrow).toHaveBeenCalledWith( - ChainId.LOCALHOST, - MOCK_ADDRESS, - ); - }); - - it('should throw unauthorized exception if signature is not verified', async () => { - (verifySignature as jest.Mock).mockReturnValue(false); - - await expect(guard.canActivate(context as any)).rejects.toThrow( - UnauthorizedException, - ); - }); - - it('should throw unauthorized exception for unrecognized oracle type', async () => { - mockRequest.originalUrl = '/some/random/path'; - await expect(guard.canActivate(context as any)).rejects.toThrow( - UnauthorizedException, - ); - }); - }); -}); diff --git a/packages/apps/hufi/exchange-oracle/server/src/common/guards/signature.auth.ts b/packages/apps/hufi/exchange-oracle/server/src/common/guards/signature.auth.ts deleted file mode 100644 index e8e57cc4ac..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/src/common/guards/signature.auth.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { - CanActivate, - ExecutionContext, - Injectable, - UnauthorizedException, -} from '@nestjs/common'; -import { verifySignature } from '../utils/signature'; -import { HEADER_SIGNATURE_KEY } from '../constant'; -import { EscrowUtils } from '@human-protocol/sdk'; -import { Role } from '../enums/role'; - -@Injectable() -export class SignatureAuthGuard implements CanActivate { - constructor(private role: Role[]) {} - - public async canActivate(context: ExecutionContext): Promise { - const request = context.switchToHttp().getRequest(); - - const data = request.body; - const signature = request.headers[HEADER_SIGNATURE_KEY]; - const oracleAdresses: string[] = []; - try { - const escrowData = await EscrowUtils.getEscrow( - data.chainId, - data.escrowAddress, - ); - if (this.role.includes(Role.JobLaucher)) - oracleAdresses.push(escrowData.launcher); - if (this.role.includes(Role.Recording)) - oracleAdresses.push(escrowData.recordingOracle!); - if (this.role.includes(Role.Reputation)) - oracleAdresses.push(escrowData.reputationOracle!); - - const isVerified = verifySignature(data, signature, oracleAdresses); - - if (isVerified) { - return true; - } - } catch (error) { - console.error(error); - } - - throw new UnauthorizedException('Unauthorized'); - } -} diff --git a/packages/apps/hufi/exchange-oracle/server/src/common/utils/signature.spec.ts b/packages/apps/hufi/exchange-oracle/server/src/common/utils/signature.spec.ts deleted file mode 100644 index 2a4fb155a0..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/src/common/utils/signature.spec.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { verifySignature, recoverSigner, signMessage } from './signature'; -import { MOCK_ADDRESS, MOCK_PRIVATE_KEY } from '../../../test/constants'; - -jest.doMock('ethers', () => { - return { - utils: { - get verifyMessage() { - return jest.fn((message, signature) => { - if (message === 'valid-message' && signature === 'valid-signature') { - return 'recovered-address'; - } else { - throw new Error('Invalid signature'); - } - }); - }, - }, - }; -}); - -describe('Signature utility', () => { - describe('verifySignature', () => { - it('should return true for valid signature', async () => { - const message = 'Hello, this is a signed message!'; - const signature = await signMessage(message, MOCK_PRIVATE_KEY); - - const result = verifySignature(message, signature, [MOCK_ADDRESS]); - - expect(result).toBe(true); - }); - - it('should throw conflict exception for signature not verified', async () => { - const message = 'Hello, this is a signed message!'; - - const invalidSignature = await signMessage(message, MOCK_PRIVATE_KEY); - const invalidAddress = '0x1234567890123456789012345678901234567892'; - - expect(() => { - verifySignature(message, invalidSignature, [invalidAddress]); - }).toThrow('Signature not verified'); - }); - - it('should throw conflict exception for invalid signature', () => { - const message = 'Hello, this is a signed message!'; - const invalidSignature = '0xInvalidSignature'; - - expect(() => { - verifySignature(message, invalidSignature, [MOCK_ADDRESS]); - }).toThrow('Invalid signature'); - }); - }); - - describe('recoverSigner', () => { - it('should recover the correct signer', async () => { - const message = 'value'; - const signature = await signMessage(message, MOCK_PRIVATE_KEY); - - const result = recoverSigner(message, signature); - - expect(result).toBe(MOCK_ADDRESS); - }); - - it('should throw conflict exception for invalid signature', () => { - const message = 'Hello, this is a signed message!'; - const invalidSignature = '0xInvalidSignature'; - - expect(() => { - recoverSigner(message, invalidSignature); - }).toThrow('Invalid signature'); - }); - - it('should stringify message object if it is not already a string', async () => { - const message = { key: 'value' }; - const signature = await signMessage(message, MOCK_PRIVATE_KEY); - - const recoveredAddress = recoverSigner(message, signature); - - expect(recoveredAddress).toBe(MOCK_ADDRESS); - }); - - it('should not stringify message if it is already a string', async () => { - const message = 'valid message'; - const signature = await signMessage(message, MOCK_PRIVATE_KEY); - - const recoveredAddress = recoverSigner(message, signature); - - expect(recoveredAddress).toBe(MOCK_ADDRESS); - }); - }); - - describe('signMessage', () => { - it('should return a valid signature', async () => { - const message = 'Hello, this is a test message'; - const signature = await signMessage(message, MOCK_PRIVATE_KEY); - - expect(signature).toBeDefined(); - }); - - it('should stringify message object if it is not already a string', async () => { - const message = { key: 'value' }; - const signature = await signMessage(message, MOCK_PRIVATE_KEY); - - expect(signature).toBeDefined(); - }); - - it('should not stringify message if it is already a string', async () => { - const message = 'valid message'; - const signature = await signMessage(message, MOCK_PRIVATE_KEY); - - expect(signature).toBeDefined(); - }); - }); -}); diff --git a/packages/apps/hufi/exchange-oracle/server/src/common/utils/signature.ts b/packages/apps/hufi/exchange-oracle/server/src/common/utils/signature.ts deleted file mode 100644 index 20cc25b726..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/src/common/utils/signature.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { ConflictException } from '@nestjs/common'; -import { ethers } from 'ethers'; - -export function verifySignature( - message: object | string, - signature: string, - addresses: string[], -): boolean { - const signer = recoverSigner(message, signature); - - if ( - !addresses.some((address) => address.toLowerCase() === signer.toLowerCase()) - ) { - throw new ConflictException('Signature not verified'); - } - - return true; -} - -export async function signMessage( - message: object | string, - privateKey: string, -): Promise { - if (typeof message !== 'string') { - message = JSON.stringify(message); - } - - const wallet = new ethers.Wallet(privateKey); - const signature = await wallet.signMessage(message); - - return signature; -} - -export function recoverSigner( - message: object | string, - signature: string, -): string { - if (typeof message !== 'string') { - message = JSON.stringify(message); - } - - try { - return ethers.verifyMessage(message, signature); - } catch (e) { - throw new ConflictException('Invalid signature'); - } -} diff --git a/packages/apps/hufi/exchange-oracle/server/src/common/validators/ethers.spec.ts b/packages/apps/hufi/exchange-oracle/server/src/common/validators/ethers.spec.ts deleted file mode 100644 index 3fb1762b40..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/src/common/validators/ethers.spec.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Validator } from 'class-validator'; -import { IsValidEthereumAddress } from './ethers'; - -const validator = new Validator(); - -describe('IsValidEthereumAddress', () => { - class MyClass { - @IsValidEthereumAddress() - someString: string; - } - - it('should be valid', () => { - const obj = new MyClass(); - obj.someString = '0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc'; - return validator.validate(obj).then((errors) => { - expect(errors.length).toBe(0); - }); - }); - - it('should be invalid', () => { - const obj = new MyClass(); - obj.someString = '0x9965507D1a55bcC2695C58ba16FB37d819B0A4dd'; - return validator.validate(obj).then((errors) => { - expect(errors.length).toBe(1); - }); - }); -}); diff --git a/packages/apps/hufi/exchange-oracle/server/src/common/validators/ethers.ts b/packages/apps/hufi/exchange-oracle/server/src/common/validators/ethers.ts deleted file mode 100644 index 55bc380197..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/src/common/validators/ethers.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { - registerDecorator, - ValidationOptions, - ValidatorConstraint, - ValidatorConstraintInterface, -} from 'class-validator'; -import { ethers } from 'ethers'; - -@ValidatorConstraint({ name: 'IsValidEthereumAddress' }) -@Injectable() -class ValidateEthereumAddress implements ValidatorConstraintInterface { - public validate(value: string): boolean { - return ethers.isAddress(value); - } - - public defaultMessage(): string { - return 'Invalid Ethereum address'; - } -} - -export function IsValidEthereumAddress(validationOptions?: ValidationOptions) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return (object: any, propertyName: string): void => { - registerDecorator({ - name: 'IsValidEthereumAddress', - target: object.constructor, - propertyName, - options: validationOptions, - validator: ValidateEthereumAddress, - }); - }; -} diff --git a/packages/apps/hufi/exchange-oracle/server/src/common/validators/index.ts b/packages/apps/hufi/exchange-oracle/server/src/common/validators/index.ts deleted file mode 100644 index 01dda0ca1a..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/src/common/validators/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './ethers'; diff --git a/packages/apps/hufi/exchange-oracle/server/src/main.ts b/packages/apps/hufi/exchange-oracle/server/src/main.ts deleted file mode 100644 index 32908b099b..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/src/main.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { ConfigService } from '@nestjs/config'; -import { NestFactory } from '@nestjs/core'; -import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; -import { json, urlencoded } from 'body-parser'; -import { useContainer } from 'class-validator'; - -import { AppModule } from './app.module'; -import { ConfigNames } from './common/config'; -import { INestApplication } from '@nestjs/common'; - -async function bootstrap() { - const app = await NestFactory.create(AppModule, { - cors: true, - }); - - const configService: ConfigService = app.get(ConfigService); - - const host = configService.get(ConfigNames.HOST)!; - const port = configService.get(ConfigNames.PORT)!; - - app.enableCors({ - origin: - process.env.NODE_ENV === 'development' || - process.env.NODE_ENV === 'staging' - ? [ - `http://localhost:${port}`, - `http://127.0.0.1:${port}`, - `http://0.0.0.0:${port}`, - `http://${host}:${port}`, - ] - : [`http://${host}:${port}`], - credentials: true, - exposedHeaders: ['Content-Disposition'], - }); - - useContainer(app.select(AppModule), { fallbackOnErrors: true }); - - app.use(json({ limit: '5mb' })); - app.use(urlencoded({ limit: '5mb', extended: true })); - - const config = new DocumentBuilder() - .addBearerAuth() - .setTitle('Hufi Exchange Oracle API') - .setDescription('Swagger Hufi Exchange Oracle API') - .setVersion('1.0') - .build(); - const document = SwaggerModule.createDocument(app, config); - SwaggerModule.setup('swagger', app, document); - - await app.listen(port, host, async () => { - console.info(`API server is running on http://${host}:${port}`); - }); -} - -void bootstrap(); diff --git a/packages/apps/hufi/exchange-oracle/server/src/modules/job/job.controller.spec.ts b/packages/apps/hufi/exchange-oracle/server/src/modules/job/job.controller.spec.ts deleted file mode 100644 index ff5c364e5d..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/src/modules/job/job.controller.spec.ts +++ /dev/null @@ -1,173 +0,0 @@ -import { ConfigService } from '@nestjs/config'; -import { Test } from '@nestjs/testing'; -import { JobController } from './job.controller'; -import { JobService } from './job.service'; -import { InvalidJobDto, JobDetailsDto, SolveJobDto } from './job.dto'; -import { Web3Service } from '../web3/web3.service'; -import { HttpService } from '@nestjs/axios'; -import { of } from 'rxjs'; -import { ConfigModule, registerAs } from '@nestjs/config'; -import { - MOCK_REPUTATION_ORACLE_WEBHOOK_URL, - MOCK_S3_ACCESS_KEY, - MOCK_S3_BUCKET, - MOCK_S3_ENDPOINT, - MOCK_S3_PORT, - MOCK_S3_SECRET_KEY, - MOCK_S3_USE_SSL, - MOCK_SIGNATURE, -} from '../../../test/constants'; -import { StorageService } from '../storage/storage.service'; -import { verifySignature } from '../../common/utils/signature'; - -jest.mock('../../common/utils/signature'); - -describe('JobController', () => { - let jobController: JobController; - let jobService: JobService; - - const chainId = 1; - const escrowAddress = '0x1234567890123456789012345678901234567890'; - const workerAddress = '0x1234567890123456789012345678901234567891'; - - const reputationOracleURL = 'https://example.com/reputationoracle'; - const configServiceMock = { - get: jest.fn().mockReturnValue(reputationOracleURL), - }; - - beforeAll(async () => { - const moduleRef = await Test.createTestingModule({ - imports: [ - ConfigModule.forFeature( - registerAs('s3', () => ({ - accessKey: MOCK_S3_ACCESS_KEY, - secretKey: MOCK_S3_SECRET_KEY, - endPoint: MOCK_S3_ENDPOINT, - port: MOCK_S3_PORT, - useSSL: MOCK_S3_USE_SSL, - bucket: MOCK_S3_BUCKET, - })), - ), - ConfigModule.forFeature( - registerAs('server', () => ({ - reputationOracleWebhookUrl: MOCK_REPUTATION_ORACLE_WEBHOOK_URL, - })), - ), - ], - controllers: [JobController], - providers: [ - JobService, - { - provide: ConfigService, - useValue: configServiceMock, - }, - { - provide: Web3Service, - useValue: { - getSigner: jest.fn().mockReturnValue({ - address: '0x1234567890123456789012345678901234567892', - getNetwork: jest.fn().mockResolvedValue({ chainId: 1 }), - }), - }, - }, - StorageService, - { - provide: HttpService, - useValue: { - post: jest.fn().mockReturnValue(of({ status: 200, data: {} })), - }, - }, - ], - }).compile(); - - jobController = moduleRef.get(JobController); - jobService = moduleRef.get(JobService); - }); - - describe('getDetails', () => { - it('should return job details', async () => { - const expectedDetails: JobDetailsDto = { - escrowAddress, - chainId, - manifest: { - requesterTitle: 'Example Title', - requesterDescription: 'Example Description', - submissionsRequired: 5, - fundAmount: 100, - }, - }; - - jest.spyOn(jobService, 'getDetails').mockResolvedValue(expectedDetails); - - const result = await jobController.getDetails(chainId, escrowAddress); - - expect(result).toBe(expectedDetails); - expect(jobService.getDetails).toHaveBeenCalledWith( - chainId, - escrowAddress, - ); - }); - }); - - describe('getPendingJobs', () => { - it('should return pending jobs', async () => { - const expectedJobs: any[] = [ - '0x1234567890123456789012345678901234567891', - '0x1234567890123456789012345678901234567892', - ]; - - jest.spyOn(jobService, 'getPendingJobs').mockResolvedValue(expectedJobs); - - const result = await jobController.getPendingJobs(chainId, workerAddress); - - expect(result).toBe(expectedJobs); - expect(jobService.getPendingJobs).toHaveBeenCalledWith( - chainId, - workerAddress, - ); - }); - }); - - describe('solveJob', () => { - it('should solve a job', async () => { - const solution = 'job-solution'; - const solveJobDto: SolveJobDto = { - chainId, - escrowAddress, - workerAddress, - solution, - }; - - jest.spyOn(jobService, 'solveJob').mockResolvedValue(); - - await jobController.solveJob(solveJobDto); - - expect(jobService.solveJob).toHaveBeenCalledWith( - solveJobDto.chainId, - solveJobDto.escrowAddress, - solveJobDto.workerAddress, - solveJobDto.solution, - ); - }); - }); - - describe('invalidJobSolution-solution', () => { - it('should mark a job solution as invalid', async () => { - const solveJobDto: InvalidJobDto = { - chainId, - escrowAddress, - workerAddress, - }; - - jest.spyOn(jobService, 'processInvalidJobSolution').mockResolvedValue(); - - (verifySignature as jest.Mock).mockReturnValue(true); - - await jobController.invalidJobSolution(MOCK_SIGNATURE, solveJobDto); - - expect(jobService.processInvalidJobSolution).toHaveBeenCalledWith( - solveJobDto, - ); - }); - }); -}); diff --git a/packages/apps/hufi/exchange-oracle/server/src/modules/job/job.controller.ts b/packages/apps/hufi/exchange-oracle/server/src/modules/job/job.controller.ts deleted file mode 100644 index 3d94497d87..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/src/modules/job/job.controller.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Body, Controller, Get, Param, Post } from '@nestjs/common'; -import { ApiTags } from '@nestjs/swagger'; -import { JobService } from './job.service'; -import { campaignDetailsDto, liquidityRequestDto, liquidityResponseDto } from './job.dto'; - -@ApiTags('Job') -@Controller('job') -export class JobController { - constructor(private readonly jobService: JobService) {} - - @Get('details/:chainId/:escrowAddress') - getDetails( - @Param('chainId') chainId: number, - @Param('escrowAddress') escrowAddress: string, - ): Promise { - return this.jobService.getDetails(chainId, escrowAddress); - } - - @Get('campaigns/:chainId') - getPendingJobs(@Param('chainId') chainId: number): Promise { - return this.jobService.getCampaignsList(chainId); - } - - @Post('liquidity') - getLiquidityScore(@Body() body: liquidityRequestDto): Promise { - return this.jobService.getLiquidityScore( - body.chainId, - body.escrowAddress, - body.liquidityProvider, - false, - ); - } - - @Post('saveLiquidity') - saveScore(@Body() body: liquidityRequestDto): Promise { - return this.jobService.getLiquidityScore( - body.chainId, - body.escrowAddress, - body.liquidityProvider, - true, - ); - } -} diff --git a/packages/apps/hufi/exchange-oracle/server/src/modules/job/job.dto.ts b/packages/apps/hufi/exchange-oracle/server/src/modules/job/job.dto.ts deleted file mode 100644 index 380d1df7a5..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/src/modules/job/job.dto.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { ChainId } from '@human-protocol/sdk'; -import { IsEnum, IsString, IsNumber, IsPositive } from 'class-validator'; -import { IsValidEthereumAddress } from '../../common/validators'; -import { EventType } from '../../common/enums/webhook'; -import { Exchange } from '../../common/enums/exchange'; -import { JobRequestType } from '../../common/enums/jobs'; - -export class CampaignManifestDto { - @IsNumber() - @IsPositive() - startBlock: number; - @IsNumber() - @IsPositive() - endBlock: number; - - @IsString() - @IsEnum(Exchange) - exchangeName: Exchange; - - @IsString() - tokenA: string; - @IsString() - tokenB: string; - - @IsNumber() - @IsPositive() - campaignDuration: number; - - @IsNumber() - @IsPositive() - fundAmount: number; - - @IsString() - requesterDescription: string; // address of launcher - - @IsEnum(JobRequestType) - requestType: JobRequestType; -} - -export class campaignDetailsDto { - escrowAddress: string; - chainId: number; - manifest: CampaignManifestDto; -} - -export class EscrowFailedWebhookDto { - public chain_id: ChainId; - public escrow_address: string; - public event_type: EventType; - public reason: string; -} - -export class liquidityRequestDto { - @ApiProperty() - @IsString() - @IsValidEthereumAddress() - public escrowAddress: string; - - @ApiProperty({ - enum: ChainId, - }) - @IsEnum(ChainId) - public chainId: ChainId; - - @ApiProperty() - @IsString() - @IsValidEthereumAddress() - public liquidityProvider: string; -} - -export class liquidityResponseDto{ - public liquidityScore: string; - public liquidityProvider: string; -} \ No newline at end of file diff --git a/packages/apps/hufi/exchange-oracle/server/src/modules/job/job.module.ts b/packages/apps/hufi/exchange-oracle/server/src/modules/job/job.module.ts deleted file mode 100644 index 7a90b3b562..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/src/modules/job/job.module.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Module } from '@nestjs/common'; -import { ConfigModule } from '@nestjs/config'; -import { JobController } from './job.controller'; -import { JobService } from './job.service'; -import { HttpModule } from '@nestjs/axios'; -import { Web3Module } from '../web3/web3.module'; -import { s3Config } from '../../common/config'; - -@Module({ - imports: [ - ConfigModule.forFeature(s3Config), - ConfigModule, - HttpModule, - Web3Module, - ], - controllers: [JobController], - providers: [JobService], -}) -export class JobModule {} diff --git a/packages/apps/hufi/exchange-oracle/server/src/modules/job/job.service.spec.ts b/packages/apps/hufi/exchange-oracle/server/src/modules/job/job.service.spec.ts deleted file mode 100644 index 45a8ca8d5f..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/src/modules/job/job.service.spec.ts +++ /dev/null @@ -1,509 +0,0 @@ -import { HttpService } from '@nestjs/axios'; -import { ConfigService } from '@nestjs/config'; -import { Test } from '@nestjs/testing'; -import { of } from 'rxjs'; -import { Web3Service } from '../web3/web3.service'; -import { JobService } from './job.service'; -import { - EscrowClient, - StorageClient, - EscrowUtils, - StakingClient, -} from '@human-protocol/sdk'; -import { - MOCK_PRIVATE_KEY, - MOCK_S3_ACCESS_KEY, - MOCK_S3_BUCKET, - MOCK_S3_ENDPOINT, - MOCK_S3_PORT, - MOCK_S3_SECRET_KEY, - MOCK_S3_USE_SSL, -} from '../../../test/constants'; -import { EventType } from '../../common/enums/webhook'; -import { - ESCROW_FAILED_ENDPOINT, - HEADER_SIGNATURE_KEY, -} from '../../common/constant'; -import { signMessage } from '../../common/utils/signature'; -import { ConfigModule, registerAs } from '@nestjs/config'; -import { StorageService } from '../storage/storage.service'; -import { ManifestDto } from './job.dto'; - -jest.mock('@human-protocol/sdk', () => ({ - ...jest.requireActual('@human-protocol/sdk'), - EscrowClient: { - build: jest.fn(), - }, - StakingClient: { - build: jest.fn(), - }, - StorageClient: { - downloadFileFromUrl: jest.fn(), - }, -})); -jest.mock('minio', () => { - class Client { - putObject = jest.fn(); - bucketExists = jest.fn().mockResolvedValue(true); - constructor() { - (this as any).protocol = 'http:'; - (this as any).host = 'localhost'; - (this as any).port = 9000; - } - } - - return { Client }; -}); - -describe('JobService', () => { - let jobService: JobService; - let web3Service: Web3Service; - let httpService: HttpService; - let storageService: StorageService; - - const chainId = 1; - const escrowAddress = '0x1234567890123456789012345678901234567890'; - const workerAddress = '0x1234567890123456789012345678901234567891'; - - const signerMock = { - address: '0x1234567890123456789012345678901234567892', - getNetwork: jest.fn().mockResolvedValue({ chainId: 1 }), - }; - - const reputationOracleURL = 'https://example.com/reputationoracle'; - const configServiceMock: Partial = { - get: jest.fn((key: string) => { - switch (key) { - case 'REPUTATION_ORACLE_URL': - return reputationOracleURL; - case 'WEB3_PRIVATE_KEY': - return MOCK_PRIVATE_KEY; - } - }), - }; - - const httpServicePostMock = jest - .fn() - .mockReturnValue(of({ status: 200, data: {} })); - - beforeAll(async () => { - const moduleRef = await Test.createTestingModule({ - imports: [ - ConfigModule.forFeature( - registerAs('s3', () => ({ - accessKey: MOCK_S3_ACCESS_KEY, - secretKey: MOCK_S3_SECRET_KEY, - endPoint: MOCK_S3_ENDPOINT, - port: MOCK_S3_PORT, - useSSL: MOCK_S3_USE_SSL, - bucket: MOCK_S3_BUCKET, - })), - ), - ], - providers: [ - JobService, - StorageService, - { - provide: ConfigService, - useValue: configServiceMock, - }, - { - provide: Web3Service, - useValue: { - getSigner: jest.fn().mockReturnValue(signerMock), - }, - }, - { - provide: HttpService, - useValue: { - post: httpServicePostMock, - axiosRef: { - get: jest.fn(), - }, - }, - }, - ], - }).compile(); - - jobService = moduleRef.get(JobService); - web3Service = moduleRef.get(Web3Service); - httpService = moduleRef.get(HttpService); - storageService = moduleRef.get(StorageService); - }); - - describe('getDetails', () => { - it('should return job details', async () => { - const manifest: ManifestDto = { - requesterTitle: 'Example Title', - requesterDescription: 'Example Description', - submissionsRequired: 5, - fundAmount: 100, - }; - - httpService.axiosRef.get = jest.fn().mockResolvedValue({ - status: 200, - data: manifest, - }); - - StorageClient.downloadFileFromUrl = jest.fn().mockResolvedValue([]); - - const result = await jobService.getDetails(chainId, escrowAddress); - - expect(result).toEqual({ - escrowAddress, - chainId, - manifest, - }); - }); - - it('should call job launcher webhook if manifest is empty', async () => { - const jobLauncherWebhookUrl = 'https://example.com/reputationoracle'; - (EscrowClient.build as any).mockImplementation(() => ({ - getJobLauncherAddress: jest - .fn() - .mockResolvedValue('0x1234567890123456789012345678901234567893'), - })); - (StakingClient.build as any).mockImplementation(() => ({ - getLeader: jest.fn().mockResolvedValue({ - webhookUrl: jobLauncherWebhookUrl, - }), - })); - - httpService.axiosRef.get = jest.fn().mockResolvedValue({ - status: 200, - data: null, - }); - await expect( - jobService.getDetails(chainId, escrowAddress), - ).rejects.toThrow('Unable to get manifest'); - - const expectedBody = { - escrow_address: escrowAddress, - chain_id: chainId, - event_type: EventType.TASK_CREATION_FAILED, - reason: 'Unable to get manifest', - }; - expect(httpServicePostMock).toHaveBeenCalledWith( - jobLauncherWebhookUrl + ESCROW_FAILED_ENDPOINT, - expectedBody, - { - headers: { - [HEADER_SIGNATURE_KEY]: await signMessage( - expectedBody, - MOCK_PRIVATE_KEY, - ), - }, - }, - ); - }); - - it('should fail if reputation oracle url is empty', async () => { - (configServiceMock as any).get.mockImplementationOnce((key: string) => { - if (key === 'REPUTATION_ORACLE_URL') { - return ''; - } - }); - - await expect( - jobService.getDetails(chainId, escrowAddress), - ).rejects.toThrow('Unable to get Reputation Oracle URL'); - }); - - it('should fail if job has already been completed', async () => { - const manifest: ManifestDto = { - requesterTitle: 'Example Title', - requesterDescription: 'Example Description', - submissionsRequired: 1, - fundAmount: 100, - }; - - httpService.axiosRef.get = jest.fn().mockResolvedValue({ - status: 200, - data: manifest, - }); - - StorageClient.downloadFileFromUrl = jest.fn().mockResolvedValue([ - { - exchangeAddress: '0x1234567890123456789012345678901234567892', - workerAddress: '0x1234567890123456789012345678901234567892', - solution: 'test', - }, - ]); - - await expect( - jobService.getDetails(chainId, escrowAddress), - ).rejects.toThrow('This job has already been completed'); - }); - }); - - describe('getPendingJobs', () => { - it('should return an array of pending jobs', async () => { - EscrowUtils.getEscrows = jest - .fn() - .mockReturnValue([ - { address: '0x1234567890123456789012345678901234567893' }, - { address: '0x1234567890123456789012345678901234567894' }, - ]); - - const result = await jobService.getPendingJobs(chainId, workerAddress); - - expect(result).toEqual([ - '0x1234567890123456789012345678901234567893', - '0x1234567890123456789012345678901234567894', - ]); - expect(web3Service.getSigner).toHaveBeenCalledWith(chainId); - }); - - it('should return an array of pending jobs removing jobs already submitted by worker', async () => { - EscrowUtils.getEscrows = jest - .fn() - .mockReturnValue([ - { address: '0x1234567890123456789012345678901234567893' }, - { address: '0x1234567890123456789012345678901234567894' }, - ]); - - jobService['storage']['0x1234567890123456789012345678901234567893'] = [ - workerAddress, - ]; - - const result = await jobService.getPendingJobs(chainId, workerAddress); - - expect(result).toEqual(['0x1234567890123456789012345678901234567894']); - expect(web3Service.getSigner).toHaveBeenCalledWith(chainId); - }); - - it('should return an empty array if there are no pending jobs', async () => { - EscrowUtils.getEscrows = jest.fn().mockReturnValue([]); - - const result = await jobService.getPendingJobs(chainId, workerAddress); - - expect(result).toEqual([]); - expect(web3Service.getSigner).toHaveBeenCalledWith(chainId); - }); - }); - - describe('solveJob', () => { - it('should solve a job', async () => { - const manifest: ManifestDto = { - requesterTitle: 'Example Title', - requesterDescription: 'Example Description', - submissionsRequired: 5, - fundAmount: 100, - }; - - httpService.axiosRef.get = jest.fn().mockResolvedValue({ - status: 200, - data: manifest, - }); - const solutionsUrl = - 'http://localhost:9000/solution/0x1234567890123456789012345678901234567890-1.json'; - - const recordingOracleURLMock = 'https://example.com/recordingoracle'; - - (EscrowClient.build as any).mockImplementation(() => ({ - getRecordingOracleAddress: jest - .fn() - .mockResolvedValue('0x1234567890123456789012345678901234567893'), - })); - (StakingClient.build as any).mockImplementation(() => ({ - getLeader: jest.fn().mockResolvedValue({ - webhookUrl: recordingOracleURLMock, - }), - })); - - StorageClient.downloadFileFromUrl = jest.fn().mockResolvedValue([]); - - await jobService.solveJob( - chainId, - escrowAddress, - workerAddress, - 'solution', - ); - const expectedBody = { - escrowAddress, - chainId, - solutionsUrl, - }; - expect(web3Service.getSigner).toHaveBeenCalledWith(chainId); - expect(httpServicePostMock).toHaveBeenCalledWith( - recordingOracleURLMock, - expect.objectContaining(expectedBody), - { - headers: { - [HEADER_SIGNATURE_KEY]: await signMessage( - expectedBody, - MOCK_PRIVATE_KEY, - ), - }, - }, - ); - }); - - it('should fail if job has already been completed', async () => { - const manifest: ManifestDto = { - requesterTitle: 'Example Title', - requesterDescription: 'Example Description', - submissionsRequired: 1, - fundAmount: 100, - }; - - httpService.axiosRef.get = jest.fn().mockResolvedValue({ - status: 200, - data: manifest, - }); - - const recordingOracleURLMock = 'https://example.com/recordingoracle'; - - (EscrowClient.build as any).mockImplementation(() => ({ - getRecordingOracleAddress: jest - .fn() - .mockResolvedValue('0x1234567890123456789012345678901234567893'), - })); - (StakingClient.build as any).mockImplementation(() => ({ - getLeader: jest.fn().mockResolvedValue({ - webhookUrl: recordingOracleURLMock, - }), - })); - - StorageClient.downloadFileFromUrl = jest.fn().mockResolvedValue([ - { - exchangeAddress: '0x1234567890123456789012345678901234567892', - workerAddress: '0x1234567890123456789012345678901234567892', - solution: 'test', - }, - ]); - - await expect( - jobService.solveJob(chainId, escrowAddress, workerAddress, 'solution'), - ).rejects.toThrow('This job has already been completed'); - expect(web3Service.getSigner).toHaveBeenCalledWith(chainId); - }); - - it('should fail if the escrow address is invalid', async () => { - const escrowAddress = 'invalid_address'; - const solution = 'job-solution'; - (EscrowClient.build as any).mockImplementation(() => ({ - getRecordingOracleAddress: jest - .fn() - .mockRejectedValue(new Error('Invalid address')), - })); - - await expect( - jobService.solveJob(chainId, escrowAddress, workerAddress, solution), - ).rejects.toThrow('Invalid address'); - expect(web3Service.getSigner).toHaveBeenCalledWith(chainId); - }); - - it('should fail if recording oracle url is empty', async () => { - const solution = 'job-solution'; - - (EscrowClient.build as any).mockImplementation(() => ({ - getRecordingOracleAddress: jest - .fn() - .mockResolvedValue('0x1234567890123456789012345678901234567893'), - })); - (StakingClient.build as any).mockImplementation(() => ({ - getLeader: jest.fn().mockResolvedValue({ - webhookUrl: '', - }), - })); - - await expect( - jobService.solveJob(chainId, escrowAddress, workerAddress, solution), - ).rejects.toThrow('Unable to get Recording Oracle webhook URL'); - expect(web3Service.getSigner).toHaveBeenCalledWith(chainId); - }); - - it('should fail if user has already submitted a solution', async () => { - const solution = 'job-solution'; - - (EscrowClient.build as any).mockImplementation(() => ({ - getRecordingOracleAddress: jest - .fn() - .mockResolvedValue('0x1234567890123456789012345678901234567893'), - })); - (StakingClient.build as any).mockImplementation(() => ({ - getLeader: jest.fn().mockResolvedValue({ - webhookUrl: 'https://example.com/recordingoracle', - }), - })); - - StorageClient.downloadFileFromUrl = jest.fn().mockResolvedValue([ - { - exchangeAddress: '0x1234567890123456789012345678901234567892', - workerAddress: '0x1234567890123456789012345678901234567891', - solution: 'test', - }, - ]); - - jobService['storage'][escrowAddress] = [workerAddress]; - - await expect( - jobService.solveJob(chainId, escrowAddress, workerAddress, solution), - ).rejects.toThrow('User has already submitted a solution'); - expect(web3Service.getSigner).toHaveBeenCalledWith(chainId); - }); - }); - - describe('processInvalidJob', () => { - it('should mark a job solution as invalid', async () => { - const workerAddress = '0x1234567890123456789012345678901234567891'; - const solution = 'test'; - - const jobSolution = { - workerAddress, - solution, - }; - const existingJobSolutions = [jobSolution]; - StorageClient.downloadFileFromUrl = jest - .fn() - .mockResolvedValue(existingJobSolutions); - await jobService.processInvalidJobSolution({ - chainId, - escrowAddress, - workerAddress, - }); - - expect(storageService.minioClient.putObject).toHaveBeenCalledWith( - MOCK_S3_BUCKET, - `${escrowAddress}-${chainId}.json`, - JSON.stringify([ - { - workerAddress, - solution, - error: true, - }, - ]), - - { - 'Content-Type': 'application/json', - 'Cache-Control': 'no-store', - }, - ); - }); - - it('should throw an error if solution was not previously in S3', async () => { - const exchangeAddress = '0x1234567890123456789012345678901234567892'; - const workerAddress = '0x1234567890123456789012345678901234567891'; - - const existingJobSolutions = [ - { - exchangeAddress, - workerAddress: '0x1234567890123456789012345678901234567892', - solution: 'test', - }, - ]; - StorageClient.downloadFileFromUrl = jest - .fn() - .mockResolvedValue(existingJobSolutions); - - await expect( - jobService.processInvalidJobSolution({ - chainId, - escrowAddress, - workerAddress, - }), - ).rejects.toThrow(`Solution not found in Escrow: ${escrowAddress}`); - }); - }); -}); diff --git a/packages/apps/hufi/exchange-oracle/server/src/modules/job/job.service.ts b/packages/apps/hufi/exchange-oracle/server/src/modules/job/job.service.ts deleted file mode 100644 index 3f269425c1..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/src/modules/job/job.service.ts +++ /dev/null @@ -1,188 +0,0 @@ -import { - EscrowClient, - EscrowStatus, - EscrowUtils, - KVStoreClient, - KVStoreKeys, - StakingClient, -} from '@human-protocol/sdk'; -import { HttpService } from '@nestjs/axios'; -import { Inject, Injectable, Logger, NotFoundException } from '@nestjs/common'; -import { ConfigService } from '@nestjs/config'; -import { ConfigNames } from '../../common/config'; -import { - ESCROW_FAILED_ENDPOINT, - HEADER_SIGNATURE_KEY, -} from '../../common/constant'; -import { EventType } from '../../common/enums/webhook'; -import { signMessage } from '../../common/utils/signature'; -import { Web3Service } from '../web3/web3.service'; -import { - EscrowFailedWebhookDto, - CampaignManifestDto, - campaignDetailsDto, - liquidityResponseDto, -} from './job.dto'; -import { StorageClient } from '@human-protocol/sdk'; -import { firstValueFrom } from 'rxjs'; - -@Injectable() -export class JobService { - public readonly logger = new Logger(JobService.name); - - constructor( - private readonly configService: ConfigService, - @Inject(Web3Service) - private readonly web3Service: Web3Service, - private readonly httpService: HttpService, - ) {} - - public async getDetails( - chainId: number, - escrowAddress: string, - ): Promise { - const manifest = await this.getManifest(chainId, escrowAddress); - - return { - escrowAddress, - chainId, - manifest: { - ...manifest, - }, - }; - } - - public async getCampaignsList(chainId: number): Promise { - const escrows = await EscrowUtils.getEscrows({ - status: EscrowStatus.Pending, - networks: [chainId], - exchangeOracle: this.web3Service.signerAddress, - }); - - return escrows.map((escrow) => escrow.address); - } - - private async sendWebhook(url: string, body: any): Promise { - const signedBody = await signMessage( - body, - this.configService.get(ConfigNames.WEB3_PRIVATE_KEY)!, - ); - await this.httpService.post(url, body, { - headers: { [HEADER_SIGNATURE_KEY]: signedBody }, - }); - } - - private async getManifest( - chainId: number, - escrowAddress: string, - ): Promise { - const signer = this.web3Service.getSigner(chainId); - const escrowClient = await EscrowClient.build(signer); - const manifestUrl = await escrowClient.getManifestUrl(escrowAddress); - const manifest: CampaignManifestDto = - await StorageClient.downloadFileFromUrl(manifestUrl); - - if (!manifest) { - const signer = this.web3Service.getSigner(chainId); - const escrowClient = await EscrowClient.build(signer); - const jobLauncherAddress = await escrowClient.getJobLauncherAddress( - escrowAddress, - ); - const stakingClient = await StakingClient.build(signer); - const jobLauncher = await stakingClient.getLeader(jobLauncherAddress); - const jobLauncherWebhookUrl = jobLauncher?.webhookUrl; - - if (!jobLauncherWebhookUrl) { - throw new NotFoundException('Unable to get Job Launcher webhook URL'); - } - - const body: EscrowFailedWebhookDto = { - escrow_address: escrowAddress, - chain_id: chainId, - event_type: EventType.TASK_CREATION_FAILED, - reason: 'Unable to get manifest', - }; - await this.sendWebhook( - jobLauncherWebhookUrl + ESCROW_FAILED_ENDPOINT, - body, - ); - throw new NotFoundException('Unable to get manifest'); - } else return manifest; - } - - // public async getLiquidityScore( - // chainId: number, - // escrowAddress: string, - // liquidityProvider: string, - // ): Promise { - // const signer = this.web3Service.getSigner(chainId); - // const escrowClient = await EscrowClient.build(signer); - // const recordingOracleAddress = await escrowClient.getRecordingOracleAddress( - // escrowAddress, - // ); - // const kvstore = await KVStoreClient.build(signer); - // const recordingOracleWebhookUrl = await kvstore.get( - // recordingOracleAddress, - // KVStoreKeys.webhook_url, - // ); - - // // let [exchangename, chain] = exchange.split("-"); - - // const body = { - // user: user, - // startblock: startblock, - // endblock: endblock, - // token0: token0, - // token1: token1, - // exchange: exchange, - // }; - - // try { - // const liquidityResponse = await this.httpService - // .post(LiquidityScoreoracle + '/job/liquidity', body) - // .toPromise(); - // if (liquidityResponse) { - // return { - // LiquidityScore: liquidityResponse.data.LiquidityScore, - // user: liquidityResponse.data.user, - // }; - // } - // } catch (error) { - // console.error(error); - // throw error; // or handle error appropriately - // } - // } - - public async getLiquidityScore( - chainId: number, - escrowAddress: string, - liquidityProvider: string, - save: boolean, - ): Promise { - const signer = this.web3Service.getSigner(chainId); - const escrowClient = await EscrowClient.build(signer); - const recordingOracleAddress = await escrowClient.getRecordingOracleAddress( - escrowAddress, - ); - - const kvstore = await KVStoreClient.build(signer); - const recordingOracleWebhookUrl = await kvstore.get( - recordingOracleAddress, - KVStoreKeys.webhookUrl, - ); - - if (!recordingOracleWebhookUrl) - throw new NotFoundException('Unable to get Recording Oracle webhook URL'); - - const response = await firstValueFrom( - this.httpService.post(recordingOracleWebhookUrl, { - escrowAddress, - chainId, - liquidityProvider, - save, - }), - ); - - return response.data as liquidityResponseDto; - } -} diff --git a/packages/apps/hufi/exchange-oracle/server/src/modules/web3/web3.module.ts b/packages/apps/hufi/exchange-oracle/server/src/modules/web3/web3.module.ts deleted file mode 100644 index e9e7918a93..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/src/modules/web3/web3.module.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Module } from '@nestjs/common'; -import { Web3Service } from './web3.service'; -import { ConfigModule } from '@nestjs/config'; - -@Module({ - imports: [ConfigModule], - providers: [Web3Service], - exports: [Web3Service], -}) -export class Web3Module {} diff --git a/packages/apps/hufi/exchange-oracle/server/src/modules/web3/web3.service.spec.ts b/packages/apps/hufi/exchange-oracle/server/src/modules/web3/web3.service.spec.ts deleted file mode 100644 index bb8aaa99bf..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/src/modules/web3/web3.service.spec.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { ConfigService } from '@nestjs/config'; -import { Test } from '@nestjs/testing'; -import { networkMap } from '../../common/config'; -import { Web3Service } from './web3.service'; - -describe('Web3Service', () => { - let web3Service: Web3Service; - const privateKey = - '5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a'; - - beforeAll(async () => { - const configServiceMock = { - get: jest.fn().mockReturnValue(privateKey), - }; - - const moduleRef = await Test.createTestingModule({ - providers: [ - Web3Service, - { - provide: ConfigService, - useValue: configServiceMock, - }, - ], - }).compile(); - - web3Service = moduleRef.get(Web3Service); - }); - - describe('getSigner', () => { - it('should return the signer for the specified chainId', async () => { - for (const networkKey of Object.keys(networkMap)) { - // Iterate through the networkMap to test each chainId - const network = networkMap[networkKey]; - - const signer = web3Service.getSigner(network.chainId); - expect(signer).toBeDefined(); - } - }); - - it('should return undefined if chainId is not configured', () => { - const chainId = 1; - - const signer = web3Service.getSigner(chainId); - - expect(signer).toBeUndefined(); - }); - }); -}); diff --git a/packages/apps/hufi/exchange-oracle/server/src/modules/web3/web3.service.ts b/packages/apps/hufi/exchange-oracle/server/src/modules/web3/web3.service.ts deleted file mode 100644 index d931380f52..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/src/modules/web3/web3.service.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { BadRequestException, Injectable, Logger } from '@nestjs/common'; -import { ConfigService } from '@nestjs/config'; -import { Wallet, ethers } from 'ethers'; -import { ConfigNames, networks } from '../../common/config'; -import { ChainId } from '@human-protocol/sdk'; -import { MAINNET_CHAIN_IDS, TESTNET_CHAIN_IDS } from '../../common/constant'; -import { Web3Env } from '../../common/enums/web3'; -import { ErrorWeb3 } from '../../common/constant/errors'; - -@Injectable() -export class Web3Service { - private signers: { [key: number]: Wallet } = {}; - public readonly logger = new Logger(Web3Service.name); - public readonly signerAddress: string; - - constructor(private readonly configService: ConfigService) { - const privateKey = this.configService.get(ConfigNames.WEB3_PRIVATE_KEY); - const validChains = this.getValidChains(); - const validNetworks = networks.filter((network) => - validChains.includes(network.chainId), - ); - for (const network of validNetworks) { - const provider = new ethers.JsonRpcProvider(network.rpcUrl); - this.signers[network.chainId] = new Wallet(privateKey, provider); - } - this.signerAddress = this.signers[validChains[0]].address; - } - public getSigner(chainId: number): Wallet { - this.validateChainId(chainId); - return this.signers[chainId]; - } - - public validateChainId(chainId: number): void { - const currentWeb3Env = this.configService.get(ConfigNames.WEB3_ENV); - const validChainIds = this.getValidChains(); - if (!validChainIds.includes(Number(chainId))) { - const errorType = - currentWeb3Env === Web3Env.MAINNET - ? ErrorWeb3.InvalidMainnetChainId - : ErrorWeb3.InvalidTestnetChainId; - this.logger.log(errorType, Web3Service.name); - throw new BadRequestException(errorType); - } - } - - public getValidChains(): ChainId[] { - const currentWeb3Env = this.configService.get(ConfigNames.WEB3_ENV); - const validChainIds = - currentWeb3Env === Web3Env.MAINNET - ? MAINNET_CHAIN_IDS - : TESTNET_CHAIN_IDS; - - return validChainIds; - } -} diff --git a/packages/apps/hufi/exchange-oracle/server/test/app.e2e-spec.ts b/packages/apps/hufi/exchange-oracle/server/test/app.e2e-spec.ts deleted file mode 100644 index 50cda62332..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/test/app.e2e-spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { INestApplication } from '@nestjs/common'; -import * as request from 'supertest'; -import { AppModule } from './../src/app.module'; - -describe('AppController (e2e)', () => { - let app: INestApplication; - - beforeEach(async () => { - const moduleFixture: TestingModule = await Test.createTestingModule({ - imports: [AppModule], - }).compile(); - - app = moduleFixture.createNestApplication(); - await app.init(); - }); - - it('/ (GET)', () => { - return request(app.getHttpServer()) - .get('/') - .expect(200) - .expect('Hello World!'); - }); -}); diff --git a/packages/apps/hufi/exchange-oracle/server/test/constants.ts b/packages/apps/hufi/exchange-oracle/server/test/constants.ts deleted file mode 100644 index c6138566f7..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/test/constants.ts +++ /dev/null @@ -1,13 +0,0 @@ -export const MOCK_PRIVATE_KEY = - 'd334daf65a631f40549cc7de126d5a0016f32a2d00c49f94563f9737f7135e55'; -export const MOCK_ADDRESS = '0xCf88b3f1992458C2f5a229573c768D0E9F70C44e'; -export const MOCK_SIGNATURE = - '0x1502ec7e795ed9c96b215e80d46d87e26141bc8d41f69ee1bfbc8d8ed9a700db62136da8ee35eadbfd678817342444dff0239508be51c1fae55d62fcdba2867e1b'; - -export const MOCK_S3_ENDPOINT = 'localhost'; -export const MOCK_S3_PORT = 9000; -export const MOCK_S3_ACCESS_KEY = 'access_key'; -export const MOCK_S3_SECRET_KEY = 'secret_key'; -export const MOCK_S3_BUCKET = 'solution'; -export const MOCK_S3_USE_SSL = false; -export const MOCK_REPUTATION_ORACLE_WEBHOOK_URL = 'http://localhost:3000'; diff --git a/packages/apps/hufi/exchange-oracle/server/test/jest-e2e.json b/packages/apps/hufi/exchange-oracle/server/test/jest-e2e.json deleted file mode 100644 index e9d912f3e3..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/test/jest-e2e.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "moduleFileExtensions": ["js", "json", "ts"], - "rootDir": ".", - "testEnvironment": "node", - "testRegex": ".e2e-spec.ts$", - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - } -} diff --git a/packages/apps/hufi/exchange-oracle/server/tsconfig.build.json b/packages/apps/hufi/exchange-oracle/server/tsconfig.build.json deleted file mode 100644 index 64f86c6bd2..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/tsconfig.build.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": "./tsconfig.json", - "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] -} diff --git a/packages/apps/hufi/exchange-oracle/server/tsconfig.json b/packages/apps/hufi/exchange-oracle/server/tsconfig.json deleted file mode 100644 index 55cd3b8548..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/tsconfig.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "declaration": true, - "removeComments": true, - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "allowJs": true, - "target": "es2017", - "sourceMap": true, - "outDir": "./dist", - "baseUrl": "./", - "incremental": true, - "skipLibCheck": true, - "strictNullChecks": true, - "noImplicitAny": true, - "strictBindCallApply": true, - "forceConsistentCasingInFileNames": true, - "noFallthroughCasesInSwitch": true - } -} diff --git a/packages/apps/hufi/exchange-oracle/server/vercel.json b/packages/apps/hufi/exchange-oracle/server/vercel.json deleted file mode 100644 index 3e7126787b..0000000000 --- a/packages/apps/hufi/exchange-oracle/server/vercel.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "version": 2, - "builds": [ - { - "src": "src/main.ts", - "use": "@vercel/node" - } - ], - "routes": [ - { - "src": "/(.*)", - "dest": "src/main.ts", - "headers": { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Methods": "*", - "Access-Control-Allow-Headers": "X-Requested-With,Content-Type,Accept" - } - } - ], - "ignoreCommand": "git diff HEAD^ HEAD --quiet ." -} diff --git a/packages/apps/hufi/job-launcher/server/.env.example b/packages/apps/hufi/job-launcher/server/.env.example deleted file mode 100644 index acac2db6c2..0000000000 --- a/packages/apps/hufi/job-launcher/server/.env.example +++ /dev/null @@ -1,50 +0,0 @@ -# General -NODE_ENV=development -HOST=localhost -PORT=3000 -FE_URL=http://localhost:3001 -SESSION_SECRET=test - -# Database -POSTGRES_HOST=0.0.0.0 -POSTGRES_USER=operator -POSTGRES_PASSWORD=qwerty -POSTGRES_DATABASE=job-launcher-hufi -POSTGRES_PORT=5432 -POSTGRES_SSL=false - -#Web3 -WEB3_ENV=testnet -WEB3_PRIVATE_KEY= -JOB_LAUNCHER_FEE=1 -RECORDING_ORACLE_FEE=1 -REPUTATION_ORACLE_FEE=1 -EXCHANGE_ORACLE_FEE=1 -EXCHANGE_ORACLE_ADDRESS= -EXCHANGE_ORACLE_WEBHOOK_URL= -RECORDING_ORACLE_ADDRESS= -REPUTATION_ORACLE_ADDRESS= - -# Auth -HASH_SECRET=a328af3fc1dad15342cc3d68936008fa -JWT_SECRET=test-secret -JWT_ACCESS_TOKEN_EXPIRES_IN=1d -JWT_REFRESH_TOKEN_EXPIRES_IN=1d - -# S3 -S3_ENDPOINT=localhost -S3_PORT=9000 -S3_ACCESS_KEY=access-key -S3_SECRET_KEY=secret-key -S3_BUCKET=manifests -S3_USE_SSL=false - -# Stripe -STRIPE_SECRET_KEY= -STRIPE_API_VERSION= -STRIPE_APP_NAME= -STRIPE_APP_VERSION= -STRIPE_APP_INFO_URL= - -# Sendgrid -SENDGRID_API_KEY= \ No newline at end of file diff --git a/packages/apps/hufi/job-launcher/server/.eslintrc.js b/packages/apps/hufi/job-launcher/server/.eslintrc.js deleted file mode 100644 index a2d2575861..0000000000 --- a/packages/apps/hufi/job-launcher/server/.eslintrc.js +++ /dev/null @@ -1,26 +0,0 @@ -module.exports = { - parser: '@typescript-eslint/parser', - parserOptions: { - project: 'tsconfig.json', - tsconfigRootDir: __dirname, - sourceType: 'module', - }, - plugins: ['@typescript-eslint/eslint-plugin'], - extends: [ - 'plugin:@typescript-eslint/recommended', - 'plugin:prettier/recommended', - ], - root: true, - env: { - node: true, - jest: true, - }, - ignorePatterns: ['.eslintrc.js'], - rules: { - '@typescript-eslint/interface-name-prefix': 'off', - '@typescript-eslint/explicit-function-return-type': 'off', - '@typescript-eslint/explicit-module-boundary-types': 'off', - '@typescript-eslint/no-explicit-any': 'off', - "@typescript-eslint/no-empty-function": "off", - }, -}; diff --git a/packages/apps/hufi/job-launcher/server/.gitignore b/packages/apps/hufi/job-launcher/server/.gitignore deleted file mode 100644 index feacd3c08b..0000000000 --- a/packages/apps/hufi/job-launcher/server/.gitignore +++ /dev/null @@ -1,41 +0,0 @@ -# compiled output -/dist -/node_modules - -# Logs -logs -*.log -npm-debug.log* -pnpm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* - -# OS -.DS_Store - -# Tests -/coverage -/.nyc_output - -# IDEs and editors -/.idea -.project -.classpath -.c9/ -*.launch -.settings/ -*.sublime-workspace - -# IDE - VSCode -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json - -.env.development -.env.production - -#Postgres Data -db \ No newline at end of file diff --git a/packages/apps/hufi/job-launcher/server/.huskyrc b/packages/apps/hufi/job-launcher/server/.huskyrc deleted file mode 100644 index 4d077c8292..0000000000 --- a/packages/apps/hufi/job-launcher/server/.huskyrc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "hooks": { - "pre-commit": "lint-staged" - } -} diff --git a/packages/apps/hufi/job-launcher/server/.prettierrc b/packages/apps/hufi/job-launcher/server/.prettierrc deleted file mode 100644 index dcb72794f5..0000000000 --- a/packages/apps/hufi/job-launcher/server/.prettierrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "singleQuote": true, - "trailingComma": "all" -} \ No newline at end of file diff --git a/packages/apps/hufi/job-launcher/server/README.md b/packages/apps/hufi/job-launcher/server/README.md deleted file mode 100644 index 33a95456fa..0000000000 --- a/packages/apps/hufi/job-launcher/server/README.md +++ /dev/null @@ -1,117 +0,0 @@ -

- Human Protocol -

- -[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456 -[circleci-url]: https://circleci.com/gh/nestjs/nest -

Job Launcher

-

API that allows Human Protocol users to create jobs.

- -

- - License: MIT - - -

- -## ✨ Demo -First, let's install the dependencies, `yarn` is used as a package manager: -```bash -$ yarn install -``` - -The application needs access to environment variables in order to work correctly, for this, create one of the `.env.` files, depending on the state of your environment: - -```bash -$ export NODE_ENV=development -``` - -Use the `.env.example` file as an example to create a configuration file with certain environment variables: - -```bash -$ cp .env.example .env.development -``` - -Next, the requirement that the application puts forward is to set up a database, for this there are two different options, `manually` or using `docker`. - -### Set up the database manually -First of all, postgres needs to be installed, please see here please see here. - -Then run the following commands in the postgres console to create the database and issue permissions: -```bash -$ CREATE DATABASE "reputation-oracle"; -$ CREATE USER operator WITH ENCRYPTED PASSWORD 'qwerty'; -$ GRANT ALL PRIVILEGES ON DATABASE "reputation-oracle" TO "operator"; -$ \c "reputation-oracle" postgres -$ GRANT CREATE ON SCHEMA public TO operator; -``` -Now we're ready to run the migrations: -```bash -yarn migration:run -``` - -### Set up the database with Docker -To run with docker, you need to enter the following command, which raises the container with postgres and runs the migrations: - -```bash -yarn docker:db:up -``` - -## 🚀 Usage -### Running the app - -```bash -# development -$ yarn run start - -# watch mode -$ yarn run start:dev - -# production mode -$ yarn run start:prod - -# debug mode -$ yarn run start:debug -``` - -### Testing the app - -```bash -# unit tests -$ yarn run test - -# e2e tests -$ yarn run test:e2e - -# test coverage -$ yarn run test:cov -``` - -### Migrations - -```bash -# Create new migration -$ yarn migration:create addNameTable - -# Generate new migration -$ yarn migration:generate addNameTable - -# Revert latest migration -$ yarn migration:revert - -# Run all pending migrations -$ yarn migration:run - -# Show all migrations -$ yarn migration:show -``` - -### Docker - -```bash -# Up a postgres container and run migrations -$ docker:db:up - -# Down postgres container -$ docker:db:down -``` diff --git a/packages/apps/hufi/job-launcher/server/docker-compose.yml b/packages/apps/hufi/job-launcher/server/docker-compose.yml deleted file mode 100644 index c55285aea0..0000000000 --- a/packages/apps/hufi/job-launcher/server/docker-compose.yml +++ /dev/null @@ -1,49 +0,0 @@ -version: '3.8' - -services: - postgres: - image: postgres:latest - restart: always - environment: - - POSTGRES_HOST=${POSTGRES_HOST} - - POSTGRES_USER=${POSTGRES_USER} - - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} - - POSTGRES_DB=${POSTGRES_DATABASE} - - POSTGRES_PORT=${POSTGRES_PORT} - logging: - options: - max-size: 10m - max-file: "3" - ports: - - '${POSTGRES_PORT}:${POSTGRES_PORT}' - # volumes: - # - ./db:/var/lib/postgresql/data - minio: - container_name: minio - image: minio/minio:RELEASE.2022-05-26T05-48-41Z - ports: - - 9001:9001 - - 9000:9000 - environment: - MINIO_ROOT_USER: ${S3_ACCESS_KEY} - MINIO_ROOT_PASSWORD: ${S3_SECRET_KEY} - entrypoint: 'sh' - command: - -c "mkdir -p /data/manifests && minio server /data --console-address ':9001'" - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"] - interval: 5s - timeout: 5s - retries: 3 - minio-mc: - container_name: minio-mc - image: minio/mc - depends_on: - minio: - condition: service_healthy - entrypoint: > - /bin/sh -c " - /usr/bin/mc config host add myminio http://minio:9000 ${S3_ACCESS_KEY} ${S3_SECRET_KEY}; - /usr/bin/mc mb myminio/manifests; - /usr/bin/mc anonymous set public myminio/manifests; - " \ No newline at end of file diff --git a/packages/apps/hufi/job-launcher/server/jest.config.ts b/packages/apps/hufi/job-launcher/server/jest.config.ts deleted file mode 100644 index 1a43206abc..0000000000 --- a/packages/apps/hufi/job-launcher/server/jest.config.ts +++ /dev/null @@ -1,15 +0,0 @@ -module.exports = { - coverageDirectory: '../coverage', - collectCoverageFrom: ['**/*.(t|j)s'], - moduleFileExtensions: ['js', 'json', 'ts'], - rootDir: 'src', - testEnvironment: 'node', - testRegex: '.*\\.spec\\.ts$', - transform: { - '^.+\\.(t|j)s$': 'ts-jest', - }, - moduleNameMapper: { - '^uuid$': require.resolve('uuid'), - '^typeorm$': require.resolve('typeorm'), - }, -}; diff --git a/packages/apps/hufi/job-launcher/server/nest-cli.json b/packages/apps/hufi/job-launcher/server/nest-cli.json deleted file mode 100644 index f9aa683b1a..0000000000 --- a/packages/apps/hufi/job-launcher/server/nest-cli.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/nest-cli", - "collection": "@nestjs/schematics", - "sourceRoot": "src", - "compilerOptions": { - "deleteOutDir": true - } -} diff --git a/packages/apps/hufi/job-launcher/server/package.json b/packages/apps/hufi/job-launcher/server/package.json deleted file mode 100644 index 828aad1d92..0000000000 --- a/packages/apps/hufi/job-launcher/server/package.json +++ /dev/null @@ -1,90 +0,0 @@ -{ - "name": "hufi-job-launcher-server", - "version": "0.0.1", - "description": "Hufi Job Launcher Server", - "author": "Human Protocol", - "private": true, - "license": "UNLICENSED", - "scripts": { - "build": "nest build", - "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", - "start": "nest start", - "start:dev": "nest start --watch", - "start:debug": "nest start --debug --watch", - "start:prod": "node dist/src/main", - "migration:create": "typeorm-ts-node-commonjs migration:create", - "migration:generate": "yarn build && typeorm-ts-node-commonjs migration:generate -p -d typeorm.config.ts", - "migration:revert": "typeorm-ts-node-commonjs migration:revert -d typeorm.config.ts", - "migration:run": "typeorm-ts-node-commonjs migration:run -d typeorm.config.ts", - "migration:show": "typeorm-ts-node-commonjs migration:show -d typeorm.config.ts", - "docker:db:up": "docker-compose up -d postgres && yarn build && yarn migration:run", - "docker:db:down": "docker-compose down", - "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", - "test": "jest", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "jest --config ./test/jest-e2e.json", - "vercel-build": "yarn workspace @human-protocol/sdk build" - }, - "dependencies": { - "@human-protocol/sdk": "*", - "@nestjs/axios": "^2.0.0", - "@nestjs/common": "^10.2.7", - "@nestjs/config": "^3.1.1", - "@nestjs/core": "^10.2.8", - "@nestjs/jwt": "^10.2.0", - "@nestjs/passport": "^10.0.0", - "@nestjs/platform-express": "^10.2.6", - "@nestjs/schedule": "^4.0.0", - "@nestjs/serve-static": "^4.0.0", - "@nestjs/swagger": "^7.1.13", - "@nestjs/terminus": "^10.2.0", - "@nestjs/typeorm": "^10.0.1", - "@sendgrid/mail": "^7.7.0", - "@types/cookie-parser": "^1.4.3", - "@types/express-session": "^1.17.10", - "@types/passport-jwt": "^3.0.10", - "bcrypt": "^5.1.1", - "class-transformer": "^0.5.1", - "cookie-parser": "^1.4.6", - "decimal.js": "^10.4.3", - "express-session": "^1.17.3", - "joi": "^17.9.2", - "nestjs-minio-client": "^2.2.0", - "passport": "^0.6.0", - "passport-jwt": "^4.0.1", - "pg": "8.11.3", - "reflect-metadata": "^0.1.13", - "rxjs": "^7.2.0", - "typeorm": "^0.3.17", - "typeorm-naming-strategies": "^4.1.0", - "zxcvbn": "^4.4.2" - }, - "devDependencies": { - "@golevelup/ts-jest": "^0.4.0", - "@nestjs/cli": "^9.4.3", - "@nestjs/schematics": "^9.2.0", - "@nestjs/testing": "^9.4.3", - "@types/bcrypt": "^5.0.2", - "@types/express": "^4.17.13", - "@types/jest": "29.5.11", - "@types/node": "20.10.6", - "@types/supertest": "^6.0.2", - "@types/zxcvbn": "4.4.1", - "@typescript-eslint/eslint-plugin": "^5.0.0", - "@typescript-eslint/parser": "^5.0.0", - "eslint": "^8.55.0", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-prettier": "^5.0.0", - "jest": "29.7.0", - "prettier": "^3.1.1", - "source-map-support": "^0.5.20", - "supertest": "^6.3.4", - "ts-jest": "29.1.1", - "ts-loader": "^9.2.3", - "ts-node": "^10.9.2", - "tsconfig-paths": "4.2.0", - "typescript": "^5.0.0" - } -} diff --git a/packages/apps/hufi/job-launcher/server/src/app.controller.ts b/packages/apps/hufi/job-launcher/server/src/app.controller.ts deleted file mode 100644 index 4f793b6866..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/app.controller.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Controller, Get, Redirect } from '@nestjs/common'; - -import { Public } from './common/decorators'; -import { ApiTags } from '@nestjs/swagger'; - -@Controller('/') -@ApiTags('Main') -export class AppController { - @Public() - @Get('/') - @Redirect('/swagger', 301) - public redirect(): void {} -} diff --git a/packages/apps/hufi/job-launcher/server/src/app.module.ts b/packages/apps/hufi/job-launcher/server/src/app.module.ts deleted file mode 100644 index b5f8c382a0..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/app.module.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { Module } from '@nestjs/common'; -import { APP_GUARD, APP_PIPE } from '@nestjs/core'; -import { ConfigModule } from '@nestjs/config'; -import { ScheduleModule } from '@nestjs/schedule'; -import { AppController } from './app.controller'; -import { DatabaseModule } from './database/database.module'; -import { JwtAuthGuard } from './common/guards'; -import { HttpValidationPipe } from './common/pipes'; -import { HealthModule } from './modules/health/health.module'; -import { AuthModule } from './modules/auth/auth.module'; -import { UserModule } from './modules/user/user.module'; -import { JobModule } from './modules/job/job.module'; -import { PaymentModule } from './modules/payment/payment.module'; -import { Web3Module } from './modules/web3/web3.module'; -import { envValidator } from './common/config'; -import { ServeStaticModule } from '@nestjs/serve-static'; -import { join } from 'path'; - -@Module({ - providers: [ - { - provide: APP_GUARD, - useClass: JwtAuthGuard, - }, - { - provide: APP_PIPE, - useClass: HttpValidationPipe, - }, - ], - imports: [ - ScheduleModule.forRoot(), - ConfigModule.forRoot({ - envFilePath: process.env.NODE_ENV - ? `.env.${process.env.NODE_ENV as string}` - : '.env', - validationSchema: envValidator, - }), - DatabaseModule, - HealthModule, - AuthModule, - UserModule, - JobModule, - PaymentModule, - Web3Module, - ServeStaticModule.forRoot({ - rootPath: join( - __dirname, - '../../../../../../', - 'node_modules/swagger-ui-dist', - ), - }), - ], - controllers: [AppController], -}) -export class AppModule {} diff --git a/packages/apps/hufi/job-launcher/server/src/common/config/env.ts b/packages/apps/hufi/job-launcher/server/src/common/config/env.ts deleted file mode 100644 index e303fd5617..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/common/config/env.ts +++ /dev/null @@ -1,93 +0,0 @@ -import * as Joi from 'joi'; - -export const ConfigNames = { - NODE_ENV: 'NODE_ENV', - HOST: 'HOST', - PORT: 'PORT', - FE_URL: 'FE_URL', - SESSION_SECRET: 'SESSION_SECRET', - HASH_SECRET: 'HASH_SECRET', - JWT_SECRET: 'JWT_SECRET', - JWT_ACCESS_TOKEN_EXPIRES_IN: 'JWT_ACCESS_TOKEN_EXPIRES_IN', - JWT_REFRESH_TOKEN_EXPIRES_IN: 'JWT_REFRESH_TOKEN_EXPIRES_IN', - POSTGRES_HOST: 'POSTGRES_HOST', - POSTGRES_USER: 'POSTGRES_USER', - POSTGRES_PASSWORD: 'POSTGRES_PASSWORD', - POSTGRES_DATABASE: 'POSTGRES_DATABASE', - POSTGRES_PORT: 'POSTGRES_PORT', - POSTGRES_SSL: 'POSTGRES_SSL', - WEB3_ENV: 'WEB3_ENV', - WEB3_PRIVATE_KEY: 'WEB3_PRIVATE_KEY', - JOB_LAUNCHER_FEE: 'JOB_LAUNCHER_FEE', - RECORDING_ORACLE_FEE: 'RECORDING_ORACLE_FEE', - REPUTATION_ORACLE_FEE: 'REPUTATION_ORACLE_FEE', - EXCHANGE_ORACLE_FEE: 'EXCHANGE_ORACLE_FEE', - REPUTATION_ORACLE_ADDRESS: 'REPUTATION_ORACLE_ADDRESS', - EXCHANGE_ORACLE_ADDRESS: 'EXCHANGE_ORACLE_ADDRESS', - EXCHANGE_ORACLE_WEBHOOK_URL: 'EXCHANGE_ORACLE_WEBHOOK_URL', - RECORDING_ORACLE_ADDRESS: 'RECORDING_ORACLE_ADDRESS', - S3_ENDPOINT: 'S3_ENDPOINT', - S3_PORT: 'S3_PORT', - S3_ACCESS_KEY: 'S3_ACCESS_KEY', - S3_SECRET_KEY: 'S3_SECRET_KEY', - S3_BUCKET: 'S3_BUCKET', - S3_USE_SSL: 'S3_USE_SSL', - STRIPE_SECRET_KEY: 'STRIPE_SECRET_KEY', - STRIPE_API_VERSION: 'STRIPE_API_VERSION', - STRIPE_APP_NAME: 'STRIPE_APP_NAME', - STRIPE_APP_VERSION: 'STRIPE_APP_VERSION', - STRIPE_APP_INFO_URL: 'STRIPE_APP_INFO_URL', - SENDGRID_API_KEY: 'SENDGRID_API_KEY', - SENDGRID_FROM_EMAIL: 'SENDGRID_FROM_EMAIL', - SENDGRID_FROM_NAME: 'SENDGRID_FROM_NAME', -}; - -export const envValidator = Joi.object({ - // General - NODE_ENV: Joi.string().default('development'), - HOST: Joi.string().default('localhost'), - PORT: Joi.string().default(5000), - FE_URL: Joi.string().default('http://localhost:3005'), - SESSION_SECRET: Joi.string().default('session_key'), - // Auth - HASH_SECRET: Joi.string().default('a328af3fc1dad15342cc3d68936008fa'), - JWT_SECRET: Joi.string().default('secret'), - JWT_ACCESS_TOKEN_EXPIRES_IN: Joi.string().default(1000000000), - JWT_REFRESH_TOKEN_EXPIRES_IN: Joi.string().default(1000000000), - // Database - DB_TYPE: Joi.string().default('postgres'), - POSTGRES_HOST: Joi.string().default('127.0.0.1'), - POSTGRES_USER: Joi.string().default('operator'), - POSTGRES_PASSWORD: Joi.string().default('qwerty'), - POSTGRES_DATABASE: Joi.string().default('job-launcher'), - POSTGRES_PORT: Joi.string().default('5432'), - POSTGRES_SSL: Joi.string().default('false'), - // Web3 - WEB3_ENV: Joi.string().default('testnet'), - WEB3_PRIVATE_KEY: Joi.string().required(), - JOB_LAUNCHER_FEE: Joi.string().default(10), - RECORDING_ORACLE_FEE: Joi.string().default(10), - REPUTATION_ORACLE_FEE: Joi.string().default(10), - EXCHANGE_ORACLE_FEE: Joi.string().default(10), - REPUTATION_ORACLE_ADDRESS: Joi.string().required(), - EXCHANGE_ORACLE_WEBHOOK_URL: Joi.string().default('http://localhost:3004'), - EXCHANGE_ORACLE_ADDRESS: Joi.string().required(), - RECORDING_ORACLE_ADDRESS: Joi.string().required(), - // S3 - S3_ENDPOINT: Joi.string().default('127.0.0.1'), - S3_PORT: Joi.string().default(9000), - S3_ACCESS_KEY: Joi.string().required(), - S3_SECRET_KEY: Joi.string().required(), - S3_BUCKET: Joi.string().default('launcher'), - S3_USE_SSL: Joi.string().default('false'), - // Stripe - STRIPE_SECRET_KEY: Joi.string().required(), - STRIPE_API_VERSION: Joi.string().default('2022-11-15'), - STRIPE_APP_NAME: Joi.string().default('Hufi'), - STRIPE_APP_VERSION: Joi.string().default('0.0.1'), - STRIPE_APP_INFO_URL: Joi.string().default('https://hmt.ai'), - // SendGrid - SENDGRID_API_KEY: Joi.string().required(), - SENDGRID_FROM_EMAIL: Joi.string().default('job-launcher@hmt.ai'), - SENDGRID_FROM_NAME: Joi.string().default('Human Protocol Job Launcher'), -}); diff --git a/packages/apps/hufi/job-launcher/server/src/common/config/index.ts b/packages/apps/hufi/job-launcher/server/src/common/config/index.ts deleted file mode 100644 index b257a909e5..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/common/config/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './env'; -export * from './networks'; diff --git a/packages/apps/hufi/job-launcher/server/src/common/config/networks.ts b/packages/apps/hufi/job-launcher/server/src/common/config/networks.ts deleted file mode 100644 index bb38966f69..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/common/config/networks.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { ChainId, NETWORKS } from '@human-protocol/sdk'; - -export interface TokensList { - [key: string]: string | undefined; -} -export interface NetworkDto { - chainId: number; - rpcUrl: string; - tokens: TokensList; -} - -interface NetworkMapDto { - [key: string]: NetworkDto; -} - -export const networkMap: NetworkMapDto = { - polygon: { - chainId: ChainId.POLYGON, - rpcUrl: - 'https://polygon-mainnet.g.alchemy.com/v2/0Lorh5KRkGl5FsRwy2epTg8fEFFoqUfY', - tokens: { - hmt: NETWORKS[ChainId.POLYGON]?.hmtAddress, - usdt: '0x170a18b9190669cda08965562745a323c907e5ec', - }, - }, - bsc: { - chainId: ChainId.BSC_MAINNET, - rpcUrl: 'https://bsc-dataseed1.binance.org/', - tokens: { - hmt: NETWORKS[ChainId.BSC_MAINNET]?.hmtAddress, - usdt: '0x55d398326f99059fF775485246999027B3197955', - }, - }, - mumbai: { - chainId: ChainId.POLYGON_MUMBAI, - rpcUrl: - 'https://polygon-mumbai.g.alchemy.com/v2/vKNSJzJf6SW2sdW-05bgFwoyFxUrMzii', - tokens: { - hmt: NETWORKS[ChainId.POLYGON_MUMBAI]?.hmtAddress, - usdt: '0x5b20e68f501590C130d77C87C2A2f2B43Fc09701', - }, - }, - goerli: { - chainId: ChainId.GOERLI, - rpcUrl: 'https://goerli.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161', - tokens: { - hmt: NETWORKS[ChainId.GOERLI]?.hmtAddress, - }, - }, - moonbeam: { - chainId: ChainId.MOONBEAM, - rpcUrl: 'https://rpc.api.moonbeam.network', - tokens: { - hmt: NETWORKS[ChainId.MOONBEAM]?.hmtAddress, - }, - }, - bsctest: { - chainId: ChainId.BSC_TESTNET, - rpcUrl: 'https://data-seed-prebsc-1-s1.binance.org:8545/', - tokens: { - hmt: NETWORKS[ChainId.BSC_TESTNET]?.hmtAddress, - }, - }, -}; - -export const networks = Object.values(networkMap).map((network) => network); diff --git a/packages/apps/hufi/job-launcher/server/src/common/constants/errors.ts b/packages/apps/hufi/job-launcher/server/src/common/constants/errors.ts deleted file mode 100644 index 306c86a3d1..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/common/constants/errors.ts +++ /dev/null @@ -1,122 +0,0 @@ -/** - * Represents error messages associated with a job. - */ -export enum ErrorJob { - NotFound = 'Job not found', - NotCreated = 'Job has not been created', - NotEnoughFunds = 'Not enough funds', - ManifestNotFound = 'Manifest not found', - ManifestValidationFailed = 'Manifest validation failed', - WebhookWasNotSent = 'Webhook was not sent', - ResultNotFound = 'Result not found', - ResultValidationFailed = 'Result validation failed', - InvalidRequestType = 'Invalid job type', - JobParamsValidationFailed = 'Job parameters validation failed', - InvalidEventType = 'Invalid event type', - InvalidStatusCancellation = 'Job has an invalid status for cancellation', - NotLaunched = 'Not launched', -} - -/** - * Represents error messages related to escrow. - */ -export enum ErrorEscrow { - NotFound = 'Escrow not found', - NotCreated = 'Escrow has not been created', - NotLaunched = 'Escrow has not been launched', - InvalidStatusCancellation = 'Escrow has an invalid status for cancellation', - InvalidBalanceCancellation = 'Escrow has an invalid balance for cancellation', -} - -/** - * Represents error messages related to user. - */ -export enum ErrorUser { - NotFound = 'User not found', - AccountCannotBeRegistered = 'Account cannot be registered', - BalanceCouldNotBeRetreived = 'User balance could not be retrieved', - InvalidCredentials = 'Invalid credentials', -} - -/** - * Represents error messages related to auth. - */ -export enum ErrorAuth { - NotFound = 'Auth not found', - InvalidEmailOrPassword = 'Invalid email or password', - RefreshTokenHasExpired = 'Refresh token has expired', - UserNotActive = 'User not active' -} - -/** - * Represents error messages related to token. - */ -export enum ErrorToken { - NotFound = 'Token not found', -} - -/** - * Represents error messages related to payment. - */ -export enum ErrorPayment { - NotFound = 'Payment not found', - NotSuccess = 'Unsuccessful payment', - IntentNotCreated = 'Payment intent not created', - ClientSecretDoesNotExist = 'Payment intent was not created', - CustomerNotFound = 'Customer not found', - CustomerNotCreated = 'Customer not created', - IncorrectAmount = 'Incorrect amount', - TransactionAlreadyExists = 'Transaction already exists', - TransactionNotFoundByHash = 'Transaction not found by hash', - InvalidTransactionData = 'Invalid transaction data', - TransactionHasNotEnoughAmountOfConfirmations = 'Transaction has not enough amount of confirmations', - UnsupportedToken = 'Unsupported token', - InvalidRecipient = 'Invalid recipient', - ChainIdMissing = 'ChainId is missing', -} - -/** - * Represents error messages related to currency. - */ -export enum ErrorCurrency { - PairNotFound = 'Pair not found', -} - -/** - * Represents error messages related to bucket. - */ -export enum ErrorBucket { - NotPublic = 'Bucket is not public', - UnableSaveFile = 'Unable to save file', -} - -/** - * Represents error messages related to web3. - */ -export enum ErrorWeb3 { - InvalidTestnetChainId = 'Invalid chain id provided for the testnet environment', - InvalidMainnetChainId = 'Invalid chain id provided for the mainnet environment', -} - -/** - * Represents error messages related to send grid. - */ -export enum ErrorSendGrid { - EmailNotSent = 'Email was not sent', - InvalidApiKey = 'Invalid SendGrid API key', -} - -/** - * Represents error messages related to signature. - */ -export enum ErrorSignature { - SignatureNotVerified = 'Signature not verified', - InvalidSignature = 'Invalid signature', -} - -/** - * Represents error messages related to postgres. - */ -export enum ErrorPostgres { - NumericFieldOverflow = 'Numeric field overflow', -} diff --git a/packages/apps/hufi/job-launcher/server/src/common/constants/index.ts b/packages/apps/hufi/job-launcher/server/src/common/constants/index.ts deleted file mode 100644 index 5eb1795a8f..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/common/constants/index.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { ChainId } from '@human-protocol/sdk'; -import { JobStatus } from '../enums/job'; - -export const SERVICE_NAME = 'Job Launcher'; -export const NS = 'hmt'; -export const COINGECKO_API_URL = - 'https://api.coingecko.com/api/v3/simple/price'; -export const JOB_RETRIES_COUNT_THRESHOLD = 3; -export const TX_CONFIRMATION_TRESHOLD = 1; - -export const JWT_PREFIX = 'bearer '; -export const TESTNET_CHAIN_IDS = [ - ChainId.BSC_TESTNET, - ChainId.POLYGON_MUMBAI, - ChainId.GOERLI, -]; -export const MAINNET_CHAIN_IDS = [ - ChainId.BSC_MAINNET, - ChainId.POLYGON, - ChainId.MOONBEAM, -]; - -export const SENDGRID_API_KEY_REGEX = - /^SG\.[A-Za-z0-9-_]{22}\.[A-Za-z0-9-_]{43}$/; - -export const HEADER_SIGNATURE_KEY = 'human-signature'; - -export const CANCEL_JOB_STATUSES = [ - JobStatus.PENDING, - JobStatus.PAID, - JobStatus.LAUNCHED, -]; - -export const SENDGRID_TEMPLATES = { - signup: 'd-ca99cc7410aa4e6dab3e6042d5ecb9a3', - resetPassword: 'd-3ac74546352a4e1abdd1689947632c22', - passwordChanged: 'd-ca0ac7e6fff845829cd0167af09f25cf', -}; diff --git a/packages/apps/hufi/job-launcher/server/src/common/constants/payment.ts b/packages/apps/hufi/job-launcher/server/src/common/constants/payment.ts deleted file mode 100644 index f72ce2d587..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/common/constants/payment.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { ICoingeckoTokenId } from '../interfaces'; - -export const CoingeckoTokenId: ICoingeckoTokenId = { - hmt: 'human-protocol', - usdt: 'tether', -}; diff --git a/packages/apps/hufi/job-launcher/server/src/common/decorators/index.ts b/packages/apps/hufi/job-launcher/server/src/common/decorators/index.ts deleted file mode 100644 index b7e8b7187d..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/common/decorators/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './public'; diff --git a/packages/apps/hufi/job-launcher/server/src/common/decorators/public.ts b/packages/apps/hufi/job-launcher/server/src/common/decorators/public.ts deleted file mode 100644 index a12eaafa09..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/common/decorators/public.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { SetMetadata } from '@nestjs/common'; - -export const Public = (): ((target: any, key?: any, descriptor?: any) => any) => - SetMetadata('isPublic', true); diff --git a/packages/apps/hufi/job-launcher/server/src/common/enums/collection.ts b/packages/apps/hufi/job-launcher/server/src/common/enums/collection.ts deleted file mode 100644 index cb05417818..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/common/enums/collection.ts +++ /dev/null @@ -1,4 +0,0 @@ -export enum SortDirection { - ASC = 'ASC', - DESC = 'DESC', -} diff --git a/packages/apps/hufi/job-launcher/server/src/common/enums/exchange.ts b/packages/apps/hufi/job-launcher/server/src/common/enums/exchange.ts deleted file mode 100644 index 3ea9700c8f..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/common/enums/exchange.ts +++ /dev/null @@ -1,6 +0,0 @@ -export enum Exchange { - UNISWAP_ETHEREUM = 'unispwap-ethereum', - UNISWAP_POLYGON = 'uniswap-polygon', - PANCAKESWAP_BSC = 'pancakeswap-bsc', - BINANCE = 'binance', -} diff --git a/packages/apps/hufi/job-launcher/server/src/common/enums/job.ts b/packages/apps/hufi/job-launcher/server/src/common/enums/job.ts deleted file mode 100644 index e092d415a3..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/common/enums/job.ts +++ /dev/null @@ -1,21 +0,0 @@ -export enum JobStatus { - PENDING = 'PENDING', - PAID = 'PAID', - LAUNCHED = 'LAUNCHED', - COMPLETED = 'COMPLETED', - FAILED = 'FAILED', - TO_CANCEL = 'TO_CANCEL', - CANCELED = 'CANCELED', -} - -export enum JobStatusFilter { - PENDING = 'PENDING', - LAUNCHED = 'LAUNCHED', - COMPLETED = 'COMPLETED', - FAILED = 'FAILED', - CANCELED = 'CANCELED', -} - -export enum JobRequestType { - CAMPAIGN = 'CAMPAIGN', -} diff --git a/packages/apps/hufi/job-launcher/server/src/common/enums/payment.ts b/packages/apps/hufi/job-launcher/server/src/common/enums/payment.ts deleted file mode 100644 index dbaf0d9dc6..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/common/enums/payment.ts +++ /dev/null @@ -1,77 +0,0 @@ -export enum Currency { - USD = 'usd', - AED = 'aed', - ARS = 'ars', - AUD = 'aud', - BDT = 'bdt', - BMD = 'bmd', - BRL = 'brl', - CAD = 'cad', - CHF = 'chf', - CLP = 'clp', - CNY = 'cny', - CZK = 'czk', - DKK = 'dkk', - EUR = 'eur', - GBP = 'gbp', - HKD = 'hkd', - HUF = 'huf', - IDR = 'idr', - ILS = 'ils', - INR = 'inr', - JPY = 'jpy', - KRW = 'krw', - LKR = 'lkr', - MMK = 'mmk', - MXN = 'mxn', - MYR = 'myr', - NGN = 'ngn', - NOK = 'nok', - NZD = 'nzd', - PHP = 'php', - PKR = 'pkr', - PLN = 'pln', - RUB = 'rub', - SAR = 'sar', - SEK = 'sek', - SGD = 'sgd', - THB = 'thb', - TRY = 'try', - TWD = 'twd', - UAH = 'uah', - VND = 'vnd', - ZAR = 'zar', -} - -export enum TokenId { - HMT = 'hmt', - USDT = 'usdt', -} - -export enum PaymentSource { - FIAT = 'FIAT', - CRYPTO = 'CRYPTO', - BALANCE = 'BALANCE', -} - -export enum PaymentFiatMethodType { - CARD = 'CARD', -} - -export enum PaymentType { - DEPOSIT = 'DEPOSIT', - REFUND = 'REFUND', - WITHDRAWAL = 'WITHDRAWAL', -} - -export enum PaymentStatus { - PENDING = 'PENDING', - FAILED = 'FAILED', - SUCCEEDED = 'SUCCEEDED', -} - -export enum StripePaymentStatus { - CANCELED = 'canceled', - REQUIRES_PAYMENT_METHOD = 'requires_payment_method', - SUCCEEDED = 'succeeded', -} diff --git a/packages/apps/hufi/job-launcher/server/src/common/enums/role.ts b/packages/apps/hufi/job-launcher/server/src/common/enums/role.ts deleted file mode 100644 index a4b6517e49..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/common/enums/role.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum Role { - Exchange = 'exchange', - Recording = 'recording', - Reputation = 'reputation', -} diff --git a/packages/apps/hufi/job-launcher/server/src/common/enums/user.ts b/packages/apps/hufi/job-launcher/server/src/common/enums/user.ts deleted file mode 100644 index b5128b6037..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/common/enums/user.ts +++ /dev/null @@ -1,10 +0,0 @@ -export enum UserStatus { - ACTIVE = 'ACTIVE', - INACTIVE = 'INACTIVE', - PENDING = 'PENDING', -} - -export enum UserType { - OPERATOR = 'OPERATOR', - REQUESTER = 'REQUESTER', -} diff --git a/packages/apps/hufi/job-launcher/server/src/common/enums/web3.ts b/packages/apps/hufi/job-launcher/server/src/common/enums/web3.ts deleted file mode 100644 index d2bd10a96c..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/common/enums/web3.ts +++ /dev/null @@ -1,4 +0,0 @@ -export enum Web3Env { - TESTNET = 'testnet', - MAINNET = 'mainnet', -} diff --git a/packages/apps/hufi/job-launcher/server/src/common/enums/webhook.ts b/packages/apps/hufi/job-launcher/server/src/common/enums/webhook.ts deleted file mode 100644 index 4773912237..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/common/enums/webhook.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum EventType { - ESCROW_CREATED = 'escrow_created', - ESCROW_CANCELED = 'escrow_canceled', - TASK_CREATION_FAILED = 'task_creation_failed', -} diff --git a/packages/apps/hufi/job-launcher/server/src/common/guards/index.ts b/packages/apps/hufi/job-launcher/server/src/common/guards/index.ts deleted file mode 100644 index 2d4029b2c9..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/common/guards/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './jwt.auth'; -export * from './signature.auth'; \ No newline at end of file diff --git a/packages/apps/hufi/job-launcher/server/src/common/guards/jwt.auth.ts b/packages/apps/hufi/job-launcher/server/src/common/guards/jwt.auth.ts deleted file mode 100644 index 6274d5eed7..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/common/guards/jwt.auth.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { - CanActivate, - ExecutionContext, - Injectable, - UnauthorizedException, -} from '@nestjs/common'; -import { Reflector } from '@nestjs/core'; -import { AuthGuard } from '@nestjs/passport'; - -@Injectable() -export class JwtAuthGuard extends AuthGuard('jwt-http') implements CanActivate { - constructor(private readonly reflector: Reflector) { - super(); - } - - public async canActivate(context: ExecutionContext): Promise { - // `super` has to be called to set `user` on `request` - // see https://github.com/nestjs/passport/blob/master/lib/auth.guard.ts - return (super.canActivate(context) as Promise).catch((e) => { - const isPublic = this.reflector.getAllAndOverride('isPublic', [ - context.getHandler(), - context.getClass(), - ]); - - if (isPublic) { - return true; - } - - console.error(e); - throw new UnauthorizedException('Unauthorized'); - }); - } -} diff --git a/packages/apps/hufi/job-launcher/server/src/common/guards/signature.auth.spec.ts b/packages/apps/hufi/job-launcher/server/src/common/guards/signature.auth.spec.ts deleted file mode 100644 index 415a2ee27c..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/common/guards/signature.auth.spec.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { ExecutionContext, UnauthorizedException } from '@nestjs/common'; -import { SignatureAuthGuard } from './signature.auth'; -import { verifySignature } from '../utils/signature'; -import { ChainId, EscrowUtils } from '@human-protocol/sdk'; -import { MOCK_ADDRESS } from '../../../test/constants'; -import { Role } from '../enums/role'; - -jest.mock('../../common/utils/signature'); - -jest.mock('@human-protocol/sdk', () => ({ - ...jest.requireActual('@human-protocol/sdk'), - EscrowUtils: { - getEscrow: jest.fn().mockResolvedValue({ - launcher: '0x1234567890123456789012345678901234567890', - exchangeOracle: '0x1234567890123456789012345678901234567891', - reputationOracle: '0x1234567890123456789012345678901234567892', - }), - }, -})); - -describe('SignatureAuthGuard', () => { - let guard: SignatureAuthGuard; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - { - provide: SignatureAuthGuard, - useValue: new SignatureAuthGuard([ - Role.Exchange, - Role.Recording, - Role.Reputation, - ]), - }, - ], - }).compile(); - - guard = module.get(SignatureAuthGuard); - }); - - it('should be defined', () => { - expect(guard).toBeDefined(); - }); - - describe('canActivate', () => { - let context: ExecutionContext; - let mockRequest: any; - - beforeEach(() => { - mockRequest = { - switchToHttp: jest.fn().mockReturnThis(), - getRequest: jest.fn().mockReturnThis(), - headers: {}, - body: {}, - originalUrl: '', - }; - context = { - switchToHttp: jest.fn().mockReturnThis(), - getRequest: jest.fn(() => mockRequest), - } as any as ExecutionContext; - }); - - it('should return true if signature is verified', async () => { - mockRequest.headers['header-signature-key'] = 'validSignature'; - mockRequest.body = { - escrowAddress: MOCK_ADDRESS, - chainId: ChainId.LOCALHOST, - }; - (verifySignature as jest.Mock).mockReturnValue(true); - - const result = await guard.canActivate(context as any); - expect(result).toBeTruthy(); - expect(EscrowUtils.getEscrow).toHaveBeenCalledWith( - ChainId.LOCALHOST, - MOCK_ADDRESS, - ); - }); - - it('should throw unauthorized exception if signature is not verified', async () => { - (verifySignature as jest.Mock).mockReturnValue(false); - - await expect(guard.canActivate(context as any)).rejects.toThrow( - UnauthorizedException, - ); - }); - - it('should throw unauthorized exception for unrecognized oracle type', async () => { - mockRequest.originalUrl = '/some/random/path'; - await expect(guard.canActivate(context as any)).rejects.toThrow( - UnauthorizedException, - ); - }); - }); -}); diff --git a/packages/apps/hufi/job-launcher/server/src/common/guards/signature.auth.ts b/packages/apps/hufi/job-launcher/server/src/common/guards/signature.auth.ts deleted file mode 100644 index fb7eba7986..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/common/guards/signature.auth.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { - CanActivate, - ExecutionContext, - Injectable, - UnauthorizedException, -} from '@nestjs/common'; -import { verifySignature } from '../utils/signature'; -import { HEADER_SIGNATURE_KEY } from '../constants'; -import { EscrowUtils } from '@human-protocol/sdk'; -import { Role } from '../enums/role'; - -@Injectable() -export class SignatureAuthGuard implements CanActivate { - constructor(private role: Role[]) {} - - public async canActivate(context: ExecutionContext): Promise { - const request = context.switchToHttp().getRequest(); - - const data = request.body; - const signature = request.headers[HEADER_SIGNATURE_KEY]; - const oracleAdresses: string[] = []; - try { - const escrowData = await EscrowUtils.getEscrow( - data.chainId, - data.escrowAddress, - ); - if (this.role.includes(Role.Exchange)) - oracleAdresses.push(escrowData.exchangeOracle!); - if (this.role.includes(Role.Recording)) - oracleAdresses.push(escrowData.recordingOracle!); - if (this.role.includes(Role.Reputation)) - oracleAdresses.push(escrowData.reputationOracle!); - - const isVerified = verifySignature(data, signature, oracleAdresses); - - if (isVerified) { - return true; - } - } catch (error) { - console.error(error); - } - - throw new UnauthorizedException('Unauthorized'); - } -} diff --git a/packages/apps/hufi/job-launcher/server/src/common/interfaces/base.ts b/packages/apps/hufi/job-launcher/server/src/common/interfaces/base.ts deleted file mode 100644 index 8ad596c4d9..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/common/interfaces/base.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface IBase { - id: number; - createdAt: Date; - updatedAt: Date; -} diff --git a/packages/apps/hufi/job-launcher/server/src/common/interfaces/index.ts b/packages/apps/hufi/job-launcher/server/src/common/interfaces/index.ts deleted file mode 100644 index 9766666c29..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/common/interfaces/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './base'; -export * from './job'; -export * from './user'; -export * from './payments'; diff --git a/packages/apps/hufi/job-launcher/server/src/common/interfaces/job.ts b/packages/apps/hufi/job-launcher/server/src/common/interfaces/job.ts deleted file mode 100644 index 851381ea35..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/common/interfaces/job.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { JobRequestType, JobStatus } from '../enums/job'; -import { IBase } from './base'; - -export interface IJob extends IBase { - userId: number; - chainId: number; - fee: number; - fundAmount: number; - escrowAddress: string; - manifestUrl: string; - manifestHash: string; - status: JobStatus; - failedReason?: string; - retriesCount?: number; - waitUntil: Date; -} \ No newline at end of file diff --git a/packages/apps/hufi/job-launcher/server/src/common/interfaces/payments.ts b/packages/apps/hufi/job-launcher/server/src/common/interfaces/payments.ts deleted file mode 100644 index 402a5f4a6d..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/common/interfaces/payments.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface ICoingeckoTokenId { - [key: string]: string; -} diff --git a/packages/apps/hufi/job-launcher/server/src/common/interfaces/user.ts b/packages/apps/hufi/job-launcher/server/src/common/interfaces/user.ts deleted file mode 100644 index ec4f890874..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/common/interfaces/user.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Currency } from '../enums/payment'; -import { UserStatus, UserType } from '../enums/user'; -import { IBase } from './base'; - -export interface IUser extends IBase { - password: string; - email: string; - status: UserStatus; - type: UserType; -} - -export interface IUserBalance { - amount: number; - currency: Currency; -} diff --git a/packages/apps/hufi/job-launcher/server/src/common/pipes/index.ts b/packages/apps/hufi/job-launcher/server/src/common/pipes/index.ts deleted file mode 100644 index 4d5ffa36ab..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/common/pipes/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './validation'; diff --git a/packages/apps/hufi/job-launcher/server/src/common/pipes/validation.ts b/packages/apps/hufi/job-launcher/server/src/common/pipes/validation.ts deleted file mode 100644 index f46e742df2..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/common/pipes/validation.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { - BadRequestException, - Injectable, - ValidationError, - ValidationPipe, - ValidationPipeOptions, -} from '@nestjs/common'; - -@Injectable() -export class HttpValidationPipe extends ValidationPipe { - constructor(options?: ValidationPipeOptions) { - super({ - exceptionFactory: (errors: ValidationError[]): BadRequestException => - new BadRequestException(errors), - transform: true, - whitelist: true, - forbidNonWhitelisted: true, - forbidUnknownValues: true, - ...options, - }); - } -} diff --git a/packages/apps/hufi/job-launcher/server/src/common/types/index.ts b/packages/apps/hufi/job-launcher/server/src/common/types/index.ts deleted file mode 100644 index 56e4b0555f..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/common/types/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './request'; diff --git a/packages/apps/hufi/job-launcher/server/src/common/types/request.ts b/packages/apps/hufi/job-launcher/server/src/common/types/request.ts deleted file mode 100644 index 5425b19994..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/common/types/request.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface RequestWithUser extends Request { - user: any; -} \ No newline at end of file diff --git a/packages/apps/hufi/job-launcher/server/src/common/utils/decimal.ts b/packages/apps/hufi/job-launcher/server/src/common/utils/decimal.ts deleted file mode 100644 index 7b963d0a70..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/common/utils/decimal.ts +++ /dev/null @@ -1,62 +0,0 @@ -import Decimal from 'decimal.js'; - -export function mul(a: number, b: number): number { - const decimalA = new Decimal(a); - const decimalB = new Decimal(b); - - const result = decimalA.times(decimalB); - - return result.toNumber(); -} - -export function div(a: number, b: number): number { - const decimalA = new Decimal(a); - const decimalB = new Decimal(b); - - if (decimalB.isZero()) { - throw new Error('Division by zero is not allowed.'); - } - - const result = decimalA.dividedBy(decimalB); - - return result.toNumber(); -} - -export function add(a: number, b: number): number { - const decimalA = new Decimal(a); - const decimalB = new Decimal(b); - - const result = decimalA.plus(decimalB); - - return result.toNumber(); -} - -export function sub(a: number, b: number): number { - const decimalA = new Decimal(a); - const decimalB = new Decimal(b); - - const result = decimalA.minus(decimalB); - - return result.toNumber(); -} - -export function eq(a: number, b: number): boolean { - const decimalA = new Decimal(a); - const decimalB = new Decimal(b); - - return decimalA.eq(decimalB); -} - -export function lt(a: number, b: number): boolean { - const decimalA = new Decimal(a); - const decimalB = new Decimal(b); - - return decimalA.lt(decimalB); -} - -export function gt(a: number, b: number): boolean { - const decimalA = new Decimal(a); - const decimalB = new Decimal(b); - - return decimalA.gt(decimalB); -} diff --git a/packages/apps/hufi/job-launcher/server/src/common/utils/index.ts b/packages/apps/hufi/job-launcher/server/src/common/utils/index.ts deleted file mode 100644 index 2653e5afd7..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/common/utils/index.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { firstValueFrom } from 'rxjs'; -import { CoingeckoTokenId } from '../constants/payment'; -import { TokenId } from '../enums/payment'; -import { COINGECKO_API_URL } from '../constants'; -import { NotFoundException } from '@nestjs/common'; -import { ErrorCurrency } from '../constants/errors'; -import { HttpService } from '@nestjs/axios'; - -export async function getRate(from: string, to: string): Promise { - if (from === to) { - return 1; - } - let reversed = false; - - if (Object.values(TokenId).includes(to as TokenId)) { - [from, to] = [CoingeckoTokenId[to], from]; - reversed = true; - } else { - [from, to] = [CoingeckoTokenId[from], to]; - } - - const httpService = new HttpService(); - const { data } = (await firstValueFrom( - httpService.get(`${COINGECKO_API_URL}?ids=${from}&vs_currencies=${to}`), - )) as any; - - if (!data[from] || !data[from][to]) { - throw new NotFoundException(ErrorCurrency.PairNotFound); - } - - const rate = data[from][to]; - - return reversed ? 1 / rate : rate; -} - -export const parseUrl = (url: string): { - endPoint: string, - bucket: string, - useSSL: boolean, - filename?: string, - port?: number, -} => { - const patterns = [ - { - regex: /^https:\/\/storage\.googleapis\.com\/([^/]+)\/?$/, - endPoint: 'storage.googleapis.com', - }, - { - regex: /^https:\/\/([^\.]+)\.storage\.googleapis\.com\/?$/, - endPoint: 'storage.googleapis.com', - }, - { - regex: /^https?:\/\/([^/:]+)(?::(\d+))?(\/.*)?/, - endPoint: '$1', - port: '$2', - }, - ]; - - for (const { regex, endPoint, port } of patterns) { - const match = url.match(regex); - if (match) { - const parts = match[3] ? match[3].split('/').filter(part => part) : []; - const bucket = parts[0] || ''; - const filename = parts.length > 1 ? parts[parts.length - 1] : undefined; - - return { - useSSL: url.startsWith('https:'), - endPoint: endPoint.replace('$1', match[1]), - port: port && match[2] ? Number(match[2]) : undefined, - bucket, - filename, - }; - } - } - - throw new Error('Invalid URL'); -}; \ No newline at end of file diff --git a/packages/apps/hufi/job-launcher/server/src/common/utils/signature.spec.ts b/packages/apps/hufi/job-launcher/server/src/common/utils/signature.spec.ts deleted file mode 100644 index b7ed7e4255..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/common/utils/signature.spec.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { verifySignature, recoverSigner, signMessage } from './signature'; -import { MOCK_ADDRESS, MOCK_PRIVATE_KEY } from '../../../test/constants'; -import { ErrorSignature } from '../constants/errors'; - -jest.doMock('ethers', () => { - return { - utils: { - get verifyMessage() { - return jest.fn((message, signature) => { - if (message === 'valid-message' && signature === 'valid-signature') { - return 'recovered-address'; - } else { - throw new Error('Invalid signature'); - } - }); - }, - }, - }; -}); - -describe('Signature utility', () => { - describe('verifySignature', () => { - it('should return true for valid signature', async () => { - const message = 'Hello, this is a signed message!'; - const signature = await signMessage(message, MOCK_PRIVATE_KEY); - - const result = verifySignature(message, signature, [MOCK_ADDRESS]); - - expect(result).toBe(true); - }); - - it('should throw conflict exception for signature not verified', async () => { - const message = 'Hello, this is a signed message!'; - - const invalidSignature = await signMessage(message, MOCK_PRIVATE_KEY); - const invalidAddress = '0x1234567890123456789012345678901234567892'; - - expect(() => { - verifySignature(message, invalidSignature, [invalidAddress]); - }).toThrow(ErrorSignature.SignatureNotVerified); - }); - - it('should throw conflict exception for invalid signature', () => { - const message = 'Hello, this is a signed message!'; - const invalidSignature = '0xInvalidSignature'; - - expect(() => { - verifySignature(message, invalidSignature, [MOCK_ADDRESS]); - }).toThrow(ErrorSignature.InvalidSignature); - }); - }); - - describe('recoverSigner', () => { - it('should recover the correct signer', async () => { - const message = 'value'; - const signature = await signMessage(message, MOCK_PRIVATE_KEY); - - const result = recoverSigner(message, signature); - - expect(result).toBe(MOCK_ADDRESS); - }); - - it('should throw conflict exception for invalid signature', () => { - const message = 'Hello, this is a signed message!'; - const invalidSignature = '0xInvalidSignature'; - - expect(() => { - recoverSigner(message, invalidSignature); - }).toThrow(ErrorSignature.InvalidSignature); - }); - - it('should stringify message object if it is not already a string', async () => { - const message = { key: 'value' }; - const signature = await signMessage(message, MOCK_PRIVATE_KEY); - - const recoveredAddress = recoverSigner(message, signature); - - expect(recoveredAddress).toBe(MOCK_ADDRESS); - }); - - it('should not stringify message if it is already a string', async () => { - const message = 'valid message'; - const signature = await signMessage(message, MOCK_PRIVATE_KEY); - - const recoveredAddress = recoverSigner(message, signature); - - expect(recoveredAddress).toBe(MOCK_ADDRESS); - }); - }); - - describe('signMessage', () => { - it('should return a valid signature', async () => { - const message = 'Hello, this is a test message'; - const signature = await signMessage(message, MOCK_PRIVATE_KEY); - - expect(signature).toBeDefined(); - }); - - it('should stringify message object if it is not already a string', async () => { - const message = { key: 'value' }; - const signature = await signMessage(message, MOCK_PRIVATE_KEY); - - expect(signature).toBeDefined(); - }); - - it('should not stringify message if it is already a string', async () => { - const message = 'valid message'; - const signature = await signMessage(message, MOCK_PRIVATE_KEY); - - expect(signature).toBeDefined(); - }); - }); -}); diff --git a/packages/apps/hufi/job-launcher/server/src/common/utils/signature.ts b/packages/apps/hufi/job-launcher/server/src/common/utils/signature.ts deleted file mode 100644 index c619886dcb..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/common/utils/signature.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { ConflictException } from '@nestjs/common'; -import { ethers } from 'ethers'; -import { ErrorSignature } from '../constants/errors'; - -export function verifySignature( - message: object | string, - signature: string, - addresses: string[], -): boolean { - const signer = recoverSigner(message, signature); - - if ( - !addresses.some((address) => address.toLowerCase() === signer.toLowerCase()) - ) { - throw new ConflictException(ErrorSignature.SignatureNotVerified); - } - - return true; -} - -export async function signMessage( - message: object | string, - privateKey: string, -): Promise { - if (typeof message !== 'string') { - message = JSON.stringify(message); - } - - const wallet = new ethers.Wallet(privateKey); - const signature = await wallet.signMessage(message); - - return signature; -} - -export function recoverSigner( - message: object | string, - signature: string, -): string { - if (typeof message !== 'string') { - message = JSON.stringify(message); - } - - try { - return ethers.verifyMessage(message, signature); - } catch (e) { - throw new ConflictException(ErrorSignature.InvalidSignature); - } -} diff --git a/packages/apps/hufi/job-launcher/server/src/common/utils/status.ts b/packages/apps/hufi/job-launcher/server/src/common/utils/status.ts deleted file mode 100644 index 888b04b880..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/common/utils/status.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { EscrowStatus } from '@human-protocol/sdk'; -import { JobStatusFilter } from '../enums/job'; - -export function filterToEscrowStatus( - filterStatus: JobStatusFilter, -): EscrowStatus[] { - switch (filterStatus) { - case JobStatusFilter.COMPLETED: - return [EscrowStatus.Complete]; - case JobStatusFilter.CANCELED: - return [EscrowStatus.Cancelled]; - default: - return [EscrowStatus.Launched, EscrowStatus.Partial, EscrowStatus.Paid]; - } -} diff --git a/packages/apps/hufi/job-launcher/server/src/common/validators/confirm.ts b/packages/apps/hufi/job-launcher/server/src/common/validators/confirm.ts deleted file mode 100644 index d7f54c87bd..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/common/validators/confirm.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { - registerDecorator, - ValidationArguments, - ValidationOptions, - ValidatorConstraint, - ValidatorConstraintInterface, -} from 'class-validator'; - -interface IConfirmConstraints { - required: boolean; - relatedPropertyName: string; -} - -@ValidatorConstraint() -class ValidateConfirm implements ValidatorConstraintInterface { - private reason: string; - - public validate(value: unknown, args: ValidationArguments): boolean { - this.reason = ValidateConfirm.isValid(value, args); - return !this.reason; - } - - public defaultMessage(): string { - return this.reason; - } - - private static isValid(value: unknown, args: ValidationArguments): string { - const { relatedPropertyName = 'password' }: IConfirmConstraints = - args.constraints[0]; - - const relatedValue = (args.object as any)[relatedPropertyName]; - - if (typeof value === 'undefined' || value === '') { - if (relatedValue) { - return 'valueMissing'; - } else { - return ''; - } - } - - if (typeof value !== 'string') { - return 'typeMismatch'; - } - - if (relatedValue !== value) { - return 'badInput'; - } - - return ''; - } -} - -export function IsConfirm( - constraints: Partial = {}, - validationOptions?: ValidationOptions, -) { - return (object: Record, propertyName: string): void => { - registerDecorator({ - name: 'isConfirm', - target: object.constructor, - propertyName, - constraints: [constraints], - options: validationOptions, - validator: ValidateConfirm, - }); - }; -} diff --git a/packages/apps/hufi/job-launcher/server/src/common/validators/index.ts b/packages/apps/hufi/job-launcher/server/src/common/validators/index.ts deleted file mode 100644 index 4bf70483ee..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/common/validators/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './password'; -export * from './confirm'; diff --git a/packages/apps/hufi/job-launcher/server/src/common/validators/password.ts b/packages/apps/hufi/job-launcher/server/src/common/validators/password.ts deleted file mode 100644 index edea4cdd17..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/common/validators/password.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { - registerDecorator, - ValidationArguments, - ValidationOptions, - ValidatorConstraint, - ValidatorConstraintInterface, -} from 'class-validator'; -import zxcvbn from 'zxcvbn'; - -interface IPasswordConstraints { - required: boolean; - score: number; -} - -@ValidatorConstraint() -class ValidatePassword implements ValidatorConstraintInterface { - private reason: string; - - public validate(value: unknown, args: ValidationArguments): boolean { - this.reason = ValidatePassword.isValid(value, args); - return !this.reason; - } - - public defaultMessage(): string { - return this.reason; - } - - private static isValid(value: unknown, args: ValidationArguments): string { - const { required = true, score = 0 }: IPasswordConstraints = - args.constraints[0]; - - if (typeof value === 'undefined' || value === '') { - if (required) { - return 'valueMissing'; - } else { - return ''; - } - } - - if (typeof value !== 'string') { - return 'typeMismatch'; - } - - if (zxcvbn(value).score < score) { - return 'weak'; - } - - return ''; - } -} - -export function IsPassword( - constraints: Partial = {}, - validationOptions?: ValidationOptions, -) { - return (object: Record, propertyName: string): void => { - registerDecorator({ - name: 'isPassword', - target: object.constructor, - propertyName, - constraints: [constraints], - options: validationOptions, - validator: ValidatePassword, - }); - }; -} diff --git a/packages/apps/hufi/job-launcher/server/src/database/base.entity.ts b/packages/apps/hufi/job-launcher/server/src/database/base.entity.ts deleted file mode 100644 index 963505429c..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/database/base.entity.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { - BaseEntity as OrmBaseEntity, - BeforeInsert, - BeforeUpdate, - Column, - PrimaryGeneratedColumn, -} from 'typeorm'; - -export abstract class BaseEntity extends OrmBaseEntity { - @PrimaryGeneratedColumn() - public id: number; - - @Column({ type: 'timestamptz' }) - public createdAt: Date; - - @Column({ type: 'timestamptz' }) - public updatedAt: Date; - - @BeforeInsert() - public beforeInsert(): void { - const date = new Date(); - this.createdAt = date; - this.updatedAt = date; - } - - @BeforeUpdate() - public beforeUpdate(): void { - const date = new Date(); - this.updatedAt = date; - } -} diff --git a/packages/apps/hufi/job-launcher/server/src/database/database.module.ts b/packages/apps/hufi/job-launcher/server/src/database/database.module.ts deleted file mode 100644 index 5ceaf5084b..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/database/database.module.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { Module } from '@nestjs/common'; -import { ConfigModule, ConfigService } from '@nestjs/config'; -import { TypeOrmModule } from '@nestjs/typeorm'; -import * as path from 'path'; -import { SnakeNamingStrategy } from 'typeorm-naming-strategies'; -import { AuthEntity } from '../modules/auth/auth.entity'; -import { NS } from '../common/constants'; -import { TokenEntity } from '../modules/auth/token.entity'; -import { UserEntity } from '../modules/user/user.entity'; - -import { TypeOrmLoggerModule, TypeOrmLoggerService } from './typeorm'; -import { JobEntity } from '../modules/job/job.entity'; -import { PaymentEntity } from '../modules/payment/payment.entity'; -import { ConfigNames } from '../common/config'; - -@Module({ - imports: [ - TypeOrmModule.forRootAsync({ - imports: [TypeOrmLoggerModule, ConfigModule], - inject: [TypeOrmLoggerService, ConfigService], - useFactory: ( - typeOrmLoggerService: TypeOrmLoggerService, - configService: ConfigService, - ) => { - typeOrmLoggerService.setOptions('all'); - return { - name: 'default', - type: 'postgres', - entities: [ - AuthEntity, - TokenEntity, - UserEntity, - JobEntity, - PaymentEntity, - ], - // We are using migrations, synchronize should be set to false. - synchronize: false, - // Run migrations automatically, - // you can disable this if you prefer running migration manually. - migrationsTableName: NS, - migrationsTransactionMode: 'each', - namingStrategy: new SnakeNamingStrategy(), - logging: - process.env.NODE_ENV === 'development' || - process.env.NODE_ENV === 'staging', - // Allow both start:prod and start:dev to use migrations - // __dirname is either dist or server folder, meaning either - // the compiled js in prod or the ts in dev. - migrations: [path.join(__dirname, '/migrations/**/*{.ts,.js}')], - //"migrations": ["dist/migrations/*{.ts,.js}"], - logger: typeOrmLoggerService, - host: configService.get( - ConfigNames.POSTGRES_HOST, - 'localhost', - ), - port: configService.get(ConfigNames.POSTGRES_PORT, 5432), - username: configService.get( - ConfigNames.POSTGRES_USER, - 'operator', - ), - password: configService.get( - ConfigNames.POSTGRES_PASSWORD, - 'qwerty', - ), - database: configService.get( - ConfigNames.POSTGRES_DATABASE, - 'job-launcher', - ), - keepConnectionAlive: - configService.get(ConfigNames.NODE_ENV) === 'test', - migrationsRun: false, - ssl: - configService - .get(ConfigNames.POSTGRES_SSL) - ?.toLowerCase() === 'true', - }; - }, - }), - ], -}) -export class DatabaseModule {} diff --git a/packages/apps/hufi/job-launcher/server/src/database/migrations/1691485394906-InitialMigration.ts b/packages/apps/hufi/job-launcher/server/src/database/migrations/1691485394906-InitialMigration.ts deleted file mode 100644 index dec52b8e8a..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/database/migrations/1691485394906-InitialMigration.ts +++ /dev/null @@ -1,214 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; -import { NS } from '../../common/constants'; - -export class InitialMigration1691485394906 implements MigrationInterface { - name = 'InitialMigration1691485394906'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.createSchema(NS, true); - await queryRunner.query(` - CREATE TYPE "hmt"."payments_type_enum" AS ENUM('DEPOSIT', 'REFUND', 'WITHDRAWAL') - `); - await queryRunner.query(` - CREATE TYPE "hmt"."payments_source_enum" AS ENUM('FIAT', 'CRYPTO', 'BALANCE') - `); - await queryRunner.query(` - CREATE TYPE "hmt"."payments_status_enum" AS ENUM('PENDING', 'FAILED', 'SUCCEEDED') - `); - await queryRunner.query(` - CREATE TABLE "hmt"."payments" ( - "id" SERIAL NOT NULL, - "created_at" TIMESTAMP WITH TIME ZONE NOT NULL, - "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL, - "transaction" character varying, - "chain_id" integer, - "amount" numeric(30, 18) NOT NULL, - "rate" numeric(30, 18) NOT NULL, - "currency" character varying NOT NULL, - "type" "hmt"."payments_type_enum" NOT NULL, - "source" "hmt"."payments_source_enum" NOT NULL, - "status" "hmt"."payments_status_enum" NOT NULL, - "user_id" integer NOT NULL, - "job_id" integer, - CONSTRAINT "REL_f83af8ea8055b85bde0e095e40" UNIQUE ("job_id"), - CONSTRAINT "PK_197ab7af18c93fbb0c9b28b4a59" PRIMARY KEY ("id") - ) - `); - await queryRunner.query(` - CREATE UNIQUE INDEX "IDX_72b30c486884d4c5be768e0ac9" ON "hmt"."payments" ("transaction") - WHERE ( - chain_Id IS NULL - AND transaction IS NOT NULL - ) - `); - await queryRunner.query(` - CREATE UNIQUE INDEX "IDX_9b5d72797ec3ba4991cba5de4c" ON "hmt"."payments" ("chain_id", "transaction") - WHERE ( - chain_Id IS NOT NULL - AND transaction IS NOT NULL - ) - `); - await queryRunner.query(` - CREATE TYPE "hmt"."jobs_status_enum" AS ENUM( - 'PENDING', - 'PAID', - 'LAUNCHED', - 'FAILED', - 'TO_CANCEL', - 'CANCELED' - ) - `); - await queryRunner.query(` - CREATE TABLE "hmt"."jobs" ( - "id" SERIAL NOT NULL, - "created_at" TIMESTAMP WITH TIME ZONE NOT NULL, - "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL, - "chain_id" integer, - "escrow_address" character varying, - "fee" numeric(30, 18) NOT NULL, - "fund_amount" numeric(30, 18) NOT NULL, - "manifest_url" character varying NOT NULL, - "manifest_hash" character varying NOT NULL, - "status" "hmt"."jobs_status_enum" NOT NULL, - "user_id" integer NOT NULL, - "retries_count" integer NOT NULL DEFAULT '0', - "wait_until" TIMESTAMP WITH TIME ZONE NOT NULL, - CONSTRAINT "PK_cf0a6c42b72fcc7f7c237def345" PRIMARY KEY ("id") - ) - `); - await queryRunner.query(` - CREATE UNIQUE INDEX "IDX_59f6c552b618c432f019500e7c" ON "hmt"."jobs" ("chain_id", "escrow_address") - `); - await queryRunner.query(` - CREATE TYPE "hmt"."tokens_token_type_enum" AS ENUM('EMAIL', 'PASSWORD') - `); - await queryRunner.query(` - CREATE TABLE "hmt"."tokens" ( - "id" SERIAL NOT NULL, - "created_at" TIMESTAMP WITH TIME ZONE NOT NULL, - "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL, - "uuid" uuid NOT NULL DEFAULT uuid_generate_v4(), - "token_type" "hmt"."tokens_token_type_enum" NOT NULL, - "user_id" integer NOT NULL, - CONSTRAINT "UQ_57b0dd7af7c6a0b7d4c3fd5c464" UNIQUE ("uuid"), - CONSTRAINT "REL_8769073e38c365f315426554ca" UNIQUE ("user_id"), - CONSTRAINT "PK_3001e89ada36263dabf1fb6210a" PRIMARY KEY ("id") - ) - `); - await queryRunner.query(` - CREATE TYPE "hmt"."users_type_enum" AS ENUM('OPERATOR', 'REQUESTER') - `); - await queryRunner.query(` - CREATE TYPE "hmt"."users_status_enum" AS ENUM('ACTIVE', 'INACTIVE', 'PENDING') - `); - await queryRunner.query(` - CREATE TABLE "hmt"."users" ( - "id" SERIAL NOT NULL, - "created_at" TIMESTAMP WITH TIME ZONE NOT NULL, - "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL, - "password" character varying NOT NULL, - "email" character varying, - "type" "hmt"."users_type_enum" NOT NULL, - "status" "hmt"."users_status_enum" NOT NULL, - CONSTRAINT "UQ_97672ac88f789774dd47f7c8be3" UNIQUE ("email"), - CONSTRAINT "PK_a3ffb1c0c8416b9fc6f907b7433" PRIMARY KEY ("id") - ) - `); - await queryRunner.query(` - CREATE TABLE "hmt"."auths" ( - "id" SERIAL NOT NULL, - "created_at" TIMESTAMP WITH TIME ZONE NOT NULL, - "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL, - "access_token" character varying NOT NULL, - "refresh_token" character varying NOT NULL, - "user_id" integer NOT NULL, - CONSTRAINT "REL_593ea7ee438b323776029d3185" UNIQUE ("user_id"), - CONSTRAINT "PK_22fc0631a651972ddc9c5a31090" PRIMARY KEY ("id") - ) - `); - await queryRunner.query(` - ALTER TABLE "hmt"."payments" - ADD CONSTRAINT "FK_427785468fb7d2733f59e7d7d39" FOREIGN KEY ("user_id") REFERENCES "hmt"."users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION - `); - await queryRunner.query(` - ALTER TABLE "hmt"."jobs" - ADD CONSTRAINT "FK_9027c8f0ba75fbc1ac46647d043" FOREIGN KEY ("user_id") REFERENCES "hmt"."users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION - `); - await queryRunner.query(` - ALTER TABLE "hmt"."payments" - ADD CONSTRAINT "FK_f83af8ea8055b85bde0e095e400" FOREIGN KEY ("job_id") REFERENCES "hmt"."jobs"("id") ON DELETE NO ACTION ON UPDATE NO ACTION - `); - await queryRunner.query(` - ALTER TABLE "hmt"."tokens" - ADD CONSTRAINT "FK_8769073e38c365f315426554ca5" FOREIGN KEY ("user_id") REFERENCES "hmt"."users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION - `); - await queryRunner.query(` - ALTER TABLE "hmt"."auths" - ADD CONSTRAINT "FK_593ea7ee438b323776029d3185f" FOREIGN KEY ("user_id") REFERENCES "hmt"."users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE "hmt"."auths" DROP CONSTRAINT "FK_593ea7ee438b323776029d3185f" - `); - await queryRunner.query(` - ALTER TABLE "hmt"."tokens" DROP CONSTRAINT "FK_8769073e38c365f315426554ca5" - `); - await queryRunner.query(` - ALTER TABLE "hmt"."jobs" DROP CONSTRAINT "FK_9027c8f0ba75fbc1ac46647d043" - `); - await queryRunner.query(` - ALTER TABLE "hmt"."payments" DROP CONSTRAINT "FK_f83af8ea8055b85bde0e095e400" - `); - await queryRunner.query(` - ALTER TABLE "hmt"."payments" DROP CONSTRAINT "FK_427785468fb7d2733f59e7d7d39" - `); - await queryRunner.query(` - DROP TABLE "hmt"."auths" - `); - await queryRunner.query(` - DROP TABLE "hmt"."users" - `); - await queryRunner.query(` - DROP TYPE "hmt"."users_status_enum" - `); - await queryRunner.query(` - DROP TYPE "hmt"."users_type_enum" - `); - await queryRunner.query(` - DROP TABLE "hmt"."tokens" - `); - await queryRunner.query(` - DROP TYPE "hmt"."tokens_token_type_enum" - `); - await queryRunner.query(` - DROP INDEX "hmt"."IDX_59f6c552b618c432f019500e7c" - `); - await queryRunner.query(` - DROP TABLE "hmt"."jobs" - `); - await queryRunner.query(` - DROP TYPE "hmt"."jobs_status_enum" - `); - await queryRunner.query(` - DROP INDEX "hmt"."IDX_9b5d72797ec3ba4991cba5de4c" - `); - await queryRunner.query(` - DROP INDEX "hmt"."IDX_72b30c486884d4c5be768e0ac9" - `); - await queryRunner.query(` - DROP TABLE "hmt"."payments" - `); - await queryRunner.query(` - DROP TYPE "hmt"."payments_status_enum" - `); - await queryRunner.query(` - DROP TYPE "hmt"."payments_source_enum" - `); - await queryRunner.query(` - DROP TYPE "hmt"."payments_type_enum" - `); - await queryRunner.dropSchema(NS); - } -} diff --git a/packages/apps/hufi/job-launcher/server/src/database/migrations/1696331060184-AddFailedReasonToJobs.ts b/packages/apps/hufi/job-launcher/server/src/database/migrations/1696331060184-AddFailedReasonToJobs.ts deleted file mode 100644 index d70aa78636..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/database/migrations/1696331060184-AddFailedReasonToJobs.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm" - -export class AddFailedReasonToJobs1696331060184 implements MigrationInterface { - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE "hmt"."jobs" - ADD COLUMN failed_reason character varying; - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE "hmt"."jobs" - DROP COLUMN failed_reason; - `); - } - -} diff --git a/packages/apps/hufi/job-launcher/server/src/database/migrations/1696413951581-DropUniqueConstraintAndAddToRefundToJobsStatusEnum.ts b/packages/apps/hufi/job-launcher/server/src/database/migrations/1696413951581-DropUniqueConstraintAndAddToRefundToJobsStatusEnum.ts deleted file mode 100644 index d28493c833..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/database/migrations/1696413951581-DropUniqueConstraintAndAddToRefundToJobsStatusEnum.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm" - -export class DropUniqueConstraint1696413951581 implements MigrationInterface { - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "hmt"."payments" DROP CONSTRAINT "REL_f83af8ea8055b85bde0e095e40"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "hmt"."payments" ADD CONSTRAINT "REL_f83af8ea8055b85bde0e095e40" UNIQUE ("job_id")`); - } - -} diff --git a/packages/apps/hufi/job-launcher/server/src/database/typeorm/index.ts b/packages/apps/hufi/job-launcher/server/src/database/typeorm/index.ts deleted file mode 100644 index 5a07b7c166..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/database/typeorm/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './typeorm-logger.service'; -export * from './typeorm-logger.module'; diff --git a/packages/apps/hufi/job-launcher/server/src/database/typeorm/typeorm-logger.module.ts b/packages/apps/hufi/job-launcher/server/src/database/typeorm/typeorm-logger.module.ts deleted file mode 100644 index b3bc43d760..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/database/typeorm/typeorm-logger.module.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Module, Logger } from '@nestjs/common'; - -import { TypeOrmLoggerService } from './typeorm-logger.service'; - -@Module({ - providers: [Logger, TypeOrmLoggerService], - exports: [TypeOrmLoggerService], -}) -export class TypeOrmLoggerModule {} diff --git a/packages/apps/hufi/job-launcher/server/src/database/typeorm/typeorm-logger.service.ts b/packages/apps/hufi/job-launcher/server/src/database/typeorm/typeorm-logger.service.ts deleted file mode 100644 index f6e47ed3b4..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/database/typeorm/typeorm-logger.service.ts +++ /dev/null @@ -1,97 +0,0 @@ -import * as util from 'util'; -import { - Logger as TypeOrmLogger, - LoggerOptions as TypeOrmLoggerOptions, -} from 'typeorm'; -import { Inject, Injectable, Logger, LoggerService } from '@nestjs/common'; - -@Injectable() -export class TypeOrmLoggerService implements TypeOrmLogger { - private options: TypeOrmLoggerOptions = 'all'; - - constructor(@Inject(Logger) private readonly loggerService: LoggerService) {} - - public setOptions(options: TypeOrmLoggerOptions = 'all'): void { - this.options = options; - } - - logQuery(query: string, parameters?: any[]): void { - if ( - this.options === 'all' || - this.options === true || - (this.options instanceof Array && this.options.indexOf('query') !== -1) - ) { - this.loggerService.log( - `query : ${query} ${this.stringifyParams(parameters)}`, - 'TypeOrm', - ); - } - } - - logQueryError(error: string, query: string, parameters?: any[]): void { - if ( - this.options === 'all' || - this.options === true || - (this.options instanceof Array && this.options.indexOf('error') !== -1) - ) { - this.loggerService.log( - `query failed: ${query} ${this.stringifyParams(parameters)}`, - 'TypeOrm', - ); - this.loggerService.log(`error: ${error}`, 'TypeOrm'); - } - } - - logQuerySlow(time: number, query: string, parameters?: any[]): void { - this.loggerService.log( - `query is slow: ${query} ${this.stringifyParams(parameters)}`, - 'TypeOrm', - ); - this.loggerService.log(`execution time: ${time}`, 'TypeOrm'); - } - - logSchemaBuild(message: string): void { - if ( - this.options === 'all' || - (this.options instanceof Array && this.options.indexOf('schema') !== -1) - ) { - this.loggerService.log(message, 'TypeOrm'); - } - } - - logMigration(message: string): void { - this.loggerService.log(message, 'TypeOrm'); - } - - log(level: 'log' | 'info' | 'warn', message: unknown): void { - switch (level) { - case 'log': - if ( - this.options === 'all' || - (this.options instanceof Array && this.options.indexOf('log') !== -1) - ) - this.loggerService.log(message, 'TypeOrm'); - break; - case 'info': - if ( - this.options === 'all' || - (this.options instanceof Array && this.options.indexOf('info') !== -1) - ) - this.loggerService.log(message, 'TypeOrm'); - break; - case 'warn': - if ( - this.options === 'all' || - (this.options instanceof Array && this.options.indexOf('warn') !== -1) - ) - this.loggerService.warn(message, 'TypeOrm'); - break; - } - } - - protected stringifyParams(parameters: any[] = []): string { - return parameters.length - ? ` -- PARAMETERS: ${util.inspect(parameters)}` - : ''; - } -} diff --git a/packages/apps/hufi/job-launcher/server/src/main.ts b/packages/apps/hufi/job-launcher/server/src/main.ts deleted file mode 100644 index 4f3f352906..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/main.ts +++ /dev/null @@ -1,78 +0,0 @@ -import session from 'express-session'; -import { INestApplication } from '@nestjs/common'; -import { NestFactory } from '@nestjs/core'; -import { ConfigService } from '@nestjs/config'; -import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; -import { json, urlencoded } from 'body-parser'; -import { useContainer } from 'class-validator'; -import helmet from 'helmet'; -import cookieParser from 'cookie-parser'; - -import { AppModule } from './app.module'; -import { ConfigNames } from './common/config'; - -async function bootstrap() { - const app = await NestFactory.create(AppModule, { - cors: true, - }); - - const configService: ConfigService = app.get(ConfigService); - - const baseUrl = configService.get( - ConfigNames.FE_URL, - 'http://localhost:3005', - ); - - app.enableCors({ - origin: - process.env.NODE_ENV === 'development' || - process.env.NODE_ENV === 'staging' - ? [ - `http://localhost:3001`, - `http://127.0.0.1:3001`, - `http://0.0.0.0:3001`, - baseUrl, - ] - : [baseUrl], - credentials: true, - exposedHeaders: ['Content-Disposition'], - }); - - useContainer(app.select(AppModule), { fallbackOnErrors: true }); - - app.use(cookieParser()); - - const sessionSecret = configService.get( - ConfigNames.SESSION_SECRET, - 'session-secret', - ); - - app.use( - session({ - secret: sessionSecret, - resave: false, - saveUninitialized: false, - }), - ); - app.use(json({ limit: '5mb' })); - app.use(urlencoded({ limit: '5mb', extended: true })); - - const config = new DocumentBuilder() - .addBearerAuth() - .setTitle('Hufi Job Launcher API') - .setDescription('Swagger Hufi Job Launcher API') - .setVersion('1.0') - .build(); - const document = SwaggerModule.createDocument(app, config); - SwaggerModule.setup('swagger', app, document); - - const host = configService.get(ConfigNames.HOST, 'localhost'); - const port = +configService.get(ConfigNames.PORT, '5000'); - - app.use(helmet()); - await app.listen(port, host, async () => { - console.info(`API server is running on http://${host}:${port}`); - }); -} - -void bootstrap(); diff --git a/packages/apps/hufi/job-launcher/server/src/modules/auth/auth.controller.ts b/packages/apps/hufi/job-launcher/server/src/modules/auth/auth.controller.ts deleted file mode 100644 index fa9510a1b9..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/auth/auth.controller.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { - Body, - ClassSerializerInterceptor, - Controller, - HttpCode, - Post, - Req, - UseGuards, - UseInterceptors, -} from '@nestjs/common'; - -import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; -import { Public } from '../../common/decorators'; -import { UserCreateDto } from '../user/user.dto'; -import { - AuthDto, - ForgotPasswordDto, - ResendEmailVerificationDto, - RestorePasswordDto, - SignInDto, - VerifyEmailDto, -} from './auth.dto'; -import { AuthService } from './auth.service'; -import { JwtAuthGuard } from '../../common/guards'; -import { RequestWithUser } from '../../common/types'; - -@ApiTags('Auth') -@Controller('/auth') -export class AuthJwtController { - constructor(private readonly authService: AuthService) {} - - @Public() - @Post('/signup') - @UseInterceptors(ClassSerializerInterceptor) - public async signup(@Body() data: UserCreateDto): Promise { - await this.authService.signup(data); - return; - } - - @Public() - @Post('/signin') - @HttpCode(200) - public signin(@Body() data: SignInDto): Promise { - return this.authService.signin(data); - } - - @ApiBearerAuth() - @UseGuards(JwtAuthGuard) - @Post('/refresh') - @HttpCode(200) - async refreshToken(@Req() request: RequestWithUser): Promise { - return this.authService.auth(request.user); - } - - @ApiBearerAuth() - @UseGuards(JwtAuthGuard) - @Post('/logout') - @HttpCode(204) - public async logout(@Req() request: RequestWithUser): Promise { - await this.authService.logout(request.user); - } - - @Public() - @Post('/forgot-password') - @HttpCode(204) - public forgotPassword(@Body() data: ForgotPasswordDto): Promise { - return this.authService.forgotPassword(data); - } - - @Public() - @Post('/restore-password') - @HttpCode(204) - public restorePassword(@Body() data: RestorePasswordDto): Promise { - return this.authService.restorePassword(data); - } - - @Public() - @Post('/email-verification') - @HttpCode(200) - public async emailVerification(@Body() data: VerifyEmailDto): Promise { - await this.authService.emailVerification(data); - } - - @Public() - @Post('/resend-email-verification') - @HttpCode(204) - public resendEmailVerification( - @Body() data: ResendEmailVerificationDto, - ): Promise { - return this.authService.resendEmailVerification(data); - } -} diff --git a/packages/apps/hufi/job-launcher/server/src/modules/auth/auth.dto.ts b/packages/apps/hufi/job-launcher/server/src/modules/auth/auth.dto.ts deleted file mode 100644 index 3bf455691c..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/auth/auth.dto.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { Transform } from 'class-transformer'; -import { IsEmail, IsString, Matches } from 'class-validator'; -import { IsConfirm, IsPassword } from '../../common/validators'; -import { TokenType } from '../auth/token.entity'; -import { UserEntity } from '../user/user.entity'; - -export class ForgotPasswordDto { - @ApiProperty() - @IsEmail() - @Transform(({ value }: { value: string }) => value.toLowerCase()) - public email: string; -} - -export class SignInDto { - @ApiProperty() - @IsEmail() - @Transform(({ value }: { value: string }) => value.toLowerCase()) - public email: string; - - @ApiProperty() - @IsString() - public password: string; -} - -export class ValidatePasswordDto { - @Matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*])(?=.{8,})/, { - message: - 'Password is not strong enough. Password must be at least eight characters long and contain 1 upper, 1 lowercase, 1 number and 1 special character.', - }) - @ApiProperty() - @IsPassword() - public password: string; - - @ApiProperty() - @IsConfirm() - public confirm: string; -} - -export class ResendEmailVerificationDto { - @ApiProperty() - @IsEmail() - @Transform(({ value }: { value: string }) => value.toLowerCase()) - public email: string; -} - -export class RestorePasswordDto extends ValidatePasswordDto { - @ApiProperty() - @IsString() - public token: string; -} - -export class VerifyEmailDto { - @ApiProperty() - @IsString() - public token: string; -} - -export class AuthDto { - public refreshToken: string; - public accessToken: string; -} - -export class AuthCreateDto { - public user: UserEntity; - public refreshToken: string; - public accessToken: string; -} - -export class AuthUpdateDto { - public refreshToken: string; - public accessToken: string; -} - -export class TokenCreateDto { - public tokenType: TokenType; - public user: UserEntity; -} diff --git a/packages/apps/hufi/job-launcher/server/src/modules/auth/auth.entity.ts b/packages/apps/hufi/job-launcher/server/src/modules/auth/auth.entity.ts deleted file mode 100644 index 6a5a108799..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/auth/auth.entity.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { - Column, - Entity, - JoinColumn, - OneToOne, - PrimaryGeneratedColumn, -} from 'typeorm'; - -import { NS } from '../../common/constants'; -import { UserEntity } from '../user/user.entity'; -import { BaseEntity } from '../../database/base.entity'; - -@Entity({ schema: NS, name: 'auths' }) -export class AuthEntity extends BaseEntity { - @PrimaryGeneratedColumn() - public id: number; - - @Column({ type: 'varchar' }) - public accessToken: string; - - @Column({ type: 'varchar' }) - public refreshToken: string; - - @JoinColumn() - @OneToOne(() => UserEntity) - public user: UserEntity; - - @Column({ type: 'int' }) - public userId: number; -} diff --git a/packages/apps/hufi/job-launcher/server/src/modules/auth/auth.module.ts b/packages/apps/hufi/job-launcher/server/src/modules/auth/auth.module.ts deleted file mode 100644 index 26835b7212..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/auth/auth.module.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { Module } from '@nestjs/common'; -import { JwtModule } from '@nestjs/jwt'; -import { ConfigService, ConfigModule } from '@nestjs/config'; -import { TypeOrmModule } from '@nestjs/typeorm'; - -import { UserModule } from '../user/user.module'; -import { JwtHttpStrategy } from './strategy'; -import { AuthService } from './auth.service'; -import { AuthJwtController } from './auth.controller'; -import { AuthEntity } from './auth.entity'; -import { TokenEntity } from './token.entity'; -import { TokenRepository } from './token.repository'; -import { AuthRepository } from './auth.repository'; -import { ConfigNames } from '../../common/config'; -import { SendGridModule } from '../sendgrid/sendgrid.module'; - -@Module({ - imports: [ - UserModule, - ConfigModule, - JwtModule.registerAsync({ - inject: [ConfigService], - imports: [ConfigModule], - useFactory: (configService: ConfigService) => ({ - secret: configService.get(ConfigNames.JWT_SECRET, 'secretkey'), - signOptions: { - expiresIn: configService.get( - ConfigNames.JWT_ACCESS_TOKEN_EXPIRES_IN, - 3600, - ), - }, - }), - }), - TypeOrmModule.forFeature([AuthEntity, TokenEntity]), - SendGridModule, - ], - providers: [JwtHttpStrategy, AuthService, AuthRepository, TokenRepository], - controllers: [AuthJwtController], - exports: [AuthService], -}) -export class AuthModule {} diff --git a/packages/apps/hufi/job-launcher/server/src/modules/auth/auth.repository.ts b/packages/apps/hufi/job-launcher/server/src/modules/auth/auth.repository.ts deleted file mode 100644 index 55cc6f53a4..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/auth/auth.repository.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { Injectable, Logger } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { - FindOptionsWhere, - FindManyOptions, - FindOneOptions, - Repository, - DeleteResult, - UpdateResult, -} from 'typeorm'; -import { AuthEntity } from './auth.entity'; -import { AuthCreateDto, AuthUpdateDto } from './auth.dto'; - -@Injectable() -export class AuthRepository { - private readonly logger = new Logger(AuthRepository.name); - - constructor( - @InjectRepository(AuthEntity) - private readonly authEntityRepository: Repository, - ) {} - - public async update( - where: FindOptionsWhere, - dto: Partial, - ): Promise { - return this.authEntityRepository.update(where, dto); - } - - public async findOne( - where: FindOptionsWhere, - options?: FindOneOptions, - ): Promise { - const authEntity = await this.authEntityRepository.findOne({ - where, - ...options, - }); - - return authEntity; - } - - public find( - where: FindOptionsWhere, - options?: FindManyOptions, - ): Promise { - return this.authEntityRepository.find({ - where, - order: { - createdAt: 'DESC', - }, - ...options, - }); - } - - public async create(dto: AuthCreateDto): Promise { - return this.authEntityRepository.create(dto).save(); - } - - public async delete( - where: FindOptionsWhere, - ): Promise { - return this.authEntityRepository.delete(where); - } -} diff --git a/packages/apps/hufi/job-launcher/server/src/modules/auth/auth.service.spec.ts b/packages/apps/hufi/job-launcher/server/src/modules/auth/auth.service.spec.ts deleted file mode 100644 index bce81af8c5..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/auth/auth.service.spec.ts +++ /dev/null @@ -1,589 +0,0 @@ -import { Test } from '@nestjs/testing'; -import { AuthService } from './auth.service'; -import { TokenRepository } from './token.repository'; -import { ConfigService } from '@nestjs/config'; -import { HttpService } from '@nestjs/axios'; -import { createMock } from '@golevelup/ts-jest'; -import { UserRepository } from '../user/user.repository'; -import { JwtService } from '@nestjs/jwt'; -import { Repository } from 'typeorm'; -import { AuthEntity } from './auth.entity'; -import { UserService } from '../user/user.service'; -import { getRepositoryToken } from '@nestjs/typeorm'; -import { UserEntity } from '../user/user.entity'; -import { AuthRepository } from './auth.repository'; -import { ErrorAuth } from '../../common/constants/errors'; -import { - MOCK_ACCESS_TOKEN, - MOCK_ACCESS_TOKEN_HASHED, - MOCK_EMAIL, - MOCK_EXPIRES_IN, - MOCK_HASHED_PASSWORD, - MOCK_PASSWORD, - MOCK_REFRESH_TOKEN, - MOCK_REFRESH_TOKEN_HASHED, -} from '../../../test/constants'; -import { TokenType } from './token.entity'; -import { v4 } from 'uuid'; -import { PaymentService } from '../payment/payment.service'; -import { UserStatus } from '../../common/enums/user'; -import { SendGridService } from '../sendgrid/sendgrid.service'; -import { NotFoundException, UnauthorizedException } from '@nestjs/common'; -import { SENDGRID_TEMPLATES, SERVICE_NAME } from '../../common/constants'; - -jest.mock('@human-protocol/sdk'); - -jest.mock('uuid', () => ({ - v4: jest.fn().mockReturnValue('mocked-uuid'), -})); - -describe('AuthService', () => { - let authService: AuthService; - let tokenRepository: TokenRepository; - let userService: UserService; - let authRepository: AuthRepository; - let jwtService: JwtService; - let sendGridService: SendGridService; - - beforeAll(async () => { - const mockConfigService: Partial = { - get: jest.fn((key: string) => { - switch (key) { - case 'JWT_ACCESS_TOKEN_EXPIRES_IN': - return MOCK_EXPIRES_IN; - } - }), - }; - - const moduleRef = await Test.createTestingModule({ - providers: [ - AuthService, - UserService, - { - provide: getRepositoryToken(AuthEntity), - useClass: Repository, - }, - { - provide: JwtService, - useValue: { - signAsync: jest.fn(), - }, - }, - { provide: AuthRepository, useValue: createMock() }, - { provide: TokenRepository, useValue: createMock() }, - { provide: UserRepository, useValue: createMock() }, - { provide: ConfigService, useValue: mockConfigService }, - { provide: HttpService, useValue: createMock() }, - { provide: PaymentService, useValue: createMock() }, - { provide: SendGridService, useValue: createMock() }, - ], - }).compile(); - - authService = moduleRef.get(AuthService); - authRepository = moduleRef.get(AuthRepository); - tokenRepository = moduleRef.get(TokenRepository); - userService = moduleRef.get(UserService); - jwtService = moduleRef.get(JwtService); - sendGridService = moduleRef.get(SendGridService); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - describe('signin', () => { - const signInDto = { - email: MOCK_EMAIL, - password: MOCK_PASSWORD, - }; - - const userEntity: Partial = { - id: 1, - email: signInDto.email, - password: MOCK_HASHED_PASSWORD, - status: UserStatus.ACTIVE, - }; - - let getByCredentialsMock: any; - - beforeEach(() => { - getByCredentialsMock = jest.spyOn(userService, 'getByCredentials'); - jest.spyOn(authService, 'auth').mockResolvedValue({ - accessToken: MOCK_ACCESS_TOKEN, - refreshToken: MOCK_REFRESH_TOKEN, - }); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - it('should sign in the user and return the JWT', async () => { - getByCredentialsMock.mockResolvedValue(userEntity as UserEntity); - - const result = await authService.signin(signInDto); - - expect(userService.getByCredentials).toHaveBeenCalledWith( - signInDto.email, - signInDto.password, - ); - expect(authService.auth).toHaveBeenCalledWith(userEntity); - expect(result).toStrictEqual({ - accessToken: MOCK_ACCESS_TOKEN, - refreshToken: MOCK_REFRESH_TOKEN, - }); - }); - - it('should throw UnauthorizedException if user credentials are invalid', async () => { - getByCredentialsMock.mockResolvedValue(undefined); - - await expect(authService.signin(signInDto)).rejects.toThrow( - ErrorAuth.InvalidEmailOrPassword, - ); - - expect(userService.getByCredentials).toHaveBeenCalledWith( - signInDto.email, - signInDto.password, - ); - }); - }); - - describe('signup', () => { - const userCreateDto = { - email: MOCK_EMAIL, - password: MOCK_PASSWORD, - confirm: MOCK_PASSWORD, - }; - - const userEntity: Partial = { - id: 1, - email: userCreateDto.email, - password: MOCK_HASHED_PASSWORD, - }; - - const tokenEntity = { - uuid: v4(), - tokenType: TokenType.EMAIL, - user: userEntity, - }; - - let createUserMock: any, createTokenMock: any; - - beforeEach(() => { - createUserMock = jest.spyOn(userService, 'create'); - createTokenMock = jest.spyOn(tokenRepository, 'create'); - - createUserMock.mockResolvedValue(userEntity); - createTokenMock.mockResolvedValue(tokenEntity); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - it('should create a new user and return the user entity', async () => { - const result = await authService.signup(userCreateDto); - - expect(userService.create).toHaveBeenCalledWith(userCreateDto); - expect(tokenRepository.create).toHaveBeenCalledWith({ - tokenType: TokenType.EMAIL, - user: userEntity, - }); - expect(result).toBe(userEntity); - }); - - it("should call sendGridService sendEmail if user's email is valid", async () => { - sendGridService.sendEmail = jest.fn(); - - await authService.signup(userCreateDto); - - expect(sendGridService.sendEmail).toHaveBeenCalled(); - }); - }); - - describe('logout', () => { - let updateAuth: any; - const userEntity: Partial = { - id: 1, - }; - - const updateResult = {}; - - beforeEach(() => { - updateAuth = jest.spyOn(authRepository, 'update'); - updateAuth.mockResolvedValue(updateResult); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - it('should delete the authentication entities based on email', async () => { - const result = await authService.logout(userEntity as UserEntity); - - const expectedUpdateQuery = { - userId: userEntity.id, - }; - - expect(authRepository.delete).toHaveBeenCalledWith(expectedUpdateQuery); - expect(result).toBe(undefined); - }); - }); - - describe('auth', () => { - const userEntity: Partial = { - id: 1, - email: 'user@example.com', - }; - - const authEntity: Partial = { - id: 1, - }; - - let createAuthMock: any; - let updateAuthMock: any; - let jwtSignMock: any; - let hashTokenMock: any; - let logoutMock: any; - beforeEach(() => { - createAuthMock = jest - .spyOn(authRepository, 'create' as any) - .mockResolvedValueOnce(authEntity); - - updateAuthMock = jest - .spyOn(authRepository, 'update' as any) - .mockResolvedValueOnce(authEntity); - - jwtSignMock = jest - .spyOn(jwtService, 'signAsync') - .mockResolvedValueOnce(MOCK_ACCESS_TOKEN) - .mockResolvedValueOnce(MOCK_REFRESH_TOKEN); - - hashTokenMock = jest - .spyOn(authService, 'hashToken') - .mockReturnValueOnce(MOCK_ACCESS_TOKEN_HASHED) - .mockReturnValueOnce(MOCK_REFRESH_TOKEN_HASHED); - logoutMock = jest - .spyOn(authService, 'logout' as any) - .mockResolvedValueOnce(undefined); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - it('should create authentication tokens and return them', async () => { - const findAuthMock = jest - .spyOn(authRepository, 'findOne' as any) - .mockResolvedValueOnce(undefined); - - const result = await authService.auth(userEntity as UserEntity); - - expect(findAuthMock).toHaveBeenCalledWith({ userId: userEntity.id }); - expect(updateAuthMock).not.toHaveBeenCalled(); - expect(createAuthMock).toHaveBeenCalledWith({ - user: userEntity, - refreshToken: MOCK_REFRESH_TOKEN_HASHED, - accessToken: MOCK_ACCESS_TOKEN_HASHED, - }); - expect(jwtSignMock).toHaveBeenCalledWith({ - email: userEntity.email, - userId: userEntity.id, - }); - expect(jwtSignMock).toHaveBeenLastCalledWith( - { - email: userEntity.email, - userId: userEntity.id, - }, - { - expiresIn: undefined, - }, - ); - expect(logoutMock).not.toHaveBeenCalled(); - expect(hashTokenMock).toHaveBeenCalledWith(MOCK_ACCESS_TOKEN); - expect(hashTokenMock).toHaveBeenLastCalledWith(MOCK_REFRESH_TOKEN); - expect(result).toEqual({ - accessToken: MOCK_ACCESS_TOKEN, - refreshToken: MOCK_REFRESH_TOKEN, - }); - }); - - it('should logout, create authentication tokens and return them', async () => { - const findAuthMock = jest - .spyOn(authRepository, 'findOne' as any) - .mockResolvedValueOnce(authEntity); - - const result = await authService.auth(userEntity as UserEntity); - - expect(findAuthMock).toHaveBeenCalledWith({ userId: userEntity.id }); - expect(updateAuthMock).not.toHaveBeenCalled(); - expect(createAuthMock).toHaveBeenCalledWith({ - user: userEntity, - refreshToken: MOCK_REFRESH_TOKEN_HASHED, - accessToken: MOCK_ACCESS_TOKEN_HASHED, - }); - expect(jwtSignMock).toHaveBeenCalledWith({ - email: userEntity.email, - userId: userEntity.id, - }); - expect(jwtSignMock).toHaveBeenLastCalledWith( - { - email: userEntity.email, - userId: userEntity.id, - }, - { - expiresIn: undefined, - }, - ); - expect(logoutMock).toHaveBeenCalled(); - expect(hashTokenMock).toHaveBeenCalledWith(MOCK_ACCESS_TOKEN); - expect(hashTokenMock).toHaveBeenLastCalledWith(MOCK_REFRESH_TOKEN); - expect(result).toEqual({ - accessToken: MOCK_ACCESS_TOKEN, - refreshToken: MOCK_REFRESH_TOKEN, - }); - }); - }); - - describe('forgotPassword', () => { - const userEntity: Partial = { - id: 1, - email: 'user@example.com', - status: UserStatus.ACTIVE, - }; - - const tokenEntity = { - uuid: v4(), - tokenType: TokenType.EMAIL, - user: userEntity, - }; - - let createTokenMock: any; - - beforeEach(() => { - createTokenMock = jest.spyOn(tokenRepository, 'create'); - - createTokenMock.mockResolvedValue(tokenEntity); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - it('should throw NotFound exception if user is not found', () => { - userService.getByEmail = jest.fn().mockResolvedValueOnce(undefined); - - expect( - authService.forgotPassword({ email: 'user@example.com' }), - ).rejects.toThrow(NotFoundException); - }); - - it('should throw Unauthorized exception if user is not active', () => { - userService.getByEmail = jest - .fn() - .mockResolvedValueOnce({ ...userEntity, status: UserStatus.PENDING }); - - expect( - authService.forgotPassword({ email: 'user@example.com' }), - ).rejects.toThrow(UnauthorizedException); - }); - - it('should remove existing token if it exists', async () => { - const userEntity = { - id: 1, - status: UserStatus.ACTIVE, - } as UserEntity; - - userService.getByEmail = jest.fn().mockResolvedValue(userEntity); - - const existingToken = { - id: 2, - userId: userEntity.id, - tokenType: TokenType.PASSWORD, - remove: jest.fn(), - }; - tokenRepository.findOne = jest.fn().mockResolvedValue(existingToken); - - await authService.forgotPassword({ email: 'user@example.com' }); - - expect(existingToken.remove).toHaveBeenCalled(); - }); - - it('should create a new token and send email', async () => { - userService.getByEmail = jest.fn().mockResolvedValueOnce(userEntity); - - sendGridService.sendEmail = jest.fn(); - const email = 'user@example.com'; - - await authService.forgotPassword({ email }); - - expect(createTokenMock).toHaveBeenCalled(); - expect(sendGridService.sendEmail).toHaveBeenCalledWith( - expect.objectContaining({ - personalizations: [ - { - dynamicTemplateData: { - service_name: SERVICE_NAME, - url: expect.stringContaining( - 'undefined/reset-password?token=mocked-uuid', - ), - }, - to: email, - }, - ], - templateId: SENDGRID_TEMPLATES.resetPassword, - }), - ); - }); - }); - - describe('restorePassword', () => { - const userEntity: Partial = { - id: 1, - email: 'user@example.com', - }; - - const tokenEntity = { - uuid: v4(), - tokenType: TokenType.EMAIL, - user: userEntity, - remove: jest.fn(), - }; - - let findTokenMock: any; - - beforeEach(() => { - findTokenMock = jest.spyOn(tokenRepository, 'findOne'); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - it('should throw NotFound exception if token is not found', () => { - findTokenMock.mockResolvedValueOnce(undefined); - - expect( - authService.restorePassword({ - token: 'token', - password: 'password', - confirm: 'password', - }), - ).rejects.toThrow(NotFoundException); - }); - - it('should update password and send email', async () => { - findTokenMock.mockResolvedValueOnce(tokenEntity); - - userService.updatePassword = jest.fn(); - sendGridService.sendEmail = jest.fn(); - - const updatePasswordMock = jest.spyOn(userService, 'updatePassword'); - - await authService.restorePassword({ - token: 'token', - password: 'password', - confirm: 'password', - }); - - expect(updatePasswordMock).toHaveBeenCalled(); - expect(sendGridService.sendEmail).toHaveBeenCalled(); - expect(tokenEntity.remove).toHaveBeenCalled(); - }); - }); - - describe('emailVerification', () => { - const userEntity: Partial = { - id: 1, - email: 'user@example.com', - }; - - const tokenEntity = { - uuid: v4(), - tokenType: TokenType.EMAIL, - user: userEntity, - remove: jest.fn(), - }; - - let findTokenMock: any; - - beforeEach(() => { - findTokenMock = jest.spyOn(tokenRepository, 'findOne'); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - it('should throw NotFound exception if token is not found', () => { - findTokenMock.mockResolvedValueOnce(undefined); - - expect(authService.emailVerification({ token: 'token' })).rejects.toThrow( - NotFoundException, - ); - }); - - it('should activate user', async () => { - findTokenMock.mockResolvedValueOnce(tokenEntity); - - userService.activate = jest.fn(); - const userActivateMock = jest.spyOn(userService, 'activate'); - - await authService.emailVerification({ token: 'token' }); - - expect(userActivateMock).toHaveBeenCalled(); - expect(tokenEntity.remove).toHaveBeenCalled(); - }); - }); - - describe('resendEmailVerification', () => { - const userEntity: Partial = { - id: 1, - email: 'user@example.com', - status: UserStatus.PENDING, - }; - - let createTokenMock: any; - - beforeEach(() => { - createTokenMock = jest.spyOn(tokenRepository, 'create'); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - it('should throw NotFound exception if user is not found', () => { - userService.getByEmail = jest.fn().mockResolvedValueOnce(undefined); - - expect( - authService.resendEmailVerification({ email: 'user@example.com' }), - ).rejects.toThrow(NotFoundException); - }); - - it('should create token and send email', async () => { - userService.getByEmail = jest.fn().mockResolvedValueOnce(userEntity); - - sendGridService.sendEmail = jest.fn(); - const email = 'user@example.com'; - - await authService.resendEmailVerification({ email }); - - expect(createTokenMock).toHaveBeenCalled(); - expect(sendGridService.sendEmail).toHaveBeenCalledWith( - expect.objectContaining({ - personalizations: [ - { - dynamicTemplateData: { - service_name: SERVICE_NAME, - url: expect.stringContaining('/verify?token=mocked-uuid'), - }, - to: email, - }, - ], - templateId: SENDGRID_TEMPLATES.signup, - }), - ); - }); - }); -}); diff --git a/packages/apps/hufi/job-launcher/server/src/modules/auth/auth.service.ts b/packages/apps/hufi/job-launcher/server/src/modules/auth/auth.service.ts deleted file mode 100644 index e717ba8e9c..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/auth/auth.service.ts +++ /dev/null @@ -1,266 +0,0 @@ -import { - Injectable, - Logger, - NotFoundException, - UnauthorizedException, -} from '@nestjs/common'; -import { JwtService } from '@nestjs/jwt'; - -import { ErrorAuth, ErrorUser } from '../../common/constants/errors'; -import { UserStatus } from '../../common/enums/user'; -import { UserCreateDto } from '../user/user.dto'; -import { UserEntity } from '../user/user.entity'; -import { UserService } from '../user/user.service'; -import { - AuthDto, - ForgotPasswordDto, - ResendEmailVerificationDto, - RestorePasswordDto, - SignInDto, - VerifyEmailDto, -} from './auth.dto'; -import { TokenType } from './token.entity'; -import { TokenRepository } from './token.repository'; -import { AuthRepository } from './auth.repository'; -import { ConfigNames } from '../../common/config'; -import { ConfigService } from '@nestjs/config'; -import { createHash } from 'crypto'; -import { SendGridService } from '../sendgrid/sendgrid.service'; -import { SENDGRID_TEMPLATES, SERVICE_NAME } from '../../common/constants'; - -@Injectable() -export class AuthService { - private readonly logger = new Logger(AuthService.name); - private readonly refreshTokenExpiresIn: string; - private readonly salt: string; - private readonly feURL: string; - - constructor( - private readonly jwtService: JwtService, - private readonly userService: UserService, - private readonly tokenRepository: TokenRepository, - private readonly authRepository: AuthRepository, - private readonly configService: ConfigService, - private readonly sendgridService: SendGridService, - ) { - this.refreshTokenExpiresIn = this.configService.get( - ConfigNames.JWT_REFRESH_TOKEN_EXPIRES_IN, - '100000000', - ); - - this.salt = this.configService.get( - ConfigNames.HASH_SECRET, - 'a328af3fc1dad15342cc3d68936008fa', - ); - this.feURL = this.configService.get( - ConfigNames.FE_URL, - 'http://localhost:3005', - ); - } - - public async signin(data: SignInDto): Promise { - const userEntity = await this.userService.getByCredentials( - data.email, - data.password, - ); - - if (!userEntity) { - throw new NotFoundException(ErrorAuth.InvalidEmailOrPassword); - } - - if (userEntity.status !== UserStatus.ACTIVE) { - throw new UnauthorizedException(ErrorAuth.UserNotActive); - } - - return this.auth(userEntity); - } - - public async signup(data: UserCreateDto): Promise { - const userEntity = await this.userService.create(data); - - const tokenEntity = await this.tokenRepository.create({ - tokenType: TokenType.EMAIL, - user: userEntity, - }); - - await this.sendgridService.sendEmail({ - personalizations: [ - { - to: data.email, - dynamicTemplateData: { - service_name: SERVICE_NAME, - url: `${this.feURL}/verify?token=${tokenEntity.uuid}`, - }, - }, - ], - templateId: SENDGRID_TEMPLATES.signup, - }); - - return userEntity; - } - - public async logout(user: UserEntity): Promise { - await this.authRepository.delete({ userId: user.id }); - } - - public async auth(userEntity: UserEntity): Promise { - const auth = await this.authRepository.findOne({ userId: userEntity.id }); - - const accessToken = await this.jwtService.signAsync({ - email: userEntity.email, - userId: userEntity.id, - }); - - const refreshToken = await this.jwtService.signAsync( - { - email: userEntity.email, - userId: userEntity.id, - }, - { - expiresIn: this.refreshTokenExpiresIn, - }, - ); - - const accessTokenHashed = this.hashToken(accessToken); - const refreshTokenHashed = this.hashToken(refreshToken); - - if (auth) { - await this.logout(userEntity); - } - - await this.authRepository.create({ - user: userEntity, - refreshToken: refreshTokenHashed, - accessToken: accessTokenHashed, - }); - - return { accessToken, refreshToken }; - } - - public async forgotPassword(data: ForgotPasswordDto): Promise { - const userEntity = await this.userService.getByEmail(data.email); - - if (!userEntity) { - throw new NotFoundException(ErrorUser.NotFound); - } - - if (userEntity.status !== UserStatus.ACTIVE) - throw new UnauthorizedException(ErrorAuth.UserNotActive); - - const existingToken = await this.tokenRepository.findOne({ - userId: userEntity.id, - tokenType: TokenType.PASSWORD, - }); - - if (existingToken) { - await existingToken.remove(); - } - - const newTokenEntity = await this.tokenRepository.create({ - tokenType: TokenType.PASSWORD, - user: userEntity, - }); - - await this.sendgridService.sendEmail({ - personalizations: [ - { - to: data.email, - dynamicTemplateData: { - service_name: SERVICE_NAME, - url: `${this.feURL}/reset-password?token=${newTokenEntity.uuid}`, - }, - }, - ], - templateId: SENDGRID_TEMPLATES.resetPassword, - }); - } - - public async restorePassword(data: RestorePasswordDto): Promise { - const tokenEntity = await this.tokenRepository.findOne({ - uuid: data.token, - tokenType: TokenType.PASSWORD, - }); - - if (!tokenEntity) { - throw new NotFoundException('Token not found'); - } - - await this.userService.updatePassword(tokenEntity.user, data); - await this.sendgridService.sendEmail({ - personalizations: [ - { - to: tokenEntity.user.email, - dynamicTemplateData: { - service_name: SERVICE_NAME, - }, - }, - ], - templateId: SENDGRID_TEMPLATES.passwordChanged, - }); - - await tokenEntity.remove(); - - return true; - } - - public async emailVerification(data: VerifyEmailDto): Promise { - const tokenEntity = await this.tokenRepository.findOne({ - uuid: data.token, - tokenType: TokenType.EMAIL, - }); - - if (!tokenEntity) { - throw new NotFoundException('Token not found'); - } - - this.userService.activate(tokenEntity.user); - await tokenEntity.remove(); - } - - public async resendEmailVerification( - data: ResendEmailVerificationDto, - ): Promise { - const userEntity = await this.userService.getByEmail(data.email); - - if (!userEntity || userEntity?.status != UserStatus.PENDING) { - throw new NotFoundException(ErrorUser.NotFound); - } - - const existingToken = await this.tokenRepository.findOne({ - userId: userEntity.id, - tokenType: TokenType.EMAIL, - }); - - if (existingToken) { - await existingToken.remove(); - } - - const newTokenEntity = await this.tokenRepository.create({ - tokenType: TokenType.EMAIL, - user: userEntity, - }); - - await this.sendgridService.sendEmail({ - personalizations: [ - { - to: data.email, - dynamicTemplateData: { - service_name: SERVICE_NAME, - url: `${this.feURL}/verify?token=${newTokenEntity.uuid}`, - }, - }, - ], - templateId: SENDGRID_TEMPLATES.signup, - }); - } - - public hashToken(token: string): string { - const hash = createHash('sha256'); - hash.update(token + this.salt); - return hash.digest('hex'); - } - - public compareToken(token: string, hashedToken: string): boolean { - return this.hashToken(token) === hashedToken; - } -} diff --git a/packages/apps/hufi/job-launcher/server/src/modules/auth/strategy/index.ts b/packages/apps/hufi/job-launcher/server/src/modules/auth/strategy/index.ts deleted file mode 100644 index e38b86c866..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/auth/strategy/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './jwt.http'; diff --git a/packages/apps/hufi/job-launcher/server/src/modules/auth/strategy/jwt.http.ts b/packages/apps/hufi/job-launcher/server/src/modules/auth/strategy/jwt.http.ts deleted file mode 100644 index d36d51000d..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/auth/strategy/jwt.http.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { ExtractJwt, Strategy } from 'passport-jwt'; -import { PassportStrategy } from '@nestjs/passport'; -import { Injectable, Req, UnauthorizedException } from '@nestjs/common'; -import { ConfigService } from '@nestjs/config'; - -import { UserEntity } from '../../user/user.entity'; -import { UserStatus } from '../../../common/enums/user'; -import { ConfigNames } from '../../../common/config'; -import { AuthRepository } from '../auth.repository'; -import { AuthService } from '../auth.service'; -import { JWT_PREFIX } from '../../../common/constants'; - -@Injectable() -export class JwtHttpStrategy extends PassportStrategy(Strategy, 'jwt-http') { - constructor( - private readonly authRepository: AuthRepository, - private readonly authService: AuthService, - private readonly configService: ConfigService, - ) { - super({ - jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), - ignoreExpiration: false, - secretOrKey: configService.get( - ConfigNames.JWT_SECRET, - 'secretkey', - ), - passReqToCallback: true, - }); - } - - public async validate( - @Req() request: any, - payload: { email: string; userId: number }, - ): Promise { - const auth = await this.authRepository.findOne( - { - userId: payload.userId, - }, - { - relations: ['user'], - }, - ); - - if (!auth?.user) { - throw new UnauthorizedException('User not found'); - } - - if (auth?.user.status !== UserStatus.ACTIVE) { - throw new UnauthorizedException('User not active'); - } - - //check that the jwt exists in the database - let jwt = request.headers['authorization'] as string; - if (jwt.toLowerCase().substring(0, JWT_PREFIX.length) === JWT_PREFIX) { - jwt = jwt.substring(JWT_PREFIX.length); - } - if (request.url === '/auth/refresh') { - if (!this.authService.compareToken(jwt, auth?.refreshToken)) { - throw new UnauthorizedException('Token expired'); - } - } else { - if (!this.authService.compareToken(jwt, auth?.accessToken)) { - throw new UnauthorizedException('Token expired'); - } - } - - return auth?.user; - } -} diff --git a/packages/apps/hufi/job-launcher/server/src/modules/auth/token.entity.ts b/packages/apps/hufi/job-launcher/server/src/modules/auth/token.entity.ts deleted file mode 100644 index de132308a4..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/auth/token.entity.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Column, Entity, Generated, JoinColumn, OneToOne } from 'typeorm'; - -import { UserEntity } from '../user/user.entity'; -import { BaseEntity } from '../../database/base.entity'; -import { NS } from '../../common/constants'; -import { IBase } from '../../common/interfaces/base'; - -export enum TokenType { - EMAIL = 'EMAIL', - PASSWORD = 'PASSWORD', -} - -export interface IToken extends IBase { - uuid: string; - tokenType: TokenType; -} - -@Entity({ schema: NS, name: 'tokens' }) -export class TokenEntity extends BaseEntity implements IToken { - @Column({ type: 'uuid', unique: true }) - @Generated('uuid') - public uuid: string; - - @Column({ - type: 'enum', - enum: TokenType, - }) - public tokenType: TokenType; - - @JoinColumn() - @OneToOne(() => UserEntity) - public user: UserEntity; - - @Column({ type: 'int' }) - public userId: number; -} diff --git a/packages/apps/hufi/job-launcher/server/src/modules/auth/token.repository.ts b/packages/apps/hufi/job-launcher/server/src/modules/auth/token.repository.ts deleted file mode 100644 index b6eb942e4d..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/auth/token.repository.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Injectable, Logger, NotFoundException } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { FindOptionsWhere, Repository } from 'typeorm'; -import { TokenEntity } from './token.entity'; -import { TokenCreateDto } from './auth.dto'; -import { ErrorToken } from '../../common/constants/errors'; - -@Injectable() -export class TokenRepository { - private readonly logger = new Logger(TokenRepository.name); - - constructor( - @InjectRepository(TokenEntity) - private readonly tokenEntityRepository: Repository, - ) {} - - public async findOne( - where: FindOptionsWhere, - ): Promise { - const tokenEntity = await this.tokenEntityRepository.findOne({ - where, - relations: ['user'], - }); - - return tokenEntity; - } - - public async create(dto: TokenCreateDto): Promise { - return this.tokenEntityRepository.create(dto).save(); - } -} diff --git a/packages/apps/hufi/job-launcher/server/src/modules/health/health.controller.ts b/packages/apps/hufi/job-launcher/server/src/modules/health/health.controller.ts deleted file mode 100644 index 4f73ef28af..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/health/health.controller.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Controller, Get } from '@nestjs/common'; -import { - HealthCheck, - HealthCheckResult, - HealthCheckService, - HealthIndicatorResult, - TypeOrmHealthIndicator, -} from '@nestjs/terminus'; -import { Public } from '../../common/decorators'; -import { ApiTags } from '@nestjs/swagger'; - -@Public() -@ApiTags('Health') -@Controller('/health') -export class HealthController { - constructor( - private readonly health: HealthCheckService, - private readonly db: TypeOrmHealthIndicator, - ) {} - - @Get() - @HealthCheck() - readiness(): Promise { - return this.health.check([ - async (): Promise => - this.db.pingCheck('database', { - timeout: 5000, - }), - ]); - } -} diff --git a/packages/apps/hufi/job-launcher/server/src/modules/health/health.module.ts b/packages/apps/hufi/job-launcher/server/src/modules/health/health.module.ts deleted file mode 100644 index 9455cee23b..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/health/health.module.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Module } from '@nestjs/common'; -import { TerminusModule } from '@nestjs/terminus'; -import { ConfigModule } from '@nestjs/config'; - -import { HealthController } from './health.controller'; - -@Module({ - imports: [TerminusModule, ConfigModule], - controllers: [HealthController], -}) -export class HealthModule {} diff --git a/packages/apps/hufi/job-launcher/server/src/modules/job/job.controller.ts b/packages/apps/hufi/job-launcher/server/src/modules/job/job.controller.ts deleted file mode 100644 index c68e66edef..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/job/job.controller.ts +++ /dev/null @@ -1,120 +0,0 @@ -import { - BadRequestException, - Body, - Controller, - Get, - Headers, - Param, - Patch, - Post, - Query, - Request, - UseGuards, -} from '@nestjs/common'; -import { ApiBearerAuth, ApiQuery, ApiTags } from '@nestjs/swagger'; -import { JwtAuthGuard, SignatureAuthGuard } from '../../common/guards'; -import { RequestWithUser } from '../../common/types'; -import { - JobListDto, - JobCancelDto, - EscrowFailedWebhookDto, - JobDetailsDto, - JobIdDto, - JobCampaignDto, -} from './job.dto'; -import { JobService } from './job.service'; -import { JobStatusFilter } from '../../common/enums/job'; -import { Public } from '../../common/decorators'; -import { HEADER_SIGNATURE_KEY } from '../../common/constants'; -import { ChainId } from '@human-protocol/sdk'; -import { Role } from '../../common/enums/role'; - -@ApiBearerAuth() -@UseGuards(JwtAuthGuard) -@ApiTags('Job') -@Controller('/job') -export class JobController { - constructor(private readonly jobService: JobService) {} - - @Post('/campaign') - public async createCampaignJob( - @Request() req: RequestWithUser, - @Body() data: JobCampaignDto, - ): Promise { - return this.jobService.createJob(req.user.id, data); - } - - @Get('/list') - @ApiQuery({ - name: 'networks', - required: true, - enum: ChainId, - type: [String], - isArray: true, - }) - @ApiQuery({ name: 'status', required: false, enum: JobStatusFilter }) - @ApiQuery({ name: 'skip', required: false }) - @ApiQuery({ name: 'limit', required: false }) - public async getJobList( - @Request() req: RequestWithUser, - @Query('networks') networks: ChainId[], - @Query('status') status: JobStatusFilter, - @Query('skip') skip = 0, - @Query('limit') limit = 10, - ): Promise { - networks = !Array.isArray(networks) ? [networks] : networks; - return this.jobService.getJobsByStatus( - networks, - req.user.id, - status, - skip, - limit, - ); - } - - @Get('/result') - public async getResult( - @Request() req: RequestWithUser, - @Query('jobId') jobId: number, - ): Promise { - return this.jobService.getResult(req.user.id, jobId); - } - - @Public() - @Get('/cron/launch') - public async launchCronJob(): Promise { - return this.jobService.launchCronJob(); - } - - @Patch('/cancel/:id') - public async cancelJob( - @Request() req: RequestWithUser, - @Param() params: JobCancelDto, - ): Promise { - return this.jobService.requestToCancelJob(req.user.id, params.id); - } - - @Public() - @Get('/cron/cancel') - public async cancelCronJob(): Promise { - return this.jobService.cancelCronJob(); - } - - @Public() - @UseGuards(new SignatureAuthGuard([Role.Exchange])) - @Post('/escrow-failed-webhook') - public async( - @Headers(HEADER_SIGNATURE_KEY) _: string, - @Body() data: EscrowFailedWebhookDto, - ): Promise { - return this.jobService.escrowFailedWebhook(data); - } - - @Get('/details/:id') - public async getDetails( - @Request() req: RequestWithUser, - @Param() params: JobIdDto, - ): Promise { - return this.jobService.getDetails(req.user.id, params.id); - } -} diff --git a/packages/apps/hufi/job-launcher/server/src/modules/job/job.dto.ts b/packages/apps/hufi/job-launcher/server/src/modules/job/job.dto.ts deleted file mode 100644 index 93dfa306f8..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/job/job.dto.ts +++ /dev/null @@ -1,317 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { - IsArray, - IsEnum, - IsNumber, - IsPositive, - IsString, - IsUrl, - IsDate, - IsOptional, - IsNumberString, - Min, - IsNotEmpty, - IsEthereumAddress, -} from 'class-validator'; -import { ChainId } from '@human-protocol/sdk'; -import { JobRequestType, JobStatus } from '../../common/enums/job'; -import { EventType } from '../../common/enums/webhook'; -import { Exchange } from '../../common/enums/exchange'; - -export class JobCreateDto { - public chainId: ChainId; - public userId: number; - public manifestUrl: string; - public manifestHash: string; - public fee: number; - public fundAmount: number; - public status: JobStatus; - public waitUntil: Date; -} - -export class JobDto { - @ApiProperty({ - enum: ChainId, - }) - @IsEnum(ChainId) - @IsOptional() - public chainId?: ChainId; - - @ApiProperty() - @IsString() - public requesterDescription: string; - - @ApiProperty() - @IsNumber() - @IsPositive() - public fundAmount: number; -} - -export class JobCampaignDto extends JobDto { - @ApiProperty() - @IsNumber() - @IsPositive() - public startBlock: number; - - @ApiProperty() - @IsNumber() - @IsPositive() - public endBlock: number; - - @ApiProperty() - @IsString() - @IsEnum(Exchange) - public exchangeName: Exchange; - - @ApiProperty() - @IsString() - public tokenA: string; - - @ApiProperty() - @IsString() - public tokenB: string; - - @ApiProperty() - @IsNumber() - @IsPositive() - public campaignDuration: number; - - @ApiProperty() - @IsNumber() - @IsPositive() - public fundAmount: number; - - @ApiProperty() - @IsEnum(JobRequestType) - type: JobRequestType; -} - -export class JobCancelDto { - @ApiProperty() - @IsNumberString() - public id: number; -} - -export class JobIdDto { - @ApiProperty() - @IsNumberString() - public id: number; -} - -export class JobUpdateDto { - @ApiPropertyOptional({ - enum: JobStatus, - }) - @IsEnum(JobStatus) - public status: JobStatus; -} - -export class JobUpdateDataDto extends JobUpdateDto { - @IsNumber() - public retriesCount: number; - - @IsDate() - public waitUntil: Date; -} -export class StakingDetails { - @IsEthereumAddress() - staker: string; - - @IsNumber() - @Min(0) - allocated: number; - - @IsNumber() - @Min(0) - slashed: number; -} - -export class CampaignManifestDto { - @IsNumber() - @IsPositive() - startBlock: number; - @IsNumber() - @IsPositive() - endBlock: number; - - @IsString() - @IsEnum(Exchange) - exchangeName: Exchange; - - @IsString() - tokenA: string; - @IsString() - tokenB: string; - - @IsNumber() - @IsPositive() - campaignDuration: number; - - @IsNumber() - @IsPositive() - fundAmount: number; - - @IsString() - requesterDescription: string; // address of launcher - - @IsEnum(JobRequestType) - requestType: JobRequestType; -} - -export class ManifestDetails extends CampaignManifestDto { - @IsNumber() - @Min(1) - chainId: number; - - @IsEthereumAddress() - tokenAddress: string; - - @IsEthereumAddress() - requesterAddress: string; - - @IsOptional() - @IsNotEmpty() - @IsString() - exchangeOracleAddress?: string; - - @IsOptional() - @IsNotEmpty() - @IsString() - recordingOracleAddress?: string; - - @IsOptional() - @IsNotEmpty() - @IsString() - reputationOracleAddress?: string; -} - -export class CommonDetails { - @IsEthereumAddress() - escrowAddress: string; - - @IsUrl() - manifestUrl: string; - - @IsString() - manifestHash: string; - - @IsNumber() - @Min(0) - balance: number; - - @IsNumber() - @Min(0) - paidOut: number; - - @IsNumber() - amountOfTasks?: number; - - @IsEnum(JobStatus) - status: JobStatus; -} - -export class JobDetailsDto { - @IsNotEmpty() - details: CommonDetails; - - @IsNotEmpty() - manifest: ManifestDetails; - - @IsNotEmpty() - staking: StakingDetails; -} - -export class SaveManifestDto { - public manifestUrl: string; - public manifestHash: string; -} - -export class SendWebhookDto { - public escrowAddress: string; - public chainId: number; - public eventType: EventType; -} - -export class Label { - @IsString() - name: string; -} - -export class Annotation { - @IsArray() - labels: Label[]; - - @IsString() - description: string; - - @IsString() - user_guide: string; - - @IsEnum(JobRequestType) - type: JobRequestType; - - @IsNumber() - @IsPositive() - job_size: number; - - @IsNumber() - @IsPositive() - max_time: number; -} - -export class Validation { - @IsNumber() - @IsPositive() - min_quality: number; - - @IsNumber() - @IsPositive() - val_size: number; - - @IsString() - gt_url: string; -} - -export class JobListDto { - jobId: number; - escrowAddress?: string; - network: string; - fundAmount: number; - status: JobStatus; -} - -export class EscrowFailedWebhookDto { - @ApiProperty({ - enum: ChainId, - }) - @IsEnum(ChainId) - public chainId: ChainId; - - @ApiProperty() - @IsString() - public escrowAddress: string; - - @ApiProperty() - @IsEnum(EventType) - public eventType: EventType; - - @ApiProperty() - @IsString() - public reason: string; -} - -export class EscrowCancelDto { - txHash: string; - amountRefunded: bigint; -} - -export class CampaignFinalResultDto { - @IsString() - exchangeAddress: string; - - @IsString() - workerIdentifier: string; // worker address for DEX or read only API key encrypted for CEX - - @IsString() - liquidityScore: string; -} diff --git a/packages/apps/hufi/job-launcher/server/src/modules/job/job.entity.ts b/packages/apps/hufi/job-launcher/server/src/modules/job/job.entity.ts deleted file mode 100644 index 68a9037e0c..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/job/job.entity.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { Column, Entity, Index, ManyToOne, OneToMany, OneToOne } from 'typeorm'; - -import { NS } from '../../common/constants'; -import { IJob } from '../../common/interfaces'; -import { JobStatus } from '../../common/enums/job'; -import { BaseEntity } from '../../database/base.entity'; -import { UserEntity } from '../user/user.entity'; -import { PaymentEntity } from '../payment/payment.entity'; - -@Entity({ schema: NS, name: 'jobs' }) -@Index(['chainId', 'escrowAddress'], { unique: true }) -export class JobEntity extends BaseEntity implements IJob { - @Column({ type: 'int', nullable: true }) - public chainId: number; - - @Column({ type: 'varchar', nullable: true }) - public escrowAddress: string; - - @Column({ type: 'decimal', precision: 30, scale: 18 }) - public fee: number; - - @Column({ type: 'decimal', precision: 30, scale: 18 }) - public fundAmount: number; - - @Column({ type: 'varchar' }) - public manifestUrl: string; - - @Column({ type: 'varchar' }) - public manifestHash: string; - - @Column({ - type: 'enum', - enum: JobStatus, - }) - public status: JobStatus; - - @Column({ type: 'varchar', nullable: true }) - public failedReason: string; - - @ManyToOne(() => UserEntity, (user) => user.jobs, { eager: true }) - user: UserEntity; - - @Column({ type: 'int' }) - public userId: number; - - @OneToMany(() => PaymentEntity, (payment) => payment.job) - public payment: PaymentEntity; - - @Column({ type: 'int', default: 0 }) - public retriesCount: number; - - @Column({ type: 'timestamptz' }) - public waitUntil: Date; -} diff --git a/packages/apps/hufi/job-launcher/server/src/modules/job/job.module.ts b/packages/apps/hufi/job-launcher/server/src/modules/job/job.module.ts deleted file mode 100644 index 771e99bae7..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/job/job.module.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Logger, Module } from '@nestjs/common'; -import { TypeOrmModule } from '@nestjs/typeorm'; -import { ConfigModule } from '@nestjs/config'; - -import { JobService } from './job.service'; -import { JobEntity } from './job.entity'; -import { JobController } from './job.controller'; -import { HttpModule } from '@nestjs/axios'; -import { PaymentModule } from '../payment/payment.module'; -import { JobRepository } from './job.repository'; -import { Web3Module } from '../web3/web3.module'; -import { RoutingProtocolService } from './routing-protocol.service'; - -@Module({ - imports: [ - TypeOrmModule.forFeature([JobEntity]), - ConfigModule, - HttpModule, - PaymentModule, - Web3Module, - ], - controllers: [JobController], - providers: [Logger, JobService, JobRepository, RoutingProtocolService], - exports: [JobService], -}) -export class JobModule {} diff --git a/packages/apps/hufi/job-launcher/server/src/modules/job/job.repository.ts b/packages/apps/hufi/job-launcher/server/src/modules/job/job.repository.ts deleted file mode 100644 index 8fd4ff2a9e..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/job/job.repository.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { Injectable, Logger, NotFoundException } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { - FindOptionsWhere, - FindManyOptions, - FindOneOptions, - Repository, - In, -} from 'typeorm'; -import { JobEntity } from './job.entity'; -import { JobCreateDto, JobUpdateDataDto } from './job.dto'; -import { ErrorJob } from '../../common/constants/errors'; -import { JobStatus, JobStatusFilter } from '../../common/enums/job'; -import { ChainId } from '@human-protocol/sdk'; - -@Injectable() -export class JobRepository { - private readonly logger = new Logger(JobRepository.name); - - constructor( - @InjectRepository(JobEntity) - private readonly jobEntityRepository: Repository, - ) {} - - public async updateOne( - where: FindOptionsWhere, - dto: Partial, - ): Promise { - const jobEntity = await this.jobEntityRepository.findOneBy(where); - - if (!jobEntity) { - this.logger.log(ErrorJob.NotFound, JobRepository.name); - throw new NotFoundException(ErrorJob.NotFound); - } - - Object.assign(jobEntity, dto); - return jobEntity.save(); - } - - public async findOne( - where: FindOptionsWhere, - options?: FindOneOptions, - ): Promise { - const jobEntity = await this.jobEntityRepository.findOne({ - where, - ...options, - }); - - if (!jobEntity) { - this.logger.log(ErrorJob.NotFound, JobRepository.name); - throw new NotFoundException(ErrorJob.NotFound); - } - - return jobEntity; - } - - public find( - where: FindOptionsWhere, - options?: FindManyOptions, - ): Promise { - return this.jobEntityRepository.find({ - where, - order: { - createdAt: 'DESC', - }, - ...options, - }); - } - - public async findJobsByStatusFilter( - chainIds: ChainId[], - userId: number, - status: JobStatusFilter, - skip: number, - limit: number, - ): Promise { - const statusFilter = - status === JobStatusFilter.PENDING - ? In([JobStatus.PENDING, JobStatus.PAID]) - : In([status]); - - return await this.find( - { userId, status: statusFilter, chainId: In(chainIds) }, - { skip, take: limit }, - ); - } - - public async findJobsByEscrowAddresses( - userId: number, - escrowAddresses: string[], - ): Promise { - return await this.find({ - userId, - escrowAddress: In(escrowAddresses), - }); - } - - public async create(dto: JobCreateDto): Promise { - return this.jobEntityRepository.create(dto).save(); - } -} diff --git a/packages/apps/hufi/job-launcher/server/src/modules/job/job.service.spec.ts b/packages/apps/hufi/job-launcher/server/src/modules/job/job.service.spec.ts deleted file mode 100644 index 3d0d57567c..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/job/job.service.spec.ts +++ /dev/null @@ -1,1523 +0,0 @@ -import { createMock } from '@golevelup/ts-jest'; -import { - ChainId, - EscrowClient, - StorageClient, - EscrowStatus, - StakingClient, - IAllocation, - EscrowUtils, - NETWORKS, -} from '@human-protocol/sdk'; -import { HttpService } from '@nestjs/axios'; -import { - BadGatewayException, - BadRequestException, - ConflictException, - NotFoundException, -} from '@nestjs/common'; -import { ConfigService } from '@nestjs/config'; -import { Test } from '@nestjs/testing'; -import { - ErrorBucket, - ErrorEscrow, - ErrorJob, - ErrorWeb3, -} from '../../common/constants/errors'; -import { - PaymentSource, - PaymentStatus, - PaymentType, - TokenId, -} from '../../common/enums/payment'; -import { - JobRequestType, - JobStatus, - JobStatusFilter, -} from '../../common/enums/job'; -import { - MOCK_ADDRESS, - MOCK_BUCKET_FILES, - MOCK_BUCKET_NAME, - MOCK_CHAIN_ID, - MOCK_EXCHANGE_ORACLE_ADDRESS, - MOCK_EXCHANGE_ORACLE_FEE, - MOCK_EXCHANGE_ORACLE_WEBHOOK_URL, - MOCK_FILE_HASH, - MOCK_FILE_KEY, - MOCK_FILE_URL, - MOCK_JOB_ID, - MOCK_JOB_LAUNCHER_FEE, - MOCK_PRIVATE_KEY, - MOCK_RECORDING_ORACLE_ADDRESS, - MOCK_RECORDING_ORACLE_FEE, - MOCK_REPUTATION_ORACLE_ADDRESS, - MOCK_REPUTATION_ORACLE_FEE, - MOCK_REQUESTER_DESCRIPTION, - MOCK_REQUESTER_TITLE, - MOCK_SUBMISSION_REQUIRED, - MOCK_TRANSACTION_HASH, - MOCK_USER_ID, -} from '../../../test/constants'; -import { PaymentService } from '../payment/payment.service'; -import { Web3Service } from '../web3/Web3Service'; -import { - FortuneFinalResultDto, - FortuneManifestDto, - CvatManifestDto, - JobFortuneDto, - JobCvatDto, - CvatFinalResultDto, - JobDetailsDto, -} from './job.dto'; -import { JobEntity } from './job.entity'; -import { JobRepository } from './job.repository'; -import { JobService } from './job.service'; - -import { div, mul } from '../../common/utils/decimal'; -import { PaymentRepository } from '../payment/payment.repository'; -import { RoutingProtocolService } from './routing-protocol.service'; -import { EventType } from '../../common/enums/webhook'; -import { PaymentEntity } from '../payment/payment.entity'; -import Decimal from 'decimal.js'; -import { BigNumber, ethers } from 'ethers'; -import { HMToken__factory } from '@human-protocol/core/typechain-types'; - -const rate = 1.5; -jest.mock('@human-protocol/sdk', () => ({ - ...jest.requireActual('@human-protocol/sdk'), - EscrowClient: { - build: jest.fn().mockImplementation(() => ({ - createAndSetupEscrow: jest.fn().mockResolvedValue(MOCK_ADDRESS), - })), - }, - EscrowUtils: { - getEscrows: jest.fn(), - getEscrow: jest.fn(), - }, - StakingClient: { - build: jest.fn().mockImplementation(() => ({ - getAllocation: jest.fn(), - })), - }, - StorageClient: jest.fn().mockImplementation(() => ({ - uploadFiles: jest - .fn() - .mockResolvedValue([ - { key: MOCK_FILE_KEY, url: MOCK_FILE_URL, hash: MOCK_FILE_HASH }, - ]), - listObjects: jest.fn().mockResolvedValue(MOCK_BUCKET_FILES), - })), -})); - -jest.mock('../../common/utils', () => ({ - ...jest.requireActual('../../common/utils'), - getRate: jest.fn().mockImplementation(() => rate), - parseUrl: jest.fn().mockImplementation(() => { - return { - useSSL: false, - host: '127.0.0.1', - port: 9000, - bucket: MOCK_BUCKET_NAME, - }; - }), -})); - -describe('JobService', () => { - let jobService: JobService, - jobRepository: JobRepository, - paymentRepository: PaymentRepository, - paymentService: PaymentService, - createPaymentMock: any, - routingProtocolService: RoutingProtocolService, - web3Service: Web3Service; - - const signerMock = { - address: MOCK_ADDRESS, - getNetwork: jest.fn().mockResolvedValue({ chainId: 1 }), - }; - - beforeEach(async () => { - const mockConfigService: Partial = { - get: jest.fn((key: string) => { - switch (key) { - case 'JOB_LAUNCHER_FEE': - return MOCK_JOB_LAUNCHER_FEE; - case 'EXCHANGE_ORACLE_FEE': - return MOCK_EXCHANGE_ORACLE_FEE; - case 'RECORDING_ORACLE_FEE': - return MOCK_RECORDING_ORACLE_FEE; - case 'REPUTATION_ORACLE_FEE': - return MOCK_REPUTATION_ORACLE_FEE; - case 'WEB3_JOB_LAUNCHER_PRIVATE_KEY': - return MOCK_PRIVATE_KEY; - case 'FORTUNE_EXCHANGE_ORACLE_ADDRESS': - return MOCK_EXCHANGE_ORACLE_ADDRESS; - case 'FORTUNE_RECORDING_ORACLE_ADDRESS': - return MOCK_RECORDING_ORACLE_ADDRESS; - case 'CVAT_EXCHANGE_ORACLE_ADDRESS': - return MOCK_EXCHANGE_ORACLE_ADDRESS; - case 'CVAT_RECORDING_ORACLE_ADDRESS': - return MOCK_RECORDING_ORACLE_ADDRESS; - case 'REPUTATION_ORACLE_ADDRESS': - return MOCK_REPUTATION_ORACLE_ADDRESS; - case 'FORTUNE_EXCHANGE_ORACLE_WEBHOOK_URL': - return MOCK_EXCHANGE_ORACLE_WEBHOOK_URL; - case 'CVAT_EXCHANGE_ORACLE_WEBHOOK_URL': - return MOCK_EXCHANGE_ORACLE_WEBHOOK_URL; - case 'HOST': - return '127.0.0.1'; - case 'PORT': - return 5000; - case 'WEB3_PRIVATE_KEY': - return MOCK_PRIVATE_KEY; - case 'S3_BUCKET': - return MOCK_BUCKET_NAME; - case 'CVAT_JOB_SIZE': - return 1; - } - }), - }; - - const moduleRef = await Test.createTestingModule({ - providers: [ - JobService, - { - provide: Web3Service, - useValue: { - getSigner: jest.fn().mockReturnValue(signerMock), - validateChainId: jest.fn().mockReturnValue(new Error()), - }, - }, - { provide: JobRepository, useValue: createMock() }, - { - provide: PaymentRepository, - useValue: createMock(), - }, - { provide: PaymentService, useValue: createMock() }, - { provide: ConfigService, useValue: mockConfigService }, - { provide: HttpService, useValue: createMock() }, - { - provide: RoutingProtocolService, - useValue: createMock(), - }, - ], - }).compile(); - - jobService = moduleRef.get(JobService); - jobRepository = moduleRef.get(JobRepository); - paymentRepository = moduleRef.get(PaymentRepository); - paymentService = moduleRef.get(PaymentService); - routingProtocolService = moduleRef.get(RoutingProtocolService); - createPaymentMock = jest.spyOn(paymentRepository, 'create'); - web3Service = moduleRef.get(Web3Service); - }); - - describe('createJob', () => { - const userId = 1; - const jobId = 123; - const fortuneJobDto: JobFortuneDto = { - chainId: MOCK_CHAIN_ID, - submissionsRequired: MOCK_SUBMISSION_REQUIRED, - requesterTitle: MOCK_REQUESTER_TITLE, - requesterDescription: MOCK_REQUESTER_DESCRIPTION, - fundAmount: 10, - }; - - let getUserBalanceMock: any; - - beforeEach(() => { - getUserBalanceMock = jest.spyOn(paymentService, 'getUserBalance'); - createPaymentMock.mockResolvedValue(true); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('should create a job successfully', async () => { - const fundAmount = 10; - const fee = (MOCK_JOB_LAUNCHER_FEE / 100) * fundAmount; - - const userBalance = 25; - getUserBalanceMock.mockResolvedValue(userBalance); - - const mockJobEntity: Partial = { - id: jobId, - userId: userId, - chainId: ChainId.LOCALHOST, - manifestUrl: MOCK_FILE_URL, - manifestHash: MOCK_FILE_HASH, - escrowAddress: MOCK_ADDRESS, - fee, - fundAmount, - status: JobStatus.PENDING, - save: jest.fn().mockResolvedValue(true), - }; - - jobRepository.create = jest.fn().mockResolvedValue(mockJobEntity); - - await jobService.createJob(userId, JobRequestType.FORTUNE, fortuneJobDto); - - expect(paymentService.getUserBalance).toHaveBeenCalledWith(userId); - expect(paymentRepository.create).toHaveBeenCalledWith({ - userId, - jobId, - source: PaymentSource.BALANCE, - type: PaymentType.WITHDRAWAL, - currency: TokenId.HMT, - amount: -mul(fundAmount + fee, rate), - rate: div(1, rate), - status: PaymentStatus.SUCCEEDED, - }); - expect(jobRepository.create).toHaveBeenCalledWith({ - chainId: fortuneJobDto.chainId, - userId, - manifestUrl: expect.any(String), - manifestHash: expect.any(String), - fee: mul(fee, rate), - fundAmount: mul(fundAmount, rate), - status: JobStatus.PENDING, - waitUntil: expect.any(Date), - }); - }); - - it('should create a fortune job successfully on network selected from round robin logic', async () => { - const fundAmount = 10; - const fee = (MOCK_JOB_LAUNCHER_FEE / 100) * fundAmount; - - const userBalance = 25; - getUserBalanceMock.mockResolvedValue(userBalance); - - jest - .spyOn(routingProtocolService, 'selectNetwork') - .mockReturnValue(ChainId.MOONBEAM); - - await jobService.createJob(userId, JobRequestType.FORTUNE, { - ...fortuneJobDto, - chainId: undefined, - }); - - expect(paymentService.getUserBalance).toHaveBeenCalledWith(userId); - expect(jobRepository.create).toHaveBeenCalledWith({ - chainId: ChainId.MOONBEAM, - userId, - manifestUrl: expect.any(String), - manifestHash: expect.any(String), - fee: mul(fee, rate), - fundAmount: mul(fundAmount, rate), - status: JobStatus.PENDING, - waitUntil: expect.any(Date), - }); - }); - - it('should throw an exception for invalid chain id provided', async () => { - web3Service.validateChainId = jest.fn(() => { - throw new Error(ErrorWeb3.InvalidTestnetChainId); - }); - - await expect( - jobService.createJob(userId, JobRequestType.FORTUNE, fortuneJobDto), - ).rejects.toThrowError(ErrorWeb3.InvalidTestnetChainId); - }); - - it('should throw an exception for insufficient user balance', async () => { - const userBalance = 1; - jest - .spyOn(paymentService, 'getUserBalance') - .mockResolvedValue(userBalance); - - getUserBalanceMock.mockResolvedValue(userBalance); - - await expect( - jobService.createJob(userId, JobRequestType.FORTUNE, fortuneJobDto), - ).rejects.toThrowError(ErrorJob.NotEnoughFunds); - }); - - it('should throw an exception if job entity creation fails', async () => { - const userBalance = 25; - - getUserBalanceMock.mockResolvedValue(userBalance); - - jest.spyOn(jobRepository, 'create').mockResolvedValue(undefined!); - - await expect( - jobService.createJob(userId, JobRequestType.FORTUNE, fortuneJobDto), - ).rejects.toThrowError(ErrorJob.NotCreated); - }); - }); - - describe('calculateJobBounty', () => { - it('should calculate the job bounty correctly', async () => { - const fundAmount = 0.013997056833333334; - const result = await jobService['calculateJobBounty']( - MOCK_FILE_URL, - fundAmount, - ); - - expect(result).toEqual('0.002332842805555555'); - }); - }); - - describe('createJob with image label binary type', () => { - const userId = 1; - const jobId = 123; - - const imageLabelBinaryJobDto: JobCvatDto = { - chainId: MOCK_CHAIN_ID, - dataUrl: MOCK_FILE_URL, - labels: ['cat', 'dog'], - requesterDescription: MOCK_REQUESTER_DESCRIPTION, - minQuality: 0.95, - fundAmount: 10, - gtUrl: '', - userGuide: MOCK_FILE_URL, - type: JobRequestType.IMAGE_POINTS, - }; - - let getUserBalanceMock: any; - - beforeEach(() => { - getUserBalanceMock = jest.spyOn(paymentService, 'getUserBalance'); - createPaymentMock.mockResolvedValue(true); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('should create a job successfully', async () => { - const fundAmount = 10; - const fee = (MOCK_JOB_LAUNCHER_FEE / 100) * fundAmount; - - const userBalance = 25; - getUserBalanceMock.mockResolvedValue(userBalance); - - const mockJobEntity: Partial = { - id: jobId, - userId: userId, - chainId: ChainId.LOCALHOST, - manifestUrl: MOCK_FILE_URL, - manifestHash: MOCK_FILE_HASH, - escrowAddress: MOCK_ADDRESS, - fee, - fundAmount, - status: JobStatus.PENDING, - save: jest.fn().mockResolvedValue(true), - }; - - jobRepository.create = jest.fn().mockResolvedValue(mockJobEntity); - - await jobService.createJob( - userId, - JobRequestType.IMAGE_POINTS, - imageLabelBinaryJobDto, - ); - - expect(paymentService.getUserBalance).toHaveBeenCalledWith(userId); - expect(paymentRepository.create).toHaveBeenCalledWith({ - userId, - jobId, - source: PaymentSource.BALANCE, - type: PaymentType.WITHDRAWAL, - currency: TokenId.HMT, - amount: -mul(fundAmount + fee, rate), - rate: div(1, rate), - status: PaymentStatus.SUCCEEDED, - }); - expect(jobRepository.create).toHaveBeenCalledWith({ - chainId: imageLabelBinaryJobDto.chainId, - userId, - manifestUrl: expect.any(String), - manifestHash: expect.any(String), - fee: mul(fee, rate), - fundAmount: mul(fundAmount, rate), - status: JobStatus.PENDING, - waitUntil: expect.any(Date), - }); - }); - - it('should create a fortune job successfully on network selected from round robin logic', async () => { - const fundAmount = imageLabelBinaryJobDto.fundAmount; - const fee = (MOCK_JOB_LAUNCHER_FEE / 100) * fundAmount; - - const userBalance = 25; - getUserBalanceMock.mockResolvedValue(userBalance); - - jest - .spyOn(routingProtocolService, 'selectNetwork') - .mockReturnValue(ChainId.MOONBEAM); - - await jobService.createJob(userId, JobRequestType.IMAGE_POINTS, { - ...imageLabelBinaryJobDto, - chainId: undefined, - }); - - expect(paymentService.getUserBalance).toHaveBeenCalledWith(userId); - expect(jobRepository.create).toHaveBeenCalledWith({ - chainId: ChainId.MOONBEAM, - userId, - manifestUrl: expect.any(String), - manifestHash: expect.any(String), - fee: mul(fee, rate), - fundAmount: mul(fundAmount, rate), - status: JobStatus.PENDING, - waitUntil: expect.any(Date), - }); - }); - - it('should throw an exception for invalid chain id provided', async () => { - web3Service.validateChainId = jest.fn(() => { - throw new Error(ErrorWeb3.InvalidTestnetChainId); - }); - - await expect( - jobService.createJob( - userId, - JobRequestType.IMAGE_POINTS, - imageLabelBinaryJobDto, - ), - ).rejects.toThrowError(ErrorWeb3.InvalidTestnetChainId); - }); - - it('should throw an exception for insufficient user balance', async () => { - const userBalance = 1; - - jest - .spyOn(paymentService, 'getUserBalance') - .mockResolvedValue(userBalance); - - getUserBalanceMock.mockResolvedValue(userBalance); - - await expect( - jobService.createJob( - userId, - JobRequestType.IMAGE_POINTS, - imageLabelBinaryJobDto, - ), - ).rejects.toThrowError(ErrorJob.NotEnoughFunds); - }); - - it('should throw an exception if job entity creation fails', async () => { - const userBalance = 100; - - getUserBalanceMock.mockResolvedValue(userBalance); - - jest.spyOn(jobRepository, 'create').mockResolvedValue(undefined!); - - await expect( - jobService.createJob( - userId, - JobRequestType.IMAGE_POINTS, - imageLabelBinaryJobDto, - ), - ).rejects.toThrowError(ErrorJob.NotCreated); - }); - }); - - describe('launchJob', () => { - const chainId = ChainId.LOCALHOST; - - it('should launch a job successfully', async () => { - const fundAmount = 10; - const fee = (MOCK_JOB_LAUNCHER_FEE / 100) * fundAmount; - - const mockJobEntity: Partial = { - chainId, - manifestUrl: MOCK_FILE_URL, - manifestHash: MOCK_FILE_HASH, - fee, - fundAmount, - status: JobStatus.PENDING, - save: jest.fn().mockResolvedValue(true), - userId: 1, - }; - - const manifest: FortuneManifestDto = { - submissionsRequired: 10, - requesterTitle: MOCK_REQUESTER_TITLE, - requesterDescription: MOCK_REQUESTER_DESCRIPTION, - fundAmount, - requestType: JobRequestType.FORTUNE, - }; - - StorageClient.downloadFileFromUrl = jest.fn().mockReturnValue(manifest); - - const jobEntityResult = await jobService.launchJob( - mockJobEntity as JobEntity, - ); - - mockJobEntity.escrowAddress = MOCK_ADDRESS; - expect(jobEntityResult).toMatchObject(mockJobEntity); - expect(mockJobEntity.save).toHaveBeenCalled(); - }); - - it('should handle error during job launch', async () => { - (EscrowClient.build as any).mockImplementation(() => ({ - createAndSetupEscrow: jest.fn().mockRejectedValue(new Error()), - })); - - const mockJobEntity: Partial = { - chainId: 1, - manifestUrl: MOCK_FILE_URL, - manifestHash: MOCK_FILE_HASH, - status: JobStatus.PENDING, - save: jest.fn().mockResolvedValue(true), - }; - - await expect( - jobService.launchJob(mockJobEntity as JobEntity), - ).rejects.toThrow(); - }); - }); - - describe('fundJob', () => { - const chainId = ChainId.LOCALHOST; - - it('should fund a job successfully', async () => { - (EscrowClient.build as any).mockImplementation(() => ({ - fund: jest.fn(), - })); - - const fundAmount = 10; - const fee = (MOCK_JOB_LAUNCHER_FEE / 100) * fundAmount; - - const mockJobEntity: Partial = { - chainId, - manifestUrl: MOCK_FILE_URL, - manifestHash: MOCK_FILE_HASH, - fee, - fundAmount, - status: JobStatus.PAID, - save: jest.fn().mockResolvedValue(true), - }; - - const jobEntityResult = await jobService.fundJob( - mockJobEntity as JobEntity, - ); - - mockJobEntity.escrowAddress = MOCK_ADDRESS; - expect(jobEntityResult).toMatchObject(mockJobEntity); - expect(jobEntityResult.status).toBe(JobStatus.LAUNCHED); - expect(mockJobEntity.save).toHaveBeenCalled(); - }); - - it('should handle error during job fund', async () => { - (EscrowClient.build as any).mockImplementation(() => ({ - fund: jest.fn().mockRejectedValue(new Error()), - })); - - const mockJobEntity: Partial = { - chainId: 1, - manifestUrl: MOCK_FILE_URL, - manifestHash: MOCK_FILE_HASH, - status: JobStatus.PENDING, - save: jest.fn().mockResolvedValue(true), - }; - - await expect( - jobService.fundJob(mockJobEntity as JobEntity), - ).rejects.toThrow(); - }); - }); - - describe('requestToCancelJob', () => { - const jobId = 1; - const userId = 123; - - it('should cancel the job', async () => { - const escrowAddress = MOCK_ADDRESS; - const mockJobEntity: Partial = { - id: jobId, - userId, - status: JobStatus.LAUNCHED, - escrowAddress, - chainId: ChainId.LOCALHOST, - save: jest.fn().mockResolvedValue(true), - }; - - jobRepository.findOne = jest.fn().mockResolvedValue(mockJobEntity); - - const result = await jobService.requestToCancelJob(userId, jobId); - - expect(result).toEqual(true); - expect(jobRepository.findOne).toHaveBeenCalledWith({ id: jobId, userId }); - expect(mockJobEntity.save).toHaveBeenCalled(); - }); - - it('should throw not found exception if job not found', async () => { - jobRepository.findOne = jest.fn().mockResolvedValue(undefined); - - await expect( - jobService.requestToCancelJob(userId, jobId), - ).rejects.toThrow(NotFoundException); - }); - }); - - describe('cancelCronJob', () => { - let escrowClientMock: any, - getManifestMock: any, - jobSaveMock: any, - findOneJobMock: any, - findOnePaymentMock: any, - buildMock: any, - sendWebhookMock: any, - jobEntityMock: Partial, - paymentEntityMock: Partial; - - beforeEach(() => { - escrowClientMock = { - cancel: jest.fn().mockResolvedValue(undefined), - getStatus: jest.fn().mockResolvedValue(EscrowStatus.Launched), - getBalance: jest.fn().mockResolvedValue(new Decimal(10)), - }; - - jobEntityMock = { - status: JobStatus.TO_CANCEL, - fundAmount: 100, - userId: 1, - id: 1, - manifestUrl: MOCK_FILE_URL, - escrowAddress: MOCK_ADDRESS, - chainId: ChainId.LOCALHOST, - save: jest.fn(), - }; - - paymentEntityMock = { - chainId: 1, - jobId: jobEntityMock.id, - status: PaymentStatus.SUCCEEDED, - save: jest.fn(), - }; - - getManifestMock = jest.spyOn(jobService, 'getManifest'); - jobSaveMock = jest.spyOn(jobEntityMock, 'save'); - findOneJobMock = jest.spyOn(jobRepository, 'findOne'); - findOnePaymentMock = jest.spyOn(paymentRepository, 'findOne'); - buildMock = jest.spyOn(EscrowClient, 'build'); - sendWebhookMock = jest.spyOn(jobService, 'sendWebhook'); - findOnePaymentMock.mockResolvedValueOnce( - paymentEntityMock as PaymentEntity, - ); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - it('should return undefined when no job entity is found', async () => { - findOneJobMock.mockResolvedValue(null); - - const result = await jobService.cancelCronJob(); - - expect(result).toBeUndefined(); - }); - - it('should return true when the job is successfully canceled', async () => { - findOneJobMock.mockResolvedValue(jobEntityMock as any); - - jest - .spyOn(jobService, 'processEscrowCancellation') - .mockResolvedValueOnce({ - txHash: MOCK_TRANSACTION_HASH, - amountRefunded: BigNumber.from(1), - }); - const manifestMock = { - requestType: JobRequestType.FORTUNE, - }; - jobService.getManifest = jest.fn().mockResolvedValue(manifestMock); - sendWebhookMock.mockResolvedValue(true); - - const result = await jobService.cancelCronJob(); - - expect(result).toBeTruthy(); - expect(jobService.processEscrowCancellation).toHaveBeenCalledWith( - jobEntityMock, - ); - expect(jobEntityMock.save).toHaveBeenCalled(); - }); - - it('should not call process escrow cancellation when escrowAddress is not present', async () => { - const jobEntityWithoutEscrow = { - ...jobEntityMock, - escrowAddress: undefined, - }; - - jest - .spyOn(jobRepository, 'findOne') - .mockResolvedValueOnce(jobEntityWithoutEscrow as any); - jest - .spyOn(jobService, 'processEscrowCancellation') - .mockResolvedValueOnce(undefined as any); - const manifestMock = { - requestType: JobRequestType.FORTUNE, - }; - jobService.getManifest = jest.fn().mockResolvedValue(manifestMock); - sendWebhookMock.mockResolvedValue(true); - - expect(await jobService.cancelCronJob()).toBe(true); - expect(jobService.processEscrowCancellation).toHaveBeenCalledTimes(0); - }); - - it('should throw bad request exception if escrowStatus is Complete', async () => { - (EscrowClient.build as any).mockImplementation(() => ({ - getStatus: jest.fn().mockResolvedValue(EscrowStatus.Complete), - })); - - await expect( - jobService.processEscrowCancellation(jobEntityMock as any), - ).rejects.toThrow(BadRequestException); - }); - - it('should throw bad request exception if escrowStatus is Paid', async () => { - (EscrowClient.build as any).mockImplementation(() => ({ - getStatus: jest.fn().mockResolvedValue(EscrowStatus.Paid), - })); - - await expect( - jobService.processEscrowCancellation(jobEntityMock as any), - ).rejects.toThrow(BadRequestException); - }); - - it('should throw bad request exception if escrowStatus is Cancelled', async () => { - (EscrowClient.build as any).mockImplementation(() => ({ - getStatus: jest.fn().mockResolvedValue(EscrowStatus.Cancelled), - })); - - await expect( - jobService.processEscrowCancellation(jobEntityMock as any), - ).rejects.toThrow(BadRequestException); - }); - - it('should throw bad request exception if escrow balance is zero', async () => { - (EscrowClient.build as any).mockImplementation(() => ({ - getStatus: jest.fn().mockResolvedValue(EscrowStatus.Launched), - getBalance: jest.fn().mockResolvedValue({ eq: () => true }), - })); - - await expect( - jobService.processEscrowCancellation(jobEntityMock as any), - ).rejects.toThrow(BadRequestException); - }); - }); - - describe('saveManifest with fortune request type', () => { - const fortuneManifestParams = { - requestType: JobRequestType.FORTUNE, - submissionsRequired: MOCK_SUBMISSION_REQUIRED, - requesterDescription: MOCK_REQUESTER_DESCRIPTION, - fundAmount: 10, - requesterTitle: MOCK_REQUESTER_TITLE, - }; - - let uploadFilesMock: any; - - beforeEach(() => { - uploadFilesMock = jest.spyOn(jobService.storageClient, 'uploadFiles'); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('should save the manifest and return the manifest URL and hash', async () => { - uploadFilesMock.mockResolvedValue([ - { - url: MOCK_FILE_URL, - hash: MOCK_FILE_HASH, - }, - ]); - - const result = await jobService.saveManifest(fortuneManifestParams); - - expect(result).toEqual({ - manifestUrl: MOCK_FILE_URL, - manifestHash: MOCK_FILE_HASH, - }); - expect(jobService.storageClient.uploadFiles).toHaveBeenCalledWith( - [fortuneManifestParams], - MOCK_BUCKET_NAME, - ); - }); - - it('should throw an error if the manifest file fails to upload', async () => { - const uploadError = new Error(ErrorBucket.UnableSaveFile); - - uploadFilesMock.mockRejectedValue(uploadError); - - await expect( - jobService.saveManifest(fortuneManifestParams), - ).rejects.toThrowError( - new BadGatewayException(ErrorBucket.UnableSaveFile), - ); - expect(jobService.storageClient.uploadFiles).toHaveBeenCalledWith( - [fortuneManifestParams], - MOCK_BUCKET_NAME, - ); - }); - - it('should rethrow any other errors encountered', async () => { - const errorMessage = 'Something went wrong'; - const uploadError = new Error(errorMessage); - - uploadFilesMock.mockRejectedValue(uploadError); - - await expect( - jobService.saveManifest(fortuneManifestParams), - ).rejects.toThrowError(new Error(errorMessage)); - expect(jobService.storageClient.uploadFiles).toHaveBeenCalledWith( - [fortuneManifestParams], - MOCK_BUCKET_NAME, - ); - }); - }); - - describe('saveManifest with image label binary request type', () => { - const manifest: CvatManifestDto = { - data: { - data_url: MOCK_FILE_URL, - }, - annotation: { - labels: [{ name: 'label1' }], - description: MOCK_REQUESTER_DESCRIPTION, - user_guide: MOCK_FILE_URL, - type: JobRequestType.IMAGE_POINTS, - job_size: 10, - max_time: 300, - }, - validation: { - min_quality: 1, - val_size: 2, - gt_url: '', - }, - job_bounty: '1', - }; - - let uploadFilesMock: any; - - beforeEach(() => { - uploadFilesMock = jest.spyOn(jobService.storageClient, 'uploadFiles'); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('should save the manifest and return the manifest URL and hash', async () => { - uploadFilesMock.mockResolvedValue([ - { - url: MOCK_FILE_URL, - hash: MOCK_FILE_HASH, - }, - ]); - - const result = await jobService.saveManifest(manifest); - - expect(result).toEqual({ - manifestUrl: MOCK_FILE_URL, - manifestHash: MOCK_FILE_HASH, - }); - expect(jobService.storageClient.uploadFiles).toHaveBeenCalledWith( - [manifest], - MOCK_BUCKET_NAME, - ); - }); - - it('should throw an error if the manifest file fails to upload', async () => { - const uploadError = new Error(ErrorBucket.UnableSaveFile); - - uploadFilesMock.mockRejectedValue(uploadError); - - await expect(jobService.saveManifest(manifest)).rejects.toThrowError( - new BadGatewayException(ErrorBucket.UnableSaveFile), - ); - expect(jobService.storageClient.uploadFiles).toHaveBeenCalledWith( - [manifest], - MOCK_BUCKET_NAME, - ); - }); - - it('should rethrow any other errors encountered', async () => { - const errorMessage = 'Something went wrong'; - const uploadError = new Error(errorMessage); - - uploadFilesMock.mockRejectedValue(uploadError); - - await expect(jobService.saveManifest(manifest)).rejects.toThrowError( - new Error(errorMessage), - ); - expect(jobService.storageClient.uploadFiles).toHaveBeenCalledWith( - [manifest], - MOCK_BUCKET_NAME, - ); - }); - }); - - describe('getManifest', () => { - it('should download and return the manifest', async () => { - const fundAmount = 10; - - const manifest: FortuneManifestDto = { - submissionsRequired: 10, - requesterTitle: MOCK_REQUESTER_TITLE, - requesterDescription: MOCK_REQUESTER_DESCRIPTION, - fundAmount, - requestType: JobRequestType.FORTUNE, - }; - - StorageClient.downloadFileFromUrl = jest.fn().mockReturnValue(manifest); - - const result = await jobService.getManifest(MOCK_FILE_URL); - - expect(StorageClient.downloadFileFromUrl).toHaveBeenCalledWith( - MOCK_FILE_URL, - ); - expect(result).toEqual(manifest); - }); - - it('should throw a NotFoundException if the manifest is not found', async () => { - StorageClient.downloadFileFromUrl = jest.fn().mockResolvedValue(null); - - await expect(jobService.getManifest(MOCK_FILE_URL)).rejects.toThrowError( - new NotFoundException(ErrorJob.ManifestNotFound), - ); - }); - }); - - describe('getManifest', () => { - let downloadFileFromUrlMock: any; - - beforeEach(() => { - downloadFileFromUrlMock = jest.spyOn( - StorageClient, - 'downloadFileFromUrl', - ); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('should download and return the manifest', async () => { - const fundAmount = 10; - - const manifest: FortuneManifestDto = { - submissionsRequired: 10, - requesterTitle: MOCK_REQUESTER_TITLE, - requesterDescription: MOCK_REQUESTER_DESCRIPTION, - fundAmount, - requestType: JobRequestType.FORTUNE, - }; - - downloadFileFromUrlMock.mockReturnValue(manifest); - - const result = await jobService.getManifest(MOCK_FILE_URL); - - expect(StorageClient.downloadFileFromUrl).toHaveBeenCalledWith( - MOCK_FILE_URL, - ); - expect(result).toEqual(manifest); - }); - - it('should throw a NotFoundException if the manifest is not found', async () => { - downloadFileFromUrlMock.mockResolvedValue(null); - - await expect(jobService.getManifest(MOCK_FILE_URL)).rejects.toThrowError( - new NotFoundException(ErrorJob.ManifestNotFound), - ); - expect(StorageClient.downloadFileFromUrl).toHaveBeenCalledWith( - MOCK_FILE_URL, - ); - }); - }); - - describe('getResult', () => { - let downloadFileFromUrlMock: any; - - beforeEach(() => { - downloadFileFromUrlMock = jest.spyOn( - StorageClient, - 'downloadFileFromUrl', - ); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('should download and return the fortune result', async () => { - const fortuneResult: FortuneFinalResultDto = { - exchangeAddress: MOCK_ADDRESS, - workerAddress: MOCK_ADDRESS, - solution: 'good', - }; - - (EscrowClient.build as any).mockImplementation(() => ({ - getResultsUrl: jest.fn().mockResolvedValue(MOCK_FILE_URL), - })); - downloadFileFromUrlMock.mockResolvedValue(fortuneResult); - - const result = await jobService.getResult(MOCK_USER_ID, MOCK_JOB_ID); - - expect(StorageClient.downloadFileFromUrl).toHaveBeenCalledWith( - MOCK_FILE_URL, - ); - expect(result).toEqual(fortuneResult); - }); - - it('should download and return the image binary result', async () => { - const imageBinaryResult: CvatFinalResultDto = { - url: 'https://example.com', - final_answer: 'good', - correct: ['good', 'good', 'good'], - wrong: [''], - }; - - downloadFileFromUrlMock.mockResolvedValue(imageBinaryResult); - - const result = await jobService.getResult(MOCK_USER_ID, MOCK_JOB_ID); - - expect(StorageClient.downloadFileFromUrl).toHaveBeenCalledWith( - MOCK_FILE_URL, - ); - expect(result).toEqual(imageBinaryResult); - }); - - it('should throw a NotFoundException if the result is not found', async () => { - downloadFileFromUrlMock.mockResolvedValue(null); - - await expect( - jobService.getResult(MOCK_USER_ID, MOCK_JOB_ID), - ).rejects.toThrowError(new NotFoundException(ErrorJob.ResultNotFound)); - expect(StorageClient.downloadFileFromUrl).toHaveBeenCalledWith( - MOCK_FILE_URL, - ); - }); - - it('should throw a NotFoundException if the result is not valid', async () => { - downloadFileFromUrlMock.mockResolvedValue({ - exchangeAddress: MOCK_ADDRESS, - workerAddress: MOCK_ADDRESS, - solutionNotFortune: 'good', - }); - - await expect( - jobService.getResult(MOCK_USER_ID, MOCK_JOB_ID), - ).rejects.toThrowError( - new NotFoundException(ErrorJob.ResultValidationFailed), - ); - expect(StorageClient.downloadFileFromUrl).toHaveBeenCalledWith( - MOCK_FILE_URL, - ); - }); - }); - - describe('getJobsByStatus', () => { - const userId = 1; - const skip = 0; - const limit = 5; - - it('should call the database with PENDING status', async () => { - jobService.getJobsByStatus( - [ChainId.LOCALHOST], - userId, - JobStatusFilter.PENDING, - skip, - limit, - ); - expect(jobRepository.findJobsByStatusFilter).toHaveBeenCalledWith( - [ChainId.LOCALHOST], - userId, - JobStatusFilter.PENDING, - skip, - limit, - ); - }); - it('should call the database with FAILED status', async () => { - jobService.getJobsByStatus( - [ChainId.LOCALHOST], - userId, - JobStatusFilter.FAILED, - skip, - limit, - ); - expect(jobRepository.findJobsByStatusFilter).toHaveBeenCalledWith( - [ChainId.LOCALHOST], - userId, - JobStatusFilter.FAILED, - skip, - limit, - ); - }); - it('should call subgraph and database with LAUNCHED status', async () => { - const jobEntityMock = [ - { - status: JobStatus.LAUNCHED, - fundAmount: 100, - userId: 1, - id: 1, - escrowAddress: MOCK_ADDRESS, - chainId: ChainId.LOCALHOST, - }, - ]; - const getEscrowsData = [ - { - address: MOCK_ADDRESS, - status: EscrowStatus[EscrowStatus.Launched], - }, - ]; - jobRepository.findJobsByEscrowAddresses = jest - .fn() - .mockResolvedValue(jobEntityMock as any); - EscrowUtils.getEscrows = jest.fn().mockResolvedValue(getEscrowsData); - - const results = await jobService.getJobsByStatus( - [ChainId.LOCALHOST], - userId, - JobStatusFilter.LAUNCHED, - skip, - limit, - ); - - expect(results).toMatchObject([ - { - status: JobStatus.LAUNCHED, - fundAmount: 100, - jobId: 1, - escrowAddress: MOCK_ADDRESS, - network: NETWORKS[ChainId.LOCALHOST]?.title, - }, - ]); - expect(jobRepository.findJobsByEscrowAddresses).toHaveBeenCalledWith( - userId, - [MOCK_ADDRESS, MOCK_ADDRESS, MOCK_ADDRESS], - ); - }); - it('should call subgraph and database with CANCELLED status', async () => { - const jobEntityMock = [ - { - status: JobStatus.CANCELED, - fundAmount: 100, - userId: 1, - id: 1, - escrowAddress: MOCK_ADDRESS, - chainId: ChainId.LOCALHOST, - }, - ]; - const getEscrowsData = [ - { - address: MOCK_ADDRESS, - status: EscrowStatus[EscrowStatus.Cancelled], - }, - ]; - jobRepository.findJobsByEscrowAddresses = jest - .fn() - .mockResolvedValue(jobEntityMock as any); - EscrowUtils.getEscrows = jest.fn().mockResolvedValue(getEscrowsData); - - const results = await jobService.getJobsByStatus( - [ChainId.LOCALHOST], - userId, - JobStatusFilter.CANCELED, - skip, - limit, - ); - - expect(results).toMatchObject([ - { - status: JobStatus.CANCELED, - fundAmount: 100, - jobId: 1, - escrowAddress: MOCK_ADDRESS, - network: NETWORKS[ChainId.LOCALHOST]?.title, - }, - ]); - expect(jobRepository.findJobsByEscrowAddresses).toHaveBeenCalledWith( - userId, - [MOCK_ADDRESS], - ); - }); - }); - - describe('escrowFailedWebhook', () => { - it('should throw BadRequestException for invalid event type', async () => { - const dto = { - eventType: 'ANOTHER_EVENT' as EventType, - chainId: 1, - escrowAddress: 'address', - reason: 'invalid manifest', - }; - - await expect(jobService.escrowFailedWebhook(dto)).rejects.toThrow( - BadRequestException, - ); - }); - - it('should throw NotFoundException if jobEntity is not found', async () => { - const dto = { - eventType: EventType.TASK_CREATION_FAILED, - chainId: 1, - escrowAddress: 'address', - reason: 'invalid manifest', - }; - jobRepository.findOne = jest.fn().mockResolvedValue(null); - - await expect(jobService.escrowFailedWebhook(dto)).rejects.toThrow( - NotFoundException, - ); - }); - - it('should throw ConflictException if jobEntity status is not LAUNCHED', async () => { - const dto = { - eventType: EventType.TASK_CREATION_FAILED, - chainId: 1, - escrowAddress: 'address', - reason: 'invalid manifest', - }; - const mockJobEntity = { - status: 'ANOTHER_STATUS' as JobStatus, - save: jest.fn(), - }; - jobRepository.findOne = jest.fn().mockResolvedValue(mockJobEntity); - - await expect(jobService.escrowFailedWebhook(dto)).rejects.toThrow( - ConflictException, - ); - }); - - it('should update jobEntity status to FAILED and return true if all checks pass', async () => { - const dto = { - eventType: EventType.TASK_CREATION_FAILED, - chainId: 1, - escrowAddress: 'address', - reason: 'invalid manifest', - }; - const mockJobEntity = { - status: JobStatus.LAUNCHED, - failedReason: dto.reason, - save: jest.fn(), - }; - jobRepository.findOne = jest.fn().mockResolvedValue(mockJobEntity); - - const result = await jobService.escrowFailedWebhook(dto); - expect(result).toBe(true); - expect(mockJobEntity.status).toBe(JobStatus.FAILED); - expect(mockJobEntity.failedReason).toBe(dto.reason); - expect(mockJobEntity.save).toHaveBeenCalled(); - }); - }); - - describe('getDetails', () => { - it('should return job details with escrow address successfully', async () => { - const balance = '1'; - const allocationMock: IAllocation = { - escrowAddress: ethers.constants.AddressZero, - staker: ethers.constants.AddressZero, - tokens: BigNumber.from('1'), - createdAt: BigNumber.from('1'), - closedAt: BigNumber.from('1'), - }; - - const manifestMock: FortuneManifestDto = { - submissionsRequired: 10, - requesterTitle: MOCK_REQUESTER_TITLE, - requesterDescription: MOCK_REQUESTER_DESCRIPTION, - fundAmount: 10, - requestType: JobRequestType.FORTUNE, - }; - - const jobEntityMock = { - status: JobStatus.TO_CANCEL, - fundAmount: 100, - userId: 1, - id: 1, - manifestUrl: MOCK_FILE_URL, - manifestHash: MOCK_FILE_HASH, - escrowAddress: MOCK_ADDRESS, - chainId: ChainId.LOCALHOST, - save: jest.fn(), - }; - - const expectedJobDetailsDto: JobDetailsDto = { - details: { - escrowAddress: MOCK_ADDRESS, - manifestUrl: MOCK_FILE_URL, - manifestHash: MOCK_FILE_HASH, - balance: expect.any(Number), - paidOut: expect.any(Number), - status: JobStatus.TO_CANCEL, - }, - manifest: { - chainId: ChainId.LOCALHOST, - title: MOCK_REQUESTER_TITLE, - description: MOCK_REQUESTER_DESCRIPTION, - submissionsRequired: expect.any(Number), - tokenAddress: MOCK_ADDRESS, - fundAmount: expect.any(Number), - requesterAddress: MOCK_ADDRESS, - requestType: JobRequestType.FORTUNE, - exchangeOracleAddress: expect.any(String), - recordingOracleAddress: expect.any(String), - reputationOracleAddress: expect.any(String), - }, - staking: { - staker: expect.any(String), - allocated: expect.any(Number), - slashed: 0, - }, - }; - - const getEscrowData = { - token: MOCK_ADDRESS, - totalFundedAmount: '100', - balance: Number(balance), - amountPaid: '10', - exchangeOracle: MOCK_ADDRESS, - recordingOracle: MOCK_ADDRESS, - reputationOracle: MOCK_ADDRESS, - }; - - jobRepository.findOne = jest.fn().mockResolvedValue(jobEntityMock as any); - EscrowUtils.getEscrow = jest.fn().mockResolvedValue(getEscrowData); - (StakingClient.build as any).mockImplementation(() => ({ - getAllocation: jest.fn().mockResolvedValue(allocationMock), - })); - jobService.getManifest = jest.fn().mockResolvedValue(manifestMock); - jobService.getPaidOutAmount = jest.fn().mockResolvedValue(10); - - const result = await jobService.getDetails(1, 123); - expect(result).toMatchObject(expectedJobDetailsDto); - }); - - it('should return job details without escrow address successfully', async () => { - const manifestMock: FortuneManifestDto = { - submissionsRequired: 10, - requesterTitle: MOCK_REQUESTER_TITLE, - requesterDescription: MOCK_REQUESTER_DESCRIPTION, - fundAmount: 10, - requestType: JobRequestType.FORTUNE, - }; - - const jobEntityMock = { - status: JobStatus.TO_CANCEL, - fundAmount: 100, - userId: 1, - id: 1, - manifestUrl: MOCK_FILE_URL, - manifestHash: MOCK_FILE_HASH, - escrowAddress: null, - chainId: ChainId.LOCALHOST, - save: jest.fn(), - }; - - const expectedJobDetailsDto: JobDetailsDto = { - details: { - escrowAddress: ethers.constants.AddressZero, - manifestUrl: MOCK_FILE_URL, - manifestHash: MOCK_FILE_HASH, - balance: 0, - paidOut: 0, - status: JobStatus.TO_CANCEL, - }, - manifest: { - chainId: ChainId.LOCALHOST, - title: MOCK_REQUESTER_TITLE, - description: MOCK_REQUESTER_DESCRIPTION, - submissionsRequired: expect.any(Number), - tokenAddress: ethers.constants.AddressZero, - fundAmount: expect.any(Number), - requesterAddress: MOCK_ADDRESS, - requestType: JobRequestType.FORTUNE, - exchangeOracleAddress: undefined, - recordingOracleAddress: undefined, - reputationOracleAddress: undefined, - }, - staking: { - staker: expect.any(String), - allocated: 0, - slashed: 0, - }, - }; - - jobRepository.findOne = jest.fn().mockResolvedValue(jobEntityMock as any); - jobService.getManifest = jest.fn().mockResolvedValue(manifestMock); - jobService.getPaidOutAmount = jest.fn().mockResolvedValue(10); - - const result = await jobService.getDetails(1, 123); - expect(result).toMatchObject(expectedJobDetailsDto); - }); - - it('should throw not found exception when job not found', async () => { - jobService.jobRepository.findOne = jest.fn().mockResolvedValue(undefined); - - await expect(jobService.getDetails(1, 123)).rejects.toThrow( - NotFoundException, - ); - }); - }); - - describe('getTransferLogs', () => { - it('should retrieve logs', async () => { - const chainId = ChainId.LOCALHOST; - web3Service.getSigner = jest.fn().mockReturnValue({ - ...signerMock, - provider: { - getLogs: jest.fn().mockResolvedValue([{}]), - getBlockNumber: jest.fn().mockResolvedValue(100), - }, - }); - - await jobService.getTransferLogs(chainId, MOCK_ADDRESS, 0, 'latest'); - expect( - web3Service.getSigner(chainId).provider.getLogs, - ).toHaveBeenCalled(); - }); - }); - - describe('getPaidOutAmount', () => { - it('should calculate the paid out amount', async () => { - const chainId = ChainId.LOCALHOST; - const amount = ethers.parseEther('1.5'); - const mockLogs = [ - { - data: 'mockData', - topics: ['mockTopic'], - }, - ]; - - const mockParsedLog = { - args: [MOCK_ADDRESS, MOCK_ADDRESS, amount], - }; - - web3Service.getSigner = jest.fn().mockReturnValue({ - ...signerMock, - provider: { - getLogs: jest.fn().mockResolvedValue(mockLogs), - }, - }); - - const mockHMTokenFactoryContract = { - interface: { - parseLog: jest.fn().mockReturnValue({ - args: [MOCK_ADDRESS, MOCK_ADDRESS, amount], - }), - }, - }; - - jest - .spyOn(HMToken__factory, 'connect') - .mockReturnValue(mockHMTokenFactoryContract as any); - - (ethers as any).Contract.prototype.interface = { - parseLog: jest.fn().mockReturnValue(mockParsedLog), - }; - - const result = await jobService.getPaidOutAmount( - chainId, - MOCK_ADDRESS, - MOCK_ADDRESS, - ); - expect(result).toBe(1.5); - }); - }); -}); diff --git a/packages/apps/hufi/job-launcher/server/src/modules/job/job.service.ts b/packages/apps/hufi/job-launcher/server/src/modules/job/job.service.ts deleted file mode 100644 index 4f7dd8e124..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/job/job.service.ts +++ /dev/null @@ -1,789 +0,0 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -import { - ChainId, - EscrowClient, - EscrowStatus, - EscrowUtils, - NETWORKS, - StakingClient, - StorageClient, - StorageCredentials, - StorageParams, - UploadFile, -} from '@human-protocol/sdk'; -import { HttpService } from '@nestjs/axios'; -import { - BadGatewayException, - BadRequestException, - ConflictException, - Inject, - Injectable, - Logger, - NotFoundException, - ValidationError, -} from '@nestjs/common'; -import { ConfigService } from '@nestjs/config'; -import { validate } from 'class-validator'; -import { ethers } from 'ethers'; -import { firstValueFrom } from 'rxjs'; -import { LessThanOrEqual, QueryFailedError } from 'typeorm'; -import { ConfigNames } from '../../common/config'; -import { - ErrorBucket, - ErrorEscrow, - ErrorJob, - ErrorPayment, - ErrorPostgres, -} from '../../common/constants/errors'; -import { - JobRequestType, - JobStatus, - JobStatusFilter, -} from '../../common/enums/job'; -import { - Currency, - PaymentSource, - PaymentStatus, - PaymentType, - TokenId, -} from '../../common/enums/payment'; -import { getRate } from '../../common/utils'; -import { add, div, lt, mul } from '../../common/utils/decimal'; -import { PaymentRepository } from '../payment/payment.repository'; -import { PaymentService } from '../payment/payment.service'; -import { Web3Service } from '../web3/Web3Service'; -import { - CampaignFinalResultDto, - CampaignManifestDto, - EscrowCancelDto, - EscrowFailedWebhookDto, - JobCampaignDto, - JobDetailsDto, - JobListDto, - SaveManifestDto, - SendWebhookDto, -} from './job.dto'; -import { JobEntity } from './job.entity'; -import { JobRepository } from './job.repository'; -import { RoutingProtocolService } from './routing-protocol.service'; -import { - CANCEL_JOB_STATUSES, - HEADER_SIGNATURE_KEY, - JOB_RETRIES_COUNT_THRESHOLD, -} from '../../common/constants'; -import { SortDirection } from '../../common/enums/collection'; -import { EventType } from '../../common/enums/webhook'; -import { - HMToken, - HMToken__factory, -} from '@human-protocol/core/typechain-types'; -import Decimal from 'decimal.js'; -import { EscrowData } from '@human-protocol/sdk/dist/graphql'; -import { filterToEscrowStatus } from '../../common/utils/status'; -import { signMessage } from '../../common/utils/signature'; - -@Injectable() -export class JobService { - public readonly logger = new Logger(JobService.name); - public readonly storageClient: StorageClient; - public readonly storageParams: StorageParams; - public readonly bucket: string; - - constructor( - @Inject(Web3Service) - private readonly web3Service: Web3Service, - public readonly jobRepository: JobRepository, - private readonly paymentRepository: PaymentRepository, - private readonly paymentService: PaymentService, - public readonly httpService: HttpService, - public readonly configService: ConfigService, - private readonly routingProtocolService: RoutingProtocolService, - ) { - const storageCredentials: StorageCredentials = { - accessKey: this.configService.get(ConfigNames.S3_ACCESS_KEY)!, - secretKey: this.configService.get(ConfigNames.S3_SECRET_KEY)!, - }; - - const useSSL = - this.configService.get(ConfigNames.S3_USE_SSL) === 'true'; - this.storageParams = { - endPoint: this.configService.get(ConfigNames.S3_ENDPOINT)!, - port: Number(this.configService.get(ConfigNames.S3_PORT)!), - useSSL, - }; - - this.bucket = this.configService.get(ConfigNames.S3_BUCKET)!; - - this.storageClient = new StorageClient( - this.storageParams, - storageCredentials, - ); - } - - public async createJob(userId: number, dto: JobCampaignDto): Promise { - const { chainId, fundAmount } = dto; - - if (chainId) { - this.web3Service.validateChainId(chainId); - } - - const userBalance = await this.paymentService.getUserBalance(userId); - - const rate = await getRate(Currency.USD, TokenId.HMT); - - const feePercentage = this.configService.get( - ConfigNames.JOB_LAUNCHER_FEE, - )!; - const fee = mul(div(feePercentage, 100), fundAmount); - const usdTotalAmount = add(fundAmount, fee); - - if (lt(userBalance, usdTotalAmount)) { - this.logger.log(ErrorJob.NotEnoughFunds, JobService.name); - throw new BadRequestException(ErrorJob.NotEnoughFunds); - } - - const tokenFundAmount = mul(fundAmount, rate); - const tokenFee = mul(fee, rate); - const tokenTotalAmount = add(tokenFundAmount, tokenFee); - - const { manifestUrl, manifestHash } = await this.saveManifest({ - ...(dto as JobCampaignDto), - requestType: JobRequestType.CAMPAIGN, - fundAmount: tokenFundAmount, - }); - - const jobEntity = await this.jobRepository.create({ - chainId: chainId ?? this.routingProtocolService.selectNetwork(), - userId, - manifestUrl, - manifestHash, - fee: tokenFee, - fundAmount: tokenFundAmount, - status: JobStatus.PENDING, - waitUntil: new Date(), - }); - - if (!jobEntity) { - this.logger.log(ErrorJob.NotCreated, JobService.name); - throw new NotFoundException(ErrorJob.NotCreated); - } - - try { - await this.paymentRepository.create({ - userId, - jobId: jobEntity.id, - source: PaymentSource.BALANCE, - type: PaymentType.WITHDRAWAL, - amount: -tokenTotalAmount, - currency: TokenId.HMT, - rate: div(1, rate), - status: PaymentStatus.SUCCEEDED, - }); - } catch (error) { - if ( - error instanceof QueryFailedError && - error.message.includes(ErrorPostgres.NumericFieldOverflow.toLowerCase()) - ) { - this.logger.log(ErrorPostgres.NumericFieldOverflow, JobService.name); - throw new ConflictException(ErrorPayment.IncorrectAmount); - } else { - this.logger.log(error, JobService.name); - throw new ConflictException(ErrorPayment.NotSuccess); - } - } - - jobEntity.status = JobStatus.PAID; - await jobEntity.save(); - - return jobEntity.id; - } - - public async launchJob(jobEntity: JobEntity): Promise { - const signer = this.web3Service.getSigner(jobEntity.chainId); - - const escrowClient = await EscrowClient.build(signer); - - const escrowConfig = { - recordingOracle: this.configService.get( - ConfigNames.RECORDING_ORACLE_ADDRESS, - )!, - reputationOracle: this.configService.get( - ConfigNames.REPUTATION_ORACLE_ADDRESS, - )!, - exchangeOracle: this.configService.get( - ConfigNames.EXCHANGE_ORACLE_ADDRESS, - )!, - recordingOracleFee: BigInt( - this.configService.get(ConfigNames.RECORDING_ORACLE_FEE)!, - ), - reputationOracleFee: BigInt( - this.configService.get(ConfigNames.REPUTATION_ORACLE_FEE)!, - ), - exchangeOracleFee: BigInt( - this.configService.get(ConfigNames.EXCHANGE_ORACLE_FEE)!, - ), - manifestUrl: jobEntity.manifestUrl, - manifestHash: jobEntity.manifestHash, - }; - - const escrowAddress = await escrowClient.createAndSetupEscrow( - NETWORKS[jobEntity.chainId as ChainId]!.hmtAddress, - [], - jobEntity.userId.toString(), - escrowConfig, - ); - - if (!escrowAddress) { - this.logger.log(ErrorEscrow.NotCreated, JobService.name); - throw new NotFoundException(ErrorEscrow.NotCreated); - } - - jobEntity.escrowAddress = escrowAddress; - await jobEntity.save(); - - return jobEntity; - } - - public async fundJob(jobEntity: JobEntity): Promise { - const signer = this.web3Service.getSigner(jobEntity.chainId); - - const escrowClient = await EscrowClient.build(signer); - - const weiAmount = ethers.parseUnits( - jobEntity.fundAmount.toString(), - 'ether', - ); - await escrowClient.fund(jobEntity.escrowAddress, weiAmount); - - jobEntity.status = JobStatus.LAUNCHED; - await jobEntity.save(); - - return jobEntity; - } - - public async requestToCancelJob( - userId: number, - id: number, - ): Promise { - const jobEntity = await this.jobRepository.findOne({ id, userId }); - - if (!jobEntity) { - this.logger.log(ErrorJob.NotFound, JobService.name); - throw new NotFoundException(ErrorJob.NotFound); - } - - if (!CANCEL_JOB_STATUSES.includes(jobEntity.status)) { - this.logger.log(ErrorJob.InvalidStatusCancellation, JobService.name); - throw new ConflictException(ErrorJob.InvalidStatusCancellation); - } - - jobEntity.status = JobStatus.TO_CANCEL; - jobEntity.retriesCount = 0; - await jobEntity.save(); - - return true; - } - - public async saveManifest( - manifest: CampaignManifestDto, - ): Promise { - const uploadedFiles: UploadFile[] = await this.storageClient.uploadFiles( - [manifest], - this.bucket, - ); - - if (!uploadedFiles[0]) { - this.logger.log(ErrorBucket.UnableSaveFile, JobService.name); - throw new BadGatewayException(ErrorBucket.UnableSaveFile); - } - - const { url, hash } = uploadedFiles[0]; - const manifestUrl = url; - - return { manifestUrl, manifestHash: hash }; - } - - private async validateManifest( - manifest: CampaignManifestDto, - ): Promise { - const validationErrors: ValidationError[] = await validate(manifest); - if (validationErrors.length > 0) { - this.logger.log( - ErrorJob.ManifestValidationFailed, - JobService.name, - validationErrors, - ); - throw new NotFoundException(ErrorJob.ManifestValidationFailed); - } - - return true; - } - - public async getManifest(manifestUrl: string): Promise { - const manifest = await StorageClient.downloadFileFromUrl(manifestUrl); - - if (!manifest) { - throw new NotFoundException(ErrorJob.ManifestNotFound); - } - - return manifest; - } - - public async sendWebhook( - webhookUrl: string, - webhookData: SendWebhookDto, - ): Promise { - const signedBody = await signMessage( - webhookData, - this.configService.get(ConfigNames.WEB3_PRIVATE_KEY)!, - ); - const { data } = await firstValueFrom( - await this.httpService.post(webhookUrl, webhookData, { - headers: { [HEADER_SIGNATURE_KEY]: signedBody }, - }), - ); - - if (!data) { - this.logger.log(ErrorJob.WebhookWasNotSent, JobService.name); - throw new NotFoundException(ErrorJob.WebhookWasNotSent); - } - - return true; - } - - public async getJobsByStatus( - networks: ChainId[], - userId: number, - status?: JobStatusFilter, - skip = 0, - limit = 10, - ): Promise { - try { - let jobs: JobEntity[] = []; - let escrows: EscrowData[] | undefined; - - networks.forEach((chainId) => - this.web3Service.validateChainId(Number(chainId)), - ); - - switch (status) { - case JobStatusFilter.FAILED: - case JobStatusFilter.PENDING: - jobs = await this.jobRepository.findJobsByStatusFilter( - networks, - userId, - status, - skip, - limit, - ); - break; - case JobStatusFilter.CANCELED: - case JobStatusFilter.LAUNCHED: - case JobStatusFilter.COMPLETED: - const escrows = await this.findEscrowsByStatus( - networks, - userId, - status, - skip, - limit, - ); - const escrowAddresses = escrows.map((escrow) => - ethers.getAddress(escrow.address), - ); - - jobs = await this.jobRepository.findJobsByEscrowAddresses( - userId, - escrowAddresses, - ); - break; - } - - return this.transformJobs(jobs, escrows); - } catch (error) { - console.error(error); - throw new BadRequestException(error.message); - } - } - - private async findEscrowsByStatus( - networks: ChainId[], - userId: number, - status: JobStatusFilter, - skip: number, - limit: number, - ): Promise { - const escrows: EscrowData[] = []; - const statuses = filterToEscrowStatus(status); - - for (const escrowStatus of statuses) { - escrows.push( - ...(await EscrowUtils.getEscrows({ - networks, - jobRequesterId: userId.toString(), - status: escrowStatus, - launcher: this.web3Service.signerAddress, - })), - ); - } - - if (statuses.length > 1) { - escrows.sort((a, b) => Number(b.createdAt) - Number(a.createdAt)); - } - - return escrows.slice(skip, limit); - } - - private transformJobs( - jobs: JobEntity[], - escrows: EscrowData[] | undefined, - ): JobListDto[] { - return jobs.map((job) => ({ - jobId: job.id, - escrowAddress: job.escrowAddress, - network: NETWORKS[job.chainId as ChainId]!.title, - fundAmount: job.fundAmount, - status: this.mapJobStatus(job, escrows), - })); - } - - private mapJobStatus(job: JobEntity, escrows?: EscrowData[]) { - if (job.status === JobStatus.PAID) { - return JobStatus.PENDING; - } - - if (escrows) { - const escrow = escrows.find( - (escrow) => escrow.address === job.escrowAddress, - ); - if (escrow) { - return (JobStatus)[escrow.status.toUpperCase()]; - } - } - - return job.status; - } - - public async getResult( - userId: number, - jobId: number, - ): Promise { - const jobEntity = await this.jobRepository.findOne({ - id: jobId, - userId, - status: JobStatus.LAUNCHED, - }); - if (!jobEntity) { - this.logger.log(ErrorJob.NotFound, JobService.name); - throw new NotFoundException(ErrorJob.NotFound); - } - - const signer = this.web3Service.getSigner(jobEntity.chainId); - const escrowClient = await EscrowClient.build(signer); - - const finalResultUrl = await escrowClient.getResultsUrl( - jobEntity.escrowAddress, - ); - - if (!finalResultUrl) { - this.logger.log(ErrorJob.ResultNotFound, JobService.name); - throw new NotFoundException(ErrorJob.ResultNotFound); - } - - const result = await StorageClient.downloadFileFromUrl(finalResultUrl); - - if (!result) { - throw new NotFoundException(ErrorJob.ResultNotFound); - } - - const validationErrors: ValidationError[] = await validate(result); - if (validationErrors.length > 0) { - this.logger.log( - ErrorJob.ResultValidationFailed, - JobService.name, - validationErrors, - ); - throw new NotFoundException(ErrorJob.ResultValidationFailed); - } - - return result; - } - - public async launchCronJob() { - try { - // TODO: Add retry policy and process failure requests https://github.com/humanprotocol/human-protocol/issues/334 - let jobEntity = await this.jobRepository.findOne( - { - status: JobStatus.PAID, - retriesCount: LessThanOrEqual(JOB_RETRIES_COUNT_THRESHOLD), - waitUntil: LessThanOrEqual(new Date()), - }, - { - order: { - waitUntil: SortDirection.ASC, - }, - }, - ); - - if (!jobEntity) return; - - const manifest = await this.getManifest(jobEntity.manifestUrl); - await this.validateManifest(manifest); - - if (!jobEntity.escrowAddress) { - jobEntity = await this.launchJob(jobEntity); - } - if (jobEntity.escrowAddress && jobEntity.status === JobStatus.PAID) { - jobEntity = await this.fundJob(jobEntity); - } - if (jobEntity.escrowAddress && jobEntity.status === JobStatus.LAUNCHED) { - await this.sendWebhook( - this.configService.get( - ConfigNames.EXCHANGE_ORACLE_WEBHOOK_URL, - )!, - { - escrowAddress: jobEntity.escrowAddress, - chainId: jobEntity.chainId, - eventType: EventType.ESCROW_CREATED, - }, - ); - } - } catch (e) { - console.log(e); - this.logger.error(e); - return; - } - } - - public async cancelCronJob() { - const jobEntity = await this.jobRepository.findOne( - { - status: JobStatus.TO_CANCEL, - retriesCount: LessThanOrEqual(JOB_RETRIES_COUNT_THRESHOLD), - waitUntil: LessThanOrEqual(new Date()), - }, - { - order: { - waitUntil: SortDirection.ASC, - }, - }, - ); - if (!jobEntity) return; - - if (jobEntity.escrowAddress) { - const { amountRefunded } = - await this.processEscrowCancellation(jobEntity); - await this.paymentService.createRefundPayment({ - refundAmount: Number(ethers.formatEther(amountRefunded)), - userId: jobEntity.userId, - jobId: jobEntity.id, - }); - } else { - await this.paymentService.createRefundPayment({ - refundAmount: jobEntity.fundAmount, - userId: jobEntity.userId, - jobId: jobEntity.id, - }); - } - - jobEntity.status = JobStatus.CANCELED; - await jobEntity.save(); - - await this.sendWebhook( - this.configService.get(ConfigNames.EXCHANGE_ORACLE_WEBHOOK_URL)!, - { - escrowAddress: jobEntity.escrowAddress, - chainId: jobEntity.chainId, - eventType: EventType.ESCROW_CANCELED, - }, - ); - - return true; - } - - public async processEscrowCancellation( - jobEntity: JobEntity, - ): Promise { - const { chainId, escrowAddress } = jobEntity; - - const signer = this.web3Service.getSigner(chainId); - const escrowClient = await EscrowClient.build(signer); - - const escrowStatus = await escrowClient.getStatus(escrowAddress); - if ( - escrowStatus === EscrowStatus.Complete || - escrowStatus === EscrowStatus.Paid || - escrowStatus === EscrowStatus.Cancelled - ) { - this.logger.log(ErrorEscrow.InvalidStatusCancellation, JobService.name); - throw new BadRequestException(ErrorEscrow.InvalidStatusCancellation); - } - - const balance = await escrowClient.getBalance(escrowAddress); - if (balance === 0n) { - this.logger.log(ErrorEscrow.InvalidBalanceCancellation, JobService.name); - throw new BadRequestException(ErrorEscrow.InvalidBalanceCancellation); - } - - return escrowClient.cancel(escrowAddress); - } - - public async escrowFailedWebhook( - dto: EscrowFailedWebhookDto, - ): Promise { - if (dto.eventType !== EventType.TASK_CREATION_FAILED) { - this.logger.log(ErrorJob.InvalidEventType, JobService.name); - throw new BadRequestException(ErrorJob.InvalidEventType); - } - - const jobEntity = await this.jobRepository.findOne({ - chainId: dto.chainId, - escrowAddress: dto.escrowAddress, - }); - - if (!jobEntity) { - this.logger.log(ErrorJob.NotFound, JobService.name); - throw new NotFoundException(ErrorJob.NotFound); - } - - if (jobEntity.status !== JobStatus.LAUNCHED) { - this.logger.log(ErrorJob.NotLaunched, JobService.name); - throw new ConflictException(ErrorJob.NotLaunched); - } - - jobEntity.status = JobStatus.FAILED; - jobEntity.failedReason = dto.reason; - await jobEntity.save(); - - return true; - } - - public async getDetails( - userId: number, - jobId: number, - ): Promise { - const jobEntity = await this.jobRepository.findOne({ id: jobId, userId }); - - if (!jobEntity) { - this.logger.log(ErrorJob.NotFound, JobService.name); - throw new NotFoundException(ErrorJob.NotFound); - } - - const { chainId, escrowAddress, manifestUrl, manifestHash } = jobEntity; - const signer = this.web3Service.getSigner(chainId); - - let escrow, allocation; - - if (escrowAddress) { - const stakingClient = await StakingClient.build(signer); - - escrow = await EscrowUtils.getEscrow(chainId, escrowAddress); - allocation = await stakingClient.getAllocation(escrowAddress); - } - - const status = - escrow?.status === 'Completed' ? JobStatus.COMPLETED : jobEntity.status; - - const manifest = await this.getManifest(manifestUrl); - if (!manifest) { - throw new NotFoundException(ErrorJob.ManifestNotFound); - } - - const baseManifestDetails = { - chainId, - tokenAddress: escrow ? escrow.token : ethers.ZeroAddress, - fundAmount: escrow ? Number(escrow.totalFundedAmount) : 0, - requesterAddress: signer.address, - exchangeOracleAddress: escrow?.exchangeOracle, - recordingOracleAddress: escrow?.recordingOracle, - reputationOracleAddress: escrow?.reputationOracle, - }; - - const manifestDetails = { - ...baseManifestDetails, - ...manifest, - }; - - if (!escrowAddress) { - return { - details: { - escrowAddress: ethers.ZeroAddress, - manifestUrl, - manifestHash, - balance: 0, - paidOut: 0, - status, - }, - manifest: manifestDetails, - staking: { - staker: ethers.ZeroAddress, - allocated: 0, - slashed: 0, - }, - }; - } - - return { - details: { - escrowAddress, - manifestUrl, - manifestHash, - balance: Number(ethers.formatEther(escrow?.balance || 0)), - paidOut: Number(escrow?.amountPaid || 0), - status, - }, - manifest: manifestDetails, - staking: { - staker: allocation?.staker as string, - allocated: Number(allocation?.tokens), - slashed: 0, // TODO: Retrieve slash tokens - }, - }; - } - - public async getTransferLogs( - chainId: ChainId, - tokenAddress: string, - fromBlock: number, - toBlock: string | number, - ) { - const signer = this.web3Service.getSigner(chainId); - const filter = { - address: tokenAddress, - topics: [ethers.id('Transfer(address,address,uint256)')], - fromBlock: fromBlock, - toBlock: toBlock, - }; - - return signer.provider?.getLogs(filter); - } - - public async getPaidOutAmount( - chainId: ChainId, - tokenAddress: string, - escrowAddress: string, - ): Promise { - const signer = this.web3Service.getSigner(chainId); - const tokenContract: HMToken = HMToken__factory.connect( - tokenAddress, - signer, - ); - - const logs = await this.getTransferLogs(chainId, tokenAddress, 0, 'latest'); - let paidOutAmount = new Decimal(0); - - logs?.forEach((log) => { - const parsedLog = tokenContract.interface.parseLog({ - topics: log.topics as string[], - data: log.data, - }); - const from = parsedLog?.args[0]; - const amount = parsedLog?.args[2]; - - if (from === escrowAddress) { - paidOutAmount = paidOutAmount.add(ethers.formatEther(amount)); - } - }); - - return Number(paidOutAmount); - } -} diff --git a/packages/apps/hufi/job-launcher/server/src/modules/job/routing-protocol.service.spec.ts b/packages/apps/hufi/job-launcher/server/src/modules/job/routing-protocol.service.spec.ts deleted file mode 100644 index aff19a8c66..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/job/routing-protocol.service.spec.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { Test } from '@nestjs/testing'; - -import { RoutingProtocolService } from './routing-protocol.service'; -import { NETWORKS } from '@human-protocol/sdk'; -import { - MOCK_ADDRESS, - MOCK_FILE_HASH, - MOCK_FILE_KEY, - MOCK_FILE_URL, -} from '../../../test/constants'; - -jest.mock('@human-protocol/sdk', () => ({ - ...jest.requireActual('@human-protocol/sdk'), - EscrowClient: { - build: jest.fn().mockImplementation(() => ({ - createAndSetupEscrow: jest.fn().mockResolvedValue(MOCK_ADDRESS), - })), - }, - StorageClient: jest.fn().mockImplementation(() => ({ - uploadFiles: jest - .fn() - .mockResolvedValue([ - { key: MOCK_FILE_KEY, url: MOCK_FILE_URL, hash: MOCK_FILE_HASH }, - ]), - })), -})); - -describe('RoutingProtocolService', () => { - let routingProtocolService: RoutingProtocolService; - - beforeEach(async () => { - const moduleRef = await Test.createTestingModule({ - providers: [RoutingProtocolService], - }).compile(); - - routingProtocolService = moduleRef.get(RoutingProtocolService); - }); - - describe('selectNetwork', () => { - it('should select network in random order', async () => { - const chainIds = []; - for (let i = 0; i < Object.keys(NETWORKS).length; i++) { - chainIds.push(routingProtocolService.selectNetwork()); - } - expect(chainIds).not.toEqual(Object.keys(NETWORKS)); - }); - - it('should repeat first round network selection for the second round', async () => { - const firstSelection = []; - for (let i = 0; i < Object.keys(NETWORKS).length; i++) { - firstSelection.push(routingProtocolService.selectNetwork()); - } - const secondSelection = []; - for (let i = 0; i < Object.keys(NETWORKS).length; i++) { - secondSelection.push(routingProtocolService.selectNetwork()); - } - expect(firstSelection).toEqual(secondSelection); - }); - }); -}); diff --git a/packages/apps/hufi/job-launcher/server/src/modules/job/routing-protocol.service.ts b/packages/apps/hufi/job-launcher/server/src/modules/job/routing-protocol.service.ts deleted file mode 100644 index aa79c6db24..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/job/routing-protocol.service.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Injectable, Logger } from '@nestjs/common'; -import { ChainId, NETWORKS } from '@human-protocol/sdk'; - -@Injectable() -export class RoutingProtocolService { - public readonly logger = new Logger(RoutingProtocolService.name); - private readonly chains: ChainId[]; - private currentIndex: number; - private readonly priorityOrder: number[]; - - constructor() { - this.chains = Object.keys(NETWORKS).map((chainId) => +chainId); - this.currentIndex = 0; - this.priorityOrder = this.generatePriorityOrder(); - } - - private generatePriorityOrder() { - const priorityOrder = Array.from(Array(this.chains.length).keys()); - for (let i = priorityOrder.length - 1; i > 0; i--) { - const j = Math.floor(Math.random() * (i + 1)); - [priorityOrder[i], priorityOrder[j]] = [ - priorityOrder[j], - priorityOrder[i], - ]; - } - return priorityOrder; - } - - public selectNetwork(): ChainId { - const chainId = this.chains[this.priorityOrder[this.currentIndex]]; - this.currentIndex = (this.currentIndex + 1) % this.chains.length; - return chainId; - } -} diff --git a/packages/apps/hufi/job-launcher/server/src/modules/payment/payment.controller.ts b/packages/apps/hufi/job-launcher/server/src/modules/payment/payment.controller.ts deleted file mode 100644 index 94b34c3b7b..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/payment/payment.controller.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { - Body, - Controller, - Get, - Post, - Query, - Request, - UseGuards, - Headers, -} from '@nestjs/common'; -import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; -import { JwtAuthGuard } from '../../common/guards'; -import { RequestWithUser } from '../../common/types'; - -import { - GetRateDto, - PaymentCryptoCreateDto, - PaymentFiatConfirmDto, - PaymentFiatCreateDto, -} from './payment.dto'; -import { PaymentService } from './payment.service'; -import { getRate } from '../../common/utils'; -import { HEADER_SIGNATURE_KEY } from '../../common/constants'; - -@ApiBearerAuth() -@UseGuards(JwtAuthGuard) -@ApiTags('Payment') -@Controller('/payment') -export class PaymentController { - constructor(private readonly paymentService: PaymentService) {} - - @Post('/fiat') - public async createFiatPayment( - @Request() req: RequestWithUser, - @Body() data: PaymentFiatCreateDto, - ): Promise { - return this.paymentService.createFiatPayment(req.user.id, data); - } - - @Post('/fiat/confirm-payment') - public async confirmFiatPayment( - @Request() req: RequestWithUser, - @Body() data: PaymentFiatConfirmDto, - ): Promise { - return this.paymentService.confirmFiatPayment(req.user.id, data); - } - - @Post('/crypto') - public async createCryptoPayment( - @Headers(HEADER_SIGNATURE_KEY) signature: string, - @Request() req: RequestWithUser, - @Body() data: PaymentCryptoCreateDto, - ): Promise { - return this.paymentService.createCryptoPayment( - req.user.id, - data, - signature, - ); - } - - @Get('/rates') - public async getRate(@Query() data: GetRateDto): Promise { - try { - return getRate(data.from, data.to); - } catch (e) { - throw new Error(e); - } - } -} diff --git a/packages/apps/hufi/job-launcher/server/src/modules/payment/payment.dto.ts b/packages/apps/hufi/job-launcher/server/src/modules/payment/payment.dto.ts deleted file mode 100644 index 495db4ddb2..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/payment/payment.dto.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { IsEnum, IsNumber, IsString, Min } from 'class-validator'; -import { - Currency, - PaymentSource, - PaymentStatus, - PaymentType, -} from '../../common/enums/payment'; -import { ChainId } from '@human-protocol/sdk'; - -export class PaymentFiatConfirmDto { - @ApiProperty() - @IsString() - public paymentId: string; -} - -export class PaymentFiatCreateDto { - @ApiProperty() - @IsNumber() - @Min(0.5) - public amount: number; - - @ApiProperty({ - enum: Currency, - }) - @IsEnum(Currency) - public currency: Currency; -} - -export class PaymentCryptoCreateDto { - @ApiProperty({ - enum: ChainId, - }) - @IsEnum(ChainId) - public chainId: ChainId; - - @ApiProperty() - @IsString() - public transactionHash: string; -} - -export class PaymentCreateDto { - public transaction?: string; - public amount?: number; - public currency?: string; - public source?: PaymentSource; - public userId?: number; - public rate?: number; - public type?: PaymentType; - public chainId?: number; - public status?: PaymentStatus; - public jobId?: number; -} - -export class GetRateDto { - @ApiProperty() - @IsString() - public from: string; - - @ApiProperty() - @IsString() - public to: string; -} - -export class PaymentRefundCreateDto { - @IsNumber() - public refundAmount: number - - @IsNumber() - public userId: number; - - @IsNumber() - public jobId: number; -} \ No newline at end of file diff --git a/packages/apps/hufi/job-launcher/server/src/modules/payment/payment.entity.ts b/packages/apps/hufi/job-launcher/server/src/modules/payment/payment.entity.ts deleted file mode 100644 index d42e24c383..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/payment/payment.entity.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { Column, Entity, Index, JoinColumn, ManyToOne, OneToOne } from 'typeorm'; -import { NS } from '../../common/constants'; -import { BaseEntity } from '../../database/base.entity'; -import { - PaymentSource, - PaymentStatus, - PaymentType, -} from '../../common/enums/payment'; -import { UserEntity } from '../user/user.entity'; -import { JobEntity } from '../job/job.entity'; - -@Entity({ schema: NS, name: 'payments' }) -@Index(['chainId', 'transaction'], { - unique: true, - where: '(chain_Id IS NOT NULL AND transaction IS NOT NULL)', -}) -@Index(['transaction'], { - unique: true, - where: '(chain_Id IS NULL AND transaction IS NOT NULL)', -}) -export class PaymentEntity extends BaseEntity { - @Column({ type: 'varchar', nullable: true }) - public transaction: string; - - @Column({ type: 'int', nullable: true }) - public chainId: number; - - @Column({ type: 'decimal', precision: 30, scale: 18 }) - public amount: number; - - @Column({ type: 'decimal', precision: 30, scale: 18 }) - public rate: number; - - @Column({ type: 'varchar' }) - public currency: string; - - @Column({ - type: 'enum', - enum: PaymentType, - }) - public type: PaymentType; - - @Column({ - type: 'enum', - enum: PaymentSource, - }) - public source: PaymentSource; - - @Column({ - type: 'enum', - enum: PaymentStatus, - }) - public status: PaymentStatus; - - @JoinColumn() - @ManyToOne(() => UserEntity, (user) => user.payments) - public user: UserEntity; - - @Column({ type: 'int' }) - public userId: number; - - @JoinColumn() - @ManyToOne(() => JobEntity, (job) => job.payment) - public job: JobEntity; - - @Column({ type: 'int', nullable: true }) - public jobId: number; -} diff --git a/packages/apps/hufi/job-launcher/server/src/modules/payment/payment.module.ts b/packages/apps/hufi/job-launcher/server/src/modules/payment/payment.module.ts deleted file mode 100644 index 15eb85aaca..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/payment/payment.module.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Module } from '@nestjs/common'; - -import { PaymentService } from './payment.service'; -import { MinioModule } from 'nestjs-minio-client'; -import { ConfigModule, ConfigService } from '@nestjs/config'; -import { PaymentEntity } from './payment.entity'; -import { TypeOrmModule } from '@nestjs/typeorm'; -import { PaymentController } from './payment.controller'; -import { PaymentRepository } from './payment.repository'; -import { HttpModule } from '@nestjs/axios'; -import { Web3Module } from '../web3/web3.module'; - -@Module({ - imports: [ - HttpModule, - TypeOrmModule.forFeature([PaymentEntity]), - ConfigModule, - Web3Module, - MinioModule.registerAsync({ - imports: [ConfigModule], - inject: [ConfigService], - useFactory: (configService: ConfigService) => { - return { - endPoint: configService.get('S3_HOST', '127.0.0.1'), - port: Number(configService.get('S3_PORT', 9000)), - useSSL: false, - accessKey: configService.get('S3_ACCESS_KEY', 'access-key'), - secretKey: configService.get('S3_SECRET_KEY', 'secrete-key'), - }; - }, - }), - ], - controllers: [PaymentController], - providers: [PaymentService, PaymentRepository], - exports: [PaymentService, PaymentRepository], -}) -export class PaymentModule {} diff --git a/packages/apps/hufi/job-launcher/server/src/modules/payment/payment.repository.ts b/packages/apps/hufi/job-launcher/server/src/modules/payment/payment.repository.ts deleted file mode 100644 index 609b1b6cc9..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/payment/payment.repository.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { Injectable, Logger } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { - FindOptionsWhere, - FindManyOptions, - FindOneOptions, - Repository, -} from 'typeorm'; -import { PaymentEntity } from './payment.entity'; -import { PaymentCreateDto } from './payment.dto'; - -@Injectable() -export class PaymentRepository { - private readonly logger = new Logger(PaymentRepository.name); - - constructor( - @InjectRepository(PaymentEntity) - private readonly paymentEntityRepository: Repository, - ) {} - - public async findOne( - where: FindOptionsWhere, - options?: FindOneOptions, - ): Promise { - const paymentEntity = await this.paymentEntityRepository.findOne({ - where, - ...options, - }); - - return paymentEntity; - } - - public find( - where: FindOptionsWhere, - options?: FindManyOptions, - ): Promise { - return this.paymentEntityRepository.find({ - where, - order: { - createdAt: 'DESC', - }, - ...options, - }); - } - - public async create(dto: PaymentCreateDto): Promise { - return this.paymentEntityRepository.create(dto).save(); - } -} diff --git a/packages/apps/hufi/job-launcher/server/src/modules/payment/payment.service.spec.ts b/packages/apps/hufi/job-launcher/server/src/modules/payment/payment.service.spec.ts deleted file mode 100644 index 14ede8d24d..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/payment/payment.service.spec.ts +++ /dev/null @@ -1,702 +0,0 @@ -import { ethers } from 'ethers'; -import { Test } from '@nestjs/testing'; -import Stripe from 'stripe'; -import { PaymentService } from './payment.service'; -import { PaymentRepository } from './payment.repository'; -import { ConfigService } from '@nestjs/config'; -import { HttpService } from '@nestjs/axios'; -import { createMock } from '@golevelup/ts-jest'; -import { - ErrorPayment, - ErrorPostgres, - ErrorSignature, -} from '../../common/constants/errors'; -import { TransactionReceipt } from '@ethersproject/abstract-provider'; -import { - Currency, - PaymentSource, - PaymentStatus, - PaymentType, - StripePaymentStatus, - TokenId, -} from '../../common/enums/payment'; -import { TX_CONFIRMATION_TRESHOLD } from '../../common/constants'; -import { - MOCK_ADDRESS, - MOCK_PAYMENT_ID, - MOCK_SIGNATURE, - MOCK_TRANSACTION_HASH, -} from '../../../test/constants'; -import { Web3Service } from '../web3/Web3Service'; -import { HMToken__factory } from '@human-protocol/core/typechain-types'; -import { ChainId, NETWORKS } from '@human-protocol/sdk'; -import { PaymentEntity } from './payment.entity'; -import { QueryFailedError } from 'typeorm'; -import { verifySignature } from '../../common/utils/signature'; -import { ConflictException } from '@nestjs/common'; - -jest.mock('@human-protocol/sdk'); - -jest.mock('../../common/utils', () => ({ - getRate: jest.fn().mockImplementation(() => 1.5), -})); - -jest.mock('../../common/utils/signature', () => ({ - verifySignature: jest.fn().mockReturnValue(true), -})); - -describe('PaymentService', () => { - let stripe: Stripe; - let paymentService: PaymentService; - let paymentRepository: PaymentRepository; - - const signerMock = { - address: MOCK_ADDRESS, - getNetwork: jest.fn().mockResolvedValue({ chainId: ChainId.LOCALHOST }), - }; - - beforeEach(async () => { - const mockConfigService: Partial = { - get: jest.fn((key: string, defaultValue?: any) => { - switch (key) { - case 'STRIPE_SECRET_KEY': - return 'test-secret'; - case 'STRIPE_API_VERSION': - return '2022-11-15'; - case 'NAME': - return 'Fortune'; - case 'VERSION': - return '0.0.1'; - case 'STRIPE_APP_INFO_URL': - return 'https://test-app-url.com'; - default: - return defaultValue; - } - }), - }; - - const moduleRef = await Test.createTestingModule({ - providers: [ - PaymentService, - { - provide: PaymentRepository, - useValue: createMock(), - }, - { - provide: Web3Service, - useValue: { - getSigner: jest.fn().mockReturnValue(signerMock), - validateChainId: jest.fn(), - }, - }, - { provide: ConfigService, useValue: mockConfigService }, - { provide: HttpService, useValue: createMock() }, - ], - }).compile(); - - paymentService = moduleRef.get(PaymentService); - paymentRepository = moduleRef.get(PaymentRepository); - - const stripeCustomersCreateMock = jest.fn(); - const stripePaymentIntentsCreateMock = jest.fn(); - const stripePaymentIntentsRetrieveMock = jest.fn(); - - stripe = { - customers: { - create: stripeCustomersCreateMock, - }, - paymentIntents: { - create: stripePaymentIntentsCreateMock, - retrieve: stripePaymentIntentsRetrieveMock, - }, - } as any; - - paymentService['stripe'] = stripe; - - jest - .spyOn(stripe.customers, 'create') - .mockImplementation(stripeCustomersCreateMock); - jest - .spyOn(stripe.paymentIntents, 'create') - .mockImplementation(stripePaymentIntentsCreateMock); - jest - .spyOn(stripe.paymentIntents, 'retrieve') - .mockImplementation(stripePaymentIntentsRetrieveMock); - }); - - describe('createFiatPayment', () => { - let createPaymentIntentMock: any, findOneMock: any; - - beforeEach(() => { - findOneMock = jest.spyOn(paymentRepository, 'findOne'); - createPaymentIntentMock = jest.spyOn(stripe.paymentIntents, 'create'); - }); - - afterEach(() => { - expect(createPaymentIntentMock).toHaveBeenCalledTimes(1); - createPaymentIntentMock.mockRestore(); - }); - - it('should create a fiat payment successfully', async () => { - const dto = { - amount: 100, - currency: Currency.USD, - }; - - const userId = 1; - - const paymentIntent = { - client_secret: 'clientSecret123', - }; - - createPaymentIntentMock.mockResolvedValue(paymentIntent); - findOneMock.mockResolvedValue(null); - - const result = await paymentService.createFiatPayment(userId, dto); - - expect(createPaymentIntentMock).toHaveBeenCalledWith({ - amount: dto.amount * 100, - currency: dto.currency, - }); - expect(result).toEqual(paymentIntent.client_secret); - }); - - it('should throw a bad request exception if transaction already exist', async () => { - 0; - const dto = { - amount: 100, - currency: Currency.USD, - }; - - const userId = 1; - - const paymentIntent = { - client_secret: 'clientSecret123', - }; - - createPaymentIntentMock.mockResolvedValue(paymentIntent); - findOneMock.mockResolvedValue({ - transaction: paymentIntent.client_secret, - } as PaymentEntity); - - await expect( - paymentService.createFiatPayment(userId, dto), - ).rejects.toThrowError(ErrorPayment.TransactionAlreadyExists); - }); - - it('should throw a bad request exception if the payment intent creation fails', async () => { - 0; - const dto = { - amount: 100, - currency: Currency.USD, - }; - - const userId = 1; - - createPaymentIntentMock.mockRejectedValue(new Error()); - - await expect( - paymentService.createFiatPayment(userId, dto), - ).rejects.toThrowError(); - }); - }); - - describe('confirmFiatPayment', () => { - let retrievePaymentIntentMock: any; - - beforeEach(() => { - retrievePaymentIntentMock = jest.spyOn(stripe.paymentIntents, 'retrieve'); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('should confirm a fiat payment successfully', async () => { - const userId = 1; - const dto = { - paymentId: MOCK_PAYMENT_ID, - }; - - const paymentData = { - status: StripePaymentStatus.SUCCEEDED, - amount: 100, - amount_received: 100, - currency: Currency.USD, - }; - - retrievePaymentIntentMock.mockResolvedValue(paymentData); - - const result = await paymentService.confirmFiatPayment(userId, dto); - - expect(result).toBe(true); - }); - - it('should handle payment cancellation', async () => { - const userId = 1; - const dto = { - paymentId: MOCK_PAYMENT_ID, - }; - - const paymentData = { - status: StripePaymentStatus.CANCELED, - amount: 100, - amount_received: 0, - currency: Currency.USD, - }; - - retrievePaymentIntentMock.mockResolvedValue(paymentData); - - await expect( - paymentService.confirmFiatPayment(userId, dto), - ).rejects.toThrowError(ErrorPayment.NotSuccess); - }); - - it('should handle payment requiring a payment method', async () => { - const userId = 1; - const dto = { - paymentId: MOCK_PAYMENT_ID, - }; - - const paymentData = { - status: StripePaymentStatus.REQUIRES_PAYMENT_METHOD, - amount: 100, - amount_received: 0, - currency: Currency.USD, - }; - - retrievePaymentIntentMock.mockResolvedValue(paymentData); - - await expect( - paymentService.confirmFiatPayment(userId, dto), - ).rejects.toThrowError(ErrorPayment.NotSuccess); - }); - - it('should handle payment status other than succeeded', async () => { - const userId = 1; - const dto = { - paymentId: MOCK_PAYMENT_ID, - }; - - const paymentData = { - status: 'unknown_status', - amount: 100, - amount_received: 0, - currency: Currency.USD, - }; - - retrievePaymentIntentMock.mockResolvedValue(paymentData); - - const result = await paymentService.confirmFiatPayment(userId, dto); - - expect(result).toBe(false); - }); - - it('should return false if the payment is not found', async () => { - const userId = 1; - const dto = { - paymentId: MOCK_PAYMENT_ID, - }; - - retrievePaymentIntentMock.mockResolvedValue(null); - - await expect( - paymentService.confirmFiatPayment(userId, dto), - ).rejects.toThrowError(ErrorPayment.NotFound); - }); - }); - - describe('createCryptoPayment', () => { - let jsonRpcProviderMock: any, findOneMock: any, createPaymentMock: any; - - const mockTokenContract: any = { - symbol: jest.fn(), - }; - - beforeEach(() => { - jsonRpcProviderMock = { - getTransactionReceipt: jest.fn(), - }; - - jest - .spyOn(ethers.providers, 'JsonRpcProvider') - .mockReturnValue(jsonRpcProviderMock as any); - - jest - .spyOn(HMToken__factory, 'connect') - .mockReturnValue(mockTokenContract); - jest.spyOn(mockTokenContract, 'symbol'); - findOneMock = jest.spyOn(paymentRepository, 'findOne'); - createPaymentMock = jest.spyOn(paymentRepository, 'create'); - }); - - afterEach(() => { - jest.restoreAllMocks(); - (verifySignature as jest.Mock).mockRestore(); - }); - - it('should create a crypto payment successfully', async () => { - const userId = 1; - const dto = { - chainId: ChainId.POLYGON_MUMBAI, - transactionHash: MOCK_TRANSACTION_HASH, - }; - - const token = 'hmt'; - - const transactionReceipt: Partial = { - from: MOCK_ADDRESS, - logs: [ - { - data: ethers.utils.parseUnits('10').toString(), - blockNumber: 123, - blockHash: '123', - transactionIndex: 123, - removed: false, - address: NETWORKS[ChainId.POLYGON_MUMBAI]?.hmtAddress as string, - topics: ['0x123', '0x0000000000000000000000000123', MOCK_ADDRESS], - transactionHash: MOCK_TRANSACTION_HASH, - logIndex: 123, - }, - ], - transactionHash: MOCK_TRANSACTION_HASH, - confirmations: TX_CONFIRMATION_TRESHOLD, - }; - - jsonRpcProviderMock.getTransactionReceipt.mockResolvedValue( - transactionReceipt, - ); - - mockTokenContract.symbol.mockResolvedValue(token); - findOneMock.mockResolvedValue(null); - createPaymentMock.mockResolvedValue(true); - - const result = await paymentService.createCryptoPayment( - userId, - dto, - MOCK_SIGNATURE, - ); - - expect(paymentRepository.findOne).toHaveBeenCalledWith({ - transaction: dto.transactionHash, - chainId: dto.chainId, - }); - expect(paymentRepository.create).toHaveBeenCalledWith({ - userId, - source: PaymentSource.CRYPTO, - type: PaymentType.DEPOSIT, - currency: TokenId.HMT, - amount: 10, - rate: 1.5, - transaction: MOCK_TRANSACTION_HASH, - chainId: ChainId.POLYGON_MUMBAI, - status: PaymentStatus.SUCCEEDED, - }); - expect(result).toBe(true); - }); - - it('should throw a conflict exception if the token address is unsupported', async () => { - const userId = 1; - const dto = { - chainId: ChainId.POLYGON_MUMBAI, - transactionHash: MOCK_TRANSACTION_HASH, - }; - - const token = 'hmt'; - - const transactionReceipt: Partial = { - from: MOCK_ADDRESS, - logs: [ - { - data: ethers.utils.parseUnits('10').toString(), - blockNumber: 123, - blockHash: '123', - transactionIndex: 123, - removed: false, - address: MOCK_ADDRESS, - topics: ['0x123', '0x0000000000000000000000000123', MOCK_ADDRESS], - transactionHash: MOCK_TRANSACTION_HASH, - logIndex: 123, - }, - ], - transactionHash: MOCK_TRANSACTION_HASH, - confirmations: TX_CONFIRMATION_TRESHOLD, - }; - - jsonRpcProviderMock.getTransactionReceipt.mockResolvedValue( - transactionReceipt, - ); - - mockTokenContract.symbol.mockResolvedValue(token); - - await expect( - paymentService.createCryptoPayment(userId, dto, MOCK_SIGNATURE), - ).rejects.toThrowError(ErrorPayment.UnsupportedToken); - }); - - it('should throw a conflict exception if an unsupported token is used', async () => { - const userId = 1; - const dto = { - chainId: ChainId.POLYGON_MUMBAI, - transactionHash: MOCK_TRANSACTION_HASH, - }; - - const unsupportedToken = 'doge'; - - const transactionReceipt: Partial = { - from: MOCK_ADDRESS, - logs: [ - { - data: ethers.utils.parseUnits('10').toString(), - blockNumber: 123, - blockHash: '123', - transactionIndex: 123, - removed: false, - address: NETWORKS[ChainId.POLYGON_MUMBAI]?.hmtAddress as string, - topics: ['0x123', '0x0000000000000000000000000123', MOCK_ADDRESS], - transactionHash: MOCK_TRANSACTION_HASH, - logIndex: 123, - }, - ], - transactionHash: MOCK_TRANSACTION_HASH, - confirmations: TX_CONFIRMATION_TRESHOLD, - }; - - jsonRpcProviderMock.getTransactionReceipt.mockResolvedValue( - transactionReceipt, - ); - - mockTokenContract.symbol.mockResolvedValue(unsupportedToken); - - await expect( - paymentService.createCryptoPayment(userId, dto, MOCK_SIGNATURE), - ).rejects.toThrowError(ErrorPayment.UnsupportedToken); - }); - - it('should throw a signature error if the signature is wrong', async () => { - const userId = 1; - const dto = { - chainId: ChainId.POLYGON_MUMBAI, - transactionHash: MOCK_TRANSACTION_HASH, - }; - (verifySignature as jest.Mock).mockImplementation(() => { - throw new ConflictException(ErrorSignature.SignatureNotVerified); - }); - - const transactionReceipt: Partial = { - from: MOCK_ADDRESS, - logs: [], - confirmations: TX_CONFIRMATION_TRESHOLD, - }; - - jsonRpcProviderMock.getTransactionReceipt.mockResolvedValue( - transactionReceipt, - ); - - await expect( - paymentService.createCryptoPayment(userId, dto, MOCK_SIGNATURE), - ).rejects.toThrowError(ErrorSignature.SignatureNotVerified); - }); - - it('should throw a not found exception if the transaction is not found by hash', async () => { - const userId = 1; - const dto = { - chainId: ChainId.POLYGON_MUMBAI, - transactionHash: MOCK_TRANSACTION_HASH, - }; - - jsonRpcProviderMock.getTransactionReceipt.mockResolvedValue(null); - - await expect( - paymentService.createCryptoPayment(userId, dto, MOCK_SIGNATURE), - ).rejects.toThrowError(ErrorPayment.TransactionNotFoundByHash); - }); - - it('should throw a not found exception if the transaction data is invalid', async () => { - const userId = 1; - const dto = { - chainId: ChainId.LOCALHOST, - transactionHash: MOCK_TRANSACTION_HASH, - }; - - const transactionReceipt: Partial = { - from: MOCK_ADDRESS, - logs: [], - confirmations: TX_CONFIRMATION_TRESHOLD, - }; - - jsonRpcProviderMock.getTransactionReceipt.mockResolvedValue( - transactionReceipt, - ); - - await expect( - paymentService.createCryptoPayment(userId, dto, MOCK_SIGNATURE), - ).rejects.toThrowError(ErrorPayment.InvalidTransactionData); - }); - - it('should throw a not found exception if the transaction has insufficient confirmations', async () => { - const userId = 1; - const dto = { - chainId: ChainId.POLYGON_MUMBAI, - transactionHash: MOCK_TRANSACTION_HASH, - }; - - const transactionReceipt: Partial = { - from: MOCK_ADDRESS, - logs: [ - { - data: ethers.utils.parseUnits('10').toString(), - blockNumber: 123, - blockHash: '123', - transactionIndex: 123, - removed: false, - address: NETWORKS[ChainId.POLYGON_MUMBAI]?.hmtAddress as string, - topics: ['0x123', '0x0000000000000000000000000123', MOCK_ADDRESS], - transactionHash: MOCK_TRANSACTION_HASH, - logIndex: 123, - }, - ], - transactionHash: MOCK_TRANSACTION_HASH, - confirmations: TX_CONFIRMATION_TRESHOLD - 1, - }; - - jsonRpcProviderMock.getTransactionReceipt.mockResolvedValue( - transactionReceipt, - ); - - await expect( - paymentService.createCryptoPayment(userId, dto, MOCK_SIGNATURE), - ).rejects.toThrowError( - ErrorPayment.TransactionHasNotEnoughAmountOfConfirmations, - ); - }); - - it('should throw a bad request exception if the payment with the same transaction hash already exists', async () => { - const userId = 1; - const dto = { - chainId: ChainId.POLYGON_MUMBAI, - transactionHash: MOCK_TRANSACTION_HASH, - }; - - const token = 'hmt'; - - const transactionReceipt: Partial = { - from: MOCK_ADDRESS, - logs: [ - { - data: ethers.utils.parseUnits('10').toString(), - blockNumber: 123, - blockHash: '123', - transactionIndex: 123, - removed: false, - address: NETWORKS[ChainId.POLYGON_MUMBAI]?.hmtAddress as string, - topics: ['0x123', '0x0000000000000000000000000123', MOCK_ADDRESS], - transactionHash: MOCK_TRANSACTION_HASH, - logIndex: 123, - }, - ], - transactionHash: MOCK_TRANSACTION_HASH, - confirmations: TX_CONFIRMATION_TRESHOLD, - }; - - jsonRpcProviderMock.getTransactionReceipt.mockResolvedValue( - transactionReceipt, - ); - - mockTokenContract.symbol.mockResolvedValue(token); - - findOneMock.mockResolvedValue({} as any); - - await expect( - paymentService.createCryptoPayment(userId, dto, MOCK_SIGNATURE), - ).rejects.toThrowError(ErrorPayment.TransactionAlreadyExists); - }); - }); - - describe('getUserBalance', () => { - it('should return the correct balance for a user', async () => { - const userId = 1; - const expectedBalance = 20; - - paymentRepository.find = jest.fn().mockResolvedValue([ - { - amount: 50, - rate: 1, - type: PaymentType.DEPOSIT, - status: PaymentStatus.SUCCEEDED, - }, - { - amount: 150, - rate: 1, - type: PaymentType.DEPOSIT, - status: PaymentStatus.SUCCEEDED, - }, - { - amount: -180, - rate: 1, - type: PaymentType.WITHDRAWAL, - status: PaymentStatus.SUCCEEDED, - }, - ]); - - const balance = await paymentService.getUserBalance(userId); - - expect(balance).toEqual(expectedBalance); - expect(paymentRepository.find).toHaveBeenCalledWith({ - userId, - status: PaymentStatus.SUCCEEDED, - }); - }); - - it('should return 0 balance for a user with no payment entities', async () => { - const userId = 1; - paymentRepository.find = jest.fn().mockResolvedValue([]); - - const balance = await paymentService.getUserBalance(userId); - - expect(balance).toEqual(0); - expect(paymentRepository.find).toHaveBeenCalledWith({ - userId, - status: PaymentStatus.SUCCEEDED, - }); - }); - }); - - describe('createRefundPayment', () => { - const mockPaymentRefundCreateDto = { - userId: 1, - jobId: 2, - refundAmount: 100, - }; - - it('should successfully create a refund payment', async () => { - jest - .spyOn(paymentRepository, 'create') - .mockResolvedValueOnce(undefined as any); - - await expect( - paymentService.createRefundPayment(mockPaymentRefundCreateDto), - ).resolves.not.toThrow(); - }); - - it('should throw IncorrectAmount error when overflow occurs', async () => { - const mockError = new QueryFailedError('', [], ''); - mockError.message = ErrorPostgres.NumericFieldOverflow.toLowerCase(); - jest.spyOn(paymentRepository, 'create').mockRejectedValueOnce(mockError); - - await expect( - paymentService.createRefundPayment(mockPaymentRefundCreateDto), - ).rejects.toThrow(ConflictException); - }); - - it('should throw NotSuccess error on other database errors', async () => { - jest - .spyOn(paymentRepository, 'create') - .mockRejectedValueOnce(new Error()); - - await expect( - paymentService.createRefundPayment(mockPaymentRefundCreateDto), - ).rejects.toThrow(ConflictException); - }); - }); -}); diff --git a/packages/apps/hufi/job-launcher/server/src/modules/payment/payment.service.ts b/packages/apps/hufi/job-launcher/server/src/modules/payment/payment.service.ts deleted file mode 100644 index 3e0d58eec4..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/payment/payment.service.ts +++ /dev/null @@ -1,300 +0,0 @@ -import { - BadRequestException, - ConflictException, - Injectable, - Logger, - NotFoundException, -} from '@nestjs/common'; -import { ConfigService } from '@nestjs/config'; -import Stripe from 'stripe'; -import { ethers } from 'ethers'; -import { ErrorPayment, ErrorPostgres } from '../../common/constants/errors'; -import { PaymentRepository } from './payment.repository'; -import { - PaymentCryptoCreateDto, - PaymentFiatConfirmDto, - PaymentFiatCreateDto, - PaymentRefundCreateDto, -} from './payment.dto'; -import { - Currency, - PaymentSource, - PaymentStatus, - PaymentType, - StripePaymentStatus, - TokenId, -} from '../../common/enums/payment'; -import { TX_CONFIRMATION_TRESHOLD } from '../../common/constants'; -import { ConfigNames, networkMap } from '../../common/config'; -import { - HMToken, - HMToken__factory, -} from '@human-protocol/core/typechain-types'; -import { Web3Service } from '../web3/Web3Service'; -import { CoingeckoTokenId } from '../../common/constants/payment'; -import { getRate } from '../../common/utils'; -import { add, div, mul } from '../../common/utils/decimal'; -import { QueryFailedError } from 'typeorm'; -import { verifySignature } from '../../common/utils/signature'; - -@Injectable() -export class PaymentService { - private readonly logger = new Logger(PaymentService.name); - private stripe: Stripe; - - constructor( - private readonly web3Service: Web3Service, - private readonly paymentRepository: PaymentRepository, - private configService: ConfigService, - ) { - this.stripe = new Stripe( - this.configService.get(ConfigNames.STRIPE_SECRET_KEY)!, - { - apiVersion: this.configService.get( - ConfigNames.STRIPE_API_VERSION, - )!, - appInfo: { - name: this.configService.get( - ConfigNames.STRIPE_APP_NAME, - 'Hufi', - )!, - version: this.configService.get( - ConfigNames.STRIPE_APP_VERSION, - )!, - url: this.configService.get(ConfigNames.STRIPE_APP_INFO_URL)!, - }, - }, - ); - } - - public async createFiatPayment( - userId: number, - dto: PaymentFiatCreateDto, - ): Promise { - const { amount, currency } = dto; - - const amountInCents = Math.ceil(mul(amount, 100)); - const params: Stripe.PaymentIntentCreateParams = { - amount: amountInCents, - currency: currency, - }; - - const paymentIntent = await this.stripe.paymentIntents.create(params); - - if (!paymentIntent.client_secret) { - this.logger.log( - ErrorPayment.ClientSecretDoesNotExist, - PaymentService.name, - ); - throw new NotFoundException(ErrorPayment.ClientSecretDoesNotExist); - } - - const paymentEntity = await this.paymentRepository.findOne({ - transaction: paymentIntent.id, - }); - - if (paymentEntity) { - this.logger.log( - ErrorPayment.TransactionAlreadyExists, - PaymentRepository.name, - ); - throw new BadRequestException(ErrorPayment.TransactionAlreadyExists); - } - - const rate = await getRate(currency, Currency.USD); - - await this.paymentRepository.create({ - userId, - source: PaymentSource.FIAT, - type: PaymentType.DEPOSIT, - amount: div(amountInCents, 100), - currency, - rate, - transaction: paymentIntent.id, - status: PaymentStatus.PENDING, - }); - - return paymentIntent.client_secret; - } - - public async confirmFiatPayment( - userId: number, - data: PaymentFiatConfirmDto, - ): Promise { - const paymentData = await this.stripe.paymentIntents.retrieve( - data.paymentId, - ); - - if (!paymentData) { - this.logger.log(ErrorPayment.NotFound, PaymentService.name); - throw new NotFoundException(ErrorPayment.NotFound); - } - - const paymentEntity = await this.paymentRepository.findOne({ - userId, - transaction: data.paymentId, - status: PaymentStatus.PENDING, - amount: div(paymentData.amount_received, 100), - currency: paymentData.currency, - }); - - if (!paymentEntity) { - this.logger.log(ErrorPayment.NotFound, PaymentRepository.name); - throw new BadRequestException(ErrorPayment.NotFound); - } - - if ( - paymentData?.status === StripePaymentStatus.CANCELED || - paymentData?.status === StripePaymentStatus.REQUIRES_PAYMENT_METHOD - ) { - paymentEntity.status = PaymentStatus.FAILED; - await paymentEntity.save(); - this.logger.log(ErrorPayment.NotSuccess, PaymentService.name); - throw new BadRequestException(ErrorPayment.NotSuccess); - } else if (paymentData?.status !== StripePaymentStatus.SUCCEEDED) { - return false; // TODO: Handling other cases - } - - paymentEntity.status = PaymentStatus.SUCCEEDED; - await paymentEntity.save(); - - return true; - } - - public async createCryptoPayment( - userId: number, - dto: PaymentCryptoCreateDto, - signature: string, - ): Promise { - this.web3Service.validateChainId(dto.chainId); - const network = Object.values(networkMap).find( - (item) => item.chainId === dto.chainId, - ); - const provider = new ethers.JsonRpcProvider(network?.rpcUrl); - - const transaction = await provider.getTransactionReceipt( - dto.transactionHash, - ); - - if (!transaction) { - this.logger.error(ErrorPayment.TransactionNotFoundByHash); - throw new NotFoundException(ErrorPayment.TransactionNotFoundByHash); - } - - verifySignature(dto, signature, [transaction.from]); - - if (!transaction.logs[0] || !transaction.logs[0].data) { - this.logger.error(ErrorPayment.InvalidTransactionData); - throw new NotFoundException(ErrorPayment.InvalidTransactionData); - } - - if ((await transaction.confirmations()) < TX_CONFIRMATION_TRESHOLD) { - this.logger.error( - `Transaction has ${transaction.confirmations} confirmations instead of ${TX_CONFIRMATION_TRESHOLD}`, - ); - throw new NotFoundException( - ErrorPayment.TransactionHasNotEnoughAmountOfConfirmations, - ); - } - - const signer = this.web3Service.getSigner(dto.chainId); - - const recipientAddress = transaction.logs[0].topics.some( - (topic) => ethers.hexlify(topic) === ethers.hexlify(signer.address), - ); - if (!recipientAddress) { - this.logger.error(ErrorPayment.InvalidRecipient); - throw new ConflictException(ErrorPayment.InvalidRecipient); - } - - const amount = Number(ethers.formatEther(transaction.logs[0].data)); - const tokenAddress = transaction.logs[0].address; - - const tokenContract: HMToken = HMToken__factory.connect( - tokenAddress, - signer, - ); - const tokenId = (await tokenContract.symbol()).toLowerCase(); - - if ( - network?.tokens[tokenId] != tokenAddress || - !CoingeckoTokenId[tokenId] - ) { - this.logger.log(ErrorPayment.UnsupportedToken, PaymentRepository.name); - throw new ConflictException(ErrorPayment.UnsupportedToken); - } - - const paymentEntity = await this.paymentRepository.findOne({ - transaction: transaction.hash, - chainId: dto.chainId, - }); - - if (paymentEntity) { - this.logger.log( - ErrorPayment.TransactionAlreadyExists, - PaymentRepository.name, - ); - throw new BadRequestException(ErrorPayment.TransactionAlreadyExists); - } - - const rate = await getRate(tokenId, Currency.USD); - - await this.paymentRepository.create({ - userId, - source: PaymentSource.CRYPTO, - type: PaymentType.DEPOSIT, - amount: amount, - currency: tokenId, - rate, - chainId: dto.chainId, - transaction: dto.transactionHash, - status: PaymentStatus.SUCCEEDED, - }); - - return true; - } - - public async getUserBalance(userId: number): Promise { - const paymentEntities = await this.paymentRepository.find({ - userId, - status: PaymentStatus.SUCCEEDED, - }); - - const totalAmount = paymentEntities.reduce((total, payment) => { - return add(total, mul(payment.amount, payment.rate)); - }, 0); - - return totalAmount; - } - - public async createRefundPayment(dto: PaymentRefundCreateDto) { - const rate = await getRate(TokenId.HMT, Currency.USD); - - try { - await this.paymentRepository.create({ - userId: dto.userId, - jobId: dto.jobId, - source: PaymentSource.BALANCE, - type: PaymentType.REFUND, - amount: dto.refundAmount, - currency: TokenId.HMT, - rate, - status: PaymentStatus.SUCCEEDED, - }); - } catch (error) { - if ( - error instanceof QueryFailedError && - error.message.includes(ErrorPostgres.NumericFieldOverflow.toLowerCase()) - ) { - this.logger.log( - ErrorPostgres.NumericFieldOverflow, - PaymentService.name, - ); - throw new ConflictException(ErrorPayment.IncorrectAmount); - } else { - this.logger.log(error, PaymentService.name); - throw new ConflictException(ErrorPayment.NotSuccess); - } - } - } -} diff --git a/packages/apps/hufi/job-launcher/server/src/modules/sendgrid/sendgrid.module.ts b/packages/apps/hufi/job-launcher/server/src/modules/sendgrid/sendgrid.module.ts deleted file mode 100644 index f1a1c2a3d1..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/sendgrid/sendgrid.module.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Global, Module } from '@nestjs/common'; -import { ConfigModule } from '@nestjs/config'; -import { MailService } from '@sendgrid/mail'; - -import { SendGridService } from './sendgrid.service'; - -@Global() -@Module({ - imports: [ConfigModule], - providers: [SendGridService, MailService], - exports: [SendGridService], -}) -export class SendGridModule {} diff --git a/packages/apps/hufi/job-launcher/server/src/modules/sendgrid/sendgrid.service.spec.ts b/packages/apps/hufi/job-launcher/server/src/modules/sendgrid/sendgrid.service.spec.ts deleted file mode 100644 index c26f8b7a25..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/sendgrid/sendgrid.service.spec.ts +++ /dev/null @@ -1,136 +0,0 @@ -import { ConfigService } from '@nestjs/config'; -import { Test } from '@nestjs/testing'; -import { SendGridService } from './sendgrid.service'; -import { MailService } from '@sendgrid/mail'; -import { ErrorSendGrid } from '../../common/constants/errors'; -import { - MOCK_SENDGRID_API_KEY, - MOCK_SENDGRID_FROM_EMAIL, - MOCK_SENDGRID_FROM_NAME, -} from '../../../test/constants'; - -describe('SendGridService', () => { - let sendGridService: SendGridService; - let mailService: MailService; - let mockConfigService: Partial; - - beforeAll(async () => { - const mockMailService = { - send: jest.fn(), - setApiKey: jest.fn(), - }; - - mockConfigService = { - get: jest.fn((key: string) => { - switch (key) { - case 'SENDGRID_API_KEY': - return MOCK_SENDGRID_API_KEY; - case 'SENDGRID_FROM_EMAIL': - return MOCK_SENDGRID_FROM_EMAIL; - case 'SENDGRID_FROM_NAME': - return MOCK_SENDGRID_FROM_NAME; - } - }), - }; - - const app = await Test.createTestingModule({ - providers: [ - SendGridService, - { - provide: MailService, - useValue: mockMailService, - }, - { - provide: ConfigService, - useValue: mockConfigService, - }, - ], - }).compile(); - sendGridService = app.get(SendGridService); - mailService = app.get(MailService); - }); - - describe('sendEmail', () => { - it('should send email', async () => { - const mock = jest - .spyOn(mailService, 'send') - .mockImplementationOnce(async () => { - return [{} as any, {}]; - }); - await sendGridService.sendEmail({ - to: 'test@example.com', - from: 'test@example.com', - subject: 'Sending with SendGrid is Fun', - text: 'and easy to do anywhere, even with Node.js', - html: 'and easy to do anywhere, even with Node.js', - }); - expect(mock).toHaveBeenCalled(); - }); - - it('should send email from default address', async () => { - const mock = jest - .spyOn(mailService, 'send') - .mockImplementationOnce(async () => { - return [{} as any, {}]; - }); - await sendGridService.sendEmail({ - to: 'test@example.com', - subject: 'Sending with SendGrid is Fun', - text: 'and easy to do anywhere, even with Node.js', - html: 'and easy to do anywhere, even with Node.js', - }); - - expect(mock).toHaveBeenCalledWith( - expect.objectContaining({ - from: expect.objectContaining({ - email: 'info@hmt.ai', - }), - }), - ); - }); - - it("should throw error if email wasn't sent", async () => { - jest.spyOn(mailService, 'send').mockImplementationOnce(async () => { - throw new Error(ErrorSendGrid.EmailNotSent); - }); - - await expect( - sendGridService.sendEmail({ - to: 'test@example.com', - subject: 'Sending with SendGrid is Fun', - text: 'and easy to do anywhere, even with Node.js', - html: 'and easy to do anywhere, even with Node.js', - }), - ).rejects.toThrowError(ErrorSendGrid.EmailNotSent); - }); - }); - - describe('constructor', () => { - it('should initialize SendGridService with valid API key', () => { - sendGridService = new SendGridService( - mailService, - mockConfigService as any, - ); - - expect(mailService.setApiKey).toHaveBeenCalledWith(MOCK_SENDGRID_API_KEY); - expect(sendGridService['defaultFromEmail']).toEqual( - MOCK_SENDGRID_FROM_EMAIL, - ); - expect(sendGridService['defaultFromName']).toEqual( - MOCK_SENDGRID_FROM_NAME, - ); - }); - - it('should throw an error with invalid API key', async () => { - const invalidApiKey = 'invalid-api-key'; - mockConfigService.get = jest.fn().mockReturnValue(invalidApiKey); - - expect(() => { - sendGridService = new SendGridService( - mailService, - mockConfigService as any, - ); - }).toThrowError(ErrorSendGrid.InvalidApiKey); - }); - }); -}); diff --git a/packages/apps/hufi/job-launcher/server/src/modules/sendgrid/sendgrid.service.ts b/packages/apps/hufi/job-launcher/server/src/modules/sendgrid/sendgrid.service.ts deleted file mode 100644 index c36afe91aa..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/sendgrid/sendgrid.service.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { BadRequestException, Injectable, Logger } from '@nestjs/common'; -import { ConfigService } from '@nestjs/config'; -import { MailDataRequired, MailService } from '@sendgrid/mail'; -import { ConfigNames } from '../../common/config'; -import { SENDGRID_API_KEY_REGEX } from '../../common/constants'; -import { ErrorSendGrid } from '../../common/constants/errors'; - -@Injectable() -export class SendGridService { - private readonly logger = new Logger(SendGridService.name); - - private readonly defaultFromEmail: string; - private readonly defaultFromName: string; - - constructor( - private readonly mailService: MailService, - private readonly configService: ConfigService, - ) { - const apiKey = this.configService.get( - ConfigNames.SENDGRID_API_KEY, - )!; - - if (!SENDGRID_API_KEY_REGEX.test(apiKey)) { - throw new Error(ErrorSendGrid.InvalidApiKey); - } - - this.mailService.setApiKey(apiKey); - - this.defaultFromEmail = this.configService.get( - ConfigNames.SENDGRID_FROM_EMAIL, - )!; - this.defaultFromName = this.configService.get( - ConfigNames.SENDGRID_FROM_NAME, - )!; - } - - async sendEmail({ - from = { - email: this.defaultFromEmail, - name: this.defaultFromName, - }, - templateId = '', - personalizations, - ...emailData - }: Partial): Promise { - try { - await this.mailService.send({ - from, - templateId, - personalizations, - ...emailData, - }); - - this.logger.log('Email sent successfully'); - return; - } catch (error) { - this.logger.error(error, SendGridService.name); - throw new BadRequestException(ErrorSendGrid.EmailNotSent); - } - } -} diff --git a/packages/apps/hufi/job-launcher/server/src/modules/user/user.controller.ts b/packages/apps/hufi/job-launcher/server/src/modules/user/user.controller.ts deleted file mode 100644 index 8a6dbbb71b..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/user/user.controller.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { - Controller, - Get, - Logger, - Request, - UnprocessableEntityException, - UseGuards, -} from '@nestjs/common'; -import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; -import { UserService } from './user.service'; -import { IUserBalance } from '../../common/interfaces'; -import { JwtAuthGuard } from '../../common/guards'; -import { RequestWithUser } from '../../common/types'; -import { ErrorUser } from '../../common/constants/errors'; - -@ApiBearerAuth() -@UseGuards(JwtAuthGuard) -@ApiTags('User') -@Controller('/user') -export class UserController { - private readonly logger = new Logger(UserController.name); - - constructor(private readonly userService: UserService) {} - - @Get('/balance') - public async getBalance( - @Request() req: RequestWithUser, - ): Promise { - try { - return this.userService.getBalance(req.user.id); - } catch (e) { - this.logger.log( - e.message, - `${UserController.name} - ${ErrorUser.BalanceCouldNotBeRetreived}`, - ); - throw new UnprocessableEntityException( - ErrorUser.BalanceCouldNotBeRetreived, - ); - } - } -} diff --git a/packages/apps/hufi/job-launcher/server/src/modules/user/user.dto.ts b/packages/apps/hufi/job-launcher/server/src/modules/user/user.dto.ts deleted file mode 100644 index d6718fdc49..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/user/user.dto.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { IsEmail, IsEnum, IsOptional } from 'class-validator'; -import { Transform } from 'class-transformer'; -import { UserStatus, UserType } from '../../common/enums/user'; -import { ValidatePasswordDto } from '../auth/auth.dto'; - -export class UserCreateDto extends ValidatePasswordDto { - @ApiProperty() - @IsEmail() - @Transform(({ value }: { value: string }) => value.toLowerCase()) - public email: string; -} - -export class UserDto extends UserCreateDto { - public type: UserType; - public status: UserStatus; -} - -export class UserUpdateDto { - @ApiPropertyOptional() - @IsOptional() - @IsEmail() - @Transform(({ value }: { value: string }) => value.toLowerCase()) - public email?: string; - - @ApiPropertyOptional({ - enum: UserStatus, - }) - @IsEnum(UserStatus) - public status?: UserStatus; -} diff --git a/packages/apps/hufi/job-launcher/server/src/modules/user/user.entity.ts b/packages/apps/hufi/job-launcher/server/src/modules/user/user.entity.ts deleted file mode 100644 index 55e4215352..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/user/user.entity.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Column, Entity, OneToMany, OneToOne } from 'typeorm'; -import { Exclude } from 'class-transformer'; - -import { NS } from '../../common/constants'; -import { BaseEntity } from '../../database/base.entity'; -import { IUser } from '../../common/interfaces'; -import { UserStatus, UserType } from '../../common/enums/user'; -import { PaymentEntity } from '../payment/payment.entity'; -import { JobEntity } from '../job/job.entity'; -import { TokenEntity } from '../auth/token.entity'; -import { AuthEntity } from '../auth/auth.entity'; - -@Entity({ schema: NS, name: 'users' }) -export class UserEntity extends BaseEntity implements IUser { - @Exclude() - @Column({ type: 'varchar' }) - public password: string; - - @Column({ type: 'varchar', nullable: true, unique: true }) - public email: string; - - @Column({ type: 'enum', enum: UserType }) - public type: UserType; - - @Column({ - type: 'enum', - enum: UserStatus, - }) - public status: UserStatus; - - @OneToOne(() => AuthEntity) - public auth: AuthEntity; - - @OneToOne(() => TokenEntity) - public token: TokenEntity; - - @OneToMany(() => JobEntity, (job) => job.user) - public jobs: JobEntity[]; - - @OneToMany(() => PaymentEntity, (payment) => payment.user) - public payments: PaymentEntity[]; -} diff --git a/packages/apps/hufi/job-launcher/server/src/modules/user/user.module.ts b/packages/apps/hufi/job-launcher/server/src/modules/user/user.module.ts deleted file mode 100644 index c061b02b99..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/user/user.module.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Logger, Module } from '@nestjs/common'; -import { TypeOrmModule } from '@nestjs/typeorm'; -import { ConfigModule } from '@nestjs/config'; - -import { UserService } from './user.service'; -import { UserEntity } from './user.entity'; -import { UserController } from './user.controller'; -import { UserRepository } from './user.repository'; -import { PaymentModule } from '../payment/payment.module'; - -@Module({ - imports: [ - TypeOrmModule.forFeature([UserEntity]), - ConfigModule, - PaymentModule, - ], - controllers: [UserController], - providers: [Logger, UserService, UserRepository], - exports: [UserService], -}) -export class UserModule {} diff --git a/packages/apps/hufi/job-launcher/server/src/modules/user/user.repository.ts b/packages/apps/hufi/job-launcher/server/src/modules/user/user.repository.ts deleted file mode 100644 index 8c01f92468..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/user/user.repository.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { Injectable, Logger, NotFoundException } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { - FindOptionsWhere, - FindManyOptions, - FindOneOptions, - Repository, -} from 'typeorm'; - -import { UserEntity } from './user.entity'; -import { UserDto, UserUpdateDto } from './user.dto'; -import { ErrorUser } from '../../common/constants/errors'; - -@Injectable() -export class UserRepository { - private readonly logger = new Logger(UserRepository.name); - - constructor( - @InjectRepository(UserEntity) - private readonly userEntityRepository: Repository, - ) {} - - public async updateOne( - where: FindOptionsWhere, - dto: Partial, - ): Promise { - const userEntity = await this.userEntityRepository.findOneBy(where); - - if (!userEntity) { - this.logger.log(ErrorUser.NotFound, UserRepository.name); - throw new NotFoundException(ErrorUser.NotFound); - } - - Object.assign(userEntity, dto); - return userEntity.save(); - } - - public async findOne( - where: FindOptionsWhere, - options?: FindOneOptions, - ): Promise { - const userEntity = await this.userEntityRepository.findOne({ - where, - ...options, - }); - - return userEntity; - } - - public find( - where: FindOptionsWhere, - options?: FindManyOptions, - ): Promise { - return this.userEntityRepository.find({ - where, - order: { - createdAt: 'DESC', - }, - ...options, - }); - } - - public async create(dto: UserDto): Promise { - return this.userEntityRepository.create(dto).save(); - } -} diff --git a/packages/apps/hufi/job-launcher/server/src/modules/user/user.service.spec.ts b/packages/apps/hufi/job-launcher/server/src/modules/user/user.service.spec.ts deleted file mode 100644 index 9522b18c0a..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/user/user.service.spec.ts +++ /dev/null @@ -1,182 +0,0 @@ -import { Test } from '@nestjs/testing'; -import { ConflictException, NotFoundException } from '@nestjs/common'; -import { PaymentService } from '../payment/payment.service'; -import { ConfigService } from '@nestjs/config'; -import { HttpService } from '@nestjs/axios'; -import { createMock } from '@golevelup/ts-jest'; -import { ErrorUser } from '../../common/constants/errors'; -import { UserRepository } from './user.repository'; -import { UserService } from './user.service'; -import { UserCreateDto, UserUpdateDto } from './user.dto'; -import { UserEntity } from './user.entity'; -import { UserStatus, UserType } from '../../common/enums/user'; -import { ethers } from 'ethers'; -import { IUserBalance } from '../../common/interfaces'; -import { Currency } from '../../common/enums/payment'; - -jest.mock('@human-protocol/sdk'); - -describe('UserService', () => { - let userService: UserService; - let paymentService: PaymentService; - let userRepository: UserRepository; - let configService: ConfigService; - let httpService: HttpService; - - beforeAll(async () => { - const mockConfigService: Partial = {}; - - const moduleRef = await Test.createTestingModule({ - providers: [ - UserService, - { provide: UserRepository, useValue: createMock() }, - { provide: PaymentService, useValue: createMock() }, - { provide: ConfigService, useValue: mockConfigService }, - { provide: HttpService, useValue: createMock() }, - ], - }).compile(); - - userService = moduleRef.get(UserService); - userRepository = moduleRef.get(UserRepository); - configService = moduleRef.get(ConfigService); - httpService = moduleRef.get(HttpService); - paymentService = moduleRef.get(PaymentService); - }); - - describe('update', () => { - it('should update a user and return the updated user entity', async () => { - const userId = 1; - const dto: UserUpdateDto = { - email: 'test@example.com', - status: UserStatus.ACTIVE, - }; - - const updatedUser: Partial = { - id: userId, - email: dto.email, - status: dto.status, - }; - - jest - .spyOn(userRepository, 'updateOne') - .mockResolvedValue(updatedUser as UserEntity); - - const result = await userService.update(userId, dto); - - expect(userRepository.updateOne).toHaveBeenCalledWith( - { id: userId }, - dto, - ); - expect(result).toBe(updatedUser); - }); - }); - - describe('create', () => { - it('should create a new user and return the created user entity', async () => { - const dto: UserCreateDto = { - email: 'test@example.com', - password: 'password123', - confirm: 'password123', - }; - const hashedPassword = - '$2b$12$Z02o9/Ay7CT0n99icApZYORH8iJI9VGtl3mju7d0c4SdDDujhSzOa'; - const createdUser: Partial = { - id: 1, - email: dto.email, - password: hashedPassword, - }; - - jest.spyOn(userService, 'checkEmail').mockResolvedValue(undefined); - jest - .spyOn(userRepository, 'create') - .mockResolvedValue(createdUser as UserEntity); - - const result = await userService.create(dto); - - expect(userService.checkEmail).toHaveBeenCalledWith(dto.email, 0); - expect(userRepository.create).toHaveBeenCalledWith({ - ...dto, - email: dto.email, - password: expect.any(String), - type: UserType.REQUESTER, - status: UserStatus.PENDING, - }); - expect(result).toBe(createdUser); - }); - - it('should throw ConflictException if the email is already taken', async () => { - const dto: UserCreateDto = { - email: 'test@example.com', - password: 'password123', - confirm: 'password123', - }; - - jest - .spyOn(userService, 'checkEmail') - .mockRejectedValue( - new ConflictException(ErrorUser.AccountCannotBeRegistered), - ); - - await expect(userService.create(dto)).rejects.toThrow( - ErrorUser.AccountCannotBeRegistered, - ); - - expect(userService.checkEmail).toHaveBeenCalledWith(dto.email, 0); - }); - }); - - describe('getByCredentials', () => { - const email = 'test@example.com'; - const password = 'password123'; - const hashedPassword = - '$2b$12$Z02o9/Ay7CT0n99icApZYORH8iJI9VGtl3mju7d0c4SdDDujhSzOa'; - - const userEntity: Partial = { - id: 1, - email, - password: hashedPassword, - }; - - it('should return the user entity if credentials are valid', async () => { - jest - .spyOn(userRepository, 'findOne') - .mockResolvedValue(userEntity as UserEntity); - - const result = await userService.getByCredentials(email, password); - - expect(userRepository.findOne).toHaveBeenCalledWith({ - email, - }); - expect(result).toBe(userEntity); - }); - - it('should throw NotFoundException if credentials are invalid', async () => { - jest.spyOn(userRepository, 'findOne').mockResolvedValue(null); - - await expect( - userService.getByCredentials(email, password), - ).rejects.toThrow(NotFoundException); - - expect(userRepository.findOne).toHaveBeenCalledWith({ - email, - }); - }); - }); - - describe('getBalance', () => { - it('should return the correct balance with currency for a user', async () => { - const userId = 1; - const expectedBalance: IUserBalance = { - amount: 10, - currency: Currency.USD, - }; - - jest.spyOn(paymentService, 'getUserBalance').mockResolvedValue(10); - - const balance = await userService.getBalance(userId); - - expect(balance).toEqual(expectedBalance); - expect(paymentService.getUserBalance).toHaveBeenCalledWith(userId); - }); - }); -}); diff --git a/packages/apps/hufi/job-launcher/server/src/modules/user/user.service.ts b/packages/apps/hufi/job-launcher/server/src/modules/user/user.service.ts deleted file mode 100644 index 419c05d048..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/user/user.service.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { - ConflictException, - Injectable, - Logger, - NotFoundException, -} from '@nestjs/common'; -import { ConfigService } from '@nestjs/config'; -import * as bcrypt from 'bcrypt'; -import { Not } from 'typeorm'; - -import { UserEntity } from './user.entity'; -import { UserStatus, UserType } from '../../common/enums/user'; -import { UserCreateDto, UserUpdateDto } from './user.dto'; -import { UserRepository } from './user.repository'; -import { ValidatePasswordDto } from '../auth/auth.dto'; -import { ErrorUser } from '../../common/constants/errors'; -import { PaymentService } from '../payment/payment.service'; -import { IUserBalance } from '../../common/interfaces'; -import { Currency } from '../../common/enums/payment'; - -@Injectable() -export class UserService { - private readonly logger = new Logger(UserService.name); - private HASH_ROUNDS = 12; - constructor( - private userRepository: UserRepository, - private readonly configService: ConfigService, - private readonly paymentService: PaymentService, - ) {} - - public async update(userId: number, dto: UserUpdateDto): Promise { - return this.userRepository.updateOne({ id: userId }, dto); - } - - public async create(dto: UserCreateDto): Promise { - const { email, password, ...rest } = dto; - - await this.checkEmail(email, 0); - - return await this.userRepository.create({ - ...rest, - email, - password: bcrypt.hashSync(password, this.HASH_ROUNDS), - type: UserType.REQUESTER, - status: UserStatus.PENDING, - }); - } - - public async getByCredentials( - email: string, - password: string, - ): Promise { - const userEntity = await this.userRepository.findOne({ - email, - }); - - if (!userEntity) { - throw new NotFoundException(ErrorUser.InvalidCredentials); - } - - if (!bcrypt.compareSync(password, userEntity.password)) { - throw new NotFoundException(ErrorUser.InvalidCredentials); - } - - return userEntity; - } - - public async getByEmail(email: string): Promise { - return this.userRepository.findOne({ email }); - } - - public updatePassword( - userEntity: UserEntity, - data: ValidatePasswordDto, - ): Promise { - userEntity.password = bcrypt.hashSync(data.password, this.HASH_ROUNDS); - return userEntity.save(); - } - - public activate(userEntity: UserEntity): Promise { - userEntity.status = UserStatus.ACTIVE; - return userEntity.save(); - } - - public async checkEmail(email: string, id: number): Promise { - const userEntity = await this.userRepository.findOne({ - email, - id: Not(id), - }); - - if (userEntity) { - this.logger.log(ErrorUser.AccountCannotBeRegistered, UserService.name); - throw new ConflictException(ErrorUser.AccountCannotBeRegistered); - } - } - - public async getBalance(userId: number): Promise { - return { - amount: await this.paymentService.getUserBalance(userId), - currency: Currency.USD, - }; - } -} diff --git a/packages/apps/hufi/job-launcher/server/src/modules/web3/Web3Service.ts b/packages/apps/hufi/job-launcher/server/src/modules/web3/Web3Service.ts deleted file mode 100644 index c76962ec48..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/web3/Web3Service.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { BadRequestException, Injectable, Logger } from '@nestjs/common'; -import { ConfigService } from '@nestjs/config'; -import { Wallet, ethers } from 'ethers'; -import { ConfigNames, networks } from '../../common/config'; -import { Web3Env } from '../../common/enums/web3'; -import { MAINNET_CHAIN_IDS, TESTNET_CHAIN_IDS } from '../../common/constants'; -import { ErrorWeb3 } from '../../common/constants/errors'; -import { ChainId } from '@human-protocol/sdk'; - -@Injectable() -export class Web3Service { - private signers: { [key: number]: Wallet } = {}; - public readonly logger = new Logger(Web3Service.name); - public readonly signerAddress: string; - - constructor(private readonly configService: ConfigService) { - const privateKey = this.configService.get(ConfigNames.WEB3_PRIVATE_KEY); - const validChains = this.getValidChains(); - const validNetworks = networks.filter((network) => - validChains.includes(network.chainId), - ); - for (const network of validNetworks) { - const provider = new ethers.JsonRpcProvider(network.rpcUrl); - this.signers[network.chainId] = new Wallet(privateKey, provider); - } - this.signerAddress = this.signers[validChains[0]].address; - } - - public getSigner(chainId: number): Wallet { - this.validateChainId(chainId); - return this.signers[chainId]; - } - - public validateChainId(chainId: number): void { - const currentWeb3Env = this.configService.get(ConfigNames.WEB3_ENV); - const validChainIds = this.getValidChains(); - - if (!validChainIds.includes(chainId)) { - const errorType = - currentWeb3Env === Web3Env.MAINNET - ? ErrorWeb3.InvalidMainnetChainId - : ErrorWeb3.InvalidTestnetChainId; - this.logger.log(errorType, Web3Service.name); - throw new BadRequestException(errorType); - } - } - - public getValidChains(): ChainId[] { - const currentWeb3Env = this.configService.get(ConfigNames.WEB3_ENV); - const validChainIds = - currentWeb3Env === Web3Env.MAINNET - ? MAINNET_CHAIN_IDS - : TESTNET_CHAIN_IDS; - - return validChainIds; - } -} diff --git a/packages/apps/hufi/job-launcher/server/src/modules/web3/web3.controller.ts b/packages/apps/hufi/job-launcher/server/src/modules/web3/web3.controller.ts deleted file mode 100644 index 58c2d116e3..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/web3/web3.controller.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Controller, Get } from '@nestjs/common'; -import { Public } from '../../common/decorators'; -import { ApiTags } from '@nestjs/swagger'; -import { Web3Service } from './Web3Service'; -import { ChainId } from '@human-protocol/sdk'; - -@ApiTags('Web3') -@Controller('/web3') -export class Web3Controller { - constructor(private readonly web3Service: Web3Service) {} - - @Public() - @Get('/networks') - getValidChains(): ChainId[] { - return this.web3Service.getValidChains(); - } -} diff --git a/packages/apps/hufi/job-launcher/server/src/modules/web3/web3.module.ts b/packages/apps/hufi/job-launcher/server/src/modules/web3/web3.module.ts deleted file mode 100644 index 1cb936f905..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/web3/web3.module.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Logger, Module } from '@nestjs/common'; -import { Web3Service } from './Web3Service'; -import { ConfigModule } from '@nestjs/config'; -import { Web3Controller } from './web3.controller'; - -@Module({ - imports: [ConfigModule], - providers: [Web3Service], - exports: [Web3Service], - controllers: [Web3Controller], -}) -export class Web3Module {} diff --git a/packages/apps/hufi/job-launcher/server/src/modules/web3/web3.service.spec.ts b/packages/apps/hufi/job-launcher/server/src/modules/web3/web3.service.spec.ts deleted file mode 100644 index af1e57ef82..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/web3/web3.service.spec.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { ChainId } from '@human-protocol/sdk'; -import { ConfigService } from '@nestjs/config'; -import { Test } from '@nestjs/testing'; -import { MAINNET_CHAIN_IDS, TESTNET_CHAIN_IDS } from '../../common/constants'; -import { ErrorWeb3 } from '../../common/constants/errors'; -import { Web3Env } from '../../common/enums/web3'; -import { Web3Service } from './Web3Service'; - -describe('Web3Service', () => { - let mockConfigService: Partial; - let web3Service: Web3Service; - const privateKey = - '5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a'; - - beforeAll(async () => { - mockConfigService = { - get: jest.fn((key: string, defaultValue?: any) => { - switch (key) { - case 'WEB3_PRIVATE_KEY': - return privateKey; - case 'WEB3_ENV': - return 'testnet'; - default: - return defaultValue; - } - }), - }; - - const moduleRef = await Test.createTestingModule({ - providers: [ - Web3Service, - { - provide: ConfigService, - useValue: mockConfigService, - }, - ], - }).compile(); - - web3Service = moduleRef.get(Web3Service); - }); - - describe('getSigner', () => { - it('should return a signer for a valid chainId on TESTNET', () => { - const validChainId = ChainId.POLYGON_MUMBAI; - - const signer = web3Service.getSigner(validChainId); - expect(signer).toBeDefined(); - }); - - it('should throw invalid chain id provided for the testnet environment', () => { - const invalidChainId = ChainId.POLYGON; - - expect(() => web3Service.getSigner(invalidChainId)).toThrow( - ErrorWeb3.InvalidTestnetChainId, - ); - }); - }); - - describe('getValidChains', () => { - it('should get all valid chainIds on MAINNET', () => { - mockConfigService.get = jest.fn().mockReturnValue(Web3Env.MAINNET); - const validChainIds = web3Service.getValidChains(); - expect(validChainIds).toBe(MAINNET_CHAIN_IDS); - }); - - it('should get all valid chainIds on TESTNET', () => { - mockConfigService.get = jest.fn().mockReturnValue(Web3Env.TESTNET); - const validChainIds = web3Service.getValidChains(); - expect(validChainIds).toBe(TESTNET_CHAIN_IDS); - }); - }); -}); diff --git a/packages/apps/hufi/job-launcher/server/src/modules/web3/web3.service.ts b/packages/apps/hufi/job-launcher/server/src/modules/web3/web3.service.ts deleted file mode 100644 index c76962ec48..0000000000 --- a/packages/apps/hufi/job-launcher/server/src/modules/web3/web3.service.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { BadRequestException, Injectable, Logger } from '@nestjs/common'; -import { ConfigService } from '@nestjs/config'; -import { Wallet, ethers } from 'ethers'; -import { ConfigNames, networks } from '../../common/config'; -import { Web3Env } from '../../common/enums/web3'; -import { MAINNET_CHAIN_IDS, TESTNET_CHAIN_IDS } from '../../common/constants'; -import { ErrorWeb3 } from '../../common/constants/errors'; -import { ChainId } from '@human-protocol/sdk'; - -@Injectable() -export class Web3Service { - private signers: { [key: number]: Wallet } = {}; - public readonly logger = new Logger(Web3Service.name); - public readonly signerAddress: string; - - constructor(private readonly configService: ConfigService) { - const privateKey = this.configService.get(ConfigNames.WEB3_PRIVATE_KEY); - const validChains = this.getValidChains(); - const validNetworks = networks.filter((network) => - validChains.includes(network.chainId), - ); - for (const network of validNetworks) { - const provider = new ethers.JsonRpcProvider(network.rpcUrl); - this.signers[network.chainId] = new Wallet(privateKey, provider); - } - this.signerAddress = this.signers[validChains[0]].address; - } - - public getSigner(chainId: number): Wallet { - this.validateChainId(chainId); - return this.signers[chainId]; - } - - public validateChainId(chainId: number): void { - const currentWeb3Env = this.configService.get(ConfigNames.WEB3_ENV); - const validChainIds = this.getValidChains(); - - if (!validChainIds.includes(chainId)) { - const errorType = - currentWeb3Env === Web3Env.MAINNET - ? ErrorWeb3.InvalidMainnetChainId - : ErrorWeb3.InvalidTestnetChainId; - this.logger.log(errorType, Web3Service.name); - throw new BadRequestException(errorType); - } - } - - public getValidChains(): ChainId[] { - const currentWeb3Env = this.configService.get(ConfigNames.WEB3_ENV); - const validChainIds = - currentWeb3Env === Web3Env.MAINNET - ? MAINNET_CHAIN_IDS - : TESTNET_CHAIN_IDS; - - return validChainIds; - } -} diff --git a/packages/apps/hufi/job-launcher/server/test/app.e2e-spec.ts b/packages/apps/hufi/job-launcher/server/test/app.e2e-spec.ts deleted file mode 100644 index 50cda62332..0000000000 --- a/packages/apps/hufi/job-launcher/server/test/app.e2e-spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { INestApplication } from '@nestjs/common'; -import * as request from 'supertest'; -import { AppModule } from './../src/app.module'; - -describe('AppController (e2e)', () => { - let app: INestApplication; - - beforeEach(async () => { - const moduleFixture: TestingModule = await Test.createTestingModule({ - imports: [AppModule], - }).compile(); - - app = moduleFixture.createNestApplication(); - await app.init(); - }); - - it('/ (GET)', () => { - return request(app.getHttpServer()) - .get('/') - .expect(200) - .expect('Hello World!'); - }); -}); diff --git a/packages/apps/hufi/job-launcher/server/test/constants.ts b/packages/apps/hufi/job-launcher/server/test/constants.ts deleted file mode 100644 index 0cf8076942..0000000000 --- a/packages/apps/hufi/job-launcher/server/test/constants.ts +++ /dev/null @@ -1,51 +0,0 @@ -export const MOCK_REQUESTER_TITLE = 'Mock job title'; -export const MOCK_REQUESTER_DESCRIPTION = 'Mock job description'; -export const MOCK_SUBMISSION_REQUIRED = 5; -export const MOCK_CHAIN_ID = 1; -export const MOCK_ADDRESS = '0xCf88b3f1992458C2f5a229573c768D0E9F70C44e'; -export const MOCK_FILE_URL = 'http://mockedFileUrl.test/bucket/file.json'; -export const MOCK_FILE_HASH = 'mockedFileHash'; -export const MOCK_FILE_KEY = 'manifest.json'; -export const MOCK_BUCKET_FILES = [ - 'file0', - 'file1', - 'file2', - 'file3', - 'file4', - 'file5', -]; -export const MOCK_PRIVATE_KEY = - 'd334daf65a631f40549cc7de126d5a0016f32a2d00c49f94563f9737f7135e55'; -export const MOCK_BUCKET_NAME = 'bucket-name'; -export const MOCK_EXCHANGE_ORACLE_ADDRESS = - '0xCf88b3f1992458C2f5a229573c768D0E9F70C44e'; -export const MOCK_RECORDING_ORACLE_ADDRESS = - '0xCf88b3f1992458C2f5a229573c768D0E9F70C44e'; -export const MOCK_REPUTATION_ORACLE_ADDRESS = - '0x2E04d5D6cE3fF2261D0Cb04d41Fb4Cd67362A473'; -export const MOCK_EXCHANGE_ORACLE_WEBHOOK_URL = 'http://localhost:3000'; -export const MOCK_JOB_LAUNCHER_FEE = 5; -export const MOCK_EXCHANGE_ORACLE_FEE = 5; -export const MOCK_RECORDING_ORACLE_FEE = 5; -export const MOCK_REPUTATION_ORACLE_FEE = 5; -export const MOCK_TRANSACTION_HASH = - '0xd28e4c40571530afcb25ea1890e77b2d18c35f06049980ca4fb71829f64d89dc'; -export const MOCK_SIGNATURE = - '0x1502ec7e795ed9c96b215e80d46d87e26141bc8d41f69ee1bfbc8d8ed9a700db62136da8ee35eadbfd678817342444dff0239508be51c1fae55d62fcdba2867e1b'; -export const MOCK_EMAIL = 'test@example.com'; -export const MOCK_PASSWORD = 'password123'; -export const MOCK_HASHED_PASSWORD = 'hashedPassword'; -export const MOCK_CUSTOMER_ID = 'customer123'; -export const MOCK_PAYMENT_ID = 'payment123'; -export const MOCK_ACCESS_TOKEN = 'access_token'; -export const MOCK_REFRESH_TOKEN = 'refresh_token'; -export const MOCK_ACCESS_TOKEN_HASHED = 'access_token_hashed'; -export const MOCK_REFRESH_TOKEN_HASHED = 'refresh_token_hashed'; -export const MOCK_EXPIRES_IN = 1000000000000000; -export const MOCK_USER_ID = 1; -export const MOCK_JOB_ID = 1; - -export const MOCK_SENDGRID_API_KEY = - 'SG.xxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'; -export const MOCK_SENDGRID_FROM_EMAIL = 'info@hmt.ai'; -export const MOCK_SENDGRID_FROM_NAME = 'John Doe'; diff --git a/packages/apps/hufi/job-launcher/server/test/jest-e2e.json b/packages/apps/hufi/job-launcher/server/test/jest-e2e.json deleted file mode 100644 index e9d912f3e3..0000000000 --- a/packages/apps/hufi/job-launcher/server/test/jest-e2e.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "moduleFileExtensions": ["js", "json", "ts"], - "rootDir": ".", - "testEnvironment": "node", - "testRegex": ".e2e-spec.ts$", - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - } -} diff --git a/packages/apps/hufi/job-launcher/server/tsconfig.build.json b/packages/apps/hufi/job-launcher/server/tsconfig.build.json deleted file mode 100644 index 64f86c6bd2..0000000000 --- a/packages/apps/hufi/job-launcher/server/tsconfig.build.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": "./tsconfig.json", - "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] -} diff --git a/packages/apps/hufi/job-launcher/server/tsconfig.json b/packages/apps/hufi/job-launcher/server/tsconfig.json deleted file mode 100644 index fb6e941176..0000000000 --- a/packages/apps/hufi/job-launcher/server/tsconfig.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "declaration": true, - "removeComments": true, - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "allowJs": true, - "target": "ES2020", - "sourceMap": true, - "outDir": "./dist", - "baseUrl": "./", - "incremental": true, - "skipLibCheck": true, - "strictNullChecks": true, - "noImplicitAny": true, - "strictBindCallApply": true, - "forceConsistentCasingInFileNames": true, - "noFallthroughCasesInSwitch": true, - "esModuleInterop": true - } -} diff --git a/packages/apps/hufi/job-launcher/server/typeorm.config.ts b/packages/apps/hufi/job-launcher/server/typeorm.config.ts deleted file mode 100644 index c8f5021eb9..0000000000 --- a/packages/apps/hufi/job-launcher/server/typeorm.config.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { DataSource } from 'typeorm'; -import { SnakeNamingStrategy } from 'typeorm-naming-strategies'; -import * as dotenv from 'dotenv'; - -dotenv.config({ - path: process.env.NODE_ENV - ? `.env.${process.env.NODE_ENV as string}` - : '.env', -}); - -export default new DataSource({ - type: 'postgres', - host: process.env.POSTGRES_HOST, - port: Number(process.env.POSTGRES_PORT), - username: process.env.POSTGRES_USER, - password: process.env.POSTGRES_PASSWORD, - database: process.env.POSTGRES_DATABASE, - entities: ['dist/src/**/*.entity{ .ts,.js}'], - synchronize: false, - migrations: ['dist/src/database/migrations/*{.ts,.js}'], - migrationsTableName: 'migrations_typeorm', - migrationsRun: true, - namingStrategy: new SnakeNamingStrategy(), - ssl: process.env.POSTGRES_SSL?.toLowerCase() === 'true', -}); diff --git a/packages/apps/hufi/job-launcher/server/vercel.json b/packages/apps/hufi/job-launcher/server/vercel.json deleted file mode 100644 index 46f89bd0aa..0000000000 --- a/packages/apps/hufi/job-launcher/server/vercel.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "version": 2, - "builds": [ - { - "src": "src/main.ts", - "use": "@vercel/node" - } - ], - "routes": [ - { - "src": "/(.*)", - "dest": "src/main.ts", - "headers": { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Methods": "*", - "Access-Control-Allow-Headers": "X-Requested-With,Content-Type,Accept" - } - } - ], - "crons": [ - { - "path": "/job/cron/launch", - "schedule": "*/10 * * * *" - }, - { - "path": "/job/cron/cancel", - "schedule": "*/10 * * * *" - } - ], - "ignoreCommand": "git diff HEAD^ HEAD --quiet ." -} diff --git a/packages/apps/hufi/recording-oracle/.env.example b/packages/apps/hufi/recording-oracle/.env.example deleted file mode 100644 index 641c1767ca..0000000000 --- a/packages/apps/hufi/recording-oracle/.env.example +++ /dev/null @@ -1,18 +0,0 @@ -NODE_ENV=development -HOST=localhost -PORT=3002 -SESSION_SECRET=secret - -WEB3_PRIVATE_KEY='WEB3_PRIVATE_KEY' - -S3_ENDPOINT=localhost -S3_PORT=9000 -S3_ACCESS_KEY=access-key -S3_SECRET_KEY=secret-key -S3_BUCKET=liquidity -S3_USE_SSL=false - -UniswapEthereumEndpoint=UniswapEthereumEndpoint -UniswapPolygonEndpoint=UniswapPolygonEndpoint -pancakeSwapEndpoint=pancakeSwapEndpoint -BINANCE_URL= \ No newline at end of file diff --git a/packages/apps/hufi/recording-oracle/.eslintrc.js b/packages/apps/hufi/recording-oracle/.eslintrc.js deleted file mode 100644 index a2d2575861..0000000000 --- a/packages/apps/hufi/recording-oracle/.eslintrc.js +++ /dev/null @@ -1,26 +0,0 @@ -module.exports = { - parser: '@typescript-eslint/parser', - parserOptions: { - project: 'tsconfig.json', - tsconfigRootDir: __dirname, - sourceType: 'module', - }, - plugins: ['@typescript-eslint/eslint-plugin'], - extends: [ - 'plugin:@typescript-eslint/recommended', - 'plugin:prettier/recommended', - ], - root: true, - env: { - node: true, - jest: true, - }, - ignorePatterns: ['.eslintrc.js'], - rules: { - '@typescript-eslint/interface-name-prefix': 'off', - '@typescript-eslint/explicit-function-return-type': 'off', - '@typescript-eslint/explicit-module-boundary-types': 'off', - '@typescript-eslint/no-explicit-any': 'off', - "@typescript-eslint/no-empty-function": "off", - }, -}; diff --git a/packages/apps/hufi/recording-oracle/.gitignore b/packages/apps/hufi/recording-oracle/.gitignore deleted file mode 100644 index ed235e7700..0000000000 --- a/packages/apps/hufi/recording-oracle/.gitignore +++ /dev/null @@ -1,46 +0,0 @@ -# compiled output -dist -pgdata -/node_modules -.lintstagedrc - -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# Tests -/coverage -/.nyc_output - -# IDEs and editors -/.idea - -# IDE - VSCode -.vscode/* - -# OS -.DS_Store - -# env -.env.* -.env -!.env.example - -# VisualStudioCode - ../.vscode/ -.vscode -.vscode/* - -# yarn -.yarn/build-state.yml -.yarn/cache -yarn-debug.log* -yarn-error.log* -.yarn/install-state.gz -.yarn-integrity -# Yarn Integrity file -.yarn/unplugged - diff --git a/packages/apps/hufi/recording-oracle/.huskyrc b/packages/apps/hufi/recording-oracle/.huskyrc deleted file mode 100644 index 4d077c8292..0000000000 --- a/packages/apps/hufi/recording-oracle/.huskyrc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "hooks": { - "pre-commit": "lint-staged" - } -} diff --git a/packages/apps/hufi/recording-oracle/.prettierrc b/packages/apps/hufi/recording-oracle/.prettierrc deleted file mode 100644 index dcb72794f5..0000000000 --- a/packages/apps/hufi/recording-oracle/.prettierrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "singleQuote": true, - "trailingComma": "all" -} \ No newline at end of file diff --git a/packages/apps/hufi/recording-oracle/README.md b/packages/apps/hufi/recording-oracle/README.md deleted file mode 100644 index ed99b4289e..0000000000 --- a/packages/apps/hufi/recording-oracle/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# Recording Oracle Server for Fortune App - -The Fortune Recording Oracle application works directly with the results from Exchange Oracle, validating them and recording them to a file. - -## API - -- POST `/job/solve` - - Submits a solution from exchange oracle. - - Request Payload: - - ```json - { - "escrowAddress": string, - "chainId": number, - "exchangeAddress": string, - "workerAddress": string, - "solution": string - } - ``` - -## Development - -- Install dependencies with `yarn`. -- Run minio docker container. - ```bash - cd .. - docker compose up - ``` -- Run dev server with `yarn start:dev`. - -## Deployment diff --git a/packages/apps/hufi/recording-oracle/docker-compose.yml b/packages/apps/hufi/recording-oracle/docker-compose.yml deleted file mode 100644 index 329fcd6ca6..0000000000 --- a/packages/apps/hufi/recording-oracle/docker-compose.yml +++ /dev/null @@ -1,34 +0,0 @@ -version: '3.7' - -services: - minio: - container_name: minio - image: minio/minio:RELEASE.2022-05-26T05-48-41Z - ports: - - 9001:9001 - - 9000:9000 - environment: - MINIO_ROOT_USER: dev - MINIO_ROOT_PASSWORD: devdevdev - entrypoint: 'sh' - command: - -c "minio server /data --console-address ':9001'" - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"] - interval: 5s - timeout: 5s - retries: 3 - minio-mc: - container_name: minio-mc - image: minio/mc - depends_on: - minio: - condition: service_healthy - entrypoint: > - /bin/sh -c " - /usr/bin/mc config host add myminio http://minio:9000 dev devdevdev; - /usr/bin/mc mb myminio/launcher; - /usr/bin/mc anonymous set public myminio/launcher; - /usr/bin/mc mb myminio/solution; - /usr/bin/mc anonymous set public myminio/solution; - " diff --git a/packages/apps/hufi/recording-oracle/jest.config.js b/packages/apps/hufi/recording-oracle/jest.config.js deleted file mode 100644 index 446be44e6f..0000000000 --- a/packages/apps/hufi/recording-oracle/jest.config.js +++ /dev/null @@ -1,15 +0,0 @@ -module.exports = { - coverageDirectory: "../coverage", - moduleFileExtensions: ["js", "json", "ts"], - rootDir: "src", - testEnvironment: "node", - testRegex: ".spec.ts$", - transform: { - ".+\\.(t|j)s$": "ts-jest", - }, - moduleNameMapper: { - "^@/(.*)$": "/$1", - "^axios$": require.resolve("axios"), - "^uuid$": require.resolve("uuid"), - }, -}; diff --git a/packages/apps/hufi/recording-oracle/nest-cli.json b/packages/apps/hufi/recording-oracle/nest-cli.json deleted file mode 100644 index f9aa683b1a..0000000000 --- a/packages/apps/hufi/recording-oracle/nest-cli.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/nest-cli", - "collection": "@nestjs/schematics", - "sourceRoot": "src", - "compilerOptions": { - "deleteOutDir": true - } -} diff --git a/packages/apps/hufi/recording-oracle/package.json b/packages/apps/hufi/recording-oracle/package.json deleted file mode 100644 index 73a7edeebb..0000000000 --- a/packages/apps/hufi/recording-oracle/package.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "name": "hufi-recording-oracle-server", - "version": "1.0.0", - "description": "Fortune Recording Oracle", - "author": "Human Protocol", - "license": "MIT", - "scripts": { - "build": "nest build", - "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", - "start": "nest start", - "start:dev": "NODE_ENV=${NODE_ENV:=development} nest start --watch", - "start:debug": "nest start --debug --watch", - "start:prod": "node dist/main", - "lint": "eslint .", - "lint:fix": "eslint . --fix", - "test": "jest", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:e2e": "jest --config ./test/jest-e2e.json", - "postgres": "docker compose up -d postgres", - "docker": "docker compose up -d", - "local": "docker compose down && (concurrently --hide 0 \"yarn docker\" \"yarn recording-oracle:dev\" )", - "vercel-build": "yarn workspace @human-protocol/sdk build" - }, - "dependencies": { - "@human-protocol/sdk": "*", - "@nestjs/axios": "^2.0.0", - "@nestjs/common": "^10.2.7", - "@nestjs/config": "^3.1.1", - "@nestjs/core": "^10.2.8", - "@nestjs/platform-express": "^10.2.6", - "@nestjs/serve-static": "^4.0.0", - "@nestjs/swagger": "^7.1.13", - "body-parser": "^1.20.2", - "class-validator": "^0.14.0", - "cookie-parser": "^1.4.6", - "graphql-request": "^6.1.0", - "helmet": "^7.1.0", - "reflect-metadata": "^0.1.13" - }, - "devDependencies": { - "@nestjs/cli": "^9.4.3", - "@nestjs/schematics": "^9.2.0", - "@nestjs/testing": "^9.4.3", - "@types/express": "^4.17.13" - } -} diff --git a/packages/apps/hufi/recording-oracle/src/app.controller.spec.ts b/packages/apps/hufi/recording-oracle/src/app.controller.spec.ts deleted file mode 100644 index 741a42c324..0000000000 --- a/packages/apps/hufi/recording-oracle/src/app.controller.spec.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { AppController } from './app.controller'; - -describe('AppController', () => { - let appController: AppController; - - beforeEach(() => { - appController = new AppController(); - }); - - describe('Health Check', () => { - it('should return OK', async () => { - expect(await appController.health()).toBe('OK'); - }); - }); -}); diff --git a/packages/apps/hufi/recording-oracle/src/app.controller.ts b/packages/apps/hufi/recording-oracle/src/app.controller.ts deleted file mode 100644 index f126104213..0000000000 --- a/packages/apps/hufi/recording-oracle/src/app.controller.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Controller, Get, Redirect } from '@nestjs/common'; - -import { Public } from './common/decorators'; - -@Controller('/') -export class AppController { - @Public() - @Get('/') - @Redirect('/swagger', 301) - public health(): string { - return 'OK'; - } -} diff --git a/packages/apps/hufi/recording-oracle/src/app.module.ts b/packages/apps/hufi/recording-oracle/src/app.module.ts deleted file mode 100644 index bb23c650ae..0000000000 --- a/packages/apps/hufi/recording-oracle/src/app.module.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Module } from '@nestjs/common'; -import { ConfigModule } from '@nestjs/config'; -import { APP_PIPE } from '@nestjs/core'; -import { HttpValidationPipe } from './common/pipes'; -import { LiquidityModule } from './modules/liquidity/liquidity.module'; - -import { AppController } from './app.controller'; -import { - envValidator, - s3Config, - serverConfig, - web3Config, -} from './common/config'; -import { ServeStaticModule } from '@nestjs/serve-static'; -import { join } from 'path'; - -@Module({ - providers: [ - { - provide: APP_PIPE, - useClass: HttpValidationPipe, - }, - ], - imports: [ - ConfigModule.forRoot({ - envFilePath: process.env.NODE_ENV - ? `.env.${process.env.NODE_ENV as string}` - : '.env', - validationSchema: envValidator, - load: [serverConfig, s3Config, web3Config], - }), - LiquidityModule, - ServeStaticModule.forRoot({ - rootPath: join( - __dirname, - '../../../../../', - 'node_modules/swagger-ui-dist', - ), - }), - ], - controllers: [AppController], -}) -export class AppModule {} diff --git a/packages/apps/hufi/recording-oracle/src/common/config/index.ts b/packages/apps/hufi/recording-oracle/src/common/config/index.ts deleted file mode 100644 index b23c0bb036..0000000000 --- a/packages/apps/hufi/recording-oracle/src/common/config/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './web3'; -export * from './server'; -export * from './s3'; -export * from './validation'; diff --git a/packages/apps/hufi/recording-oracle/src/common/config/s3.ts b/packages/apps/hufi/recording-oracle/src/common/config/s3.ts deleted file mode 100644 index b2d4e3731b..0000000000 --- a/packages/apps/hufi/recording-oracle/src/common/config/s3.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { ConfigType, registerAs } from '@nestjs/config'; - -export const s3Config = registerAs('s3', () => ({ - endPoint: process.env.S3_ENDPOINT!, - port: +process.env.S3_PORT!, - accessKey: process.env.S3_ACCESS_KEY!, - secretKey: process.env.S3_SECRET_KEY!, - bucket: process.env.S3_BUCKET!, - useSSL: process.env.S3_USE_SSL === 'true', -})); - -export const s3ConfigKey = s3Config.KEY; -export type S3ConfigType = ConfigType; diff --git a/packages/apps/hufi/recording-oracle/src/common/config/server.ts b/packages/apps/hufi/recording-oracle/src/common/config/server.ts deleted file mode 100644 index 6124b1ea03..0000000000 --- a/packages/apps/hufi/recording-oracle/src/common/config/server.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { ConfigType, registerAs } from '@nestjs/config'; - -export const serverConfig = registerAs('server', () => ({ - host: process.env.HOST!, - port: +process.env.PORT!, - sessionSecret: process.env.SESSION_SECRET!, -})); -export const serverConfigKey = serverConfig.KEY; -export type ServerConfigType = ConfigType; diff --git a/packages/apps/hufi/recording-oracle/src/common/config/validation.ts b/packages/apps/hufi/recording-oracle/src/common/config/validation.ts deleted file mode 100644 index db273acf9c..0000000000 --- a/packages/apps/hufi/recording-oracle/src/common/config/validation.ts +++ /dev/null @@ -1,40 +0,0 @@ -import * as Joi from 'joi'; - -export const ConfigNames = { - NODE_ENV: 'NODE_ENV', - HOST: 'HOST', - PORT: 'PORT', - SESSION_SECRET: 'SESSION_SECRET', - WEB3_PRIVATE_KEY: 'WEB3_PRIVATE_KEY', - S3_ENDPOINT: 'S3_ENDPOINT', - S3_PORT: 'S3_PORT', - S3_ACCESS_KEY: 'S3_ACCESS_KEY', - S3_SECRET_KEY: 'S3_SECRET_KEY', - S3_BUCKET: 'S3_BUCKET', - S3_USE_SSL: 'S3_USE_SSL', - UniswapEthereumEndpoint:'UniswapEthereumEndpoint', - UniswapPolygonEndpoint: 'UniswapPolygonEndpoint', - pancakeSwapEndpoint: 'pancakeSwapEndpoint' , - BINANCE_URL: 'BINANCE_URL', -}; - -export const envValidator = Joi.object({ - // General - NODE_ENV: Joi.string().default('development'), - HOST: Joi.string().default('localhost'), - PORT: Joi.string().default(5000), - SESSION_SECRET: Joi.string().default('session_key'), - // Web3 - WEB3_PRIVATE_KEY: Joi.string().required(), - // S3 - S3_ENDPOINT: Joi.string().default('127.0.0.1'), - S3_PORT: Joi.string().default(9000), - S3_ACCESS_KEY: Joi.string().required(), - S3_SECRET_KEY: Joi.string().required(), - S3_BUCKET: Joi.string().default('solution'), - S3_USE_SSL: Joi.string().default(false), - UniswapEthereumEndpoint:Joi.string().default('https://api.thegraph.com/subgraphs/name/messari/uniswap-v3-ethereum'), - UniswapPolygonEndpoint: Joi.string().default('https://api.thegraph.com/subgraphs/name/messari/uniswap-v3-polygon'), - pancakeSwapEndpoint:Joi.string().default('https://api.thegraph.com/subgraphs/name/messari/pancakeswap-v3-bsc'), - BINANCE_URL: Joi.string().default('https://testnet.binance.vision/api'), -}); diff --git a/packages/apps/hufi/recording-oracle/src/common/config/web3.ts b/packages/apps/hufi/recording-oracle/src/common/config/web3.ts deleted file mode 100644 index a8c2e8f4cd..0000000000 --- a/packages/apps/hufi/recording-oracle/src/common/config/web3.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { ConfigType, registerAs } from '@nestjs/config'; - -export const web3Config = registerAs('web3', () => ({ - web3PrivateKey: process.env.WEB3_PRIVATE_KEY!, -})); - -export const web3ConfigKey = web3Config.KEY; -export type Web3ConfigType = ConfigType; diff --git a/packages/apps/hufi/recording-oracle/src/common/constants/errors.ts b/packages/apps/hufi/recording-oracle/src/common/constants/errors.ts deleted file mode 100644 index 6a10395650..0000000000 --- a/packages/apps/hufi/recording-oracle/src/common/constants/errors.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Represents error messages associated with a job. - */ -export enum ErrorJob { - AddressMismatches = 'Escrow Recording Oracle address mismatches the current one', - InvalidStatus = 'Escrow is not in the Pending status', - InvalidManifest = 'Manifest does not contain the required data', - InvalidJobType = 'Manifest contains an invalid job type', - NotFoundIntermediateResultsUrl = 'Error while getting intermediate results url from escrow contract', - NotFoundIntermediateResults = 'Error while getting intermediate results file', - WebhookWasNotSent = 'Webhook was not sent', - ManifestNotFound = 'Manifest not found', - InvalidExchange = 'Invalid exchange', -} - -/** - * Represents error messages related to bucket. - */ -export enum ErrorBucket { - NotPublic = 'Bucket is not public', - UnableSaveFile = 'Unable to save file', -} diff --git a/packages/apps/hufi/recording-oracle/src/common/constants/exchange.ts b/packages/apps/hufi/recording-oracle/src/common/constants/exchange.ts deleted file mode 100644 index 6cbcacd676..0000000000 --- a/packages/apps/hufi/recording-oracle/src/common/constants/exchange.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Exchange } from "../enums/exchange"; - -export const DEX: Exchange[] = [ - Exchange.UNISWAP_ETHEREUM, - Exchange.UNISWAP_POLYGON, - Exchange.PANCAKESWAP_BSC, -]; - -export const CEX: Exchange[] = [ - Exchange.BINANCE -] \ No newline at end of file diff --git a/packages/apps/hufi/recording-oracle/src/common/constants/index.ts b/packages/apps/hufi/recording-oracle/src/common/constants/index.ts deleted file mode 100644 index 3d274450ba..0000000000 --- a/packages/apps/hufi/recording-oracle/src/common/constants/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export const EXCHANGE_INVALID_ENDPOINT = '/invalid-solution'; -export const HEADER_SIGNATURE_KEY = 'human-signature'; diff --git a/packages/apps/hufi/recording-oracle/src/common/constants/networks.ts b/packages/apps/hufi/recording-oracle/src/common/constants/networks.ts deleted file mode 100644 index 5f65991c54..0000000000 --- a/packages/apps/hufi/recording-oracle/src/common/constants/networks.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { NetworkMapDto } from '../interfaces/network'; - -export const networkMap: NetworkMapDto = { - polygon: { - chainId: 137, - rpcUrl: - 'https://polygon-mainnet.g.alchemy.com/v2/0Lorh5KRkGl5FsRwy2epTg8fEFFoqUfY', - }, - bsc: { - chainId: 56, - rpcUrl: 'https://bsc-dataseed1.binance.org/', - }, - mumbai: { - chainId: 80001, - rpcUrl: - 'https://polygon-mumbai.g.alchemy.com/v2/vKNSJzJf6SW2sdW-05bgFwoyFxUrMzii', - }, - goerli: { - chainId: 5, - rpcUrl: 'https://goerli.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161', - }, - moonbeam: { - chainId: 1284, - rpcUrl: 'https://rpc.api.moonbeam.network', - }, - bsctest: { - chainId: 97, - rpcUrl: 'https://data-seed-prebsc-1-s1.binance.org:8545/', - }, -}; - -export const networks = Object.values(networkMap).map((network) => network); diff --git a/packages/apps/hufi/recording-oracle/src/common/decorators/index.ts b/packages/apps/hufi/recording-oracle/src/common/decorators/index.ts deleted file mode 100644 index b7e8b7187d..0000000000 --- a/packages/apps/hufi/recording-oracle/src/common/decorators/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './public'; diff --git a/packages/apps/hufi/recording-oracle/src/common/decorators/public.ts b/packages/apps/hufi/recording-oracle/src/common/decorators/public.ts deleted file mode 100644 index 6b47ae5d44..0000000000 --- a/packages/apps/hufi/recording-oracle/src/common/decorators/public.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { SetMetadata } from '@nestjs/common'; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export const Public = (): ((target: any, key?: any, descriptor?: any) => any) => - SetMetadata('isPublic', true); diff --git a/packages/apps/hufi/recording-oracle/src/common/enums/exchange.ts b/packages/apps/hufi/recording-oracle/src/common/enums/exchange.ts deleted file mode 100644 index 2c39dc586c..0000000000 --- a/packages/apps/hufi/recording-oracle/src/common/enums/exchange.ts +++ /dev/null @@ -1,6 +0,0 @@ -export enum Exchange { - UNISWAP_ETHEREUM = 'unispwap-ethereum', - UNISWAP_POLYGON = 'uniswap-polygon', - PANCAKESWAP_BSC = 'pancakeswap-bsc', - BINANCE = 'binance' - } \ No newline at end of file diff --git a/packages/apps/hufi/recording-oracle/src/common/enums/job.ts b/packages/apps/hufi/recording-oracle/src/common/enums/job.ts deleted file mode 100644 index 34d4e08f7e..0000000000 --- a/packages/apps/hufi/recording-oracle/src/common/enums/job.ts +++ /dev/null @@ -1,3 +0,0 @@ -export enum JobRequestType { - Campaign = 'CAMPAIGN', -} diff --git a/packages/apps/hufi/recording-oracle/src/common/enums/role.ts b/packages/apps/hufi/recording-oracle/src/common/enums/role.ts deleted file mode 100644 index b998ec7fe7..0000000000 --- a/packages/apps/hufi/recording-oracle/src/common/enums/role.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum Role { - JobLaucher = 'job_launcher', - Exchange = 'exchange', - Reputation = 'reputation', -} diff --git a/packages/apps/hufi/recording-oracle/src/common/filter/global-exceptions.filter.ts b/packages/apps/hufi/recording-oracle/src/common/filter/global-exceptions.filter.ts deleted file mode 100644 index 2dd4c86a8c..0000000000 --- a/packages/apps/hufi/recording-oracle/src/common/filter/global-exceptions.filter.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { - ArgumentsHost, - Catch, - ExceptionFilter, - HttpException, - HttpStatus, -} from '@nestjs/common'; - -@Catch() -export class GlobalExceptionsFilter implements ExceptionFilter { - catch(exception: unknown, host: ArgumentsHost) { - const ctx = host.switchToHttp(); - const response = ctx.getResponse(); - - const status = - exception instanceof HttpException - ? exception.getStatus() - : HttpStatus.INTERNAL_SERVER_ERROR; - - response.status(status).json({ - statusCode: status, - message: - exception instanceof HttpException - ? exception.getResponse() - : exception instanceof Error - ? exception.message - : 'Internal Server Error', - }); - } -} diff --git a/packages/apps/hufi/recording-oracle/src/common/filter/index.ts b/packages/apps/hufi/recording-oracle/src/common/filter/index.ts deleted file mode 100644 index fa1fe5651b..0000000000 --- a/packages/apps/hufi/recording-oracle/src/common/filter/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './global-exceptions.filter'; diff --git a/packages/apps/hufi/recording-oracle/src/common/guards/index.ts b/packages/apps/hufi/recording-oracle/src/common/guards/index.ts deleted file mode 100644 index cd53cfcc27..0000000000 --- a/packages/apps/hufi/recording-oracle/src/common/guards/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './signature.auth'; diff --git a/packages/apps/hufi/recording-oracle/src/common/guards/signature.auth.spec.ts b/packages/apps/hufi/recording-oracle/src/common/guards/signature.auth.spec.ts deleted file mode 100644 index ed92d3aeee..0000000000 --- a/packages/apps/hufi/recording-oracle/src/common/guards/signature.auth.spec.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { ExecutionContext, UnauthorizedException } from '@nestjs/common'; -import { SignatureAuthGuard } from './signature.auth'; -import { verifySignature } from '../utils/signature'; -import { ChainId, EscrowUtils } from '@human-protocol/sdk'; -import { MOCK_ADDRESS } from '../../../test/constants'; -import { Role } from '../enums/role'; - -jest.mock('../../common/utils/signature'); - -jest.mock('@human-protocol/sdk', () => ({ - ...jest.requireActual('@human-protocol/sdk'), - EscrowUtils: { - getEscrow: jest.fn().mockResolvedValue({ - exchangeOracle: '0x1234567890123456789012345678901234567891', - reputationOracle: '0x1234567890123456789012345678901234567892', - }), - }, -})); - -describe('SignatureAuthGuard', () => { - let guard: SignatureAuthGuard; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - { - provide: SignatureAuthGuard, - useValue: new SignatureAuthGuard([ - Role.JobLaucher, - Role.Exchange, - Role.Reputation, - ]), - }, - ], - }).compile(); - - guard = module.get(SignatureAuthGuard); - }); - - it('should be defined', () => { - expect(guard).toBeDefined(); - }); - - describe('canActivate', () => { - let context: ExecutionContext; - let mockRequest: any; - - beforeEach(() => { - mockRequest = { - switchToHttp: jest.fn().mockReturnThis(), - getRequest: jest.fn().mockReturnThis(), - headers: {}, - body: {}, - originalUrl: '', - }; - context = { - switchToHttp: jest.fn().mockReturnThis(), - getRequest: jest.fn(() => mockRequest), - } as any as ExecutionContext; - }); - - it('should return true if signature is verified', async () => { - mockRequest.headers['header-signature-key'] = 'validSignature'; - mockRequest.body = { - escrowAddress: MOCK_ADDRESS, - chainId: ChainId.LOCALHOST, - }; - (verifySignature as jest.Mock).mockReturnValue(true); - - const result = await guard.canActivate(context as any); - expect(result).toBeTruthy(); - expect(EscrowUtils.getEscrow).toHaveBeenCalledWith( - ChainId.LOCALHOST, - MOCK_ADDRESS, - ); - }); - - it('should throw unauthorized exception if signature is not verified', async () => { - (verifySignature as jest.Mock).mockReturnValue(false); - - await expect(guard.canActivate(context as any)).rejects.toThrow( - UnauthorizedException, - ); - }); - - it('should throw unauthorized exception for unrecognized oracle type', async () => { - mockRequest.originalUrl = '/some/random/path'; - await expect(guard.canActivate(context as any)).rejects.toThrow( - UnauthorizedException, - ); - }); - }); -}); diff --git a/packages/apps/hufi/recording-oracle/src/common/guards/signature.auth.ts b/packages/apps/hufi/recording-oracle/src/common/guards/signature.auth.ts deleted file mode 100644 index c61185a279..0000000000 --- a/packages/apps/hufi/recording-oracle/src/common/guards/signature.auth.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { - CanActivate, - ExecutionContext, - Injectable, - UnauthorizedException, -} from '@nestjs/common'; -import { verifySignature } from '../utils/signature'; -import { HEADER_SIGNATURE_KEY } from '../constants'; -import { EscrowUtils } from '@human-protocol/sdk'; -import { Role } from '../enums/role'; - -@Injectable() -export class SignatureAuthGuard implements CanActivate { - constructor(private role: Role[]) {} - - public async canActivate(context: ExecutionContext): Promise { - const request = context.switchToHttp().getRequest(); - - const data = request.body; - const signature = request.headers[HEADER_SIGNATURE_KEY]; - const oracleAdresses: string[] = []; - try { - const escrowData = await EscrowUtils.getEscrow( - data.chainId, - data.escrowAddress, - ); - if (this.role.includes(Role.JobLaucher)) - oracleAdresses.push(escrowData.launcher!); - if (this.role.includes(Role.Exchange)) - oracleAdresses.push(escrowData.exchangeOracle!); - if (this.role.includes(Role.Reputation)) - oracleAdresses.push(escrowData.reputationOracle!); - - const isVerified = verifySignature(data, signature, oracleAdresses); - - if (isVerified) { - return true; - } - } catch (error) { - console.error(error); - } - - throw new UnauthorizedException('Unauthorized'); - } -} diff --git a/packages/apps/hufi/recording-oracle/src/common/interfaces/liquidity.ts b/packages/apps/hufi/recording-oracle/src/common/interfaces/liquidity.ts deleted file mode 100644 index e501a7774a..0000000000 --- a/packages/apps/hufi/recording-oracle/src/common/interfaces/liquidity.ts +++ /dev/null @@ -1,7 +0,0 @@ -export interface ILiquidityScore { - exchangeAddress: string, - escrowAddress: string, - chainId: string, - liquidityProvider:string, - liquidityScore: string, - } \ No newline at end of file diff --git a/packages/apps/hufi/recording-oracle/src/common/interfaces/network.ts b/packages/apps/hufi/recording-oracle/src/common/interfaces/network.ts deleted file mode 100644 index 898fc79100..0000000000 --- a/packages/apps/hufi/recording-oracle/src/common/interfaces/network.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface NetworkMapDto { - [key: string]: { - chainId: number; - rpcUrl: string; - }; -} diff --git a/packages/apps/hufi/recording-oracle/src/common/pipes/index.ts b/packages/apps/hufi/recording-oracle/src/common/pipes/index.ts deleted file mode 100644 index 4d5ffa36ab..0000000000 --- a/packages/apps/hufi/recording-oracle/src/common/pipes/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './validation'; diff --git a/packages/apps/hufi/recording-oracle/src/common/pipes/validation.ts b/packages/apps/hufi/recording-oracle/src/common/pipes/validation.ts deleted file mode 100644 index f46e742df2..0000000000 --- a/packages/apps/hufi/recording-oracle/src/common/pipes/validation.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { - BadRequestException, - Injectable, - ValidationError, - ValidationPipe, - ValidationPipeOptions, -} from '@nestjs/common'; - -@Injectable() -export class HttpValidationPipe extends ValidationPipe { - constructor(options?: ValidationPipeOptions) { - super({ - exceptionFactory: (errors: ValidationError[]): BadRequestException => - new BadRequestException(errors), - transform: true, - whitelist: true, - forbidNonWhitelisted: true, - forbidUnknownValues: true, - ...options, - }); - } -} diff --git a/packages/apps/hufi/recording-oracle/src/common/utils/signature.spec.ts b/packages/apps/hufi/recording-oracle/src/common/utils/signature.spec.ts deleted file mode 100644 index 1af5509c3e..0000000000 --- a/packages/apps/hufi/recording-oracle/src/common/utils/signature.spec.ts +++ /dev/null @@ -1,115 +0,0 @@ -import { verifySignature, recoverSigner, signMessage } from './signature'; -import { MOCK_ADDRESS, MOCK_WEB3_PRIVATE_KEY } from '../../../test/constants'; - -jest.doMock('ethers', () => { - return { - utils: { - get verifyMessage() { - return jest.fn((message, signature) => { - if (message === 'valid-message' && signature === 'valid-signature') { - return 'recovered-address'; - } else { - throw new Error('Invalid signature'); - } - }); - }, - }, - }; -}); - -describe('Signature utility', () => { - describe('verifySignature', () => { - it('should return true for valid signature', async () => { - const message = 'Hello, this is a signed message!'; - const signature = await signMessage(message, MOCK_WEB3_PRIVATE_KEY); - - const result = verifySignature(message, signature, [MOCK_ADDRESS]); - - expect(result).toBe(true); - }); - - it('should throw conflict exception for signature not verified', async () => { - const message = 'Hello, this is a signed message!'; - - const invalidSignature = await signMessage( - message, - MOCK_WEB3_PRIVATE_KEY, - ); - const invalidAddress = '0x1234567890123456789012345678901234567892'; - - expect(() => { - verifySignature(message, invalidSignature, [invalidAddress]); - }).toThrow('Signature not verified'); - }); - - it('should throw conflict exception for invalid signature', () => { - const message = 'Hello, this is a signed message!'; - const invalidSignature = '0xInvalidSignature'; - - expect(() => { - verifySignature(message, invalidSignature, [MOCK_ADDRESS]); - }).toThrow('Invalid signature'); - }); - }); - - describe('recoverSigner', () => { - it('should recover the correct signer', async () => { - const message = 'value'; - const signature = await signMessage(message, MOCK_WEB3_PRIVATE_KEY); - - const result = recoverSigner(message, signature); - - expect(result).toBe(MOCK_ADDRESS); - }); - - it('should throw conflict exception for invalid signature', () => { - const message = 'Hello, this is a signed message!'; - const invalidSignature = '0xInvalidSignature'; - - expect(() => { - recoverSigner(message, invalidSignature); - }).toThrow('Invalid signature'); - }); - - it('should stringify message object if it is not already a string', async () => { - const message = { key: 'value' }; - const signature = await signMessage(message, MOCK_WEB3_PRIVATE_KEY); - - const recoveredAddress = recoverSigner(message, signature); - - expect(recoveredAddress).toBe(MOCK_ADDRESS); - }); - - it('should not stringify message if it is already a string', async () => { - const message = 'valid message'; - const signature = await signMessage(message, MOCK_WEB3_PRIVATE_KEY); - - const recoveredAddress = recoverSigner(message, signature); - - expect(recoveredAddress).toBe(MOCK_ADDRESS); - }); - }); - - describe('signMessage', () => { - it('should return a valid signature', async () => { - const message = 'Hello, this is a test message'; - const signature = await signMessage(message, MOCK_WEB3_PRIVATE_KEY); - - expect(signature).toBeDefined(); - }); - - it('should stringify message object if it is not already a string', async () => { - const message = { key: 'value' }; - const signature = await signMessage(message, MOCK_WEB3_PRIVATE_KEY); - - expect(signature).toBeDefined(); - }); - - it('should not stringify message if it is already a string', async () => { - const message = 'valid message'; - const signature = await signMessage(message, MOCK_WEB3_PRIVATE_KEY); - - expect(signature).toBeDefined(); - }); - }); -}); diff --git a/packages/apps/hufi/recording-oracle/src/common/utils/signature.ts b/packages/apps/hufi/recording-oracle/src/common/utils/signature.ts deleted file mode 100644 index 20cc25b726..0000000000 --- a/packages/apps/hufi/recording-oracle/src/common/utils/signature.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { ConflictException } from '@nestjs/common'; -import { ethers } from 'ethers'; - -export function verifySignature( - message: object | string, - signature: string, - addresses: string[], -): boolean { - const signer = recoverSigner(message, signature); - - if ( - !addresses.some((address) => address.toLowerCase() === signer.toLowerCase()) - ) { - throw new ConflictException('Signature not verified'); - } - - return true; -} - -export async function signMessage( - message: object | string, - privateKey: string, -): Promise { - if (typeof message !== 'string') { - message = JSON.stringify(message); - } - - const wallet = new ethers.Wallet(privateKey); - const signature = await wallet.signMessage(message); - - return signature; -} - -export function recoverSigner( - message: object | string, - signature: string, -): string { - if (typeof message !== 'string') { - message = JSON.stringify(message); - } - - try { - return ethers.verifyMessage(message, signature); - } catch (e) { - throw new ConflictException('Invalid signature'); - } -} diff --git a/packages/apps/hufi/recording-oracle/src/common/utils/webhook.ts b/packages/apps/hufi/recording-oracle/src/common/utils/webhook.ts deleted file mode 100644 index 0a6fbda8ad..0000000000 --- a/packages/apps/hufi/recording-oracle/src/common/utils/webhook.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Logger, NotFoundException } from '@nestjs/common'; -import { firstValueFrom } from 'rxjs'; -import { HttpService } from '@nestjs/axios'; -import { ErrorJob } from '../constants/errors'; -import { signMessage } from './signature'; -import { HEADER_SIGNATURE_KEY } from '../constants'; - -export async function sendWebhook( - httpService: HttpService, - logger: Logger, - webhookUrl: string, - webhookData: any, - privateKey: string, -): Promise { - const signedBody = await signMessage(webhookData, privateKey); - const { data } = await firstValueFrom( - await httpService.post(webhookUrl, webhookData, { - headers: { [HEADER_SIGNATURE_KEY]: signedBody }, - }), - ); - - if (!data) { - logger.log(ErrorJob.WebhookWasNotSent, 'JobService'); - throw new NotFoundException(ErrorJob.WebhookWasNotSent); - } - - return true; -} diff --git a/packages/apps/hufi/recording-oracle/src/common/validators/ethers.spec.ts b/packages/apps/hufi/recording-oracle/src/common/validators/ethers.spec.ts deleted file mode 100644 index 3fb1762b40..0000000000 --- a/packages/apps/hufi/recording-oracle/src/common/validators/ethers.spec.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Validator } from 'class-validator'; -import { IsValidEthereumAddress } from './ethers'; - -const validator = new Validator(); - -describe('IsValidEthereumAddress', () => { - class MyClass { - @IsValidEthereumAddress() - someString: string; - } - - it('should be valid', () => { - const obj = new MyClass(); - obj.someString = '0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc'; - return validator.validate(obj).then((errors) => { - expect(errors.length).toBe(0); - }); - }); - - it('should be invalid', () => { - const obj = new MyClass(); - obj.someString = '0x9965507D1a55bcC2695C58ba16FB37d819B0A4dd'; - return validator.validate(obj).then((errors) => { - expect(errors.length).toBe(1); - }); - }); -}); diff --git a/packages/apps/hufi/recording-oracle/src/common/validators/ethers.ts b/packages/apps/hufi/recording-oracle/src/common/validators/ethers.ts deleted file mode 100644 index 55bc380197..0000000000 --- a/packages/apps/hufi/recording-oracle/src/common/validators/ethers.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { - registerDecorator, - ValidationOptions, - ValidatorConstraint, - ValidatorConstraintInterface, -} from 'class-validator'; -import { ethers } from 'ethers'; - -@ValidatorConstraint({ name: 'IsValidEthereumAddress' }) -@Injectable() -class ValidateEthereumAddress implements ValidatorConstraintInterface { - public validate(value: string): boolean { - return ethers.isAddress(value); - } - - public defaultMessage(): string { - return 'Invalid Ethereum address'; - } -} - -export function IsValidEthereumAddress(validationOptions?: ValidationOptions) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return (object: any, propertyName: string): void => { - registerDecorator({ - name: 'IsValidEthereumAddress', - target: object.constructor, - propertyName, - options: validationOptions, - validator: ValidateEthereumAddress, - }); - }; -} diff --git a/packages/apps/hufi/recording-oracle/src/common/validators/index.ts b/packages/apps/hufi/recording-oracle/src/common/validators/index.ts deleted file mode 100644 index 01dda0ca1a..0000000000 --- a/packages/apps/hufi/recording-oracle/src/common/validators/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './ethers'; diff --git a/packages/apps/hufi/recording-oracle/src/main.ts b/packages/apps/hufi/recording-oracle/src/main.ts deleted file mode 100644 index ebd3fed0b0..0000000000 --- a/packages/apps/hufi/recording-oracle/src/main.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { NestFactory } from '@nestjs/core'; -import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; -import { json, urlencoded } from 'body-parser'; -import { useContainer } from 'class-validator'; -import cookieParser from 'cookie-parser'; -import session from 'express-session'; -import helmet from 'helmet'; - -import { INestApplication } from '@nestjs/common'; -import { AppModule } from './app.module'; -import { ServerConfigType, serverConfigKey } from './common/config'; -import { GlobalExceptionsFilter } from './common/filter'; - -async function bootstrap() { - const app = await NestFactory.create(AppModule, { - cors: true, - }); - - const { sessionSecret, host, port }: ServerConfigType = - app.get(serverConfigKey); - - app.useGlobalFilters(new GlobalExceptionsFilter()); - - app.enableCors({ - credentials: true, - exposedHeaders: ['Content-Disposition'], - }); - - useContainer(app.select(AppModule), { fallbackOnErrors: true }); - - app.use(cookieParser()); - - app.use( - session({ - secret: sessionSecret, - resave: false, - saveUninitialized: false, - cookie: { - secure: true, - }, - }), - ); - app.use(json({ limit: '5mb' })); - app.use(urlencoded({ limit: '5mb', extended: true })); - - const config = new DocumentBuilder() - .addBearerAuth() - .setTitle('Fortune Recording Oracle API') - .setDescription('Swagger Fortune Recording Oracle API') - .setVersion('1.0') - .build(); - const document = SwaggerModule.createDocument(app, config); - SwaggerModule.setup('swagger', app, document); - - app.use(helmet()); - - await app.listen(port, host, async () => { - console.info(`API server is running on http://${host}:${port}`); - }); -} - -void bootstrap(); diff --git a/packages/apps/hufi/recording-oracle/src/modules/liquidity/liquidity.controller.ts b/packages/apps/hufi/recording-oracle/src/modules/liquidity/liquidity.controller.ts deleted file mode 100644 index 64f0855f00..0000000000 --- a/packages/apps/hufi/recording-oracle/src/modules/liquidity/liquidity.controller.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Body, Controller, Post, UseGuards } from '@nestjs/common'; -import { ApiTags } from '@nestjs/swagger'; - -import { liquidityRequestDto } from './liquidity.dto'; -import { LiquidityService } from './liquidity.service'; -// import { SignatureAuthGuard } from '../../common/guards'; -// import { Role } from '../../common/enums/role'; -import { Public } from '../../common/decorators'; - -@Controller('/liquidity') -@ApiTags('liquidity') -export class LiquidityController { - constructor(private readonly liquidityService: LiquidityService) {} - - // @UseGuards(new SignatureAuthGuard([Role.Exchange])) - @Post() - getLiquidity(@Body() body: liquidityRequestDto): Promise{ - return this.liquidityService.getLiquidityScore( - body - ) - } -} diff --git a/packages/apps/hufi/recording-oracle/src/modules/liquidity/liquidity.dto.ts b/packages/apps/hufi/recording-oracle/src/modules/liquidity/liquidity.dto.ts deleted file mode 100644 index 07ac040b9d..0000000000 --- a/packages/apps/hufi/recording-oracle/src/modules/liquidity/liquidity.dto.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { ChainId } from '@human-protocol/sdk'; -import { IsBoolean, IsEnum, IsNumber, IsPositive, IsString } from 'class-validator'; - -import { IsValidEthereumAddress } from '../../common/validators'; -import { JobRequestType } from '../../common/enums/job'; -import { Exchange } from '../../common/enums/exchange'; - -export class liquidityRequestDto { - @ApiProperty() - @IsString() - @IsValidEthereumAddress() - public escrowAddress: string; - - @ApiProperty({ - enum: ChainId, - }) - @IsEnum(ChainId) - public chainId: ChainId; - - @ApiProperty() - @IsString() - @IsValidEthereumAddress() - public liquidityProvider: string; - - @ApiPropertyOptional() - @IsString() - public liquidityProviderAPIKEY?: string ; - - @ApiPropertyOptional() - @IsString() - public liquidityProviderAPISecret?: string; - - @ApiProperty() - @IsBoolean() - public save:boolean; -} - - -export class SaveLiquidityDto { - public url: string; - public hash: string; -} - -export class liquidityResponseDto{ - public liquidityScore: string; - public liquidityProvider: string; -} - -export class CampaignManifestDto { - @IsNumber() - @IsPositive() - startBlock: number; - @IsNumber() - @IsPositive() - endBlock: number; - - @IsString() - @IsEnum(Exchange) - exchangeName: Exchange; - - @IsString() - tokenA: string; - @IsString() - tokenB: string; - - @IsNumber() - @IsPositive() - campaignDuration: number; - - @IsNumber() - @IsPositive() - fundAmount: number; - - @IsString() - requesterDescription: string; // address of launcher - - @IsEnum(JobRequestType) - requestType: JobRequestType; -} - -export class liquidityDto { - chainId: ChainId; - liquidityProvider: string; - liquidityScore: string; -} \ No newline at end of file diff --git a/packages/apps/hufi/recording-oracle/src/modules/liquidity/liquidity.module.ts b/packages/apps/hufi/recording-oracle/src/modules/liquidity/liquidity.module.ts deleted file mode 100644 index 37a42c651c..0000000000 --- a/packages/apps/hufi/recording-oracle/src/modules/liquidity/liquidity.module.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { HttpModule } from '@nestjs/axios'; -import { Logger, Module } from '@nestjs/common'; -import { ConfigModule } from '@nestjs/config'; -import { LiquidityController } from './liquidity.controller'; -import { LiquidityService } from './liquidity.service'; -import { Web3Module } from '../web3/web3.module'; -import { serverConfig, web3Config } from '../../common/config'; -import { StorageModule } from '../storage/storage.module'; - -@Module({ - imports: [ - ConfigModule.forFeature(serverConfig), - ConfigModule.forFeature(web3Config), - HttpModule, - Web3Module, - StorageModule, - ], - controllers: [LiquidityController], - providers: [Logger, LiquidityService], - exports: [LiquidityService], -}) -export class LiquidityModule {} diff --git a/packages/apps/hufi/recording-oracle/src/modules/liquidity/liquidity.service.ts b/packages/apps/hufi/recording-oracle/src/modules/liquidity/liquidity.service.ts deleted file mode 100644 index 90552ad865..0000000000 --- a/packages/apps/hufi/recording-oracle/src/modules/liquidity/liquidity.service.ts +++ /dev/null @@ -1,481 +0,0 @@ -import { - ChainId, - EscrowClient, - EscrowStatus, - StorageClient, -} from '@human-protocol/sdk'; -import { HttpService } from '@nestjs/axios'; -import { - BadRequestException, - Inject, - Injectable, - Logger, - NotFoundException, -} from '@nestjs/common'; -import { ethers } from 'ethers'; -import * as Minio from 'minio'; - -import { - ConfigNames, - ServerConfigType, - Web3ConfigType, - serverConfigKey, - web3ConfigKey, -} from '../../common/config'; -import { ErrorJob } from '../../common/constants/errors'; -import { JobRequestType } from '../../common/enums/job'; -import { StorageService } from '../storage/storage.service'; -import { Web3Service } from '../web3/web3.service'; -import { - CampaignManifestDto, - liquidityDto, - liquidityRequestDto, - liquidityResponseDto, -} from './liquidity.dto'; -import { ConfigService } from '@nestjs/config'; -import { signMessage } from '../../common/utils/signature'; -import { HEADER_SIGNATURE_KEY } from '../../common/constants'; -import { GraphQLClient, gql } from 'graphql-request'; -import crypto from 'crypto'; -import { CEX, DEX } from '../../common/constants/exchange'; - -@Injectable() -export class LiquidityService { - public readonly logger = new Logger(LiquidityService.name); - public readonly minioClient: Minio.Client; - - constructor( - private readonly configService: ConfigService, - @Inject(serverConfigKey) - private serverConfig: ServerConfigType, - @Inject(web3ConfigKey) - private web3Config: Web3ConfigType, - @Inject(Web3Service) - private readonly web3Service: Web3Service, - @Inject(StorageService) - private readonly storageService: StorageService, - private readonly httpService: HttpService, - ) {} - - private async getManifest( - chainId: number, - escrowAddress: string, - ): Promise { - const signer = this.web3Service.getSigner(chainId); - const escrowClient = await EscrowClient.build(signer); - const manifestUrl = await escrowClient.getManifestUrl(escrowAddress); - const manifest: CampaignManifestDto = - await StorageClient.downloadFileFromUrl(manifestUrl); - - if (!manifest) { - // const signer = this.web3Service.getSigner(chainId); - // const escrowClient = await EscrowClient.build(signer); - // const jobLauncherAddress = await escrowClient.getJobLauncherAddress( - // escrowAddress, - // ); - // const stakingClient = await StakingClient.build(signer); - // const jobLauncher = await stakingClient.getLeader(jobLauncherAddress); - // const jobLauncherWebhookUrl = jobLauncher?.webhookUrl; - - // if (!jobLauncherWebhookUrl) { - // throw new NotFoundException('Unable to get Job Launcher webhook URL'); - // } - - // const body: EscrowFailedWebhookDto = { - // escrow_address: escrowAddress, - // chain_id: chainId, - // event_type: EventType.TASK_CREATION_FAILED, - // reason: 'Unable to get manifest', - // }; - // await this.sendWebhook( - // jobLauncherWebhookUrl + ESCROW_FAILED_ENDPOINT, - // body, - // ); - throw new NotFoundException('Unable to get manifest'); - } else return manifest; - } - - private getGraphQLClient(chain: string, exchange: string): GraphQLClient { - if (chain == 'ethereum' && exchange == 'uniswap') { - return new GraphQLClient( - this.configService.get(ConfigNames.UniswapEthereumEndpoint)!, - ); - } else if (chain == 'polygon' && exchange == 'uniswap') { - return new GraphQLClient( - this.configService.get(ConfigNames.UniswapPolygonEndpoint)!, - ); - } else if (chain == 'bsc' && exchange == 'pancakeswap') { - return new GraphQLClient( - this.configService.get(ConfigNames.pancakeSwapEndpoint)!, - ); - } - - throw new BadRequestException(ErrorJob.InvalidExchange); - } - - private async sendWebhook(url: string, body: any): Promise { - const signedBody = await signMessage( - body, - this.configService.get(ConfigNames.WEB3_PRIVATE_KEY)!, - ); - await this.httpService.post(url, body, { - headers: { [HEADER_SIGNATURE_KEY]: signedBody }, - }); - } - - public async getLiquidityScore( - liquidityRequest: liquidityRequestDto, - ): Promise { - const UniswapQuery = gql` - query GetPositionSnapshots( - $user: Bytes! - $startTime: Int! - $endTime: Int! - ) { - positionSnapshots( - where: { - position_: { account: $user } - timestamp_gte: $startTime - timestamp_lte: $endTime - } - orderBy: timestamp - orderDirection: asc - ) { - id - timestamp - position { - id - liquidity - pool { - id - totalLiquidity - inputTokens { - symbol - decimals - } - } - withdrawCount - depositCount - timestampClosed - timestampOpened - } - } - } - `; - - try { - const manifest = await this.getManifest( - liquidityRequest.chainId, - liquidityRequest.escrowAddress, - ); - - if (manifest.requestType !== JobRequestType.Campaign) { - this.logger.log(ErrorJob.InvalidJobType, LiquidityService.name); - throw new BadRequestException(ErrorJob.InvalidJobType); - } - - const signer = this.web3Service.getSigner(liquidityRequest.chainId); - const escrowClient = await EscrowClient.build(signer); - const escrowStatus = await escrowClient.getStatus( - liquidityRequest.escrowAddress, - ); - if ( - escrowStatus !== EscrowStatus.Pending && - escrowStatus !== EscrowStatus.Partial - ) { - this.logger.log(ErrorJob.InvalidStatus, LiquidityService.name); - throw new BadRequestException(ErrorJob.InvalidStatus); - } - - const recordingOracleAddress = - await escrowClient.getRecordingOracleAddress( - liquidityRequest.escrowAddress, - ); - - if ( - ethers.getAddress(recordingOracleAddress) !== - (await signer.getAddress()) - ) { - this.logger.log(ErrorJob.AddressMismatches, LiquidityService.name); - throw new BadRequestException(ErrorJob.AddressMismatches); - } - - const [exchange, chain] = manifest.exchangeName.split('-'); - const variables = { - user: liquidityRequest.liquidityProvider, - startTime: manifest.startBlock, - endTime: manifest.endBlock, - token0: manifest.tokenA, - token1: manifest.tokenB, - exchange, - chain, - }; - - if (CEX.includes(manifest.exchangeName)) { - if ( - !liquidityRequest.liquidityProviderAPISecret || - !liquidityRequest.liquidityProviderAPIKEY - ) { - throw new Error('Empty API keys'); - } - const queryString = `symbol=${manifest.tokenA}${ - manifest.tokenB - }×tamp=${Date.now()}&startTime=${manifest.startBlock}`; - const signature = crypto - .createHmac('sha256', liquidityRequest.liquidityProviderAPISecret) - .update(queryString) - .digest('hex'); - const signedQueryString = `${queryString}&signature=${signature}`; - const headers = { - 'X-MBX-APIKEY': liquidityRequest.liquidityProviderAPIKEY, - }; - const response = await this.httpService - .get( - `${this.configService.get( - ConfigNames.BINANCE_URL, - )}/v3/allOrders?${signedQueryString}`, - { headers }, - ) - .toPromise(); - if (!response) { - throw new Error('Failed to get response from server'); - } - const data = response.data; - if (data) { - const filteredOrders = data.filter( - (order: any) => order.status === 'FILLED' || order.type === 'LIMIT', - ); - const liquidityScore = - this.calculateCentralizedLiquidityScore(filteredOrders); - if (liquidityRequest.save) { - await this.pushLiquidityScore( - liquidityRequest.escrowAddress, - liquidityRequest.chainId, - liquidityRequest.liquidityProvider, - liquidityScore, - ); - } - return liquidityScore; - } - throw new Error('No data received from server'); - } else { - const client = this.getGraphQLClient( - variables.chain, - variables.exchange, - ); - const result: any = await client.request(UniswapQuery, variables); - const positionSnapshots = result?.positionSnapshots; - - const filteredSnapshots = this.filterObjectsByInputTokenSymbol( - positionSnapshots, - variables.token0, - variables.token1, - ); - const liquidityScore = this.calculateLiquidityScore(filteredSnapshots); - - if (liquidityRequest.save) { - try { - await this.pushLiquidityScore( - liquidityRequest.escrowAddress, - liquidityRequest.chainId, - liquidityRequest.liquidityProvider, - liquidityScore, - ); - } catch (error: any) { - console.error(`Error in getLiquidityScore: ${error.message}`); - throw error; - } - } - const response: liquidityResponseDto = { - liquidityScore, - liquidityProvider: liquidityRequest.liquidityProvider, - }; - return response; - } - } catch (error: any) { - console.error(`Error in getLiquidityScore: ${error.message}`); - throw error; - } - } - - public calculateLiquidityScore(snapshots: any[]): string { - let totalScore = 0n; - - // Check for no snapshots - if (snapshots.length === 0) { - return '0'; - } - - // Log snapshot count - this.logger.debug(`Snapshot count: ${snapshots.length}`); - - // Single snapshot case - if (snapshots.length === 1) { - totalScore = this.calculateSingleSnapshotScore(snapshots[0]); - } - // Multiple snapshots case - else { - totalScore = this.calculateMultipleSnapshotsScore(snapshots); - } - - return totalScore.toString(); - } - - /** - * Calculate score for a single snapshot. - * - * @param snapshot - The liquidity snapshot. - * @returns The liquidity score as a BigInt. - */ - private calculateSingleSnapshotScore(snapshot: any): bigint { - const totalLiquidity = BigInt(snapshot.position.pool.totalLiquidity); - const liquidityAmount = BigInt(snapshot.position.liquidity); - const currentTime = BigInt(Math.floor(Date.now() / 1000)); - const timeWithheld = currentTime - snapshot.timestamp; - - return (liquidityAmount * timeWithheld) / totalLiquidity; - } - - /** - * Calculate score for multiple snapshots. - * - * @param snapshots - Array of liquidity snapshots. - * @returns The cumulative liquidity score as a bigint. - */ - private calculateMultipleSnapshotsScore(snapshots: any[]): bigint { - let totalScore = 0n; - - for (let i = 1; i < snapshots.length; i++) { - const snapshot = snapshots[i]; - const prevSnapshot = snapshots[i - 1]; - - // Skip if total liquidity is zero to avoid division by zero - if (snapshot.position.pool.totalLiquidity === '0') { - this.logger.warn('Total liquidity is zero, skipping snapshot.'); - continue; - } - - const totalLiquidity = BigInt(snapshot.position.pool.totalLiquidity); - const liquidityAmount = BigInt(snapshot.position.liquidity); - const timeWithheld = BigInt(snapshot.timestamp) - prevSnapshot.timestamp; - - const PRECISION = BigInt((10 ** 3).toString()); // Let's try with a higher precision to keep more decimals - - const adjustedLiquidity = liquidityAmount * PRECISION; - const multipliedLiquidity = adjustedLiquidity * timeWithheld; - const rawScore = multipliedLiquidity / totalLiquidity; - const snapshotScore = rawScore; - console.log(snapshot); - // Log all steps to find out what's going wrong - console.log('Adjusted Liquidity:', adjustedLiquidity.toString()); - console.log('Multiplied Liquidity:', multipliedLiquidity.toString()); - console.log('Raw Score:', rawScore.toString()); - console.log('Snapshot Score:', snapshotScore.toString()); - - // const snapshotScore = liquidityAmount.mul(timeWithheld).div(totalLiquidity); - console.log('Total Liquidity:', totalLiquidity.toString()); - console.log('Liquidity Amount:', liquidityAmount.toString()); - console.log('Time Withheld:', timeWithheld.toString()); - - totalScore = totalScore + snapshotScore; - console.log('total score', snapshotScore.toString()); - } - - return totalScore; - } - - public calculateCentralizedLiquidityScore( - orders: { cummulativeQuoteQty: number; time: number; updateTime: number }[], - ): string { - if (orders.length === 0) { - return '0'; - } - - const totalScore = orders.reduce((acc, order) => { - console.log('Order: ', order); - const liquidityAmount = order.cummulativeQuoteQty; - const timeWithheld = - order.time === order.updateTime ? 1 : order.updateTime - order.time; - const score = liquidityAmount * timeWithheld; - acc += score; - console.log('Total Score: ', acc); - return acc; - }, 0); - - return totalScore.toString(); - } - - private filterObjectsByInputTokenSymbol( - objects: any[], - symbol1: any, - symbol2: any, - ) { - return objects.filter( - (obj: { position: { pool: { inputTokens: any } } }) => { - const inputTokens = obj.position.pool.inputTokens; - const firstToken = inputTokens[0]?.symbol; - const secondToken = inputTokens[1]?.symbol; - return firstToken === symbol1 && secondToken === symbol2; - }, - ); - } - - public async pushLiquidityScore( - escrowAddress: string, - chainId: ChainId, - liquidityProvider: string, - score: string, - ): Promise { - const signer = this.web3Service.getSigner(chainId); - const escrowClient = await EscrowClient.build(signer); - - let liquidities: liquidityDto[]; - const existingLiquiditiesURL: string = - await escrowClient.getIntermediateResultsUrl(escrowAddress); - if (existingLiquiditiesURL) { - liquidities = JSON.parse( - await this.storageService.download(existingLiquiditiesURL), - ); - if (liquidities) { - const exisitingLiquidity = liquidities.find( - (liq) => liq.liquidityProvider === liquidityProvider, - ); - if (exisitingLiquidity) { - exisitingLiquidity.liquidityScore = score; - } else { - liquidities.push({ - chainId: chainId, - liquidityProvider: liquidityProvider, - liquidityScore: score, - }); - } - } else { - throw new NotFoundException(ErrorJob.NotFoundIntermediateResults); - } - } else { - liquidities = [ - { - chainId, - liquidityProvider, - liquidityScore: score, - }, - ]; - } - - const saveLiquidityResult = await this.storageService.uploadLiquidities( - escrowAddress, - chainId, - liquidities, - ); - - if (!existingLiquiditiesURL) { - await escrowClient.storeResults( - escrowAddress, - saveLiquidityResult.url, - saveLiquidityResult.hash, - ); - } - - return 'Liquidity Scores are recorded.'; - } -} diff --git a/packages/apps/hufi/recording-oracle/src/modules/storage/storage.module.ts b/packages/apps/hufi/recording-oracle/src/modules/storage/storage.module.ts deleted file mode 100644 index f9cf1659df..0000000000 --- a/packages/apps/hufi/recording-oracle/src/modules/storage/storage.module.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Module } from '@nestjs/common'; -import { StorageService } from './storage.service'; -import { ConfigModule } from '@nestjs/config'; -import { s3Config } from '../../common/config'; - -@Module({ - imports: [ConfigModule.forFeature(s3Config)], - providers: [StorageService], - exports: [StorageService], -}) -export class StorageModule {} diff --git a/packages/apps/hufi/recording-oracle/src/modules/storage/storage.service.spec.ts b/packages/apps/hufi/recording-oracle/src/modules/storage/storage.service.spec.ts deleted file mode 100644 index c461569f00..0000000000 --- a/packages/apps/hufi/recording-oracle/src/modules/storage/storage.service.spec.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { StorageClient } from '@human-protocol/sdk'; -import { ConfigModule, registerAs } from '@nestjs/config'; -import { Test } from '@nestjs/testing'; -import { - MOCK_FILE_URL, - MOCK_S3_ACCESS_KEY, - MOCK_S3_BUCKET, - MOCK_S3_ENDPOINT, - MOCK_S3_PORT, - MOCK_S3_SECRET_KEY, - MOCK_S3_USE_SSL, -} from '../../../test/constants'; -import { StorageService } from './storage.service'; - -jest.mock('@human-protocol/sdk', () => ({ - ...jest.requireActual('@human-protocol/sdk'), - StorageClient: { - downloadFileFromUrl: jest.fn(), - }, -})); - -jest.mock('minio', () => { - class Client { - putObject = jest.fn(); - bucketExists = jest.fn(); - constructor() { - (this as any).protocol = 'http:'; - (this as any).host = 'localhost'; - (this as any).port = 9000; - } - } - - return { Client }; -}); - -describe('Web3Service', () => { - let storageService: StorageService; - - beforeAll(async () => { - const moduleRef = await Test.createTestingModule({ - imports: [ - ConfigModule.forFeature( - registerAs('s3', () => ({ - accessKey: MOCK_S3_ACCESS_KEY, - secretKey: MOCK_S3_SECRET_KEY, - endPoint: MOCK_S3_ENDPOINT, - port: MOCK_S3_PORT, - useSSL: MOCK_S3_USE_SSL, - bucket: MOCK_S3_BUCKET, - })), - ), - ], - providers: [StorageService], - }).compile(); - - storageService = moduleRef.get(StorageService); - }); - - describe('download', () => { - it('should download the file correctly', async () => { - const exchangeAddress = '0x1234567890123456789012345678901234567892'; - const workerAddress = '0x1234567890123456789012345678901234567891'; - const solution = 'test'; - - const expectedJobFile = { - exchangeAddress, - solutions: [ - { - workerAddress, - solution, - }, - ], - }; - - StorageClient.downloadFileFromUrl = jest - .fn() - .mockResolvedValue(expectedJobFile); - const solutionsFile = await storageService.download(MOCK_FILE_URL); - expect(solutionsFile).toBe(expectedJobFile); - }); - - it('should return empty array when file cannot be downloaded', async () => { - StorageClient.downloadFileFromUrl = jest - .fn() - .mockRejectedValue('Network error'); - - const solutionsFile = await storageService.download(MOCK_FILE_URL); - expect(solutionsFile).toStrictEqual([]); - }); - }); -}); diff --git a/packages/apps/hufi/recording-oracle/src/modules/storage/storage.service.ts b/packages/apps/hufi/recording-oracle/src/modules/storage/storage.service.ts deleted file mode 100644 index ca9c38106c..0000000000 --- a/packages/apps/hufi/recording-oracle/src/modules/storage/storage.service.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { ChainId, StorageClient } from '@human-protocol/sdk'; -import { BadRequestException, Inject, Injectable } from '@nestjs/common'; -import * as Minio from 'minio'; -import { S3ConfigType, s3ConfigKey } from '../../common/config'; -import crypto from 'crypto'; -import { SaveLiquidityDto, liquidityDto } from '../liquidity/liquidity.dto'; -import { ILiquidityScore } from '../../common/interfaces/liquidity'; - -@Injectable() -export class StorageService { - public readonly minioClient: Minio.Client; - - constructor( - @Inject(s3ConfigKey) - private s3Config: S3ConfigType, - ) { - this.minioClient = new Minio.Client({ - endPoint: this.s3Config.endPoint, - port: this.s3Config.port, - accessKey: this.s3Config.accessKey, - secretKey: this.s3Config.secretKey, - useSSL: this.s3Config.useSSL, - }); - } - - public getJobUrl(escrowAddress: string, chainId: ChainId): string { - return `${this.s3Config.useSSL ? 'https' : 'http'}://${ - this.s3Config.endPoint - }:${this.s3Config.port}/${ - this.s3Config.bucket - }/${escrowAddress}-${chainId}.json`; - } - - public async uploadLiquidities( - escrowAddress: string, - chainId: ChainId, - liquidities: liquidityDto[], - ): Promise { - if (!(await this.minioClient.bucketExists(this.s3Config.bucket))) { - throw new BadRequestException('Bucket not found'); - } - const content = JSON.stringify(liquidities); - try { - const hash = crypto.createHash('sha1').update(content).digest('hex'); - const filename = `${escrowAddress}-${chainId}.json`; - await this.minioClient.putObject( - this.s3Config.bucket, - filename, - JSON.stringify(content), - { - 'Content-Type': 'application/json', - 'Cache-Control': 'no-store', - }, - ); - - return { url: this.getJobUrl(escrowAddress, chainId), hash }; - } catch (e) { - throw new BadRequestException('File not uploaded'); - } - } - - public async download(url: string): Promise { - try { - return await StorageClient.downloadFileFromUrl(url); - } catch { - return []; - } - } - - public async downloadLiquidityScores( - escrowAddress: string, - chainId: ChainId, - ): Promise { - const url = this.getJobUrl(escrowAddress, chainId); - try { - return await StorageClient.downloadFileFromUrl(url); - } catch { - return []; - } - } -} diff --git a/packages/apps/hufi/recording-oracle/src/modules/web3/web3.module.ts b/packages/apps/hufi/recording-oracle/src/modules/web3/web3.module.ts deleted file mode 100644 index 1a67598c72..0000000000 --- a/packages/apps/hufi/recording-oracle/src/modules/web3/web3.module.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Module } from '@nestjs/common'; -import { Web3Service } from './web3.service'; -import { ConfigModule } from '@nestjs/config'; -import { web3Config } from '../../common/config'; - -@Module({ - imports: [ConfigModule.forFeature(web3Config)], - providers: [Web3Service], - exports: [Web3Service], -}) -export class Web3Module {} diff --git a/packages/apps/hufi/recording-oracle/src/modules/web3/web3.service.spec.ts b/packages/apps/hufi/recording-oracle/src/modules/web3/web3.service.spec.ts deleted file mode 100644 index 068bbde68d..0000000000 --- a/packages/apps/hufi/recording-oracle/src/modules/web3/web3.service.spec.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { ConfigModule, ConfigService, registerAs } from '@nestjs/config'; -import { Test } from '@nestjs/testing'; -import { Web3Service } from './web3.service'; -import { - MOCK_S3_ACCESS_KEY, - MOCK_S3_BUCKET, - MOCK_S3_ENDPOINT, - MOCK_S3_PORT, - MOCK_S3_SECRET_KEY, - MOCK_S3_USE_SSL, - MOCK_WEB3_PRIVATE_KEY, -} from '../../../test/constants'; -import { networkMap } from '../../common/constants/networks'; - -describe('Web3Service', () => { - let web3Service: Web3Service; - - beforeAll(async () => { - const configServiceMock = { - get: jest.fn().mockReturnValue(MOCK_WEB3_PRIVATE_KEY), - }; - - const moduleRef = await Test.createTestingModule({ - imports: [ - ConfigModule.forFeature( - registerAs('web3', () => ({ - web3PrivateKey: MOCK_WEB3_PRIVATE_KEY, - })), - ), - ], - providers: [Web3Service], - }).compile(); - - web3Service = moduleRef.get(Web3Service); - }); - - describe('getSigner', () => { - it('should return the signer for the specified chainId', async () => { - for (const networkKey of Object.keys(networkMap)) { - // Iterate through the networkMap to test each chainId - const network = networkMap[networkKey]; - - const signer = web3Service.getSigner(network.chainId); - expect(signer).toBeDefined(); - expect((signer.provider as any).connection.url).toBe(network.rpcUrl); - } - }); - - it('should return undefined if chainId is not configured', () => { - const chainId = 1; - - const signer = web3Service.getSigner(chainId); - - expect(signer).toBeUndefined(); - }); - }); -}); diff --git a/packages/apps/hufi/recording-oracle/src/modules/web3/web3.service.ts b/packages/apps/hufi/recording-oracle/src/modules/web3/web3.service.ts deleted file mode 100644 index 70b97fe0c6..0000000000 --- a/packages/apps/hufi/recording-oracle/src/modules/web3/web3.service.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { Wallet, ethers } from 'ethers'; -import { Web3ConfigType, web3ConfigKey } from '../../common/config'; -import { networkMap } from '../../common/constants/networks'; - -@Injectable() -export class Web3Service { - private signers: { [key: number]: Wallet } = {}; - - constructor( - @Inject(web3ConfigKey) - private web3Config: Web3ConfigType, - ) { - const privateKey = this.web3Config.web3PrivateKey; - - for (const networkKey of Object.keys(networkMap)) { - const network = networkMap[networkKey]; - const provider = new ethers.JsonRpcProvider(network.rpcUrl); - this.signers[network.chainId] = new Wallet(privateKey, provider); - } - } - - getSigner(chainId: number): Wallet { - return this.signers[chainId]; - } -} diff --git a/packages/apps/hufi/recording-oracle/test/app.e2e-spec.ts b/packages/apps/hufi/recording-oracle/test/app.e2e-spec.ts deleted file mode 100644 index 50cda62332..0000000000 --- a/packages/apps/hufi/recording-oracle/test/app.e2e-spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { INestApplication } from '@nestjs/common'; -import * as request from 'supertest'; -import { AppModule } from './../src/app.module'; - -describe('AppController (e2e)', () => { - let app: INestApplication; - - beforeEach(async () => { - const moduleFixture: TestingModule = await Test.createTestingModule({ - imports: [AppModule], - }).compile(); - - app = moduleFixture.createNestApplication(); - await app.init(); - }); - - it('/ (GET)', () => { - return request(app.getHttpServer()) - .get('/') - .expect(200) - .expect('Hello World!'); - }); -}); diff --git a/packages/apps/hufi/recording-oracle/test/constants.ts b/packages/apps/hufi/recording-oracle/test/constants.ts deleted file mode 100644 index e9091da884..0000000000 --- a/packages/apps/hufi/recording-oracle/test/constants.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { JobRequestType } from '../src/common/enums/job'; - -export const MOCK_HOST = '127.0.0.1'; -export const MOCK_PORT = 5000; -export const MOCK_WEB3_PRIVATE_KEY = - '5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a'; -export const MOCK_REQUESTER_TITLE = 'Mock job title'; -export const MOCK_REQUESTER_DESCRIPTION = 'Mock job description'; -export const MOCK_ADDRESS = '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC'; -export const MOCK_SIGNATURE = - '0x1502ec7e795ed9c96b215e80d46d87e26141bc8d41f69ee1bfbc8d8ed9a700db62136da8ee35eadbfd678817342444dff0239508be51c1fae55d62fcdba2867e1b'; -export const MOCK_FILE_URL = 'mockedFileUrl'; -export const MOCK_FILE_HASH = 'mockedFileHash'; -export const MOCK_FILE_KEY = 'manifest.json'; -export const MOCK_EXCHANGE_ORACLE_WEBHOOK_URL = 'http://localhost:3000'; -export const MOCK_S3_ENDPOINT = 'localhost'; -export const MOCK_S3_PORT = 9000; -export const MOCK_S3_ACCESS_KEY = 'access_key'; -export const MOCK_S3_SECRET_KEY = 'secret_key'; -export const MOCK_S3_BUCKET = 'solution'; -export const MOCK_S3_USE_SSL = false; diff --git a/packages/apps/hufi/recording-oracle/test/jest-e2e.json b/packages/apps/hufi/recording-oracle/test/jest-e2e.json deleted file mode 100644 index e9d912f3e3..0000000000 --- a/packages/apps/hufi/recording-oracle/test/jest-e2e.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "moduleFileExtensions": ["js", "json", "ts"], - "rootDir": ".", - "testEnvironment": "node", - "testRegex": ".e2e-spec.ts$", - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - } -} diff --git a/packages/apps/hufi/recording-oracle/tsconfig.build.json b/packages/apps/hufi/recording-oracle/tsconfig.build.json deleted file mode 100644 index 64f86c6bd2..0000000000 --- a/packages/apps/hufi/recording-oracle/tsconfig.build.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": "./tsconfig.json", - "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] -} diff --git a/packages/apps/hufi/recording-oracle/tsconfig.json b/packages/apps/hufi/recording-oracle/tsconfig.json deleted file mode 100644 index 124ce1f08d..0000000000 --- a/packages/apps/hufi/recording-oracle/tsconfig.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "extends": "../../../../tsconfig.json", - "compilerOptions": { - "alwaysStrict": true, - "allowJs": true, - "declaration": true, - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "lib": ["es2019", "ES2020.Promise"], - "moduleResolution": "node", - "noImplicitAny": true, - "noImplicitReturns": true, - "noImplicitThis": true, - "noUnusedLocals": false, - "noUnusedParameters": false, - "outDir": "./dist", - "removeComments": true, - "sourceMap": true, - "strictBindCallApply": true, - "strictFunctionTypes": true, - "strictNullChecks": true, - "strictPropertyInitialization": false, - "baseUrl": ".", - "paths": { - "@/*": ["src/*"] - } - }, - "include": ["./src", "./scripts"], - "exclude": ["node_modules", "dist", "scripts", "test"] -} diff --git a/packages/apps/hufi/recording-oracle/vercel.json b/packages/apps/hufi/recording-oracle/vercel.json deleted file mode 100644 index 3e7126787b..0000000000 --- a/packages/apps/hufi/recording-oracle/vercel.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "version": 2, - "builds": [ - { - "src": "src/main.ts", - "use": "@vercel/node" - } - ], - "routes": [ - { - "src": "/(.*)", - "dest": "src/main.ts", - "headers": { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Methods": "*", - "Access-Control-Allow-Headers": "X-Requested-With,Content-Type,Accept" - } - } - ], - "ignoreCommand": "git diff HEAD^ HEAD --quiet ." -} diff --git a/packages/apps/hufi/reputation-oracle/server/.env.example b/packages/apps/hufi/reputation-oracle/server/.env.example deleted file mode 100644 index 8bb6647046..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/.env.example +++ /dev/null @@ -1,31 +0,0 @@ -# General -NODE_ENV=development -HOST=localhost -PORT=3008 -SESSION_SECRET=test - -# Database -POSTGRES_HOST=0.0.0.0 -POSTGRES_USER=operator -POSTGRES_PASSWORD=qwerty -POSTGRES_DATABASE=reputation-oracle -POSTGRES_SYNC=false -POSTGRES_PORT=5432 -POSTGRES_SSL=false - -# Auth -JWT_SECRET= -JWT_ACCESS_TOKEN_EXPIRES_IN= -JWT_REFRESH_TOKEN_EXPIRES_IN= - -S3_ENDPOINT= -S3_PORT= -S3_ACCESS_KEY= -S3_SECRET_KEY= -S3_REGION= - -WEB3_PRIVATE_KEY= - -# Reputation Level -REPUTATION_LEVEL_LOW= -REPUTATION_LEVEL_HIGH= diff --git a/packages/apps/hufi/reputation-oracle/server/.eslintrc.js b/packages/apps/hufi/reputation-oracle/server/.eslintrc.js deleted file mode 100644 index 259de13c73..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/.eslintrc.js +++ /dev/null @@ -1,25 +0,0 @@ -module.exports = { - parser: '@typescript-eslint/parser', - parserOptions: { - project: 'tsconfig.json', - tsconfigRootDir: __dirname, - sourceType: 'module', - }, - plugins: ['@typescript-eslint/eslint-plugin'], - extends: [ - 'plugin:@typescript-eslint/recommended', - 'plugin:prettier/recommended', - ], - root: true, - env: { - node: true, - jest: true, - }, - ignorePatterns: ['.eslintrc.js'], - rules: { - '@typescript-eslint/interface-name-prefix': 'off', - '@typescript-eslint/explicit-function-return-type': 'off', - '@typescript-eslint/explicit-module-boundary-types': 'off', - '@typescript-eslint/no-explicit-any': 'off', - }, -}; diff --git a/packages/apps/hufi/reputation-oracle/server/.gitignore b/packages/apps/hufi/reputation-oracle/server/.gitignore deleted file mode 100644 index 146d706814..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/.gitignore +++ /dev/null @@ -1,38 +0,0 @@ -# compiled output -/dist -/node_modules - -# Logs -logs -*.log -npm-debug.log* -pnpm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* - -# OS -.DS_Store - -# Tests -/coverage -/.nyc_output - -# IDEs and editors -/.idea -.project -.classpath -.c9/ -*.launch -.settings/ -*.sublime-workspace - -# IDE - VSCode -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json - -.env.development -.env.production \ No newline at end of file diff --git a/packages/apps/hufi/reputation-oracle/server/.prettierrc b/packages/apps/hufi/reputation-oracle/server/.prettierrc deleted file mode 100644 index dcb72794f5..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/.prettierrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "singleQuote": true, - "trailingComma": "all" -} \ No newline at end of file diff --git a/packages/apps/hufi/reputation-oracle/server/README.md b/packages/apps/hufi/reputation-oracle/server/README.md deleted file mode 100644 index b669354cf7..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/README.md +++ /dev/null @@ -1,107 +0,0 @@ -

- Human Protocol -

- -[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456 -[circleci-url]: https://circleci.com/gh/nestjs/nest -

Reputation Oracle

-

The Reputation Oracle application directly interacts with the Exchange Oracle and Recording Oracle to validate their results. Then, it records the reputation information to a new file and database.

- -

- - License: MIT - - -

- -## ✨ Demo -First, let's install the dependencies, `yarn` is used as a package manager: -```bash -$ yarn install -``` - -The application needs access to environment variables in order to work correctly, for this, create one of the `.env.` files, depending on the state of your environment: - -```bash -$ export NODE_ENV=development -``` - -Use the `.env.example` file as an example to create a configuration file with certain environment variables: - -```bash -$ cp .env.example .env.development -``` - -Next, the requirement that the application puts forward is to set up a database, for this there are two different options, `manually` or using `docker`. - -### Set up the database manually -First of all, postgres needs to be installed, please see here please see here. - -Then run the following commands in the postgres console to create the database and issue permissions: -```bash -$ CREATE DATABASE "reputation-oracle"; -$ CREATE USER operator WITH ENCRYPTED PASSWORD 'qwerty'; -$ GRANT ALL PRIVILEGES ON DATABASE "reputation-oracle" TO "operator"; -$ \c "reputation-oracle" postgres -$ GRANT CREATE ON SCHEMA public TO operator; -``` -Now we're ready to run the migrations: -```bash -yarn migration:run -``` - -### Set up the database with Docker -To run with docker, you need to enter the following command, which raises the container with postgres and runs the migrations: - -```bash -yarn docker:db:up -``` - -## 🚀 Usage -### Running the app - -```bash -# development -$ yarn run start - -# watch mode -$ yarn run start:dev - -# production mode -$ yarn run start:prod - -# debug mode -$ yarn run start:debug -``` - -### Testing the app - -```bash -# unit tests -$ yarn run test - -# e2e tests -$ yarn run test:e2e - -# test coverage -$ yarn run test:cov -``` - -### Migrations - -```bash -# Create new migration -$ yarn migration:create addNameTable - -# Generate new migration -$ yarn migration:generate addNameTable - -# Revert latest migration -$ yarn migration:revert - -# Run all pending migrations -$ yarn migration:run - -# Show all migrations -$ yarn migration:show -``` \ No newline at end of file diff --git a/packages/apps/hufi/reputation-oracle/server/docker-compose.yml b/packages/apps/hufi/reputation-oracle/server/docker-compose.yml deleted file mode 100644 index 8ee11010d4..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/docker-compose.yml +++ /dev/null @@ -1,50 +0,0 @@ -version: '3.8' - -services: - postgres: - image: postgres:latest - restart: always - environment: - - POSTGRES_HOST=${POSTGRES_HOST} - - POSTGRES_USER=${POSTGRES_USER} - - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} - - POSTGRES_DB=${POSTGRES_DATABASE} - - POSTGRES_PORT=${POSTGRES_PORT} - logging: - options: - max-size: 10m - max-file: "3" - ports: - - '${POSTGRES_PORT}:${POSTGRES_PORT}' - # volumes: - # - ./db:/var/lib/postgresql/data - - minio: - container_name: minio - image: minio/minio:RELEASE.2022-05-26T05-48-41Z - ports: - - 9001:9001 - - 9000:9000 - environment: - MINIO_ROOT_USER: ${S3_ACCESS_KEY} - MINIO_ROOT_PASSWORD: ${S3_SECRET_KEY} - entrypoint: 'sh' - command: - -c "mkdir -p /data/reputation && minio server /data --console-address ':9001'" - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"] - interval: 5s - timeout: 5s - retries: 3 - minio-mc: - container_name: minio-mc - image: minio/mc - depends_on: - minio: - condition: service_healthy - entrypoint: > - /bin/sh -c " - /usr/bin/mc config host add myminio http://minio:9000 ${S3_ACCESS_KEY} ${S3_SECRET_KEY}; - /usr/bin/mc mb myminio/reputation; - /usr/bin/mc anonymous set public myminio/reputation; - " \ No newline at end of file diff --git a/packages/apps/hufi/reputation-oracle/server/jest.config.ts b/packages/apps/hufi/reputation-oracle/server/jest.config.ts deleted file mode 100644 index 660e5a6e07..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/jest.config.ts +++ /dev/null @@ -1,15 +0,0 @@ -module.exports = { - coverageDirectory: '../coverage', - collectCoverageFrom: ['**/*.(t|j)s'], - moduleFileExtensions: ['js', 'json', 'ts'], - rootDir: 'src', - testEnvironment: 'node', - testRegex: '.*\\.spec\\.ts$', - transform: { - '^.+\\.(t|j)s$': 'ts-jest', - }, - moduleNameMapper: { - '^uuid$': require.resolve('uuid'), - '^typeorm$': require.resolve('typeorm'), - }, -}; \ No newline at end of file diff --git a/packages/apps/hufi/reputation-oracle/server/nest-cli.json b/packages/apps/hufi/reputation-oracle/server/nest-cli.json deleted file mode 100644 index f9aa683b1a..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/nest-cli.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/nest-cli", - "collection": "@nestjs/schematics", - "sourceRoot": "src", - "compilerOptions": { - "deleteOutDir": true - } -} diff --git a/packages/apps/hufi/reputation-oracle/server/package.json b/packages/apps/hufi/reputation-oracle/server/package.json deleted file mode 100644 index d8e81bbf47..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/package.json +++ /dev/null @@ -1,92 +0,0 @@ -{ - "name": "hufi-reputation-oracle", - "version": "0.0.1", - "description": "", - "author": "", - "private": true, - "license": "UNLICENSED", - "scripts": { - "build": "nest build", - "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", - "start": "nest start", - "start:dev": "nest start --watch", - "start:debug": "nest start --debug --watch", - "start:prod": "node dist/main", - "migration:create": "typeorm-ts-node-commonjs migration:create", - "migration:generate": "typeorm-ts-node-commonjs migration:generate -d typeorm.config.ts", - "migration:revert": "typeorm-ts-node-commonjs migration:revert -d typeorm.config.ts", - "migration:run": "typeorm-ts-node-commonjs migration:run -d typeorm.config.ts", - "migration:show": "typeorm-ts-node-commonjs migration:show -d typeorm.config.ts", - "docker:db:up": "docker compose up -d && yarn migration:run", - "docker:db:down": "docker compose down", - "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", - "test": "jest", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "jest --config ./test/jest-e2e.json", - "vercel-build": "yarn workspace @human-protocol/sdk build" - }, - "dependencies": { - "@human-protocol/core": "*", - "@human-protocol/sdk": "*", - "@nestjs/axios": "^2.0.0", - "@nestjs/common": "^10.2.7", - "@nestjs/config": "^3.1.1", - "@nestjs/core": "^10.2.8", - "@nestjs/jwt": "^10.2.0", - "@nestjs/passport": "^10.0.0", - "@nestjs/platform-express": "^10.2.6", - "@nestjs/schedule": "^4.0.0", - "@nestjs/swagger": "^7.1.13", - "@nestjs/terminus": "^10.2.0", - "@nestjs/typeorm": "^10.0.1", - "@types/passport-jwt": "^3.0.10", - "bcrypt": "^5.1.1", - "class-transformer": "^0.5.1", - "class-validator": "^0.14.0", - "cookie-parser": "^1.4.6", - "express-session": "^1.17.3", - "helmet": "^7.1.0", - "joi": "^17.9.2", - "nestjs-minio-client": "^2.2.0", - "passport": "^0.6.0", - "passport-jwt": "^4.0.1", - "pg": "8.11.3", - "reflect-metadata": "^0.1.13", - "rxjs": "^7.2.0", - "typeorm": "^0.3.16", - "typeorm-naming-strategies": "^4.1.0", - "zxcvbn": "^4.4.2" - }, - "devDependencies": { - "@golevelup/ts-jest": "^0.4.0", - "@nestjs/cli": "^9.4.3", - "@nestjs/schematics": "^9.2.0", - "@nestjs/testing": "^9.4.3", - "@types/bcrypt": "^5.0.2", - "@types/cookie-parser": "^1.4.3", - "@types/express": "^4.17.13", - "@types/express-session": "^1.17.10", - "@types/jest": "29.5.11", - "@types/node": "20.10.6", - "@types/supertest": "^6.0.2", - "@types/uuid": "^9.0.6", - "@types/zxcvbn": "4.4.1", - "@typescript-eslint/eslint-plugin": "^5.0.0", - "@typescript-eslint/parser": "^5.0.0", - "eslint": "^8.55.0", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-prettier": "^5.0.0", - "express-session": "^1.17.3", - "jest": "29.7.0", - "prettier": "^3.1.1", - "source-map-support": "^0.5.20", - "supertest": "^6.3.4", - "ts-jest": "29.1.1", - "ts-loader": "^9.2.3", - "ts-node": "^10.9.2", - "tsconfig-paths": "4.2.0", - "typescript": "^5.0.0" - } -} diff --git a/packages/apps/hufi/reputation-oracle/server/src/app.controller.spec.ts b/packages/apps/hufi/reputation-oracle/server/src/app.controller.spec.ts deleted file mode 100644 index 741a42c324..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/app.controller.spec.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { AppController } from './app.controller'; - -describe('AppController', () => { - let appController: AppController; - - beforeEach(() => { - appController = new AppController(); - }); - - describe('Health Check', () => { - it('should return OK', async () => { - expect(await appController.health()).toBe('OK'); - }); - }); -}); diff --git a/packages/apps/hufi/reputation-oracle/server/src/app.controller.ts b/packages/apps/hufi/reputation-oracle/server/src/app.controller.ts deleted file mode 100644 index f82b076b7e..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/app.controller.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Controller, Get, Redirect } from '@nestjs/common'; -import { Public } from './common/decorators'; -import { ApiExcludeController } from '@nestjs/swagger'; - -@Controller('/') -@ApiExcludeController() -export class AppController { - @Public() - @Get('/') - @Redirect('/swagger', 301) - public health(): string { - return 'OK'; - } -} diff --git a/packages/apps/hufi/reputation-oracle/server/src/app.module.ts b/packages/apps/hufi/reputation-oracle/server/src/app.module.ts deleted file mode 100644 index 046ccb72d3..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/app.module.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { Module } from '@nestjs/common'; -import { APP_PIPE } from '@nestjs/core'; -import { ConfigModule } from '@nestjs/config'; -import { ScheduleModule } from '@nestjs/schedule'; -import { AppController } from './app.controller'; -import { DatabaseModule } from './database/database.module'; -import { HttpValidationPipe } from './common/pipes'; -import { HealthModule } from './modules/health/health.module'; -import { ReputationModule } from './modules/reputation/reputation.module'; -import { WebhookModule } from './modules/webhook/webhook.module'; -import { Web3Module } from './modules/web3/web3.module'; -import { envValidator } from './common/config'; -import { ServeStaticModule } from '@nestjs/serve-static'; -import { join } from 'path'; - -@Module({ - providers: [ - { - provide: APP_PIPE, - useClass: HttpValidationPipe, - }, - ], - imports: [ - ScheduleModule.forRoot(), - ConfigModule.forRoot({ - envFilePath: process.env.NODE_ENV - ? `.env.${process.env.NODE_ENV as string}` - : '.env', - validationSchema: envValidator, - }), - DatabaseModule, - HealthModule, - ReputationModule, - WebhookModule, - Web3Module, - ServeStaticModule.forRoot({ - rootPath: join( - __dirname, - '../../../../../../', - 'node_modules/swagger-ui-dist', - ), - }), - ], - controllers: [AppController], -}) -export class AppModule {} diff --git a/packages/apps/hufi/reputation-oracle/server/src/common/config/env.ts b/packages/apps/hufi/reputation-oracle/server/src/common/config/env.ts deleted file mode 100644 index 8d1e38db9f..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/common/config/env.ts +++ /dev/null @@ -1,62 +0,0 @@ -import * as Joi from 'joi'; - -export const ConfigNames = { - NODE_ENV: 'NODE_ENV', - HOST: 'HOST', - PORT: 'PORT', - SESSION_SECRET: 'SESSION_SECRET', - HASH_SECRET: 'HASH_SECRET', - JWT_SECRET: 'JWT_SECRET', - JWT_ACCESS_TOKEN_EXPIRES_IN: 'JWT_ACCESS_TOKEN_EXPIRES_IN', - JWT_REFRESH_TOKEN_EXPIRES_IN: 'JWT_REFRESH_TOKEN_EXPIRES_IN', - POSTGRES_HOST: 'POSTGRES_HOST', - POSTGRES_USER: 'POSTGRES_USER', - POSTGRES_PASSWORD: 'POSTGRES_PASSWORD', - POSTGRES_DATABASE: 'POSTGRES_DATABASE', - POSTGRES_PORT: 'POSTGRES_PORT', - POSTGRES_SYNC: 'POSTGRES_SYNC', - POSTGRES_SSL: 'POSTGRES_SSL', - WEB3_PRIVATE_KEY: 'WEB3_PRIVATE_KEY', - S3_ENDPOINT: 'S3_ENDPOINT', - S3_PORT: 'S3_PORT', - S3_ACCESS_KEY: 'S3_ACCESS_KEY', - S3_SECRET_KEY: 'S3_SECRET_KEY', - S3_BUCKET: 'S3_BUCKET', - S3_USE_SSL: 'S3_USE_SSL', - REPUTATION_LEVEL_LOW: 'REPUTATION_LEVEL_LOW', - REPUTATION_LEVEL_HIGH: 'REPUTATION_LEVEL_HIGH', -}; - -export const envValidator = Joi.object({ - // General - NODE_ENV: Joi.string().default('development'), - HOST: Joi.string().default('localhost'), - PORT: Joi.string().default(5000), - SESSION_SECRET: Joi.string().default('session_key'), - // Auth - HASH_SECRET: Joi.string().default('a328af3fc1dad15342cc3d68936008fa'), - JWT_SECRET: Joi.string().default('secret'), - JWT_ACCESS_TOKEN_EXPIRES_IN: Joi.string().default(1000000000), - JWT_REFRESH_TOKEN_EXPIRES_IN: Joi.string().default(1000000000), - // Database - DB_TYPE: Joi.string().default('postgres'), - POSTGRES_HOST: Joi.string().default('127.0.0.1'), - POSTGRES_USER: Joi.string().default('operator'), - POSTGRES_PASSWORD: Joi.string().default('qwerty'), - POSTGRES_DATABASE: Joi.string().default('reputation-oracle'), - POSTGRES_PORT: Joi.string().default('5432'), - POSTGRES_SYNC: Joi.string().default('false'), - POSTGRES_SSL: Joi.string().default('false'), - // Web3 - WEB3_PRIVATE_KEY: Joi.string().required(), - // S3 - S3_ENDPOINT: Joi.string().default('127.0.0.1'), - S3_PORT: Joi.string().default(9000), - S3_ACCESS_KEY: Joi.string().required(), - S3_SECRET_KEY: Joi.string().required(), - S3_BUCKET: Joi.string().default('reputation'), - S3_USE_SSL: Joi.string().default(false), - // Reputation Level - REPUTATION_LEVEL_LOW: Joi.number().default(300), - REPUTATION_LEVEL_HIGH: Joi.number().default(700), -}); diff --git a/packages/apps/hufi/reputation-oracle/server/src/common/config/index.ts b/packages/apps/hufi/reputation-oracle/server/src/common/config/index.ts deleted file mode 100644 index 20c54299ce..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/common/config/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './env'; -export * from './networks'; -export * from './s3'; diff --git a/packages/apps/hufi/reputation-oracle/server/src/common/config/networks.ts b/packages/apps/hufi/reputation-oracle/server/src/common/config/networks.ts deleted file mode 100644 index b499aae81d..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/common/config/networks.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { ChainId } from '@human-protocol/sdk'; - -export interface NetworkDto { - chainId: ChainId; - rpcUrl: string; -} - -interface NetworkMapDto { - [key: string]: NetworkDto; -} - -export const networkMap: NetworkMapDto = { - polygon: { - chainId: ChainId.POLYGON, - rpcUrl: - 'https://polygon-mainnet.g.alchemy.com/v2/0Lorh5KRkGl5FsRwy2epTg8fEFFoqUfY', - }, - bsc: { - chainId: ChainId.BSC_MAINNET, - rpcUrl: 'https://bsc-dataseed1.binance.org/', - }, - mumbai: { - chainId: ChainId.POLYGON_MUMBAI, - rpcUrl: - 'https://polygon-mumbai.g.alchemy.com/v2/vKNSJzJf6SW2sdW-05bgFwoyFxUrMzii', - }, - goerli: { - chainId: ChainId.GOERLI, - rpcUrl: 'https://goerli.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161', - }, - moonbeam: { - chainId: ChainId.MOONBEAM, - rpcUrl: 'https://rpc.api.moonbeam.network', - }, - bsctest: { - chainId: ChainId.BSC_TESTNET, - rpcUrl: 'https://data-seed-prebsc-1-s1.binance.org:8545/', - }, -}; diff --git a/packages/apps/hufi/reputation-oracle/server/src/common/config/s3.ts b/packages/apps/hufi/reputation-oracle/server/src/common/config/s3.ts deleted file mode 100644 index b2d4e3731b..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/common/config/s3.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { ConfigType, registerAs } from '@nestjs/config'; - -export const s3Config = registerAs('s3', () => ({ - endPoint: process.env.S3_ENDPOINT!, - port: +process.env.S3_PORT!, - accessKey: process.env.S3_ACCESS_KEY!, - secretKey: process.env.S3_SECRET_KEY!, - bucket: process.env.S3_BUCKET!, - useSSL: process.env.S3_USE_SSL === 'true', -})); - -export const s3ConfigKey = s3Config.KEY; -export type S3ConfigType = ConfigType; diff --git a/packages/apps/hufi/reputation-oracle/server/src/common/constants/errors.ts b/packages/apps/hufi/reputation-oracle/server/src/common/constants/errors.ts deleted file mode 100644 index 32563925b2..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/common/constants/errors.ts +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Represents error messages related to webhook. - */ -export enum ErrorWebhook { - NotFound = 'Webhook not found', - NotCreated = 'Webhook has not been created', - InvalidEventType = 'Invalid event type', -} - -/** - * Represents error messages related to reputation. - */ -export enum ErrorReputation { - NotFound = 'Reputation not found', - NotCreated = 'Reputation has not been created', -} - -/** - * Represents error messages related to results. - */ -export enum ErrorResults { - IntermediateResultsURLNotSet = 'Intermediate results URL is not set', - NoIntermediateResultsFound = 'No intermediate results found', - NoResultsHaveBeenVerified = 'No results have been verified', - NotAllRequiredSolutionsHaveBeenSent = 'Not all required solutions have been sent', -} - -/** - * Represents error messages related to manifest. - */ -export enum ErrorManifest { - ManifestUrlDoesNotExist = 'Manifest url does not exist', - UnsupportedManifestType = 'Unsupported manifest type', -} - -/** - * Represents error messages related to signature. - */ -export enum ErrorSignature { - SignatureNotVerified = 'Signature not verified', - InvalidSignature = 'Invalid signature', -} - diff --git a/packages/apps/hufi/reputation-oracle/server/src/common/constants/index.ts b/packages/apps/hufi/reputation-oracle/server/src/common/constants/index.ts deleted file mode 100644 index 0139a80515..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/common/constants/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { JobRequestType } from '../enums'; - -export const SERVICE_NAME = 'Reputation Oracle'; -export const NS = 'hmt'; -export const RETRIES_COUNT_THRESHOLD = 3; -export const INITIAL_REPUTATION = 0; -export const JWT_PREFIX = 'bearer '; - -export const HEADER_SIGNATURE_KEY = 'human-signature'; diff --git a/packages/apps/hufi/reputation-oracle/server/src/common/decorators/index.ts b/packages/apps/hufi/reputation-oracle/server/src/common/decorators/index.ts deleted file mode 100644 index b7e8b7187d..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/common/decorators/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './public'; diff --git a/packages/apps/hufi/reputation-oracle/server/src/common/decorators/public.ts b/packages/apps/hufi/reputation-oracle/server/src/common/decorators/public.ts deleted file mode 100644 index a12eaafa09..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/common/decorators/public.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { SetMetadata } from '@nestjs/common'; - -export const Public = (): ((target: any, key?: any, descriptor?: any) => any) => - SetMetadata('isPublic', true); diff --git a/packages/apps/hufi/reputation-oracle/server/src/common/enums/collection.ts b/packages/apps/hufi/reputation-oracle/server/src/common/enums/collection.ts deleted file mode 100644 index cb05417818..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/common/enums/collection.ts +++ /dev/null @@ -1,4 +0,0 @@ -export enum SortDirection { - ASC = 'ASC', - DESC = 'DESC', -} diff --git a/packages/apps/hufi/reputation-oracle/server/src/common/enums/exchange.ts b/packages/apps/hufi/reputation-oracle/server/src/common/enums/exchange.ts deleted file mode 100644 index 3ea9700c8f..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/common/enums/exchange.ts +++ /dev/null @@ -1,6 +0,0 @@ -export enum Exchange { - UNISWAP_ETHEREUM = 'unispwap-ethereum', - UNISWAP_POLYGON = 'uniswap-polygon', - PANCAKESWAP_BSC = 'pancakeswap-bsc', - BINANCE = 'binance', -} diff --git a/packages/apps/hufi/reputation-oracle/server/src/common/enums/index.ts b/packages/apps/hufi/reputation-oracle/server/src/common/enums/index.ts deleted file mode 100644 index 547cdab667..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/common/enums/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './job'; -export * from './reputation'; -export * from './webhook'; -export * from './collection'; diff --git a/packages/apps/hufi/reputation-oracle/server/src/common/enums/job.ts b/packages/apps/hufi/reputation-oracle/server/src/common/enums/job.ts deleted file mode 100644 index f18b24b159..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/common/enums/job.ts +++ /dev/null @@ -1,3 +0,0 @@ -export enum JobRequestType { - CAMPAIGN='CAMPAIGN' -} \ No newline at end of file diff --git a/packages/apps/hufi/reputation-oracle/server/src/common/enums/reputation.ts b/packages/apps/hufi/reputation-oracle/server/src/common/enums/reputation.ts deleted file mode 100644 index 737bc0ff9e..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/common/enums/reputation.ts +++ /dev/null @@ -1,13 +0,0 @@ -export enum ReputationEntityType { - LIQUIDITY_PROVIDER = 'LIQUIDITY_PROVIDER', - JOB_LAUNCHER = 'JOB_LAUNCHER', - EXCHANGE_ORACLE = 'EXCHANGE_ORACLE', - RECORDING_ORACLE = 'RECORDING_ORACLE', - REPUTATION_ORACLE = 'REPUTATION_ORACLE', -} - -export enum ReputationLevel { - LOW = 'Low', - MEDIUM = 'Medium', - HIGH = 'High', -} diff --git a/packages/apps/hufi/reputation-oracle/server/src/common/enums/role.ts b/packages/apps/hufi/reputation-oracle/server/src/common/enums/role.ts deleted file mode 100644 index 0c9ab74fc3..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/common/enums/role.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum Role { - JobLaucher = 'job_launcher', - Exchange = 'exchange', - Recording = 'recording', -} diff --git a/packages/apps/hufi/reputation-oracle/server/src/common/enums/user.ts b/packages/apps/hufi/reputation-oracle/server/src/common/enums/user.ts deleted file mode 100644 index 83d98c0f92..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/common/enums/user.ts +++ /dev/null @@ -1,12 +0,0 @@ -export enum UserStatus { - ACTIVE = 'ACTIVE', - INACTIVE = 'INACTIVE', - PENDING = 'PENDING', -} - -export enum UserType { - OPERATOR = 'OPERATOR', - EXCHANGE_ORACLE = 'EXCHANGE_ORACLE', - RECORDING_ORACLE = 'RECORDING_ORACLE', - WORKER = 'WORKER', -} diff --git a/packages/apps/hufi/reputation-oracle/server/src/common/enums/webhook.ts b/packages/apps/hufi/reputation-oracle/server/src/common/enums/webhook.ts deleted file mode 100644 index f0475ca991..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/common/enums/webhook.ts +++ /dev/null @@ -1,15 +0,0 @@ -export enum EventType { - CAMPAIGN_PAYOUT = 'campaign_payout' -} - -export enum WebhookStatus { - PENDING = 'PENDING', - COMPLETED = 'COMPLETED', - FAILED = 'FAILED', - PAID = 'PAID', -} - -export enum OracleType { - FORTUNE = 'fortune', - CVAT = 'cvat', -} \ No newline at end of file diff --git a/packages/apps/hufi/reputation-oracle/server/src/common/guards/index.ts b/packages/apps/hufi/reputation-oracle/server/src/common/guards/index.ts deleted file mode 100644 index 7fe1e3d657..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/common/guards/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './signature.auth'; -export * from './jwt.auth'; diff --git a/packages/apps/hufi/reputation-oracle/server/src/common/guards/jwt.auth.ts b/packages/apps/hufi/reputation-oracle/server/src/common/guards/jwt.auth.ts deleted file mode 100644 index 6274d5eed7..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/common/guards/jwt.auth.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { - CanActivate, - ExecutionContext, - Injectable, - UnauthorizedException, -} from '@nestjs/common'; -import { Reflector } from '@nestjs/core'; -import { AuthGuard } from '@nestjs/passport'; - -@Injectable() -export class JwtAuthGuard extends AuthGuard('jwt-http') implements CanActivate { - constructor(private readonly reflector: Reflector) { - super(); - } - - public async canActivate(context: ExecutionContext): Promise { - // `super` has to be called to set `user` on `request` - // see https://github.com/nestjs/passport/blob/master/lib/auth.guard.ts - return (super.canActivate(context) as Promise).catch((e) => { - const isPublic = this.reflector.getAllAndOverride('isPublic', [ - context.getHandler(), - context.getClass(), - ]); - - if (isPublic) { - return true; - } - - console.error(e); - throw new UnauthorizedException('Unauthorized'); - }); - } -} diff --git a/packages/apps/hufi/reputation-oracle/server/src/common/guards/signature.auth.spec.ts b/packages/apps/hufi/reputation-oracle/server/src/common/guards/signature.auth.spec.ts deleted file mode 100644 index dc9983d55b..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/common/guards/signature.auth.spec.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { ExecutionContext, UnauthorizedException } from '@nestjs/common'; -import { SignatureAuthGuard } from './signature.auth'; -import { verifySignature } from '../utils/signature'; -import { ChainId, EscrowUtils } from '@human-protocol/sdk'; -import { MOCK_ADDRESS } from '../../../test/constants'; -import { Role } from '../enums/role'; - -jest.mock('../../common/utils/signature'); - -jest.mock('@human-protocol/sdk', () => ({ - ...jest.requireActual('@human-protocol/sdk'), - EscrowUtils: { - getEscrow: jest.fn().mockResolvedValue({ - launcher: '0x1234567890123456789012345678901234567890', - exchangeOracle: '0x1234567890123456789012345678901234567891', - reputationOracle: '0x1234567890123456789012345678901234567892', - }), - }, -})); - -describe('SignatureAuthGuard', () => { - let guard: SignatureAuthGuard; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - { - provide: SignatureAuthGuard, - useValue: new SignatureAuthGuard([ - Role.JobLaucher, - Role.Exchange, - Role.Recording, - ]), - }, - ], - }).compile(); - - guard = module.get(SignatureAuthGuard); - }); - - it('should be defined', () => { - expect(guard).toBeDefined(); - }); - - describe('canActivate', () => { - let context: ExecutionContext; - let mockRequest: any; - - beforeEach(() => { - mockRequest = { - switchToHttp: jest.fn().mockReturnThis(), - getRequest: jest.fn().mockReturnThis(), - headers: {}, - body: {}, - originalUrl: '', - }; - context = { - switchToHttp: jest.fn().mockReturnThis(), - getRequest: jest.fn(() => mockRequest), - } as any as ExecutionContext; - }); - - it('should return true if signature is verified', async () => { - mockRequest.headers['header-signature-key'] = 'validSignature'; - mockRequest.body = { - escrowAddress: MOCK_ADDRESS, - chainId: ChainId.LOCALHOST, - }; - (verifySignature as jest.Mock).mockReturnValue(true); - - const result = await guard.canActivate(context as any); - expect(result).toBeTruthy(); - expect(EscrowUtils.getEscrow).toHaveBeenCalledWith( - ChainId.LOCALHOST, - MOCK_ADDRESS, - ); - }); - - it('should throw unauthorized exception if signature is not verified', async () => { - (verifySignature as jest.Mock).mockReturnValue(false); - - await expect(guard.canActivate(context as any)).rejects.toThrow( - UnauthorizedException, - ); - }); - - it('should throw unauthorized exception for unrecognized oracle type', async () => { - mockRequest.originalUrl = '/some/random/path'; - await expect(guard.canActivate(context as any)).rejects.toThrow( - UnauthorizedException, - ); - }); - }); -}); diff --git a/packages/apps/hufi/reputation-oracle/server/src/common/guards/signature.auth.ts b/packages/apps/hufi/reputation-oracle/server/src/common/guards/signature.auth.ts deleted file mode 100644 index d65ae9d565..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/common/guards/signature.auth.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { - CanActivate, - ExecutionContext, - Injectable, - UnauthorizedException, -} from '@nestjs/common'; -import { verifySignature } from '../utils/signature'; -import { HEADER_SIGNATURE_KEY } from '../constants'; -import { EscrowUtils } from '@human-protocol/sdk'; -import { Role } from '../enums/role'; - -@Injectable() -export class SignatureAuthGuard implements CanActivate { - constructor(private role: Role[]) {} - - public async canActivate(context: ExecutionContext): Promise { - const request = context.switchToHttp().getRequest(); - - const data = request.body; - const signature = request.headers[HEADER_SIGNATURE_KEY]; - const oracleAdresses: string[] = []; - try { - const escrowData = await EscrowUtils.getEscrow( - data.chainId, - data.escrowAddress, - ); - if (this.role.includes(Role.JobLaucher)) - oracleAdresses.push(escrowData.launcher); - if (this.role.includes(Role.Exchange)) - oracleAdresses.push(escrowData.exchangeOracle!); - if (this.role.includes(Role.Recording)) - oracleAdresses.push(escrowData.recordingOracle!); - - const isVerified = verifySignature(data, signature, oracleAdresses); - - if (isVerified) { - return true; - } - } catch (error) { - console.error(error); - } - - throw new UnauthorizedException('Unauthorized'); - } -} diff --git a/packages/apps/hufi/reputation-oracle/server/src/common/interceptors/index.ts b/packages/apps/hufi/reputation-oracle/server/src/common/interceptors/index.ts deleted file mode 100644 index 0de137a601..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/common/interceptors/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './not-found'; diff --git a/packages/apps/hufi/reputation-oracle/server/src/common/interceptors/not-found.ts b/packages/apps/hufi/reputation-oracle/server/src/common/interceptors/not-found.ts deleted file mode 100644 index e1063855fc..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/common/interceptors/not-found.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Observable } from 'rxjs'; -import { tap } from 'rxjs/operators'; -import { - ExecutionContext, - Injectable, - NestInterceptor, - NotFoundException, - CallHandler, -} from '@nestjs/common'; - -@Injectable() -export class NotFoundInterceptor implements NestInterceptor { - intercept(_context: ExecutionContext, next: CallHandler): Observable { - return next.handle().pipe( - tap((data) => { - if (data === void 0 || data === null) { - throw new NotFoundException(); - } - }), - ); - } -} diff --git a/packages/apps/hufi/reputation-oracle/server/src/common/interfaces/base.ts b/packages/apps/hufi/reputation-oracle/server/src/common/interfaces/base.ts deleted file mode 100644 index 8ad596c4d9..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/common/interfaces/base.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface IBase { - id: number; - createdAt: Date; - updatedAt: Date; -} diff --git a/packages/apps/hufi/reputation-oracle/server/src/common/interfaces/index.ts b/packages/apps/hufi/reputation-oracle/server/src/common/interfaces/index.ts deleted file mode 100644 index 6befa5f6d9..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/common/interfaces/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './base'; -export * from './reputation'; -export * from './user'; diff --git a/packages/apps/hufi/reputation-oracle/server/src/common/interfaces/manifest.ts b/packages/apps/hufi/reputation-oracle/server/src/common/interfaces/manifest.ts deleted file mode 100644 index 27010be092..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/common/interfaces/manifest.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { JobRequestType } from '../enums'; - -export interface IFortuneManifest { - submissionsRequired: number; - requesterTitle: string; - requesterDescription: string; - fee: string; - fundAmount: string; - requestType: JobRequestType; -} - -export interface ICvatManifest { - dataUrl: string; - labels: string[]; - submissionsRequired: number; - requesterDescription: string; - requesterAccuracyTarget: number; - fee: string; - fundAmount: string; - requestType: JobRequestType; -} - -export type Manifest = IFortuneManifest | ICvatManifest; diff --git a/packages/apps/hufi/reputation-oracle/server/src/common/interfaces/reputation.ts b/packages/apps/hufi/reputation-oracle/server/src/common/interfaces/reputation.ts deleted file mode 100644 index d66b841c3f..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/common/interfaces/reputation.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { ReputationLevel } from '../enums/reputation'; - -export interface IReputation { - chainId: number; - address: string; - reputation: ReputationLevel; -} diff --git a/packages/apps/hufi/reputation-oracle/server/src/common/interfaces/s3.ts b/packages/apps/hufi/reputation-oracle/server/src/common/interfaces/s3.ts deleted file mode 100644 index 81e92ae447..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/common/interfaces/s3.ts +++ /dev/null @@ -1,4 +0,0 @@ -export class UploadedFile { - public url: string; - public hash: string; -} diff --git a/packages/apps/hufi/reputation-oracle/server/src/common/interfaces/user.ts b/packages/apps/hufi/reputation-oracle/server/src/common/interfaces/user.ts deleted file mode 100644 index cbddf4425d..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/common/interfaces/user.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { UserStatus, UserType } from '../enums/user'; -import { IBase } from './base'; - -export interface IUser extends IBase { - password: string; - email: string; - status: UserStatus; - type: UserType; -} diff --git a/packages/apps/hufi/reputation-oracle/server/src/common/pipes/index.ts b/packages/apps/hufi/reputation-oracle/server/src/common/pipes/index.ts deleted file mode 100644 index 4d5ffa36ab..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/common/pipes/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './validation'; diff --git a/packages/apps/hufi/reputation-oracle/server/src/common/pipes/validation.ts b/packages/apps/hufi/reputation-oracle/server/src/common/pipes/validation.ts deleted file mode 100644 index f46e742df2..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/common/pipes/validation.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { - BadRequestException, - Injectable, - ValidationError, - ValidationPipe, - ValidationPipeOptions, -} from '@nestjs/common'; - -@Injectable() -export class HttpValidationPipe extends ValidationPipe { - constructor(options?: ValidationPipeOptions) { - super({ - exceptionFactory: (errors: ValidationError[]): BadRequestException => - new BadRequestException(errors), - transform: true, - whitelist: true, - forbidNonWhitelisted: true, - forbidUnknownValues: true, - ...options, - }); - } -} diff --git a/packages/apps/hufi/reputation-oracle/server/src/common/types/index.ts b/packages/apps/hufi/reputation-oracle/server/src/common/types/index.ts deleted file mode 100644 index 56e4b0555f..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/common/types/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './request'; diff --git a/packages/apps/hufi/reputation-oracle/server/src/common/types/request.ts b/packages/apps/hufi/reputation-oracle/server/src/common/types/request.ts deleted file mode 100644 index 15b2a7ed78..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/common/types/request.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface RequestWithUser extends Request { - user: any; -} diff --git a/packages/apps/hufi/reputation-oracle/server/src/common/utils/index.ts b/packages/apps/hufi/reputation-oracle/server/src/common/utils/index.ts deleted file mode 100644 index 5dfad7f3a1..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/common/utils/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -import * as crypto from 'crypto'; -import { Readable } from 'stream'; - -export function hashStream(stream: Readable): Promise { - return new Promise((resolve, reject) => { - const hash = crypto.createHash('sha1'); - - stream.on('data', (chunk) => { - hash.update(chunk); - }); - - stream.on('end', () => { - resolve(hash.digest('hex')); - }); - - stream.on('error', (error) => { - reject(error); - }); - }); -} diff --git a/packages/apps/hufi/reputation-oracle/server/src/common/utils/signature.spec.ts b/packages/apps/hufi/reputation-oracle/server/src/common/utils/signature.spec.ts deleted file mode 100644 index b7ed7e4255..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/common/utils/signature.spec.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { verifySignature, recoverSigner, signMessage } from './signature'; -import { MOCK_ADDRESS, MOCK_PRIVATE_KEY } from '../../../test/constants'; -import { ErrorSignature } from '../constants/errors'; - -jest.doMock('ethers', () => { - return { - utils: { - get verifyMessage() { - return jest.fn((message, signature) => { - if (message === 'valid-message' && signature === 'valid-signature') { - return 'recovered-address'; - } else { - throw new Error('Invalid signature'); - } - }); - }, - }, - }; -}); - -describe('Signature utility', () => { - describe('verifySignature', () => { - it('should return true for valid signature', async () => { - const message = 'Hello, this is a signed message!'; - const signature = await signMessage(message, MOCK_PRIVATE_KEY); - - const result = verifySignature(message, signature, [MOCK_ADDRESS]); - - expect(result).toBe(true); - }); - - it('should throw conflict exception for signature not verified', async () => { - const message = 'Hello, this is a signed message!'; - - const invalidSignature = await signMessage(message, MOCK_PRIVATE_KEY); - const invalidAddress = '0x1234567890123456789012345678901234567892'; - - expect(() => { - verifySignature(message, invalidSignature, [invalidAddress]); - }).toThrow(ErrorSignature.SignatureNotVerified); - }); - - it('should throw conflict exception for invalid signature', () => { - const message = 'Hello, this is a signed message!'; - const invalidSignature = '0xInvalidSignature'; - - expect(() => { - verifySignature(message, invalidSignature, [MOCK_ADDRESS]); - }).toThrow(ErrorSignature.InvalidSignature); - }); - }); - - describe('recoverSigner', () => { - it('should recover the correct signer', async () => { - const message = 'value'; - const signature = await signMessage(message, MOCK_PRIVATE_KEY); - - const result = recoverSigner(message, signature); - - expect(result).toBe(MOCK_ADDRESS); - }); - - it('should throw conflict exception for invalid signature', () => { - const message = 'Hello, this is a signed message!'; - const invalidSignature = '0xInvalidSignature'; - - expect(() => { - recoverSigner(message, invalidSignature); - }).toThrow(ErrorSignature.InvalidSignature); - }); - - it('should stringify message object if it is not already a string', async () => { - const message = { key: 'value' }; - const signature = await signMessage(message, MOCK_PRIVATE_KEY); - - const recoveredAddress = recoverSigner(message, signature); - - expect(recoveredAddress).toBe(MOCK_ADDRESS); - }); - - it('should not stringify message if it is already a string', async () => { - const message = 'valid message'; - const signature = await signMessage(message, MOCK_PRIVATE_KEY); - - const recoveredAddress = recoverSigner(message, signature); - - expect(recoveredAddress).toBe(MOCK_ADDRESS); - }); - }); - - describe('signMessage', () => { - it('should return a valid signature', async () => { - const message = 'Hello, this is a test message'; - const signature = await signMessage(message, MOCK_PRIVATE_KEY); - - expect(signature).toBeDefined(); - }); - - it('should stringify message object if it is not already a string', async () => { - const message = { key: 'value' }; - const signature = await signMessage(message, MOCK_PRIVATE_KEY); - - expect(signature).toBeDefined(); - }); - - it('should not stringify message if it is already a string', async () => { - const message = 'valid message'; - const signature = await signMessage(message, MOCK_PRIVATE_KEY); - - expect(signature).toBeDefined(); - }); - }); -}); diff --git a/packages/apps/hufi/reputation-oracle/server/src/common/utils/signature.ts b/packages/apps/hufi/reputation-oracle/server/src/common/utils/signature.ts deleted file mode 100644 index c619886dcb..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/common/utils/signature.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { ConflictException } from '@nestjs/common'; -import { ethers } from 'ethers'; -import { ErrorSignature } from '../constants/errors'; - -export function verifySignature( - message: object | string, - signature: string, - addresses: string[], -): boolean { - const signer = recoverSigner(message, signature); - - if ( - !addresses.some((address) => address.toLowerCase() === signer.toLowerCase()) - ) { - throw new ConflictException(ErrorSignature.SignatureNotVerified); - } - - return true; -} - -export async function signMessage( - message: object | string, - privateKey: string, -): Promise { - if (typeof message !== 'string') { - message = JSON.stringify(message); - } - - const wallet = new ethers.Wallet(privateKey); - const signature = await wallet.signMessage(message); - - return signature; -} - -export function recoverSigner( - message: object | string, - signature: string, -): string { - if (typeof message !== 'string') { - message = JSON.stringify(message); - } - - try { - return ethers.verifyMessage(message, signature); - } catch (e) { - throw new ConflictException(ErrorSignature.InvalidSignature); - } -} diff --git a/packages/apps/hufi/reputation-oracle/server/src/common/validators/confirm.ts b/packages/apps/hufi/reputation-oracle/server/src/common/validators/confirm.ts deleted file mode 100644 index d7f54c87bd..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/common/validators/confirm.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { - registerDecorator, - ValidationArguments, - ValidationOptions, - ValidatorConstraint, - ValidatorConstraintInterface, -} from 'class-validator'; - -interface IConfirmConstraints { - required: boolean; - relatedPropertyName: string; -} - -@ValidatorConstraint() -class ValidateConfirm implements ValidatorConstraintInterface { - private reason: string; - - public validate(value: unknown, args: ValidationArguments): boolean { - this.reason = ValidateConfirm.isValid(value, args); - return !this.reason; - } - - public defaultMessage(): string { - return this.reason; - } - - private static isValid(value: unknown, args: ValidationArguments): string { - const { relatedPropertyName = 'password' }: IConfirmConstraints = - args.constraints[0]; - - const relatedValue = (args.object as any)[relatedPropertyName]; - - if (typeof value === 'undefined' || value === '') { - if (relatedValue) { - return 'valueMissing'; - } else { - return ''; - } - } - - if (typeof value !== 'string') { - return 'typeMismatch'; - } - - if (relatedValue !== value) { - return 'badInput'; - } - - return ''; - } -} - -export function IsConfirm( - constraints: Partial = {}, - validationOptions?: ValidationOptions, -) { - return (object: Record, propertyName: string): void => { - registerDecorator({ - name: 'isConfirm', - target: object.constructor, - propertyName, - constraints: [constraints], - options: validationOptions, - validator: ValidateConfirm, - }); - }; -} diff --git a/packages/apps/hufi/reputation-oracle/server/src/common/validators/index.ts b/packages/apps/hufi/reputation-oracle/server/src/common/validators/index.ts deleted file mode 100644 index 4bf70483ee..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/common/validators/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './password'; -export * from './confirm'; diff --git a/packages/apps/hufi/reputation-oracle/server/src/common/validators/password.ts b/packages/apps/hufi/reputation-oracle/server/src/common/validators/password.ts deleted file mode 100644 index edea4cdd17..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/common/validators/password.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { - registerDecorator, - ValidationArguments, - ValidationOptions, - ValidatorConstraint, - ValidatorConstraintInterface, -} from 'class-validator'; -import zxcvbn from 'zxcvbn'; - -interface IPasswordConstraints { - required: boolean; - score: number; -} - -@ValidatorConstraint() -class ValidatePassword implements ValidatorConstraintInterface { - private reason: string; - - public validate(value: unknown, args: ValidationArguments): boolean { - this.reason = ValidatePassword.isValid(value, args); - return !this.reason; - } - - public defaultMessage(): string { - return this.reason; - } - - private static isValid(value: unknown, args: ValidationArguments): string { - const { required = true, score = 0 }: IPasswordConstraints = - args.constraints[0]; - - if (typeof value === 'undefined' || value === '') { - if (required) { - return 'valueMissing'; - } else { - return ''; - } - } - - if (typeof value !== 'string') { - return 'typeMismatch'; - } - - if (zxcvbn(value).score < score) { - return 'weak'; - } - - return ''; - } -} - -export function IsPassword( - constraints: Partial = {}, - validationOptions?: ValidationOptions, -) { - return (object: Record, propertyName: string): void => { - registerDecorator({ - name: 'isPassword', - target: object.constructor, - propertyName, - constraints: [constraints], - options: validationOptions, - validator: ValidatePassword, - }); - }; -} diff --git a/packages/apps/hufi/reputation-oracle/server/src/database/base.entity.ts b/packages/apps/hufi/reputation-oracle/server/src/database/base.entity.ts deleted file mode 100644 index 963505429c..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/database/base.entity.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { - BaseEntity as OrmBaseEntity, - BeforeInsert, - BeforeUpdate, - Column, - PrimaryGeneratedColumn, -} from 'typeorm'; - -export abstract class BaseEntity extends OrmBaseEntity { - @PrimaryGeneratedColumn() - public id: number; - - @Column({ type: 'timestamptz' }) - public createdAt: Date; - - @Column({ type: 'timestamptz' }) - public updatedAt: Date; - - @BeforeInsert() - public beforeInsert(): void { - const date = new Date(); - this.createdAt = date; - this.updatedAt = date; - } - - @BeforeUpdate() - public beforeUpdate(): void { - const date = new Date(); - this.updatedAt = date; - } -} diff --git a/packages/apps/hufi/reputation-oracle/server/src/database/database.module.ts b/packages/apps/hufi/reputation-oracle/server/src/database/database.module.ts deleted file mode 100644 index b0f50c7f6c..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/database/database.module.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { Module } from '@nestjs/common'; -import { ConfigModule, ConfigService } from '@nestjs/config'; -import { TypeOrmModule } from '@nestjs/typeorm'; -import * as path from 'path'; -import { SnakeNamingStrategy } from 'typeorm-naming-strategies'; -import { NS } from '../common/constants'; - -import { TypeOrmLoggerModule, TypeOrmLoggerService } from './typeorm'; -import { WebhookIncomingEntity } from '../modules/webhook/webhook-incoming.entity'; -import { ReputationEntity } from '../modules/reputation/reputation.entity'; - -@Module({ - imports: [ - TypeOrmModule.forRootAsync({ - imports: [TypeOrmLoggerModule, ConfigModule], - inject: [TypeOrmLoggerService, ConfigService], - useFactory: ( - typeOrmLoggerService: TypeOrmLoggerService, - configService: ConfigService, - ) => { - typeOrmLoggerService.setOptions('all'); - return { - name: 'default', - type: 'postgres', - entities: [ - WebhookIncomingEntity, - ReputationEntity, - ], - // We are using migrations, synchronize should be set to false. - synchronize: false, - // Run migrations automatically, - // you can disable this if you prefer running migration manually. - migrationsTableName: NS, - migrationsTransactionMode: 'each', - namingStrategy: new SnakeNamingStrategy(), - logging: - process.env.NODE_ENV === 'development' || - process.env.NODE_ENV === 'staging', - // Allow both start:prod and start:dev to use migrations - // __dirname is either dist or server folder, meaning either - // the compiled js in prod or the ts in dev. - migrations: [path.join(__dirname, '/migrations/**/*{.ts,.js}')], - //"migrations": ["dist/migrations/*{.ts,.js}"], - logger: typeOrmLoggerService, - host: configService.get('POSTGRES_HOST', 'localhost'), - port: configService.get('POSTGRES_PORT', 5432), - username: configService.get('POSTGRES_USER', 'operator'), - password: configService.get('POSTGRES_PASSWORD', 'qwerty'), - database: configService.get( - 'POSTGRES_DATABASE', - 'reputation-oracle', - ), - keepConnectionAlive: configService.get('NODE_ENV') === 'test', - migrationsRun: false, - ssl: - configService.get('POSTGRES_SSL')!.toLowerCase() === 'true', - }; - }, - }), - ], -}) -export class DatabaseModule {} diff --git a/packages/apps/hufi/reputation-oracle/server/src/database/migrations/1698914497531-InitialMigration.ts b/packages/apps/hufi/reputation-oracle/server/src/database/migrations/1698914497531-InitialMigration.ts deleted file mode 100644 index a181d90fad..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/database/migrations/1698914497531-InitialMigration.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { NS } from '../../common/constants'; -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class InitialMigration1698914497531 implements MigrationInterface { - name = 'InitialMigration1698914497531'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.createSchema(NS, true); - await queryRunner.query( - `CREATE TYPE "hmt"."reputation_type_enum" AS ENUM('LIQUIDITY_PROVIDER', 'JOB_LAUNCHER', 'EXCHANGE_ORACLE', 'RECORDING_ORACLE', 'REPUTATION_ORACLE')`, - ); - await queryRunner.query( - `CREATE TABLE "hmt"."reputation" ("id" SERIAL NOT NULL, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL, "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL, "chain_id" integer NOT NULL, "address" character varying NOT NULL, "reputation_points" integer NOT NULL, "type" "hmt"."reputation_type_enum" NOT NULL, CONSTRAINT "PK_640807583e8622e1d9bbe6f1b7b" PRIMARY KEY ("id"))`, - ); - await queryRunner.query( - `CREATE TYPE "hmt"."webhook_incoming_status_enum" AS ENUM('PENDING', 'COMPLETED', 'FAILED', 'PAID')`, - ); - await queryRunner.query( - `CREATE TABLE "hmt"."webhook_incoming" ("id" SERIAL NOT NULL, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL, "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL, "chain_id" integer NOT NULL, "oracle_address" character varying, "escrow_address" character varying NOT NULL, "results_url" character varying, "check_passed" boolean, "retries_count" integer NOT NULL, "wait_until" TIMESTAMP WITH TIME ZONE NOT NULL, "status" "hmt"."webhook_incoming_status_enum" NOT NULL, CONSTRAINT "PK_08e16abccb4720323203bf8f7a0" PRIMARY KEY ("id"))`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE "hmt"."webhook_incoming"`); - await queryRunner.query(`DROP TYPE "hmt"."webhook_incoming_status_enum"`); - await queryRunner.query(`DROP TABLE "hmt"."reputation"`); - await queryRunner.query(`DROP TYPE "hmt"."reputation_type_enum"`); - await queryRunner.dropSchema(NS); - } -} diff --git a/packages/apps/hufi/reputation-oracle/server/src/database/typeorm/index.ts b/packages/apps/hufi/reputation-oracle/server/src/database/typeorm/index.ts deleted file mode 100644 index 5a07b7c166..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/database/typeorm/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './typeorm-logger.service'; -export * from './typeorm-logger.module'; diff --git a/packages/apps/hufi/reputation-oracle/server/src/database/typeorm/typeorm-logger.module.ts b/packages/apps/hufi/reputation-oracle/server/src/database/typeorm/typeorm-logger.module.ts deleted file mode 100644 index b3bc43d760..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/database/typeorm/typeorm-logger.module.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Module, Logger } from '@nestjs/common'; - -import { TypeOrmLoggerService } from './typeorm-logger.service'; - -@Module({ - providers: [Logger, TypeOrmLoggerService], - exports: [TypeOrmLoggerService], -}) -export class TypeOrmLoggerModule {} diff --git a/packages/apps/hufi/reputation-oracle/server/src/database/typeorm/typeorm-logger.service.ts b/packages/apps/hufi/reputation-oracle/server/src/database/typeorm/typeorm-logger.service.ts deleted file mode 100644 index f6e47ed3b4..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/database/typeorm/typeorm-logger.service.ts +++ /dev/null @@ -1,97 +0,0 @@ -import * as util from 'util'; -import { - Logger as TypeOrmLogger, - LoggerOptions as TypeOrmLoggerOptions, -} from 'typeorm'; -import { Inject, Injectable, Logger, LoggerService } from '@nestjs/common'; - -@Injectable() -export class TypeOrmLoggerService implements TypeOrmLogger { - private options: TypeOrmLoggerOptions = 'all'; - - constructor(@Inject(Logger) private readonly loggerService: LoggerService) {} - - public setOptions(options: TypeOrmLoggerOptions = 'all'): void { - this.options = options; - } - - logQuery(query: string, parameters?: any[]): void { - if ( - this.options === 'all' || - this.options === true || - (this.options instanceof Array && this.options.indexOf('query') !== -1) - ) { - this.loggerService.log( - `query : ${query} ${this.stringifyParams(parameters)}`, - 'TypeOrm', - ); - } - } - - logQueryError(error: string, query: string, parameters?: any[]): void { - if ( - this.options === 'all' || - this.options === true || - (this.options instanceof Array && this.options.indexOf('error') !== -1) - ) { - this.loggerService.log( - `query failed: ${query} ${this.stringifyParams(parameters)}`, - 'TypeOrm', - ); - this.loggerService.log(`error: ${error}`, 'TypeOrm'); - } - } - - logQuerySlow(time: number, query: string, parameters?: any[]): void { - this.loggerService.log( - `query is slow: ${query} ${this.stringifyParams(parameters)}`, - 'TypeOrm', - ); - this.loggerService.log(`execution time: ${time}`, 'TypeOrm'); - } - - logSchemaBuild(message: string): void { - if ( - this.options === 'all' || - (this.options instanceof Array && this.options.indexOf('schema') !== -1) - ) { - this.loggerService.log(message, 'TypeOrm'); - } - } - - logMigration(message: string): void { - this.loggerService.log(message, 'TypeOrm'); - } - - log(level: 'log' | 'info' | 'warn', message: unknown): void { - switch (level) { - case 'log': - if ( - this.options === 'all' || - (this.options instanceof Array && this.options.indexOf('log') !== -1) - ) - this.loggerService.log(message, 'TypeOrm'); - break; - case 'info': - if ( - this.options === 'all' || - (this.options instanceof Array && this.options.indexOf('info') !== -1) - ) - this.loggerService.log(message, 'TypeOrm'); - break; - case 'warn': - if ( - this.options === 'all' || - (this.options instanceof Array && this.options.indexOf('warn') !== -1) - ) - this.loggerService.warn(message, 'TypeOrm'); - break; - } - } - - protected stringifyParams(parameters: any[] = []): string { - return parameters.length - ? ` -- PARAMETERS: ${util.inspect(parameters)}` - : ''; - } -} diff --git a/packages/apps/hufi/reputation-oracle/server/src/main.ts b/packages/apps/hufi/reputation-oracle/server/src/main.ts deleted file mode 100644 index b3f153f6dd..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/main.ts +++ /dev/null @@ -1,63 +0,0 @@ -import session from 'express-session'; -import { NestFactory } from '@nestjs/core'; -import { ConfigService } from '@nestjs/config'; -import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; -import { json, urlencoded } from 'body-parser'; -import { useContainer } from 'class-validator'; -import helmet from 'helmet'; -import cookieParser from 'cookie-parser'; - -import { AppModule } from './app.module'; -import { INestApplication } from '@nestjs/common'; - -async function bootstrap() { - const app = await NestFactory.create(AppModule, { - cors: true, - }); - - const configService: ConfigService = app.get(ConfigService); - - app.enableCors({ - credentials: true, - exposedHeaders: ['Content-Disposition'], - }); - - useContainer(app.select(AppModule), { fallbackOnErrors: true }); - - app.use(cookieParser()); - - const sessionSecret = configService.get('SESSION_SECRET', ''); - - app.use( - session({ - secret: sessionSecret, - resave: false, - saveUninitialized: false, - cookie: { - secure: true, - }, - }), - ); - app.use(json({ limit: '5mb' })); - app.use(urlencoded({ limit: '5mb', extended: true })); - - const config = new DocumentBuilder() - .addBearerAuth() - .setTitle('Reputation Oracle API') - .setDescription('Swagger Reputation Oracle API') - .setVersion('1.0') - .build(); - const document = SwaggerModule.createDocument(app, config); - SwaggerModule.setup('swagger', app, document); - - const host = configService.get('HOST', 'localhost'); - const port = configService.get('PORT', '5000'); - - app.use(helmet()); - - await app.listen(port, host, async () => { - console.info(`API server is running on http://${host}:${port}`); - }); -} - -void bootstrap(); diff --git a/packages/apps/hufi/reputation-oracle/server/src/modules/health/health.controller.ts b/packages/apps/hufi/reputation-oracle/server/src/modules/health/health.controller.ts deleted file mode 100644 index 4f73ef28af..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/modules/health/health.controller.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Controller, Get } from '@nestjs/common'; -import { - HealthCheck, - HealthCheckResult, - HealthCheckService, - HealthIndicatorResult, - TypeOrmHealthIndicator, -} from '@nestjs/terminus'; -import { Public } from '../../common/decorators'; -import { ApiTags } from '@nestjs/swagger'; - -@Public() -@ApiTags('Health') -@Controller('/health') -export class HealthController { - constructor( - private readonly health: HealthCheckService, - private readonly db: TypeOrmHealthIndicator, - ) {} - - @Get() - @HealthCheck() - readiness(): Promise { - return this.health.check([ - async (): Promise => - this.db.pingCheck('database', { - timeout: 5000, - }), - ]); - } -} diff --git a/packages/apps/hufi/reputation-oracle/server/src/modules/health/health.module.ts b/packages/apps/hufi/reputation-oracle/server/src/modules/health/health.module.ts deleted file mode 100644 index 9455cee23b..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/modules/health/health.module.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Module } from '@nestjs/common'; -import { TerminusModule } from '@nestjs/terminus'; -import { ConfigModule } from '@nestjs/config'; - -import { HealthController } from './health.controller'; - -@Module({ - imports: [TerminusModule, ConfigModule], - controllers: [HealthController], -}) -export class HealthModule {} diff --git a/packages/apps/hufi/reputation-oracle/server/src/modules/reputation/reputation.controller.spec.ts b/packages/apps/hufi/reputation-oracle/server/src/modules/reputation/reputation.controller.spec.ts deleted file mode 100644 index 0de5159ad6..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/modules/reputation/reputation.controller.spec.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { ConfigService } from '@nestjs/config'; -import { Test } from '@nestjs/testing'; - -import { ReputationController } from './reputation.controller'; -import { ReputationService } from './reputation.service'; -import { ReputationRepository } from './reputation.repository'; -import { ReputationLevel } from '../../common/enums'; -import { ConfigNames } from '../../common/config'; -import { IReputation } from '../../common/interfaces'; - -const OPERATOR_ADDRESS = 'TEST_OPERATOR_ADDRESS'; -const CHAIN_ID = 1; - -describe('ReputationController', () => { - let reputationController: ReputationController; - let reputationService: ReputationService; - - beforeAll(async () => { - const mockConfigService: Partial = { - get: jest.fn((key: string) => { - switch (key) { - case ConfigNames.REPUTATION_LEVEL_LOW: - return 300; - case ConfigNames.REPUTATION_LEVEL_LOW: - return 700; - } - }), - }; - - const moduleRef = await Test.createTestingModule({ - providers: [ - ReputationService, - { - provide: ReputationRepository, - useValue: { - find: jest.fn(), - findOne: jest.fn(), - }, - }, - { - provide: ConfigService, - useValue: mockConfigService, - }, - ], - }).compile(); - - reputationService = moduleRef.get(ReputationService); - reputationController = new ReputationController(reputationService); - }); - - describe('getReputations', () => { - it('should call service for given chainId', async () => { - const results = [ - { - chainId: CHAIN_ID, - address: OPERATOR_ADDRESS, - reputation: ReputationLevel.LOW, - }, - ]; - - jest - .spyOn(reputationService, 'getAllReputations') - .mockResolvedValueOnce(results as IReputation[]); - - jest - .spyOn(reputationService, 'getReputationLevel') - .mockReturnValueOnce(ReputationLevel.LOW); - - expect( - await reputationController.getReputations({ chainId: CHAIN_ID }), - ).toEqual(results); - }); - }); - - describe('getReputation', () => { - it('should call service', async () => { - const result = { - chainId: CHAIN_ID, - address: OPERATOR_ADDRESS, - reputation: ReputationLevel.LOW, - }; - - jest - .spyOn(reputationService, 'getReputation') - .mockResolvedValueOnce(result as IReputation); - - jest - .spyOn(reputationService, 'getReputationLevel') - .mockReturnValueOnce(ReputationLevel.LOW); - - expect( - await reputationController.getReputation( - { - address: OPERATOR_ADDRESS, - }, - { - chainId: CHAIN_ID, - }, - ), - ).toBe(result); - }); - }); -}); diff --git a/packages/apps/hufi/reputation-oracle/server/src/modules/reputation/reputation.controller.ts b/packages/apps/hufi/reputation-oracle/server/src/modules/reputation/reputation.controller.ts deleted file mode 100644 index f90736b23b..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/modules/reputation/reputation.controller.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Controller, Get, Param, Query } from '@nestjs/common'; -import { ApiTags } from '@nestjs/swagger'; -import { Public } from '../../common/decorators'; -import { ReputationService } from './reputation.service'; -import { - ReputationGetAllQueryDto, - ReputationGetParamsDto, - ReputationGetQueryDto, -} from './reputation.dto'; -import { IReputation } from '../../common/interfaces'; - -@Public() -@ApiTags('Reputation') -@Controller('reputation') -export class ReputationController { - constructor(private readonly reputationService: ReputationService) {} - - @Get() - public async getReputations( - @Query() query: ReputationGetAllQueryDto, - ): Promise { - const { chainId } = query; - return this.reputationService.getAllReputations(chainId); - } - - @Get('/:address') - public async getReputation( - @Param() params: ReputationGetParamsDto, - @Query() query: ReputationGetQueryDto, - ): Promise { - const { chainId } = query; - const { address } = params; - - return this.reputationService.getReputation(chainId, address); - } -} diff --git a/packages/apps/hufi/reputation-oracle/server/src/modules/reputation/reputation.dto.ts b/packages/apps/hufi/reputation-oracle/server/src/modules/reputation/reputation.dto.ts deleted file mode 100644 index 7b8799acf5..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/modules/reputation/reputation.dto.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { - IsEnum, - IsEthereumAddress, - IsNumber, - IsOptional, - IsString, -} from 'class-validator'; -import { ChainId } from '@human-protocol/sdk'; -import { ReputationEntityType } from '../../common/enums'; -import { Transform } from 'class-transformer'; - -export class ReputationCreateDto { - @ApiProperty() - @IsEnum(ChainId) - public chainId: ChainId; - - @ApiProperty() - @IsString() - public address: string; - - @ApiProperty() - @IsNumber() - public reputationPoints: number; - - @ApiProperty() - @IsEnum(ReputationEntityType) - public type: ReputationEntityType; -} - -export class ReputationUpdateDto { - @ApiProperty() - @IsNumber() - public reputationPoints: number; -} - -export class ReputationGetAllQueryDto { - @ApiPropertyOptional({ - enum: ChainId, - }) - @IsEnum(ChainId) - @IsOptional() - @Transform(({ value }) => Number(value)) - public chainId?: ChainId; -} - -export class ReputationGetParamsDto { - @ApiProperty() - @IsString() - @IsEthereumAddress() - public address: string; -} - -export class ReputationGetQueryDto { - @ApiProperty({ enum: ChainId }) - @IsEnum(ChainId) - @Transform(({ value }) => Number(value)) - public chainId: ChainId; -} diff --git a/packages/apps/hufi/reputation-oracle/server/src/modules/reputation/reputation.entity.ts b/packages/apps/hufi/reputation-oracle/server/src/modules/reputation/reputation.entity.ts deleted file mode 100644 index 4b81975c05..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/modules/reputation/reputation.entity.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Column, Entity } from 'typeorm'; - -import { NS } from '../../common/constants'; -import { BaseEntity } from '../../database/base.entity'; -import { ReputationEntityType } from '../../common/enums'; - -@Entity({ schema: NS, name: 'reputation' }) -export class ReputationEntity extends BaseEntity { - @Column({ type: 'int' }) - public chainId: number; - - @Column({ type: 'varchar' }) - public address: string; - - @Column({ type: 'int' }) - public reputationPoints: number; - - @Column({ - type: 'enum', - enum: ReputationEntityType, - }) - public type: ReputationEntityType; -} diff --git a/packages/apps/hufi/reputation-oracle/server/src/modules/reputation/reputation.module.ts b/packages/apps/hufi/reputation-oracle/server/src/modules/reputation/reputation.module.ts deleted file mode 100644 index a42f7cc317..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/modules/reputation/reputation.module.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Logger, Module } from '@nestjs/common'; -import { TypeOrmModule } from '@nestjs/typeorm'; -import { ConfigModule } from '@nestjs/config'; - -import { ReputationService } from './reputation.service'; -import { ReputationEntity } from './reputation.entity'; -import { HttpModule } from '@nestjs/axios'; -import { ReputationRepository } from './reputation.repository'; -import { ReputationController } from './reputation.controller'; - -@Module({ - imports: [ - TypeOrmModule.forFeature([ReputationEntity]), - ConfigModule, - HttpModule, - ], - controllers: [ReputationController], - providers: [Logger, ReputationService, ReputationRepository], - exports: [ReputationService], -}) -export class ReputationModule {} diff --git a/packages/apps/hufi/reputation-oracle/server/src/modules/reputation/reputation.repository.ts b/packages/apps/hufi/reputation-oracle/server/src/modules/reputation/reputation.repository.ts deleted file mode 100644 index 294411cb3b..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/modules/reputation/reputation.repository.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { Injectable, Logger, NotFoundException } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { ConfigService } from '@nestjs/config'; - -import { ReputationEntity } from './reputation.entity'; -import { - FindOptionsWhere, - FindManyOptions, - FindOneOptions, - Repository, -} from 'typeorm'; -import { ErrorReputation } from '../../common/constants/errors'; -import { ReputationCreateDto, ReputationUpdateDto } from './reputation.dto'; - -@Injectable() -export class ReputationRepository { - private readonly logger = new Logger(ReputationRepository.name); - - constructor( - @InjectRepository(ReputationEntity) - private readonly reputationEntityRepository: Repository, - private readonly configService: ConfigService, - ) {} - - public async updateOne( - where: FindOptionsWhere, - dto: Partial, - ): Promise { - const reputationEntity = await this.reputationEntityRepository.findOneBy( - where, - ); - - if (!reputationEntity) { - this.logger.log(ErrorReputation.NotFound, ReputationRepository.name); - throw new NotFoundException(ErrorReputation.NotFound); - } - - Object.assign(reputationEntity, dto); - return reputationEntity.save(); - } - - public async findOne( - where: FindOptionsWhere, - options?: FindOneOptions, - ): Promise { - return this.reputationEntityRepository.findOne({ - where, - ...options, - }); - } - - public find( - where: FindOptionsWhere, - options?: FindManyOptions, - ): Promise { - return this.reputationEntityRepository.find({ - where, - order: { - createdAt: 'DESC', - }, - ...options, - }); - } - - public async create(dto: ReputationCreateDto): Promise { - return this.reputationEntityRepository.create(dto).save(); - } -} diff --git a/packages/apps/hufi/reputation-oracle/server/src/modules/reputation/reputation.service.spec.ts b/packages/apps/hufi/reputation-oracle/server/src/modules/reputation/reputation.service.spec.ts deleted file mode 100644 index 9932c4d544..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/modules/reputation/reputation.service.spec.ts +++ /dev/null @@ -1,210 +0,0 @@ -import { Test } from '@nestjs/testing'; -import { ReputationService } from './reputation.service'; -import { ReputationRepository } from './reputation.repository'; -import { ChainId } from '@human-protocol/sdk'; -import { ReputationEntity } from './reputation.entity'; -import { MOCK_ADDRESS } from '../../../test/constants'; -import { WebhookRepository } from '../webhook/webhook.repository'; -import { createMock } from '@golevelup/ts-jest'; -import { ReputationEntityType, ReputationLevel } from '../../common/enums'; -import { ConfigService } from '@nestjs/config'; -import { ConfigNames } from '../../common/config'; - -describe('ReputationService', () => { - let reputationService: ReputationService, - reputationRepository: ReputationRepository; - - beforeEach(async () => { - const mockConfigService: Partial = { - get: jest.fn((key: string) => { - switch (key) { - case ConfigNames.REPUTATION_LEVEL_LOW: - return 300; - case ConfigNames.REPUTATION_LEVEL_HIGH: - return 700; - } - }), - }; - - const moduleRef = await Test.createTestingModule({ - providers: [ - ReputationService, - { - provide: ReputationRepository, - useValue: createMock(), - }, - { - provide: WebhookRepository, - useValue: createMock(), - }, - { - provide: ConfigService, - useValue: mockConfigService, - }, - ], - }).compile(); - - reputationService = moduleRef.get(ReputationService); - reputationRepository = moduleRef.get(ReputationRepository); - }); - - describe('increaseReputation', () => { - const chainId = ChainId.LOCALHOST; - const address = MOCK_ADDRESS; - const type = ReputationEntityType.LIQUIDITY_PROVIDER; - - it('should create a new reputation entity if not found', async () => { - jest - .spyOn(reputationRepository, 'findOne') - .mockResolvedValueOnce(undefined as any); - jest.spyOn(reputationRepository, 'create'); - - await reputationService.increaseReputation(chainId, address, type); - - expect(reputationRepository.findOne).toHaveBeenCalledWith({ address }); - expect(reputationRepository.create).toHaveBeenCalledWith({ - chainId, - address, - reputationPoints: 1, - type, - }); - }); - - it('should increase reputation points if entity found', async () => { - const reputationEntity: Partial = { - address, - reputationPoints: 1, - save: jest.fn(), - }; - - jest - .spyOn(reputationRepository, 'findOne') - .mockResolvedValueOnce(reputationEntity as ReputationEntity); - - await reputationService.increaseReputation(chainId, address, type); - - expect(reputationRepository.findOne).toHaveBeenCalledWith({ address }); - expect(reputationEntity.reputationPoints).toBe(2); - expect(reputationEntity.save).toHaveBeenCalled(); - }); - }); - - describe('decreaseReputation', () => { - const chainId = ChainId.LOCALHOST; - const address = MOCK_ADDRESS; - const type = ReputationEntityType.LIQUIDITY_PROVIDER; - - it('should create a new reputation entity if not found', async () => { - jest - .spyOn(reputationRepository, 'findOne') - .mockResolvedValueOnce(undefined as any); - jest.spyOn(reputationRepository, 'create'); - - await reputationService.decreaseReputation(chainId, address, type); - - expect(reputationRepository.findOne).toHaveBeenCalledWith({ address }); - expect(reputationRepository.create).toHaveBeenCalledWith({ - chainId, - address, - reputationPoints: 0, - type, - }); - }); - - it('should decrease reputation points if entity found', async () => { - const reputationEntity: Partial = { - address, - reputationPoints: 1, - save: jest.fn(), - }; - - jest - .spyOn(reputationRepository, 'findOne') - .mockResolvedValueOnce(reputationEntity as ReputationEntity); - - await reputationService.decreaseReputation(chainId, address, type); - - expect(reputationRepository.findOne).toHaveBeenCalledWith({ address }); - expect(reputationEntity.reputationPoints).toBe(0); - expect(reputationEntity.save).toHaveBeenCalled(); - }); - }); - - describe('getReputationLevel', () => { - it('should return LOW if reputation points are less than 300', () => { - expect(reputationService.getReputationLevel(299)).toBe( - ReputationLevel.LOW, - ); - }); - it('should return MEDIUM if reputation points are less than 700', () => { - expect(reputationService.getReputationLevel(699)).toBe( - ReputationLevel.MEDIUM, - ); - }); - - it('should return HIGH if reputation points are greater than 700', () => { - expect(reputationService.getReputationLevel(701)).toBe( - ReputationLevel.HIGH, - ); - }); - }); - - describe('getReputation', () => { - const chainId = ChainId.LOCALHOST; - const address = MOCK_ADDRESS; - - it('should return reputation entity', async () => { - const reputationEntity: Partial = { - chainId, - address, - reputationPoints: 1, - }; - - jest - .spyOn(reputationRepository, 'findOne') - .mockResolvedValueOnce(reputationEntity as ReputationEntity); - - const result = await reputationService.getReputation(chainId, address); - - const resultReputation = { - chainId, - address, - reputation: ReputationLevel.LOW, - }; - - expect(reputationRepository.findOne).toHaveBeenCalledWith({ - chainId, - address, - }); - expect(result).toEqual(resultReputation); - }); - }); - - describe('getAllReputations', () => { - const chainId = ChainId.LOCALHOST; - const address = MOCK_ADDRESS; - - it('should return all reputations', async () => { - const reputationEntity: Partial = { - chainId, - address, - reputationPoints: 1, - }; - - jest - .spyOn(reputationRepository, 'find') - .mockResolvedValueOnce([reputationEntity as ReputationEntity]); - - const result = await reputationService.getAllReputations(); - - const resultReputation = { - chainId, - address, - reputation: ReputationLevel.LOW, - }; - - expect(reputationRepository.find).toHaveBeenCalled(); - expect(result).toEqual([resultReputation]); - }); - }); -}); diff --git a/packages/apps/hufi/reputation-oracle/server/src/modules/reputation/reputation.service.ts b/packages/apps/hufi/reputation-oracle/server/src/modules/reputation/reputation.service.ts deleted file mode 100644 index 72c7059c0f..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/modules/reputation/reputation.service.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { Injectable, Logger, NotFoundException } from '@nestjs/common'; -import { ConfigService } from '@nestjs/config'; -import { ChainId } from '@human-protocol/sdk'; -import { INITIAL_REPUTATION } from '../../common/constants'; -import { ConfigNames } from '../../common/config'; -import { ReputationEntityType, ReputationLevel } from '../../common/enums'; -import { ReputationRepository } from './reputation.repository'; -import { IReputation } from '../../common/interfaces'; -import { ErrorReputation } from '../../common/constants/errors'; - -@Injectable() -export class ReputationService { - private readonly logger = new Logger(ReputationService.name); - - constructor( - private readonly reputationRepository: ReputationRepository, - private readonly configService: ConfigService, - ) {} - - public getReputationLevel(reputationPoints: number): ReputationLevel { - const reputationLevelLow = this.configService.get( - ConfigNames.REPUTATION_LEVEL_LOW, - )!; - - const reputationLevelHigh = this.configService.get( - ConfigNames.REPUTATION_LEVEL_HIGH, - )!; - - if (reputationPoints <= reputationLevelLow) { - return ReputationLevel.LOW; - } - - if (reputationPoints >= reputationLevelHigh) { - return ReputationLevel.HIGH; - } - - return ReputationLevel.MEDIUM; - } - - public async increaseReputation( - chainId: ChainId, - address: string, - type: ReputationEntityType, - ): Promise { - const reputationEntity = await this.reputationRepository.findOne({ - address, - }); - - if (!reputationEntity) { - this.reputationRepository.create({ - chainId, - address, - reputationPoints: INITIAL_REPUTATION + 1, - type, - }); - - return; - } - - Object.assign(reputationEntity, { - reputationPoints: reputationEntity.reputationPoints + 1, - }); - reputationEntity.save(); - - return; - } - - public async decreaseReputation( - chainId: ChainId, - address: string, - type: ReputationEntityType, - ): Promise { - const reputationEntity = await this.reputationRepository.findOne({ - address, - }); - - if (!reputationEntity) { - this.reputationRepository.create({ - chainId, - address, - reputationPoints: INITIAL_REPUTATION, - type, - }); - - return; - } - - if (reputationEntity.reputationPoints === INITIAL_REPUTATION) { - return; - } - - Object.assign(reputationEntity, { - reputationPoints: reputationEntity.reputationPoints - 1, - }); - reputationEntity.save(); - - return; - } - - public async getReputation( - chainId: ChainId, - address: string, - ): Promise { - const reputationEntity = await this.reputationRepository.findOne({ - address, - chainId, - }); - - if (!reputationEntity) { - this.logger.log(ErrorReputation.NotFound, ReputationService.name); - throw new NotFoundException(ErrorReputation.NotFound); - } - - return { - chainId: reputationEntity.chainId, - address: reputationEntity.address, - reputation: this.getReputationLevel(reputationEntity.reputationPoints), - }; - } - - public async getAllReputations(chainId?: ChainId): Promise { - const reputations = await this.reputationRepository.find({ - chainId, - }); - - return reputations.map((reputation) => ({ - chainId: reputation.chainId, - address: reputation.address, - reputation: this.getReputationLevel(reputation.reputationPoints), - })); - } -} diff --git a/packages/apps/hufi/reputation-oracle/server/src/modules/storage/storage.dto.ts b/packages/apps/hufi/reputation-oracle/server/src/modules/storage/storage.dto.ts deleted file mode 100644 index d1e21cb46a..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/modules/storage/storage.dto.ts +++ /dev/null @@ -1,4 +0,0 @@ -export class SaveLiquidityDto { - public url: string; - public hash: string; - } \ No newline at end of file diff --git a/packages/apps/hufi/reputation-oracle/server/src/modules/storage/storage.module.ts b/packages/apps/hufi/reputation-oracle/server/src/modules/storage/storage.module.ts deleted file mode 100644 index f9cf1659df..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/modules/storage/storage.module.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Module } from '@nestjs/common'; -import { StorageService } from './storage.service'; -import { ConfigModule } from '@nestjs/config'; -import { s3Config } from '../../common/config'; - -@Module({ - imports: [ConfigModule.forFeature(s3Config)], - providers: [StorageService], - exports: [StorageService], -}) -export class StorageModule {} diff --git a/packages/apps/hufi/reputation-oracle/server/src/modules/storage/storage.service.spec.ts b/packages/apps/hufi/reputation-oracle/server/src/modules/storage/storage.service.spec.ts deleted file mode 100644 index bd7922f3a2..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/modules/storage/storage.service.spec.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { ChainId, StorageClient } from '@human-protocol/sdk'; -import { ConfigModule, registerAs } from '@nestjs/config'; -import { Test } from '@nestjs/testing'; -import { - MOCK_FILE_URL, - MOCK_S3_ACCESS_KEY, - MOCK_S3_BUCKET, - MOCK_S3_ENDPOINT, - MOCK_S3_PORT, - MOCK_S3_SECRET_KEY, - MOCK_S3_USE_SSL, -} from '../../../test/constants'; -import { StorageService } from './storage.service'; -import crypto from 'crypto'; -import axios from 'axios'; -import stream from 'stream'; - -jest.mock('@human-protocol/sdk', () => ({ - ...jest.requireActual('@human-protocol/sdk'), - StorageClient: { - downloadFileFromUrl: jest.fn(), - }, -})); - -jest.mock('minio', () => { - class Client { - putObject = jest.fn(); - bucketExists = jest.fn(); - constructor() { - (this as any).protocol = 'http:'; - (this as any).host = 'localhost'; - (this as any).port = 9000; - } - } - - return { Client }; -}); - -jest.mock('axios'); - -describe('Web3Service', () => { - let storageService: StorageService; - - beforeAll(async () => { - const moduleRef = await Test.createTestingModule({ - imports: [ - ConfigModule.forFeature( - registerAs('s3', () => ({ - accessKey: MOCK_S3_ACCESS_KEY, - secretKey: MOCK_S3_SECRET_KEY, - endPoint: MOCK_S3_ENDPOINT, - port: MOCK_S3_PORT, - useSSL: MOCK_S3_USE_SSL, - bucket: MOCK_S3_BUCKET, - })), - ), - ], - providers: [StorageService], - }).compile(); - - storageService = moduleRef.get(StorageService); - }); - - describe('download', () => { - it('should download the file correctly', async () => { - const exchangeAddress = '0x1234567890123456789012345678901234567892'; - const workerAddress = '0x1234567890123456789012345678901234567891'; - const solution = 'test'; - - const expectedJobFile = { - exchangeAddress, - solutions: [ - { - workerAddress, - solution, - }, - ], - }; - - StorageClient.downloadFileFromUrl = jest - .fn() - .mockResolvedValueOnce(expectedJobFile); - const solutionsFile = await storageService.download(MOCK_FILE_URL); - expect(solutionsFile).toBe(expectedJobFile); - }); - - it('should return empty array when file cannot be downloaded', async () => { - StorageClient.downloadFileFromUrl = jest - .fn() - .mockRejectedValue('Network error'); - - const solutionsFile = await storageService.download(MOCK_FILE_URL); - expect(solutionsFile).toStrictEqual([]); - }); - }); - -}); diff --git a/packages/apps/hufi/reputation-oracle/server/src/modules/storage/storage.service.ts b/packages/apps/hufi/reputation-oracle/server/src/modules/storage/storage.service.ts deleted file mode 100644 index e566a87198..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/modules/storage/storage.service.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { ChainId, StorageClient } from '@human-protocol/sdk'; -import { BadRequestException, Inject, Injectable } from '@nestjs/common'; -import * as Minio from 'minio'; -import { S3ConfigType, s3ConfigKey } from '../../common/config'; -import crypto from 'crypto'; -import { liquidityDto } from '../webhook/webhook.dto'; -import { SaveLiquidityDto } from './storage.dto'; - -@Injectable() -export class StorageService { - public readonly minioClient: Minio.Client; - - constructor( - @Inject(s3ConfigKey) - private s3Config: S3ConfigType, - ) { - this.minioClient = new Minio.Client({ - endPoint: this.s3Config.endPoint, - port: this.s3Config.port, - accessKey: this.s3Config.accessKey, - secretKey: this.s3Config.secretKey, - useSSL: this.s3Config.useSSL, - }); - } - public getUrl(escrowAddress: string, chainId: ChainId): string { - return `${this.s3Config.useSSL ? 'https' : 'http'}://${ - this.s3Config.endPoint - }:${this.s3Config.port}/${ - this.s3Config.bucket - }/${escrowAddress}-${chainId}.json`; - } - - public async download(url: string): Promise { - try { - return await StorageClient.downloadFileFromUrl(url); - } catch { - return []; - } - } - - public async uploadLiquidities( - escrowAddress: string, - chainId: ChainId, - liquidities: liquidityDto[], - ): Promise { - if (!(await this.minioClient.bucketExists(this.s3Config.bucket))) { - throw new BadRequestException('Bucket not found'); - } - const content = JSON.stringify(liquidities); - try { - const hash = crypto.createHash('sha1').update(content).digest('hex'); - const filename = `${escrowAddress}-${chainId}.json`; - await this.minioClient.putObject( - this.s3Config.bucket, - filename, - JSON.stringify(content), - { - 'Content-Type': 'application/json', - 'Cache-Control': 'no-store', - }, - ); - - return { url: this.getUrl(escrowAddress, chainId), hash }; - } catch (e) { - throw new BadRequestException('File not uploaded'); - } - } - -} diff --git a/packages/apps/hufi/reputation-oracle/server/src/modules/web3/web3.module.ts b/packages/apps/hufi/reputation-oracle/server/src/modules/web3/web3.module.ts deleted file mode 100644 index e9e7918a93..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/modules/web3/web3.module.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Module } from '@nestjs/common'; -import { Web3Service } from './web3.service'; -import { ConfigModule } from '@nestjs/config'; - -@Module({ - imports: [ConfigModule], - providers: [Web3Service], - exports: [Web3Service], -}) -export class Web3Module {} diff --git a/packages/apps/hufi/reputation-oracle/server/src/modules/web3/web3.service.spec.ts b/packages/apps/hufi/reputation-oracle/server/src/modules/web3/web3.service.spec.ts deleted file mode 100644 index 2a5e5011ce..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/modules/web3/web3.service.spec.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { ConfigService } from '@nestjs/config'; -import { Test } from '@nestjs/testing'; -import { networkMap } from '../../common/config'; -import { Web3Service } from './web3.service'; - -describe('Web3Service', () => { - let web3Service: Web3Service; - const privateKey = - '5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a'; - - beforeAll(async () => { - const configServiceMock = { - get: jest.fn().mockReturnValue(privateKey), - }; - - const moduleRef = await Test.createTestingModule({ - providers: [ - Web3Service, - { - provide: ConfigService, - useValue: configServiceMock, - }, - ], - }).compile(); - - web3Service = moduleRef.get(Web3Service); - }); - - describe('getSigner', () => { - it('should return the signer for the specified chainId', async () => { - for (const networkKey of Object.keys(networkMap)) { - // Iterate through the networkMap to test each chainId - const network = networkMap[networkKey]; - - const signer = web3Service.getSigner(network.chainId); - expect(signer).toBeDefined(); - expect((signer.provider as any).connection.url).toBe(network.rpcUrl); - } - }); - - it('should return undefined if chainId is not configured', () => { - const chainId = 1; - - const signer = web3Service.getSigner(chainId); - - expect(signer).toBeUndefined(); - }); - }); -}); diff --git a/packages/apps/hufi/reputation-oracle/server/src/modules/web3/web3.service.ts b/packages/apps/hufi/reputation-oracle/server/src/modules/web3/web3.service.ts deleted file mode 100644 index be2b8857a7..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/modules/web3/web3.service.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { ConfigService } from '@nestjs/config'; -import { Wallet, ethers } from 'ethers'; -import { ConfigNames, networkMap } from '../../common/config'; - -@Injectable() -export class Web3Service { - private signers: { [key: number]: Wallet } = {}; - - constructor(private readonly configService: ConfigService) { - const privateKey = this.configService.get(ConfigNames.WEB3_PRIVATE_KEY); - for (const networkKey of Object.keys(networkMap)) { - const network = networkMap[networkKey]; - const provider = new ethers.JsonRpcProvider(network.rpcUrl); - this.signers[network.chainId] = new Wallet(privateKey, provider); - } - } - - getSigner(chainId: number): Wallet { - return this.signers[chainId]; - } -} diff --git a/packages/apps/hufi/reputation-oracle/server/src/modules/webhook/webhook-incoming.entity.ts b/packages/apps/hufi/reputation-oracle/server/src/modules/webhook/webhook-incoming.entity.ts deleted file mode 100644 index 3e77145d18..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/modules/webhook/webhook-incoming.entity.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Column, Entity } from 'typeorm'; - -import { NS } from '../../common/constants'; -import { BaseEntity } from '../../database/base.entity'; -import { WebhookStatus } from '../../common/enums'; -import { ChainId } from '@human-protocol/sdk'; - -@Entity({ schema: NS, name: 'webhook_incoming' }) -export class WebhookIncomingEntity extends BaseEntity { - @Column({ type: 'int' }) - public chainId: ChainId; - - @Column({ type: 'varchar', nullable: true }) - public oracleAddress: string; - - @Column({ type: 'varchar' }) - public escrowAddress: string; - - @Column({ type: 'varchar', nullable: true }) - public resultsUrl: string; - - @Column({ type: 'boolean', nullable: true }) - public checkPassed: boolean; - - @Column({ type: 'int' }) - public retriesCount: number; - - @Column({ type: 'timestamptz' }) - public waitUntil: Date; - - @Column({ - type: 'enum', - enum: WebhookStatus, - }) - public status: WebhookStatus; -} diff --git a/packages/apps/hufi/reputation-oracle/server/src/modules/webhook/webhook.controller.ts b/packages/apps/hufi/reputation-oracle/server/src/modules/webhook/webhook.controller.ts deleted file mode 100644 index 3d72191cb0..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/modules/webhook/webhook.controller.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { - Body, - Controller, - Get, - Headers, - Post, - UseGuards, -} from '@nestjs/common'; -import { ApiTags } from '@nestjs/swagger'; -import { HEADER_SIGNATURE_KEY } from '../../common/constants'; -import { SignatureAuthGuard } from '../../common/guards'; -import { Public } from '../../common/decorators'; -import { WebhookIncomingDto } from './webhook.dto'; -import { WebhookService } from './webhook.service'; -import { Role } from '../../common/enums/role'; - -@Public() -@ApiTags('Webhook') -@Controller('/webhook') -export class WebhookController { - constructor(private readonly webhookService: WebhookService) {} - - // @UseGuards(new SignatureAuthGuard([Role.Recording])) - @Post('/') - public async createIncomingWebhook( - // @Headers(HEADER_SIGNATURE_KEY) _: string, - @Body() data: WebhookIncomingDto, - ): Promise { - return this.webhookService.createIncomingWebhook(data); - } - - @Public() - @Get('/cron/pending') - public async processPendingCronJob(): Promise { - return this.webhookService.processPendingCronJob(); - } - - @Public() - @Get('/cron/paid') - public async processPaidCronJob(): Promise { - return this.webhookService.processPaidCronJob(); - } -} diff --git a/packages/apps/hufi/reputation-oracle/server/src/modules/webhook/webhook.dto.ts b/packages/apps/hufi/reputation-oracle/server/src/modules/webhook/webhook.dto.ts deleted file mode 100644 index 5327ca0771..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/modules/webhook/webhook.dto.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { - IsBoolean, - IsDate, - IsEnum, - IsNumber, - IsString, - IsUrl, -} from 'class-validator'; -import { EventType, WebhookStatus } from '../../common/enums'; -import { ChainId } from '@human-protocol/sdk'; -import { JobRequestType } from '../../common/enums'; -import { Exchange } from '../../common/enums/exchange'; - -export class WebhookIncomingDto { - @ApiProperty() - @IsEnum(ChainId) - public chainId: ChainId; - - @ApiProperty() - @IsEnum(EventType) - public eventType: EventType; - - @ApiProperty() - @IsString() - public escrowAddress: string; -} - -export class WebhookIncomingCreateDto { - @IsEnum(ChainId) - public chainId: ChainId; - - @IsString() - public escrowAddress: string; - - @IsEnum(WebhookStatus) - public status: WebhookStatus; - - @IsDate() - public waitUntil: Date; - - @IsNumber() - public retriesCount: number; -} - -export class WebhookIncomingUpdateDto { - @ApiPropertyOptional() - @IsEnum(ChainId) - public chainId: ChainId; - - @ApiPropertyOptional() - @IsString() - public escrowAddress: string; - - @ApiPropertyOptional() - @IsUrl() - public resultsUrl: string; - - @ApiPropertyOptional() - @IsBoolean() - public checkPassed: boolean; - - @ApiPropertyOptional() - @IsNumber() - public retriesCount: number; - - @ApiPropertyOptional({ - enum: WebhookStatus, - }) - @IsEnum(WebhookStatus) - public status: WebhookStatus; - - @ApiPropertyOptional() - @IsDate() - public waitUntil: Date; -} - -export class CampaignManifestDto { - startBlock: number; - endBlock: number; - exchangeName: Exchange; - tokenA: string; - tokenB: string; - campaignDuration: number; - fundAmount: number; - requesterDescription: string; - requestType: JobRequestType; -} - -export class liquidityDto { - chainId: ChainId; - liquidityProvider: string; - liquidityScore: string; -} diff --git a/packages/apps/hufi/reputation-oracle/server/src/modules/webhook/webhook.module.ts b/packages/apps/hufi/reputation-oracle/server/src/modules/webhook/webhook.module.ts deleted file mode 100644 index 0093570210..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/modules/webhook/webhook.module.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Logger, Module } from '@nestjs/common'; -import { TypeOrmModule } from '@nestjs/typeorm'; -import { ConfigModule } from '@nestjs/config'; - -import { WebhookService } from './webhook.service'; -import { WebhookIncomingEntity } from './webhook-incoming.entity'; -import { WebhookController } from './webhook.controller'; -import { WebhookRepository } from './webhook.repository'; -import { Web3Module } from '../web3/web3.module'; -import { StorageModule } from '../storage/storage.module'; -import { ReputationModule } from '../reputation/reputation.module'; - -@Module({ - imports: [ - TypeOrmModule.forFeature([WebhookIncomingEntity]), - ConfigModule, - ReputationModule, - Web3Module, - StorageModule, - ], - controllers: [WebhookController], - providers: [Logger, WebhookService, WebhookRepository], - exports: [WebhookService], -}) -export class WebhookModule {} diff --git a/packages/apps/hufi/reputation-oracle/server/src/modules/webhook/webhook.repository.ts b/packages/apps/hufi/reputation-oracle/server/src/modules/webhook/webhook.repository.ts deleted file mode 100644 index 6051750777..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/modules/webhook/webhook.repository.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { Injectable, Logger, NotFoundException } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; - -import { WebhookIncomingEntity } from './webhook-incoming.entity'; -import { - FindOptionsWhere, - FindManyOptions, - FindOneOptions, - Repository, -} from 'typeorm'; -import { ErrorWebhook } from '../../common/constants/errors'; -import { - WebhookIncomingCreateDto, - WebhookIncomingUpdateDto, -} from './webhook.dto'; - -@Injectable() -export class WebhookRepository { - private readonly logger = new Logger(WebhookRepository.name); - - constructor( - @InjectRepository(WebhookIncomingEntity) - private readonly webhookIncomingEntityRepository: Repository, - ) {} - - public async updateOne( - where: FindOptionsWhere, - dto: Partial, - ): Promise { - const webhookIncomingEntity = - await this.webhookIncomingEntityRepository.findOneBy(where); - - if (!webhookIncomingEntity) { - this.logger.log(ErrorWebhook.NotFound, WebhookRepository.name); - throw new NotFoundException(ErrorWebhook.NotFound); - } - - Object.assign(webhookIncomingEntity, dto); - return webhookIncomingEntity.save(); - } - - public async findOne( - where: FindOptionsWhere, - options?: FindOneOptions, - ): Promise { - const webhookEntity = await this.webhookIncomingEntityRepository.findOne({ - where, - ...options, - }); - - return webhookEntity; - } - - public find( - where: FindOptionsWhere, - options?: FindManyOptions, - ): Promise { - return this.webhookIncomingEntityRepository.find({ - where, - order: { - createdAt: 'DESC', - }, - ...options, - }); - } - - public async create( - dto: WebhookIncomingCreateDto, - ): Promise { - try { - return this.webhookIncomingEntityRepository.create(dto).save(); - } catch (e) { - return; - } - } -} diff --git a/packages/apps/hufi/reputation-oracle/server/src/modules/webhook/webhook.service.ts b/packages/apps/hufi/reputation-oracle/server/src/modules/webhook/webhook.service.ts deleted file mode 100644 index d8bf361b86..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/src/modules/webhook/webhook.service.ts +++ /dev/null @@ -1,302 +0,0 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -import { - BadRequestException, - Inject, - Injectable, - Logger, - NotFoundException, -} from '@nestjs/common'; -import { EscrowClient, EscrowUtils } from '@human-protocol/sdk'; -import { WebhookIncomingEntity } from './webhook-incoming.entity'; -import { WebhookIncomingDto, liquidityDto } from './webhook.dto'; -import { ErrorWebhook } from '../../common/constants/errors'; -import { WebhookRepository } from './webhook.repository'; -import { RETRIES_COUNT_THRESHOLD } from '../../common/constants'; -import { Web3Service } from '../web3/web3.service'; -import { - EventType, - ReputationEntityType, - SortDirection, - WebhookStatus, -} from '../../common/enums'; -import { LessThanOrEqual } from 'typeorm'; -import { StorageService } from '../storage/storage.service'; -import { ReputationService } from '../reputation/reputation.service'; - -@Injectable() -export class WebhookService { - private readonly logger = new Logger(WebhookService.name); - constructor( - private readonly web3Service: Web3Service, - @Inject(StorageService) - private readonly storageService: StorageService, - private readonly webhookRepository: WebhookRepository, - private readonly reputationService: ReputationService, - ) {} - - /** - * Create a incoming webhook using the DTO data. - * @param dto - Data to create an incoming webhook. - * @returns {Promise} - Return the boolean result of the method. - * @throws {Error} - An error object if an error occurred. - */ - public async createIncomingWebhook( - dto: WebhookIncomingDto, - ): Promise { - try { - if (dto.eventType !== EventType.CAMPAIGN_PAYOUT) { - this.logger.log(ErrorWebhook.InvalidEventType, WebhookService.name); - throw new BadRequestException(ErrorWebhook.InvalidEventType); - } - - const webhookEntity = await this.webhookRepository.create({ - chainId: dto.chainId, - escrowAddress: dto.escrowAddress, - status: WebhookStatus.PENDING, - waitUntil: new Date(), - retriesCount: 0, - }); - - if (!webhookEntity) { - this.logger.log(ErrorWebhook.NotCreated, WebhookService.name); - throw new NotFoundException(ErrorWebhook.NotCreated); - } - - return true; - } catch (e) { - throw new Error(e); - } - } - - /** - * Processes a pending webhook. Validates and processes incoming data, - * then sends payments based on the processing results. - * @param webhookEntity The entity representing the webhook data. - * @throws {Error} Will throw an error if processing fails at any step. - */ - public async processPendingCronJob(): Promise { - const webhookEntity = await this.webhookRepository.findOne( - { - status: WebhookStatus.PENDING, - retriesCount: LessThanOrEqual(RETRIES_COUNT_THRESHOLD), - waitUntil: LessThanOrEqual(new Date()), - }, - { - order: { - waitUntil: SortDirection.ASC, - }, - }, - ); - - if (!webhookEntity) return false; - - try { - const { chainId, escrowAddress } = webhookEntity; - const signer = this.web3Service.getSigner(chainId); - const escrowClient = await EscrowClient.build(signer); - - // const manifestUrl = await escrowClient.getManifestUrl(escrowAddress); - // if (!manifestUrl) { - // this.logger.log( - // ErrorManifest.ManifestUrlDoesNotExist, - // WebhookService.name, - // ); - // throw new Error(ErrorManifest.ManifestUrlDoesNotExist); - // } - - // const manifest: CampaignManifestDto = await this.storageService.download( - // manifestUrl, - // ); - const intermediateResultsUrl = - await escrowClient.getIntermediateResultsUrl(escrowAddress); - const intermediateResults: liquidityDto[] = JSON.parse( - await this.storageService.download(intermediateResultsUrl), - ); - const { url, hash } = await this.storageService.uploadLiquidities( - escrowAddress, - chainId, - intermediateResults, - ); - - const recipients = this.getRecipients(intermediateResults); - const totalAmount = await escrowClient.getBalance(escrowAddress); - const amounts = this.calculateCampaignPayoutAmounts( - totalAmount, - intermediateResults, - ); - - await escrowClient.bulkPayOut( - escrowAddress, - recipients, - amounts, - url, - hash, - ); - - await this.webhookRepository.updateOne( - { id: webhookEntity.id }, - { - resultsUrl: url, - checkPassed: true, - status: WebhookStatus.PAID, - retriesCount: 0, - }, - ); - - return true; - } catch (e) { - return await this.handleWebhookError(webhookEntity, e); - } - } - - /** - * Retrieves the recipients based on the final results and request type. - * @param finalResults - The final results. - * @returns {string[]} - Returns an array of recipient addresses. - */ - private getRecipients(finalResults: liquidityDto[]): string[] { - return finalResults.map((item) => item.liquidityProvider); - } - - public calculateCampaignPayoutAmounts( - totalAmount: bigint, - results: liquidityDto[], - ): bigint[] { - // Convert the liquidity scores to bigint for precision in calculations. - const bigNumberResults = results.map((result) => ({ - ...result, - liquidityScore: BigInt(result.liquidityScore), - })); - - // Calculate the total liquidity score as a BigNumber. - const totalLiquidityScore = bigNumberResults.reduce( - (total, item) => total + item.liquidityScore, - 0n, - ); - - // Map through each result, calculate each recipient's payout, and return the array. - const payouts = bigNumberResults.map((result) => { - const participantScore = result.liquidityScore; - const participantPercentage = participantScore / totalLiquidityScore; - return totalAmount * participantPercentage; - }); - - return payouts; - } - - /** - * Handles errors that occur during webhook processing. It logs the error, - * and based on retry count, updates the webhook status accordingly. - * @param webhookEntity The entity representing the webhook data. - * @param error The error object thrown during processing. - * @returns {Promise} Returns false indicating that an error occurred. - */ - public async handleWebhookError( - webhookEntity: WebhookIncomingEntity, - error: any, - ): Promise { - if (webhookEntity.retriesCount >= RETRIES_COUNT_THRESHOLD) { - await this.webhookRepository.updateOne( - { id: webhookEntity.id }, - { status: WebhookStatus.FAILED }, - ); - } else { - await this.webhookRepository.updateOne( - { id: webhookEntity.id }, - { - retriesCount: webhookEntity.retriesCount + 1, - waitUntil: new Date(), - }, - ); - } - - this.logger.error( - 'An error occurred during webhook validation: ', - error, - WebhookService.name, - ); - return false; - } - - /** - * Processing a webhook of an entity with a paid status. - * @returns {Promise} - Return the boolean result of the method. - * @throws {Error} - An error object if an error occurred. - */ - public async processPaidCronJob(): Promise { - const webhookEntity = await this.webhookRepository.findOne( - { - status: WebhookStatus.PAID, - retriesCount: LessThanOrEqual(RETRIES_COUNT_THRESHOLD), - waitUntil: LessThanOrEqual(new Date()), - }, - { - order: { - waitUntil: SortDirection.ASC, - }, - }, - ); - - if (!webhookEntity) return false; - - try { - const signer = this.web3Service.getSigner(webhookEntity.chainId); - const escrowClient = await EscrowClient.build(signer); - - const escrow = await EscrowUtils.getEscrow( - webhookEntity.chainId, - webhookEntity.escrowAddress, - ); - - if (escrow.finalResultsUrl) { - const finalResults: liquidityDto[] = JSON.parse( - await this.storageService.download(escrow.finalResultsUrl), - ); - - finalResults.forEach(async (result) => { - await this.reputationService.increaseReputation( - webhookEntity.chainId, - result.liquidityProvider, - ReputationEntityType.LIQUIDITY_PROVIDER, - ); - }); - - await this.reputationService.increaseReputation( - webhookEntity.chainId, - escrow.launcher, - ReputationEntityType.JOB_LAUNCHER, - ); - await this.reputationService.increaseReputation( - webhookEntity.chainId, - escrow.exchangeOracle!, - ReputationEntityType.EXCHANGE_ORACLE, - ); - await this.reputationService.increaseReputation( - webhookEntity.chainId, - escrow.recordingOracle!, - ReputationEntityType.RECORDING_ORACLE, - ); - await this.reputationService.increaseReputation( - webhookEntity.chainId, - escrow.reputationOracle!, - ReputationEntityType.REPUTATION_ORACLE, - ); - - const balance = BigInt(escrow.balance); - if (balance === 0n) { - await escrowClient.complete(webhookEntity.escrowAddress); - } - await this.webhookRepository.updateOne( - { - id: webhookEntity.id, - }, - { status: WebhookStatus.COMPLETED }, - ); - } - - return true; - } catch (e) { - return await this.handleWebhookError(webhookEntity, e); - } - } -} diff --git a/packages/apps/hufi/reputation-oracle/server/test/app.e2e-spec.ts b/packages/apps/hufi/reputation-oracle/server/test/app.e2e-spec.ts deleted file mode 100644 index 50cda62332..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/test/app.e2e-spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { INestApplication } from '@nestjs/common'; -import * as request from 'supertest'; -import { AppModule } from './../src/app.module'; - -describe('AppController (e2e)', () => { - let app: INestApplication; - - beforeEach(async () => { - const moduleFixture: TestingModule = await Test.createTestingModule({ - imports: [AppModule], - }).compile(); - - app = moduleFixture.createNestApplication(); - await app.init(); - }); - - it('/ (GET)', () => { - return request(app.getHttpServer()) - .get('/') - .expect(200) - .expect('Hello World!'); - }); -}); diff --git a/packages/apps/hufi/reputation-oracle/server/test/constants.ts b/packages/apps/hufi/reputation-oracle/server/test/constants.ts deleted file mode 100644 index 98dc1abbb2..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/test/constants.ts +++ /dev/null @@ -1,33 +0,0 @@ -export const MOCK_REQUESTER_TITLE = 'Mock job title'; -export const MOCK_REQUESTER_DESCRIPTION = 'Mock job description'; -export const MOCK_ADDRESS = '0xCf88b3f1992458C2f5a229573c768D0E9F70C44e'; -export const MOCK_FILE_URL = 'mockedFileUrl'; -export const MOCK_FILE_HASH = 'mockedFileHash'; -export const MOCK_FILE_KEY = 'manifest.json'; -export const MOCK_LABEL = 'contains burnt area'; -export const MOCK_LABEL_NEGATIVE = ''; -export const MOCK_JOB_LAUNCHER_FEE = 5; -export const MOCK_RECORDING_ORACLE_FEE = 5; -export const MOCK_REPUTATION_ORACLE_FEE = 5; -export const MOCK_JOB_LAUNCHER_ADDRESS = - '0xCf88b3f1992458C2f5a229573c768D0E9F70C441'; -export const MOCK_EXCHANGE_ORACLE_ADDRESS = - '0xCf88b3f1992458C2f5a229573c768D0E9F70C441'; -export const MOCK_RECORDING_ORACLE_ADDRESS = - '0xCf88b3f1992458C2f5a229573c768D0E9F70C442'; -export const MOCK_PRIVATE_KEY = - 'd334daf65a631f40549cc7de126d5a0016f32a2d00c49f94563f9737f7135e55'; -export const MOCK_EMAIL = 'test@example.com'; -export const MOCK_PASSWORD = 'password123'; -export const MOCK_HASHED_PASSWORD = 'hashedPassword'; -export const MOCK_ACCESS_TOKEN = 'access_token'; -export const MOCK_REFRESH_TOKEN = 'refresh_token'; -export const MOCK_ACCESS_TOKEN_HASHED = 'access_token_hashed'; -export const MOCK_REFRESH_TOKEN_HASHED = 'refresh_token_hashed'; -export const MOCK_EXPIRES_IN = 1000000000000000; -export const MOCK_S3_ENDPOINT = 'localhost'; -export const MOCK_S3_PORT = 9000; -export const MOCK_S3_ACCESS_KEY = 'access_key'; -export const MOCK_S3_SECRET_KEY = 'secret_key'; -export const MOCK_S3_BUCKET = 'solution'; -export const MOCK_S3_USE_SSL = false; diff --git a/packages/apps/hufi/reputation-oracle/server/test/jest-e2e.json b/packages/apps/hufi/reputation-oracle/server/test/jest-e2e.json deleted file mode 100644 index e9d912f3e3..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/test/jest-e2e.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "moduleFileExtensions": ["js", "json", "ts"], - "rootDir": ".", - "testEnvironment": "node", - "testRegex": ".e2e-spec.ts$", - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - } -} diff --git a/packages/apps/hufi/reputation-oracle/server/tsconfig.build.json b/packages/apps/hufi/reputation-oracle/server/tsconfig.build.json deleted file mode 100644 index 626ea15220..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/tsconfig.build.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": "./tsconfig.json", - "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] -} \ No newline at end of file diff --git a/packages/apps/hufi/reputation-oracle/server/tsconfig.json b/packages/apps/hufi/reputation-oracle/server/tsconfig.json deleted file mode 100644 index fb6e941176..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/tsconfig.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "declaration": true, - "removeComments": true, - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "allowJs": true, - "target": "ES2020", - "sourceMap": true, - "outDir": "./dist", - "baseUrl": "./", - "incremental": true, - "skipLibCheck": true, - "strictNullChecks": true, - "noImplicitAny": true, - "strictBindCallApply": true, - "forceConsistentCasingInFileNames": true, - "noFallthroughCasesInSwitch": true, - "esModuleInterop": true - } -} diff --git a/packages/apps/hufi/reputation-oracle/server/typeorm.config.ts b/packages/apps/hufi/reputation-oracle/server/typeorm.config.ts deleted file mode 100644 index 4b2b47b997..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/typeorm.config.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { DataSource } from 'typeorm'; -import { SnakeNamingStrategy } from 'typeorm-naming-strategies'; -import * as dotenv from 'dotenv'; - -dotenv.config({ - path: process.env.NODE_ENV - ? `.env.${process.env.NODE_ENV as string}` - : '.env', -}); - -export default new DataSource({ - type: 'postgres', - host: process.env.POSTGRES_HOST, - port: Number(process.env.POSTGRES_PORT), - username: process.env.POSTGRES_USER, - password: process.env.POSTGRES_PASSWORD, - database: process.env.POSTGRES_DATABASE, - entities: ['dist/src/**/*.entity{.ts,.js}'], - synchronize: false, - migrations: ['dist/src/database/migrations/*{.ts,.js}'], - migrationsTableName: 'migrations_typeorm', - migrationsRun: true, - namingStrategy: new SnakeNamingStrategy(), - ssl: process.env.POSTGRES_SSL?.toLowerCase() === 'true', -}); diff --git a/packages/apps/hufi/reputation-oracle/server/vercel.json b/packages/apps/hufi/reputation-oracle/server/vercel.json deleted file mode 100644 index 9b500025f4..0000000000 --- a/packages/apps/hufi/reputation-oracle/server/vercel.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "version": 2, - "builds": [ - { - "src": "src/main.ts", - "use": "@vercel/node" - } - ], - "routes": [ - { - "src": "/(.*)", - "dest": "src/main.ts", - "headers": { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Methods": "*", - "Access-Control-Allow-Headers": "X-Requested-With,Content-Type,Accept" - } - } - ], - "crons": [ - { - "path": "/webhook/cron/pending", - "schedule": "*/10 * * * *" - }, - { - "path": "/webhook/cron/paid", - "schedule": "*/10 * * * *" - } - ], - "ignoreCommand": "git diff HEAD^ HEAD --quiet ." -} From 3c1ea356b5dc98e5d2efd7ee70f526ec2f08487c Mon Sep 17 00:00:00 2001 From: Eric Lee <98655210+leric7@users.noreply.github.com> Date: Thu, 18 Jan 2024 00:09:16 +0800 Subject: [PATCH 038/104] [Reputation Oracle] feat: Encrypt result for job launcher (#1444) * encrypt result for job launcher * move encryption logic to reusable function * move decrypt logic to reusable function * add PGP_ENCRYPT env var to enable/disable encryption --- .../server/src/common/config/env.ts | 2 + .../modules/storage/storage.service.spec.ts | 305 +++++++++++++++--- .../src/modules/storage/storage.service.ts | 116 +++++-- .../modules/webhook/webhook.service.spec.ts | 13 +- .../src/modules/webhook/webhook.service.ts | 2 + 5 files changed, 359 insertions(+), 79 deletions(-) diff --git a/packages/apps/reputation-oracle/server/src/common/config/env.ts b/packages/apps/reputation-oracle/server/src/common/config/env.ts index 836ba0fe09..3ee836fd24 100644 --- a/packages/apps/reputation-oracle/server/src/common/config/env.ts +++ b/packages/apps/reputation-oracle/server/src/common/config/env.ts @@ -32,6 +32,7 @@ export const ConfigNames = { REPUTATION_LEVEL_HIGH: 'REPUTATION_LEVEL_HIGH', ENCRYPTION_PRIVATE_KEY: 'ENCRYPTION_PRIVATE_KEY', ENCRYPTION_PASSPHRASE: 'ENCRYPTION_PASSPHRASE', + PGP_ENCRYPT: 'PGP_ENCRYPT', }; export const envValidator = Joi.object({ @@ -75,4 +76,5 @@ export const envValidator = Joi.object({ // Encryption ENCRYPTION_PRIVATE_KEY: Joi.string().default(''), ENCRYPTION_PASSPHRASE: Joi.string().default(''), + PGP_ENCRYPT: Joi.string().default(false), }); diff --git a/packages/apps/reputation-oracle/server/src/modules/storage/storage.service.spec.ts b/packages/apps/reputation-oracle/server/src/modules/storage/storage.service.spec.ts index 11dc6fece0..dcdddd6ca3 100644 --- a/packages/apps/reputation-oracle/server/src/modules/storage/storage.service.spec.ts +++ b/packages/apps/reputation-oracle/server/src/modules/storage/storage.service.spec.ts @@ -1,11 +1,17 @@ -import { ChainId, EncryptionUtils, StorageClient } from '@human-protocol/sdk'; +import { + ChainId, + Encryption, + EncryptionUtils, + EscrowClient, + StakingClient, + StorageClient, +} from '@human-protocol/sdk'; import { ConfigModule, ConfigService, registerAs } from '@nestjs/config'; import { Test } from '@nestjs/testing'; import { MOCK_ENCRYPTION_PRIVATE_KEY, MOCK_ENCRYPTION_PUBLIC_KEY, MOCK_FILE_URL, - MOCK_MANIFEST, MOCK_S3_ACCESS_KEY, MOCK_S3_BUCKET, MOCK_S3_ENDPOINT, @@ -15,14 +21,26 @@ import { } from '../../../test/constants'; import { StorageService } from './storage.service'; import crypto from 'crypto'; -import axios from 'axios'; -import stream from 'stream'; +import { Web3Service } from '../web3/web3.service'; jest.mock('@human-protocol/sdk', () => ({ ...jest.requireActual('@human-protocol/sdk'), StorageClient: { downloadFileFromUrl: jest.fn(), }, + EscrowClient: { + build: jest.fn(), + }, + StakingClient: { + build: jest.fn(), + }, + Encryption: { + build: jest.fn(), + }, + EncryptionUtils: { + encrypt: jest.fn(), + isEncrypted: jest.fn(), + }, })); jest.mock('minio', () => { @@ -44,12 +62,21 @@ jest.mock('axios'); describe('StorageService', () => { let storageService: StorageService; + let encrypt = true; + + const signerMock = { + address: '0x1234567890123456789012345678901234567892', + getNetwork: jest.fn().mockResolvedValue({ chainId: ChainId.LOCALHOST }), + }; + beforeAll(async () => { const mockConfigService: Partial = { get: jest.fn((key: string) => { switch (key) { case 'ENCRYPTION_PRIVATE_KEY': return MOCK_ENCRYPTION_PRIVATE_KEY; + case 'PGP_ENCRYPT': + return encrypt; } }), }; @@ -70,10 +97,26 @@ describe('StorageService', () => { providers: [ StorageService, { provide: ConfigService, useValue: mockConfigService }, + { + provide: Web3Service, + useValue: { + getSigner: jest.fn().mockReturnValue(signerMock), + }, + }, ], }).compile(); storageService = moduleRef.get(StorageService); + + const jobLauncherAddress = '0x1234567890123456789012345678901234567893'; + EscrowClient.build = jest.fn().mockResolvedValue({ + getJobLauncherAddress: jest.fn().mockResolvedValue(jobLauncherAddress), + }); + StakingClient.build = jest.fn().mockResolvedValue({ + getLeader: jest.fn().mockResolvedValue({ + publicKey: MOCK_ENCRYPTION_PUBLIC_KEY, + }), + }); }); describe('uploadJobSolutions', () => { @@ -87,6 +130,8 @@ describe('StorageService', () => { .fn() .mockResolvedValueOnce(true); + EncryptionUtils.encrypt = jest.fn().mockResolvedValueOnce('encrypted'); + const jobSolution = { workerAddress, solution, @@ -96,17 +141,15 @@ describe('StorageService', () => { chainId, [jobSolution], ); + expect(fileData).toEqual({ url: `http://${MOCK_S3_ENDPOINT}:${MOCK_S3_PORT}/${MOCK_S3_BUCKET}/${escrowAddress}-${chainId}.json`, - hash: crypto - .createHash('sha1') - .update(JSON.stringify([jobSolution])) - .digest('hex'), + hash: crypto.createHash('sha1').update('encrypted').digest('hex'), }); expect(storageService.minioClient.putObject).toHaveBeenCalledWith( MOCK_S3_BUCKET, `${escrowAddress}-${chainId}.json`, - expect.stringContaining(solution), + expect.stringContaining('encrypted'), { 'Content-Type': 'application/json', 'Cache-Control': 'no-store', @@ -114,6 +157,58 @@ describe('StorageService', () => { ); }); + describe('without encryption', () => { + beforeAll(() => { + encrypt = false; + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + afterAll(() => { + encrypt = true; + }); + + it('should upload the solutions', async () => { + const workerAddress = '0x1234567890123456789012345678901234567891'; + const escrowAddress = '0x1234567890123456789012345678901234567890'; + const chainId = ChainId.LOCALHOST; + const solution = 'test'; + + storageService.minioClient.bucketExists = jest + .fn() + .mockResolvedValueOnce(true); + + EncryptionUtils.encrypt = jest.fn().mockResolvedValueOnce('encrypted'); + + const jobSolution = { + workerAddress, + solution, + }; + const fileData = await storageService.uploadJobSolutions( + escrowAddress, + chainId, + [jobSolution], + ); + const content = JSON.stringify([jobSolution]); + + expect(fileData).toEqual({ + url: `http://${MOCK_S3_ENDPOINT}:${MOCK_S3_PORT}/${MOCK_S3_BUCKET}/${escrowAddress}-${chainId}.json`, + hash: crypto.createHash('sha1').update(content).digest('hex'), + }); + expect(storageService.minioClient.putObject).toHaveBeenCalledWith( + MOCK_S3_BUCKET, + `${escrowAddress}-${chainId}.json`, + expect.stringContaining(content), + { + 'Content-Type': 'application/json', + 'Cache-Control': 'no-store', + }, + ); + }); + }); + it('should fail if the bucket does not exist', async () => { const workerAddress = '0x1234567890123456789012345678901234567891'; const escrowAddress = '0x1234567890123456789012345678901234567890'; @@ -134,6 +229,7 @@ describe('StorageService', () => { ]), ).rejects.toThrow('Bucket not found'); }); + it('should fail if the file cannot be uploaded', async () => { const workerAddress = '0x1234567890123456789012345678901234567891'; const escrowAddress = '0x1234567890123456789012345678901234567890'; @@ -198,13 +294,13 @@ describe('StorageService', () => { ], }; - const encryptedMessage = await EncryptionUtils.encrypt( - JSON.stringify(expectedJobFile), - [MOCK_ENCRYPTION_PUBLIC_KEY], - ); StorageClient.downloadFileFromUrl = jest .fn() - .mockResolvedValueOnce(encryptedMessage); + .mockResolvedValueOnce('encrypted'); + EncryptionUtils.isEncrypted = jest.fn().mockReturnValue(true); + Encryption.build = jest.fn().mockResolvedValue({ + decrypt: jest.fn().mockResolvedValue(expectedJobFile), + }); const solutionsFile = await storageService.download(MOCK_FILE_URL); expect(solutionsFile).toStrictEqual(expectedJobFile); @@ -221,19 +317,28 @@ describe('StorageService', () => { }); describe('copyFileFromURLToBucket', () => { + const escrowAddress = '0x1234567890123456789012345678901234567890'; + const chainId = ChainId.LOCALHOST; + it('should copy a file from a valid URL to a bucket', async () => { - const hashStreamData = new stream.Readable(); - hashStreamData.push(JSON.stringify(MOCK_MANIFEST)); - hashStreamData.push(null); - const uploadStreamData = new stream.Readable(); - uploadStreamData.push(JSON.stringify(MOCK_MANIFEST)); - uploadStreamData.push(null); - (axios.get as any) - .mockResolvedValueOnce({ data: hashStreamData }) - .mockResolvedValueOnce({ data: uploadStreamData }); - - const uploadedFile = - await storageService.copyFileFromURLToBucket(MOCK_FILE_URL); + StorageClient.downloadFileFromUrl = jest + .fn() + .mockResolvedValueOnce('some-file-content'); + + EncryptionUtils.isEncrypted = jest.fn().mockReturnValue(false); + EncryptionUtils.encrypt = jest + .fn() + .mockResolvedValueOnce('encrypted-file-content'); + + storageService.minioClient.putObject = jest + .fn() + .mockResolvedValueOnce(true); + + const uploadedFile = await storageService.copyFileFromURLToBucket( + escrowAddress, + chainId, + MOCK_FILE_URL, + ); expect( uploadedFile.url.includes( @@ -243,32 +348,148 @@ describe('StorageService', () => { expect(uploadedFile.hash).toBeDefined(); expect(storageService.minioClient.putObject).toBeCalledWith( MOCK_S3_BUCKET, - expect.any(String), - expect.any(stream), - { 'Cache-Control': 'no-store' }, + `s3${crypto + .createHash('sha1') + .update('encrypted-file-content') + .digest('hex')}.zip`, + 'encrypted-file-content', + { 'Cache-Control': 'no-store', 'Content-Type': 'application/json' }, ); }); - it('should handle an invalid URL', async () => { - (axios.get as any).mockRejectedValue('Network error'); + it('should copy an encrypted file from a valid URL to a bucket', async () => { + StorageClient.downloadFileFromUrl = jest + .fn() + .mockResolvedValueOnce('some-file-content'); + Encryption.build = jest.fn().mockResolvedValue({ + decrypt: jest.fn().mockResolvedValue('decrypted-file-content'), + }); - await expect( - storageService.copyFileFromURLToBucket(MOCK_FILE_URL), - ).rejects.toThrow('File not uploaded'); + EncryptionUtils.isEncrypted = jest.fn().mockReturnValue(true); + EncryptionUtils.encrypt = jest + .fn() + .mockResolvedValueOnce('encrypted-file-content'); + + const uploadedFile = await storageService.copyFileFromURLToBucket( + escrowAddress, + chainId, + MOCK_FILE_URL, + ); + + expect( + uploadedFile.url.includes( + `http://${MOCK_S3_ENDPOINT}:${MOCK_S3_PORT}/${MOCK_S3_BUCKET}/`, + ), + ).toBeTruthy(); + expect(uploadedFile.hash).toBeDefined(); + expect(storageService.minioClient.putObject).toBeCalledWith( + MOCK_S3_BUCKET, + `s3${crypto + .createHash('sha1') + .update('encrypted-file-content') + .digest('hex')}.zip`, + 'encrypted-file-content', + { 'Cache-Control': 'no-store', 'Content-Type': 'application/json' }, + ); }); - it('should handle errors when copying the file', async () => { - const streamResponseData = new stream.Readable(); - streamResponseData.push(JSON.stringify(MOCK_MANIFEST)); - streamResponseData.push(null); - (axios.get as any).mockResolvedValueOnce({ data: streamResponseData }); - storageService.minioClient.putObject = jest + describe('without encryption', () => { + beforeAll(() => { + encrypt = false; + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + afterAll(() => { + encrypt = true; + }); + + it('should copy a file from a valid URL to a bucket', async () => { + StorageClient.downloadFileFromUrl = jest + .fn() + .mockResolvedValueOnce('some-file-content'); + + EncryptionUtils.isEncrypted = jest.fn().mockReturnValue(false); + EncryptionUtils.encrypt = jest + .fn() + .mockResolvedValueOnce('encrypted-file-content'); + + storageService.minioClient.putObject = jest + .fn() + .mockResolvedValueOnce(true); + + const uploadedFile = await storageService.copyFileFromURLToBucket( + escrowAddress, + chainId, + MOCK_FILE_URL, + ); + + expect( + uploadedFile.url.includes( + `http://${MOCK_S3_ENDPOINT}:${MOCK_S3_PORT}/${MOCK_S3_BUCKET}/`, + ), + ).toBeTruthy(); + expect(uploadedFile.hash).toBeDefined(); + expect(storageService.minioClient.putObject).toBeCalledWith( + MOCK_S3_BUCKET, + `s3${crypto + .createHash('sha1') + .update('some-file-content') + .digest('hex')}.zip`, + 'some-file-content', + { 'Cache-Control': 'no-store', 'Content-Type': 'application/json' }, + ); + }); + + it('should copy an encrypted file from a valid URL to a bucket', async () => { + StorageClient.downloadFileFromUrl = jest + .fn() + .mockResolvedValueOnce('some-file-content'); + Encryption.build = jest.fn().mockResolvedValue({ + decrypt: jest.fn().mockResolvedValue('decrypted-file-content'), + }); + + EncryptionUtils.isEncrypted = jest.fn().mockReturnValue(true); + EncryptionUtils.encrypt = jest + .fn() + .mockResolvedValueOnce('encrypted-file-content'); + + const uploadedFile = await storageService.copyFileFromURLToBucket( + escrowAddress, + chainId, + MOCK_FILE_URL, + ); + + expect( + uploadedFile.url.includes( + `http://${MOCK_S3_ENDPOINT}:${MOCK_S3_PORT}/${MOCK_S3_BUCKET}/`, + ), + ).toBeTruthy(); + expect(uploadedFile.hash).toBeDefined(); + expect(storageService.minioClient.putObject).toBeCalledWith( + MOCK_S3_BUCKET, + `s3${crypto + .createHash('sha1') + .update('some-file-content') + .digest('hex')}.zip`, + 'some-file-content', + { 'Cache-Control': 'no-store', 'Content-Type': 'application/json' }, + ); + }); + }); + + it('should handle an invalid URL', async () => { + StorageClient.downloadFileFromUrl = jest .fn() - .mockRejectedValue('Network error'); + .mockRejectedValueOnce('Invalid URL'); await expect( storageService.copyFileFromURLToBucket( - 'https://example.com/archivo.zip', + escrowAddress, + chainId, + MOCK_FILE_URL, ), ).rejects.toThrow('File not uploaded'); }); diff --git a/packages/apps/reputation-oracle/server/src/modules/storage/storage.service.ts b/packages/apps/reputation-oracle/server/src/modules/storage/storage.service.ts index ac4b2245aa..bc3dd7906f 100644 --- a/packages/apps/reputation-oracle/server/src/modules/storage/storage.service.ts +++ b/packages/apps/reputation-oracle/server/src/modules/storage/storage.service.ts @@ -2,6 +2,8 @@ import { ChainId, Encryption, EncryptionUtils, + EscrowClient, + StakingClient, StorageClient, } from '@human-protocol/sdk'; import { BadRequestException, Inject, Injectable } from '@nestjs/common'; @@ -10,11 +12,9 @@ import { ConfigNames, S3ConfigType, s3ConfigKey } from '../../common/config'; import crypto from 'crypto'; import { UploadedFile } from '../../common/interfaces/s3'; import { FortuneFinalResult } from '../webhook/webhook.dto'; -import { PassThrough } from 'stream'; -import axios from 'axios'; import { Logger } from '@nestjs/common'; -import { hashStream } from '../../common/utils'; import { ConfigService } from '@nestjs/config'; +import { Web3Service } from '../web3/web3.service'; @Injectable() export class StorageService { @@ -24,6 +24,7 @@ export class StorageService { @Inject(s3ConfigKey) private s3Config: S3ConfigType, public readonly configService: ConfigService, + private readonly web3Service: Web3Service, ) { this.minioClient = new Minio.Client({ endPoint: this.s3Config.endPoint, @@ -39,26 +40,58 @@ export class StorageService { }:${this.s3Config.port}/${this.s3Config.bucket}/${key}`; } + private async encryptFile( + escrowAddress: string, + chainId: ChainId, + content: any, + ) { + if (!this.configService.get(ConfigNames.PGP_ENCRYPT)) { + return content; + } + + const signer = this.web3Service.getSigner(chainId); + const escrowClient = await EscrowClient.build(signer); + const stakingClient = await StakingClient.build(signer); + + const jobLauncherAddress = + await escrowClient.getJobLauncherAddress(escrowAddress); + + const reputationOracle = await stakingClient.getLeader(signer.address); + const jobLauncher = await stakingClient.getLeader(jobLauncherAddress); + + if (!reputationOracle.publicKey || !jobLauncher.publicKey) { + throw new BadRequestException('Missing public key'); + } + + return await EncryptionUtils.encrypt(content, [ + reputationOracle.publicKey, + jobLauncher.publicKey, + ]); + } + + private async decryptFile(fileContent: any): Promise { + if ( + typeof fileContent === 'string' && + EncryptionUtils.isEncrypted(fileContent) + ) { + const encryption = await Encryption.build( + this.configService.get(ConfigNames.ENCRYPTION_PRIVATE_KEY, ''), + this.configService.get(ConfigNames.ENCRYPTION_PASSPHRASE, ''), + ); + + return await encryption.decrypt(fileContent); + } else { + return fileContent; + } + } + public async download(url: string): Promise { try { const fileContent = await StorageClient.downloadFileFromUrl(url); - if ( - typeof fileContent === 'string' && - EncryptionUtils.isEncrypted(fileContent) - ) { - const encryption = await Encryption.build( - this.configService.get( - ConfigNames.ENCRYPTION_PRIVATE_KEY, - '', - ), - this.configService.get(ConfigNames.ENCRYPTION_PASSPHRASE, ''), - ); - - return JSON.parse(await encryption.decrypt(fileContent)); - } else { - return fileContent; - } - } catch { + + return await this.decryptFile(fileContent); + } catch (error) { + Logger.error(`Error downloading ${url}:`, error); return []; } } @@ -72,9 +105,14 @@ export class StorageService { throw new BadRequestException('Bucket not found'); } - const content = JSON.stringify(solutions); - const key = `${escrowAddress}-${chainId}.json`; try { + const content = await this.encryptFile( + escrowAddress, + chainId, + JSON.stringify(solutions), + ); + + const key = `${escrowAddress}-${chainId}.json`; const hash = crypto.createHash('sha1').update(content).digest('hex'); await this.minioClient.putObject(this.s3Config.bucket, key, content, { 'Content-Type': 'application/json', @@ -82,7 +120,8 @@ export class StorageService { }); return { url: this.getUrl(key), hash }; - } catch (e) { + } catch (error) { + Logger.error('Error uploading job solution:', error); throw new BadRequestException('File not uploaded'); } } @@ -93,25 +132,32 @@ export class StorageService { * @param {string} url - URL of the source file * @returns {Promise} - Uploaded file with key/hash */ - public async copyFileFromURLToBucket(url: string): Promise { + public async copyFileFromURLToBucket( + escrowAddress: string, + chainId: ChainId, + url: string, + ): Promise { try { - const { data: hStream } = await axios.get(url, { - responseType: 'stream', - }); - const hash = await hashStream(hStream); + // Download the content of the file from the bucket + let fileContent = await StorageClient.downloadFileFromUrl(url); + fileContent = await this.decryptFile(fileContent); + + // Encrypt for job launcher + const content = await this.encryptFile( + escrowAddress, + chainId, + fileContent, + ); + // Upload the encrypted file to the bucket + const hash = crypto.createHash('sha1').update(content).digest('hex'); const key = `s3${hash}.zip`; - // Creating a second readable stream for uploading a file to the bucket - const { data: uStream } = await axios.get(url, { - responseType: 'stream', - }); - await this.minioClient.putObject(this.s3Config.bucket, key, uStream, { + await this.minioClient.putObject(this.s3Config.bucket, key, content, { + 'Content-Type': 'application/json', 'Cache-Control': 'no-store', }); - Logger.log(`File from ${url} copied to ${this.s3Config.bucket}/${key}`); - return { url: this.getUrl(key), hash, diff --git a/packages/apps/reputation-oracle/server/src/modules/webhook/webhook.service.spec.ts b/packages/apps/reputation-oracle/server/src/modules/webhook/webhook.service.spec.ts index 69470c2f85..b58e23c54e 100644 --- a/packages/apps/reputation-oracle/server/src/modules/webhook/webhook.service.spec.ts +++ b/packages/apps/reputation-oracle/server/src/modules/webhook/webhook.service.spec.ts @@ -24,6 +24,7 @@ import { } from '../../common/enums'; import { MOCK_ADDRESS, + MOCK_ENCRYPTION_PUBLIC_KEY, MOCK_EXCHANGE_ORACLE_ADDRESS, MOCK_FILE_HASH, MOCK_FILE_KEY, @@ -54,6 +55,14 @@ jest.mock('@human-protocol/sdk', () => ({ getResultsUrl: jest.fn().mockResolvedValue(MOCK_FILE_URL), bulkPayOut: jest.fn().mockResolvedValue(true), getBalance: jest.fn().mockResolvedValue(10n), + getJobLauncherAddress: jest.fn().mockResolvedValue(MOCK_ADDRESS), + })), + }, + StakingClient: { + build: jest.fn().mockImplementation(() => ({ + getLeader: jest + .fn() + .mockResolvedValue({ publicKey: MOCK_ENCRYPTION_PUBLIC_KEY }), })), }, StorageClient: jest.fn().mockImplementation(() => ({ @@ -89,7 +98,7 @@ describe('WebhookService', () => { const signerMock = { address: MOCK_ADDRESS, - getNetwork: jest.fn().mockResolvedValue({ chainId: 1 }), + getNetwork: jest.fn().mockResolvedValue({ chainId: ChainId.LOCALHOST }), }; beforeEach(async () => { @@ -306,7 +315,7 @@ describe('WebhookService', () => { .spyOn(webhookService, 'processFortune') .mockResolvedValue(results as any); - (EscrowClient.build as any).mockImplementation(() => ({ + (EscrowClient.build as any).mockImplementationOnce(() => ({ getIntermediateResultsUrl: jest.fn().mockResolvedValue(MOCK_FILE_URL), getManifestUrl: jest.fn().mockResolvedValue(MOCK_FILE_URL), getResultsUrl: jest.fn().mockResolvedValue(MOCK_FILE_URL), diff --git a/packages/apps/reputation-oracle/server/src/modules/webhook/webhook.service.ts b/packages/apps/reputation-oracle/server/src/modules/webhook/webhook.service.ts index 3073243b1e..09ee7f4708 100644 --- a/packages/apps/reputation-oracle/server/src/modules/webhook/webhook.service.ts +++ b/packages/apps/reputation-oracle/server/src/modules/webhook/webhook.service.ts @@ -253,6 +253,8 @@ export class WebhookService { escrowAddress, ); const { url, hash } = await this.storageService.copyFileFromURLToBucket( + escrowAddress, + chainId, `${intermediateResultsUrl}/${CVAT_RESULTS_ANNOTATIONS_FILENAME}`, ); const annotations: CvatAnnotationMeta = await this.storageService.download( From 43f573531e789679c49b7265c8faa277d7f65065 Mon Sep 17 00:00:00 2001 From: Eric Lee <98655210+leric7@users.noreply.github.com> Date: Thu, 18 Jan 2024 16:35:00 +0800 Subject: [PATCH 039/104] fix storage module init (#1479) --- .../server/src/modules/storage/storage.module.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/apps/reputation-oracle/server/src/modules/storage/storage.module.ts b/packages/apps/reputation-oracle/server/src/modules/storage/storage.module.ts index f9cf1659df..47be30be9c 100644 --- a/packages/apps/reputation-oracle/server/src/modules/storage/storage.module.ts +++ b/packages/apps/reputation-oracle/server/src/modules/storage/storage.module.ts @@ -2,9 +2,10 @@ import { Module } from '@nestjs/common'; import { StorageService } from './storage.service'; import { ConfigModule } from '@nestjs/config'; import { s3Config } from '../../common/config'; +import { Web3Module } from '../web3/web3.module'; @Module({ - imports: [ConfigModule.forFeature(s3Config)], + imports: [ConfigModule.forFeature(s3Config), Web3Module], providers: [StorageService], exports: [StorageService], }) From dda78811f94d196389a31c513a292fd3af3edd56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20L=C3=B3pez?= <50665615+flopez7@users.noreply.github.com> Date: Thu, 18 Jan 2024 11:04:38 +0100 Subject: [PATCH 040/104] [Job Launcher] Encrypt manifest (#1101) * Encrypt manifest * Add an env variable to enable/disable encryption --- .../server/src/common/config/env.ts | 9 +- .../modules/encryption/encryption.module.ts | 20 ++ .../server/src/modules/job/job.module.ts | 2 + .../src/modules/job/job.service.spec.ts | 337 +++++++++++++++++- .../server/src/modules/job/job.service.ts | 156 ++++---- .../job-launcher/server/test/constants.ts | 58 ++- 6 files changed, 464 insertions(+), 118 deletions(-) create mode 100644 packages/apps/job-launcher/server/src/modules/encryption/encryption.module.ts diff --git a/packages/apps/job-launcher/server/src/common/config/env.ts b/packages/apps/job-launcher/server/src/common/config/env.ts index 08b62de6a8..453e3ee229 100644 --- a/packages/apps/job-launcher/server/src/common/config/env.ts +++ b/packages/apps/job-launcher/server/src/common/config/env.ts @@ -21,7 +21,7 @@ export const ConfigNames = { WEB3_PRIVATE_KEY: 'WEB3_PRIVATE_KEY', GAS_PRICE_MULTIPLIER: 'GAS_PRICE_MULTIPLIER', PGP_PRIVATE_KEY: 'PGP_PRIVATE_KEY', - PGP_PUBLIC_KEY: 'PGP_PUBLIC_KEY', + PGP_ENCRYPT: 'PGP_ENCRYPT', JOB_LAUNCHER_FEE: 'JOB_LAUNCHER_FEE', RECORDING_ORACLE_FEE: 'RECORDING_ORACLE_FEE', REPUTATION_ORACLE_FEE: 'REPUTATION_ORACLE_FEE', @@ -36,7 +36,6 @@ export const ConfigNames = { HCAPTCHA_RECORDING_ORACLE_URI: 'HCAPTCHA_RECORDING_ORACLE_URI', HCAPTCHA_REPUTATION_ORACLE_URI: 'HCAPTCHA_REPUTATION_ORACLE_URI', HCAPTCHA_ORACLE_ADDRESS: 'HCAPTCHA_ORACLE_ADDRESS', - HCAPTCHA_PGP_PUBLIC_KEY: 'HCAPTCHA_PGP_PUBLIC_KEY', HCAPTCHA_SITE_KEY: 'HCAPTCHA_SITE_KEY', S3_ENDPOINT: 'S3_ENDPOINT', S3_PORT: 'S3_PORT', @@ -85,8 +84,6 @@ export const envValidator = Joi.object({ WEB3_ENV: Joi.string().default('testnet'), WEB3_PRIVATE_KEY: Joi.string().required(), GAS_PRICE_MULTIPLIER: Joi.number().default(null), - PGP_PRIVATE_KEY: Joi.string().required(), - PGP_PUBLIC_KEY: Joi.string().required(), JOB_LAUNCHER_FEE: Joi.string().default(10), REPUTATION_ORACLE_ADDRESS: Joi.string().required(), FORTUNE_EXCHANGE_ORACLE_ADDRESS: Joi.string().required(), @@ -96,7 +93,6 @@ export const envValidator = Joi.object({ HCAPTCHA_RECORDING_ORACLE_URI: Joi.string().required(), HCAPTCHA_REPUTATION_ORACLE_URI: Joi.string().required(), HCAPTCHA_ORACLE_ADDRESS: Joi.string().required(), - HCAPTCHA_PGP_PUBLIC_KEY: Joi.string().required(), HCAPTCHA_SITE_KEY: Joi.string().required(), // S3 S3_ENDPOINT: Joi.string().default('127.0.0.1'), @@ -119,6 +115,9 @@ export const envValidator = Joi.object({ CVAT_JOB_SIZE: Joi.string().default('10'), CVAT_MAX_TIME: Joi.string().default('300'), CVAT_VAL_SIZE: Joi.string().default('2'), + //PGP + PGP_PRIVATE_KEY: Joi.string().required(), + PGP_ENCRYPT: Joi.string().default(false), // APIKey APIKEY_ITERATIONS: Joi.number().default(1000), APIKEY_KEY_LENGTH: Joi.number().default(64), diff --git a/packages/apps/job-launcher/server/src/modules/encryption/encryption.module.ts b/packages/apps/job-launcher/server/src/modules/encryption/encryption.module.ts new file mode 100644 index 0000000000..0e582ebb5e --- /dev/null +++ b/packages/apps/job-launcher/server/src/modules/encryption/encryption.module.ts @@ -0,0 +1,20 @@ +import { Module, Global, Provider } from '@nestjs/common'; +import { Encryption } from '@human-protocol/sdk'; +import { ConfigModule, ConfigService } from '@nestjs/config'; + +const encryptionProvider: Provider = { + provide: Encryption, + useFactory: async (configService: ConfigService) => { + const privateKey = configService.get('PGP_PRIVATE_KEY')!; + return await Encryption.build(privateKey); + }, + inject: [ConfigService], +}; + +@Global() +@Module({ + imports: [ConfigModule], + providers: [encryptionProvider], + exports: [encryptionProvider], +}) +export class EncryptionModule {} diff --git a/packages/apps/job-launcher/server/src/modules/job/job.module.ts b/packages/apps/job-launcher/server/src/modules/job/job.module.ts index c2f5f273b6..819c0b0845 100644 --- a/packages/apps/job-launcher/server/src/modules/job/job.module.ts +++ b/packages/apps/job-launcher/server/src/modules/job/job.module.ts @@ -10,6 +10,7 @@ import { PaymentModule } from '../payment/payment.module'; import { JobRepository } from './job.repository'; import { Web3Module } from '../web3/web3.module'; import { RoutingProtocolService } from './routing-protocol.service'; +import { EncryptionModule } from '../encryption/encryption.module'; import { StorageModule } from '../storage/storage.module'; import { AuthModule } from '../auth/auth.module'; import { WebhookModule } from '../webhook/webhook.module'; @@ -22,6 +23,7 @@ import { WebhookModule } from '../webhook/webhook.module'; AuthModule, PaymentModule, Web3Module, + EncryptionModule, StorageModule, WebhookModule, ], 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 cf8435dee0..43fad90db3 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 @@ -8,10 +8,12 @@ import { IAllocation, EscrowUtils, NETWORKS, + Encryption, KVStoreClient, } from '@human-protocol/sdk'; import { HttpService } from '@nestjs/axios'; import { + BadGatewayException, BadRequestException, ConflictException, NotFoundException, @@ -51,7 +53,6 @@ import { MOCK_FILE_HASH, MOCK_FILE_URL, MOCK_HCAPTCHA_ORACLE_ADDRESS, - MOCK_HCAPTCHA_PGP_PUBLIC_KEY, MOCK_JOB_ID, MOCK_JOB_LAUNCHER_FEE, MOCK_PGP_PRIVATE_KEY, @@ -74,7 +75,6 @@ import { MOCK_HCAPTCHA_IMAGE_URL, MOCK_HCAPTCHA_REPO_URI, MOCK_HCAPTCHA_RO_URI, - MOCK_FILE_KEY, MOCK_BUCKET_FILE, MOCK_MAX_RETRY_COUNT, } from '../../../test/constants'; @@ -133,14 +133,6 @@ jest.mock('@human-protocol/sdk', () => ({ getAllocation: jest.fn(), })), }, - StorageClient: jest.fn().mockImplementation(() => ({ - uploadFiles: jest - .fn() - .mockResolvedValue([ - { key: MOCK_FILE_KEY, url: MOCK_FILE_URL, hash: MOCK_FILE_HASH }, - ]), - listObjects: jest.fn().mockResolvedValue(MOCK_BUCKET_FILES), - })), KVStoreClient: { build: jest.fn().mockImplementation(() => ({ get: jest.fn(), @@ -174,10 +166,13 @@ describe('JobService', () => { createPaymentMock: any, routingProtocolService: RoutingProtocolService, web3Service: Web3Service, + encryption: Encryption, storageService: StorageService, webhookService: WebhookService, cronJobService: CronJobService; + let encrypt = true; + const signerMock = { address: MOCK_ADDRESS, getNetwork: jest.fn().mockResolvedValue({ chainId: 1 }), @@ -217,10 +212,8 @@ describe('JobService', () => { return 1; case 'PGP_PRIVATE_KEY': return MOCK_PGP_PRIVATE_KEY; - case 'PGP_PUBLIC_KEY': - return MOCK_PGP_PUBLIC_KEY; - case 'HCAPTCHA_PGP_PUBLIC_KEY': - return MOCK_HCAPTCHA_PGP_PUBLIC_KEY; + case 'PGP_ENCRYPT': + return encrypt; case 'HCAPTCHA_ORACLE_ADDRESS': return MOCK_HCAPTCHA_ORACLE_ADDRESS; case 'HCAPTCHA_SITE_KEY': @@ -244,6 +237,7 @@ describe('JobService', () => { const moduleRef = await Test.createTestingModule({ providers: [ JobService, + Encryption, { provide: Web3Service, useValue: { @@ -293,6 +287,10 @@ describe('JobService', () => { web3Service.calculateGasPrice = jest.fn().mockReturnValue(1000n); }); + beforeEach(async () => { + encryption = await Encryption.build(MOCK_PGP_PRIVATE_KEY); + }); + describe('createJob', () => { const userId = 1; const jobId = 123; @@ -311,6 +309,12 @@ describe('JobService', () => { createPaymentMock.mockResolvedValue(true); }); + beforeAll(() => { + (KVStoreClient.build as any).mockImplementation(() => ({ + get: jest.fn().mockResolvedValue(MOCK_PGP_PUBLIC_KEY), + })); + }); + afterEach(() => { jest.restoreAllMocks(); }); @@ -335,10 +339,6 @@ describe('JobService', () => { save: jest.fn().mockResolvedValue(true), }; - (KVStoreClient.build as any).mockImplementation(() => ({ - get: jest.fn().mockResolvedValue(MOCK_ORACLE_FEE), - })); - jobRepository.create = jest.fn().mockResolvedValue(mockJobEntity); await jobService.createJob(userId, JobRequestType.FORTUNE, fortuneJobDto); @@ -1350,6 +1350,12 @@ describe('JobService', () => { describe('setupEscrow', () => { const chainId = ChainId.LOCALHOST; + beforeAll(() => { + (KVStoreClient.build as any).mockImplementation(() => ({ + get: jest.fn().mockResolvedValue(MOCK_ORACLE_FEE), + })); + }); + it('should setup escrow and update the status to funding', async () => { const fundAmount = 10; const fee = (MOCK_JOB_LAUNCHER_FEE / 100) * fundAmount; @@ -2162,6 +2168,301 @@ describe('JobService', () => { }); }); + describe('uploadManifest with fortune request type and encryption', () => { + const chainId = ChainId.LOCALHOST; + const fortuneManifestParams = { + requestType: JobRequestType.FORTUNE, + submissionsRequired: MOCK_SUBMISSION_REQUIRED, + requesterDescription: MOCK_REQUESTER_DESCRIPTION, + fundAmount: 10, + requesterTitle: MOCK_REQUESTER_TITLE, + }; + + let uploadFilesMock: any; + + beforeEach(() => { + uploadFilesMock = jest.spyOn(storageService, 'uploadFile'); + }); + + beforeAll(() => { + (KVStoreClient.build as any).mockImplementation(() => ({ + get: jest.fn().mockResolvedValue(MOCK_PGP_PUBLIC_KEY), + })); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('should save the manifest and return the manifest URL and hash', async () => { + uploadFilesMock.mockResolvedValue([ + { + url: MOCK_FILE_URL, + hash: MOCK_FILE_HASH, + }, + ]); + + const result = await jobService.uploadManifest( + fortuneManifestParams, + chainId, + ); + + expect(result).toEqual([ + { + url: MOCK_FILE_URL, + hash: MOCK_FILE_HASH, + }, + ]); + + expect(storageService.uploadFile).toHaveBeenCalled(); + expect( + JSON.parse( + await encryption.decrypt( + (storageService.uploadFile as any).mock.calls[0][0], + ), + ), + ).toEqual(fortuneManifestParams); + }); + + it('should throw an error if the manifest file fails to upload', async () => { + const uploadError = new Error(ErrorBucket.UnableSaveFile); + + uploadFilesMock.mockRejectedValue(uploadError); + + await expect( + jobService.uploadManifest(fortuneManifestParams, chainId), + ).rejects.toThrowError( + new BadGatewayException(ErrorBucket.UnableSaveFile), + ); + + expect(storageService.uploadFile).toHaveBeenCalled(); + expect( + JSON.parse( + await encryption.decrypt( + (storageService.uploadFile as any).mock.calls[0][0], + ), + ), + ).toEqual(fortuneManifestParams); + }); + + it('should rethrow any other errors encountered', async () => { + const errorMessage = 'Something went wrong'; + const uploadError = new Error(errorMessage); + + uploadFilesMock.mockRejectedValue(uploadError); + + await expect( + jobService.uploadManifest(fortuneManifestParams, chainId), + ).rejects.toThrowError(new Error(errorMessage)); + + expect(storageService.uploadFile).toHaveBeenCalled(); + expect( + JSON.parse( + await encryption.decrypt( + (storageService.uploadFile as any).mock.calls[0][0], + ), + ), + ).toEqual(fortuneManifestParams); + }); + }); + + describe('uploadManifest with image label binary request type and encryption', () => { + const chainId = ChainId.LOCALHOST; + const manifest: CvatManifestDto = { + data: { + data_url: MOCK_FILE_URL, + }, + annotation: { + labels: [{ name: 'label1' }], + description: MOCK_REQUESTER_DESCRIPTION, + user_guide: MOCK_FILE_URL, + type: JobRequestType.IMAGE_POINTS, + job_size: 10, + max_time: 300, + }, + validation: { + min_quality: 1, + val_size: 2, + gt_url: '', + }, + job_bounty: '1', + }; + + let uploadFilesMock: any; + + beforeEach(() => { + uploadFilesMock = jest.spyOn(storageService, 'uploadFile'); + }); + + beforeAll(() => { + (KVStoreClient.build as any).mockImplementation(() => ({ + get: jest.fn().mockResolvedValue(MOCK_PGP_PUBLIC_KEY), + })); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('should save the manifest and return the manifest URL and hash', async () => { + uploadFilesMock.mockResolvedValue([ + { + url: MOCK_FILE_URL, + hash: MOCK_FILE_HASH, + }, + ]); + + const result = await jobService.uploadManifest(manifest, chainId); + + expect(result).toEqual([ + { + hash: MOCK_FILE_HASH, + url: MOCK_FILE_URL, + }, + ]); + + expect(storageService.uploadFile).toHaveBeenCalled(); + expect( + JSON.parse( + await encryption.decrypt( + (storageService.uploadFile as any).mock.calls[0][0], + ), + ), + ).toEqual(manifest); + }); + + it('should throw an error if the manifest file fails to upload', async () => { + const uploadError = new Error(ErrorBucket.UnableSaveFile); + + uploadFilesMock.mockRejectedValue(uploadError); + + await expect( + jobService.uploadManifest(manifest, chainId), + ).rejects.toThrowError( + new BadGatewayException(ErrorBucket.UnableSaveFile), + ); + + expect(storageService.uploadFile).toHaveBeenCalled(); + expect( + JSON.parse( + await encryption.decrypt( + (storageService.uploadFile as any).mock.calls[0][0], + ), + ), + ).toEqual(manifest); + }); + + it('should rethrow any other errors encountered', async () => { + const errorMessage = 'Something went wrong'; + const uploadError = new Error(errorMessage); + + uploadFilesMock.mockRejectedValue(uploadError); + + await expect( + jobService.uploadManifest(manifest, chainId), + ).rejects.toThrowError(new Error(errorMessage)); + expect(storageService.uploadFile).toHaveBeenCalled(); + expect( + JSON.parse( + await encryption.decrypt( + (storageService.uploadFile as any).mock.calls[0][0], + ), + ), + ).toEqual(manifest); + }); + }); + + describe('uploadManifest without encryption', () => { + const chainId = ChainId.LOCALHOST; + const fortuneManifestParams = { + requestType: JobRequestType.FORTUNE, + submissionsRequired: MOCK_SUBMISSION_REQUIRED, + requesterDescription: MOCK_REQUESTER_DESCRIPTION, + fundAmount: 10, + requesterTitle: MOCK_REQUESTER_TITLE, + }; + + let uploadFilesMock: any; + + beforeEach(() => { + uploadFilesMock = jest.spyOn(storageService, 'uploadFile'); + }); + + beforeAll(() => { + (KVStoreClient.build as any).mockImplementation(() => ({ + get: jest.fn().mockResolvedValue(MOCK_PGP_PUBLIC_KEY), + })); + encrypt = false; + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + afterAll(() => { + encrypt = true; + }); + + it('should save the manifest and return the manifest URL and hash', async () => { + uploadFilesMock.mockResolvedValue([ + { + url: MOCK_FILE_URL, + hash: MOCK_FILE_HASH, + }, + ]); + + const result = await jobService.uploadManifest( + fortuneManifestParams, + chainId, + ); + + expect(result).toEqual([ + { + url: MOCK_FILE_URL, + hash: MOCK_FILE_HASH, + }, + ]); + + expect(storageService.uploadFile).toHaveBeenCalled(); + expect((storageService.uploadFile as any).mock.calls[0][0]).toEqual( + fortuneManifestParams, + ); + }); + + it('should throw an error if the manifest file fails to upload', async () => { + const uploadError = new Error(ErrorBucket.UnableSaveFile); + + uploadFilesMock.mockRejectedValue(uploadError); + + await expect( + jobService.uploadManifest(fortuneManifestParams, chainId), + ).rejects.toThrowError( + new BadGatewayException(ErrorBucket.UnableSaveFile), + ); + + expect(storageService.uploadFile).toHaveBeenCalled(); + expect((storageService.uploadFile as any).mock.calls[0][0]).toEqual( + fortuneManifestParams, + ); + }); + + it('should rethrow any other errors encountered', async () => { + const errorMessage = 'Something went wrong'; + const uploadError = new Error(errorMessage); + + uploadFilesMock.mockRejectedValue(uploadError); + + await expect( + jobService.uploadManifest(fortuneManifestParams, chainId), + ).rejects.toThrowError(new Error(errorMessage)); + + expect(storageService.uploadFile).toHaveBeenCalled(); + expect((storageService.uploadFile as any).mock.calls[0][0]).toEqual( + fortuneManifestParams, + ); + }); + }); + describe('getResult', () => { let downloadFileFromUrlMock: any; const jobEntityMock = { 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 78c19fb83f..cdc4b1d8c6 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 @@ -7,9 +7,9 @@ import { EscrowUtils, NETWORKS, StakingClient, - KVStoreKeys, + StorageParams, Encryption, - EncryptionUtils, + KVStoreKeys, } from '@human-protocol/sdk'; import { v4 as uuidv4 } from 'uuid'; import { @@ -27,6 +27,7 @@ import { ethers } from 'ethers'; import { In, LessThanOrEqual, QueryFailedError } from 'typeorm'; import { ConfigNames } from '../../common/config'; import { + ErrorBucket, ErrorEscrow, ErrorJob, ErrorPayment, @@ -47,12 +48,7 @@ import { PaymentType, TokenId, } from '../../common/enums/payment'; -import { - isPGPMessage, - getRate, - hashString, - isValidJSON, -} from '../../common/utils'; +import { isPGPMessage, getRate, isValidJSON } from '../../common/utils'; import { add, div, lt, mul } from '../../common/utils/decimal'; import { PaymentRepository } from '../payment/payment.repository'; import { PaymentService } from '../payment/payment.service'; @@ -111,10 +107,13 @@ import { generateBucketUrl, listObjectsInBucket, } from '../../common/utils/storage'; +import * as crypto from 'crypto'; @Injectable() export class JobService { public readonly logger = new Logger(JobService.name); + public readonly storageParams: StorageParams; + public readonly bucket: string; constructor( @Inject(Web3Service) @@ -124,6 +123,7 @@ export class JobService { private readonly paymentService: PaymentService, public readonly configService: ConfigService, private readonly routingProtocolService: RoutingProtocolService, + private readonly encryption: Encryption, private readonly storageService: StorageService, private readonly webhookService: WebhookService, private readonly cronJobService: CronJobService, @@ -397,7 +397,10 @@ export class JobService { }; }); - const hash = hashString(stringify(data)); + const hash = crypto + .createHash('sha1') + .update(stringify(data)) + .digest('hex'); const { url } = await this.storageService.uploadFile(data, hash); return url; } @@ -407,13 +410,15 @@ export class JobService { requestType: JobRequestType, dto: JobFortuneDto | JobCvatDto | JobCaptchaDto, ): Promise { - const { chainId } = dto; + let { chainId } = dto; if (chainId) { this.web3Service.validateChainId(chainId); + } else { + chainId = this.routingProtocolService.selectNetwork(); } - let manifestOrigin, manifestEncrypted, fundAmount; + let manifestOrigin, fundAmount; const rate = await getRate(Currency.USD, TokenId.HMT); if (requestType === JobRequestType.HCAPTCHA) { @@ -459,14 +464,6 @@ export class JobService { dto.annotations.typeOfJob, dto, ); - - manifestEncrypted = await EncryptionUtils.encrypt( - stringify(manifestOrigin), - [ - this.configService.get(ConfigNames.PGP_PUBLIC_KEY)!, - this.configService.get(ConfigNames.HCAPTCHA_PGP_PUBLIC_KEY)!, - ], - ); } else if (requestType == JobRequestType.FORTUNE) { // Fortune dto = dto as JobFortuneDto; @@ -480,14 +477,10 @@ export class JobService { tokenFundAmount, ); } - const hash = hashString(stringify(manifestOrigin)); - const { url } = await this.storageService.uploadFile( - manifestEncrypted || manifestOrigin, - hash, - ); + const { url, hash } = await this.uploadManifest(manifestOrigin, chainId); const jobEntity = await this.jobRepository.create({ - chainId: chainId ?? this.routingProtocolService.selectNetwork(), + chainId, userId, manifestUrl: url, manifestHash: hash, @@ -593,49 +586,22 @@ export class JobService { await this.validateManifest(manifest); - let recordingOracleConfigKey; - let exchangeOracleConfigKey; + const oracleAddresses = this.getOracleAddresses(manifest); - if ( - (manifest as FortuneManifestDto).requestType === JobRequestType.FORTUNE - ) { - recordingOracleConfigKey = ConfigNames.FORTUNE_RECORDING_ORACLE_ADDRESS; - exchangeOracleConfigKey = ConfigNames.FORTUNE_EXCHANGE_ORACLE_ADDRESS; - } else if ( - (manifest as HCaptchaManifestDto).job_mode === JobCaptchaMode.BATCH - ) { - recordingOracleConfigKey = ConfigNames.HCAPTCHA_ORACLE_ADDRESS; - exchangeOracleConfigKey = ConfigNames.HCAPTCHA_ORACLE_ADDRESS; - } else { - recordingOracleConfigKey = ConfigNames.CVAT_RECORDING_ORACLE_ADDRESS; - exchangeOracleConfigKey = ConfigNames.CVAT_EXCHANGE_ORACLE_ADDRESS; - } - - const recordingOracleAddress = this.configService.get( - recordingOracleConfigKey, - )!; - - const reputationOracleAddress = this.configService.get( - ConfigNames.REPUTATION_ORACLE_ADDRESS, - )!; - - const exchangeOracleAddress = this.configService.get( - exchangeOracleConfigKey, - )!; const escrowConfig = { - recordingOracle: recordingOracleAddress, - reputationOracle: reputationOracleAddress, - exchangeOracle: exchangeOracleAddress, + recordingOracle: oracleAddresses.recordingOracle, + reputationOracle: oracleAddresses.recordingOracle, + exchangeOracle: oracleAddresses.exchangeOracle, recordingOracleFee: await this.getOracleFee( - recordingOracleAddress, + oracleAddresses.recordingOracle, jobEntity.chainId, ), reputationOracleFee: await this.getOracleFee( - reputationOracleAddress, + oracleAddresses.reputationOracle, jobEntity.chainId, ), exchangeOracleFee: await this.getOracleFee( - exchangeOracleAddress, + oracleAddresses.exchangeOracle, jobEntity.chainId, ), manifestUrl: jobEntity.manifestUrl, @@ -701,6 +667,76 @@ export class JobService { await jobEntity.save(); } + private getOracleAddresses(manifest: any) { + let recordingOracleConfigKey; + let exchangeOracleConfigKey; + const oracleType = this.getOracleType(manifest); + if (oracleType === OracleType.FORTUNE) { + recordingOracleConfigKey = ConfigNames.FORTUNE_RECORDING_ORACLE_ADDRESS; + exchangeOracleConfigKey = ConfigNames.FORTUNE_EXCHANGE_ORACLE_ADDRESS; + } else if (oracleType === OracleType.HCAPTCHA) { + recordingOracleConfigKey = ConfigNames.HCAPTCHA_ORACLE_ADDRESS; + exchangeOracleConfigKey = ConfigNames.HCAPTCHA_ORACLE_ADDRESS; + } else { + recordingOracleConfigKey = ConfigNames.CVAT_RECORDING_ORACLE_ADDRESS; + exchangeOracleConfigKey = ConfigNames.CVAT_EXCHANGE_ORACLE_ADDRESS; + } + + const exchangeOracle = this.configService.get( + exchangeOracleConfigKey, + )!; + const recordingOracle = this.configService.get( + recordingOracleConfigKey, + )!; + const reputationOracle = this.configService.get( + ConfigNames.REPUTATION_ORACLE_ADDRESS, + )!; + + return { exchangeOracle, recordingOracle, reputationOracle }; + } + + public async uploadManifest( + manifest: FortuneManifestDto | CvatManifestDto | HCaptchaManifestDto, + chainId: ChainId, + ): Promise { + let manifestFile: any = manifest; + if (this.configService.get(ConfigNames.PGP_ENCRYPT)) { + const signer = this.web3Service.getSigner(chainId); + const kvstore = await KVStoreClient.build(signer); + const publicKeys: string[] = [ + await kvstore.get(signer.address, KVStoreKeys.publicKey), + ]; + const oracleAddresses = this.getOracleAddresses( + (manifest as FortuneManifestDto).requestType, + ); + for (const address in Object.values(oracleAddresses)) { + const publicKey = await kvstore.get(address, KVStoreKeys.publicKey); + if (publicKey) publicKeys.push(publicKey); + } + + const encryptedManifest = await this.encryption.signAndEncrypt( + JSON.stringify(manifest), + publicKeys, + ); + manifestFile = encryptedManifest; + } + const hash = crypto + .createHash('sha1') + .update(stringify(manifestFile)) + .digest('hex'); + const uploadedFile = await this.storageService.uploadFile( + manifestFile, + hash, + ); + + if (!uploadedFile) { + this.logger.log(ErrorBucket.UnableSaveFile, JobService.name); + throw new BadRequestException(ErrorBucket.UnableSaveFile); + } + + return uploadedFile; + } + private async validateManifest( manifest: FortuneManifestDto | CvatManifestDto | HCaptchaManifestDto, ): Promise { @@ -1173,7 +1209,7 @@ export class JobService { private getOracleType(manifest: any): OracleType { if ( - (manifest as FortuneManifestDto).requestType === JobRequestType.FORTUNE + (manifest as FortuneManifestDto)?.requestType === JobRequestType.FORTUNE ) { return OracleType.FORTUNE; } else if ( diff --git a/packages/apps/job-launcher/server/test/constants.ts b/packages/apps/job-launcher/server/test/constants.ts index fad767305b..ff788560d6 100644 --- a/packages/apps/job-launcher/server/test/constants.ts +++ b/packages/apps/job-launcher/server/test/constants.ts @@ -67,44 +67,32 @@ export const MOCK_MANIFEST: FortuneManifestDto = { export const MOCK_ENCRYPTED_MANIFEST = 'encryptedManifest'; export const MOCK_PGP_PRIVATE_KEY = `-----BEGIN PGP PRIVATE KEY BLOCK----- -xVgEZTveLhYJKwYBBAHaRw8BAQdAD7pTrDk129glXoPH+rMofQwsQuyuo1j0 -Q1fXrBmoPwcAAP4qsdYoTBZ7fXhaP9sjJMhGtYLg2Ux7ODQ5Ix6Ws7/V4A/M -zRZldWdlbmUgPGV1Z2VuZUBobXQuYWk+wowEEBYKAD4FgmU73i4ECwkHCAmQ -+tuf/UifrmMDFQgKBBYAAgECGQECmwMCHgEWIQRihZ0fm6KM8/bPWt7625/9 -SJ+uYwAAq/0BAOJ3WYERhsp2xSbS45Gixp9QGsOCC1Ef2TiUlO2R3vDAAP9Q -1snuCth7bM1EdJBsIYJPuGP0CL0Nv4hzZm0KcNxtDsddBGU73i4SCisGAQQB -l1UBBQEBB0B5C9OzSIcuiWkhkzO5xyc5k4pp0JRqGLs81d7OCWUgSQMBCAcA -AP9wcvfUXUNks+0ggHgI7iZTy7q1slQT8YUh+oyxU6UheBGJwngEGBYIACoF -gmU73i4JkPrbn/1In65jApsMFiEEYoWdH5uijPP2z1re+tuf/UifrmMAAMZQ -AQDGveM6dh1co+7vkCsYs4vCoDBRQcSByD6FSbBmq8JJugD7Bpk0bm95Oi9M -UJgXNi74umGlLKtWNrbJIYiY1yHFSA4= -=27X/ +xVgEZS6w6BYJKwYBBAHaRw8BAQdAXRzFR1ROwdb4Bu7RKYXcBvJsH6JmBxiT +Zwbnk3KUBiUAAP9N8d16MWV/M+yggH6cTODDCNCDV/Ic012RP0fTI4VEjhFF +zRtKb2IgTGF1bmNoZXIgPGFkbWluQGhtdC5haT7CjAQQFgoAPgWCZS6w6AQL +CQcICZBAfiPaLRaJeAMVCAoEFgACAQIZAQKbAwIeARYhBNvDQnyGS7m0aAs+ +BUB+I9otFol4AACYcAEA/c1peyz3aWsB9NkvOfy/erdqkNAAHfikCKzGRKtD +sKcA/Rk9IHYRBzrvXyXXpFYkeFR1H6dXTUYzoZy8xoFleSIOx10EZS6w6BIK +KwYBBAGXVQEFAQEHQPOpvDe3nptX0ZqcFsUz/K7HHnSSOIn/aGYfrZfKAwQ1 +AwEIBwAA/3fQjUAKIaoDAQTB2Jufw9g1JBybjMXSb3YCWunTB6ZgD5vCeAQY +FggAKgWCZS6w6AmQQH4j2i0WiXgCmwwWIQTbw0J8hku5tGgLPgVAfiPaLRaJ +eAAAYHMBAPI7LdZ8k4lQBvlXjVMV3hlkQGtKp+EXHd3BaT1hpniVAP4wecxi +7jxPB0Thko1w1Ro6ZYsFtlOB52qocYtduLJkCA== +=qV+I -----END PGP PRIVATE KEY BLOCK-----`; -export const MOCK_PGP_PUBLIC_KEY = `-----BEGIN PGP PUBLIC KEY BLOCK----- -xjMEZTveLhYJKwYBBAHaRw8BAQdAD7pTrDk129glXoPH+rMofQwsQuyuo1j0 -Q1fXrBmoPwfNFmV1Z2VuZSA8ZXVnZW5lQGhtdC5haT7CjAQQFgoAPgWCZTve -LgQLCQcICZD625/9SJ+uYwMVCAoEFgACAQIZAQKbAwIeARYhBGKFnR+boozz -9s9a3vrbn/1In65jAACr/QEA4ndZgRGGynbFJtLjkaLGn1Aaw4ILUR/ZOJSU -7ZHe8MAA/1DWye4K2HtszUR0kGwhgk+4Y/QIvQ2/iHNmbQpw3G0OzjgEZTve -LhIKKwYBBAGXVQEFAQEHQHkL07NIhy6JaSGTM7nHJzmTimnQlGoYuzzV3s4J -ZSBJAwEIB8J4BBgWCAAqBYJlO94uCZD625/9SJ+uYwKbDBYhBGKFnR+boozz -9s9a3vrbn/1In65jAADGUAEAxr3jOnYdXKPu75ArGLOLwqAwUUHEgcg+hUmw -ZqvCSboA+waZNG5veTovTFCYFzYu+LphpSyrVja2ySGImNchxUgO -=uLQK ------END PGP PUBLIC KEY BLOCK-----`; -export const MOCK_HCAPTCHA_PGP_PUBLIC_KEY = `-----BEGIN PGP PUBLIC KEY BLOCK----- +export const MOCK_PGP_PUBLIC_KEY = `-----BEGIN PGP PUBLIC KEY BLOCK----- -xjMEZTFB7RYJKwYBBAHaRw8BAQdAEqnF7yvbnHaL5nM7uryCts/FAnBazgBA -ldusotlPEgnNGWFsaWR6bSA8cWFyejg5QGdtYWlsLmNvbT7CjAQQFgoAPgWC -ZTFB7QQLCQcICZCyJEVcrn3KbAMVCAoEFgACAQIZAQKbAwIeARYhBCWadFbn -oT02XD9wsbIkRVyufcpsAAA02AD/bTo/OX+PceOMfWgQlK4KrUTrrEFayWgL -RODAqZIVFXABAK/q1P1t54pSXmZs1p76LR9eLkzWpXzxs1UjYUlJPzEPzjgE -ZTFB7RIKKwYBBAGXVQEFAQEHQB8t/IZJLOiA0erKV7qyXWpvdiUegoDpdeDU -68nBw1tiAwEIB8J4BBgWCAAqBYJlMUHtCZCyJEVcrn3KbAKbDBYhBCWadFbn -oT02XD9wsbIkRVyufcpsAADvXAEAu9cf+VXbCe5Kj+7G3gRQnO+smX/gySHj -Cj8wO9Ii68YA/1EpYseshTKcNncCad8Npro313/PpE3SzsCP1b+58mkD -=XWPr +xjMEZS6w6BYJKwYBBAHaRw8BAQdAXRzFR1ROwdb4Bu7RKYXcBvJsH6JmBxiT +Zwbnk3KUBiXNG0pvYiBMYXVuY2hlciA8YWRtaW5AaG10LmFpPsKMBBAWCgA+ +BYJlLrDoBAsJBwgJkEB+I9otFol4AxUICgQWAAIBAhkBApsDAh4BFiEE28NC +fIZLubRoCz4FQH4j2i0WiXgAAJhwAQD9zWl7LPdpawH02S85/L96t2qQ0AAd ++KQIrMZEq0OwpwD9GT0gdhEHOu9fJdekViR4VHUfp1dNRjOhnLzGgWV5Ig7O +OARlLrDoEgorBgEEAZdVAQUBAQdA86m8N7eem1fRmpwWxTP8rscedJI4if9o +Zh+tl8oDBDUDAQgHwngEGBYIACoFgmUusOgJkEB+I9otFol4ApsMFiEE28NC +fIZLubRoCz4FQH4j2i0WiXgAAGBzAQDyOy3WfJOJUAb5V41TFd4ZZEBrSqfh +Fx3dwWk9YaZ4lQD+MHnMYu48TwdE4ZKNcNUaOmWLBbZTgedqqHGLXbiyZAg= +=IMAe -----END PGP PUBLIC KEY BLOCK-----`; export const MOCK_HCAPTCHA_ORACLE_ADDRESS = '0xa62a1c18571b869e43eeabd217e233e7f0275af3'; From 745dfb15e9a2dc4b3b086ed621fcd07362086131 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20L=C3=B3pez?= <50665615+flopez7@users.noreply.github.com> Date: Thu, 18 Jan 2024 11:08:43 +0100 Subject: [PATCH 041/104] [Job Launcher] Convert to snake case (#1478) * Convert job launcher server ingoing and outgoing messages to snake_case * Convert job launcher client ingoing and outgoing messages to snake_case --- .../apps/job-launcher/client/src/utils/api.ts | 17 +- .../client/src/utils/case-converter.ts | 37 ++++ .../job-launcher/server/src/app.module.ts | 7 +- .../src/common/interceptors/snake-case.ts | 28 +++ .../server/src/common/utils/case-converter.ts | 31 +++ .../server/src/modules/auth/auth.dto.ts | 4 +- .../server/src/modules/job/job.controller.ts | 6 +- .../server/src/modules/job/job.dto.ts | 179 ++++++++---------- .../server/src/modules/job/job.service.ts | 6 +- .../server/src/modules/payment/payment.dto.ts | 5 +- .../server/src/modules/webhook/webhook.dto.ts | 27 ++- .../modules/webhook/webhook.service.spec.ts | 12 +- .../src/modules/webhook/webhook.service.ts | 23 +-- 13 files changed, 252 insertions(+), 130 deletions(-) create mode 100644 packages/apps/job-launcher/client/src/utils/case-converter.ts create mode 100644 packages/apps/job-launcher/server/src/common/interceptors/snake-case.ts create mode 100644 packages/apps/job-launcher/server/src/common/utils/case-converter.ts diff --git a/packages/apps/job-launcher/client/src/utils/api.ts b/packages/apps/job-launcher/client/src/utils/api.ts index 975478d9d7..3c926dc358 100644 --- a/packages/apps/job-launcher/client/src/utils/api.ts +++ b/packages/apps/job-launcher/client/src/utils/api.ts @@ -1,4 +1,5 @@ import axios from 'axios'; +import { CaseConverter } from './case-converter'; const axiosInstance = axios.create({ baseURL: import.meta.env.VITE_APP_JOB_LAUNCHER_SERVER_URL, @@ -10,13 +11,27 @@ axiosInstance.interceptors.request.use( if (accessToken) { config.headers['Authorization'] = `Bearer ${accessToken}`; } + + if (config.data) { + config.data = CaseConverter.transformToSnakeCase(config.data); + } + + if (config.params) { + config.params = CaseConverter.transformToSnakeCase(config.params); + } + return config; }, (error) => Promise.reject(error), ); axiosInstance.interceptors.response.use( - (response) => response, + (response) => { + if (response.data) { + response.data = CaseConverter.transformToCamelCase(response.data); + } + return response; + }, (error) => { if (error?.response?.status === 401) { const message = error?.response?.data?.message; diff --git a/packages/apps/job-launcher/client/src/utils/case-converter.ts b/packages/apps/job-launcher/client/src/utils/case-converter.ts new file mode 100644 index 0000000000..54bd31c904 --- /dev/null +++ b/packages/apps/job-launcher/client/src/utils/case-converter.ts @@ -0,0 +1,37 @@ +export class CaseConverter { + static transformToCamelCase(obj: any): any { + if (Array.isArray(obj)) { + return obj.map((item) => CaseConverter.transformToCamelCase(item)); + } else if (typeof obj === 'object' && obj !== null) { + return Object.keys(obj).reduce( + (acc: Record, key: string) => { + const camelCaseKey = key.replace(/_([a-z])/g, (g) => + g[1].toUpperCase(), + ); + acc[camelCaseKey] = CaseConverter.transformToCamelCase(obj[key]); + return acc; + }, + {}, + ); + } else { + return obj; + } + } + + static transformToSnakeCase(obj: any): any { + if (Array.isArray(obj)) { + return obj.map((item) => CaseConverter.transformToSnakeCase(item)); + } else if (typeof obj === 'object' && obj !== null) { + return Object.keys(obj).reduce( + (acc: Record, key: string) => { + const snakeCaseKey = key.replace(/([A-Z])/g, '_$1').toLowerCase(); + acc[snakeCaseKey] = CaseConverter.transformToSnakeCase(obj[key]); + return acc; + }, + {}, + ); + } else { + return obj; + } + } +} diff --git a/packages/apps/job-launcher/server/src/app.module.ts b/packages/apps/job-launcher/server/src/app.module.ts index 058816fd11..3aa4a85a7f 100644 --- a/packages/apps/job-launcher/server/src/app.module.ts +++ b/packages/apps/job-launcher/server/src/app.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { APP_GUARD, APP_PIPE } from '@nestjs/core'; +import { APP_GUARD, APP_INTERCEPTOR, APP_PIPE } from '@nestjs/core'; import { ConfigModule } from '@nestjs/config'; import { ScheduleModule } from '@nestjs/schedule'; import { AppController } from './app.controller'; @@ -17,6 +17,7 @@ import { ServeStaticModule } from '@nestjs/serve-static'; import { join } from 'path'; import { StorageModule } from './modules/storage/storage.module'; import { CronJobModule } from './modules/cron-job/cron-job.module'; +import { SnakeCaseInterceptor } from './common/interceptors/snake-case'; @Module({ providers: [ @@ -28,6 +29,10 @@ import { CronJobModule } from './modules/cron-job/cron-job.module'; provide: APP_PIPE, useClass: HttpValidationPipe, }, + { + provide: APP_INTERCEPTOR, + useClass: SnakeCaseInterceptor, + }, ], imports: [ ScheduleModule.forRoot(), diff --git a/packages/apps/job-launcher/server/src/common/interceptors/snake-case.ts b/packages/apps/job-launcher/server/src/common/interceptors/snake-case.ts new file mode 100644 index 0000000000..0d62e5f222 --- /dev/null +++ b/packages/apps/job-launcher/server/src/common/interceptors/snake-case.ts @@ -0,0 +1,28 @@ +import { + CallHandler, + ExecutionContext, + Injectable, + NestInterceptor, +} from '@nestjs/common'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { CaseConverter } from '../utils/case-converter'; + +@Injectable() +export class SnakeCaseInterceptor implements NestInterceptor { + intercept(context: ExecutionContext, next: CallHandler): Observable { + const request = context.switchToHttp().getRequest(); + + if (request.body) { + request.body = CaseConverter.transformToCamelCase(request.body); + } + + if (request.query) { + request.query = CaseConverter.transformToCamelCase(request.query); + } + + return next + .handle() + .pipe(map((data) => CaseConverter.transformToSnakeCase(data))); + } +} diff --git a/packages/apps/job-launcher/server/src/common/utils/case-converter.ts b/packages/apps/job-launcher/server/src/common/utils/case-converter.ts new file mode 100644 index 0000000000..980ddc589a --- /dev/null +++ b/packages/apps/job-launcher/server/src/common/utils/case-converter.ts @@ -0,0 +1,31 @@ +export class CaseConverter { + static transformToCamelCase(obj: Record): Record { + const result: Record = {}; + for (const key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + const camelCaseKey = key.replace(/_([a-z])/g, (g) => + g[1].toUpperCase(), + ); + result[camelCaseKey] = obj[key]; + } + } + return result; + } + + static transformToSnakeCase(obj: any): any { + if (Array.isArray(obj)) { + return obj.map((item) => CaseConverter.transformToSnakeCase(item)); + } else if (typeof obj === 'object' && obj !== null) { + return Object.keys(obj).reduce( + (acc: Record, key: string) => { + const snakeCaseKey = key.replace(/([A-Z])/g, '_$1').toLowerCase(); + acc[snakeCaseKey] = CaseConverter.transformToSnakeCase(obj[key]); + return acc; + }, + {}, + ); + } else { + return obj; + } + } +} diff --git a/packages/apps/job-launcher/server/src/modules/auth/auth.dto.ts b/packages/apps/job-launcher/server/src/modules/auth/auth.dto.ts index c51e130baa..467120f6c4 100644 --- a/packages/apps/job-launcher/server/src/modules/auth/auth.dto.ts +++ b/packages/apps/job-launcher/server/src/modules/auth/auth.dto.ts @@ -1,6 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; import { Transform } from 'class-transformer'; -import { IsEmail, IsString, Matches } from 'class-validator'; +import { IsEmail, IsString } from 'class-validator'; import { IsConfirm, IsPassword } from '../../common/validators'; import { TokenType } from '../auth/token.entity'; import { UserEntity } from '../user/user.entity'; @@ -74,7 +74,7 @@ export class TokenCreateDto { } export class ApiKeyDto { - @ApiProperty() + @ApiProperty({ name: 'api_key' }) @IsString() public apiKey: string; } diff --git a/packages/apps/job-launcher/server/src/modules/job/job.controller.ts b/packages/apps/job-launcher/server/src/modules/job/job.controller.ts index d1a87010b3..1af82db9a4 100644 --- a/packages/apps/job-launcher/server/src/modules/job/job.controller.ts +++ b/packages/apps/job-launcher/server/src/modules/job/job.controller.ts @@ -27,7 +27,6 @@ import { JobCvatDto, JobListDto, JobCancelDto, - EscrowFailedWebhookDto, JobDetailsDto, JobIdDto, FortuneFinalResultDto, @@ -39,6 +38,7 @@ import { Public, ApiKey } from '../../common/decorators'; import { HEADER_SIGNATURE_KEY } from '../../common/constants'; import { ChainId } from '@human-protocol/sdk'; import { Role } from '../../common/enums/role'; +import { WebhookDataDto } from '../webhook/webhook.dto'; @ApiBearerAuth() @UseGuards(JwtAuthGuard) @@ -180,7 +180,7 @@ export class JobController { @Get('/result') public async getResult( @Request() req: RequestWithUser, - @Query('jobId') jobId: number, + @Query('job_id') jobId: number, ): Promise { return this.jobService.getResult(req.user.id, jobId); } @@ -303,7 +303,7 @@ export class JobController { @Post('/escrow-failed-webhook') public async handleEscrowFailedWebhook( @Headers(HEADER_SIGNATURE_KEY) _: string, - @Body() data: EscrowFailedWebhookDto, + @Body() data: WebhookDataDto, ): Promise { await this.jobService.escrowFailedWebhook(data); return; diff --git a/packages/apps/job-launcher/server/src/modules/job/job.dto.ts b/packages/apps/job-launcher/server/src/modules/job/job.dto.ts index b70ce7f442..367c68260f 100644 --- a/packages/apps/job-launcher/server/src/modules/job/job.dto.ts +++ b/packages/apps/job-launcher/server/src/modules/job/job.dto.ts @@ -31,7 +31,6 @@ import { WorkerLanguage, WorkerLocation, } from '../../common/enums/job'; -import { EventType } from '../../common/enums/webhook'; import { AWSRegions, StorageProviders } from '../../common/enums/storage'; export class JobCreateDto { @ApiProperty({ enum: ChainId }) @@ -75,27 +74,27 @@ export class JobCreateDto { } export class JobDto { - @ApiProperty({ enum: ChainId, required: false }) + @ApiProperty({ enum: ChainId, required: false, name: 'chain_id' }) @IsEnum(ChainId) @IsOptional() public chainId?: ChainId; } export class JobFortuneDto extends JobDto { - @ApiProperty() + @ApiProperty({ name: 'requester_title' }) @IsString() public requesterTitle: string; - @ApiProperty() + @ApiProperty({ name: 'requester_description' }) @IsString() public requesterDescription: string; - @ApiProperty() + @ApiProperty({ name: 'submissions_required' }) @IsNumber() @IsPositive() public submissionsRequired: number; - @ApiProperty() + @ApiProperty({ name: 'fund_amount' }) @IsNumber() @IsPositive() public fundAmount: number; @@ -105,12 +104,15 @@ export class StorageDataDto { @ApiProperty({ enum: StorageProviders }) @IsEnum(StorageProviders) public provider: StorageProviders; + @ApiProperty({ enum: AWSRegions }) @IsEnum(AWSRegions) public region: AWSRegions | null; - @ApiProperty() + + @ApiProperty({ name: 'bucket_name' }) @IsString() public bucketName: string; + @ApiProperty() @IsOptional() @IsString() @@ -118,7 +120,7 @@ export class StorageDataDto { } export class JobCvatDto extends JobDto { - @ApiProperty() + @ApiProperty({ name: 'requester_description' }) @IsString() public requesterDescription: string; @@ -131,24 +133,24 @@ export class JobCvatDto extends JobDto { @ArrayMinSize(1) public labels: string[]; - @ApiProperty() + @ApiProperty({ name: 'min_quality' }) @IsNumber() @IsPositive() public minQuality: number; - @ApiProperty() + @ApiProperty({ name: 'ground_truth' }) @IsObject() public groundTruth: StorageDataDto; - @ApiProperty() + @ApiProperty({ name: 'user_guide' }) @IsUrl() public userGuide: string; @ApiProperty({ enum: JobRequestType }) @IsEnum(JobRequestType) - type: JobRequestType; + public type: JobRequestType; - @ApiProperty() + @ApiProperty({ name: 'fund_amount' }) @IsNumber() @IsPositive() public fundAmount: number; @@ -197,7 +199,7 @@ export class StakingDetails { } export class ManifestDetails { - @ApiProperty({ description: 'Chain ID' }) + @ApiProperty({ description: 'Chain ID', name: 'chain_id' }) @IsNumber() @Min(1) public chainId: number; @@ -212,39 +214,57 @@ export class ManifestDetails { @IsString() public description?: string; - @ApiProperty({ description: 'Submissions required' }) + @ApiProperty({ + description: 'Submissions required', + name: 'submissions_required', + }) @IsNumber() public submissionsRequired: number; - @ApiProperty({ description: 'Ethereum address of the token' }) + @ApiProperty({ + description: 'Ethereum address of the token', + name: 'token_address', + }) @IsEthereumAddress() public tokenAddress: string; - @ApiProperty({ description: 'Amount of funds' }) + @ApiProperty({ description: 'Amount of funds', name: 'fund_amount' }) @IsNumber() public fundAmount: number; - @ApiProperty({ description: 'Ethereum address of the requester' }) + @ApiProperty({ + description: 'Ethereum address of the requester', + name: 'requester_address', + }) @IsEthereumAddress() public requesterAddress: string; - @ApiProperty({ description: 'Request type' }) + @ApiProperty({ description: 'Request type', name: 'request_type' }) @IsEnum(JobRequestType) public requestType: JobRequestType; - @ApiProperty({ description: 'Address of the exchange oracle (optional)' }) + @ApiProperty({ + description: 'Address of the exchange oracle (optional)', + name: 'exchange_oracle_address', + }) @IsOptional() @IsNotEmpty() @IsString() public exchangeOracleAddress?: string; - @ApiProperty({ description: 'Address of the recording oracle (optional)' }) + @ApiProperty({ + description: 'Address of the recording oracle (optional)', + name: 'recording_oracle_address', + }) @IsOptional() @IsNotEmpty() @IsString() public recordingOracleAddress?: string; - @ApiProperty({ description: 'Address of the reputation oracle (optional)' }) + @ApiProperty({ + description: 'Address of the reputation oracle (optional)', + name: 'reputation_oracle_address', + }) @IsOptional() @IsNotEmpty() @IsString() @@ -252,15 +272,24 @@ export class ManifestDetails { } export class CommonDetails { - @ApiProperty({ description: 'Ethereum address of the escrow' }) + @ApiProperty({ + description: 'Ethereum address of the escrow', + name: 'escrow_address', + }) @IsEthereumAddress() public escrowAddress: string; - @ApiProperty({ description: 'URL of the manifest' }) + @ApiProperty({ + description: 'URL of the manifest', + name: 'manifest_url', + }) @IsUrl() public manifestUrl: string; - @ApiProperty({ description: 'Hash of the manifest' }) + @ApiProperty({ + description: 'Hash of the manifest', + name: 'manifest_hash', + }) @IsString() public manifestHash: string; @@ -269,12 +298,18 @@ export class CommonDetails { @Min(0) public balance: number; - @ApiProperty({ description: 'Amount paid out' }) + @ApiProperty({ + description: 'Amount paid out', + name: 'paid_out', + }) @IsNumber() @Min(0) public paidOut: number; - @ApiProperty({ description: 'Number of tasks (optional)' }) + @ApiProperty({ + description: 'Number of tasks (optional)', + name: 'amount_of_tasks', + }) @IsNumber() public amountOfTasks?: number; @@ -297,60 +332,26 @@ export class JobDetailsDto { public staking: StakingDetails; } -export class SaveManifestDto { - @ApiProperty() - public manifestUrl: string; - - @ApiProperty() - public manifestHash: string; -} - -export class FortuneWebhookDto { - @ApiProperty({ enum: ChainId }) - @IsEnum(ChainId) - public chainId: ChainId; - - @ApiProperty() - public escrowAddress: string; - - @ApiProperty({ enum: EventType }) - @IsEnum(EventType) - public eventType: EventType; -} - -export class CVATWebhookDto { - @ApiProperty() - public escrow_address: string; - - @ApiProperty({ enum: ChainId }) - @IsEnum(ChainId) - public chain_id: number; - - @ApiProperty({ enum: EventType }) - @IsEnum(EventType) - public event_type: EventType; -} - export class FortuneManifestDto { - @ApiProperty() + @ApiProperty({ name: 'submissions_required' }) @IsNumber() @IsPositive() public submissionsRequired: number; - @ApiProperty() + @ApiProperty({ name: 'requester_title' }) @IsString() public requesterTitle: string; - @ApiProperty() + @ApiProperty({ name: 'requester_description' }) @IsString() public requesterDescription: string; - @ApiProperty() + @ApiProperty({ name: 'fund_amount' }) @IsNumber() @IsPositive() public fundAmount: number; - @ApiProperty({ enum: JobRequestType }) + @ApiProperty({ enum: JobRequestType, name: 'request_type' }) @IsEnum(JobRequestType) public requestType: JobRequestType; } @@ -430,7 +431,7 @@ export class CvatManifestDto { } export class FortuneFinalResultDto { - @ApiProperty() + @ApiProperty({ name: 'worker_address' }) @IsNotEmpty() @IsString() public workerAddress: string; @@ -465,40 +466,22 @@ export class CvatFinalResultDto { } export class JobListDto { - @ApiProperty() + @ApiProperty({ name: 'job_id' }) public jobId: number; - @ApiProperty({ required: false }) + @ApiProperty({ required: false, name: 'escrow_address' }) public escrowAddress?: string; @ApiProperty() public network: string; - @ApiProperty() + @ApiProperty({ name: 'fund_amount' }) public fundAmount: number; @ApiProperty() public status: JobStatus; } -export class EscrowFailedWebhookDto { - @ApiProperty({ enum: ChainId }) - @IsEnum(ChainId) - public chainId: ChainId; - - @ApiProperty() - @IsString() - public escrowAddress: string; - - @ApiProperty({ enum: EventType }) - @IsEnum(EventType) - public eventType: EventType; - - @ApiProperty() - @IsString() - public reason: string; -} - export class EscrowCancelDto { @ApiProperty() public txHash: string; @@ -510,6 +493,7 @@ export class EscrowCancelDto { export class JobCaptchaAdvancedDto { @ApiProperty({ enum: WorkerLanguage, + name: 'worker_language', }) @IsEnum(WorkerLanguage) @IsOptional() @@ -517,6 +501,7 @@ export class JobCaptchaAdvancedDto { @ApiProperty({ enum: WorkerLocation, + name: 'workerocation', }) @IsEnum(WorkerLocation) @IsOptional() @@ -524,6 +509,7 @@ export class JobCaptchaAdvancedDto { @ApiProperty({ enum: WorkerBrowser, + name: 'target_browser', }) @IsEnum(WorkerBrowser) @IsOptional() @@ -533,11 +519,12 @@ export class JobCaptchaAdvancedDto { export class JobCaptchaAnnotationsDto { @ApiProperty({ enum: JobCaptchaShapeType, + name: 'type_of_job', }) @IsEnum(JobCaptchaShapeType) typeOfJob: JobCaptchaShapeType; - @ApiProperty() + @ApiProperty({ name: 'task_bid_price' }) @IsNumber() @IsPositive() taskBidPrice: number; @@ -547,15 +534,15 @@ export class JobCaptchaAnnotationsDto { @IsString() label?: string; - @ApiProperty() + @ApiProperty({ name: 'labeling_prompt' }) @IsString() labelingPrompt: string; - @ApiProperty() + @ApiProperty({ name: 'ground_truths' }) @IsString() groundTruths: string; - @ApiProperty() + @ApiProperty({ name: 'example_images' }) @IsOptional() @IsArray() exampleImages?: string[]; @@ -566,24 +553,24 @@ export class JobCaptchaDto extends JobDto { @IsUrl() data: StorageDataDto; - @ApiProperty() + @ApiProperty({ name: 'accuracy_target' }) @IsNumber() @IsPositive() @Max(1) accuracyTarget: number; - @ApiProperty() + @ApiProperty({ name: 'completion_date' }) @IsDateString() @IsOptional() completionDate: Date; - @ApiProperty() + @ApiProperty({ name: 'min_requests' }) @IsNumber() @IsPositive() @Max(100) minRequests: number; - @ApiProperty() + @ApiProperty({ name: 'max_requests' }) @IsNumber() @IsPositive() @Max(100) 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 cdc4b1d8c6..2429bb168f 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 @@ -56,7 +56,6 @@ import { Web3Service } from '../web3/web3.service'; import { CvatManifestDto, EscrowCancelDto, - EscrowFailedWebhookDto, FortuneFinalResultDto, FortuneManifestDto, JobCvatDto, @@ -107,6 +106,7 @@ import { generateBucketUrl, listObjectsInBucket, } from '../../common/utils/storage'; +import { WebhookDataDto } from '../webhook/webhook.dto'; import * as crypto from 'crypto'; @Injectable() @@ -1252,7 +1252,7 @@ export class JobService { }); } - public async escrowFailedWebhook(dto: EscrowFailedWebhookDto): Promise { + public async escrowFailedWebhook(dto: WebhookDataDto): Promise { if (dto.eventType !== EventType.TASK_CREATION_FAILED) { this.logger.log(ErrorJob.InvalidEventType, JobService.name); throw new BadRequestException(ErrorJob.InvalidEventType); @@ -1274,7 +1274,7 @@ export class JobService { } jobEntity.status = JobStatus.FAILED; - jobEntity.failedReason = dto.reason; + jobEntity.failedReason = dto.reason!; await jobEntity.save(); } diff --git a/packages/apps/job-launcher/server/src/modules/payment/payment.dto.ts b/packages/apps/job-launcher/server/src/modules/payment/payment.dto.ts index 6a70e57309..944dc55f50 100644 --- a/packages/apps/job-launcher/server/src/modules/payment/payment.dto.ts +++ b/packages/apps/job-launcher/server/src/modules/payment/payment.dto.ts @@ -9,7 +9,7 @@ import { import { ChainId } from '@human-protocol/sdk'; export class PaymentFiatConfirmDto { - @ApiProperty() + @ApiProperty({ name: 'payment_id' }) @IsString() public paymentId: string; } @@ -30,11 +30,12 @@ export class PaymentFiatCreateDto { export class PaymentCryptoCreateDto { @ApiProperty({ enum: ChainId, + name: 'chain_id', }) @IsEnum(ChainId) public chainId: ChainId; - @ApiProperty() + @ApiProperty({ name: 'transaction_hash' }) @IsString() public transactionHash: string; } diff --git a/packages/apps/job-launcher/server/src/modules/webhook/webhook.dto.ts b/packages/apps/job-launcher/server/src/modules/webhook/webhook.dto.ts index 4e3dd4c4cd..2386fb1424 100644 --- a/packages/apps/job-launcher/server/src/modules/webhook/webhook.dto.ts +++ b/packages/apps/job-launcher/server/src/modules/webhook/webhook.dto.ts @@ -1,6 +1,13 @@ import { ChainId } from '@human-protocol/sdk'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { IsEnum, IsString, IsDate, IsNumber, IsBoolean } from 'class-validator'; +import { + IsEnum, + IsString, + IsDate, + IsNumber, + IsBoolean, + IsOptional, +} from 'class-validator'; import { EventType, OracleType, @@ -93,3 +100,21 @@ export class UpdateWebhookDto { @IsDate() public waitUntil: Date; } + +export class WebhookDataDto { + @ApiProperty({ enum: ChainId, name: 'chain_id' }) + @IsEnum(ChainId) + public chainId: ChainId; + + @ApiProperty({ name: 'escrow_address' }) + public escrowAddress: string; + + @ApiProperty({ enum: EventType, name: 'event_type' }) + @IsEnum(EventType) + public eventType: EventType; + + @ApiProperty() + @IsString() + @IsOptional() + public reason?: string; +} diff --git a/packages/apps/job-launcher/server/src/modules/webhook/webhook.service.spec.ts b/packages/apps/job-launcher/server/src/modules/webhook/webhook.service.spec.ts index 45ab102464..11f2a77a83 100644 --- a/packages/apps/job-launcher/server/src/modules/webhook/webhook.service.spec.ts +++ b/packages/apps/job-launcher/server/src/modules/webhook/webhook.service.spec.ts @@ -207,9 +207,9 @@ describe('WebhookService', () => { expect(httpService.post).toHaveBeenCalledWith( MOCK_EXCHANGE_ORACLE_WEBHOOK_URL, { - escrowAddress: webhookEntity.escrowAddress, - chainId: webhookEntity.chainId, - eventType: webhookEntity.eventType, + escrow_address: webhookEntity.escrowAddress, + chain_id: webhookEntity.chainId, + event_type: webhookEntity.eventType, }, {}, ); @@ -258,9 +258,9 @@ describe('WebhookService', () => { expect(httpService.post).toHaveBeenCalledWith( MOCK_EXCHANGE_ORACLE_WEBHOOK_URL, { - escrowAddress: webhookEntity.escrowAddress, - chainId: webhookEntity.chainId, - eventType: webhookEntity.eventType, + escrow_address: webhookEntity.escrowAddress, + chain_id: webhookEntity.chainId, + event_type: webhookEntity.eventType, }, { headers: { [HEADER_SIGNATURE_KEY]: expect.any(String) } }, ); diff --git a/packages/apps/job-launcher/server/src/modules/webhook/webhook.service.ts b/packages/apps/job-launcher/server/src/modules/webhook/webhook.service.ts index 607a298b8f..c1bfc4039c 100644 --- a/packages/apps/job-launcher/server/src/modules/webhook/webhook.service.ts +++ b/packages/apps/job-launcher/server/src/modules/webhook/webhook.service.ts @@ -12,7 +12,6 @@ import { ConfigService } from '@nestjs/config'; import { ConfigNames } from '../../common/config'; import { signMessage } from '../../common/utils/signature'; import { WebhookRepository } from './webhook.repository'; -import { CVATWebhookDto, FortuneWebhookDto } from '../job/job.dto'; import { firstValueFrom } from 'rxjs'; import { DEFAULT_MAX_RETRY_COUNT, @@ -20,12 +19,13 @@ import { } from '../../common/constants'; import { HttpService } from '@nestjs/axios'; import { Web3Service } from '../web3/web3.service'; -import { OracleType, WebhookStatus } from '../../common/enums/webhook'; +import { WebhookStatus } from '../../common/enums/webhook'; import { ErrorWebhook } from '../../common/constants/errors'; import { WebhookEntity } from './webhook.entity'; -import { WebhookDto } from './webhook.dto'; import { CronJobService } from '../cron-job/cron-job.service'; import { CronJobType } from '../../common/enums/cron-job'; +import { WebhookDataDto, WebhookDto } from './webhook.dto'; +import { CaseConverter } from '../../common/utils/case-converter'; @Injectable() export class WebhookService { private readonly logger = new Logger(WebhookService.name); @@ -90,18 +90,11 @@ export class WebhookService { } // Build the webhook data object based on the oracle type. - const webhookData = - webhook.oracleType === OracleType.FORTUNE - ? ({ - escrowAddress: webhook.escrowAddress, - chainId: webhook.chainId, - eventType: webhook.eventType, - } as FortuneWebhookDto) - : ({ - escrow_address: webhook.escrowAddress, - chain_id: webhook.chainId, - event_type: webhook.eventType, - } as CVATWebhookDto); + const webhookData = CaseConverter.transformToSnakeCase({ + escrowAddress: webhook.escrowAddress, + chainId: webhook.chainId, + eventType: webhook.eventType, + } as WebhookDataDto); // Add the signature to the request body if necessary. if (webhook.hasSignature) { From 47b5f8b2beddd45f886e71d961c253670f989ae9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20L=C3=B3pez?= <50665615+flopez7@users.noreply.github.com> Date: Thu, 18 Jan 2024 13:51:48 +0100 Subject: [PATCH 042/104] [CVAT][Oracles] Fix CVAT oracles tests (#1144) * Fix test dockerfiles for CVAT Exchange and Recording oracles * Fix chain module and webhook service tests in exchange oracle * update integration tests for webhook service. * Add cvat service tests * fix tests for exchange oracle webhook handler. * fix web3 tests. * fix escrow tests (WIP). * Fix test dockerfiles for CVAT Exchange and Recording oracles * Fix chain module and webhook service tests in exchange oracle * update integration tests for webhook service. * fix tests for exchange oracle webhook handler. * fix web3 tests. * Add cvat service tests * fix escrow tests (WIP). * fix escrow tests. * Fix database tests in CVAT Exchange Oracle * Fix test dockerfiles for CVAT Exchange and Recording oracles * Fix chain module and webhook service tests in exchange oracle * update integration tests for webhook service. * fix tests for exchange oracle webhook handler. * fix web3 tests. * Add cvat service tests * fix escrow tests (WIP). * fix escrow tests. * Fix database tests in CVAT Exchange Oracle * fix kvstore tests. (WIP) * fix kvstore tests. * fix exo cron tests. * fix repo cron tests. * add tests for validation service. * add tests for client service. * add degenerate tests for client service. * Fix cvat exchange oracle api tests * add endpoint test. * Add exchange API tests * Add cron tests * Job launcher webhook api tests in CVAT Exchange Oracle * Add CVAT api test in Exchange Oracle * remove commented code * Remove unused dockerfile * Unit tests for the agreement module removed since we don't have them in src * Added .dockerignore file; Code reformatted with black tool; Minor fixes + 2 test cases were commented because of the bugs; * refactor API tests --------- Co-authored-by: Marius Hamacher Co-authored-by: Sergey Dzeranov --- .../cvat/exchange-oracle/.dockerignore | 4 + .../alembic/versions/16ecc586d685_init.py | 260 ++-- .../exchange-oracle/docker-compose.test.yml | 28 +- .../dockerfiles/blockchain-node.Dockerfile | 12 - .../dockerfiles/test.Dockerfile | 2 +- .../src/crons/state_trackers.py | 2 +- .../tests/api/test_cvat_webhook.py | 124 -- .../tests/api/test_cvat_webhook_api.py | 184 +++ .../tests/api/test_exchange_api.py | 341 +++++ .../tests/api/test_jl_webhook.py | 184 --- .../tests/api/test_webhook_api.py | 141 ++ .../tests/integration/chain/test_escrow.py | 177 ++- .../tests/integration/chain/test_kvstore.py | 90 +- .../tests/integration/chain/test_web3.py | 13 +- .../test_retrieve_annotations.py | 315 +++++ .../state_trackers/test_track_assignments.py | 152 ++ .../test_track_completed_projects.py | 0 .../test_track_completed_tasks.py | 3 - .../test_track_task_creation.py | 114 ++ .../test_process_job_launcher_webhooks.py | 389 ++++-- .../test_process_recording_oracle_webhooks.py | 292 ++-- .../cron/test_retrieve_annotations.py | 345 ----- .../tests/integration/services/test_cvat.py | 1224 +++++++++++++---- .../integration/services/test_exchange.py | 282 ++++ .../integration/services/test_webhook.py | 230 +++- .../unit/helpers/test_format_annotations.py | 62 - .../exchange-oracle/tests/utils/constants.py | 10 +- .../exchange-oracle/tests/utils/db_helper.py | 49 + .../exchange-oracle/tests/utils/manifest.json | 26 +- .../exchange-oracle/tests/utils/setup_cvat.py | 34 +- .../tests/utils/setup_escrow.py | 51 - .../tests/utils/setup_kvstore.py | 21 - .../dockerfiles/blockchain-node.Dockerfile | 2 +- .../dockerfiles/test.Dockerfile | 2 +- .../recording-oracle/src/services/__init__.py | 0 .../tests/integration/chain/__init__.py | 0 .../tests/integration/chain/test_escrow.py | 137 +- .../tests/integration/chain/test_kvstore.py | 39 +- .../tests/integration/chain/test_web3.py | 32 +- .../tests/integration/cron/__init__.py | 0 .../test_process_exchange_oracle_webhooks.py | 219 +-- ...test_process_reputation_oracle_webhooks.py | 216 ++- .../tests/integration/endpoints/__init__.py | 0 .../integration/endpoints/test_webhook.py | 56 + .../tests/integration/services/__init__.py | 0 .../integration/services/cloud/__init__.py | 0 .../services/cloud/test_client_service.py | 73 + .../services/test_validation_service.py | 78 ++ .../services/test_webhook_service.py | 280 +--- .../tests/unit/agreement/conftest.py | 85 -- .../tests/unit/agreement/test_bootstrap.py | 38 - .../tests/unit/agreement/test_measures.py | 69 - .../tests/unit/agreement/test_utils.py | 20 - .../tests/unit/webhook/conftest.py | 12 - .../test_process_intermediate_results.py | 7 - .../recording-oracle/tests/utils/constants.py | 2 + .../tests/utils/setup_escrow.py | 26 +- .../tests/utils/setup_kvstore.py | 2 +- 58 files changed, 4083 insertions(+), 2473 deletions(-) create mode 100644 packages/examples/cvat/exchange-oracle/.dockerignore delete mode 100644 packages/examples/cvat/exchange-oracle/dockerfiles/blockchain-node.Dockerfile delete mode 100644 packages/examples/cvat/exchange-oracle/tests/api/test_cvat_webhook.py create mode 100644 packages/examples/cvat/exchange-oracle/tests/api/test_cvat_webhook_api.py create mode 100644 packages/examples/cvat/exchange-oracle/tests/api/test_exchange_api.py delete mode 100644 packages/examples/cvat/exchange-oracle/tests/api/test_jl_webhook.py create mode 100644 packages/examples/cvat/exchange-oracle/tests/api/test_webhook_api.py create mode 100644 packages/examples/cvat/exchange-oracle/tests/integration/cron/state_trackers/test_retrieve_annotations.py create mode 100644 packages/examples/cvat/exchange-oracle/tests/integration/cron/state_trackers/test_track_assignments.py rename packages/examples/cvat/exchange-oracle/tests/integration/cron/{track_completed => state_trackers}/test_track_completed_projects.py (100%) rename packages/examples/cvat/exchange-oracle/tests/integration/cron/{track_completed => state_trackers}/test_track_completed_tasks.py (96%) create mode 100644 packages/examples/cvat/exchange-oracle/tests/integration/cron/state_trackers/test_track_task_creation.py delete mode 100644 packages/examples/cvat/exchange-oracle/tests/integration/cron/test_retrieve_annotations.py create mode 100644 packages/examples/cvat/exchange-oracle/tests/integration/services/test_exchange.py delete mode 100644 packages/examples/cvat/exchange-oracle/tests/unit/helpers/test_format_annotations.py create mode 100644 packages/examples/cvat/exchange-oracle/tests/utils/db_helper.py delete mode 100644 packages/examples/cvat/exchange-oracle/tests/utils/setup_escrow.py delete mode 100644 packages/examples/cvat/exchange-oracle/tests/utils/setup_kvstore.py create mode 100644 packages/examples/cvat/recording-oracle/src/services/__init__.py create mode 100644 packages/examples/cvat/recording-oracle/tests/integration/chain/__init__.py create mode 100644 packages/examples/cvat/recording-oracle/tests/integration/cron/__init__.py create mode 100644 packages/examples/cvat/recording-oracle/tests/integration/endpoints/__init__.py create mode 100644 packages/examples/cvat/recording-oracle/tests/integration/endpoints/test_webhook.py create mode 100644 packages/examples/cvat/recording-oracle/tests/integration/services/__init__.py create mode 100644 packages/examples/cvat/recording-oracle/tests/integration/services/cloud/__init__.py create mode 100644 packages/examples/cvat/recording-oracle/tests/integration/services/cloud/test_client_service.py create mode 100644 packages/examples/cvat/recording-oracle/tests/integration/services/test_validation_service.py delete mode 100644 packages/examples/cvat/recording-oracle/tests/unit/agreement/conftest.py delete mode 100644 packages/examples/cvat/recording-oracle/tests/unit/agreement/test_bootstrap.py delete mode 100644 packages/examples/cvat/recording-oracle/tests/unit/agreement/test_measures.py delete mode 100644 packages/examples/cvat/recording-oracle/tests/unit/agreement/test_utils.py delete mode 100644 packages/examples/cvat/recording-oracle/tests/unit/webhook/conftest.py delete mode 100644 packages/examples/cvat/recording-oracle/tests/unit/webhook/test_process_intermediate_results.py diff --git a/packages/examples/cvat/exchange-oracle/.dockerignore b/packages/examples/cvat/exchange-oracle/.dockerignore new file mode 100644 index 0000000000..ae6030127f --- /dev/null +++ b/packages/examples/cvat/exchange-oracle/.dockerignore @@ -0,0 +1,4 @@ +# dockerfiles/.dockerignore + +# Exclude the .env file in the src directory +src/.env \ No newline at end of file diff --git a/packages/examples/cvat/exchange-oracle/alembic/versions/16ecc586d685_init.py b/packages/examples/cvat/exchange-oracle/alembic/versions/16ecc586d685_init.py index 45c88c2d37..6ff8ca49b5 100644 --- a/packages/examples/cvat/exchange-oracle/alembic/versions/16ecc586d685_init.py +++ b/packages/examples/cvat/exchange-oracle/alembic/versions/16ecc586d685_init.py @@ -11,7 +11,7 @@ # revision identifiers, used by Alembic. -revision = '16ecc586d685' +revision = "16ecc586d685" down_revision = None branch_labels = None depends_on = None @@ -19,134 +19,158 @@ def upgrade() -> None: # ### commands auto generated by Alembic - please adjust! ### - op.create_table('projects', - sa.Column('id', sa.String(), nullable=False), - sa.Column('cvat_id', sa.Integer(), nullable=False), - sa.Column('cvat_cloudstorage_id', sa.Integer(), nullable=False), - sa.Column('status', sa.String(), nullable=False), - sa.Column('job_type', sa.String(), nullable=False), - sa.Column('escrow_address', sa.String(length=42), nullable=False), - sa.Column('chain_id', sa.Integer(), nullable=False), - sa.Column('bucket_url', sa.String(), nullable=False), - sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True), - sa.Column('updated_at', sa.DateTime(timezone=True), nullable=True), - sa.Column('cvat_webhook_id', sa.Integer(), nullable=True), - sa.PrimaryKeyConstraint('id'), - sa.UniqueConstraint('escrow_address') + op.create_table( + "projects", + sa.Column("id", sa.String(), nullable=False), + sa.Column("cvat_id", sa.Integer(), nullable=False), + sa.Column("cvat_cloudstorage_id", sa.Integer(), nullable=False), + sa.Column("status", sa.String(), nullable=False), + sa.Column("job_type", sa.String(), nullable=False), + sa.Column("escrow_address", sa.String(length=42), nullable=False), + sa.Column("chain_id", sa.Integer(), nullable=False), + sa.Column("bucket_url", sa.String(), nullable=False), + sa.Column( + "created_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=True + ), + sa.Column("updated_at", sa.DateTime(timezone=True), nullable=True), + sa.Column("cvat_webhook_id", sa.Integer(), nullable=True), + sa.PrimaryKeyConstraint("id"), + sa.UniqueConstraint("escrow_address"), ) - op.create_index(op.f('ix_projects_cvat_cloudstorage_id'), 'projects', ['cvat_cloudstorage_id'], unique=False) - op.create_index(op.f('ix_projects_cvat_id'), 'projects', ['cvat_id'], unique=True) - op.create_index(op.f('ix_projects_id'), 'projects', ['id'], unique=False) - op.create_table('users', - sa.Column('wallet_address', sa.String(), nullable=False), - sa.Column('cvat_email', sa.String(), nullable=True), - sa.Column('cvat_id', sa.Integer(), nullable=True), - sa.PrimaryKeyConstraint('wallet_address') + op.create_index( + op.f("ix_projects_cvat_cloudstorage_id"), "projects", ["cvat_cloudstorage_id"], unique=False ) - op.create_index(op.f('ix_users_cvat_email'), 'users', ['cvat_email'], unique=True) - op.create_index(op.f('ix_users_cvat_id'), 'users', ['cvat_id'], unique=True) - op.create_index(op.f('ix_users_wallet_address'), 'users', ['wallet_address'], unique=False) - op.create_table('webhooks', - sa.Column('id', sa.String(), nullable=False), - sa.Column('signature', sa.String(), nullable=True), - sa.Column('escrow_address', sa.String(length=42), nullable=False), - sa.Column('chain_id', sa.Integer(), nullable=False), - sa.Column('type', sa.String(), nullable=False), - sa.Column('status', sa.String(), server_default='pending', nullable=True), - sa.Column('attempts', sa.Integer(), server_default='0', nullable=True), - sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True), - sa.Column('updated_at', sa.DateTime(timezone=True), nullable=True), - sa.Column('wait_until', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True), - sa.Column('event_type', sa.String(), nullable=False), - sa.Column('event_data', sa.JSON(), nullable=True), - sa.Column('direction', sa.String(), nullable=False), - sa.PrimaryKeyConstraint('id') + op.create_index(op.f("ix_projects_cvat_id"), "projects", ["cvat_id"], unique=True) + op.create_index(op.f("ix_projects_id"), "projects", ["id"], unique=False) + op.create_table( + "users", + sa.Column("wallet_address", sa.String(), nullable=False), + sa.Column("cvat_email", sa.String(), nullable=True), + sa.Column("cvat_id", sa.Integer(), nullable=True), + sa.PrimaryKeyConstraint("wallet_address"), ) - op.create_index(op.f('ix_webhooks_id'), 'webhooks', ['id'], unique=False) - op.create_index(op.f('ix_webhooks_signature'), 'webhooks', ['signature'], unique=True) - op.create_table('images', - sa.Column('id', sa.String(), nullable=False), - sa.Column('cvat_project_id', sa.Integer(), nullable=False), - sa.Column('filename', sa.String(), nullable=False), - sa.ForeignKeyConstraint(['cvat_project_id'], ['projects.cvat_id'], ondelete='CASCADE'), - sa.PrimaryKeyConstraint('id'), - sa.UniqueConstraint('cvat_project_id', 'filename', name='_project_filename_uc') + op.create_index(op.f("ix_users_cvat_email"), "users", ["cvat_email"], unique=True) + op.create_index(op.f("ix_users_cvat_id"), "users", ["cvat_id"], unique=True) + op.create_index(op.f("ix_users_wallet_address"), "users", ["wallet_address"], unique=False) + op.create_table( + "webhooks", + sa.Column("id", sa.String(), nullable=False), + sa.Column("signature", sa.String(), nullable=True), + sa.Column("escrow_address", sa.String(length=42), nullable=False), + sa.Column("chain_id", sa.Integer(), nullable=False), + sa.Column("type", sa.String(), nullable=False), + sa.Column("status", sa.String(), server_default="pending", nullable=True), + sa.Column("attempts", sa.Integer(), server_default="0", nullable=True), + sa.Column( + "created_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=True + ), + sa.Column("updated_at", sa.DateTime(timezone=True), nullable=True), + sa.Column( + "wait_until", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=True + ), + sa.Column("event_type", sa.String(), nullable=False), + sa.Column("event_data", sa.JSON(), nullable=True), + sa.Column("direction", sa.String(), nullable=False), + sa.PrimaryKeyConstraint("id"), ) - op.create_index(op.f('ix_images_id'), 'images', ['id'], unique=False) - op.create_table('tasks', - sa.Column('id', sa.String(), nullable=False), - sa.Column('cvat_id', sa.Integer(), nullable=False), - sa.Column('cvat_project_id', sa.Integer(), nullable=False), - sa.Column('status', sa.String(), nullable=False), - sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True), - sa.Column('updated_at', sa.DateTime(timezone=True), nullable=True), - sa.ForeignKeyConstraint(['cvat_project_id'], ['projects.cvat_id'], ondelete='CASCADE'), - sa.PrimaryKeyConstraint('id') + op.create_index(op.f("ix_webhooks_id"), "webhooks", ["id"], unique=False) + op.create_index(op.f("ix_webhooks_signature"), "webhooks", ["signature"], unique=True) + op.create_table( + "images", + sa.Column("id", sa.String(), nullable=False), + sa.Column("cvat_project_id", sa.Integer(), nullable=False), + sa.Column("filename", sa.String(), nullable=False), + sa.ForeignKeyConstraint(["cvat_project_id"], ["projects.cvat_id"], ondelete="CASCADE"), + sa.PrimaryKeyConstraint("id"), + sa.UniqueConstraint("cvat_project_id", "filename", name="_project_filename_uc"), ) - op.create_index(op.f('ix_tasks_cvat_id'), 'tasks', ['cvat_id'], unique=True) - op.create_index(op.f('ix_tasks_id'), 'tasks', ['id'], unique=False) - op.create_table('data_uploads', - sa.Column('id', sa.String(), nullable=False), - sa.Column('task_id', sa.Integer(), nullable=False), - sa.ForeignKeyConstraint(['task_id'], ['tasks.cvat_id'], ondelete='CASCADE'), - sa.PrimaryKeyConstraint('id') + op.create_index(op.f("ix_images_id"), "images", ["id"], unique=False) + op.create_table( + "tasks", + sa.Column("id", sa.String(), nullable=False), + sa.Column("cvat_id", sa.Integer(), nullable=False), + sa.Column("cvat_project_id", sa.Integer(), nullable=False), + sa.Column("status", sa.String(), nullable=False), + sa.Column( + "created_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=True + ), + sa.Column("updated_at", sa.DateTime(timezone=True), nullable=True), + sa.ForeignKeyConstraint(["cvat_project_id"], ["projects.cvat_id"], ondelete="CASCADE"), + sa.PrimaryKeyConstraint("id"), ) - op.create_index(op.f('ix_data_uploads_id'), 'data_uploads', ['id'], unique=False) - op.create_index(op.f('ix_data_uploads_task_id'), 'data_uploads', ['task_id'], unique=True) - op.create_table('jobs', - sa.Column('id', sa.String(), nullable=False), - sa.Column('cvat_id', sa.Integer(), nullable=False), - sa.Column('cvat_task_id', sa.Integer(), nullable=False), - sa.Column('cvat_project_id', sa.Integer(), nullable=False), - sa.Column('status', sa.String(), nullable=False), - sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True), - sa.Column('updated_at', sa.DateTime(timezone=True), nullable=True), - sa.ForeignKeyConstraint(['cvat_project_id'], ['projects.cvat_id'], ondelete='CASCADE'), - sa.ForeignKeyConstraint(['cvat_task_id'], ['tasks.cvat_id'], ondelete='CASCADE'), - sa.PrimaryKeyConstraint('id') + op.create_index(op.f("ix_tasks_cvat_id"), "tasks", ["cvat_id"], unique=True) + op.create_index(op.f("ix_tasks_id"), "tasks", ["id"], unique=False) + op.create_table( + "data_uploads", + sa.Column("id", sa.String(), nullable=False), + sa.Column("task_id", sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(["task_id"], ["tasks.cvat_id"], ondelete="CASCADE"), + sa.PrimaryKeyConstraint("id"), ) - op.create_index(op.f('ix_jobs_cvat_id'), 'jobs', ['cvat_id'], unique=True) - op.create_index(op.f('ix_jobs_id'), 'jobs', ['id'], unique=False) - op.create_table('assignments', - sa.Column('id', sa.String(), nullable=False), - sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True), - sa.Column('expires_at', sa.DateTime(timezone=True), nullable=False), - sa.Column('completed_at', sa.DateTime(timezone=True), nullable=True), - sa.Column('user_wallet_address', sa.String(), nullable=False), - sa.Column('cvat_job_id', sa.Integer(), nullable=False), - sa.Column('status', sa.String(), server_default='created', nullable=False), - sa.ForeignKeyConstraint(['cvat_job_id'], ['jobs.cvat_id'], ondelete='CASCADE'), - sa.ForeignKeyConstraint(['user_wallet_address'], ['users.wallet_address'], ondelete='CASCADE'), - sa.PrimaryKeyConstraint('id') + op.create_index(op.f("ix_data_uploads_id"), "data_uploads", ["id"], unique=False) + op.create_index(op.f("ix_data_uploads_task_id"), "data_uploads", ["task_id"], unique=True) + op.create_table( + "jobs", + sa.Column("id", sa.String(), nullable=False), + sa.Column("cvat_id", sa.Integer(), nullable=False), + sa.Column("cvat_task_id", sa.Integer(), nullable=False), + sa.Column("cvat_project_id", sa.Integer(), nullable=False), + sa.Column("status", sa.String(), nullable=False), + sa.Column( + "created_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=True + ), + sa.Column("updated_at", sa.DateTime(timezone=True), nullable=True), + sa.ForeignKeyConstraint(["cvat_project_id"], ["projects.cvat_id"], ondelete="CASCADE"), + sa.ForeignKeyConstraint(["cvat_task_id"], ["tasks.cvat_id"], ondelete="CASCADE"), + sa.PrimaryKeyConstraint("id"), ) - op.create_index(op.f('ix_assignments_id'), 'assignments', ['id'], unique=False) + op.create_index(op.f("ix_jobs_cvat_id"), "jobs", ["cvat_id"], unique=True) + op.create_index(op.f("ix_jobs_id"), "jobs", ["id"], unique=False) + op.create_table( + "assignments", + sa.Column("id", sa.String(), nullable=False), + sa.Column( + "created_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=True + ), + sa.Column("expires_at", sa.DateTime(timezone=True), nullable=False), + sa.Column("completed_at", sa.DateTime(timezone=True), nullable=True), + sa.Column("user_wallet_address", sa.String(), nullable=False), + sa.Column("cvat_job_id", sa.Integer(), nullable=False), + sa.Column("status", sa.String(), server_default="created", nullable=False), + sa.ForeignKeyConstraint(["cvat_job_id"], ["jobs.cvat_id"], ondelete="CASCADE"), + sa.ForeignKeyConstraint( + ["user_wallet_address"], ["users.wallet_address"], ondelete="CASCADE" + ), + sa.PrimaryKeyConstraint("id"), + ) + op.create_index(op.f("ix_assignments_id"), "assignments", ["id"], unique=False) # ### end Alembic commands ### def downgrade() -> None: # ### commands auto generated by Alembic - please adjust! ### - op.drop_index(op.f('ix_assignments_id'), table_name='assignments') - op.drop_table('assignments') - op.drop_index(op.f('ix_jobs_id'), table_name='jobs') - op.drop_index(op.f('ix_jobs_cvat_id'), table_name='jobs') - op.drop_table('jobs') - op.drop_index(op.f('ix_data_uploads_task_id'), table_name='data_uploads') - op.drop_index(op.f('ix_data_uploads_id'), table_name='data_uploads') - op.drop_table('data_uploads') - op.drop_index(op.f('ix_tasks_id'), table_name='tasks') - op.drop_index(op.f('ix_tasks_cvat_id'), table_name='tasks') - op.drop_table('tasks') - op.drop_index(op.f('ix_images_id'), table_name='images') - op.drop_table('images') - op.drop_index(op.f('ix_webhooks_signature'), table_name='webhooks') - op.drop_index(op.f('ix_webhooks_id'), table_name='webhooks') - op.drop_table('webhooks') - op.drop_index(op.f('ix_users_wallet_address'), table_name='users') - op.drop_index(op.f('ix_users_cvat_id'), table_name='users') - op.drop_index(op.f('ix_users_cvat_email'), table_name='users') - op.drop_table('users') - op.drop_index(op.f('ix_projects_id'), table_name='projects') - op.drop_index(op.f('ix_projects_cvat_id'), table_name='projects') - op.drop_index(op.f('ix_projects_cvat_cloudstorage_id'), table_name='projects') - op.drop_table('projects') + op.drop_index(op.f("ix_assignments_id"), table_name="assignments") + op.drop_table("assignments") + op.drop_index(op.f("ix_jobs_id"), table_name="jobs") + op.drop_index(op.f("ix_jobs_cvat_id"), table_name="jobs") + op.drop_table("jobs") + op.drop_index(op.f("ix_data_uploads_task_id"), table_name="data_uploads") + op.drop_index(op.f("ix_data_uploads_id"), table_name="data_uploads") + op.drop_table("data_uploads") + op.drop_index(op.f("ix_tasks_id"), table_name="tasks") + op.drop_index(op.f("ix_tasks_cvat_id"), table_name="tasks") + op.drop_table("tasks") + op.drop_index(op.f("ix_images_id"), table_name="images") + op.drop_table("images") + op.drop_index(op.f("ix_webhooks_signature"), table_name="webhooks") + op.drop_index(op.f("ix_webhooks_id"), table_name="webhooks") + op.drop_table("webhooks") + op.drop_index(op.f("ix_users_wallet_address"), table_name="users") + op.drop_index(op.f("ix_users_cvat_id"), table_name="users") + op.drop_index(op.f("ix_users_cvat_email"), table_name="users") + op.drop_table("users") + op.drop_index(op.f("ix_projects_id"), table_name="projects") + op.drop_index(op.f("ix_projects_cvat_id"), table_name="projects") + op.drop_index(op.f("ix_projects_cvat_cloudstorage_id"), table_name="projects") + op.drop_table("projects") # ### end Alembic commands ### diff --git a/packages/examples/cvat/exchange-oracle/docker-compose.test.yml b/packages/examples/cvat/exchange-oracle/docker-compose.test.yml index 058e580b5c..cc45d1bfd7 100644 --- a/packages/examples/cvat/exchange-oracle/docker-compose.test.yml +++ b/packages/examples/cvat/exchange-oracle/docker-compose.test.yml @@ -14,21 +14,6 @@ services: ports: - 5432:5432 - blockchain-node: - build: - context: ./ - dockerfile: dockerfiles/blockchain-node.Dockerfile - healthcheck: - # Using a magic nubmer of 28 here because this is a block number when blockchain-node container is ready to use - test: if [ $(( $(wget -q --post-data='{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' -O- http://blockchain-node:8545 | grep -o '"result":"[^"]*"' | awk -F'"' '{print $4}' ) )) -ge 28 ]; then exit 0; else exit 1; fi - interval: 5s - timeout: 5s - retries: 15 - networks: - - test-network - ports: - - 8545:8545 - minio: container_name: minio image: minio/minio:RELEASE.2022-05-26T05-48-41Z @@ -80,16 +65,15 @@ services: PG_PASSWORD: 'test' PG_DB: 'exchange_oracle_test' WEB3_HTTP_PROVIDER_URI: 'http://blockchain-node:8545' - ENDPOINT_URL: 'host.docker.internal:9000' - ACCESS_KEY: 'dev' - SECRET_KEY: 'devdevdev' - RESULTS_BUCKET_NAME: 'results' - USE_SSL: False + STORAGE_ENDPOINT_URL: 'host.docker.internal:9000' + STORAGE_ACCESS_KEY: 'dev' + STORAGE_SECRET_KEY: 'devdevdev' + STORAGE_RESULTS_BUCKET_NAME: 'results' + STORAGE_USE_SSL: False + ENABLE_CUSTOM_CLOUD_HOST: Yes depends_on: postgres: condition: service_started - blockchain-node: - condition: service_healthy minio-mc: condition: service_completed_successfully # Used to reference localhost since minio:9000 is rejected by the SDK URL validator diff --git a/packages/examples/cvat/exchange-oracle/dockerfiles/blockchain-node.Dockerfile b/packages/examples/cvat/exchange-oracle/dockerfiles/blockchain-node.Dockerfile deleted file mode 100644 index 48a01c40cb..0000000000 --- a/packages/examples/cvat/exchange-oracle/dockerfiles/blockchain-node.Dockerfile +++ /dev/null @@ -1,12 +0,0 @@ -FROM node:lts-alpine - -RUN apk add git - -RUN git clone https://github.com/humanprotocol/human-protocol.git - -WORKDIR /human-protocol - -EXPOSE 8545 - -RUN yarn workspace @human-protocol/core install --ignore-scripts -CMD yarn workspace @human-protocol/core local \ No newline at end of file diff --git a/packages/examples/cvat/exchange-oracle/dockerfiles/test.Dockerfile b/packages/examples/cvat/exchange-oracle/dockerfiles/test.Dockerfile index cafd0772c5..e793fed276 100644 --- a/packages/examples/cvat/exchange-oracle/dockerfiles/test.Dockerfile +++ b/packages/examples/cvat/exchange-oracle/dockerfiles/test.Dockerfile @@ -3,7 +3,7 @@ FROM python:3.10 WORKDIR /app RUN apt-get update -y && \ - apt-get install -y jq && \ + apt-get install -y jq ffmpeg libsm6 libxext6 && \ pip install poetry COPY pyproject.toml poetry.lock ./ diff --git a/packages/examples/cvat/exchange-oracle/src/crons/state_trackers.py b/packages/examples/cvat/exchange-oracle/src/crons/state_trackers.py index 1f246c9a08..8b5d138d0e 100644 --- a/packages/examples/cvat/exchange-oracle/src/crons/state_trackers.py +++ b/packages/examples/cvat/exchange-oracle/src/crons/state_trackers.py @@ -208,7 +208,7 @@ def retrieve_annotations() -> None: # Check if all jobs within the project are completed if not cvat_service.is_project_completed(session, project.id): cvat_service.update_project_status( - session, project.id, ProjectStatuses.annotation.value + session, project.id, ProjectStatuses.annotation ) continue diff --git a/packages/examples/cvat/exchange-oracle/tests/api/test_cvat_webhook.py b/packages/examples/cvat/exchange-oracle/tests/api/test_cvat_webhook.py deleted file mode 100644 index 9b8291e817..0000000000 --- a/packages/examples/cvat/exchange-oracle/tests/api/test_cvat_webhook.py +++ /dev/null @@ -1,124 +0,0 @@ -from fastapi.testclient import TestClient - -from tests.utils.setup_cvat import ( - add_cvat_project_to_db, - add_cvat_task_to_db, - generate_cvat_signature, - get_cvat_job_from_db, -) - - -def test_incoming_webhook_200(client: TestClient) -> None: - data = { - "event": "ping", - } - signature = generate_cvat_signature(data) - - # Should respond with 200 status to a "ping" event - response = client.post( - "/cvat", - headers={"X-Signature-256": signature}, - json=data, - ) - - assert response.status_code == 200 - - # Create some entities in test DB - project = add_cvat_project_to_db(cvat_id=1) - task = add_cvat_task_to_db(cvat_id=1, cvat_project_id=1, status="annotation") - - # Payload for "create:job" event - data = { - "event": "create:job", - "job": { - "url": "http://localhost:8080/api/jobs/1", - "id": 1, - "task_id": 1, - "project_id": 1, - "assignee": None, - "state": "new", - }, - "webhook_id": 1, - } - - signature = generate_cvat_signature(data) - - # Check if "create:job" event works correctly - response = client.post( - "/cvat", - headers={"X-Signature-256": signature}, - json=data, - ) - - assert response.status_code == 200 - - job = get_cvat_job_from_db(1) - assert job.cvat_id == 1 - assert job.cvat_task_id == 1 - assert job.cvat_project_id == 1 - assert job.assignee == "" - - # Check if "update:job" event works correctly - response = client.post( - "/cvat", - headers={"X-Signature-256": signature}, - json=data, - ) - - data = { - "event": "update:job", - "job": { - "url": "http://localhost:8080/api/jobs/1", - "id": 1, - "task_id": 1, - "project_id": 1, - "assignee": { - "url": "http://localhost:8080/api/users/1", - "id": 1, - "username": "admin", - }, - }, - "before_update": {"assignee": None}, - "webhook_id": 1, - } - - signature = generate_cvat_signature(data) - - # Check if "update:job" event works correctly - response = client.post( - "/cvat", - headers={"X-Signature-256": signature}, - json=data, - ) - - assert response.status_code == 200 - - job = get_cvat_job_from_db(1) - assert job.assignee == "admin" - - -def test_incoming_webhook_403(client: TestClient) -> None: - data = { - "event": "ping", - } - - # Send a request with bad signature - response = client.post( - "/cvat", - headers={"X-Signature-256": "dummy_signature"}, - json=data, - ) - - assert response.status_code == 403 - assert response.json() == {"message": "Signature doesn't match"} - - response = client.post( - "/cvat", - json=data, - ) - - # Send a request without a signature - assert response.status_code == 400 - assert response.json() == { - "errors": [{"field": "x-signature-256", "message": "field required"}] - } diff --git a/packages/examples/cvat/exchange-oracle/tests/api/test_cvat_webhook_api.py b/packages/examples/cvat/exchange-oracle/tests/api/test_cvat_webhook_api.py new file mode 100644 index 0000000000..59a106419f --- /dev/null +++ b/packages/examples/cvat/exchange-oracle/tests/api/test_cvat_webhook_api.py @@ -0,0 +1,184 @@ +from datetime import datetime, timedelta, timezone +from unittest.mock import patch + +from fastapi.testclient import TestClient + +from src.core.types import AssignmentStatus, JobStatuses + +from tests.utils.setup_cvat import ( + add_asignment_to_db, + add_cvat_job_to_db, + add_cvat_project_to_db, + add_cvat_task_to_db, + generate_cvat_signature, + get_cvat_job_from_db, +) + +api_url = "http://localhost:8080/api/" + + +def test_ping_incoming_webhook(client: TestClient) -> None: + data = { + "event": "ping", + } + signature = generate_cvat_signature(data) + + # Should respond with 200 status to a "ping" event + response = client.post( + "/cvat-webhook", + headers={"X-Signature-256": signature}, + json=data, + ) + + assert response.status_code == 200 + + +def test_incoming_webhook_200(client: TestClient) -> None: + # Create some entities in test DB + add_cvat_project_to_db(cvat_id=1) + add_cvat_task_to_db(cvat_id=1, cvat_project_id=1, status="annotation") + + # Payload for "create:job" event + data = { + "event": "create:job", + "job": { + "url": api_url + "jobs/1", + "id": 1, + "task_id": 1, + "project_id": 1, + "state": "new", + "type": "annotation", + }, + "webhook_id": 1, + } + + signature = generate_cvat_signature(data) + + # Check if "create:job" event works correctly + response = client.post( + "/cvat-webhook", + headers={"X-Signature-256": signature}, + json=data, + ) + + assert response.status_code == 200 + + (job, _) = get_cvat_job_from_db(1) + assert job.cvat_id == 1 + assert job.cvat_task_id == 1 + assert job.cvat_project_id == 1 + + +def test_incoming_webhook_200_update_expired_assignmets(client: TestClient) -> None: + add_cvat_project_to_db(cvat_id=1) + add_cvat_task_to_db(cvat_id=1, cvat_project_id=1, status="annotation") + add_cvat_job_to_db(cvat_id=1, cvat_task_id=1, cvat_project_id=1, status="new") + (job, _) = get_cvat_job_from_db(1) + # Check if "update:job" event works with expired assignments + wallet_address = "0x86e83d346041E8806e352681f3F14549C0d2BC68" + add_asignment_to_db(wallet_address, 1, job.cvat_id, datetime.now()) + + data = { + "event": "update:job", + "job": { + "url": api_url + "jobs/1", + "id": 1, + "task_id": 1, + "project_id": 1, + "state": "completed", + "assignee": { + "url": api_url + "users/1", + "id": 1, + }, + "updated_date": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.%f") + "Z", + }, + "before_update": {"state": "new", "assignee": None}, + "webhook_id": 1, + } + + signature = generate_cvat_signature(data) + + with patch("src.handlers.cvat_events.cvat_api"): + response = client.post( + "/cvat-webhook", + headers={"X-Signature-256": signature}, + json=data, + ) + + assert response.status_code == 200 + + (job, asignees) = get_cvat_job_from_db(1) + assert job.status == JobStatuses.new.value + assert asignees[0].status == AssignmentStatus.expired.value + + +def test_incoming_webhook_200_update(client: TestClient) -> None: + add_cvat_project_to_db(cvat_id=1) + add_cvat_task_to_db(cvat_id=1, cvat_project_id=1, status="annotation") + add_cvat_job_to_db(cvat_id=1, cvat_task_id=1, cvat_project_id=1, status="new") + (job, _) = get_cvat_job_from_db(1) + # Check if "update:job" event works correctly + wallet_address = "0x86e83d346041E8806e352681f3F14549C0d2BC69" + add_asignment_to_db(wallet_address, 2, job.cvat_id, datetime.now() + timedelta(hours=1)) + + data = { + "event": "update:job", + "job": { + "url": api_url + "jobs/1", + "id": 1, + "task_id": 1, + "project_id": 1, + "state": "completed", + "assignee": { + "url": api_url + "users/1", + "id": 2, + }, + "updated_date": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.%f") + "Z", + }, + "before_update": {"state": "new", "assignee": None}, + "webhook_id": 1, + } + + signature = generate_cvat_signature(data) + + with patch("src.handlers.cvat_events.cvat_api"): + response = client.post( + "/cvat-webhook", + headers={"X-Signature-256": signature}, + json=data, + ) + + assert response.status_code == 200 + + (job, asignees) = get_cvat_job_from_db(1) + assert job.status == JobStatuses.completed.value + assert asignees[0].status == AssignmentStatus.completed.value + + +data = { + "event": "ping", +} + + +def test_incoming_webhook_401_bad_signature(client: TestClient) -> None: + # Send a request with bad signature + response = client.post( + "/cvat-webhook", + headers={"X-Signature-256": "dummy_signature"}, + json=data, + ) + assert response.status_code == 401 + assert response.json() == {"message": "Unauthorized"} + + +def test_incoming_webhook_401_without_signature(client: TestClient) -> None: + response = client.post( + "/cvat-webhook", + json=data, + ) + + # Send a request without a signature + assert response.status_code == 400 + assert response.json() == { + "errors": [{"field": "x-signature-256", "message": "field required"}] + } diff --git a/packages/examples/cvat/exchange-oracle/tests/api/test_exchange_api.py b/packages/examples/cvat/exchange-oracle/tests/api/test_exchange_api.py new file mode 100644 index 0000000000..4d36dcf465 --- /dev/null +++ b/packages/examples/cvat/exchange-oracle/tests/api/test_exchange_api.py @@ -0,0 +1,341 @@ +import json +import uuid +from datetime import datetime, timedelta +from unittest.mock import patch + +from fastapi.testclient import TestClient + +from src.core.types import AssignmentStatus +from src.db import SessionLocal +from src.models.cvat import Assignment, User + +from tests.utils.db_helper import create_project_task_and_job + +escrow_address = "0x12E66A452f95bff49eD5a30b0d06Ebc37C5A94B6" +user_address = "0x86e83d346041E8806e352681f3F14549C0d2BC60" +cvat_email = "test@hmt.ai" + + +def test_empty_list_tasks_200_with_address(client: TestClient) -> None: + response = client.get( + "/tasks", headers={"signature": "sample"}, params={"wallet_address": user_address} + ) + + assert response.status_code == 200 + assert isinstance(response.json(), list) + assert len(response.json()) == 0 + + +def test_empty_list_tasks_200_without_address(client: TestClient) -> None: + response = client.get( + "/tasks", + headers={"signature": "sample"}, + ) + + assert response.status_code == 200 + assert isinstance(response.json(), list) + assert len(response.json()) == 0 + + +def test_list_tasks_200_with_address(client: TestClient) -> None: + with (SessionLocal.begin() as session,): + _, _, cvat_job_1 = create_project_task_and_job( + session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", 1 + ) + create_project_task_and_job(session, "0x86e83d346041E8806e352681f3F14549C0d2BC68", 2) + create_project_task_and_job(session, "0x86e83d346041E8806e352681f3F14549C0d2BC69", 3) + + user = User( + wallet_address=user_address, + cvat_email="test@hmt.ai", + cvat_id=1, + ) + session.add(user) + + assignment = Assignment( + id=str(uuid.uuid4()), + user_wallet_address=user_address, + cvat_job_id=cvat_job_1.cvat_id, + expires_at=datetime.now() + timedelta(days=1), + ) + session.add(assignment) + + session.commit() + + with ( + open("tests/utils/manifest.json") as data, + patch("src.services.exchange.get_escrow_manifest") as mock_get_manifest, + ): + manifest = json.load(data) + mock_get_manifest.return_value = manifest + + # With wallet address + response = client.get( + "/tasks", headers={"signature": "sample"}, params={"wallet_address": user_address} + ) + + assert response.status_code == 200 + assert isinstance(response.json(), list) + assert len(response.json()) == 1 + for task in response.json(): + assert task["assignment"] + assert set(task.keys()) == { + "id", + "escrow_address", + "title", + "description", + "platform", + "job_bounty", + "job_size", + "job_time_limit", + "job_type", + "assignment", + "status", + } + + +def test_list_tasks_200_without_address(client: TestClient) -> None: + with (SessionLocal.begin() as session,): + _, _, cvat_job_1 = create_project_task_and_job( + session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", 1 + ) + create_project_task_and_job(session, "0x86e83d346041E8806e352681f3F14549C0d2BC68", 2) + create_project_task_and_job(session, "0x86e83d346041E8806e352681f3F14549C0d2BC69", 3) + + user = User( + wallet_address=user_address, + cvat_email="test@hmt.ai", + cvat_id=1, + ) + session.add(user) + + assignment = Assignment( + id=str(uuid.uuid4()), + user_wallet_address=user_address, + cvat_job_id=cvat_job_1.cvat_id, + expires_at=datetime.now() + timedelta(days=1), + ) + session.add(assignment) + + session.commit() + + with ( + open("tests/utils/manifest.json") as data, + patch("src.services.exchange.get_escrow_manifest") as mock_get_manifest, + ): + manifest = json.load(data) + mock_get_manifest.return_value = manifest + + response = client.get( + "/tasks", + headers={"signature": "sample"}, + ) + + assert response.status_code == 200 + assert isinstance(response.json(), list) + assert len(response.json()) == 2 + for task in response.json(): + assert not task["assignment"] + assert set(task.keys()) == { + "id", + "escrow_address", + "title", + "description", + "platform", + "job_bounty", + "job_size", + "job_time_limit", + "job_type", + "assignment", + "status", + } + + +def test_list_tasks_401(client: TestClient) -> None: + response = client.get("/tasks", headers={"signature": "test"}) + + assert response.status_code == 401 + assert response.json() == {"message": "Unauthorized"} + + response = client.get( + "/tasks", + ) + + # Send a request without a signature + assert response.status_code == 400 + assert response.json() == {"errors": [{"field": "signature", "message": "field required"}]} + + +def test_register_200(client: TestClient) -> None: + with SessionLocal.begin() as session: + with patch("src.endpoints.exchange.cvat_api.get_user_id") as mock_get_user: + mock_get_user.return_value = 1 + response = client.put( + "/register", + headers={"signature": "sample"}, + json={"wallet_address": user_address, "cvat_email": cvat_email}, + ) + + user = response.json() + assert response.status_code == 200 + db_user = session.query(User).where(User.wallet_address == user_address).first() + assert user["wallet_address"] == db_user.wallet_address + assert user["cvat_id"] == db_user.cvat_id + assert user["cvat_email"] == db_user.cvat_email + assert user["wallet_address"] == user_address + assert user["cvat_email"] == cvat_email + + +def test_register_200_duplicated_address(client: TestClient) -> None: + with SessionLocal.begin() as session: + user = User( + wallet_address=user_address, + cvat_email=cvat_email, + cvat_id=1, + ) + session.add(user) + session.commit() + new_cvat_email = "test2@hmt.ai" + with ( + patch("src.endpoints.exchange.cvat_api.remove_user_from_org") as mock_remove_user, + patch("src.endpoints.exchange.cvat_api.get_user_id") as mock_get_user, + ): + mock_get_user.return_value = 1 + response = client.put( + "/register", + headers={"signature": "sample"}, + json={"wallet_address": user_address, "cvat_email": new_cvat_email}, + ) + + user = response.json() + assert response.status_code == 200 + assert user["wallet_address"] == user_address + assert user["cvat_email"] == new_cvat_email + mock_remove_user.assert_called_once() + + +def test_register_200_duplicated_user(client: TestClient) -> None: + with SessionLocal.begin() as session: + user = User( + wallet_address=user_address, + cvat_email="test@hmt.ai", + cvat_id=1, + ) + session.add(user) + session.commit() + with ( + patch("src.endpoints.exchange.cvat_api.remove_user_from_org") as mock_remove_user, + patch("src.endpoints.exchange.cvat_api.get_user_id") as mock_get_user, + ): + mock_get_user.return_value = 1 + new_cvat_email = "test2@hmt.ai" + response = client.put( + "/register", + headers={"signature": "sample"}, + json={"wallet_address": user_address, "cvat_email": new_cvat_email}, + ) + + user = response.json() + assert response.status_code == 200 + assert user["wallet_address"] == user_address + assert user["cvat_email"] == new_cvat_email + mock_remove_user.assert_called_once() + + +def test_register_400(client: TestClient) -> None: + with SessionLocal.begin() as session: + new_user_address = "0x86e83d346041E8806e352681f3F14549C0d2BC61" + user = User( + wallet_address=new_user_address, + cvat_email=cvat_email, + cvat_id=1, + ) + session.add(user) + session.commit() + response = client.put( + "/register", + headers={"signature": "sample"}, + json={"wallet_address": user_address, "cvat_email": cvat_email}, + ) + assert response.status_code == 400 + assert response.json() == {"message": "User already exists"} + assert new_user_address != user_address + + +def test_register_401_unauthorized(client: TestClient) -> None: + response = client.put( + "/register", + headers={"signature": "test"}, + json={"wallet_address": user_address, "cvat_email": cvat_email}, + ) + + assert response.status_code == 401 + assert response.json() == {"message": "Unauthorized"} + + +def test_register_401_without_signature(client: TestClient) -> None: + response = client.put( + "/register", json={"wallet_address": user_address, "cvat_email": cvat_email} + ) + + assert response.status_code == 400 + assert response.json() == {"errors": [{"field": "signature", "message": "field required"}]} + + +def test_create_assignment_200(client: TestClient) -> None: + session = SessionLocal() + session.begin() + cvat_project_1, _, cvat_job_1 = create_project_task_and_job( + session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", 1 + ) + user = User( + wallet_address=user_address, + cvat_email="test@hmt.ai", + cvat_id=1, + ) + session.add(user) + session.commit() + with ( + open("tests/utils/manifest.json") as data, + patch("src.services.exchange.get_escrow_manifest") as mock_get_manifest, + patch("src.services.exchange.cvat_api") as cvat_api, + ): + manifest = json.load(data) + mock_get_manifest.return_value = manifest + + response = client.post( + "/tasks/" + cvat_project_1.id + "/assignment", + headers={"signature": "sample"}, + json={"wallet_address": user_address}, + ) + cvat_api.clear_job_annotations.assert_called_once() + cvat_api.restart_job.assert_called_once() + cvat_api.update_job_assignee.assert_called_once() + + assert response.status_code == 200 + db_assignment = session.query(Assignment).filter_by(user_wallet_address=user_address).first() + + assert db_assignment.cvat_job_id == cvat_job_1.cvat_id + assert db_assignment.user_wallet_address == user_address + assert db_assignment.status == AssignmentStatus.created + assert response.json()["assignment"] + session.close() + + +def test_create_assignment_400_unauthorized(client: TestClient) -> None: + response = client.post( + "/tasks/1/assignment", + headers={"signature": "test"}, + json={"wallet_address": user_address, "cvat_email": cvat_email}, + ) + + assert response.status_code == 401 + assert response.json() == {"message": "Unauthorized"} + + +def test_create_assignment_401_without_signature(client: TestClient) -> None: + response = client.post( + "/tasks/1/assignment", json={"wallet_address": user_address, "cvat_email": cvat_email} + ) + assert response.status_code == 400 + assert response.json() == {"errors": [{"field": "signature", "message": "field required"}]} diff --git a/packages/examples/cvat/exchange-oracle/tests/api/test_jl_webhook.py b/packages/examples/cvat/exchange-oracle/tests/api/test_jl_webhook.py deleted file mode 100644 index 22d81d7f58..0000000000 --- a/packages/examples/cvat/exchange-oracle/tests/api/test_jl_webhook.py +++ /dev/null @@ -1,184 +0,0 @@ -from unittest.mock import MagicMock, patch - -from fastapi.testclient import TestClient -from human_protocol_sdk.constants import Status - -from tests.utils.constants import DEFAULT_GAS_PAYER as JOB_LAUNCHER -from tests.utils.constants import ( - DEFAULT_GAS_PAYER_PRIV, - SIGNATURE, - WEBHOOK_MESSAGE, - WEBHOOK_MESSAGE_SIGNED, -) - - -class PolygonMumbaiConfig: - chain_id = 80001 - rpc_api = "http://blockchain-node:8545/" - private_key = DEFAULT_GAS_PAYER_PRIV - - -def test_incoming_webhook_200(client: TestClient) -> None: - with patch("src.api.webhook.SessionLocal.begin") as mock_session_local, patch( - "src.api.webhook.create_webhook" - ) as mock_create_webhook, patch("src.chain.escrow.get_web3") as mock_get_web3, patch( - "src.chain.escrow.EscrowClient" - ) as mock_escrow_client, patch( - "src.chain.web3.Config.polygon_mumbai", PolygonMumbaiConfig - ): - mock_session = MagicMock() - mock_session_local.return_value.__enter__.return_value = mock_session - mock_create_webhook.return_value = "mocked_webhook_id" - - mock_web3_instance = MagicMock() - mock_get_web3.return_value = mock_web3_instance - - mock_escrow_instance = MagicMock() - mock_escrow_instance.get_balance.return_value = 100 - mock_escrow_instance.get_status.return_value = Status.Pending - mock_escrow_instance.get_job_launcher_address.return_value = JOB_LAUNCHER - mock_escrow_client.return_value = mock_escrow_instance - - response = client.post( - "/job-launcher", - headers={"human-signature": WEBHOOK_MESSAGE_SIGNED}, - json=WEBHOOK_MESSAGE, - ) - - assert response.status_code == 200 - assert response.json() == {"id": "mocked_webhook_id"} - mock_get_web3.assert_called() - mock_escrow_client.assert_called_with(mock_web3_instance) - mock_escrow_instance.get_balance.assert_called_once_with( - "0x12E66A452f95bff49eD5a30b0d06Ebc37C5A94B6" - ) - mock_escrow_instance.get_status.assert_called_once_with( - "0x12E66A452f95bff49eD5a30b0d06Ebc37C5A94B6" - ) - mock_escrow_instance.get_job_launcher_address.assert_called_once_with( - "0x12E66A452f95bff49eD5a30b0d06Ebc37C5A94B6" - ) - mock_session_local.assert_called_once() - mock_create_webhook.assert_called_once_with( - mock_session, - WEBHOOK_MESSAGE["escrow_address"], - WEBHOOK_MESSAGE["chain_id"], - "job_launcher", - WEBHOOK_MESSAGE_SIGNED, - ) - - -def test_incoming_webhook_400(client: TestClient) -> None: - # Invalid Address - data = { - "escrow_address": "bad_address", - "chain_id": 80001, - } - - response = client.post( - "/job-launcher", - headers={"human-signature": WEBHOOK_MESSAGE_SIGNED}, - json=data, - ) - - assert response.status_code == 400 - assert response.json() == { - "errors": [ - { - "field": "escrow_address", - "message": "bad_address is not a correct Web3 address", - } - ] - } - - # Invalid Chain Id - data = { - "escrow_address": "0x12E66A452f95bff49eD5a30b0d06Ebc37C5A94B6", - "chain_id": 1234, - } - - response = client.post( - "/job-launcher", - headers={"human-signature": WEBHOOK_MESSAGE_SIGNED}, - json=data, - ) - - assert response.status_code == 400 - assert response.json() == { - "errors": [ - { - "field": "chain_id", - "message": "value is not a valid enumeration member; permitted: 137, 80001, 1338", - } - ] - } - - # Invalid balance - - with patch("src.chain.escrow.get_web3") as mock_get_web3, patch( - "src.chain.escrow.EscrowClient" - ) as mock_escrow_client, patch("src.chain.web3.Config.polygon_mumbai", PolygonMumbaiConfig): - mock_web3_instance = MagicMock() - mock_get_web3.return_value = mock_web3_instance - - mock_escrow_instance = MagicMock() - mock_escrow_instance.get_balance.return_value = 0 - mock_escrow_instance.get_status.return_value = Status.Pending - mock_escrow_instance.get_job_launcher_address.return_value = JOB_LAUNCHER - mock_escrow_client.return_value = mock_escrow_instance - - response = client.post( - "/job-launcher", - headers={"human-signature": WEBHOOK_MESSAGE_SIGNED}, - json=WEBHOOK_MESSAGE, - ) - assert response.status_code == 400 - assert response.json() == {"message": "Escrow doesn't have funds"} - - # Invalid status - - with patch("src.chain.escrow.get_web3") as mock_get_web3, patch( - "src.chain.escrow.EscrowClient" - ) as mock_escrow_client, patch("src.chain.web3.Config.polygon_mumbai", PolygonMumbaiConfig): - mock_web3_instance = MagicMock() - mock_get_web3.return_value = mock_web3_instance - - mock_escrow_instance = MagicMock() - mock_escrow_instance.get_balance.return_value = 100 - mock_escrow_instance.get_status.return_value = Status.Complete - mock_escrow_instance.get_job_launcher_address.return_value = JOB_LAUNCHER - mock_escrow_client.return_value = mock_escrow_instance - - response = client.post( - "/job-launcher", - headers={"human-signature": WEBHOOK_MESSAGE_SIGNED}, - json=WEBHOOK_MESSAGE, - ) - assert response.status_code == 400 - assert response.json() == { - "message": "Escrow is not in a Pending state. Current state: Complete" - } - - # Invalid signature - - with patch("src.chain.escrow.get_web3") as mock_get_web3, patch( - "src.chain.escrow.EscrowClient" - ) as mock_escrow_client, patch("src.chain.web3.Config.polygon_mumbai", PolygonMumbaiConfig): - mock_web3_instance = MagicMock() - mock_get_web3.return_value = mock_web3_instance - - mock_escrow_instance = MagicMock() - mock_escrow_instance.get_balance.return_value = 100 - mock_escrow_instance.get_status.return_value = Status.Complete - mock_escrow_instance.get_job_launcher_address.return_value = JOB_LAUNCHER - mock_escrow_client.return_value = mock_escrow_instance - - response = client.post( - "/job-launcher", - headers={"human-signature": SIGNATURE}, - json=WEBHOOK_MESSAGE, - ) - assert response.status_code == 400 - assert response.json() == { - "message": "Webhook sender address doesn't match. Expected: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266, received: 0x0a2a7194105FeC656c2cB10F53aA75db504B7281." - } diff --git a/packages/examples/cvat/exchange-oracle/tests/api/test_webhook_api.py b/packages/examples/cvat/exchange-oracle/tests/api/test_webhook_api.py new file mode 100644 index 0000000000..56958c1f32 --- /dev/null +++ b/packages/examples/cvat/exchange-oracle/tests/api/test_webhook_api.py @@ -0,0 +1,141 @@ +from unittest.mock import Mock, patch + +from fastapi.testclient import TestClient +from sqlalchemy.sql import select +from web3 import HTTPProvider, Web3 + +from src.core.types import JobLauncherEventType, Networks +from src.db import SessionLocal +from src.models.webhook import Webhook + +from tests.utils.constants import DEFAULT_GAS_PAYER as JOB_LAUNCHER +from tests.utils.constants import RECORDING_ORACLE_ADDRESS, WEBHOOK_MESSAGE, WEBHOOK_MESSAGE_SIGNED + +escrow_address = "0x12E66A452f95bff49eD5a30b0d06Ebc37C5A94B6" + + +def test_incoming_webhook_200(client: TestClient) -> None: + with ( + SessionLocal.begin() as session, + patch("src.chain.web3.get_web3") as mock_get_web3, + patch("src.chain.escrow.get_escrow") as mock_get_escrow, + ): + mock_get_web3.return_value = Web3(HTTPProvider(Networks.localhost)) + mock_escrow = Mock() + mock_escrow.launcher = JOB_LAUNCHER + mock_escrow.recordingOracle = RECORDING_ORACLE_ADDRESS + mock_get_escrow.return_value = mock_escrow + + response = client.post( + "/oracle-webhook", + headers={"human-signature": WEBHOOK_MESSAGE_SIGNED}, + json=WEBHOOK_MESSAGE, + ) + + assert response.status_code == 200 + + db_query = select(Webhook).where(Webhook.escrow_address == escrow_address) + webhook = session.execute(db_query).scalars().first() + assert response.json() == {"id": webhook.id} + + mock_get_web3.assert_called() + mock_get_escrow.assert_called_with( + WEBHOOK_MESSAGE["chain_id"], + WEBHOOK_MESSAGE["escrow_address"], + ) + assert webhook.escrow_address == escrow_address + assert webhook.chain_id == 80001 + assert webhook.event_type == JobLauncherEventType.escrow_created.value + assert webhook.event_data == {} + assert webhook.direction == "incoming" + assert webhook.signature == WEBHOOK_MESSAGE_SIGNED + + +def test_incoming_webhook_400_missing_field(client: TestClient) -> None: + data = { + "escrow_address": escrow_address, + "chain_id": 80001, + } + + response = client.post( + "/oracle-webhook", + headers={"human-signature": WEBHOOK_MESSAGE_SIGNED}, + json=data, + ) + + assert response.status_code == 400 + assert response.json() == { + "errors": [ + { + "field": "event_type", + "message": "field required", + } + ] + } + + +def test_incoming_webhook_400_invalid_address(client: TestClient) -> None: + data = { + "escrow_address": "bad_address", + "chain_id": 80001, + "event_type": "escrow_created", + } + + response = client.post( + "/oracle-webhook", + headers={"human-signature": WEBHOOK_MESSAGE_SIGNED}, + json=data, + ) + + assert response.status_code == 400 + assert response.json() == { + "errors": [ + { + "field": "escrow_address", + "message": "bad_address is not a correct Web3 address", + } + ] + } + + +def test_incoming_webhook_400_invalid_chain_id(client: TestClient) -> None: + data = { + "escrow_address": "0x12E66A452f95bff49eD5a30b0d06Ebc37C5A94B6", + "chain_id": 1234, + "event_type": "escrow_created", + } + + response = client.post( + "/oracle-webhook", + headers={"human-signature": WEBHOOK_MESSAGE_SIGNED}, + json=data, + ) + + assert response.status_code == 400 + assert response.json() == { + "errors": [ + { + "field": "chain_id", + "message": "value is not a valid enumeration member; permitted: 137, 80001, 1338", + } + ] + } + + +def test_incoming_webhook_401(client: TestClient) -> None: + with ( + patch("src.chain.web3.get_web3") as mock_get_web3, + patch("src.chain.escrow.get_escrow") as mock_get_escrow, + ): + mock_get_web3.return_value = Web3(HTTPProvider(Networks.localhost)) + mock_escrow = Mock() + mock_escrow.launcher = escrow_address + mock_escrow.recordingOracle = RECORDING_ORACLE_ADDRESS + mock_get_escrow.return_value = mock_escrow + response = client.post( + "/oracle-webhook", + headers={"human-signature": WEBHOOK_MESSAGE_SIGNED}, + json=WEBHOOK_MESSAGE, + ) + assert response.status_code == 401 + assert response.json() == {"message": "Unauthorized"} diff --git a/packages/examples/cvat/exchange-oracle/tests/integration/chain/test_escrow.py b/packages/examples/cvat/exchange-oracle/tests/integration/chain/test_escrow.py index c79ed5a077..06a714b69f 100644 --- a/packages/examples/cvat/exchange-oracle/tests/integration/chain/test_escrow.py +++ b/packages/examples/cvat/exchange-oracle/tests/integration/chain/test_escrow.py @@ -1,116 +1,149 @@ +import json import unittest from unittest.mock import patch -from human_protocol_sdk.escrow import EscrowClientError +from human_protocol_sdk.constants import ChainId, Status +from human_protocol_sdk.escrow import EscrowClientError, EscrowData from human_protocol_sdk.storage import StorageClientError -from web3 import Web3 -from web3.middleware import construct_sign_and_send_raw_middleware -from web3.providers.rpc import HTTPProvider -from src.chain.escrow import get_escrow_manifest, get_job_launcher_address, validate_escrow +from src.chain.escrow import ( + get_escrow_manifest, + get_job_launcher_address, + get_recording_oracle_address, + validate_escrow, +) -from tests.utils.constants import DEFAULT_GAS_PAYER_PRIV -from tests.utils.setup_escrow import bulk_payout, create_escrow, fund_escrow +from tests.utils.constants import ( + ESCROW_ADDRESS, + FACTORY_ADDRESS, + JOB_LAUNCHER_ADDRESS, + RECORDING_ORACLE_ADDRESS, + TOKEN_ADDRESS, +) + +escrow_address = ESCROW_ADDRESS +chain_id = ChainId.LOCALHOST.value class ServiceIntegrationTest(unittest.TestCase): def setUp(self): - self.w3 = Web3(HTTPProvider()) - - # Set default gas payer - self.gas_payer = self.w3.eth.account.from_key(DEFAULT_GAS_PAYER_PRIV) - self.w3.middleware_onion.add( - construct_sign_and_send_raw_middleware(self.gas_payer), - "construct_sign_and_send_raw_middleware", + self.escrow_data = EscrowData( + escrow_address, + escrow_address, + "0", + "1000", + 0, + FACTORY_ADDRESS, + JOB_LAUNCHER_ADDRESS, + Status.Pending.name, + TOKEN_ADDRESS, + "1000", + "", + ChainId.LOCALHOST.name, ) - self.w3.eth.default_account = self.gas_payer.address def test_validate_escrow(self): - escrow_address = create_escrow(self.w3) - fund_escrow(self.w3, escrow_address) - with patch("src.chain.escrow.get_web3") as mock_function: - mock_function.return_value = self.w3 - validation = validate_escrow(self.w3.eth.chain_id, escrow_address) + with patch("src.chain.escrow.EscrowUtils.get_escrow") as mock_function: + mock_function.return_value = self.escrow_data + validation = validate_escrow(chain_id, escrow_address) self.assertIsNone(validation) def test_validate_escrow_invalid_address(self): - with patch("src.chain.escrow.get_web3") as mock_function: - mock_function.return_value = self.w3 - with self.assertRaises(EscrowClientError) as error: - validate_escrow(self.w3.eth.chain_id, "invalid_address") + with self.assertRaises(EscrowClientError) as error: + validate_escrow(chain_id, "invalid_address") self.assertEqual(f"Invalid escrow address: invalid_address", str(error.exception)) - def test_validate_escrow_without_funds(self): - escrow_address = create_escrow(self.w3) - with patch("src.chain.escrow.get_web3") as mock_function: - mock_function.return_value = self.w3 - with self.assertRaises(ValueError) as error: - validate_escrow(self.w3.eth.chain_id, escrow_address) - - self.assertEqual(f"Escrow doesn't have funds", str(error.exception)) - def test_validate_escrow_invalid_status(self): - escrow_address = create_escrow(self.w3) - fund_escrow(self.w3, escrow_address) - bulk_payout( - self.w3, - escrow_address, - self.gas_payer.address, - Web3.toWei(50, "milliether"), + with patch("src.chain.escrow.EscrowUtils.get_escrow") as mock_function: + self.escrow_data.status = Status.Launched.name + mock_function.return_value = self.escrow_data + with self.assertRaises(ValueError) as error: + validate_escrow(chain_id, escrow_address) + self.assertEqual( + f"Escrow is not in any of the accepted states (Pending). Current state: {self.escrow_data.status}", + str(error.exception), ) - with patch("src.chain.escrow.get_web3") as mock_function: - mock_function.return_value = self.w3 + def test_validate_escrow_without_funds(self): + with patch("src.chain.escrow.EscrowUtils.get_escrow") as mock_function: + self.escrow_data.balance = "0" + mock_function.return_value = self.escrow_data with self.assertRaises(ValueError) as error: - validate_escrow(self.w3.eth.chain_id, escrow_address) + validate_escrow(chain_id, escrow_address) self.assertEqual( - f"Escrow is not in a Pending state. Current state: Partial", + f"Escrow doesn't have funds", str(error.exception), ) def test_get_escrow_manifest(self): - escrow_address = create_escrow(self.w3) - with patch("src.chain.escrow.get_web3") as mock_function: - mock_function.return_value = self.w3 - manifest = get_escrow_manifest(self.w3.eth.chain_id, escrow_address) + with patch("src.chain.escrow.EscrowUtils.get_escrow") as mock_function, patch( + "src.chain.escrow.StorageClient.download_file_from_url" + ) as mock_storage: + mock_storage.return_value = json.dumps({"title": "test"}).encode() + mock_function.return_value = self.escrow_data + manifest = get_escrow_manifest(chain_id, escrow_address) self.assertIsInstance(manifest, dict) self.assertIsNotNone(manifest) def test_get_escrow_manifest_invalid_address(self): - with patch("src.chain.escrow.get_web3") as mock_function: - mock_function.return_value = self.w3 - with self.assertRaises(EscrowClientError) as error: - get_escrow_manifest(self.w3.eth.chain_id, "invalid_address") + with self.assertRaises(EscrowClientError) as error: + get_escrow_manifest(chain_id, "invalid_address") self.assertEqual(f"Invalid escrow address: invalid_address", str(error.exception)) def test_get_escrow_manifest_invalid_url(self): - escrow_address = create_escrow(self.w3) - with patch("src.chain.escrow.get_web3") as mock_function: - with patch("src.chain.escrow.EscrowClient") as mock_client: - mock_escrow_client = mock_client.return_value - mock_escrow_client.get_manifest_url.return_value = "invalid_url" - mock_function.return_value = self.w3 - with self.assertRaises(StorageClientError) as error: - get_escrow_manifest(self.w3.eth.chain_id, escrow_address) + with patch("src.chain.escrow.EscrowUtils.get_escrow") as mock_function: + self.escrow_data.manifestUrl = "invalid_url" + mock_function.return_value = self.escrow_data + with self.assertRaises(StorageClientError) as error: + get_escrow_manifest(chain_id, escrow_address) self.assertEqual(f"Invalid URL: invalid_url", str(error.exception)) def test_get_job_launcher_address(self): - escrow_address = create_escrow(self.w3) - with patch("src.chain.escrow.get_web3") as mock_function: - mock_function.return_value = self.w3 - job_launcher_address = get_job_launcher_address(self.w3.eth.chain_id, escrow_address) + with patch("src.chain.escrow.EscrowUtils.get_escrow") as mock_function: + mock_function.return_value = self.escrow_data + job_launcher_address = get_job_launcher_address(chain_id, escrow_address) self.assertIsInstance(job_launcher_address, str) - self.assertIsNotNone(job_launcher_address) + self.assertEqual(job_launcher_address, JOB_LAUNCHER_ADDRESS) def test_get_job_launcher_address_invalid_address(self): - with patch("src.chain.escrow.get_web3") as mock_function: - mock_function.return_value = self.w3 - with self.assertRaises(EscrowClientError) as error: - get_job_launcher_address(self.w3.eth.chain_id, "invalid_address") + with self.assertRaises(EscrowClientError) as error: + get_job_launcher_address(chain_id, "invalid_address") self.assertEqual(f"Invalid escrow address: invalid_address", str(error.exception)) def test_get_job_launcher_address_invalid_chain_id(self): - with self.assertRaises(ValueError) as error: - get_job_launcher_address(1, "0x1234567890123456789012345678901234567890") - self.assertEqual(f"1 is not in available list of networks.", str(error.exception)) + with self.assertRaises(EscrowClientError) as error: + get_job_launcher_address(123, escrow_address) + self.assertEqual(f"Invalid ChainId", str(error.exception)) + + def test_get_job_launcher_address_empty_escrow(self): + with patch("src.chain.escrow.EscrowUtils.get_escrow") as mock_function: + mock_function.return_value = None + with self.assertRaises(Exception) as error: + get_job_launcher_address(chain_id, escrow_address) + self.assertEqual(f"Can't find escrow {ESCROW_ADDRESS}", str(error.exception)) + + def test_get_recording_oracle_address(self): + with patch("src.chain.escrow.EscrowUtils.get_escrow") as mock_function: + self.escrow_data.recordingOracle = RECORDING_ORACLE_ADDRESS + mock_function.return_value = self.escrow_data + recording_oracle_address = get_recording_oracle_address(chain_id, escrow_address) + self.assertIsInstance(recording_oracle_address, str) + self.assertEqual(recording_oracle_address, RECORDING_ORACLE_ADDRESS) + + def test_get_recording_oracle_address_invalid_address(self): + with self.assertRaises(EscrowClientError) as error: + get_recording_oracle_address(chain_id, "invalid_address") + self.assertEqual(f"Invalid escrow address: invalid_address", str(error.exception)) + + def test_get_recording_oracle_address_invalid_chain_id(self): + with self.assertRaises(EscrowClientError) as error: + get_recording_oracle_address(123, escrow_address) + self.assertEqual(f"Invalid ChainId", str(error.exception)) + + def test_get_recording_oracle_address_empty_escrow(self): + with patch("src.chain.escrow.EscrowUtils.get_escrow") as mock_function: + mock_function.return_value = None + with self.assertRaises(Exception) as error: + get_recording_oracle_address(chain_id, escrow_address) + self.assertEqual(f"Can't find escrow {ESCROW_ADDRESS}", str(error.exception)) diff --git a/packages/examples/cvat/exchange-oracle/tests/integration/chain/test_kvstore.py b/packages/examples/cvat/exchange-oracle/tests/integration/chain/test_kvstore.py index e7db6ed052..a655dc873b 100644 --- a/packages/examples/cvat/exchange-oracle/tests/integration/chain/test_kvstore.py +++ b/packages/examples/cvat/exchange-oracle/tests/integration/chain/test_kvstore.py @@ -1,33 +1,76 @@ import unittest -from unittest.mock import patch +from unittest.mock import Mock, patch -from human_protocol_sdk.escrow import EscrowClientError -from web3 import HTTPProvider, Web3 -from web3.middleware import construct_sign_and_send_raw_middleware +from human_protocol_sdk.constants import ChainId, Status +from human_protocol_sdk.escrow import EscrowClientError, EscrowData -from src.chain.kvstore import get_recording_oracle_url +from src.chain.kvstore import get_job_launcher_url, get_recording_oracle_url -from tests.utils.constants import DEFAULT_GAS_PAYER_PRIV, DEFAULT_URL -from tests.utils.setup_escrow import create_escrow -from tests.utils.setup_kvstore import store_kvstore_value +from tests.utils.constants import ( + DEFAULT_URL, + ESCROW_ADDRESS, + FACTORY_ADDRESS, + JOB_LAUNCHER_ADDRESS, + RECORDING_ORACLE_ADDRESS, + TOKEN_ADDRESS, +) + +escrow_address = ESCROW_ADDRESS class ServiceIntegrationTest(unittest.TestCase): def setUp(self): - self.w3 = Web3(HTTPProvider()) - - # Set default gas payer - self.gas_payer = self.w3.eth.account.from_key(DEFAULT_GAS_PAYER_PRIV) - self.w3.middleware_onion.add( - construct_sign_and_send_raw_middleware(self.gas_payer), - "construct_sign_and_send_raw_middleware", + self.w3 = Mock() + self.w3.eth.chain_id = ChainId.LOCALHOST.value + self.escrow_data = EscrowData( + escrow_address, + escrow_address, + "0", + "1000", + 0, + FACTORY_ADDRESS, + JOB_LAUNCHER_ADDRESS, + Status.Pending.name, + TOKEN_ADDRESS, + "1000", + "", + ChainId.LOCALHOST.name, ) - self.w3.eth.default_account = self.gas_payer.address - def test_get_recording_oracle_url(self): - escrow_address = create_escrow(self.w3) - store_kvstore_value(DEFAULT_URL) + def test_get_job_launcher_url(self): + with patch("src.chain.kvstore.get_web3") as mock_function, patch( + "src.chain.kvstore.get_escrow" + ) as mock_escrow, patch("src.chain.kvstore.StakingClient.get_leader") as mock_leader: + mock_escrow.return_value = self.escrow_data + mock_leader.return_value = {"webhook_url": DEFAULT_URL} + mock_function.return_value = self.w3 + recording_url = get_job_launcher_url(self.w3.eth.chain_id, escrow_address) + self.assertEqual(recording_url, DEFAULT_URL) + + def test_get_job_launcher_url_invalid_escrow(self): with patch("src.chain.kvstore.get_web3") as mock_function: + mock_function.return_value = self.w3 + with self.assertRaises(EscrowClientError) as error: + get_job_launcher_url(self.w3.eth.chain_id, "invalid_address") + self.assertEqual(f"Invalid escrow address: invalid_address", str(error.exception)) + + def test_get_job_launcher_url_invalid_recording_address(self): + with patch("src.chain.kvstore.get_web3") as mock_function, patch( + "src.chain.kvstore.get_escrow" + ) as mock_escrow, patch("src.chain.kvstore.StakingClient.get_leader") as mock_leader: + mock_escrow.return_value = self.escrow_data + mock_leader.return_value = {"webhook_url": ""} + mock_function.return_value = self.w3 + recording_url = get_job_launcher_url(self.w3.eth.chain_id, escrow_address) + self.assertEqual(recording_url, "") + + def test_get_recording_oracle_url(self): + with patch("src.chain.kvstore.get_web3") as mock_function, patch( + "src.chain.kvstore.get_escrow" + ) as mock_escrow, patch("src.chain.kvstore.StakingClient.get_leader") as mock_leader: + self.escrow_data.recordingOracle = RECORDING_ORACLE_ADDRESS + mock_escrow.return_value = self.escrow_data + mock_leader.return_value = {"webhook_url": DEFAULT_URL} mock_function.return_value = self.w3 recording_url = get_recording_oracle_url(self.w3.eth.chain_id, escrow_address) self.assertEqual(recording_url, DEFAULT_URL) @@ -40,9 +83,12 @@ def test_get_recording_oracle_url_invalid_escrow(self): self.assertEqual(f"Invalid escrow address: invalid_address", str(error.exception)) def test_get_recording_oracle_url_invalid_recording_address(self): - escrow_address = create_escrow(self.w3) - store_kvstore_value("") - with patch("src.chain.kvstore.get_web3") as mock_function: + with patch("src.chain.kvstore.get_web3") as mock_function, patch( + "src.chain.kvstore.get_escrow" + ) as mock_escrow, patch("src.chain.kvstore.StakingClient.get_leader") as mock_leader: + self.escrow_data.recordingOracle = RECORDING_ORACLE_ADDRESS + mock_escrow.return_value = self.escrow_data + mock_leader.return_value = {"webhook_url": ""} mock_function.return_value = self.w3 recording_url = get_recording_oracle_url(self.w3.eth.chain_id, escrow_address) self.assertEqual(recording_url, "") diff --git a/packages/examples/cvat/exchange-oracle/tests/integration/chain/test_web3.py b/packages/examples/cvat/exchange-oracle/tests/integration/chain/test_web3.py index c30fae52d7..5918c33905 100644 --- a/packages/examples/cvat/exchange-oracle/tests/integration/chain/test_web3.py +++ b/packages/examples/cvat/exchange-oracle/tests/integration/chain/test_web3.py @@ -1,3 +1,4 @@ +import json import unittest from unittest.mock import patch @@ -68,8 +69,9 @@ def test_sign_message_polygon(self): DEFAULT_GAS_PAYER_PRIV, ): mock_function.return_value = self.w3 - signed_message = sign_message(ChainId.POLYGON.value, "message") - self.assertEqual(signed_message, SIGNATURE) + signature, serialized_message = sign_message(ChainId.POLYGON.value, "message") + self.assertEqual(signature, SIGNATURE) + self.assertEqual(serialized_message, json.dumps("message")) def test_sign_message_mumbai(self): with patch("src.chain.web3.get_web3") as mock_function: @@ -78,8 +80,11 @@ def test_sign_message_mumbai(self): DEFAULT_GAS_PAYER_PRIV, ): mock_function.return_value = self.w3 - signed_message = sign_message(ChainId.POLYGON_MUMBAI.value, "message") - self.assertEqual(signed_message, SIGNATURE) + signature, serialized_message = sign_message( + ChainId.POLYGON_MUMBAI.value, "message" + ) + self.assertEqual(signature, SIGNATURE) + self.assertEqual(serialized_message, json.dumps("message")) def test_sign_message_invalid_chain_id(self): with self.assertRaises(ValueError) as error: diff --git a/packages/examples/cvat/exchange-oracle/tests/integration/cron/state_trackers/test_retrieve_annotations.py b/packages/examples/cvat/exchange-oracle/tests/integration/cron/state_trackers/test_retrieve_annotations.py new file mode 100644 index 0000000000..600a30a624 --- /dev/null +++ b/packages/examples/cvat/exchange-oracle/tests/integration/cron/state_trackers/test_retrieve_annotations.py @@ -0,0 +1,315 @@ +import json +import unittest +import uuid +from datetime import datetime, timedelta +from io import RawIOBase +from unittest.mock import Mock, patch + +from src.core.types import ( + ExchangeOracleEventType, + JobStatuses, + Networks, + ProjectStatuses, + TaskStatus, + TaskType, +) +from src.crons.state_trackers import retrieve_annotations +from src.db import SessionLocal +from src.models.cvat import Assignment, Job, Project, Task, User +from src.models.webhook import Webhook + + +class ServiceIntegrationTest(unittest.TestCase): + def setUp(self): + self.session = SessionLocal() + + def tearDown(self): + self.session.close() + + def test_retrieve_annotations(self): + cvat_project_id = 1 + escrow_address = "0x86e83d346041E8806e352681f3F14549C0d2BC67" + project_id = str(uuid.uuid4()) + cvat_project = Project( + id=project_id, + cvat_id=cvat_project_id, + cvat_cloudstorage_id=1, + status=ProjectStatuses.completed.value, + job_type=TaskType.image_label_binary.value, + escrow_address=escrow_address, + chain_id=Networks.localhost.value, + bucket_url="https://test.storage.googleapis.com/", + ) + self.session.add(cvat_project) + + cvat_task_id = 1 + cvat_task = Task( + id=str(uuid.uuid4()), + cvat_id=cvat_task_id, + cvat_project_id=cvat_project_id, + status=TaskStatus.completed.value, + ) + self.session.add(cvat_task) + + cvat_job = Job( + id=str(uuid.uuid4()), + cvat_id=1, + cvat_project_id=cvat_project_id, + cvat_task_id=cvat_task_id, + status=JobStatuses.completed, + ) + self.session.add(cvat_job) + wallet_address = "0x86e83d346041E8806e352681f3F14549C0d2BC67" + user = User( + wallet_address=wallet_address, + cvat_email="test@hmt.ai", + cvat_id=1, + ) + self.session.add(user) + + wallet_address_2 = "0x86e83d346041E8806e352681f3F14549C0d2BC68" + user = User( + wallet_address=wallet_address_2, + cvat_email="test2@hmt.ai", + cvat_id=2, + ) + self.session.add(user) + assignment = Assignment( + id=str(uuid.uuid4()), + user_wallet_address=wallet_address, + cvat_job_id=cvat_job.cvat_id, + expires_at=datetime.now() + timedelta(days=1), + ) + self.session.add(assignment) + self.session.commit() + + with ( + open("tests/utils/manifest.json") as data, + patch("src.crons.state_trackers.get_escrow_manifest") as mock_get_manifest, + patch("src.crons.state_trackers.cvat_api"), + patch("src.crons.state_trackers.validate_escrow"), + patch("src.crons.state_trackers.cloud_client.S3Client") as mock_S3Client, + ): + manifest = json.load(data) + mock_get_manifest.return_value = manifest + mock_create_file = Mock() + mock_S3Client.return_value.create_file = mock_create_file + + retrieve_annotations() + + webhook = ( + self.session.query(Webhook) + .filter_by(escrow_address=escrow_address, chain_id=Networks.localhost.value) + .first() + ) + self.assertIsNotNone(webhook) + self.assertEqual(webhook.event_type, ExchangeOracleEventType.task_finished) + db_project = self.session.query(Project).filter_by(id=project_id).first() + + self.assertEqual(db_project.status, ProjectStatuses.validation) + + def test_retrieve_annotations_unfinished_jobs(self): + cvat_project_id = 1 + escrow_address = "0x86e83d346041E8806e352681f3F14549C0d2BC67" + project_id = str(uuid.uuid4()) + cvat_project = Project( + id=project_id, + cvat_id=cvat_project_id, + cvat_cloudstorage_id=1, + status=ProjectStatuses.completed.value, + job_type=TaskType.image_label_binary.value, + escrow_address=escrow_address, + chain_id=Networks.localhost.value, + bucket_url="https://test.storage.googleapis.com/", + ) + self.session.add(cvat_project) + + cvat_task_id = 1 + cvat_task = Task( + id=str(uuid.uuid4()), + cvat_id=cvat_task_id, + cvat_project_id=cvat_project_id, + status=TaskStatus.completed.value, + ) + self.session.add(cvat_task) + + cvat_job = Job( + id=str(uuid.uuid4()), + cvat_id=1, + cvat_project_id=cvat_project_id, + cvat_task_id=cvat_task_id, + status=JobStatuses.in_progress, + ) + self.session.add(cvat_job) + self.session.commit() + + retrieve_annotations() + + self.session.commit() + db_project = self.session.query(Project).filter_by(id=project_id).first() + + self.assertEqual(db_project.status, ProjectStatuses.annotation) + + @patch("src.cvat.api_calls.get_job_annotations") + def test_retrieve_annotations_error_getting_annotations(self, mock_annotations): + cvat_project_id = 1 + escrow_address = "0x86e83d346041E8806e352681f3F14549C0d2BC67" + project_id = str(uuid.uuid4()) + cvat_project = Project( + id=project_id, + cvat_id=cvat_project_id, + cvat_cloudstorage_id=1, + status=ProjectStatuses.completed.value, + job_type=TaskType.image_label_binary.value, + escrow_address=escrow_address, + chain_id=Networks.localhost.value, + bucket_url="https://test.storage.googleapis.com/", + ) + self.session.add(cvat_project) + + cvat_task_id = 1 + cvat_task = Task( + id=str(uuid.uuid4()), + cvat_id=cvat_task_id, + cvat_project_id=cvat_project_id, + status=TaskStatus.completed.value, + ) + self.session.add(cvat_task) + + cvat_job = Job( + id=str(uuid.uuid4()), + cvat_id=1, + cvat_project_id=cvat_project_id, + cvat_task_id=cvat_task_id, + status=JobStatuses.completed, + ) + self.session.add(cvat_job) + wallet_address = "0x86e83d346041E8806e352681f3F14549C0d2BC67" + user = User( + wallet_address=wallet_address, + cvat_email="test@hmt.ai", + cvat_id=1, + ) + self.session.add(user) + + wallet_address_2 = "0x86e83d346041E8806e352681f3F14549C0d2BC68" + user = User( + wallet_address=wallet_address_2, + cvat_email="test2@hmt.ai", + cvat_id=2, + ) + self.session.add(user) + assignment = Assignment( + id=str(uuid.uuid4()), + user_wallet_address=wallet_address, + cvat_job_id=cvat_job.cvat_id, + expires_at=datetime.now() + timedelta(days=1), + ) + self.session.add(assignment) + self.session.commit() + + with ( + open("tests/utils/manifest.json") as data, + patch("src.crons.state_trackers.get_escrow_manifest") as mock_get_manifest, + patch("src.crons.state_trackers.cvat_api"), + patch("src.crons.state_trackers.cvat_api.get_job_annotations") as mock_annotations, + patch("src.crons.state_trackers.validate_escrow"), + patch("src.crons.state_trackers.cloud_client.S3Client") as mock_S3Client, + ): + manifest = json.load(data) + mock_get_manifest.return_value = manifest + mock_create_file = Mock() + mock_S3Client.return_value.create_file = mock_create_file + mock_annotations.side_effect = Exception("Connection error") + + retrieve_annotations() + + webhook = ( + self.session.query(Webhook) + .filter_by(escrow_address=escrow_address, chain_id=Networks.localhost.value) + .first() + ) + self.assertIsNone(webhook) + + db_project = self.session.query(Project).filter_by(id=project_id).first() + + self.assertEqual(db_project.status, ProjectStatuses.completed.value) + + def test_retrieve_annotations_error_uploading_files(self): + cvat_project_id = 1 + escrow_address = "0x86e83d346041E8806e352681f3F14549C0d2BC67" + project_id = str(uuid.uuid4()) + cvat_project = Project( + id=project_id, + cvat_id=cvat_project_id, + cvat_cloudstorage_id=1, + status=ProjectStatuses.completed.value, + job_type=TaskType.image_label_binary.value, + escrow_address=escrow_address, + chain_id=Networks.localhost.value, + bucket_url="https://test.storage.googleapis.com/", + ) + self.session.add(cvat_project) + + cvat_task_id = 1 + cvat_task = Task( + id=str(uuid.uuid4()), + cvat_id=cvat_task_id, + cvat_project_id=cvat_project_id, + status=TaskStatus.completed.value, + ) + self.session.add(cvat_task) + + cvat_job = Job( + id=str(uuid.uuid4()), + cvat_id=1, + cvat_project_id=cvat_project_id, + cvat_task_id=cvat_task_id, + status=JobStatuses.completed, + ) + self.session.add(cvat_job) + wallet_address = "0x86e83d346041E8806e352681f3F14549C0d2BC67" + user = User( + wallet_address=wallet_address, + cvat_email="test@hmt.ai", + cvat_id=1, + ) + self.session.add(user) + + wallet_address_2 = "0x86e83d346041E8806e352681f3F14549C0d2BC68" + user = User( + wallet_address=wallet_address_2, + cvat_email="test2@hmt.ai", + cvat_id=2, + ) + self.session.add(user) + assignment = Assignment( + id=str(uuid.uuid4()), + user_wallet_address=wallet_address, + cvat_job_id=cvat_job.cvat_id, + expires_at=datetime.now() + timedelta(days=1), + ) + self.session.add(assignment) + self.session.commit() + + with ( + open("tests/utils/manifest.json") as data, + patch("src.crons.state_trackers.get_escrow_manifest") as mock_get_manifest, + patch("src.crons.state_trackers.cvat_api"), + patch("src.crons.state_trackers.validate_escrow"), + ): + manifest = json.load(data) + mock_get_manifest.return_value = manifest + + retrieve_annotations() + + webhook = ( + self.session.query(Webhook) + .filter_by(escrow_address=escrow_address, chain_id=Networks.localhost.value) + .first() + ) + self.assertIsNone(webhook) + + db_project = self.session.query(Project).filter_by(id=project_id).first() + + self.assertEqual(db_project.status, ProjectStatuses.completed.value) diff --git a/packages/examples/cvat/exchange-oracle/tests/integration/cron/state_trackers/test_track_assignments.py b/packages/examples/cvat/exchange-oracle/tests/integration/cron/state_trackers/test_track_assignments.py new file mode 100644 index 0000000000..10a5b8b772 --- /dev/null +++ b/packages/examples/cvat/exchange-oracle/tests/integration/cron/state_trackers/test_track_assignments.py @@ -0,0 +1,152 @@ +import unittest +import uuid +from datetime import datetime, timedelta +from unittest.mock import patch + +from sqlalchemy import update +from sqlalchemy.sql import select + +from src.core.types import ( + AssignmentStatus, + JobStatuses, + Networks, + ProjectStatuses, + TaskStatus, + TaskType, +) +from src.crons.state_trackers import track_assignments +from src.db import SessionLocal +from src.models.cvat import Assignment, Job, Project, Task, User + +from tests.utils.db_helper import create_project_task_and_job + + +class ServiceIntegrationTest(unittest.TestCase): + def setUp(self): + self.session = SessionLocal() + + def tearDown(self): + self.session.close() + + def test_track_expired_assignments(self): + (_, _, cvat_job) = create_project_task_and_job( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", 1 + ) + wallet_address_1 = "0x86e83d346041E8806e352681f3F14549C0d2BC67" + user = User( + wallet_address=wallet_address_1, + cvat_email="test@hmt.ai", + cvat_id=1, + ) + self.session.add(user) + + wallet_address_2 = "0x86e83d346041E8806e352681f3F14549C0d2BC68" + user = User( + wallet_address=wallet_address_2, + cvat_email="test2@hmt.ai", + cvat_id=2, + ) + self.session.add(user) + assignment = Assignment( + id=str(uuid.uuid4()), + user_wallet_address=wallet_address_1, + cvat_job_id=cvat_job.cvat_id, + expires_at=datetime.now() + timedelta(days=1), + ) + assignment_2 = Assignment( + id=str(uuid.uuid4()), + user_wallet_address=wallet_address_2, + cvat_job_id=cvat_job.cvat_id, + expires_at=datetime.now() - timedelta(days=1), + created_at=datetime.now() + timedelta(hours=1), + ) + self.session.add(assignment) + self.session.add(assignment_2) + self.session.commit() + + db_assignments = sorted( + self.session.query(Assignment).all(), key=lambda assignment: assignment.user.cvat_id + ) + self.assertEqual(db_assignments[0].status, AssignmentStatus.created.value) + self.assertEqual(db_assignments[1].status, AssignmentStatus.created.value) + + with patch("src.crons.state_trackers.cvat_api.update_job_assignee") as mock_cvat_api: + track_assignments() + mock_cvat_api.assert_called_once_with(assignment_2.cvat_job_id, assignee_id=None) + + self.session.commit() + + db_assignments = sorted( + self.session.query(Assignment).all(), key=lambda assignment: assignment.user.cvat_id + ) + self.assertEqual(db_assignments[0].status, AssignmentStatus.created.value) + self.assertEqual(db_assignments[1].status, AssignmentStatus.expired.value) + + # TODO: + # Fix src/crons/state_trackers.py + # Where in `cvat_service.get_active_assignments()` return value will be empty + # because it actually looking for the expired assignments + + # def test_track_canceled_assignments(self): + # (_, _, cvat_job) = create_project_task_and_job( + # self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", 1 + # ) + # (cvat_project_2, _, cvat_job_2) = create_project_task_and_job( + # self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC68", 2 + # ) + # wallet_address_1 = "0x86e83d346041E8806e352681f3F14549C0d2BC67" + # user = User( + # wallet_address=wallet_address_1, + # cvat_email="test@hmt.ai", + # cvat_id=1, + # ) + # self.session.add(user) + + # wallet_address_2 = "0x86e83d346041E8806e352681f3F14549C0d2BC68" + # user = User( + # wallet_address=wallet_address_2, + # cvat_email="test2@hmt.ai", + # cvat_id=2, + # ) + # self.session.add(user) + # assignment = Assignment( + # id=str(uuid.uuid4()), + # user_wallet_address=wallet_address_1, + # cvat_job_id=cvat_job.cvat_id, + # expires_at=datetime.now() + timedelta(days=1), + # ) + # assignment_2 = Assignment( + # id=str(uuid.uuid4()), + # user_wallet_address=wallet_address_2, + # cvat_job_id=cvat_job_2.cvat_id, + # expires_at=datetime.now() + timedelta(days=1), + # created_at=datetime.now() + timedelta(hours=1), + # ) + # self.session.add(assignment) + # self.session.add(assignment_2) + + # self.session.execute( + # update(Project) + # .where(Project.id == cvat_project_2.id) + # .values(status=ProjectStatuses.completed.value) + # ) + + # self.session.commit() + + # db_assignments = sorted( + # self.session.query(Assignment).all(), key=lambda assignment: assignment.user.cvat_id + # ) + # self.assertEqual(db_assignments[0].status, AssignmentStatus.created.value) + # self.assertEqual(db_assignments[1].status, AssignmentStatus.created.value) + + # with patch("src.crons.state_trackers.cvat_api.update_job_assignee") as mock_cvat_api: + # track_assignments() + # mock_cvat_api.assert_called_once_with(assignment_2.cvat_job_id, assignee_id=None) + + # self.session.commit() + + # db_assignments = sorted( + # self.session.query(Assignment).all(), key=lambda assignment: assignment.user.cvat_id + # ) + # self.assertEqual(db_assignments[0].status, AssignmentStatus.created.value) + # self.assertEqual(db_assignments[1].status, AssignmentStatus.canceled.value) diff --git a/packages/examples/cvat/exchange-oracle/tests/integration/cron/track_completed/test_track_completed_projects.py b/packages/examples/cvat/exchange-oracle/tests/integration/cron/state_trackers/test_track_completed_projects.py similarity index 100% rename from packages/examples/cvat/exchange-oracle/tests/integration/cron/track_completed/test_track_completed_projects.py rename to packages/examples/cvat/exchange-oracle/tests/integration/cron/state_trackers/test_track_completed_projects.py diff --git a/packages/examples/cvat/exchange-oracle/tests/integration/cron/track_completed/test_track_completed_tasks.py b/packages/examples/cvat/exchange-oracle/tests/integration/cron/state_trackers/test_track_completed_tasks.py similarity index 96% rename from packages/examples/cvat/exchange-oracle/tests/integration/cron/track_completed/test_track_completed_tasks.py rename to packages/examples/cvat/exchange-oracle/tests/integration/cron/state_trackers/test_track_completed_tasks.py index 42d9e69107..c665c8a260 100644 --- a/packages/examples/cvat/exchange-oracle/tests/integration/cron/track_completed/test_track_completed_tasks.py +++ b/packages/examples/cvat/exchange-oracle/tests/integration/cron/state_trackers/test_track_completed_tasks.py @@ -42,7 +42,6 @@ def test_track_completed_tasks(self): cvat_task_id=1, cvat_project_id=1, status=JobStatuses.completed.value, - assignee="John Doe", ) self.session.add(project) @@ -84,7 +83,6 @@ def test_track_completed_tasks_with_unfinished_job(self): cvat_task_id=1, cvat_project_id=1, status=JobStatuses.completed.value, - assignee="John Doe", ) job_2 = Job( id=str(uuid.uuid4()), @@ -92,7 +90,6 @@ def test_track_completed_tasks_with_unfinished_job(self): cvat_task_id=1, cvat_project_id=1, status=JobStatuses.new.value, - assignee="John Doe", ) self.session.add(project) diff --git a/packages/examples/cvat/exchange-oracle/tests/integration/cron/state_trackers/test_track_task_creation.py b/packages/examples/cvat/exchange-oracle/tests/integration/cron/state_trackers/test_track_task_creation.py new file mode 100644 index 0000000000..3c98f4b1fb --- /dev/null +++ b/packages/examples/cvat/exchange-oracle/tests/integration/cron/state_trackers/test_track_task_creation.py @@ -0,0 +1,114 @@ +import unittest +import uuid +from unittest.mock import Mock, patch + +import src.cvat.api_calls as cvat_api +from src.core.types import ExchangeOracleEventType, JobStatuses +from src.crons.state_trackers import track_task_creation +from src.db import SessionLocal +from src.models.cvat import DataUpload, Job +from src.models.webhook import Webhook + +from tests.utils.db_helper import create_project_and_task, create_project_task_and_job + + +class ServiceIntegrationTest(unittest.TestCase): + def setUp(self): + self.session = SessionLocal() + + def tearDown(self): + self.session.close() + + def test_track_track_failed_task_creation(self): + escrow_address = "0x86e83d346041E8806e352681f3F14549C0d2BC67" + (_, cvat_task) = create_project_and_task(self.session, escrow_address, 1) + upload_id = str(uuid.uuid4()) + upload = DataUpload( + id=upload_id, + task_id=cvat_task.cvat_id, + ) + self.session.add(upload) + self.session.commit() + + with patch( + "src.crons.state_trackers.cvat_api.get_task_upload_status" + ) as mock_get_task_upload_status: + mock_get_task_upload_status.return_value = (cvat_api.UploadStatus.FAILED, "Failed") + + track_task_creation() + + webhook = self.session.query(Webhook).filter_by(escrow_address=escrow_address).first() + self.assertIsNotNone(webhook) + data_upload = self.session.query(DataUpload).filter_by(id=upload_id).first() + self.assertIsNone(data_upload) + + def test_track_track_completed_task_creation(self): + escrow_address = "0x86e83d346041E8806e352681f3F14549C0d2BC67" + (_, cvat_task, cvat_job) = create_project_task_and_job(self.session, escrow_address, 1) + upload_id = str(uuid.uuid4()) + upload = DataUpload( + id=upload_id, + task_id=cvat_task.cvat_id, + ) + self.session.add(upload) + self.session.commit() + + new_cvat_job_id = 2 + with ( + patch( + "src.crons.state_trackers.cvat_api.get_task_upload_status" + ) as mock_get_task_upload_status, + patch("src.crons.state_trackers.cvat_api.fetch_task_jobs") as mock_fetch_task_jobs, + ): + mock_get_task_upload_status.return_value = (cvat_api.UploadStatus.FINISHED, None) + mock_cvat_job_1 = Mock() + mock_cvat_job_1.id = cvat_job.cvat_id + + mock_cvat_job_2 = Mock() + mock_cvat_job_2.id = new_cvat_job_id + mock_cvat_job_2.state = JobStatuses.in_progress + + mock_fetch_task_jobs.return_value = [mock_cvat_job_1, mock_cvat_job_2] + + track_task_creation() + + self.session.commit() + + jobs = self.session.query(Job).all() + self.assertIsNotNone(jobs) + self.assertEqual(len(jobs), 2) + self.assertTrue(any(job.cvat_id == 2 for job in jobs)) + data_upload = self.session.query(DataUpload).filter_by(id=upload_id).first() + self.assertIsNone(data_upload) + + # TODO: + # Fix "local variable 'project' referenced before assignment" error in src/crons/state_trackers.py and uncomment this test case + + # def test_track_track_completed_task_creation_error(self): + # escrow_address = "0x86e83d346041E8806e352681f3F14549C0d2BC67" + # (_, cvat_task, cvat_job) = create_project_task_and_job(self.session, escrow_address, 1) + # upload = DataUpload( + # id=str(uuid.uuid4()), + # task_id=cvat_task.cvat_id, + # ) + # self.session.add(upload) + # self.session.commit() + + # with ( + # patch( + # "src.crons.state_trackers.cvat_api.get_task_upload_status" + # ) as mock_get_task_upload_status, + # patch( + # "src.crons.state_trackers.cvat_api.fetch_task_jobs", + # side_effect=cvat_api.exceptions.ApiException("Error"), + # ), + # ): + # mock_get_task_upload_status.return_value = (cvat_api.UploadStatus.FINISHED, None) + + # track_task_creation() + + # self.session.commit() + + # webhook = self.session.query(Webhook).filter_by(escrow_address=escrow_address).first() + # self.assertIsNotNone(webhook) + # self.assertEqual(webhook.event_type, ExchangeOracleEventType.task_creation_failed) diff --git a/packages/examples/cvat/exchange-oracle/tests/integration/cron/test_process_job_launcher_webhooks.py b/packages/examples/cvat/exchange-oracle/tests/integration/cron/test_process_job_launcher_webhooks.py index 95cdd0bf07..e3809f3a05 100644 --- a/packages/examples/cvat/exchange-oracle/tests/integration/cron/test_process_job_launcher_webhooks.py +++ b/packages/examples/cvat/exchange-oracle/tests/integration/cron/test_process_job_launcher_webhooks.py @@ -1,95 +1,207 @@ +import json import unittest import uuid -from unittest.mock import patch +from unittest.mock import MagicMock, Mock, patch +from human_protocol_sdk.constants import ChainId, Status from sqlalchemy.sql import select -from web3 import Web3 -from web3.middleware import construct_sign_and_send_raw_middleware -from web3.providers.rpc import HTTPProvider -from src.core.types import Networks, OracleWebhookSenderType, OracleWebhookStatuses -from src.crons.process_job_launcher_webhooks import process_incoming_job_launcher_webhooks +from src.core.types import ( + ExchangeOracleEventType, + JobLauncherEventType, + JobStatuses, + Networks, + OracleWebhookStatuses, + OracleWebhookTypes, + ProjectStatuses, + TaskStatus, + TaskType, +) +from src.crons.process_job_launcher_webhooks import ( + process_incoming_job_launcher_webhooks, + process_outgoing_job_launcher_webhooks, +) from src.db import SessionLocal -from src.models.cvat import Project +from src.models.cvat import Job, Project, Task from src.models.webhook import Webhook +from src.services.webhook import OracleWebhookDirectionTag -from tests.utils.constants import DEFAULT_GAS_PAYER_PRIV -from tests.utils.setup_escrow import create_escrow, fund_escrow +from tests.utils.constants import DEFAULT_URL, JOB_LAUNCHER_ADDRESS + +escrow_address = "0x86e83d346041E8806e352681f3F14549C0d2BC67" +chain_id = Networks.localhost.value class ServiceIntegrationTest(unittest.TestCase): def setUp(self): self.session = SessionLocal() - self.w3 = Web3(HTTPProvider()) - - self.gas_payer = self.w3.eth.account.from_key(DEFAULT_GAS_PAYER_PRIV) - self.w3.middleware_onion.add( - construct_sign_and_send_raw_middleware(self.gas_payer), - "construct_sign_and_send_raw_middleware", - ) - self.w3.eth.default_account = self.gas_payer.address def tearDown(self): self.session.close() - @patch("src.cvat.create_job.cvat_api") - def test_process_job_launcher_webhooks_successful(self, mock_cvat_api): - mock_cvat_api.create_cloudstorage.return_value.id = 1 - mock_cvat_api.create_project.return_value.id = 1 - mock_cvat_api.create_task.return_value.id = 1 - mock_cvat_api.create_task.return_value.status = "annotation" - - chain_id = Networks.localhost.value - escrow_address = create_escrow(self.w3) - fund_escrow(self.w3, escrow_address) - + def test_process_incoming_job_launcher_webhooks_escrow_created_type(self): webhok_id = str(uuid.uuid4()) webhook = Webhook( id=webhok_id, signature="signature", escrow_address=escrow_address, chain_id=chain_id, - type=OracleWebhookSenderType.job_launcher.value, + type=OracleWebhookTypes.job_launcher.value, status=OracleWebhookStatuses.pending.value, + event_type=JobLauncherEventType.escrow_created.value, + direction=OracleWebhookDirectionTag.incoming, ) self.session.add(webhook) self.session.commit() + with ( + patch("src.chain.escrow.get_escrow") as mock_escrow, + open("tests/utils/manifest.json") as data, + patch("src.cvat.tasks.get_escrow_manifest") as mock_get_manifest, + patch("src.cvat.tasks.cvat_api") as mock_cvat_api, + patch("src.cvat.tasks.cloud_service") as mock_cloud_service, + patch("src.cvat.tasks.get_gt_filenames") as mock_gt_filenames, + ): + manifest = json.load(data) + mock_get_manifest.return_value = manifest + mock_escrow_data = Mock() + mock_escrow_data.status = Status.Pending.name + mock_escrow.return_value = mock_escrow_data + mock_cvat_id = Mock() + mock_cvat_id.id = 1 + mock_cvat_api.create_project.return_value = mock_cvat_id + mock_cvat_api.create_cvat_webhook.return_value = mock_cvat_id + mock_cvat_api.create_cloudstorage.return_value = mock_cvat_id + filenames = [ + "image1.jpg", + "image2.jpg", + ] + mock_gt_filenames.return_value = filenames + mock_cloud_service.list_files.return_value = filenames - process_incoming_job_launcher_webhooks() + process_incoming_job_launcher_webhooks() updated_webhook = ( self.session.execute(select(Webhook).where(Webhook.id == webhok_id)).scalars().first() ) + self.assertEqual(updated_webhook.status, OracleWebhookStatuses.completed.value) self.assertEqual(updated_webhook.attempts, 1) + db_project = ( + self.session.query(Project) + .filter_by(escrow_address=escrow_address, chain_id=chain_id) + .first() + ) + + self.assertEqual(db_project.status, ProjectStatuses.annotation.value) - mock_cvat_api.create_cloudstorage.assert_called_once_with("GOOGLE_CLOUD_STORAGE", "test") - mock_cvat_api.create_project.assert_called_once_with( - escrow_address, [{"name": "dummy_label", "type": "tag"}] + def test_process_incoming_job_launcher_webhooks_escrow_created_type_invalid_escrow_status(self): + webhok_id = str(uuid.uuid4()) + webhook = Webhook( + id=webhok_id, + signature="signature", + escrow_address=escrow_address, + chain_id=chain_id, + type=OracleWebhookTypes.job_launcher.value, + status=OracleWebhookStatuses.pending.value, + event_type=JobLauncherEventType.escrow_created.value, + direction=OracleWebhookDirectionTag.incoming, ) - mock_cvat_api.create_task.assert_called_once_with( - mock_cvat_api.create_cloudstorage.return_value.id, escrow_address + + self.session.add(webhook) + self.session.commit() + with patch("src.chain.escrow.get_escrow") as mock_escrow: + mock_escrow_data = Mock() + mock_escrow_data.status = Status.Complete.name + mock_escrow.return_value = mock_escrow_data + + process_incoming_job_launcher_webhooks() + + self.session.commit() + + updated_webhook = self.session.query(Webhook).filter_by(id=webhok_id).first() + + self.assertEqual(updated_webhook.status, OracleWebhookStatuses.pending.value) + self.assertEqual(updated_webhook.attempts, 1) + + def test_process_incoming_job_launcher_webhooks_escrow_created_type_exceed_max_retries(self): + webhok_id = str(uuid.uuid4()) + webhook = Webhook( + id=webhok_id, + signature="signature", + escrow_address=escrow_address, + chain_id=chain_id, + type=OracleWebhookTypes.job_launcher.value, + status=OracleWebhookStatuses.pending.value, + event_type=JobLauncherEventType.escrow_created.value, + direction=OracleWebhookDirectionTag.incoming, + attempts=5, ) - def test_process_job_launcher_webhooks_invalid_escrow_address(self): - chain_id = Networks.localhost.value - escrow_address = "invalid_address" + self.session.add(webhook) + self.session.commit() + with patch("src.chain.escrow.get_escrow") as mock_escrow: + mock_escrow_data = Mock() + mock_escrow_data.status = Status.Complete.name + mock_escrow.return_value = mock_escrow_data + + process_incoming_job_launcher_webhooks() + + self.session.commit() + updated_webhook = self.session.query(Webhook).filter_by(id=webhok_id).first() + + self.assertEqual(updated_webhook.status, OracleWebhookStatuses.failed.value) + self.assertEqual(updated_webhook.attempts, 6) + + new_webhook = ( + self.session.query(Webhook) + .filter_by( + escrow_address=escrow_address, + chain_id=chain_id, + type=OracleWebhookTypes.exchange_oracle, + ) + .first() + ) + + self.assertEqual(new_webhook.status, OracleWebhookStatuses.pending.value) + self.assertEqual(new_webhook.attempts, 0) + + def test_process_incoming_job_launcher_webhooks_escrow_created_type_remove_when_error(self): webhok_id = str(uuid.uuid4()) webhook = Webhook( id=webhok_id, signature="signature", escrow_address=escrow_address, chain_id=chain_id, - type=OracleWebhookSenderType.job_launcher.value, + type=OracleWebhookTypes.job_launcher.value, status=OracleWebhookStatuses.pending.value, + event_type=JobLauncherEventType.escrow_created.value, + direction=OracleWebhookDirectionTag.incoming, ) self.session.add(webhook) self.session.commit() + with ( + patch("src.chain.escrow.get_escrow") as mock_escrow, + open("tests/utils/manifest.json") as data, + patch("src.cvat.tasks.get_escrow_manifest") as mock_get_manifest, + patch("src.cvat.tasks.cvat_api") as mock_cvat_api, + patch("src.cvat.tasks.cloud_service"), + patch("src.cvat.tasks.get_gt_filenames"), + patch("src.cvat.tasks.db_service.add_project_images", side_effect=Exception("Error")), + ): + manifest = json.load(data) + mock_get_manifest.return_value = manifest + mock_escrow_data = Mock() + mock_escrow_data.status = Status.Pending.name + mock_escrow.return_value = mock_escrow_data + mock_cvat_id = Mock() + mock_cvat_id.id = 1 + mock_cvat_api.create_project.return_value = mock_cvat_id + mock_cvat_api.create_cloudstorage.return_value = mock_cvat_id + mock_cvat_api.create_cvat_webhook.return_value = mock_cvat_id - with self.assertLogs(level="ERROR") as cm: process_incoming_job_launcher_webhooks() updated_webhook = ( @@ -98,16 +210,27 @@ def test_process_job_launcher_webhooks_invalid_escrow_address(self): self.assertEqual(updated_webhook.status, OracleWebhookStatuses.pending.value) self.assertEqual(updated_webhook.attempts, 1) - self.assertEqual( - cm.output, - [ - f"ERROR:app:Webhook: {webhok_id} failed during execution. Error Invalid escrow address: invalid_address" - ], - ) - def test_process_job_launcher_webhooks_invalid_escrow_balance(self): - chain_id = Networks.localhost.value - escrow_address = create_escrow(self.w3) + db_project = ( + self.session.query(Project) + .filter_by(escrow_address=escrow_address, chain_id=chain_id) + .first() + ) + self.assertIsNone(db_project) + + def test_process_incoming_job_launcher_webhooks_escrow_canceled_type(self): + project_id = str(uuid.uuid4()) + cvat_project = Project( + id=project_id, + cvat_id=1, + cvat_cloudstorage_id=1, + status=ProjectStatuses.completed.value, + job_type=TaskType.image_label_binary.value, + escrow_address=escrow_address, + chain_id=Networks.localhost.value, + bucket_url="https://test.storage.googleapis.com/", + ) + self.session.add(cvat_project) webhok_id = str(uuid.uuid4()) webhook = Webhook( @@ -115,33 +238,49 @@ def test_process_job_launcher_webhooks_invalid_escrow_balance(self): signature="signature", escrow_address=escrow_address, chain_id=chain_id, - type=OracleWebhookSenderType.job_launcher.value, + type=OracleWebhookTypes.job_launcher.value, status=OracleWebhookStatuses.pending.value, + event_type=JobLauncherEventType.escrow_canceled.value, + direction=OracleWebhookDirectionTag.incoming, ) self.session.add(webhook) self.session.commit() + with patch("src.chain.escrow.get_escrow") as mock_escrow: + mock_escrow_data = Mock() + mock_escrow_data.status = Status.Pending.name + mock_escrow_data.balance = 1 + mock_escrow.return_value = mock_escrow_data - with self.assertLogs(level="ERROR") as cm: process_incoming_job_launcher_webhooks() updated_webhook = ( self.session.execute(select(Webhook).where(Webhook.id == webhok_id)).scalars().first() ) - self.assertEqual(updated_webhook.status, OracleWebhookStatuses.pending.value) + self.assertEqual(updated_webhook.status, OracleWebhookStatuses.completed.value) self.assertEqual(updated_webhook.attempts, 1) - self.assertEqual( - cm.output, - [ - f"ERROR:app:Webhook: {webhok_id} failed during execution. Error Escrow doesn't have funds" - ], + db_project = ( + self.session.query(Project) + .filter_by(escrow_address=escrow_address, chain_id=chain_id) + .first() ) - def test_process_job_launcher_webhooks_invalid_manifest_url(self): - chain_id = Networks.localhost.value - escrow_address = create_escrow(self.w3) - fund_escrow(self.w3, escrow_address) + self.assertEqual(db_project.status, ProjectStatuses.canceled.value) + + def test_process_incoming_job_launcher_webhooks_escrow_canceled_type_invalid_status(self): + project_id = str(uuid.uuid4()) + cvat_project = Project( + id=project_id, + cvat_id=1, + cvat_cloudstorage_id=1, + status=ProjectStatuses.annotation.value, + job_type=TaskType.image_label_binary.value, + escrow_address=escrow_address, + chain_id=Networks.localhost.value, + bucket_url="https://test.storage.googleapis.com/", + ) + self.session.add(cvat_project) webhok_id = str(uuid.uuid4()) webhook = Webhook( @@ -149,14 +288,19 @@ def test_process_job_launcher_webhooks_invalid_manifest_url(self): signature="signature", escrow_address=escrow_address, chain_id=chain_id, - type=OracleWebhookSenderType.job_launcher.value, + type=OracleWebhookTypes.job_launcher.value, status=OracleWebhookStatuses.pending.value, + event_type=JobLauncherEventType.escrow_canceled.value, + direction=OracleWebhookDirectionTag.incoming, ) self.session.add(webhook) self.session.commit() + with patch("src.chain.escrow.get_escrow") as mock_escrow: + mock_escrow_data = Mock() + mock_escrow_data.status = Status.Complete.name + mock_escrow.return_value = mock_escrow_data - with self.assertLogs(level="ERROR") as cm: process_incoming_job_launcher_webhooks() updated_webhook = ( @@ -165,17 +309,27 @@ def test_process_job_launcher_webhooks_invalid_manifest_url(self): self.assertEqual(updated_webhook.status, OracleWebhookStatuses.pending.value) self.assertEqual(updated_webhook.attempts, 1) - self.assertTrue( - f"ERROR:app:Webhook: {webhok_id} failed during execution. Error HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /api/cloudstorages" - in cm.output[0], + db_project = ( + self.session.query(Project) + .filter_by(escrow_address=escrow_address, chain_id=chain_id) + .first() ) - @patch("src.cvat.create_job.cvat_api") - def test_process_job_launcher_webhooks_invalid_cloudstorage(self, mock_cvat_api): - mock_cvat_api.create_cloudstorage.side_effect = Exception("Connection error") - chain_id = Networks.localhost.value - escrow_address = create_escrow(self.w3) - fund_escrow(self.w3, escrow_address) + self.assertEqual(db_project.status, ProjectStatuses.annotation.value) + + def test_process_incoming_job_launcher_webhooks_escrow_canceled_type_invalid_balance(self): + project_id = str(uuid.uuid4()) + cvat_project = Project( + id=project_id, + cvat_id=1, + cvat_cloudstorage_id=1, + status=ProjectStatuses.annotation.value, + job_type=TaskType.image_label_binary.value, + escrow_address=escrow_address, + chain_id=Networks.localhost.value, + bucket_url="https://test.storage.googleapis.com/", + ) + self.session.add(cvat_project) webhok_id = str(uuid.uuid4()) webhook = Webhook( @@ -183,14 +337,20 @@ def test_process_job_launcher_webhooks_invalid_cloudstorage(self, mock_cvat_api) signature="signature", escrow_address=escrow_address, chain_id=chain_id, - type=OracleWebhookSenderType.job_launcher.value, + type=OracleWebhookTypes.job_launcher.value, status=OracleWebhookStatuses.pending.value, + event_type=JobLauncherEventType.escrow_canceled.value, + direction=OracleWebhookDirectionTag.incoming, ) self.session.add(webhook) self.session.commit() + with patch("src.chain.escrow.get_escrow") as mock_escrow: + mock_escrow_data = Mock() + mock_escrow_data.status = Status.Complete.name + mock_escrow_data.balance = 0 + mock_escrow.return_value = mock_escrow_data - with self.assertLogs(level="ERROR") as cm: process_incoming_job_launcher_webhooks() updated_webhook = ( @@ -199,18 +359,16 @@ def test_process_job_launcher_webhooks_invalid_cloudstorage(self, mock_cvat_api) self.assertEqual(updated_webhook.status, OracleWebhookStatuses.pending.value) self.assertEqual(updated_webhook.attempts, 1) - self.assertEqual( - cm.output, - [f"ERROR:app:Webhook: {webhok_id} failed during execution. Error Connection error"], + db_project = ( + self.session.query(Project) + .filter_by(escrow_address=escrow_address, chain_id=chain_id) + .first() ) - @patch("src.cvat.create_job.cvat_api") - def test_process_job_launcher_webhooks_invalid_project(self, mock_cvat_api): - mock_cvat_api.create_cloudstorage.return_value.id = 1 - mock_cvat_api.create_project.side_effect = Exception("Connection error") + self.assertEqual(db_project.status, ProjectStatuses.annotation.value) + + def test_process_outgoing_job_launcher_webhooks(self): chain_id = Networks.localhost.value - escrow_address = create_escrow(self.w3) - fund_escrow(self.w3, escrow_address) webhok_id = str(uuid.uuid4()) webhook = Webhook( @@ -218,38 +376,43 @@ def test_process_job_launcher_webhooks_invalid_project(self, mock_cvat_api): signature="signature", escrow_address=escrow_address, chain_id=chain_id, - type=OracleWebhookSenderType.job_launcher.value, + type=OracleWebhookTypes.job_launcher.value, status=OracleWebhookStatuses.pending.value, + event_type=ExchangeOracleEventType.task_finished.value, + direction=OracleWebhookDirectionTag.outgoing, ) self.session.add(webhook) self.session.commit() - - with self.assertLogs(level="ERROR") as cm: - process_incoming_job_launcher_webhooks() + with ( + patch("src.chain.kvstore.get_web3") as mock_web3, + patch("src.chain.kvstore.get_escrow") as mock_escrow, + patch("src.chain.kvstore.StakingClient.get_leader") as mock_leader, + patch("httpx.Client.post") as mock_httpx_post, + ): + w3 = Mock() + w3.eth.chain_id = ChainId.LOCALHOST.value + mock_web3.return_value = w3 + mock_escrow_data = Mock() + mock_escrow_data.launcher = JOB_LAUNCHER_ADDRESS + mock_escrow.return_value = mock_escrow_data + mock_leader.return_value = {"webhook_url": DEFAULT_URL} + mock_response = MagicMock() + mock_response.raise_for_status.return_value = None + mock_httpx_post.return_value = mock_response + + process_outgoing_job_launcher_webhooks() updated_webhook = ( self.session.execute(select(Webhook).where(Webhook.id == webhok_id)).scalars().first() ) - self.assertEqual(updated_webhook.status, OracleWebhookStatuses.pending.value) + self.assertEqual(updated_webhook.status, OracleWebhookStatuses.completed.value) self.assertEqual(updated_webhook.attempts, 1) - self.assertEqual( - cm.output, - [f"ERROR:app:Webhook: {webhok_id} failed during execution. Error Connection error"], - ) + mock_httpx_post.assert_called_once() - @patch("src.cvat.revert_job.cvat_api") - @patch("src.cvat.create_job.cvat_api") - def test_process_job_launcher_webhooks_invalid_task( - self, mock_create_cvat_api, mock_revert_cvat_api - ): - mock_create_cvat_api.create_cloudstorage.return_value.id = 1 - mock_create_cvat_api.create_project.return_value.id = 1 - mock_create_cvat_api.create_task.side_effect = Exception("Connection error") + def test_process_outgoing_job_launcher_webhooks_invalid_type(self): chain_id = Networks.localhost.value - escrow_address = create_escrow(self.w3) - fund_escrow(self.w3, escrow_address) webhok_id = str(uuid.uuid4()) webhook = Webhook( @@ -257,34 +420,20 @@ def test_process_job_launcher_webhooks_invalid_task( signature="signature", escrow_address=escrow_address, chain_id=chain_id, - type=OracleWebhookSenderType.job_launcher.value, + type=OracleWebhookTypes.job_launcher.value, status=OracleWebhookStatuses.pending.value, + event_type=JobLauncherEventType.escrow_created.value, + direction=OracleWebhookDirectionTag.outgoing, ) self.session.add(webhook) self.session.commit() - with self.assertLogs(level="ERROR") as cm: - process_incoming_job_launcher_webhooks() + process_outgoing_job_launcher_webhooks() updated_webhook = ( self.session.execute(select(Webhook).where(Webhook.id == webhok_id)).scalars().first() ) - project = ( - self.session.execute( - select(Project).where( - Project.cvat_id == mock_create_cvat_api.create_project.return_value.id - ) - ) - .scalars() - .first() - ) - - self.assertIsNone(project) self.assertEqual(updated_webhook.status, OracleWebhookStatuses.pending.value) self.assertEqual(updated_webhook.attempts, 1) - self.assertEqual( - cm.output, - [f"ERROR:app:Webhook: {webhok_id} failed during execution. Error Connection error"], - ) diff --git a/packages/examples/cvat/exchange-oracle/tests/integration/cron/test_process_recording_oracle_webhooks.py b/packages/examples/cvat/exchange-oracle/tests/integration/cron/test_process_recording_oracle_webhooks.py index 91d24232e6..f77cd30ded 100644 --- a/packages/examples/cvat/exchange-oracle/tests/integration/cron/test_process_recording_oracle_webhooks.py +++ b/packages/examples/cvat/exchange-oracle/tests/integration/cron/test_process_recording_oracle_webhooks.py @@ -1,47 +1,56 @@ import unittest import uuid -from unittest.mock import MagicMock, patch +from unittest.mock import MagicMock, Mock, patch +from human_protocol_sdk.constants import ChainId from sqlalchemy.sql import select -from web3 import Web3 -from web3.middleware import construct_sign_and_send_raw_middleware -from web3.providers.rpc import HTTPProvider -from src.core.types import Networks, OracleWebhookSenderType, OracleWebhookStatuses -from src.crons.process_recording_oracle_webhooks import process_outgoing_recording_oracle_webhooks +from src.core.types import ( + ExchangeOracleEventType, + JobStatuses, + Networks, + OracleWebhookStatuses, + OracleWebhookTypes, + ProjectStatuses, + RecordingOracleEventType, + TaskStatus, + TaskType, +) +from src.crons.process_recording_oracle_webhooks import ( + process_incoming_recording_oracle_webhooks, + process_outgoing_recording_oracle_webhooks, +) from src.db import SessionLocal +from src.models.cvat import Job, Project, Task from src.models.webhook import Webhook +from src.services.webhook import OracleWebhookDirectionTag -from tests.utils.constants import DEFAULT_GAS_PAYER_PRIV -from tests.utils.setup_escrow import create_escrow -from tests.utils.setup_kvstore import store_kvstore_value +from tests.utils.constants import DEFAULT_URL, RECORDING_ORACLE_ADDRESS + +escrow_address = "0x86e83d346041E8806e352681f3F14549C0d2BC67" +chain_id = Networks.localhost.value class ServiceIntegrationTest(unittest.TestCase): def setUp(self): self.session = SessionLocal() - self.w3 = Web3(HTTPProvider()) - - self.gas_payer = self.w3.eth.account.from_key(DEFAULT_GAS_PAYER_PRIV) - self.w3.middleware_onion.add( - construct_sign_and_send_raw_middleware(self.gas_payer), - "construct_sign_and_send_raw_middleware", - ) - self.w3.eth.default_account = self.gas_payer.address def tearDown(self): self.session.close() - @patch("httpx.Client.post") - def test_process_recording_oracle_webhooks(self, mock_httpx_post): - mock_response = MagicMock() - mock_response.raise_for_status.return_value = None - mock_httpx_post.return_value = mock_response - expected_url = "expected_url" - - chain_id = Networks.localhost.value - escrow_address = create_escrow(self.w3) - store_kvstore_value(expected_url) + def test_process_incoming_recording_oracle_webhooks_task_completed_type(self): + project_id = str(uuid.uuid4()) + cvat_project = Project( + id=project_id, + cvat_id=1, + cvat_cloudstorage_id=1, + status=ProjectStatuses.validation.value, + job_type=TaskType.image_label_binary.value, + escrow_address=escrow_address, + chain_id=Networks.localhost.value, + bucket_url="https://test.storage.googleapis.com/", + ) + self.session.add(cvat_project) webhok_id = str(uuid.uuid4()) webhook = Webhook( @@ -49,15 +58,16 @@ def test_process_recording_oracle_webhooks(self, mock_httpx_post): signature="signature", escrow_address=escrow_address, chain_id=chain_id, - s3_url="s3_url", - type=OracleWebhookSenderType.recording_oracle.value, + type=OracleWebhookTypes.recording_oracle.value, status=OracleWebhookStatuses.pending.value, + event_type=RecordingOracleEventType.task_completed.value, + direction=OracleWebhookDirectionTag.incoming, ) self.session.add(webhook) self.session.commit() - process_outgoing_recording_oracle_webhooks() + process_incoming_recording_oracle_webhooks() updated_webhook = ( self.session.execute(select(Webhook).where(Webhook.id == webhok_id)).scalars().first() @@ -65,19 +75,25 @@ def test_process_recording_oracle_webhooks(self, mock_httpx_post): self.assertEqual(updated_webhook.status, OracleWebhookStatuses.completed.value) self.assertEqual(updated_webhook.attempts, 1) - mock_httpx_post.assert_called_once_with( - expected_url, - headers={"human-signature": "signature"}, - json={ - "escrow_address": escrow_address, - "chain_id": chain_id, - "s3_url": "s3_url", - }, - ) + db_project = self.session.query(Project).filter_by(id=project_id).first() - def test_process_recording_oracle_webhooks_invalid_escrow_address(self): - chain_id = Networks.localhost.value - escrow_address = "invalid_address" + self.assertEqual(db_project.status, ProjectStatuses.recorded.value) + + def test_process_incoming_recording_oracle_webhooks_task_completed_type_invalid_project_status( + self, + ): + project_id = str(uuid.uuid4()) + cvat_project = Project( + id=project_id, + cvat_id=1, + cvat_cloudstorage_id=1, + status=ProjectStatuses.completed.value, + job_type=TaskType.image_label_binary.value, + escrow_address=escrow_address, + chain_id=Networks.localhost.value, + bucket_url="https://test.storage.googleapis.com/", + ) + self.session.add(cvat_project) webhok_id = str(uuid.uuid4()) webhook = Webhook( @@ -85,35 +101,115 @@ def test_process_recording_oracle_webhooks_invalid_escrow_address(self): signature="signature", escrow_address=escrow_address, chain_id=chain_id, - s3_url="s3_url", - type=OracleWebhookSenderType.recording_oracle.value, + type=OracleWebhookTypes.recording_oracle.value, status=OracleWebhookStatuses.pending.value, + event_type=RecordingOracleEventType.task_completed.value, + direction=OracleWebhookDirectionTag.incoming, ) self.session.add(webhook) self.session.commit() - with self.assertLogs(level="ERROR") as cm: - process_outgoing_recording_oracle_webhooks() + process_incoming_recording_oracle_webhooks() updated_webhook = ( self.session.execute(select(Webhook).where(Webhook.id == webhok_id)).scalars().first() ) - self.assertEqual(updated_webhook.status, OracleWebhookStatuses.pending.value) + self.assertEqual(updated_webhook.status, OracleWebhookStatuses.completed.value) self.assertEqual(updated_webhook.attempts, 1) - self.assertEqual( - cm.output, - [ - f"ERROR:app:[cron][webhook][process_recording_oracle_webhooks] Webhook: {webhok_id} failed during execution. Error Invalid escrow address: invalid_address" - ], + + db_project = self.session.query(Project).filter_by(id=project_id).first() + + self.assertEqual(db_project.status, ProjectStatuses.completed.value) + + def test_process_incoming_recording_oracle_webhooks_task_task_rejected_type(self): + cvat_id = 1 + + project_id = str(uuid.uuid4()) + cvat_project = Project( + id=project_id, + cvat_id=cvat_id, + cvat_cloudstorage_id=1, + status=ProjectStatuses.validation.value, + job_type=TaskType.image_label_binary.value, + escrow_address=escrow_address, + chain_id=Networks.localhost.value, + bucket_url="https://test.storage.googleapis.com/", + ) + self.session.add(cvat_project) + task_id = str(uuid.uuid4()) + cvat_task = Task( + id=task_id, + cvat_id=cvat_id, + cvat_project_id=cvat_project.cvat_id, + status=TaskStatus.completed.value, + ) + self.session.add(cvat_task) + + job_id = str(uuid.uuid4()) + cvat_job = Job( + id=job_id, + cvat_id=cvat_id, + cvat_project_id=cvat_project.cvat_id, + cvat_task_id=cvat_task.cvat_id, + status=JobStatuses.completed, + ) + self.session.add(cvat_job) + + webhok_id = str(uuid.uuid4()) + webhook = Webhook( + id=webhok_id, + signature="signature", + escrow_address=escrow_address, + chain_id=chain_id, + type=OracleWebhookTypes.recording_oracle.value, + status=OracleWebhookStatuses.pending.value, + event_type=RecordingOracleEventType.task_rejected.value, + event_data={"rejected_job_ids": [cvat_id]}, + direction=OracleWebhookDirectionTag.incoming, + ) + + self.session.add(webhook) + self.session.commit() + + process_incoming_recording_oracle_webhooks() + + updated_webhook = ( + self.session.execute(select(Webhook).where(Webhook.id == webhok_id)).scalars().first() ) - def test_process_recording_oracle_webhooks_invalid_recording_oracle_url( + self.assertEqual(updated_webhook.status, OracleWebhookStatuses.completed.value) + self.assertEqual(updated_webhook.attempts, 1) + db_project = self.session.query(Project).filter_by(id=project_id).first() + + self.assertEqual(db_project.status, ProjectStatuses.annotation.value) + + db_task = self.session.query(Task).filter_by(id=task_id).first() + + self.assertEqual(db_task.status, TaskStatus.annotation.value) + + db_job = self.session.query(Job).filter_by(id=job_id).first() + + self.assertEqual(db_job.status, JobStatuses.new.value) + + def test_process_incoming_recording_oracle_webhooks_task_task_rejected_type_invalid_project_status( self, ): - chain_id = Networks.localhost.value - escrow_address = create_escrow(self.w3) + cvat_id = 1 + + project_id = str(uuid.uuid4()) + cvat_project = Project( + id=project_id, + cvat_id=cvat_id, + cvat_cloudstorage_id=1, + status=ProjectStatuses.completed.value, + job_type=TaskType.image_label_binary.value, + escrow_address=escrow_address, + chain_id=Networks.localhost.value, + bucket_url="https://test.storage.googleapis.com/", + ) + self.session.add(cvat_project) webhok_id = str(uuid.uuid4()) webhook = Webhook( @@ -121,41 +217,31 @@ def test_process_recording_oracle_webhooks_invalid_recording_oracle_url( signature="signature", escrow_address=escrow_address, chain_id=chain_id, - s3_url="s3_url", - type=OracleWebhookSenderType.recording_oracle.value, + type=OracleWebhookTypes.recording_oracle.value, status=OracleWebhookStatuses.pending.value, + event_type=RecordingOracleEventType.task_rejected.value, + event_data={"rejected_job_ids": [cvat_id]}, + direction=OracleWebhookDirectionTag.incoming, ) self.session.add(webhook) self.session.commit() - with self.assertLogs(level="ERROR") as cm: - process_outgoing_recording_oracle_webhooks() + process_incoming_recording_oracle_webhooks() updated_webhook = ( self.session.execute(select(Webhook).where(Webhook.id == webhok_id)).scalars().first() ) - self.assertEqual(updated_webhook.status, OracleWebhookStatuses.pending.value) + self.assertEqual(updated_webhook.status, OracleWebhookStatuses.completed.value) self.assertEqual(updated_webhook.attempts, 1) - self.assertEqual( - cm.output, - [ - f"ERROR:app:[cron][webhook][process_recording_oracle_webhooks] Webhook: {webhok_id} failed during execution. Error Request URL is missing an 'http://' or 'https://' protocol." - ], - ) - @patch("httpx.Client.post") - def test_process_recording_oracle_webhooks_invalid_request(self, mock_httpx_post): - mock_response = MagicMock() - mock_response.status_code = 404 - mock_response.raise_for_status.side_effect = Exception("The requested URL was not found.") - mock_httpx_post.return_value = mock_response - expected_url = "expected_url" + db_project = self.session.query(Project).filter_by(id=project_id).first() + self.assertEqual(db_project.status, ProjectStatuses.completed.value) + + def test_process_outgoing_recording_oracle_webhooks(self): chain_id = Networks.localhost.value - escrow_address = create_escrow(self.w3) - store_kvstore_value(expected_url) webhok_id = str(uuid.uuid4()) webhook = Webhook( @@ -163,26 +249,64 @@ def test_process_recording_oracle_webhooks_invalid_request(self, mock_httpx_post signature="signature", escrow_address=escrow_address, chain_id=chain_id, - s3_url="s3_url", - type=OracleWebhookSenderType.recording_oracle.value, + type=OracleWebhookTypes.recording_oracle.value, status=OracleWebhookStatuses.pending.value, + event_type=ExchangeOracleEventType.task_finished.value, + direction=OracleWebhookDirectionTag.outgoing, ) self.session.add(webhook) self.session.commit() + with ( + patch("src.chain.kvstore.get_web3") as mock_web3, + patch("src.chain.kvstore.get_escrow") as mock_escrow, + patch("src.chain.kvstore.StakingClient.get_leader") as mock_leader, + patch("httpx.Client.post") as mock_httpx_post, + ): + w3 = Mock() + w3.eth.chain_id = ChainId.LOCALHOST.value + mock_web3.return_value = w3 + mock_escrow_data = Mock() + mock_escrow_data.recordingOracle = RECORDING_ORACLE_ADDRESS + mock_escrow.return_value = mock_escrow_data + mock_leader.return_value = {"webhook_url": DEFAULT_URL} + mock_response = MagicMock() + mock_response.raise_for_status.return_value = None + mock_httpx_post.return_value = mock_response - with self.assertLogs(level="ERROR") as cm: process_outgoing_recording_oracle_webhooks() updated_webhook = ( self.session.execute(select(Webhook).where(Webhook.id == webhok_id)).scalars().first() ) - self.assertEqual(updated_webhook.status, OracleWebhookStatuses.pending.value) + self.assertEqual(updated_webhook.status, OracleWebhookStatuses.completed.value) self.assertEqual(updated_webhook.attempts, 1) - self.assertEqual( - cm.output, - [ - f"ERROR:app:[cron][webhook][process_recording_oracle_webhooks] Webhook: {webhok_id} failed during execution. Error The requested URL was not found." - ], + mock_httpx_post.assert_called_once() + + def test_process_outgoing_recording_oracle_webhooks_invalid_type(self): + chain_id = Networks.localhost.value + + webhok_id = str(uuid.uuid4()) + webhook = Webhook( + id=webhok_id, + signature="signature", + escrow_address=escrow_address, + chain_id=chain_id, + type=OracleWebhookTypes.recording_oracle.value, + status=OracleWebhookStatuses.pending.value, + event_type=RecordingOracleEventType.task_completed.value, + direction=OracleWebhookDirectionTag.outgoing, + ) + + self.session.add(webhook) + self.session.commit() + + process_outgoing_recording_oracle_webhooks() + + updated_webhook = ( + self.session.execute(select(Webhook).where(Webhook.id == webhok_id)).scalars().first() ) + + self.assertEqual(updated_webhook.status, OracleWebhookStatuses.pending.value) + self.assertEqual(updated_webhook.attempts, 1) diff --git a/packages/examples/cvat/exchange-oracle/tests/integration/cron/test_retrieve_annotations.py b/packages/examples/cvat/exchange-oracle/tests/integration/cron/test_retrieve_annotations.py deleted file mode 100644 index 3fd8afb88f..0000000000 --- a/packages/examples/cvat/exchange-oracle/tests/integration/cron/test_retrieve_annotations.py +++ /dev/null @@ -1,345 +0,0 @@ -import unittest -import uuid -from unittest.mock import patch - -from human_protocol_sdk.storage import StorageClient -from sqlalchemy.sql import select - -from src.core.config import StorageConfig -from src.core.types import ( - JobStatuses, - Networks, - OracleWebhookStatuses, - OracleWebhookTypes, - ProjectStatuses, - TaskStatus, - TaskType, -) -from src.crons.retrieve_annotations import retrieve_annotations -from src.db import SessionLocal -from src.models.cvat import Job, Project, Task -from src.models.webhook import Webhook - -from tests.unit.helpers.predefined_annotations import raw_binary_annotations - - -class ServiceIntegrationTest(unittest.TestCase): - def setUp(self): - self.session = SessionLocal() - - def tearDown(self): - self.session.close() - - @patch("src.cvat.api_calls.get_job_annotations") - def test_process_recording_oracle_webhooks(self, mock_annotations): - mock_annotations.return_value = raw_binary_annotations - escrow_address = "0x86e83d346041E8806e352681f3F14549C0d2BC67" - - project_id = str(uuid.uuid4()) - project = Project( - id=project_id, - cvat_id=1, - cvat_cloudstorage_id=1, - status=ProjectStatuses.completed.value, - job_type=TaskType.image_label_binary.value, - escrow_address=escrow_address, - chain_id=Networks.localhost.value, - bucket_url="https://test.storage.googleapis.com/", - ) - - task = Task( - id=str(uuid.uuid4()), - cvat_id=1, - cvat_project_id=1, - status=TaskStatus.completed.value, - ) - - job = Job( - id=str(uuid.uuid4()), - cvat_id=1, - cvat_task_id=1, - cvat_project_id=1, - status=JobStatuses.completed.value, - assignee="John Doe", - ) - - self.session.add(project) - self.session.add(task) - self.session.add(job) - self.session.commit() - - retrieve_annotations() - - webhook = ( - self.session.execute( - select(Webhook).where( - Webhook.escrow_address == escrow_address - and Webhook.chain_id == Networks.localhost.value - ) - ) - .scalars() - .first() - ) - self.assertEqual(webhook.type, OracleWebhookTypes.recording_oracle.value) - self.assertEqual(webhook.status, OracleWebhookStatuses.pending.value) - self.assertIsNotNone( - webhook.signature, - "0x8d1b4e3c5e98ea57280b5910e110bf8000b1dfd9a2b9d16349eadeba46c6e373778e5368eaffaef4cd1c915046b1c706da9e6b9200b2c71eb1217305267d3e311c", - ) - - file = StorageClient.download_file_from_url( - f"http://{StorageConfig.endpoint_url}/{StorageConfig.results_bucket_name}/s36f849163dce528923b9ecd81089903bad5c461f6.json" - ).decode() - - self.assertEqual( - file, - '[{"answers": [{"assignee": "John Doe", "tag": "dummy_label"}], "url": "https://test.storage.googleapis.com/1.jpg"}, {"answers": [{"assignee": "John Doe", "tag": "dummy_label"}], "url": "https://test.storage.googleapis.com/2.jpg"}, {"answers": [{"assignee": "John Doe", "tag": "dummy_label"}], "url": "https://test.storage.googleapis.com/3.jpg"}]', - ) - - project = ( - self.session.execute(select(Project).where(Project.id == project_id)).scalars().first() - ) - - self.assertEqual(project.status, ProjectStatuses.recorded.value) - - def test_process_recording_oracle_webhooks_unfinished_jobs(self): - escrow_address = "0x86e83d346041E8806e352681f3F14549C0d2BC67" - - project_id = str(uuid.uuid4()) - project = Project( - id=project_id, - cvat_id=1, - cvat_cloudstorage_id=1, - status=ProjectStatuses.completed.value, - job_type=TaskType.image_label_binary.value, - escrow_address=escrow_address, - chain_id=Networks.localhost.value, - bucket_url="https://test.storage.googleapis.com/", - ) - - task = Task( - id=str(uuid.uuid4()), - cvat_id=1, - cvat_project_id=1, - status=TaskStatus.completed.value, - ) - - job = Job( - id=str(uuid.uuid4()), - cvat_id=1, - cvat_task_id=1, - cvat_project_id=1, - status=JobStatuses.in_progress.value, - assignee="John Doe", - ) - - self.session.add(project) - self.session.add(task) - self.session.add(job) - self.session.commit() - - retrieve_annotations() - - webhook = ( - self.session.execute( - select(Webhook).where( - Webhook.escrow_address == escrow_address - and Webhook.chain_id == Networks.localhost.value - ) - ) - .scalars() - .first() - ) - self.assertIsNone(webhook) - - project = ( - self.session.execute(select(Project).where(Project.id == project_id)).scalars().first() - ) - - self.assertEqual(project.status, ProjectStatuses.annotation.value) - - @patch("src.cvat.api_calls.get_job_annotations") - def test_process_recording_oracle_webhooks_error_getting_annotations(self, mock_annotations): - mock_annotations.side_effect = Exception("Connection error") - escrow_address = "0x86e83d346041E8806e352681f3F14549C0d2BC67" - - project_id = str(uuid.uuid4()) - project = Project( - id=project_id, - cvat_id=1, - cvat_cloudstorage_id=1, - status=ProjectStatuses.completed.value, - job_type=TaskType.image_label_binary.value, - escrow_address=escrow_address, - chain_id=Networks.localhost.value, - bucket_url="https://test.storage.googleapis.com/", - ) - - task = Task( - id=str(uuid.uuid4()), - cvat_id=1, - cvat_project_id=1, - status=TaskStatus.completed.value, - ) - - job = Job( - id=str(uuid.uuid4()), - cvat_id=1, - cvat_task_id=1, - cvat_project_id=1, - status=JobStatuses.completed.value, - assignee="John Doe", - ) - - self.session.add(project) - self.session.add(task) - self.session.add(job) - self.session.commit() - - with self.assertLogs(level="ERROR") as cm: - retrieve_annotations() - - webhook = ( - self.session.execute( - select(Webhook).where( - Webhook.escrow_address == escrow_address - and Webhook.chain_id == Networks.localhost.value - ) - ) - .scalars() - .first() - ) - self.assertIsNone(webhook) - - project = ( - self.session.execute(select(Project).where(Project.id == project_id)).scalars().first() - ) - - self.assertEqual(project.status, ProjectStatuses.completed.value) - - self.assertEqual( - cm.output, - [f"ERROR:app:[cron][cvat][retrieve_annotations] Connection error"], - ) - - @patch("src.cvat.api_calls.get_job_annotations") - @patch("src.core.config.StorageConfig.secure") - def test_process_recording_oracle_webhooks_error_uploading_files( - self, mock_storage_config, mock_annotations - ): - mock_annotations.return_value = raw_binary_annotations - mock_storage_config.return_value = True - escrow_address = "0x86e83d346041E8806e352681f3F14549C0d2BC67" - - project_id = str(uuid.uuid4()) - project = Project( - id=project_id, - cvat_id=1, - cvat_cloudstorage_id=1, - status=ProjectStatuses.completed.value, - job_type=TaskType.image_label_binary.value, - escrow_address=escrow_address, - chain_id=Networks.localhost.value, - bucket_url="https://test.storage.googleapis.com/", - ) - - task = Task( - id=str(uuid.uuid4()), - cvat_id=1, - cvat_project_id=1, - status=TaskStatus.completed.value, - ) - - job = Job( - id=str(uuid.uuid4()), - cvat_id=1, - cvat_task_id=1, - cvat_project_id=1, - status=JobStatuses.completed.value, - assignee="John Doe", - ) - - self.session.add(project) - self.session.add(task) - self.session.add(job) - self.session.commit() - - with self.assertLogs(level="ERROR") as cm: - retrieve_annotations() - - webhook = ( - self.session.execute( - select(Webhook).where( - Webhook.escrow_address == escrow_address - and Webhook.chain_id == Networks.localhost.value - ) - ) - .scalars() - .first() - ) - self.assertIsNone(webhook) - - self.assertNotEqual( - cm.output, - [], - ) - - @patch("src.cvat.api_calls.get_job_annotations") - def test_process_recording_oracle_webhooks_invalid_chain_id(self, mock_annotations): - mock_annotations.return_value = raw_binary_annotations - escrow_address = "0x86e83d346041E8806e352681f3F14549C0d2BC67" - - project_id = str(uuid.uuid4()) - project = Project( - id=project_id, - cvat_id=1, - cvat_cloudstorage_id=1, - status=ProjectStatuses.completed.value, - job_type=TaskType.image_label_binary.value, - escrow_address=escrow_address, - chain_id=1234, - bucket_url="https://test.storage.googleapis.com/", - ) - - task = Task( - id=str(uuid.uuid4()), - cvat_id=1, - cvat_project_id=1, - status=TaskStatus.completed.value, - ) - - job = Job( - id=str(uuid.uuid4()), - cvat_id=1, - cvat_task_id=1, - cvat_project_id=1, - status=JobStatuses.completed.value, - assignee="John Doe", - ) - - self.session.add(project) - self.session.add(task) - self.session.add(job) - self.session.commit() - - with self.assertLogs(level="ERROR") as cm: - retrieve_annotations() - - webhook = ( - self.session.execute( - select(Webhook).where( - Webhook.escrow_address == escrow_address - and Webhook.chain_id == Networks.localhost.value - ) - ) - .scalars() - .first() - ) - self.assertIsNone(webhook) - - self.assertEqual( - cm.output, - [ - f"ERROR:app:[cron][cvat][retrieve_annotations] 1234 is not in available list of networks." - ], - ) diff --git a/packages/examples/cvat/exchange-oracle/tests/integration/services/test_cvat.py b/packages/examples/cvat/exchange-oracle/tests/integration/services/test_cvat.py index 914dd115d7..f6c1855087 100644 --- a/packages/examples/cvat/exchange-oracle/tests/integration/services/test_cvat.py +++ b/packages/examples/cvat/exchange-oracle/tests/integration/services/test_cvat.py @@ -1,53 +1,27 @@ import unittest import uuid +from datetime import datetime, timedelta from sqlalchemy.exc import IntegrityError +from sqlalchemy.orm.exc import UnmappedInstanceError import src.services.cvat as cvat_service -from src.core.types import JobStatuses, Networks, ProjectStatuses, TaskStatus, TaskType +from src.core.types import ( + AssignmentStatus, + JobStatuses, + Networks, + ProjectStatuses, + TaskStatus, + TaskType, +) from src.db import SessionLocal -from src.models.cvat import Job, Project, Task - - -def create_project(session: SessionLocal) -> tuple: - cvat_project = Project( - id=str(uuid.uuid4()), - cvat_id=1, - cvat_cloudstorage_id=1, - status=ProjectStatuses.annotation.value, - job_type=TaskType.image_label_binary.value, - escrow_address="0x86e83d346041E8806e352681f3F14549C0d2BC67", - chain_id=Networks.localhost.value, - bucket_url="https://test.storage.googleapis.com/", - ) - session.add(cvat_project) - session.commit() - - return cvat_project - - -def create_project_and_task(session: SessionLocal) -> tuple: - cvat_project = Project( - id=str(uuid.uuid4()), - cvat_id=1, - cvat_cloudstorage_id=1, - status=ProjectStatuses.annotation.value, - job_type=TaskType.image_label_binary.value, - escrow_address="0x86e83d346041E8806e352681f3F14549C0d2BC67", - chain_id=Networks.localhost.value, - bucket_url="https://test.storage.googleapis.com/", - ) - cvat_task = Task( - id=str(uuid.uuid4()), - cvat_id=1, - cvat_project_id=1, - status=TaskStatus.annotation.value, - ) - session.add(cvat_project) - session.add(cvat_task) - session.commit() - - return cvat_project, cvat_task +from src.models.cvat import Assignment, DataUpload, Image, Job, Project, Task, User + +from tests.utils.db_helper import ( + create_project, + create_project_and_task, + create_project_task_and_job, +) class ServiceIntegrationTest(unittest.TestCase): @@ -92,7 +66,7 @@ def test_create_duplicated_project(self): chain_id = Networks.localhost.value cvat_cloudstorage_id = 1 bucket_url = "https://test.storage.googleapis.com/" - project_id = cvat_service.create_project( + cvat_service.create_project( self.session, cvat_id, cvat_cloudstorage_id, @@ -255,7 +229,25 @@ def test_get_project_by_id(self): self.assertIsNone(project) - def test_get_project_escrow_address(self): + project = cvat_service.get_project_by_id( + self.session, p_id, status_in=[ProjectStatuses.annotation] + ) + + self.assertIsNotNone(project) + self.assertEqual(project.id, p_id) + self.assertEqual(project.cvat_id, cvat_id) + self.assertEqual(project.status, ProjectStatuses.annotation.value) + self.assertEqual(project.job_type, job_type) + self.assertEqual(project.escrow_address, escrow_address) + self.assertEqual(project.bucket_url, bucket_url) + + project = cvat_service.get_project_by_id( + self.session, p_id, status_in=[ProjectStatuses.canceled] + ) + + self.assertIsNone(project) + + def test_get_project_by_escrow_address(self): cvat_id = 1 cvat_cloudstorage_id = 1 job_type = TaskType.image_label_binary.value @@ -334,25 +326,100 @@ def test_get_projects_by_status(self): bucket_url, ) - projects = cvat_service.get_projects_by_status( - self.session, ProjectStatuses.annotation.value + projects = cvat_service.get_projects_by_status(self.session, ProjectStatuses.annotation) + + self.assertEqual(len(projects), 3) + + cvat_service.update_project_status(self.session, p_id, ProjectStatuses.completed) + + projects = cvat_service.get_projects_by_status(self.session, ProjectStatuses.annotation) + + self.assertEqual(len(projects), 2) + + projects = cvat_service.get_projects_by_status(self.session, ProjectStatuses.completed) + + self.assertEqual(len(projects), 1) + + def test_get_available_projects(self): + cvat_id_1 = 456 + (cvat_project, cvat_task, cvat_job) = create_project_task_and_job( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", cvat_id_1 + ) + + projects = cvat_service.get_available_projects(self.session) + + self.assertEqual(len(projects), 1) + + cvat_id_2 = 457 + (cvat_project, cvat_task) = create_project_and_task( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC68", cvat_id_2 + ) + + cvat_task_id = cvat_task.cvat_id + cvat_project_id = cvat_project.cvat_id + + cvat_service.create_job( + session=self.session, + cvat_id=cvat_id_2, + cvat_task_id=cvat_task_id, + cvat_project_id=cvat_project_id, + status=JobStatuses.in_progress, + ) + + cvat_id_3 = 458 + (cvat_project, cvat_task, _) = create_project_task_and_job( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC69", cvat_id_3 + ) + + projects = cvat_service.get_available_projects(self.session) + self.assertEqual(len(projects), 2) + self.assertTrue(any(project.cvat_id == cvat_id_1 for project in projects)) + self.assertTrue(any(project.cvat_id == cvat_id_3 for project in projects)) + + def test_get_projects_by_assignee(self): + wallet_address_1 = "0x86e83d346041E8806e352681f3F14549C0d2BC60" + cvat_id_1 = 456 + + create_project_task_and_job( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", cvat_id_1 ) - assert len(projects) == 3 + user = User(wallet_address=wallet_address_1, cvat_id=cvat_id_1, cvat_email="test@hmt.ai") + self.session.add(user) - cvat_service.update_project_status(self.session, p_id, ProjectStatuses.completed.value) + cvat_service.create_assignment( + session=self.session, + wallet_address=wallet_address_1, + cvat_job_id=cvat_id_1, + expires_at=datetime.now(), + ) - projects = cvat_service.get_projects_by_status( - self.session, ProjectStatuses.annotation.value + wallet_address_2 = "0x86e83d346041E8806e352681f3F14549C0d2BC61" + cvat_id_2 = 457 + + create_project_task_and_job( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC68", cvat_id_2 ) - assert len(projects) == 2 + user = User(wallet_address=wallet_address_2, cvat_id=cvat_id_2, cvat_email="test2@hmt.ai") + self.session.add(user) - projects = cvat_service.get_projects_by_status( - self.session, ProjectStatuses.completed.value + cvat_service.create_assignment( + session=self.session, + wallet_address=wallet_address_2, + cvat_job_id=cvat_id_2, + expires_at=datetime.now(), ) - assert len(projects) == 1 + projects = cvat_service.get_projects_by_assignee(self.session, wallet_address_1) + + self.assertEqual(len(projects), 1) + self.assertEqual(projects[0].cvat_id, cvat_id_1) + + projects = cvat_service.get_projects_by_assignee(self.session, wallet_address_2) + + self.assertEqual(len(projects), 1) + self.assertEqual(projects[0].cvat_id, cvat_id_2) def test_update_project_status(self): cvat_id = 1 @@ -371,7 +438,7 @@ def test_update_project_status(self): bucket_url, ) - cvat_service.update_project_status(self.session, p_id, ProjectStatuses.completed.value) + cvat_service.update_project_status(self.session, p_id, ProjectStatuses.completed) project = cvat_service.get_project_by_id(self.session, p_id) @@ -384,30 +451,79 @@ def test_update_project_status(self): self.assertEqual(project.chain_id, chain_id) self.assertEqual(project.bucket_url, bucket_url) - def test_update_project_invalid_status(self): - cvat_id = 1 - cvat_cloudstorage_id = 1 - job_type = TaskType.image_label_binary.value - escrow_address = "0x86e83d346041E8806e352681f3F14549C0d2BC67" - chain_id = Networks.localhost.value - bucket_url = "https://test.storage.googleapis.com/" - p_id = cvat_service.create_project( - self.session, - cvat_id, - cvat_cloudstorage_id, - job_type, - escrow_address, - chain_id, - bucket_url, + def test_delete_project(self): + cvat_id_1 = 456 + + project = create_project( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", cvat_id_1 ) - with self.assertRaises(ValueError): - cvat_service.update_project_status(self.session, p_id, "Invalid Status") - def test_create_task(self): - cvat_project = create_project(self.session) + projects = self.session.query(Project).all() + self.assertEqual(len(projects), 1) + + cvat_service.delete_project(self.session, project.id) + + projects = self.session.query(Project).all() + self.assertEqual(len(projects), 0) + + (cvat_project, cvat_task) = create_project_and_task( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", cvat_id_1 + ) + cvat_service.create_job( + session=self.session, + cvat_id=1, + cvat_task_id=cvat_task.cvat_id, + cvat_project_id=cvat_project.cvat_id, + status=JobStatuses.new, + ) + cvat_service.create_job( + session=self.session, + cvat_id=2, + cvat_task_id=cvat_task.cvat_id, + cvat_project_id=cvat_project.cvat_id, + status=JobStatuses.new, + ) + self.session.commit() + cvat_project_db = cvat_service.get_project_by_id(self.session, cvat_project.id) + cvat_task_db = cvat_service.get_task_by_id(self.session, cvat_task.id) + jobs = cvat_service.get_jobs_by_cvat_task_id(self.session, cvat_task_id=cvat_task.cvat_id) + + self.assertIsNotNone(cvat_project) + self.assertEqual(cvat_project_db.id, cvat_project.id) + + self.assertIsNotNone(cvat_task_db) + self.assertEqual(cvat_task_db.id, cvat_task.id) + + self.assertEqual(len(jobs), 2) + + cvat_service.delete_project(self.session, cvat_project_db.id) + + cvat_project_db = cvat_service.get_project_by_id(self.session, cvat_project.id) + cvat_task_db = cvat_service.get_task_by_id(self.session, cvat_task.id) + jobs = cvat_service.get_jobs_by_cvat_task_id(self.session, cvat_task_id=cvat_task.cvat_id) + + self.assertIsNone(cvat_project_db) + self.assertIsNone(cvat_task_db) + self.assertEqual(len(jobs), 0) + + def test_delete_project_wrong_project_id(self): + cvat_id = 456 + + create_project(self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", cvat_id) + + projects = self.session.query(Project).all() + self.assertEqual(len(projects), 1) + with self.assertRaises(UnmappedInstanceError): + cvat_service.delete_project(self.session, "project_id") + + def test_create_task(self): cvat_id = 1 - status = TaskStatus.annotation.value + cvat_project = create_project( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", cvat_id + ) + + status = TaskStatus.annotation task_id = cvat_service.create_task(self.session, cvat_id, cvat_project.cvat_id, status) @@ -420,10 +536,12 @@ def test_create_task(self): self.assertEqual(task.status, status) def test_create_task_duplicated_cvat_id(self): - cvat_project = create_project(self.session) - cvat_id = 1 - status = TaskStatus.annotation.value + cvat_project = create_project( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", cvat_id + ) + + status = TaskStatus.annotation cvat_service.create_task(self.session, cvat_id, cvat_project.cvat_id, status) self.session.commit() @@ -433,30 +551,25 @@ def test_create_task_duplicated_cvat_id(self): self.session.commit() def test_create_tas_without_project(self): - cvat_service.create_task(self.session, 123, 123, TaskStatus.annotation.value) + cvat_service.create_task(self.session, 123, 123, TaskStatus.annotation) with self.assertRaises(IntegrityError): self.session.commit() def test_create_task_none_cvat_id(self): - cvat_service.create_task(self.session, None, 123, TaskStatus.annotation.value) + cvat_service.create_task(self.session, None, 123, TaskStatus.annotation) with self.assertRaises(IntegrityError): self.session.commit() def test_create_task_none_cvat_project_id(self): - cvat_service.create_task(self.session, 123, None, TaskStatus.annotation.value) - with self.assertRaises(IntegrityError): - self.session.commit() - - def test_create_task_none_status(self): - cvat_service.create_task(self.session, 123, 123, None) + cvat_service.create_task(self.session, 123, None, TaskStatus.annotation) with self.assertRaises(IntegrityError): self.session.commit() def test_get_task_by_id(self): - cvat_project = create_project(self.session) + cvat_project = create_project(self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", 1) task_id = cvat_service.create_task( - self.session, 1, cvat_project.cvat_id, TaskStatus.annotation.value + self.session, 1, cvat_project.cvat_id, TaskStatus.annotation ) task = cvat_service.get_task_by_id(self.session, task_id) @@ -470,33 +583,48 @@ def test_get_task_by_id(self): self.assertIsNone(task) - def test_get_tasks_by_status(self): - cvat_project = create_project(self.session) + def test_get_tasks_by_cvat_id(self): + cvat_project = create_project(self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", 1) + + cvat_service.create_task(self.session, 1, cvat_project.cvat_id, TaskStatus.annotation) + cvat_service.create_task(self.session, 2, cvat_project.cvat_id, TaskStatus.annotation) + cvat_service.create_task(self.session, 3, cvat_project.cvat_id, TaskStatus.annotation) + + tasks = cvat_service.get_tasks_by_cvat_id(self.session, [1, 2]) + + self.assertEqual(len(tasks), 2) + + tasks = cvat_service.get_tasks_by_cvat_id(self.session, [3]) + + self.assertEqual(len(tasks), 1) + + tasks = cvat_service.get_tasks_by_cvat_id(self.session, [999]) - cvat_service.create_task(self.session, 1, cvat_project.cvat_id, TaskStatus.annotation.value) - cvat_service.create_task(self.session, 2, cvat_project.cvat_id, TaskStatus.annotation.value) - cvat_service.create_task(self.session, 3, cvat_project.cvat_id, TaskStatus.completed.value) + self.assertEqual(len(tasks), 0) - tasks = cvat_service.get_tasks_by_status(self.session, TaskStatus.annotation.value) + def test_get_tasks_by_status(self): + cvat_project = create_project(self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", 1) - assert len(tasks) == 2 + cvat_service.create_task(self.session, 1, cvat_project.cvat_id, TaskStatus.annotation) + cvat_service.create_task(self.session, 2, cvat_project.cvat_id, TaskStatus.annotation) + cvat_service.create_task(self.session, 3, cvat_project.cvat_id, TaskStatus.completed) - tasks = cvat_service.get_tasks_by_status(self.session, TaskStatus.completed.value) + tasks = cvat_service.get_tasks_by_status(self.session, TaskStatus.annotation) - assert len(tasks) == 1 + self.assertEqual(len(tasks), 2) - tasks = cvat_service.get_tasks_by_status(self.session, "Invalid status") + tasks = cvat_service.get_tasks_by_status(self.session, TaskStatus.completed) - assert len(tasks) == 0 + self.assertEqual(len(tasks), 1) def test_update_task_status(self): - cvat_project = create_project(self.session) + cvat_project = create_project(self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", 1) task_id = cvat_service.create_task( - self.session, 1, cvat_project.cvat_id, TaskStatus.annotation.value + self.session, 1, cvat_project.cvat_id, TaskStatus.annotation ) - cvat_service.update_task_status(self.session, task_id, TaskStatus.completed.value) + cvat_service.update_task_status(self.session, task_id, TaskStatus.completed) task = cvat_service.get_task_by_id(self.session, task_id) @@ -506,68 +634,131 @@ def test_update_task_status(self): self.assertEqual(task.cvat_project_id, cvat_project.cvat_id) self.assertEqual(task.status, TaskStatus.completed.value) - def test_update_task_invalid_status(self): - cvat_project = create_project(self.session) + def test_get_tasks_by_cvat_project_id(self): + cvat_project = create_project(self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", 1) + + cvat_service.create_task(self.session, 1, cvat_project.cvat_id, TaskStatus.annotation) + cvat_service.create_task(self.session, 2, cvat_project.cvat_id, TaskStatus.annotation) + cvat_service.create_task(self.session, 3, cvat_project.cvat_id, TaskStatus.completed) - task_id = cvat_service.create_task( - self.session, 1, cvat_project.cvat_id, TaskStatus.annotation.value + cvat_project_2 = create_project( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC68", 2 ) - with self.assertRaises(ValueError): - cvat_service.update_task_status(self.session, task_id, "Invalid status") + cvat_service.create_task(self.session, 4, cvat_project_2.cvat_id, TaskStatus.annotation) - def test_get_tasks_by_cvat_project_id(self): - cvat_project = create_project(self.session) + tasks = cvat_service.get_tasks_by_cvat_project_id(self.session, cvat_project.cvat_id) - cvat_service.create_task(self.session, 1, cvat_project.cvat_id, TaskStatus.annotation.value) - cvat_service.create_task(self.session, 2, cvat_project.cvat_id, TaskStatus.annotation.value) - cvat_service.create_task(self.session, 3, cvat_project.cvat_id, TaskStatus.completed.value) + self.assertEqual(len(tasks), 3) - cvat_id = 2 - cvat_cloudstorage_id = 2 - job_type = TaskType.image_label_binary.value - escrow_address = "0x86e83d346041E8806e352681f3F14549C0d2BC68" - bucket_url = "https://test2.storage.googleapis.com/" - chain_id = Networks.localhost.value - project_id = cvat_service.create_project( - self.session, - cvat_id, - cvat_cloudstorage_id, - job_type, - escrow_address, - chain_id, - bucket_url, + tasks = cvat_service.get_tasks_by_cvat_project_id(self.session, cvat_project_2.cvat_id) + + self.assertEqual(len(tasks), 1) + + tasks = cvat_service.get_tasks_by_cvat_project_id(self.session, 123) + + self.assertEqual(len(tasks), 0) + + def test_create_data_upload(self): + cvat_id = 1 + (_, cvat_task) = create_project_and_task( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", cvat_id ) - cvat_service.create_task(self.session, 4, 2, TaskStatus.annotation.value) + data_upload_id = cvat_service.create_data_upload(self.session, cvat_task.cvat_id) - tasks = cvat_service.get_tasks_by_cvat_project_id(self.session, cvat_project.cvat_id) + data_upload = self.session.query(DataUpload).filter_by(task_id=cvat_task.cvat_id).first() + + self.assertIsNotNone(data_upload) + self.assertEqual(data_upload.id, data_upload_id) + self.assertEqual(data_upload.task_id, cvat_id) + + def test_get_active_task_uploads_by_task_id(self): + cvat_id_1 = 1 + (_, cvat_task) = create_project_and_task( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", cvat_id_1 + ) + cvat_id_2 = 2 + (_, cvat_task_2) = create_project_and_task( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC68", cvat_id_2 + ) - assert len(tasks) == 3 + cvat_service.create_data_upload(self.session, cvat_task.cvat_id) + cvat_service.create_data_upload(self.session, cvat_task_2.cvat_id) - tasks = cvat_service.get_tasks_by_cvat_project_id(self.session, 2) + data_uploads = cvat_service.get_active_task_uploads_by_task_id( + self.session, [cvat_id_1, cvat_id_2] + ) + self.assertEqual(len(data_uploads), 2) - assert len(tasks) == 1 + data_uploads = cvat_service.get_active_task_uploads_by_task_id(self.session, [cvat_id_1]) + self.assertEqual(len(data_uploads), 1) - tasks = cvat_service.get_tasks_by_cvat_project_id(self.session, 123) + data_uploads = cvat_service.get_active_task_uploads_by_task_id(self.session, [cvat_id_2]) + self.assertEqual(len(data_uploads), 1) - assert len(tasks) == 0 + data_uploads = cvat_service.get_active_task_uploads_by_task_id(self.session, []) + self.assertEqual(len(data_uploads), 0) - def test_create_job(self): - (cvat_project, cvat_task) = create_project_and_task(self.session) + def test_get_active_task_uploads(self): + cvat_id_1 = 1 + (_, cvat_task) = create_project_and_task( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", cvat_id_1 + ) + cvat_id_2 = 2 + (_, cvat_task_2) = create_project_and_task( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC68", cvat_id_2 + ) - cvat_id = 456 + cvat_service.create_data_upload(self.session, cvat_task.cvat_id) + cvat_service.create_data_upload(self.session, cvat_task_2.cvat_id) + + data_uploads = cvat_service.get_active_task_uploads(self.session) + self.assertEqual(len(data_uploads), 2) + + data_uploads = cvat_service.get_active_task_uploads(self.session, limit=1) + self.assertEqual(len(data_uploads), 1) + + def test_get_active_task_uploads(self): + cvat_id_1 = 1 + (_, cvat_task) = create_project_and_task( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", cvat_id_1 + ) + cvat_id_2 = 2 + (_, cvat_task_2) = create_project_and_task( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC68", cvat_id_2 + ) + + cvat_service.create_data_upload(self.session, cvat_task.cvat_id) + cvat_service.create_data_upload(self.session, cvat_task_2.cvat_id) + + data_uploads = self.session.query(DataUpload).all() + self.assertEqual(len(data_uploads), 2) + + cvat_service.finish_uploads(self.session, [data_uploads[0]]) + + data_uploads = self.session.query(DataUpload).all() + self.assertEqual(len(data_uploads), 1) + + cvat_service.finish_uploads(self.session, [data_uploads[0]]) + + data_uploads = self.session.query(DataUpload).all() + self.assertEqual(len(data_uploads), 0) + + def test_create_job(self): + (cvat_project, cvat_task) = create_project_and_task( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", 1 + ) cvat_task_id = cvat_task.cvat_id + cvat_job_id = 456 cvat_project_id = cvat_project.cvat_id - assignee = "John Doe" - status = JobStatuses.new.value + status = JobStatuses.new job_id = cvat_service.create_job( session=self.session, - cvat_id=cvat_id, + cvat_id=cvat_job_id, cvat_task_id=cvat_task_id, cvat_project_id=cvat_project_id, - assignee=assignee, status=status, ) @@ -576,92 +767,80 @@ def test_create_job(self): job = self.session.query(Job).filter_by(id=job_id).first() - self.assertEqual(job.cvat_id, cvat_id) + self.assertEqual(job.cvat_id, cvat_job_id) self.assertEqual(job.cvat_task_id, cvat_task_id) self.assertEqual(job.cvat_project_id, cvat_project_id) - self.assertEqual(job.assignee, assignee) self.assertEqual(job.status, status) def test_create_job_invalid_cvat_id(self): - (cvat_project, cvat_task) = create_project_and_task(self.session) + (cvat_project, cvat_task) = create_project_and_task( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", 1 + ) cvat_service.create_job( session=self.session, cvat_id=None, cvat_task_id=cvat_task.cvat_id, cvat_project_id=cvat_project.cvat_id, - assignee="John Doe", - status=JobStatuses.new.value, + status=JobStatuses.new, ) with self.assertRaises(IntegrityError): self.session.commit() def test_create_job_without_task(self): - cvat_project = create_project(self.session) + cvat_project = create_project(self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", 1) cvat_service.create_job( session=self.session, cvat_id=123, cvat_project_id=cvat_project.cvat_id, cvat_task_id=None, - assignee="John Doe", - status=JobStatuses.new.value, + status=JobStatuses.new, ) with self.assertRaises(IntegrityError): self.session.commit() def test_create_job_invalid_task_reference(self): - (cvat_project, cvat_task) = create_project_and_task(self.session) + (cvat_project, _) = create_project_and_task( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", 1 + ) cvat_service.create_job( session=self.session, cvat_id=456, cvat_task_id=122, cvat_project_id=cvat_project.cvat_id, - assignee="John Doe", - status=JobStatuses.new.value, + status=JobStatuses.new, ) with self.assertRaises(IntegrityError): self.session.commit() def test_create_job_invalid_project_reference(self): - (cvat_project, cvat_task) = create_project_and_task(self.session) + (_, cvat_task) = create_project_and_task( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", 1 + ) cvat_service.create_job( session=self.session, cvat_id=456, cvat_task_id=cvat_task.cvat_id, cvat_project_id=122, - assignee="John Doe", - status=JobStatuses.new.value, - ) - with self.assertRaises(IntegrityError): - self.session.commit() - - def test_create_job_invalid_status(self): - (cvat_project, cvat_task) = create_project_and_task(self.session) - - cvat_service.create_job( - session=self.session, - cvat_id=456, - cvat_task_id=123, - cvat_project_id=123, - assignee="John Doe", - status=None, + status=JobStatuses.new, ) with self.assertRaises(IntegrityError): self.session.commit() def test_create_job_duplicated_cvat_id(self): - (cvat_project, cvat_task) = create_project_and_task(self.session) + (cvat_project, cvat_task) = create_project_and_task( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", 1 + ) cvat_service.create_job( session=self.session, cvat_id=456, cvat_task_id=cvat_task.cvat_id, cvat_project_id=cvat_project.cvat_id, - assignee="John Doe", - status=JobStatuses.new.value, + status=JobStatuses.new, ) self.session.commit() cvat_service.create_job( @@ -669,19 +848,19 @@ def test_create_job_duplicated_cvat_id(self): cvat_id=456, cvat_task_id=cvat_task.cvat_id, cvat_project_id=cvat_project.cvat_id, - assignee="John Doe", - status=JobStatuses.new.value, + status=JobStatuses.new, ) with self.assertRaises(IntegrityError): self.session.commit() def test_get_job_by_id(self): - (cvat_project, cvat_task) = create_project_and_task(self.session) + (cvat_project, cvat_task) = create_project_and_task( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", 1 + ) job_id = str(uuid.uuid4()) cvat_id = 456 - assignee = "John Doe" - status = JobStatuses.new.value + status = JobStatuses.new # Create a job with valid foreign key reference job_id = cvat_service.create_job( @@ -689,7 +868,6 @@ def test_get_job_by_id(self): cvat_id, cvat_task.cvat_id, cvat_project.cvat_id, - assignee, status, ) job = cvat_service.get_job_by_id(self.session, job_id) @@ -704,12 +882,13 @@ def test_get_job_by_id(self): self.assertIsNone(job) - def test_get_job_by_cvat_id(self): - (cvat_project, cvat_task) = create_project_and_task(self.session) + def test_get_jobs_by_cvat_id(self): + (cvat_project, cvat_task) = create_project_and_task( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", 1 + ) cvat_id = 456 - assignee = "John Doe" - status = JobStatuses.new.value + status = JobStatuses.new # Create a job with valid foreign key reference job_id = cvat_service.create_job( @@ -717,28 +896,29 @@ def test_get_job_by_cvat_id(self): cvat_id, cvat_task.cvat_id, cvat_project.cvat_id, - assignee, status, ) - job = cvat_service.get_job_by_cvat_id(self.session, cvat_id) + jobs = cvat_service.get_jobs_by_cvat_id(self.session, [cvat_id]) - self.assertIsNotNone(job) - self.assertEqual(job.id, job_id) - self.assertEqual(job.cvat_id, cvat_id) - self.assertEqual(job.cvat_task_id, cvat_task.cvat_id) - self.assertEqual(job.cvat_project_id, cvat_project.cvat_id) + self.assertIsNotNone(jobs) + self.assertEqual(jobs[0].id, job_id) + self.assertEqual(jobs[0].cvat_id, cvat_id) + self.assertEqual(jobs[0].cvat_task_id, cvat_task.cvat_id) + self.assertEqual(jobs[0].cvat_project_id, cvat_project.cvat_id) - def test_get_job_by_cvat_id_wrong_cvat_id(self): - job = cvat_service.get_job_by_cvat_id(self.session, 457) - self.assertIsNone(job) + def test_get_jobs_by_cvat_id_wrong_cvat_id(self): + job = cvat_service.get_jobs_by_cvat_id(self.session, [457]) + self.assertEqual(job, []) - def test_update_job_assignee(self): - (cvat_project, cvat_task) = create_project_and_task(self.session) + def test_update_job_status(self): + (cvat_project, cvat_task) = create_project_and_task( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", 1 + ) + job_id = str(uuid.uuid4()) cvat_id = 456 - assignee = "" - status = JobStatuses.new.value + status = JobStatuses.new # Create a job with valid foreign key reference job_id = cvat_service.create_job( @@ -746,7 +926,6 @@ def test_update_job_assignee(self): cvat_id, cvat_task.cvat_id, cvat_project.cvat_id, - assignee, status, ) job = cvat_service.get_job_by_id(self.session, job_id) @@ -754,96 +933,71 @@ def test_update_job_assignee(self): self.assertEqual(job.cvat_id, cvat_id) self.assertEqual(job.cvat_task_id, cvat_task.cvat_id) self.assertEqual(job.cvat_project_id, cvat_project.cvat_id) - self.assertEqual(job.assignee, assignee) self.assertEqual(job.status, status) - new_assignee = "Harry Doe" + new_status = JobStatuses.completed - cvat_service.update_job_assignee(self.session, job.id, new_assignee) + cvat_service.update_job_status(self.session, job.id, new_status) job = self.session.query(Job).filter_by(id=job_id).first() self.assertIsNotNone(job) self.assertEqual(job.id, job_id) self.assertEqual(job.cvat_id, cvat_id) - self.assertEqual(job.assignee, new_assignee) self.assertEqual(job.cvat_task_id, cvat_task.cvat_id) self.assertEqual(job.cvat_project_id, cvat_project.cvat_id) - self.assertEqual(job.status, status) + self.assertEqual(job.status, new_status) - def test_update_job_status(self): - (cvat_project, cvat_task) = create_project_and_task(self.session) + def test_get_jobs_by_cvat_task_id(self): + (cvat_project, cvat_task) = create_project_and_task( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", 1 + ) - job_id = str(uuid.uuid4()) cvat_id = 456 - assignee = "John Doe" - status = JobStatuses.new.value + status = JobStatuses.new # Create a job with valid foreign key reference - job_id = cvat_service.create_job( + cvat_service.create_job( self.session, cvat_id, cvat_task.cvat_id, cvat_project.cvat_id, - assignee, status, ) - job = cvat_service.get_job_by_id(self.session, job_id) - self.assertEqual(job.cvat_id, cvat_id) - self.assertEqual(job.cvat_task_id, cvat_task.cvat_id) - self.assertEqual(job.cvat_project_id, cvat_project.cvat_id) - self.assertEqual(job.assignee, assignee) - self.assertEqual(job.status, status) - - new_status = JobStatuses.completed.value - - cvat_service.update_job_status(self.session, job.id, new_status) - - job = self.session.query(Job).filter_by(id=job_id).first() - - self.assertIsNotNone(job) - self.assertEqual(job.id, job_id) - self.assertEqual(job.cvat_id, cvat_id) - self.assertEqual(job.assignee, assignee) - self.assertEqual(job.cvat_task_id, cvat_task.cvat_id) - self.assertEqual(job.cvat_project_id, cvat_project.cvat_id) - self.assertEqual(job.status, new_status) + cvat_id = 457 + status = JobStatuses.new - def test_update_job_invalid_status(self): - (cvat_project, cvat_task) = create_project_and_task(self.session) + cvat_service.create_job( + self.session, + cvat_id, + cvat_task.cvat_id, + cvat_project.cvat_id, + status, + ) - job_id = str(uuid.uuid4()) - cvat_id = 456 - assignee = "John Doe" - status = JobStatuses.new.value + cvat_id = 458 + status = JobStatuses.new - # Create a job with valid foreign key reference - job_id = cvat_service.create_job( + cvat_service.create_job( self.session, cvat_id, cvat_task.cvat_id, cvat_project.cvat_id, - assignee, status, ) - job = cvat_service.get_job_by_id(self.session, job_id) - self.assertEqual(job.cvat_id, cvat_id) - self.assertEqual(job.cvat_task_id, cvat_task.cvat_id) - self.assertEqual(job.cvat_project_id, cvat_project.cvat_id) - self.assertEqual(job.assignee, assignee) - self.assertEqual(job.status, status) + jobs = cvat_service.get_jobs_by_cvat_task_id(self.session, cvat_task_id=cvat_task.cvat_id) - with self.assertRaises(ValueError): - cvat_service.update_job_status(self.session, job.id, "Invalid status") + self.assertEqual(len(jobs), 3) - def test_get_jobs_by_cvat_task_id(self): - (cvat_project, cvat_task) = create_project_and_task(self.session) + def test_get_jobs_by_cvat_project_id(self): + (cvat_project, cvat_task) = create_project_and_task( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", 1 + ) cvat_id = 456 - assignee = "John Doe" - status = JobStatuses.new.value + status = JobStatuses.new # Create a job with valid foreign key reference cvat_service.create_job( @@ -851,78 +1005,574 @@ def test_get_jobs_by_cvat_task_id(self): cvat_id, cvat_task.cvat_id, cvat_project.cvat_id, - assignee, status, ) cvat_id = 457 - assignee = "Bob" - status = JobStatuses.new.value + status = JobStatuses.new cvat_service.create_job( self.session, cvat_id, cvat_task.cvat_id, cvat_project.cvat_id, - assignee, status, ) cvat_id = 458 - assignee = "Alice" - status = JobStatuses.new.value + status = JobStatuses.new cvat_service.create_job( self.session, cvat_id, cvat_task.cvat_id, cvat_project.cvat_id, - assignee, status, ) - jobs = cvat_service.get_jobs_by_cvat_task_id(self.session, cvat_task_id=cvat_task.cvat_id) + jobs = cvat_service.get_jobs_by_cvat_project_id(self.session, cvat_project.cvat_id) - assert len(jobs) == 3 + self.assertEqual(len(jobs), 3) - def test_delete_project(self): - (cvat_project, cvat_task) = create_project_and_task(self.session) - cvat_service.create_job( + def test_put_user(self): + wallet_address = "0x86e83d346041E8806e352681f3F14549C0d2BC67" + cvat_email = "test@hmt.ai" + cvat_id = 1 + user = cvat_service.put_user( + self.session, + wallet_address, + cvat_email, + cvat_id, + ) + + db_user = self.session.query(User).filter_by(cvat_id=cvat_id).first() + + self.assertIsNotNone(user) + self.assertEqual(user, db_user) + self.assertEqual(user.id, db_user.id) + self.assertEqual(user.cvat_id, cvat_id) + self.assertEqual(user.cvat_email, cvat_email) + self.assertEqual(user.wallet_address, wallet_address) + + def test_put_user(self): + wallet_address = "0x86e83d346041E8806e352681f3F14549C0d2BC67" + cvat_email = "test@hmt.ai" + cvat_id = 1 + user = cvat_service.put_user( + self.session, + wallet_address, + cvat_email, + cvat_id, + ) + + db_user = self.session.query(User).filter_by(cvat_id=cvat_id).first() + + self.assertIsNotNone(user) + self.assertEqual(user.cvat_id, cvat_id) + self.assertEqual(user.cvat_email, cvat_email) + self.assertEqual(user.wallet_address, wallet_address) + self.assertEqual(db_user.cvat_id, cvat_id) + self.assertEqual(db_user.cvat_email, cvat_email) + self.assertEqual(db_user.wallet_address, wallet_address) + + def test_put_user_duplicated_address(self): + wallet_address = "0x86e83d346041E8806e352681f3F14549C0d2BC67" + cvat_service.put_user( + self.session, + wallet_address, + "test@hmt.ai", + 1, + ) + self.session.commit() + + db_users = self.session.query(User).filter_by(wallet_address=wallet_address).all() + self.assertEqual(len(db_users), 1) + self.assertEqual(db_users[0].cvat_id, 1) + self.assertEqual(db_users[0].cvat_email, "test@hmt.ai") + + cvat_service.put_user( + self.session, + wallet_address, + "test2@hmt.ai", + 2, + ) + self.session.commit() + + db_users = self.session.query(User).filter_by(wallet_address=wallet_address).all() + self.assertEqual(len(db_users), 1) + self.assertEqual(db_users[0].cvat_id, 2) + self.assertEqual(db_users[0].cvat_email, "test2@hmt.ai") + + def test_put_user_duplicated_email(self): + email = "test@hmt.ai" + cvat_service.put_user( + self.session, + "0x86e83d346041E8806e352681f3F14549C0d2BC67", + email, + 1, + ) + + cvat_service.put_user( + self.session, + "0x86e83d346041E8806e352681f3F14549C0d2BC68", + email, + 2, + ) + with self.assertRaises(IntegrityError): + self.session.commit() + + def test_put_user_duplicated_id(self): + cvat_id = 1 + cvat_service.put_user( + self.session, + "0x86e83d346041E8806e352681f3F14549C0d2BC67", + "test@hmt.ai", + cvat_id, + ) + + cvat_service.put_user( + self.session, + "0x86e83d346041E8806e352681f3F14549C0d2BC68", + "test2@hmt.ai", + cvat_id, + ) + with self.assertRaises(IntegrityError): + self.session.commit() + + def test_get_user_by_id(self): + user = User( + wallet_address="0x86e83d346041E8806e352681f3F14549C0d2BC67", + cvat_email="test@hmt.ai", + cvat_id=1, + ) + self.session.add(user) + + user = User( + wallet_address="0x86e83d346041E8806e352681f3F14549C0d2BC68", + cvat_email="test2@hmt.ai", + cvat_id=2, + ) + self.session.add(user) + self.session.commit() + + user_1 = cvat_service.get_user_by_id( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67" + ) + self.assertEqual(user_1.cvat_id, 1) + self.assertEqual(user_1.cvat_email, "test@hmt.ai") + + user_2 = cvat_service.get_user_by_id( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC68" + ) + self.assertEqual(user_2.cvat_id, 2) + self.assertEqual(user_2.cvat_email, "test2@hmt.ai") + + user_3 = cvat_service.get_user_by_id( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC69" + ) + self.assertIsNone(user_3) + + def test_create_assignment(self): + (_, _, cvat_job) = create_project_task_and_job( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", 1 + ) + + wallet_address = "0x86e83d346041E8806e352681f3F14549C0d2BC68" + user = User( + wallet_address=wallet_address, + cvat_email="test@hmt.ai", + cvat_id=1, + ) + self.session.add(user) + + assignment_id = cvat_service.create_assignment( + session=self.session, + wallet_address=wallet_address, + cvat_job_id=cvat_job.cvat_id, + expires_at=datetime.now(), + ) + + assignment_count = self.session.query(Assignment).count() + self.assertEqual(assignment_count, 1) + + assignment = self.session.query(Assignment).filter_by(id=assignment_id).first() + + self.assertIsNotNone(assignment) + self.assertEqual(assignment.user_wallet_address, wallet_address) + self.assertEqual(assignment.cvat_job_id, cvat_job.cvat_id) + self.assertEqual(assignment.status, AssignmentStatus.created.value) + + def test_create_assignment_invalid_address(self): + (_, _, cvat_job) = create_project_task_and_job( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", 1 + ) + + cvat_service.create_assignment( session=self.session, + wallet_address="invalid_address", + cvat_job_id=cvat_job.cvat_id, + expires_at=datetime.now(), + ) + with self.assertRaises(IntegrityError): + self.session.commit() + + def test_create_assignment_invalid_address(self): + wallet_address = "0x86e83d346041E8806e352681f3F14549C0d2BC68" + user = User( + wallet_address=wallet_address, + cvat_email="test@hmt.ai", cvat_id=1, - cvat_task_id=cvat_task.cvat_id, - cvat_project_id=cvat_project.cvat_id, - assignee="John Doe", - status=JobStatuses.new.value, ) - cvat_service.create_job( + self.session.add(user) + + cvat_service.create_assignment( session=self.session, + wallet_address=wallet_address, + cvat_job_id=0, + expires_at=datetime.now(), + ) + with self.assertRaises(IntegrityError): + self.session.commit() + + def test_get_assignments_by_id(self): + (_, _, cvat_job) = create_project_task_and_job( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", 1 + ) + wallet_address_1 = "0x86e83d346041E8806e352681f3F14549C0d2BC67" + user = User( + wallet_address=wallet_address_1, + cvat_email="test@hmt.ai", + cvat_id=1, + ) + self.session.add(user) + + wallet_address_2 = "0x86e83d346041E8806e352681f3F14549C0d2BC68" + user = User( + wallet_address=wallet_address_2, + cvat_email="test2@hmt.ai", cvat_id=2, - cvat_task_id=cvat_task.cvat_id, - cvat_project_id=cvat_project.cvat_id, - assignee="John Doe", - status=JobStatuses.new.value, + ) + self.session.add(user) + assignment = cvat_service.create_assignment( + session=self.session, + wallet_address=wallet_address_1, + cvat_job_id=cvat_job.cvat_id, + expires_at=datetime.now(), + ) + assignment_2 = cvat_service.create_assignment( + session=self.session, + wallet_address=wallet_address_2, + cvat_job_id=cvat_job.cvat_id, + expires_at=datetime.now(), ) self.session.commit() - cvat_project_db = cvat_service.get_project_by_id(self.session, cvat_project.id) - cvat_task_db = cvat_service.get_task_by_id(self.session, cvat_task.id) - jobs = cvat_service.get_jobs_by_cvat_task_id(self.session, cvat_task_id=cvat_task.cvat_id) + assignments = cvat_service.get_assignments_by_id(self.session, [assignment, assignment_2]) + self.assertEqual(len(assignments), 2) - self.assertIsNotNone(cvat_project) - self.assertEqual(cvat_project_db.id, cvat_project.id) + assignments = cvat_service.get_assignments_by_id(self.session, [assignment]) + self.assertEqual(len(assignments), 1) + self.assertEqual(assignments[0].id, assignment) + self.assertEqual(assignments[0].user_wallet_address, wallet_address_1) - self.assertIsNotNone(cvat_task_db) - self.assertEqual(cvat_task_db.id, cvat_task.id) + assignments = cvat_service.get_assignments_by_id(self.session, [assignment_2]) + self.assertEqual(len(assignments), 1) + self.assertEqual(assignments[0].id, assignment_2) + self.assertEqual(assignments[0].user_wallet_address, wallet_address_2) - self.assertEqual(len(jobs), 2) + def test_get_latest_assignment_by_cvat_job_id(self): + (_, _, cvat_job) = create_project_task_and_job( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", 1 + ) + wallet_address_1 = "0x86e83d346041E8806e352681f3F14549C0d2BC67" + user = User( + wallet_address=wallet_address_1, + cvat_email="test@hmt.ai", + cvat_id=1, + ) + self.session.add(user) - cvat_service.delete_project(self.session, cvat_project_db.id) + wallet_address_2 = "0x86e83d346041E8806e352681f3F14549C0d2BC68" + user = User( + wallet_address=wallet_address_2, + cvat_email="test2@hmt.ai", + cvat_id=2, + ) + self.session.add(user) + assignment = Assignment( + id=str(uuid.uuid4()), + user_wallet_address=wallet_address_1, + cvat_job_id=cvat_job.cvat_id, + expires_at=datetime.now(), + created_at=datetime.now() - timedelta(days=1), + ) + assignment_2 = Assignment( + id=str(uuid.uuid4()), + user_wallet_address=wallet_address_2, + cvat_job_id=cvat_job.cvat_id, + expires_at=datetime.now(), + ) + self.session.add(assignment) + self.session.add(assignment_2) + self.session.commit() - cvat_project_db = cvat_service.get_project_by_id(self.session, cvat_project.id) - cvat_task_db = cvat_service.get_task_by_id(self.session, cvat_task.id) - jobs = cvat_service.get_jobs_by_cvat_task_id(self.session, cvat_task_id=cvat_task.cvat_id) + received_assignment = cvat_service.get_latest_assignment_by_cvat_job_id( + self.session, cvat_job.cvat_id + ) + self.assertEqual(received_assignment.id, assignment_2.id) + self.assertNotEqual(received_assignment.id, assignment.id) + self.assertEqual(received_assignment.user_wallet_address, wallet_address_2) - self.assertIsNone(cvat_project_db) - self.assertIsNone(cvat_task_db) - self.assertEqual(len(jobs), 0) + def test_get_unprocessed_expired_assignments(self): + (_, _, cvat_job) = create_project_task_and_job( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", 1 + ) + wallet_address_1 = "0x86e83d346041E8806e352681f3F14549C0d2BC67" + user = User( + wallet_address=wallet_address_1, + cvat_email="test@hmt.ai", + cvat_id=1, + ) + self.session.add(user) + + wallet_address_2 = "0x86e83d346041E8806e352681f3F14549C0d2BC68" + user = User( + wallet_address=wallet_address_2, + cvat_email="test2@hmt.ai", + cvat_id=2, + ) + self.session.add(user) + assignment = Assignment( + id=str(uuid.uuid4()), + user_wallet_address=wallet_address_1, + cvat_job_id=cvat_job.cvat_id, + expires_at=datetime.now() + timedelta(days=1), + ) + assignment_2 = Assignment( + id=str(uuid.uuid4()), + user_wallet_address=wallet_address_2, + cvat_job_id=cvat_job.cvat_id, + expires_at=datetime.now() - timedelta(days=1), + ) + self.session.add(assignment) + self.session.add(assignment_2) + self.session.commit() + + assignments = cvat_service.get_unprocessed_expired_assignments(self.session) + self.assertEqual(len(assignments), 1) + self.assertEqual(assignments[0].id, assignment_2.id) + self.assertNotEqual(assignments[0].id, assignment.id) + self.assertEqual(assignments[0].user_wallet_address, wallet_address_2) + + def test_update_assignment(self): + (_, _, cvat_job) = create_project_task_and_job( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", 1 + ) + wallet_address_1 = "0x86e83d346041E8806e352681f3F14549C0d2BC67" + user = User( + wallet_address=wallet_address_1, + cvat_email="test@hmt.ai", + cvat_id=1, + ) + self.session.add(user) + + assignment = Assignment( + id=str(uuid.uuid4()), + user_wallet_address=wallet_address_1, + cvat_job_id=cvat_job.cvat_id, + expires_at=datetime.now() + timedelta(days=1), + ) + self.session.add(assignment) + self.session.commit() + + cvat_service.update_assignment( + self.session, assignment.id, status=AssignmentStatus.completed + ) + + db_assignment = self.session.query(Assignment).filter_by(id=assignment.id).first() + + self.assertEqual(db_assignment.id, assignment.id) + self.assertEqual(db_assignment.status, AssignmentStatus.completed) + + def test_cancel_assignment(self): + (_, _, cvat_job) = create_project_task_and_job( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", 1 + ) + wallet_address_1 = "0x86e83d346041E8806e352681f3F14549C0d2BC67" + user = User( + wallet_address=wallet_address_1, + cvat_email="test@hmt.ai", + cvat_id=1, + ) + self.session.add(user) + + assignment = Assignment( + id=str(uuid.uuid4()), + user_wallet_address=wallet_address_1, + cvat_job_id=cvat_job.cvat_id, + expires_at=datetime.now() + timedelta(days=1), + ) + self.session.add(assignment) + self.session.commit() + + cvat_service.cancel_assignment(self.session, assignment.id) + + db_assignment = self.session.query(Assignment).filter_by(id=assignment.id).first() + + self.assertEqual(db_assignment.id, assignment.id) + self.assertEqual(db_assignment.status, AssignmentStatus.canceled) + + def test_expire_assignment(self): + (_, _, cvat_job) = create_project_task_and_job( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", 1 + ) + wallet_address_1 = "0x86e83d346041E8806e352681f3F14549C0d2BC67" + user = User( + wallet_address=wallet_address_1, + cvat_email="test@hmt.ai", + cvat_id=1, + ) + self.session.add(user) + + assignment = Assignment( + id=str(uuid.uuid4()), + user_wallet_address=wallet_address_1, + cvat_job_id=cvat_job.cvat_id, + expires_at=datetime.now() + timedelta(days=1), + ) + self.session.add(assignment) + self.session.commit() + + cvat_service.expire_assignment(self.session, assignment.id) + + db_assignment = self.session.query(Assignment).filter_by(id=assignment.id).first() + + self.assertEqual(db_assignment.id, assignment.id) + self.assertEqual(db_assignment.status, AssignmentStatus.expired) + + def test_complete_assignment(self): + (_, _, cvat_job) = create_project_task_and_job( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", 1 + ) + wallet_address_1 = "0x86e83d346041E8806e352681f3F14549C0d2BC67" + user = User( + wallet_address=wallet_address_1, + cvat_email="test@hmt.ai", + cvat_id=1, + ) + self.session.add(user) + + assignment = Assignment( + id=str(uuid.uuid4()), + user_wallet_address=wallet_address_1, + cvat_job_id=cvat_job.cvat_id, + expires_at=datetime.now() + timedelta(days=1), + ) + self.session.add(assignment) + self.session.commit() + completed_date = datetime.now() + timedelta(days=1) + cvat_service.complete_assignment(self.session, assignment.id, completed_date) + + db_assignment = self.session.query(Assignment).filter_by(id=assignment.id).first() + + self.assertEqual(db_assignment.id, assignment.id) + self.assertEqual(db_assignment.status, AssignmentStatus.completed) + self.assertEqual(db_assignment.completed_at, completed_date) + + def test_test_add_project_images(self): + (_, _, cvat_job) = create_project_task_and_job( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", 1 + ) + wallet_address_1 = "0x86e83d346041E8806e352681f3F14549C0d2BC67" + user = User( + wallet_address=wallet_address_1, + cvat_email="test@hmt.ai", + cvat_id=1, + ) + self.session.add(user) + + wallet_address_2 = "0x86e83d346041E8806e352681f3F14549C0d2BC68" + user = User( + wallet_address=wallet_address_2, + cvat_email="test2@hmt.ai", + cvat_id=2, + ) + self.session.add(user) + assignment = Assignment( + id=str(uuid.uuid4()), + user_wallet_address=wallet_address_1, + cvat_job_id=cvat_job.cvat_id, + expires_at=datetime.now(), + ) + assignment_2 = Assignment( + id=str(uuid.uuid4()), + user_wallet_address=wallet_address_2, + cvat_job_id=cvat_job.cvat_id, + expires_at=datetime.now(), + ) + self.session.add(assignment) + self.session.add(assignment_2) + self.session.commit() + + assignments = cvat_service.get_user_assignments_in_cvat_projects( + self.session, wallet_address_1, [cvat_job.cvat_id] + ) + self.assertEqual(len(assignments), 1) + self.assertEqual(assignments[0].id, assignment.id) + self.assertNotEqual(assignments[0].id, assignment_2.id) + self.assertEqual(assignments[0].user_wallet_address, wallet_address_1) + + def test_add_project_images(self): + cvat_project = create_project(self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", 1) + + filenames = [ + "image1.jpg", + "image2.jpg", + ] + cvat_service.add_project_images( + self.session, cvat_project_id=cvat_project.cvat_id, filenames=filenames + ) + + images = ( + self.session.query(Image).where(Image.cvat_project_id == cvat_project.cvat_id).all() + ) + + self.assertEqual(len(images), 2) + self.assertEqual(images[0].filename, filenames[0]) + self.assertEqual(images[1].filename, filenames[1]) + + def test_add_project_images_wrong_project_id(self): + filenames = [ + "image1.jpg", + "image2.jpg", + ] + with self.assertRaises(IntegrityError): + cvat_service.add_project_images(self.session, cvat_project_id=1, filenames=filenames) + + def test_add_project_images(self): + cvat_project = create_project(self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", 1) + + filenames = [ + "image1.jpg", + "image2.jpg", + ] + image_1 = Image( + id=str(uuid.uuid4()), cvat_project_id=cvat_project.cvat_id, filename=filenames[0] + ) + image_2 = Image( + id=str(uuid.uuid4()), cvat_project_id=cvat_project.cvat_id, filename=filenames[1] + ) + + self.session.add(image_1) + self.session.add(image_2) + self.session.commit() + + images = cvat_service.get_project_images(self.session, cvat_project.cvat_id) + + self.assertEqual(len(images), 2) + self.assertEqual(images[0].filename, filenames[0]) + self.assertEqual(images[1].filename, filenames[1]) + + images = cvat_service.get_project_images(self.session, 2) + + self.assertEqual(len(images), 0) diff --git a/packages/examples/cvat/exchange-oracle/tests/integration/services/test_exchange.py b/packages/examples/cvat/exchange-oracle/tests/integration/services/test_exchange.py new file mode 100644 index 0000000000..60859f2927 --- /dev/null +++ b/packages/examples/cvat/exchange-oracle/tests/integration/services/test_exchange.py @@ -0,0 +1,282 @@ +import json +import unittest +import uuid +from datetime import datetime, timedelta +from unittest.mock import patch + +from fastapi import HTTPException +from pydantic import ValidationError + +import src.services.cvat as cvat_service +from src.core.types import AssignmentStatus, PlatformType, ProjectStatuses +from src.db import SessionLocal +from src.models.cvat import Assignment, User +from src.schemas import exchange as service_api +from src.services.exchange import ( + create_assignment, + get_available_tasks, + get_tasks_by_assignee, + serialize_task, +) + +from tests.utils.db_helper import create_project, create_project_task_and_job + + +class ServiceIntegrationTest(unittest.TestCase): + def setUp(self): + self.session = SessionLocal() + + def tearDown(self): + self.session.close() + + def test_serialize_task(self): + cvat_id = 1 + escrow_address = "0x86e83d346041E8806e352681f3F14549C0d2BC67" + + cvat_project = create_project(self.session, escrow_address, cvat_id) + self.session.commit() + + with ( + open("tests/utils/manifest.json") as data, + patch("src.services.exchange.get_escrow_manifest") as mock_get_manifest, + ): + manifest = json.load(data) + mock_get_manifest.return_value = manifest + data = serialize_task(cvat_project.id) + + self.assertEqual(data.id, cvat_project.id) + self.assertEqual(data.escrow_address, escrow_address) + self.assertIn("Task ", data.title) + self.assertTrue(len(data.title.split("Task ")[1]) <= 10) + self.assertIsInstance(data.description, str) + self.assertIsInstance(data.job_bounty, str) + self.assertIsInstance(data.job_time_limit, int) + self.assertIsInstance(data.job_size, int) + self.assertEqual(data.job_type, cvat_project.job_type) + self.assertEqual(data.platform, PlatformType.CVAT) + self.assertEqual(data.status, cvat_project.status) + self.assertIsNone(data.assignment) + self.assertIsInstance(data, service_api.TaskResponse) + + def test_serialize_task_with_assignment(self): + cvat_id = 1 + escrow_address = "0x86e83d346041E8806e352681f3F14549C0d2BC67" + user_address = "0x86e83d346041E8806e352681f3F14549C0d2BC68" + + cvat_project, _, cvat_job = create_project_task_and_job( + self.session, escrow_address, cvat_id + ) + + user = User( + wallet_address=user_address, + cvat_email="test@hmt.ai", + cvat_id=1, + ) + self.session.add(user) + + assignment = Assignment( + id=str(uuid.uuid4()), + user_wallet_address=user_address, + cvat_job_id=cvat_job.cvat_id, + expires_at=datetime.now() + timedelta(days=1), + ) + self.session.add(assignment) + self.session.commit() + + with ( + open("tests/utils/manifest.json") as data, + patch("src.services.exchange.get_escrow_manifest") as mock_get_manifest, + ): + manifest = json.load(data) + mock_get_manifest.return_value = manifest + data = serialize_task(project_id=cvat_project.id, assignment_id=assignment.id) + + self.assertEqual(data.id, cvat_project.id) + self.assertEqual(data.escrow_address, escrow_address) + self.assertIn("Task ", data.title) + self.assertTrue(len(data.title.split("Task ")[1]) <= 10) + self.assertIsInstance(data.description, str) + self.assertIsInstance(data.job_bounty, str) + self.assertIsInstance(data.job_time_limit, int) + self.assertIsInstance(data.job_size, int) + self.assertEqual(data.job_type, cvat_project.job_type) + self.assertEqual(data.platform, PlatformType.CVAT) + self.assertEqual(data.status, cvat_project.status) + self.assertIsNotNone(data.assignment) + self.assertIsInstance(data.assignment.assignment_url, str) + self.assertEqual(data.assignment.started_at, assignment.created_at) + self.assertEqual(data.assignment.finishes_at, assignment.expires_at) + self.assertIsInstance(data, service_api.TaskResponse) + + def test_serialize_task_invalid_project(self): + with self.assertRaises(AttributeError): + serialize_task(project_id=str(uuid.uuid4())) + + def test_serialize_task_invalid_manifest(self): + cvat_id = 1 + escrow_address = "0x86e83d346041E8806e352681f3F14549C0d2BC67" + + cvat_project = create_project(self.session, escrow_address, cvat_id) + self.session.commit() + + with patch("src.services.exchange.get_escrow_manifest") as mock_get_manifest: + mock_get_manifest.return_value = None + with self.assertRaises(ValidationError): + serialize_task(project_id=cvat_project.id) + + def test_get_available_tasks(self): + cvat_project_1, _, _ = create_project_task_and_job( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", 1 + ) + cvat_project_2, _, _ = create_project_task_and_job( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC68", 2 + ) + self.session.commit() + + with ( + open("tests/utils/manifest.json") as data, + patch("src.services.exchange.get_escrow_manifest") as mock_get_manifest, + ): + manifest = json.load(data) + mock_get_manifest.return_value = manifest + tasks = get_available_tasks() + + self.assertEqual(len(tasks), 2) + self.assertIsInstance(tasks[0], service_api.TaskResponse) + self.assertIsInstance(tasks[1], service_api.TaskResponse) + self.assertTrue(any(task.id == cvat_project_1.id for task in tasks)) + self.assertTrue(any(task.id == cvat_project_2.id for task in tasks)) + + cvat_service.update_project_status( + self.session, cvat_project_2.id, ProjectStatuses.completed + ) + self.session.commit() + tasks = get_available_tasks() + + self.assertEqual(len(tasks), 1) + self.assertIsInstance(tasks[0], service_api.TaskResponse) + self.assertEqual(tasks[0].id, cvat_project_1.id) + + def test_get_tasks_by_assignee(self): + cvat_project_1, _, cvat_job_1 = create_project_task_and_job( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", 1 + ) + cvat_project_2, _, cvat_job_2 = create_project_task_and_job( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC68", 2 + ) + + user_address = "0x86e83d346041E8806e352681f3F14549C0d2BC69" + user = User( + wallet_address=user_address, + cvat_email="test@hmt.ai", + cvat_id=1, + ) + self.session.add(user) + + assignment = Assignment( + id=str(uuid.uuid4()), + user_wallet_address=user_address, + cvat_job_id=cvat_job_1.cvat_id, + expires_at=datetime.now() + timedelta(days=1), + ) + self.session.add(assignment) + self.session.commit() + + with ( + open("tests/utils/manifest.json") as data, + patch("src.services.exchange.get_escrow_manifest") as mock_get_manifest, + ): + manifest = json.load(data) + mock_get_manifest.return_value = manifest + tasks = get_tasks_by_assignee(user_address) + + self.assertEqual(len(tasks), 1) + self.assertIsInstance(tasks[0], service_api.TaskResponse) + self.assertEqual(tasks[0].id, cvat_project_1.id) + self.assertIsNotNone(tasks[0].assignment) + + def test_get_tasks_by_assignee_invalid_address(self): + tasks = get_tasks_by_assignee("invalid_address") + self.assertEqual(len(tasks), 0) + + def test_create_assignment(self): + cvat_project_1, _, cvat_job_1 = create_project_task_and_job( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", 1 + ) + user_address = "0x86e83d346041E8806e352681f3F14549C0d2BC69" + user = User( + wallet_address=user_address, + cvat_email="test@hmt.ai", + cvat_id=1, + ) + self.session.add(user) + self.session.commit() + + with ( + open("tests/utils/manifest.json") as data, + patch("src.services.exchange.get_escrow_manifest") as mock_get_manifest, + patch("src.services.exchange.cvat_api"), + ): + manifest = json.load(data) + mock_get_manifest.return_value = manifest + assingment_id = create_assignment(cvat_project_1.id, user_address) + + assignment = self.session.query(Assignment).filter_by(id=assingment_id).first() + + self.assertEqual(assignment.cvat_job_id, cvat_job_1.cvat_id) + self.assertEqual(assignment.user_wallet_address, user_address) + self.assertEqual(assignment.status, AssignmentStatus.created) + + def test_create_assignment_invalid_user_address(self): + cvat_project_1, _, _ = create_project_task_and_job( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", 1 + ) + self.session.commit() + + with self.assertRaises(HTTPException): + create_assignment(cvat_project_1.id, "invalid_address") + + def test_create_assignment_invalid_project(self): + user_address = "0x86e83d346041E8806e352681f3F14549C0d2BC69" + user = User( + wallet_address=user_address, + cvat_email="test@hmt.ai", + cvat_id=1, + ) + self.session.add(user) + self.session.commit() + + with self.assertRaises(HTTPException): + create_assignment("1", user_address) + + def test_create_assignment_unfinished_assignment(self): + _, _, cvat_job = create_project_task_and_job( + self.session, "0x86e83d346041E8806e352681f3F14549C0d2BC67", 1 + ) + user_address = "0x86e83d346041E8806e352681f3F14549C0d2BC69" + user = User( + wallet_address=user_address, + cvat_email="test@hmt.ai", + cvat_id=1, + ) + self.session.add(user) + + assignment = Assignment( + id=str(uuid.uuid4()), + user_wallet_address=user_address, + cvat_job_id=cvat_job.cvat_id, + expires_at=datetime.now() + timedelta(days=1), + ) + self.session.add(assignment) + self.session.commit() + + with ( + open("tests/utils/manifest.json") as data, + patch("src.services.exchange.get_escrow_manifest") as mock_get_manifest, + patch("src.services.exchange.cvat_api"), + ): + manifest = json.load(data) + mock_get_manifest.return_value = manifest + + with self.assertRaises(HTTPException): + create_assignment("1", user_address) diff --git a/packages/examples/cvat/exchange-oracle/tests/integration/services/test_webhook.py b/packages/examples/cvat/exchange-oracle/tests/integration/services/test_webhook.py index 1b28c447d7..2569553479 100644 --- a/packages/examples/cvat/exchange-oracle/tests/integration/services/test_webhook.py +++ b/packages/examples/cvat/exchange-oracle/tests/integration/services/test_webhook.py @@ -4,7 +4,15 @@ from sqlalchemy.exc import IntegrityError import src.services.webhook as webhook_service -from src.core.types import Networks, OracleWebhookStatuses, OracleWebhookTypes +from src.core.oracle_events import ExchangeOracleEvent_TaskFinished +from src.core.types import ( + ExchangeOracleEventType, + JobLauncherEventType, + Networks, + OracleWebhookStatuses, + OracleWebhookTypes, + RecordingOracleEventType, +) from src.db import SessionLocal from src.models.webhook import Webhook @@ -16,82 +24,169 @@ def setUp(self): def tearDown(self): self.session.close() - def test_create_webhook(self): + def test_create_incoming_webhook(self): escrow_address = "0x1234567890123456789012345678901234567890" chain_id = Networks.localhost.value signature = "signature" - webhook_id = webhook_service.create_webhook( + webhook_id = webhook_service.inbox.create_webhook( self.session, escrow_address=escrow_address, chain_id=chain_id, - sender_type=OracleWebhookTypes.job_launcher.value, - sender_signature=signature, + signature=signature, + type=OracleWebhookTypes.job_launcher, + event_type=JobLauncherEventType.escrow_created.value, ) webhook = self.session.query(Webhook).filter_by(id=webhook_id).first() self.assertEqual(webhook.escrow_address, escrow_address) self.assertEqual(webhook.chain_id, chain_id) - self.assertEqual(webhook.attempts, 0) self.assertEqual(webhook.signature, signature) + self.assertEqual(webhook.attempts, 0) self.assertEqual(webhook.type, OracleWebhookTypes.job_launcher.value) + self.assertEqual(webhook.event_type, JobLauncherEventType.escrow_created.value) + self.assertEqual(webhook.event_data, None) self.assertEqual(webhook.status, OracleWebhookStatuses.pending.value) - def test_create_webhook_none_escrow_address(self): + def test_create_incoming_webhook_none_escrow_address(self): chain_id = Networks.localhost.value signature = "signature" - webhook_service.create_webhook( + webhook_service.inbox.create_webhook( self.session, escrow_address=None, chain_id=chain_id, - sender_type=OracleWebhookTypes.job_launcher.value, - sender_signature=signature, + signature=signature, + type=OracleWebhookTypes.job_launcher, + event_type=JobLauncherEventType.escrow_created.value, ) with self.assertRaises(IntegrityError): self.session.commit() - def test_create_webhook_none_chain_id(self): + def test_create_incoming_webhook_none_chain_id(self): escrow_address = "0x1234567890123456789012345678901234567890" signature = "signature" - webhook_service.create_webhook( + webhook_service.inbox.create_webhook( self.session, escrow_address=escrow_address, chain_id=None, - sender_type=OracleWebhookTypes.job_launcher.value, - sender_signature=signature, + signature=signature, + type=OracleWebhookTypes.job_launcher, + event_type=JobLauncherEventType.escrow_created.value, ) with self.assertRaises(IntegrityError): self.session.commit() - def test_create_webhook_none_signature(self): + def test_create_incoming_webhook_none_event_type(self): + escrow_address = "0x1234567890123456789012345678901234567890" + signature = "signature" + with self.assertRaises(AssertionError) as error: + webhook_service.inbox.create_webhook( + self.session, + escrow_address=escrow_address, + chain_id=None, + signature=signature, + type=OracleWebhookTypes.job_launcher, + ) + self.assertEqual( + str(error.exception), + "'event' and 'event_type' cannot be used together. Please use only one of the fields", + ) + + def test_create_incoming_webhook_none_signature(self): + escrow_address = "0x1234567890123456789012345678901234567890" + chain_id = Networks.localhost.value + + with self.assertRaises(ValueError) as error: + webhook_service.inbox.create_webhook( + self.session, + escrow_address=escrow_address, + chain_id=chain_id, + type=OracleWebhookTypes.job_launcher, + event_type=JobLauncherEventType.escrow_created.value, + ) + self.assertEqual( + str(error.exception), "Webhook signature must be specified for incoming events" + ) + + def test_create_outgoing_webhook(self): escrow_address = "0x1234567890123456789012345678901234567890" chain_id = Networks.localhost.value - webhook_service.create_webhook( + webhook_id = webhook_service.outbox.create_webhook( self.session, escrow_address=escrow_address, chain_id=chain_id, - sender_type=OracleWebhookTypes.job_launcher.value, - sender_signature=None, + type=OracleWebhookTypes.exchange_oracle, + event=ExchangeOracleEvent_TaskFinished(), + ) + + webhook = self.session.query(Webhook).filter_by(id=webhook_id).first() + + self.assertEqual(webhook.escrow_address, escrow_address) + self.assertEqual(webhook.chain_id, chain_id) + self.assertEqual(webhook.attempts, 0) + self.assertEqual(webhook.type, OracleWebhookTypes.exchange_oracle.value) + self.assertEqual(webhook.event_type, ExchangeOracleEventType.task_finished.value) + self.assertEqual(webhook.event_data, {}) + self.assertEqual(webhook.status, OracleWebhookStatuses.pending.value) + + def test_create_outgoing_webhook_none_escrow_address(self): + chain_id = Networks.localhost.value + webhook_service.outbox.create_webhook( + self.session, + escrow_address=None, + chain_id=chain_id, + type=OracleWebhookTypes.exchange_oracle, + event=ExchangeOracleEvent_TaskFinished(), ) with self.assertRaises(IntegrityError): self.session.commit() - def test_create_recoracle_webhook_none_s3_url(self): + def test_create_outgoing_webhook_none_chain_id(self): escrow_address = "0x1234567890123456789012345678901234567890" - chain_id = Networks.localhost.value - - webhook_service.create_webhook( + webhook_service.outbox.create_webhook( self.session, escrow_address=escrow_address, - chain_id=chain_id, - sender_type=OracleWebhookTypes.recording_oracle.value, - sender_signature=None, + chain_id=None, + type=OracleWebhookTypes.exchange_oracle, + event=ExchangeOracleEvent_TaskFinished(), ) - with self.assertRaises(ValueError): + with self.assertRaises(IntegrityError) as error: self.session.commit() + def test_create_outgoing_webhook_none_event_type(self): + escrow_address = "0x1234567890123456789012345678901234567890" + with self.assertRaises(AssertionError) as error: + webhook_service.outbox.create_webhook( + self.session, + escrow_address=escrow_address, + chain_id=None, + type=OracleWebhookTypes.exchange_oracle, + ) + self.assertEqual( + str(error.exception), + "'event' and 'event_type' cannot be used together. Please use only one of the fields", + ) + + def test_create_outgoing_webhook_with_signature(self): + escrow_address = "0x1234567890123456789012345678901234567890" + chain_id = Networks.localhost.value + signature = "signature" + + with self.assertRaises(ValueError) as error: + webhook_service.outbox.create_webhook( + self.session, + escrow_address=escrow_address, + chain_id=chain_id, + type=OracleWebhookTypes.exchange_oracle, + event=ExchangeOracleEvent_TaskFinished(), + signature=signature, + ) + self.assertEqual( + str(error.exception), "Webhook signature must not be specified for outgoing events" + ) + def test_get_pending_webhooks(self): chain_id = Networks.localhost.value @@ -103,6 +198,8 @@ def test_get_pending_webhooks(self): chain_id=chain_id, type=OracleWebhookTypes.job_launcher.value, status=OracleWebhookStatuses.pending.value, + event_type=JobLauncherEventType.escrow_created.value, + direction=webhook_service.OracleWebhookDirectionTag.incoming, ) webhook2_id = str(uuid.uuid4()) webhook2 = Webhook( @@ -112,6 +209,8 @@ def test_get_pending_webhooks(self): chain_id=chain_id, type=OracleWebhookTypes.job_launcher.value, status=OracleWebhookStatuses.pending.value, + event_type=JobLauncherEventType.escrow_created.value, + direction=webhook_service.OracleWebhookDirectionTag.incoming, ) webhook3_id = str(uuid.uuid4()) webhook3 = Webhook( @@ -121,53 +220,74 @@ def test_get_pending_webhooks(self): chain_id=chain_id, type=OracleWebhookTypes.job_launcher.value, status=OracleWebhookStatuses.completed.value, + event_type=JobLauncherEventType.escrow_created.value, + direction=webhook_service.OracleWebhookDirectionTag.incoming, ) webhook4_id = str(uuid.uuid4()) webhook4 = Webhook( id=webhook4_id, signature="signature4", - escrow_address="0x1234567890123456789012345678901234567892", + escrow_address="0x1234567890123456789012345678901234567893", chain_id=chain_id, - s3_url="https://some-dummy-url.com/1.json", type=OracleWebhookTypes.recording_oracle.value, status=OracleWebhookStatuses.pending.value, + event_type=RecordingOracleEventType.task_completed.value, + direction=webhook_service.OracleWebhookDirectionTag.incoming, + ) + webhook5_id = str(uuid.uuid4()) + webhook5 = Webhook( + id=webhook5_id, + escrow_address="0x1234567890123456789012345678901234567894", + chain_id=chain_id, + type=OracleWebhookTypes.job_launcher.value, + status=OracleWebhookStatuses.pending.value, + event_type=JobLauncherEventType.escrow_created.value, + direction=webhook_service.OracleWebhookDirectionTag.outgoing, ) self.session.add(webhook1) self.session.add(webhook2) self.session.add(webhook3) self.session.add(webhook4) + self.session.add(webhook5) self.session.commit() - pending_webhooks = webhook_service.get_pending_webhooks( - self.session, OracleWebhookTypes.job_launcher.value, 10 + pending_webhooks = webhook_service.inbox.get_pending_webhooks( + self.session, sender_type=OracleWebhookTypes.job_launcher, limit=10 ) self.assertEqual(len(pending_webhooks), 2) self.assertEqual(pending_webhooks[0].id, webhook1_id) self.assertEqual(pending_webhooks[1].id, webhook2_id) - pending_webhooks = webhook_service.get_pending_webhooks( - self.session, OracleWebhookTypes.recording_oracle.value, 10 + pending_webhooks = webhook_service.inbox.get_pending_webhooks( + self.session, sender_type=OracleWebhookTypes.recording_oracle, limit=10 ) self.assertEqual(len(pending_webhooks), 1) self.assertEqual(pending_webhooks[0].id, webhook4_id) + pending_webhooks = webhook_service.outbox.get_pending_webhooks( + self.session, sender_type=OracleWebhookTypes.job_launcher, limit=10 + ) + self.assertEqual(len(pending_webhooks), 1) + self.assertEqual(pending_webhooks[0].id, webhook5_id) + def test_update_webhook_status(self): escrow_address = "0x1234567890123456789012345678901234567890" chain_id = Networks.localhost.value signature = "signature" - webhook_id = webhook_service.create_webhook( + webhook_id = webhook_service.inbox.create_webhook( self.session, escrow_address=escrow_address, chain_id=chain_id, - sender_type=OracleWebhookTypes.job_launcher.value, - sender_signature=signature, + signature=signature, + type=OracleWebhookTypes.job_launcher, + event_type=JobLauncherEventType.escrow_created.value, ) - webhook_service.update_webhook_status( - self.session, webhook_id, OracleWebhookStatuses.completed.value + webhook_service.inbox.update_webhook_status( + self.session, webhook_id, OracleWebhookStatuses.completed ) webhook = self.session.query(Webhook).filter_by(id=webhook_id).first() @@ -179,36 +299,21 @@ def test_update_webhook_status(self): self.assertEqual(webhook.type, OracleWebhookTypes.job_launcher.value) self.assertEqual(webhook.status, OracleWebhookStatuses.completed.value) - def test_update_webhook_invalid_status(self): - escrow_address = "0x1234567890123456789012345678901234567890" - chain_id = Networks.localhost.value - signature = "signature" - - webhook_id = webhook_service.create_webhook( - self.session, - escrow_address=escrow_address, - chain_id=chain_id, - sender_type=OracleWebhookTypes.job_launcher.value, - sender_signature=signature, - ) - - with self.assertRaises(ValueError): - webhook_service.update_webhook_status(self.session, webhook_id, "Invalid status") - def test_handle_webhook_success(self): escrow_address = "0x1234567890123456789012345678901234567890" chain_id = Networks.localhost.value signature = "signature" - webhook_id = webhook_service.create_webhook( + webhook_id = webhook_service.inbox.create_webhook( self.session, escrow_address=escrow_address, chain_id=chain_id, - sender_type=OracleWebhookTypes.job_launcher.value, - sender_signature=signature, + signature=signature, + type=OracleWebhookTypes.job_launcher, + event_type=JobLauncherEventType.escrow_created.value, ) - webhook_service.handle_webhook_success(self.session, webhook_id) + webhook_service.inbox.handle_webhook_success(self.session, webhook_id) webhook = self.session.query(Webhook).filter_by(id=webhook_id).first() @@ -224,15 +329,16 @@ def test_handle_webhook_fail(self): chain_id = Networks.localhost.value signature = "signature" - webhook_id = webhook_service.create_webhook( + webhook_id = webhook_service.inbox.create_webhook( self.session, escrow_address=escrow_address, chain_id=chain_id, - sender_type=OracleWebhookTypes.job_launcher.value, - sender_signature=signature, + signature=signature, + type=OracleWebhookTypes.job_launcher, + event_type=JobLauncherEventType.escrow_created.value, ) - webhook_service.handle_webhook_fail(self.session, webhook_id) + webhook_service.inbox.handle_webhook_fail(self.session, webhook_id) webhook = self.session.query(Webhook).filter_by(id=webhook_id).first() @@ -244,7 +350,7 @@ def test_handle_webhook_fail(self): self.assertEqual(webhook.status, OracleWebhookStatuses.pending.value) for i in range(4): - webhook_service.handle_webhook_fail(self.session, webhook_id) + webhook_service.inbox.handle_webhook_fail(self.session, webhook_id) webhook = self.session.query(Webhook).filter_by(id=webhook_id).first() diff --git a/packages/examples/cvat/exchange-oracle/tests/unit/helpers/test_format_annotations.py b/packages/examples/cvat/exchange-oracle/tests/unit/helpers/test_format_annotations.py deleted file mode 100644 index 53c404c880..0000000000 --- a/packages/examples/cvat/exchange-oracle/tests/unit/helpers/test_format_annotations.py +++ /dev/null @@ -1,62 +0,0 @@ -from src.handlers.annotation import process_image_label_binary_raw_annotations - -from tests.unit.helpers.predefined_annotations import binary_annotations, raw_binary_annotations - - -def test_process_image_label_binary_annotations(): - bucket_url = "https://test.storage.googleapis.com/" - assignee = "0x86e83d346041E8806e352681f3F14549C0d2BC68" - handler = process_image_label_binary_raw_annotations - - annotations = handler([], raw_binary_annotations, bucket_url, assignee) - annotations_check = [ - { - "url": f"{bucket_url}1.jpg", - "answers": [{"tag": "dummy_label", "assignee": assignee}], - }, - { - "url": f"{bucket_url}2.jpg", - "answers": [{"tag": "dummy_label", "assignee": assignee}], - }, - { - "url": f"{bucket_url}3.jpg", - "answers": [{"tag": "dummy_label", "assignee": assignee}], - }, - ] - assert annotations == annotations_check - - new_annotations = handler(binary_annotations, raw_binary_annotations, bucket_url, assignee) - - annotations_check = [ - { - "url": f"{bucket_url}1.jpg", - "answers": [ - { - "tag": "dummy_label", - "assignee": "0x86e83d346041E8806e352681f3F14549C0d2BC61", - }, - {"tag": "dummy_label", "assignee": assignee}, - ], - }, - { - "url": f"{bucket_url}2.jpg", - "answers": [ - { - "tag": "dummy_label", - "assignee": "0x86e83d346041E8806e352681f3F14549C0d2BC61", - }, - {"tag": "dummy_label", "assignee": assignee}, - ], - }, - { - "url": f"{bucket_url}3.jpg", - "answers": [ - { - "tag": "dummy_label", - "assignee": "0x86e83d346041E8806e352681f3F14549C0d2BC61", - }, - {"tag": "dummy_label", "assignee": assignee}, - ], - }, - ] - assert new_annotations == annotations_check diff --git a/packages/examples/cvat/exchange-oracle/tests/utils/constants.py b/packages/examples/cvat/exchange-oracle/tests/utils/constants.py index 8b38e88727..33e16d146e 100644 --- a/packages/examples/cvat/exchange-oracle/tests/utils/constants.py +++ b/packages/examples/cvat/exchange-oracle/tests/utils/constants.py @@ -11,6 +11,12 @@ EXCHANGE_ORACLE_ADDRESS = "0x90F79bf6EB2c4f870365E785982E1f101E93b906" EXCHANGE_ORACLE_FEE = 10 +JOB_LAUNCHER_ADDRESS = "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65" + +ESCROW_ADDRESS = "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc" +TOKEN_ADDRESS = "0x976EA74026E726554dB657fA54763abd0C3a0aa9" +FACTORY_ADDRESS = "0x14dC79964da2C08b23698B3D3cc7Ca32193d9955" + DEFAULT_URL = "http://host.docker.internal:9000/manifests/manifest.json" DEFAULT_HASH = "test" @@ -19,6 +25,8 @@ WEBHOOK_MESSAGE = { "escrow_address": "0x12E66A452f95bff49eD5a30b0d06Ebc37C5A94B6", "chain_id": 80001, + "event_type": "escrow_created", + "event_data": {}, } -WEBHOOK_MESSAGE_SIGNED = "0x99819b33bcb0297ea2b59b84f3ab184360f5e7ad64c44df1f761d45242c32970605e9fe761b191504607e0070cd0d291e5d7aa04a2e4c362a698c2e2de3c81351c" +WEBHOOK_MESSAGE_SIGNED = "0x90bdbd55c09a8f6a7fcadc91f32b9a131c8360a12bf736e1b851e42f6ba8b2c20c1954e01f302d3677ca9ca22c1b0cfd505509e9d2c15a34315a646b53a705f21b" diff --git a/packages/examples/cvat/exchange-oracle/tests/utils/db_helper.py b/packages/examples/cvat/exchange-oracle/tests/utils/db_helper.py new file mode 100644 index 0000000000..e315357dc5 --- /dev/null +++ b/packages/examples/cvat/exchange-oracle/tests/utils/db_helper.py @@ -0,0 +1,49 @@ +import uuid + +from src.core.types import JobStatuses, Networks, ProjectStatuses, TaskStatus, TaskType +from src.db import SessionLocal +from src.models.cvat import Job, Project, Task + + +def create_project(session: SessionLocal, escrow_address: str, cvat_id: int) -> tuple: + cvat_project = Project( + id=str(uuid.uuid4()), + cvat_id=cvat_id, + cvat_cloudstorage_id=1, + status=ProjectStatuses.annotation.value, + job_type=TaskType.image_label_binary.value, + escrow_address=escrow_address, + chain_id=Networks.localhost.value, + bucket_url="https://test.storage.googleapis.com/", + ) + session.add(cvat_project) + + return cvat_project + + +def create_project_and_task(session: SessionLocal, escrow_address: str, cvat_id: int) -> tuple: + cvat_project = create_project(session, escrow_address, cvat_id) + cvat_task = Task( + id=str(uuid.uuid4()), + cvat_id=cvat_id, + cvat_project_id=cvat_id, + status=TaskStatus.annotation.value, + ) + session.add(cvat_task) + + return cvat_project, cvat_task + + +def create_project_task_and_job(session: SessionLocal, escrow_address: str, cvat_id: int) -> tuple: + cvat_project, cvat_task = create_project_and_task(session, escrow_address, cvat_id) + + cvat_job = Job( + id=str(uuid.uuid4()), + cvat_id=cvat_id, + cvat_project_id=cvat_id, + cvat_task_id=cvat_id, + status=JobStatuses.new, + ) + session.add(cvat_job) + + return cvat_project, cvat_task, cvat_job diff --git a/packages/examples/cvat/exchange-oracle/tests/utils/manifest.json b/packages/examples/cvat/exchange-oracle/tests/utils/manifest.json index 5b04de8487..18c5b7badb 100644 --- a/packages/examples/cvat/exchange-oracle/tests/utils/manifest.json +++ b/packages/examples/cvat/exchange-oracle/tests/utils/manifest.json @@ -1,11 +1,19 @@ { - "dataUrl": "https://test.storage.googleapis.com/", - "submissionsRequired": 1, - "labels": ["dummy_label"], - "requesterDescription": "string", - "requesterAccuracyTarget": 10, - "fee": "250000000000", - "fundAmount": "1250000000000", - "mode": "BATCH", - "requestType": "IMAGE_LABEL_BINARY" + "data": { + "data_url": "https://test.storage.googleapis.com/" + }, + "annotation": { + "labels": [{ "name": "dummy_label" }], + "description": "Dummy description", + "user_guide": "https://test.storage.googleapis.com", + "type": "IMAGE_BOXES", + "job_size": 10, + "max_time": 300 + }, + "validation": { + "min_quality": 0.8, + "val_size": 2, + "gt_url": "https://test.storage.googleapis.com" + }, + "job_bounty": "5.001123929619726" } diff --git a/packages/examples/cvat/exchange-oracle/tests/utils/setup_cvat.py b/packages/examples/cvat/exchange-oracle/tests/utils/setup_cvat.py index a75be36554..e426bd8d03 100644 --- a/packages/examples/cvat/exchange-oracle/tests/utils/setup_cvat.py +++ b/packages/examples/cvat/exchange-oracle/tests/utils/setup_cvat.py @@ -1,13 +1,14 @@ import hmac import json import uuid +from datetime import datetime from hashlib import sha256 from sqlalchemy.sql import select from src.core.config import CvatConfig from src.db import SessionLocal -from src.models.cvat import Job, Project, Task +from src.models.cvat import Assignment, Job, Project, Task, User def generate_cvat_signature(data: dict): @@ -68,7 +69,6 @@ def add_cvat_job_to_db(cvat_id: int, cvat_task_id: int, cvat_project_id: int, st cvat_task_id=cvat_task_id, cvat_project_id=cvat_project_id, status=status, - assignee="", ) session.add(job) @@ -76,10 +76,36 @@ def add_cvat_job_to_db(cvat_id: int, cvat_task_id: int, cvat_project_id: int, st return job_id -def get_cvat_job_from_db(cvat_id: int) -> dict: +def add_asignment_to_db( + wallet_address: str, cvat_id: int, cvat_job_id: int, expires_at: datetime +) -> str: + with SessionLocal.begin() as session: + user = User( + wallet_address=wallet_address, + cvat_email="test" + str(cvat_id) + "@hmt.ai", + cvat_id=cvat_id, + ) + session.add(user) + assignment_id = str(uuid.uuid4()) + assignment = Assignment( + id=assignment_id, + user_wallet_address=wallet_address, + cvat_job_id=cvat_job_id, + expires_at=expires_at, + ) + + session.add(assignment) + + return assignment_id + + +def get_cvat_job_from_db(cvat_id: int) -> tuple: with SessionLocal.begin() as session: session.expire_on_commit = False job_query = select(Job).where(Job.cvat_id == cvat_id) job = session.execute(job_query).scalars().first() - return job + asignments_query = select(Assignment).where(Assignment.cvat_job_id == cvat_id) + asignments = session.execute(asignments_query).scalars().all() + + return job, asignments diff --git a/packages/examples/cvat/exchange-oracle/tests/utils/setup_escrow.py b/packages/examples/cvat/exchange-oracle/tests/utils/setup_escrow.py deleted file mode 100644 index 225653c5f0..0000000000 --- a/packages/examples/cvat/exchange-oracle/tests/utils/setup_escrow.py +++ /dev/null @@ -1,51 +0,0 @@ -from decimal import Decimal - -from human_protocol_sdk.constants import NETWORKS, ChainId -from human_protocol_sdk.escrow import EscrowClient, EscrowConfig -from human_protocol_sdk.staking import StakingClient -from web3 import Web3 - -from tests.utils.constants import ( - DEFAULT_HASH, - DEFAULT_URL, - EXCHANGE_ORACLE_ADDRESS, - EXCHANGE_ORACLE_FEE, - RECORDING_ORACLE_ADDRESS, - RECORDING_ORACLE_FEE, - REPUTATION_ORACLE_ADDRESS, - REPUTATION_ORACLE_FEE, -) - -amount = Web3.toWei(1, "ether") - - -def create_escrow(web3: Web3): - staking_client = StakingClient(web3) - escrow_client = EscrowClient(web3) - staking_client.approve_stake(amount) - staking_client.stake(amount) - escrow_address = escrow_client.create_and_setup_escrow( - NETWORKS[ChainId.LOCALHOST]["hmt_address"], - [web3.eth.default_account], - EscrowConfig( - RECORDING_ORACLE_ADDRESS, - REPUTATION_ORACLE_ADDRESS, - EXCHANGE_ORACLE_ADDRESS, - RECORDING_ORACLE_FEE, - REPUTATION_ORACLE_FEE, - EXCHANGE_ORACLE_FEE, - DEFAULT_URL, - DEFAULT_HASH, - ), - ) - return escrow_address - - -def fund_escrow(web3: Web3, escrow_address: str): - escrow_client = EscrowClient(web3) - escrow_client.fund(escrow_address, amount) - - -def bulk_payout(web3: Web3, escrow_address: str, recipient: str, amount: Decimal): - escrow_client = EscrowClient(web3) - escrow_client.bulk_payout(escrow_address, [recipient], [amount], DEFAULT_URL, DEFAULT_HASH, 1) diff --git a/packages/examples/cvat/exchange-oracle/tests/utils/setup_kvstore.py b/packages/examples/cvat/exchange-oracle/tests/utils/setup_kvstore.py deleted file mode 100644 index 25520ef9e3..0000000000 --- a/packages/examples/cvat/exchange-oracle/tests/utils/setup_kvstore.py +++ /dev/null @@ -1,21 +0,0 @@ -from human_protocol_sdk.kvstore import KVStoreClient -from web3 import HTTPProvider, Web3 -from web3.middleware import construct_sign_and_send_raw_middleware - -from tests.utils.constants import RECORDING_ORACLE_PRIV - -amount = Web3.toWei(1, "ether") - - -def store_kvstore_value(value: str): - w3 = Web3(HTTPProvider()) - - # Set default gas payer - recording_oracle = w3.eth.account.from_key(RECORDING_ORACLE_PRIV) - w3.middleware_onion.add( - construct_sign_and_send_raw_middleware(recording_oracle), - "construct_sign_and_send_raw_middleware", - ) - w3.eth.default_account = recording_oracle.address - kvstore_client = KVStoreClient(w3) - kvstore_client.set("webhook_url", value) diff --git a/packages/examples/cvat/recording-oracle/dockerfiles/blockchain-node.Dockerfile b/packages/examples/cvat/recording-oracle/dockerfiles/blockchain-node.Dockerfile index 48a01c40cb..ab4b66d200 100644 --- a/packages/examples/cvat/recording-oracle/dockerfiles/blockchain-node.Dockerfile +++ b/packages/examples/cvat/recording-oracle/dockerfiles/blockchain-node.Dockerfile @@ -1,4 +1,4 @@ -FROM node:lts-alpine +FROM node:18-alpine RUN apk add git diff --git a/packages/examples/cvat/recording-oracle/dockerfiles/test.Dockerfile b/packages/examples/cvat/recording-oracle/dockerfiles/test.Dockerfile index 835a907902..09c670973a 100644 --- a/packages/examples/cvat/recording-oracle/dockerfiles/test.Dockerfile +++ b/packages/examples/cvat/recording-oracle/dockerfiles/test.Dockerfile @@ -3,7 +3,7 @@ FROM python:3.10 WORKDIR /app RUN apt-get update -y && \ - apt-get install -y jq && \ + apt-get install -y jq ffmpeg libsm6 libxext6 && \ pip install poetry COPY pyproject.toml poetry.lock ./ diff --git a/packages/examples/cvat/recording-oracle/src/services/__init__.py b/packages/examples/cvat/recording-oracle/src/services/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/examples/cvat/recording-oracle/tests/integration/chain/__init__.py b/packages/examples/cvat/recording-oracle/tests/integration/chain/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/examples/cvat/recording-oracle/tests/integration/chain/test_escrow.py b/packages/examples/cvat/recording-oracle/tests/integration/chain/test_escrow.py index 2837f8da7f..c4774811df 100644 --- a/packages/examples/cvat/recording-oracle/tests/integration/chain/test_escrow.py +++ b/packages/examples/cvat/recording-oracle/tests/integration/chain/test_escrow.py @@ -1,22 +1,28 @@ import unittest -from unittest.mock import patch +from unittest.mock import MagicMock, patch -from human_protocol_sdk.escrow import EscrowClientError -from human_protocol_sdk.storage import StorageClientError +import pytest +from human_protocol_sdk.escrow import EscrowClientError, EscrowData from web3 import Web3 from web3.middleware import construct_sign_and_send_raw_middleware from web3.providers.rpc import HTTPProvider from src.chain.escrow import ( - get_escrow_job_type, + get_escrow_manifest, get_reputation_oracle_address, store_results, validate_escrow, ) -from src.core.types import JobTypes +from src.core.config import Config -from tests.utils.constants import DEFAULT_GAS_PAYER_PRIV, DEFAULT_HASH, DEFAULT_URL +from tests.utils.constants import ( + DEFAULT_GAS_PAYER_PRIV, + DEFAULT_HASH, + DEFAULT_URL, + REPUTATION_ORACLE_ADDRESS, +) from tests.utils.setup_escrow import ( + amount, bulk_payout, create_escrow, fund_escrow, @@ -35,31 +41,34 @@ def setUp(self): "construct_sign_and_send_raw_middleware", ) self.w3.eth.default_account = self.gas_payer.address + self.network_config = Config.localhost - def test_validate_escrow(self): - escrow_address = create_escrow(self.w3) - fund_escrow(self.w3, escrow_address) - with patch("src.chain.escrow.get_web3") as mock_function: - mock_function.return_value = self.w3 - validation = validate_escrow(self.w3.eth.chain_id, escrow_address) - self.assertIsNone(validation) + self.escrow_address = create_escrow(self.w3) + fund_escrow(self.w3, self.escrow_address) - def test_validate_escrow_invalid_address(self): - with patch("src.chain.escrow.get_web3") as mock_function: - mock_function.return_value = self.w3 - with self.assertRaises(EscrowClientError) as error: - validate_escrow(self.w3.eth.chain_id, "invalid_address") + def escrow(self, status: str = "Pending", balance: float = amount): + mock_escrow = MagicMock() + mock_escrow.status = status + mock_escrow.balance = balance + mock_escrow.reputationOracle = REPUTATION_ORACLE_ADDRESS + mock_escrow.manifestUrl = DEFAULT_URL + return mock_escrow - self.assertEqual(f"Invalid escrow address: invalid_address", str(error.exception)) + def test_validate_escrow(self): + with patch("src.chain.escrow.get_escrow") as mock_get_escrow: + mock_get_escrow.return_value = self.escrow("Pending", amount) + # should not throw an exception + validate_escrow(self.network_config.chain_id, self.escrow_address) def test_validate_escrow_without_funds(self): - escrow_address = create_escrow(self.w3) - with patch("src.chain.escrow.get_web3") as mock_function: - mock_function.return_value = self.w3 - with self.assertRaises(ValueError) as error: - validate_escrow(self.w3.eth.chain_id, escrow_address) + with patch("src.chain.escrow.get_escrow") as mock_get_escrow: + mock_get_escrow.return_value = self.escrow("Pending", 0) - self.assertEqual(f"Escrow doesn't have funds", str(error.exception)) + with pytest.raises(ValueError, match="Escrow doesn't have funds"): + validate_escrow(-1, "", allow_no_funds=False) + + # should not throw an exception + validate_escrow(self.network_config.chain_id, self.escrow_address, allow_no_funds=True) def test_validate_escrow_invalid_status(self): escrow_address = create_escrow(self.w3) @@ -68,57 +77,18 @@ def test_validate_escrow_invalid_status(self): self.w3, escrow_address, self.gas_payer.address, - Web3.toWei(50, "milliether"), + Web3.to_wei(50, "milliether"), ) - with patch("src.chain.escrow.get_web3") as mock_function: - mock_function.return_value = self.w3 + with patch("src.chain.escrow.get_escrow") as mock_get_escrow: + mock_get_escrow.return_value = self.escrow("Partial", 0.95) - with self.assertRaises(ValueError) as error: + with pytest.raises(ValueError, match="Escrow is not in any of the accepted states"): validate_escrow(self.w3.eth.chain_id, escrow_address) - self.assertEqual( - f"Escrow is not in a Pending state. Current state: Partial", - str(error.exception), - ) - def test_validate_escrow_invalid_job_type(self): - escrow_address = create_escrow(self.w3) - fund_escrow(self.w3, escrow_address) - with patch("src.chain.escrow.get_web3") as mock_function: - with patch("src.chain.escrow.StorageClient.download_file_from_url") as mock_json: - mock_function.return_value = self.w3 - mock_json.return_value = '{"requestType":"test"}'.encode() - - with self.assertRaises(ValueError) as error: - validate_escrow(self.w3.eth.chain_id, escrow_address) - self.assertEqual( - f"Oracle doesn't support job type test", - str(error.exception), - ) - - def test_get_escrow_job_type(self): - escrow_address = create_escrow(self.w3) - with patch("src.chain.escrow.get_web3") as mock_function: - mock_function.return_value = self.w3 - job_type = get_escrow_job_type(self.w3.eth.chain_id, escrow_address) - self.assertEqual(job_type, JobTypes.image_label_binary) - - def test_get_escrow_job_type_invalid_address(self): - with patch("src.chain.escrow.get_web3") as mock_function: - mock_function.return_value = self.w3 - with self.assertRaises(EscrowClientError) as error: - get_escrow_job_type(self.w3.eth.chain_id, "invalid_address") - self.assertEqual(f"Invalid escrow address: invalid_address", str(error.exception)) - - def test_get_escrow_job_type_invalid_url(self): - escrow_address = create_escrow(self.w3) - with patch("src.chain.escrow.get_web3") as mock_function: - with patch("src.chain.escrow.EscrowClient") as mock_client: - mock_escrow_client = mock_client.return_value - mock_escrow_client.get_manifest_url.return_value = "invalid_url" - mock_function.return_value = self.w3 - with self.assertRaises(StorageClientError) as error: - get_escrow_job_type(self.w3.eth.chain_id, escrow_address) - self.assertEqual(f"Invalid URL: invalid_url", str(error.exception)) + def test_get_escrow_manifest(self): + with patch("src.chain.escrow.get_escrow") as mock_get_escrow: + mock_get_escrow.return_value = self.escrow() + get_escrow_manifest(self.network_config.chain_id, self.escrow_address) def test_store_results(self): escrow_address = create_escrow(self.w3) @@ -147,22 +117,23 @@ def test_store_results_invalid_hash(self): def test_get_reputation_oracle_address(self): escrow_address = create_escrow(self.w3) - with patch("src.chain.escrow.get_web3") as mock_function: - mock_function.return_value = self.w3 - job_launcher_address = get_reputation_oracle_address( - self.w3.eth.chain_id, escrow_address - ) - self.assertIsInstance(job_launcher_address, str) - self.assertIsNotNone(job_launcher_address) + with patch("src.chain.escrow.get_web3") as mock_get_web3, patch( + "src.chain.escrow.get_escrow" + ) as mock_get_escrow: + mock_get_web3.return_value = self.w3 + mock_escrow = MagicMock() + mock_escrow.reputationOracle = REPUTATION_ORACLE_ADDRESS + mock_get_escrow.return_value = mock_escrow + address = get_reputation_oracle_address(self.w3.eth.chain_id, escrow_address) + self.assertIsInstance(address, str) + self.assertIsNotNone(address) def test_get_reputation_oracle_address_invalid_address(self): with patch("src.chain.escrow.get_web3") as mock_function: mock_function.return_value = self.w3 - with self.assertRaises(EscrowClientError) as error: + with pytest.raises(EscrowClientError, match="Invalid escrow address:"): get_reputation_oracle_address(self.w3.eth.chain_id, "invalid_address") - self.assertEqual(f"Invalid escrow address: invalid_address", str(error.exception)) def test_get_reputation_oracle_address_invalid_chain_id(self): - with self.assertRaises(ValueError) as error: + with pytest.raises(Exception, match="Can't find escrow"): get_reputation_oracle_address(1, "0x1234567890123456789012345678901234567890") - self.assertEqual(f"1 is not in available list of networks.", str(error.exception)) diff --git a/packages/examples/cvat/recording-oracle/tests/integration/chain/test_kvstore.py b/packages/examples/cvat/recording-oracle/tests/integration/chain/test_kvstore.py index 51e9a31f59..bacfea71fb 100644 --- a/packages/examples/cvat/recording-oracle/tests/integration/chain/test_kvstore.py +++ b/packages/examples/cvat/recording-oracle/tests/integration/chain/test_kvstore.py @@ -1,6 +1,8 @@ import unittest -from unittest.mock import patch +from unittest.mock import Mock, patch +import pytest +from human_protocol_sdk.escrow import EscrowClientError from human_protocol_sdk.kvstore import KVStoreClientError from web3 import HTTPProvider, Web3 from web3.middleware import construct_sign_and_send_raw_middleware @@ -25,26 +27,43 @@ def setUp(self): self.w3.eth.default_account = self.gas_payer.address def test_get_reputation_oracle_url(self): + escrow_address = create_escrow(self.w3) store_kvstore_value("webhook_url", DEFAULT_URL) - with patch("src.chain.kvstore.get_web3") as mock_function: - mock_function.return_value = self.w3 - reputation_url = get_reputation_oracle_url( - self.w3.eth.chain_id, REPUTATION_ORACLE_ADDRESS - ) + + with ( + patch("src.chain.kvstore.get_web3") as mock_get_web3, + patch("src.chain.kvstore.get_escrow") as mock_get_escrow, + patch("src.chain.kvstore.StakingClient.get_leader") as mock_leader, + ): + mock_get_web3.return_value = self.w3 + mock_escrow = Mock() + mock_escrow.reputationOracle = REPUTATION_ORACLE_ADDRESS + mock_get_escrow.return_value = mock_escrow + mock_leader.return_value = {"webhook_url": DEFAULT_URL} + + reputation_url = get_reputation_oracle_url(self.w3.eth.chain_id, escrow_address) self.assertEqual(reputation_url, DEFAULT_URL) def test_get_reputation_oracle_url_invalid_escrow(self): with patch("src.chain.kvstore.get_web3") as mock_function: mock_function.return_value = self.w3 - with self.assertRaises(KVStoreClientError) as error: + with pytest.raises(EscrowClientError, match="Invalid escrow address: "): get_reputation_oracle_url(self.w3.eth.chain_id, "invalid_address") - self.assertEqual(f"Invalid address: invalid_address", str(error.exception)) def test_get_reputation_oracle_url_invalid_address(self): create_escrow(self.w3) store_kvstore_value("webhook_url", "") - with patch("src.chain.kvstore.get_web3") as mock_function: - mock_function.return_value = self.w3 + with ( + patch("src.chain.kvstore.get_web3") as mock_get_web3, + patch("src.chain.kvstore.get_escrow") as mock_get_escrow, + patch("src.chain.kvstore.StakingClient.get_leader") as mock_leader, + ): + mock_get_web3.return_value = self.w3 + mock_escrow = Mock() + mock_escrow.reputationOracle = REPUTATION_ORACLE_ADDRESS + mock_get_escrow.return_value = mock_escrow + mock_leader.return_value = {"webhook_url": ""} + reputation_url = get_reputation_oracle_url( self.w3.eth.chain_id, REPUTATION_ORACLE_ADDRESS ) diff --git a/packages/examples/cvat/recording-oracle/tests/integration/chain/test_web3.py b/packages/examples/cvat/recording-oracle/tests/integration/chain/test_web3.py index 062efc68ca..d294882bfb 100644 --- a/packages/examples/cvat/recording-oracle/tests/integration/chain/test_web3.py +++ b/packages/examples/cvat/recording-oracle/tests/integration/chain/test_web3.py @@ -1,7 +1,7 @@ import unittest from unittest.mock import patch -from human_protocol_sdk.constants import NETWORKS, ChainId +from human_protocol_sdk.constants import ChainId from web3 import HTTPProvider, Web3 from web3.middleware import construct_sign_and_send_raw_middleware @@ -62,24 +62,22 @@ def test_get_web3_invalid_chain_id(self): ) def test_sign_message_polygon(self): - with patch("src.chain.web3.get_web3") as mock_function: - with patch( - "src.chain.web3.Config.polygon_mainnet.private_key", - DEFAULT_GAS_PAYER_PRIV, - ): - mock_function.return_value = self.w3 - signed_message = sign_message(ChainId.POLYGON.value, "message") - self.assertEqual(signed_message, SIGNATURE) + with patch("src.chain.web3.get_web3") as mock_function, patch( + "src.chain.web3.Config.polygon_mainnet.private_key", + DEFAULT_GAS_PAYER_PRIV, + ): + mock_function.return_value = self.w3 + signed_message, _ = sign_message(ChainId.POLYGON.value, "message") + self.assertEqual(signed_message, SIGNATURE) def test_sign_message_mumbai(self): - with patch("src.chain.web3.get_web3") as mock_function: - with patch( - "src.chain.web3.Config.polygon_mumbai.private_key", - DEFAULT_GAS_PAYER_PRIV, - ): - mock_function.return_value = self.w3 - signed_message = sign_message(ChainId.POLYGON_MUMBAI.value, "message") - self.assertEqual(signed_message, SIGNATURE) + with patch("src.chain.web3.get_web3") as mock_function, patch( + "src.chain.web3.Config.polygon_mumbai.private_key", + DEFAULT_GAS_PAYER_PRIV, + ): + mock_function.return_value = self.w3 + signed_message, _ = sign_message(ChainId.POLYGON_MUMBAI.value, "message") + self.assertEqual(signed_message, SIGNATURE) def test_sign_message_invalid_chain_id(self): with self.assertRaises(ValueError) as error: diff --git a/packages/examples/cvat/recording-oracle/tests/integration/cron/__init__.py b/packages/examples/cvat/recording-oracle/tests/integration/cron/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/examples/cvat/recording-oracle/tests/integration/cron/test_process_exchange_oracle_webhooks.py b/packages/examples/cvat/recording-oracle/tests/integration/cron/test_process_exchange_oracle_webhooks.py index 17f82c056f..045d9a7064 100644 --- a/packages/examples/cvat/recording-oracle/tests/integration/cron/test_process_exchange_oracle_webhooks.py +++ b/packages/examples/cvat/recording-oracle/tests/integration/cron/test_process_exchange_oracle_webhooks.py @@ -2,6 +2,7 @@ import uuid from unittest.mock import patch +from human_protocol_sdk.constants import NETWORKS, ChainId from human_protocol_sdk.storage import StorageClient from sqlalchemy.sql import select from web3 import Web3 @@ -9,12 +10,22 @@ from web3.providers.rpc import HTTPProvider from src.core.config import StorageConfig -from src.core.types import Networks, OracleWebhookStatuses, OracleWebhookTypes -from src.crons.process_exchange_oracle_webhooks import process_exchange_oracle_webhooks +from src.core.types import ( + ExchangeOracleEventType, + Networks, + OracleWebhookStatuses, + OracleWebhookTypes, +) +from src.crons.process_exchange_oracle_webhooks import ( + process_incoming_exchange_oracle_webhooks, + process_outgoing_exchange_oracle_webhooks, +) from src.db import SessionLocal from src.models.webhook import Webhook +from src.services.webhook import OracleWebhookDirectionTag +from src.utils.logging import get_function_logger -from tests.utils.constants import DEFAULT_GAS_PAYER_PRIV +from tests.utils.constants import DEFAULT_GAS_PAYER_PRIV, RECORDING_ORACLE_FEE, SIGNATURE from tests.utils.setup_escrow import create_escrow, fund_escrow @@ -33,225 +44,91 @@ def setUp(self): def tearDown(self): self.session.close() - def test_process_exchange_oracle_webhook(self): - chain_id = Networks.localhost.value - escrow_address = create_escrow(self.w3) - fund_escrow(self.w3, escrow_address) - - webhok_id = str(uuid.uuid4()) - webhook = Webhook( - id=webhok_id, - signature="signature", - s3_url="http://host.docker.internal:9000/results/intermediate-results.json", + def make_webhook(self, escrow_address): + return Webhook( + id=str(uuid.uuid4()), + direction=OracleWebhookDirectionTag.incoming.value, + signature=SIGNATURE, escrow_address=escrow_address, - chain_id=chain_id, + chain_id=Networks.localhost.value, type=OracleWebhookTypes.exchange_oracle.value, status=OracleWebhookStatuses.pending.value, + event_type=ExchangeOracleEventType.task_finished.value, ) + def test_process_exchange_oracle_webhook(self): + escrow_address = create_escrow(self.w3) + fund_escrow(self.w3, escrow_address) + + webhook = self.make_webhook(escrow_address) self.session.add(webhook) self.session.commit() - process_exchange_oracle_webhooks() + with patch("src.crons.process_exchange_oracle_webhooks.handle_exchange_oracle_event"): + process_incoming_exchange_oracle_webhooks() updated_webhook = ( - self.session.execute(select(Webhook).where(Webhook.id == webhok_id)).scalars().first() + self.session.execute(select(Webhook).where(Webhook.id == webhook.id)).scalars().first() ) - self.assertEqual(updated_webhook.status, OracleWebhookStatuses.completed.value) + self.assertEqual(updated_webhook.status, OracleWebhookStatuses.completed) self.assertEqual(updated_webhook.attempts, 1) - reputation_oracle_webhook = ( - self.session.execute( - select(Webhook).where( - Webhook.escrow_address == escrow_address, - Webhook.type == OracleWebhookTypes.reputation_oracle.value, - ) - ) - .scalars() - .first() - ) - self.assertEqual(reputation_oracle_webhook.status, OracleWebhookStatuses.pending.value) - - self.assertIsNotNone( - webhook.signature, - ) - self.assertIsInstance(webhook.signature, str) - - file_name = reputation_oracle_webhook.s3_url.split("/")[-1] - file = StorageClient.download_file_from_url( - f"http://{StorageConfig.endpoint_url}/{StorageConfig.results_bucket_name}/{file_name}" - ).decode() - - self.assertIsNotNone(file) - self.assertIsInstance(file, str) - def test_process_recording_oracle_webhooks_invalid_escrow_address(self): - chain_id = Networks.localhost.value escrow_address = "invalid_address" - - webhok_id = str(uuid.uuid4()) - webhook = Webhook( - id=webhok_id, - signature="signature", - s3_url="http://host.docker.internal:9000/results/intermediate-results.json", - escrow_address=escrow_address, - chain_id=chain_id, - type=OracleWebhookTypes.exchange_oracle.value, - status=OracleWebhookStatuses.pending.value, - ) - + webhook = self.make_webhook(escrow_address) self.session.add(webhook) self.session.commit() - with self.assertLogs(level="ERROR") as cm: - process_exchange_oracle_webhooks() + with patch( + "src.crons.process_exchange_oracle_webhooks.handle_exchange_oracle_event" + ) as mock_handler: + mock_handler.side_effect = Exception(f"Can't find escrow {escrow_address}") + process_incoming_exchange_oracle_webhooks() + mock_handler.assert_called_once() updated_webhook = ( - self.session.execute(select(Webhook).where(Webhook.id == webhok_id)).scalars().first() + self.session.execute(select(Webhook).where(Webhook.id == webhook.id)).scalars().first() ) self.assertEqual(updated_webhook.status, OracleWebhookStatuses.pending.value) self.assertEqual(updated_webhook.attempts, 1) - self.assertEqual( - cm.output, - [ - f"ERROR:app:Webhook: {webhok_id} failed during execution. Error Invalid escrow address: invalid_address" - ], - ) def test_process_recording_oracle_webhooks_invalid_escrow_balance(self): - chain_id = Networks.localhost.value escrow_address = create_escrow(self.w3) - webhok_id = str(uuid.uuid4()) - webhook = Webhook( - id=webhok_id, - signature="signature", - s3_url="http://host.docker.internal:9000/results/intermediate-results.json", - escrow_address=escrow_address, - chain_id=chain_id, - type=OracleWebhookTypes.exchange_oracle.value, - status=OracleWebhookStatuses.pending.value, - ) + webhook = self.make_webhook(escrow_address) self.session.add(webhook) self.session.commit() - - with self.assertLogs(level="ERROR") as cm: - process_exchange_oracle_webhooks() + with patch( + "src.crons.process_exchange_oracle_webhooks.handle_exchange_oracle_event" + ) as mock_handler: + mock_handler.side_effect = ValueError("Escrow doesn't have funds") + process_incoming_exchange_oracle_webhooks() updated_webhook = ( - self.session.execute(select(Webhook).where(Webhook.id == webhok_id)).scalars().first() + self.session.execute(select(Webhook).where(Webhook.id == webhook.id)).scalars().first() ) self.assertEqual(updated_webhook.status, OracleWebhookStatuses.pending.value) self.assertEqual(updated_webhook.attempts, 1) - self.assertEqual( - cm.output, - [ - f"ERROR:app:Webhook: {webhok_id} failed during execution. Error Escrow doesn't have funds" - ], - ) @patch("src.chain.escrow.EscrowClient.get_manifest_url") def test_process_job_launcher_webhooks_invalid_manifest_url(self, mock_manifest_url): mock_manifest_url.return_value = "invalid_url" - chain_id = Networks.localhost.value - escrow_address = create_escrow(self.w3) - fund_escrow(self.w3, escrow_address) - - webhok_id = str(uuid.uuid4()) - webhook = Webhook( - id=webhok_id, - signature="signature", - s3_url="http://host.docker.internal:9000/results/intermediate-results.json", - escrow_address=escrow_address, - chain_id=chain_id, - type=OracleWebhookTypes.exchange_oracle.value, - status=OracleWebhookStatuses.pending.value, - ) - - self.session.add(webhook) - self.session.commit() - - with self.assertLogs(level="ERROR") as cm: - process_exchange_oracle_webhooks() - - updated_webhook = ( - self.session.execute(select(Webhook).where(Webhook.id == webhok_id)).scalars().first() - ) - - self.assertEqual(updated_webhook.status, OracleWebhookStatuses.pending.value) - self.assertEqual(updated_webhook.attempts, 1) - self.assertTrue( - f"ERROR:app:Webhook: {webhok_id} failed during execution. Error Invalid URL: invalid_url" - in cm.output[0], - ) - - def test_process_job_launcher_webhooks_invalid_intermediate_results_url(self): - chain_id = Networks.localhost.value escrow_address = create_escrow(self.w3) fund_escrow(self.w3, escrow_address) - webhok_id = str(uuid.uuid4()) - webhook = Webhook( - id=webhok_id, - signature="signature", - s3_url="invalid_url", - escrow_address=escrow_address, - chain_id=chain_id, - type=OracleWebhookTypes.exchange_oracle.value, - status=OracleWebhookStatuses.pending.value, - ) + webhook = self.make_webhook(escrow_address) self.session.add(webhook) self.session.commit() - with self.assertLogs(level="ERROR") as cm: - process_exchange_oracle_webhooks() + process_incoming_exchange_oracle_webhooks() updated_webhook = ( - self.session.execute(select(Webhook).where(Webhook.id == webhok_id)).scalars().first() + self.session.execute(select(Webhook).where(Webhook.id == webhook.id)).scalars().first() ) self.assertEqual(updated_webhook.status, OracleWebhookStatuses.pending.value) self.assertEqual(updated_webhook.attempts, 1) - self.assertTrue( - f"ERROR:app:Webhook: {webhok_id} failed during execution. Error Invalid URL: invalid_url" - in cm.output[0], - ) - - @patch("src.core.config.StorageConfig.secure") - def test_process_job_launcher_webhooks_error_uploading_files(self, mock_storage_config): - mock_storage_config.return_value = True - chain_id = Networks.localhost.value - escrow_address = create_escrow(self.w3) - fund_escrow(self.w3, escrow_address) - - webhok_id = str(uuid.uuid4()) - webhook = Webhook( - id=webhok_id, - signature="signature", - s3_url="http://host.docker.internal:9000/results/intermediate-results.json", - escrow_address=escrow_address, - chain_id=chain_id, - type=OracleWebhookTypes.exchange_oracle.value, - status=OracleWebhookStatuses.pending.value, - ) - - self.session.add(webhook) - self.session.commit() - - with self.assertLogs(level="ERROR") as cm: - process_exchange_oracle_webhooks() - - updated_webhook = ( - self.session.execute(select(Webhook).where(Webhook.id == webhok_id)).scalars().first() - ) - - self.assertEqual(updated_webhook.status, OracleWebhookStatuses.pending.value) - self.assertEqual(updated_webhook.attempts, 1) - self.assertNotEqual( - cm.output, - [], - ) diff --git a/packages/examples/cvat/recording-oracle/tests/integration/cron/test_process_reputation_oracle_webhooks.py b/packages/examples/cvat/recording-oracle/tests/integration/cron/test_process_reputation_oracle_webhooks.py index 6ba17941a0..06b0b86bf7 100644 --- a/packages/examples/cvat/recording-oracle/tests/integration/cron/test_process_reputation_oracle_webhooks.py +++ b/packages/examples/cvat/recording-oracle/tests/integration/cron/test_process_reputation_oracle_webhooks.py @@ -7,12 +7,18 @@ from web3.middleware import construct_sign_and_send_raw_middleware from web3.providers.rpc import HTTPProvider -from src.core.types import Networks, OracleWebhookStatuses, OracleWebhookTypes -from src.crons.process_reputation_oracle_webhooks import process_reputation_oracle_webhooks +from src.core.types import ( + Networks, + OracleWebhookStatuses, + OracleWebhookTypes, + RecordingOracleEventType, +) +from src.crons.process_reputation_oracle_webhooks import process_outgoing_reputation_oracle_webhooks from src.db import SessionLocal from src.models.webhook import Webhook +from src.services.webhook import OracleWebhookDirectionTag -from tests.utils.constants import DEFAULT_GAS_PAYER_PRIV +from tests.utils.constants import DEFAULT_GAS_PAYER_PRIV, SIGNATURE from tests.utils.setup_escrow import create_escrow from tests.utils.setup_kvstore import store_kvstore_value @@ -32,154 +38,98 @@ def setUp(self): def tearDown(self): self.session.close() - @patch("src.crons.process_reputation_oracle_webhooks.httpx.Client.post") - def test_process_reputation_oracle_webhooks(self, mock_httpx_post): - mock_response = MagicMock() - mock_response.raise_for_status.return_value = None - mock_httpx_post.return_value = mock_response - expected_url = "expected_url" - - chain_id = Networks.localhost.value - escrow_address = create_escrow(self.w3) - store_kvstore_value("webhook_url", expected_url) - - webhok_id = str(uuid.uuid4()) - webhook = Webhook( - id=webhok_id, - signature="signature", + def get_webhook(self, escrow_address, chain_id, event_data): + return Webhook( + id=str(uuid.uuid4()), + direction=OracleWebhookDirectionTag.outgoing.value, + signature=SIGNATURE, escrow_address=escrow_address, chain_id=chain_id, - s3_url="s3_url", type=OracleWebhookTypes.reputation_oracle.value, status=OracleWebhookStatuses.pending.value, + event_type=RecordingOracleEventType.task_completed, + event_data=event_data, ) - self.session.add(webhook) - self.session.commit() - - process_reputation_oracle_webhooks() - - updated_webhook = ( - self.session.execute(select(Webhook).where(Webhook.id == webhok_id)).scalars().first() - ) - - self.assertEqual(updated_webhook.status, OracleWebhookStatuses.completed.value) - self.assertEqual(updated_webhook.attempts, 1) - mock_httpx_post.assert_called_once_with( - expected_url, - headers={"human-signature": "signature"}, - json={ - "escrow_address": escrow_address, - "chain_id": chain_id, - }, - ) + def test_process_reputation_oracle_webhooks(self): + expected_url = "expected_url" + with patch( + "src.crons.process_reputation_oracle_webhooks.httpx.Client.post" + ) as mock_httpx, patch( + "src.crons.process_reputation_oracle_webhooks.get_reputation_oracle_url" + ) as mock_get_repo_url, patch( + "src.crons.process_reputation_oracle_webhooks.prepare_signed_message" + ) as mock_signature: + mock_response = MagicMock() + mock_response.raise_for_status.return_value = None + mock_httpx.return_value = mock_response + mock_get_repo_url.return_value = expected_url + + chain_id = Networks.localhost.value + escrow_address = create_escrow(self.w3) + store_kvstore_value("webhook_url", expected_url) + event_data = dict() + mock_signature.return_value = (None, SIGNATURE) + + webhook = self.get_webhook(escrow_address, chain_id, event_data) + self.session.add(webhook) + self.session.commit() + + process_outgoing_reputation_oracle_webhooks() + + updated_webhook = ( + self.session.execute(select(Webhook).where(Webhook.id == webhook.id)) + .scalars() + .first() + ) + + mock_signature.assert_called_once() + mock_get_repo_url.assert_called_once_with(webhook.chain_id, webhook.escrow_address) + mock_httpx.assert_called_once_with( + expected_url, + headers={"human-signature": SIGNATURE}, + json={ + "escrowAddress": escrow_address, + "chainId": chain_id, + "eventType": RecordingOracleEventType.task_completed.value, + }, + ) + self.assertEqual(updated_webhook.status, OracleWebhookStatuses.completed.value) + self.assertEqual(updated_webhook.attempts, 1) def test_process_reputation_oracle_webhooks_invalid_escrow_address(self): chain_id = Networks.localhost.value escrow_address = "invalid_address" - - webhok_id = str(uuid.uuid4()) - webhook = Webhook( - id=webhok_id, - signature="signature", - escrow_address=escrow_address, - chain_id=chain_id, - s3_url="s3_url", - type=OracleWebhookTypes.reputation_oracle.value, - status=OracleWebhookStatuses.pending.value, - ) - - self.session.add(webhook) - self.session.commit() - - with self.assertLogs(level="ERROR") as cm: - process_reputation_oracle_webhooks() - - updated_webhook = ( - self.session.execute(select(Webhook).where(Webhook.id == webhok_id)).scalars().first() - ) - - self.assertEqual(updated_webhook.status, OracleWebhookStatuses.pending.value) - self.assertEqual(updated_webhook.attempts, 1) - self.assertEqual( - cm.output, - [ - f"ERROR:app:[cron][webhook][process_reputation_oracle_webhooks] Webhook: {webhok_id} failed during execution. Error Invalid escrow address: invalid_address" - ], - ) - - def test_process_reputation_oracle_webhooks_invalid_recording_oracle_url(self): - chain_id = Networks.localhost.value - escrow_address = create_escrow(self.w3) - - webhok_id = str(uuid.uuid4()) - webhook = Webhook( - id=webhok_id, - signature="signature", - escrow_address=escrow_address, - chain_id=chain_id, - s3_url="s3_url", - type=OracleWebhookTypes.reputation_oracle.value, - status=OracleWebhookStatuses.pending.value, - ) + event_data = {} + webhook = self.get_webhook(escrow_address, chain_id, event_data) self.session.add(webhook) self.session.commit() - with self.assertLogs(level="ERROR") as cm: - process_reputation_oracle_webhooks() + process_outgoing_reputation_oracle_webhooks() updated_webhook = ( - self.session.execute(select(Webhook).where(Webhook.id == webhok_id)).scalars().first() + self.session.execute(select(Webhook).where(Webhook.id == webhook.id)).scalars().first() ) self.assertEqual(updated_webhook.status, OracleWebhookStatuses.pending.value) self.assertEqual(updated_webhook.attempts, 1) - self.assertEqual( - cm.output, - [ - f"ERROR:app:[cron][webhook][process_reputation_oracle_webhooks] Webhook: {webhok_id} failed during execution. Error Request URL is missing an 'http://' or 'https://' protocol." - ], - ) - - @patch("src.crons.process_reputation_oracle_webhooks.httpx.Client.post") - def test_process_reputation_oracle_webhooks_invalid_request(self, mock_httpx_post): - mock_response = MagicMock() - mock_response.status_code = 404 - mock_response.raise_for_status.side_effect = Exception("The requested URL was not found.") - mock_httpx_post.return_value = mock_response - expected_url = "expected_url" - - chain_id = Networks.localhost.value - escrow_address = create_escrow(self.w3) - store_kvstore_value("webhook_url", expected_url) - - webhok_id = str(uuid.uuid4()) - webhook = Webhook( - id=webhok_id, - signature="signature", - escrow_address=escrow_address, - chain_id=chain_id, - s3_url="s3_url", - type=OracleWebhookTypes.reputation_oracle.value, - status=OracleWebhookStatuses.pending.value, - ) - - self.session.add(webhook) - self.session.commit() - with self.assertLogs(level="ERROR") as cm: - process_reputation_oracle_webhooks() - - updated_webhook = ( - self.session.execute(select(Webhook).where(Webhook.id == webhok_id)).scalars().first() - ) - - self.assertEqual(updated_webhook.status, OracleWebhookStatuses.pending.value) - self.assertEqual(updated_webhook.attempts, 1) - self.assertEqual( - cm.output, - [ - f"ERROR:app:[cron][webhook][process_reputation_oracle_webhooks] Webhook: {webhok_id} failed during execution. Error The requested URL was not found." - ], - ) + def test_process_reputation_oracle_webhooks_invalid_reputation_oracle_url(self): + with patch( + "src.crons.process_reputation_oracle_webhooks.get_reputation_oracle_url" + ) as mock_get_repo_url: + mock_get_repo_url.return_value = "https://not.a.real/url/existing.somewhere" + + webhook = self.get_webhook(create_escrow(self.w3), Networks.localhost.value, {}) + self.session.add(webhook) + self.session.commit() + process_outgoing_reputation_oracle_webhooks() + + updated_webhook = ( + self.session.execute(select(Webhook).where(Webhook.id == webhook.id)) + .scalars() + .first() + ) + self.assertEqual(updated_webhook.status, OracleWebhookStatuses.pending.value) + self.assertEqual(updated_webhook.attempts, 1) diff --git a/packages/examples/cvat/recording-oracle/tests/integration/endpoints/__init__.py b/packages/examples/cvat/recording-oracle/tests/integration/endpoints/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/examples/cvat/recording-oracle/tests/integration/endpoints/test_webhook.py b/packages/examples/cvat/recording-oracle/tests/integration/endpoints/test_webhook.py new file mode 100644 index 0000000000..f765dc2140 --- /dev/null +++ b/packages/examples/cvat/recording-oracle/tests/integration/endpoints/test_webhook.py @@ -0,0 +1,56 @@ +import random +import unittest +from unittest.mock import MagicMock, patch + +from fastapi.testclient import TestClient + +from src.chain.web3 import sign_message +from src.core.types import ExchangeOracleEventType, Networks +from src.db import SessionLocal +from src.endpoints.webhook import router +from src.models.webhook import Webhook + +from tests.utils.constants import DEFAULT_GAS_PAYER + + +class ServiceIntegrationTest(unittest.TestCase): + def setUp(self): + random.seed(42) + self.session = SessionLocal() + self.client = TestClient(router) + self.mock_escrow = MagicMock() + self.mock_escrow.exchangeOracle = DEFAULT_GAS_PAYER + self.mock_escrow.status = "Pending" + self.mock_escrow.balance = 1 + + def tearDown(self): + self.session.close() + + @patch("src.chain.escrow.get_escrow") + def test_receive_oracle_webhook_client(self, mock_get_escrow): + mock_get_escrow.return_value = self.mock_escrow + + escrow_address = "0x" + "".join([str(random.randint(0, 9)) for _ in range(40)]) + chain_id = Networks.localhost + event_type = ExchangeOracleEventType.task_finished.value + + message = { + "escrow_address": escrow_address, + "chain_id": chain_id, + "event_type": event_type, + "event_data": {}, + } + + signed_message, _ = sign_message(chain_id, message) + + response = self.client.post( + "/oracle-webhook", json=message, headers={"human-signature": signed_message} + ) + + assert response.status_code == 200 + response_body = response.json() + webhook = self.session.query(Webhook).where(Webhook.id == response_body["id"]).one() + assert webhook is not None + assert webhook.escrow_address == escrow_address + assert webhook.chain_id == chain_id + assert webhook.event_type == event_type diff --git a/packages/examples/cvat/recording-oracle/tests/integration/services/__init__.py b/packages/examples/cvat/recording-oracle/tests/integration/services/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/examples/cvat/recording-oracle/tests/integration/services/cloud/__init__.py b/packages/examples/cvat/recording-oracle/tests/integration/services/cloud/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/examples/cvat/recording-oracle/tests/integration/services/cloud/test_client_service.py b/packages/examples/cvat/recording-oracle/tests/integration/services/cloud/test_client_service.py new file mode 100644 index 0000000000..a71ec3d0d4 --- /dev/null +++ b/packages/examples/cvat/recording-oracle/tests/integration/services/cloud/test_client_service.py @@ -0,0 +1,73 @@ +import unittest + +import boto3 +import pytest +from botocore.exceptions import ClientError, EndpointConnectionError + +from src.services.cloud import S3Client + + +class ServiceIntegrationTest(unittest.TestCase): + def setUp(self): + self.url = "http://minio:9000" + self.bucket_name = "test-bucket" + self.access_key = "dev" + self.secret = "devdevdev" + self.client = boto3.resource( + "s3", + aws_access_key_id=self.access_key, + aws_secret_access_key=self.secret, + endpoint_url=self.url, + ).meta.client + self.client.create_bucket(Bucket=self.bucket_name) + + def tearDown(self): + self.client.delete_bucket(Bucket=self.bucket_name) + + def test_file_operations(self): + client = S3Client(self.url, access_key=self.access_key, secret_key=self.secret) + + assert len(client.list_files(self.bucket_name)) == 0 + + file_name = "test_file" + data = "this is a test".encode("utf-8") + + assert not client.file_exists(self.bucket_name, file_name) + client.create_file(self.bucket_name, file_name, data) + assert client.file_exists(self.bucket_name, file_name) + assert len(client.list_files(self.bucket_name)) == 1 + + file_content = client.download_fileobj(bucket=self.bucket_name, key=file_name) + assert file_content == data + + client.remove_file(self.bucket_name, file_name) + assert not client.file_exists(self.bucket_name, file_name) + + def test_degenerate_file_operations(self): + client = S3Client(self.url, access_key=self.access_key, secret_key=self.secret) + invalid_bucket = "non-existent-bucket" + invalid_file = "non-existent-file" + + with pytest.raises(ClientError): + client.download_fileobj(bucket=invalid_bucket, key=invalid_file) + + with pytest.raises(ClientError): + client.download_fileobj(bucket=self.bucket_name, key=invalid_file) + + with pytest.raises(ClientError): + client.create_file(bucket=invalid_bucket, filename=invalid_file) + + with pytest.raises(ClientError): + client.list_files(bucket=invalid_bucket) + + client.remove_file(bucket=self.bucket_name, filename=invalid_file) + + def test_degenerate_client(self): + with pytest.raises(EndpointConnectionError): + invalid_client = S3Client( + "http://not.an.url:1234", access_key=self.access_key, secret_key=self.secret + ) + invalid_client.create_file(self.bucket_name, "test.txt") + + with pytest.raises(ValueError): + S3Client("nonsense-stuff") diff --git a/packages/examples/cvat/recording-oracle/tests/integration/services/test_validation_service.py b/packages/examples/cvat/recording-oracle/tests/integration/services/test_validation_service.py new file mode 100644 index 0000000000..c56f478721 --- /dev/null +++ b/packages/examples/cvat/recording-oracle/tests/integration/services/test_validation_service.py @@ -0,0 +1,78 @@ +import random +import unittest +import uuid + +from src.core.types import Networks +from src.db import SessionLocal +from src.services.validation import ( + create_job, + create_task, + create_validation_result, + get_job_by_cvat_id, + get_job_by_id, + get_task_by_escrow_address, + get_task_by_id, + get_task_validation_results, + get_validation_result_by_assignment_id, +) + + +class ServiceIntegrationTest(unittest.TestCase): + def setUp(self): + random.seed(42) + self.session = SessionLocal() + self.escrow_address = "0x" + "".join([str(random.randint(0, 9)) for _ in range(40)]) + self.chain_id = Networks.localhost + self.cvat_id = 0 + self.annotator_wallet_address = "0x" + "".join( + [str(random.randint(0, 9)) for _ in range(40)] + ) + self.annotation_quality = 0.9 + self.assigment_id = str(uuid.uuid4()) + + def tearDown(self): + self.session.close() + + def test_create_and_get_task(self): + task_id = create_task( + session=self.session, escrow_address=self.escrow_address, chain_id=self.chain_id + ) + + task = get_task_by_id(self.session, task_id) + assert task.chain_id == self.chain_id + assert task.escrow_address == self.escrow_address + + other_task = get_task_by_escrow_address(self.session, self.escrow_address) + assert task.id == other_task.id + + def test_create_and_get_job(self): + task_id = create_task( + session=self.session, escrow_address=self.escrow_address, chain_id=self.chain_id + ) + job_id = create_job(self.session, self.cvat_id, task_id) + + job = get_job_by_cvat_id(self.session, self.cvat_id) + assert job.task_id == task_id + + other_job = get_job_by_id(self.session, job_id) + assert job == other_job + + def test_create_and_get_validation_result(self): + task_id = create_task( + session=self.session, escrow_address=self.escrow_address, chain_id=self.chain_id + ) + job_id = create_job(self.session, self.cvat_id, task_id) + vr_id = create_validation_result( + self.session, + job_id, + self.annotator_wallet_address, + self.annotation_quality, + self.assigment_id, + ) + + vr = get_validation_result_by_assignment_id(self.session, self.assigment_id) + assert vr.id == vr_id + + vrs = get_task_validation_results(self.session, task_id) + assert len(vrs) == 1 + assert vrs[0] == vr diff --git a/packages/examples/cvat/recording-oracle/tests/integration/services/test_webhook_service.py b/packages/examples/cvat/recording-oracle/tests/integration/services/test_webhook_service.py index 8af4131769..124c1ec953 100644 --- a/packages/examples/cvat/recording-oracle/tests/integration/services/test_webhook_service.py +++ b/packages/examples/cvat/recording-oracle/tests/integration/services/test_webhook_service.py @@ -1,3 +1,4 @@ +import random import unittest import uuid @@ -6,145 +7,83 @@ from src.core.types import Networks, OracleWebhookStatuses, OracleWebhookTypes from src.db import SessionLocal from src.models.webhook import Webhook -from src.services import webhooks as webhook_service +from src.services.webhook import OracleWebhookDirectionTag, inbox class ServiceIntegrationTest(unittest.TestCase): def setUp(self): self.session = SessionLocal() + self.webhook_kwargs = dict( + session=self.session, + escrow_address="0x1234567890123456789012345678901234567890", + chain_id=Networks.polygon_mainnet.value, + type=OracleWebhookTypes.exchange_oracle, + signature="signature", + event_type="task_finished", + ) + random.seed(42) def tearDown(self): self.session.close() - def test_create_webhook(self): - escrow_address = "0x1234567890123456789012345678901234567890" - chain_id = Networks.polygon_mainnet.value - signature = "signature" - s3_url = "s3_url" - - webhook_id = webhook_service.create_webhook( - self.session, - escrow_address=escrow_address, - chain_id=chain_id, - s3_url=s3_url, - type=OracleWebhookTypes.exchange_oracle.value, - signature=signature, + def dummy_webhook(self, oracle_webhook_type: OracleWebhookTypes, status: OracleWebhookStatuses): + address = "0x" + "".join([str(random.randint(0, 9)) for _ in range(40)]) + return Webhook( + id=str(uuid.uuid4()), + direction=OracleWebhookDirectionTag.incoming.value, + signature=f"signature-{uuid.uuid4()}", + escrow_address=address, + chain_id=Networks.polygon_mainnet.value, + type=oracle_webhook_type.value, + status=status.value, + event_type="task_finished", ) + def test_create_webhook(self): + webhook_id = inbox.create_webhook(**self.webhook_kwargs) + webhook = self.session.query(Webhook).filter_by(id=webhook_id).first() - self.assertEqual(webhook.escrow_address, escrow_address) - self.assertEqual(webhook.chain_id, chain_id) - self.assertEqual(webhook.s3_url, s3_url) + self.assertEqual(webhook.escrow_address, self.webhook_kwargs["escrow_address"]) + self.assertEqual(webhook.chain_id, self.webhook_kwargs["chain_id"]) self.assertEqual(webhook.attempts, 0) - self.assertEqual(webhook.signature, signature) - self.assertEqual(webhook.type, OracleWebhookTypes.exchange_oracle.value) - self.assertEqual(webhook.status, OracleWebhookStatuses.pending.value) + self.assertEqual(webhook.signature, self.webhook_kwargs["signature"]) + self.assertEqual(webhook.type, OracleWebhookTypes.exchange_oracle) + self.assertEqual(webhook.status, OracleWebhookStatuses.pending) + # TODO: check intended fields and verify those + + def _test_none_webhook_argument(self, argument_name, error_type): + kwargs = dict(**self.webhook_kwargs) + kwargs[argument_name] = None + with self.assertRaises(error_type): + inbox.create_webhook(**kwargs) + self.session.commit() def test_create_webhook_none_escrow_address(self): - chain_id = Networks.polygon_mainnet.value - signature = "signature" - s3_url = "s3_url" - webhook_service.create_webhook( - self.session, - escrow_address=None, - chain_id=chain_id, - s3_url=s3_url, - type=OracleWebhookTypes.exchange_oracle.value, - signature=signature, - ) - with self.assertRaises(IntegrityError): - self.session.commit() + self._test_none_webhook_argument("escrow_address", IntegrityError) def test_create_webhook_none_chain_id(self): - escrow_address = "0x1234567890123456789012345678901234567890" - signature = "signature" - s3_url = "s3_url" - webhook_service.create_webhook( - self.session, - escrow_address=escrow_address, - chain_id=None, - s3_url=s3_url, - type=OracleWebhookTypes.exchange_oracle.value, - signature=signature, - ) - with self.assertRaises(IntegrityError): - self.session.commit() - - def test_create_webhook_none_s3_url(self): - escrow_address = "0x1234567890123456789012345678901234567890" - chain_id = Networks.polygon_mainnet.value - signature = "signature" - webhook_service.create_webhook( - self.session, - escrow_address=escrow_address, - chain_id=chain_id, - s3_url=None, - type=OracleWebhookTypes.exchange_oracle.value, - signature=signature, - ) - with self.assertRaises(IntegrityError): - self.session.commit() + self._test_none_webhook_argument("chain_id", IntegrityError) def test_create_webhook_none_signature(self): - escrow_address = "0x1234567890123456789012345678901234567890" - chain_id = Networks.polygon_mainnet.value - s3_url = "s3_url" - - webhook_service.create_webhook( - self.session, - escrow_address=escrow_address, - chain_id=chain_id, - s3_url=s3_url, - type=OracleWebhookTypes.exchange_oracle.value, - signature=None, - ) - with self.assertRaises(IntegrityError): - self.session.commit() + self._test_none_webhook_argument("signature", ValueError) def test_get_pending_webhooks(self): - chain_id = Networks.polygon_mainnet.value - - webhook1_id = str(uuid.uuid4()) - webhook1 = Webhook( - id=webhook1_id, - signature="signature1", - escrow_address="0x1234567890123456789012345678901234567890", - chain_id=chain_id, - s3_url="s3_url", - type=OracleWebhookTypes.exchange_oracle.value, - status=OracleWebhookStatuses.pending.value, + webhook1 = self.dummy_webhook( + oracle_webhook_type=OracleWebhookTypes.exchange_oracle, + status=OracleWebhookStatuses.pending, ) - webhook2_id = str(uuid.uuid4()) - webhook2 = Webhook( - id=webhook2_id, - signature="signature2", - escrow_address="0x1234567890123456789012345678901234567891", - chain_id=chain_id, - s3_url="s3_url", - type=OracleWebhookTypes.exchange_oracle.value, - status=OracleWebhookStatuses.pending.value, + webhook2 = self.dummy_webhook( + oracle_webhook_type=OracleWebhookTypes.exchange_oracle, + status=OracleWebhookStatuses.pending, ) - webhook3_id = str(uuid.uuid4()) - webhook3 = Webhook( - id=webhook3_id, - signature="signature3", - escrow_address="0x1234567890123456789012345678901234567892", - chain_id=chain_id, - s3_url="s3_url", - type=OracleWebhookTypes.exchange_oracle.value, - status=OracleWebhookStatuses.completed.value, + webhook3 = self.dummy_webhook( + oracle_webhook_type=OracleWebhookTypes.exchange_oracle, + status=OracleWebhookStatuses.completed, ) - - webhook4_id = str(uuid.uuid4()) - webhook4 = Webhook( - id=webhook4_id, - signature="signature4", - escrow_address="0x1234567890123456789012345678901234567892", - chain_id=chain_id, - s3_url="s3_url", - type=OracleWebhookTypes.reputation_oracle.value, - status=OracleWebhookStatuses.pending.value, + webhook4 = self.dummy_webhook( + oracle_webhook_type=OracleWebhookTypes.reputation_oracle, + status=OracleWebhookStatuses.pending, ) self.session.add(webhook1) @@ -153,129 +92,58 @@ def test_get_pending_webhooks(self): self.session.add(webhook4) self.session.commit() - pending_webhooks = webhook_service.get_pending_webhooks( - self.session, OracleWebhookTypes.exchange_oracle.value, 10 + pending_webhooks = inbox.get_pending_webhooks( + self.session, OracleWebhookTypes.exchange_oracle ) self.assertEqual(len(pending_webhooks), 2) - self.assertEqual(pending_webhooks[0].id, webhook1_id) - self.assertEqual(pending_webhooks[1].id, webhook2_id) + self.assertEqual(pending_webhooks[0].id, webhook1.id) + self.assertEqual(pending_webhooks[1].id, webhook2.id) - pending_webhooks = webhook_service.get_pending_webhooks( - self.session, OracleWebhookTypes.reputation_oracle.value, 10 + pending_webhooks = inbox.get_pending_webhooks( + self.session, OracleWebhookTypes.reputation_oracle ) self.assertEqual(len(pending_webhooks), 1) - self.assertEqual(pending_webhooks[0].id, webhook4_id) + self.assertEqual(pending_webhooks[0].id, webhook4.id) def test_update_webhook_status(self): - escrow_address = "0x1234567890123456789012345678901234567890" - chain_id = Networks.polygon_mainnet.value - signature = "signature" - s3_url = "s3_url" - - webhook_id = webhook_service.create_webhook( - self.session, - escrow_address=escrow_address, - chain_id=chain_id, - s3_url=s3_url, - type=OracleWebhookTypes.exchange_oracle.value, - signature=signature, - ) + webhook_id = inbox.create_webhook(**self.webhook_kwargs) + webhook = self.session.query(Webhook).filter_by(id=webhook_id).first() + self.assertEqual(webhook.status, OracleWebhookStatuses.pending) - webhook_service.update_webhook_status( - self.session, webhook_id, OracleWebhookStatuses.completed.value - ) + inbox.update_webhook_status(self.session, webhook_id, OracleWebhookStatuses.completed) webhook = self.session.query(Webhook).filter_by(id=webhook_id).first() - - self.assertEqual(webhook.escrow_address, escrow_address) - self.assertEqual(webhook.chain_id, chain_id) - self.assertEqual(webhook.s3_url, s3_url) - self.assertEqual(webhook.attempts, 0) - self.assertEqual(webhook.signature, signature) - self.assertEqual(webhook.type, OracleWebhookTypes.exchange_oracle.value) - self.assertEqual(webhook.status, OracleWebhookStatuses.completed.value) + self.assertEqual(webhook.status, OracleWebhookStatuses.completed) def test_update_webhook_invalid_status(self): - escrow_address = "0x1234567890123456789012345678901234567890" - chain_id = Networks.polygon_mainnet.value - signature = "signature" - s3_url = "s3_url" - - webhook_id = webhook_service.create_webhook( - self.session, - escrow_address=escrow_address, - chain_id=chain_id, - s3_url=s3_url, - type=OracleWebhookTypes.exchange_oracle.value, - signature=signature, - ) - - with self.assertRaises(ValueError): - webhook_service.update_webhook_status(self.session, webhook_id, "Invalid status") + webhook_id = inbox.create_webhook(**self.webhook_kwargs) + with self.assertRaises(AttributeError): + inbox.update_webhook_status(self.session, webhook_id, "Invalid status") def test_handle_webhook_success(self): - escrow_address = "0x1234567890123456789012345678901234567890" - chain_id = Networks.polygon_mainnet.value - signature = "signature" - s3_url = "s3_url" + webhook_id = inbox.create_webhook(**self.webhook_kwargs) - webhook_id = webhook_service.create_webhook( - self.session, - escrow_address=escrow_address, - chain_id=chain_id, - s3_url=s3_url, - type=OracleWebhookTypes.exchange_oracle.value, - signature=signature, - ) - - webhook_service.handle_webhook_success(self.session, webhook_id) + inbox.handle_webhook_success(self.session, webhook_id) webhook = self.session.query(Webhook).filter_by(id=webhook_id).first() - self.assertEqual(webhook.escrow_address, escrow_address) - self.assertEqual(webhook.chain_id, chain_id) - self.assertEqual(webhook.s3_url, s3_url) self.assertEqual(webhook.attempts, 1) - self.assertEqual(webhook.signature, signature) - self.assertEqual(webhook.type, OracleWebhookTypes.exchange_oracle.value) self.assertEqual(webhook.status, OracleWebhookStatuses.completed.value) def test_handle_webhook_fail(self): - escrow_address = "0x1234567890123456789012345678901234567890" - chain_id = Networks.polygon_mainnet.value - signature = "signature" - s3_url = "s3_url" - - webhook_id = webhook_service.create_webhook( - self.session, - escrow_address=escrow_address, - chain_id=chain_id, - s3_url=s3_url, - type=OracleWebhookTypes.exchange_oracle.value, - signature=signature, - ) - - webhook_service.handle_webhook_fail(self.session, webhook_id) - + webhook_id = inbox.create_webhook(**self.webhook_kwargs) + inbox.handle_webhook_fail(self.session, webhook_id) webhook = self.session.query(Webhook).filter_by(id=webhook_id).first() - self.assertEqual(webhook.escrow_address, escrow_address) - self.assertEqual(webhook.chain_id, chain_id) - self.assertEqual(webhook.s3_url, s3_url) self.assertEqual(webhook.attempts, 1) - self.assertEqual(webhook.signature, signature) self.assertEqual(webhook.type, OracleWebhookTypes.exchange_oracle.value) self.assertEqual(webhook.status, OracleWebhookStatuses.pending.value) + # assumes Config.webhook_max_retries == 5 for i in range(4): - webhook_service.handle_webhook_fail(self.session, webhook_id) + inbox.handle_webhook_fail(self.session, webhook_id) webhook = self.session.query(Webhook).filter_by(id=webhook_id).first() - self.assertEqual(webhook.escrow_address, escrow_address) - self.assertEqual(webhook.chain_id, chain_id) - self.assertEqual(webhook.s3_url, s3_url) self.assertEqual(webhook.attempts, 5) - self.assertEqual(webhook.signature, signature) - self.assertEqual(webhook.type, OracleWebhookTypes.exchange_oracle.value) self.assertEqual(webhook.status, OracleWebhookStatuses.failed.value) diff --git a/packages/examples/cvat/recording-oracle/tests/unit/agreement/conftest.py b/packages/examples/cvat/recording-oracle/tests/unit/agreement/conftest.py deleted file mode 100644 index 8f948c1fd0..0000000000 --- a/packages/examples/cvat/recording-oracle/tests/unit/agreement/conftest.py +++ /dev/null @@ -1,85 +0,0 @@ -import numpy as np -import pytest -from hypothesis.strategies import builds, integers, just, tuples - - -@pytest.fixture -def bin_2r_cm() -> np.ndarray: - """ - Returns a confusion matrix (rater_a x rater_b) for a binary classification - problem with two raters. - """ - return np.asarray([[2, 2], [1, 5]]) - - -@pytest.fixture -def bin_2r_im() -> np.ndarray: - """ - Returns an incidence matrix (item x class) for a binary classification - problem with two raters. - """ - return np.asarray( - [[2, 0], [2, 0], [1, 1], [1, 1], [1, 1], [0, 2], [0, 2], [0, 2], [0, 2], [0, 2]] - ) - - -@pytest.fixture -def bin_mr_im() -> np.ndarray: - """ - Returns an incidence matrix (item x class) for a binary classification - problem with multiple raters. - """ - return np.asarray( - [[3, 0], [2, 1], [2, 1], [2, 1], [1, 2], [0, 3], [0, 3], [1, 2], [1, 2], [1, 2]] - ) - - -@pytest.fixture -def single_anno_cm() -> np.ndarray: - """Returns a confusion matrix with only a single annotation.""" - return np.asarray([[1, 0], [0, 0]]) - - -@pytest.fixture -def wrong_dtype_cm() -> np.ndarray: - """Returns a confusion matrix with the wrong dtype.""" - return np.asarray([["a", "b"], ["c", "d"]]) - - -@pytest.fixture -def seq_labels(): - """Returns sequence containing labels.""" - return np.asarray(["a", "b", "c"]) - - -@pytest.fixture -def seq_labels_nan(): - """Returns a sequence containing labels and nan values.""" - return np.asarray([np.NaN, "b", "c"]) - - -@pytest.fixture -def seq_values(): - """Returns a sequence containing values.""" - return [1, 2, 3, 4] - - -@pytest.fixture -def seq_labels_long(): - """Returns a sequence containing more values.""" - return np.asarray(["e", "f", "g", "h"]) - - -@pytest.fixture -def normal_sample(): - return np.random.randn(10_000) - - -_incidence_matrix_generator = tuples(integers(1, 500), integers(2, 50)).map( - lambda xs: np.random.randint(0, 100, size=xs) -) -_confusion_matrix_generator = integers(2).map(lambda x: np.random.randint(0, 100, size=(x, x))) - - -def _eq_rounded(a, b, n_digits=3): - return round(a, n_digits) == round(b, n_digits) diff --git a/packages/examples/cvat/recording-oracle/tests/unit/agreement/test_bootstrap.py b/packages/examples/cvat/recording-oracle/tests/unit/agreement/test_bootstrap.py deleted file mode 100644 index edbe360140..0000000000 --- a/packages/examples/cvat/recording-oracle/tests/unit/agreement/test_bootstrap.py +++ /dev/null @@ -1,38 +0,0 @@ -import numpy as np - -from src.agreement.bootstrap import bootstrap_ci - -from tests.unit.agreement.conftest import _eq_rounded - - -def test_bootstrap_percentage(normal_sample): - ci, statistics_bootstrap = bootstrap_ci( - data=normal_sample, - statistic_fn=np.mean, - n_iterations=5_000, - n_sample=1_000, - ci=0.95, - algorithm="percentile", - ) - - assert len(statistics_bootstrap) == 5_000 - assert _eq_rounded(np.mean(statistics_bootstrap), 0.0, 1) - - low, high = ci - assert low < high - - -def test_bootstrap_bca(normal_sample): - fn = lambda s: np.var(s) / np.mean(s) ** 2 - 1 / np.mean(s) - - ci, statistics_bootstrap = bootstrap_ci( - data=normal_sample, - statistic_fn=fn, - n_iterations=5_000, - n_sample=1_000, - ci=0.95, - algorithm="bca", - ) - - low, high = ci - assert low < high diff --git a/packages/examples/cvat/recording-oracle/tests/unit/agreement/test_measures.py b/packages/examples/cvat/recording-oracle/tests/unit/agreement/test_measures.py deleted file mode 100644 index e9340c816e..0000000000 --- a/packages/examples/cvat/recording-oracle/tests/unit/agreement/test_measures.py +++ /dev/null @@ -1,69 +0,0 @@ -import numpy as np -import pytest -from hypothesis import given, note, settings - -from src.agreement import cohens_kappa, fleiss_kappa, percent_agreement - -from tests.unit.agreement.conftest import ( - _confusion_matrix_generator, - _eq_rounded, - _incidence_matrix_generator, -) - - -def test_percent_agreement(bin_2r_cm, bin_2r_im, single_anno_cm, wrong_dtype_cm): - percentage = percent_agreement(bin_2r_cm, "cm") - assert _eq_rounded(percentage, 0.7) - - percentage_incidence = percent_agreement(bin_2r_im, "im") - assert _eq_rounded(percentage, percentage_incidence) - - with pytest.raises(ValueError, match="have more than 1 annotation"): - percent_agreement(single_anno_cm, "cm") - - with pytest.raises(ValueError, match="must be a square"): - percent_agreement(bin_2r_im, "cm") - - with pytest.raises(ValueError, match="must be a numeric"): - percent_agreement(wrong_dtype_cm) - - -@given(im=_incidence_matrix_generator) -@settings(max_examples=5_000) -def test_percent_agreement_property(im): - note(f"Example incidence matrix: {im}") - result = percent_agreement(im, "im") - assert -1.0 <= result <= 1.0 or np.isnan(result) - - -def test_cohens_kappa(bin_2r_cm): - kappa = cohens_kappa(bin_2r_cm) - assert _eq_rounded(kappa, 0.348) - - -@given(cm=_confusion_matrix_generator) -@settings(max_examples=5_000) -def test_fleiss_kappa_property(cm): - note(f"Example confusion matrix: {cm}") - result = cohens_kappa(cm) - assert -1.0 <= result <= 1.0 or np.isnan(result) - - -def test_fleiss_kappa(bin_mr_im): - kappa = fleiss_kappa(bin_mr_im) - assert _eq_rounded(kappa, 0.05) - - -@given(im=_incidence_matrix_generator) -@settings(max_examples=5_000) -def test_fleiss_kappa_property(im): - note(f"Example incidence matrix: {im}") - result = fleiss_kappa(im) - assert -1.0 <= result <= 1.0 or np.isnan(result) - - -def test_invalid_return(): - invalid = "INVALID" - assert cohens_kappa([[5]], invalid) == invalid - assert fleiss_kappa([[5], [0]], invalid) == invalid - assert percent_agreement([[5], [0]], invalid_return=invalid) == invalid diff --git a/packages/examples/cvat/recording-oracle/tests/unit/agreement/test_utils.py b/packages/examples/cvat/recording-oracle/tests/unit/agreement/test_utils.py deleted file mode 100644 index e1cb053e25..0000000000 --- a/packages/examples/cvat/recording-oracle/tests/unit/agreement/test_utils.py +++ /dev/null @@ -1,20 +0,0 @@ -import numpy as np -import pytest - -from src.utils import confusion_matrix_from_sequence - - -def test_confusion_matrix_from_sequence( - seq_values, seq_labels, seq_labels_nan, seq_labels_long, bin_2r_cm -): - assert np.all(confusion_matrix_from_sequence(seq_values, seq_values) == np.eye(4)) - assert np.all(confusion_matrix_from_sequence(seq_labels, seq_labels_nan) == np.eye(2)) - - with pytest.raises(ValueError, match="same shape"): - confusion_matrix_from_sequence(seq_labels_long, seq_labels_nan) - - with pytest.raises(ValueError, match="1-dimensional"): - confusion_matrix_from_sequence(seq_values, bin_2r_cm) - - with pytest.raises(ValueError, match="must have the same kind of dtype"): - confusion_matrix_from_sequence(seq_values, seq_labels_nan) diff --git a/packages/examples/cvat/recording-oracle/tests/unit/webhook/conftest.py b/packages/examples/cvat/recording-oracle/tests/unit/webhook/conftest.py deleted file mode 100644 index 4e4960bebe..0000000000 --- a/packages/examples/cvat/recording-oracle/tests/unit/webhook/conftest.py +++ /dev/null @@ -1,12 +0,0 @@ -import json -from pathlib import Path - -import pytest - -resource_path_root = Path(__file__).parent.parent.parent / "utils" - - -@pytest.fixture -def intermediate_results(): - with open(resource_path_root / "recording_oracle_dummy_input.json") as f: - return json.load(f) diff --git a/packages/examples/cvat/recording-oracle/tests/unit/webhook/test_process_intermediate_results.py b/packages/examples/cvat/recording-oracle/tests/unit/webhook/test_process_intermediate_results.py deleted file mode 100644 index f376597ae0..0000000000 --- a/packages/examples/cvat/recording-oracle/tests/unit/webhook/test_process_intermediate_results.py +++ /dev/null @@ -1,7 +0,0 @@ -from src.schemas.agreement import ImageLabelBinaryJobResults -from src.utils.process_intermediate_results import process_image_label_binary_intermediate_results - - -def test_process_image_label_binary_intermediate_results(intermediate_results): - results = process_image_label_binary_intermediate_results(intermediate_results) - assert ImageLabelBinaryJobResults.validate(results) diff --git a/packages/examples/cvat/recording-oracle/tests/utils/constants.py b/packages/examples/cvat/recording-oracle/tests/utils/constants.py index 1334f94204..07dde21a4a 100644 --- a/packages/examples/cvat/recording-oracle/tests/utils/constants.py +++ b/packages/examples/cvat/recording-oracle/tests/utils/constants.py @@ -22,3 +22,5 @@ } WEBHOOK_MESSAGE_SIGNED = "0x653242fc57766948d0e84c8466b1d52ce701a20fc6b08b5c1bee361aa8c480a61470db594dccb5724345712a216c35aa14be7d435faaa596a7ffba7f476887bb1b" + +JOB_REQUESTER_ID = "9001" diff --git a/packages/examples/cvat/recording-oracle/tests/utils/setup_escrow.py b/packages/examples/cvat/recording-oracle/tests/utils/setup_escrow.py index 14ae12f91a..a225373349 100644 --- a/packages/examples/cvat/recording-oracle/tests/utils/setup_escrow.py +++ b/packages/examples/cvat/recording-oracle/tests/utils/setup_escrow.py @@ -8,13 +8,16 @@ from tests.utils.constants import ( DEFAULT_HASH, DEFAULT_URL, + EXCHANGE_ORACLE_ADDRESS, + EXCHANGE_ORACLE_FEE, + JOB_REQUESTER_ID, RECORDING_ORACLE_ADDRESS, RECORDING_ORACLE_FEE, REPUTATION_ORACLE_ADDRESS, REPUTATION_ORACLE_FEE, ) -amount = Web3.toWei(1, "ether") +amount = Web3.to_wei(1, "ether") def create_escrow(web3: Web3): @@ -23,15 +26,18 @@ def create_escrow(web3: Web3): staking_client.approve_stake(amount) staking_client.stake(amount) escrow_address = escrow_client.create_and_setup_escrow( - NETWORKS[ChainId.LOCALHOST]["hmt_address"], - [web3.eth.default_account], - EscrowConfig( - RECORDING_ORACLE_ADDRESS, - REPUTATION_ORACLE_ADDRESS, - RECORDING_ORACLE_FEE, - REPUTATION_ORACLE_FEE, - DEFAULT_URL, - DEFAULT_HASH, + token_address=NETWORKS[ChainId.LOCALHOST]["hmt_address"], + trusted_handlers=[web3.eth.default_account], + job_requester_id=JOB_REQUESTER_ID, + escrow_config=EscrowConfig( + exchange_oracle_address=EXCHANGE_ORACLE_ADDRESS, + exchange_oracle_fee=EXCHANGE_ORACLE_FEE, + recording_oracle_address=RECORDING_ORACLE_ADDRESS, + recording_oracle_fee=RECORDING_ORACLE_FEE, + reputation_oracle_address=REPUTATION_ORACLE_ADDRESS, + reputation_oracle_fee=REPUTATION_ORACLE_FEE, + manifest_url=DEFAULT_URL, + hash=DEFAULT_HASH, ), ) return escrow_address diff --git a/packages/examples/cvat/recording-oracle/tests/utils/setup_kvstore.py b/packages/examples/cvat/recording-oracle/tests/utils/setup_kvstore.py index ca0675ee14..e2bb65255e 100644 --- a/packages/examples/cvat/recording-oracle/tests/utils/setup_kvstore.py +++ b/packages/examples/cvat/recording-oracle/tests/utils/setup_kvstore.py @@ -4,7 +4,7 @@ from tests.utils.constants import REPUTATION_ORACLE_PRIV -amount = Web3.toWei(1, "ether") +amount = Web3.to_wei(1, "ether") def store_kvstore_value(key: str, value: str): From 180e32c5bf8b2c24a881ca34ded56c8e88605660 Mon Sep 17 00:00:00 2001 From: Eric Lee <98655210+leric7@users.noreply.github.com> Date: Thu, 18 Jan 2024 23:57:23 +0800 Subject: [PATCH 043/104] [Job Launcher] Decrypt file after downloading (#1328) * decrypt file content after downloading * use existing pgp private key --- .../modules/storage/storage.service.spec.ts | 58 +++++++++++++++++-- .../src/modules/storage/storage.service.ts | 30 +++++++--- .../job-launcher/server/test/constants.ts | 1 + 3 files changed, 77 insertions(+), 12 deletions(-) diff --git a/packages/apps/job-launcher/server/src/modules/storage/storage.service.spec.ts b/packages/apps/job-launcher/server/src/modules/storage/storage.service.spec.ts index 7e677be357..6885ad3ffb 100644 --- a/packages/apps/job-launcher/server/src/modules/storage/storage.service.spec.ts +++ b/packages/apps/job-launcher/server/src/modules/storage/storage.service.spec.ts @@ -1,10 +1,15 @@ -import { StorageClient } from '@human-protocol/sdk'; -import { ConfigModule, registerAs } from '@nestjs/config'; +import { + Encryption, + EncryptionUtils, + StorageClient, +} from '@human-protocol/sdk'; +import { ConfigModule, ConfigService, registerAs } from '@nestjs/config'; import { Test } from '@nestjs/testing'; import { MOCK_FILE_HASH, MOCK_FILE_URL, MOCK_MANIFEST, + MOCK_PGP_PRIVATE_KEY, MOCK_S3_ACCESS_KEY, MOCK_S3_BUCKET, MOCK_S3_ENDPOINT, @@ -23,6 +28,12 @@ jest.mock('@human-protocol/sdk', () => ({ StorageClient: { downloadFileFromUrl: jest.fn(), }, + Encryption: { + build: jest.fn(), + }, + EncryptionUtils: { + isEncrypted: jest.fn(), + }, })); jest.mock('minio', () => { @@ -45,6 +56,15 @@ describe('StorageService', () => { let storageService: StorageService; beforeAll(async () => { + const mockConfigService: Partial = { + get: jest.fn((key: string) => { + switch (key) { + case 'MOCK_PGP_PRIVATE_KEY': + return MOCK_PGP_PRIVATE_KEY; + } + }), + }; + const moduleRef = await Test.createTestingModule({ imports: [ ConfigModule.forFeature( @@ -58,7 +78,10 @@ describe('StorageService', () => { })), ), ], - providers: [StorageService], + providers: [ + StorageService, + { provide: ConfigService, useValue: mockConfigService }, + ], }).compile(); storageService = moduleRef.get(StorageService); @@ -128,11 +151,38 @@ describe('StorageService', () => { ], }; + EncryptionUtils.isEncrypted = jest.fn().mockReturnValueOnce(false); StorageClient.downloadFileFromUrl = jest .fn() .mockResolvedValueOnce(expectedJobFile); const solutionsFile = await storageService.download(MOCK_FILE_URL); - expect(solutionsFile).toBe(expectedJobFile); + expect(solutionsFile).toStrictEqual(expectedJobFile); + }); + + it('should download the encrypted file correctly', async () => { + const exchangeAddress = '0x1234567890123456789012345678901234567892'; + const workerAddress = '0x1234567890123456789012345678901234567891'; + const solution = 'test'; + + const expectedJobFile = { + exchangeAddress, + solutions: [ + { + workerAddress, + solution, + }, + ], + }; + + EncryptionUtils.isEncrypted = jest.fn().mockReturnValueOnce(true); + StorageClient.downloadFileFromUrl = jest + .fn() + .mockResolvedValueOnce('encrypted'); + Encryption.build = jest.fn().mockResolvedValueOnce({ + decrypt: jest.fn().mockResolvedValueOnce(expectedJobFile), + }); + const solutionsFile = await storageService.download(MOCK_FILE_URL); + expect(solutionsFile).toStrictEqual(expectedJobFile); }); it('should return empty array when file cannot be downloaded', async () => { diff --git a/packages/apps/job-launcher/server/src/modules/storage/storage.service.ts b/packages/apps/job-launcher/server/src/modules/storage/storage.service.ts index 9f7628cd9f..0903c7827b 100644 --- a/packages/apps/job-launcher/server/src/modules/storage/storage.service.ts +++ b/packages/apps/job-launcher/server/src/modules/storage/storage.service.ts @@ -1,16 +1,16 @@ -import { StorageClient } from '@human-protocol/sdk'; +import { + Encryption, + EncryptionUtils, + StorageClient, +} from '@human-protocol/sdk'; import { BadRequestException, Inject, Injectable } from '@nestjs/common'; import * as Minio from 'minio'; -import { S3ConfigType, s3ConfigKey } from '../../common/config'; -import axios from 'axios'; +import { ConfigNames, S3ConfigType, s3ConfigKey } from '../../common/config'; import { ErrorBucket } from '../../common/constants/errors'; -import { parseString } from 'xml2js'; import stringify from 'json-stable-stringify'; import { ContentType, Extension } from '../../common/enums/storage'; import { UploadedFile } from '../../common/interfaces'; -import { generateBucketUrl } from '../../common/utils/storage'; -import { StorageDataDto } from '../job/job.dto'; -import { JobRequestType } from '../../common/enums/job'; +import { ConfigService } from '@nestjs/config'; @Injectable() export class StorageService { @@ -19,6 +19,7 @@ export class StorageService { constructor( @Inject(s3ConfigKey) private s3Config: S3ConfigType, + public readonly configService: ConfigService, ) { this.minioClient = new Minio.Client({ endPoint: this.s3Config.endPoint, @@ -36,7 +37,20 @@ export class StorageService { public async download(url: string): Promise { try { - return await StorageClient.downloadFileFromUrl(url); + const fileContent = await StorageClient.downloadFileFromUrl(url); + + if ( + typeof fileContent === 'string' && + EncryptionUtils.isEncrypted(fileContent) + ) { + const encryption = await Encryption.build( + this.configService.get(ConfigNames.PGP_ENCRYPT, ''), + ); + + return await encryption.decrypt(fileContent); + } else { + return fileContent; + } } catch { return []; } diff --git a/packages/apps/job-launcher/server/test/constants.ts b/packages/apps/job-launcher/server/test/constants.ts index ff788560d6..94b8ff6247 100644 --- a/packages/apps/job-launcher/server/test/constants.ts +++ b/packages/apps/job-launcher/server/test/constants.ts @@ -64,6 +64,7 @@ export const MOCK_MANIFEST: FortuneManifestDto = { fundAmount: 10, requestType: JobRequestType.FORTUNE, }; + export const MOCK_ENCRYPTED_MANIFEST = 'encryptedManifest'; export const MOCK_PGP_PRIVATE_KEY = `-----BEGIN PGP PRIVATE KEY BLOCK----- From 24226a9bdd1c926e958947033365a2d09478a8ca Mon Sep 17 00:00:00 2001 From: Mehdi <75360886+mrhouzlane@users.noreply.github.com> Date: Fri, 19 Jan 2024 10:27:42 +0100 Subject: [PATCH 044/104] feat: clean unused environment variables (#1483) --- packages/apps/job-launcher/server/.env.example | 1 - packages/apps/job-launcher/server/src/common/config/env.ts | 7 +------ .../server/src/modules/job/job.service.spec.ts | 4 ---- 3 files changed, 1 insertion(+), 11 deletions(-) diff --git a/packages/apps/job-launcher/server/.env.example b/packages/apps/job-launcher/server/.env.example index ea27e61be7..0d6e8f5cbc 100644 --- a/packages/apps/job-launcher/server/.env.example +++ b/packages/apps/job-launcher/server/.env.example @@ -29,7 +29,6 @@ REPUTATION_ORACLE_ADDRESS= HCAPTCHA_RECORDING_ORACLE_URI= HCAPTCHA_REPUTATION_ORACLE_URI= HCAPTCHA_ORACLE_ADDRESS= -HCAPTCHA_PGP_PUBLIC_KEY= HCAPTCHA_SITE_KEY= # Auth diff --git a/packages/apps/job-launcher/server/src/common/config/env.ts b/packages/apps/job-launcher/server/src/common/config/env.ts index 453e3ee229..2f752bb558 100644 --- a/packages/apps/job-launcher/server/src/common/config/env.ts +++ b/packages/apps/job-launcher/server/src/common/config/env.ts @@ -17,21 +17,17 @@ export const ConfigNames = { POSTGRES_DATABASE: 'POSTGRES_DATABASE', POSTGRES_PORT: 'POSTGRES_PORT', POSTGRES_SSL: 'POSTGRES_SSL', + POSTGRES_LOGGING: 'POSTGRES_LOGGING', WEB3_ENV: 'WEB3_ENV', WEB3_PRIVATE_KEY: 'WEB3_PRIVATE_KEY', GAS_PRICE_MULTIPLIER: 'GAS_PRICE_MULTIPLIER', PGP_PRIVATE_KEY: 'PGP_PRIVATE_KEY', PGP_ENCRYPT: 'PGP_ENCRYPT', JOB_LAUNCHER_FEE: 'JOB_LAUNCHER_FEE', - RECORDING_ORACLE_FEE: 'RECORDING_ORACLE_FEE', - REPUTATION_ORACLE_FEE: 'REPUTATION_ORACLE_FEE', - EXCHANGE_ORACLE_FEE: 'EXCHANGE_ORACLE_FEE', REPUTATION_ORACLE_ADDRESS: 'REPUTATION_ORACLE_ADDRESS', FORTUNE_EXCHANGE_ORACLE_ADDRESS: 'FORTUNE_EXCHANGE_ORACLE_ADDRESS', - FORTUNE_EXCHANGE_ORACLE_WEBHOOK_URL: 'FORTUNE_EXCHANGE_ORACLE_WEBHOOK_URL', FORTUNE_RECORDING_ORACLE_ADDRESS: 'FORTUNE_RECORDING_ORACLE_ADDRESS', CVAT_EXCHANGE_ORACLE_ADDRESS: 'CVAT_EXCHANGE_ORACLE_ADDRESS', - CVAT_EXCHANGE_ORACLE_WEBHOOK_URL: 'CVAT_EXCHANGE_ORACLE_WEBHOOK_URL', CVAT_RECORDING_ORACLE_ADDRESS: 'CVAT_RECORDING_ORACLE_ADDRESS', HCAPTCHA_RECORDING_ORACLE_URI: 'HCAPTCHA_RECORDING_ORACLE_URI', HCAPTCHA_REPUTATION_ORACLE_URI: 'HCAPTCHA_REPUTATION_ORACLE_URI', @@ -56,7 +52,6 @@ export const ConfigNames = { CVAT_VAL_SIZE: 'CVAT_VAL_SIZE', APIKEY_ITERATIONS: 'APIKEY_ITERATIONS', APIKEY_KEY_LENGTH: 'APIKEY_KEY_LENGTH', - POSTGRES_LOGGING: 'POSTGRES_LOGGING', }; export const envValidator = Joi.object({ 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 43fad90db3..8a783c5585 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 @@ -196,10 +196,6 @@ describe('JobService', () => { return MOCK_RECORDING_ORACLE_ADDRESS; case 'REPUTATION_ORACLE_ADDRESS': return MOCK_REPUTATION_ORACLE_ADDRESS; - case 'FORTUNE_EXCHANGE_ORACLE_WEBHOOK_URL': - return MOCK_EXCHANGE_ORACLE_WEBHOOK_URL; - case 'CVAT_EXCHANGE_ORACLE_WEBHOOK_URL': - return MOCK_EXCHANGE_ORACLE_WEBHOOK_URL; case 'HOST': return '127.0.0.1'; case 'PORT': From e68ba93ea780337817e95f61ff190fbb00854c35 Mon Sep 17 00:00:00 2001 From: m00n620 <50647994+m00n620@users.noreply.github.com> Date: Mon, 22 Jan 2024 11:08:08 -0500 Subject: [PATCH 045/104] [Dashboard UI] Support historical data on middle task graph (#1491) * show historical data on middle task graph * fix token values --- .../HumanAppData/HumanAppDataView.tsx | 33 +++++++++++++-- .../components/HumanAppData/views/Tasks.tsx | 40 +++++-------------- .../ui/src/components/Token/TokenView.tsx | 6 +-- .../dashboard/ui/src/hooks/useHumanAppData.ts | 14 ++++--- .../dashboard/ui/src/hooks/useTaskStats.ts | 21 ---------- 5 files changed, 52 insertions(+), 62 deletions(-) delete mode 100644 packages/apps/dashboard/ui/src/hooks/useTaskStats.ts diff --git a/packages/apps/dashboard/ui/src/components/HumanAppData/HumanAppDataView.tsx b/packages/apps/dashboard/ui/src/components/HumanAppData/HumanAppDataView.tsx index ce3a02e232..2739c30666 100644 --- a/packages/apps/dashboard/ui/src/components/HumanAppData/HumanAppDataView.tsx +++ b/packages/apps/dashboard/ui/src/components/HumanAppData/HumanAppDataView.tsx @@ -38,7 +38,7 @@ export const HumanAppDataView: FC = () => { const transactionsSeries = useMemo(() => { if (data) { - const cumulativeData = [...data.data[0].attributes.dailyHMTData] + const cumulativeData = [...data[0].data[0].attributes.dailyHMTData] .map((d: any) => ({ date: d.timestamp, value: Number(d.totalTransactionCount), @@ -59,7 +59,7 @@ export const HumanAppDataView: FC = () => { const paymentsSeries = useMemo(() => { if (data) { - const cumulativeData = [...data.data[0].attributes.dailyPaymentsData] + const cumulativeData = [...data[0].data[0].attributes.dailyPaymentsData] .map((d: any) => ({ date: d.timestamp, value: Number(d.totalAmountPaid), @@ -78,6 +78,29 @@ export const HumanAppDataView: FC = () => { return []; }, [data, days]); + const taskSeries = useMemo(() => { + if (data) { + const cumulativeData = data[1].data + .map((d: any) => { + const multiplier = d.attributes.date <= '2022-11-30' ? 18 : 9; + return { + date: d.attributes.date, + value: d.attributes.solved_count * multiplier, + }; + }) + .reduce((acc: any, d: any) => { + acc.push({ + date: d.date, + value: acc.length ? acc[acc.length - 1].value + d.value : d.value, + }); + return acc; + }, [] as any[]); + + return cumulativeData.reverse().slice(0, days).reverse(); + } + return []; + }, [data, days]); + const getTooltipTitle = (button: ViewButton) => { switch (button) { case ViewButton.Tasks: @@ -93,7 +116,7 @@ export const HumanAppDataView: FC = () => { return ( - + ); @@ -140,7 +163,9 @@ export const HumanAppDataView: FC = () => { {viewButton === ViewButton.Transactions && ( )} - {viewButton === ViewButton.Tasks && } + {viewButton === ViewButton.Tasks && ( + + )} {viewButton === ViewButton.Payments && ( )} diff --git a/packages/apps/dashboard/ui/src/components/HumanAppData/views/Tasks.tsx b/packages/apps/dashboard/ui/src/components/HumanAppData/views/Tasks.tsx index 8a82032f95..5cfe7c1a05 100644 --- a/packages/apps/dashboard/ui/src/components/HumanAppData/views/Tasks.tsx +++ b/packages/apps/dashboard/ui/src/components/HumanAppData/views/Tasks.tsx @@ -1,46 +1,28 @@ import { ChainId } from '@human-protocol/sdk'; -import React, { useMemo } from 'react'; +import React from 'react'; import { ChartContainer } from './Container'; import { TooltipIcon } from 'src/components/TooltipIcon'; import { TOOLTIPS } from 'src/constants/tooltips'; -import { useTaskStats } from 'src/hooks/useTaskStats'; -import { useChainId, useDays } from 'src/state/humanAppData/hooks'; +import { useChainId } from 'src/state/humanAppData/hooks'; -export const TasksView = () => { - const days = useDays(); +export const TasksView = ({ + isLoading, + data, +}: { + isLoading: boolean; + data: any[]; +}) => { const chainId = useChainId(); - const { data, isLoading } = useTaskStats(); - - const seriesData = useMemo(() => { - if (data) { - const multiplier = 9; - const cumulativeDailyTasksData = [...data.dailyTasksData] - .map((d) => ({ - date: d.timestamp, - value: Number(d.tasksSolved) * multiplier, - })) - .reduce((acc, d) => { - acc.push({ - date: d.date, - value: acc.length ? acc[acc.length - 1].value + d.value : d.value, - }); - return acc; - }, [] as any[]); - - return cumulativeDailyTasksData.slice(-days); - } - return []; - }, [data, days]); return ( diff --git a/packages/apps/dashboard/ui/src/components/Token/TokenView.tsx b/packages/apps/dashboard/ui/src/components/Token/TokenView.tsx index b9f1242da3..861dc5b8c2 100644 --- a/packages/apps/dashboard/ui/src/components/Token/TokenView.tsx +++ b/packages/apps/dashboard/ui/src/components/Token/TokenView.tsx @@ -102,7 +102,7 @@ export const TokenView: FC = () => { const transferCount = useMemo(() => { if (data) { - return data.data[0].attributes.dailyHMTData + return data[0].data[0].attributes.dailyHMTData .slice(0, days) .reverse() .reduce( @@ -125,7 +125,7 @@ export const TokenView: FC = () => { @@ -135,7 +135,7 @@ export const TokenView: FC = () => { value={ chainId === ChainId.ALL ? 1_000_000_000 - : data?.data?.[0]?.attributes?.totalSupply + : data?.[0].data?.[0]?.attributes?.totalSupply } component={TotalSupplyComponent} tooltipTitle={TOOLTIPS.TOTAL_SUPPLY} diff --git a/packages/apps/dashboard/ui/src/hooks/useHumanAppData.ts b/packages/apps/dashboard/ui/src/hooks/useHumanAppData.ts index 63fa5834f0..f385418ce7 100644 --- a/packages/apps/dashboard/ui/src/hooks/useHumanAppData.ts +++ b/packages/apps/dashboard/ui/src/hooks/useHumanAppData.ts @@ -6,11 +6,15 @@ export function useHumanAppData(chainId: ChainId) { `human-protocol-dashboard-human-app-data-${chainId}`, async () => { const apiURL = import.meta.env.VITE_APP_ADMIN_API_URL; - const response = await fetch( - `${apiURL}/network-data-items?filters[chainId][$eq]=${chainId}` - ); - const json = await response.json(); - return json; + const responses = await Promise.all([ + fetch( + `${apiURL}/network-data-items?filters[chainId][$eq]=${chainId}` + ).then((res) => res.json()), + fetch(`${apiURL}/daily-task-summaries?pagination[limit]=-1`).then( + (res) => res.json() + ), + ]); + return responses; } ); } diff --git a/packages/apps/dashboard/ui/src/hooks/useTaskStats.ts b/packages/apps/dashboard/ui/src/hooks/useTaskStats.ts deleted file mode 100644 index e9a7c386dc..0000000000 --- a/packages/apps/dashboard/ui/src/hooks/useTaskStats.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { ChainId } from '@human-protocol/sdk'; -import axios from 'axios'; -import dayjs from 'dayjs'; -import useSWR from 'swr'; -import { useChainId } from 'src/state/humanAppData/hooks'; - -export function useTaskStats() { - const chainId = useChainId(); - return useSWR(`human-protocol-dashboard-task-stats-${chainId}`, async () => { - if (chainId !== ChainId.POLYGON && chainId !== ChainId.ALL) return null; - - const apiURL = import.meta.env.VITE_APP_ADMIN_API_URL; - const to = dayjs().format('YYYY-MM-DD'); - const from = dayjs().subtract(60, 'days').format('YYYY-MM-DD'); - const { data } = await axios.get( - `${apiURL}/stats/tasks?to=${to}&from=${from}` - ); - - return data; - }); -} From 9fe15e6eabae0bfc36d04229896dfb7199ff932f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Jan 2024 10:04:25 +0800 Subject: [PATCH 046/104] Bump dotenv from 16.3.1 to 16.3.2 (#1500) Bumps [dotenv](https://github.com/motdotla/dotenv) from 16.3.1 to 16.3.2. - [Changelog](https://github.com/motdotla/dotenv/blob/master/CHANGELOG.md) - [Commits](https://github.com/motdotla/dotenv/compare/v16.3.1...v16.3.2) --- updated-dependencies: - dependency-name: dotenv dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- packages/apps/dashboard/ui/package.json | 2 +- yarn.lock | 7 ++++++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 63400d3f19..3f4ed6c41b 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "@typescript-eslint/parser": "^5.43.0", "@typescript-eslint/utils": "^6.10.0", "concurrently": "^7.5.0", - "dotenv": "^16.0.3", + "dotenv": "^16.3.2", "eslint": "^8.55.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-jest": "^27.1.5", diff --git a/packages/apps/dashboard/ui/package.json b/packages/apps/dashboard/ui/package.json index bfdba54247..4e2225853c 100644 --- a/packages/apps/dashboard/ui/package.json +++ b/packages/apps/dashboard/ui/package.json @@ -48,7 +48,7 @@ "@types/react-test-renderer": "^18.0.0", "@vitejs/plugin-react": "^3.1.0", "crypto-js": "^4.2.0", - "dotenv": "^16.0.3", + "dotenv": "^16.3.2", "eslint-config-react-app": "^7.0.1", "eslint-import-resolver-typescript": "^3.5.2", "eslint-plugin-import": "^2.29.0", diff --git a/yarn.lock b/yarn.lock index 4dd921e154..b271aa155f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11563,11 +11563,16 @@ dotenv@14.2.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-14.2.0.tgz#7e77fd5dd6cff5942c4496e1acf2d0f37a9e67aa" integrity sha512-05POuPJyPpO6jqzTNweQFfAyMSD4qa4lvsMOWyTRTdpHKy6nnnN+IYWaXF+lHivhBH/ufDKlR4IWCAN3oPnHuw== -dotenv@16.3.1, dotenv@^16.0.0, dotenv@^16.0.3: +dotenv@16.3.1: version "16.3.1" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e" integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ== +dotenv@^16.0.0, dotenv@^16.0.3, dotenv@^16.3.2: + version "16.3.2" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.2.tgz#3cb611ce5a63002dbabf7c281bc331f69d28f03f" + integrity sha512-HTlk5nmhkm8F6JcdXvHIzaorzCoziNQT9mGxLPVXW8wJF1TiGSL60ZGB4gHWabHOaMmWmhvk2/lPHfnBiT78AQ== + dset@^3.1.2: version "3.1.3" resolved "https://registry.yarnpkg.com/dset/-/dset-3.1.3.tgz#c194147f159841148e8e34ca41f638556d9542d2" From c99c5108fa2fedb22ea25b2cce7b387ee3d225b9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Jan 2024 10:32:45 +0800 Subject: [PATCH 047/104] Bump dotenv-cli from 6.0.0 to 7.3.0 (#1501) Bumps [dotenv-cli](https://github.com/entropitor/dotenv-cli) from 6.0.0 to 7.3.0. - [Release notes](https://github.com/entropitor/dotenv-cli/releases) - [Commits](https://github.com/entropitor/dotenv-cli/compare/v6.0.0...v7.3.0) --- updated-dependencies: - dependency-name: dotenv-cli dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .../tools/data-recovery-tool/package.json | 2 +- yarn.lock | 23 ++++++++----------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/packages/tools/data-recovery-tool/package.json b/packages/tools/data-recovery-tool/package.json index 72c912e24d..1d1cb71b05 100644 --- a/packages/tools/data-recovery-tool/package.json +++ b/packages/tools/data-recovery-tool/package.json @@ -4,7 +4,7 @@ "main": "index.js", "license": "MIT", "devDependencies": { - "dotenv-cli": "^6.0.0", + "dotenv-cli": "^7.3.0", "ethers": "^5.7.2", "mustache": "^4.2.0" }, diff --git a/yarn.lock b/yarn.lock index b271aa155f..9aa7f90d90 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11538,26 +11538,21 @@ dot-prop@^5.2.0: dependencies: is-obj "^2.0.0" -dotenv-cli@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/dotenv-cli/-/dotenv-cli-6.0.0.tgz#8a30cbc59d0a8aaa166b2fee0a9a55e23a1223ab" - integrity sha512-qXlCOi3UMDhCWFKe0yq5sg3X+pJAz+RQDiFN38AMSbUrnY3uZshSfDJUAge951OS7J9gwLZGfsBlWRSOYz/TRg== +dotenv-cli@^7.3.0: + version "7.3.0" + resolved "https://registry.yarnpkg.com/dotenv-cli/-/dotenv-cli-7.3.0.tgz#21e33e7944713001677658d68856063968edfbd2" + integrity sha512-314CA4TyK34YEJ6ntBf80eUY+t1XaFLyem1k9P0sX1gn30qThZ5qZr/ZwE318gEnzyYP9yj9HJk6SqwE0upkfw== dependencies: cross-spawn "^7.0.3" - dotenv "^16.0.0" - dotenv-expand "^8.0.1" - minimist "^1.2.5" + dotenv "^16.3.0" + dotenv-expand "^10.0.0" + minimist "^1.2.6" -dotenv-expand@10.0.0: +dotenv-expand@10.0.0, dotenv-expand@^10.0.0: version "10.0.0" resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-10.0.0.tgz#12605d00fb0af6d0a592e6558585784032e4ef37" integrity sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A== -dotenv-expand@^8.0.1: - version "8.0.3" - resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-8.0.3.tgz#29016757455bcc748469c83a19b36aaf2b83dd6e" - integrity sha512-SErOMvge0ZUyWd5B0NXMQlDkN+8r+HhVUsxgOO7IoPDOdDRD2JjExpN6y3KnFR66jsJMwSn1pqIivhU5rcJiNg== - dotenv@14.2.0: version "14.2.0" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-14.2.0.tgz#7e77fd5dd6cff5942c4496e1acf2d0f37a9e67aa" @@ -11568,7 +11563,7 @@ dotenv@16.3.1: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e" integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ== -dotenv@^16.0.0, dotenv@^16.0.3, dotenv@^16.3.2: +dotenv@^16.0.3, dotenv@^16.3.0, dotenv@^16.3.2: version "16.3.2" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.2.tgz#3cb611ce5a63002dbabf7c281bc331f69d28f03f" integrity sha512-HTlk5nmhkm8F6JcdXvHIzaorzCoziNQT9mGxLPVXW8wJF1TiGSL60ZGB4gHWabHOaMmWmhvk2/lPHfnBiT78AQ== From 66d69c48a97fff972e86572130cf80af41e82248 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Jan 2024 10:33:22 +0800 Subject: [PATCH 048/104] Bump actions/dependency-review-action from 3 to 4 (#1499) Bumps [actions/dependency-review-action](https://github.com/actions/dependency-review-action) from 3 to 4. - [Release notes](https://github.com/actions/dependency-review-action/releases) - [Commits](https://github.com/actions/dependency-review-action/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/dependency-review-action dependency-type: direct:production update-type: version-update:semver-major ... 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 2167bb12cc..8caf799d12 100644 --- a/.github/workflows/ci-dependency-review.yaml +++ b/.github/workflows/ci-dependency-review.yaml @@ -11,4 +11,4 @@ jobs: - name: "Checkout Repository" uses: actions/checkout@v4 - name: "Dependency Review" - uses: actions/dependency-review-action@v3 + uses: actions/dependency-review-action@v4 From e8fd650d6d2502e831982fb81692de6e40dd32ea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Jan 2024 10:33:42 +0800 Subject: [PATCH 049/104] Bump vite-plugin-node-polyfills from 0.7.0 to 0.19.0 (#1502) Bumps [vite-plugin-node-polyfills](https://github.com/davidmyersdev/vite-plugin-node-polyfills) from 0.7.0 to 0.19.0. - [Release notes](https://github.com/davidmyersdev/vite-plugin-node-polyfills/releases) - [Commits](https://github.com/davidmyersdev/vite-plugin-node-polyfills/compare/v0.7.0...v0.19.0) --- updated-dependencies: - dependency-name: vite-plugin-node-polyfills dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- packages/apps/dashboard/ui/package.json | 2 +- .../apps/job-launcher/client/package.json | 2 +- yarn.lock | 20 ++++++------------- 3 files changed, 8 insertions(+), 16 deletions(-) diff --git a/packages/apps/dashboard/ui/package.json b/packages/apps/dashboard/ui/package.json index 4e2225853c..369533c5a3 100644 --- a/packages/apps/dashboard/ui/package.json +++ b/packages/apps/dashboard/ui/package.json @@ -61,7 +61,7 @@ "resize-observer-polyfill": "^1.5.1", "sinon": "^15.0.4", "vite": "^5.0.9", - "vite-plugin-node-polyfills": "^0.7.0", + "vite-plugin-node-polyfills": "^0.19.0", "vitest": "^0.30.1" }, "scripts": { diff --git a/packages/apps/job-launcher/client/package.json b/packages/apps/job-launcher/client/package.json index 7b741b74e6..e967d94e2f 100644 --- a/packages/apps/job-launcher/client/package.json +++ b/packages/apps/job-launcher/client/package.json @@ -72,7 +72,7 @@ "jsdom": "^22.1.0", "resize-observer-polyfill": "^1.5.1", "vite": "^5.0.9", - "vite-plugin-node-polyfills": "^0.9.0", + "vite-plugin-node-polyfills": "^0.19.0", "vitest": "^0.30.1" }, "lint-staged": { diff --git a/yarn.lock b/yarn.lock index 9aa7f90d90..4521fa7de7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4733,7 +4733,7 @@ "@rollup/pluginutils" "^5.0.1" eslint "^8.24.0" -"@rollup/plugin-inject@^5.0.3": +"@rollup/plugin-inject@^5.0.5": version "5.0.5" resolved "https://registry.yarnpkg.com/@rollup/plugin-inject/-/plugin-inject-5.0.5.tgz#616f3a73fe075765f91c5bec90176608bed277a3" integrity sha512-2+DEJbNBoPROPkgTDNe8/1YXWcqxbN5DTjASVIOx8HS+pITXushyNiBV56RB08zuptzz8gT3YfkqriTBVycepg== @@ -23955,20 +23955,12 @@ vite-plugin-chrome-extension@^0.0.7: vite "^2.2.0" webextension-polyfill "^0.6.0" -vite-plugin-node-polyfills@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/vite-plugin-node-polyfills/-/vite-plugin-node-polyfills-0.7.0.tgz#004f400cfae791338c6f0a46d4ddf559c331c899" - integrity sha512-DKBSGDOx3R8pUIsQFRZAWNYp0vIffJOT0NkuByX4WIQq80nJ2eamAph5T9zG91liO1Fyl1lo7/Onh6xoQO52XQ== - dependencies: - "@rollup/plugin-inject" "^5.0.3" - node-stdlib-browser "^1.2.0" - -vite-plugin-node-polyfills@^0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/vite-plugin-node-polyfills/-/vite-plugin-node-polyfills-0.9.0.tgz#181d096c43e22cae219c6c2434a204b665044de0" - integrity sha512-+i+WPUuIBhJy+ODfxx6S6FTl28URCxUszbl/IL4GwrZvbqqY/8VDIp+zpjMS8Us/a7GwN4Iaqr/fVIBtkNQojQ== +vite-plugin-node-polyfills@^0.19.0: + version "0.19.0" + resolved "https://registry.yarnpkg.com/vite-plugin-node-polyfills/-/vite-plugin-node-polyfills-0.19.0.tgz#54338c47d29fa4c3a19fcd369001331290094c81" + integrity sha512-AhdVxAmVnd1doUlIRGUGV6ZRPfB9BvIwDF10oCOmL742IsvsFIAV4tSMxSfu5e0Px0QeJLgWVOSbtHIvblzqMw== dependencies: - "@rollup/plugin-inject" "^5.0.3" + "@rollup/plugin-inject" "^5.0.5" node-stdlib-browser "^1.2.0" vite@4.4.9: From d02bc25a46069613d95c51a693b897264afcf76e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Jan 2024 10:34:22 +0800 Subject: [PATCH 050/104] Bump json-stable-stringify from 1.1.0 to 1.1.1 (#1503) Bumps [json-stable-stringify](https://github.com/ljharb/json-stable-stringify) from 1.1.0 to 1.1.1. - [Changelog](https://github.com/ljharb/json-stable-stringify/blob/main/CHANGELOG.md) - [Commits](https://github.com/ljharb/json-stable-stringify/compare/v1.1.0...v1.1.1) --- updated-dependencies: - dependency-name: json-stable-stringify dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- packages/apps/job-launcher/server/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/apps/job-launcher/server/package.json b/packages/apps/job-launcher/server/package.json index dda2bb317a..f725cb505b 100644 --- a/packages/apps/job-launcher/server/package.json +++ b/packages/apps/job-launcher/server/package.json @@ -51,7 +51,7 @@ "decimal.js": "^10.4.3", "express-session": "^1.17.3", "joi": "^17.9.2", - "json-stable-stringify": "^1.0.2", + "json-stable-stringify": "^1.1.1", "nestjs-minio-client": "^2.2.0", "passport": "^0.6.0", "passport-jwt": "^4.0.1", diff --git a/yarn.lock b/yarn.lock index 4521fa7de7..ee7829aaaa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16498,10 +16498,10 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== -json-stable-stringify@^1.0.2: - version "1.1.0" - resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.1.0.tgz#43d39c7c8da34bfaf785a61a56808b0def9f747d" - integrity sha512-zfA+5SuwYN2VWqN1/5HZaDzQKLJHaBVMZIIM+wuYjdptkaQsqzDdqjqf+lZZJUuJq1aanHiY8LhH8LmH+qBYJA== +json-stable-stringify@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.1.1.tgz#52d4361b47d49168bcc4e564189a42e5a7439454" + integrity sha512-SU/971Kt5qVQfJpyDveVhQ/vya+5hvrjClFOcr8c0Fq5aODJjMwutrOfCU+eCnVD5gpx1Q3fEqkyom77zH1iIg== dependencies: call-bind "^1.0.5" isarray "^2.0.5" From 72cf9ac1d548f30accfccc220bef4a89c70e67b7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Jan 2024 10:34:48 +0800 Subject: [PATCH 051/104] Bump class-validator from 0.14.0 to 0.14.1 (#1504) Bumps [class-validator](https://github.com/typestack/class-validator) from 0.14.0 to 0.14.1. - [Release notes](https://github.com/typestack/class-validator/releases) - [Changelog](https://github.com/typestack/class-validator/blob/develop/CHANGELOG.md) - [Commits](https://github.com/typestack/class-validator/compare/v0.14.0...v0.14.1) --- updated-dependencies: - dependency-name: class-validator dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .../fortune/recording-oracle/package.json | 2 +- .../reputation-oracle/server/package.json | 2 +- yarn.lock | 32 +++++++++---------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/packages/apps/fortune/recording-oracle/package.json b/packages/apps/fortune/recording-oracle/package.json index 11e4d66c62..a1351839cd 100644 --- a/packages/apps/fortune/recording-oracle/package.json +++ b/packages/apps/fortune/recording-oracle/package.json @@ -30,7 +30,7 @@ "@nestjs/platform-express": "^10.2.6", "@nestjs/swagger": "^7.1.13", "body-parser": "^1.20.2", - "class-validator": "^0.14.0", + "class-validator": "^0.14.1", "cookie-parser": "^1.4.6", "helmet": "^7.1.0", "reflect-metadata": "^0.1.13" diff --git a/packages/apps/reputation-oracle/server/package.json b/packages/apps/reputation-oracle/server/package.json index e4f6d4d1de..91f7d44e7a 100644 --- a/packages/apps/reputation-oracle/server/package.json +++ b/packages/apps/reputation-oracle/server/package.json @@ -44,7 +44,7 @@ "@types/passport-jwt": "^3.0.10", "bcrypt": "^5.1.1", "class-transformer": "^0.5.1", - "class-validator": "^0.14.0", + "class-validator": "^0.14.1", "cookie-parser": "^1.4.6", "express-session": "^1.17.3", "helmet": "^7.1.0", diff --git a/yarn.lock b/yarn.lock index ee7829aaaa..b1098348c6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6900,10 +6900,10 @@ resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.7.tgz#b14cebc75455eeeb160d5fe23c2fcc0c64f724d8" integrity sha512-WUtIVRUZ9i5dYXefDEAI7sh9/O7jGvHg7Df/5O/gtH3Yabe5odI3UWopVR1qbPXQtvOxWu3mM4XxlYeZtMWF4g== -"@types/validator@^13.7.10": - version "13.11.7" - resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.11.7.tgz#99e19760297667ae46b7069ec8b96cbfe0a08b98" - integrity sha512-q0JomTsJ2I5Mv7dhHhQLGjMvX0JJm5dyZ1DXQySIUzU1UlwzB8bt+R6+LODUbz0UDIOvEzGc28tk27gBJw2N8Q== +"@types/validator@^13.11.8": + version "13.11.8" + resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.11.8.tgz#bb1162ec0fe6f87c95ca812f15b996fcc5e1e2dc" + integrity sha512-c/hzNDBh7eRF+KbCf+OoZxKbnkpaK/cKp9iLQWqB7muXtM+MtL9SUUH8vCFcLn6dH1Qm05jiexK0ofWY7TfOhQ== "@types/ws@8.5.3": version "8.5.3" @@ -9804,14 +9804,14 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" -class-validator@^0.14.0: - version "0.14.0" - resolved "https://registry.yarnpkg.com/class-validator/-/class-validator-0.14.0.tgz#40ed0ecf3c83b2a8a6a320f4edb607be0f0df159" - integrity sha512-ct3ltplN8I9fOwUd8GrP8UQixwff129BkEtuWDKL5W45cQuLd19xqmTLu5ge78YDm/fdje6FMt0hGOhl0lii3A== +class-validator@^0.14.1: + version "0.14.1" + resolved "https://registry.yarnpkg.com/class-validator/-/class-validator-0.14.1.tgz#ff2411ed8134e9d76acfeb14872884448be98110" + integrity sha512-2VEG9JICxIqTpoK1eMzZqaV+u/EiwEJkMGzTrZf6sU/fwsnOITVgYJ8yojSy6CaXtO9V0Cc6ZQZ8h8m4UBuLwQ== dependencies: - "@types/validator" "^13.7.10" - libphonenumber-js "^1.10.14" - validator "^13.7.0" + "@types/validator" "^13.11.8" + libphonenumber-js "^1.10.53" + validator "^13.9.0" classic-level@^1.2.0: version "1.3.0" @@ -16984,10 +16984,10 @@ libmime@^2.0.3: libbase64 "0.1.0" libqp "1.1.0" -libphonenumber-js@^1.10.14: - version "1.10.53" - resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.53.tgz#8dbfe1355ef1a3d8e13b8d92849f7db7ebddc98f" - integrity sha512-sDTnnqlWK4vH4AlDQuswz3n4Hx7bIQWTpIcScJX+Sp7St3LXHmfiax/ZFfyYxHmkdCvydOLSuvtAO/XpXiSySw== +libphonenumber-js@^1.10.53: + version "1.10.54" + resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.54.tgz#8dfba112f49d1b9c2a160e55f9697f22e50f0841" + integrity sha512-P+38dUgJsmh0gzoRDoM4F5jLbyfztkU6PY6eSK6S5HwTi/LPvnwXqVCQZlAy1FxZ5c48q25QhxGQ0pq+WQcSlQ== libqp@1.1.0: version "1.1.0" @@ -23843,7 +23843,7 @@ validate-npm-package-license@^3.0.1: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" -validator@^13.7.0: +validator@^13.9.0: version "13.11.0" resolved "https://registry.yarnpkg.com/validator/-/validator-13.11.0.tgz#23ab3fd59290c61248364eabf4067f04955fbb1b" integrity sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ== From 260b73ed58567b199f2bd36f093e4073e2d5697e Mon Sep 17 00:00:00 2001 From: Eric Lee <98655210+leric7@users.noreply.github.com> Date: Tue, 23 Jan 2024 22:59:14 +0800 Subject: [PATCH 052/104] fix: update vite version to fix security warning (#1512) --- package.json | 4 ++-- packages/apps/dashboard/ui/package.json | 2 +- .../apps/job-launcher/client/package.json | 2 +- packages/apps/meta-code-verify/package.json | 2 +- yarn.lock | 20 +++++++++---------- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index 3f4ed6c41b..43b0b1a747 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ "eslint-config-prettier": "^9.1.0", "eslint-plugin-jest": "^27.1.5", "eslint-plugin-prettier": "^5.0.0", + "ethers": "^6.9.1", "husky": "^8.0.2", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", @@ -63,8 +64,7 @@ "prettier": "^3.1.1", "ts-jest": "^29.1.1", "ts-node": "^10.9.2", - "typescript": "^4.9.3", - "ethers": "^6.9.1" + "typescript": "^4.9.3" }, "resolutions": { "ejs": "^3.1.8", diff --git a/packages/apps/dashboard/ui/package.json b/packages/apps/dashboard/ui/package.json index 369533c5a3..3cecf6a609 100644 --- a/packages/apps/dashboard/ui/package.json +++ b/packages/apps/dashboard/ui/package.json @@ -60,7 +60,7 @@ "merkletreejs": "^0.3.11", "resize-observer-polyfill": "^1.5.1", "sinon": "^15.0.4", - "vite": "^5.0.9", + "vite": "^5.0.12", "vite-plugin-node-polyfills": "^0.19.0", "vitest": "^0.30.1" }, diff --git a/packages/apps/job-launcher/client/package.json b/packages/apps/job-launcher/client/package.json index e967d94e2f..1f84e1ea81 100644 --- a/packages/apps/job-launcher/client/package.json +++ b/packages/apps/job-launcher/client/package.json @@ -71,7 +71,7 @@ "identity-obj-proxy": "^3.0.0", "jsdom": "^22.1.0", "resize-observer-polyfill": "^1.5.1", - "vite": "^5.0.9", + "vite": "^5.0.12", "vite-plugin-node-polyfills": "^0.19.0", "vitest": "^0.30.1" }, diff --git a/packages/apps/meta-code-verify/package.json b/packages/apps/meta-code-verify/package.json index bbabe9ca73..38c9343b18 100644 --- a/packages/apps/meta-code-verify/package.json +++ b/packages/apps/meta-code-verify/package.json @@ -39,7 +39,7 @@ "ts-jest": "^29.1.1", "tslib": "^2.5.0", "typescript": "^4.9.5", - "vite": "^5.0.9", + "vite": "^5.0.12", "vite-plugin-chrome-extension": "^0.0.7", "vitest": "^0.30.1" } diff --git a/yarn.lock b/yarn.lock index b1098348c6..f7dc4bcfd7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -23975,9 +23975,9 @@ vite@4.4.9: fsevents "~2.3.2" vite@^2.2.0: - version "2.9.16" - resolved "https://registry.yarnpkg.com/vite/-/vite-2.9.16.tgz#daf7ba50f5cc37a7bf51b118ba06bc36e97898e9" - integrity sha512-X+6q8KPyeuBvTQV8AVSnKDvXoBMnTx8zxh54sOwmmuOdxkjMmEJXH2UEchA+vTMps1xw9vL64uwJOWryULg7nA== + version "2.9.17" + resolved "https://registry.yarnpkg.com/vite/-/vite-2.9.17.tgz#6b770525e12fa2a2e3a0fa0d028d304f4f7dc7d4" + integrity sha512-XxcRzra6d7xrKXH66jZUgb+srThoPu+TLJc06GifUyKq9JmjHkc1Numc8ra0h56rju2jfVWw3B3fs5l3OFMvUw== dependencies: esbuild "^0.14.27" postcss "^8.4.13" @@ -23987,9 +23987,9 @@ vite@^2.2.0: fsevents "~2.3.2" "vite@^3.0.0 || ^4.0.0": - version "4.5.1" - resolved "https://registry.yarnpkg.com/vite/-/vite-4.5.1.tgz#3370986e1ed5dbabbf35a6c2e1fb1e18555b968a" - integrity sha512-AXXFaAJ8yebyqzoNB9fu2pHoo/nWX+xZlaRwoeYUxEqBO+Zj4msE5G+BhGBll9lYEKv9Hfks52PAF2X7qDYXQA== + version "4.5.2" + resolved "https://registry.yarnpkg.com/vite/-/vite-4.5.2.tgz#d6ea8610e099851dad8c7371599969e0f8b97e82" + integrity sha512-tBCZBNSBbHQkaGyhGCDUGqeo2ph8Fstyp6FMSvTtsXeZSPpSMGlviAOav2hxVTqFcx8Hj/twtWKsMJXNY0xI8w== dependencies: esbuild "^0.18.10" postcss "^8.4.27" @@ -23997,10 +23997,10 @@ vite@^2.2.0: optionalDependencies: fsevents "~2.3.2" -vite@^5.0.9: - version "5.0.11" - resolved "https://registry.yarnpkg.com/vite/-/vite-5.0.11.tgz#31562e41e004cb68e1d51f5d2c641ab313b289e4" - integrity sha512-XBMnDjZcNAw/G1gEiskiM1v6yzM4GE5aMGvhWTlHAYYhxb7S3/V1s3m2LDHa8Vh6yIWYYB0iJwsEaS523c4oYA== +vite@^5.0.12: + version "5.0.12" + resolved "https://registry.yarnpkg.com/vite/-/vite-5.0.12.tgz#8a2ffd4da36c132aec4adafe05d7adde38333c47" + integrity sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w== dependencies: esbuild "^0.19.3" postcss "^8.4.32" From 39e9b125049fece4364c7e529f57c4c2262a30bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20L=C3=B3pez?= <50665615+flopez7@users.noreply.github.com> Date: Wed, 24 Jan 2024 09:46:21 +0100 Subject: [PATCH 053/104] Remove confirm from ValidatePasswordDto (#1516) --- .../src/components/Auth/ResetPasswordForm.jsx | 3 +- .../client/src/components/Auth/SignUpForm.jsx | 4 +- .../job-launcher/client/src/types/index.ts | 2 - .../server/src/common/validators/confirm.ts | 67 ------------------- .../server/src/common/validators/index.ts | 1 - .../server/src/modules/auth/auth.dto.ts | 6 +- .../src/modules/auth/auth.service.spec.ts | 2 - .../src/modules/user/user.service.spec.ts | 2 - 8 files changed, 4 insertions(+), 83 deletions(-) delete mode 100644 packages/apps/job-launcher/server/src/common/validators/confirm.ts diff --git a/packages/apps/job-launcher/client/src/components/Auth/ResetPasswordForm.jsx b/packages/apps/job-launcher/client/src/components/Auth/ResetPasswordForm.jsx index b5f8b73704..c1920e83e3 100644 --- a/packages/apps/job-launcher/client/src/components/Auth/ResetPasswordForm.jsx +++ b/packages/apps/job-launcher/client/src/components/Auth/ResetPasswordForm.jsx @@ -22,12 +22,11 @@ export const ResetPasswordForm = () => { const navigate = useNavigate(); const token = new URLSearchParams(window.location.search).get('token'); - const handleResetPassword = async ({ password, repeatPassword }) => { + const handleResetPassword = async ({ password }) => { setIsLoading(true); try { await authService.resetPassword({ password, - confirm: repeatPassword, token, }); diff --git a/packages/apps/job-launcher/client/src/components/Auth/SignUpForm.jsx b/packages/apps/job-launcher/client/src/components/Auth/SignUpForm.jsx index 932c208c47..86d5b89f79 100644 --- a/packages/apps/job-launcher/client/src/components/Auth/SignUpForm.jsx +++ b/packages/apps/job-launcher/client/src/components/Auth/SignUpForm.jsx @@ -24,10 +24,10 @@ export const SignUpForm = ({ onFinish, onError }) => { const [email, setEmail] = useState(''); const [alertMsg, setAlertMsg] = useState(''); - const handleRegister = async ({ email, password, confirm }) => { + const handleRegister = async ({ email, password }) => { setIsLoading(true); try { - await authService.signUp({ email, password, confirm }); + await authService.signUp({ email, password }); setEmail(email); setIsSuccess(true); } catch (err) { diff --git a/packages/apps/job-launcher/client/src/types/index.ts b/packages/apps/job-launcher/client/src/types/index.ts index 1bb50b9619..445e5e5c3f 100644 --- a/packages/apps/job-launcher/client/src/types/index.ts +++ b/packages/apps/job-launcher/client/src/types/index.ts @@ -8,7 +8,6 @@ export type SignInRequest = { export type SignUpRequest = { email: string; password: string; - confirm: string; }; export type SignUpResponse = { @@ -18,7 +17,6 @@ export type SignUpResponse = { export type ResetPasswordRequest = { password: string; - confirm: string; token: string; }; diff --git a/packages/apps/job-launcher/server/src/common/validators/confirm.ts b/packages/apps/job-launcher/server/src/common/validators/confirm.ts deleted file mode 100644 index d7f54c87bd..0000000000 --- a/packages/apps/job-launcher/server/src/common/validators/confirm.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { - registerDecorator, - ValidationArguments, - ValidationOptions, - ValidatorConstraint, - ValidatorConstraintInterface, -} from 'class-validator'; - -interface IConfirmConstraints { - required: boolean; - relatedPropertyName: string; -} - -@ValidatorConstraint() -class ValidateConfirm implements ValidatorConstraintInterface { - private reason: string; - - public validate(value: unknown, args: ValidationArguments): boolean { - this.reason = ValidateConfirm.isValid(value, args); - return !this.reason; - } - - public defaultMessage(): string { - return this.reason; - } - - private static isValid(value: unknown, args: ValidationArguments): string { - const { relatedPropertyName = 'password' }: IConfirmConstraints = - args.constraints[0]; - - const relatedValue = (args.object as any)[relatedPropertyName]; - - if (typeof value === 'undefined' || value === '') { - if (relatedValue) { - return 'valueMissing'; - } else { - return ''; - } - } - - if (typeof value !== 'string') { - return 'typeMismatch'; - } - - if (relatedValue !== value) { - return 'badInput'; - } - - return ''; - } -} - -export function IsConfirm( - constraints: Partial = {}, - validationOptions?: ValidationOptions, -) { - return (object: Record, propertyName: string): void => { - registerDecorator({ - name: 'isConfirm', - target: object.constructor, - propertyName, - constraints: [constraints], - options: validationOptions, - validator: ValidateConfirm, - }); - }; -} diff --git a/packages/apps/job-launcher/server/src/common/validators/index.ts b/packages/apps/job-launcher/server/src/common/validators/index.ts index 4bf70483ee..47b7e7a263 100644 --- a/packages/apps/job-launcher/server/src/common/validators/index.ts +++ b/packages/apps/job-launcher/server/src/common/validators/index.ts @@ -1,2 +1 @@ export * from './password'; -export * from './confirm'; diff --git a/packages/apps/job-launcher/server/src/modules/auth/auth.dto.ts b/packages/apps/job-launcher/server/src/modules/auth/auth.dto.ts index 467120f6c4..5fd2da8c76 100644 --- a/packages/apps/job-launcher/server/src/modules/auth/auth.dto.ts +++ b/packages/apps/job-launcher/server/src/modules/auth/auth.dto.ts @@ -1,7 +1,7 @@ import { ApiProperty } from '@nestjs/swagger'; import { Transform } from 'class-transformer'; import { IsEmail, IsString } from 'class-validator'; -import { IsConfirm, IsPassword } from '../../common/validators'; +import { IsPassword } from '../../common/validators'; import { TokenType } from '../auth/token.entity'; import { UserEntity } from '../user/user.entity'; @@ -27,10 +27,6 @@ export class ValidatePasswordDto { @ApiProperty() @IsPassword() public password: string; - - @ApiProperty() - @IsConfirm() - public confirm: string; } export class ResendEmailVerificationDto { diff --git a/packages/apps/job-launcher/server/src/modules/auth/auth.service.spec.ts b/packages/apps/job-launcher/server/src/modules/auth/auth.service.spec.ts index ebcd314b58..cdc3d323e6 100644 --- a/packages/apps/job-launcher/server/src/modules/auth/auth.service.spec.ts +++ b/packages/apps/job-launcher/server/src/modules/auth/auth.service.spec.ts @@ -469,7 +469,6 @@ describe('AuthService', () => { authService.restorePassword({ token: 'token', password: 'password', - confirm: 'password', }), ).rejects.toThrow(NotFoundException); }); @@ -485,7 +484,6 @@ describe('AuthService', () => { await authService.restorePassword({ token: 'token', password: 'password', - confirm: 'password', }); expect(updatePasswordMock).toHaveBeenCalled(); diff --git a/packages/apps/job-launcher/server/src/modules/user/user.service.spec.ts b/packages/apps/job-launcher/server/src/modules/user/user.service.spec.ts index 6be3aaf3d9..3a244f34d6 100644 --- a/packages/apps/job-launcher/server/src/modules/user/user.service.spec.ts +++ b/packages/apps/job-launcher/server/src/modules/user/user.service.spec.ts @@ -70,7 +70,6 @@ describe('UserService', () => { const dto: UserCreateDto = { email: 'test@example.com', password: 'password123', - confirm: 'password123', }; const hashedPassword = '$2b$12$Z02o9/Ay7CT0n99icApZYORH8iJI9VGtl3mju7d0c4SdDDujhSzOa'; @@ -102,7 +101,6 @@ describe('UserService', () => { const dto: UserCreateDto = { email: 'test@example.com', password: 'password123', - confirm: 'password123', }; jest From 63f1c25508600d4f5b2282519eea26ad379e5757 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20L=C3=B3pez?= <50665615+flopez7@users.noreply.github.com> Date: Wed, 24 Jan 2024 09:59:51 +0100 Subject: [PATCH 054/104] Create an endpoint to retrieve the job launcher address (#1498) --- .../server/src/modules/web3/web3.controller.ts | 15 +++++++++++++++ .../server/src/modules/web3/web3.service.spec.ts | 12 +++++++++--- .../server/src/modules/web3/web3.service.ts | 4 ++++ 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/packages/apps/job-launcher/server/src/modules/web3/web3.controller.ts b/packages/apps/job-launcher/server/src/modules/web3/web3.controller.ts index 7362baf6ad..ef0580e3f0 100644 --- a/packages/apps/job-launcher/server/src/modules/web3/web3.controller.ts +++ b/packages/apps/job-launcher/server/src/modules/web3/web3.controller.ts @@ -31,4 +31,19 @@ export class Web3Controller { getValidChains(): ChainId[] { return this.web3Service.getValidChains(); } + + @ApiOperation({ + summary: 'Get the address of the Job Launcher', + description: 'Endpoint to get the address of the Job Launcher.', + }) + @ApiResponse({ + status: 200, + description: 'Job Launcher address', + type: String, + }) + @Public() + @Get('/operator-address') + getOperatorAddress(): string { + return this.web3Service.getOperatorAddress(); + } } diff --git a/packages/apps/job-launcher/server/src/modules/web3/web3.service.spec.ts b/packages/apps/job-launcher/server/src/modules/web3/web3.service.spec.ts index 26aecc09ce..c30174ded0 100644 --- a/packages/apps/job-launcher/server/src/modules/web3/web3.service.spec.ts +++ b/packages/apps/job-launcher/server/src/modules/web3/web3.service.spec.ts @@ -5,19 +5,18 @@ import { MAINNET_CHAIN_IDS, TESTNET_CHAIN_IDS } from '../../common/constants'; import { ErrorWeb3 } from '../../common/constants/errors'; import { Web3Env } from '../../common/enums/web3'; import { Web3Service } from './web3.service'; +import { MOCK_ADDRESS, MOCK_PRIVATE_KEY } from './../../../test/constants'; describe('Web3Service', () => { let mockConfigService: Partial; let web3Service: Web3Service; - const privateKey = - '5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a'; beforeAll(async () => { mockConfigService = { get: jest.fn((key: string, defaultValue?: any) => { switch (key) { case 'WEB3_PRIVATE_KEY': - return privateKey; + return MOCK_PRIVATE_KEY; case 'WEB3_ENV': return 'testnet'; default: @@ -69,4 +68,11 @@ describe('Web3Service', () => { expect(validChainIds).toBe(TESTNET_CHAIN_IDS); }); }); + + describe('getOperatorAddress', () => { + it('should get the operator address', () => { + const operatorAddress = web3Service.getOperatorAddress(); + expect(operatorAddress).toBe(MOCK_ADDRESS); + }); + }); }); diff --git a/packages/apps/job-launcher/server/src/modules/web3/web3.service.ts b/packages/apps/job-launcher/server/src/modules/web3/web3.service.ts index 336445142a..a8946584f9 100644 --- a/packages/apps/job-launcher/server/src/modules/web3/web3.service.ts +++ b/packages/apps/job-launcher/server/src/modules/web3/web3.service.ts @@ -69,4 +69,8 @@ export class Web3Service { return 1n; } + + public getOperatorAddress(): string { + return Object.values(this.signers)[0].address; + } } From c44e7f96ace3e57fc50f51ff4850128e3b23685e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20L=C3=B3pez?= <50665615+flopez7@users.noreply.github.com> Date: Thu, 25 Jan 2024 09:58:16 +0100 Subject: [PATCH 055/104] Add new untracked statuses to getJobsByStatus (#1518) --- .../job-launcher/server/src/modules/job/job.repository.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/apps/job-launcher/server/src/modules/job/job.repository.ts b/packages/apps/job-launcher/server/src/modules/job/job.repository.ts index a5c3ccad84..ad959be489 100644 --- a/packages/apps/job-launcher/server/src/modules/job/job.repository.ts +++ b/packages/apps/job-launcher/server/src/modules/job/job.repository.ts @@ -69,7 +69,12 @@ export class JobRepository { ): Promise { const statusFilter = status === JobStatusFilter.PENDING - ? In([JobStatus.PENDING, JobStatus.PAID]) + ? In([ + JobStatus.PENDING, + JobStatus.PAID, + JobStatus.CREATED, + JobStatus.SET_UP, + ]) : In([status]); return this.find( From e859447fdd7f3d527baa7a9f6e6d370b29a2c070 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20L=C3=B3pez?= <50665615+flopez7@users.noreply.github.com> Date: Thu, 25 Jan 2024 13:06:30 +0100 Subject: [PATCH 056/104] Fix the usage of encryption module inside Job Launcher (#1505) --- .../server/src/modules/job/job.service.ts | 12 ++------ .../src/modules/storage/storage.module.ts | 3 +- .../modules/storage/storage.service.spec.ts | 30 ++++++++++--------- .../src/modules/storage/storage.service.ts | 10 ++----- 4 files changed, 24 insertions(+), 31 deletions(-) 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 2429bb168f..d010b5b509 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 @@ -123,10 +123,10 @@ export class JobService { private readonly paymentService: PaymentService, public readonly configService: ConfigService, private readonly routingProtocolService: RoutingProtocolService, - private readonly encryption: Encryption, private readonly storageService: StorageService, private readonly webhookService: WebhookService, private readonly cronJobService: CronJobService, + @Inject(Encryption) private readonly encryption: Encryption, ) {} public async createCvatManifest( @@ -574,10 +574,7 @@ export class JobService { let manifest = await this.storageService.download(jobEntity.manifestUrl); if (typeof manifest === 'string' && isPGPMessage(manifest)) { - const encription = await Encryption.build( - this.configService.get(ConfigNames.PGP_PRIVATE_KEY)!, - ); - manifest = await encription.decrypt(manifest as any); + manifest = await this.encryption.decrypt(manifest as any); } if (isValidJSON(manifest)) { @@ -1310,10 +1307,7 @@ export class JobService { let manifest; if (typeof manifestData === 'string' && isPGPMessage(manifestData)) { - const encription = await Encryption.build( - this.configService.get(ConfigNames.PGP_PRIVATE_KEY)!, - ); - manifestData = await encription.decrypt(manifestData as any); + manifestData = await this.encryption.decrypt(manifestData as any); } if (isValidJSON(manifestData)) { diff --git a/packages/apps/job-launcher/server/src/modules/storage/storage.module.ts b/packages/apps/job-launcher/server/src/modules/storage/storage.module.ts index f9cf1659df..551196f956 100644 --- a/packages/apps/job-launcher/server/src/modules/storage/storage.module.ts +++ b/packages/apps/job-launcher/server/src/modules/storage/storage.module.ts @@ -2,9 +2,10 @@ import { Module } from '@nestjs/common'; import { StorageService } from './storage.service'; import { ConfigModule } from '@nestjs/config'; import { s3Config } from '../../common/config'; +import { EncryptionModule } from '../encryption/encryption.module'; @Module({ - imports: [ConfigModule.forFeature(s3Config)], + imports: [ConfigModule.forFeature(s3Config), EncryptionModule], providers: [StorageService], exports: [StorageService], }) diff --git a/packages/apps/job-launcher/server/src/modules/storage/storage.service.spec.ts b/packages/apps/job-launcher/server/src/modules/storage/storage.service.spec.ts index 6885ad3ffb..7bd0454367 100644 --- a/packages/apps/job-launcher/server/src/modules/storage/storage.service.spec.ts +++ b/packages/apps/job-launcher/server/src/modules/storage/storage.service.spec.ts @@ -10,6 +10,7 @@ import { MOCK_FILE_URL, MOCK_MANIFEST, MOCK_PGP_PRIVATE_KEY, + MOCK_PGP_PUBLIC_KEY, MOCK_S3_ACCESS_KEY, MOCK_S3_BUCKET, MOCK_S3_ENDPOINT, @@ -28,12 +29,6 @@ jest.mock('@human-protocol/sdk', () => ({ StorageClient: { downloadFileFromUrl: jest.fn(), }, - Encryption: { - build: jest.fn(), - }, - EncryptionUtils: { - isEncrypted: jest.fn(), - }, })); jest.mock('minio', () => { @@ -80,6 +75,10 @@ describe('StorageService', () => { ], providers: [ StorageService, + { + provide: Encryption, + useValue: await Encryption.build(MOCK_PGP_PRIVATE_KEY), + }, { provide: ConfigService, useValue: mockConfigService }, ], }).compile(); @@ -151,12 +150,13 @@ describe('StorageService', () => { ], }; - EncryptionUtils.isEncrypted = jest.fn().mockReturnValueOnce(false); + jest.spyOn(EncryptionUtils, 'isEncrypted').mockReturnValueOnce(false); StorageClient.downloadFileFromUrl = jest .fn() .mockResolvedValueOnce(expectedJobFile); const solutionsFile = await storageService.download(MOCK_FILE_URL); expect(solutionsFile).toStrictEqual(expectedJobFile); + jest.spyOn(EncryptionUtils, 'isEncrypted').mockRestore(); }); it('should download the encrypted file correctly', async () => { @@ -164,7 +164,7 @@ describe('StorageService', () => { const workerAddress = '0x1234567890123456789012345678901234567891'; const solution = 'test'; - const expectedJobFile = { + const jobFile = { exchangeAddress, solutions: [ { @@ -174,15 +174,17 @@ describe('StorageService', () => { ], }; - EncryptionUtils.isEncrypted = jest.fn().mockReturnValueOnce(true); + const encryption = await Encryption.build(MOCK_PGP_PRIVATE_KEY); + const encryptedJobFile = await encryption.signAndEncrypt( + JSON.stringify(jobFile), + [MOCK_PGP_PUBLIC_KEY], + ); StorageClient.downloadFileFromUrl = jest .fn() - .mockResolvedValueOnce('encrypted'); - Encryption.build = jest.fn().mockResolvedValueOnce({ - decrypt: jest.fn().mockResolvedValueOnce(expectedJobFile), - }); + .mockResolvedValueOnce(encryptedJobFile); + const solutionsFile = await storageService.download(MOCK_FILE_URL); - expect(solutionsFile).toStrictEqual(expectedJobFile); + expect(JSON.parse(solutionsFile)).toStrictEqual(jobFile); }); it('should return empty array when file cannot be downloaded', async () => { diff --git a/packages/apps/job-launcher/server/src/modules/storage/storage.service.ts b/packages/apps/job-launcher/server/src/modules/storage/storage.service.ts index 0903c7827b..0e6f922663 100644 --- a/packages/apps/job-launcher/server/src/modules/storage/storage.service.ts +++ b/packages/apps/job-launcher/server/src/modules/storage/storage.service.ts @@ -5,7 +5,7 @@ import { } from '@human-protocol/sdk'; import { BadRequestException, Inject, Injectable } from '@nestjs/common'; import * as Minio from 'minio'; -import { ConfigNames, S3ConfigType, s3ConfigKey } from '../../common/config'; +import { S3ConfigType, s3ConfigKey } from '../../common/config'; import { ErrorBucket } from '../../common/constants/errors'; import stringify from 'json-stable-stringify'; import { ContentType, Extension } from '../../common/enums/storage'; @@ -20,6 +20,7 @@ export class StorageService { @Inject(s3ConfigKey) private s3Config: S3ConfigType, public readonly configService: ConfigService, + @Inject(Encryption) private readonly encryption: Encryption, ) { this.minioClient = new Minio.Client({ endPoint: this.s3Config.endPoint, @@ -38,16 +39,11 @@ export class StorageService { public async download(url: string): Promise { try { const fileContent = await StorageClient.downloadFileFromUrl(url); - if ( typeof fileContent === 'string' && EncryptionUtils.isEncrypted(fileContent) ) { - const encryption = await Encryption.build( - this.configService.get(ConfigNames.PGP_ENCRYPT, ''), - ); - - return await encryption.decrypt(fileContent); + return await this.encryption.decrypt(fileContent); } else { return fileContent; } From 4e945a81226edb5acabba575084f534311fb3309 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20L=C3=B3pez?= <50665615+flopez7@users.noreply.github.com> Date: Thu, 25 Jan 2024 13:09:58 +0100 Subject: [PATCH 057/104] Use endpoint to get job launcher address (#1517) --- packages/apps/job-launcher/client/.env.example | 1 - .../client/src/components/Jobs/Create/CryptoPayForm.tsx | 2 +- .../client/src/components/TopUpAccount/CryptoTopUpForm.tsx | 2 +- packages/apps/job-launcher/client/src/services/payment.ts | 6 ++++++ packages/apps/job-launcher/client/src/vite-env.d.ts | 1 - 5 files changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/apps/job-launcher/client/.env.example b/packages/apps/job-launcher/client/.env.example index 67d83f56c6..78d8bb7f2b 100644 --- a/packages/apps/job-launcher/client/.env.example +++ b/packages/apps/job-launcher/client/.env.example @@ -1,5 +1,4 @@ VITE_APP_JOB_LAUNCHER_SERVER_URL= -VITE_APP_JOB_LAUNCHER_ADDRESS= VITE_APP_WALLETCONNECT_PROJECT_ID= VITE_APP_STRIPE_PUBLISHABLE_KEY= VITE_APP_HCAPTCHA_SITE_KEY= diff --git a/packages/apps/job-launcher/client/src/components/Jobs/Create/CryptoPayForm.tsx b/packages/apps/job-launcher/client/src/components/Jobs/Create/CryptoPayForm.tsx index 61479336d2..632a0599ad 100644 --- a/packages/apps/job-launcher/client/src/components/Jobs/Create/CryptoPayForm.tsx +++ b/packages/apps/job-launcher/client/src/components/Jobs/Create/CryptoPayForm.tsx @@ -84,7 +84,7 @@ export const CryptoPayForm = ({ ); const tx = await contract.transfer( - import.meta.env.VITE_APP_JOB_LAUNCHER_ADDRESS, + await paymentService.getOperatorAddress(), ethers.utils.parseUnits(tokenAmount.toFixed(2), 18), ); diff --git a/packages/apps/job-launcher/client/src/components/TopUpAccount/CryptoTopUpForm.tsx b/packages/apps/job-launcher/client/src/components/TopUpAccount/CryptoTopUpForm.tsx index a8e9b6130f..81e6b8657e 100644 --- a/packages/apps/job-launcher/client/src/components/TopUpAccount/CryptoTopUpForm.tsx +++ b/packages/apps/job-launcher/client/src/components/TopUpAccount/CryptoTopUpForm.tsx @@ -50,7 +50,7 @@ export const CryptoTopUpForm = () => { const tokenAmount = ethers.utils.parseUnits(amount, 18); const tx = await contract.transfer( - import.meta.env.VITE_APP_JOB_LAUNCHER_ADDRESS, + await paymentService.getOperatorAddress(), tokenAmount, ); diff --git a/packages/apps/job-launcher/client/src/services/payment.ts b/packages/apps/job-launcher/client/src/services/payment.ts index 481bbfaaa1..759ce60f36 100644 --- a/packages/apps/job-launcher/client/src/services/payment.ts +++ b/packages/apps/job-launcher/client/src/services/payment.ts @@ -34,3 +34,9 @@ export const getRate = async (from: string, to: string) => { return data; }; + +export const getOperatorAddress = async () => { + const { data } = await api.get('/web3/operator-address'); + + return data; +}; diff --git a/packages/apps/job-launcher/client/src/vite-env.d.ts b/packages/apps/job-launcher/client/src/vite-env.d.ts index 698e88466e..05e98cfcf4 100644 --- a/packages/apps/job-launcher/client/src/vite-env.d.ts +++ b/packages/apps/job-launcher/client/src/vite-env.d.ts @@ -2,7 +2,6 @@ interface ImportMetaEnv { readonly VITE_APP_JOB_LAUNCHER_SERVER_URL: string; - readonly VITE_APP_JOB_LAUNCHER_ADDRESS: string; readonly VITE_APP_WALLETCONNECT_PROJECT_ID: string; readonly VITE_APP_STRIPE_PUBLISHABLE_KEY: string; readonly VITE_APP_HCAPTCHA_SITE_KEY: string; From 85eec41a44a5515532dc6c8198d0814e12aa9743 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20L=C3=B3pez?= <50665615+flopez7@users.noreply.github.com> Date: Thu, 25 Jan 2024 13:52:11 +0100 Subject: [PATCH 058/104] [Job Launcher] Get Job Launcher fee from KVStore (#1513) * Get Job Launcher fee from KVStore * Make the address parameter of getOracleFee as required to make it more intuitive * Remove useless code --- .../src/modules/job/job.service.spec.ts | 89 +++++++++++++++++-- .../server/src/modules/job/job.service.ts | 9 +- 2 files changed, 87 insertions(+), 11 deletions(-) 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 8a783c5585..22345e6f39 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 @@ -239,6 +239,8 @@ describe('JobService', () => { useValue: { getSigner: jest.fn().mockReturnValue(signerMock), validateChainId: jest.fn().mockReturnValue(new Error()), + calculateGasPrice: jest.fn().mockReturnValue(1000n), + getOperatorAddress: jest.fn().mockReturnValue(MOCK_ADDRESS), }, }, { provide: JobRepository, useValue: createMock() }, @@ -279,8 +281,6 @@ describe('JobService', () => { }); storageService.download = jest.fn(); - - web3Service.calculateGasPrice = jest.fn().mockReturnValue(1000n); }); beforeEach(async () => { @@ -305,12 +305,6 @@ describe('JobService', () => { createPaymentMock.mockResolvedValue(true); }); - beforeAll(() => { - (KVStoreClient.build as any).mockImplementation(() => ({ - get: jest.fn().mockResolvedValue(MOCK_PGP_PUBLIC_KEY), - })); - }); - afterEach(() => { jest.restoreAllMocks(); }); @@ -335,6 +329,14 @@ describe('JobService', () => { save: jest.fn().mockResolvedValue(true), }; + (KVStoreClient.build as any) + .mockImplementationOnce(() => ({ + get: jest.fn().mockResolvedValue(MOCK_ORACLE_FEE), + })) + .mockImplementation(() => ({ + get: jest.fn().mockResolvedValue(MOCK_PGP_PUBLIC_KEY), + })); + jobRepository.create = jest.fn().mockResolvedValue(mockJobEntity); await jobService.createJob(userId, JobRequestType.FORTUNE, fortuneJobDto); @@ -373,6 +375,14 @@ describe('JobService', () => { .spyOn(routingProtocolService, 'selectNetwork') .mockReturnValue(ChainId.MOONBEAM); + (KVStoreClient.build as any) + .mockImplementationOnce(() => ({ + get: jest.fn().mockResolvedValue(MOCK_ORACLE_FEE), + })) + .mockImplementation(() => ({ + get: jest.fn().mockResolvedValue(MOCK_PGP_PUBLIC_KEY), + })); + await jobService.createJob(userId, JobRequestType.FORTUNE, { ...fortuneJobDto, chainId: undefined, @@ -407,6 +417,10 @@ describe('JobService', () => { .spyOn(paymentService, 'getUserBalance') .mockResolvedValue(userBalance); + (KVStoreClient.build as any).mockImplementationOnce(() => ({ + get: jest.fn().mockResolvedValue(MOCK_ORACLE_FEE), + })); + getUserBalanceMock.mockResolvedValue(userBalance); await expect( @@ -419,6 +433,14 @@ describe('JobService', () => { getUserBalanceMock.mockResolvedValue(userBalance); + (KVStoreClient.build as any) + .mockImplementationOnce(() => ({ + get: jest.fn().mockResolvedValue(MOCK_ORACLE_FEE), + })) + .mockImplementation(() => ({ + get: jest.fn().mockResolvedValue(MOCK_PGP_PUBLIC_KEY), + })); + jest.spyOn(jobRepository, 'create').mockResolvedValue(undefined!); await expect( @@ -908,6 +930,14 @@ describe('JobService', () => { jobRepository.create = jest.fn().mockResolvedValue(mockJobEntity); + (KVStoreClient.build as any) + .mockImplementationOnce(() => ({ + get: jest.fn().mockResolvedValue(MOCK_ORACLE_FEE), + })) + .mockImplementation(() => ({ + get: jest.fn().mockResolvedValue(MOCK_PGP_PUBLIC_KEY), + })); + await jobService.createJob( userId, JobRequestType.IMAGE_POINTS, @@ -960,6 +990,10 @@ describe('JobService', () => { type: JobRequestType.IMAGE_POINTS, }; + (KVStoreClient.build as any).mockImplementationOnce(() => ({ + get: jest.fn().mockResolvedValue(MOCK_ORACLE_FEE), + })); + await expect( jobService.createJob( userId, @@ -994,6 +1028,10 @@ describe('JobService', () => { type: JobRequestType.IMAGE_POINTS, }; + (KVStoreClient.build as any).mockImplementationOnce(() => ({ + get: jest.fn().mockResolvedValue(MOCK_ORACLE_FEE), + })); + await expect( jobService.createJob( userId, @@ -1027,6 +1065,10 @@ describe('JobService', () => { type: JobRequestType.IMAGE_POINTS, }; + (KVStoreClient.build as any).mockImplementationOnce(() => ({ + get: jest.fn().mockResolvedValue(MOCK_ORACLE_FEE), + })); + await expect( jobService.createJob( userId, @@ -1060,6 +1102,10 @@ describe('JobService', () => { type: JobRequestType.IMAGE_POINTS, }; + (KVStoreClient.build as any).mockImplementationOnce(() => ({ + get: jest.fn().mockResolvedValue(MOCK_ORACLE_FEE), + })); + await expect( jobService.createJob( userId, @@ -1082,6 +1128,14 @@ describe('JobService', () => { .spyOn(routingProtocolService, 'selectNetwork') .mockReturnValue(ChainId.MOONBEAM); + (KVStoreClient.build as any) + .mockImplementationOnce(() => ({ + get: jest.fn().mockResolvedValue(MOCK_ORACLE_FEE), + })) + .mockImplementation(() => ({ + get: jest.fn().mockResolvedValue(MOCK_PGP_PUBLIC_KEY), + })); + await jobService.createJob(userId, JobRequestType.IMAGE_POINTS, { ...imageLabelBinaryJobDto, chainId: undefined, @@ -1123,6 +1177,10 @@ describe('JobService', () => { getUserBalanceMock.mockResolvedValue(userBalance); + (KVStoreClient.build as any).mockImplementationOnce(() => ({ + get: jest.fn().mockResolvedValue(MOCK_ORACLE_FEE), + })); + await expect( jobService.createJob( userId, @@ -1139,6 +1197,14 @@ describe('JobService', () => { jest.spyOn(jobRepository, 'create').mockResolvedValue(undefined!); + (KVStoreClient.build as any) + .mockImplementationOnce(() => ({ + get: jest.fn().mockResolvedValue(MOCK_ORACLE_FEE), + })) + .mockImplementation(() => ({ + get: jest.fn().mockResolvedValue(MOCK_PGP_PUBLIC_KEY), + })); + await expect( jobService.createJob( userId, @@ -1178,6 +1244,13 @@ describe('JobService', () => { beforeEach(() => { getUserBalanceMock = jest.spyOn(paymentService, 'getUserBalance'); createPaymentMock.mockResolvedValue(true); + (KVStoreClient.build as any) + .mockImplementationOnce(() => ({ + get: jest.fn().mockResolvedValue(MOCK_ORACLE_FEE), + })) + .mockImplementation(() => ({ + get: jest.fn().mockResolvedValue(MOCK_PGP_PUBLIC_KEY), + })); }); afterEach(() => { 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 d010b5b509..7a63dd2341 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 @@ -442,9 +442,12 @@ export class JobService { fundAmount = dto.fundAmount; } const userBalance = await this.paymentService.getUserBalance(userId); - const feePercentage = this.configService.get( - ConfigNames.JOB_LAUNCHER_FEE, - )!; + const feePercentage = Number( + await this.getOracleFee( + await this.web3Service.getOperatorAddress(), + chainId, + ), + ); const fee = mul(div(feePercentage, 100), fundAmount); const usdTotalAmount = add(fundAmount, fee); From 3e6c209690e90b3bc0b00ef0d1e253df914a8766 Mon Sep 17 00:00:00 2001 From: m00n620 <50647994+m00n620@users.noreply.github.com> Date: Thu, 25 Jan 2024 08:23:50 -0500 Subject: [PATCH 059/104] fix build issue, move codebase (#1533) --- packages/apps/dashboard/admin/config/api.ts | 4 +- .../apps/dashboard/admin/config/cron-tasks.ts | 128 ++++++++++++++++-- packages/apps/dashboard/admin/package.json | 1 + .../daily-task-summary/schema.json | 24 ++++ .../controllers/daily-task-summary.ts | 62 +++++++++ .../routes/01-custom-daily-task-summary.ts | 9 ++ .../routes/daily-task-summary.ts | 7 + .../services/daily-task-summary.ts | 7 + .../admin/types/generated/contentTypes.d.ts | 25 ++++ 9 files changed, 251 insertions(+), 16 deletions(-) create mode 100644 packages/apps/dashboard/admin/src/api/daily-task-summary/content-types/daily-task-summary/schema.json create mode 100644 packages/apps/dashboard/admin/src/api/daily-task-summary/controllers/daily-task-summary.ts create mode 100644 packages/apps/dashboard/admin/src/api/daily-task-summary/routes/01-custom-daily-task-summary.ts create mode 100644 packages/apps/dashboard/admin/src/api/daily-task-summary/routes/daily-task-summary.ts create mode 100644 packages/apps/dashboard/admin/src/api/daily-task-summary/services/daily-task-summary.ts diff --git a/packages/apps/dashboard/admin/config/api.ts b/packages/apps/dashboard/admin/config/api.ts index 37f7c14a42..4d29ba53f5 100644 --- a/packages/apps/dashboard/admin/config/api.ts +++ b/packages/apps/dashboard/admin/config/api.ts @@ -1,7 +1,7 @@ export default { rest: { - defaultLimit: 25, - maxLimit: 100, + defaultLimit: 100, + maxLimit: 1000, withCount: true, }, }; diff --git a/packages/apps/dashboard/admin/config/cron-tasks.ts b/packages/apps/dashboard/admin/config/cron-tasks.ts index e05ff1ce2b..cb8d7ae33e 100644 --- a/packages/apps/dashboard/admin/config/cron-tasks.ts +++ b/packages/apps/dashboard/admin/config/cron-tasks.ts @@ -1,4 +1,6 @@ import { ChainId, NETWORKS, StatisticsClient } from '@human-protocol/sdk'; +import axios from 'axios'; +import dayjs from 'dayjs'; import { createPublicClient, http } from 'viem'; import { bsc, @@ -92,43 +94,50 @@ const fetchData = async () => { export default { syncDashboardData: { task: async ({ strapi }) => { + console.log('sync started...'); try { - console.log('sync started...'); const dataItems = await fetchData(); - const allNetworkDataItem = { ...dataItems[0] }; - allNetworkDataItem.chainId = '-1'; - for (let i = 1; i < dataItems.length; i++) { + const allNetworkDataItem = { + chainId: '-1', + dailyHMTData: [], + dailyPaymentsData: [], + totalTransferAmount: '0', + totalTransferCount: 0, + totalHolders: 0, + totalSupply: '0', + }; + for (let i = 0; i < dataItems.length; i++) { const dataItem = dataItems[i]; dataItem.dailyHMTData.forEach((hmtDayData) => { const index = allNetworkDataItem.dailyHMTData.findIndex( - (d) => d.timestamp.getTime() === hmtDayData.timestamp.getTime() + (d) => d.timestamp.getTime() === hmtDayData.timestamp.getTime(), ); if (index === -1) { - allNetworkDataItem.dailyHMTData.push(hmtDayData); + allNetworkDataItem.dailyHMTData.push({ ...hmtDayData }); } else { allNetworkDataItem.dailyHMTData[index].totalTransactionAmount = addBigInts( allNetworkDataItem.dailyHMTData[index].totalTransactionAmount, - hmtDayData.totalTransactionAmount + hmtDayData.totalTransactionAmount, ); allNetworkDataItem.dailyHMTData[index].totalTransactionCount += hmtDayData.totalTransactionCount; } }); dataItem.dailyPaymentsData.forEach((paymentDayData) => { const index = allNetworkDataItem.dailyPaymentsData.findIndex( - (d) => d.timestamp.getTime() === paymentDayData.timestamp.getTime() + (d) => d.timestamp.getTime() === paymentDayData.timestamp.getTime(), ); if (index === -1) { - allNetworkDataItem.dailyPaymentsData.push(paymentDayData); + allNetworkDataItem.dailyPaymentsData.push({ ...paymentDayData }); } else { allNetworkDataItem.dailyPaymentsData[index].totalAmountPaid = addBigInts( allNetworkDataItem.dailyPaymentsData[index].totalAmountPaid, - paymentDayData.totalAmountPaid + paymentDayData.totalAmountPaid, ); allNetworkDataItem.dailyPaymentsData[index].totalCount += paymentDayData.totalCount; allNetworkDataItem.dailyPaymentsData[index].averageAmountPerWorker = addBigInts( allNetworkDataItem.dailyPaymentsData[index].averageAmountPerWorker, - paymentDayData.averageAmountPerWorker + paymentDayData.averageAmountPerWorker, ); } }); @@ -137,15 +146,15 @@ export default { allNetworkDataItem.totalHolders += dataItem.totalHolders; allNetworkDataItem.totalTransferAmount = addBigInts( allNetworkDataItem.totalTransferAmount, - dataItem.totalTransferAmount + dataItem.totalTransferAmount, ); allNetworkDataItem.totalSupply = addBigInts(allNetworkDataItem.totalSupply, dataItem.totalSupply); } allNetworkDataItem.dailyHMTData = allNetworkDataItem.dailyHMTData.sort( - (a, b) => b.timestamp.getTime() - a.timestamp.getTime() + (a, b) => b.timestamp.getTime() - a.timestamp.getTime(), ); allNetworkDataItem.dailyPaymentsData = allNetworkDataItem.dailyPaymentsData.sort( - (a, b) => b.timestamp.getTime() - a.timestamp.getTime() + (a, b) => b.timestamp.getTime() - a.timestamp.getTime(), ); const networkDataItems = [allNetworkDataItem, ...dataItems]; @@ -168,10 +177,101 @@ export default { } catch (err) { console.log(err); } + + console.log('sync daily task summary started...'); + try { + const uid = 'api::daily-task-summary.daily-task-summary'; + + const date = dayjs().format('YYYY-MM-DD'); + + const { data } = await axios.get('/support/summary-stats', { + baseURL: 'https://foundation-accounts.hmt.ai', + method: 'GET', + params: { + start_date: date, + end_date: date, + api_key: process.env.HCAPTCHA_LABELING_STAFF_API_KEY, + }, + }); + + const dailyData = { + date, + served_count: data[date]?.served ?? 0, + solved_count: data[date]?.solved ?? 0, + }; + + const entries = await strapi.entityService.findMany(uid, { + filters: { date }, + }); + + if (entries.length > 0) { + await strapi.entityService.update(uid, entries[0].id, { + data: dailyData, + }); + } else { + await strapi.entityService.create(uid, { data: dailyData }); + } + + console.log('sync daily task summary ended...'); + } catch (err) { + console.log(err); + } }, options: { // Every 1 minute rule: '*/1 * * * *', }, }, + syncMonthlySummaryData: { + task: async ({ strapi }) => { + const uid = 'api::monthly-task-summary.monthly-task-summary'; + const entries = await strapi.entityService.findMany(uid); + + let startDate = dayjs('2022-07-01'); + const currentDate = dayjs().subtract(1, 'month').endOf('month'); + const dates = []; + + while (startDate <= currentDate) { + const from = startDate.startOf('month').format('YYYY-MM-DD'); + const to = startDate.endOf('month').format('YYYY-MM-DD'); + + const entry = entries.find((e) => e.date === to); + if (!entry) { + dates.push({ from, to }); + } + + startDate = startDate.add(1, 'month'); + } + + const results = await Promise.all( + dates.map(({ from, to }) => + axios + .get('/support/summary-stats', { + baseURL: 'https://foundation-accounts.hmt.ai', + method: 'GET', + params: { + start_date: from, + end_date: to, + api_key: process.env.HCAPTCHA_LABELING_STAFF_API_KEY, + }, + }) + .then((res) => res.data), + ), + ); + + const entriesToCreate = results.map((r, i) => ({ + date: dates[i].to, + served_count: r.total.served, + solved_count: r.total.solved, + })); + + if (entriesToCreate.length > 0) { + await strapi.db.query(uid).createMany({ data: entriesToCreate }); + } + }, + options: { + // Monthly + rule: '0 0 1 * *', + }, + }, }; diff --git a/packages/apps/dashboard/admin/package.json b/packages/apps/dashboard/admin/package.json index cf2eb98a46..4640b3ad74 100644 --- a/packages/apps/dashboard/admin/package.json +++ b/packages/apps/dashboard/admin/package.json @@ -6,6 +6,7 @@ "scripts": { "develop": "strapi develop", "start": "strapi start", + "prebuild": "yarn workspace @human-protocol/core install && yarn workspace @human-protocol/sdk install && yarn workspace @human-protocol/sdk build", "build": "strapi build", "strapi": "strapi" }, diff --git a/packages/apps/dashboard/admin/src/api/daily-task-summary/content-types/daily-task-summary/schema.json b/packages/apps/dashboard/admin/src/api/daily-task-summary/content-types/daily-task-summary/schema.json new file mode 100644 index 0000000000..3c7df5a090 --- /dev/null +++ b/packages/apps/dashboard/admin/src/api/daily-task-summary/content-types/daily-task-summary/schema.json @@ -0,0 +1,24 @@ +{ + "kind": "collectionType", + "collectionName": "daily_task_summaries", + "info": { + "singularName": "daily-task-summary", + "pluralName": "daily-task-summaries", + "displayName": "DailyTaskSummary" + }, + "options": { + "draftAndPublish": false, + "comment": "" + }, + "attributes": { + "date": { + "type": "string" + }, + "served_count": { + "type": "integer" + }, + "solved_count": { + "type": "integer" + } + } +} diff --git a/packages/apps/dashboard/admin/src/api/daily-task-summary/controllers/daily-task-summary.ts b/packages/apps/dashboard/admin/src/api/daily-task-summary/controllers/daily-task-summary.ts new file mode 100644 index 0000000000..4d3e0a51f6 --- /dev/null +++ b/packages/apps/dashboard/admin/src/api/daily-task-summary/controllers/daily-task-summary.ts @@ -0,0 +1,62 @@ +/** + * daily-task-summary controller + */ + +import { factories } from '@strapi/strapi'; +import axios from 'axios'; +import dayjs from 'dayjs'; + +export default factories.createCoreController('api::daily-task-summary.daily-task-summary', ({ strapi }) => ({ + async syncPreviousData(ctx) { + const uid = 'api::daily-task-summary.daily-task-summary'; + + let startDate = dayjs('2022-07-01'); + const currentDate = dayjs(); + const dates = []; + + while (startDate <= currentDate) { + const from = startDate.startOf('month').format('YYYY-MM-DD'); + const to = startDate.endOf('month').format('YYYY-MM-DD'); + + dates.push({ from, to }); + + startDate = startDate.add(1, 'month'); + } + + const results = await Promise.all( + dates.map(({ from, to }) => + axios + .get('/support/summary-stats', { + baseURL: 'https://foundation-accounts.hmt.ai', + method: 'GET', + params: { + start_date: from, + end_date: to, + api_key: process.env.HCAPTCHA_LABELING_STAFF_API_KEY, + }, + }) + .then((res) => res.data), + ), + ); + + const entriesToCreate = []; + results.forEach((result, i) => { + Object.keys(result).forEach((date) => { + if (date === 'total') return; + entriesToCreate.push({ + date, + served_count: result[date].served, + solved_count: result[date].solved, + }); + }); + }); + + if (entriesToCreate.length > 0) { + await strapi.db.query(uid).createMany({ data: entriesToCreate }); + } + + ctx.body = { + length: entriesToCreate.length, + }; + }, +})); diff --git a/packages/apps/dashboard/admin/src/api/daily-task-summary/routes/01-custom-daily-task-summary.ts b/packages/apps/dashboard/admin/src/api/daily-task-summary/routes/01-custom-daily-task-summary.ts new file mode 100644 index 0000000000..691d54ecc9 --- /dev/null +++ b/packages/apps/dashboard/admin/src/api/daily-task-summary/routes/01-custom-daily-task-summary.ts @@ -0,0 +1,9 @@ +module.exports = { + routes: [ + { + method: 'POST', + path: '/daily-task-summary/sync-past', + handler: 'daily-task-summary.syncPreviousData', + }, + ], +}; diff --git a/packages/apps/dashboard/admin/src/api/daily-task-summary/routes/daily-task-summary.ts b/packages/apps/dashboard/admin/src/api/daily-task-summary/routes/daily-task-summary.ts new file mode 100644 index 0000000000..c7d7afd96e --- /dev/null +++ b/packages/apps/dashboard/admin/src/api/daily-task-summary/routes/daily-task-summary.ts @@ -0,0 +1,7 @@ +/** + * daily-task-summary router + */ + +import { factories } from '@strapi/strapi'; + +export default factories.createCoreRouter('api::daily-task-summary.daily-task-summary'); diff --git a/packages/apps/dashboard/admin/src/api/daily-task-summary/services/daily-task-summary.ts b/packages/apps/dashboard/admin/src/api/daily-task-summary/services/daily-task-summary.ts new file mode 100644 index 0000000000..460dd26743 --- /dev/null +++ b/packages/apps/dashboard/admin/src/api/daily-task-summary/services/daily-task-summary.ts @@ -0,0 +1,7 @@ +/** + * daily-task-summary service + */ + +import { factories } from '@strapi/strapi'; + +export default factories.createCoreService('api::daily-task-summary.daily-task-summary'); diff --git a/packages/apps/dashboard/admin/types/generated/contentTypes.d.ts b/packages/apps/dashboard/admin/types/generated/contentTypes.d.ts index 71c173abb9..74f8afacab 100644 --- a/packages/apps/dashboard/admin/types/generated/contentTypes.d.ts +++ b/packages/apps/dashboard/admin/types/generated/contentTypes.d.ts @@ -512,6 +512,30 @@ export interface PluginUsersPermissionsUser extends Schema.CollectionType { }; } +export interface ApiDailyTaskSummaryDailyTaskSummary extends Schema.CollectionType { + collectionName: 'daily_task_summaries'; + info: { + singularName: 'daily-task-summary'; + pluralName: 'daily-task-summaries'; + displayName: 'DailyTaskSummary'; + }; + options: { + draftAndPublish: false; + comment: ''; + }; + attributes: { + date: Attribute.String; + served_count: Attribute.Integer; + solved_count: Attribute.Integer; + createdAt: Attribute.DateTime; + updatedAt: Attribute.DateTime; + createdBy: Attribute.Relation<'api::daily-task-summary.daily-task-summary', 'oneToOne', 'admin::user'> & + Attribute.Private; + updatedBy: Attribute.Relation<'api::daily-task-summary.daily-task-summary', 'oneToOne', 'admin::user'> & + Attribute.Private; + }; +} + export interface ApiMonthlyTaskSummaryMonthlyTaskSummary extends Schema.CollectionType { collectionName: 'monthly_task_summaries'; info: { @@ -603,6 +627,7 @@ declare module '@strapi/types' { 'plugin::users-permissions.permission': PluginUsersPermissionsPermission; 'plugin::users-permissions.role': PluginUsersPermissionsRole; 'plugin::users-permissions.user': PluginUsersPermissionsUser; + 'api::daily-task-summary.daily-task-summary': ApiDailyTaskSummaryDailyTaskSummary; 'api::monthly-task-summary.monthly-task-summary': ApiMonthlyTaskSummaryMonthlyTaskSummary; 'api::network-data-item.network-data-item': ApiNetworkDataItemNetworkDataItem; 'api::news-item.news-item': ApiNewsItemNewsItem; From 1753970e14c210a6500453d0d927e7fda643bda0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20L=C3=B3pez?= <50665615+flopez7@users.noreply.github.com> Date: Fri, 26 Jan 2024 13:01:55 +0100 Subject: [PATCH 060/104] Remove confirm from ValidatePasswordDTO in Reputation Oracle (#1534) --- .../server/src/common/validators/confirm.ts | 67 ------------------- .../server/src/common/validators/index.ts | 1 - .../server/src/modules/auth/auth.dto.ts | 6 +- .../src/modules/auth/auth.service.spec.ts | 3 - .../src/modules/user/user.service.spec.ts | 2 - 5 files changed, 1 insertion(+), 78 deletions(-) delete mode 100644 packages/apps/reputation-oracle/server/src/common/validators/confirm.ts diff --git a/packages/apps/reputation-oracle/server/src/common/validators/confirm.ts b/packages/apps/reputation-oracle/server/src/common/validators/confirm.ts deleted file mode 100644 index d7f54c87bd..0000000000 --- a/packages/apps/reputation-oracle/server/src/common/validators/confirm.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { - registerDecorator, - ValidationArguments, - ValidationOptions, - ValidatorConstraint, - ValidatorConstraintInterface, -} from 'class-validator'; - -interface IConfirmConstraints { - required: boolean; - relatedPropertyName: string; -} - -@ValidatorConstraint() -class ValidateConfirm implements ValidatorConstraintInterface { - private reason: string; - - public validate(value: unknown, args: ValidationArguments): boolean { - this.reason = ValidateConfirm.isValid(value, args); - return !this.reason; - } - - public defaultMessage(): string { - return this.reason; - } - - private static isValid(value: unknown, args: ValidationArguments): string { - const { relatedPropertyName = 'password' }: IConfirmConstraints = - args.constraints[0]; - - const relatedValue = (args.object as any)[relatedPropertyName]; - - if (typeof value === 'undefined' || value === '') { - if (relatedValue) { - return 'valueMissing'; - } else { - return ''; - } - } - - if (typeof value !== 'string') { - return 'typeMismatch'; - } - - if (relatedValue !== value) { - return 'badInput'; - } - - return ''; - } -} - -export function IsConfirm( - constraints: Partial = {}, - validationOptions?: ValidationOptions, -) { - return (object: Record, propertyName: string): void => { - registerDecorator({ - name: 'isConfirm', - target: object.constructor, - propertyName, - constraints: [constraints], - options: validationOptions, - validator: ValidateConfirm, - }); - }; -} 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 4bf70483ee..47b7e7a263 100644 --- a/packages/apps/reputation-oracle/server/src/common/validators/index.ts +++ b/packages/apps/reputation-oracle/server/src/common/validators/index.ts @@ -1,2 +1 @@ export * from './password'; -export * from './confirm'; diff --git a/packages/apps/reputation-oracle/server/src/modules/auth/auth.dto.ts b/packages/apps/reputation-oracle/server/src/modules/auth/auth.dto.ts index 768d808c76..07f77d0a6b 100644 --- a/packages/apps/reputation-oracle/server/src/modules/auth/auth.dto.ts +++ b/packages/apps/reputation-oracle/server/src/modules/auth/auth.dto.ts @@ -1,7 +1,7 @@ import { ApiProperty } from '@nestjs/swagger'; import { Transform } from 'class-transformer'; import { IsEmail, IsEnum, IsString, Matches } from 'class-validator'; -import { IsConfirm, IsPassword } from '../../common/validators'; +import { IsPassword } from '../../common/validators'; import { TokenType } from '../auth/token.entity'; import { UserEntity } from '../user/user.entity'; import { UserType } from '../../common/enums/user'; @@ -32,10 +32,6 @@ export class ValidatePasswordDto { @ApiProperty() @IsPassword() public password: string; - - @ApiProperty() - @IsConfirm() - public confirm: string; } export class ResendEmailVerificationDto { diff --git a/packages/apps/reputation-oracle/server/src/modules/auth/auth.service.spec.ts b/packages/apps/reputation-oracle/server/src/modules/auth/auth.service.spec.ts index 2015acfd67..e101c90648 100644 --- a/packages/apps/reputation-oracle/server/src/modules/auth/auth.service.spec.ts +++ b/packages/apps/reputation-oracle/server/src/modules/auth/auth.service.spec.ts @@ -161,7 +161,6 @@ describe('AuthService', () => { const userCreateDto = { email: MOCK_EMAIL, password: MOCK_PASSWORD, - confirm: MOCK_PASSWORD, type: UserType.WORKER, }; @@ -456,7 +455,6 @@ describe('AuthService', () => { authService.restorePassword({ token: 'token', password: 'password', - confirm: 'password', }), ).rejects.toThrow(NotFoundException); }); @@ -472,7 +470,6 @@ describe('AuthService', () => { await authService.restorePassword({ token: 'token', password: 'password', - confirm: 'password', }); expect(updatePasswordMock).toHaveBeenCalled(); diff --git a/packages/apps/reputation-oracle/server/src/modules/user/user.service.spec.ts b/packages/apps/reputation-oracle/server/src/modules/user/user.service.spec.ts index cfb75542b5..004c3236f1 100644 --- a/packages/apps/reputation-oracle/server/src/modules/user/user.service.spec.ts +++ b/packages/apps/reputation-oracle/server/src/modules/user/user.service.spec.ts @@ -61,7 +61,6 @@ describe('UserService', () => { const dto: UserCreateDto = { email: 'test@example.com', password: 'password123', - confirm: 'password123', type: UserType.WORKER, }; const hashedPassword = @@ -94,7 +93,6 @@ describe('UserService', () => { const dto: UserCreateDto = { email: 'test@example.com', password: 'password123', - confirm: 'password123', type: UserType.WORKER, }; From e347920fc6890eaeb28e5458ec2006a00aa3a22e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20L=C3=B3pez?= <50665615+flopez7@users.noreply.github.com> Date: Fri, 26 Jan 2024 13:35:22 +0100 Subject: [PATCH 061/104] [Recording Oracle][Exchange Oracle] Submissions rejected (#1482) * Modification of the body of the webhook endpoint to adapt it to the new human architecture * Change dtos to snake_case in Reputation and Exchange Oracles * Update enpoint in Exchange Oracle to create a new one to receive webhooks * Move webhook endpoint from job controller to a new webhook module --- .../exchange-oracle/server/src/app.module.ts | 8 + .../server/src/common/enums/webhook.ts | 1 + .../src/common/interceptors/snake-case.ts | 28 ++++ .../server/src/common/utils/case-converter.ts | 31 ++++ .../src/modules/job/job.controller.spec.ts | 35 +--- .../server/src/modules/job/job.controller.ts | 37 +--- .../server/src/modules/job/job.dto.ts | 33 +--- .../src/modules/job/job.service.spec.ts | 14 +- .../server/src/modules/job/job.service.ts | 81 +++++---- .../src/modules/storage/storage.module.ts | 3 +- .../webhook/webhook.controller.spec.ts | 158 ++++++++++++++++++ .../src/modules/webhook/webhook.controller.ts | 26 +++ .../server/src/modules/webhook/webhook.dto.ts | 43 +++++ .../src/modules/webhook/webhook.module.ts | 12 ++ .../modules/webhook/webhook.service.spec.ts | 148 ++++++++++++++++ .../src/modules/webhook/webhook.service.ts | 29 ++++ .../recording-oracle/src/app.module.ts | 7 +- .../src/common/constants/index.ts | 1 - .../src/common/enums/webhook.ts | 6 + .../src/common/interceptors/snake-case.ts | 28 ++++ .../src/common/utils/case-converter.ts | 31 ++++ .../src/common/utils/webhook.ts | 9 +- .../src/modules/job/job.dto.ts | 19 ++- .../src/modules/job/job.service.spec.ts | 56 ++++--- .../src/modules/job/job.service.ts | 47 ++++-- .../src/modules/storage/storage.module.ts | 9 +- 26 files changed, 714 insertions(+), 186 deletions(-) create mode 100644 packages/apps/fortune/exchange-oracle/server/src/common/interceptors/snake-case.ts create mode 100644 packages/apps/fortune/exchange-oracle/server/src/common/utils/case-converter.ts create mode 100644 packages/apps/fortune/exchange-oracle/server/src/modules/webhook/webhook.controller.spec.ts create mode 100644 packages/apps/fortune/exchange-oracle/server/src/modules/webhook/webhook.controller.ts create mode 100644 packages/apps/fortune/exchange-oracle/server/src/modules/webhook/webhook.dto.ts create mode 100644 packages/apps/fortune/exchange-oracle/server/src/modules/webhook/webhook.module.ts create mode 100644 packages/apps/fortune/exchange-oracle/server/src/modules/webhook/webhook.service.spec.ts create mode 100644 packages/apps/fortune/exchange-oracle/server/src/modules/webhook/webhook.service.ts create mode 100644 packages/apps/fortune/recording-oracle/src/common/enums/webhook.ts create mode 100644 packages/apps/fortune/recording-oracle/src/common/interceptors/snake-case.ts create mode 100644 packages/apps/fortune/recording-oracle/src/common/utils/case-converter.ts diff --git a/packages/apps/fortune/exchange-oracle/server/src/app.module.ts b/packages/apps/fortune/exchange-oracle/server/src/app.module.ts index 013f0fd0f2..d4a0073382 100644 --- a/packages/apps/fortune/exchange-oracle/server/src/app.module.ts +++ b/packages/apps/fortune/exchange-oracle/server/src/app.module.ts @@ -3,8 +3,16 @@ import { AppController } from './app.controller'; import { JobModule } from './modules/job/job.module'; import { ConfigModule } from '@nestjs/config'; import { envValidator } from './common/config'; +import { APP_INTERCEPTOR } from '@nestjs/core'; +import { SnakeCaseInterceptor } from './common/interceptors/snake-case'; @Module({ + providers: [ + { + provide: APP_INTERCEPTOR, + useClass: SnakeCaseInterceptor, + }, + ], imports: [ JobModule, ConfigModule.forRoot({ diff --git a/packages/apps/fortune/exchange-oracle/server/src/common/enums/webhook.ts b/packages/apps/fortune/exchange-oracle/server/src/common/enums/webhook.ts index 4773912237..89394e37c6 100644 --- a/packages/apps/fortune/exchange-oracle/server/src/common/enums/webhook.ts +++ b/packages/apps/fortune/exchange-oracle/server/src/common/enums/webhook.ts @@ -2,4 +2,5 @@ export enum EventType { ESCROW_CREATED = 'escrow_created', ESCROW_CANCELED = 'escrow_canceled', TASK_CREATION_FAILED = 'task_creation_failed', + SUBMISSION_REJECTED = 'submission_rejected', } diff --git a/packages/apps/fortune/exchange-oracle/server/src/common/interceptors/snake-case.ts b/packages/apps/fortune/exchange-oracle/server/src/common/interceptors/snake-case.ts new file mode 100644 index 0000000000..0d62e5f222 --- /dev/null +++ b/packages/apps/fortune/exchange-oracle/server/src/common/interceptors/snake-case.ts @@ -0,0 +1,28 @@ +import { + CallHandler, + ExecutionContext, + Injectable, + NestInterceptor, +} from '@nestjs/common'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { CaseConverter } from '../utils/case-converter'; + +@Injectable() +export class SnakeCaseInterceptor implements NestInterceptor { + intercept(context: ExecutionContext, next: CallHandler): Observable { + const request = context.switchToHttp().getRequest(); + + if (request.body) { + request.body = CaseConverter.transformToCamelCase(request.body); + } + + if (request.query) { + request.query = CaseConverter.transformToCamelCase(request.query); + } + + return next + .handle() + .pipe(map((data) => CaseConverter.transformToSnakeCase(data))); + } +} diff --git a/packages/apps/fortune/exchange-oracle/server/src/common/utils/case-converter.ts b/packages/apps/fortune/exchange-oracle/server/src/common/utils/case-converter.ts new file mode 100644 index 0000000000..980ddc589a --- /dev/null +++ b/packages/apps/fortune/exchange-oracle/server/src/common/utils/case-converter.ts @@ -0,0 +1,31 @@ +export class CaseConverter { + static transformToCamelCase(obj: Record): Record { + const result: Record = {}; + for (const key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + const camelCaseKey = key.replace(/_([a-z])/g, (g) => + g[1].toUpperCase(), + ); + result[camelCaseKey] = obj[key]; + } + } + return result; + } + + static transformToSnakeCase(obj: any): any { + if (Array.isArray(obj)) { + return obj.map((item) => CaseConverter.transformToSnakeCase(item)); + } else if (typeof obj === 'object' && obj !== null) { + return Object.keys(obj).reduce( + (acc: Record, key: string) => { + const snakeCaseKey = key.replace(/([A-Z])/g, '_$1').toLowerCase(); + acc[snakeCaseKey] = CaseConverter.transformToSnakeCase(obj[key]); + return acc; + }, + {}, + ); + } else { + return obj; + } + } +} diff --git a/packages/apps/fortune/exchange-oracle/server/src/modules/job/job.controller.spec.ts b/packages/apps/fortune/exchange-oracle/server/src/modules/job/job.controller.spec.ts index ff5c364e5d..545c0a6077 100644 --- a/packages/apps/fortune/exchange-oracle/server/src/modules/job/job.controller.spec.ts +++ b/packages/apps/fortune/exchange-oracle/server/src/modules/job/job.controller.spec.ts @@ -1,12 +1,7 @@ -import { ConfigService } from '@nestjs/config'; -import { Test } from '@nestjs/testing'; -import { JobController } from './job.controller'; -import { JobService } from './job.service'; -import { InvalidJobDto, JobDetailsDto, SolveJobDto } from './job.dto'; -import { Web3Service } from '../web3/web3.service'; import { HttpService } from '@nestjs/axios'; +import { ConfigModule, ConfigService, registerAs } from '@nestjs/config'; +import { Test } from '@nestjs/testing'; import { of } from 'rxjs'; -import { ConfigModule, registerAs } from '@nestjs/config'; import { MOCK_REPUTATION_ORACLE_WEBHOOK_URL, MOCK_S3_ACCESS_KEY, @@ -15,10 +10,12 @@ import { MOCK_S3_PORT, MOCK_S3_SECRET_KEY, MOCK_S3_USE_SSL, - MOCK_SIGNATURE, } from '../../../test/constants'; import { StorageService } from '../storage/storage.service'; -import { verifySignature } from '../../common/utils/signature'; +import { Web3Service } from '../web3/web3.service'; +import { JobController } from './job.controller'; +import { JobDetailsDto, SolveJobDto } from './job.dto'; +import { JobService } from './job.service'; jest.mock('../../common/utils/signature'); @@ -150,24 +147,4 @@ describe('JobController', () => { ); }); }); - - describe('invalidJobSolution-solution', () => { - it('should mark a job solution as invalid', async () => { - const solveJobDto: InvalidJobDto = { - chainId, - escrowAddress, - workerAddress, - }; - - jest.spyOn(jobService, 'processInvalidJobSolution').mockResolvedValue(); - - (verifySignature as jest.Mock).mockReturnValue(true); - - await jobController.invalidJobSolution(MOCK_SIGNATURE, solveJobDto); - - expect(jobService.processInvalidJobSolution).toHaveBeenCalledWith( - solveJobDto, - ); - }); - }); }); diff --git a/packages/apps/fortune/exchange-oracle/server/src/modules/job/job.controller.ts b/packages/apps/fortune/exchange-oracle/server/src/modules/job/job.controller.ts index 205ae6ed74..ceea64703c 100644 --- a/packages/apps/fortune/exchange-oracle/server/src/modules/job/job.controller.ts +++ b/packages/apps/fortune/exchange-oracle/server/src/modules/job/job.controller.ts @@ -1,37 +1,25 @@ -import { - Body, - Controller, - Get, - Param, - Patch, - Post, - UseGuards, - Headers, -} from '@nestjs/common'; +import { Body, Controller, Get, Param, Post } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; +import { JobDetailsDto, SolveJobDto } from './job.dto'; import { JobService } from './job.service'; -import { InvalidJobDto, JobDetailsDto, SolveJobDto } from './job.dto'; -import { SignatureAuthGuard } from '../../common/guards'; -import { Role } from '../../common/enums/role'; -import { HEADER_SIGNATURE_KEY } from '../../common/constant'; @ApiTags('Job') @Controller('job') export class JobController { constructor(private readonly jobService: JobService) {} - @Get('details/:chainId/:escrowAddress') + @Get('details/:chain_id/:escrow_address') getDetails( - @Param('chainId') chainId: number, - @Param('escrowAddress') escrowAddress: string, + @Param('chain_id') chainId: number, + @Param('escrow_address') escrowAddress: string, ): Promise { return this.jobService.getDetails(chainId, escrowAddress); } - @Get('pending/:chainId/:workerAddress') + @Get('pending/:chain_id/:worker_address') getPendingJobs( - @Param('chainId') chainId: number, - @Param('workerAddress') workerAddress: string, + @Param('chain_id') chainId: number, + @Param('worker_address') workerAddress: string, ): Promise { return this.jobService.getPendingJobs(chainId, workerAddress); } @@ -45,13 +33,4 @@ export class JobController { body.solution, ); } - - @UseGuards(new SignatureAuthGuard([Role.Recording, Role.Reputation])) - @Patch('invalid-solution') - invalidJobSolution( - @Headers(HEADER_SIGNATURE_KEY) _: string, - @Body() body: InvalidJobDto, - ): Promise { - return this.jobService.processInvalidJobSolution(body); - } } diff --git a/packages/apps/fortune/exchange-oracle/server/src/modules/job/job.dto.ts b/packages/apps/fortune/exchange-oracle/server/src/modules/job/job.dto.ts index bb8381cb1c..e121503f9b 100644 --- a/packages/apps/fortune/exchange-oracle/server/src/modules/job/job.dto.ts +++ b/packages/apps/fortune/exchange-oracle/server/src/modules/job/job.dto.ts @@ -1,8 +1,7 @@ -import { ApiProperty } from '@nestjs/swagger'; import { ChainId } from '@human-protocol/sdk'; +import { ApiProperty } from '@nestjs/swagger'; import { IsEnum, IsString } from 'class-validator'; import { IsValidEthereumAddress } from '../../common/validators'; -import { EventType } from '../../common/enums/webhook'; export class ManifestDto { requesterTitle: string; @@ -18,18 +17,19 @@ export class JobDetailsDto { } export class SolveJobDto { - @ApiProperty() + @ApiProperty({ name: 'escrow_address' }) @IsString() @IsValidEthereumAddress() public escrowAddress: string; @ApiProperty({ enum: ChainId, + name: 'chain_id', }) @IsEnum(ChainId) public chainId: ChainId; - @ApiProperty() + @ApiProperty({ name: 'worker_address' }) @IsString() @IsValidEthereumAddress() public workerAddress: string; @@ -38,28 +38,3 @@ export class SolveJobDto { @IsString() public solution: string; } - -export class InvalidJobDto { - @ApiProperty() - @IsString() - @IsValidEthereumAddress() - public escrowAddress: string; - - @ApiProperty({ - enum: ChainId, - }) - @IsEnum(ChainId) - public chainId: ChainId; - - @ApiProperty() - @IsString() - @IsValidEthereumAddress() - workerAddress: string; -} - -export class EscrowFailedWebhookDto { - public chain_id: ChainId; - public escrow_address: string; - public event_type: EventType; - public reason: string; -} diff --git a/packages/apps/fortune/exchange-oracle/server/src/modules/job/job.service.spec.ts b/packages/apps/fortune/exchange-oracle/server/src/modules/job/job.service.spec.ts index cf0c154781..8bd898e8c7 100644 --- a/packages/apps/fortune/exchange-oracle/server/src/modules/job/job.service.spec.ts +++ b/packages/apps/fortune/exchange-oracle/server/src/modules/job/job.service.spec.ts @@ -219,7 +219,7 @@ describe('JobService', () => { escrow_address: escrowAddress, chain_id: chainId, event_type: EventType.TASK_CREATION_FAILED, - reason: 'Unable to get manifest', + event_data: [{ reason: 'Unable to get manifest' }], }; expect(httpServicePostMock).toHaveBeenCalledWith( JOB_LAUNCHER_WEBHOOK_URL + ESCROW_FAILED_ENDPOINT, @@ -385,9 +385,9 @@ describe('JobService', () => { 'solution', ); const expectedBody = { - escrowAddress, - chainId, - solutionsUrl, + escrow_address: escrowAddress, + chain_id: chainId, + solutions_url: solutionsUrl, }; expect(web3Service.getSigner).toHaveBeenCalledWith(chainId); expect(httpServicePostMock).toHaveBeenCalledWith( @@ -526,7 +526,8 @@ describe('JobService', () => { await jobService.processInvalidJobSolution({ chainId, escrowAddress, - workerAddress, + eventType: EventType.SUBMISSION_REJECTED, + eventData: [{ assigneeId: workerAddress }], }); expect(storageService.uploadJobSolutions).toHaveBeenCalledWith( @@ -561,7 +562,8 @@ describe('JobService', () => { jobService.processInvalidJobSolution({ chainId, escrowAddress, - workerAddress, + eventType: EventType.SUBMISSION_REJECTED, + eventData: [{ assigneeId: workerAddress }], }), ).rejects.toThrow(`Solution not found in Escrow: ${escrowAddress}`); }); diff --git a/packages/apps/fortune/exchange-oracle/server/src/modules/job/job.service.ts b/packages/apps/fortune/exchange-oracle/server/src/modules/job/job.service.ts index 33372a0993..a62e9a91f6 100644 --- a/packages/apps/fortune/exchange-oracle/server/src/modules/job/job.service.ts +++ b/packages/apps/fortune/exchange-oracle/server/src/modules/job/job.service.ts @@ -26,12 +26,9 @@ import { EventType } from '../../common/enums/webhook'; import { signMessage } from '../../common/utils/signature'; import { StorageService } from '../storage/storage.service'; import { Web3Service } from '../web3/web3.service'; -import { - EscrowFailedWebhookDto, - InvalidJobDto, - JobDetailsDto, - ManifestDto, -} from './job.dto'; +import { JobDetailsDto, ManifestDto } from './job.dto'; +import { CaseConverter } from '../../common/utils/case-converter'; +import { WebhookDto } from '../webhook/webhook.dto'; @Injectable() export class JobService { @@ -99,9 +96,8 @@ export class JobService { ): Promise { const signer = this.web3Service.getSigner(chainId); const escrowClient = await EscrowClient.build(signer); - const recordingOracleAddress = await escrowClient.getRecordingOracleAddress( - escrowAddress, - ); + const recordingOracleAddress = + await escrowClient.getRecordingOracleAddress(escrowAddress); const stakingClient = await StakingClient.build(signer); const leader = await stakingClient.getLeader(recordingOracleAddress); @@ -125,30 +121,34 @@ export class JobService { } public async processInvalidJobSolution( - invalidJobSolution: InvalidJobDto, + invalidJobSolution: WebhookDto, ): Promise { - const existingJobSolutions = await this.storageService.downloadJobSolutions( - invalidJobSolution.escrowAddress, - invalidJobSolution.chainId, - ); + if (invalidJobSolution.eventData) { + const existingJobSolutions = + await this.storageService.downloadJobSolutions( + invalidJobSolution.escrowAddress, + invalidJobSolution.chainId, + ); + for (const invalidSolution of invalidJobSolution.eventData) { + const foundSolution = existingJobSolutions.find( + (sol) => sol.workerAddress === invalidSolution.assigneeId, + ); - const foundSolution = existingJobSolutions.find( - (sol) => sol.workerAddress === invalidJobSolution.workerAddress, - ); + if (foundSolution) { + foundSolution.error = true; + } else { + throw new BadRequestException( + `Solution not found in Escrow: ${invalidJobSolution.escrowAddress}`, + ); + } + } - if (foundSolution) { - foundSolution.error = true; - } else { - throw new BadRequestException( - `Solution not found in Escrow: ${invalidJobSolution.escrowAddress}`, + await this.storageService.uploadJobSolutions( + invalidJobSolution.escrowAddress, + invalidJobSolution.chainId, + existingJobSolutions, ); } - - await this.storageService.uploadJobSolutions( - invalidJobSolution.escrowAddress, - invalidJobSolution.chainId, - existingJobSolutions, - ); } private async addSolution( @@ -196,11 +196,12 @@ export class JobService { } private async sendWebhook(url: string, body: any): Promise { + const snake_case_body = CaseConverter.transformToSnakeCase(body); const signedBody = await signMessage( - body, + snake_case_body, this.configService.get(ConfigNames.WEB3_PRIVATE_KEY)!, ); - await this.httpService.post(url, body, { + await this.httpService.post(url, snake_case_body, { headers: { [HEADER_SIGNATURE_KEY]: signedBody }, }); } @@ -212,9 +213,8 @@ export class JobService { const signer = this.web3Service.getSigner(chainId); const escrowClient = await EscrowClient.build(signer); const manifestUrl = await escrowClient.getManifestUrl(escrowAddress); - const manifestEncrypted = await StorageClient.downloadFileFromUrl( - manifestUrl, - ); + const manifestEncrypted = + await StorageClient.downloadFileFromUrl(manifestUrl); let manifest: ManifestDto | null; @@ -240,9 +240,8 @@ export class JobService { if (!manifest) { const signer = this.web3Service.getSigner(chainId); const escrowClient = await EscrowClient.build(signer); - const jobLauncherAddress = await escrowClient.getJobLauncherAddress( - escrowAddress, - ); + const jobLauncherAddress = + await escrowClient.getJobLauncherAddress(escrowAddress); const stakingClient = await StakingClient.build(signer); const jobLauncher = await stakingClient.getLeader(jobLauncherAddress); const jobLauncherWebhookUrl = jobLauncher?.webhookUrl; @@ -251,11 +250,11 @@ export class JobService { throw new NotFoundException('Unable to get Job Launcher webhook URL'); } - const body: EscrowFailedWebhookDto = { - escrow_address: escrowAddress, - chain_id: chainId, - event_type: EventType.TASK_CREATION_FAILED, - reason: 'Unable to get manifest', + const body: WebhookDto = { + escrowAddress: escrowAddress, + chainId: chainId, + eventType: EventType.TASK_CREATION_FAILED, + eventData: [{ reason: 'Unable to get manifest' }], }; await this.sendWebhook( jobLauncherWebhookUrl + ESCROW_FAILED_ENDPOINT, diff --git a/packages/apps/fortune/exchange-oracle/server/src/modules/storage/storage.module.ts b/packages/apps/fortune/exchange-oracle/server/src/modules/storage/storage.module.ts index f9cf1659df..47be30be9c 100644 --- a/packages/apps/fortune/exchange-oracle/server/src/modules/storage/storage.module.ts +++ b/packages/apps/fortune/exchange-oracle/server/src/modules/storage/storage.module.ts @@ -2,9 +2,10 @@ import { Module } from '@nestjs/common'; import { StorageService } from './storage.service'; import { ConfigModule } from '@nestjs/config'; import { s3Config } from '../../common/config'; +import { Web3Module } from '../web3/web3.module'; @Module({ - imports: [ConfigModule.forFeature(s3Config)], + imports: [ConfigModule.forFeature(s3Config), Web3Module], providers: [StorageService], exports: [StorageService], }) diff --git a/packages/apps/fortune/exchange-oracle/server/src/modules/webhook/webhook.controller.spec.ts b/packages/apps/fortune/exchange-oracle/server/src/modules/webhook/webhook.controller.spec.ts new file mode 100644 index 0000000000..f2a504b057 --- /dev/null +++ b/packages/apps/fortune/exchange-oracle/server/src/modules/webhook/webhook.controller.spec.ts @@ -0,0 +1,158 @@ +import { ConfigService } from '@nestjs/config'; +import { Test } from '@nestjs/testing'; +import { WebhookController } from './webhook.controller'; +import { WebhookService } from './webhook.service'; +import { WebhookDto } from './webhook.dto'; +import { Web3Service } from '../web3/web3.service'; +import { HttpService } from '@nestjs/axios'; +import { of } from 'rxjs'; +import { ConfigModule, registerAs } from '@nestjs/config'; +import { + MOCK_REPUTATION_ORACLE_WEBHOOK_URL, + MOCK_S3_ACCESS_KEY, + MOCK_S3_BUCKET, + MOCK_S3_ENDPOINT, + MOCK_S3_PORT, + MOCK_S3_SECRET_KEY, + MOCK_S3_USE_SSL, + MOCK_SIGNATURE, +} from '../../../test/constants'; +import { StorageService } from '../storage/storage.service'; +import { verifySignature } from '../../common/utils/signature'; +import { EventType } from '../../common/enums/webhook'; +import { JobService } from '../job/job.service'; + +jest.mock('../../common/utils/signature'); + +describe('webhookController', () => { + let webhookController: WebhookController; + let webhookService: WebhookService; + + const chainId = 1; + const escrowAddress = '0x1234567890123456789012345678901234567890'; + const workerAddress = '0x1234567890123456789012345678901234567891'; + + const reputationOracleURL = 'https://example.com/reputationoracle'; + const configServiceMock = { + get: jest.fn().mockReturnValue(reputationOracleURL), + }; + + beforeAll(async () => { + const moduleRef = await Test.createTestingModule({ + imports: [ + ConfigModule.forFeature( + registerAs('s3', () => ({ + accessKey: MOCK_S3_ACCESS_KEY, + secretKey: MOCK_S3_SECRET_KEY, + endPoint: MOCK_S3_ENDPOINT, + port: MOCK_S3_PORT, + useSSL: MOCK_S3_USE_SSL, + bucket: MOCK_S3_BUCKET, + })), + ), + ConfigModule.forFeature( + registerAs('server', () => ({ + reputationOracleWebhookUrl: MOCK_REPUTATION_ORACLE_WEBHOOK_URL, + })), + ), + ], + controllers: [WebhookController], + providers: [ + WebhookService, + JobService, + { + provide: ConfigService, + useValue: configServiceMock, + }, + { + provide: Web3Service, + useValue: { + getSigner: jest.fn().mockReturnValue({ + address: '0x1234567890123456789012345678901234567892', + getNetwork: jest.fn().mockResolvedValue({ chainId: 1 }), + }), + }, + }, + StorageService, + { + provide: HttpService, + useValue: { + post: jest.fn().mockReturnValue(of({ status: 200, data: {} })), + }, + }, + ], + }).compile(); + + webhookController = moduleRef.get(WebhookController); + webhookService = moduleRef.get(WebhookService); + }); + + describe('processWebhook', () => { + afterEach(() => { + jest.restoreAllMocks(); + }); + it('should handle an incoming escrow created webhook', async () => { + const webhook: WebhookDto = { + chainId, + escrowAddress, + eventType: EventType.ESCROW_CREATED, + }; + jest.spyOn(webhookService, 'handleWebhook'); + + (verifySignature as jest.Mock).mockReturnValue(true); + + await webhookController.processWebhook(MOCK_SIGNATURE, webhook); + + expect(webhookService.handleWebhook).toHaveBeenCalledWith(webhook); + }); + + it('should handle an incoming escrow canceled webhook', async () => { + const webhook: WebhookDto = { + chainId, + escrowAddress, + eventType: EventType.ESCROW_CANCELED, + }; + jest.spyOn(webhookService, 'handleWebhook'); + + (verifySignature as jest.Mock).mockReturnValue(true); + + await webhookController.processWebhook(MOCK_SIGNATURE, webhook); + + expect(webhookService.handleWebhook).toHaveBeenCalledWith(webhook); + }); + + it('should mark a webhook solution as invalid', async () => { + const webhook: WebhookDto = { + chainId, + escrowAddress, + eventType: EventType.SUBMISSION_REJECTED, + eventData: [{ assigneeId: workerAddress }], + }; + + jest.spyOn(webhookService, 'handleWebhook').mockResolvedValue(); + + (verifySignature as jest.Mock).mockReturnValue(true); + + await webhookController.processWebhook(MOCK_SIGNATURE, webhook); + + expect(webhookService.handleWebhook).toHaveBeenCalledWith(webhook); + }); + + it('should return an error when the event type is invalid', async () => { + const webhook: WebhookDto = { + chainId, + escrowAddress, + eventType: EventType.TASK_CREATION_FAILED, + }; + jest.spyOn(webhookService, 'handleWebhook'); + + (verifySignature as jest.Mock).mockReturnValue(true); + + await expect( + webhookController.processWebhook(MOCK_SIGNATURE, webhook), + ).rejects.toThrow('Invalid webhook event type: task_creation_failed'); + + expect(webhookService.handleWebhook).toHaveBeenCalledWith(webhook); + }); + }); +}); diff --git a/packages/apps/fortune/exchange-oracle/server/src/modules/webhook/webhook.controller.ts b/packages/apps/fortune/exchange-oracle/server/src/modules/webhook/webhook.controller.ts new file mode 100644 index 0000000000..5be5b41e59 --- /dev/null +++ b/packages/apps/fortune/exchange-oracle/server/src/modules/webhook/webhook.controller.ts @@ -0,0 +1,26 @@ +import { Body, Controller, Headers, Post, UseGuards } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; +import { Public } from '../../common/decorators'; +import { Role } from '../../common/enums/role'; +import { SignatureAuthGuard } from '../../common/guards'; +import { WebhookService } from './webhook.service'; +import { HEADER_SIGNATURE_KEY } from '../../common/constant'; +import { WebhookDto } from './webhook.dto'; + +@Public() +@ApiTags('Webhook') +@Controller('/webhook') +export class WebhookController { + constructor(private readonly webhookService: WebhookService) {} + + @UseGuards( + new SignatureAuthGuard([Role.Recording, Role.Reputation, Role.JobLaucher]), + ) + @Post('webhook') + processWebhook( + @Headers(HEADER_SIGNATURE_KEY) _: string, + @Body() body: WebhookDto, + ): Promise { + return this.webhookService.handleWebhook(body); + } +} diff --git a/packages/apps/fortune/exchange-oracle/server/src/modules/webhook/webhook.dto.ts b/packages/apps/fortune/exchange-oracle/server/src/modules/webhook/webhook.dto.ts new file mode 100644 index 0000000000..66406878cb --- /dev/null +++ b/packages/apps/fortune/exchange-oracle/server/src/modules/webhook/webhook.dto.ts @@ -0,0 +1,43 @@ +import { ChainId } from '@human-protocol/sdk'; +import { ApiProperty } from '@nestjs/swagger'; +import { IsArray, IsEnum, IsString } from 'class-validator'; +import { IsValidEthereumAddress } from '../../common/validators'; +import { EventType } from '../../common/enums/webhook'; + +export class EventData { + @ApiProperty({ name: 'assignee_id' }) + @IsString() + assigneeId?: string; + + @ApiProperty() + @IsString() + reason?: string; +} + +export class WebhookDto { + @ApiProperty({ + enum: ChainId, + name: 'chain_id', + }) + @IsEnum(ChainId) + public chainId: ChainId; + + @ApiProperty({ name: 'escrow_address' }) + @IsString() + @IsValidEthereumAddress() + public escrowAddress: string; + + @ApiProperty({ + enum: EventType, + name: 'event_type', + }) + @IsEnum(EventType) + public eventType: EventType; + + @ApiProperty({ + type: [EventData], + name: 'event_data', + }) + @IsArray() + public eventData?: EventData[]; +} diff --git a/packages/apps/fortune/exchange-oracle/server/src/modules/webhook/webhook.module.ts b/packages/apps/fortune/exchange-oracle/server/src/modules/webhook/webhook.module.ts new file mode 100644 index 0000000000..8ac7591f32 --- /dev/null +++ b/packages/apps/fortune/exchange-oracle/server/src/modules/webhook/webhook.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; + +import { WebhookController } from './webhook.controller'; +import { WebhookService } from './webhook.service'; +import { JobService } from '../job/job.service'; + +@Module({ + controllers: [WebhookController], + providers: [WebhookService, JobService], + exports: [WebhookService], +}) +export class WebhookModule {} diff --git a/packages/apps/fortune/exchange-oracle/server/src/modules/webhook/webhook.service.spec.ts b/packages/apps/fortune/exchange-oracle/server/src/modules/webhook/webhook.service.spec.ts new file mode 100644 index 0000000000..94b2b03898 --- /dev/null +++ b/packages/apps/fortune/exchange-oracle/server/src/modules/webhook/webhook.service.spec.ts @@ -0,0 +1,148 @@ +import { Test } from '@nestjs/testing'; +import { EventType } from '../../common/enums/webhook'; +import { WebhookDto } from './webhook.dto'; +import { WebhookService } from './webhook.service'; +import { ConfigService } from '@nestjs/config'; +import { JobService } from '../job/job.service'; +import { ConfigModule, registerAs } from '@nestjs/config'; +import { + MOCK_PRIVATE_KEY, + MOCK_S3_ACCESS_KEY, + MOCK_S3_BUCKET, + MOCK_S3_ENDPOINT, + MOCK_S3_PORT, + MOCK_S3_SECRET_KEY, + MOCK_S3_USE_SSL, +} from '../../../test/constants'; +import { Web3Service } from '../web3/web3.service'; +import { of } from 'rxjs'; +import { HttpService } from '@nestjs/axios'; +import { StorageService } from '../storage/storage.service'; + +describe('WebhookService', () => { + let webhookService: WebhookService, jobService: JobService; + + const chainId = 1; + const escrowAddress = '0x1234567890123456789012345678901234567890'; + const workerAddress = '0x1234567890123456789012345678901234567891'; + + const signerMock = { + address: '0x1234567890123456789012345678901234567892', + getNetwork: jest.fn().mockResolvedValue({ chainId: 1 }), + }; + + const configServiceMock: Partial = { + get: jest.fn((key: string) => { + switch (key) { + case 'WEB3_PRIVATE_KEY': + return MOCK_PRIVATE_KEY; + } + }), + }; + + const httpServicePostMock = jest + .fn() + .mockReturnValue(of({ status: 200, data: {} })); + + beforeEach(async () => { + const moduleRef = await Test.createTestingModule({ + imports: [ + ConfigModule.forFeature( + registerAs('s3', () => ({ + accessKey: MOCK_S3_ACCESS_KEY, + secretKey: MOCK_S3_SECRET_KEY, + endPoint: MOCK_S3_ENDPOINT, + port: MOCK_S3_PORT, + useSSL: MOCK_S3_USE_SSL, + bucket: MOCK_S3_BUCKET, + })), + ), + ], + providers: [ + WebhookService, + JobService, + StorageService, + { + provide: ConfigService, + useValue: configServiceMock, + }, + { + provide: Web3Service, + useValue: { + getSigner: jest.fn().mockReturnValue(signerMock), + }, + }, + { + provide: HttpService, + useValue: { + post: httpServicePostMock, + axiosRef: { + get: jest.fn(), + }, + }, + }, + ], + }).compile(); + + webhookService = moduleRef.get(WebhookService); + jobService = moduleRef.get(JobService); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('handleWebhook', () => { + afterEach(() => { + jest.restoreAllMocks(); + }); + it('should handle an incoming escrow created webhook', async () => { + const webhook: WebhookDto = { + chainId, + escrowAddress, + eventType: EventType.ESCROW_CREATED, + }; + + expect(await webhookService.handleWebhook(webhook)).toBe(undefined); + }); + + it('should handle an incoming escrow canceled webhook', async () => { + const webhook: WebhookDto = { + chainId, + escrowAddress, + eventType: EventType.ESCROW_CANCELED, + }; + + expect(await webhookService.handleWebhook(webhook)).toBe(undefined); + }); + + it('should mark a job solution as invalid', async () => { + const webhook: WebhookDto = { + chainId, + escrowAddress, + eventType: EventType.SUBMISSION_REJECTED, + eventData: [{ assigneeId: workerAddress }], + }; + + jest.spyOn(jobService, 'processInvalidJobSolution').mockResolvedValue(); + + await webhookService.handleWebhook(webhook); + + expect(jobService.processInvalidJobSolution).toHaveBeenCalledWith( + webhook, + ); + }); + + it('should return an error when the event type is invalid', async () => { + const webhook: WebhookDto = { + chainId, + escrowAddress, + eventType: EventType.TASK_CREATION_FAILED, + }; + + await expect(webhookService.handleWebhook(webhook)).rejects.toThrow( + 'Invalid webhook event type: task_creation_failed', + ); + }); + }); +}); diff --git a/packages/apps/fortune/exchange-oracle/server/src/modules/webhook/webhook.service.ts b/packages/apps/fortune/exchange-oracle/server/src/modules/webhook/webhook.service.ts new file mode 100644 index 0000000000..827c47d83e --- /dev/null +++ b/packages/apps/fortune/exchange-oracle/server/src/modules/webhook/webhook.service.ts @@ -0,0 +1,29 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import { BadRequestException, Injectable } from '@nestjs/common'; +import { EventType } from '../../common/enums/webhook'; +import { WebhookDto } from './webhook.dto'; +import { JobService } from '../job/job.service'; + +@Injectable() +export class WebhookService { + constructor(private readonly jobService: JobService) {} + + public async handleWebhook(wehbook: WebhookDto): Promise { + switch (wehbook.eventType) { + case EventType.ESCROW_CREATED: + break; + + case EventType.ESCROW_CANCELED: + break; + + case EventType.SUBMISSION_REJECTED: + await this.jobService.processInvalidJobSolution(wehbook); + break; + + default: + throw new BadRequestException( + `Invalid webhook event type: ${wehbook.eventType}`, + ); + } + } +} diff --git a/packages/apps/fortune/recording-oracle/src/app.module.ts b/packages/apps/fortune/recording-oracle/src/app.module.ts index 0d0c604573..20eba00598 100644 --- a/packages/apps/fortune/recording-oracle/src/app.module.ts +++ b/packages/apps/fortune/recording-oracle/src/app.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; -import { APP_PIPE } from '@nestjs/core'; +import { APP_INTERCEPTOR, APP_PIPE } from '@nestjs/core'; import { HttpValidationPipe } from '@/common/pipes'; import { JobModule } from '@/modules/job/job.module'; @@ -11,6 +11,7 @@ import { serverConfig, web3Config, } from './common/config'; +import { SnakeCaseInterceptor } from './common/interceptors/snake-case'; @Module({ providers: [ @@ -18,6 +19,10 @@ import { provide: APP_PIPE, useClass: HttpValidationPipe, }, + { + provide: APP_INTERCEPTOR, + useClass: SnakeCaseInterceptor, + }, ], imports: [ ConfigModule.forRoot({ diff --git a/packages/apps/fortune/recording-oracle/src/common/constants/index.ts b/packages/apps/fortune/recording-oracle/src/common/constants/index.ts index 3d274450ba..993982eef8 100644 --- a/packages/apps/fortune/recording-oracle/src/common/constants/index.ts +++ b/packages/apps/fortune/recording-oracle/src/common/constants/index.ts @@ -1,2 +1 @@ -export const EXCHANGE_INVALID_ENDPOINT = '/invalid-solution'; export const HEADER_SIGNATURE_KEY = 'human-signature'; diff --git a/packages/apps/fortune/recording-oracle/src/common/enums/webhook.ts b/packages/apps/fortune/recording-oracle/src/common/enums/webhook.ts new file mode 100644 index 0000000000..af6c902688 --- /dev/null +++ b/packages/apps/fortune/recording-oracle/src/common/enums/webhook.ts @@ -0,0 +1,6 @@ +export enum EventType { + escrow_created = 'escrow_created', + escrow_canceled = 'escrow_canceled', + escrow_recorded = 'escrow_recorded', + submission_rejected = 'submission_rejected', +} diff --git a/packages/apps/fortune/recording-oracle/src/common/interceptors/snake-case.ts b/packages/apps/fortune/recording-oracle/src/common/interceptors/snake-case.ts new file mode 100644 index 0000000000..0d62e5f222 --- /dev/null +++ b/packages/apps/fortune/recording-oracle/src/common/interceptors/snake-case.ts @@ -0,0 +1,28 @@ +import { + CallHandler, + ExecutionContext, + Injectable, + NestInterceptor, +} from '@nestjs/common'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { CaseConverter } from '../utils/case-converter'; + +@Injectable() +export class SnakeCaseInterceptor implements NestInterceptor { + intercept(context: ExecutionContext, next: CallHandler): Observable { + const request = context.switchToHttp().getRequest(); + + if (request.body) { + request.body = CaseConverter.transformToCamelCase(request.body); + } + + if (request.query) { + request.query = CaseConverter.transformToCamelCase(request.query); + } + + return next + .handle() + .pipe(map((data) => CaseConverter.transformToSnakeCase(data))); + } +} diff --git a/packages/apps/fortune/recording-oracle/src/common/utils/case-converter.ts b/packages/apps/fortune/recording-oracle/src/common/utils/case-converter.ts new file mode 100644 index 0000000000..980ddc589a --- /dev/null +++ b/packages/apps/fortune/recording-oracle/src/common/utils/case-converter.ts @@ -0,0 +1,31 @@ +export class CaseConverter { + static transformToCamelCase(obj: Record): Record { + const result: Record = {}; + for (const key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + const camelCaseKey = key.replace(/_([a-z])/g, (g) => + g[1].toUpperCase(), + ); + result[camelCaseKey] = obj[key]; + } + } + return result; + } + + static transformToSnakeCase(obj: any): any { + if (Array.isArray(obj)) { + return obj.map((item) => CaseConverter.transformToSnakeCase(item)); + } else if (typeof obj === 'object' && obj !== null) { + return Object.keys(obj).reduce( + (acc: Record, key: string) => { + const snakeCaseKey = key.replace(/([A-Z])/g, '_$1').toLowerCase(); + acc[snakeCaseKey] = CaseConverter.transformToSnakeCase(obj[key]); + return acc; + }, + {}, + ); + } else { + return obj; + } + } +} diff --git a/packages/apps/fortune/recording-oracle/src/common/utils/webhook.ts b/packages/apps/fortune/recording-oracle/src/common/utils/webhook.ts index 0a6fbda8ad..c8f9cd7f17 100644 --- a/packages/apps/fortune/recording-oracle/src/common/utils/webhook.ts +++ b/packages/apps/fortune/recording-oracle/src/common/utils/webhook.ts @@ -4,17 +4,20 @@ import { HttpService } from '@nestjs/axios'; import { ErrorJob } from '../constants/errors'; import { signMessage } from './signature'; import { HEADER_SIGNATURE_KEY } from '../constants'; +import { WebhookBody } from '@/modules/job/job.dto'; +import { CaseConverter } from './case-converter'; export async function sendWebhook( httpService: HttpService, logger: Logger, webhookUrl: string, - webhookData: any, + webhookBody: WebhookBody, privateKey: string, ): Promise { - const signedBody = await signMessage(webhookData, privateKey); + const snake_case_body = CaseConverter.transformToSnakeCase(webhookBody); + const signedBody = await signMessage(snake_case_body, privateKey); const { data } = await firstValueFrom( - await httpService.post(webhookUrl, webhookData, { + await httpService.post(webhookUrl, snake_case_body, { headers: { [HEADER_SIGNATURE_KEY]: signedBody }, }), ); diff --git a/packages/apps/fortune/recording-oracle/src/modules/job/job.dto.ts b/packages/apps/fortune/recording-oracle/src/modules/job/job.dto.ts index 46109e6c08..d53e4d5d15 100644 --- a/packages/apps/fortune/recording-oracle/src/modules/job/job.dto.ts +++ b/packages/apps/fortune/recording-oracle/src/modules/job/job.dto.ts @@ -3,20 +3,23 @@ import { ChainId } from '@human-protocol/sdk'; import { IsEnum, IsString, IsUrl } from 'class-validator'; import { IsValidEthereumAddress } from '@/common/validators'; +import { SolutionError } from '@/common/enums/job'; +import { EventType } from '@/common/enums/webhook'; export class JobSolutionsRequestDto { - @ApiProperty() + @ApiProperty({ name: 'escrow_address' }) @IsString() @IsValidEthereumAddress() public escrowAddress: string; @ApiProperty({ enum: ChainId, + name: 'chain_id', }) @IsEnum(ChainId) public chainId: ChainId; - @ApiProperty() + @ApiProperty({ name: 'solutions_url' }) @IsString() @IsUrl() public solutionsUrl: string; @@ -26,3 +29,15 @@ export class SaveSolutionsDto { public url: string; public hash: string; } + +export class WebhookBody { + escrowAddress: string; + chainId: ChainId; + eventType: EventType; + eventData?: EventData[]; +} + +export class EventData { + assigneeId: string; + reason?: SolutionError; +} diff --git a/packages/apps/fortune/recording-oracle/src/modules/job/job.service.spec.ts b/packages/apps/fortune/recording-oracle/src/modules/job/job.service.spec.ts index 0a5fe026cb..9d3be509c6 100644 --- a/packages/apps/fortune/recording-oracle/src/modules/job/job.service.spec.ts +++ b/packages/apps/fortune/recording-oracle/src/modules/job/job.service.spec.ts @@ -10,7 +10,7 @@ import { KVStoreClient, StorageClient, } from '@human-protocol/sdk'; -import { JobRequestType } from '../../common/enums/job'; +import { JobRequestType, SolutionError } from '../../common/enums/job'; import { MOCK_ADDRESS, MOCK_ENCRYPTION_PASSPHRASE, @@ -34,11 +34,9 @@ import { IManifest, ISolution } from '../../common/interfaces/job'; import { of } from 'rxjs'; import { JobSolutionsRequestDto } from './job.dto'; import { StorageService } from '../storage/storage.service'; -import { - EXCHANGE_INVALID_ENDPOINT, - HEADER_SIGNATURE_KEY, -} from '../../common/constants'; +import { HEADER_SIGNATURE_KEY } from '../../common/constants'; import { signMessage } from '../../common/utils/signature'; +import { EventType } from '@/common/enums/webhook'; jest.mock('minio', () => { class Client { @@ -478,8 +476,9 @@ describe('JobService', () => { const result = await jobService.processJobSolution(jobSolution); const expectedBody = { - chainId: jobSolution.chainId, - escrowAddress: jobSolution.escrowAddress, + chain_id: jobSolution.chainId, + escrow_address: jobSolution.escrowAddress, + event_type: EventType.escrow_recorded, }; expect(result).toEqual('The requested job is completed.'); expect(httpServicePostMock).toHaveBeenCalledWith( @@ -565,8 +564,9 @@ describe('JobService', () => { const result = await jobService.processJobSolution(jobSolution); const expectedBody = { - chainId: jobSolution.chainId, - escrowAddress: jobSolution.escrowAddress, + chain_id: jobSolution.chainId, + escrow_address: jobSolution.escrowAddress, + event_type: EventType.escrow_recorded, }; expect(result).toEqual('The requested job is completed.'); expect(httpServicePostMock).toHaveBeenCalledWith( @@ -638,20 +638,23 @@ describe('JobService', () => { const result = await jobService.processJobSolution(jobSolution); const expectedBody = { - chainId: jobSolution.chainId, - escrowAddress: jobSolution.escrowAddress, - workerAddress: exchangeJobSolutions[0].workerAddress, + chain_id: jobSolution.chainId, + escrow_address: jobSolution.escrowAddress, + event_type: EventType.submission_rejected, + event_data: [ + { + assignee_id: exchangeJobSolutions[0].workerAddress, + reason: SolutionError.Duplicated, + }, + ], }; expect(result).toEqual('Solution are recorded.'); expect(httpServicePostMock).toHaveBeenCalledWith( - MOCK_EXCHANGE_ORACLE_WEBHOOK_URL + EXCHANGE_INVALID_ENDPOINT, + MOCK_EXCHANGE_ORACLE_WEBHOOK_URL, expectedBody, { headers: { - [HEADER_SIGNATURE_KEY]: await signMessage( - expectedBody, - MOCK_WEB3_PRIVATE_KEY, - ), + [HEADER_SIGNATURE_KEY]: expect.any(String), }, }, ); @@ -710,21 +713,24 @@ describe('JobService', () => { }; const expectedBody = { - chainId: jobSolution.chainId, - escrowAddress: jobSolution.escrowAddress, - workerAddress: exchangeJobSolutions[1].workerAddress, + chain_id: jobSolution.chainId, + escrow_address: jobSolution.escrowAddress, + event_type: EventType.submission_rejected, + event_data: [ + { + assignee_id: exchangeJobSolutions[1].workerAddress, + reason: SolutionError.CurseWord, + }, + ], }; const result = await jobService.processJobSolution(jobSolution); expect(result).toEqual('Solution are recorded.'); expect(httpServicePostMock).toHaveBeenCalledWith( - MOCK_EXCHANGE_ORACLE_WEBHOOK_URL + EXCHANGE_INVALID_ENDPOINT, + MOCK_EXCHANGE_ORACLE_WEBHOOK_URL, expectedBody, { headers: { - [HEADER_SIGNATURE_KEY]: await signMessage( - expectedBody, - MOCK_WEB3_PRIVATE_KEY, - ), + [HEADER_SIGNATURE_KEY]: expect.any(String), }, }, ); diff --git a/packages/apps/fortune/recording-oracle/src/modules/job/job.service.ts b/packages/apps/fortune/recording-oracle/src/modules/job/job.service.ts index 63fe0cfbbd..05d4e2d33d 100644 --- a/packages/apps/fortune/recording-oracle/src/modules/job/job.service.ts +++ b/packages/apps/fortune/recording-oracle/src/modules/job/job.service.ts @@ -1,4 +1,9 @@ -import { EscrowClient, EscrowStatus, KVStoreClient } from '@human-protocol/sdk'; +import { + EscrowClient, + EscrowStatus, + KVStoreClient, + KVStoreKeys, +} from '@human-protocol/sdk'; import { HttpService } from '@nestjs/axios'; import { BadRequestException, @@ -22,8 +27,8 @@ import { checkCurseWords } from '../../common/utils/curseWords'; import { sendWebhook } from '../../common/utils/webhook'; import { StorageService } from '../storage/storage.service'; import { Web3Service } from '../web3/web3.service'; -import { JobSolutionsRequestDto } from './job.dto'; -import { EXCHANGE_INVALID_ENDPOINT } from '../../common/constants'; +import { EventData, JobSolutionsRequestDto, WebhookBody } from './job.dto'; +import { EventType } from '@/common/enums/webhook'; @Injectable() export class JobService { @@ -200,6 +205,7 @@ export class JobService { { chainId: jobSolution.chainId, escrowAddress: jobSolution.escrowAddress, + eventType: EventType.escrow_recorded, }, this.web3Config.web3PrivateKey, ); @@ -209,21 +215,28 @@ export class JobService { if (errorSolutions.length) { const exchangeOracleURL = (await kvstoreClient.get( await escrowClient.getExchangeOracleAddress(jobSolution.escrowAddress), - 'webhookUrl', + KVStoreKeys.webhookUrl, )) as string; - for (const solution of errorSolutions) { - await sendWebhook( - this.httpService, - this.logger, - exchangeOracleURL + EXCHANGE_INVALID_ENDPOINT, - { - chainId: jobSolution.chainId, - escrowAddress: jobSolution.escrowAddress, - workerAddress: solution.workerAddress, - }, - this.web3Config.web3PrivateKey, - ); - } + const eventData: EventData[] = errorSolutions.map((solution) => ({ + assigneeId: solution.workerAddress, + reason: solution.error as SolutionError, + })); + + const webhookBody: WebhookBody = { + escrowAddress: jobSolution.escrowAddress, + chainId: jobSolution.chainId, + eventType: EventType.submission_rejected, + eventData: eventData, + }; + + // Enviar la llamada al webhook una vez con todos los errores + await sendWebhook( + this.httpService, + this.logger, + exchangeOracleURL, + webhookBody, + this.web3Config.web3PrivateKey, + ); } return 'Solution are recorded.'; diff --git a/packages/apps/fortune/recording-oracle/src/modules/storage/storage.module.ts b/packages/apps/fortune/recording-oracle/src/modules/storage/storage.module.ts index f9cf1659df..cf4c63fc05 100644 --- a/packages/apps/fortune/recording-oracle/src/modules/storage/storage.module.ts +++ b/packages/apps/fortune/recording-oracle/src/modules/storage/storage.module.ts @@ -1,10 +1,15 @@ import { Module } from '@nestjs/common'; import { StorageService } from './storage.service'; import { ConfigModule } from '@nestjs/config'; -import { s3Config } from '../../common/config'; +import { s3Config, serverConfig } from '../../common/config'; +import { Web3Module } from '../web3/web3.module'; @Module({ - imports: [ConfigModule.forFeature(s3Config)], + imports: [ + ConfigModule.forFeature(s3Config), + ConfigModule.forFeature(serverConfig), + Web3Module, + ], providers: [StorageService], exports: [StorageService], }) From a3c9def6bbfec5a9918c331a46229614955edea4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20L=C3=B3pez?= <50665615+flopez7@users.noreply.github.com> Date: Fri, 26 Jan 2024 15:02:36 +0100 Subject: [PATCH 062/104] [Job Launcher] Move cron jobs into cron jobs controller (#1514) * Move cron jobs into cron jobs controller * Move all cron methods to cron service * Remove cron jobs from vercel.json --- .../src/modules/cron-job/cron-job.module.ts | 16 +- .../modules/cron-job/cron-job.service.spec.ts | 737 +++++++++++++++++- .../src/modules/cron-job/cron-job.service.ts | 243 +++++- .../modules/cron-job/cron.job.controller.ts | 93 +++ .../server/src/modules/job/job.controller.ts | 72 -- .../server/src/modules/job/job.module.ts | 2 - .../src/modules/job/job.service.spec.ts | 524 +------------ .../server/src/modules/job/job.service.ts | 271 +------ .../src/modules/webhook/webhook.controller.ts | 17 - .../src/modules/webhook/webhook.module.ts | 2 - .../modules/webhook/webhook.service.spec.ts | 137 +--- .../src/modules/webhook/webhook.service.ts | 100 +-- packages/apps/job-launcher/server/vercel.json | 22 - 13 files changed, 1147 insertions(+), 1089 deletions(-) create mode 100644 packages/apps/job-launcher/server/src/modules/cron-job/cron.job.controller.ts delete mode 100644 packages/apps/job-launcher/server/src/modules/webhook/webhook.controller.ts diff --git a/packages/apps/job-launcher/server/src/modules/cron-job/cron-job.module.ts b/packages/apps/job-launcher/server/src/modules/cron-job/cron-job.module.ts index 554e49ca8e..a09a03b778 100644 --- a/packages/apps/job-launcher/server/src/modules/cron-job/cron-job.module.ts +++ b/packages/apps/job-launcher/server/src/modules/cron-job/cron-job.module.ts @@ -4,11 +4,25 @@ import { CronJobService } from './cron-job.service'; import { CronJobRepository } from './cron-job.repository'; import { TypeOrmModule } from '@nestjs/typeorm'; import { CronJobEntity } from './cron-job.entity'; +import { CronJobController } from './cron.job.controller'; +import { PaymentModule } from '../payment/payment.module'; +import { Web3Module } from '../web3/web3.module'; +import { StorageModule } from '../storage/storage.module'; +import { WebhookModule } from '../webhook/webhook.module'; +import { JobModule } from '../job/job.module'; @Global() @Module({ - imports: [TypeOrmModule.forFeature([CronJobEntity])], + imports: [ + TypeOrmModule.forFeature([CronJobEntity]), + JobModule, + PaymentModule, + Web3Module, + StorageModule, + WebhookModule, + ], providers: [CronJobService, CronJobRepository], + controllers: [CronJobController], exports: [CronJobService], }) export class CronJobModule {} 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 6f099a9e2f..fae0b36296 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 @@ -6,12 +6,77 @@ import { CronJobService } from './cron-job.service'; import { CronJobRepository } from './cron-job.repository'; import { CronJobEntity } from './cron-job.entity'; import { createMock } from '@golevelup/ts-jest'; +import { JobEntity } from '../job/job.entity'; +import { JobRequestType, JobStatus } from '../../common/enums/job'; +import { + MOCK_ADDRESS, + MOCK_EXCHANGE_ORACLE_ADDRESS, + MOCK_EXCHANGE_ORACLE_WEBHOOK_URL, + MOCK_FILE_HASH, + MOCK_FILE_URL, + MOCK_MAX_RETRY_COUNT, + MOCK_TRANSACTION_HASH, +} from '../../../test/constants'; +import { + ChainId, + Encryption, + EscrowClient, + KVStoreClient, +} from '@human-protocol/sdk'; +import { JobService } from '../job/job.service'; +import { DeepPartial } from 'typeorm'; +import { CvatManifestDto } from '../job/job.dto'; +import { WebhookService } from '../webhook/webhook.service'; +import { StorageService } from '../storage/storage.service'; +import { Web3Service } from '../web3/web3.service'; +import { PaymentService } from '../payment/payment.service'; +import { JobRepository } from '../job/job.repository'; +import { PaymentRepository } from '../payment/payment.repository'; +import { ConfigService } from '@nestjs/config'; +import { RoutingProtocolService } from '../job/routing-protocol.service'; +import { WebhookEntity } from '../webhook/webhook.entity'; +import { WebhookStatus } from '../../common/enums/webhook'; +import { WebhookRepository } from '../webhook/webhook.repository'; +import { HttpService } from '@nestjs/axios'; + +jest.mock('@human-protocol/sdk', () => ({ + ...jest.requireActual('@human-protocol/sdk'), + EscrowClient: { + build: jest.fn().mockImplementation(() => ({ + createEscrow: jest.fn().mockResolvedValue(MOCK_ADDRESS), + setup: jest.fn().mockResolvedValue(null), + fund: jest.fn().mockResolvedValue(null), + })), + }, + KVStoreClient: { + build: jest.fn().mockImplementation(() => ({ + get: jest.fn(), + })), + }, +})); describe('CronJobService', () => { - let service: CronJobService; - let repository: CronJobRepository; + let service: CronJobService, + repository: CronJobRepository, + webhookService: WebhookService, + webhookRepository: WebhookRepository, + storageService: StorageService, + jobService: JobService; + + const signerMock = { + address: MOCK_ADDRESS, + getNetwork: jest.fn().mockResolvedValue({ chainId: 1 }), + }; beforeEach(async () => { + const mockConfigService: Partial = { + get: jest.fn((key: string) => { + switch (key) { + case 'MAX_RETRY_COUNT': + return MOCK_MAX_RETRY_COUNT; + } + }), + }; const module: TestingModule = await Test.createTestingModule({ providers: [ CronJobService, @@ -19,11 +84,42 @@ describe('CronJobService', () => { provide: CronJobRepository, useValue: createMock(), }, + { + provide: Web3Service, + useValue: { + getSigner: jest.fn().mockReturnValue(signerMock), + validateChainId: jest.fn().mockReturnValue(new Error()), + }, + }, + JobService, + WebhookService, + Encryption, + { provide: JobRepository, useValue: createMock() }, + { + provide: PaymentRepository, + useValue: createMock(), + }, + { provide: StorageService, useValue: createMock() }, + { provide: PaymentService, useValue: createMock() }, + { provide: ConfigService, useValue: mockConfigService }, + { + provide: RoutingProtocolService, + useValue: createMock(), + }, + { + provide: WebhookRepository, + useValue: createMock(), + }, + { provide: HttpService, useValue: createMock() }, ], }).compile(); service = module.get(CronJobService); + jobService = module.get(JobService); repository = module.get(CronJobRepository); + webhookService = module.get(WebhookService); + webhookRepository = module.get(WebhookRepository); + storageService = module.get(StorageService); }); describe('startCronJob', () => { @@ -169,4 +265,641 @@ describe('CronJobService', () => { expect(saveSpy).not.toHaveBeenCalled(); }); }); + + describe('createEscrowCronJob', () => { + let createEscrowMock: any; + let cronJobEntityMock: Partial; + let jobEntityMock1: Partial, jobEntityMock2: Partial; + + beforeEach(() => { + cronJobEntityMock = { + cronJobType: CronJobType.CreateEscrow, + startedAt: new Date(), + save: jest.fn(), + }; + + jobEntityMock1 = { + status: JobStatus.PAID, + fundAmount: 100, + userId: 1, + id: 1, + manifestUrl: MOCK_FILE_URL, + manifestHash: MOCK_FILE_HASH, + escrowAddress: MOCK_ADDRESS, + chainId: ChainId.LOCALHOST, + retriesCount: 1, + save: jest.fn(), + }; + + jobEntityMock2 = { + status: JobStatus.PAID, + fundAmount: 100, + userId: 1, + id: 1, + manifestUrl: MOCK_FILE_URL, + manifestHash: MOCK_FILE_HASH, + escrowAddress: MOCK_ADDRESS, + chainId: ChainId.LOCALHOST, + retriesCount: 1, + save: jest.fn(), + }; + + jest + .spyOn(jobService, 'findJobByStatus') + .mockResolvedValue([jobEntityMock1 as any, jobEntityMock2 as any]); + + createEscrowMock = jest.spyOn(jobService, 'createEscrow'); + createEscrowMock.mockResolvedValue(true); + + jest.spyOn(service, 'isCronJobRunning').mockResolvedValue(false); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('should not run if the cron job is already running', async () => { + jest.spyOn(service, 'isCronJobRunning').mockResolvedValueOnce(true); + + await service.createEscrowCronJob(); + + expect(createEscrowMock).not.toHaveBeenCalled(); + }); + + it('should create cron job entity on database to lock', async () => { + jest + .spyOn(service, 'startCronJob') + .mockResolvedValueOnce(cronJobEntityMock as any); + + await service.createEscrowCronJob(); + + expect(service.startCronJob).toHaveBeenCalledWith( + CronJobType.CreateEscrow, + ); + }); + + it('should run createEscrow for all of the jobs with status PAID', async () => { + await service.createEscrowCronJob(); + + expect(createEscrowMock).toHaveBeenCalledTimes(2); + }); + + it('should increase retriesCount by 1, if the job creation fails', async () => { + createEscrowMock.mockRejectedValueOnce(new Error('creation failed')); + + await service.createEscrowCronJob(); + + expect(createEscrowMock).toHaveBeenCalledTimes(2); + expect(jobEntityMock1.retriesCount).toBe(2); + expect(jobEntityMock2.retriesCount).toBe(1); + }); + + it('should mark job as failed if the job creation fails more than max retries count', async () => { + createEscrowMock.mockRejectedValueOnce(new Error('creation failed')); + jobEntityMock1.retriesCount = MOCK_MAX_RETRY_COUNT; + + await service.createEscrowCronJob(); + + expect(createEscrowMock).toHaveBeenCalledTimes(2); + expect(jobEntityMock1.status).toBe(JobStatus.FAILED); + expect(jobEntityMock2.status).toBe(JobStatus.PAID); + }); + + it('should complete the cron job entity on database to unlock', async () => { + jest + .spyOn(service, 'completeCronJob') + .mockResolvedValueOnce(cronJobEntityMock as any); + + await service.createEscrowCronJob(); + + expect(service.completeCronJob).toHaveBeenCalledWith( + CronJobType.CreateEscrow, + ); + }); + }); + + describe('setupEscrowCronJob', () => { + let setupEscrowMock: any; + let cronJobEntityMock: Partial; + let jobEntityMock1: Partial, jobEntityMock2: Partial; + + beforeEach(() => { + cronJobEntityMock = { + cronJobType: CronJobType.SetupEscrow, + createdAt: new Date(), + save: jest.fn(), + }; + + jobEntityMock1 = { + status: JobStatus.CREATED, + fundAmount: 100, + userId: 1, + id: 1, + manifestUrl: MOCK_FILE_URL, + manifestHash: MOCK_FILE_HASH, + escrowAddress: MOCK_ADDRESS, + chainId: ChainId.LOCALHOST, + retriesCount: 1, + save: jest.fn(), + }; + + jobEntityMock2 = { + status: JobStatus.CREATED, + fundAmount: 100, + userId: 1, + id: 1, + manifestUrl: MOCK_FILE_URL, + manifestHash: MOCK_FILE_HASH, + escrowAddress: MOCK_ADDRESS, + chainId: ChainId.LOCALHOST, + retriesCount: 1, + save: jest.fn(), + }; + + jest + .spyOn(jobService, 'findJobByStatus') + .mockResolvedValue([jobEntityMock1 as any, jobEntityMock2 as any]); + + setupEscrowMock = jest.spyOn(jobService, 'setupEscrow'); + setupEscrowMock.mockResolvedValue(true); + + jest.spyOn(service, 'isCronJobRunning').mockResolvedValue(false); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('should not run if the cron job is already running', async () => { + jest.spyOn(service, 'isCronJobRunning').mockResolvedValueOnce(true); + + await service.setupEscrowCronJob(); + + expect(setupEscrowMock).not.toHaveBeenCalled(); + }); + + it('should create cron job entity on database to lock', async () => { + jest + .spyOn(service, 'startCronJob') + .mockResolvedValueOnce(cronJobEntityMock as any); + + await service.setupEscrowCronJob(); + + expect(service.startCronJob).toHaveBeenCalledWith( + CronJobType.SetupEscrow, + ); + }); + + it('should run setupEscrow for all of the jobs with status LAUNCHING', async () => { + await service.setupEscrowCronJob(); + + expect(setupEscrowMock).toHaveBeenCalledTimes(2); + }); + + it('should increase retriesCount by 1, if the job setup fails', async () => { + setupEscrowMock.mockRejectedValueOnce(new Error('setup failed')); + + await service.setupEscrowCronJob(); + + expect(setupEscrowMock).toHaveBeenCalledTimes(2); + expect(jobEntityMock1.retriesCount).toBe(2); + expect(jobEntityMock2.retriesCount).toBe(1); + }); + + it('should mark job as failed if the job setup fails more than max retries count', async () => { + setupEscrowMock.mockRejectedValueOnce(new Error('setup failed')); + jobEntityMock1.retriesCount = MOCK_MAX_RETRY_COUNT; + + await service.setupEscrowCronJob(); + + expect(setupEscrowMock).toHaveBeenCalledTimes(2); + expect(jobEntityMock1.status).toBe(JobStatus.FAILED); + expect(jobEntityMock2.status).toBe(JobStatus.CREATED); + }); + + it('should complete the cron job entity on database to unlock', async () => { + jest + .spyOn(service, 'completeCronJob') + .mockResolvedValueOnce(cronJobEntityMock as any); + + await service.setupEscrowCronJob(); + + expect(service.completeCronJob).toHaveBeenCalledWith( + CronJobType.SetupEscrow, + ); + }); + }); + + describe('fundEscrowCronJob', () => { + let fundEscrowMock: any; + let cronJobEntityMock: Partial; + let jobEntityMock1: Partial, jobEntityMock2: Partial; + let createWebhookMock: any; + + beforeEach(() => { + cronJobEntityMock = { + cronJobType: CronJobType.FundEscrow, + createdAt: new Date(), + save: jest.fn(), + }; + + jobEntityMock1 = { + status: JobStatus.SET_UP, + fundAmount: 100, + userId: 1, + id: 1, + manifestUrl: MOCK_FILE_URL, + manifestHash: MOCK_FILE_HASH, + escrowAddress: MOCK_ADDRESS, + chainId: ChainId.LOCALHOST, + retriesCount: 1, + save: jest.fn(), + }; + + jobEntityMock2 = { + status: JobStatus.SET_UP, + fundAmount: 100, + userId: 1, + id: 1, + manifestUrl: MOCK_FILE_URL, + manifestHash: MOCK_FILE_HASH, + escrowAddress: MOCK_ADDRESS, + chainId: ChainId.LOCALHOST, + retriesCount: 1, + save: jest.fn(), + }; + + jest + .spyOn(jobService, 'findJobByStatus') + .mockResolvedValue([jobEntityMock1 as any, jobEntityMock2 as any]); + + fundEscrowMock = jest.spyOn(jobService, 'fundEscrow'); + fundEscrowMock.mockResolvedValue(true); + + jest.spyOn(service, 'isCronJobRunning').mockResolvedValue(false); + + createWebhookMock = jest.spyOn(webhookService, 'createWebhook'); + + const cvatManifestMock: DeepPartial = { + data: { + data_url: MOCK_FILE_URL, + }, + annotation: { + type: JobRequestType.IMAGE_POINTS, + }, + }; + jest + .spyOn(storageService, 'download') + .mockResolvedValue(cvatManifestMock); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('should not run if the cron job is already running', async () => { + jest.spyOn(service, 'isCronJobRunning').mockResolvedValueOnce(true); + + await service.fundEscrowCronJob(); + + expect(fundEscrowMock).not.toHaveBeenCalled(); + }); + + it('should create cron job entity on database to lock', async () => { + jest + .spyOn(service, 'startCronJob') + .mockResolvedValueOnce(cronJobEntityMock as any); + + await service.fundEscrowCronJob(); + + expect(service.startCronJob).toHaveBeenCalledWith(CronJobType.FundEscrow); + }); + + it('should run fundEscrow for all of the jobs with status FUNDING, and trigger webhook', async () => { + await service.fundEscrowCronJob(); + + expect(fundEscrowMock).toHaveBeenCalledTimes(2); + expect(createWebhookMock).toHaveBeenCalledTimes(2); + }); + + it('should increase retriesCount by 1, if the job fund fails', async () => { + fundEscrowMock.mockRejectedValueOnce(new Error('fund failed')); + + await service.fundEscrowCronJob(); + + expect(fundEscrowMock).toHaveBeenCalledTimes(2); + expect(jobEntityMock1.retriesCount).toBe(2); + expect(jobEntityMock2.retriesCount).toBe(1); + + expect(createWebhookMock).toHaveBeenCalledTimes(1); + }); + + it('should mark job as failed if the job fund fails more than max retries count', async () => { + fundEscrowMock.mockRejectedValueOnce(new Error('fund failed')); + jobEntityMock1.retriesCount = MOCK_MAX_RETRY_COUNT; + + await service.fundEscrowCronJob(); + + expect(fundEscrowMock).toHaveBeenCalledTimes(2); + expect(jobEntityMock1.status).toBe(JobStatus.FAILED); + expect(jobEntityMock2.status).toBe(JobStatus.SET_UP); + + expect(createWebhookMock).toHaveBeenCalledTimes(1); + }); + + it('should complete the cron job entity on database to unlock', async () => { + jest + .spyOn(service, 'completeCronJob') + .mockResolvedValueOnce(cronJobEntityMock as any); + + await service.fundEscrowCronJob(); + + expect(service.completeCronJob).toHaveBeenCalledWith( + CronJobType.FundEscrow, + ); + }); + }); + + describe('cancelCronJob', () => { + let findJobMock: any, + cronJobEntityMock: Partial, + jobEntityMock1: Partial, + jobEntityMock2: Partial; + + beforeEach(() => { + cronJobEntityMock = { + cronJobType: CronJobType.CancelEscrow, + createdAt: new Date(), + save: jest.fn(), + }; + + jobEntityMock1 = { + status: JobStatus.TO_CANCEL, + fundAmount: 100, + userId: 1, + id: 1, + manifestUrl: MOCK_FILE_URL, + escrowAddress: MOCK_ADDRESS, + chainId: ChainId.LOCALHOST, + save: jest.fn(), + retriesCount: 0, + }; + + jobEntityMock2 = { + status: JobStatus.TO_CANCEL, + fundAmount: 100, + userId: 1, + id: 2, + manifestUrl: MOCK_FILE_URL, + escrowAddress: MOCK_ADDRESS, + chainId: ChainId.LOCALHOST, + save: jest.fn(), + retriesCount: 0, + }; + + findJobMock = jest + .spyOn(jobService, 'findJobByStatus') + .mockResolvedValue([jobEntityMock1 as any, jobEntityMock2 as any]); + + jest.spyOn(service, 'isCronJobRunning').mockResolvedValue(false); + + jest.spyOn(jobService, 'processEscrowCancellation').mockResolvedValue({ + txHash: MOCK_TRANSACTION_HASH, + amountRefunded: 1n, + }); + + (EscrowClient.build as any).mockImplementation(() => ({ + getExchangeOracleAddress: jest + .fn() + .mockResolvedValue(MOCK_EXCHANGE_ORACLE_ADDRESS), + })); + + (KVStoreClient.build as any).mockImplementation(() => ({ + get: jest.fn().mockResolvedValue(MOCK_EXCHANGE_ORACLE_WEBHOOK_URL), + })); + + const manifestMock = { + requestType: JobRequestType.FORTUNE, + }; + storageService.download = jest.fn().mockResolvedValue(manifestMock); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should not run if cron job is already running', async () => { + jest.spyOn(service, 'isCronJobRunning').mockResolvedValueOnce(true); + + await service.cancelCronJob(); + + expect(findJobMock).not.toHaveBeenCalled(); + }); + + it('should create cron job entity on database to lock', async () => { + jest + .spyOn(service, 'startCronJob') + .mockResolvedValueOnce(cronJobEntityMock as any); + + await service.cancelCronJob(); + + expect(service.startCronJob).toHaveBeenCalledWith( + CronJobType.CancelEscrow, + ); + }); + + it('should cancel all of the jobs with status TO_CANCEL', async () => { + jest.spyOn(webhookService, 'createWebhook'); + const result = await service.cancelCronJob(); + + expect(result).toBeTruthy(); + expect(jobService.processEscrowCancellation).toHaveBeenCalledWith( + jobEntityMock1, + ); + expect(jobEntityMock1.save).toHaveBeenCalled(); + expect(jobService.processEscrowCancellation).toHaveBeenCalledWith( + jobEntityMock2, + ); + expect(jobEntityMock2.save).toHaveBeenCalled(); + expect(webhookService.createWebhook).toHaveBeenCalledTimes(2); + }); + + it('should not call process escrow cancellation when escrowAddress is not present', async () => { + const jobEntityWithoutEscrow = { + ...jobEntityMock1, + escrowAddress: undefined, + }; + + jest + .spyOn(jobService, 'findJobByStatus') + .mockResolvedValueOnce([jobEntityWithoutEscrow as any]); + jest + .spyOn(jobService, 'processEscrowCancellation') + .mockResolvedValueOnce(undefined as any); + + expect(await service.cancelCronJob()).toBe(true); + expect(jobService.processEscrowCancellation).toHaveBeenCalledTimes(0); + }); + + it('should increase retriesCount by 1 if the job cancellation fails', async () => { + jest + .spyOn(jobService, 'processEscrowCancellation') + .mockRejectedValueOnce(new Error('cancellation failed')); + + expect(jobEntityMock1.retriesCount).toBe(0); + expect(jobEntityMock2.retriesCount).toBe(0); + + await service.cancelCronJob(); + + expect(jobEntityMock1.retriesCount).toBe(1); + expect(jobEntityMock2.retriesCount).toBe(0); + }); + + it('should mark job as failed if the job cancellation fails more than max retries count', async () => { + jest + .spyOn(jobService, 'processEscrowCancellation') + .mockRejectedValueOnce(new Error('cancellation failed')); + jobEntityMock1.retriesCount = MOCK_MAX_RETRY_COUNT; + + await service.cancelCronJob(); + + expect(jobService.processEscrowCancellation).toHaveBeenCalledTimes(2); + expect(jobEntityMock1.status).toBe(JobStatus.FAILED); + expect(jobEntityMock2.status).toBe(JobStatus.CANCELED); + }); + + it('should complete the cron job entity on database to unlock', async () => { + jest.spyOn(service, 'completeCronJob').mockResolvedValueOnce({} as any); + + await service.cancelCronJob(); + + expect(service.completeCronJob).toHaveBeenCalledWith( + CronJobType.CancelEscrow, + ); + }); + }); + + describe('processPendingCronJob', () => { + let sendWebhookMock: any; + let cronJobEntityMock: Partial; + let webhookEntity1: Partial, + webhookEntity2: Partial; + + beforeEach(() => { + cronJobEntityMock = { + cronJobType: CronJobType.ProcessPendingWebhook, + startedAt: new Date(), + save: jest.fn(), + }; + + webhookEntity1 = { + id: 1, + chainId: ChainId.LOCALHOST, + escrowAddress: MOCK_ADDRESS, + status: WebhookStatus.PENDING, + waitUntil: new Date(), + retriesCount: 0, + }; + + webhookEntity2 = { + id: 2, + chainId: ChainId.LOCALHOST, + escrowAddress: MOCK_ADDRESS, + status: WebhookStatus.PENDING, + waitUntil: new Date(), + retriesCount: 0, + }; + + jest + .spyOn(webhookRepository, 'find') + .mockResolvedValue([webhookEntity1 as any, webhookEntity2 as any]); + + sendWebhookMock = jest.spyOn(webhookService as any, 'sendWebhook'); + sendWebhookMock.mockResolvedValue(true); + + jest.spyOn(service, 'isCronJobRunning').mockResolvedValue(false); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('should not run if cron job is already running', async () => { + jest.spyOn(service, 'isCronJobRunning').mockResolvedValueOnce(true); + + const startCronJobMock = jest.spyOn(service, 'startCronJob'); + + await service.processPendingWebhooks(); + + expect(startCronJobMock).not.toHaveBeenCalled(); + }); + + it('should create cron job entity to lock the process', async () => { + jest + .spyOn(service, 'startCronJob') + .mockResolvedValueOnce(cronJobEntityMock as any); + + await service.processPendingWebhooks(); + + expect(service.startCronJob).toHaveBeenCalledWith( + CronJobType.ProcessPendingWebhook, + ); + }); + + it('should send webhook for all of the pending webhooks', async () => { + await service.processPendingWebhooks(); + + expect(sendWebhookMock).toHaveBeenCalledTimes(2); + expect(sendWebhookMock).toHaveBeenCalledWith(webhookEntity1); + expect(sendWebhookMock).toHaveBeenCalledWith(webhookEntity2); + + expect(webhookRepository.updateOne).toHaveBeenCalledTimes(2); + expect(webhookRepository.updateOne).toHaveBeenCalledWith( + { id: webhookEntity1.id }, + { status: WebhookStatus.COMPLETED }, + ); + expect(webhookRepository.updateOne).toHaveBeenCalledWith( + { id: webhookEntity2.id }, + { status: WebhookStatus.COMPLETED }, + ); + }); + + it('should increase retriesCount by 1 if sending webhook fails', async () => { + sendWebhookMock.mockRejectedValueOnce(new Error()); + await service.processPendingWebhooks(); + + expect(webhookRepository.updateOne).toHaveBeenCalledWith( + { id: webhookEntity1.id }, + { + retriesCount: 1, + waitUntil: expect.any(Date), + }, + ); + }); + + it('should mark webhook as failed if retriesCount exceeds threshold', async () => { + sendWebhookMock.mockRejectedValueOnce(new Error()); + + webhookEntity1.retriesCount = MOCK_MAX_RETRY_COUNT; + + await service.processPendingWebhooks(); + + expect(webhookRepository.updateOne).toHaveBeenCalledWith( + { id: webhookEntity1.id }, + { status: WebhookStatus.FAILED }, + ); + }); + + it('should complete the cron job entity to unlock', async () => { + jest + .spyOn(service, 'completeCronJob') + .mockResolvedValueOnce(cronJobEntityMock as any); + + await service.processPendingWebhooks(); + + expect(service.completeCronJob).toHaveBeenCalledWith( + cronJobEntityMock as any, + ); + }); + }); }); diff --git a/packages/apps/job-launcher/server/src/modules/cron-job/cron-job.service.ts b/packages/apps/job-launcher/server/src/modules/cron-job/cron-job.service.ts index 302b382e5d..b029ab36e7 100644 --- a/packages/apps/job-launcher/server/src/modules/cron-job/cron-job.service.ts +++ b/packages/apps/job-launcher/server/src/modules/cron-job/cron-job.service.ts @@ -5,12 +5,31 @@ import { ErrorCronJob } from '../../common/constants/errors'; import { CronJobEntity } from './cron-job.entity'; import { CronJobRepository } from './cron-job.repository'; +import { Cron, CronExpression } from '@nestjs/schedule'; +import { JobService } from '../job/job.service'; +import { JobRequestType, JobStatus } from '../../common/enums/job'; +import { WebhookService } from '../webhook/webhook.service'; +import { StorageService } from '../storage/storage.service'; +import { + EventType, + OracleType, + WebhookStatus, +} from '../../common/enums/webhook'; +import { CvatManifestDto, FortuneManifestDto } from '../job/job.dto'; +import { PaymentService } from '../payment/payment.service'; +import { ethers } from 'ethers'; @Injectable() export class CronJobService { private readonly logger = new Logger(CronJobService.name); - constructor(private readonly cronJobRepository: CronJobRepository) {} + constructor( + private readonly cronJobRepository: CronJobRepository, + private readonly jobService: JobService, + private readonly webhookService: WebhookService, + private readonly storageService: StorageService, + private readonly paymentService: PaymentService, + ) {} public async startCronJob(cronJobType: CronJobType): Promise { let cronJob = await this.cronJobRepository.findOne({ @@ -52,4 +71,226 @@ export class CronJobService { cronJobEntity.completedAt = new Date(); return cronJobEntity.save(); } + + @Cron(CronExpression.EVERY_10_MINUTES) + public async createEscrowCronJob() { + const isCronJobRunning = await this.isCronJobRunning( + CronJobType.CreateEscrow, + ); + + if (isCronJobRunning) { + return; + } + + this.logger.log('Create escrow START'); + const cronJob = await this.startCronJob(CronJobType.CreateEscrow); + + try { + const jobEntities = await this.jobService.findJobByStatus(JobStatus.PAID); + for (const jobEntity of jobEntities) { + try { + await this.jobService.createEscrow(jobEntity); + } catch (err) { + this.logger.error(`Error creating escrow: ${err.message}`); + await this.jobService.handleProcessJobFailure(jobEntity); + } + } + } catch (e) { + this.logger.error(e); + } + + this.logger.log('Create escrow STOP'); + await this.completeCronJob(cronJob); + } + + @Cron(CronExpression.EVERY_10_MINUTES) + public async setupEscrowCronJob() { + const isCronJobRunning = await this.isCronJobRunning( + CronJobType.SetupEscrow, + ); + + if (isCronJobRunning) { + return; + } + + this.logger.log('Setup escrow START'); + const cronJob = await this.startCronJob(CronJobType.SetupEscrow); + + try { + const jobEntities = await this.jobService.findJobByStatus( + JobStatus.CREATED, + ); + + for (const jobEntity of jobEntities) { + try { + await this.jobService.setupEscrow(jobEntity); + } catch (err) { + this.logger.error(`Error setting up escrow: ${err.message}`); + await this.jobService.handleProcessJobFailure(jobEntity); + } + } + } catch (e) { + this.logger.error(e); + } + + this.logger.log('Setup escrow STOP'); + await this.completeCronJob(cronJob); + } + + @Cron(CronExpression.EVERY_10_MINUTES) + public async fundEscrowCronJob() { + const isCronJobRunning = await this.isCronJobRunning( + CronJobType.FundEscrow, + ); + + if (isCronJobRunning) { + return; + } + + this.logger.log('Fund escrow START'); + const cronJob = await this.startCronJob(CronJobType.FundEscrow); + + try { + const jobEntities = await this.jobService.findJobByStatus( + JobStatus.SET_UP, + ); + + for (const jobEntity of jobEntities) { + try { + await this.jobService.fundEscrow(jobEntity); + + const manifest = await this.storageService.download( + jobEntity.manifestUrl, + ); + + if ((manifest as CvatManifestDto)?.annotation?.type) { + await this.webhookService.createWebhook({ + escrowAddress: jobEntity.escrowAddress, + chainId: jobEntity.chainId, + eventType: EventType.ESCROW_CREATED, + oracleType: OracleType.CVAT, + hasSignature: false, + }); + } + } catch (err) { + this.logger.error(`Error funding escrow: ${err.message}`); + await this.jobService.handleProcessJobFailure(jobEntity); + } + } + } catch (e) { + this.logger.error(e); + } + + this.logger.log('Fund escrow STOP'); + await this.completeCronJob(cronJob); + } + + @Cron(CronExpression.EVERY_10_MINUTES) + public async cancelCronJob() { + const isCronJobRunning = await this.isCronJobRunning( + CronJobType.CancelEscrow, + ); + + if (isCronJobRunning) { + return; + } + + this.logger.log('Cancel jobs START'); + const cronJob = await this.startCronJob(CronJobType.CancelEscrow); + + try { + const jobEntities = await this.jobService.findJobByStatus( + JobStatus.TO_CANCEL, + ); + + for (const jobEntity of jobEntities) { + try { + if (jobEntity.escrowAddress) { + const { amountRefunded } = + await this.jobService.processEscrowCancellation(jobEntity); + await this.paymentService.createRefundPayment({ + refundAmount: Number(ethers.formatEther(amountRefunded)), + userId: jobEntity.userId, + jobId: jobEntity.id, + }); + } else { + await this.paymentService.createRefundPayment({ + refundAmount: jobEntity.fundAmount, + userId: jobEntity.userId, + jobId: jobEntity.id, + }); + } + jobEntity.status = JobStatus.CANCELED; + await jobEntity.save(); + + const manifest = await this.storageService.download( + jobEntity.manifestUrl, + ); + + const oracleType = this.jobService.getOracleType(manifest); + if (oracleType !== OracleType.HCAPTCHA) { + await this.webhookService.createWebhook({ + escrowAddress: jobEntity.escrowAddress, + chainId: jobEntity.chainId, + eventType: EventType.ESCROW_CANCELED, + oracleType: this.jobService.getOracleType(manifest), + hasSignature: + (manifest as FortuneManifestDto).requestType === + JobRequestType.FORTUNE, + }); + } + } catch (err) { + this.logger.error(`Error canceling escrow: ${err.message}`); + await this.jobService.handleProcessJobFailure(jobEntity); + } + } + } catch (e) { + this.logger.error(e); + } + await this.completeCronJob(cronJob); + this.logger.log('Cancel jobs STOP'); + return true; + } + + /** + * Process a pending webhook job. + * @returns {Promise} - Returns a promise that resolves when the operation is complete. + */ + @Cron(CronExpression.EVERY_10_MINUTES) + public async processPendingWebhooks(): Promise { + const isCronJobRunning = await this.isCronJobRunning( + CronJobType.ProcessPendingWebhook, + ); + + if (isCronJobRunning) { + return; + } + + this.logger.log('Pending webhooks START'); + const cronJob = await this.startCronJob(CronJobType.ProcessPendingWebhook); + + try { + const webhookEntities = await this.webhookService.findWebhookByStatus( + WebhookStatus.PENDING, + ); + + for (const webhookEntity of webhookEntities) { + try { + await this.webhookService.sendWebhook(webhookEntity); + await this.webhookService.updateWebhookStatus( + webhookEntity.id, + WebhookStatus.COMPLETED, + ); + } catch (err) { + this.logger.error(`Error sending webhook: ${err.message}`); + await this.webhookService.handleWebhookError(webhookEntity, err); + } + } + } catch (e) { + this.logger.error(e); + } + + this.logger.log('Pending webhooks STOP'); + await this.completeCronJob(cronJob); + } } diff --git a/packages/apps/job-launcher/server/src/modules/cron-job/cron.job.controller.ts b/packages/apps/job-launcher/server/src/modules/cron-job/cron.job.controller.ts new file mode 100644 index 0000000000..735f3ca689 --- /dev/null +++ b/packages/apps/job-launcher/server/src/modules/cron-job/cron.job.controller.ts @@ -0,0 +1,93 @@ +import { Controller, Get } from '@nestjs/common'; +import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; +import { Public } from '../../common/decorators'; +import { CronJobService } from './cron-job.service'; + +@Public() +@ApiTags('Cron') +@Controller('/cron') +export class CronJobController { + constructor(private readonly cronJobService: CronJobService) {} + + @ApiOperation({ + summary: 'Launch the cron job to create escrows', + description: 'Endpoint to launch the cron job to create escrows.', + }) + @ApiResponse({ + status: 200, + description: 'Cron job to create escrows launched successfully.', + }) + @ApiResponse({ + status: 404, + description: 'Not Found. Could not find the requested content.', + }) + @Get('/escrow/create') + public async launchCreateEscrowCronJob(): Promise { + await this.cronJobService.createEscrowCronJob(); + return; + } + + @ApiOperation({ + summary: 'Launch the cron job to setup escrows', + description: 'Endpoint to launch the cron job to setup escrows.', + }) + @ApiResponse({ + status: 200, + description: 'Cron job to setup escrows launched successfully.', + }) + @ApiResponse({ + status: 404, + description: 'Not Found. Could not find the requested content.', + }) + @Get('/escrow/setup') + public async launchSetupEscrowCronJob(): Promise { + await this.cronJobService.setupEscrowCronJob(); + return; + } + + @ApiOperation({ + summary: 'Launch the cron job to fund escrows', + description: 'Endpoint to launch the cron job to fund escrows.', + }) + @ApiResponse({ + status: 200, + description: 'Cron job to fund escrows launched successfully.', + }) + @ApiResponse({ + status: 404, + description: 'Not Found. Could not find the requested content.', + }) + @Get('/escrow/fund') + public async launchFundEscrowCronJob(): Promise { + await this.cronJobService.fundEscrowCronJob(); + return; + } + + @ApiOperation({ + summary: 'Cancel cron job', + description: 'Endpoint to launch cancel cron job.', + }) + @ApiResponse({ + status: 200, + description: 'Cron job launched successfully.', + }) + @Get('/escrow/cancel') + public async cancelCronJob(): Promise { + await this.cronJobService.cancelCronJob(); + return; + } + + @ApiOperation({ + summary: 'Process pending webhooks cron job', + description: 'Endpoint to launch Process pending webhooks cron job.', + }) + @ApiResponse({ + status: 200, + description: 'Cron job launched successfully.', + }) + @Get('/wehbhook/process') + public async processPendingWebhooks(): Promise { + await this.cronJobService.processPendingWebhooks(); + return; + } +} diff --git a/packages/apps/job-launcher/server/src/modules/job/job.controller.ts b/packages/apps/job-launcher/server/src/modules/job/job.controller.ts index 1af82db9a4..22ae4aa0b8 100644 --- a/packages/apps/job-launcher/server/src/modules/job/job.controller.ts +++ b/packages/apps/job-launcher/server/src/modules/job/job.controller.ts @@ -185,63 +185,6 @@ export class JobController { return this.jobService.getResult(req.user.id, jobId); } - @ApiOperation({ - summary: 'Launch the cron job to create escrows', - description: 'Endpoint to launch the cron job to create escrows.', - }) - @Public() - @ApiResponse({ - status: 200, - description: 'Cron job to create escrows launched successfully.', - }) - @ApiResponse({ - status: 404, - description: 'Not Found. Could not find the requested content.', - }) - @Get('/cron/create-escrow') - public async launchCreateEscrowCronJob(): Promise { - await this.jobService.createEscrowCronJob(); - return; - } - - @ApiOperation({ - summary: 'Launch the cron job to setup escrows', - description: 'Endpoint to launch the cron job to setup escrows.', - }) - @Public() - @ApiResponse({ - status: 200, - description: 'Cron job to setup escrows launched successfully.', - }) - @ApiResponse({ - status: 404, - description: 'Not Found. Could not find the requested content.', - }) - @Get('/cron/setup-escrow') - public async launchSetupEscrowCronJob(): Promise { - await this.jobService.setupEscrowCronJob(); - return; - } - - @ApiOperation({ - summary: 'Launch the cron job to fund escrows', - description: 'Endpoint to launch the cron job to fund escrows.', - }) - @Public() - @ApiResponse({ - status: 200, - description: 'Cron job to fund escrows launched successfully.', - }) - @ApiResponse({ - status: 404, - description: 'Not Found. Could not find the requested content.', - }) - @Get('/cron/fund-escrow') - public async launchFundEscrowCronJob(): Promise { - await this.jobService.fundEscrowCronJob(); - return; - } - @ApiOperation({ summary: 'Cancel a job', description: 'Endpoint to cancel a specified job.', @@ -267,21 +210,6 @@ export class JobController { return; } - @ApiOperation({ - summary: 'Cancel a cron job', - description: 'Endpoint to cancel a cron job.', - }) - @Public() - @ApiResponse({ - status: 200, - description: 'Cron job launched successfully.', - }) - @Get('/cron/cancel') - public async cancelCronJob(): Promise { - await this.jobService.cancelCronJob(); - return; - } - @ApiOperation({ summary: 'Handle escrow failed webhook', description: 'Endpoint to handle an escrow failed webhook.', diff --git a/packages/apps/job-launcher/server/src/modules/job/job.module.ts b/packages/apps/job-launcher/server/src/modules/job/job.module.ts index 819c0b0845..05760a902b 100644 --- a/packages/apps/job-launcher/server/src/modules/job/job.module.ts +++ b/packages/apps/job-launcher/server/src/modules/job/job.module.ts @@ -13,7 +13,6 @@ import { RoutingProtocolService } from './routing-protocol.service'; import { EncryptionModule } from '../encryption/encryption.module'; import { StorageModule } from '../storage/storage.module'; import { AuthModule } from '../auth/auth.module'; -import { WebhookModule } from '../webhook/webhook.module'; @Module({ imports: [ @@ -25,7 +24,6 @@ import { WebhookModule } from '../webhook/webhook.module'; Web3Module, EncryptionModule, StorageModule, - WebhookModule, ], controllers: [JobController], providers: [Logger, JobService, JobRepository, RoutingProtocolService], 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 22345e6f39..49c3397862 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 @@ -49,7 +49,6 @@ import { MOCK_CHAIN_ID, MOCK_EXCHANGE_ORACLE_ADDRESS, MOCK_ORACLE_FEE, - MOCK_EXCHANGE_ORACLE_WEBHOOK_URL, MOCK_FILE_HASH, MOCK_FILE_URL, MOCK_HCAPTCHA_ORACLE_ADDRESS, @@ -109,9 +108,6 @@ import { } from '../../common/constants'; import { WebhookService } from '../webhook/webhook.service'; import { CronJobService } from '../cron-job/cron-job.service'; -import { CronJobEntity } from '../cron-job/cron-job.entity'; -import { CronJobType } from '../../common/enums/cron-job'; -import { DeepPartial } from 'typeorm'; import { AWSRegions, StorageProviders } from '../../common/enums/storage'; const rate = 1.5; @@ -167,9 +163,7 @@ describe('JobService', () => { routingProtocolService: RoutingProtocolService, web3Service: Web3Service, encryption: Encryption, - storageService: StorageService, - webhookService: WebhookService, - cronJobService: CronJobService; + storageService: StorageService; let encrypt = true; @@ -272,8 +266,6 @@ describe('JobService', () => { createPaymentMock = jest.spyOn(paymentRepository, 'create'); web3Service = moduleRef.get(Web3Service); storageService = moduleRef.get(StorageService); - webhookService = moduleRef.get(WebhookService); - cronJobService = moduleRef.get(CronJobService); storageService.uploadFile = jest.fn().mockResolvedValue({ url: MOCK_FILE_URL, @@ -1642,520 +1634,6 @@ describe('JobService', () => { }); }); - describe('createEscrowCronJob', () => { - let createEscrowMock: any; - let cronJobEntityMock: Partial; - let jobEntityMock1: Partial, jobEntityMock2: Partial; - - beforeEach(() => { - cronJobEntityMock = { - cronJobType: CronJobType.CreateEscrow, - startedAt: new Date(), - }; - - jobEntityMock1 = { - status: JobStatus.PAID, - fundAmount: 100, - userId: 1, - id: 1, - manifestUrl: MOCK_FILE_URL, - manifestHash: MOCK_FILE_HASH, - escrowAddress: MOCK_ADDRESS, - chainId: ChainId.LOCALHOST, - retriesCount: 1, - save: jest.fn(), - }; - - jobEntityMock2 = { - status: JobStatus.PAID, - fundAmount: 100, - userId: 1, - id: 1, - manifestUrl: MOCK_FILE_URL, - manifestHash: MOCK_FILE_HASH, - escrowAddress: MOCK_ADDRESS, - chainId: ChainId.LOCALHOST, - retriesCount: 1, - save: jest.fn(), - }; - - jest - .spyOn(jobRepository, 'find') - .mockResolvedValue([jobEntityMock1 as any, jobEntityMock2 as any]); - - createEscrowMock = jest.spyOn(jobService, 'createEscrow'); - createEscrowMock.mockResolvedValue(true); - - jest.spyOn(cronJobService, 'isCronJobRunning').mockResolvedValue(false); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('should not run if the cron job is already running', async () => { - jest - .spyOn(cronJobService, 'isCronJobRunning') - .mockResolvedValueOnce(true); - - await jobService.createEscrowCronJob(); - - expect(createEscrowMock).not.toHaveBeenCalled(); - }); - - it('should create cron job entity on database to lock', async () => { - jest - .spyOn(cronJobService, 'startCronJob') - .mockResolvedValueOnce(cronJobEntityMock as any); - - await jobService.createEscrowCronJob(); - - expect(cronJobService.startCronJob).toHaveBeenCalledWith( - CronJobType.CreateEscrow, - ); - }); - - it('should run createEscrow for all of the jobs with status PAID', async () => { - await jobService.createEscrowCronJob(); - - expect(createEscrowMock).toHaveBeenCalledTimes(2); - }); - - it('should increase retriesCount by 1, if the job creation fails', async () => { - createEscrowMock.mockRejectedValueOnce(new Error('creation failed')); - - await jobService.createEscrowCronJob(); - - expect(createEscrowMock).toHaveBeenCalledTimes(2); - expect(jobEntityMock1.retriesCount).toBe(2); - expect(jobEntityMock2.retriesCount).toBe(1); - }); - - it('should mark job as failed if the job creation fails more than max retries count', async () => { - createEscrowMock.mockRejectedValueOnce(new Error('creation failed')); - jobEntityMock1.retriesCount = MOCK_MAX_RETRY_COUNT; - - await jobService.createEscrowCronJob(); - - expect(createEscrowMock).toHaveBeenCalledTimes(2); - expect(jobEntityMock1.status).toBe(JobStatus.FAILED); - expect(jobEntityMock2.status).toBe(JobStatus.PAID); - }); - - it('should complete the cron job entity on database to unlock', async () => { - jest - .spyOn(cronJobService, 'completeCronJob') - .mockResolvedValueOnce(cronJobEntityMock as any); - - await jobService.createEscrowCronJob(); - - expect(cronJobService.completeCronJob).toHaveBeenCalledWith( - CronJobType.CreateEscrow, - ); - }); - }); - - describe('setupEscrowCronJob', () => { - let setupEscrowMock: any; - let cronJobEntityMock: Partial; - let jobEntityMock1: Partial, jobEntityMock2: Partial; - - beforeEach(() => { - cronJobEntityMock = { - cronJobType: CronJobType.SetupEscrow, - createdAt: new Date(), - }; - - jobEntityMock1 = { - status: JobStatus.CREATED, - fundAmount: 100, - userId: 1, - id: 1, - manifestUrl: MOCK_FILE_URL, - manifestHash: MOCK_FILE_HASH, - escrowAddress: MOCK_ADDRESS, - chainId: ChainId.LOCALHOST, - retriesCount: 1, - save: jest.fn(), - }; - - jobEntityMock2 = { - status: JobStatus.CREATED, - fundAmount: 100, - userId: 1, - id: 1, - manifestUrl: MOCK_FILE_URL, - manifestHash: MOCK_FILE_HASH, - escrowAddress: MOCK_ADDRESS, - chainId: ChainId.LOCALHOST, - retriesCount: 1, - save: jest.fn(), - }; - - jest - .spyOn(jobRepository, 'find') - .mockResolvedValue([jobEntityMock1 as any, jobEntityMock2 as any]); - - setupEscrowMock = jest.spyOn(jobService, 'setupEscrow'); - setupEscrowMock.mockResolvedValue(true); - - jest.spyOn(cronJobService, 'isCronJobRunning').mockResolvedValue(false); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('should not run if the cron job is already running', async () => { - jest - .spyOn(cronJobService, 'isCronJobRunning') - .mockResolvedValueOnce(true); - - await jobService.setupEscrowCronJob(); - - expect(setupEscrowMock).not.toHaveBeenCalled(); - }); - - it('should create cron job entity on database to lock', async () => { - jest - .spyOn(cronJobService, 'startCronJob') - .mockResolvedValueOnce(cronJobEntityMock as any); - - await jobService.setupEscrowCronJob(); - - expect(cronJobService.startCronJob).toHaveBeenCalledWith( - CronJobType.SetupEscrow, - ); - }); - - it('should run setupEscrow for all of the jobs with status LAUNCHING', async () => { - await jobService.setupEscrowCronJob(); - - expect(setupEscrowMock).toHaveBeenCalledTimes(2); - }); - - it('should increase retriesCount by 1, if the job setup fails', async () => { - setupEscrowMock.mockRejectedValueOnce(new Error('setup failed')); - - await jobService.setupEscrowCronJob(); - - expect(setupEscrowMock).toHaveBeenCalledTimes(2); - expect(jobEntityMock1.retriesCount).toBe(2); - expect(jobEntityMock2.retriesCount).toBe(1); - }); - - it('should mark job as failed if the job setup fails more than max retries count', async () => { - setupEscrowMock.mockRejectedValueOnce(new Error('setup failed')); - jobEntityMock1.retriesCount = MOCK_MAX_RETRY_COUNT; - - await jobService.setupEscrowCronJob(); - - expect(setupEscrowMock).toHaveBeenCalledTimes(2); - expect(jobEntityMock1.status).toBe(JobStatus.FAILED); - expect(jobEntityMock2.status).toBe(JobStatus.CREATED); - }); - - it('should complete the cron job entity on database to unlock', async () => { - jest - .spyOn(cronJobService, 'completeCronJob') - .mockResolvedValueOnce(cronJobEntityMock as any); - - await jobService.setupEscrowCronJob(); - - expect(cronJobService.completeCronJob).toHaveBeenCalledWith( - CronJobType.SetupEscrow, - ); - }); - }); - - describe('fundEscrowCronJob', () => { - let fundEscrowMock: any; - let cronJobEntityMock: Partial; - let jobEntityMock1: Partial, jobEntityMock2: Partial; - let createWebhookMock: any; - - beforeEach(() => { - cronJobEntityMock = { - cronJobType: CronJobType.FundEscrow, - createdAt: new Date(), - }; - - jobEntityMock1 = { - status: JobStatus.SET_UP, - fundAmount: 100, - userId: 1, - id: 1, - manifestUrl: MOCK_FILE_URL, - manifestHash: MOCK_FILE_HASH, - escrowAddress: MOCK_ADDRESS, - chainId: ChainId.LOCALHOST, - retriesCount: 1, - save: jest.fn(), - }; - - jobEntityMock2 = { - status: JobStatus.SET_UP, - fundAmount: 100, - userId: 1, - id: 1, - manifestUrl: MOCK_FILE_URL, - manifestHash: MOCK_FILE_HASH, - escrowAddress: MOCK_ADDRESS, - chainId: ChainId.LOCALHOST, - retriesCount: 1, - save: jest.fn(), - }; - - jest - .spyOn(jobRepository, 'find') - .mockResolvedValue([jobEntityMock1 as any, jobEntityMock2 as any]); - - fundEscrowMock = jest.spyOn(jobService, 'fundEscrow'); - fundEscrowMock.mockResolvedValue(true); - - jest.spyOn(cronJobService, 'isCronJobRunning').mockResolvedValue(false); - - createWebhookMock = jest.spyOn(webhookService, 'createWebhook'); - - const cvatManifestMock: DeepPartial = { - data: { - data_url: MOCK_FILE_URL, - }, - annotation: { - type: JobRequestType.IMAGE_POINTS, - }, - }; - jest - .spyOn(storageService, 'download') - .mockResolvedValue(cvatManifestMock); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('should not run if the cron job is already running', async () => { - jest - .spyOn(cronJobService, 'isCronJobRunning') - .mockResolvedValueOnce(true); - - await jobService.fundEscrowCronJob(); - - expect(fundEscrowMock).not.toHaveBeenCalled(); - }); - - it('should create cron job entity on database to lock', async () => { - jest - .spyOn(cronJobService, 'startCronJob') - .mockResolvedValueOnce(cronJobEntityMock as any); - - await jobService.fundEscrowCronJob(); - - expect(cronJobService.startCronJob).toHaveBeenCalledWith( - CronJobType.FundEscrow, - ); - }); - - it('should run fundEscrow for all of the jobs with status FUNDING, and trigger webhook', async () => { - await jobService.fundEscrowCronJob(); - - expect(fundEscrowMock).toHaveBeenCalledTimes(2); - expect(createWebhookMock).toHaveBeenCalledTimes(2); - }); - - it('should increase retriesCount by 1, if the job fund fails', async () => { - fundEscrowMock.mockRejectedValueOnce(new Error('fund failed')); - - await jobService.fundEscrowCronJob(); - - expect(fundEscrowMock).toHaveBeenCalledTimes(2); - expect(jobEntityMock1.retriesCount).toBe(2); - expect(jobEntityMock2.retriesCount).toBe(1); - - expect(createWebhookMock).toHaveBeenCalledTimes(1); - }); - - it('should mark job as failed if the job fund fails more than max retries count', async () => { - fundEscrowMock.mockRejectedValueOnce(new Error('fund failed')); - jobEntityMock1.retriesCount = MOCK_MAX_RETRY_COUNT; - - await jobService.fundEscrowCronJob(); - - expect(fundEscrowMock).toHaveBeenCalledTimes(2); - expect(jobEntityMock1.status).toBe(JobStatus.FAILED); - expect(jobEntityMock2.status).toBe(JobStatus.SET_UP); - - expect(createWebhookMock).toHaveBeenCalledTimes(1); - }); - - it('should complete the cron job entity on database to unlock', async () => { - jest - .spyOn(cronJobService, 'completeCronJob') - .mockResolvedValueOnce(cronJobEntityMock as any); - - await jobService.fundEscrowCronJob(); - - expect(cronJobService.completeCronJob).toHaveBeenCalledWith( - CronJobType.FundEscrow, - ); - }); - }); - - describe('cancelCronJob', () => { - let findJobMock: any, - jobEntityMock1: Partial, - jobEntityMock2: Partial; - - beforeEach(() => { - jobEntityMock1 = { - status: JobStatus.TO_CANCEL, - fundAmount: 100, - userId: 1, - id: 1, - manifestUrl: MOCK_FILE_URL, - escrowAddress: MOCK_ADDRESS, - chainId: ChainId.LOCALHOST, - save: jest.fn(), - retriesCount: 0, - }; - - jobEntityMock2 = { - status: JobStatus.TO_CANCEL, - fundAmount: 100, - userId: 1, - id: 2, - manifestUrl: MOCK_FILE_URL, - escrowAddress: MOCK_ADDRESS, - chainId: ChainId.LOCALHOST, - save: jest.fn(), - retriesCount: 0, - }; - - findJobMock = jest - .spyOn(jobRepository, 'find') - .mockResolvedValue([jobEntityMock1 as any, jobEntityMock2 as any]); - - jest.spyOn(cronJobService, 'isCronJobRunning').mockResolvedValue(false); - - jest.spyOn(jobService, 'processEscrowCancellation').mockResolvedValue({ - txHash: MOCK_TRANSACTION_HASH, - amountRefunded: 1n, - }); - - (EscrowClient.build as any).mockImplementation(() => ({ - getExchangeOracleAddress: jest - .fn() - .mockResolvedValue(MOCK_EXCHANGE_ORACLE_ADDRESS), - })); - - (KVStoreClient.build as any).mockImplementation(() => ({ - get: jest.fn().mockResolvedValue(MOCK_EXCHANGE_ORACLE_WEBHOOK_URL), - })); - - const manifestMock = { - requestType: JobRequestType.FORTUNE, - }; - storageService.download = jest.fn().mockResolvedValue(manifestMock); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - it('should not run if cron job is already running', async () => { - jest - .spyOn(cronJobService, 'isCronJobRunning') - .mockResolvedValueOnce(true); - - await jobService.cancelCronJob(); - - expect(findJobMock).not.toHaveBeenCalled(); - }); - - it('should create cron job entity on database to lock', async () => { - jest - .spyOn(cronJobService, 'startCronJob') - .mockResolvedValueOnce({} as any); - - await jobService.cancelCronJob(); - - expect(cronJobService.startCronJob).toHaveBeenCalledWith( - CronJobType.CancelEscrow, - ); - }); - - it('should cancel all of the jobs with status TO_CANCEL', async () => { - const result = await jobService.cancelCronJob(); - - expect(result).toBeTruthy(); - expect(jobService.processEscrowCancellation).toHaveBeenCalledWith( - jobEntityMock1, - ); - expect(jobEntityMock1.save).toHaveBeenCalled(); - expect(jobService.processEscrowCancellation).toHaveBeenCalledWith( - jobEntityMock2, - ); - expect(jobEntityMock2.save).toHaveBeenCalled(); - expect(webhookService.createWebhook).toHaveBeenCalledTimes(2); - }); - - it('should not call process escrow cancellation when escrowAddress is not present', async () => { - const jobEntityWithoutEscrow = { - ...jobEntityMock1, - escrowAddress: undefined, - }; - - jest - .spyOn(jobRepository, 'find') - .mockResolvedValueOnce([jobEntityWithoutEscrow as any]); - jest - .spyOn(jobService, 'processEscrowCancellation') - .mockResolvedValueOnce(undefined as any); - - expect(await jobService.cancelCronJob()).toBe(true); - expect(jobService.processEscrowCancellation).toHaveBeenCalledTimes(0); - }); - - it('should increase retriesCount by 1 if the job cancellation fails', async () => { - jest - .spyOn(jobService, 'processEscrowCancellation') - .mockRejectedValueOnce(new Error('cancellation failed')); - - expect(jobEntityMock1.retriesCount).toBe(0); - expect(jobEntityMock2.retriesCount).toBe(0); - - await jobService.cancelCronJob(); - - expect(jobEntityMock1.retriesCount).toBe(1); - expect(jobEntityMock2.retriesCount).toBe(0); - }); - - it('should mark job as failed if the job cancellation fails more than max retries count', async () => { - jest - .spyOn(jobService, 'processEscrowCancellation') - .mockRejectedValueOnce(new Error('cancellation failed')); - jobEntityMock1.retriesCount = MOCK_MAX_RETRY_COUNT; - - await jobService.cancelCronJob(); - - expect(jobService.processEscrowCancellation).toHaveBeenCalledTimes(2); - expect(jobEntityMock1.status).toBe(JobStatus.FAILED); - expect(jobEntityMock2.status).toBe(JobStatus.CANCELED); - }); - - it('should complete the cron job entity on database to unlock', async () => { - jest - .spyOn(cronJobService, 'completeCronJob') - .mockResolvedValueOnce({} as any); - - await jobService.cancelCronJob(); - - expect(cronJobService.completeCronJob).toHaveBeenCalledWith( - CronJobType.CancelEscrow, - ); - }); - }); - describe('processEscrowCancellation', () => { const jobEntityMock = { status: JobStatus.TO_CANCEL, 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 7a63dd2341..821e3796e1 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 @@ -24,7 +24,7 @@ import { import { ConfigService } from '@nestjs/config'; import { validate } from 'class-validator'; import { ethers } from 'ethers'; -import { In, LessThanOrEqual, QueryFailedError } from 'typeorm'; +import { LessThanOrEqual, QueryFailedError } from 'typeorm'; import { ConfigNames } from '../../common/config'; import { ErrorBucket, @@ -97,11 +97,7 @@ import Decimal from 'decimal.js'; import { EscrowData } from '@human-protocol/sdk/dist/graphql'; import { filterToEscrowStatus } from '../../common/utils/status'; import { StorageService } from '../storage/storage.service'; -import { WebhookService } from '../webhook/webhook.service'; import stringify from 'json-stable-stringify'; -import { Cron, CronExpression } from '@nestjs/schedule'; -import { CronJobService } from '../cron-job/cron-job.service'; -import { CronJobType } from '../../common/enums/cron-job'; import { generateBucketUrl, listObjectsInBucket, @@ -124,8 +120,6 @@ export class JobService { public readonly configService: ConfigService, private readonly routingProtocolService: RoutingProtocolService, private readonly storageService: StorageService, - private readonly webhookService: WebhookService, - private readonly cronJobService: CronJobService, @Inject(Encryption) private readonly encryption: Encryption, ) {} @@ -944,254 +938,27 @@ export class JobService { return finalResultUrl; } - @Cron(CronExpression.EVERY_10_MINUTES) - public async createEscrowCronJob() { - const isCronJobRunning = await this.cronJobService.isCronJobRunning( - CronJobType.CreateEscrow, - ); - - if (isCronJobRunning) { - return; - } - - this.logger.log('Create escrow START'); - const cronJob = await this.cronJobService.startCronJob( - CronJobType.CreateEscrow, - ); - - try { - const jobEntities = await this.jobRepository.find( - { - status: In([JobStatus.PAID]), - retriesCount: LessThanOrEqual( - this.configService.get( - ConfigNames.MAX_RETRY_COUNT, - DEFAULT_MAX_RETRY_COUNT, - ), - ), - waitUntil: LessThanOrEqual(new Date()), - }, - { - order: { - waitUntil: SortDirection.ASC, - }, - }, - ); - - for (const jobEntity of jobEntities) { - try { - await this.createEscrow(jobEntity); - } catch (err) { - this.logger.error(`Error creating escrow: ${err.message}`); - await this.handleProcessJobFailure(jobEntity); - } - } - } catch (e) { - this.logger.error(e); - } - - this.logger.log('Create escrow STOP'); - await this.cronJobService.completeCronJob(cronJob); - } - - @Cron(CronExpression.EVERY_10_MINUTES) - public async setupEscrowCronJob() { - const isCronJobRunning = await this.cronJobService.isCronJobRunning( - CronJobType.SetupEscrow, - ); - - if (isCronJobRunning) { - return; - } - - this.logger.log('Setup escrow START'); - const cronJob = await this.cronJobService.startCronJob( - CronJobType.SetupEscrow, - ); - - try { - const jobEntities = await this.jobRepository.find( - { - status: JobStatus.CREATED, - retriesCount: LessThanOrEqual( - this.configService.get( - ConfigNames.MAX_RETRY_COUNT, - DEFAULT_MAX_RETRY_COUNT, - ), - ), - waitUntil: LessThanOrEqual(new Date()), - }, - { - order: { - waitUntil: SortDirection.ASC, - }, - }, - ); - - for (const jobEntity of jobEntities) { - try { - await this.setupEscrow(jobEntity); - } catch (err) { - this.logger.error(`Error setting up escrow: ${err.message}`); - await this.handleProcessJobFailure(jobEntity); - } - } - } catch (e) { - this.logger.error(e); - } - - this.logger.log('Setup escrow STOP'); - await this.cronJobService.completeCronJob(cronJob); - } - - @Cron(CronExpression.EVERY_10_MINUTES) - public async fundEscrowCronJob() { - const isCronJobRunning = await this.cronJobService.isCronJobRunning( - CronJobType.FundEscrow, - ); - - if (isCronJobRunning) { - return; - } - - this.logger.log('Fund escrow START'); - const cronJob = await this.cronJobService.startCronJob( - CronJobType.FundEscrow, - ); - - try { - const jobEntities = await this.jobRepository.find( - { - status: JobStatus.SET_UP, - retriesCount: LessThanOrEqual( - this.configService.get( - ConfigNames.MAX_RETRY_COUNT, - DEFAULT_MAX_RETRY_COUNT, - ), + public findJobByStatus = async (status: JobStatus) => { + return this.jobRepository.find( + { + status: status, + retriesCount: LessThanOrEqual( + this.configService.get( + ConfigNames.MAX_RETRY_COUNT, + DEFAULT_MAX_RETRY_COUNT, ), - waitUntil: LessThanOrEqual(new Date()), - }, - { - order: { - waitUntil: SortDirection.ASC, - }, + ), + waitUntil: LessThanOrEqual(new Date()), + }, + { + order: { + waitUntil: SortDirection.ASC, }, - ); - - for (const jobEntity of jobEntities) { - try { - await this.fundEscrow(jobEntity); - - const manifest = await this.storageService.download( - jobEntity.manifestUrl, - ); - - if ((manifest as CvatManifestDto)?.annotation?.type) { - await this.webhookService.createWebhook({ - escrowAddress: jobEntity.escrowAddress, - chainId: jobEntity.chainId, - eventType: EventType.ESCROW_CREATED, - oracleType: OracleType.CVAT, - hasSignature: false, - }); - } - } catch (err) { - this.logger.error(`Error funding escrow: ${err.message}`); - await this.handleProcessJobFailure(jobEntity); - } - } - } catch (e) { - this.logger.error(e); - } - - this.logger.log('Fund escrow STOP'); - await this.cronJobService.completeCronJob(cronJob); - } - - @Cron(CronExpression.EVERY_10_MINUTES) - public async cancelCronJob() { - const isCronJobRunning = await this.cronJobService.isCronJobRunning( - CronJobType.FundEscrow, - ); - - if (isCronJobRunning) { - return; - } - - this.logger.log('Cancel jobs START'); - const cronJob = await this.cronJobService.startCronJob( - CronJobType.CancelEscrow, + }, ); + }; - try { - const jobEntities = await this.jobRepository.find( - { - status: JobStatus.TO_CANCEL, - retriesCount: LessThanOrEqual( - this.configService.get( - ConfigNames.MAX_RETRY_COUNT, - DEFAULT_MAX_RETRY_COUNT, - ), - ), - waitUntil: LessThanOrEqual(new Date()), - }, - { - order: { - waitUntil: SortDirection.ASC, - }, - }, - ); - - for (const jobEntity of jobEntities) { - try { - if (jobEntity.escrowAddress) { - const { amountRefunded } = - await this.processEscrowCancellation(jobEntity); - await this.paymentService.createRefundPayment({ - refundAmount: Number(ethers.formatEther(amountRefunded)), - userId: jobEntity.userId, - jobId: jobEntity.id, - }); - } else { - await this.paymentService.createRefundPayment({ - refundAmount: jobEntity.fundAmount, - userId: jobEntity.userId, - jobId: jobEntity.id, - }); - } - jobEntity.status = JobStatus.CANCELED; - await jobEntity.save(); - - const manifest = await this.storageService.download( - jobEntity.manifestUrl, - ); - - const oracleType = this.getOracleType(manifest); - if (oracleType !== OracleType.HCAPTCHA) { - await this.webhookService.createWebhook({ - escrowAddress: jobEntity.escrowAddress, - chainId: jobEntity.chainId, - eventType: EventType.ESCROW_CANCELED, - oracleType: this.getOracleType(manifest), - hasSignature: - (manifest as FortuneManifestDto).requestType === - JobRequestType.FORTUNE, - }); - } - } catch (err) { - this.logger.error(`Error canceling escrow: ${err.message}`); - await this.handleProcessJobFailure(jobEntity); - } - } - } catch (e) { - this.logger.error(e); - } - await this.cronJobService.completeCronJob(cronJob); - this.logger.log('Cancel jobs STOP'); - return true; - } - - private handleProcessJobFailure = async (jobEntity: JobEntity) => { + public handleProcessJobFailure = async (jobEntity: JobEntity) => { if ( jobEntity.retriesCount < this.configService.get( @@ -1207,7 +974,7 @@ export class JobService { await jobEntity.save(); }; - private getOracleType(manifest: any): OracleType { + public getOracleType(manifest: any): OracleType { if ( (manifest as FortuneManifestDto)?.requestType === JobRequestType.FORTUNE ) { diff --git a/packages/apps/job-launcher/server/src/modules/webhook/webhook.controller.ts b/packages/apps/job-launcher/server/src/modules/webhook/webhook.controller.ts deleted file mode 100644 index 618873ad99..0000000000 --- a/packages/apps/job-launcher/server/src/modules/webhook/webhook.controller.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Controller, Get } from '@nestjs/common'; -import { ApiTags } from '@nestjs/swagger'; -import { Public } from '../../common/decorators'; -import { WebhookService } from './webhook.service'; - -@Public() -@ApiTags('Webhook') -@Controller('/webhook') -export class WebhookController { - constructor(private readonly webhookService: WebhookService) {} - @Public() - @Get('/cron') - public async processPendingWebhooks(): Promise { - await this.webhookService.processPendingWebhooks(); - return; - } -} diff --git a/packages/apps/job-launcher/server/src/modules/webhook/webhook.module.ts b/packages/apps/job-launcher/server/src/modules/webhook/webhook.module.ts index 393307a688..82b37d7335 100644 --- a/packages/apps/job-launcher/server/src/modules/webhook/webhook.module.ts +++ b/packages/apps/job-launcher/server/src/modules/webhook/webhook.module.ts @@ -4,7 +4,6 @@ import { ConfigModule } from '@nestjs/config'; import { WebhookService } from './webhook.service'; import { WebhookEntity } from './webhook.entity'; -import { WebhookController } from './webhook.controller'; import { WebhookRepository } from './webhook.repository'; import { Web3Module } from '../web3/web3.module'; import { HttpModule } from '@nestjs/axios'; @@ -16,7 +15,6 @@ import { HttpModule } from '@nestjs/axios'; Web3Module, HttpModule, ], - controllers: [WebhookController], providers: [Logger, WebhookService, WebhookRepository], exports: [WebhookService], }) diff --git a/packages/apps/job-launcher/server/src/modules/webhook/webhook.service.spec.ts b/packages/apps/job-launcher/server/src/modules/webhook/webhook.service.spec.ts index 11f2a77a83..4952f0a7d7 100644 --- a/packages/apps/job-launcher/server/src/modules/webhook/webhook.service.spec.ts +++ b/packages/apps/job-launcher/server/src/modules/webhook/webhook.service.spec.ts @@ -23,9 +23,6 @@ import { WebhookRepository } from './webhook.repository'; import { WebhookService } from './webhook.service'; import { of } from 'rxjs'; import { HEADER_SIGNATURE_KEY } from '../../common/constants'; -import { CronJobService } from '../cron-job/cron-job.service'; -import { CronJobEntity } from '../cron-job/cron-job.entity'; -import { CronJobType } from '../../common/enums/cron-job'; jest.mock('@human-protocol/sdk', () => ({ ...jest.requireActual('@human-protocol/sdk'), @@ -41,8 +38,7 @@ describe('WebhookService', () => { let webhookService: WebhookService, webhookRepository: WebhookRepository, web3Service: Web3Service, - httpService: HttpService, - cronJobService: CronJobService; + httpService: HttpService; const signerMock = { address: MOCK_ADDRESS, @@ -80,10 +76,6 @@ describe('WebhookService', () => { }, { provide: ConfigService, useValue: mockConfigService }, { provide: HttpService, useValue: createMock() }, - { - provide: CronJobService, - useValue: createMock(), - }, ], }).compile(); @@ -91,7 +83,6 @@ describe('WebhookService', () => { webhookRepository = moduleRef.get(WebhookRepository); web3Service = moduleRef.get(Web3Service); httpService = moduleRef.get(HttpService); - cronJobService = moduleRef.get(CronJobService); }); afterEach(() => { @@ -293,132 +284,6 @@ describe('WebhookService', () => { }); }); - describe('processPendingCronJob', () => { - let sendWebhookMock: any; - let cronJobEntityMock: Partial; - let webhookEntity1: Partial, - webhookEntity2: Partial; - - beforeEach(() => { - cronJobEntityMock = { - cronJobType: CronJobType.ProcessPendingWebhook, - startedAt: new Date(), - }; - - webhookEntity1 = { - id: 1, - chainId: ChainId.LOCALHOST, - escrowAddress: MOCK_ADDRESS, - status: WebhookStatus.PENDING, - waitUntil: new Date(), - retriesCount: 0, - }; - - webhookEntity2 = { - id: 2, - chainId: ChainId.LOCALHOST, - escrowAddress: MOCK_ADDRESS, - status: WebhookStatus.PENDING, - waitUntil: new Date(), - retriesCount: 0, - }; - - jest - .spyOn(webhookRepository, 'find') - .mockResolvedValue([webhookEntity1 as any, webhookEntity2 as any]); - - sendWebhookMock = jest.spyOn(webhookService as any, 'sendWebhook'); - sendWebhookMock.mockResolvedValue(true); - - jest.spyOn(cronJobService, 'isCronJobRunning').mockResolvedValue(false); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('should not run if cron job is already running', async () => { - jest - .spyOn(cronJobService, 'isCronJobRunning') - .mockResolvedValueOnce(true); - - const startCronJobMock = jest.spyOn(cronJobService, 'startCronJob'); - - await (webhookService as any).processPendingWebhooks(); - - expect(startCronJobMock).not.toHaveBeenCalled(); - }); - - it('should create cron job entity to lock the process', async () => { - jest - .spyOn(cronJobService, 'startCronJob') - .mockResolvedValueOnce(cronJobEntityMock as any); - - await (webhookService as any).processPendingWebhooks(); - - expect(cronJobService.startCronJob).toHaveBeenCalledWith( - CronJobType.ProcessPendingWebhook, - ); - }); - - it('should send webhook for all of the pending webhooks', async () => { - await (webhookService as any).processPendingWebhooks(); - - expect(sendWebhookMock).toHaveBeenCalledTimes(2); - expect(sendWebhookMock).toHaveBeenCalledWith(webhookEntity1); - expect(sendWebhookMock).toHaveBeenCalledWith(webhookEntity2); - - expect(webhookRepository.updateOne).toHaveBeenCalledTimes(2); - expect(webhookRepository.updateOne).toHaveBeenCalledWith( - { id: webhookEntity1.id }, - { status: WebhookStatus.COMPLETED }, - ); - expect(webhookRepository.updateOne).toHaveBeenCalledWith( - { id: webhookEntity2.id }, - { status: WebhookStatus.COMPLETED }, - ); - }); - - it('should increase retriesCount by 1 if sending webhook fails', async () => { - sendWebhookMock.mockRejectedValueOnce(new Error()); - - await (webhookService as any).processPendingWebhooks(); - - expect(webhookRepository.updateOne).toHaveBeenCalledWith( - { id: webhookEntity1.id }, - { - retriesCount: 1, - waitUntil: expect.any(Date), - }, - ); - }); - - it('should mark webhook as failed if retriesCount exceeds threshold', async () => { - sendWebhookMock.mockRejectedValueOnce(new Error()); - - webhookEntity1.retriesCount = MOCK_MAX_RETRY_COUNT; - - await (webhookService as any).processPendingWebhooks(); - - expect(webhookRepository.updateOne).toHaveBeenCalledWith( - { id: webhookEntity1.id }, - { status: WebhookStatus.FAILED }, - ); - }); - - it('should complete the cron job entity to unlock', async () => { - jest - .spyOn(cronJobService, 'completeCronJob') - .mockResolvedValueOnce(cronJobEntityMock as any); - - await (webhookService as any).processPendingWebhooks(); - - expect(cronJobService.completeCronJob).toHaveBeenCalledWith( - cronJobEntityMock as any, - ); - }); - }); - describe('getExchangeOracleWebhookUrl', () => { it('should get the exchange oracle webhook URL', async () => { web3Service.getSigner = jest.fn().mockReturnValue({ diff --git a/packages/apps/job-launcher/server/src/modules/webhook/webhook.service.ts b/packages/apps/job-launcher/server/src/modules/webhook/webhook.service.ts index c1bfc4039c..5a7717f319 100644 --- a/packages/apps/job-launcher/server/src/modules/webhook/webhook.service.ts +++ b/packages/apps/job-launcher/server/src/modules/webhook/webhook.service.ts @@ -7,7 +7,6 @@ import { KVStoreKeys, } from '@human-protocol/sdk'; import { LessThanOrEqual } from 'typeorm'; -import { Cron, CronExpression } from '@nestjs/schedule'; import { ConfigService } from '@nestjs/config'; import { ConfigNames } from '../../common/config'; import { signMessage } from '../../common/utils/signature'; @@ -22,8 +21,6 @@ import { Web3Service } from '../web3/web3.service'; import { WebhookStatus } from '../../common/enums/webhook'; import { ErrorWebhook } from '../../common/constants/errors'; import { WebhookEntity } from './webhook.entity'; -import { CronJobService } from '../cron-job/cron-job.service'; -import { CronJobType } from '../../common/enums/cron-job'; import { WebhookDataDto, WebhookDto } from './webhook.dto'; import { CaseConverter } from '../../common/utils/case-converter'; @Injectable() @@ -36,7 +33,6 @@ export class WebhookService { private readonly webhookRepository: WebhookRepository, public readonly configService: ConfigService, public readonly httpService: HttpService, - private readonly cronJobService: CronJobService, ) {} /** @@ -75,7 +71,7 @@ export class WebhookService { * @returns {Promise} - Returns a promise that resolves when the operation is complete. * @throws {Error} - Throws an error if an issue occurs during the process. */ - private async sendWebhook(webhook: WebhookEntity): Promise { + public async sendWebhook(webhook: WebhookEntity): Promise { // Configure the HTTP request object. let config = {}; const webhookUrl = await this.getExchangeOracleWebhookUrl( @@ -148,59 +144,6 @@ export class WebhookService { return exchangeOracleUrl; } - /** - * Process a pending webhook job. - * @returns {Promise} - Returns a promise that resolves when the operation is complete. - */ - @Cron(CronExpression.EVERY_10_MINUTES) - public async processPendingWebhooks(): Promise { - const isCronJobRunning = await this.cronJobService.isCronJobRunning( - CronJobType.ProcessPendingWebhook, - ); - - if (isCronJobRunning) { - return; - } - - this.logger.log('Pending webhooks START'); - const cronJob = await this.cronJobService.startCronJob( - CronJobType.ProcessPendingWebhook, - ); - - try { - const webhookEntities = await this.webhookRepository.find({ - status: WebhookStatus.PENDING, - retriesCount: LessThanOrEqual( - this.configService.get( - ConfigNames.MAX_RETRY_COUNT, - DEFAULT_MAX_RETRY_COUNT, - ), - ), - waitUntil: LessThanOrEqual(new Date()), - }); - - for (const webhookEntity of webhookEntities) { - try { - await this.sendWebhook(webhookEntity); - await this.webhookRepository.updateOne( - { id: webhookEntity.id }, - { - status: WebhookStatus.COMPLETED, - }, - ); - } catch (err) { - this.logger.error(`Error sending webhook: ${err.message}`); - await this.handleWebhookError(webhookEntity, err); - } - } - } catch (e) { - this.logger.error(e); - } - - this.logger.log('Pending webhooks STOP'); - await this.cronJobService.completeCronJob(cronJob); - } - /** * Handles errors that occur during webhook processing. * It logs the error and, based on retry count, updates the webhook status accordingly. @@ -208,7 +151,7 @@ export class WebhookService { * @param error - The error object thrown during processing. * @returns {Promise} - Returns a promise that resolves when the operation is complete. */ - private async handleWebhookError( + public async handleWebhookError( webhookEntity: WebhookEntity, error: any, ): Promise { @@ -239,4 +182,43 @@ export class WebhookService { WebhookService.name, ); } + /** + * Finds webhooks based on the provided status and additional criteria. + * @param status - WebhookStatus enum representing the status to filter by. + * @returns {Promise} - Returns a promise that resolves with an array of WebhookEntity objects. + * @throws {Error} - Throws an error if an issue occurs during the retrieval process. + */ + public async findWebhookByStatus( + status: WebhookStatus, + ): Promise { + return this.webhookRepository.find({ + status: status, + retriesCount: LessThanOrEqual( + this.configService.get( + ConfigNames.MAX_RETRY_COUNT, + DEFAULT_MAX_RETRY_COUNT, + ), + ), + waitUntil: LessThanOrEqual(new Date()), + }); + } + + /** + * Updates the status of a webhook identified by its ID. + * @param id - The ID of the webhook to update. + * @param status - WebhookStatus enum representing the new status. + * @returns {Promise} - Returns a promise that resolves when the update operation is complete. + * @throws {Error} - Throws an error if an issue occurs during the update process. + */ + public async updateWebhookStatus( + id: number, + status: WebhookStatus, + ): Promise { + await this.webhookRepository.updateOne( + { id: id }, + { + status: status, + }, + ); + } } diff --git a/packages/apps/job-launcher/server/vercel.json b/packages/apps/job-launcher/server/vercel.json index a43e293568..3e7126787b 100644 --- a/packages/apps/job-launcher/server/vercel.json +++ b/packages/apps/job-launcher/server/vercel.json @@ -17,27 +17,5 @@ } } ], - "crons": [ - { - "path": "/job/cron/create-escrow", - "schedule": "*/10 * * * *" - }, - { - "path": "/job/cron/setup-escrow", - "schedule": "*/10 * * * *" - }, - { - "path": "/job/cron/fund-escrow", - "schedule": "*/10 * * * *" - }, - { - "path": "/job/cron/cancel", - "schedule": "*/10 * * * *" - }, - { - "path": "/webhook/cron", - "schedule": "*/10 * * * *" - } - ], "ignoreCommand": "git diff HEAD^ HEAD --quiet ." } From 62e184866e678c05a9fb616a7d7e6671fd0e62d2 Mon Sep 17 00:00:00 2001 From: Eric Lee <98655210+leric7@users.noreply.github.com> Date: Fri, 26 Jan 2024 22:28:47 +0800 Subject: [PATCH 063/104] [Reputation Oracle] feat: Implement API endpoints for KYC (#1495) * basic kyc endpoint * register address endpoint * add unit test * refactor * resolve comments * return existing session id inside kyc start * fix start session endpoint --- .../server/src/app.module.ts | 2 + .../server/src/common/config/env.ts | 5 + .../server/src/common/constants/errors.ts | 11 + .../server/src/common/enums/user.ts | 10 + .../server/src/database/database.module.ts | 2 + .../migrations/1706196712859-AddKycTable.ts | 25 ++ .../src/modules/auth/auth.service.spec.ts | 7 + .../server/src/modules/auth/auth.service.ts | 2 + .../src/modules/auth/strategy/jwt.http.ts | 2 +- .../src/modules/kyc/kyc.controller.spec.ts | 67 +++++ .../server/src/modules/kyc/kyc.controller.ts | 72 +++++ .../server/src/modules/kyc/kyc.dto.ts | 69 +++++ .../server/src/modules/kyc/kyc.entity.ts | 29 ++ .../server/src/modules/kyc/kyc.module.ts | 23 ++ .../server/src/modules/kyc/kyc.repository.ts | 66 +++++ .../src/modules/kyc/kyc.service.spec.ts | 275 ++++++++++++++++++ .../server/src/modules/kyc/kyc.service.ts | 129 ++++++++ .../src/modules/user/user.controller.ts | 67 +++++ .../server/src/modules/user/user.dto.ts | 17 ++ .../server/src/modules/user/user.entity.ts | 4 + .../server/src/modules/user/user.module.ts | 5 +- .../src/modules/user/user.service.spec.ts | 83 +++++- .../server/src/modules/user/user.service.ts | 35 ++- 23 files changed, 1000 insertions(+), 7 deletions(-) create mode 100644 packages/apps/reputation-oracle/server/src/database/migrations/1706196712859-AddKycTable.ts create mode 100644 packages/apps/reputation-oracle/server/src/modules/kyc/kyc.controller.spec.ts create mode 100644 packages/apps/reputation-oracle/server/src/modules/kyc/kyc.controller.ts create mode 100644 packages/apps/reputation-oracle/server/src/modules/kyc/kyc.dto.ts create mode 100644 packages/apps/reputation-oracle/server/src/modules/kyc/kyc.entity.ts create mode 100644 packages/apps/reputation-oracle/server/src/modules/kyc/kyc.module.ts create mode 100644 packages/apps/reputation-oracle/server/src/modules/kyc/kyc.repository.ts create mode 100644 packages/apps/reputation-oracle/server/src/modules/kyc/kyc.service.spec.ts create mode 100644 packages/apps/reputation-oracle/server/src/modules/kyc/kyc.service.ts create mode 100644 packages/apps/reputation-oracle/server/src/modules/user/user.controller.ts diff --git a/packages/apps/reputation-oracle/server/src/app.module.ts b/packages/apps/reputation-oracle/server/src/app.module.ts index 9832d3370e..d898500a62 100644 --- a/packages/apps/reputation-oracle/server/src/app.module.ts +++ b/packages/apps/reputation-oracle/server/src/app.module.ts @@ -14,6 +14,7 @@ import { AuthModule } from './modules/auth/auth.module'; import { ServeStaticModule } from '@nestjs/serve-static'; import { join } from 'path'; import { SnakeCaseInterceptor } from './common/interceptors/snake-case'; +import { KycModule } from './modules/kyc/kyc.module'; @Module({ providers: [ @@ -40,6 +41,7 @@ import { SnakeCaseInterceptor } from './common/interceptors/snake-case'; WebhookModule, Web3Module, AuthModule, + KycModule, ServeStaticModule.forRoot({ rootPath: join( __dirname, diff --git a/packages/apps/reputation-oracle/server/src/common/config/env.ts b/packages/apps/reputation-oracle/server/src/common/config/env.ts index 3ee836fd24..772580a637 100644 --- a/packages/apps/reputation-oracle/server/src/common/config/env.ts +++ b/packages/apps/reputation-oracle/server/src/common/config/env.ts @@ -33,6 +33,8 @@ export const ConfigNames = { ENCRYPTION_PRIVATE_KEY: 'ENCRYPTION_PRIVATE_KEY', ENCRYPTION_PASSPHRASE: 'ENCRYPTION_PASSPHRASE', PGP_ENCRYPT: 'PGP_ENCRYPT', + SYNAPS_API_KEY: 'SYNAPS_API_KEY', + SYNAPS_WEBHOOK_SECRET: 'SYNAPS_WEBHOOK_SECRET', }; export const envValidator = Joi.object({ @@ -77,4 +79,7 @@ export const envValidator = Joi.object({ ENCRYPTION_PRIVATE_KEY: Joi.string().default(''), ENCRYPTION_PASSPHRASE: Joi.string().default(''), PGP_ENCRYPT: Joi.string().default(false), + // Synaps Kyc + SYNAPS_API_KEY: Joi.string().required(), + SYNAPS_WEBHOOK_SECRET: Joi.string().required(), }); diff --git a/packages/apps/reputation-oracle/server/src/common/constants/errors.ts b/packages/apps/reputation-oracle/server/src/common/constants/errors.ts index 5e978040ab..32382ec041 100644 --- a/packages/apps/reputation-oracle/server/src/common/constants/errors.ts +++ b/packages/apps/reputation-oracle/server/src/common/constants/errors.ts @@ -49,6 +49,8 @@ export enum ErrorUser { AccountCannotBeRegistered = 'Account cannot be registered', BalanceCouldNotBeRetreived = 'User balance could not be retrieved', InvalidCredentials = 'Invalid credentials', + IncorrectAddress = 'Incorrect address', + KycNotApproved = 'KYC not approved', } /** @@ -76,3 +78,12 @@ export enum ErrorSendGrid { EmailNotSent = 'Email was not sent', InvalidApiKey = 'Invalid SendGrid API key', } + +export enum ErrorKyc { + NotFound = 'KYC session not found', + AlreadyApproved = 'KYC session already approved', + VerificationInProgress = 'KYC session verification in progress', + Rejected = 'KYC session rejected', + InvalidSynapsAPIResponse = 'Invalid Synaps API response', + InvalidWebhookSecret = 'Invalid webhook secret', +} diff --git a/packages/apps/reputation-oracle/server/src/common/enums/user.ts b/packages/apps/reputation-oracle/server/src/common/enums/user.ts index 83d98c0f92..e04c837807 100644 --- a/packages/apps/reputation-oracle/server/src/common/enums/user.ts +++ b/packages/apps/reputation-oracle/server/src/common/enums/user.ts @@ -10,3 +10,13 @@ export enum UserType { RECORDING_ORACLE = 'RECORDING_ORACLE', WORKER = 'WORKER', } + +export enum KycStatus { + NONE = 'NONE', + APPROVED = 'APPROVED', + SUBMISSION_REQUIRED = 'SUBMISSION_REQUIRED', + RESUBMISSION_REQUIRED = 'RESUBMISSION_REQUIRED', + REJECTED = 'REJECTED', + PENDING_VERIFICATION = 'PENDING_VERIFICATION', + RESET = 'RESET', +} diff --git a/packages/apps/reputation-oracle/server/src/database/database.module.ts b/packages/apps/reputation-oracle/server/src/database/database.module.ts index b602437d5a..c76338e86b 100644 --- a/packages/apps/reputation-oracle/server/src/database/database.module.ts +++ b/packages/apps/reputation-oracle/server/src/database/database.module.ts @@ -11,6 +11,7 @@ import { ReputationEntity } from '../modules/reputation/reputation.entity'; import { AuthEntity } from '../modules/auth/auth.entity'; import { TokenEntity } from '../modules/auth/token.entity'; import { UserEntity } from '../modules/user/user.entity'; +import { KycEntity } from 'src/modules/kyc/kyc.entity'; @Module({ imports: [ @@ -31,6 +32,7 @@ import { UserEntity } from '../modules/user/user.entity'; AuthEntity, TokenEntity, UserEntity, + KycEntity, ], // We are using migrations, synchronize should be set to false. synchronize: false, diff --git a/packages/apps/reputation-oracle/server/src/database/migrations/1706196712859-AddKycTable.ts b/packages/apps/reputation-oracle/server/src/database/migrations/1706196712859-AddKycTable.ts new file mode 100644 index 0000000000..752b0a853b --- /dev/null +++ b/packages/apps/reputation-oracle/server/src/database/migrations/1706196712859-AddKycTable.ts @@ -0,0 +1,25 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddKycTable1706196712859 implements MigrationInterface { + name = 'AddKycTable1706196712859'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TYPE "hmt"."kycs_status_enum" AS ENUM('NONE', 'APPROVED', 'SUBMISSION_REQUIRED', 'RESUBMISSION_REQUIRED', 'REJECTED', 'PENDING_VERIFICATION', 'RESET')`, + ); + await queryRunner.query( + `CREATE TABLE "hmt"."kycs" ("id" SERIAL NOT NULL, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL, "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL, "session_id" character varying NOT NULL, "status" "hmt"."kycs_status_enum" NOT NULL DEFAULT 'NONE', "message" character varying, "user_id" integer NOT NULL, CONSTRAINT "UQ_76ea5d4f1a010a5fe4949771f4c" UNIQUE ("session_id"), CONSTRAINT "REL_bbfe1fa864841e82cff1be09e8" UNIQUE ("user_id"), CONSTRAINT "PK_6f4a4d2b94576014ab3d5e77694" PRIMARY KEY ("id", "session_id"))`, + ); + await queryRunner.query( + `ALTER TABLE "hmt"."kycs" ADD CONSTRAINT "FK_bbfe1fa864841e82cff1be09e8b" FOREIGN KEY ("user_id") REFERENCES "hmt"."users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "hmt"."kycs" DROP CONSTRAINT "FK_bbfe1fa864841e82cff1be09e8b"`, + ); + await queryRunner.query(`DROP TABLE "hmt"."kycs"`); + await queryRunner.query(`DROP TYPE "hmt"."kycs_status_enum"`); + } +} diff --git a/packages/apps/reputation-oracle/server/src/modules/auth/auth.service.spec.ts b/packages/apps/reputation-oracle/server/src/modules/auth/auth.service.spec.ts index e101c90648..ec0e729c15 100644 --- a/packages/apps/reputation-oracle/server/src/modules/auth/auth.service.spec.ts +++ b/packages/apps/reputation-oracle/server/src/modules/auth/auth.service.spec.ts @@ -40,6 +40,7 @@ import { WEB3_SIGNUP_MESSAGE, } from '../../common/constants'; import { getNonce, signMessage } from '../../common/utils/signature'; +import { Web3Service } from '../web3/web3.service'; jest.mock('@human-protocol/sdk'); @@ -85,6 +86,12 @@ describe('AuthService', () => { { provide: ConfigService, useValue: mockConfigService }, { provide: HttpService, useValue: createMock() }, { provide: SendGridService, useValue: createMock() }, + { + provide: Web3Service, + useValue: { + signMessage: jest.fn(), + }, + }, ], }).compile(); diff --git a/packages/apps/reputation-oracle/server/src/modules/auth/auth.service.ts b/packages/apps/reputation-oracle/server/src/modules/auth/auth.service.ts index 76b90e6f0d..db62feb444 100644 --- a/packages/apps/reputation-oracle/server/src/modules/auth/auth.service.ts +++ b/packages/apps/reputation-oracle/server/src/modules/auth/auth.service.ts @@ -117,6 +117,7 @@ export class AuthService { email: userEntity.email, evmAddress: userEntity.evmAddress, userId: userEntity.id, + kycStatus: userEntity.kyc?.status, }); const refreshToken = await this.jwtService.signAsync( @@ -124,6 +125,7 @@ export class AuthService { email: userEntity.email, evmAddress: userEntity.evmAddress, userId: userEntity.id, + kycStatus: userEntity.kyc?.status, }, { expiresIn: this.refreshTokenExpiresIn, diff --git a/packages/apps/reputation-oracle/server/src/modules/auth/strategy/jwt.http.ts b/packages/apps/reputation-oracle/server/src/modules/auth/strategy/jwt.http.ts index d36d51000d..651072be6b 100644 --- a/packages/apps/reputation-oracle/server/src/modules/auth/strategy/jwt.http.ts +++ b/packages/apps/reputation-oracle/server/src/modules/auth/strategy/jwt.http.ts @@ -37,7 +37,7 @@ export class JwtHttpStrategy extends PassportStrategy(Strategy, 'jwt-http') { userId: payload.userId, }, { - relations: ['user'], + relations: ['user', 'user.kyc'], }, ); diff --git a/packages/apps/reputation-oracle/server/src/modules/kyc/kyc.controller.spec.ts b/packages/apps/reputation-oracle/server/src/modules/kyc/kyc.controller.spec.ts new file mode 100644 index 0000000000..c68ad52de2 --- /dev/null +++ b/packages/apps/reputation-oracle/server/src/modules/kyc/kyc.controller.spec.ts @@ -0,0 +1,67 @@ +import { Test } from '@nestjs/testing'; + +import { KycController } from './kyc.controller'; +import { KycService } from './kyc.service'; +import { KycSessionDto } from './kyc.dto'; +import { KycStatus } from '../../common/enums/user'; + +describe('KycController', () => { + let kycController: KycController; + let kycService: KycService; + + beforeAll(async () => { + const moduleRef = await Test.createTestingModule({ + providers: [ + { + provide: KycService, + useValue: { + initSession: jest.fn(), + updateKycStatus: jest.fn(), + }, + }, + ], + }).compile(); + + kycService = moduleRef.get(KycService); + kycController = new KycController(kycService); + }); + + describe('startKyc', () => { + it('should call service for authorized user', async () => { + const session: KycSessionDto = { + sessionId: '123', + }; + + jest.spyOn(kycService, 'initSession').mockResolvedValueOnce(session); + + expect( + await kycController.startKyc({ + user: { + email: 'test@example.com', + }, + } as any), + ).toEqual(session); + }); + }); + + describe('updateKycStatus', () => { + it('should call service', async () => { + kycService.updateKycStatus = jest.fn(); + + await kycController.updateKycStatus( + { + secret: 'secret', + }, + { + session_id: '123', + state: KycStatus.APPROVED, + } as any, + ); + + expect(kycService.updateKycStatus).toHaveBeenCalledWith('secret', { + session_id: '123', + state: KycStatus.APPROVED, + }); + }); + }); +}); diff --git a/packages/apps/reputation-oracle/server/src/modules/kyc/kyc.controller.ts b/packages/apps/reputation-oracle/server/src/modules/kyc/kyc.controller.ts new file mode 100644 index 0000000000..a5f2a8a4ca --- /dev/null +++ b/packages/apps/reputation-oracle/server/src/modules/kyc/kyc.controller.ts @@ -0,0 +1,72 @@ +import { + ApiBearerAuth, + ApiBody, + ApiOperation, + ApiQuery, + ApiResponse, + ApiTags, +} from '@nestjs/swagger'; +import { + Body, + Controller, + HttpCode, + Post, + Query, + Req, + UseGuards, +} from '@nestjs/common'; +import { JwtAuthGuard } from '../../common/guards'; +import { RequestWithUser } from '../../common/types'; +import { + KycSessionDto, + KycStatusDto, + KycUpdateWebhookQueryDto, +} from './kyc.dto'; +import { KycService } from './kyc.service'; + +@ApiTags('Kyc') +@Controller('/kyc') +export class KycController { + constructor(private readonly kycService: KycService) {} + + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Post('/start') + @HttpCode(200) + @ApiOperation({ + summary: 'Start Kyc', + description: 'Endpoint to start Kyc process for the user.', + }) + @ApiResponse({ + status: 200, + description: 'Kyc session started successfully', + type: KycSessionDto, + }) + async startKyc(@Req() request: RequestWithUser): Promise { + return this.kycService.initSession(request.user); + } + + @Post('/update') + @HttpCode(200) + @ApiOperation({ + summary: 'Update Kyc status', + description: 'Endpoint to update Kyc process for the user.', + }) + @ApiQuery({ + name: 'secret', + description: 'Secret for the webhook authentication.', + type: String, + required: true, + }) + @ApiBody({ type: KycStatusDto }) + @ApiResponse({ + status: 200, + description: 'Kyc status updated successfully', + }) + public updateKycStatus( + @Query() query: KycUpdateWebhookQueryDto, + @Body() data: KycStatusDto, + ): Promise { + return this.kycService.updateKycStatus(query.secret, data); + } +} diff --git a/packages/apps/reputation-oracle/server/src/modules/kyc/kyc.dto.ts b/packages/apps/reputation-oracle/server/src/modules/kyc/kyc.dto.ts new file mode 100644 index 0000000000..4a2c8f74cd --- /dev/null +++ b/packages/apps/reputation-oracle/server/src/modules/kyc/kyc.dto.ts @@ -0,0 +1,69 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsEnum, IsNumber, IsOptional, IsString } from 'class-validator'; +import { KycStatus } from '../../common/enums/user'; + +export class KycSessionDto { + @ApiProperty({ name: 'session_id' }) + @IsString() + public sessionId: string; +} + +export class KycStatusDto { + @ApiProperty() + @IsString() + @IsOptional() + public reason?: string; + + @ApiProperty({ name: 'step_id' }) + @IsString() + public stepId: string; + + @ApiProperty() + @IsString() + public service: string; + + @ApiProperty({ name: 'session_id' }) + @IsString() + public sessionId: string; + + @ApiProperty({ + enum: KycStatus, + }) + @IsEnum(KycStatus) + public state: KycStatus; +} + +export class KycUpdateWebhookQueryDto { + @ApiProperty() + @IsString() + public secret: string; +} + +export class KycCreateDto { + @ApiProperty() + @IsString() + public sessionId: string; + + @ApiProperty({ + enum: KycStatus, + }) + @IsEnum(KycStatus) + public status: KycStatus; + + @ApiProperty() + @IsNumber() + public userId: number; +} + +export class KycUpdateDto { + @ApiProperty({ + enum: KycStatus, + }) + @IsEnum(KycStatus) + public status: KycStatus; + + @ApiPropertyOptional() + @IsString() + @IsOptional() + public message?: string; +} diff --git a/packages/apps/reputation-oracle/server/src/modules/kyc/kyc.entity.ts b/packages/apps/reputation-oracle/server/src/modules/kyc/kyc.entity.ts new file mode 100644 index 0000000000..6617b06604 --- /dev/null +++ b/packages/apps/reputation-oracle/server/src/modules/kyc/kyc.entity.ts @@ -0,0 +1,29 @@ +import { Column, Entity, JoinColumn, OneToOne } from 'typeorm'; + +import { NS } from '../../common/constants'; +import { KycStatus } from '../../common/enums/user'; +import { BaseEntity } from '../../database/base.entity'; +import { UserEntity } from '../user/user.entity'; + +@Entity({ schema: NS, name: 'kycs' }) +export class KycEntity extends BaseEntity { + @Column({ type: 'varchar', unique: true, primary: true }) + public sessionId: string; + + @Column({ + type: 'enum', + enum: KycStatus, + default: KycStatus.NONE, + }) + public status: KycStatus; + + @Column({ type: 'varchar', nullable: true }) + public message?: string; + + @JoinColumn() + @OneToOne(() => UserEntity, (user) => user.kyc) + public user: UserEntity; + + @Column({ type: 'int' }) + public userId: number; +} diff --git a/packages/apps/reputation-oracle/server/src/modules/kyc/kyc.module.ts b/packages/apps/reputation-oracle/server/src/modules/kyc/kyc.module.ts new file mode 100644 index 0000000000..48378120f5 --- /dev/null +++ b/packages/apps/reputation-oracle/server/src/modules/kyc/kyc.module.ts @@ -0,0 +1,23 @@ +import { HttpModule } from '@nestjs/axios'; +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; + +import { KycService } from './kyc.service'; +import { KycController } from './kyc.controller'; +import { KycRepository } from './kyc.repository'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { KycEntity } from './kyc.entity'; +import { UserModule } from '../user/user.module'; + +@Module({ + imports: [ + UserModule, + TypeOrmModule.forFeature([KycEntity]), + ConfigModule, + HttpModule, + ], + providers: [KycService, KycRepository], + controllers: [KycController], + exports: [KycService], +}) +export class KycModule {} diff --git a/packages/apps/reputation-oracle/server/src/modules/kyc/kyc.repository.ts b/packages/apps/reputation-oracle/server/src/modules/kyc/kyc.repository.ts new file mode 100644 index 0000000000..770b68c93f --- /dev/null +++ b/packages/apps/reputation-oracle/server/src/modules/kyc/kyc.repository.ts @@ -0,0 +1,66 @@ +import { Injectable, Logger, NotFoundException } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { + FindOptionsWhere, + FindManyOptions, + FindOneOptions, + Repository, +} from 'typeorm'; + +import { KycEntity } from './kyc.entity'; +import { ErrorKyc } from '../../common/constants/errors'; +import { KycCreateDto, KycUpdateDto } from './kyc.dto'; + +@Injectable() +export class KycRepository { + private readonly logger = new Logger(KycRepository.name); + + constructor( + @InjectRepository(KycEntity) + private readonly kycEntityRepository: Repository, + ) {} + + public async updateOne( + where: FindOptionsWhere, + dto: Partial, + ): Promise { + const kycEntity = await this.kycEntityRepository.findOneBy(where); + + if (!kycEntity) { + this.logger.log(ErrorKyc.NotFound, KycRepository.name); + throw new NotFoundException(ErrorKyc.NotFound); + } + + Object.assign(kycEntity, dto); + return kycEntity.save(); + } + + public async findOne( + where: FindOptionsWhere, + options?: FindOneOptions, + ): Promise { + const userEntity = await this.kycEntityRepository.findOne({ + where, + ...options, + }); + + return userEntity; + } + + public find( + where: FindOptionsWhere, + options?: FindManyOptions, + ): Promise { + return this.kycEntityRepository.find({ + where, + order: { + createdAt: 'DESC', + }, + ...options, + }); + } + + public async create(dto: KycCreateDto): Promise { + return this.kycEntityRepository.create(dto).save(); + } +} diff --git a/packages/apps/reputation-oracle/server/src/modules/kyc/kyc.service.spec.ts b/packages/apps/reputation-oracle/server/src/modules/kyc/kyc.service.spec.ts new file mode 100644 index 0000000000..22a36b1d87 --- /dev/null +++ b/packages/apps/reputation-oracle/server/src/modules/kyc/kyc.service.spec.ts @@ -0,0 +1,275 @@ +import { ConfigService } from '@nestjs/config'; +import { ConfigNames } from '../../common/config'; +import { Test } from '@nestjs/testing'; +import { KycService } from './kyc.service'; +import { HttpService } from '@nestjs/axios'; +import { DeepPartial } from 'typeorm'; +import { createMock } from '@golevelup/ts-jest'; +import { BadRequestException, UnauthorizedException } from '@nestjs/common'; +import { KycStatus } from '../../common/enums/user'; +import { KycRepository } from './kyc.repository'; +import { KycEntity } from './kyc.entity'; +import { of } from 'rxjs'; +import { ErrorKyc } from '../../common/constants/errors'; + +describe('Kyc Service', () => { + let kycService: KycService; + let httpService: HttpService; + let kycRepository: KycRepository; + + beforeAll(async () => { + const mockConfigService: Partial = { + get: jest.fn((key: string) => { + switch (key) { + case ConfigNames.SYNAPS_API_KEY: + return 'synaps-api-key'; + case ConfigNames.SYNAPS_WEBHOOK_SECRET: + return 'synaps-webhook-secret'; + } + }), + }; + + const mockHttpService: DeepPartial = { + axiosRef: { + request: jest.fn(), + }, + }; + + const moduleRef = await Test.createTestingModule({ + providers: [ + KycService, + { + provide: ConfigService, + useValue: mockConfigService, + }, + { + provide: HttpService, + useValue: mockHttpService, + }, + { provide: KycRepository, useValue: createMock() }, + ], + }).compile(); + + httpService = moduleRef.get(HttpService); + kycService = moduleRef.get(KycService); + kycRepository = moduleRef.get(KycRepository); + }); + + describe('initSession', () => { + describe('Should return existing session id if user already has an active Kyc session, and is waiting for user to make an action', () => { + it('status is NONE', async () => { + const mockUserEntity = { + kyc: { + sessionId: '123', + status: KycStatus.NONE, + }, + }; + + const result = await kycService.initSession(mockUserEntity as any); + + expect(result).toEqual({ + sessionId: '123', + }); + }); + + it('status is SUBMISSION_REQUIRED', async () => { + const mockUserEntity = { + kyc: { + sessionId: '123', + status: KycStatus.SUBMISSION_REQUIRED, + }, + }; + + const result = await kycService.initSession(mockUserEntity as any); + + expect(result).toEqual({ + sessionId: '123', + }); + }); + + it('status is RESUBMISSION_REQUIRED', async () => { + const mockUserEntity = { + kyc: { + sessionId: '123', + status: KycStatus.RESUBMISSION_REQUIRED, + }, + }; + + const result = await kycService.initSession(mockUserEntity as any); + + expect(result).toEqual({ + sessionId: '123', + }); + }); + + it('status is RESET', async () => { + const mockUserEntity = { + kyc: { + sessionId: '123', + status: KycStatus.RESET, + }, + }; + + const result = await kycService.initSession(mockUserEntity as any); + + expect(result).toEqual({ + sessionId: '123', + }); + }); + }); + + it('Should throw an error if user already has an active Kyc session, but is approved already', async () => { + const mockUserEntity = { + kyc: { + sessionId: '123', + status: KycStatus.APPROVED, + }, + }; + + await expect( + kycService.initSession(mockUserEntity as any), + ).rejects.toThrow(new BadRequestException(ErrorKyc.AlreadyApproved)); + }); + + it("Should throw an error if user already has an active Kyc session, but it's pending verification", async () => { + const mockUserEntity = { + kyc: { + sessionId: '123', + status: KycStatus.PENDING_VERIFICATION, + }, + }; + + await expect( + kycService.initSession(mockUserEntity as any), + ).rejects.toThrow( + new BadRequestException(ErrorKyc.VerificationInProgress), + ); + }); + + it("Should throw an error if user already has an active Kyc session, but it's rejected", async () => { + const mockUserEntity = { + kyc: { + sessionId: '123', + status: KycStatus.REJECTED, + message: 'test', + }, + }; + + await expect( + kycService.initSession(mockUserEntity as any), + ).rejects.toThrow( + new BadRequestException( + `${ErrorKyc.Rejected}. Reason: ${mockUserEntity.kyc.message}`, + ), + ); + }); + + it("Should throw an error if synaps doesn't return a session id", async () => { + const mockUserEntity = { + kyc: { + sessionId: null, + }, + }; + + httpService.post = jest.fn().mockImplementation(() => { + return of({ + data: {}, + }); + }); + + await expect( + kycService.initSession(mockUserEntity as any), + ).rejects.toThrow(); + }); + + it('Should start a Kyc session for the user', async () => { + const mockUserEntity = { + kyc: { + sessionId: null, + }, + id: 1, + email: 'test@example.com', + }; + + httpService.post = jest.fn().mockImplementation(() => { + return of({ + data: { + session_id: '123', + }, + }); + }); + + jest.spyOn(kycRepository, 'create').mockResolvedValue({} as KycEntity); + + const result = await kycService.initSession(mockUserEntity as any); + + expect(result).toEqual({ + sessionId: '123', + }); + expect(httpService.post).toHaveBeenCalledWith( + 'session/init', + { alias: 'test@example.com' }, + { + baseURL: 'https://api.synaps.io/v4', + headers: { 'Api-Key': 'synaps-api-key' }, + }, + ); + expect(kycRepository.create).toHaveBeenCalledWith({ + sessionId: '123', + status: KycStatus.NONE, + userId: 1, + }); + }); + }); + + describe('updateKycStatus', () => { + const mockKycUpdate = { + stepId: 'xx', + service: 'ID DOCUMENT', + sessionId: '123', + state: KycStatus.APPROVED, + }; + + it('Should throw an error if the secret is invalid', async () => { + await expect( + kycService.updateKycStatus('invalid', mockKycUpdate), + ).rejects.toThrow(UnauthorizedException); + }); + + it('Should throw an error if the session data is invalid from synaps', async () => { + jest.spyOn(kycRepository, 'updateOne').mockResolvedValue({} as any); + + httpService.get = jest.fn().mockImplementation(() => { + return of({ + data: {}, + }); + }); + + await expect( + kycService.updateKycStatus('synaps-webhook-secret', mockKycUpdate), + ).rejects.toThrow(); + }); + + it('Should update the Kyc status of the user', async () => { + jest.spyOn(kycRepository, 'updateOne').mockResolvedValue({} as any); + + httpService.get = jest.fn().mockImplementation(() => { + return of({ + data: { + session: { + id: '123', + status: KycStatus.APPROVED, + }, + }, + }); + }); + + await kycService.updateKycStatus('synaps-webhook-secret', mockKycUpdate); + + expect(kycRepository.updateOne).toHaveBeenCalledWith( + { sessionId: '123' }, + { status: KycStatus.APPROVED }, + ); + }); + }); +}); diff --git a/packages/apps/reputation-oracle/server/src/modules/kyc/kyc.service.ts b/packages/apps/reputation-oracle/server/src/modules/kyc/kyc.service.ts new file mode 100644 index 0000000000..06fd72c2fd --- /dev/null +++ b/packages/apps/reputation-oracle/server/src/modules/kyc/kyc.service.ts @@ -0,0 +1,129 @@ +import { + BadRequestException, + Injectable, + InternalServerErrorException, + Logger, + UnauthorizedException, +} from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; + +import { UserEntity } from '../user/user.entity'; +import { ConfigNames } from '../../common/config'; +import { HttpService } from '@nestjs/axios'; +import { KycSessionDto, KycStatusDto } from './kyc.dto'; +import { KycRepository } from './kyc.repository'; +import { KycStatus } from '../../common/enums/user'; +import { firstValueFrom } from 'rxjs'; +import { ErrorKyc } from '../../common/constants/errors'; + +@Injectable() +export class KycService { + private readonly logger = new Logger(KycService.name); + private readonly synapsBaseURL: string; + private readonly synapsApiKey: string; + private readonly synapsWebhookSecret: string; + + constructor( + private kycRepository: KycRepository, + private readonly httpService: HttpService, + private readonly configService: ConfigService, + ) { + this.synapsBaseURL = 'https://api.synaps.io/v4'; + + this.synapsApiKey = this.configService.get( + ConfigNames.SYNAPS_API_KEY, + '', + ); + + this.synapsWebhookSecret = this.configService.get( + ConfigNames.SYNAPS_WEBHOOK_SECRET, + '', + ); + } + + public async initSession(userEntity: UserEntity): Promise { + if (userEntity.kyc?.sessionId) { + if (userEntity.kyc.status === KycStatus.APPROVED) { + throw new BadRequestException(ErrorKyc.AlreadyApproved); + } + + if (userEntity.kyc.status === KycStatus.PENDING_VERIFICATION) { + throw new BadRequestException(ErrorKyc.VerificationInProgress); + } + + if (userEntity.kyc.status === KycStatus.REJECTED) { + throw new BadRequestException( + `${ErrorKyc.Rejected}. Reason: ${userEntity.kyc.message}`, + ); + } + + return { + sessionId: userEntity.kyc.sessionId, + }; + } + + const { data } = await firstValueFrom( + await this.httpService.post( + 'session/init', + { + alias: userEntity.email, + }, + { + baseURL: this.synapsBaseURL, + headers: { + 'Api-Key': this.synapsApiKey, + }, + }, + ), + ); + + if (!data?.session_id) { + throw new InternalServerErrorException(ErrorKyc.InvalidSynapsAPIResponse); + } + + await this.kycRepository.create({ + sessionId: data.session_id, + status: KycStatus.NONE, + userId: userEntity.id, + }); + + return { + sessionId: data.session_id, + }; + } + + public async updateKycStatus( + secret: string, + data: KycStatusDto, + ): Promise { + if (secret !== this.synapsWebhookSecret) { + throw new UnauthorizedException(ErrorKyc.InvalidWebhookSecret); + } + + const { data: sessionData } = await firstValueFrom( + await this.httpService.get(`/individual/session/${data.sessionId}`, { + baseURL: this.synapsBaseURL, + headers: { + 'Api-Key': this.synapsApiKey, + }, + }), + ); + + if ( + !sessionData?.session?.status || + sessionData.session.status !== data.state + ) { + throw new InternalServerErrorException(ErrorKyc.InvalidSynapsAPIResponse); + } + + await this.kycRepository.updateOne( + { + sessionId: data.sessionId, + }, + { + status: data.state, + message: data.reason, + }, + ); + } +} 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 new file mode 100644 index 0000000000..21b0cd17b3 --- /dev/null +++ b/packages/apps/reputation-oracle/server/src/modules/user/user.controller.ts @@ -0,0 +1,67 @@ +import { + ApiBearerAuth, + ApiOperation, + ApiResponse, + ApiTags, + ApiBody, +} from '@nestjs/swagger'; +import { + Body, + Controller, + HttpCode, + Post, + Req, + UseGuards, +} from '@nestjs/common'; +import { Public } from '../../common/decorators'; +import { + RegisterAddressRequestDto, + RegisterAddressResponseDto, +} from './user.dto'; +import { JwtAuthGuard } from '../../common/guards'; +import { RequestWithUser } from '../../common/types'; +import { UserService } from './user.service'; + +@ApiTags('User') +@Controller('/user') +export class UserController { + constructor(private readonly userService: UserService) {} + @ApiBearerAuth() + @UseGuards(JwtAuthGuard) + @Public() + @Post('/register-address') + @HttpCode(200) + @ApiOperation({ + summary: 'Register Blockchain Address', + description: 'Endpoint to register blockchain address.', + }) + @ApiBody({ type: RegisterAddressRequestDto }) + @ApiResponse({ + status: 200, + description: 'Blockchain address registered successfully', + type: RegisterAddressResponseDto, + }) + @ApiResponse({ + status: 400, + description: 'Bad Request. Invalid input parameters.', + }) + @ApiResponse({ + status: 401, + description: 'Unauthorized. Missing or invalid credentials.', + }) + @ApiResponse({ + status: 404, + description: 'Not Found. Could not find the requested content.', + }) + public async registerAddress( + @Req() request: RequestWithUser, + @Body() data: RegisterAddressRequestDto, + ): Promise { + const signedAddress = await this.userService.registerAddress( + request.user, + data, + ); + + return { signedAddress }; + } +} 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 5fc9229727..2c81c4702a 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 @@ -3,6 +3,7 @@ import { IsEmail, IsEnum, IsOptional, IsString } from 'class-validator'; import { Transform } from 'class-transformer'; import { UserStatus, UserType } from '../../common/enums/user'; import { ValidatePasswordDto } from '../auth/auth.dto'; +import { ChainId } from '@human-protocol/sdk'; export class UserCreateDto extends ValidatePasswordDto { @ApiProperty() @@ -54,3 +55,19 @@ export class UserUpdateDto { @IsEnum(UserStatus) public status?: UserStatus; } + +export class RegisterAddressRequestDto { + @ApiProperty({ name: 'chain_id' }) + @IsEnum(ChainId) + public chainId: ChainId; + + @ApiProperty() + @IsString() + public address: string; +} + +export class RegisterAddressResponseDto { + @ApiProperty({ name: 'signed_address' }) + @IsString() + public signedAddress: string; +} diff --git a/packages/apps/reputation-oracle/server/src/modules/user/user.entity.ts b/packages/apps/reputation-oracle/server/src/modules/user/user.entity.ts index a0d62440d4..fe98b4d327 100644 --- a/packages/apps/reputation-oracle/server/src/modules/user/user.entity.ts +++ b/packages/apps/reputation-oracle/server/src/modules/user/user.entity.ts @@ -7,6 +7,7 @@ import { IUser } from '../../common/interfaces'; import { BaseEntity } from '../../database/base.entity'; import { AuthEntity } from '../auth/auth.entity'; import { TokenEntity } from '../auth/token.entity'; +import { KycEntity } from '../kyc/kyc.entity'; @Entity({ schema: NS, name: 'users' }) export class UserEntity extends BaseEntity implements IUser { @@ -37,4 +38,7 @@ export class UserEntity extends BaseEntity implements IUser { @OneToOne(() => TokenEntity) public token: TokenEntity; + + @OneToOne(() => KycEntity, (kyc) => kyc.user) + public kyc?: KycEntity; } diff --git a/packages/apps/reputation-oracle/server/src/modules/user/user.module.ts b/packages/apps/reputation-oracle/server/src/modules/user/user.module.ts index 49d7568023..d965906914 100644 --- a/packages/apps/reputation-oracle/server/src/modules/user/user.module.ts +++ b/packages/apps/reputation-oracle/server/src/modules/user/user.module.ts @@ -5,10 +5,13 @@ import { ConfigModule } from '@nestjs/config'; import { UserService } from './user.service'; import { UserEntity } from './user.entity'; import { UserRepository } from './user.repository'; +import { UserController } from './user.controller'; +import { Web3Module } from '../web3/web3.module'; @Module({ - imports: [TypeOrmModule.forFeature([UserEntity]), ConfigModule], + imports: [TypeOrmModule.forFeature([UserEntity]), ConfigModule, Web3Module], providers: [Logger, UserService, UserRepository], + controllers: [UserController], exports: [UserService], }) export class UserModule {} diff --git a/packages/apps/reputation-oracle/server/src/modules/user/user.service.spec.ts b/packages/apps/reputation-oracle/server/src/modules/user/user.service.spec.ts index 004c3236f1..63ceb41126 100644 --- a/packages/apps/reputation-oracle/server/src/modules/user/user.service.spec.ts +++ b/packages/apps/reputation-oracle/server/src/modules/user/user.service.spec.ts @@ -1,31 +1,46 @@ import { Test } from '@nestjs/testing'; -import { ConflictException, NotFoundException } from '@nestjs/common'; +import { + BadRequestException, + ConflictException, + NotFoundException, +} from '@nestjs/common'; import { createMock } from '@golevelup/ts-jest'; import { ErrorUser } from '../../common/constants/errors'; import { UserRepository } from './user.repository'; import { UserService } from './user.service'; import { UserCreateDto, UserUpdateDto } from './user.dto'; import { UserEntity } from './user.entity'; -import { UserStatus, UserType } from '../../common/enums/user'; +import { KycStatus, UserStatus, UserType } from '../../common/enums/user'; import { getNonce } from '../../common/utils/signature'; import { MOCK_ADDRESS } from '../../../test/constants'; +import { Web3Service } from '../web3/web3.service'; +import { DeepPartial } from 'typeorm'; +import { ChainId } from '@human-protocol/sdk'; jest.mock('@human-protocol/sdk'); describe('UserService', () => { let userService: UserService; let userRepository: UserRepository; + let web3Service: Web3Service; beforeAll(async () => { const moduleRef = await Test.createTestingModule({ providers: [ UserService, { provide: UserRepository, useValue: createMock() }, + { + provide: Web3Service, + useValue: { + signMessage: jest.fn(), + }, + }, ], }).compile(); userService = moduleRef.get(UserService); userRepository = moduleRef.get(UserRepository); + web3Service = moduleRef.get(Web3Service); }); describe('update', () => { @@ -224,4 +239,68 @@ describe('UserService', () => { }); }); }); + + describe('registerAddress', () => { + it('should update evm address and sign the address', async () => { + const userEntity: DeepPartial = { + id: 1, + email: '', + kyc: { + status: KycStatus.APPROVED, + }, + save: jest.fn(), + }; + + const address = '0x123'; + + web3Service.getSigner = jest.fn().mockReturnValue({ + signMessage: jest.fn().mockResolvedValue('signature'), + }); + + const result = await userService.registerAddress( + userEntity as UserEntity, + { chainId: ChainId.POLYGON_MUMBAI, address }, + ); + + expect(userEntity.save).toHaveBeenCalledWith(); + expect(result).toBe('signature'); + }); + + it("should fail if address is different from user's evm address", async () => { + const userEntity: Partial = { + id: 1, + email: '', + evmAddress: '0x123', + }; + + const address = '0x456'; + + await expect( + userService.registerAddress(userEntity as UserEntity, { + chainId: ChainId.POLYGON_MUMBAI, + address, + }), + ).rejects.toThrow(BadRequestException); + }); + + it("should fail if user's kyc is not approved", async () => { + const userEntity: DeepPartial = { + id: 1, + email: '', + evmAddress: '0x123', + kyc: { + status: KycStatus.PENDING_VERIFICATION, + }, + }; + + const address = '0x123'; + + await expect( + userService.registerAddress(userEntity as UserEntity, { + chainId: ChainId.POLYGON_MUMBAI, + address, + }), + ).rejects.toThrow(BadRequestException); + }); + }); }); diff --git a/packages/apps/reputation-oracle/server/src/modules/user/user.service.ts b/packages/apps/reputation-oracle/server/src/modules/user/user.service.ts index 92c9b9422b..1f1725eec4 100644 --- a/packages/apps/reputation-oracle/server/src/modules/user/user.service.ts +++ b/packages/apps/reputation-oracle/server/src/modules/user/user.service.ts @@ -1,4 +1,5 @@ import { + BadRequestException, ConflictException, Injectable, Logger, @@ -7,18 +8,26 @@ import { import * as bcrypt from 'bcrypt'; import { Not } from 'typeorm'; import { ErrorUser } from '../../common/constants/errors'; -import { UserStatus, UserType } from '../../common/enums/user'; +import { KycStatus, UserStatus, UserType } from '../../common/enums/user'; import { getNonce } from '../../common/utils/signature'; import { UserEntity } from './user.entity'; -import { UserCreateDto, UserUpdateDto } from './user.dto'; +import { + RegisterAddressRequestDto, + UserCreateDto, + UserUpdateDto, +} from './user.dto'; import { UserRepository } from './user.repository'; import { ValidatePasswordDto } from '../auth/auth.dto'; +import { Web3Service } from '../web3/web3.service'; @Injectable() export class UserService { private readonly logger = new Logger(UserService.name); private HASH_ROUNDS = 12; - constructor(private userRepository: UserRepository) {} + constructor( + private userRepository: UserRepository, + private readonly web3Service: Web3Service, + ) {} public async update(userId: number, dto: UserUpdateDto): Promise { return this.userRepository.updateOne({ id: userId }, dto); @@ -126,4 +135,24 @@ export class UserService { userEntity.nonce = getNonce(); return userEntity.save(); } + + public async registerAddress( + user: UserEntity, + data: RegisterAddressRequestDto, + ): Promise { + if (user.evmAddress && user.evmAddress !== data.address) { + throw new BadRequestException(ErrorUser.IncorrectAddress); + } + + if (user.kyc?.status !== KycStatus.APPROVED) { + throw new BadRequestException(ErrorUser.KycNotApproved); + } + + user.evmAddress = data.address; + await user.save(); + + return await this.web3Service + .getSigner(data.chainId) + .signMessage(data.address); + } } From b6c9210009176488186481ae3bb1cce0caa42a8a Mon Sep 17 00:00:00 2001 From: Eric Lee <98655210+leric7@users.noreply.github.com> Date: Fri, 26 Jan 2024 22:55:06 +0800 Subject: [PATCH 064/104] fix env.example with all relevant env data (#1536) --- .../reputation-oracle/server/.env.example | 38 +++++++++++++------ 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/packages/apps/reputation-oracle/server/.env.example b/packages/apps/reputation-oracle/server/.env.example index 1516211360..2554b305ca 100644 --- a/packages/apps/reputation-oracle/server/.env.example +++ b/packages/apps/reputation-oracle/server/.env.example @@ -15,21 +15,37 @@ POSTGRES_PORT=5432 POSTGRES_SSL=false # Auth -JWT_SECRET= -JWT_ACCESS_TOKEN_EXPIRES_IN= -JWT_REFRESH_TOKEN_EXPIRES_IN= +HASH_SECRET=a328af3fc1dad15342cc3d68936008fa +JWT_SECRET=secret +JWT_ACCESS_TOKEN_EXPIRES_IN=1000000000 +JWT_REFRESH_TOKEN_EXPIRES_IN=1000000000 -S3_ENDPOINT= -S3_PORT= -S3_ACCESS_KEY= -S3_SECRET_KEY= -S3_REGION= +# S3 +S3_ENDPOINT=localhost +S3_PORT=9000 +S3_ACCESS_KEY=dev +S3_SECRET_KEY=devdevdev +S3_BUCKET=reputation +S3_USE_SSL= +# Sendgrid SENDGRID_API_KEY= +SENDGRID_FROM_EMAIL=reputation-oracle@hmt.ai +SENDGRID_FROM_NAME="Human Protocol Reputation Oracle" +# Web3 WEB3_PRIVATE_KEY= -#GAS_PRICE_MULTIPLIER= +GAS_PRICE_MULTIPLIER= # Reputation Level -REPUTATION_LEVEL_LOW= -REPUTATION_LEVEL_HIGH= \ No newline at end of file +REPUTATION_LEVEL_LOW=300 +REPUTATION_LEVEL_HIGH=700 + +# Encryption +ENCRYPTION_PRIVATE_KEY= +ENCRYPTION_PASSPHRASE= +PGP_ENCRYPT= + +# Synaps Kyc +SYNAPS_API_KEY= +SYNAPS_WEBHOOK_SECRET= From 3d4b3059087540df5a678300f12b73e15c9a4fb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20L=C3=B3pez?= <50665615+flopez7@users.noreply.github.com> Date: Tue, 30 Jan 2024 12:36:48 +0100 Subject: [PATCH 065/104] [Job Launcher] Rework user service - repository schema (#1535) * Rework service - repository schema in user module * Create enum for postgres error codes --- .../job-launcher/server/src/app.module.ts | 7 +- .../src/common/exceptions/database.filter.ts | 31 ++++++ .../server/src/database/database.enum.ts | 8 ++ .../server/src/database/database.error.ts | 28 ++++++ .../src/modules/auth/auth.controller.ts | 4 + .../src/modules/user/user.repository.ts | 74 ++++----------- .../src/modules/user/user.service.spec.ts | 94 ++++--------------- .../server/src/modules/user/user.service.ts | 52 +++------- 8 files changed, 124 insertions(+), 174 deletions(-) create mode 100644 packages/apps/job-launcher/server/src/common/exceptions/database.filter.ts create mode 100644 packages/apps/job-launcher/server/src/database/database.enum.ts create mode 100644 packages/apps/job-launcher/server/src/database/database.error.ts diff --git a/packages/apps/job-launcher/server/src/app.module.ts b/packages/apps/job-launcher/server/src/app.module.ts index 3aa4a85a7f..f197770ced 100644 --- a/packages/apps/job-launcher/server/src/app.module.ts +++ b/packages/apps/job-launcher/server/src/app.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { APP_GUARD, APP_INTERCEPTOR, APP_PIPE } from '@nestjs/core'; +import { APP_FILTER, APP_GUARD, APP_INTERCEPTOR, APP_PIPE } from '@nestjs/core'; import { ConfigModule } from '@nestjs/config'; import { ScheduleModule } from '@nestjs/schedule'; import { AppController } from './app.controller'; @@ -18,6 +18,7 @@ import { join } from 'path'; import { StorageModule } from './modules/storage/storage.module'; import { CronJobModule } from './modules/cron-job/cron-job.module'; import { SnakeCaseInterceptor } from './common/interceptors/snake-case'; +import { DatabaseExceptionFilter } from './common/exceptions/database.filter'; @Module({ providers: [ @@ -33,6 +34,10 @@ import { SnakeCaseInterceptor } from './common/interceptors/snake-case'; provide: APP_INTERCEPTOR, useClass: SnakeCaseInterceptor, }, + { + provide: APP_FILTER, + useClass: DatabaseExceptionFilter, + }, ], imports: [ ScheduleModule.forRoot(), diff --git a/packages/apps/job-launcher/server/src/common/exceptions/database.filter.ts b/packages/apps/job-launcher/server/src/common/exceptions/database.filter.ts new file mode 100644 index 0000000000..431e3b816a --- /dev/null +++ b/packages/apps/job-launcher/server/src/common/exceptions/database.filter.ts @@ -0,0 +1,31 @@ +import { + ExceptionFilter, + Catch, + ArgumentsHost, + Logger, + HttpStatus, +} from '@nestjs/common'; +import { Request, Response } from 'express'; +import { DatabaseError } from 'src/database/database.error'; + +@Catch(DatabaseError) +export class DatabaseExceptionFilter implements ExceptionFilter { + private logger = new Logger(DatabaseExceptionFilter.name); + + catch(exception: DatabaseError, host: ArgumentsHost) { + const ctx = host.switchToHttp(); + const response = ctx.getResponse(); + const request = ctx.getRequest(); + + const status = HttpStatus.UNPROCESSABLE_ENTITY; + const message = exception.message; + this.logger.error(`Database error: ${message}`, exception.stack); + + response.status(status).json({ + statusCode: status, + timestamp: new Date().toISOString(), + message: message, + path: request.url, + }); + } +} diff --git a/packages/apps/job-launcher/server/src/database/database.enum.ts b/packages/apps/job-launcher/server/src/database/database.enum.ts new file mode 100644 index 0000000000..f15d28035d --- /dev/null +++ b/packages/apps/job-launcher/server/src/database/database.enum.ts @@ -0,0 +1,8 @@ +export enum DatabaseErrorCodes { + EntityDuplication = 'ENTITY_DUPLICATION', + Unknown = 'UNKNOWN', +} + +export enum PostgresErrorCodes { + Duplicated = '23505', +} diff --git a/packages/apps/job-launcher/server/src/database/database.error.ts b/packages/apps/job-launcher/server/src/database/database.error.ts new file mode 100644 index 0000000000..052b80a154 --- /dev/null +++ b/packages/apps/job-launcher/server/src/database/database.error.ts @@ -0,0 +1,28 @@ +import { QueryFailedError } from 'typeorm'; +import { DatabaseErrorCodes, PostgresErrorCodes } from './database.enum'; + +export class DatabaseError extends Error { + public readonly code: string; + constructor(message: string, code: string, stack: string) { + super(message); + this.code = code; + this.stack = stack; + } +} + +export function handleQueryFailedError(error: QueryFailedError): DatabaseError { + const stack = error.stack || ''; + let message = error.message, + code: string; + + switch ((error.driverError as any).code) { + case PostgresErrorCodes.Duplicated: + message = (error.driverError as any).detail; + code = DatabaseErrorCodes.EntityDuplication; + break; + default: + code = DatabaseErrorCodes.Unknown; + break; + } + return new DatabaseError(message, code, stack); +} diff --git a/packages/apps/job-launcher/server/src/modules/auth/auth.controller.ts b/packages/apps/job-launcher/server/src/modules/auth/auth.controller.ts index 29deae668e..565a8a5c9e 100644 --- a/packages/apps/job-launcher/server/src/modules/auth/auth.controller.ts +++ b/packages/apps/job-launcher/server/src/modules/auth/auth.controller.ts @@ -60,6 +60,10 @@ export class AuthJwtController { status: 400, description: 'Bad Request. Invalid input parameters.', }) + @ApiResponse({ + status: 422, + description: 'Unprocessable entity.', + }) public async signup(@Body() data: UserCreateDto): Promise { await this.authService.signup(data); return; diff --git a/packages/apps/job-launcher/server/src/modules/user/user.repository.ts b/packages/apps/job-launcher/server/src/modules/user/user.repository.ts index 8c01f92468..e98b62e84d 100644 --- a/packages/apps/job-launcher/server/src/modules/user/user.repository.ts +++ b/packages/apps/job-launcher/server/src/modules/user/user.repository.ts @@ -1,66 +1,30 @@ -import { Injectable, Logger, NotFoundException } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { - FindOptionsWhere, - FindManyOptions, - FindOneOptions, - Repository, -} from 'typeorm'; - +import { Injectable, Logger } from '@nestjs/common'; +import { DataSource, QueryFailedError, Repository } from 'typeorm'; import { UserEntity } from './user.entity'; -import { UserDto, UserUpdateDto } from './user.dto'; -import { ErrorUser } from '../../common/constants/errors'; +import { handleQueryFailedError } from '../../database/database.error'; @Injectable() -export class UserRepository { +export class UserRepository extends Repository { private readonly logger = new Logger(UserRepository.name); - constructor( - @InjectRepository(UserEntity) - private readonly userEntityRepository: Repository, - ) {} - - public async updateOne( - where: FindOptionsWhere, - dto: Partial, - ): Promise { - const userEntity = await this.userEntityRepository.findOneBy(where); - - if (!userEntity) { - this.logger.log(ErrorUser.NotFound, UserRepository.name); - throw new NotFoundException(ErrorUser.NotFound); - } - - Object.assign(userEntity, dto); - return userEntity.save(); + constructor(private dataSource: DataSource) { + super(UserEntity, dataSource.createEntityManager()); } - public async findOne( - where: FindOptionsWhere, - options?: FindOneOptions, - ): Promise { - const userEntity = await this.userEntityRepository.findOne({ - where, - ...options, - }); - - return userEntity; - } - - public find( - where: FindOptionsWhere, - options?: FindManyOptions, - ): Promise { - return this.userEntityRepository.find({ - where, - order: { - createdAt: 'DESC', - }, - ...options, - }); + async createUnique(user: UserEntity): Promise { + try { + await this.insert(user); + } catch (error) { + if (error instanceof QueryFailedError) { + throw handleQueryFailedError(error); + } else { + throw error; + } + } + return user; } - public async create(dto: UserDto): Promise { - return this.userEntityRepository.create(dto).save(); + async findByEmail(email: string): Promise { + return this.findOne({ where: { email } }); } } diff --git a/packages/apps/job-launcher/server/src/modules/user/user.service.spec.ts b/packages/apps/job-launcher/server/src/modules/user/user.service.spec.ts index 3a244f34d6..f0d551499b 100644 --- a/packages/apps/job-launcher/server/src/modules/user/user.service.spec.ts +++ b/packages/apps/job-launcher/server/src/modules/user/user.service.spec.ts @@ -1,16 +1,15 @@ +import { createMock } from '@golevelup/ts-jest'; +import { HttpService } from '@nestjs/axios'; +import { NotFoundException } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; import { Test } from '@nestjs/testing'; -import { ConflictException, NotFoundException } from '@nestjs/common'; +import { Currency } from '../../common/enums/payment'; +import { UserStatus, UserType } from '../../common/enums/user'; import { PaymentService } from '../payment/payment.service'; -import { ConfigService } from '@nestjs/config'; -import { HttpService } from '@nestjs/axios'; -import { createMock } from '@golevelup/ts-jest'; -import { ErrorUser } from '../../common/constants/errors'; +import { UserBalanceDto, UserCreateDto } from './user.dto'; +import { UserEntity } from './user.entity'; import { UserRepository } from './user.repository'; import { UserService } from './user.service'; -import { UserBalanceDto, UserCreateDto, UserUpdateDto } from './user.dto'; -import { UserEntity } from './user.entity'; -import { UserStatus, UserType } from '../../common/enums/user'; -import { Currency } from '../../common/enums/payment'; jest.mock('@human-protocol/sdk'); @@ -37,83 +36,28 @@ describe('UserService', () => { paymentService = moduleRef.get(PaymentService); }); - describe('update', () => { - it('should update a user and return the updated user entity', async () => { - const userId = 1; - const dto: UserUpdateDto = { - email: 'test@example.com', - status: UserStatus.ACTIVE, - }; - - const updatedUser: Partial = { - id: userId, - email: dto.email, - status: dto.status, - }; - - jest - .spyOn(userRepository, 'updateOne') - .mockResolvedValue(updatedUser as UserEntity); - - const result = await userService.update(userId, dto); - - expect(userRepository.updateOne).toHaveBeenCalledWith( - { id: userId }, - dto, - ); - expect(result).toBe(updatedUser); - }); - }); - describe('create', () => { it('should create a new user and return the created user entity', async () => { const dto: UserCreateDto = { email: 'test@example.com', password: 'password123', }; - const hashedPassword = - '$2b$12$Z02o9/Ay7CT0n99icApZYORH8iJI9VGtl3mju7d0c4SdDDujhSzOa'; const createdUser: Partial = { - id: 1, email: dto.email, - password: hashedPassword, + password: expect.any(String), + type: UserType.REQUESTER, + status: UserStatus.PENDING, }; - jest.spyOn(userService, 'checkEmail').mockResolvedValue(undefined); - jest - .spyOn(userRepository, 'create') - .mockResolvedValue(createdUser as UserEntity); - const result = await userService.create(dto); - - expect(userService.checkEmail).toHaveBeenCalledWith(dto.email, 0); - expect(userRepository.create).toHaveBeenCalledWith({ + expect(userRepository.createUnique).toHaveBeenCalledWith({ ...dto, email: dto.email, password: expect.any(String), type: UserType.REQUESTER, status: UserStatus.PENDING, }); - expect(result).toBe(createdUser); - }); - - it('should throw ConflictException if the email is already taken', async () => { - const dto: UserCreateDto = { - email: 'test@example.com', - password: 'password123', - }; - - jest - .spyOn(userService, 'checkEmail') - .mockRejectedValue( - new ConflictException(ErrorUser.AccountCannotBeRegistered), - ); - - await expect(userService.create(dto)).rejects.toThrow( - ErrorUser.AccountCannotBeRegistered, - ); - - expect(userService.checkEmail).toHaveBeenCalledWith(dto.email, 0); + expect(result).toMatchObject(createdUser); }); }); @@ -131,27 +75,23 @@ describe('UserService', () => { it('should return the user entity if credentials are valid', async () => { jest - .spyOn(userRepository, 'findOne') + .spyOn(userRepository, 'findByEmail') .mockResolvedValue(userEntity as UserEntity); const result = await userService.getByCredentials(email, password); - expect(userRepository.findOne).toHaveBeenCalledWith({ - email, - }); + expect(userRepository.findByEmail).toHaveBeenCalledWith(email); expect(result).toBe(userEntity); }); it('should throw NotFoundException if credentials are invalid', async () => { - jest.spyOn(userRepository, 'findOne').mockResolvedValue(null); + jest.spyOn(userRepository, 'findByEmail').mockResolvedValue(null); await expect( userService.getByCredentials(email, password), ).rejects.toThrow(NotFoundException); - expect(userRepository.findOne).toHaveBeenCalledWith({ - email, - }); + expect(userRepository.findByEmail).toHaveBeenCalledWith(email); }); }); diff --git a/packages/apps/job-launcher/server/src/modules/user/user.service.ts b/packages/apps/job-launcher/server/src/modules/user/user.service.ts index 2294eb2229..cb7610579b 100644 --- a/packages/apps/job-launcher/server/src/modules/user/user.service.ts +++ b/packages/apps/job-launcher/server/src/modules/user/user.service.ts @@ -1,16 +1,9 @@ -import { - BadRequestException, - Injectable, - Logger, - NotFoundException, -} from '@nestjs/common'; -import { ConfigService } from '@nestjs/config'; +import { Injectable, Logger, NotFoundException } from '@nestjs/common'; import * as bcrypt from 'bcrypt'; -import { Not } from 'typeorm'; import { UserEntity } from './user.entity'; import { UserStatus, UserType } from '../../common/enums/user'; -import { UserBalanceDto, UserCreateDto, UserUpdateDto } from './user.dto'; +import { UserBalanceDto, UserCreateDto } from './user.dto'; import { UserRepository } from './user.repository'; import { ValidatePasswordDto } from '../auth/auth.dto'; import { ErrorUser } from '../../common/constants/errors'; @@ -23,35 +16,24 @@ export class UserService { private HASH_ROUNDS = 12; constructor( private userRepository: UserRepository, - private readonly configService: ConfigService, private readonly paymentService: PaymentService, ) {} - public async update(userId: number, dto: UserUpdateDto): Promise { - return this.userRepository.updateOne({ id: userId }, dto); - } - public async create(dto: UserCreateDto): Promise { - const { email, password, ...rest } = dto; - - await this.checkEmail(email, 0); - - return await this.userRepository.create({ - ...rest, - email, - password: bcrypt.hashSync(password, this.HASH_ROUNDS), - type: UserType.REQUESTER, - status: UserStatus.PENDING, - }); + const newUser = new UserEntity(); + newUser.email = dto.email; + newUser.password = bcrypt.hashSync(dto.password, this.HASH_ROUNDS); + newUser.type = UserType.REQUESTER; + newUser.status = UserStatus.PENDING; + await this.userRepository.createUnique(newUser); + return newUser; } public async getByCredentials( email: string, password: string, ): Promise { - const userEntity = await this.userRepository.findOne({ - email, - }); + const userEntity = await this.userRepository.findByEmail(email); if (!userEntity) { throw new NotFoundException(ErrorUser.InvalidCredentials); @@ -65,7 +47,7 @@ export class UserService { } public async getByEmail(email: string): Promise { - return this.userRepository.findOne({ email }); + return this.userRepository.findByEmail(email); } public updatePassword( @@ -81,18 +63,6 @@ export class UserService { return userEntity.save(); } - public async checkEmail(email: string, id: number): Promise { - const userEntity = await this.userRepository.findOne({ - email, - id: Not(id), - }); - - if (userEntity) { - this.logger.log(ErrorUser.AccountCannotBeRegistered, UserService.name); - throw new BadRequestException(ErrorUser.AccountCannotBeRegistered); - } - } - public async getBalance(userId: number): Promise { return { amount: await this.paymentService.getUserBalance(userId), From 55f65834eb46ad64de886ee21c8c35d109e3ac03 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 30 Jan 2024 16:07:51 +0100 Subject: [PATCH 066/104] Bump react-redux from 9.0.4 to 9.1.0 (#1538) Bumps [react-redux](https://github.com/reduxjs/react-redux) from 9.0.4 to 9.1.0. - [Release notes](https://github.com/reduxjs/react-redux/releases) - [Changelog](https://github.com/reduxjs/react-redux/blob/master/CHANGELOG.md) - [Commits](https://github.com/reduxjs/react-redux/compare/v9.0.4...v9.1.0) --- updated-dependencies: - dependency-name: react-redux dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- packages/apps/dashboard/ui/package.json | 2 +- packages/apps/job-launcher/client/package.json | 2 +- yarn.lock | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/apps/dashboard/ui/package.json b/packages/apps/dashboard/ui/package.json index 3cecf6a609..857b76d1e2 100644 --- a/packages/apps/dashboard/ui/package.json +++ b/packages/apps/dashboard/ui/package.json @@ -26,7 +26,7 @@ "react-dom": "^18.2.0", "react-gtm-module": "^2.0.11", "react-loading-skeleton": "^3.3.1", - "react-redux": "^9.0.4", + "react-redux": "^9.1.0", "react-router-dom": "^6.4.3", "react-test-renderer": "^18.2.0", "recharts": "2.9.0", diff --git a/packages/apps/job-launcher/client/package.json b/packages/apps/job-launcher/client/package.json index 1f84e1ea81..295f6e0cef 100644 --- a/packages/apps/job-launcher/client/package.json +++ b/packages/apps/job-launcher/client/package.json @@ -20,7 +20,7 @@ "jwt-decode": "^4.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-redux": "^9.0.4", + "react-redux": "^9.1.0", "react-router-dom": "^6.14.1", "recharts": "^2.7.2", "swr": "^2.2.4", diff --git a/yarn.lock b/yarn.lock index f7dc4bcfd7..8bf9052513 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20345,10 +20345,10 @@ react-redux@8.1.1: react-is "^18.0.0" use-sync-external-store "^1.0.0" -react-redux@^9.0.4: - version "9.0.4" - resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-9.0.4.tgz#6892d465f086507a517d4b53eb589876e6bc8344" - integrity sha512-9J1xh8sWO0vYq2sCxK2My/QO7MzUMRi3rpiILP/+tDr8krBHixC6JMM17fMK88+Oh3e4Ae6/sHIhNBgkUivwFA== +react-redux@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-9.1.0.tgz#46a46d4cfed4e534ce5452bb39ba18e1d98a8197" + integrity sha512-6qoDzIO+gbrza8h3hjMA9aq4nwVFCKFtY2iLxCtVT38Swyy2C/dJCGBXHeHLtx6qlg/8qzc2MrhOeduf5K32wQ== dependencies: "@types/use-sync-external-store" "^0.0.3" use-sync-external-store "^1.0.0" From ea4623f85348bc1102693875e71b056af31195e4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 30 Jan 2024 16:08:15 +0100 Subject: [PATCH 067/104] Bump sinon from 15.2.0 to 17.0.1 (#1541) Bumps [sinon](https://github.com/sinonjs/sinon) from 15.2.0 to 17.0.1. - [Release notes](https://github.com/sinonjs/sinon/releases) - [Changelog](https://github.com/sinonjs/sinon/blob/main/docs/changelog.md) - [Commits](https://github.com/sinonjs/sinon/compare/v15.2.0...v17.0.1) --- updated-dependencies: - dependency-name: sinon dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- packages/apps/dashboard/ui/package.json | 2 +- yarn.lock | 48 +++++++++++++++---------- 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/packages/apps/dashboard/ui/package.json b/packages/apps/dashboard/ui/package.json index 857b76d1e2..53c741c14b 100644 --- a/packages/apps/dashboard/ui/package.json +++ b/packages/apps/dashboard/ui/package.json @@ -59,7 +59,7 @@ "jsdom": "^22.1.0", "merkletreejs": "^0.3.11", "resize-observer-polyfill": "^1.5.1", - "sinon": "^15.0.4", + "sinon": "^17.0.1", "vite": "^5.0.12", "vite-plugin-node-polyfills": "^0.19.0", "vitest": "^0.30.1" diff --git a/yarn.lock b/yarn.lock index 8bf9052513..836d09ab5b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5152,13 +5152,20 @@ dependencies: type-detect "4.0.8" -"@sinonjs/fake-timers@^10.0.2", "@sinonjs/fake-timers@^10.3.0": +"@sinonjs/fake-timers@^10.0.2": version "10.3.0" resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66" integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA== dependencies: "@sinonjs/commons" "^3.0.0" +"@sinonjs/fake-timers@^11.2.2": + version "11.2.2" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-11.2.2.tgz#50063cc3574f4a27bd8453180a04171c85cc9699" + integrity sha512-G2piCSxQ7oWOxwGSAyFHfPIsyeJGXYtc6mFbnFA+kRXkiEnTl8c/8jul2S329iFBnDI9HGoeWWAZvuvOkZccgw== + dependencies: + "@sinonjs/commons" "^3.0.0" + "@sinonjs/formatio@^3.2.1": version "3.2.2" resolved "https://registry.yarnpkg.com/@sinonjs/formatio/-/formatio-3.2.2.tgz#771c60dfa75ea7f2d68e3b94c7e888a78781372c" @@ -5185,7 +5192,7 @@ lodash.get "^4.4.2" type-detect "^4.0.8" -"@sinonjs/text-encoding@^0.7.1": +"@sinonjs/text-encoding@^0.7.1", "@sinonjs/text-encoding@^0.7.2": version "0.7.2" resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz#5981a8db18b56ba38ef0efb7d995b12aa7b51918" integrity sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ== @@ -16639,6 +16646,11 @@ just-extend@^4.0.2: resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.2.1.tgz#ef5e589afb61e5d66b24eca749409a8939a8c744" integrity sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg== +just-extend@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-6.2.0.tgz#b816abfb3d67ee860482e7401564672558163947" + integrity sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw== + jwa@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" @@ -18380,16 +18392,16 @@ nise@^1.5.2: lolex "^5.0.1" path-to-regexp "^1.7.0" -nise@^5.1.4: - version "5.1.5" - resolved "https://registry.yarnpkg.com/nise/-/nise-5.1.5.tgz#f2aef9536280b6c18940e32ba1fbdc770b8964ee" - integrity sha512-VJuPIfUFaXNRzETTQEEItTOP8Y171ijr+JLq42wHes3DiryR8vT+1TXQW/Rx8JNUhyYYWyIvjXTU6dOhJcs9Nw== +nise@^5.1.5: + version "5.1.7" + resolved "https://registry.yarnpkg.com/nise/-/nise-5.1.7.tgz#03ca96539efb306612eb60a8c5d6beeb208e27e5" + integrity sha512-wWtNUhkT7k58uvWTB/Gy26eA/EJKtPZFVAhEilN5UYVmmGRYOURbejRUyKm0Uu9XVEW7K5nBOZfR8VMB4QR2RQ== dependencies: - "@sinonjs/commons" "^2.0.0" - "@sinonjs/fake-timers" "^10.0.2" - "@sinonjs/text-encoding" "^0.7.1" - just-extend "^4.0.2" - path-to-regexp "^1.7.0" + "@sinonjs/commons" "^3.0.0" + "@sinonjs/fake-timers" "^11.2.2" + "@sinonjs/text-encoding" "^0.7.2" + just-extend "^6.2.0" + path-to-regexp "^6.2.1" no-case@^2.2.0, no-case@^2.3.2: version "2.3.2" @@ -19448,7 +19460,7 @@ path-to-regexp@^1.7.0: dependencies: isarray "0.0.1" -path-to-regexp@^6.1.0: +path-to-regexp@^6.1.0, path-to-regexp@^6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.2.1.tgz#d54934d6798eb9e5ef14e7af7962c945906918e5" integrity sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw== @@ -21671,16 +21683,16 @@ sinon-chrome@^3.0.1: sinon "^7.2.3" urijs "^1.18.2" -sinon@^15.0.4: - version "15.2.0" - resolved "https://registry.yarnpkg.com/sinon/-/sinon-15.2.0.tgz#5e44d4bc5a9b5d993871137fd3560bebfac27565" - integrity sha512-nPS85arNqwBXaIsFCkolHjGIkFo+Oxu9vbgmBJizLAhqe6P2o3Qmj3KCUoRkfhHtvgDhZdWD3risLHAUJ8npjw== +sinon@^17.0.1: + version "17.0.1" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-17.0.1.tgz#26b8ef719261bf8df43f925924cccc96748e407a" + integrity sha512-wmwE19Lie0MLT+ZYNpDymasPHUKTaZHUH/pKEubRXIzySv9Atnlw+BUMGCzWgV7b7wO+Hw6f1TEOr0IUnmU8/g== dependencies: "@sinonjs/commons" "^3.0.0" - "@sinonjs/fake-timers" "^10.3.0" + "@sinonjs/fake-timers" "^11.2.2" "@sinonjs/samsam" "^8.0.0" diff "^5.1.0" - nise "^5.1.4" + nise "^5.1.5" supports-color "^7.2.0" sinon@^7.2.3: From 1ba9bcd079e7d8bb48a7ad67a447b3f916faf41a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 30 Jan 2024 16:08:46 +0100 Subject: [PATCH 068/104] Bump rollup from 2.79.1 to 4.9.6 (#1542) Bumps [rollup](https://github.com/rollup/rollup) from 2.79.1 to 4.9.6. - [Release notes](https://github.com/rollup/rollup/releases) - [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md) - [Commits](https://github.com/rollup/rollup/compare/v2.79.1...v4.9.6) --- updated-dependencies: - dependency-name: rollup dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- packages/apps/meta-code-verify/package.json | 2 +- yarn.lock | 164 ++++++++++---------- 2 files changed, 83 insertions(+), 83 deletions(-) diff --git a/packages/apps/meta-code-verify/package.json b/packages/apps/meta-code-verify/package.json index 38c9343b18..3c731988cd 100644 --- a/packages/apps/meta-code-verify/package.json +++ b/packages/apps/meta-code-verify/package.json @@ -34,7 +34,7 @@ "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", "prettier": "^3.1.1", - "rollup": "^2.56.3", + "rollup": "^4.9.6", "sinon-chrome": "^3.0.1", "ts-jest": "^29.1.1", "tslib": "^2.5.0", diff --git a/yarn.lock b/yarn.lock index 836d09ab5b..ae1d22e889 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4779,70 +4779,70 @@ estree-walker "^2.0.2" picomatch "^2.3.1" -"@rollup/rollup-android-arm-eabi@4.9.4": - version "4.9.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.4.tgz#b1094962742c1a0349587040bc06185e2a667c9b" - integrity sha512-ub/SN3yWqIv5CWiAZPHVS1DloyZsJbtXmX4HxUTIpS0BHm9pW5iYBo2mIZi+hE3AeiTzHz33blwSnhdUo+9NpA== - -"@rollup/rollup-android-arm64@4.9.4": - version "4.9.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.4.tgz#96eb86fb549e05b187f2ad06f51d191a23cb385a" - integrity sha512-ehcBrOR5XTl0W0t2WxfTyHCR/3Cq2jfb+I4W+Ch8Y9b5G+vbAecVv0Fx/J1QKktOrgUYsIKxWAKgIpvw56IFNA== - -"@rollup/rollup-darwin-arm64@4.9.4": - version "4.9.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.4.tgz#2456630c007cc5905cb368acb9ff9fc04b2d37be" - integrity sha512-1fzh1lWExwSTWy8vJPnNbNM02WZDS8AW3McEOb7wW+nPChLKf3WG2aG7fhaUmfX5FKw9zhsF5+MBwArGyNM7NA== - -"@rollup/rollup-darwin-x64@4.9.4": - version "4.9.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.4.tgz#97742214fc7dfd47a0f74efba6f5ae264e29c70c" - integrity sha512-Gc6cukkF38RcYQ6uPdiXi70JB0f29CwcQ7+r4QpfNpQFVHXRd0DfWFidoGxjSx1DwOETM97JPz1RXL5ISSB0pA== - -"@rollup/rollup-linux-arm-gnueabihf@4.9.4": - version "4.9.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.4.tgz#cd933e61d6f689c9cdefde424beafbd92cfe58e2" - integrity sha512-g21RTeFzoTl8GxosHbnQZ0/JkuFIB13C3T7Y0HtKzOXmoHhewLbVTFBQZu+z5m9STH6FZ7L/oPgU4Nm5ErN2fw== - -"@rollup/rollup-linux-arm64-gnu@4.9.4": - version "4.9.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.4.tgz#33b09bf462f1837afc1e02a1b352af6b510c78a6" - integrity sha512-TVYVWD/SYwWzGGnbfTkrNpdE4HON46orgMNHCivlXmlsSGQOx/OHHYiQcMIOx38/GWgwr/po2LBn7wypkWw/Mg== - -"@rollup/rollup-linux-arm64-musl@4.9.4": - version "4.9.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.4.tgz#50257fb248832c2308064e3764a16273b6ee4615" - integrity sha512-XcKvuendwizYYhFxpvQ3xVpzje2HHImzg33wL9zvxtj77HvPStbSGI9czrdbfrf8DGMcNNReH9pVZv8qejAQ5A== - -"@rollup/rollup-linux-riscv64-gnu@4.9.4": - version "4.9.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.4.tgz#09589e4e1a073cf56f6249b77eb6c9a8e9b613a8" - integrity sha512-LFHS/8Q+I9YA0yVETyjonMJ3UA+DczeBd/MqNEzsGSTdNvSJa1OJZcSH8GiXLvcizgp9AlHs2walqRcqzjOi3A== - -"@rollup/rollup-linux-x64-gnu@4.9.4": - version "4.9.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.4.tgz#bd312bb5b5f02e54d15488605d15cfd3f90dda7c" - integrity sha512-dIYgo+j1+yfy81i0YVU5KnQrIJZE8ERomx17ReU4GREjGtDW4X+nvkBak2xAUpyqLs4eleDSj3RrV72fQos7zw== - -"@rollup/rollup-linux-x64-musl@4.9.4": - version "4.9.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.4.tgz#25b3bede85d86438ce28cc642842d10d867d40e9" - integrity sha512-RoaYxjdHQ5TPjaPrLsfKqR3pakMr3JGqZ+jZM0zP2IkDtsGa4CqYaWSfQmZVgFUCgLrTnzX+cnHS3nfl+kB6ZQ== - -"@rollup/rollup-win32-arm64-msvc@4.9.4": - version "4.9.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.4.tgz#95957067eb107f571da1d81939f017d37b4958d3" - integrity sha512-T8Q3XHV+Jjf5e49B4EAaLKV74BbX7/qYBRQ8Wop/+TyyU0k+vSjiLVSHNWdVd1goMjZcbhDmYZUYW5RFqkBNHQ== - -"@rollup/rollup-win32-ia32-msvc@4.9.4": - version "4.9.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.4.tgz#71b6facad976db527863f698692c6964c0b6e10e" - integrity sha512-z+JQ7JirDUHAsMecVydnBPWLwJjbppU+7LZjffGf+Jvrxq+dVjIE7By163Sc9DKc3ADSU50qPVw0KonBS+a+HQ== - -"@rollup/rollup-win32-x64-msvc@4.9.4": - version "4.9.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.4.tgz#16295ccae354707c9bc6842906bdeaad4f3ba7a5" - integrity sha512-LfdGXCV9rdEify1oxlN9eamvDSjv9md9ZVMAbNHA87xqIfFCxImxan9qZ8+Un54iK2nnqPlbnSi4R54ONtbWBw== +"@rollup/rollup-android-arm-eabi@4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.6.tgz#66b8d9cb2b3a474d115500f9ebaf43e2126fe496" + integrity sha512-MVNXSSYN6QXOulbHpLMKYi60ppyO13W9my1qogeiAqtjb2yR4LSmfU2+POvDkLzhjYLXz9Rf9+9a3zFHW1Lecg== + +"@rollup/rollup-android-arm64@4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.6.tgz#46327d5b86420d2307946bec1535fdf00356e47d" + integrity sha512-T14aNLpqJ5wzKNf5jEDpv5zgyIqcpn1MlwCrUXLrwoADr2RkWA0vOWP4XxbO9aiO3dvMCQICZdKeDrFl7UMClw== + +"@rollup/rollup-darwin-arm64@4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.6.tgz#166987224d2f8b1e2fd28ee90c447d52271d5e90" + integrity sha512-CqNNAyhRkTbo8VVZ5R85X73H3R5NX9ONnKbXuHisGWC0qRbTTxnF1U4V9NafzJbgGM0sHZpdO83pLPzq8uOZFw== + +"@rollup/rollup-darwin-x64@4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.6.tgz#a2e6e096f74ccea6e2f174454c26aef6bcdd1274" + integrity sha512-zRDtdJuRvA1dc9Mp6BWYqAsU5oeLixdfUvkTHuiYOHwqYuQ4YgSmi6+/lPvSsqc/I0Omw3DdICx4Tfacdzmhog== + +"@rollup/rollup-linux-arm-gnueabihf@4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.6.tgz#09fcd4c55a2d6160c5865fec708a8e5287f30515" + integrity sha512-oNk8YXDDnNyG4qlNb6is1ojTOGL/tRhbbKeE/YuccItzerEZT68Z9gHrY3ROh7axDc974+zYAPxK5SH0j/G+QQ== + +"@rollup/rollup-linux-arm64-gnu@4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.6.tgz#19a3c0b6315c747ca9acf86e9b710cc2440f83c9" + integrity sha512-Z3O60yxPtuCYobrtzjo0wlmvDdx2qZfeAWTyfOjEDqd08kthDKexLpV97KfAeUXPosENKd8uyJMRDfFMxcYkDQ== + +"@rollup/rollup-linux-arm64-musl@4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.6.tgz#94aaf95fdaf2ad9335983a4552759f98e6b2e850" + integrity sha512-gpiG0qQJNdYEVad+1iAsGAbgAnZ8j07FapmnIAQgODKcOTjLEWM9sRb+MbQyVsYCnA0Im6M6QIq6ax7liws6eQ== + +"@rollup/rollup-linux-riscv64-gnu@4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.6.tgz#160510e63f4b12618af4013bddf1761cf9fc9880" + integrity sha512-+uCOcvVmFUYvVDr27aiyun9WgZk0tXe7ThuzoUTAukZJOwS5MrGbmSlNOhx1j80GdpqbOty05XqSl5w4dQvcOA== + +"@rollup/rollup-linux-x64-gnu@4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.6.tgz#5ac5d068ce0726bd0a96ca260d5bd93721c0cb98" + integrity sha512-HUNqM32dGzfBKuaDUBqFB7tP6VMN74eLZ33Q9Y1TBqRDn+qDonkAUyKWwF9BR9unV7QUzffLnz9GrnKvMqC/fw== + +"@rollup/rollup-linux-x64-musl@4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.6.tgz#bafa759ab43e8eab9edf242a8259ffb4f2a57a5d" + integrity sha512-ch7M+9Tr5R4FK40FHQk8VnML0Szi2KRujUgHXd/HjuH9ifH72GUmw6lStZBo3c3GB82vHa0ZoUfjfcM7JiiMrQ== + +"@rollup/rollup-win32-arm64-msvc@4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.6.tgz#1cc3416682e5a20d8f088f26657e6e47f8db468e" + integrity sha512-VD6qnR99dhmTQ1mJhIzXsRcTBvTjbfbGGwKAHcu+52cVl15AC/kplkhxzW/uT0Xl62Y/meBKDZvoJSJN+vTeGA== + +"@rollup/rollup-win32-ia32-msvc@4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.6.tgz#7d2251e1aa5e8a1e47c86891fe4547a939503461" + integrity sha512-J9AFDq/xiRI58eR2NIDfyVmTYGyIZmRcvcAoJ48oDld/NTR8wyiPUu2X/v1navJ+N/FGg68LEbX3Ejd6l8B7MQ== + +"@rollup/rollup-win32-x64-msvc@4.9.6": + version "4.9.6" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.6.tgz#2c1fb69e02a3f1506f52698cfdc3a8b6386df9a6" + integrity sha512-jqzNLhNDvIZOrt69Ce4UjGRpXJBzhUBzawMwnaDAwyHriki3XollsewxWzOzz+4yOFDkuJHtTsZFwMxhYJWmLQ== "@rushstack/eslint-patch@^1.1.0": version "1.6.1" @@ -21166,7 +21166,7 @@ rlp@^2.2.3, rlp@^2.2.4: optionalDependencies: fsevents "~2.3.2" -rollup@^2.38.5, rollup@^2.56.3: +rollup@^2.38.5: version "2.79.1" resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.79.1.tgz#bedee8faef7c9f93a2647ac0108748f497f081c7" integrity sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw== @@ -21180,26 +21180,26 @@ rollup@^3.27.1: optionalDependencies: fsevents "~2.3.2" -rollup@^4.2.0: - version "4.9.4" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.9.4.tgz#37bc0c09ae6b4538a9c974f4d045bb64b2e7c27c" - integrity sha512-2ztU7pY/lrQyXSCnnoU4ICjT/tCG9cdH3/G25ERqE3Lst6vl2BCM5hL2Nw+sslAvAf+ccKsAq1SkKQALyqhR7g== +rollup@^4.2.0, rollup@^4.9.6: + version "4.9.6" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.9.6.tgz#4515facb0318ecca254a2ee1315e22e09efc50a0" + integrity sha512-05lzkCS2uASX0CiLFybYfVkwNbKZG5NFQ6Go0VWyogFTXXbR039UVsegViTntkk4OglHBdF54ccApXRRuXRbsg== dependencies: "@types/estree" "1.0.5" optionalDependencies: - "@rollup/rollup-android-arm-eabi" "4.9.4" - "@rollup/rollup-android-arm64" "4.9.4" - "@rollup/rollup-darwin-arm64" "4.9.4" - "@rollup/rollup-darwin-x64" "4.9.4" - "@rollup/rollup-linux-arm-gnueabihf" "4.9.4" - "@rollup/rollup-linux-arm64-gnu" "4.9.4" - "@rollup/rollup-linux-arm64-musl" "4.9.4" - "@rollup/rollup-linux-riscv64-gnu" "4.9.4" - "@rollup/rollup-linux-x64-gnu" "4.9.4" - "@rollup/rollup-linux-x64-musl" "4.9.4" - "@rollup/rollup-win32-arm64-msvc" "4.9.4" - "@rollup/rollup-win32-ia32-msvc" "4.9.4" - "@rollup/rollup-win32-x64-msvc" "4.9.4" + "@rollup/rollup-android-arm-eabi" "4.9.6" + "@rollup/rollup-android-arm64" "4.9.6" + "@rollup/rollup-darwin-arm64" "4.9.6" + "@rollup/rollup-darwin-x64" "4.9.6" + "@rollup/rollup-linux-arm-gnueabihf" "4.9.6" + "@rollup/rollup-linux-arm64-gnu" "4.9.6" + "@rollup/rollup-linux-arm64-musl" "4.9.6" + "@rollup/rollup-linux-riscv64-gnu" "4.9.6" + "@rollup/rollup-linux-x64-gnu" "4.9.6" + "@rollup/rollup-linux-x64-musl" "4.9.6" + "@rollup/rollup-win32-arm64-msvc" "4.9.6" + "@rollup/rollup-win32-ia32-msvc" "4.9.6" + "@rollup/rollup-win32-x64-msvc" "4.9.6" fsevents "~2.3.2" rpc-websockets@^7.5.1: From 9c04d663fc7f4a11af84cd758b850904f905d625 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 30 Jan 2024 16:09:31 +0100 Subject: [PATCH 069/104] Bump @mui/x-date-pickers from 6.18.7 to 6.19.2 (#1540) Bumps [@mui/x-date-pickers](https://github.com/mui/mui-x/tree/HEAD/packages/x-date-pickers) from 6.18.7 to 6.19.2. - [Release notes](https://github.com/mui/mui-x/releases) - [Changelog](https://github.com/mui/mui-x/blob/v6.19.2/CHANGELOG.md) - [Commits](https://github.com/mui/mui-x/commits/v6.19.2/packages/x-date-pickers) --- updated-dependencies: - dependency-name: "@mui/x-date-pickers" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- packages/apps/job-launcher/client/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/apps/job-launcher/client/package.json b/packages/apps/job-launcher/client/package.json index 295f6e0cef..40647e160d 100644 --- a/packages/apps/job-launcher/client/package.json +++ b/packages/apps/job-launcher/client/package.json @@ -9,7 +9,7 @@ "@human-protocol/sdk": "*", "@mui/lab": "^5.0.0-alpha.141", "@mui/material": "^5.14.14", - "@mui/x-date-pickers": "^6.18.6", + "@mui/x-date-pickers": "^6.19.2", "@reduxjs/toolkit": "^1.9.5", "@stripe/react-stripe-js": "^2.4.0", "@stripe/stripe-js": "^2.2.1", diff --git a/yarn.lock b/yarn.lock index ae1d22e889..d2d651031c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3451,10 +3451,10 @@ prop-types "^15.8.1" react-is "^18.2.0" -"@mui/x-date-pickers@^6.18.6": - version "6.18.7" - resolved "https://registry.yarnpkg.com/@mui/x-date-pickers/-/x-date-pickers-6.18.7.tgz#6b00163c77dc450c11b44a479baf62541e6f8b36" - integrity sha512-4NoapaCT3jvEk2cuAUjG0ReZvTEk1i4dGDz94Gt1Oc08GuC1AuzYRwCR1/1tdmbDynwkR8ilkKL6AyS3NL1H4A== +"@mui/x-date-pickers@^6.19.2": + version "6.19.2" + resolved "https://registry.yarnpkg.com/@mui/x-date-pickers/-/x-date-pickers-6.19.2.tgz#ac616c289bc9abbb6c9fd5b4e63bc58f2b0495d6" + integrity sha512-/bdWZabexuz+1rKG15XryxiMGb5D0XVx65NU7CZYKm/1+HuUzc0FX9smKEa/YVZnLSNsAp6SULIyPZtAKE+3AA== dependencies: "@babel/runtime" "^7.23.2" "@mui/base" "^5.0.0-beta.22" From bf4fedfdbcec7c5b74122515fc82c8798c41566c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 31 Jan 2024 05:08:55 +0800 Subject: [PATCH 070/104] Bump @nestjs/testing from 9.4.3 to 10.3.1 (#1539) Bumps [@nestjs/testing](https://github.com/nestjs/nest/tree/HEAD/packages/testing) from 9.4.3 to 10.3.1. - [Release notes](https://github.com/nestjs/nest/releases) - [Commits](https://github.com/nestjs/nest/commits/v10.3.1/packages/testing) --- updated-dependencies: - dependency-name: "@nestjs/testing" dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .../fortune/exchange-oracle/server/package.json | 2 +- .../apps/fortune/recording-oracle/package.json | 2 +- packages/apps/job-launcher/server/package.json | 2 +- .../apps/reputation-oracle/server/package.json | 2 +- yarn.lock | 15 +++++---------- 5 files changed, 9 insertions(+), 14 deletions(-) diff --git a/packages/apps/fortune/exchange-oracle/server/package.json b/packages/apps/fortune/exchange-oracle/server/package.json index 410dc75267..0382729185 100644 --- a/packages/apps/fortune/exchange-oracle/server/package.json +++ b/packages/apps/fortune/exchange-oracle/server/package.json @@ -32,7 +32,7 @@ "@golevelup/ts-jest": "^0.4.0", "@nestjs/cli": "^9.4.3", "@nestjs/schematics": "^9.2.0", - "@nestjs/testing": "^9.4.3", + "@nestjs/testing": "^10.3.1", "@types/express": "^4.17.13", "@types/jest": "29.5.11", "@types/node": "20.10.6", diff --git a/packages/apps/fortune/recording-oracle/package.json b/packages/apps/fortune/recording-oracle/package.json index a1351839cd..2dba5c2c29 100644 --- a/packages/apps/fortune/recording-oracle/package.json +++ b/packages/apps/fortune/recording-oracle/package.json @@ -38,7 +38,7 @@ "devDependencies": { "@nestjs/cli": "^9.4.3", "@nestjs/schematics": "^9.2.0", - "@nestjs/testing": "^9.4.3", + "@nestjs/testing": "^10.3.1", "@types/express": "^4.17.13" } } diff --git a/packages/apps/job-launcher/server/package.json b/packages/apps/job-launcher/server/package.json index f725cb505b..9bc6953403 100644 --- a/packages/apps/job-launcher/server/package.json +++ b/packages/apps/job-launcher/server/package.json @@ -67,7 +67,7 @@ "@golevelup/ts-jest": "^0.4.0", "@nestjs/cli": "^9.4.3", "@nestjs/schematics": "^9.2.0", - "@nestjs/testing": "^9.4.3", + "@nestjs/testing": "^10.3.1", "@types/bcrypt": "^5.0.2", "@types/express": "^4.17.13", "@types/jest": "29.5.11", diff --git a/packages/apps/reputation-oracle/server/package.json b/packages/apps/reputation-oracle/server/package.json index 91f7d44e7a..45c57c67ae 100644 --- a/packages/apps/reputation-oracle/server/package.json +++ b/packages/apps/reputation-oracle/server/package.json @@ -63,7 +63,7 @@ "@golevelup/ts-jest": "^0.4.0", "@nestjs/cli": "^9.4.3", "@nestjs/schematics": "^9.2.0", - "@nestjs/testing": "^9.4.3", + "@nestjs/testing": "^10.3.1", "@types/bcrypt": "^5.0.2", "@types/cookie-parser": "^1.4.3", "@types/express": "^4.17.13", diff --git a/yarn.lock b/yarn.lock index d2d651031c..9a7afc787c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3609,12 +3609,12 @@ boxen "5.1.2" check-disk-space "3.4.0" -"@nestjs/testing@^9.4.3": - version "9.4.3" - resolved "https://registry.yarnpkg.com/@nestjs/testing/-/testing-9.4.3.tgz#53ffbabdd38f500b145c30f2fbb76dedad393d79" - integrity sha512-LDT8Ai2eKnTzvnPaJwWOK03qTaFap5uHHsJCv6dL0uKWk6hyF9jms8DjyVaGsaujCaXDG8izl1mDEER0OmxaZA== +"@nestjs/testing@^10.3.1": + version "10.3.1" + resolved "https://registry.yarnpkg.com/@nestjs/testing/-/testing-10.3.1.tgz#ea28a7d29122dd3a2df1542842e741a52dd7c474" + integrity sha512-74aSAugWT31jSPnStyRWDXgjHXWO3GYaUfAZ2T7Dml88UGkGy95iwaWgYy7aYM8/xVFKcDYkfL5FAYqZYce/yg== dependencies: - tslib "2.5.3" + tslib "2.6.2" "@nestjs/typeorm@^10.0.1": version "10.0.1" @@ -23173,11 +23173,6 @@ tslib@2.4.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== -tslib@2.5.3: - version "2.5.3" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.3.tgz#24944ba2d990940e6e982c4bea147aba80209913" - integrity sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w== - tslib@2.6.2, tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.4.1, tslib@^2.5.0, tslib@^2.6.0, tslib@^2.6.1, tslib@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" From 9ef87377215b0a762fa2678d445a898454ce0d66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20L=C3=B3pez?= <50665615+flopez7@users.noreply.github.com> Date: Wed, 31 Jan 2024 15:15:25 +0100 Subject: [PATCH 071/104] Update imports for Vercel (#1549) --- .../server/src/common/exceptions/database.filter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/apps/job-launcher/server/src/common/exceptions/database.filter.ts b/packages/apps/job-launcher/server/src/common/exceptions/database.filter.ts index 431e3b816a..aa51bb54ee 100644 --- a/packages/apps/job-launcher/server/src/common/exceptions/database.filter.ts +++ b/packages/apps/job-launcher/server/src/common/exceptions/database.filter.ts @@ -6,7 +6,7 @@ import { HttpStatus, } from '@nestjs/common'; import { Request, Response } from 'express'; -import { DatabaseError } from 'src/database/database.error'; +import { DatabaseError } from '../../database/database.error'; @Catch(DatabaseError) export class DatabaseExceptionFilter implements ExceptionFilter { From 06fa3d948ed48eac1ce267755e94ec4580a7da8a Mon Sep 17 00:00:00 2001 From: m00n620 <50647994+m00n620@users.noreply.github.com> Date: Wed, 31 Jan 2024 09:21:29 -0500 Subject: [PATCH 072/104] clean up error messages (#1515) --- .../components/Auth/ForgotPasswordForm.jsx | 13 +++--------- .../src/components/Auth/ResetPasswordForm.jsx | 20 ++++-------------- .../client/src/components/Auth/SignInForm.jsx | 6 ++++-- .../client/src/components/Auth/SignUpForm.jsx | 21 +++++-------------- .../src/components/Auth/VerifyEmailForm.tsx | 21 ++++--------------- .../src/components/Jobs/Create/PayJob.tsx | 16 ++++---------- .../TopUpAccount/CryptoTopUpForm.tsx | 4 ++-- .../components/TopUpAccount/FiatTopUpForm.tsx | 4 ++-- .../client/src/pages/Home/index.tsx | 20 +++--------------- .../client/src/pages/Job/JobDetail/index.tsx | 7 ++----- .../client/src/providers/SnackProvider.tsx | 8 ++++++- .../job-launcher/client/src/utils/string.ts | 9 ++++++++ 12 files changed, 49 insertions(+), 100 deletions(-) create mode 100644 packages/apps/job-launcher/client/src/utils/string.ts diff --git a/packages/apps/job-launcher/client/src/components/Auth/ForgotPasswordForm.jsx b/packages/apps/job-launcher/client/src/components/Auth/ForgotPasswordForm.jsx index a1ffed8cdb..13e58d6bc8 100644 --- a/packages/apps/job-launcher/client/src/components/Auth/ForgotPasswordForm.jsx +++ b/packages/apps/job-launcher/client/src/components/Auth/ForgotPasswordForm.jsx @@ -1,8 +1,6 @@ import HCaptcha from '@hcaptcha/react-hcaptcha'; import { LoadingButton } from '@mui/lab'; import { - Alert, - AlertTitle, Box, Button, FormHelperText, @@ -14,14 +12,15 @@ import { Formik } from 'formik'; import React, { useRef, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { CheckFilledIcon } from '../../components/Icons/CheckFilledIcon'; +import { useSnackbar } from '../../providers/SnackProvider'; import * as authService from '../../services/auth'; import { ForgotPasswordValidationSchema } from './schema'; export const ForgotPasswordForm = () => { const captchaRef = useRef(null); - const [alertMsg, setAlertMsg] = useState(''); const [isSuccess, setIsSuccess] = useState(false); const [isLoading, setIsLoading] = useState(false); + const { showError } = useSnackbar(); const navigate = useNavigate(); const handleForgotPassword = async ({ email }) => { @@ -30,7 +29,7 @@ export const ForgotPasswordForm = () => { await authService.forgotPassword(email); setIsSuccess(true); } catch (err) { - setAlertMsg(err?.response?.data?.message ?? err?.message); + showError(err); } setIsLoading(false); }; @@ -78,12 +77,6 @@ export const ForgotPasswordForm = () => { ) : ( - {alertMsg && alertMsg.length && ( - setAlertMsg('')} sx={{ my: 2 }}> - Send email failed! - {alertMsg} - - )} Reset Password diff --git a/packages/apps/job-launcher/client/src/components/Auth/ResetPasswordForm.jsx b/packages/apps/job-launcher/client/src/components/Auth/ResetPasswordForm.jsx index c1920e83e3..e6b301a1d0 100644 --- a/packages/apps/job-launcher/client/src/components/Auth/ResetPasswordForm.jsx +++ b/packages/apps/job-launcher/client/src/components/Auth/ResetPasswordForm.jsx @@ -1,24 +1,18 @@ import HCaptcha from '@hcaptcha/react-hcaptcha'; import { LoadingButton } from '@mui/lab'; -import { - Alert, - AlertTitle, - Box, - FormHelperText, - Link, - Typography, -} from '@mui/material'; +import { Box, FormHelperText, Link, Typography } from '@mui/material'; import { Formik } from 'formik'; import React, { useRef, useState } from 'react'; import { useNavigate } from 'react-router-dom'; +import { useSnackbar } from '../../providers/SnackProvider'; import * as authService from '../../services/auth'; import { Password } from './Password'; import { ResetPasswordValidationSchema } from './schema'; export const ResetPasswordForm = () => { const captchaRef = useRef(null); - const [alertMsg, setAlertMsg] = useState(''); const [isLoading, setIsLoading] = useState(false); + const { showError } = useSnackbar(); const navigate = useNavigate(); const token = new URLSearchParams(window.location.search).get('token'); @@ -32,7 +26,7 @@ export const ResetPasswordForm = () => { navigate('/'); } catch (err) { - console.error(err); + showError(err); } setIsLoading(false); }; @@ -45,12 +39,6 @@ export const ResetPasswordForm = () => { return ( - {alertMsg && alertMsg.length && ( - setAlertMsg('')} sx={{ mb: 2 }}> - Reset password failed! - {alertMsg} - - )} Reset Password diff --git a/packages/apps/job-launcher/client/src/components/Auth/SignInForm.jsx b/packages/apps/job-launcher/client/src/components/Auth/SignInForm.jsx index b71b96e9b1..26d6d881c3 100644 --- a/packages/apps/job-launcher/client/src/components/Auth/SignInForm.jsx +++ b/packages/apps/job-launcher/client/src/components/Auth/SignInForm.jsx @@ -4,17 +4,19 @@ import { Box, FormHelperText, Link as MuiLink, TextField } from '@mui/material'; import { useFormik } from 'formik'; import React, { useRef, useState } from 'react'; import { Link } from 'react-router-dom'; +import { useSnackbar } from '../../providers/SnackProvider'; import * as authService from '../../services/auth'; import { useAppDispatch } from '../../state'; import { fetchUserBalanceAsync, signIn } from '../../state/auth/reducer'; import { Password } from './Password'; import { LoginValidationSchema } from './schema'; -export const SignInForm = ({ onError }) => { +export const SignInForm = () => { const captchaRef = useRef(null); const [hcaptchaToken, setHcaptchaToken] = useState(''); const [isLoading, setIsLoading] = useState(false); const dispatch = useAppDispatch(); + const { showError } = useSnackbar(); const formik = useFormik({ initialValues: { email: '', @@ -35,7 +37,7 @@ export const SignInForm = ({ onError }) => { dispatch(signIn(data)); dispatch(fetchUserBalanceAsync()); } catch (err) { - onError(err?.response?.data?.message ?? err.message); + showError(err); } setIsLoading(false); }; diff --git a/packages/apps/job-launcher/client/src/components/Auth/SignUpForm.jsx b/packages/apps/job-launcher/client/src/components/Auth/SignUpForm.jsx index 86d5b89f79..a6aa573eda 100644 --- a/packages/apps/job-launcher/client/src/components/Auth/SignUpForm.jsx +++ b/packages/apps/job-launcher/client/src/components/Auth/SignUpForm.jsx @@ -1,8 +1,6 @@ import HCaptcha from '@hcaptcha/react-hcaptcha'; import { LoadingButton } from '@mui/lab'; import { - Alert, - AlertTitle, Box, Button, FormHelperText, @@ -13,16 +11,17 @@ import { } from '@mui/material'; import { Formik } from 'formik'; import React, { useRef, useState } from 'react'; +import { useSnackbar } from '../../providers/SnackProvider'; import * as authService from '../../services/auth'; import { Password } from './Password'; import { RegisterValidationSchema } from './schema'; -export const SignUpForm = ({ onFinish, onError }) => { +export const SignUpForm = ({ onFinish }) => { const captchaRef = useRef(null); const [isLoading, setIsLoading] = useState(false); const [isSuccess, setIsSuccess] = useState(false); const [email, setEmail] = useState(''); - const [alertMsg, setAlertMsg] = useState(''); + const { showError } = useSnackbar(); const handleRegister = async ({ email, password }) => { setIsLoading(true); @@ -31,7 +30,7 @@ export const SignUpForm = ({ onFinish, onError }) => { setEmail(email); setIsSuccess(true); } catch (err) { - onError(err?.response?.data?.message ?? err.message); + showError(err); } setIsLoading(false); }; @@ -42,7 +41,7 @@ export const SignUpForm = ({ onFinish, onError }) => { await authService.resendEmailVerification(email); onFinish(); } catch (err) { - setAlertMsg(err?.response?.data?.message ?? err?.message); + showError(err); } setIsLoading(false); }; @@ -57,16 +56,6 @@ export const SignUpForm = ({ onFinish, onError }) => { if (isSuccess) { return ( - {alertMsg && alertMsg.length && ( - setAlertMsg('')} - sx={{ my: 2 }} - > - Send email failed! - {alertMsg} - - )} Verify email diff --git a/packages/apps/job-launcher/client/src/components/Auth/VerifyEmailForm.tsx b/packages/apps/job-launcher/client/src/components/Auth/VerifyEmailForm.tsx index cd3a916a80..fa779b78ee 100644 --- a/packages/apps/job-launcher/client/src/components/Auth/VerifyEmailForm.tsx +++ b/packages/apps/job-launcher/client/src/components/Auth/VerifyEmailForm.tsx @@ -1,21 +1,14 @@ -import { - Alert, - AlertTitle, - Box, - Button, - CircularProgress, - Grid, - Typography, -} from '@mui/material'; +import { Box, Button, CircularProgress, Grid, Typography } from '@mui/material'; import React, { useEffect, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { CheckCircleFilledIcon } from '../../components/Icons/CheckCircleFilledIcon'; +import { useSnackbar } from '../../providers/SnackProvider'; import * as authService from '../../services/auth'; export const VerifyEmailForm = () => { - const [alertMsg, setAlertMsg] = useState(''); const [isLoading, setIsLoading] = useState(false); const [isSuccess, setIsSuccess] = useState(false); + const { showError } = useSnackbar(); const token = new URLSearchParams(window.location.search).get('token'); const navigate = useNavigate(); @@ -31,7 +24,7 @@ export const VerifyEmailForm = () => { setIsLoading(false); }) .catch((err: any) => { - setAlertMsg(err?.response?.data?.message ?? err.message); + showError(err); setIsLoading(false); }); }, [token]); @@ -89,12 +82,6 @@ export const VerifyEmailForm = () => { Verify email - {alertMsg && alertMsg.length && ( - setAlertMsg('')} sx={{ mb: 2 }}> - Verify email failed! - {alertMsg} - - )}