UNPKG

@ordinalsbot/bitcoin-fee-estimator

Version:

A library for calculating Bitcoin transaction fees

254 lines (220 loc) 9.29 kB
import { estimateTxVBytesSimple, estimateTxFee, } from "../src/calculators/baseTx"; import { expect } from "chai"; import { ScriptType } from "../src/types"; describe("Real Transactions", () => { let tolerancePct = 0.01; // 0.1% it("should match 1 P2TR - 1 P2TR spend", () => { // https://mempool.space/signet/tx/d6610a3b03d8e460d0e8b1235413a9690136bc6603cfceaf42f33b5959ef046d const txInfo = { inputType: [ScriptType.P2TR], outputType: [ScriptType.P2TR], vsize: 111, fee: 407, feeRate: 3.67, }; const vBytes = estimateTxVBytesSimple(txInfo.inputType, txInfo.outputType); let tolerance = Math.ceil(txInfo.vsize * tolerancePct); expect(vBytes).closeTo(txInfo.vsize, tolerance, "Size mismatch"); expect(vBytes).gte(txInfo.vsize, "Size lower than expected"); const calculatedFee = estimateTxFee( txInfo.inputType, txInfo.outputType, txInfo.feeRate, ); tolerance = Math.ceil(txInfo.fee * tolerancePct); expect(calculatedFee).closeTo(txInfo.fee, tolerance, "Fee mismatch"); expect(calculatedFee).gte(txInfo.fee, "Fee lower than expected"); const feeRate = calculatedFee / vBytes; tolerance = Math.ceil(txInfo.feeRate * tolerancePct); expect(feeRate).closeTo(txInfo.feeRate, tolerance, "Fee rate mismatch"); expect(feeRate).gte(txInfo.feeRate, "Fee rate lower than expected"); }); it("should match 3 P2WPKH - (25 P2TR - 1 P2WPKH) spend", () => { // https://mempool.space/signet/tx/bf7efd062c99c7ce500e0b706134344e22a485620bf6df80999d97f7bdf71c58 const inputType = Array(3).fill(ScriptType.P2WPKH); const outputType = Array(25).fill(ScriptType.P2TR); outputType.push(ScriptType.P2WPKH); const txInfo = { inputType, outputType, vsize: 1_320, fee: 1_320, feeRate: 1, }; const vBytes = estimateTxVBytesSimple(txInfo.inputType, txInfo.outputType); let tolerance = Math.ceil(txInfo.vsize * tolerancePct); expect(vBytes).closeTo(txInfo.vsize, tolerance, "Size mismatch"); expect(vBytes).gte(txInfo.vsize, "Size lower than expected"); const calculatedFee = estimateTxFee( txInfo.inputType, txInfo.outputType, txInfo.feeRate, ); tolerance = Math.ceil(txInfo.fee * tolerancePct); expect(calculatedFee).closeTo(txInfo.fee, tolerance, "Fee mismatch"); expect(calculatedFee).gte(txInfo.fee, "Fee lower than expected"); const feeRate = calculatedFee / vBytes; tolerance = Math.ceil(txInfo.feeRate * tolerancePct); expect(feeRate).closeTo(txInfo.feeRate, tolerance, "Fee rate mismatch"); expect(feeRate).gte(txInfo.feeRate, "Fee rate lower than expected"); }); it("should match 1 P2PKH - (12 P2WPKH - 2 P2SH) spend", () => { tolerancePct = 0.005; // https://mempool.space/tx/e5b238731246678d6df8f38e0ab0c72093fb9df02fc4f326f80be15975dbecfa const inputType = Array(1).fill(ScriptType.P2WPKH); const outputType = Array(12).fill(ScriptType.P2WPKH); outputType.push(ScriptType.P2SH, ScriptType.P2SH); const txInfo = { inputType, outputType, vsize: 514.25, fee: 2_060, feeRate: 4.01, }; const vBytes = estimateTxVBytesSimple(txInfo.inputType, txInfo.outputType); let tolerance = Math.ceil(txInfo.vsize * tolerancePct); expect(vBytes).closeTo(txInfo.vsize, tolerance, "Size mismatch"); expect(vBytes).gte(txInfo.vsize, "Size lower than expected"); const calculatedFee = estimateTxFee( txInfo.inputType, txInfo.outputType, txInfo.feeRate, ); tolerance = Math.ceil(txInfo.fee * tolerancePct); expect(calculatedFee).closeTo(txInfo.fee, tolerance, "Fee mismatch"); expect(calculatedFee).gte(txInfo.fee, "Fee lower than expected"); const feeRate = calculatedFee / vBytes; tolerance = Math.ceil(txInfo.feeRate * tolerancePct); expect(feeRate).closeTo(txInfo.feeRate, tolerance, "Fee rate mismatch"); expect(feeRate).gte(txInfo.feeRate, "Fee rate lower than expected"); }); it("should match 1 P2TR - 8 P2TR spend", () => { const inputType = Array(1).fill(ScriptType.P2TR); const outputType = Array(8).fill(ScriptType.P2TR); const txInfo = { inputType, outputType, vsize: 412, fee: 4_078, feeRate: 9.9, }; const vBytes = estimateTxVBytesSimple(txInfo.inputType, txInfo.outputType); let tolerance = Math.ceil(txInfo.vsize * tolerancePct); expect(vBytes).closeTo(txInfo.vsize, tolerance, "Size mismatch"); expect(vBytes).gte(txInfo.vsize, "Size lower than expected"); const calculatedFee = estimateTxFee( txInfo.inputType, txInfo.outputType, txInfo.feeRate, ); tolerance = Math.ceil(txInfo.fee * tolerancePct); expect(calculatedFee).closeTo(txInfo.fee, tolerance, "Fee mismatch"); expect(calculatedFee).gte(txInfo.fee, "Fee lower than expected"); const feeRate = calculatedFee / vBytes; tolerance = Math.ceil(txInfo.feeRate * tolerancePct); expect(feeRate).closeTo(txInfo.feeRate, tolerance, "Fee rate mismatch"); expect(feeRate).gte(txInfo.feeRate, "Fee rate lower than expected"); }); it("should match 1 P2WPKH - (1 P2SH - 1 P2WPKH) spend", () => { // https://mempool.space/tx/0875f8f0114183269c7c13a2392ae3bb3df30a4525a49b89de0751970a439469 const inputType = Array(1).fill(ScriptType.P2WPKH); const outputType = [ScriptType.P2SH, ScriptType.P2WPKH]; const txInfo = { inputType, outputType, vsize: 141.25, fee: 2_840, feeRate: 20.1, }; const vBytes = estimateTxVBytesSimple(txInfo.inputType, txInfo.outputType); let tolerance = Math.ceil(txInfo.vsize * tolerancePct); expect(vBytes).closeTo(txInfo.vsize, tolerance, "Size mismatch"); expect(vBytes).gte(txInfo.vsize, "Size lower than expected"); const calculatedFee = estimateTxFee( txInfo.inputType, txInfo.outputType, txInfo.feeRate, ); tolerance = Math.ceil(txInfo.fee * tolerancePct); expect(calculatedFee).closeTo(txInfo.fee, tolerance, "Fee mismatch"); expect(calculatedFee).gte(txInfo.fee, "Fee lower than expected"); const feeRate = calculatedFee / vBytes; tolerance = Math.ceil(txInfo.feeRate * tolerancePct); expect(feeRate).closeTo(txInfo.feeRate, tolerance, "Fee rate mismatch"); expect(feeRate).gte(txInfo.feeRate, "Fee rate lower than expected"); }); it("should match 1 P2SH-P2WPKH - (8 P2WPKH - 2 P2SH - 2 P2PKH) spend", () => { // https://mempool.space/tx/cb7f5859d57998be3e4f236a6059ca6c3c68d12229da4cc527b51e794a8598f8 const inputType = Array(1).fill(ScriptType.P2SH_P2WPKH); const outputType = Array(8).fill(ScriptType.P2WPKH); outputType.push( ScriptType.P2SH, ScriptType.P2SH, ScriptType.P2PKH, ScriptType.P2PKH, ); const txInfo = { inputType, outputType, vsize: 481.25, fee: 539, feeRate: 1.12, }; const vBytes = estimateTxVBytesSimple(txInfo.inputType, txInfo.outputType); let tolerance = Math.ceil(txInfo.vsize * tolerancePct); expect(vBytes).closeTo(txInfo.vsize, tolerance, "Size mismatch"); expect(vBytes).gte(txInfo.vsize, "Size lower than expected"); const calculatedFee = estimateTxFee( txInfo.inputType, txInfo.outputType, txInfo.feeRate, ); tolerance = Math.ceil(txInfo.fee * tolerancePct); expect(calculatedFee).closeTo(txInfo.fee, tolerance, "Fee mismatch"); expect(calculatedFee).gte(txInfo.fee, "Fee lower than expected"); const feeRate = calculatedFee / vBytes; tolerance = Math.ceil(txInfo.feeRate * tolerancePct); expect(feeRate).closeTo(txInfo.feeRate, tolerance, "Fee rate mismatch"); expect(feeRate).gte(txInfo.feeRate, "Fee rate lower than expected"); }); it("should match 1 P2SH-P2WPKH - (1 OP_RETURN - 2 P2WPKH - 1 P2SH) spend", () => { // https://mempool.space/tx/95c8d605c43eebb0ca7f2d9bce8f02ad5348f9952b5615418f41e2135a21aef7 const inputType = Array(1).fill(ScriptType.P2SH_P2WPKH); const outputType = Array(2).fill(ScriptType.P2WPKH); outputType.push(ScriptType.P2SH); const txInfo = { inputType, outputType, vsize: 246, fee: 247_662, feeRate: 1_006, op_return: Buffer.from( "2098acf9ea96b1672479e27daa495df8dbeea5ea3ff16f5ed0e8d814b189a6ff8d00000000000000", "hex", ), }; const vBytes = estimateTxVBytesSimple( txInfo.inputType, txInfo.outputType, txInfo.op_return, ); let tolerance = Math.ceil(txInfo.vsize * tolerancePct); expect(vBytes).closeTo(txInfo.vsize, tolerance, "Size mismatch"); expect(vBytes).gte(txInfo.vsize, "Size lower than expected"); const calculatedFee = estimateTxFee( txInfo.inputType, txInfo.outputType, txInfo.feeRate, txInfo.op_return, ); tolerance = Math.ceil(txInfo.fee * tolerancePct); expect(calculatedFee).closeTo(txInfo.fee, tolerance, "Fee mismatch"); const feeRate = calculatedFee / vBytes; tolerance = Math.ceil(txInfo.feeRate * tolerancePct); expect(feeRate).closeTo(txInfo.feeRate, tolerance, "Fee rate mismatch"); expect(feeRate).gte(txInfo.feeRate, "Fee rate lower than expected"); }); });