UNPKG

lotus-sdk

Version:

Central repository for several classes of tools for integrating with, and building for, the Lotusia ecosystem

168 lines (167 loc) 6.06 kB
import { Preconditions } from '../util/preconditions.js'; import { JSUtil } from '../util/js.js'; import { Script } from '../script.js'; import { Address } from '../address.js'; import { PublicKey } from '../publickey.js'; import { Unit } from '../unit.js'; export class UnspentOutput { address; txId; outputIndex; script; satoshis; internalPubKey; merkleRoot; keyAggContext; mySignerIndex; constructor(data) { Preconditions.checkArgument(typeof data === 'object' && data !== null, 'Must provide an object from where to extract data'); const address = data.address ? new Address(data.address) : undefined; const txId = data.txid || data.txId; if (!txId || !JSUtil.isHexaString(txId) || txId.length > 64) { throw new Error('Invalid TXID in object: ' + JSON.stringify(data)); } const outputIndex = data.vout !== undefined ? data.vout : data.outputIndex; if (typeof outputIndex !== 'number') { throw new Error('Invalid outputIndex, received ' + outputIndex); } Preconditions.checkArgument(data.scriptPubKey !== undefined || data.script !== undefined, 'Must provide the scriptPubKey for that output!'); const script = new Script(data.scriptPubKey || data.script); Preconditions.checkArgument(data.amount !== undefined || data.satoshis !== undefined, 'Must provide an amount for the output'); let amount; if (data.amount !== undefined) { amount = Number(Unit.fromXPI(data.amount).toSatoshis()); } else if (data.satoshis !== undefined) { amount = typeof data.satoshis === 'bigint' ? Number(data.satoshis) : data.satoshis; } else { throw new Error('No amount provided'); } Preconditions.checkArgument(typeof amount === 'number', 'Amount must be a number'); this.address = address; this.txId = txId; this.outputIndex = outputIndex; this.script = script; this.satoshis = amount; if (data.internalPubKey) { if (data.internalPubKey instanceof PublicKey) { this.internalPubKey = data.internalPubKey; } else if (Buffer.isBuffer(data.internalPubKey)) { this.internalPubKey = new PublicKey(data.internalPubKey); } else if (typeof data.internalPubKey === 'string') { this.internalPubKey = new PublicKey(data.internalPubKey); } } this.merkleRoot = data.merkleRoot; this.keyAggContext = data.keyAggContext; this.mySignerIndex = data.mySignerIndex; } getXPI() { return this.satoshis / 1000000; } getUnit() { return Unit.fromSatoshis(this.satoshis); } isValid() { return (JSUtil.isHexaString(this.txId) && this.txId.length === 64 && this.outputIndex >= 0 && this.satoshis > 0 && this.script.isValid()); } isDust(dustThreshold = 546) { return this.satoshis < dustThreshold; } toObject() { return { address: this.address ? this.address.toString() : undefined, txid: this.txId, vout: this.outputIndex, scriptPubKey: this.script.toBuffer().toString('hex'), amount: Unit.fromSatoshis(this.satoshis).toXPI(), }; } toJSON = this.toObject; inspect() { return ('<UnspentOutput: ' + this.txId + ':' + this.outputIndex + ', satoshis: ' + this.satoshis + ', address: ' + this.address + '>'); } clone() { return new UnspentOutput({ txId: this.txId, outputIndex: this.outputIndex, script: this.script.clone(), satoshis: this.satoshis, address: this.address, internalPubKey: this.internalPubKey, merkleRoot: this.merkleRoot, keyAggContext: this.keyAggContext, mySignerIndex: this.mySignerIndex, }); } toString() { return this.txId + ':' + this.outputIndex; } isPayToPublicKeyHash() { return this.script.isPayToPublicKeyHash(); } isPayToScriptHash() { return this.script.isPayToScriptHash(); } getAddress() { try { const addressInfo = this.script.getAddressInfo(); return addressInfo ? addressInfo.toString() : null; } catch (e) { return null; } } static fromObject(obj) { return new UnspentOutput(obj); } static fromObjects(objects) { return objects.map(obj => new UnspentOutput(obj)); } static filterByAddress(unspentOutputs, address) { const addressStr = typeof address === 'string' ? address : address.toString(); return unspentOutputs.filter(utxo => { const utxoAddress = utxo.getAddress(); return utxoAddress === addressStr; }); } static filterByMinAmount(unspentOutputs, minAmount) { const minSatoshis = typeof minAmount === 'bigint' ? Number(minAmount) : minAmount; return unspentOutputs.filter(utxo => utxo.satoshis >= minSatoshis); } static calculateTotal(unspentOutputs) { return unspentOutputs.reduce((total, utxo) => total + utxo.satoshis, 0); } static selectForAmount(unspentOutputs, targetAmount) { const targetSatoshis = typeof targetAmount === 'bigint' ? Number(targetAmount) : targetAmount; const selected = []; let total = 0; const sorted = [...unspentOutputs].sort((a, b) => b.satoshis - a.satoshis); for (const utxo of sorted) { selected.push(utxo); total += utxo.satoshis; if (total >= targetSatoshis) { break; } } return selected; } }