import * as BN from 'bn.js' ; import * as BitcoinJsAddress from '../address' ; import { p2data } from '../payments/embed' ; import { Permutation } from './permutation' ; import { reverseBuffer } from '../bufferutils' ; import type { ComposeInput , ComposeFinalOutput } from './request' ; import type { CoinSelectInput , CoinSelectOutputFinal } from '../coinselect' ; import type { Network } from '../networks' ; export type ComposedTxOutput = { path : number [ ] ; value : string ; address ? : typeof undefined ; opReturnData ? : typeof undefined ; } | { address : string ; value : string ; path ? : typeof undefined ; opReturnData ? : typeof undefined ; } | { opReturnData : Buffer ; path ? : typeof undefined ; address ? : typeof undefined ; value ? : typeof undefined ; } ; export type ComposedTxInput = { hash : Buffer ; index : number ; path : number [ ] ; amount : string ; } ; export type ComposedTransaction = { inputs : ComposedTxInput [ ] ; outputs : Permutation < ComposedTxOutput > ; } ; function convertInput ( utxo , basePath ) { return { hash : reverseBuffer ( Buffer . from ( utxo . transactionHash , 'hex' ) ) , index : utxo . index , path : basePath . concat ( [ ... utxo . addressPath ] ) , amount : utxo . value , } ; } function convertOpReturnOutput ( opReturnData ) { const opReturnDataBuffer = Buffer . from ( opReturnData , 'hex' ) ; const output = { opReturnData : opReturnDataBuffer , value : undefined , } ; const script = p2data ( { data : [ opReturnDataBuffer ] } ) . output as Buffer ; return { output , script , } ; } function convertOutput ( address , value , network , basePath , changeId , isChange ) { const output = isChange ? { path : [ ... basePath , 1 , changeId ] , value , } : { address , value , } ; return { output , script : BitcoinJsAddress . toOutputScript ( address , network ) , } ; } function inputComparator ( aHash , aVout , bHash , bVout ) { return reverseBuffer ( aHash ) . compare ( reverseBuffer ( bHash ) ) || aVout - bVout ; } function outputComparator ( aScript , aValue , bScript , bValue ) { return new BN ( aValue ) . sub ( new BN ( bValue ) ) . toNumber ( ) || aScript . compare ( bScript ) ; } export function createTransaction ( allInputs , selectedInputs , allOutputs , selectedOutputs , basePath , changeId , changeAddress , network , skipPermutation ? ) { const convertedInputs = selectedInputs . map ( input => convertInput ( allInputs [ input . i ] , basePath ) ) ; const convertedOutputs = selectedOutputs . map ( ( output , i ) => { const isChange = i === allOutputs . length ; const original = allOutputs [ i ] ; if ( original && original . type === 'opreturn' ) { return convertOpReturnOutput ( original . dataHex ) ; } const address = ! original ? changeAddress : original . address ; const amount = output . value ; return convertOutput ( address , amount , network , basePath , changeId , isChange ) ; } ) ; if ( skipPermutation ) { return { inputs : convertedInputs , outputs : new Permutation ( convertedOutputs . map ( o => o . output ) , convertedOutputs . map ( ( _o , i ) => i ) ) , } ; } convertedInputs . sort ( ( a , b ) => inputComparator ( a . hash , a . index , b . hash , b . index ) ) ; const permutedOutputs = Permutation . fromFunction ( convertedOutputs , ( a , b ) => { const aValue = typeof a . output . value === 'string' ? a . output . value : '0' ; const bValue = typeof b . output . value === 'string' ? b . output . value : '0' ; return outputComparator ( a . script , aValue , b . script , bValue ) ; } ) . map ( o => o . output ) ; return { inputs : convertedInputs , outputs : permutedOutputs , } ; }