Skip to content

Commit

Permalink
feat: add rgbpp coin module
Browse files Browse the repository at this point in the history
  • Loading branch information
ahonn committed Jul 18, 2024
1 parent b9a5e89 commit c7d4cd6
Show file tree
Hide file tree
Showing 14 changed files with 249 additions and 32 deletions.
34 changes: 34 additions & 0 deletions backend/src/core/ckb-explorer/ckb-explorer.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,3 +152,37 @@ export interface RgbppTransaction {
rgb_cell_changes: number;
rgb_txid: string;
}

export enum XUDTTag {
RgbppCompatible = 'rgbpp-compatible',
Layer1Asset = 'layer-1-asset',
Layer2Asset = 'layer-2-asset',
SupplyLimited = 'supply-limited',
SupplyUnlimited = 'supply-unlimited',
Suspicious = 'suspicious',
Invalid = 'invalid',
}

export interface XUDT {
symbol: string;
full_name: string;
icon_file: string | null;
published: boolean;
description: string | null;
type_hash: string;
type_script: {
args: string;
code_hash: string;
hash_type: string;
};
issuer_address: string;
udt_type: string;
operator_website: string | null;
email: string | null;
total_amount: string;
addresses_count: string;
decimal: string;
h24_ckb_transactions_count: string;
created_at: string;
xudt_tags: XUDTTag[];
}
31 changes: 31 additions & 0 deletions backend/src/core/ckb-explorer/ckb-explorer.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {
RgbppTransaction,
Transaction,
TransactionSortType,
XUDT,
XUDTTag,
} from './ckb-explorer.interface';

type BasePaginationParams = {
Expand All @@ -28,6 +30,11 @@ type GetRgbppTransactionsParams = BasePaginationParams & {
leapDirection?: 'in' | 'out';
};

type GetXUDTListParams = BasePaginationParams & {
symbol?: string;
tags?: XUDTTag[];
};

@Injectable()
export class CkbExplorerService {
private logger = new Logger(CkbExplorerService.name);
Expand Down Expand Up @@ -96,4 +103,28 @@ export class CkbExplorerService {
const response = await this.request.get(`/v2/rgb_transactions?${params.toString()}`);
return response.data;
}

public async getXUDTList({
symbol,
tags,
page = 1,
pageSize = 10,
}: GetXUDTListParams): Promise<PaginatedResponse<XUDT>> {
const params = new URLSearchParams();
params.append('page', page.toString());
params.append('page_size', pageSize.toString());
if (symbol) {
params.append('symbol', symbol);
}
if (tags) {
params.append('tags', tags.join(','));
}
const response = await this.request.get(`/v1/xudts?${params.toString()}`);
return response.data;
}

public async getXUDT(typeHash: string): Promise<NonPaginatedResponse<XUDT>> {
const response = await this.request.get(`/v1/xudts/${typeHash}`);
return response.data;
}
}
33 changes: 33 additions & 0 deletions backend/src/modules/bitcoin/bitcoin.model.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,36 @@
import { Field, Float, ObjectType } from '@nestjs/graphql';
import * as BitcoinApi from 'src/core/bitcoin-api/bitcoin-api.schema';

export type BitcoinBaseChainInfo = Omit<BitcoinChainInfo, 'fees'>;

@ObjectType({ description: 'Bitcoin Fees' })
export class BitcoinFees {
@Field(() => Float)
fastest: number;

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

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

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

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

public static from(fees: BitcoinApi.RecommendedFees) {
return {
fastest: fees.fastestFee,
halfHour: fees.halfHourFee,
hour: fees.hourFee,
economy: fees.economyFee,
minimum: fees.minimumFee,
};
}
}

@ObjectType({ description: 'Bitcoin ChainInfo' })
export class BitcoinChainInfo {
@Field(() => Float)
Expand All @@ -12,6 +42,9 @@ export class BitcoinChainInfo {
@Field(() => Float)
difficulty: number;

@Field(() => BitcoinFees)
fees: BitcoinFees;

public static from(info: BitcoinApi.ChainInfo) {
return {
tipBlockHeight: info.blocks,
Expand Down
14 changes: 10 additions & 4 deletions backend/src/modules/bitcoin/bitcoin.resolver.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
import { Query, Resolver } from '@nestjs/graphql';
import { BitcoinChainInfo } from './bitcoin.model';
import { Query, ResolveField, Resolver } from '@nestjs/graphql';
import { BitcoinBaseChainInfo, BitcoinChainInfo, BitcoinFees } from './bitcoin.model';
import { BitcoinApiService } from 'src/core/bitcoin-api/bitcoin-api.service';

@Resolver(() => BitcoinChainInfo)
export class BitcoinResolver {
constructor(private bitcoinApiService: BitcoinApiService) {}

@Query(() => BitcoinChainInfo)
public async getBitcoinChainInfo(): Promise<BitcoinChainInfo> {
@Query(() => BitcoinChainInfo, { name: 'btcChainInfo' })
public async chainInfo(): Promise<BitcoinBaseChainInfo> {
const info = await this.bitcoinApiService.getBlockchainInfo();
return BitcoinChainInfo.from(info);
}

@ResolveField(() => BitcoinFees)
public async fees(): Promise<BitcoinFees> {
const fees = await this.bitcoinApiService.getFeesRecommended();
return BitcoinFees.from(fees);
}
}
2 changes: 1 addition & 1 deletion backend/src/modules/ckb/block/block.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { BI } from '@ckb-lumos/bi';

@Resolver(() => CkbBlock)
export class CkbBlockResolver {
@Query(() => CkbBlock, { name: 'getCkbBlock' })
@Query(() => CkbBlock, { name: 'ckbBlock' })
public async getBlock(
@Args('heightOrHash', { type: () => String }) heightOrHash: string,
@Loader(CkbBlockLoader) blockLoader: DataLoader<string, CkbBlockLoaderResponse>,
Expand Down
4 changes: 2 additions & 2 deletions backend/src/modules/ckb/ckb.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { CkbRpcWebsocketService } from 'src/core/ckb-rpc/ckb-rpc-websocket.servi
export class CkbResolver {
constructor(private ckbRpcService: CkbRpcWebsocketService) {}

@Query(() => CkbChainInfo)
public async getCkbChainInfo(): Promise<CkbChainInfo> {
@Query(() => CkbChainInfo, { name: 'ckbChainInfo' })
public async chainInfo(): Promise<CkbChainInfo> {
const tipBlockNumber = await this.ckbRpcService.getTipBlockNumber();
return {
tipBlockNumber,
Expand Down
72 changes: 72 additions & 0 deletions backend/src/modules/rgbpp/coin/coin.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
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";

@ObjectType({ description: 'RGB++ Coin' })
export class RgbppCoin {
@Field(() => String)
name: string;

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

@Field(() => String)
symbol: string;

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

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

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

@Field(() => CkbScript)
typeScript: CkbScript;

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

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

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

@Field(() => String)
issuer: string;

@Field(() => Date)
deployedAt: Date;

public static from(xudt: CkbExplorer.XUDT): RgbppCoin {
return {
name: xudt.full_name,
description: xudt.description,
symbol: xudt.symbol,
decimal: toNumber(xudt.decimal),
icon: xudt.icon_file,
typeHash: xudt.type_hash,
typeScript: CkbScript.from(xudt.type_script),
holdersCount: toNumber(xudt.addresses_count),
h24CkbTransactionsCount: toNumber(xudt.h24_ckb_transactions_count),
totalAmount: toNumber(xudt.total_amount),
issuer: xudt.issuer_address,
deployedAt: new Date(xudt.created_at),
};
}
}

@ObjectType({ description: 'RGB++ Coin List' })
export class RgbppCoinList {
@Field(() => [RgbppCoin])
coins: RgbppCoin[];

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

@Field(() => Int)
page: number;
}
9 changes: 9 additions & 0 deletions backend/src/modules/rgbpp/coin/coin.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Module } from '@nestjs/common';
import { RgbppCoinResolver } from './coin.resolver';
import { CkbExplorerModule } from 'src/core/ckb-explorer/ckb-explorer.module';

@Module({
imports: [CkbExplorerModule],
providers: [RgbppCoinResolver],
})
export class CoinModule {}
35 changes: 35 additions & 0 deletions backend/src/modules/rgbpp/coin/coin.resolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Args, Int, Query, Resolver } from '@nestjs/graphql';
import { 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';

@Resolver(() => RgbppCoin)
export class RgbppCoinResolver {
constructor(private ckbExplorerService: CkbExplorerService) { }

@Query(() => RgbppCoinList, { name: 'rgbppCoinList' })
public async coins(
@Args('page', { type: () => Int, nullable: true }) page: number = 1,
@Args('pageSize', { type: () => Int, nullable: true }) pageSize: number = 10,
): Promise<RgbppCoinList> {
const response = await this.ckbExplorerService.getXUDTList({
page,
pageSize,
tags: [XUDTTag.RgbppCompatible],
});
const coins = response.data.map((coin) => RgbppCoin.from(coin.attributes));
return {
coins,
total: response.meta.total,
page: response.meta.page_size,
};
}

@Query(() => RgbppCoin, { name: 'rgbppCoin' })
public async coin(
@Args('typeHash', { type: () => String }) typeHash: string,
): Promise<RgbppCoin> {
const response = await this.ckbExplorerService.getXUDT(typeHash);
return RgbppCoin.from(response.data.attributes);
}
}
3 changes: 2 additions & 1 deletion backend/src/modules/rgbpp/rgbpp.module.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Module } from '@nestjs/common';
import { TransactionModule } from './transaction/transaction.module';
import { CoinModule } from './coin/coin.module';

@Module({
imports: [TransactionModule]
imports: [TransactionModule, CoinModule]
})
export class RgbppModule {}
4 changes: 4 additions & 0 deletions backend/src/modules/rgbpp/transaction/transaction.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ export class RgbppTransaction {
@Field(() => Int)
blockNumber: number;

@Field(() => Date)
timestamp: Date;

@Field(() => CkbTransaction)
ckbTransaction: CkbTransaction;

Expand All @@ -48,6 +51,7 @@ export class RgbppTransaction {
btcTxid: tx.rgb_txid,
leapDirection: LeapDirectionMap[tx.leap_direction],
blockNumber: tx.block_number,
timestamp: new Date(tx.block_timestamp),
};
}
}
3 changes: 1 addition & 2 deletions backend/src/modules/rgbpp/transaction/transaction.module.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { Module } from '@nestjs/common';
import { RgbppTransactionResolver } from './transaction.resolver';
import { CkbExplorerModule } from 'src/core/ckb-explorer/ckb-explorer.module';
import { RgbppTransactionService } from './transaction.service';
import { CkbTransactionModule } from 'src/modules/ckb/transaction/transaction.module';

@Module({
imports: [CkbExplorerModule, CkbTransactionModule],
providers: [RgbppTransactionResolver, RgbppTransactionService],
providers: [RgbppTransactionResolver],
})
export class TransactionModule {}
22 changes: 15 additions & 7 deletions backend/src/modules/rgbpp/transaction/transaction.resolver.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Parent, Query, ResolveField, Resolver } from '@nestjs/graphql';
import { Args, Int, Parent, Query, ResolveField, Resolver } from '@nestjs/graphql';
import { RgbppBaseTransaction, RgbppTransaction } from './transaction.model';
import { RgbppTransactionService } from './transaction.service';
import { CkbTransaction } from 'src/modules/ckb/transaction/transaction.model';
import { Loader } from '@applifting-io/nestjs-dataloader';
import {
Expand All @@ -14,15 +13,24 @@ import {
BitcoinTransactionLoader,
BitcoinTransactionLoaderResponse,
} from 'src/modules/bitcoin/transaction/transaction.dataloader';
import { CkbExplorerService } from 'src/core/ckb-explorer/ckb-explorer.service';

@Resolver(() => RgbppTransaction)
export class RgbppTransactionResolver {
constructor(private rgbppTransactionService: RgbppTransactionService) { }
constructor(private ckbExplorerService: CkbExplorerService) { }

@Query(() => [RgbppTransaction], { name: 'getRgbppLatestTransactions' })
public async getLatestRransaction(): Promise<RgbppBaseTransaction[]> {
const transactions = await this.rgbppTransactionService.getLatestTransaction();
return transactions.map((transaction) => RgbppTransaction.fromCkbExplorer(transaction));
@Query(() => [RgbppTransaction], { name: 'rgbppLatestTransactions' })
public async getLatestRransaction(
@Args('page', { type: () => Int, nullable: true }) page: number = 1,
@Args('pageSize', { type: () => Int, nullable: true }) pageSize: number = 10,
): Promise<RgbppBaseTransaction[]> {
const response = await this.ckbExplorerService.getRgbppTransactions({
page,
pageSize,
});
return response.data.ckb_transactions.map((transaction) =>
RgbppTransaction.fromCkbExplorer(transaction),
);
}

@ResolveField(() => CkbTransaction)
Expand Down
15 changes: 0 additions & 15 deletions backend/src/modules/rgbpp/transaction/transaction.service.ts

This file was deleted.

0 comments on commit c7d4cd6

Please sign in to comment.