Skip to content

Commit

Permalink
feat: add rgbpp coin transaction and xudt into
Browse files Browse the repository at this point in the history
  • Loading branch information
ahonn committed Jul 18, 2024
1 parent c7d4cd6 commit a8f374a
Show file tree
Hide file tree
Showing 11 changed files with 132 additions and 23 deletions.
22 changes: 21 additions & 1 deletion backend/src/core/ckb-explorer/ckb-explorer.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,18 @@ export interface DisplayInput {
capacity: string;
occupied_capacity: string;
address_hash: string;
target_block_number: string;
generated_tx_hash: string;
target_block_number?: string;

// XUDT
cell_type: string;
xudt_info: {
symbol: string;
amount: string;
decimal: string;
type_hash: string;
published: boolean;
};
}

export interface DisplayOutput {
Expand All @@ -121,6 +131,16 @@ export interface DisplayOutput {
consumed_tx_hash: string;
generated_tx_hash: string;
cell_index: string;

// XUDT
cell_type: string;
xudt_info: {
symbol: string;
amount: string;
decimal: string;
type_hash: string;
published: boolean;
};
}

export interface Transaction {
Expand Down
29 changes: 29 additions & 0 deletions backend/src/core/ckb-explorer/ckb-explorer.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ type GetXUDTListParams = BasePaginationParams & {
tags?: XUDTTag[];
};

type GetXUDTTransactionsParams = BasePaginationParams & {
txHash?: string;
addressHash?: string;
};

@Injectable()
export class CkbExplorerService {
private logger = new Logger(CkbExplorerService.name);
Expand Down Expand Up @@ -104,6 +109,11 @@ export class CkbExplorerService {
return response.data;
}

public async getTransaction(txHash: string): Promise<NonPaginatedResponse<Transaction>> {
const response = await this.request.get(`/v1/transactions/${txHash}`);
return response.data;
}

public async getXUDTList({
symbol,
tags,
Expand All @@ -127,4 +137,23 @@ export class CkbExplorerService {
const response = await this.request.get(`/v1/xudts/${typeHash}`);
return response.data;
}

public async getXUDTTransactions(
typeHash: string,
{ page = 1, pageSize = 10, txHash, addressHash }: GetXUDTTransactionsParams = {},
): Promise<PaginatedResponse<Transaction>> {
const params = new URLSearchParams();
params.append('page', page.toString());
params.append('page_size', pageSize.toString());
if (txHash) {
params.append('tx_hash', txHash);
}
if (addressHash) {
params.append('address_hash', addressHash);
}
const response = await this.request.get(
`/v1/udt_transactions/${typeHash}?${params.toString()}`,
);
return response.data;
}
}
22 changes: 20 additions & 2 deletions backend/src/modules/ckb/cell/cell.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,22 @@ import { toNumber } from 'lodash';
import * as CkbRpc from 'src/core/ckb-rpc/ckb-rpc.interface';
import { CkbScript } from '../script/script.model';

export type CkbBaseCell = CkbCell;
@ObjectType({ description: 'CKB XUDT Info' })
export class CkbXUDTInfo {
@Field(() => String)
symbol: string;

@Field(() => Float)
amount: number;

@Field(() => Int)
decimal: number;

@Field(() => String)
typeHash: string;
}

export type CkbBaseCell = Omit<CkbCell, 'xudtInfo'>;

@ObjectType({ description: 'CKB Cell' })
export class CkbCell {
Expand All @@ -22,6 +37,9 @@ export class CkbCell {
@Field(() => CkbScript)
lock: CkbScript;

@Field(() => CkbXUDTInfo, { nullable: true })
xudtInfo: CkbXUDTInfo;

public static from(tx: CkbRpc.Transaction, index: number): CkbBaseCell {
const output = tx.outputs[index];
return {
Expand All @@ -30,6 +48,6 @@ export class CkbCell {
capacity: toNumber(output.capacity),
type: output.type ? CkbScript.from(output.type) : null,
lock: CkbScript.from(output.lock),
}
};
}
}
3 changes: 2 additions & 1 deletion backend/src/modules/ckb/cell/cell.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import { Module } from '@nestjs/common';
import { CkbCellService } from './cell.service';
import { CellResolver } from './cell.resolver';
import { CkbTransactionModule } from '../transaction/transaction.module';
import { CkbExplorerModule } from 'src/core/ckb-explorer/ckb-explorer.module';

@Module({
imports: [CkbTransactionModule],
imports: [CkbTransactionModule, CkbExplorerModule],
providers: [CellResolver, CkbCellService],
})
export class CkbCellModule {}
15 changes: 12 additions & 3 deletions backend/src/modules/ckb/cell/cell.resolver.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
import { Resolver } from '@nestjs/graphql';
import { CkbCell } from './cell.model';
import { Parent, ResolveField, Resolver } from '@nestjs/graphql';
import { CkbCell, CkbXUDTInfo } from './cell.model';
import { CkbExplorerService } from 'src/core/ckb-explorer/ckb-explorer.service';

@Resolver(() => CkbCell)
export class CellResolver {}
export class CellResolver {
constructor(private ckbExplorerService: CkbExplorerService) {}

@ResolveField(() => CkbXUDTInfo)
public async xudtInfo(@Parent() cell: CkbCell) {
const tx = await this.ckbExplorerService.getTransaction(cell.txHash);
return tx.data.attributes.display_outputs[cell.index].xudt_info;
}
}
6 changes: 3 additions & 3 deletions backend/src/modules/ckb/transaction/transaction.model.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Field, Float, Int, ObjectType } from '@nestjs/graphql';
import { toNumber } from 'lodash';
import * as CkbRpc from 'src/core/ckb-rpc/ckb-rpc.interface';
import { CkbCell } from '../cell/cell.model';
import { CkbBaseCell, CkbCell } from '../cell/cell.model';
import { CkbBlock } from '../block/block.model';
import { BI } from '@ckb-lumos/bi';

Expand All @@ -28,10 +28,10 @@ export class CkbTransaction {
fee: number;

@Field(() => [CkbCell])
inputs: CkbCell[];
inputs: CkbBaseCell[];

@Field(() => [CkbCell])
outputs: CkbCell[];
outputs: CkbBaseCell[];

@Field(() => CkbBlock)
block: CkbBlock;
Expand Down
11 changes: 6 additions & 5 deletions backend/src/modules/ckb/transaction/transaction.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { CkbTransaction, CkbBaseTransaction } from './transaction.model';
import { CkbBlockLoader, CkbBlockLoaderResponse } from '../block/block.dataloader';
import { Loader } from '@applifting-io/nestjs-dataloader';
import { BaseCkbBlock, CkbBlock } from '../block/block.model';
import { CkbCell } from '../cell/cell.model';
import { CkbBaseCell, CkbCell } from '../cell/cell.model';
import { CkbTransactionLoader, CkbTransactionLoaderResponse } from './transaction.dataloader';
import { BI } from '@ckb-lumos/bi';

Expand All @@ -26,17 +26,18 @@ export class CkbTransactionResolver {
@Parent() transaction: CkbBaseTransaction,
@Loader(CkbTransactionLoader)
transactionLoader: DataLoader<string, CkbTransactionLoaderResponse>,
): Promise<CkbCell[]> {
): Promise<CkbBaseCell[]> {
const tx = await transactionLoader.load(transaction.hash);
const inputs = Promise.all(
tx.transaction.inputs
// Filter out cellbase transaction
.filter((input) => !input.previous_output.tx_hash.endsWith('0'.repeat(64)))
.map(async (_, index) => {
const input = tx.transaction.inputs[index];
.map(async (_, i) => {
const input = tx.transaction.inputs[i];
const previousTx = await transactionLoader.load(input.previous_output.tx_hash);
const index = BI.from(input.previous_output.index).toNumber();
return CkbCell.from(previousTx.transaction, index);
}),
})
);
return inputs;
}
Expand Down
10 changes: 8 additions & 2 deletions backend/src/modules/rgbpp/coin/coin.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import { Field, Float, Int, ObjectType } from "@nestjs/graphql";
import { CkbScript } from "src/modules/ckb/script/script.model";
import * as CkbExplorer from 'src/core/ckb-explorer/ckb-explorer.interface';
import { toNumber } from "lodash";
import { RgbppTransaction } from "../transaction/transaction.model";

export type RgbppBaseCoin = Omit<RgbppCoin, 'transactions'>;

@ObjectType({ description: 'RGB++ Coin' })
export class RgbppCoin {
Expand Down Expand Up @@ -41,7 +44,10 @@ export class RgbppCoin {
@Field(() => Date)
deployedAt: Date;

public static from(xudt: CkbExplorer.XUDT): RgbppCoin {
@Field(() => [RgbppTransaction])
transactions: RgbppTransaction[];

public static from(xudt: CkbExplorer.XUDT): RgbppBaseCoin {
return {
name: xudt.full_name,
description: xudt.description,
Expand All @@ -62,7 +68,7 @@ export class RgbppCoin {
@ObjectType({ description: 'RGB++ Coin List' })
export class RgbppCoinList {
@Field(() => [RgbppCoin])
coins: RgbppCoin[];
coins: RgbppBaseCoin[];

@Field(() => Int)
total: number;
Expand Down
21 changes: 18 additions & 3 deletions backend/src/modules/rgbpp/coin/coin.resolver.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Args, Int, Query, Resolver } from '@nestjs/graphql';
import { RgbppCoin, RgbppCoinList } from './coin.model';
import { Args, Int, Parent, Query, ResolveField, Resolver } from '@nestjs/graphql';
import { RgbppBaseCoin, RgbppCoin, RgbppCoinList } from './coin.model';
import { CkbExplorerService } from 'src/core/ckb-explorer/ckb-explorer.service';
import { XUDTTag } from 'src/core/ckb-explorer/ckb-explorer.interface';
import { RgbppTransaction } from '../transaction/transaction.model';

@Resolver(() => RgbppCoin)
export class RgbppCoinResolver {
Expand All @@ -28,8 +29,22 @@ export class RgbppCoinResolver {
@Query(() => RgbppCoin, { name: 'rgbppCoin' })
public async coin(
@Args('typeHash', { type: () => String }) typeHash: string,
): Promise<RgbppCoin> {
): Promise<RgbppBaseCoin> {
const response = await this.ckbExplorerService.getXUDT(typeHash);
return RgbppCoin.from(response.data.attributes);
}

@ResolveField(() => [RgbppTransaction])
public async transactions(
@Parent() coin: RgbppBaseCoin,
@Args('page', { type: () => Int, nullable: true }) page: number = 1,
@Args('pageSize', { type: () => Int, nullable: true }) pageSize: number = 10,
) {
// XXX: should we implement dataloader here?
const response = await this.ckbExplorerService.getXUDTTransactions(coin.typeHash, {
page,
pageSize,
});
return response.data.map((tx) => RgbppTransaction.fromCkbTransaction(tx.attributes));
}
}
14 changes: 12 additions & 2 deletions backend/src/modules/rgbpp/transaction/transaction.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export class RgbppTransaction {
@Field(() => String)
ckbTxHash: string;

@Field(() => String)
@Field(() => String, { nullable: true })
btcTxid: string;

// FIXME: need to be fixed, don't know why it's null from the explorer api
Expand All @@ -45,7 +45,7 @@ export class RgbppTransaction {
@Field(() => BitcoinTransaction, { nullable: true })
btcTransaction: BitcoinTransaction;

public static fromCkbExplorer(tx: CkbExplorer.RgbppTransaction) {
public static from(tx: CkbExplorer.RgbppTransaction) {
return {
ckbTxHash: tx.tx_hash,
btcTxid: tx.rgb_txid,
Expand All @@ -54,4 +54,14 @@ export class RgbppTransaction {
timestamp: new Date(tx.block_timestamp),
};
}

public static fromCkbTransaction(tx: CkbExplorer.Transaction) {
return {
ckbTxHash: tx.transaction_hash,
btcTxid: tx.rgb_txid,
leapDirection: null,
blockNumber: parseInt(tx.block_number),
timestamp: new Date(tx.block_timestamp),
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export class RgbppTransactionResolver {
pageSize,
});
return response.data.ckb_transactions.map((transaction) =>
RgbppTransaction.fromCkbExplorer(transaction),
RgbppTransaction.from(transaction),
);
}

Expand Down

0 comments on commit a8f374a

Please sign in to comment.