UNPKG

@ordinalsbot/bitcoin-fee-estimator

Version:

A library for calculating Bitcoin transaction fees

109 lines (108 loc) 4.95 kB
"use strict"; 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; }