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..b96263b0 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 { @@ -27,24 +26,15 @@ 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 { - // TODO: implement this - return []; + const assets = await this.rgbppAddressService.getAddressAssets(address); + return assets; } @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..25198916 --- /dev/null +++ b/backend/src/modules/rgbpp/address/address.service.ts @@ -0,0 +1,83 @@ +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 { 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 { + 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, + chainId: CKB_CHAIN_ID, + }, + }); + if (lockScript.length === 0) { + return []; + } + const results = await this.prismaService.asset.findMany({ + where: { + lockScriptHash: { + in: lockScript.map((script) => script.scriptHash), + }, + isLive: true, + }, + }); + 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 }) => { + 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; + } +} 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!]! }