ribc/scripts/btcOperator.min.js
2024-02-24 03:18:30 +05:30

1 line
25 KiB
JavaScript

!function(EXPORTS){const btcOperator="object"===typeof module?module.exports:window.btcOperator={},URL="https://blockchain.info/",fetch_api=btcOperator.fetch=function(api,json_res=!0){return new Promise(((resolve,reject)=>{console.debug(URL+api),fetch(URL+api).then((response=>{response.ok?(json_res?response.json():response.text()).then((result=>resolve(result))).catch((error=>reject(error))):response.json().then((result=>reject(result))).catch((error=>reject(error)))})).catch((error=>reject(error)))}))},util=btcOperator.util={};util.Sat_to_BTC=value=>parseFloat((value/1e8).toFixed(8)),util.BTC_to_Sat=value=>parseInt(1e8*value);const broadcastTx=btcOperator.broadcastTx=rawTxHex=>new Promise(((resolve,reject)=>{fetch("https://coinb.in/api/?uid=1&key=12345678901234567890123456789012&setmodule=bitcoin&request=sendrawtransaction",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:"rawtx="+rawTxHex}).then((response=>{response.text().then((resultText=>{let r=resultText.match(/<result>.*<\/result>/);if(r)if(r=r.pop().replace("<result>","").replace("</result>",""),"1"==r){let txid=resultText.match(/<txid>.*<\/txid>/).pop().replace("<txid>","").replace("</txid>","");resolve(txid)}else if("0"==r){let error=resultText.match(/<response>.*<\/response>/).pop().replace("<response>","").replace("</response>","");reject(decodeURIComponent(error.replace(/\+/g," ")))}else reject(resultText);else reject(resultText)})).catch((error=>reject(error)))})).catch((error=>reject(error)))}));Object.defineProperties(btcOperator,{newKeys:{get:()=>{let r=coinjs.newKeys();return r.segwitAddress=coinjs.segwitAddress(r.pubkey).address,r.bech32Address=coinjs.bech32Address(r.pubkey).address,r}},pubkey:{value:key=>key.length>=66?key:64==key.length?coinjs.newPubkey(key):coinjs.wif2pubkey(key).pubkey},address:{value:(key,prefix=void 0)=>coinjs.pubkey2address(btcOperator.pubkey(key),prefix)},segwitAddress:{value:key=>coinjs.segwitAddress(btcOperator.pubkey(key)).address},bech32Address:{value:key=>coinjs.bech32Address(btcOperator.pubkey(key)).address}}),coinjs.compressed=!0;const verifyKey=btcOperator.verifyKey=function(addr,key){if(addr&&key)switch(coinjs.addressDecode(addr).type){case"standard":return btcOperator.address(key)===addr;case"multisig":return btcOperator.segwitAddress(key)===addr;case"bech32":return btcOperator.bech32Address(key)===addr;default:return null}},validateAddress=btcOperator.validateAddress=function(addr){if(!addr)return;let type=coinjs.addressDecode(addr).type;return!!["standard","multisig","bech32","multisigBech32"].includes(type)&&type};btcOperator.multiSigAddress=function(pubKeys,minRequired,bech32=!0){if(!Array.isArray(pubKeys))throw"pubKeys must be an array of public keys";if(pubKeys.length<minRequired)throw"minimum required should be less than the number of pubKeys";return bech32?coinjs.pubkeys2MultisigAddressBech32(pubKeys,minRequired):coinjs.pubkeys2MultisigAddress(pubKeys,minRequired)},btcOperator.decodeRedeemScript=function(redeemScript,bech32=!0){let script=coinjs.script(),decoded=bech32?script.decodeRedeemScriptBech32(redeemScript):script.decodeRedeemScript(redeemScript);return decoded?{address:decoded.address,pubKeys:decoded.pubkeys,redeemScript:decoded.redeemscript,required:decoded.signaturesRequired}:null},btcOperator.convert={},btcOperator.convert.wif=function(source_wif,target_version=coinjs.priv){let keyHex=util.decodeLegacy(source_wif).hex;return!keyHex||keyHex.length<66||!/01$/.test(keyHex)?null:util.encodeLegacy(keyHex,target_version)},btcOperator.convert.legacy2legacy=function(source_addr,target_version=coinjs.pub){let rawHex=util.decodeLegacy(source_addr).hex;return rawHex?util.encodeLegacy(rawHex,target_version):null},btcOperator.convert.legacy2bech=function(source_addr,target_version=coinjs.bech32.version,target_hrp=coinjs.bech32.hrp){let rawHex=util.decodeLegacy(source_addr).hex;return rawHex?util.encodeBech32(rawHex,target_version,target_hrp):null},btcOperator.convert.bech2bech=function(source_addr,target_version=coinjs.bech32.version,target_hrp=coinjs.bech32.hrp){let rawHex=util.decodeBech32(source_addr).hex;return rawHex?util.encodeBech32(rawHex,target_version,target_hrp):null},btcOperator.convert.bech2legacy=function(source_addr,target_version=coinjs.pub){let rawHex=util.decodeBech32(source_addr).hex;return rawHex?util.encodeLegacy(rawHex,target_version):null},btcOperator.convert.multisig2multisig=function(source_addr,target_version=coinjs.multisig){let rawHex=util.decodeLegacy(source_addr).hex;return rawHex?util.encodeLegacy(rawHex,target_version):null},btcOperator.convert.bech2multisig=function(source_addr,target_version=coinjs.multisig){let rawHex=util.decodeBech32(source_addr).hex;return rawHex?(rawHex=Crypto.util.bytesToHex(ripemd160(Crypto.util.hexToBytes(rawHex),{asBytes:!0})),util.encodeLegacy(rawHex,target_version)):null},util.decodeLegacy=function(source){var decode=coinjs.base58decode(source),raw=decode.slice(0,decode.length-4),checksum=decode.slice(decode.length-4),hash=Crypto.SHA256(Crypto.SHA256(raw,{asBytes:!0}),{asBytes:!0});if(hash[0]!=checksum[0]||hash[1]!=checksum[1]||hash[2]!=checksum[2]||hash[3]!=checksum[3])return!1;return{version:raw.shift(),hex:Crypto.util.bytesToHex(raw)}},util.encodeLegacy=function(hex,version){var bytes=Crypto.util.hexToBytes(hex);bytes.unshift(version);var checksum=Crypto.SHA256(Crypto.SHA256(bytes,{asBytes:!0}),{asBytes:!0}).slice(0,4);return coinjs.base58encode(bytes.concat(checksum))},util.decodeBech32=function(source){let decode=coinjs.bech32_decode(source);if(!decode)return!1;var raw=decode.data;let version=raw.shift();return raw=coinjs.bech32_convert(raw,5,8,!1),{hrp:decode.hrp,version:version,hex:Crypto.util.bytesToHex(raw)}},util.encodeBech32=function(hex,version,hrp){var bytes=Crypto.util.hexToBytes(hex);return(bytes=coinjs.bech32_convert(bytes,8,5,!0)).unshift(version),coinjs.bech32_encode(hrp,bytes)},btcOperator.getBalance=addr=>new Promise(((resolve,reject)=>{fetch_api(`q/addressbalance/${addr}`).then((result=>resolve(util.Sat_to_BTC(result)))).catch((error=>reject(error)))}));const BASE_TX_SIZE=12,BASE_INPUT_SIZE=41,LEGACY_INPUT_SIZE=107,BECH32_INPUT_SIZE=27,BECH32_MULTISIG_INPUT_SIZE=35,SEGWIT_INPUT_SIZE=59,MULTISIG_INPUT_SIZE_ES=351,BASE_OUTPUT_SIZE=9,LEGACY_OUTPUT_SIZE=25,BECH32_OUTPUT_SIZE=23,BECH32_MULTISIG_OUTPUT_SIZE=34,SEGWIT_OUTPUT_SIZE=23;function _redeemScript(addr,key){let decode=coinjs.addressDecode(addr);switch(decode.type){case"standard":return!1;case"multisig":return key?coinjs.segwitAddress(btcOperator.pubkey(key)).redeemscript:null;case"bech32":return decode.redeemscript;default:return null}}function _sizePerOutput(addr){switch(coinjs.addressDecode(addr).type){case"standard":return BASE_OUTPUT_SIZE+LEGACY_OUTPUT_SIZE;case"bech32":return BASE_OUTPUT_SIZE+BECH32_OUTPUT_SIZE;case"multisigBech32":return BASE_OUTPUT_SIZE+BECH32_MULTISIG_OUTPUT_SIZE;case"multisig":return BASE_OUTPUT_SIZE+SEGWIT_OUTPUT_SIZE;default:return null}}function validateTxParameters(parameters){let invalids=[];if(parameters.senders&&(Array.isArray(parameters.senders)||(parameters.senders=[parameters.senders]),parameters.senders.forEach((id=>validateAddress(id)?null:invalids.push(id))),invalids.length))throw"Invalid senders:"+invalids;if(parameters.privkeys){if(Array.isArray(parameters.privkeys)||(parameters.privkeys=[parameters.privkeys]),parameters.senders.length!=parameters.privkeys.length)throw"Array length for senders and privkeys should be equal";if(parameters.senders.forEach(((id,i)=>{let key=parameters.privkeys[i];verifyKey(id,key)||invalids.push(id),64===key.length&&(parameters.privkeys[i]=coinjs.privkey2wif(key))})),invalids.length)throw"Invalid private key for address:"+invalids}if(Array.isArray(parameters.receivers)||(parameters.receivers=[parameters.receivers]),parameters.receivers.forEach((id=>validateAddress(id)?null:invalids.push(id))),invalids.length)throw"Invalid receivers:"+invalids;if(parameters.change_address&&!validateAddress(parameters.change_address))throw"Invalid change_address:"+parameters.change_address;if(("number"!=typeof parameters.fee||parameters.fee<=0)&&null!==parameters.fee)throw"Invalid fee:"+parameters.fee;if(Array.isArray(parameters.amounts)||(parameters.amounts=[parameters.amounts]),parameters.receivers.length!=parameters.amounts.length)throw"Array length for receivers and amounts should be equal";if(parameters.amounts.forEach((a=>"number"!=typeof a||a<=0?invalids.push(a):null)),invalids.length)throw"Invalid amounts:"+invalids;return parameters}function createTransaction(senders,redeemScripts,receivers,amounts,fee,change_address,fee_from_receiver){return new Promise(((resolve,reject)=>{let total_amount=parseFloat(amounts.reduce(((t,a)=>t+a),0).toFixed(8));const tx=coinjs.transaction();let output_size=function(tx,receivers,amounts,change_address){let size=0;for(let i in receivers)tx.addoutput(receivers[i],amounts[i]),size+=_sizePerOutput(receivers[i]);return tx.addoutput(change_address,0),size+=_sizePerOutput(change_address),size}(tx,receivers,amounts,change_address);(function(tx,senders,redeemScripts,total_amount,fee,output_size,fee_from_receiver){return new Promise(((resolve,reject)=>{null!==fee?addUTXOs(tx,senders,redeemScripts,fee_from_receiver?total_amount:total_amount+fee,!1).then((result=>{result.fee=fee,resolve(result)})).catch((error=>reject(error))):new Promise(((resolve,reject)=>{fetch("https://api.blockchain.info/mempool/fees").then((response=>{response.ok?response.json().then((result=>resolve(util.Sat_to_BTC(result.regular)))).catch((error=>reject(error))):reject(response)})).catch((error=>reject(error)))})).then((fee_rate=>{let net_fee=BASE_TX_SIZE*fee_rate;net_fee+=output_size*fee_rate,(fee_from_receiver?addUTXOs(tx,senders,redeemScripts,total_amount,!1):addUTXOs(tx,senders,redeemScripts,total_amount+net_fee,fee_rate)).then((result=>{result.fee=parseFloat((net_fee+result.input_size*fee_rate).toFixed(8)),result.fee_rate=fee_rate,resolve(result)})).catch((error=>reject(error)))})).catch((error=>reject(error)))}))})(tx,senders,redeemScripts,total_amount,fee,output_size,fee_from_receiver).then((result=>{if(result.change_amount>0&&result.change_amount>result.fee&&(tx.outs[tx.outs.length-1].value=util.BTC_to_Sat(result.change_amount)),fee_from_receiver){let fee_remaining=util.BTC_to_Sat(result.fee);for(let i=0;i<tx.outs.length-1&&fee_remaining>0;i++)fee_remaining<tx.outs[i].value?(tx.outs[i].value-=fee_remaining,fee_remaining=0):(fee_remaining-=tx.outs[i].value,tx.outs[i].value=0);if(fee_remaining>0)return reject("Send amount is less than fee")}let filtered_outputs=[],dust_value=0;tx.outs.forEach((o=>o.value>=546?filtered_outputs.push(o):dust_value+=o.value)),tx.outs=filtered_outputs,result.fee+=util.Sat_to_BTC(dust_value),result.output_size=output_size,result.output_amount=total_amount-(fee_from_receiver?result.fee:0),result.total_size=BASE_TX_SIZE+output_size+result.input_size,result.transaction=tx,resolve(result)})).catch((error=>reject(error)))}))}function addUTXOs(tx,senders,redeemScripts,required_amount,fee_rate,rec_args={}){return new Promise(((resolve,reject)=>{if(required_amount=parseFloat(required_amount.toFixed(8)),void 0===rec_args.n&&(rec_args.n=0,rec_args.input_size=0,rec_args.input_amount=0),required_amount<=0)return resolve({input_size:rec_args.input_size,input_amount:rec_args.input_amount,change_amount:-1*required_amount});if(rec_args.n>=senders.length)return reject("Insufficient Balance");let addr=senders[rec_args.n],rs=redeemScripts[rec_args.n],addr_type=coinjs.addressDecode(addr).type,size_per_input=function(addr,rs){switch(coinjs.addressDecode(addr).type){case"standard":return BASE_INPUT_SIZE+LEGACY_INPUT_SIZE;case"bech32":return BASE_INPUT_SIZE+BECH32_INPUT_SIZE;case"multisigBech32":return BASE_INPUT_SIZE+BECH32_MULTISIG_INPUT_SIZE;case"multisig":switch(coinjs.script().decodeRedeemScript(rs).type){case"segwit__":return BASE_INPUT_SIZE+SEGWIT_INPUT_SIZE;case"multisig__":return BASE_INPUT_SIZE+MULTISIG_INPUT_SIZE_ES;default:return null}default:return null}}(addr,rs);fetch_api(`unspent?active=${addr}`).then((result=>{let utxos=result.unspent_outputs;for(let i=0;i<utxos.length&&required_amount>0;i++)if(utxos[i].confirmations){var script;if(rs&&rs.length)if(rs.match(/^00/)&&44==rs.length||40==rs.length&&rs.match(/^[a-f0-9]+$/gi)||"multisigBech32"===addr_type){let s=coinjs.script();s.writeBytes(Crypto.util.hexToBytes(rs)),s.writeOp(0),s.writeBytes(coinjs.numToBytes(utxos[i].value.toFixed(0),8)),script=Crypto.util.bytesToHex(s.buffer)}else script=rs;else script=utxos[i].script;tx.addinput(utxos[i].tx_hash_big_endian,utxos[i].tx_output_n,script,4294967293),rec_args.input_size+=size_per_input,rec_args.input_amount+=util.Sat_to_BTC(utxos[i].value),required_amount-=util.Sat_to_BTC(utxos[i].value),fee_rate&&(required_amount+=size_per_input*fee_rate)}rec_args.n+=1,addUTXOs(tx,senders,redeemScripts,required_amount,fee_rate,rec_args).then((result=>resolve(result))).catch((error=>reject(error)))})).catch((error=>reject(error)))}))}btcOperator.editFee=function(tx_hex,new_fee,private_keys,change_only=!0){return new Promise(((resolve,reject)=>{var tx;Array.isArray(private_keys)||(private_keys=[private_keys]),(tx=tx_hex,new Promise(((resolve,reject)=>{"string"==typeof tx&&/^[0-9a-f]{64}$/i.test(tx)?getTx.hex(tx).then((txhex=>resolve(deserializeTx(txhex)))).catch((error=>reject(error))):resolve(deserializeTx(tx))}))).then((tx=>{parseTransaction(tx).then((tx_parsed=>{if(tx_parsed.fee>=new_fee)return reject("Fees can only be increased");var edit_output_address=new Set;!0===change_only?tx_parsed.inputs.forEach((inp=>edit_output_address.add(inp.address))):!1===change_only?tx_parsed.outputs.forEach((out=>edit_output_address.add(out.address))):"string"==typeof change_only?edit_output_address.add(change_only):Array.isArray(change_only)&&change_only.forEach((id=>edit_output_address.add(id)));let inc_fee=util.BTC_to_Sat(new_fee-tx_parsed.fee);if(inc_fee<219)return reject("Insufficient additional fee. Minimum increment: 219");for(let i=tx.outs.length-1;i>=0&&inc_fee>0;i--)if(edit_output_address.has(tx_parsed.outputs[i].address)){let current_value=tx.outs[i].value;current_value instanceof BigInteger&&(current_value=current_value.intValue()),current_value>inc_fee?(tx.outs[i].value=current_value-inc_fee,inc_fee=0):(inc_fee-=current_value,tx.outs[i].value=0)}if(inc_fee>0){let max_possible_fee=util.BTC_to_Sat(new_fee)-inc_fee;return reject(`Insufficient output values to increase fee. Maximum fee possible: ${util.Sat_to_BTC(max_possible_fee)}`)}tx.outs=tx.outs.filter((o=>o.value>=546));let wif_keys=[];for(let i in tx.ins){var addr=tx_parsed.inputs[i].address,value=util.BTC_to_Sat(tx_parsed.inputs[i].value);let addr_decode=coinjs.addressDecode(addr);var privKey=private_keys.find((pk=>verifyKey(addr,pk)));if(!privKey)return reject(`Private key missing for ${addr}`);const rs=_redeemScript(addr,privKey);var script;if(!1===rs?wif_keys.unshift(privKey):wif_keys.push(privKey),rs&&rs.length)if(rs.match(/^00/)&&44==rs.length||40==rs.length&&rs.match(/^[a-f0-9]+$/gi)||"multisigBech32"===addr_decode.type){let s=coinjs.script();s.writeBytes(Crypto.util.hexToBytes(rs)),s.writeOp(0),s.writeBytes(coinjs.numToBytes(value.toFixed(0),8)),script=Crypto.util.bytesToHex(s.buffer)}else script=rs;else{let s=coinjs.script();s.writeOp(118),s.writeOp(169),s.writeBytes(addr_decode.bytes),s.writeOp(136),s.writeOp(172),script=Crypto.util.bytesToHex(s.buffer)}tx.ins[i].script=coinjs.script(script)}tx.witness=!1,console.debug("Unsigned:",tx.serialize()),new Set(wif_keys).forEach((key=>tx.sign(key,1))),resolve(tx.serialize())})).catch((error=>reject(error)))})).catch((error=>reject(error)))}))},btcOperator.sendTx=function(senders,privkeys,receivers,amounts,fee=null,options={}){return new Promise(((resolve,reject)=>{createSignedTx(senders,privkeys,receivers,amounts,fee,options).then((result=>{broadcastTx(result.transaction.serialize()).then((txid=>resolve(txid))).catch((error=>reject(error)))})).catch((error=>reject(error)))}))};const createSignedTx=btcOperator.createSignedTx=function(senders,privkeys,receivers,amounts,fee=null,options={}){return new Promise(((resolve,reject)=>{try{({senders:senders,privkeys:privkeys,receivers:receivers,amounts:amounts}=validateTxParameters({senders:senders,privkeys:privkeys,receivers:receivers,amounts:amounts,fee:fee,change_address:options.change_address}))}catch(e){return reject(e)}let redeemScripts=[],wif_keys=[];for(let i in senders){let rs=_redeemScript(senders[i],privkeys[i]);redeemScripts.push(rs),!1===rs?wif_keys.unshift(privkeys[i]):wif_keys.push(privkeys[i])}if(redeemScripts.includes(null))return reject("Unable to get redeem-script");createTransaction(senders,redeemScripts,receivers,amounts,fee,options.change_address||senders[0],options.fee_from_receiver).then((result=>{let tx=result.transaction;console.debug("Unsigned:",tx.serialize()),new Set(wif_keys).forEach((key=>tx.sign(key,1))),console.debug("Signed:",tx.serialize()),resolve(result)})).catch((error=>reject(error)))}))};function deserializeTx(tx){if("string"==typeof tx||Array.isArray(tx))try{tx=coinjs.transaction().deserialize(tx)}catch{throw"Invalid transaction hex"}else if("object"!=typeof tx||"function"!=typeof tx.sign)throw"Invalid transaction object";return tx}btcOperator.createTx=function(senders,receivers,amounts,fee=null,options={}){return new Promise(((resolve,reject)=>{try{({senders:senders,receivers:receivers,amounts:amounts}=validateTxParameters({senders:senders,receivers:receivers,amounts:amounts,fee:fee,change_address:options.change_address}))}catch(e){return reject(e)}let redeemScripts=senders.map((id=>_redeemScript(id)));if(redeemScripts.includes(null))return reject("Unable to get redeem-script");createTransaction(senders,redeemScripts,receivers,amounts,fee,options.change_address||senders[0],options.fee_from_receiver).then((result=>{result.tx_hex=result.transaction.serialize(),delete result.transaction,resolve(result)})).catch((error=>reject(error)))}))},btcOperator.createMultiSigTx=function(sender,redeemScript,receivers,amounts,fee=null,options={}){return new Promise(((resolve,reject)=>{let addr_type=validateAddress(sender);if(!["multisig","multisigBech32"].includes(addr_type))return reject("Invalid sender (multisig):"+sender);{let script=coinjs.script(),decode="multisig"==addr_type?script.decodeRedeemScript(redeemScript):script.decodeRedeemScriptBech32(redeemScript);if(!decode||decode.address!==sender)return reject("Invalid redeem-script")}try{({receivers:receivers,amounts:amounts}=validateTxParameters({receivers:receivers,amounts:amounts,fee:fee,change_address:options.change_address}))}catch(e){return reject(e)}createTransaction([sender],[redeemScript],receivers,amounts,fee,options.change_address||sender,options.fee_from_receiver).then((result=>{result.tx_hex=result.transaction.serialize(),delete result.transaction,resolve(result)})).catch((error=>reject(error)))}))},btcOperator.signTx=function(tx,privkeys,sighashtype=1){tx=deserializeTx(tx),Array.isArray(privkeys)||(privkeys=[privkeys]);for(let i in privkeys)64===privkeys[i].length&&(privkeys[i]=coinjs.privkey2wif(privkeys[i]));return new Set(privkeys).forEach((key=>tx.sign(key,sighashtype))),tx.serialize()};const checkSigned=btcOperator.checkSigned=function(tx,bool=!0){tx=deserializeTx(tx);let n=[];for(let i in tx.ins){var s=tx.extractScriptKey(i);if("multisig"!==s.type&&"multisig_bech32"!==s.type)n.push("true"==s.signed||tx.witness[i]&&2==tx.witness[i].length);else{var rs=coinjs.script().decodeRedeemScript(s.script);let x={s:s.signatures,r:rs.signaturesRequired,t:rs.pubkeys.length};if(x.r>x.t)throw"signaturesRequired is more than publicKeys";x.s<x.r?n.push(x):n.push(!0)}}return bool?!n.filter((x=>!0!==x)).length:n};btcOperator.checkIfSameTx=function(tx1,tx2){if(tx1=deserializeTx(tx1),tx2=deserializeTx(tx2),tx1.ins.length!==tx2.ins.length||tx1.outs.length!==tx2.outs.length)return!1;for(let i=0;i<tx1.ins.length;i++)if(tx1.ins[i].outpoint.hash!==tx2.ins[i].outpoint.hash||tx1.ins[i].outpoint.index!==tx2.ins[i].outpoint.index)return!1;for(let i=0;i<tx1.outs.length;i++)if(tx1.outs[i].value!==tx2.outs[i].value||Crypto.util.bytesToHex(tx1.outs[i].script.buffer)!==Crypto.util.bytesToHex(tx2.outs[i].script.buffer))return!1;return!0};const getTxOutput=(txid,i)=>new Promise(((resolve,reject)=>{fetch_api(`rawtx/${txid}`).then((result=>resolve(result.out[i]))).catch((error=>reject(error)))})),parseTransaction=btcOperator.parseTransaction=function(tx){return new Promise(((resolve,reject)=>{tx=deserializeTx(tx);let result={},promises=[];for(let i=0;i<tx.ins.length;i++)promises.push(getTxOutput(tx.ins[i].outpoint.hash,tx.ins[i].outpoint.index));Promise.all(promises).then((inputs=>{result.inputs=inputs.map((inp=>Object({address:inp.addr,value:util.Sat_to_BTC(inp.value)})));let signed=checkSigned(tx,!1);result.inputs.forEach(((inp,i)=>inp.signed=signed[i])),result.outputs=tx.outs.map((out=>{var address;switch(out.script.chunks[0]){case 0:address=util.encodeBech32(Crypto.util.bytesToHex(out.script.chunks[1]),coinjs.bech32.version,coinjs.bech32.hrp);break;case 169:address=util.encodeLegacy(Crypto.util.bytesToHex(out.script.chunks[1]),coinjs.multisig);break;case 118:address=util.encodeLegacy(Crypto.util.bytesToHex(out.script.chunks[2]),coinjs.pub)}return{address:address,value:util.Sat_to_BTC(out.value)}})),result.total_input=parseFloat(result.inputs.reduce(((a,inp)=>a+inp.value),0).toFixed(8)),result.total_output=parseFloat(result.outputs.reduce(((a,out)=>a+out.value),0).toFixed(8)),result.fee=parseFloat((result.total_input-result.total_output).toFixed(8)),resolve(result)})).catch((error=>reject(error)))}))};btcOperator.transactionID=function(tx){tx=deserializeTx(tx);let clone=coinjs.clone(tx);clone.witness=null;let raw_bytes=Crypto.util.hexToBytes(clone.serialize()),txid=Crypto.SHA256(Crypto.SHA256(raw_bytes,{asBytes:!0}),{asBytes:!0}).reverse();return Crypto.util.bytesToHex(txid)};const getLatestBlock=btcOperator.getLatestBlock=()=>new Promise(((resolve,reject)=>{fetch_api("q/getblockcount").then((result=>resolve(result))).catch((error=>reject(error)))})),getTx=btcOperator.getTx=txid=>new Promise(((resolve,reject)=>{fetch_api(`rawtx/${txid}`).then((result=>{getLatestBlock().then((latest_block=>resolve({block:result.block_height,txid:result.hash,time:1e3*result.time,confirmations:null===result.block_height?0:latest_block-result.block_height,size:result.size,fee:util.Sat_to_BTC(result.fee),inputs:result.inputs.map((i=>Object({address:i.prev_out.addr,value:util.Sat_to_BTC(i.prev_out.value)}))),total_input_value:util.Sat_to_BTC(result.inputs.reduce(((a,i)=>a+i.prev_out.value),0)),outputs:result.out.map((o=>Object({address:o.addr,value:util.Sat_to_BTC(o.value)}))),total_output_value:util.Sat_to_BTC(result.out.reduce(((a,o)=>a+o.value),0))})))})).catch((error=>reject(error)))}));getTx.hex=txid=>new Promise(((resolve,reject)=>{fetch_api(`rawtx/${txid}?format=hex`,!1).then((result=>resolve(result))).catch((error=>reject(error)))})),btcOperator.getAddressData=address=>new Promise(((resolve,reject)=>{fetch_api(`rawaddr/${address}`).then((data=>{let details={};details.balance=util.Sat_to_BTC(data.final_balance),details.address=data.address,details.txs=data.txs.map((tx=>{let d={txid:tx.hash,time:1e3*tx.time,block:tx.block_height,tx_senders:{}};tx.inputs.forEach((i=>{i.prev_out.addr in d.tx_senders?d.tx_senders[i.prev_out.addr]+=i.prev_out.value:d.tx_senders[i.prev_out.addr]=i.prev_out.value})),d.tx_input_value=0;for(let s in d.tx_senders){let val=d.tx_senders[s];d.tx_senders[s]=util.Sat_to_BTC(val),d.tx_input_value+=val}d.tx_input_value=util.Sat_to_BTC(d.tx_input_value),d.tx_receivers={},tx.out.forEach((o=>{o.addr in d.tx_receivers?d.tx_receivers[o.addr]+=o.value:d.tx_receivers[o.addr]=o.value})),d.tx_output_value=0;for(let r in d.tx_receivers){let val=d.tx_receivers[r];d.tx_receivers[r]=util.Sat_to_BTC(val),d.tx_output_value+=val}return d.tx_output_value=util.Sat_to_BTC(d.tx_output_value),d.tx_fee=util.Sat_to_BTC(tx.fee),tx.result>0?(d.type="in",d.amount=util.Sat_to_BTC(tx.result),d.sender=Object.keys(d.tx_senders).filter((s=>s!==address))):Object.keys(d.tx_receivers).some((r=>r!==address))?(d.type="out",d.amount=util.Sat_to_BTC(-1*tx.result),d.receiver=Object.keys(d.tx_receivers).filter((r=>r!==address)),d.fee=d.tx_fee):(d.type="self",d.amount=d.tx_receivers[address],d.address=address),d})),resolve(details)})).catch((error=>reject(error)))})),btcOperator.getBlock=block=>new Promise(((resolve,reject)=>{fetch_api(`rawblock/${block}`).then((result=>resolve({height:result.height,hash:result.hash,merkle_root:result.mrkl_root,prev_block:result.prev_block,next_block:result.next_block[0],size:result.size,time:1e3*result.time,txs:result.tx.map((t=>Object({fee:t.fee,size:t.size,inputs:t.inputs.map((i=>Object({address:i.prev_out.addr,value:util.Sat_to_BTC(i.prev_out.value)}))),total_input_value:util.Sat_to_BTC(t.inputs.reduce(((a,i)=>a+i.prev_out.value),0)),outputs:t.out.map((o=>Object({address:o.addr,value:util.Sat_to_BTC(o.value)}))),total_output_value:util.Sat_to_BTC(t.out.reduce(((a,o)=>a+o.value),0))})))}))).catch((error=>reject(error)))}))}();