-
Notifications
You must be signed in to change notification settings - Fork 45
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(sdk): add mempool client (#394)
- Loading branch information
1 parent
2150f15
commit 52a7920
Showing
4 changed files
with
145 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
/** | ||
* Base path for the mainnet Memopool API. | ||
* @default "https://btc-mainnet.gobob.xyz" | ||
*/ | ||
export const MAINNET_MEMPOOL_BASE_PATH = 'https://mempool.space/api/v1/'; | ||
/** | ||
* Base path for the testnet Memopool API. | ||
* @default "https://btc-testnet.gobob.xyz" | ||
*/ | ||
export const TESTNET_MEMPOOL_BASE_PATH = 'https://mempool.space/testnet4/api/v1'; | ||
/** | ||
* Base path for the regtest Memopool API. | ||
* @default "http://localhost:3003" | ||
*/ | ||
export const REGTEST_MEMPOOL_BASE_PATH = 'http://localhost:3003'; | ||
|
||
/** | ||
* @ignore | ||
*/ | ||
export type MempoolRecomendedFee = { | ||
fastestFee: number; | ||
halfHourFee: number; | ||
hourFee: number; | ||
economyFee: number; | ||
minimumFee: number; | ||
}; | ||
|
||
export class MempoolClient { | ||
private basePath: string; | ||
|
||
/** | ||
* Create an instance of the `MempoolPool` with the specified network or URL. | ||
* If the `networkOrUrl` parameter is omitted, it defaults to "mainnet." | ||
* | ||
* @param networkOrUrl The Bitcoin network (e.g., "mainnet," "testnet," "regtest") | ||
* | ||
* @returns An instance of the `MempoolPool` configured for the specified network or URL. | ||
* | ||
* @example | ||
* const BITCOIN_NETWORK = "regtest"; | ||
* const mempoolClient = new MempoolPool(BITCOIN_NETWORK); | ||
* | ||
* @example | ||
* // Create a client for the mainnet using the default URL. | ||
* const mempoolClientMainnet = new MempoolPool(); | ||
*/ | ||
constructor(networkOrUrl: string = 'mainnet') { | ||
switch (networkOrUrl) { | ||
case 'mainnet': | ||
this.basePath = MAINNET_MEMPOOL_BASE_PATH; | ||
break; | ||
case 'testnet': | ||
this.basePath = TESTNET_MEMPOOL_BASE_PATH; | ||
break; | ||
case 'regtest': | ||
this.basePath = REGTEST_MEMPOOL_BASE_PATH; | ||
break; | ||
default: | ||
this.basePath = networkOrUrl; | ||
} | ||
} | ||
|
||
/** | ||
* Get the recommended Bitcoin transaction fee rates from the Mempool API. | ||
* | ||
* This method returns the fee estimates in satoshis per virtual byte (sat/vB) | ||
* for different confirmation targets, including: | ||
* | ||
* - `fastestFee`: The fee rate for transactions that are likely to be included | ||
* in the next block (fastest possible confirmation). | ||
* - `halfHourFee`: The fee rate for transactions that are likely to be confirmed | ||
* within 30 minutes. | ||
* - `hourFee`: The fee rate for transactions that are likely to be confirmed | ||
* within an hour. | ||
* - `economyFee`: The fee rate for transactions that are likely to be confirmed | ||
* in a longer period (low priority). | ||
* - `minimumFee`: The lowest fee rate that is still accepted by miners. | ||
* | ||
* @returns {Promise<MempoolRecomendedFee>} A promise that resolves to an object containing | ||
* the recommended fees for various confirmation times. | ||
* | ||
* @example | ||
* const mempoolClient = new MempoolClient(); | ||
* mempoolClient.getRecommendedFees() | ||
* .then(fees => console.log(fees)) | ||
* .catch(error => console.error('Failed to fetch fees:', error)); | ||
*/ | ||
async getRecommendedFees(): Promise<MempoolRecomendedFee> { | ||
return this.getJson<MempoolRecomendedFee>(`${this.basePath}/fees/recommended`); | ||
} | ||
|
||
/** | ||
* @ignore | ||
*/ | ||
private async getJson<T>(url: string): Promise<T> { | ||
const response = await fetch(url); | ||
if (!response.ok) { | ||
throw new Error(response.statusText); | ||
} | ||
return (await response.json()) as Promise<T>; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; | ||
import { MempoolClient } from '../src/mempool'; | ||
|
||
const MOCKS = { | ||
fees: { | ||
recommended: { | ||
fastestFee: 100, | ||
halfHourFee: 80, | ||
hourFee: 60, | ||
economyFee: 40, | ||
minimumFee: 10, | ||
}, | ||
}, | ||
}; | ||
|
||
describe('Mempool Tests', () => { | ||
const client = new MempoolClient(); | ||
|
||
beforeEach(() => { | ||
// Mock the fetch API only for URLs including /fees/recommended | ||
global.fetch = vi.fn((url) => { | ||
if (url.includes('/fees/recommended')) { | ||
return Promise.resolve({ | ||
ok: true, | ||
json: () => Promise.resolve(MOCKS.fees.recommended), | ||
} as Response); | ||
} | ||
return Promise.reject(new Error('Unexpected URL')); | ||
}); | ||
}); | ||
|
||
afterEach(() => { | ||
vi.clearAllMocks(); | ||
}); | ||
|
||
it('should get recommended fee', async () => { | ||
const fees = await client.getRecommendedFees(); | ||
|
||
expect(fees).toEqual(MOCKS.fees.recommended); | ||
}); | ||
}); |