Skip to content

Commit

Permalink
feat: implemented validator v2
Browse files Browse the repository at this point in the history
  • Loading branch information
tmrdlt committed Apr 18, 2024
1 parent 4a4db5d commit d51fcd4
Show file tree
Hide file tree
Showing 8 changed files with 195 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,29 @@ export class PairLiquidityInfoHistoryV2DbService {
],
});
}

getWithinHeightSorted(heightLimit: number) {
return this.prisma.pairLiquidityInfoHistoryV2.findMany({
where: {
height: {
gte: heightLimit,
},
},
orderBy: [
{ microBlockTime: 'asc' },
{ transactionIndex: 'asc' },
{ logIndex: 'asc' },
],
});
}

deleteFromMicroBlockTime(microBlockTime: bigint) {
return this.prisma.pairLiquidityInfoHistoryV2.deleteMany({
where: {
microBlockTime: {
gte: microBlockTime,
},
},
});
}
}
7 changes: 7 additions & 0 deletions src/database/pair/pair-db.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ export class PairDbService {
});
}

get(pairId: number): Promise<PairWithTokens | null> {
return this.prisma.pair.findUnique({
where: { id: pairId },
include: { token0: true, token1: true },
});
}

getOne(address: string) {
return this.prisma.pair.findUnique({
where: { address },
Expand Down
2 changes: 1 addition & 1 deletion src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,4 @@ export const bigIntToDecimal = (bigInt: bigint): Decimal =>
new Decimal(bigInt.toString());

export const decimalToBigInt = (decimal: Decimal): bigint =>
BigInt(decimal.toString());
BigInt(decimal.toFixed().toString());
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { Injectable, Logger } from '@nestjs/common';
import { groupBy, last, map } from 'lodash';

import { MdwHttpClientService } from '@/clients/mdw-http-client.service';
import {
ContractAddress,
contractAddrToAccountAddr,
} from '@/clients/sdk-client.model';
import { SdkClientService } from '@/clients/sdk-client.service';
import { PairDbService } from '@/database/pair/pair-db.service';
import { PairLiquidityInfoHistoryV2DbService } from '@/database/pair-liquidity-info-history/pair-liquidity-info-history-v2-db.service';
import { decimalToBigInt } from '@/lib/utils';

@Injectable()
export class PairLiquidityInfoHistoryValidatorV2Service {
constructor(
private pairLiquidityInfoHistoryDb: PairLiquidityInfoHistoryV2DbService,
private pairDb: PairDbService,
private mdwClient: MdwHttpClientService,
private sdkClient: SdkClientService,
) {}

readonly logger = new Logger(PairLiquidityInfoHistoryValidatorV2Service.name);

readonly VALIDATION_WINDOW_BLOCKS = 20;

async validate() {
this.logger.log(`Started validating pair liquidity info history.`);

// Get current height
const currentHeight = await this.sdkClient.getHeight();

// Get all liquidity entries greater or equal the current height minus VALIDATION_WINDOW_BLOCKS
// and take the last entry of every microBlock to get the final reserve in that microBlock
const liquidityEntriesWithinHeightSorted = map(
groupBy(
await this.pairLiquidityInfoHistoryDb.getWithinHeightSorted(
currentHeight - this.VALIDATION_WINDOW_BLOCKS,
),
'microBlockHash',
),
(group) => last(group)!,
);

// If the reserves of a local microBlock do not match with the data from the middleware or the block does not exist,
// delete this block and all newer entries
let numDeleted = 0;
for (const liquidityEntry of liquidityEntriesWithinHeightSorted) {
let isError = false;
let mdwReserve0: bigint | undefined;
let mdwReserve1: bigint | undefined;

const pairWithTokens = (await this.pairDb.get(liquidityEntry?.pairId))!;

try {
// reserve0 is the balance of the pair contract's account of token0
mdwReserve0 = BigInt(
(
await this.mdwClient.getAccountBalanceForContractAtMicroBlockHash(
pairWithTokens.token0.address as ContractAddress,
contractAddrToAccountAddr(
pairWithTokens.address as ContractAddress,
),
liquidityEntry.microBlockHash,
)
).amount,
);

// reserve1 is the balance of the pair contract's account of token1
mdwReserve1 = BigInt(
(
await this.mdwClient.getAccountBalanceForContractAtMicroBlockHash(
pairWithTokens.token1.address as ContractAddress,
contractAddrToAccountAddr(
pairWithTokens.address as ContractAddress,
),
liquidityEntry.microBlockHash,
)
).amount,
);
} catch (e) {
this.logger.error(e);
isError = true;
}
if (
isError ||
decimalToBigInt(liquidityEntry.reserve0) !== mdwReserve0 ||
decimalToBigInt(liquidityEntry.reserve1) !== mdwReserve1
) {
numDeleted = (
await this.pairLiquidityInfoHistoryDb.deleteFromMicroBlockTime(
liquidityEntry.microBlockTime,
)
).count;
break;
}
}

if (numDeleted > 0) {
this.logger.log(
`Found an inconsistency in pair liquidity info history. Deleted ${numDeleted} entries.`,
);
} else {
this.logger.log('No problems in pair liquidity info history found.');
}

this.logger.log('Finished validating pair liquidity info history.');
}
}
2 changes: 2 additions & 0 deletions src/tasks/tasks.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { DatabaseModule } from '@/database/database.module';
import { PairLiquidityInfoHistoryImporterService } from '@/tasks/pair-liquidity-info-history-importer/pair-liquidity-info-history-importer.service';
import { PairLiquidityInfoHistoryImporterV2Service } from '@/tasks/pair-liquidity-info-history-importer/pair-liquidity-info-history-importer-v2.service';
import { PairLiquidityInfoHistoryValidatorService } from '@/tasks/pair-liquidity-info-history-validator/pair-liquidity-info-history-validator.service';
import { PairLiquidityInfoHistoryValidatorV2Service } from '@/tasks/pair-liquidity-info-history-validator/pair-liquidity-info-history-validator-v2.service';
import { PairSyncService } from '@/tasks/pair-sync/pair-sync.service';
import { TasksService } from '@/tasks/tasks.service';

Expand All @@ -15,6 +16,7 @@ import { TasksService } from '@/tasks/tasks.service';
PairLiquidityInfoHistoryImporterService,
PairLiquidityInfoHistoryImporterV2Service,
PairLiquidityInfoHistoryValidatorService,
PairLiquidityInfoHistoryValidatorV2Service,
TasksService,
PairSyncService,
],
Expand Down
14 changes: 8 additions & 6 deletions src/tasks/tasks.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@ import { PairLiquidityInfoHistoryImporterService } from '@/tasks/pair-liquidity-
import { PairLiquidityInfoHistoryImporterV2Service } from '@/tasks/pair-liquidity-info-history-importer/pair-liquidity-info-history-importer-v2.service';
import { PairLiquidityInfoHistoryValidatorService } from '@/tasks/pair-liquidity-info-history-validator/pair-liquidity-info-history-validator.service';
import { TasksService } from '@/tasks/tasks.service';
import { PairLiquidityInfoHistoryValidatorV2Service } from '@/tasks/pair-liquidity-info-history-validator/pair-liquidity-info-history-validator-v2.service';

describe('TasksService', () => {
let tasksService: TasksService;
let pairLiquidityInfoHistoryImporterService: PairLiquidityInfoHistoryImporterV2Service;
let pairLiquidityInfoHistoryValidatorService: PairLiquidityInfoHistoryValidatorService;
let pairLiquidityInfoHistoryValidatorService: PairLiquidityInfoHistoryValidatorV2Service;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
Expand All @@ -25,6 +26,7 @@ describe('TasksService', () => {
PairLiquidityInfoHistoryImporterService,
PairLiquidityInfoHistoryImporterV2Service,
PairLiquidityInfoHistoryValidatorService,
PairLiquidityInfoHistoryValidatorV2Service,
MdwHttpClientService,
SdkClientService,
PairDbService,
Expand All @@ -42,8 +44,8 @@ describe('TasksService', () => {
PairLiquidityInfoHistoryImporterV2Service,
);
pairLiquidityInfoHistoryValidatorService =
module.get<PairLiquidityInfoHistoryValidatorService>(
PairLiquidityInfoHistoryValidatorService,
module.get<PairLiquidityInfoHistoryValidatorV2Service>(
PairLiquidityInfoHistoryValidatorV2Service,
);
});

Expand Down Expand Up @@ -88,7 +90,7 @@ describe('TasksService', () => {
.spyOn(pairLiquidityInfoHistoryValidatorService, 'validate')
.mockResolvedValue();

await tasksService.runPairLiquidityInfoHistoryValidator();
await tasksService.runPairLiquidityInfoHistoryValidatorV2();
expect(
pairLiquidityInfoHistoryValidatorService.validate,
).toHaveBeenCalled();
Expand All @@ -99,7 +101,7 @@ describe('TasksService', () => {

jest.spyOn(pairLiquidityInfoHistoryValidatorService, 'validate');

await tasksService.runPairLiquidityInfoHistoryValidator();
await tasksService.runPairLiquidityInfoHistoryValidatorV2();
expect(
pairLiquidityInfoHistoryValidatorService.validate,
).not.toHaveBeenCalled();
Expand All @@ -112,7 +114,7 @@ describe('TasksService', () => {
.mockRejectedValue(error);
jest.spyOn(pairLiquidityInfoHistoryValidatorService.logger, 'error');

await tasksService.runPairLiquidityInfoHistoryValidator();
await tasksService.runPairLiquidityInfoHistoryValidatorV2();
expect(
pairLiquidityInfoHistoryValidatorService.logger.error,
).toHaveBeenCalledWith(`Validation failed. ${error}`);
Expand Down
24 changes: 21 additions & 3 deletions src/tasks/tasks.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Cron, CronExpression } from '@nestjs/schedule';
import { PairLiquidityInfoHistoryImporterService } from '@/tasks/pair-liquidity-info-history-importer/pair-liquidity-info-history-importer.service';
import { PairLiquidityInfoHistoryImporterV2Service } from '@/tasks/pair-liquidity-info-history-importer/pair-liquidity-info-history-importer-v2.service';
import { PairLiquidityInfoHistoryValidatorService } from '@/tasks/pair-liquidity-info-history-validator/pair-liquidity-info-history-validator.service';
import { PairLiquidityInfoHistoryValidatorV2Service } from '@/tasks/pair-liquidity-info-history-validator/pair-liquidity-info-history-validator-v2.service';

const EVERY_5_MINUTES_STARTING_AT_02_30 = '30 2-57/5 * * * *';

Expand All @@ -13,6 +14,7 @@ export class TasksService {
private pairLiquidityInfoHistoryImporterService: PairLiquidityInfoHistoryImporterService,
private pairLiquidityInfoHistoryImporterV2Service: PairLiquidityInfoHistoryImporterV2Service,
private pairLiquidityInfoHistoryValidatorService: PairLiquidityInfoHistoryValidatorService,
private pairLiquidityInfoHistoryValidatorV2Service: PairLiquidityInfoHistoryValidatorV2Service,
) {}

private _isRunning = false;
Expand Down Expand Up @@ -57,16 +59,32 @@ export class TasksService {
}
}

// @Cron(EVERY_5_MINUTES_STARTING_AT_02_30)
// async runPairLiquidityInfoHistoryValidator() {
// try {
// if (!this.isRunning) {
// this.setIsRunning(true);
// await this.pairLiquidityInfoHistoryValidatorService.validate();
// this.setIsRunning(false);
// }
// } catch (error) {
// this.pairLiquidityInfoHistoryValidatorService.logger.error(
// `Validation failed. ${error}`,
// );
// this.setIsRunning(false);
// }
// }

@Cron(EVERY_5_MINUTES_STARTING_AT_02_30)
async runPairLiquidityInfoHistoryValidator() {
async runPairLiquidityInfoHistoryValidatorV2() {
try {
if (!this.isRunning) {
this.setIsRunning(true);
await this.pairLiquidityInfoHistoryValidatorService.validate();
await this.pairLiquidityInfoHistoryValidatorV2Service.validate();
this.setIsRunning(false);
}
} catch (error) {
this.pairLiquidityInfoHistoryValidatorService.logger.error(
this.pairLiquidityInfoHistoryValidatorV2Service.logger.error(
`Validation failed. ${error}`,
);
this.setIsRunning(false);
Expand Down
22 changes: 22 additions & 0 deletions test/e2e/pair-liquidity-info-history-v2-db.service.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,26 @@ describe('PairLiquidityInfoHistoryV2DbService', () => {
expect(result2?.id).toEqual(historyEntry4.id);
});
});

describe('getWithinHeightSorted', () => {
it('should correctly return all entries greater or equal a given height limit sorted ascending', async () => {
const result = await service.getWithinHeightSorted(200002);
expect(result.map((e) => e.id)).toEqual([
historyEntry2.id,
historyEntry3.id,
historyEntry4.id,
]);
});
});

describe('deleteFromMicroBlockTime', () => {
it('should correctly delete all entries newer or equal a given block time', async () => {
await service.deleteFromMicroBlockTime(3000000000003n);
const result = await prismaService.pairLiquidityInfoHistoryV2.findMany();
expect(result.map((e) => e.id)).toEqual([
historyEntry1.id,
historyEntry2.id,
]);
});
});
});

0 comments on commit d51fcd4

Please sign in to comment.