UNPKG

@atomiqlabs/sdk-lib

Version:

Basic SDK functionality library for atomiq

180 lines (154 loc) 4.68 kB
// baseline estimates, used to improve performance const TX_EMPTY_SIZE = 4 + 1 + 1 + 4; const TX_INPUT_BASE = 32 + 4 + 1 + 4; const WITNESS_OVERHEAD = 2/4; const P2WPKH_WITNESS = (1+1+72+1+33)/4; const P2TR_WITNESS = (1+1+65)/4; const TX_INPUT_PUBKEYHASH = 107; const TX_INPUT_P2SH_P2WPKH = 23 + P2WPKH_WITNESS + 1; const TX_INPUT_P2WPKH = 0 + P2WPKH_WITNESS; const TX_INPUT_P2WSH = 0 + (1+1+64)/4; const TX_INPUT_P2TR = 0 + P2TR_WITNESS; const TX_OUTPUT_BASE = 8 + 1; const TX_OUTPUT_PUBKEYHASH = 25; const TX_OUTPUT_P2SH_P2WPKH = 23; const TX_OUTPUT_P2WPKH = 22; const TX_OUTPUT_P2WSH = 34; const TX_OUTPUT_P2TR = 34; export type CoinselectAddressTypes = "p2sh-p2wpkh" | "p2wpkh" | "p2wsh" | "p2tr" | "p2pkh"; export type CoinselectTxInput = { script?: Buffer, txId: string, vout: number, type?: CoinselectAddressTypes, value: number, outputScript?: Buffer, address?: string, cpfp?: { txVsize: number, txEffectiveFeeRate: number } }; export type CoinselectTxOutput = { script?: Buffer, address?: string, type?: CoinselectAddressTypes, value: number }; const INPUT_BYTES = { "p2sh-p2wpkh": TX_INPUT_P2SH_P2WPKH, "p2wpkh": TX_INPUT_P2WPKH, "p2tr": TX_INPUT_P2TR, "p2pkh": TX_INPUT_PUBKEYHASH, "p2wsh": TX_INPUT_P2WSH }; function inputBytes (input: { script?: Buffer, type?: CoinselectAddressTypes }) { return TX_INPUT_BASE + (input.script ? input.script.length : INPUT_BYTES[input.type]); } const OUTPUT_BYTES = { "p2sh-p2wpkh": TX_OUTPUT_P2SH_P2WPKH, "p2wpkh": TX_OUTPUT_P2WPKH, "p2tr": TX_OUTPUT_P2TR, "p2pkh": TX_OUTPUT_PUBKEYHASH, "p2wsh": TX_OUTPUT_P2WSH }; function outputBytes (output: { script?: Buffer, type?: CoinselectAddressTypes }): number { return TX_OUTPUT_BASE + (output.script ? output.script.length : OUTPUT_BYTES[output.type]); } export const DUST_THRESHOLDS = { "p2sh-p2wpkh": 540, "p2wpkh": 294, "p2tr": 330, "p2pkh": 546, "p2wsh": 330 }; function dustThreshold (output: { script?: Buffer, type: CoinselectAddressTypes }): number { return DUST_THRESHOLDS[output.type]; } function transactionBytes ( inputs: { script?: Buffer, type?: CoinselectAddressTypes }[], outputs: { script?: Buffer, type?: CoinselectAddressTypes }[], changeType: CoinselectAddressTypes ): number { let size = TX_EMPTY_SIZE; let isSegwit = false; if(changeType!=="p2pkh") { size += WITNESS_OVERHEAD; let isSegwit = true; } for(let input of inputs) { if(!isSegwit && (input.type!=="p2pkh")) { isSegwit = true; size += WITNESS_OVERHEAD; } size += inputBytes(input); } for(let output of outputs) { size += outputBytes(output); } return Math.ceil(size); } function uintOrNaN(v: number): number { if (typeof v !== 'number') return NaN; if (!isFinite(v)) return NaN; if (Math.floor(v) !== v) return NaN; if (v < 0) return NaN; return v; } function sumForgiving(range: {value: number}[]): number { return range.reduce((a, x) => a + (isFinite(x.value) ? x.value : 0), 0); } function sumOrNaN(range: {value: number}[]): number { return range.reduce((a, x) => a + uintOrNaN(x.value), 0); } function finalize( inputs: CoinselectTxInput[], outputs: CoinselectTxOutput[], feeRate: number, changeType: CoinselectAddressTypes, cpfpAddFee: number = 0 ): { inputs?: CoinselectTxInput[], outputs?: CoinselectTxOutput[], fee: number } { const bytesAccum = transactionBytes(inputs, outputs, changeType); const feeAfterExtraOutput = (feeRate * (bytesAccum + outputBytes({type: changeType}))) + cpfpAddFee; const remainderAfterExtraOutput = sumOrNaN(inputs) - (sumOrNaN(outputs) + feeAfterExtraOutput) // is it worth a change output? if (remainderAfterExtraOutput >= dustThreshold({type: changeType})) { outputs = outputs.concat({ value: remainderAfterExtraOutput, type: changeType }) } const fee = sumOrNaN(inputs) - sumOrNaN(outputs) if (!isFinite(fee)) return { fee: (feeRate * bytesAccum) + cpfpAddFee } return { inputs: inputs, outputs: outputs, fee: fee } } export const utils = { dustThreshold: dustThreshold, finalize: finalize, inputBytes: inputBytes, outputBytes: outputBytes, sumOrNaN: sumOrNaN, sumForgiving: sumForgiving, transactionBytes: transactionBytes, uintOrNaN: uintOrNaN };