From f067117795f1036574f34b207aa30006bdd9acb2 Mon Sep 17 00:00:00 2001 From: ahonn Date: Wed, 11 Sep 2024 20:43:32 +1000 Subject: [PATCH 1/3] feat: add rgbpp address balances field --- .../modules/rgbpp/address/address.module.ts | 3 +- .../modules/rgbpp/address/address.resolver.ts | 21 +++++------- .../modules/rgbpp/address/address.service.ts | 34 +++++++++++++++++++ 3 files changed, 44 insertions(+), 14 deletions(-) create mode 100644 backend/src/modules/rgbpp/address/address.service.ts diff --git a/backend/src/modules/rgbpp/address/address.module.ts b/backend/src/modules/rgbpp/address/address.module.ts index 49e3d1d0..a0ffb5f2 100644 --- a/backend/src/modules/rgbpp/address/address.module.ts +++ b/backend/src/modules/rgbpp/address/address.module.ts @@ -6,6 +6,7 @@ import { CkbRpcModule } from 'src/core/ckb-rpc/ckb-rpc.module'; import { RgbppModule } from '../rgbpp.module'; import { CkbTransactionModule } from 'src/modules/ckb/transaction/transaction.module'; import { BullModule } from '@nestjs/bullmq'; +import { RgbppAddressService } from './address.service'; @Module({ imports: [ @@ -22,6 +23,6 @@ import { BullModule } from '@nestjs/bullmq'; }, }), ], - providers: [RgbppAddressResolver], + providers: [RgbppAddressResolver, RgbppAddressService], }) export class RgbppAddressModule {} diff --git a/backend/src/modules/rgbpp/address/address.resolver.ts b/backend/src/modules/rgbpp/address/address.resolver.ts index 45e36b01..eb163186 100644 --- a/backend/src/modules/rgbpp/address/address.resolver.ts +++ b/backend/src/modules/rgbpp/address/address.resolver.ts @@ -4,15 +4,14 @@ import { RgbppAddress } from './address.model'; import { ParentField } from 'src/decorators/parent-field.decorator'; import { RgbppAsset } from '../asset/asset.model'; import { CkbXUDTInfo } from 'src/modules/ckb/cell/cell.model'; -import { Loader } from 'src/common/dataloader'; -import { - CkbExplorerTransactionLoader, - CkbExplorerTransactionLoaderType, -} from 'src/modules/ckb/transaction/transaction.dataloader'; +import { RgbppAddressService } from './address.service'; @Resolver(() => RgbppAddress) export class RgbppAddressResolver { - constructor(private ckbExplorerService: CkbExplorerService) { } + constructor( + private ckbExplorerService: CkbExplorerService, + private rgbppAddressService: RgbppAddressService, + ) {} @Query(() => RgbppAddress, { name: 'rgbppAddress', nullable: true }) public async getBtcAddress(@Args('address') address: string): Promise { @@ -35,16 +34,12 @@ export class RgbppAddressResolver { @ResolveField(() => [RgbppAsset]) public async assets(@ParentField('address') address: string): Promise { - // TODO: implement this return []; } @ResolveField(() => [CkbXUDTInfo]) - public async balances( - @ParentField('address') address: string, - @Loader(CkbExplorerTransactionLoader) explorerTxLoader: CkbExplorerTransactionLoaderType, - ): Promise<(CkbXUDTInfo | null)[]> { - // TODO: implement this - return []; + public async balances(@ParentField('address') address: string): Promise<(CkbXUDTInfo | null)[]> { + const balances = await this.rgbppAddressService.getAddressBalances(address); + return balances; } } diff --git a/backend/src/modules/rgbpp/address/address.service.ts b/backend/src/modules/rgbpp/address/address.service.ts new file mode 100644 index 00000000..f35f29ec --- /dev/null +++ b/backend/src/modules/rgbpp/address/address.service.ts @@ -0,0 +1,34 @@ +import { BI } from '@ckb-lumos/bi'; +import { Injectable } from '@nestjs/common'; +import { CkbExplorerService } from 'src/core/ckb-explorer/ckb-explorer.service'; +import { PrismaService } from 'src/core/database/prisma/prisma.service'; +import { CkbXUDTInfo } from 'src/modules/ckb/cell/cell.model'; + +@Injectable() +export class RgbppAddressService { + constructor( + private prismaService: PrismaService, + private ckbExplorerService: CkbExplorerService, + ) { } + + public async getAddressBalances(address: string): Promise { + const result = await this.prismaService.holder.findMany({ + where: { + address, + }, + }); + const balances = await Promise.all( + result.map(async ({ typeScriptHash, assetAmount }) => { + const info = await this.ckbExplorerService.getXUDT(typeScriptHash); + const xudtInfo: CkbXUDTInfo = { + symbol: info.data.attributes.symbol, + decimal: BI.from(info.data.attributes.decimal).toNumber(), + typeHash: typeScriptHash, + amount: assetAmount.toHex(), + }; + return xudtInfo; + }), + ); + return balances; + } +} From 757fbc61f11f1d22395a7d8b07ba0f9fb291bf03 Mon Sep 17 00:00:00 2001 From: ahonn Date: Thu, 12 Sep 2024 17:27:52 +1000 Subject: [PATCH 2/3] feat: add rgbpp address balances/assets field resolver --- .../modules/rgbpp/address/address.resolver.ts | 9 +--- .../modules/rgbpp/address/address.service.ts | 48 ++++++++++++++++++- .../src/modules/rgbpp/asset/asset.model.ts | 13 ++++- backend/src/schema.gql | 1 - 4 files changed, 61 insertions(+), 10 deletions(-) diff --git a/backend/src/modules/rgbpp/address/address.resolver.ts b/backend/src/modules/rgbpp/address/address.resolver.ts index eb163186..b96263b0 100644 --- a/backend/src/modules/rgbpp/address/address.resolver.ts +++ b/backend/src/modules/rgbpp/address/address.resolver.ts @@ -26,15 +26,10 @@ export class RgbppAddressResolver { return cells.meta.total; } - @ResolveField(() => Float) - public async cellsCount(@ParentField('address') address: string): Promise { - // TODO: implement this - return 0; - } - @ResolveField(() => [RgbppAsset]) public async assets(@ParentField('address') address: string): Promise { - return []; + const assets = await this.rgbppAddressService.getAddressAssets(address); + return assets; } @ResolveField(() => [CkbXUDTInfo]) diff --git a/backend/src/modules/rgbpp/address/address.service.ts b/backend/src/modules/rgbpp/address/address.service.ts index f35f29ec..448c247e 100644 --- a/backend/src/modules/rgbpp/address/address.service.ts +++ b/backend/src/modules/rgbpp/address/address.service.ts @@ -2,20 +2,66 @@ import { BI } from '@ckb-lumos/bi'; import { Injectable } from '@nestjs/common'; import { CkbExplorerService } from 'src/core/ckb-explorer/ckb-explorer.service'; import { PrismaService } from 'src/core/database/prisma/prisma.service'; -import { CkbXUDTInfo } from 'src/modules/ckb/cell/cell.model'; +import { CkbCell, CkbXUDTInfo } from 'src/modules/ckb/cell/cell.model'; +import { RgbppAsset } from '../asset/asset.model'; +import { CkbRpcWebsocketService } from 'src/core/ckb-rpc/ckb-rpc-websocket.service'; @Injectable() export class RgbppAddressService { constructor( private prismaService: PrismaService, private ckbExplorerService: CkbExplorerService, + private ckbRpcService: CkbRpcWebsocketService, ) { } + public async getAddressAssets(address: string) { + const lockScript = await this.prismaService.lockScript.findMany({ + where: { + ownerAddress: address, + }, + }); + if (lockScript.length === 0) { + return []; + } + const results = await this.prismaService.asset.findMany({ + where: { + lockScriptHash: { + in: lockScript.map((script) => script.scriptHash), + }, + }, + }); + if (results.length === 0) { + return []; + } + const outputMap = results.reduce( + (map, { txHash, index }) => { + if (map[txHash] === undefined) { + map[txHash] = []; + } + map[txHash].push(index); + return map; + }, + {} as Record, + ); + const assets = await Promise.all( + Object.keys(outputMap).map(async (txHash) => { + const tx = await this.ckbRpcService.getTransaction(txHash); + return outputMap[txHash].map((index) => + RgbppAsset.fromTransaction(address, tx.transaction, BI.from(index).toNumber()), + ); + }), + ); + return assets.flat(); + } + public async getAddressBalances(address: string): Promise { const result = await this.prismaService.holder.findMany({ where: { address, }, + orderBy: { + assetAmount: 'desc', + }, }); const balances = await Promise.all( result.map(async ({ typeScriptHash, assetAmount }) => { diff --git a/backend/src/modules/rgbpp/asset/asset.model.ts b/backend/src/modules/rgbpp/asset/asset.model.ts index a0654f5b..93386736 100644 --- a/backend/src/modules/rgbpp/asset/asset.model.ts +++ b/backend/src/modules/rgbpp/asset/asset.model.ts @@ -10,10 +10,21 @@ export class RgbppAsset { @Field(() => CkbCell) cell: CkbCell; - public static from(address: string, cell: CkbRpc.Cell): RgbppAsset { + public static fromCell(address: string, cell: CkbRpc.Cell): RgbppAsset { return { owner: address, cell: CkbCell.fromCell(cell), }; } + + public static fromTransaction( + address: string, + tx: CkbRpc.Transaction, + index: number, + ): RgbppAsset { + return { + owner: address, + cell: CkbCell.fromTransaction(tx, index), + }; + } } diff --git a/backend/src/schema.gql b/backend/src/schema.gql index d5ba5043..0d854527 100644 --- a/backend/src/schema.gql +++ b/backend/src/schema.gql @@ -252,7 +252,6 @@ type BitcoinTransaction { type RgbppAddress { address: String! utxosCount: Float! - cellsCount: Float! assets: [RgbppAsset!]! balances: [CkbXUDTInfo!]! } From b6cae538219ad9a1572e163646e7e0f59112e372 Mon Sep 17 00:00:00 2001 From: ahonn Date: Fri, 13 Sep 2024 17:19:14 +1000 Subject: [PATCH 3/3] fix: fix rgbpp address service prisma query by add missing where fields --- backend/src/modules/rgbpp/address/address.service.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backend/src/modules/rgbpp/address/address.service.ts b/backend/src/modules/rgbpp/address/address.service.ts index 448c247e..25198916 100644 --- a/backend/src/modules/rgbpp/address/address.service.ts +++ b/backend/src/modules/rgbpp/address/address.service.ts @@ -5,6 +5,7 @@ import { PrismaService } from 'src/core/database/prisma/prisma.service'; import { CkbCell, CkbXUDTInfo } from 'src/modules/ckb/cell/cell.model'; import { RgbppAsset } from '../asset/asset.model'; import { CkbRpcWebsocketService } from 'src/core/ckb-rpc/ckb-rpc-websocket.service'; +import { CKB_CHAIN_ID } from 'src/constants'; @Injectable() export class RgbppAddressService { @@ -18,6 +19,7 @@ export class RgbppAddressService { const lockScript = await this.prismaService.lockScript.findMany({ where: { ownerAddress: address, + chainId: CKB_CHAIN_ID, }, }); if (lockScript.length === 0) { @@ -28,6 +30,7 @@ export class RgbppAddressService { lockScriptHash: { in: lockScript.map((script) => script.scriptHash), }, + isLive: true, }, }); if (results.length === 0) {