Skip to content

Commit

Permalink
feat: modify fee rate fetching by adding complex logic
Browse files Browse the repository at this point in the history
  • Loading branch information
Polybius93 committed Dec 16, 2024
1 parent 8545bd0 commit fbd863e
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 20 deletions.
92 changes: 72 additions & 20 deletions src/functions/bitcoin/bitcoin-functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ import {
BitcoinInputSigningConfig,
BitcoinTransaction,
BitcoinTransactionVectorOutput,
BlockData,
FeeRates,
HistoricalFeeRate,
PaymentTypes,
UTXO,
} from '../../models/bitcoin-models.js';
Expand Down Expand Up @@ -143,44 +145,94 @@ export function createTaprootMultisigPayment(
}

/**
* Evaluates the fee rate from the bitcoin blockchain API.
* Fetches the last two blocks' fee rates from the bitcoin blockchain API.
*
* @returns The fee rate.
* @returns A promise that resolves to the last two blocks' median fee rates.
*/
function checkFeeRate(feeRate: number | undefined): number {
if (!feeRate || feeRate < 2) {
return 2;
export async function getLastTwoBlocksFeeRate(
bitcoinBlockchainAPIFeeURL: string
): Promise<number[]> {
const dayFeeRateAPI = `${bitcoinBlockchainAPIFeeURL}/api/v1/mining/blocks/fee-rates/24h`;

const response = await fetch(dayFeeRateAPI);

if (!response.ok) {
throw new Error(`Bitcoin Blockchain Fee Rate Response was not OK: ${response.statusText}`);
}
return feeRate;

const historicalFeeRates: HistoricalFeeRate[] = await response.json();

return historicalFeeRates.slice(historicalFeeRates.length - 2).map(rate => rate.avgFee_50);
}

/**
* Fetches the fee rate from the bitcoin blockchain API.
* Fetches the current mempool block median fee rate from the bitcoin blockchain API.
*
* @returns A promise that resolves to the hour fee rate.
* @param bitcoinBlockchainAPIFeeURL
* @returns
*/
export async function getFeeRate(
bitcoinBlockchainAPIFeeURL: string,
feeRateMultiplier?: number
export async function getCurrentMempoolBlockFeeRate(
bitcoinBlockchainAPIFeeURL: string
): Promise<number> {
const response = await fetch(bitcoinBlockchainAPIFeeURL);
const mempoolBlocksAPI = `${bitcoinBlockchainAPIFeeURL}/api/v1/fees/mempool-blocks`;

const response = await fetch(mempoolBlocksAPI);

if (!response.ok) {
throw new Error(`Bitcoin Blockchain Fee Rate Response was not OK: ${response.statusText}`);
}

let feeRates: FeeRates;
const currentBlockFeeRate: BlockData[] = await response.json();

try {
feeRates = await response.json();
} catch (error) {
throw new Error(`Error parsing Bitcoin Blockchain Fee Rate Response JSON: ${error}`);
return currentBlockFeeRate[0].medianFee;
}

/**
* Fetches the estimated fee rate from the bitcoin blockchain API.
*
* @returns A promise that resolves to the fastest fee rate.
*/
export async function getEstimatedFeeRate(bitcoinBlockchainAPIFeeURL: string): Promise<number> {
const estimatedFeeAPI = `${bitcoinBlockchainAPIFeeURL}/api/v1/fees/recommended`;

const response = await fetch(estimatedFeeAPI);

if (!response.ok) {
throw new Error(`Bitcoin Blockchain Fee Rate Response was not OK: ${response.statusText}`);
}

const feeRate = checkFeeRate(feeRates.fastestFee);
const multipliedFeeRate = feeRate * (feeRateMultiplier ?? 1);
const feeRates: FeeRates = await response.json();

return feeRates.fastestFee;
}

/**
* Return the fee rate for the transaction.
*
* @returns A promise that resolves to the fee rate.
*/
export async function getFeeRate(
bitcoinBlockchainAPIFeeURL: string,
feeRateMultiplier?: number,
isRegtest = false
): Promise<number> {
if (isRegtest) return 2;

const multiplier = feeRateMultiplier ?? 1;

const currentBlockFeeRate = await getCurrentMempoolBlockFeeRate(bitcoinBlockchainAPIFeeURL);
const lastTwoBlocksFeeRate = await getLastTwoBlocksFeeRate(bitcoinBlockchainAPIFeeURL);

const feeRates = lastTwoBlocksFeeRate.concat(currentBlockFeeRate);
const feeRateAverage = feeRates.reduce((a, b) => a + b) / feeRates.length;
const feeRateAverageMultiplied = Math.ceil(feeRateAverage) * multiplier;
console.log('Fee Rate Average Multiplied:', feeRateAverageMultiplied);

const estimatedFeeRate = await getEstimatedFeeRate(bitcoinBlockchainAPIFeeURL);
const estimatedFeeRateMultiplied = estimatedFeeRate * multiplier;
console.log('Estimated Fee Rate Multiplied:', estimatedFeeRateMultiplied);

return multipliedFeeRate;
return Math.max(feeRateAverageMultiplied, estimatedFeeRateMultiplied);
}

/**
Expand Down
21 changes: 21 additions & 0 deletions src/models/bitcoin-models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,27 @@ export interface FeeRates {
minimumFee: number;
}

export interface BlockData {
blockSize: number;
blockVSize: number;
nTx: number;
totalFees: number;
medianFee: number;
feeRange: number[];
}

export interface HistoricalFeeRate {
avgHeight: number;
timestamp: number;
avgFee_0: number;
avgFee_10: number;
avgFee_25: number;
avgFee_50: number;
avgFee_75: number;
avgFee_90: number;
avgFee_100: number;
}

export interface PaymentInformation {
fundingPayment: P2Ret | P2TROut;
multisigPayment: P2TROut;
Expand Down
7 changes: 7 additions & 0 deletions tests/unit/bitcoin-functions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
ecdsaPublicKeyToSchnorr,
finalizeUserInputs,
getFeeAmount,
getFeeRate,
getFeeRecipientAddress,
getInputIndicesByScript,
getScriptMatchingOutputFromTransaction,
Expand Down Expand Up @@ -39,6 +40,12 @@ import {
import { TEST_VAULT_UUID_1 } from '../mocks/ethereum.test.constants';

describe('Bitcoin Functions', () => {
describe('getFeeRate', () => {
it('should return the fee rate in satoshis per byte', async () => {
const feeRate = await getFeeRate('https://mempool.space');
console.log(feeRate);
}, 30000);
});
describe('getInputIndicesByScript', () => {
it('correctly retrieves one input index by script', () => {
const transaction = Transaction.fromPSBT(
Expand Down

0 comments on commit fbd863e

Please sign in to comment.