Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .changeset/rich-kids-rush.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@human-protocol/sdk": major
---

### Changed

- Created proper Subgraph types and cast values to the interfaces we have for SDK
- Refactor EscrowUtils, TransactionUtils, OperatorUtils and WorkerUtils methods to fix mismatch types with Subgraph
- Created some mappings to remove duplicated code
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { NETWORKS, StatisticsClient } from '@human-protocol/sdk';
import { DailyHMTData } from '@human-protocol/sdk/dist/graphql';
import { IDailyHMT, NETWORKS, StatisticsClient } from '@human-protocol/sdk';
import { HttpService } from '@nestjs/axios';
import { Cache, CACHE_MANAGER } from '@nestjs/cache-manager';
import { Inject, Injectable, OnModuleInit } from '@nestjs/common';
Expand Down Expand Up @@ -265,7 +264,7 @@ export class StatsService implements OnModuleInit {
}

private async isHmtDailyStatsFetched(): Promise<boolean> {
const data = await this.cacheManager.get<DailyHMTData>(
const data = await this.cacheManager.get<IDailyHMT>(
`${HMT_PREFIX}${HMT_STATS_START_DATE}`,
);
return !!data;
Expand Down Expand Up @@ -298,7 +297,7 @@ export class StatsService implements OnModuleInit {
operatingNetworks.map(async (network) => {
const statisticsClient = new StatisticsClient(NETWORKS[network]);
let skip = 0;
let fetchedRecords: DailyHMTData[] = [];
let fetchedRecords: IDailyHMT[] = [];

do {
fetchedRecords = await statisticsClient.getHMTDailyData({
Expand All @@ -310,7 +309,7 @@ export class StatsService implements OnModuleInit {

for (const record of fetchedRecords) {
const dailyCacheKey = `${HMT_PREFIX}${
record.timestamp.toISOString().split('T')[0]
new Date(record.timestamp).toISOString().split('T')[0]
}`;

// Sum daily values
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,24 @@ describe('WebhookService', () => {
),
).rejects.toThrow(new NotFoundError('Oracle not found'));
});

it('should throw NotFoundError if webhook url is not found', async () => {
(EscrowClient.build as any).mockImplementation(() => ({
getJobLauncherAddress: jest.fn().mockResolvedValue(MOCK_ADDRESS),
}));

(OperatorUtils.getOperator as any).mockResolvedValue({
webhookUrl: null,
});

await expect(
(webhookService as any).getOracleWebhookUrl(
JOB_LAUNCHER_WEBHOOK_URL,
ChainId.LOCALHOST,
EventType.ESCROW_FAILED,
),
).rejects.toThrow(new NotFoundError('Oracle webhook URL not found'));
});
});

describe('handleWebhookError', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,10 @@ export class WebhookService {
if (!oracle) {
throw new NotFoundError('Oracle not found');
}
const oracleWebhookUrl = oracle.webhookUrl;
if (!oracle.webhookUrl) {
throw new NotFoundError('Oracle webhook URL not found');
}

return oracleWebhookUrl;
return oracle.webhookUrl;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,24 @@ type DiscoveredOracleCreateProps = {
id: string;
address: string;
chainId: ChainId;
stakedAmount: bigint;
lockedAmount: bigint;
lockedUntilTimestamp: bigint;
withdrawnAmount: bigint;
slashedAmount: bigint;
amountJobsProcessed: bigint;
role?: string;
fee?: bigint;
publicKey?: string;
webhookUrl?: string;
website?: string;
stakedAmount: bigint | null;
lockedAmount: bigint | null;
lockedUntilTimestamp: number | null;
withdrawnAmount: bigint | null;
slashedAmount: bigint | null;
amountJobsProcessed: bigint | null;
role: string;
fee: bigint | null;
publicKey: string | null;
webhookUrl: string | null;
website: string | null;
url: string;
jobTypes: string[];
registrationNeeded?: boolean;
registrationInstructions?: string;
reputationNetworks?: string[];
name?: string;
category?: string;
jobTypes: string[] | null;
registrationNeeded: boolean | null;
registrationInstructions: string | null;
reputationNetworks: string[];
name: string;
category: string | null;
};

export class DiscoveredOracle {
Expand All @@ -38,23 +38,31 @@ export class DiscoveredOracle {
@ApiProperty({ description: 'Chain ID where the oracle is registered' })
chainId: ChainId;

@ApiProperty({ description: 'Amount staked by the operator' })
stakedAmount: string;
@ApiPropertyOptional({ description: 'Amount staked by the operator' })
stakedAmount?: string;

@ApiProperty({ description: 'Amount currently locked by the operator' })
lockedAmount: string;
@ApiPropertyOptional({
description: 'Amount currently locked by the operator',
})
lockedAmount?: string;

@ApiProperty({ description: 'Timestamp until funds are locked' })
lockedUntilTimestamp: string;
@ApiPropertyOptional({ description: 'Timestamp until funds are locked' })
lockedUntilTimestamp?: string;

@ApiProperty({ description: 'Total amount withdrawn by the operator' })
withdrawnAmount: string;
@ApiPropertyOptional({
description: 'Total amount withdrawn by the operator',
})
withdrawnAmount?: string;

@ApiProperty({ description: 'Total amount slashed from the operator' })
slashedAmount: string;
@ApiPropertyOptional({
description: 'Total amount slashed from the operator',
})
slashedAmount?: string;

@ApiProperty({ description: 'Number of jobs processed by the operator' })
amountJobsProcessed: string;
@ApiPropertyOptional({
description: 'Number of jobs processed by the operator',
})
amountJobsProcessed?: string;

@ApiPropertyOptional({ description: 'Fee charged by the operator' })
fee?: bigint;
Expand All @@ -81,7 +89,7 @@ export class DiscoveredOracle {
jobTypes: string[];

@ApiPropertyOptional({ description: 'Indicates if registration is needed' })
registrationNeeded: boolean;
registrationNeeded?: boolean;

@ApiPropertyOptional({
description: 'Instructions for registration, if needed',
Expand All @@ -107,14 +115,27 @@ export class DiscoveredOracle {
executionsToSkip = 0;

constructor(props: DiscoveredOracleCreateProps) {
Object.assign(this, props);
this.registrationNeeded = props.registrationNeeded || false;
this.stakedAmount = this.stakedAmount.toString();
this.lockedAmount = this.lockedAmount.toString();
this.withdrawnAmount = this.withdrawnAmount.toString();
this.slashedAmount = this.slashedAmount.toString();
this.amountJobsProcessed = this.amountJobsProcessed.toString();
this.lockedUntilTimestamp = this.lockedUntilTimestamp.toString();
this.id = props.id;
this.address = props.address;
this.chainId = props.chainId;
this.registrationNeeded = props.registrationNeeded ?? undefined;
this.role = props.role;
this.url = props.url;
this.name = props.name;
this.fee = props.fee ?? undefined;
this.publicKey = props.publicKey ?? undefined;
this.webhookUrl = props.webhookUrl ?? undefined;
this.website = props.website ?? undefined;
this.category = props.category ?? undefined;
this.registrationInstructions = props.registrationInstructions ?? undefined;
this.jobTypes = props.jobTypes ?? [];
this.reputationNetworks = props.reputationNetworks ?? undefined;
this.stakedAmount = props.stakedAmount?.toString();
this.lockedAmount = props.lockedAmount?.toString();
this.withdrawnAmount = props.withdrawnAmount?.toString();
this.slashedAmount = props.slashedAmount?.toString();
this.amountJobsProcessed = props.amountJobsProcessed?.toString();
this.lockedUntilTimestamp = props.lockedUntilTimestamp?.toString();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,18 +55,24 @@ export class OracleDiscoveryController {
id: 'thisrty-oracle',
address: process.env.THIRSTYFI_ORACLE_ADDRESS ?? '',
chainId: ChainId.POLYGON,
stakedAmount: '0' as any,
lockedAmount: '0' as any,
lockedUntilTimestamp: '0' as any,
withdrawnAmount: '0' as any,
slashedAmount: '0' as any,
amountJobsProcessed: '0' as any,
stakedAmount: 0n,
lockedAmount: 0n,
lockedUntilTimestamp: 0,
withdrawnAmount: 0n,
slashedAmount: 0n,
amountJobsProcessed: 0n,
role: 'exchange_oracle',
url: ' ',
jobTypes: ['thirstyfi'],
name: 'ThirstyFi',
registrationNeeded: false,
// registrationInstructions: 'https://www.thisrty.com/',
registrationInstructions: null,
publicKey: null,
webhookUrl: null,
website: null,
fee: null,
reputationNetworks: [],
category: null,
});
oracles.push(thisrtyOracle);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,8 @@ export class OracleDiscoveryService {
new DiscoveredOracle({
id: exchangeOracle.id,
address: exchangeOracle.address,
name: exchangeOracle.name,
role: exchangeOracle.role,
name: exchangeOracle.name as string,
role: exchangeOracle.role as string,
url: exchangeOracle.url as string,
jobTypes: exchangeOracle.jobTypes as string[],
registrationNeeded: exchangeOracle.registrationNeeded,
Expand All @@ -106,6 +106,12 @@ export class OracleDiscoveryService {
slashedAmount: exchangeOracle.slashedAmount,
amountJobsProcessed: exchangeOracle.amountJobsProcessed,
lockedUntilTimestamp: exchangeOracle.lockedUntilTimestamp,
fee: exchangeOracle.fee,
publicKey: exchangeOracle.publicKey,
webhookUrl: exchangeOracle.webhookUrl,
website: exchangeOracle.website,
reputationNetworks: exchangeOracle.reputationNetworks,
category: exchangeOracle.category,
}),
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import {
EscrowClient,
EscrowStatus,
EscrowUtils,
IStatusEvent,
KVStoreUtils,
NETWORKS,
} from '@human-protocol/sdk';
import { StatusEvent } from '@human-protocol/sdk/dist/graphql';
import { HttpService } from '@nestjs/axios';
import { ConfigService } from '@nestjs/config';
import { Test, TestingModule } from '@nestjs/testing';
Expand Down Expand Up @@ -1107,7 +1107,7 @@ describe('CronJobService', () => {
describe('syncJobStatuses Cron Job', () => {
let cronJobEntityMock: Partial<CronJobEntity>;
let jobEntityMock: Partial<JobEntity>;
let escrowEventMock: Partial<StatusEvent>;
let escrowEventMock: Partial<IStatusEvent>;

beforeEach(() => {
cronJobEntityMock = {
Expand All @@ -1125,7 +1125,7 @@ describe('CronJobService', () => {
escrowEventMock = {
chainId: ChainId.LOCALHOST,
escrowAddress: MOCK_ADDRESS,
status: EscrowStatus[EscrowStatus.Partial],
status: EscrowStatus.Partial,
};

jest.spyOn(repository, 'findOneByType').mockResolvedValue(null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -515,19 +515,19 @@ export class CronJobService {
}
if (!job || job.status === JobStatus.CANCELED) continue;

if (event.status === EscrowStatus[EscrowStatus.Cancelled]) {
if (event.status === EscrowStatus.Cancelled) {
await this.jobService.cancelJob(job);
continue;
}

let newStatus: JobStatus | null = null;
if (
event.status === EscrowStatus[EscrowStatus.Partial] &&
event.status === EscrowStatus.Partial &&
job.status !== JobStatus.PARTIAL
) {
newStatus = JobStatus.PARTIAL;
} else if (
event.status === EscrowStatus[EscrowStatus.Complete] &&
event.status === EscrowStatus.Complete &&
job.status !== JobStatus.COMPLETED
) {
newStatus = JobStatus.COMPLETED;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1745,16 +1745,17 @@ describe('JobService', () => {
describe('cancelJob', () => {
it('should create a refund payment and set status to CANCELED', async () => {
const jobEntity = createJobEntity();
const refundAmount = faker.number.float({ min: 1, max: 10 });
const tokenDecimals = (TOKEN_ADDRESSES[jobEntity.chainId as ChainId] ??
{})[jobEntity.token as EscrowFundToken]?.decimals;
const refundAmount = faker.number.float({
min: 1,
max: 10,
fractionDigits: tokenDecimals,
});

mockPaymentService.getJobPayments.mockResolvedValueOnce([]);
mockedEscrowUtils.getCancellationRefund.mockResolvedValueOnce({
amount: ethers.parseUnits(
refundAmount.toString(),
(TOKEN_ADDRESSES[jobEntity.chainId as ChainId] ?? {})[
jobEntity.token as EscrowFundToken
]?.decimals,
),
amount: ethers.parseUnits(refundAmount.toString(), tokenDecimals),
escrowAddress: jobEntity.escrowAddress!,
} as any);
mockPaymentService.createRefundPayment.mockResolvedValueOnce(undefined);
Expand Down
Loading
Loading