diff --git a/prisma/migrations/20241104143024_init/migration.sql b/prisma/migrations/20241120103937_init/migration.sql similarity index 95% rename from prisma/migrations/20241104143024_init/migration.sql rename to prisma/migrations/20241120103937_init/migration.sql index ef78c76..fff32a5 100644 --- a/prisma/migrations/20241104143024_init/migration.sql +++ b/prisma/migrations/20241120103937_init/migration.sql @@ -5,11 +5,20 @@ CREATE TABLE "Account" ( "publicKey" TEXT, "name" TEXT, "description" TEXT, + + CONSTRAINT "Account_pkey" PRIMARY KEY ("address") +); + +-- CreateTable +CREATE TABLE "TokenBalance" ( + "id" SERIAL NOT NULL, + "tokenID" TEXT NOT NULL, "totalBalance" BIGINT NOT NULL DEFAULT 0, "availableBalance" BIGINT NOT NULL DEFAULT 0, "lockedBalance" BIGINT NOT NULL DEFAULT 0, + "accountAddress" TEXT NOT NULL, - CONSTRAINT "Account_pkey" PRIMARY KEY ("address") + CONSTRAINT "TokenBalance_pkey" PRIMARY KEY ("id") ); -- CreateTable @@ -306,6 +315,9 @@ CREATE UNIQUE INDEX "Account_address_key" ON "Account"("address"); -- CreateIndex CREATE UNIQUE INDEX "Account_publicKey_key" ON "Account"("publicKey"); +-- CreateIndex +CREATE UNIQUE INDEX "TokenBalance_accountAddress_tokenID_key" ON "TokenBalance"("accountAddress", "tokenID"); + -- CreateIndex CREATE UNIQUE INDEX "App_chainID_key" ON "App"("chainID"); @@ -339,6 +351,9 @@ CREATE UNIQUE INDEX "Token_tokenID_key" ON "Token"("tokenID"); -- CreateIndex CREATE UNIQUE INDEX "TokenLogo_tokenID_key" ON "TokenLogo"("tokenID"); +-- AddForeignKey +ALTER TABLE "TokenBalance" ADD CONSTRAINT "TokenBalance_accountAddress_fkey" FOREIGN KEY ("accountAddress") REFERENCES "Account"("address") ON DELETE RESTRICT ON UPDATE CASCADE; + -- AddForeignKey ALTER TABLE "ServiceURL" ADD CONSTRAINT "ServiceURL_appChainID_fkey" FOREIGN KEY ("appChainID") REFERENCES "App"("chainID") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/schema/account.prisma b/prisma/schema/account.prisma index 08c8011..a8b2882 100644 --- a/prisma/schema/account.prisma +++ b/prisma/schema/account.prisma @@ -4,12 +4,22 @@ model Account { publicKey String? @unique name String? description String? - totalBalance BigInt @default(0) - availableBalance BigInt @default(0) - lockedBalance BigInt @default(0) validator Validator? + tokenBalances TokenBalance[] block Block[] stakes Stake[] transactionSender Transaction[] @relation("sender") transactionRecipient Transaction[] @relation("recipient") } + +model TokenBalance { + id Int @id @default(autoincrement()) + tokenID String + totalBalance BigInt @default(0) + availableBalance BigInt @default(0) + lockedBalance BigInt @default(0) + accountAddress String + account Account @relation(fields: [accountAddress], references: [address]) + + @@unique([accountAddress, tokenID]) +} \ No newline at end of file diff --git a/src/modules/account/account.controller.ts b/src/modules/account/account.controller.ts index 324202f..3a1a6cc 100644 --- a/src/modules/account/account.controller.ts +++ b/src/modules/account/account.controller.ts @@ -1,7 +1,7 @@ import { Controller, Get, Query, UsePipes, ValidationPipe } from '@nestjs/common'; import { ApiResponse, ApiTags } from '@nestjs/swagger'; import { PrismaService } from '../prisma/prisma.service'; -import { getAccountsRes } from './dto/get-accounts-res.dto'; +import { getAccountsRes, GetAccountsResDto } from './dto/get-accounts-res.dto'; import { GatewayResponse } from 'src/utils/controller-helpers'; import { GetAccountsDto } from './dto/get-accounts.dto'; import { Prisma } from '@prisma/client'; @@ -15,9 +15,8 @@ export class AccountController { @Get() @UsePipes(new ValidationPipe({ transform: true, whitelist: true, forbidNonWhitelisted: true })) @ApiResponse(getAccountsRes) - async getAccounts(@Query() query: GetAccountsDto): Promise> { - const { address, name, publicKey, offset, sort, limit } = query; - const [field, direction] = sort.split(':'); + async getAccounts(@Query() query: GetAccountsDto): Promise { + const { address, name, publicKey, offset, limit } = query; const take = Math.min(limit, MAX_ACCOUNTS_TO_FETCH); const where: Prisma.AccountWhereInput = { @@ -29,20 +28,34 @@ export class AccountController { const accounts = await this.prisma.account.findMany({ where, take, - orderBy: { - [field]: direction, + include: { + tokenBalances: true, }, skip: offset, }); const total = await this.prisma.account.count({ where }); - const response = accounts.map((account) => ({ - ...account, - availableBalance: account.availableBalance.toString(), - lockedBalance: account.lockedBalance.toString(), - totalBalance: account.totalBalance.toString(), - })); + // ? can this be done in a better way? + const response = accounts.map((account) => { + const tokenBalances = account.tokenBalances.reduce((acc, balance) => { + const tokenID = balance.tokenID; + if (!acc[tokenID]) { + acc[tokenID] = []; + } + acc[tokenID].push({ + availableBalance: balance.availableBalance.toString(), + lockedBalance: balance.lockedBalance.toString(), + totalBalance: balance.totalBalance.toString(), + }); + return acc; + }, {}); + + return { + ...account, + tokenBalances, + }; + }); return new GatewayResponse(response, { count: accounts.length, offset, total }); } diff --git a/src/modules/account/dto/get-accounts-res.dto.ts b/src/modules/account/dto/get-accounts-res.dto.ts index 1d57751..f2da833 100644 --- a/src/modules/account/dto/get-accounts-res.dto.ts +++ b/src/modules/account/dto/get-accounts-res.dto.ts @@ -1,13 +1,17 @@ import { ApiResponseOptions } from '@nestjs/swagger'; +class TokenBalance { + availableBalance: string; + lockedBalance: string; + totalBalance: string; +} export class GetAccountsData { address: string; publicKey: string; name: string; nonce: string; - totalBalance: string; - availableBalance: string; - lockedBalance: string; + description?: string; + tokenBalances: Record; } export class GetAccountMeta { diff --git a/src/modules/account/dto/get-accounts.dto.ts b/src/modules/account/dto/get-accounts.dto.ts index cc97815..91095b2 100644 --- a/src/modules/account/dto/get-accounts.dto.ts +++ b/src/modules/account/dto/get-accounts.dto.ts @@ -25,16 +25,6 @@ export class GetAccountsDto { @IsOptional() publicKey?: string; - /** - * Sort accounts. - */ - @IsString() - @IsEnum(AccountSortTypes, { - message: - 'sort must be one of the following values: ' + Object.values(AccountSortTypes).join(', '), - }) - sort?: AccountSortTypes = AccountSortTypes.TOTAL_BALANCE_DESC; - /** * Limit the number of accounts fetched. */ diff --git a/src/modules/indexer/event/commands/update-account.command.ts b/src/modules/indexer/event/commands/update-account.command.ts index 89bbe50..1ad43a6 100644 --- a/src/modules/indexer/event/commands/update-account.command.ts +++ b/src/modules/indexer/event/commands/update-account.command.ts @@ -23,6 +23,7 @@ export class UpdateAccountHandler implements ICommandHandler { const { address, tokenID } = command; + console.log({ address }); const accountInfo = await this.nodeApi.invokeApi(NodeApi.TOKEN_GET_BALANCE, { address, @@ -40,9 +41,21 @@ export class UpdateAccountHandler implements ICommandHandler