Skip to content

Commit

Permalink
feat: modify fee rate fetching by adding complex logic (#47)
Browse files Browse the repository at this point in the history
* feat: modify fee rate fetching by adding complex logic
  • Loading branch information
Polybius93 authored Dec 17, 2024
1 parent 8545bd0 commit ccdc7bd
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 36 deletions.
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
# dlc-btc-lib

**dlc-btc-lib** is a comprehensive library for interacting with DLC.Link smart contracts and the Bitcoin blockchain. It includes functions for creating valid Partially Signed Bitcoin Transactions, handling setup, deposit, and withdrawal interactions, and interfacing with Attestors. This library provides all the essential tools and utilities for seamless blockchain and smart contract interactions.

## Fee Rate Calculation

The transaction fee rate is calculated by taking the maximum value among three metrics from the Bitcoin blockchain:

- Average fee rate from the last two blocks `/api/v1/mining/blocks/fee-rates/24h`
- Current mempool block's median fee rate `/api/v1/fees/mempool-blocks`
- Recommended "fastest" fee rate by API `/api/v1/fees/recommended`

Each metric is adjusted by an optional multiplier (defaults to 1.0) and the final result is rounded up to the nearest whole number. For regtest environments, a fixed fee rate of 2 is used.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"type": "module",
"name": "dlc-btc-lib",
"version": "2.5.0",
"version": "2.5.1",
"description": "This library provides a comprehensive set of interfaces and functions for minting dlcBTC tokens on supported blockchains.",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
16 changes: 11 additions & 5 deletions src/dlc-handlers/abstract-dlc-handler.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Transaction } from '@scure/btc-signer';
import { P2Ret, P2TROut } from '@scure/btc-signer/payment';
import { Network } from 'bitcoinjs-lib';
import { regtest } from 'bitcoinjs-lib/src/networks.js';

import {
createNativeSegwitPayment,
Expand Down Expand Up @@ -133,11 +134,16 @@ export abstract class AbstractDLCHandler {
}
}

private async getFeeRate(feeRateMultiplier?: number, customFeeRate?: bigint): Promise<bigint> {
return (
customFeeRate ??
BigInt(await getFeeRate(this.bitcoinBlockchainFeeRecommendationAPI, feeRateMultiplier))
);
protected async getFeeRate(feeRateMultiplier?: number, customFeeRate?: bigint): Promise<bigint> {
if (customFeeRate) {
return customFeeRate;
}

if (this.bitcoinNetwork === regtest) {
return BigInt(2);
}

return BigInt(await getFeeRate(this.bitcoinBlockchainFeeRecommendationAPI, feeRateMultiplier));
}

async createFundingPSBT(
Expand Down
13 changes: 3 additions & 10 deletions src/dlc-handlers/ledger-dlc-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {
deriveUnhardenedPublicKey,
ecdsaPublicKeyToSchnorr,
getBalance,
getFeeRate,
getInputByPaymentTypeArray,
getUnspendableKeyCommittedToUUID,
} from '../functions/bitcoin/bitcoin-functions.js';
Expand Down Expand Up @@ -246,9 +245,7 @@ export class LedgerDLCHandler extends AbstractDLCHandler {
attestorGroupPublicKey
);

const feeRate =
customFeeRate ??
BigInt(await getFeeRate(this.bitcoinBlockchainFeeRecommendationAPI, feeRateMultiplier));
const feeRate = await this.getFeeRate(feeRateMultiplier, customFeeRate);

const addressBalance = await getBalance(fundingPayment, this.bitcoinBlockchainAPI);

Expand Down Expand Up @@ -327,9 +324,7 @@ export class LedgerDLCHandler extends AbstractDLCHandler {
attestorGroupPublicKey
);

const feeRate =
customFeeRate ??
BigInt(await getFeeRate(this.bitcoinBlockchainFeeRecommendationAPI, feeRateMultiplier));
const feeRate = await this.getFeeRate(feeRateMultiplier, customFeeRate);

const withdrawTransaction = await createWithdrawTransaction(
this.bitcoinBlockchainAPI,
Expand Down Expand Up @@ -388,9 +383,7 @@ export class LedgerDLCHandler extends AbstractDLCHandler {
const { fundingPayment, taprootDerivedPublicKey, fundingDerivedPublicKey, multisigPayment } =
await this.createPayment(vault.uuid, attestorGroupPublicKey);

const feeRate =
customFeeRate ??
BigInt(await getFeeRate(this.bitcoinBlockchainFeeRecommendationAPI, feeRateMultiplier));
const feeRate = await this.getFeeRate(feeRateMultiplier, customFeeRate);

const depositTransaction = await createDepositTransaction(
this.bitcoinBlockchainAPI,
Expand Down
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 getLastTwoBlocksFeeRateAverage(
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)
.reduce((a, b) => a + b) / 2
);
}

/**
* 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 multipliedFeeRate;
/**
* Return the fee rate for the transaction.
*
* @returns A promise that resolves to the fee rate.
*/
export async function getFeeRate(
bitcoinBlockchainAPIFeeURL: string,
feeRateMultiplier = 1
): Promise<number> {
const [lastTwoBlocksFeeRateAverage, currentBlockFeeRate, estimatedFeeRate] = await Promise.all([
getLastTwoBlocksFeeRateAverage(bitcoinBlockchainAPIFeeURL),
getCurrentMempoolBlockFeeRate(bitcoinBlockchainAPIFeeURL),
getEstimatedFeeRate(bitcoinBlockchainAPIFeeURL),
]);

return Math.ceil(
Math.max(
lastTwoBlocksFeeRateAverage * feeRateMultiplier,
currentBlockFeeRate * feeRateMultiplier,
estimatedFeeRate * feeRateMultiplier
)
);
}

/**
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

0 comments on commit ccdc7bd

Please sign in to comment.