UNPKG

airgap-coin-lib

Version:

The airgap-coin-lib is a protocol agnostic library to prepare, sign and broadcast cryptocurrency transactions.

841 lines 47.8 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __spreadArrays = (this && this.__spreadArrays) || function () { for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; for (var r = Array(s), k = 0, i = 0; i < il; i++) for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) r[k] = a[j]; return r; }; Object.defineProperty(exports, "__esModule", { value: true }); var bitcoinJSMessage = require("../../dependencies/src/bitcoinjs-message-2.1.1/index"); var index_1 = require("../../dependencies/src/axios-0.19.0/index"); var bignumber_1 = require("../../dependencies/src/bignumber.js-9.0.0/bignumber"); var index_2 = require("../../dependencies/src/bip39-2.5.0/index"); var ProtocolSymbols_1 = require("../../utils/ProtocolSymbols"); var BitcoinProtocolOptions_1 = require("./BitcoinProtocolOptions"); var BitcoinCryptoClient_1 = require("./BitcoinCryptoClient"); var DUST_AMOUNT = 50; var BitcoinProtocol = /** @class */ (function () { function BitcoinProtocol(options) { if (options === void 0) { options = new BitcoinProtocolOptions_1.BitcoinProtocolOptions(); } this.options = options; this.symbol = 'BTC'; this.name = 'Bitcoin'; this.marketSymbol = 'btc'; this.feeSymbol = 'btc'; this.subProtocols = []; this.feeDefaults = { low: '0.00002', medium: '0.00004', high: '0.00005' }; this.decimals = 8; this.feeDecimals = 8; this.identifier = ProtocolSymbols_1.MainProtocolSymbols.BTC; this.units = [ { unitSymbol: 'BTC', factor: '1' }, { unitSymbol: 'mBTC', factor: '0.0001' }, { unitSymbol: 'Satoshi', factor: '0.00000001' } ]; this.supportsHD = true; this.standardDerivationPath = "m/44'/0'/0'"; this.addressIsCaseSensitive = true; this.addressValidationPattern = '^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$'; this.addressPlaceholder = '1ABC...'; } BitcoinProtocol.prototype.getBlockExplorerLinkForAddress = function (address) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, this.options.network.blockExplorer.getAddressLink(address)]; }); }); }; BitcoinProtocol.prototype.getBlockExplorerLinkForTxId = function (txId) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, this.options.network.blockExplorer.getTransactionLink(txId)]; }); }); }; BitcoinProtocol.prototype.getPublicKeyFromMnemonic = function (mnemonic, derivationPath, password) { return __awaiter(this, void 0, void 0, function () { var secret; return __generator(this, function (_a) { secret = index_2.mnemonicToSeed(mnemonic, password); return [2 /*return*/, this.getPublicKeyFromHexSecret(secret, derivationPath)]; }); }); }; BitcoinProtocol.prototype.getPrivateKeyFromMnemonic = function (mnemonic, derivationPath, password) { return __awaiter(this, void 0, void 0, function () { var secret; return __generator(this, function (_a) { secret = index_2.mnemonicToSeed(mnemonic, password); return [2 /*return*/, this.getPrivateKeyFromHexSecret(secret, derivationPath)]; }); }); }; BitcoinProtocol.prototype.getExtendedPrivateKeyFromMnemonic = function (mnemonic, derivationPath, password) { return __awaiter(this, void 0, void 0, function () { var secret; return __generator(this, function (_a) { secret = index_2.mnemonicToSeed(mnemonic, password); return [2 /*return*/, this.getExtendedPrivateKeyFromHexSecret(secret, derivationPath)]; }); }); }; BitcoinProtocol.prototype.getPublicKeyFromHexSecret = function (secret, derivationPath) { return __awaiter(this, void 0, void 0, function () { var bitcoinNode; return __generator(this, function (_a) { bitcoinNode = this.options.config.bitcoinJSLib.HDNode.fromSeedHex(secret, this.options.network.extras.network); return [2 /*return*/, bitcoinNode.derivePath(derivationPath).neutered().toBase58()]; }); }); }; BitcoinProtocol.prototype.getPrivateKeyFromHexSecret = function (secret, derivationPath) { return __awaiter(this, void 0, void 0, function () { var bitcoinNode; return __generator(this, function (_a) { bitcoinNode = this.options.config.bitcoinJSLib.HDNode.fromSeedHex(secret, this.options.network.extras.network); return [2 /*return*/, bitcoinNode.derivePath(derivationPath).keyPair.d.toBuffer(32)]; }); }); }; BitcoinProtocol.prototype.getExtendedPrivateKeyFromHexSecret = function (secret, derivationPath) { return __awaiter(this, void 0, void 0, function () { var bitcoinNode; return __generator(this, function (_a) { bitcoinNode = this.options.config.bitcoinJSLib.HDNode.fromSeedHex(secret, this.options.network.extras.network); return [2 /*return*/, bitcoinNode.derivePath(derivationPath).toBase58()]; }); }); }; BitcoinProtocol.prototype.getAddressFromPublicKey = function (publicKey) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { // broadcaster knows this (both broadcaster and signer) return [2 /*return*/, this.options.config.bitcoinJSLib.HDNode.fromBase58(publicKey, this.options.network.extras.network).getAddress()]; }); }); }; BitcoinProtocol.prototype.getAddressesFromPublicKey = function (publicKey) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.getAddressFromPublicKey(publicKey)]; case 1: return [2 /*return*/, [_a.sent()]]; } }); }); }; BitcoinProtocol.prototype.getAddressFromExtendedPublicKey = function (extendedPublicKey, visibilityDerivationIndex, addressDerivationIndex) { // broadcaster knows this (both broadcaster and signer) return this.options.config.bitcoinJSLib.HDNode.fromBase58(extendedPublicKey, this.options.network.extras.network) .derive(visibilityDerivationIndex) .derive(addressDerivationIndex) .getAddress(); }; BitcoinProtocol.prototype.getAddressesFromExtendedPublicKey = function (extendedPublicKey, visibilityDerivationIndex, addressCount, offset) { // broadcaster knows this (both broadcaster and signer) var node = this.options.config.bitcoinJSLib.HDNode.fromBase58(extendedPublicKey, this.options.network.extras.network); var generatorArray = Array.from(new Array(addressCount), function (x, i) { return i + offset; }); return Promise.all(generatorArray.map(function (x) { return node.derive(visibilityDerivationIndex).derive(x).getAddress(); })); }; BitcoinProtocol.prototype.signWithPrivateKey = function (privateKey, transaction) { return __awaiter(this, void 0, void 0, function () { var transactionBuilder, _i, _a, input, _b, _c, output, generatedChangeAddress, i; return __generator(this, function (_d) { switch (_d.label) { case 0: transactionBuilder = new this.options.config.bitcoinJSLib.TransactionBuilder(this.options.network.extras.network); for (_i = 0, _a = transaction.ins; _i < _a.length; _i++) { input = _a[_i]; transactionBuilder.addInput(input.txId, input.vout); } _b = 0, _c = transaction.outs; _d.label = 1; case 1: if (!(_b < _c.length)) return [3 /*break*/, 5]; output = _c[_b]; if (!output.isChange) return [3 /*break*/, 3]; return [4 /*yield*/, this.getAddressFromPublicKey(privateKey.toString('hex'))]; case 2: generatedChangeAddress = _d.sent(); if (generatedChangeAddress !== output.recipient) { throw new Error('Change address could not be verified.'); } _d.label = 3; case 3: transactionBuilder.addOutput(output.recipient, new bignumber_1.default(output.value).toNumber()); _d.label = 4; case 4: _b++; return [3 /*break*/, 1]; case 5: for (i = 0; i < transaction.ins.length; i++) { transactionBuilder.sign(i, privateKey); } return [2 /*return*/, transactionBuilder.build().toHex()]; } }); }); }; BitcoinProtocol.prototype.signWithExtendedPrivateKey = function (extendedPrivateKey, transaction) { return __awaiter(this, void 0, void 0, function () { var transactionBuilder, node, _i, _a, input, changeAddressBatchSize, changeAddressMaxAddresses, _b, _c, output, changeAddressIsValid, generatedChangeAddress, x, addresses, i; return __generator(this, function (_d) { switch (_d.label) { case 0: transactionBuilder = new this.options.config.bitcoinJSLib.TransactionBuilder(this.options.network.extras.network); node = this.options.config.bitcoinJSLib.HDNode.fromBase58(extendedPrivateKey, this.options.network.extras.network); for (_i = 0, _a = transaction.ins; _i < _a.length; _i++) { input = _a[_i]; transactionBuilder.addInput(input.txId, input.vout); } changeAddressBatchSize = 10; changeAddressMaxAddresses = 500; _b = 0, _c = transaction.outs; _d.label = 1; case 1: if (!(_b < _c.length)) return [3 /*break*/, 10]; output = _c[_b]; changeAddressIsValid = false; if (!output.isChange) return [3 /*break*/, 8]; if (!output.derivationPath) return [3 /*break*/, 3]; return [4 /*yield*/, this.getAddressesFromExtendedPublicKey(extendedPrivateKey, 1, 1, parseInt(output.derivationPath, 10))]; case 2: generatedChangeAddress = _d.sent(); changeAddressIsValid = generatedChangeAddress.includes(output.recipient); return [3 /*break*/, 7]; case 3: x = 0; _d.label = 4; case 4: if (!(x < changeAddressMaxAddresses)) return [3 /*break*/, 7]; return [4 /*yield*/, this.getAddressesFromExtendedPublicKey(extendedPrivateKey, 1, changeAddressBatchSize, x)]; case 5: addresses = _d.sent(); if (addresses.indexOf(output.recipient) >= 0) { changeAddressIsValid = true; x = changeAddressMaxAddresses; } _d.label = 6; case 6: x += changeAddressBatchSize; return [3 /*break*/, 4]; case 7: if (!changeAddressIsValid) { throw new Error('Change address could not be verified.'); } _d.label = 8; case 8: transactionBuilder.addOutput(output.recipient, new bignumber_1.default(output.value).toNumber()); _d.label = 9; case 9: _b++; return [3 /*break*/, 1]; case 10: for (i = 0; i < transaction.ins.length; i++) { transactionBuilder.sign(i, node.derivePath(transaction.ins[i].derivationPath)); } return [2 /*return*/, transactionBuilder.build().toHex()]; } }); }); }; BitcoinProtocol.prototype.getTransactionDetails = function (unsignedTx) { return __awaiter(this, void 0, void 0, function () { var transaction, feeCalculator, _i, _a, txIn, _b, _c, txOut; return __generator(this, function (_d) { transaction = unsignedTx.transaction; feeCalculator = new bignumber_1.default(0); for (_i = 0, _a = transaction.ins; _i < _a.length; _i++) { txIn = _a[_i]; feeCalculator = feeCalculator.plus(new bignumber_1.default(txIn.value)); } for (_b = 0, _c = transaction.outs; _b < _c.length; _b++) { txOut = _c[_b]; feeCalculator = feeCalculator.minus(new bignumber_1.default(txOut.value)); } return [2 /*return*/, [ { from: transaction.ins.map(function (obj) { return obj.address; }), to: transaction.outs.filter(function (obj) { return !obj.isChange; }).map(function (obj) { return obj.recipient; }), amount: transaction.outs .filter(function (obj) { return !obj.isChange; }) .map(function (obj) { return new bignumber_1.default(obj.value); }) .reduce(function (accumulator, currentValue) { return accumulator.plus(currentValue); }) .toString(10), fee: feeCalculator.toString(10), protocolIdentifier: this.identifier, network: this.options.network, isInbound: false, transactionDetails: unsignedTx.transaction } ]]; }); }); }; BitcoinProtocol.prototype.getTransactionDetailsFromSigned = function (signedTx) { return __awaiter(this, void 0, void 0, function () { var tx, bitcoinTx; var _this = this; return __generator(this, function (_a) { tx = { to: [], from: signedTx.from, amount: signedTx.amount, fee: signedTx.fee, protocolIdentifier: this.identifier, network: this.options.network, isInbound: false, transactionDetails: signedTx.transaction }; bitcoinTx = this.options.config.bitcoinJSLib.Transaction.fromHex(signedTx.transaction); bitcoinTx.outs.forEach(function (output) { var address = _this.options.config.bitcoinJSLib.address.fromOutputScript(output.script, _this.options.network.extras.network); // only works if one output is target and rest is change, but this way we can filter out change addresses if (new bignumber_1.default(output.value).isEqualTo(signedTx.amount)) { tx.to.push(address); } }); return [2 /*return*/, [tx]]; }); }); }; BitcoinProtocol.prototype.getBalanceOfAddresses = function (addresses) { return __awaiter(this, void 0, void 0, function () { var valueAccumulator, _i, addresses_1, address, data; return __generator(this, function (_a) { switch (_a.label) { case 0: valueAccumulator = new bignumber_1.default(0); _i = 0, addresses_1 = addresses; _a.label = 1; case 1: if (!(_i < addresses_1.length)) return [3 /*break*/, 4]; address = addresses_1[_i]; return [4 /*yield*/, index_1.default.get(this.options.network.extras.indexerApi + "/api/v2/address/" + address + "?details=basic", { responseType: 'json' })]; case 2: data = (_a.sent()).data; valueAccumulator = valueAccumulator.plus(new bignumber_1.default(data.balance)); _a.label = 3; case 3: _i++; return [3 /*break*/, 1]; case 4: return [2 /*return*/, valueAccumulator.toString(10)]; } }); }); }; BitcoinProtocol.prototype.getBalanceOfPublicKey = function (publicKey) { return __awaiter(this, void 0, void 0, function () { var address; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.getAddressFromPublicKey(publicKey)]; case 1: address = _a.sent(); return [2 /*return*/, this.getBalanceOfAddresses([address])]; } }); }); }; BitcoinProtocol.prototype.getBalanceOfExtendedPublicKey = function (extendedPublicKey, offset) { if (offset === void 0) { offset = 0; } return __awaiter(this, void 0, void 0, function () { var data; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, index_1.default.get(this.options.network.extras.indexerApi + "/api/v2/xpub/" + extendedPublicKey + "?pageSize=1", { responseType: 'json' })]; case 1: data = (_a.sent()).data; return [2 /*return*/, data.balance]; } }); }); }; BitcoinProtocol.prototype.getBalanceOfPublicKeyForSubProtocols = function (publicKey, subProtocols) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { throw Promise.reject('get balance of sub protocols not supported'); }); }); }; BitcoinProtocol.prototype.getAvailableBalanceOfAddresses = function (addresses) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, this.getBalanceOfAddresses(addresses)]; }); }); }; BitcoinProtocol.prototype.estimateMaxTransactionValueFromExtendedPublicKey = function (extendedPublicKey, recipients, fee) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, this.getBalanceOfExtendedPublicKey(extendedPublicKey)]; }); }); }; BitcoinProtocol.prototype.estimateMaxTransactionValueFromPublicKey = function (publicKey, recipients, fee) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, this.getBalanceOfPublicKey(publicKey)]; }); }); }; BitcoinProtocol.prototype.estimateFeeDefaultsFromExtendedPublicKey = function (publicKey, recipients, values, data) { return __awaiter(this, void 0, void 0, function () { var result, estimatedFee, feeStepFactor, mediumFee, lowFee, highFee; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, index_1.default.get(this.options.network.extras.indexerApi + "/api/v2/estimatefee/5")]; case 1: result = (_a.sent()).data.result; estimatedFee = new bignumber_1.default(result).shiftedBy(this.feeDecimals); if (estimatedFee.isZero()) { return [2 /*return*/, this.feeDefaults]; } feeStepFactor = new bignumber_1.default(0.5); mediumFee = estimatedFee; lowFee = mediumFee.minus(mediumFee.times(feeStepFactor)).integerValue(bignumber_1.default.ROUND_FLOOR); highFee = mediumFee.plus(mediumFee.times(feeStepFactor)).integerValue(bignumber_1.default.ROUND_FLOOR); return [2 /*return*/, { low: lowFee.shiftedBy(-this.feeDecimals).toFixed(), medium: mediumFee.shiftedBy(-this.feeDecimals).toFixed(), high: highFee.shiftedBy(-this.feeDecimals).toFixed() }]; } }); }); }; BitcoinProtocol.prototype.estimateFeeDefaultsFromPublicKey = function (publicKey, recipients, values, data) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, Promise.reject('estimating fee defaults using non extended public key not implemented')]; }); }); }; BitcoinProtocol.prototype.prepareTransactionFromExtendedPublicKey = function (extendedPublicKey, offset, recipients, values, fee) { return __awaiter(this, void 0, void 0, function () { var wrappedValues, wrappedFee, transaction, utxos, totalRequiredBalance, valueAccumulator, getPathIndexes, _i, utxos_1, utxo, indexes, derivedAddress, i, lastUsedInternalAddress, changeValue, changeAddressIndex, derivedAddress; return __generator(this, function (_a) { switch (_a.label) { case 0: wrappedValues = values.map(function (value) { return new bignumber_1.default(value); }); wrappedFee = new bignumber_1.default(fee); transaction = { ins: [], outs: [] }; if (recipients.length !== wrappedValues.length) { throw new Error('recipients do not match values'); } console.log(this.options.network.extras.indexerApi + "/api/v2/utxo/" + extendedPublicKey + "?confirmed=true"); return [4 /*yield*/, index_1.default.get(this.options.network.extras.indexerApi + "/api/v2/utxo/" + extendedPublicKey + "?confirmed=true", { responseType: 'json' })]; case 1: utxos = (_a.sent()).data; if (utxos.length <= 0) { throw new Error('not enough balance'); // no transactions found on those addresses, probably won't find anything in the next ones } totalRequiredBalance = wrappedValues .reduce(function (accumulator, currentValue) { return accumulator.plus(currentValue); }) .plus(wrappedFee); valueAccumulator = new bignumber_1.default(0); getPathIndexes = function (path) { var result = path .split('/') .slice(-2) .map(function (item) { return parseInt(item); }) .filter(function (item) { return !isNaN(item); }); if (result.length !== 2) { throw new Error('Unexpected path format'); } return [result[0], result[1]]; }; _i = 0, utxos_1 = utxos; _a.label = 2; case 2: if (!(_i < utxos_1.length)) return [3 /*break*/, 5]; utxo = utxos_1[_i]; valueAccumulator = valueAccumulator.plus(utxo.value); indexes = getPathIndexes(utxo.path); return [4 /*yield*/, this.getAddressFromExtendedPublicKey(extendedPublicKey, indexes[0], indexes[1])]; case 3: derivedAddress = _a.sent(); if (derivedAddress === utxo.address) { transaction.ins.push({ txId: utxo.txid, value: new bignumber_1.default(utxo.value).toString(10), vout: utxo.vout, address: utxo.address, derivationPath: indexes.join('/') }); } else { throw new Error('Invalid address returned from API'); } if (valueAccumulator.isGreaterThanOrEqualTo(totalRequiredBalance)) { return [3 /*break*/, 5]; } _a.label = 4; case 4: _i++; return [3 /*break*/, 2]; case 5: if (valueAccumulator.isLessThan(totalRequiredBalance)) { throw new Error('not enough balance'); } for (i = 0; i < recipients.length; i++) { transaction.outs.push({ recipient: recipients[i], isChange: false, value: wrappedValues[i].toString(10), derivationPath: '' // TODO: Remove this as soon as our serializer supports optional properties }); valueAccumulator = valueAccumulator.minus(wrappedValues[i]); } lastUsedInternalAddress = Math.max.apply(Math, __spreadArrays([-1], utxos .map(function (utxo) { return getPathIndexes(utxo.path); }) .filter(function (indexes) { return indexes[0] === 1; }) .map(function (indexes) { return indexes[1]; }))); changeValue = valueAccumulator.minus(wrappedFee); if (!changeValue.isGreaterThan(new bignumber_1.default(DUST_AMOUNT))) return [3 /*break*/, 7]; changeAddressIndex = lastUsedInternalAddress + 1; return [4 /*yield*/, this.getAddressFromExtendedPublicKey(extendedPublicKey, 1, changeAddressIndex)]; case 6: derivedAddress = _a.sent(); transaction.outs.push({ recipient: derivedAddress, isChange: true, value: changeValue.toString(10), derivationPath: changeAddressIndex.toString() }); _a.label = 7; case 7: return [2 /*return*/, transaction]; } }); }); }; BitcoinProtocol.prototype.prepareTransactionFromPublicKey = function (publicKey, recipients, values, fee) { return __awaiter(this, void 0, void 0, function () { var wrappedValues, wrappedFee, transaction, address, utxos, totalRequiredBalance, valueAccumulator, _i, utxos_2, utxo, i, changeValue; return __generator(this, function (_a) { switch (_a.label) { case 0: wrappedValues = values.map(function (value) { return new bignumber_1.default(value); }); wrappedFee = new bignumber_1.default(fee); transaction = { ins: [], outs: [] }; if (recipients.length !== wrappedValues.length) { throw new Error('Recipient and value length does not match.'); } return [4 /*yield*/, this.getAddressFromPublicKey(publicKey)]; case 1: address = _a.sent(); return [4 /*yield*/, index_1.default.get(this.options.network.extras.indexerApi + "/api/v2/utxo/" + address, { responseType: 'json' })]; case 2: utxos = (_a.sent()).data; totalRequiredBalance = wrappedValues .reduce(function (accumulator, currentValue) { return accumulator.plus(currentValue); }) .plus(wrappedFee); valueAccumulator = new bignumber_1.default(0); for (_i = 0, utxos_2 = utxos; _i < utxos_2.length; _i++) { utxo = utxos_2[_i]; valueAccumulator = valueAccumulator.plus(new bignumber_1.default(utxo.value)); transaction.ins.push({ txId: utxo.txid, value: new bignumber_1.default(utxo.value).toString(10), vout: utxo.vout, address: address }); if (valueAccumulator.isGreaterThanOrEqualTo(totalRequiredBalance)) { break; } } if (valueAccumulator.isLessThan(totalRequiredBalance)) { throw new Error("not enough balance, having " + valueAccumulator.toFixed() + " of " + totalRequiredBalance.toFixed()); } // tx.addInput(utxo.txid, utxo.vout) for (i = 0; i < recipients.length; i++) { transaction.outs.push({ recipient: recipients[i], isChange: false, value: wrappedValues[i].toString(10) }); valueAccumulator = valueAccumulator.minus(wrappedValues[i]); // tx.addOutput(recipients[i], values[i]) } changeValue = valueAccumulator.minus(wrappedFee); if (changeValue.isGreaterThan(new bignumber_1.default(DUST_AMOUNT))) { transaction.outs.push({ recipient: address, isChange: true, value: changeValue.toString(10) }); } return [2 /*return*/, transaction]; } }); }); }; BitcoinProtocol.prototype.broadcastTransaction = function (rawTransaction) { return __awaiter(this, void 0, void 0, function () { var data; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, index_1.default.post(this.options.network.extras.indexerApi + '/api/v2/sendtx/', rawTransaction)]; case 1: data = (_a.sent()).data; return [2 /*return*/, data.result]; } }); }); }; BitcoinProtocol.prototype.getTransactionsFromExtendedPublicKey = function (extendedPublicKey, limit, cursor, addressOffset) { if (addressOffset === void 0) { addressOffset = 0; } var _a, _b; return __awaiter(this, void 0, void 0, function () { var page, data, ourAddresses, airGapTransactions, _i, _c, transaction, tempAirGapTransactionFrom, tempAirGapTransactionTo, tempAirGapTransactionIsInbound, amount, _d, _e, vin, _f, _g, vout, airGapTransaction; return __generator(this, function (_h) { switch (_h.label) { case 0: page = (_b = (_a = cursor) === null || _a === void 0 ? void 0 : _a.page, (_b !== null && _b !== void 0 ? _b : 1)); return [4 /*yield*/, index_1.default.get(this.options.network.extras.indexerApi + '/api/v2/xpub/' + extendedPublicKey + ("?details=txs&tokens=used&pageSize=" + limit + "&page=" + page), { responseType: 'json' })]; case 1: data = (_h.sent()).data; ourAddresses = (data.tokens || []).filter(function (token) { return token.type === 'XPUBAddress'; }).map(function (token) { return token.name; }); airGapTransactions = []; if (data.page == page) { for (_i = 0, _c = data.transactions || []; _i < _c.length; _i++) { transaction = _c[_i]; tempAirGapTransactionFrom = []; tempAirGapTransactionTo = []; tempAirGapTransactionIsInbound = true; amount = new bignumber_1.default(0); for (_d = 0, _e = transaction.vin; _d < _e.length; _d++) { vin = _e[_d]; if (this.containsSome(vin.addresses, ourAddresses)) { tempAirGapTransactionIsInbound = false; } tempAirGapTransactionFrom.push.apply(tempAirGapTransactionFrom, vin.addresses); amount = amount.plus(vin.value); } for (_f = 0, _g = transaction.vout; _f < _g.length; _f++) { vout = _g[_f]; if (vout.addresses) { tempAirGapTransactionTo.push.apply(tempAirGapTransactionTo, vout.addresses); // If receiving address is our address, and transaction is outbound => our change if (this.containsSome(vout.addresses, ourAddresses) && !tempAirGapTransactionIsInbound) { // remove only if related to this address amount = amount.minus(vout.value); } // If receiving address is not ours, and transaction isbound => senders change if (!this.containsSome(vout.addresses, ourAddresses) && tempAirGapTransactionIsInbound) { amount = amount.minus(vout.value); } } } // deduct fee from amount amount = amount.minus(transaction.fees); airGapTransaction = { hash: transaction.txid, from: tempAirGapTransactionFrom, to: tempAirGapTransactionTo, isInbound: tempAirGapTransactionIsInbound, amount: amount.toString(10), fee: new bignumber_1.default(transaction.fees).toString(10), blockHeight: transaction.blockHeight.toString(), protocolIdentifier: this.identifier, network: this.options.network, timestamp: transaction.blockTime }; airGapTransactions.push(airGapTransaction); } } return [2 /*return*/, { transactions: airGapTransactions, cursor: { page: cursor ? cursor.page + 1 : 2 } }]; } }); }); }; BitcoinProtocol.prototype.getTransactionsFromPublicKey = function (publicKey, limit, cursor) { return __awaiter(this, void 0, void 0, function () { var _a; return __generator(this, function (_b) { switch (_b.label) { case 0: _a = this.getTransactionsFromAddresses; return [4 /*yield*/, this.getAddressFromPublicKey(publicKey)]; case 1: return [2 /*return*/, _a.apply(this, [[_b.sent()], limit, cursor])]; } }); }); }; BitcoinProtocol.prototype.getTransactionsFromAddresses = function (addresses, limit, cursor) { var _a, _b; return __awaiter(this, void 0, void 0, function () { var airGapTransactions, page, url, data, _i, _c, transaction, tempAirGapTransactionFrom, tempAirGapTransactionTo, tempAirGapTransactionIsInbound, amount, _d, _e, vin, _f, _g, vout, airGapTransaction; return __generator(this, function (_h) { switch (_h.label) { case 0: airGapTransactions = []; page = (_b = (_a = cursor) === null || _a === void 0 ? void 0 : _a.page, (_b !== null && _b !== void 0 ? _b : 1)); url = this.options.network.extras.indexerApi + "/api/v2/address/" + addresses[0] + "?page=" + page + "&pageSize=" + limit + "&details=txs"; return [4 /*yield*/, index_1.default.get(url, { responseType: 'json' })]; case 1: data = (_h.sent()).data; if (data.page == page) { for (_i = 0, _c = data.transactions || []; _i < _c.length; _i++) { transaction = _c[_i]; tempAirGapTransactionFrom = []; tempAirGapTransactionTo = []; tempAirGapTransactionIsInbound = true; amount = new bignumber_1.default(0); for (_d = 0, _e = transaction.vin; _d < _e.length; _d++) { vin = _e[_d]; if (vin.addresses && this.containsSome(vin.addresses, addresses)) { tempAirGapTransactionIsInbound = false; } tempAirGapTransactionFrom.push.apply(tempAirGapTransactionFrom, vin.addresses); amount = vin.value ? amount.plus(vin.value) : amount; } for (_f = 0, _g = transaction.vout; _f < _g.length; _f++) { vout = _g[_f]; if (vout.addresses) { tempAirGapTransactionTo.push.apply(tempAirGapTransactionTo, vout.addresses); // If receiving address is our address, and transaction is outbound => our change if (this.containsSome(vout.addresses, addresses) && !tempAirGapTransactionIsInbound) { // remove only if related to this address amount = amount.minus(new bignumber_1.default(vout.value).shiftedBy(this.decimals)); } // If receiving address is not ours, and transaction isbound => senders change if (!this.containsSome(vout.addresses, addresses) && tempAirGapTransactionIsInbound) { amount = amount.minus(new bignumber_1.default(vout.value).shiftedBy(this.decimals)); } } } // deduct fee from amount amount = amount.minus(new bignumber_1.default(transaction.fees).shiftedBy(this.feeDecimals)); airGapTransaction = { hash: transaction.txid, from: tempAirGapTransactionFrom, to: tempAirGapTransactionTo, isInbound: tempAirGapTransactionIsInbound, amount: amount.toString(10), fee: new bignumber_1.default(transaction.fees).shiftedBy(this.feeDecimals).toString(10), blockHeight: transaction.blockHeight.toString(), protocolIdentifier: this.identifier, network: this.options.network, timestamp: transaction.blockTime }; airGapTransactions.push(airGapTransaction); } } return [2 /*return*/, { transactions: airGapTransactions, cursor: { page: cursor ? cursor.page + 1 : 2 } }]; } }); }); }; BitcoinProtocol.prototype.containsSome = function (needles, haystack) { for (var _i = 0, needles_1 = needles; _i < needles_1.length; _i++) { var needle = needles_1[_i]; if (haystack.indexOf(needle) > -1) { return true; } } return false; }; BitcoinProtocol.prototype.signMessage = function (message, keypair) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, new BitcoinCryptoClient_1.BitcoinCryptoClient(this, bitcoinJSMessage).signMessage(message, keypair)]; }); }); }; BitcoinProtocol.prototype.verifyMessage = function (message, signature, publicKey) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, new BitcoinCryptoClient_1.BitcoinCryptoClient(this, bitcoinJSMessage).verifyMessage(message, signature, publicKey)]; }); }); }; BitcoinProtocol.prototype.getTransactionStatuses = function (transactionHashes) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, Promise.reject('Transaction status not implemented')]; }); }); }; return BitcoinProtocol; }()); exports.BitcoinProtocol = BitcoinProtocol; //# sourceMappingURL=BitcoinProtocol.js.map