Skip to content

Commit

Permalink
Merge pull request #278 from ckb-cell/feat/app-transfer-all-api
Browse files Browse the repository at this point in the history
feat(rgbpp-sdk-service): support `generate_rgbpp_transfer_all_txs` API
  • Loading branch information
ShookLyngs authored Aug 15, 2024
2 parents 26003cd + 817870c commit 9daf4ae
Show file tree
Hide file tree
Showing 19 changed files with 704 additions and 1,948 deletions.
5 changes: 4 additions & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,14 @@ jobs:
- name: Lint packages
run: pnpm run lint

- name: Run tests for packages
- name: Run unit tests for the packages
run: pnpm run test:packages
env:
VITE_CKB_NODE_URL: https://testnet.ckb.dev/rpc
VITE_CKB_INDEXER_URL: https://testnet.ckb.dev/indexer
VITE_BTC_SERVICE_URL: https://btc-assets-api.testnet.mibao.pro
VITE_BTC_SERVICE_TOKEN: ${{ secrets.TESTNET_SERVICE_TOKEN }}
VITE_BTC_SERVICE_ORIGIN: https://btc-assets-api.testnet.mibao.pro

- name: Run unit tests for the rgbpp-sdk-service
run: pnpm run test:service
44 changes: 14 additions & 30 deletions apps/service/package.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
{
"name": "rgbpp-sdk-service",
"version": "0.0.1",
"description": "",
"author": "",
"private": true,
"license": "UNLICENSED",
"type": "module",
"scripts": {
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\"",
Expand All @@ -14,54 +12,40 @@
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/src/main",
"lint": "eslint \"src/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
"test": "vitest",
"test:watch": "vitest --watch",
"test:cov": "vitest run --coverage",
"test:debug": "vitest --inspect-brk --inspect --logHeapUsage --threads=false"
},
"dependencies": {
"@nestjs/common": "^10.0.0",
"@nestjs/config": "^3.2.2",
"@nestjs/core": "^10.3.9",
"@nestjs/platform-fastify": "^10.3.9",
"convert-keys": "^1.3.4",
"camelcase-keys": "^7.0.2",
"json-rpc-2.0": "^1.7.0",
"lodash": "^4.17.21",
"reflect-metadata": "^0.2.0",
"rgbpp": "^0.5.0",
"rgbpp": "0.0.0-snap-20240813134030",
"rxjs": "^7.8.1",
"snakecase-keys": "^8.0.1",
"zod": "^3.23.8"
},
"devDependencies": {
"@nestjs/cli": "^10.0.0",
"@nestjs/schematics": "^10.0.0",
"@nestjs/testing": "^10.0.0",
"@types/jest": "^29.5.2",
"@swc/core": "^1.7.11",
"@types/node": "^20.3.1",
"@types/supertest": "^6.0.0",
"@vitest/coverage-v8": "^2.0.5",
"source-map-support": "^0.5.21",
"supertest": "^6.3.3",
"ts-jest": "^29.1.0",
"ts-loader": "^9.4.3",
"ts-node": "^10.9.1",
"tsconfig-paths": "^4.2.0",
"typescript": "^5.1.3"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
"type-fest": "^4.24.0",
"typescript": "^5.1.3",
"unplugin-swc": "^1.5.1"
}
}
15 changes: 8 additions & 7 deletions apps/service/src/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import { Global, Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import JsonRpcModule from './json-rpc/json-rpc.module';
import { RgbppModule } from './rgbpp/rgbpp.module';
import { AppService } from './app.service';
import { envSchema } from './env';
import { BtcAssetsApi } from 'rgbpp/service';
import { BTCTestnetType, Collector } from 'rgbpp/ckb';
import { BtcAssetsApi } from 'rgbpp';
import JsonRpcModule from './json-rpc/json-rpc.module.js';
import { RgbppModule } from './rgbpp/rgbpp.module.js';
import { AppService } from './app.service.js';
import { envSchema } from './env.js';

@Global()
@Module({
imports: [
ConfigModule.forRoot({
validate: envSchema.parse,
envFilePath: ['.env', '.env.local'],
}),
JsonRpcModule.forRoot({
path: '/json-rpc',
Expand All @@ -31,7 +32,7 @@ import { BtcAssetsApi } from 'rgbpp';
inject: [ConfigService],
},
{
provide: 'COLLECTOR',
provide: 'CKB_COLLECTOR',
useFactory: (configService: ConfigService) => {
const ckbRpcUrl = configService.get('CKB_RPC_URL');
return new Collector({
Expand All @@ -52,6 +53,6 @@ import { BtcAssetsApi } from 'rgbpp';
inject: [ConfigService],
},
],
exports: ['IS_MAINNET', 'COLLECTOR', 'BTC_ASSETS_API', 'BTC_TESTNET_TYPE'],
exports: ['IS_MAINNET', 'CKB_COLLECTOR', 'BTC_ASSETS_API', 'BTC_TESTNET_TYPE'],
})
export class AppModule {}
4 changes: 2 additions & 2 deletions apps/service/src/app.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { RpcHandler, RpcMethodHandler } from 'src/json-rpc/json-rpc.decorators';
import * as pkg from '../package.json';
import { RpcHandler, RpcMethodHandler } from './json-rpc/json-rpc.decorators.js';
import pkg from '../package.json' with { type: 'json' };

@RpcHandler()
export class AppService {
Expand Down
2 changes: 1 addition & 1 deletion apps/service/src/json-rpc/json-rpc.module.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Inject, Logger, Module, OnModuleInit } from '@nestjs/common';
import { JsonRpcServer } from './json-rpc.server';
import { JsonRpcServer } from './json-rpc.server.js';

export const JSON_RPC_OPTIONS = '__JSON_RPC_OPTIONS__';

Expand Down
6 changes: 3 additions & 3 deletions apps/service/src/json-rpc/json-rpc.server.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Injectable, Logger } from '@nestjs/common';
import { HttpAdapterHost, ModuleRef, ModulesContainer } from '@nestjs/core';
import { JSONRPCServer, SimpleJSONRPCMethod } from 'json-rpc-2.0';
import { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper';
import { JsonRpcMetadataKey, JsonRpcMethodMetadataKey } from './json-rpc.decorators';
import { JsonRpcConfig } from './json-rpc.module';
import { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper.js';
import { JsonRpcMetadataKey, JsonRpcMethodMetadataKey } from './json-rpc.decorators.js';
import { JsonRpcConfig } from './json-rpc.module.js';

class JsonRpcServerError extends Error {
constructor(message: string) {
Expand Down
8 changes: 5 additions & 3 deletions apps/service/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { Logger } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify';
import { AppModule } from './app.module.js';

async function bootstrap() {
const logger = new Logger('AppBootstrap');
const app = await NestFactory.create<NestFastifyApplication>(AppModule, new FastifyAdapter());
const app = await NestFactory.create<NestFastifyApplication>(AppModule, new FastifyAdapter(), {
cors: true,
});
await app.listen(3000, '0.0.0.0');
logger.log('Application is running on: http://0.0.0.0:3000');
}
Expand Down
2 changes: 1 addition & 1 deletion apps/service/src/rgbpp/rgbpp.module.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Module } from '@nestjs/common';
import { RgbppService } from './rgbpp.service';
import { RgbppService } from './rgbpp.service.js';

@Module({
imports: [],
Expand Down
117 changes: 80 additions & 37 deletions apps/service/src/rgbpp/rgbpp.service.ts
Original file line number Diff line number Diff line change
@@ -1,86 +1,129 @@
import { Inject } from '@nestjs/common';
import { RpcHandler, RpcMethodHandler } from 'src/json-rpc/json-rpc.decorators';
import { DataSource, NetworkType } from 'rgbpp/btc';
import { BTCTestnetType, Collector, Hex, toCamelcase } from 'rgbpp/ckb';
import { BtcAssetsApi, RgbppApiTransactionState } from 'rgbpp/service';
import { BTCTestnetType, Collector, Hex, append0x } from 'rgbpp/ckb';
import { buildRgbppTransferTx, buildRgbppTransferAllTxs } from 'rgbpp';
import { RpcHandler, RpcMethodHandler } from '../json-rpc/json-rpc.decorators.js';
import { toSnakeCase, toCamelCase, SnakeCased } from '../utils/case.js';
import { ensureSafeJson } from '../utils/json.js';
import {
RgbppTransferReq,
RgbppCkbBtcTransaction,
RgbppCkbTxBtcTxId,
RgbppStateReq,
RgbppCkbTxHashReq,
BtcTxSendReq,
} from './types';
import { toSnakeCase } from 'src/utils/snake';
import { buildRgbppTransferTx } from 'rgbpp';
import { BtcAssetsApi } from 'rgbpp/service';
RgbppTransferAllReq,
RgbppTransferAllRes,
} from './types.js';

@RpcHandler()
export class RgbppService {
private readonly btcDataSource: DataSource;

constructor(
@Inject('IS_MAINNET') private isMainnet: boolean,
@Inject('BTC_TESTNET_TYPE') private btcTestnetType: BTCTestnetType,
@Inject('COLLECTOR') private collector: Collector,
@Inject('CKB_COLLECTOR') private ckbCollector: Collector,
@Inject('BTC_ASSETS_API') private btcAssetsApi: BtcAssetsApi,
) {}
@Inject('BTC_TESTNET_TYPE') private btcTestnetType: BTCTestnetType,
) {
const networkType = isMainnet ? NetworkType.MAINNET : NetworkType.TESTNET;
this.btcDataSource = new DataSource(btcAssetsApi, networkType);
}

@RpcMethodHandler({ name: 'generate_rgbpp_transfer_tx' })
public async generateRgbppTransferTx(request: object[]) {
const { xudtTypeArgs, rgbppLockArgsList, transferAmount, fromBtcAddress, toBtcAddress } =
toCamelcase<RgbppTransferReq>(request[0]);
const networkType = this.isMainnet ? NetworkType.MAINNET : NetworkType.TESTNET;
const btcDataSource = new DataSource(this.btcAssetsApi, networkType);
const { ckbVirtualTxResult, btcPsbtHex } = await buildRgbppTransferTx({
public async generateRgbppTransferTx(request: [RgbppTransferReq]): Promise<SnakeCased<RgbppCkbBtcTransaction>> {
const params = toCamelCase(request[0]);
const result = await buildRgbppTransferTx({
ckb: {
collector: this.ckbCollector,
xudtTypeArgs: params.xudtTypeArgs,
rgbppLockArgsList: params.rgbppLockArgsList,
transferAmount: BigInt(params.transferAmount),
},
btc: {
fromAddress: params.fromBtcAddress,
toAddress: params.toBtcAddress,
dataSource: this.btcDataSource,
testnetType: this.btcTestnetType,
},
isMainnet: this.isMainnet,
});

return toSnakeCase<RgbppCkbBtcTransaction>({
ckbVirtualTxResult: JSON.stringify(result.ckbVirtualTxResult),
btcPsbtHex: result.btcPsbtHex,
});
}

@RpcMethodHandler({ name: 'generate_rgbpp_transfer_all_txs' })
public async generateRgbppTransferAllTxs(request: [RgbppTransferAllReq]): Promise<SnakeCased<RgbppTransferAllRes>> {
const params = toCamelCase(request[0]);
const result = await buildRgbppTransferAllTxs({
ckb: {
collector: this.collector,
xudtTypeArgs,
rgbppLockArgsList,
transferAmount: BigInt(transferAmount),
collector: this.ckbCollector,
xudtTypeArgs: params.ckb.xudtTypeArgs,
feeRate: params.ckb.feeRate ? BigInt(append0x(params.ckb.feeRate)) : undefined,
},
btc: {
fromAddress: fromBtcAddress,
toAddress: toBtcAddress,
dataSource: btcDataSource,
assetAddresses: params.btc.assetAddresses,
fromAddress: params.btc.fromAddress,
toAddress: params.btc.toAddress,
fromPubkey: params.btc.fromPubkey,
pubkeyMap: params.btc.pubkeyMap,
feeRate: params.btc.feeRate,
dataSource: this.btcDataSource,
testnetType: this.btcTestnetType,
},
isMainnet: this.isMainnet,
});
const response: RgbppCkbBtcTransaction = {
ckbVirtualTxResult: JSON.stringify(ckbVirtualTxResult),
btcPsbtHex,
};
return toSnakeCase(response);

return ensureSafeJson<SnakeCased<RgbppTransferAllRes>>(
toSnakeCase<RgbppTransferAllRes>({
...result,
transactions: result.transactions.map((group) => {
return {
...group,
ckb: {
...group.ckb,
virtualTxResult: JSON.stringify(group.ckb.virtualTxResult),
},
};
}),
}),
);
}

@RpcMethodHandler({ name: 'report_rgbpp_ckb_tx_btc_txid' })
public async reportRgbppCkbTxBtcTxId(request: object[]) {
const { ckbVirtualTxResult, btcTxId } = toCamelcase<RgbppCkbTxBtcTxId>(request[0]);
public async reportRgbppCkbTxBtcTxId(request: [RgbppCkbTxBtcTxId]): Promise<SnakeCased<RgbppApiTransactionState>> {
const { ckbVirtualTxResult, btcTxId } = toCamelCase(request[0]);
const response = await this.btcAssetsApi.sendRgbppCkbTransaction({
btc_txid: btcTxId,
ckb_virtual_result: ckbVirtualTxResult,
});
return toSnakeCase(response);
return toSnakeCase<RgbppApiTransactionState>(response);
}

@RpcMethodHandler({ name: 'get_rgbpp_tx_state' })
public async getRgbppTxState(request: object[]) {
public async getRgbppTxState(request: [RgbppStateReq]): Promise<SnakeCased<RgbppApiTransactionState>> {
const {
btcTxId,
params: { withData },
} = toCamelcase<RgbppStateReq>(request[0]);
} = toCamelCase(request[0]);
const response = await this.btcAssetsApi.getRgbppTransactionState(btcTxId, { with_data: withData });
return toSnakeCase(response);
return toSnakeCase<RgbppApiTransactionState>(response);
}

@RpcMethodHandler({ name: 'get_rgbpp_ckb_tx_hash' })
public async getRgbppCkbTxHash(request: object[]): Promise<Hex> {
const { btcTxId } = toCamelcase<RgbppCkbTxHashReq>(request[0]);
public async getRgbppCkbTxHash(request: [RgbppCkbTxHashReq]): Promise<Hex> {
const { btcTxId } = toCamelCase(request[0]);
const { txhash: txHash } = await this.btcAssetsApi.getRgbppTransactionHash(btcTxId);
return txHash;
}

@RpcMethodHandler({ name: 'send_btc_transaction' })
public async sendBtcTransaction(request: object[]): Promise<Hex> {
const { txHex } = toCamelcase<BtcTxSendReq>(request[0]);
public async sendBtcTransaction(request: [BtcTxSendReq]): Promise<Hex> {
const { txHex } = toCamelCase(request[0]);
const { txid } = await this.btcAssetsApi.sendBtcTransaction(txHex);
return txid;
}
Expand Down
Loading

1 comment on commit 9daf4ae

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New snapshot version of the rgbpp-sdk packages have been released:

Name Version
@rgbpp-sdk/btc 0.0.0-snap-20240815014529
@rgbpp-sdk/ckb 0.0.0-snap-20240815014529
rgbpp 0.0.0-snap-20240815014529
@rgbpp-sdk/service 0.0.0-snap-20240815014529

Please sign in to comment.