UNPKG

bitcore-wallet-client

Version:
427 lines 19.5 kB
'use strict'; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Utils = void 0; const crypto_wallet_core_1 = require("crypto-wallet-core"); const json_stable_stringify_1 = __importDefault(require("json-stable-stringify")); const preconditions_1 = require("preconditions"); const sjcl_1 = __importDefault(require("sjcl")); const constants_1 = require("./constants"); const defaults_1 = require("./defaults"); const $ = (0, preconditions_1.singleton)(); const Bitcore_ = { btc: crypto_wallet_core_1.BitcoreLib, bch: crypto_wallet_core_1.BitcoreLibCash, eth: crypto_wallet_core_1.BitcoreLib, matic: crypto_wallet_core_1.BitcoreLib, arb: crypto_wallet_core_1.BitcoreLib, base: crypto_wallet_core_1.BitcoreLib, op: crypto_wallet_core_1.BitcoreLib, xrp: crypto_wallet_core_1.BitcoreLib, doge: crypto_wallet_core_1.BitcoreLibDoge, ltc: crypto_wallet_core_1.BitcoreLibLtc, sol: crypto_wallet_core_1.BitcoreLib }; const PrivateKey = crypto_wallet_core_1.BitcoreLib.PrivateKey; const PublicKey = crypto_wallet_core_1.BitcoreLib.PublicKey; const crypto = crypto_wallet_core_1.BitcoreLib.crypto; const MAX_DECIMAL_ANY_CHAIN = 18; class Utils { static getChain(coin) { try { let normalizedChain = coin.toLowerCase(); if (constants_1.Constants.BITPAY_SUPPORTED_ETH_ERC20.includes(normalizedChain) || !constants_1.Constants.CHAINS.includes(normalizedChain)) { normalizedChain = 'eth'; } return normalizedChain; } catch (_) { return 'btc'; } } static encryptMessage(message, encryptingKey) { var key = sjcl_1.default.codec.base64.toBits(encryptingKey); return sjcl_1.default.encrypt(key, message, { ks: 128, iter: 1 }); } static decryptMessage(cyphertextJson, encryptingKey) { if (!cyphertextJson) return; if (!encryptingKey) throw new Error('No key'); var key = sjcl_1.default.codec.base64.toBits(encryptingKey); return sjcl_1.default.decrypt(key, cyphertextJson); } static decryptMessageNoThrow(cyphertextJson, encryptingKey) { if (!encryptingKey) return '<ECANNOTDECRYPT>'; if (!cyphertextJson) return ''; var r = this.isJsonString(cyphertextJson); if (!r || !r.iv || !r.ct) { return cyphertextJson; } try { return this.decryptMessage(cyphertextJson, encryptingKey); } catch (e) { return '<ECANNOTDECRYPT>'; } } static isJsonString(str) { var r; try { r = JSON.parse(str); } catch (e) { return false; } return r; } static hashMessage(text) { $.checkArgument(text); var buf = Buffer.from(text); var ret = crypto.Hash.sha256sha256(buf); ret = new crypto_wallet_core_1.BitcoreLib.encoding.BufferReader(ret).readReverse(); return ret; } static signMessage(message, privKey) { $.checkArgument(message); var priv = new PrivateKey(privKey); const flattenedMessage = Array.isArray(message) ? message.join(',') : message; var hash = this.hashMessage(flattenedMessage); return crypto.ECDSA.sign(hash, priv, { endian: 'little' }).toString(); } static verifyMessage(message, signature, pubKey) { $.checkArgument(message); $.checkArgument(pubKey); if (!signature) return false; var pub = new PublicKey(pubKey); const flattenedMessage = Array.isArray(message) ? message.join(',') : message; const hash = this.hashMessage(flattenedMessage); try { var sig = new crypto.Signature.fromString(signature); return crypto.ECDSA.verify(hash, sig, pub, { endian: 'little' }); } catch (e) { return false; } } static privateKeyToAESKey(privKey) { $.checkArgument(privKey && typeof privKey === 'string'); $.checkArgument(crypto_wallet_core_1.BitcoreLib.PrivateKey.isValid(privKey), 'The private key received is invalid'); var pk = crypto_wallet_core_1.BitcoreLib.PrivateKey.fromString(privKey); return crypto_wallet_core_1.BitcoreLib.crypto.Hash.sha256(pk.toBuffer()) .slice(0, 16) .toString('base64'); } static getCopayerHash(name, xPubKey, requestPubKey) { return [name, xPubKey, requestPubKey].join('|'); } static getProposalHash(proposalHeader, ...args) { if (args.length > 0) { return this.getOldHash.apply(this, [proposalHeader, ...args]); } return (0, json_stable_stringify_1.default)(proposalHeader); } static getOldHash(toAddress, amount, message, payProUrl) { return [toAddress, amount, message || '', payProUrl || ''].join('|'); } static parseDerivationPath(path) { const pathIndex = /m\/([0-9]*)\/([0-9]*)/; const [_input, changeIndex, addressIndex] = path.match(pathIndex); const isChange = Number.parseInt(changeIndex) > 0; return { _input, addressIndex, isChange }; } static deriveAddress(scriptType, publicKeyRing, path, m, network, chain, escrowInputs, hardwareSourcePublicKey, clientDerivedPublicKey) { $.checkArgument(Object.values(constants_1.Constants.SCRIPT_TYPES).includes(scriptType)); const externSourcePublicKey = hardwareSourcePublicKey || clientDerivedPublicKey; if (externSourcePublicKey) { const bitcoreAddress = crypto_wallet_core_1.Deriver.getAddress(chain.toUpperCase(), network, externSourcePublicKey, scriptType); return { address: bitcoreAddress.toString(), path, publicKeys: [externSourcePublicKey] }; } chain = chain || 'btc'; const bitcore = Bitcore_[chain]; let publicKeys = (publicKeyRing || []).map(item => { var xpub = new bitcore.HDPublicKey(item.xPubKey); return xpub.deriveChild(path).publicKey; }); var bitcoreAddress; switch (scriptType) { case constants_1.Constants.SCRIPT_TYPES.P2WSH: const nestedWitness = false; bitcoreAddress = bitcore.Address.createMultisig(publicKeys, m, network, nestedWitness, 'witnessscripthash'); break; case constants_1.Constants.SCRIPT_TYPES.P2SH: if (escrowInputs) { var xpub = new bitcore.HDPublicKey(publicKeyRing[0].xPubKey); const inputPublicKeys = escrowInputs.map(input => xpub.deriveChild(input.path).publicKey); bitcoreAddress = bitcore.Address.createEscrow(inputPublicKeys, publicKeys[0], network); publicKeys = [publicKeys[0], ...inputPublicKeys]; } else { bitcoreAddress = bitcore.Address.createMultisig(publicKeys, m, network); } break; case constants_1.Constants.SCRIPT_TYPES.P2WPKH: bitcoreAddress = bitcore.Address.fromPublicKey(publicKeys[0], network, 'witnesspubkeyhash'); break; case constants_1.Constants.SCRIPT_TYPES.P2PKH: $.checkState(Array.isArray(publicKeys) && publicKeys.length == 1, 'publicKeys array undefined'); if (constants_1.Constants.UTXO_CHAINS.includes(chain)) { bitcoreAddress = bitcore.Address.fromPublicKey(publicKeys[0], network); } else { const { addressIndex, isChange } = this.parseDerivationPath(path); const [{ xPubKey }] = publicKeyRing; bitcoreAddress = crypto_wallet_core_1.Deriver.deriveAddress(chain.toUpperCase(), network, xPubKey, addressIndex, isChange); } break; case constants_1.Constants.SCRIPT_TYPES.P2TR: bitcoreAddress = bitcore.Address.fromPublicKey(publicKeys[0], network, 'taproot'); break; } return { address: bitcoreAddress.toString(true), path, publicKeys: publicKeys.map(p => p.toString()) }; } static xPubToCopayerId(_chain, xpub) { const chain = _chain.toLowerCase(); var str = chain == 'btc' ? xpub : chain + xpub; var hash = sjcl_1.default.hash.sha256.hash(str); return sjcl_1.default.codec.hex.fromBits(hash); } static signRequestPubKey(requestPubKey, xPrivKey) { var priv = new crypto_wallet_core_1.BitcoreLib.HDPrivateKey(xPrivKey).deriveChild(constants_1.Constants.PATHS.REQUEST_KEY_AUTH).privateKey; return this.signMessage(requestPubKey, priv); } static verifyRequestPubKey(requestPubKey, signature, xPubKey) { var pub = new crypto_wallet_core_1.BitcoreLib.HDPublicKey(xPubKey).deriveChild(constants_1.Constants.PATHS.REQUEST_KEY_AUTH).publicKey; return this.verifyMessage(requestPubKey, signature, pub.toString()); } static formatAmount(satoshis, unit, opts) { $.shouldBeNumber(satoshis); var clipDecimals = (number, decimals) => { let str = number.toString(); if (str.indexOf('e') >= 0) { str = number.toFixed(MAX_DECIMAL_ANY_CHAIN); } var x = str.split('.'); var d = (x[1] || '0').substring(0, decimals); const ret = parseFloat(x[0] + '.' + d); return ret; }; var addSeparators = (nStr, thousands, decimal, minDecimals) => { nStr = nStr.replace('.', decimal); var x = nStr.split(decimal); var x0 = x[0]; var x1 = x[1] || ''; while (x1.endsWith('0') && x1.length > minDecimals) { x1 = x1.slice(0, -1); } var x2 = x.length > 1 ? decimal + x1 : ''; x0 = x0.replace(/\B(?=(\d{3})+(?!\d))/g, thousands); return x0 + x2; }; opts = opts || {}; var u = constants_1.Constants.UNITS[unit]; var precision = opts.fullPrecision ? 'full' : 'short'; var decimals = opts.decimals ? opts.decimals[precision] : u[precision]; var toSatoshis = opts.toSatoshis ? opts.toSatoshis : u.toSatoshis; var amount = clipDecimals(satoshis / toSatoshis, decimals.maxDecimals).toFixed(decimals.maxDecimals); return addSeparators(amount, opts.thousandsSeparator || ',', opts.decimalSeparator || '.', decimals.minDecimals); } static buildTx(txp) { var _a; var chain = ((_a = txp.chain) === null || _a === void 0 ? void 0 : _a.toLowerCase()) || Utils.getChain(txp.coin); if (constants_1.Constants.UTXO_CHAINS.includes(chain)) { var bitcore = Bitcore_[chain]; var t = new bitcore.Transaction(); if (txp.version >= 4) { t.setVersion(2); } else { t.setVersion(1); } $.checkState(Object.values(constants_1.Constants.SCRIPT_TYPES).includes(txp.addressType), 'Failed state: addressType not in SCRIPT_TYPES'); switch (txp.addressType) { case constants_1.Constants.SCRIPT_TYPES.P2WSH: case constants_1.Constants.SCRIPT_TYPES.P2SH: for (const i of txp.inputs || []) { t.from(i, i.publicKeys, txp.requiredSignatures); } break; case constants_1.Constants.SCRIPT_TYPES.P2WPKH: case constants_1.Constants.SCRIPT_TYPES.P2PKH: case constants_1.Constants.SCRIPT_TYPES.P2TR: t.from(txp.inputs); break; } if (txp.toAddress && txp.amount && !txp.outputs) { t.to(txp.toAddress, txp.amount); } else if (txp.outputs) { for (const o of (txp.outputs || [])) { $.checkState(o.script || o.toAddress, 'Output should have either toAddress or script specified'); if (o.script) { t.addOutput(new bitcore.Transaction.Output({ script: o.script, satoshis: o.amount })); } else { t.to(o.toAddress, o.amount); } } } t.fee(txp.fee); if (txp.instantAcceptanceEscrow && txp.escrowAddress) { t.escrow(txp.escrowAddress.address, txp.instantAcceptanceEscrow + txp.fee); } t.change(txp.changeAddress.address); if (txp.enableRBF) t.enableRBF(); if (t.outputs.length > 1) { const outputOrder = (txp.outputOrder || []).filter(order => order < t.outputs.length); $.checkState(t.outputs.length === outputOrder.length, 'Failed state: t.ouputs.length == outputOrder.length at buildTx()'); t.sortOutputs(outputs => outputOrder.map(i => outputs[i])); } const totalInputs = (txp.inputs || []).reduce((memo, i) => { return +i.satoshis + memo; }, 0); const totalOutputs = (t.outputs || []).reduce((memo, o) => { return +o.satoshis + memo; }, 0); $.checkState(totalInputs - totalOutputs >= 0, 'Failed state: totalInputs - totalOutputs >= 0 at buildTx'); $.checkState(totalInputs - totalOutputs <= defaults_1.Defaults.MAX_TX_FEE(chain), 'Failed state: totalInputs - totalOutputs <= Defaults.MAX_TX_FEE(chain) at buildTx'); return t; } else { const { data, destinationTag, outputs, payProUrl, tokenAddress, multisigContractAddress, multiSendContractAddress, isTokenSwap, gasLimit, multiTx, outputOrder } = txp; const recipients = outputs.map(output => { return { amount: output.amount, address: output.toAddress, data: output.data, gasLimit: output.gasLimit }; }); if (data) { recipients[0].data = data; } const unsignedTxs = []; const isToken = tokenAddress && !payProUrl && !isTokenSwap; const isMULTISIG = multisigContractAddress; const chainName = chain.toUpperCase(); const tokenType = chainName === 'SOL' ? 'SPL' : 'ERC20'; const _chain = isMULTISIG ? chainName + 'MULTISIG' : isToken ? chainName + tokenType : chainName; if (multiSendContractAddress) { let multiSendParams = { nonce: Number(txp.nonce), recipients, chain: _chain, contractAddress: multiSendContractAddress, gasLimit }; unsignedTxs.push(crypto_wallet_core_1.Transactions.create(Object.assign(Object.assign({}, txp), multiSendParams))); } else if (multiTx) { for (let index = 0; index < outputOrder.length; index++) { const outputIdx = outputOrder[index]; if (!(outputs === null || outputs === void 0 ? void 0 : outputs[outputIdx])) { throw new Error('Output index out of range'); } const recepient = { amount: outputs[outputIdx].amount, address: outputs[outputIdx].toAddress, tag: outputs[outputIdx].tag }; const _tag = (recepient === null || recepient === void 0 ? void 0 : recepient.tag) || destinationTag; const rawTx = crypto_wallet_core_1.Transactions.create(Object.assign(Object.assign(Object.assign({}, txp), recepient), { tag: _tag ? Number(_tag) : undefined, chain: _chain, nonce: this.formatNonce(chainName, txp.nonce, index), recipients: [recepient] })); unsignedTxs.push(rawTx); } } else if (chainName === 'SOL') { const rawTx = crypto_wallet_core_1.Transactions.create(Object.assign(Object.assign({}, txp), { chain: _chain, recipients })); unsignedTxs.push(rawTx); } else { for (let index = 0; index < recipients.length; index++) { const rawTx = crypto_wallet_core_1.Transactions.create(Object.assign(Object.assign(Object.assign({}, txp), recipients[index]), { tag: destinationTag ? Number(destinationTag) : undefined, chain: _chain, nonce: this.formatNonce(chainName, txp.nonce, index), recipients: [recipients[index]] })); unsignedTxs.push(rawTx); } } return { uncheckedSerialize: () => unsignedTxs }; } } static formatNonce(chain, nonce, index) { if (constants_1.Constants.SVM_CHAINS.includes(chain.toLowerCase())) { return nonce; } else { return Number(nonce) + Number(index); } } static getCurrencyCodeFromCoinAndChain(coin, chain) { if (coin.toLowerCase() === chain.toLowerCase()) { return coin.toUpperCase(); } if (coin.toLowerCase() === 'pol') { return 'MATIC'; } if (coin.toLowerCase() === 'usdt' && chain.toLowerCase() === 'arb') { return 'USDTe_arb'; } if (coin.toLowerCase() === 'usdt' && chain.toLowerCase() === 'op') { return 'USDTe_op'; } const suffix = constants_1.Constants.EVM_CHAINSUFFIXMAP[chain.toLowerCase()]; const coinIsAChain = !!constants_1.Constants.EVM_CHAINSUFFIXMAP[coin.toLowerCase()]; if (suffix && (coinIsAChain || chain.toLowerCase() !== 'eth')) { if (chain.toLowerCase() === 'matic' && coin.toLowerCase() === 'usdc.e') { return 'USDC_m'; } else if (chain.toLowerCase() === 'matic' && coin.toLowerCase() === 'usdc') { return 'USDCn_m'; } return `${coin.toUpperCase()}_${suffix}`; } return coin.toUpperCase(); } static isNativeSegwit(addressType) { return [ constants_1.Constants.SCRIPT_TYPES.P2WPKH, constants_1.Constants.SCRIPT_TYPES.P2WSH, constants_1.Constants.SCRIPT_TYPES.P2TR, ].includes(addressType); } static getSegwitVersion(addressType) { switch (addressType) { case constants_1.Constants.SCRIPT_TYPES.P2WPKH: case constants_1.Constants.SCRIPT_TYPES.P2WSH: return 0; case constants_1.Constants.SCRIPT_TYPES.P2TR: return 1; default: return undefined; } } } exports.Utils = Utils; //# sourceMappingURL=utils.js.map