From 6ea0c71bc47b087afd39cd0f8a62f70c3727e00f Mon Sep 17 00:00:00 2001 From: Shook Date: Sun, 28 Apr 2024 15:42:31 +0800 Subject: [PATCH 1/7] feat: adapt mempool data provider update in btc-assets-api#96 --- packages/btc/package.json | 1 - packages/btc/src/query/mempool.ts | 41 ----------- packages/btc/src/query/source.ts | 31 ++++---- packages/btc/tests/DataSource.test.ts | 20 ++---- packages/service/src/service/service.ts | 14 +++- packages/service/src/types/btc.ts | 16 ++++- packages/service/tests/Service.test.ts | 95 ++++++++++++++++--------- pnpm-lock.yaml | 35 --------- 8 files changed, 108 insertions(+), 145 deletions(-) delete mode 100644 packages/btc/src/query/mempool.ts diff --git a/packages/btc/package.json b/packages/btc/package.json index 5a3f1660..93dc3d27 100644 --- a/packages/btc/package.json +++ b/packages/btc/package.json @@ -18,7 +18,6 @@ "dependencies": { "@bitcoinerlab/secp256k1": "^1.1.1", "@ckb-lumos/codec": "0.22.2", - "@mempool/mempool.js": "^2.3.0", "@nervosnetwork/ckb-types": "^0.109.1", "@rgbpp-sdk/ckb": "workspace:^", "@rgbpp-sdk/service": "workspace:^", diff --git a/packages/btc/src/query/mempool.ts b/packages/btc/src/query/mempool.ts deleted file mode 100644 index 78c25e3b..00000000 --- a/packages/btc/src/query/mempool.ts +++ /dev/null @@ -1,41 +0,0 @@ -import Mempool from '@mempool/mempool.js'; -import { NetworkType } from '../preset/types'; - -export type MempoolInstance = ReturnType; - -export interface MempoolConfig { - hostname: string; - network: string; -} - -export const mempoolConfigs: Record<'mainnet' | 'testnet', MempoolConfig> = { - testnet: { - hostname: 'mempool.space', - network: 'testnet', - }, - mainnet: { - hostname: 'mempool.space', - network: 'mainnet', - }, -}; - -/** - * Get predefined mempool config by network type. - * If the network is regtest, it will use the testnet config. - */ -export function networkTypeToMempoolConfig(network: NetworkType) { - if (network === NetworkType.MAINNET) { - return mempoolConfigs.mainnet; - } - - // Using the testnet config for both testnet/regtest - return mempoolConfigs.testnet; -} - -/** - * Create a mempool instance by network type. - */ -export function createMempool(network: NetworkType) { - const config = networkTypeToMempoolConfig(network); - return Mempool(config); -} diff --git a/packages/btc/src/query/source.ts b/packages/btc/src/query/source.ts index 4c246f0c..c1e444e4 100644 --- a/packages/btc/src/query/source.ts +++ b/packages/btc/src/query/source.ts @@ -1,23 +1,25 @@ -import { FeesRecommended } from '@mempool/mempool.js/lib/interfaces/bitcoin/fees'; -import { BtcApiUtxoParams, BtcAssetsApi, BtcAssetsApiError, ErrorCodes as ServiceErrorCodes } from '@rgbpp-sdk/service'; +import { + BtcApiRecommendedFeeRates, + BtcApiUtxoParams, + BtcAssetsApi, + BtcAssetsApiError, + ErrorCodes as ServiceErrorCodes, +} from '@rgbpp-sdk/service'; import { Output, Utxo } from '../transaction/utxo'; import { NetworkType } from '../preset/types'; import { ErrorCodes, TxBuildError } from '../error'; +import { TxAddressOutput } from '../transaction/build'; import { isOpReturnScriptPubkey } from '../transaction/embed'; import { addressToScriptPublicKeyHex, getAddressType } from '../address'; -import { createMempool, MempoolInstance } from './mempool'; import { remove0x } from '../utils'; -import { TxAddressOutput } from '../transaction/build'; export class DataSource { public service: BtcAssetsApi; public networkType: NetworkType; - public mempool: MempoolInstance; constructor(service: BtcAssetsApi, networkType: NetworkType) { this.service = service; this.networkType = networkType; - this.mempool = createMempool(networkType); } // Query a UTXO from the service. @@ -158,19 +160,18 @@ export class DataSource { }; } - // Get recommended fee rates from mempool.space. - // From fastest to slowest: fastestFee > halfHourFee > economyFee > hourFee > minimumFee - async getRecommendedFeeRates(): Promise { - try { - return await this.mempool.bitcoin.fees.getFeesRecommended(); - } catch (err: any) { - throw TxBuildError.withComment(ErrorCodes.MEMPOOL_API_RESPONSE_ERROR, err.message ?? JSON.stringify(err)); - } + /** + * Get recommended fee rates from mempool.space. + * From fastest to slowest: fastestFee > halfHourFee > economyFee > hourFee > minimumFee + * @deprecated Use BtcAssetsApi.getBtcRecommendedFeeRates() instead. + */ + async getRecommendedFeeRates(): Promise { + return await this.service.getBtcRecommendedFeeRates(); } // Get the recommended average fee rate. async getAverageFeeRate(): Promise { - const fees = await this.getRecommendedFeeRates(); + const fees = await this.service.getBtcRecommendedFeeRates(); return fees.halfHourFee; } diff --git a/packages/btc/tests/DataSource.test.ts b/packages/btc/tests/DataSource.test.ts index 23986b0c..f359a362 100644 --- a/packages/btc/tests/DataSource.test.ts +++ b/packages/btc/tests/DataSource.test.ts @@ -1,23 +1,13 @@ import { describe, expect, it } from 'vitest'; -import { source } from './shared/env'; +import { service, source } from './shared/env'; import { ErrorCodes } from '../src'; describe('DataSource', () => { - it('Get recommended fee rates', async () => { - const fees = await source.getRecommendedFeeRates(); - - expect(fees).toBeDefined(); - expect(fees.fastestFee).toBeTypeOf('number'); - expect(fees.halfHourFee).toBeTypeOf('number'); - expect(fees.hourFee).toBeTypeOf('number'); - expect(fees.minimumFee).toBeTypeOf('number'); - - expect(fees.fastestFee).toBeGreaterThanOrEqual(fees.halfHourFee); - expect(fees.halfHourFee).toBeGreaterThanOrEqual(fees.hourFee); - expect(fees.hourFee).toBeGreaterThanOrEqual(fees.minimumFee); - }); it('Get average fee rate', async () => { - const [feeRates, averageFeeRate] = await Promise.all([source.getRecommendedFeeRates(), source.getAverageFeeRate()]); + const [feeRates, averageFeeRate] = await Promise.all([ + service.getBtcRecommendedFeeRates(), + source.getAverageFeeRate(), + ]); expect(averageFeeRate).toBeTypeOf('number'); expect(feeRates.halfHourFee).toBeTypeOf('number'); diff --git a/packages/service/src/service/service.ts b/packages/service/src/service/service.ts index 1e511466..9783054f 100644 --- a/packages/service/src/service/service.ts +++ b/packages/service/src/service/service.ts @@ -13,7 +13,8 @@ import { BtcApiTransaction, BtcApiUtxo, BtcApiUtxoParams, - RgbppApiTransactionStateParams, + BtcApiTransactionParams, + BtcApiRecommendedFeeRates, } from '../types'; import { RgbppApis, @@ -24,6 +25,7 @@ import { RgbppApiCkbTransactionHash, RgbppApiAssetsByAddressParams, RgbppApiRetryCkbTransactionPayload, + RgbppApiTransactionStateParams, RgbppApiTransactionRetry, } from '../types'; @@ -60,6 +62,10 @@ export class BtcAssetsApi extends BtcAssetsApiBase implements BtcApis, RgbppApis return this.request(`/bitcoin/v1/block/${blockHash}/txids`); } + getBtcRecommendedFeeRates() { + return this.request(`/bitcoin/v1/fees/recommended`); + } + getBtcBalance(address: string, params?: BtcApiBalanceParams) { return this.request(`/bitcoin/v1/address/${address}/balance`, { params, @@ -72,8 +78,10 @@ export class BtcAssetsApi extends BtcAssetsApiBase implements BtcApis, RgbppApis }); } - getBtcTransactions(address: string) { - return this.request(`/bitcoin/v1/address/${address}/txs`); + getBtcTransactions(address: string, params?: BtcApiTransactionParams) { + return this.request(`/bitcoin/v1/address/${address}/txs`, { + params, + }); } getBtcTransaction(txId: string) { diff --git a/packages/service/src/types/btc.ts b/packages/service/src/types/btc.ts index 7d0564df..1b320bcd 100644 --- a/packages/service/src/types/btc.ts +++ b/packages/service/src/types/btc.ts @@ -4,9 +4,10 @@ export interface BtcApis { getBtcBlockHeaderByHash(blockHash: string): Promise; getBtcBlockHashByHeight(blockHeight: number): Promise; getBtcBlockTransactionIdsByHash(blockHash: number): Promise; + getBtcRecommendedFeeRates(): Promise; getBtcBalance(address: string, params?: BtcApiBalanceParams): Promise; getBtcUtxos(address: string, params?: BtcApiUtxoParams): Promise; - getBtcTransactions(address: string): Promise; + getBtcTransactions(address: string, params?: BtcApiTransactionParams): Promise; getBtcTransaction(txId: string): Promise; sendBtcTransaction(txHex: string): Promise; } @@ -14,7 +15,6 @@ export interface BtcApis { export interface BtcApiBlockchainInfo { chain: string; blocks: number; - headers: number; bestblockhash: number; difficulty: number; mediantime: number; @@ -48,6 +48,14 @@ export interface BtcApiBlockTransactionIds { txids: string[]; } +export interface BtcApiRecommendedFeeRates { + fastestFee: number; + halfHourFee: number; + hourFee: number; + economyFee: number; + minimumFee: number; +} + export interface BtcApiBalanceParams { min_satoshi?: number; } @@ -79,6 +87,10 @@ export interface BtcApiSentTransaction { txid: string; } +export interface BtcApiTransactionParams { + after_txid?: string; +} + export interface BtcApiTransaction { txid: string; version: number; diff --git a/packages/service/tests/Service.test.ts b/packages/service/tests/Service.test.ts index 1aff376b..e79cd3c2 100644 --- a/packages/service/tests/Service.test.ts +++ b/packages/service/tests/Service.test.ts @@ -17,15 +17,6 @@ describe( ); describe('Initiation and token generation', () => { - it('Initiate with invalid "domain" param', async () => { - expect( - () => - new BtcAssetsApi({ - url: process.env.VITE_SERVICE_URL!, - domain: 'https://btc-test.app', - }), - ).toThrow(`${ErrorMessages[ErrorCodes.ASSETS_API_INVALID_PARAM]}: domain`); - }); it('Generate a valid token', async () => { const serviceWithApp = new BtcAssetsApi({ url: process.env.VITE_SERVICE_URL!, @@ -41,7 +32,16 @@ describe( const blockchainInfo = await serviceWithApp.getBtcBlockchainInfo(); expect(blockchainInfo.chain).toBeTypeOf('string'); }); - it('Generate token without the "app" param', async () => { + it('Try initiate with invalid "domain" param', async () => { + expect( + () => + new BtcAssetsApi({ + url: process.env.VITE_SERVICE_URL!, + domain: 'https://btc-test.app', + }), + ).toThrow(`${ErrorMessages[ErrorCodes.ASSETS_API_INVALID_PARAM]}: domain`); + }); + it('Try generate token without the "app" param', async () => { const serviceWithoutApp = new BtcAssetsApi({ url: process.env.VITE_SERVICE_URL!, domain: 'btc-test.app', @@ -53,25 +53,37 @@ describe( }); }); - describe('Bitcoin', () => { - it('Get blockchain info', async () => { + describe('BTC', () => { + it('getBtcBlockchainInfo()', async () => { const res = await service.getBtcBlockchainInfo(); expect(res.chain).toBeTypeOf('string'); expect(res.blocks).toBeTypeOf('number'); - expect(res.headers).toBeTypeOf('number'); expect(res.mediantime).toBeTypeOf('number'); expect(res.difficulty).toBeTypeOf('number'); expect(res.bestblockhash).toBeTypeOf('string'); }); - it('Get balance', async () => { - const res = await service.getBtcBalance('tb1qm06rvrq8jyyckzc5v709u7qpthel9j4d9f7nh3'); - expect(res.address).toEqual('tb1qm06rvrq8jyyckzc5v709u7qpthel9j4d9f7nh3'); + it('getBtcRecommendedFeeRates()', async () => { + const fees = await service.getBtcRecommendedFeeRates(); + expect(fees).toBeDefined(); + expect(fees.fastestFee).toBeTypeOf('number'); + expect(fees.halfHourFee).toBeTypeOf('number'); + expect(fees.hourFee).toBeTypeOf('number'); + expect(fees.economyFee).toBeTypeOf('number'); + expect(fees.minimumFee).toBeTypeOf('number'); + expect(fees.fastestFee).toBeGreaterThanOrEqual(fees.halfHourFee); + expect(fees.halfHourFee).toBeGreaterThanOrEqual(fees.hourFee); + expect(fees.hourFee).toBeGreaterThanOrEqual(fees.economyFee); + expect(fees.economyFee).toBeGreaterThanOrEqual(fees.minimumFee); + }); + it('getBtcBalance()', async () => { + const res = await service.getBtcBalance(btcAddress); + expect(res.address).toEqual(btcAddress); expect(res.satoshi).toBeTypeOf('number'); expect(res.pending_satoshi).toBeTypeOf('number'); expect(res.dust_satoshi).toBeTypeOf('number'); expect(res.utxo_count).toBeTypeOf('number'); }); - it('Get balance with min_satoshi filter', async () => { + it('getBtcBalance() with min_satoshi', async () => { const originalBalance = await service.getBtcBalance(btcAddress); const filteredBalance = await service.getBtcBalance(btcAddress, { min_satoshi: originalBalance.satoshi + 1, @@ -80,7 +92,7 @@ describe( expect(filteredBalance.satoshi).toEqual(0); expect(filteredBalance.dust_satoshi).toEqual(originalBalance.satoshi + originalBalance.dust_satoshi); }); - it('Get UTXO[]', async () => { + it('getBtcUtxos()', async () => { const res = await service.getBtcUtxos(btcAddress); expect(Array.isArray(res)).toBe(true); expect(res.length).toBeGreaterThan(0); @@ -100,7 +112,7 @@ describe( } }); }); - it('Get UTXO[] with min_satoshi filter', async () => { + it('getBtcUtxos() with min_satoshi', async () => { const originalUtxos = await service.getBtcUtxos(btcAddress); const maxValue = originalUtxos.reduce((max, out) => Math.max(max, out.value), 0); @@ -110,7 +122,7 @@ describe( expect(filteredUtxos.length).toBe(0); }); - it('Get UTXO[] with only_collected filter', async () => { + it('getBtcUtxos() with only_collected', async () => { const confirmedUtxos = await service.getBtcUtxos(btcAddress, { only_confirmed: true, }); @@ -119,8 +131,9 @@ describe( expect(utxo.status.confirmed).toBe(true); } }); - it('Get transactions', async () => { + it('getBtcTransactions()', async () => { const res = await service.getBtcTransactions(btcAddress); + console.log(res.map((tx) => tx.txid)); expect(Array.isArray(res)).toBe(true); expect(res.length).toBeGreaterThan(0); res.forEach((transaction) => { @@ -133,7 +146,23 @@ describe( } }); }); - it('Get transaction', async () => { + // TODO: wait for the btc-assets-api with org mempool api updates + it.todo('getBtcTransactions() with after_txid', async () => { + const txs = await service.getBtcTransactions(btcAddress); + expect(Array.isArray(txs)).toBe(true); + expect(Array.isArray(txs)).toBe(true); + expect(txs.length).toBeGreaterThan(0); + + const lastSecondId = txs[1].txid; + const filteredTxs = await service.getBtcTransactions(btcAddress, { + after_txid: lastSecondId, + }); + console.log(filteredTxs.length); + expect(Array.isArray(filteredTxs)).toBe(true); + expect(filteredTxs).toHaveLength(1); + expect(filteredTxs[0].txid).toEqual(lastSecondId); + }); + it('getBtcTransaction()', async () => { const res = await service.getBtcTransaction('102d5a002e72f0781944eef636117377da6d3601061e47e03025e7cd29a91579'); expect(res.txid).toBe('102d5a002e72f0781944eef636117377da6d3601061e47e03025e7cd29a91579'); }); @@ -153,7 +182,7 @@ describe( const emptyBtcTxId = '0000000000000000000000000000000000000000000000000000000000000001'; - it('Get paymaster info', async () => { + it('getRgbppPaymasterInfo()', async () => { try { const res = await service.getRgbppPaymasterInfo(); expect(res).toBeDefined(); @@ -164,35 +193,35 @@ describe( expect(e.code).toBe(ErrorCodes.ASSETS_API_RESOURCE_NOT_FOUND); } }); - it('Get the hash of RGBPP CKB_TX', async () => { + it('getRgbppTransactionHash()', async () => { const res = await service.getRgbppTransactionHash(rgbppBtcTxId); expect(res).toBeDefined(); expect(res.txhash).toBeTypeOf('string'); expect(res.txhash).toHaveLength(66); }); - it('Try to get the hash of non-existent RGBPP CKB_TX', async () => { + it('getRgbppTransactionHash() with empty BTC_TXID', async () => { await expect(() => service.getRgbppTransactionHash(emptyBtcTxId)).rejects.toHaveProperty( 'code', ErrorCodes.ASSETS_API_RESOURCE_NOT_FOUND, ); }); // TODO: make a record and remove the "skip" marker - it.skip('Get the state of RGBPP CKB_TX', async () => { + it.skip('getRgbppTransactionState()', async () => { const res = await service.getRgbppTransactionState(rgbppBtcTxId); expect(res).toBeDefined(); expect(res.state).toBeTypeOf('string'); expect(res.state).toSatisfy( (state: string) => ['completed', 'failed', 'delayed', 'active', 'waiting'].includes(state), - `state "${res.state}" should be one of the RgbppTransactionState enum`, + `state "${res.state}" should be one of the RgbppTransactionState`, ); }); - it('Try to get the state of non-existent RGBPP CKB_TX', async () => { + it('getRgbppTransactionState() with empty BTC_TXID', async () => { await expect(() => service.getRgbppTransactionState(emptyBtcTxId)).rejects.toHaveProperty( 'code', ErrorCodes.ASSETS_API_RESOURCE_NOT_FOUND, ); }); - it('Get RGBPP cells by BTC_TX_ID', async () => { + it('getRgbppAssetsByBtcTxId()', async () => { const res = await service.getRgbppAssetsByBtcTxId(rgbppBtcTxId); expect(res).toBeDefined(); expect(res.length).toBeGreaterThan(0); @@ -200,7 +229,7 @@ describe( expectCell(cell); } }); - it('Get RGBPP cells by BTC_UTXO', async () => { + it('getRgbppAssetsByBtcUtxo()', async () => { const res = await service.getRgbppAssetsByBtcUtxo(rgbppBtcTxId, rgbppBtcVout); expect(res).toBeDefined(); expect(res.length).toBeGreaterThan(0); @@ -208,7 +237,7 @@ describe( expectCell(cell); } }); - it('Get RGBPP cells by BTC_ADDRESS', async () => { + it('getRgbppAssetsByBtcAddress()', async () => { const res = await service.getRgbppAssetsByBtcAddress(rgbppBtcAddress, { type_script: rgbppCellType, }); @@ -218,7 +247,7 @@ describe( expectCell(cell); } }); - it('Get RGBPP SPV proof', async () => { + it('getRgbppSpvProof()', async () => { const res = await service.getRgbppSpvProof(rgbppBtcTxId, 6); expect(res).toBeDefined(); expect(res.proof).toBeTypeOf('string'); @@ -226,7 +255,7 @@ describe( expect(res.spv_client.index).toBeTypeOf('string'); expect(res.spv_client.tx_hash).toBeTypeOf('string'); }); - it('Try to get non-existent RGBPP SPV proof', async () => { + it('getRgbppSpvProof() with empty BTC_TXID', async () => { await expect(() => service.getRgbppSpvProof(emptyBtcTxId, 0)).rejects.toHaveProperty( 'code', ErrorCodes.ASSETS_API_RESOURCE_NOT_FOUND, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 80234f78..a5ca1993 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -173,9 +173,6 @@ importers: '@ckb-lumos/codec': specifier: 0.22.2 version: 0.22.2 - '@mempool/mempool.js': - specifier: ^2.3.0 - version: 2.3.0 '@nervosnetwork/ckb-types': specifier: ^0.109.1 version: 0.109.1 @@ -1520,17 +1517,6 @@ packages: read-yaml-file: 1.1.0 dev: true - /@mempool/mempool.js@2.3.0: - resolution: {integrity: sha512-FrN9WjZCEyyLodrTPQxmlWDh8B/UGK0jlKfVNzJxqzQ1IMPo/Hpdws8xwYEcsks5JqsaxbjLwaC3GAtJ6Brd0A==} - dependencies: - axios: 0.24.0 - ws: 8.3.0 - transitivePeerDependencies: - - bufferutil - - debug - - utf-8-validate - dev: false - /@nervosnetwork/ckb-sdk-core@0.109.1: resolution: {integrity: sha512-YU3yJZvz69WU6Bw//vRxzxLGcIj74CwGffdp6GYZbQpZwFaACT6FCojvOMjHIyJ8Rw32r114LhMKHvyBwagWjw==} dependencies: @@ -2583,14 +2569,6 @@ packages: possible-typed-array-names: 1.0.0 dev: true - /axios@0.24.0: - resolution: {integrity: sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==} - dependencies: - follow-redirects: 1.15.6 - transitivePeerDependencies: - - debug - dev: false - /axios@1.6.7: resolution: {integrity: sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==} dependencies: @@ -6633,19 +6611,6 @@ packages: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} dev: true - /ws@8.3.0: - resolution: {integrity: sha512-Gs5EZtpqZzLvmIM59w4igITU57lrtYVFneaa434VROv4thzJyV6UjIL3D42lslWlI+D4KzLYnxSwtfuiO79sNw==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - dev: false - /xtend@4.0.2: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} From 799c57c95783397ab7f9dadcfcb5dd6c2d35085c Mon Sep 17 00:00:00 2001 From: Shook Date: Fri, 26 Apr 2024 10:05:43 +0800 Subject: [PATCH 2/7] test: replace deprecated test infos in the service lib --- packages/service/tests/Service.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/service/tests/Service.test.ts b/packages/service/tests/Service.test.ts index e79cd3c2..cbb10b5d 100644 --- a/packages/service/tests/Service.test.ts +++ b/packages/service/tests/Service.test.ts @@ -169,13 +169,13 @@ describe( }); describe('RGBPP', () => { - const rgbppBtcAddress = 'tb1q3k837txmex9p294h6trmzquf9vyx36k740dpv4'; - const rgbppBtcTxId = 'a7c3f37227a2becbaeb5840be2bdaa33bb4bc88d0ffb5f90f814c92982b8c367'; - const rgbppBtcVout = 1; + const rgbppBtcAddress = 'tb1qwksrmna6emxrerrgyc8hrlxvl2z4x4tdhzzyej'; + const rgbppBtcTxId = 'da1f32672e3fb0432e1c94ed41298820c8dcca9495cf04a49d992ca4dfc5853d'; + const rgbppBtcVout = 0; const rgbppCellType = bytes.hexify( blockchain.Script.pack({ codeHash: '0x25c29dc317811a6f6f3985a7a9ebc4838bd388d19d0feeecf0bcd60f6c0975bb', - args: '0x1ba116c119d1cfd98a53e9d1a615cf2af2bb87d95515c9d217d367054cfc696b', + args: '0x661cfbe2124b3e79e50e505c406be5b2dcf9da15d8654b749ec536fa4c2eaaae', hashType: 'type', }), ); From 012538f5a064185e4c8e93c0108cff0e0839ffee Mon Sep 17 00:00:00 2001 From: Shook Date: Tue, 30 Apr 2024 11:11:11 +0800 Subject: [PATCH 3/7] test: update after_txid test for the BtcAssetsApi.getBtcTransactions() API --- packages/service/tests/Service.test.ts | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/packages/service/tests/Service.test.ts b/packages/service/tests/Service.test.ts index cbb10b5d..edfd0910 100644 --- a/packages/service/tests/Service.test.ts +++ b/packages/service/tests/Service.test.ts @@ -146,21 +146,22 @@ describe( } }); }); - // TODO: wait for the btc-assets-api with org mempool api updates - it.todo('getBtcTransactions() with after_txid', async () => { + it('getBtcTransactions() with after_txid', async () => { const txs = await service.getBtcTransactions(btcAddress); expect(Array.isArray(txs)).toBe(true); - expect(Array.isArray(txs)).toBe(true); expect(txs.length).toBeGreaterThan(0); - const lastSecondId = txs[1].txid; const filteredTxs = await service.getBtcTransactions(btcAddress, { - after_txid: lastSecondId, + after_txid: txs[0].txid, }); - console.log(filteredTxs.length); expect(Array.isArray(filteredTxs)).toBe(true); - expect(filteredTxs).toHaveLength(1); - expect(filteredTxs[0].txid).toEqual(lastSecondId); + + if (txs.length > 1) { + expect(txs.length).toBeGreaterThan(0); + expect(filteredTxs[0].txid).toEqual(txs[1].txid); + } else { + expect(filteredTxs).toHaveLength(0); + } }); it('getBtcTransaction()', async () => { const res = await service.getBtcTransaction('102d5a002e72f0781944eef636117377da6d3601061e47e03025e7cd29a91579'); From ca21d3c1f4adf9db1cf7b983642395701fe51dd3 Mon Sep 17 00:00:00 2001 From: Shook Date: Tue, 30 Apr 2024 11:58:09 +0800 Subject: [PATCH 4/7] test: allow DataSource tests to retry 3 times --- packages/btc/tests/DataSource.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/btc/tests/DataSource.test.ts b/packages/btc/tests/DataSource.test.ts index f359a362..b6e2ca09 100644 --- a/packages/btc/tests/DataSource.test.ts +++ b/packages/btc/tests/DataSource.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest'; import { service, source } from './shared/env'; import { ErrorCodes } from '../src'; -describe('DataSource', () => { +describe('DataSource', { retry: 3 }, () => { it('Get average fee rate', async () => { const [feeRates, averageFeeRate] = await Promise.all([ service.getBtcRecommendedFeeRates(), From 41bb3cef99768b2f0b5418e895b7e62e930be4bb Mon Sep 17 00:00:00 2001 From: Shook Date: Tue, 30 Apr 2024 11:59:27 +0800 Subject: [PATCH 5/7] chore: re-enable btc/service tests --- .github/workflows/test.yaml | 2 +- package.json | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 6814df34..a83a3e9d 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -50,7 +50,7 @@ jobs: run: pnpm run build:packages - name: Run tests for packages - run: pnpm run test:ckb + run: pnpm run test:packages env: VITE_SERVICE_URL: ${{ secrets.SERVICE_URL }} VITE_SERVICE_TOKEN: ${{ secrets.SERVICE_TOKEN }} diff --git a/package.json b/package.json index 1f88f9bd..ad1bfb1d 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,6 @@ ], "scripts": { "prepare": "husky", - "test:ckb": "turbo run test --filter=./packages/ckb", "test:packages": "turbo run test --filter=./packages/*", "build": "turbo run build", "build:packages": "turbo run build --filter=./packages/*", From 4f97df26f855d7e3a74345251822eeb8e1df8333 Mon Sep 17 00:00:00 2001 From: Shook Date: Tue, 30 Apr 2024 12:07:09 +0800 Subject: [PATCH 6/7] docs: add after_txid type in service lib readme --- packages/service/README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/service/README.md b/packages/service/README.md index 902247b2..b9dc03f8 100644 --- a/packages/service/README.md +++ b/packages/service/README.md @@ -131,9 +131,10 @@ interface BtcApis { getBtcBlockHeaderByHash(blockHash: string): Promise; getBtcBlockHashByHeight(blockHeight: number): Promise; getBtcBlockTransactionIdsByHash(blockHash: number): Promise; + getBtcRecommendedFeeRates(): Promise; getBtcBalance(address: string, params?: BtcApiBalanceParams): Promise; getBtcUtxos(address: string, params?: BtcApiUtxoParams): Promise; - getBtcTransactions(address: string): Promise; + getBtcTransactions(address: string, params?: BtcApiTransactionParams): Promise; getBtcTransaction(txId: string): Promise; sendBtcTransaction(txHex: string): Promise; } @@ -208,6 +209,10 @@ interface BtcApiSentTransaction { txid: string; } +export interface BtcApiTransactionParams { + after_txid?: string; +} + interface BtcApiTransaction { txid: string; version: number; From b0014d465887192d2fed274a08d709ffd48c5d46 Mon Sep 17 00:00:00 2001 From: Shook Date: Tue, 30 Apr 2024 15:16:49 +0800 Subject: [PATCH 7/7] refactor!: remove getRecommendedFeeRates() and getAverageFeeRate() from DataSource, and use fastestFee by default in TxBuilder --- packages/btc/README.md | 2 -- packages/btc/src/query/source.ts | 15 --------------- packages/btc/src/transaction/build.ts | 3 ++- packages/btc/tests/DataSource.test.ts | 10 ---------- 4 files changed, 2 insertions(+), 28 deletions(-) diff --git a/packages/btc/README.md b/packages/btc/README.md index fae0da39..d6af5e11 100644 --- a/packages/btc/README.md +++ b/packages/btc/README.md @@ -380,8 +380,6 @@ interface DataSource { satoshi: number; exceedSatoshi: number; }>; - getRecommendedFeeRates(): Promise; - getAverageFeeRate(): Promise; } ``` diff --git a/packages/btc/src/query/source.ts b/packages/btc/src/query/source.ts index c1e444e4..ba34d749 100644 --- a/packages/btc/src/query/source.ts +++ b/packages/btc/src/query/source.ts @@ -160,21 +160,6 @@ export class DataSource { }; } - /** - * Get recommended fee rates from mempool.space. - * From fastest to slowest: fastestFee > halfHourFee > economyFee > hourFee > minimumFee - * @deprecated Use BtcAssetsApi.getBtcRecommendedFeeRates() instead. - */ - async getRecommendedFeeRates(): Promise { - return await this.service.getBtcRecommendedFeeRates(); - } - - // Get the recommended average fee rate. - async getAverageFeeRate(): Promise { - const fees = await this.service.getBtcRecommendedFeeRates(); - return fees.halfHourFee; - } - async getPaymasterOutput(): Promise { try { const paymasterInfo = await this.service.getRgbppPaymasterInfo(); diff --git a/packages/btc/src/transaction/build.ts b/packages/btc/src/transaction/build.ts index 4907bbe8..c2e477ba 100644 --- a/packages/btc/src/transaction/build.ts +++ b/packages/btc/src/transaction/build.ts @@ -149,7 +149,8 @@ export class TxBuilder { // The transaction is expected be confirmed within half an hour with the fee rate let averageFeeRate: number | undefined; if (!feeRate && !this.feeRate) { - averageFeeRate = await this.source.getAverageFeeRate(); + const feeRates = await this.source.service.getBtcRecommendedFeeRates(); + averageFeeRate = feeRates.fastestFee; } // Use the feeRate param if it is specified, diff --git a/packages/btc/tests/DataSource.test.ts b/packages/btc/tests/DataSource.test.ts index b6e2ca09..e581640a 100644 --- a/packages/btc/tests/DataSource.test.ts +++ b/packages/btc/tests/DataSource.test.ts @@ -3,16 +3,6 @@ import { service, source } from './shared/env'; import { ErrorCodes } from '../src'; describe('DataSource', { retry: 3 }, () => { - it('Get average fee rate', async () => { - const [feeRates, averageFeeRate] = await Promise.all([ - service.getBtcRecommendedFeeRates(), - source.getAverageFeeRate(), - ]); - - expect(averageFeeRate).toBeTypeOf('number'); - expect(feeRates.halfHourFee).toBeTypeOf('number'); - expect(averageFeeRate).toEqual(feeRates.halfHourFee); - }); it('Get OP_RETURN output via getOutput()', async () => { const output = await source.getOutput('70b250e2a3cc7a33b47f7a4e94e41e1ee2501ce73b393d824db1dd4c872c5348', 0);