Skip to content

Commit

Permalink
fix: add pagination metadata on positions endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
xsteadybcgo committed Jun 14, 2024
1 parent bfd84cf commit 98a810b
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 45 deletions.
18 changes: 18 additions & 0 deletions src/common/pagination.util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,22 @@ export class PaginationUtil {
items: paginatedItems,
} as PagingDto;
}

static genPaginateMetaByTotalCount(
total: number,
page: number,
limit: number,
): PagingMetaDto {
const itemCount =
page * limit > total ? Math.max(total - (page - 1) * limit, 0) : limit;
const pagingMeta = {
currentPage: Number(page),
itemCount: itemCount,
itemsPerPage: Number(limit),
totalItems: total,
totalPages: Math.ceil(total / limit),
};

return pagingMeta;
}
}
9 changes: 5 additions & 4 deletions src/positions/positions.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,20 +31,21 @@ export class PositionsController {
@ApiNotFoundResponse({
description: '{ "errno": 1, "errmsg": "not found" }',
})
async getUserPositionsByProjectAndTokens(
async getProjectPositions(
@Param("projectName") projectName: string,
@Query() queryParams: GetUserPositionsDto,
): Promise<UserPositionsResponseDto> {
const balances =
await this.positionsService.getUserPositionsByProjectAndTokens({
const { data, meta } =
await this.positionsService.getPositionsByProjectAndAddress({
projectName,
...queryParams,
});

return {
errmsg: "no error",
errno: 0,
data: balances,
meta: meta,
data: data,
};
}

Expand Down
8 changes: 8 additions & 0 deletions src/positions/positions.dto.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ApiProperty } from "@nestjs/swagger";
import { Type } from "class-transformer";
import { IsInt, IsOptional, Min } from "class-validator";
import { PagingMetaDto } from "src/common/paging.dto";
import { PagingOptionsDto } from "src/common/pagingOptionsDto.dto";

export class GetAGXPositionDto {
Expand Down Expand Up @@ -74,6 +75,13 @@ export class UserPositionsResponseDto {
})
public readonly errmsg: string;

@ApiProperty({
type: PagingMetaDto,
description: "page meta",
example: 0,
})
public readonly meta?: PagingMetaDto;

@ApiProperty({
type: UserPositionsDto,
description: "user position list",
Expand Down
30 changes: 14 additions & 16 deletions src/positions/positions.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import { Injectable } from "@nestjs/common";
import { BalanceOfLpRepository } from "src/repositories/balanceOfLp.repository";
import { GetUserPositionsDto } from "./positions.dto";
import { ethers } from "ethers";
import { PaginationUtil } from "src/common/pagination.util";

@Injectable()
export class PositionsService {
constructor(private balanceOfRepository: BalanceOfLpRepository) {}

async getUserPositionsByProjectAndTokens(
async getPositionsByProjectAndAddress(
params: GetUserPositionsDto & { projectName: string },
) {
const { tokenAddresses, limit = 100, page = 1 } = params;
Expand All @@ -17,29 +18,26 @@ export class PositionsService {
limit,
page,
};
const data =
await this.balanceOfRepository.getUserPositionsByProjectAndTokens(
const { list, totalCount } =
await this.balanceOfRepository.getProjectPositionsByAddress(
formattedParams,
);
return data;
const meta = PaginationUtil.genPaginateMetaByTotalCount(
totalCount,
page,
limit,
);
return { data: list, meta: meta };
}

async getAgxEtherfiPositionsByBlock(blockNumber: number) {
const tokenAddresses = [
"0x35D5f1b41319e0ebb5a10e55C3BD23f121072da8",
"0xE227155217513f1ACaA2849A872ab933cF2d6a9A",
];

let result: Array<{ address: string; effective_balance: number }> = [];

const balances =
await this.balanceOfRepository.getUserPositionsByProjectAndTokens({
projectName: "agx",
tokenAddresses,
blockNumber: blockNumber,
});
const data = await this.balanceOfRepository.getAgxEtherfiPositions({
blockNumber,
});

result = balances.map((i) => ({
result = data.map((i) => ({
address: i.userAddress,
effective_balance: Number(ethers.formatUnits(i.balance)),
}));
Expand Down
97 changes: 72 additions & 25 deletions src/repositories/balanceOfLp.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,17 @@ import { UnitOfWork } from "../unitOfWork";
import { BaseRepository } from "./base.repository";
import { BalanceOfLp } from "../entities/balanceOfLp.entity";
import { ProjectRepository } from "./project.repository";
import { GetUserPositionsDto } from "src/positions/positions.dto";
import {
GetAGXPositionDto,
GetUserPositionsDto,
} from "src/positions/positions.dto";

export interface Position {
userAddress: string;
tokenAddress: string;
balance: string;
}

@Injectable()
export class BalanceOfLpRepository extends BaseRepository<BalanceOfLp> {
public constructor(
Expand Down Expand Up @@ -76,7 +86,7 @@ export class BalanceOfLpRepository extends BaseRepository<BalanceOfLp> {
});
}

async getClosestBlockNumber(
private async getClosestBlockNumber(
blockNumber: number,
pairAddressBuffers: Buffer[],
) {
Expand All @@ -91,7 +101,7 @@ export class BalanceOfLpRepository extends BaseRepository<BalanceOfLp> {
})
.getRawOne();

result = latestBlock ? Number(latestBlock.max) : undefined;
result = latestBlock ? Number(latestBlock.max) : 0;
} else {
const closestBlock = await entityManager
.createQueryBuilder(BalanceOfLp, "b")
Expand All @@ -103,28 +113,16 @@ export class BalanceOfLpRepository extends BaseRepository<BalanceOfLp> {
.orderBy("b.blockNumber", "DESC")
.getOne();

result = closestBlock?.blockNumber;
result = closestBlock?.blockNumber ?? 0;
}
return result;
}

async getUserPositionsByProjectAndTokens({
projectName,
tokenAddresses,
page = 1,
limit = 10,
blockNumber,
userAddress,
}: Omit<GetUserPositionsDto, "tokenAddresses"> & {
projectName: string;
tokenAddresses: string[];
}): Promise<
{
userAddress: string;
tokenAddress: string;
balance: string;
}[]
> {
private async genPositionsQueryBuilder(
projectName: string,
blockNumber: number,
tokenAddresses: string[],
) {
const pairAddressBuffers =
await this.projectRepository.getPairAddresses(projectName);

Expand All @@ -135,10 +133,6 @@ export class BalanceOfLpRepository extends BaseRepository<BalanceOfLp> {
pairAddressBuffers,
);

if (!closestBlockNumber) {
return [];
}

let queryBuilder = entityManager
.createQueryBuilder(BalanceOfLp, "b")
.select([
Expand All @@ -164,6 +158,30 @@ export class BalanceOfLpRepository extends BaseRepository<BalanceOfLp> {
{ tokenAddressList: tokenAddressBuffers },
);
}
return queryBuilder;
}

public async getProjectPositionsByAddress({
projectName,
tokenAddresses,
page,
limit,
blockNumber,
userAddress,
}: Omit<GetUserPositionsDto, "tokenAddresses"> & {
projectName: string;
tokenAddresses: string[];
}): Promise<{
totalCount: number;
list: Position[];
}> {
let queryBuilder = await this.genPositionsQueryBuilder(
projectName,
blockNumber,
tokenAddresses,
);

const total = await queryBuilder.getCount();

if (limit) {
if (page) {
Expand All @@ -187,6 +205,35 @@ export class BalanceOfLpRepository extends BaseRepository<BalanceOfLp> {
balance: string;
}>();

return {
totalCount: total,
list: balances.map((item) => ({
...item,
userAddress: "0x" + item.userAddress.toString("hex"),
tokenAddress: "0x" + item.tokenAddress.toString("hex"),
})),
};
}

public async getAgxEtherfiPositions({
blockNumber,
}: GetAGXPositionDto): Promise<Position[]> {
const tokenAddresses = [
"0x35D5f1b41319e0ebb5a10e55C3BD23f121072da8",
"0xE227155217513f1ACaA2849A872ab933cF2d6a9A",
];
const queryBuilder = await this.genPositionsQueryBuilder(
"agx",
blockNumber,
tokenAddresses,
);

const balances = await queryBuilder.getRawMany<{
userAddress: Buffer;
tokenAddress: Buffer;
balance: string;
}>();

return balances.map((item) => ({
...item,
userAddress: "0x" + item.userAddress.toString("hex"),
Expand Down

0 comments on commit 98a810b

Please sign in to comment.