@ordinalsbot/bitcoin-fee-estimator
Version:
A library for calculating Bitcoin transaction fees
109 lines (108 loc) • 4.95 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.estimateInscriptionFee = estimateInscriptionFee;
exports.estimateDirectInscriptionFee = estimateDirectInscriptionFee;
exports.getEnvelopeSize = getEnvelopeSize;
const common_1 = require("../common");
const types_1 = require("../types");
const baseTx_1 = require("./baseTx");
function estimateInscriptionFee(fileSize, count, feeRate, envelopeSize = 100, batchMode = null) {
const fileFee = Math.ceil((feeRate * (fileSize + envelopeSize)) / 4);
const commitCount = batchMode ? 1 : count;
let inputTypes = Array(2).fill(types_1.ScriptType.P2TR);
let outputTypes = Array(3).fill(types_1.ScriptType.P2TR);
const commitFee = (0, baseTx_1.estimateTxFee)(inputTypes, outputTypes, feeRate) * commitCount;
inputTypes = Array(1).fill(types_1.ScriptType.P2TR);
outputTypes = Array(1).fill(types_1.ScriptType.P2TR);
let revealFee = (0, baseTx_1.estimateTxFee)(inputTypes, outputTypes, feeRate) * count;
if (batchMode === "separate-outputs") {
outputTypes = Array(count).fill(types_1.ScriptType.P2TR);
revealFee = (0, baseTx_1.estimateTxFee)(inputTypes, outputTypes, feeRate);
}
else if (batchMode === "shared-output") {
revealFee = (0, baseTx_1.estimateTxFee)(inputTypes, outputTypes, feeRate);
}
return commitFee + revealFee + fileFee;
}
function estimateDirectInscriptionFee(feeRate, receiverAddress, files = [], delegates = 0, parentCount = 0, delegatesAverageSize = 33, parentAverageSize = 33) {
const isFiles = files?.length > 0;
const isDelegates = !!delegates;
if ((isFiles && isDelegates) || (!isFiles && !isDelegates)) {
throw new Error("Use either 'files' or 'delegates'");
}
const receiverScriptType = (0, common_1.getScriptTypeFromAddress)(receiverAddress);
let envelopeSize = 0;
if (isFiles) {
for (let i = 0; i < files.length; i++) {
envelopeSize += getEnvelopeSize(files[i], delegates, parentCount, delegatesAverageSize, parentAverageSize, parentCount > 0 || i !== 0);
}
}
if (isDelegates) {
for (let i = 0; i < delegates; i++) {
envelopeSize += getEnvelopeSize(null, delegates, parentCount, delegatesAverageSize, parentAverageSize, parentCount > 0 || i !== 0);
}
}
const inscriptionFee = Math.ceil(((65 + envelopeSize) / 4) * feeRate);
const outputCount = (files?.length || 0) + parentCount + 1 + delegates; // add fee output
const inputTypes = [
types_1.ScriptType.P2TR,
...Array(parentCount).fill(types_1.ScriptType.P2TR_INSCRIPTION),
];
const outputTypes = Array(outputCount - 1).fill(receiverScriptType);
outputTypes.push(types_1.ScriptType.P2WPKH);
const revealFee = (0, baseTx_1.estimateTxFee)(inputTypes, outputTypes, feeRate);
return Math.ceil(revealFee + inscriptionFee);
}
function getEnvelopeSize(file, delegates = 0, parentCount = 0, delegatesAverageSize = 33, parentAverageSize = 33, addPointer = true) {
if (!delegates && !file) {
throw new Error("must provide delegates or files");
}
const parentFieldsSize = parentCount * (1 + 1 + parentAverageSize); // 1 (tag) + 1 (push) + 33/32 (inscription ID)
const pointerFieldSize = addPointer ? 4 : 0;
// delegates
if (!file) {
const envelopeSize = 2 + // OP_0 OP_IF
4 + // "ord" push
parentFieldsSize +
pointerFieldSize +
1 + // tag
1 + // push
delegatesAverageSize + // delegate inscription ID
1; // OP_ENDIF
return envelopeSize;
}
const contentTypeSize = file.mimeType?.length || 0;
const contentTypeField = contentTypeSize > 0 ? 1 + 1 + contentTypeSize : 0;
const numFileChunks = Math.ceil(file.size / 520);
const lastChunkSize = file.size % 520 || 520;
let filePushOps = (numFileChunks - 1) * 3;
filePushOps +=
lastChunkSize <= 75
? 1 // OP_PUSHBYTES_X (1 byte)
: lastChunkSize <= 255
? 2 // OP_PUSHDATA1 (2 bytes)
: 3; // OP_PUSHDATA2 (3 bytes)
const metadataSize = file.metadataSize || 0;
const numMetaChunks = Math.ceil(metadataSize / 520);
const lastMetaChunkSize = metadataSize % 520 || 520;
let metadataPushOps = (numMetaChunks - 1) * 3;
metadataPushOps +=
lastMetaChunkSize <= 75
? 1 // OP_PUSHBYTES_X (1 byte)
: lastMetaChunkSize <= 255
? 2 // OP_PUSHDATA1 (2 bytes)
: 3; // OP_PUSHDATA2 (3 bytes)
const envelopeSize = 2 + // OP_FALSE OP_IF
4 + // "ord" push
parentFieldsSize +
numMetaChunks +
metadataPushOps +
metadataSize +
contentTypeField +
pointerFieldSize +
1 + // OP_PUSH 0
filePushOps +
file.size + // file content
1; // OP_ENDIF
return envelopeSize;
}