bitcore-wallet-client
Version:
Client for bitcore-wallet-service
1,194 lines • 100 kB
JavaScript
'use strict';
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
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 __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.API = void 0;
const async_1 = __importDefault(require("async"));
const bitcore_mnemonic_1 = __importDefault(require("bitcore-mnemonic"));
const CWC = __importStar(require("crypto-wallet-core"));
const events_1 = require("events");
const preconditions_1 = require("preconditions");
const querystring_1 = __importDefault(require("querystring"));
const sjcl_1 = __importDefault(require("sjcl"));
const uuid_1 = __importDefault(require("uuid"));
const bulkclient_1 = require("./bulkclient");
const common_1 = require("./common");
const credentials_1 = require("./credentials");
const errors_1 = require("./errors");
const key_1 = require("./key");
const log_1 = __importDefault(require("./log"));
const paypro_1 = require("./paypro");
const payproV2_1 = require("./payproV2");
const request_1 = require("./request");
const verifier_1 = require("./verifier");
const $ = (0, preconditions_1.singleton)();
const Bitcore = CWC.BitcoreLib;
const Bitcore_ = {
btc: CWC.BitcoreLib,
bch: CWC.BitcoreLibCash,
eth: CWC.BitcoreLib,
matic: CWC.BitcoreLib,
arb: CWC.BitcoreLib,
base: CWC.BitcoreLib,
op: CWC.BitcoreLib,
xrp: CWC.BitcoreLib,
doge: CWC.BitcoreLibDoge,
ltc: CWC.BitcoreLibLtc,
sol: CWC.BitcoreLib,
};
const NetworkChar = {
livenet: 'L',
testnet: 'T',
regtest: 'R'
};
for (const network in NetworkChar) {
NetworkChar[NetworkChar[network]] = network;
}
const BASE_URL = 'http://localhost:3232/bws/api';
class API extends events_1.EventEmitter {
constructor(opts) {
super();
opts = opts || {};
this.doNotVerifyPayPro = opts.doNotVerifyPayPro;
this.timeout = opts.timeout || 50000;
this.logLevel = opts.logLevel || 'silent';
this.supportStaffWalletId = opts.supportStaffWalletId;
this.bp_partner = opts.bp_partner;
this.bp_partner_version = opts.bp_partner_version;
this.request = new request_1.Request(opts.baseUrl || BASE_URL, {
r: opts.request,
supportStaffWalletId: opts.supportStaffWalletId
});
this.bulkClient = new bulkclient_1.BulkClient(opts.baseUrl || BASE_URL, {
r: opts.request,
supportStaffWalletId: opts.supportStaffWalletId
});
log_1.default.setLevel(this.logLevel);
}
initNotifications(cb) {
log_1.default.warn('DEPRECATED: use initialize() instead.');
this.initialize({}, cb);
}
initialize(opts, cb) {
$.checkState(this.credentials, 'Failed state: this.credentials at <initialize()>');
this.notificationIncludeOwn = !!opts.notificationIncludeOwn;
this._initNotifications(opts);
return cb();
}
dispose(cb) {
this._disposeNotifications();
this.request.logout(cb);
}
_fetchLatestNotifications(interval, cb) {
cb = cb || function () { };
var opts = {
lastNotificationId: this.lastNotificationId,
includeOwn: this.notificationIncludeOwn
};
if (!this.lastNotificationId) {
opts.timeSpan = interval + 1;
}
this.getNotifications(opts, (err, notifications) => {
if (err) {
log_1.default.warn('Error receiving notifications.');
log_1.default.debug(err);
return cb(err);
}
if (notifications.length > 0) {
this.lastNotificationId = notifications.slice(-1)[0].id;
}
for (const notification of notifications) {
this.emit('notification', notification);
}
return cb();
});
}
_initNotifications(opts) {
opts = opts || {};
var interval = opts.notificationIntervalSeconds || 5;
this.notificationsIntervalId = setInterval(() => {
this._fetchLatestNotifications(interval, err => {
if (err) {
if (err instanceof errors_1.Errors.NOT_FOUND ||
err instanceof errors_1.Errors.NOT_AUTHORIZED) {
this._disposeNotifications();
}
}
});
}, interval * 1000);
}
_disposeNotifications() {
if (this.notificationsIntervalId) {
clearInterval(this.notificationsIntervalId);
this.notificationsIntervalId = null;
}
}
setNotificationsInterval(notificationIntervalSeconds) {
this._disposeNotifications();
if (notificationIntervalSeconds > 0) {
this._initNotifications({
notificationIntervalSeconds
});
}
}
getRootPath() {
return this.credentials.getRootPath();
}
static _encryptMessage(message, encryptingKey) {
if (!message)
return null;
return common_1.Utils.encryptMessage(message, encryptingKey);
}
_processTxNotes(notes) {
if (!notes)
return;
const encryptingKey = this.credentials.sharedEncryptingKey;
for (const note of [].concat(notes)) {
note.encryptedBody = note.body;
note.body = common_1.Utils.decryptMessageNoThrow(note.body, encryptingKey);
note.encryptedEditedByName = note.editedByName;
note.editedByName = common_1.Utils.decryptMessageNoThrow(note.editedByName, encryptingKey);
}
}
_processTxps(txps) {
if (!txps)
return;
var encryptingKey = this.credentials.sharedEncryptingKey;
for (const txp of [].concat(txps)) {
txp.encryptedMessage = txp.message;
txp.message =
common_1.Utils.decryptMessageNoThrow(txp.message, encryptingKey) || null;
txp.creatorName = common_1.Utils.decryptMessageNoThrow(txp.creatorName, encryptingKey);
for (const action of txp.actions || []) {
action.copayerName = common_1.Utils.decryptMessageNoThrow(action.copayerName, encryptingKey);
action.comment = common_1.Utils.decryptMessageNoThrow(action.comment, encryptingKey);
}
for (const output of txp.outputs || []) {
output.encryptedMessage = output.message;
output.message = common_1.Utils.decryptMessageNoThrow(output.message, encryptingKey) || null;
}
txp.hasUnconfirmedInputs = (txp.inputs || []).some(input => input.confirmations == 0);
this._processTxNotes(txp.note);
}
}
validateKeyDerivation(opts, cb) {
var _deviceValidated;
opts = opts || {};
var c = this.credentials;
var testMessageSigning = (xpriv, xpub) => {
var nonHardenedPath = 'm/0/0';
var message = 'Lorem ipsum dolor sit amet, ne amet urbanitas percipitur vim, libris disputando his ne, et facer suavitate qui. Ei quidam laoreet sea. Cu pro dico aliquip gubergren, in mundi postea usu. Ad labitur posidonium interesset duo, est et doctus molestie adipiscing.';
var priv = xpriv.deriveChild(nonHardenedPath).privateKey;
var signature = common_1.Utils.signMessage(message, priv);
var pub = xpub.deriveChild(nonHardenedPath).publicKey;
return common_1.Utils.verifyMessage(message, signature, pub);
};
var testHardcodedKeys = () => {
var words = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about';
var xpriv = (0, bitcore_mnemonic_1.default)(words).toHDPrivateKey();
if (xpriv.toString() !=
'xprv9s21ZrQH143K3GJpoapnV8SFfukcVBSfeCficPSGfubmSFDxo1kuHnLisriDvSnRRuL2Qrg5ggqHKNVpxR86QEC8w35uxmGoggxtQTPvfUu')
return false;
xpriv = xpriv.deriveChild("m/44'/0'/0'");
if (xpriv.toString() !=
'xprv9xpXFhFpqdQK3TmytPBqXtGSwS3DLjojFhTGht8gwAAii8py5X6pxeBnQ6ehJiyJ6nDjWGJfZ95WxByFXVkDxHXrqu53WCRGypk2ttuqncb')
return false;
var xpub = Bitcore.HDPublicKey.fromString('xpub6BosfCnifzxcFwrSzQiqu2DBVTshkCXacvNsWGYJVVhhawA7d4R5WSWGFNbi8Aw6ZRc1brxMyWMzG3DSSSSoekkudhUd9yLb6qx39T9nMdj');
return testMessageSigning(xpriv, xpub);
};
var testLiveKeys = () => {
var words;
try {
words = c.getMnemonic();
}
catch (ex) { }
var xpriv;
if (words && (!c.mnemonicHasPassphrase || opts.passphrase)) {
var m = new bitcore_mnemonic_1.default(words);
xpriv = m.toHDPrivateKey(opts.passphrase, c.network);
}
if (!xpriv) {
xpriv = new Bitcore.HDPrivateKey(c.xPrivKey);
}
xpriv = xpriv.deriveChild(c.getBaseAddressDerivationPath());
var xpub = new Bitcore.HDPublicKey(c.xPubKey);
return testMessageSigning(xpriv, xpub);
};
var hardcodedOk = true;
if (!_deviceValidated && !opts.skipDeviceValidation) {
hardcodedOk = testHardcodedKeys();
_deviceValidated = true;
}
this.keyDerivationOk = hardcodedOk;
return cb(null, this.keyDerivationOk);
}
toObj() {
$.checkState(this.credentials, 'Failed state: this.credentials at <toObj()>');
return this.credentials.toObj();
}
toString() {
$.checkState(this.credentials, 'Failed state: this.credentials at <toString()>');
$.checkArgument(!this.noSign, 'no Sign not supported');
$.checkArgument(!this.password, 'password not supported');
const output = JSON.stringify(this.toObj());
return output;
}
fromObj(credentials) {
$.checkArgument(credentials && typeof credentials === 'object' && !Array.isArray(credentials), 'Argument should be an object');
try {
credentials = credentials_1.Credentials.fromObj(credentials);
this.credentials = credentials;
}
catch (ex) {
log_1.default.warn(`Error importing wallet: ${ex}`);
if (ex.toString().match(/Obsolete/)) {
throw new errors_1.Errors.OBSOLETE_BACKUP();
}
else {
throw new errors_1.Errors.INVALID_BACKUP();
}
}
this.request.setCredentials(this.credentials);
return this;
}
fromString(credentials) {
$.checkArgument(credentials, 'Missing argument: credentials at <fromString>');
if (typeof credentials === 'object') {
log_1.default.warn('WARN: Please use fromObj instead of fromString when importing strings');
return this.fromObj(credentials);
}
let c;
try {
c = JSON.parse(credentials);
}
catch (ex) {
log_1.default.warn(`Error importing wallet: ${ex}`);
throw new errors_1.Errors.INVALID_BACKUP();
}
return this.fromObj(c);
}
toClone() {
$.checkState(this.credentials, 'Failed state: this.credentials at <toClone()>');
const clone = new API(Object.assign({}, this, { request: this.request.r, baseUrl: this.request.baseUrl }));
clone.fromObj(this.toObj());
return clone;
}
static clone(api) {
const clone = new API(Object.assign({}, api, { request: api.request.r, baseUrl: api.request.baseUrl }));
if (api.credentials) {
clone.fromObj(api.toObj());
}
return clone;
}
decryptBIP38PrivateKey(encryptedPrivateKeyBase58, passphrase, progressCallback, cb) {
var Bip38 = require('bip38');
var bip38 = new Bip38();
var privateKeyWif;
try {
privateKeyWif = bip38.decrypt(encryptedPrivateKeyBase58, passphrase, progressCallback);
}
catch (ex) {
return cb(new Error('Could not decrypt BIP38 private key' + ex));
}
var privateKey = new Bitcore.PrivateKey(privateKeyWif);
var address = privateKey.publicKey.toAddress().toString();
var addrBuff = Buffer.from(address, 'ascii');
var actualChecksum = Bitcore.crypto.Hash.sha256sha256(addrBuff)
.toString('hex')
.substring(0, 8);
var expectedChecksum = Bitcore.encoding.Base58Check.decode(encryptedPrivateKeyBase58)
.toString('hex')
.substring(6, 14);
if (actualChecksum != expectedChecksum)
return cb(new Error('Incorrect passphrase'));
return cb(null, privateKeyWif);
}
getBalanceFromPrivateKey(privateKey, chain, cb) {
if (typeof chain === 'function') {
cb = chain;
chain = 'btc';
}
var B = Bitcore_[chain];
var privateKey = new B.PrivateKey(privateKey);
var address = privateKey.publicKey.toAddress().toString(true);
this.getUtxos({
addresses: address
}, (err, utxos) => {
if (err)
return cb(err);
return cb(null, (utxos || []).reduce((sum, u) => sum += u.satoshis, 0));
});
}
buildTxFromPrivateKey(privateKey, destinationAddress, opts, cb) {
var _a;
opts = opts || {};
var chain = ((_a = opts.chain) === null || _a === void 0 ? void 0 : _a.toLowerCase()) || common_1.Utils.getChain(opts.coin);
var signingMethod = opts.signingMethod || 'ecdsa';
if (!common_1.Constants.CHAINS.includes(chain))
return cb(new Error('Invalid chain'));
if (common_1.Constants.EVM_CHAINS.includes(chain))
return cb(new Error('EVM based chains not supported for this action'));
var B = Bitcore_[chain];
var privateKey = B.PrivateKey(privateKey);
var address = privateKey.publicKey.toAddress().toString(true);
async_1.default.waterfall([
next => {
this.getUtxos({
addresses: address
}, (err, utxos) => {
return next(err, utxos);
});
},
(utxos, next) => {
if (!Array.isArray(utxos) || utxos.length == 0)
return next(new Error('No utxos found'));
const fee = opts.fee || 10000;
const utxoSum = (utxos || []).reduce((sum, u) => sum += u.satoshis, 0);
const amount = utxoSum - fee;
if (amount <= 0)
return next(new errors_1.Errors.INSUFFICIENT_FUNDS());
try {
const toAddress = B.Address.fromString(destinationAddress);
const tx = new B.Transaction()
.from(utxos)
.to(toAddress, amount)
.fee(fee)
.sign(privateKey, undefined, signingMethod);
tx.serialize();
return next(null, tx);
}
catch (ex) {
log_1.default.error('Could not build transaction from private key', ex);
return next(new errors_1.Errors.COULD_NOT_BUILD_TRANSACTION());
}
}
], cb);
}
openWallet(opts, cb) {
if (typeof opts === 'function') {
cb = opts;
}
opts = opts || {};
$.checkState(this.credentials, 'Failed state: this.credentials at <openWallet()>');
if (this.credentials.isComplete() && this.credentials.hasWalletInfo())
return cb(null, true);
const qs = [];
qs.push('includeExtendedInfo=1');
qs.push('serverMessageArray=1');
this.request.get('/v3/wallets/?' + qs.join('&'), (err, ret) => {
if (err)
return cb(err);
var wallet = ret.wallet;
this._processStatus(ret);
if (!this.credentials.hasWalletInfo()) {
const me = (wallet.copayers || []).find(c => c.id === this.credentials.copayerId);
if (!me)
return cb(new Error('Copayer not in wallet'));
try {
this.credentials.addWalletInfo(wallet.id, wallet.name, wallet.m, wallet.n, me.name, opts);
}
catch (e) {
if (e.message) {
log_1.default.info('Trying credentials...', e.message);
}
if (e.message && e.message.match(/Bad\snr/)) {
return cb(new errors_1.Errors.WALLET_DOES_NOT_EXIST());
}
throw e;
}
}
if (wallet.status != 'complete')
return cb(null, ret);
if (this.credentials.walletPrivKey) {
if (!verifier_1.Verifier.checkCopayers(this.credentials, wallet.copayers)) {
return cb(new errors_1.Errors.SERVER_COMPROMISED());
}
}
else {
log_1.default.warn('Could not verify copayers key (missing wallet Private Key)');
}
this.credentials.addPublicKeyRing(this._extractPublicKeyRing(wallet.copayers));
this.emit('walletCompleted', wallet);
return cb(null, ret);
});
}
static _buildSecret(walletId, walletPrivKey, chain, network) {
if (typeof walletPrivKey === 'string') {
walletPrivKey = Bitcore.PrivateKey.fromString(walletPrivKey);
}
var widHex = Buffer.from(walletId.replace(/-/g, ''), 'hex');
var widBase58 = new Bitcore.encoding.Base58(widHex).toString();
const networkChar = NetworkChar[network] || 'L';
return (widBase58.padEnd(22, '0') +
walletPrivKey.toWIF() +
networkChar +
chain);
}
static parseSecret(secret) {
$.checkArgument(secret);
var split = (str, indexes) => {
var parts = [];
indexes.push(str.length);
var i = 0;
while (i < indexes.length) {
parts.push(str.substring(i == 0 ? 0 : indexes[i - 1], indexes[i]));
i++;
}
return parts;
};
try {
var secretSplit = split(secret, [22, 74, 75]);
var widBase58 = secretSplit[0].replace(/0/g, '');
var widHex = Bitcore.encoding.Base58.decode(widBase58).toString('hex');
var walletId = split(widHex, [8, 12, 16, 20]).join('-');
const walletPrivKey = Bitcore.PrivateKey.fromString(secretSplit[1]);
const network = NetworkChar[secretSplit[2]] || 'livenet';
const coin = secretSplit[3] || 'btc';
return {
walletId,
walletPrivKey,
coin,
network
};
}
catch (ex) {
throw new Error('Invalid secret');
}
}
static getRawTx(txp) {
var t = common_1.Utils.buildTx(txp);
return t.uncheckedSerialize();
}
_getCurrentSignatures(txp) {
var acceptedActions = (txp.actions || []).filter(a => a.type === 'accept');
return acceptedActions.map(x => ({
signatures: x.signatures,
xpub: x.xpub
}));
}
_addSignaturesToBitcoreTxBitcoin(txp, t, signatures, xpub) {
var _a;
$.checkState(txp.coin, 'Failed state: txp.coin undefined at _addSignaturesToBitcoreTxBitcoin');
$.checkState(txp.signingMethod, 'Failed state: txp.signingMethod undefined at _addSignaturesToBitcoreTxBitcoin');
var chain = ((_a = txp.chain) === null || _a === void 0 ? void 0 : _a.toLowerCase()) || common_1.Utils.getChain(txp.coin);
const bitcore = Bitcore_[chain];
if (signatures.length != txp.inputs.length)
throw new Error('Number of signatures does not match number of inputs');
let i = 0;
const x = new bitcore.HDPublicKey(xpub);
for (const signatureHex of signatures) {
try {
const signature = bitcore.crypto.Signature.fromString(signatureHex);
const pub = x.deriveChild(txp.inputPaths[i]).publicKey;
const s = {
inputIndex: i,
signature,
sigtype: bitcore.crypto.Signature.SIGHASH_ALL |
bitcore.crypto.Signature.SIGHASH_FORKID,
publicKey: pub
};
t.inputs[i].addSignature(t, s, txp.signingMethod);
i++;
}
catch (e) { }
}
if (i != txp.inputs.length)
throw new Error('Wrong signatures');
}
_addSignaturesToBitcoreTx(txp, t, signatures, xpub) {
const { chain, network } = txp;
switch (chain.toLowerCase()) {
case 'xrp':
case 'eth':
case 'matic':
case 'arb':
case 'base':
case 'op':
case 'sol':
const unsignedTxs = t.uncheckedSerialize();
const signedTxs = [];
for (let index = 0; index < signatures.length; index++) {
const signed = CWC.Transactions.applySignature({
chain: chain.toUpperCase(),
tx: unsignedTxs[index],
signature: signatures[index]
});
signedTxs.push(signed);
t.id = CWC.Transactions.getHash({
tx: signed,
chain: chain.toUpperCase(),
network
});
}
t.uncheckedSerialize = () => signedTxs;
t.serialize = () => signedTxs;
break;
default:
return this._addSignaturesToBitcoreTxBitcoin(txp, t, signatures, xpub);
}
}
_applyAllSignatures(txp, t) {
$.checkState(txp.status == 'accepted', 'Failed state: txp.status at _applyAllSignatures');
var sigs = this._getCurrentSignatures(txp);
for (const x of sigs) {
this._addSignaturesToBitcoreTx(txp, t, x.signatures, x.xpub);
}
}
_doJoinWallet(walletId, walletPrivKey, xPubKey, requestPubKey, copayerName, opts, cb) {
$.shouldBeFunction(cb);
opts = opts || {};
opts.customData = opts.customData || {};
opts.customData.walletPrivKey = walletPrivKey.toString();
const encCustomData = common_1.Utils.encryptMessage(JSON.stringify(opts.customData), this.credentials.personalEncryptingKey);
const encCopayerName = common_1.Utils.encryptMessage(copayerName, this.credentials.sharedEncryptingKey);
const args = {
walletId,
coin: opts.coin,
chain: opts.chain,
name: encCopayerName,
xPubKey,
requestPubKey,
customData: encCustomData,
hardwareSourcePublicKey: opts.hardwareSourcePublicKey,
clientDerivedPublicKey: opts.clientDerivedPublicKey
};
if (opts.dryRun)
args.dryRun = true;
if ([true, false].includes(opts.supportBIP44AndP2PKH))
args.supportBIP44AndP2PKH = opts.supportBIP44AndP2PKH;
const hash = common_1.Utils.getCopayerHash(args.name, args.xPubKey, args.requestPubKey);
args.copayerSignature = common_1.Utils.signMessage(hash, walletPrivKey);
const url = '/v2/wallets/' + walletId + '/copayers';
this.request.post(url, args, (err, body) => {
if (err)
return cb(err);
this._processWallet(body.wallet);
return cb(null, body.wallet);
});
}
isComplete() {
return this.credentials && this.credentials.isComplete();
}
_extractPublicKeyRing(copayers) {
return (copayers || []).map(copayer => ({
xPubKey: copayer.xPubKey,
requestPubKey: copayer.requestPubKey,
copayerName: copayer.name
}));
}
getFeeLevels(chain, network, cb) {
$.checkArgument(chain || common_1.Constants.CHAINS.includes(chain));
$.checkArgument(network || ['livenet', 'testnet'].includes(network));
this.request.get('/v2/feelevels/?coin=' +
(chain || 'btc') +
'&network=' +
(network || 'livenet'), (err, result) => {
if (err)
return cb(err);
return cb(err, result);
});
}
clearCache(opts, cb) {
if (typeof opts === 'function') {
cb = opts;
opts = {};
}
const qs = Object.entries(opts || {}).map(([key, value]) => `${key}=${value}`).join('&');
this.request.post('/v1/clearcache/' + (qs ? '?' + qs : ''), {}, (err, res) => {
return cb(err, res);
});
}
getVersion(cb) {
this.request.get('/v1/version/', cb);
}
_checkKeyDerivation() {
var isInvalid = this.keyDerivationOk === false;
if (isInvalid) {
log_1.default.error('Key derivation for this device is not working as expected');
}
return !isInvalid;
}
createWallet(walletName, copayerName, m, n, opts, cb) {
var _a;
if (!this._checkKeyDerivation())
return cb(new Error('Cannot create new wallet'));
if (opts)
$.shouldBeObject(opts);
opts = opts || {};
var coin = opts.coin || 'btc';
var chain = ((_a = opts.chain) === null || _a === void 0 ? void 0 : _a.toLowerCase()) || coin;
if (!common_1.Constants.CHAINS.includes(chain))
return cb(new Error('Invalid chain'));
var network = opts.network || 'livenet';
if (!['testnet', 'livenet', 'regtest'].includes(network))
return cb(new Error('Invalid network: ' + network));
if (!this.credentials) {
return cb(new Error('Import credentials first with setCredentials()'));
}
if (coin != this.credentials.coin) {
return cb(new Error('Existing keys were created for a different coin'));
}
if (network != this.credentials.network) {
return cb(new Error('Existing keys were created for a different network'));
}
var walletPrivKey = opts.walletPrivKey || new Bitcore.PrivateKey();
var c = this.credentials;
c.addWalletPrivateKey(walletPrivKey.toString());
var encWalletName = common_1.Utils.encryptMessage(walletName, c.sharedEncryptingKey);
var args = {
name: encWalletName,
m,
n,
pubKey: new Bitcore.PrivateKey(walletPrivKey).toPublicKey().toString(),
chain,
coin,
network,
singleAddress: !!opts.singleAddress,
id: opts.id,
usePurpose48: n > 1,
useNativeSegwit: !!opts.useNativeSegwit,
segwitVersion: opts.segwitVersion,
hardwareSourcePublicKey: c.hardwareSourcePublicKey,
clientDerivedPublicKey: c.clientDerivedPublicKey
};
this.request.post('/v2/wallets/', args, (err, res) => {
if (err)
return cb(err);
var walletId = res.walletId;
c.addWalletInfo(walletId, walletName, m, n, copayerName, {
useNativeSegwit: opts.useNativeSegwit,
segwitVersion: opts.segwitVersion
});
var secret = API._buildSecret(c.walletId, c.walletPrivKey, c.coin, c.network);
this._doJoinWallet(walletId, walletPrivKey, c.xPubKey, c.requestPubKey, copayerName, {
coin,
chain,
hardwareSourcePublicKey: c.hardwareSourcePublicKey,
clientDerivedPublicKey: c.clientDerivedPublicKey
}, (err, wallet) => {
if (err)
return cb(err);
return cb(null, n > 1 ? secret : null);
});
});
}
joinWallet(secret, copayerName, opts, cb) {
if (!cb) {
cb = opts;
opts = {};
log_1.default.warn('DEPRECATED WARN: joinWallet should receive 4 parameters.');
}
if (!this._checkKeyDerivation())
return cb(new Error('Cannot join wallet'));
opts = opts || {};
var coin = opts.coin || 'btc';
var chain = opts.chain || coin;
if (!common_1.Constants.CHAINS.includes(chain))
return cb(new Error('Invalid chain'));
try {
var secretData = API.parseSecret(secret);
}
catch (ex) {
return cb(ex);
}
if (!this.credentials) {
return cb(new Error('Import credentials first with setCredentials()'));
}
this.credentials.addWalletPrivateKey(secretData.walletPrivKey.toString());
this._doJoinWallet(secretData.walletId, secretData.walletPrivKey, this.credentials.xPubKey, this.credentials.requestPubKey, copayerName, {
coin,
chain,
dryRun: !!opts.dryRun
}, (err, wallet) => {
if (err)
return cb(err);
if (!opts.dryRun) {
this.credentials.addWalletInfo(wallet.id, wallet.name, wallet.m, wallet.n, copayerName, {
useNativeSegwit: common_1.Utils.isNativeSegwit(wallet.addressType),
segwitVersion: common_1.Utils.getSegwitVersion(wallet.addressType),
allowOverwrite: true
});
}
return cb(null, wallet);
});
}
recreateWallet(cb) {
$.checkState(this.credentials, 'Failed state: this.credentials at <recreateWallet()>');
$.checkState(this.credentials.isComplete());
$.checkState(this.credentials.walletPrivKey);
this.getStatus({ includeExtendedInfo: true }, err => {
if (!err) {
log_1.default.info('Wallet is already created');
return cb();
}
var c = this.credentials;
var walletPrivKey = Bitcore.PrivateKey.fromString(c.walletPrivKey);
var walletId = c.walletId;
var useNativeSegwit = common_1.Utils.isNativeSegwit(c.addressType);
var segwitVersion = common_1.Utils.getSegwitVersion(c.addressType);
var supportBIP44AndP2PKH = c.derivationStrategy != common_1.Constants.DERIVATION_STRATEGIES.BIP45;
var encWalletName = common_1.Utils.encryptMessage(c.walletName || 'recovered wallet', c.sharedEncryptingKey);
var args = {
name: encWalletName,
m: c.m,
n: c.n,
pubKey: walletPrivKey.toPublicKey().toString(),
coin: c.coin,
chain: c.chain,
network: c.network,
id: walletId,
usePurpose48: c.n > 1,
useNativeSegwit,
segwitVersion
};
if (!!supportBIP44AndP2PKH) {
args['supportBIP44AndP2PKH'] = supportBIP44AndP2PKH;
}
this.request.post('/v2/wallets/', args, (err, body) => {
if (err) {
log_1.default.info('openWallet error' + err);
return cb(new errors_1.Errors.WALLET_DOES_NOT_EXIST());
}
if (!walletId) {
walletId = body.walletId;
}
var i = 1;
var opts = {
coin: c.coin,
chain: c.chain
};
if (!!supportBIP44AndP2PKH)
opts['supportBIP44AndP2PKH'] = supportBIP44AndP2PKH;
async_1.default.each(this.credentials.publicKeyRing, (item, next) => {
var name = item.copayerName || 'copayer ' + i++;
this._doJoinWallet(walletId, walletPrivKey, item.xPubKey, item.requestPubKey, name, opts, err => {
if (err instanceof errors_1.Errors.COPAYER_IN_WALLET)
return next();
return next(err);
});
}, cb);
});
});
}
_processWallet(wallet) {
var encryptingKey = this.credentials.sharedEncryptingKey;
var name = common_1.Utils.decryptMessageNoThrow(wallet.name, encryptingKey);
if (name != wallet.name) {
wallet.encryptedName = wallet.name;
}
wallet.name = name;
for (const copayer of wallet.copayers || []) {
var name = common_1.Utils.decryptMessageNoThrow(copayer.name, encryptingKey);
if (name != copayer.name) {
copayer.encryptedName = copayer.name;
}
copayer.name = name;
for (const access of copayer.requestPubKeys || []) {
if (!access.name)
continue;
var name = common_1.Utils.decryptMessageNoThrow(access.name, encryptingKey);
if (name != access.name) {
access.encryptedName = access.name;
}
access.name = name;
}
}
}
_processStatus(status) {
var processCustomData = data => {
const copayers = data.wallet.copayers;
if (!copayers)
return;
const me = copayers.find(c => c.id === this.credentials.copayerId);
if (!me || !me.customData)
return;
var customData;
try {
customData = JSON.parse(common_1.Utils.decryptMessage(me.customData, this.credentials.personalEncryptingKey));
}
catch (e) {
log_1.default.warn('Could not decrypt customData:', me.customData);
}
if (!customData)
return;
data.customData = customData;
if (!this.credentials.walletPrivKey && customData.walletPrivKey)
this.credentials.addWalletPrivateKey(customData.walletPrivKey);
};
processCustomData(status);
this._processWallet(status.wallet);
this._processTxps(status.pendingTxps);
}
getNotifications(opts, cb) {
$.checkState(this.credentials, 'Failed state: this.credentials at <getNotifications()>');
opts = opts || {};
var url = '/v1/notifications/';
if (opts.lastNotificationId) {
url += '?notificationId=' + opts.lastNotificationId;
}
else if (opts.timeSpan) {
url += '?timeSpan=' + opts.timeSpan;
}
this.request.getWithLogin(url, (err, result) => {
if (err)
return cb(err);
result = result || [];
const notifications = opts.includeOwn ? result : result.filter(notification => notification.creatorId != this.credentials.copayerId);
return cb(null, notifications);
});
}
getStatus(opts, cb) {
$.checkState(this.credentials, 'Failed state: this.credentials at <getStatus()>');
if (!cb) {
cb = opts;
opts = {};
log_1.default.warn('DEPRECATED WARN: getStatus should receive 2 parameters.');
}
opts = opts || {};
const qs = [];
qs.push('includeExtendedInfo=' + (opts.includeExtendedInfo ? '1' : '0'));
qs.push('twoStep=' + (opts.twoStep ? '1' : '0'));
qs.push('serverMessageArray=1');
if (opts.tokenAddress) {
qs.push('tokenAddress=' + opts.tokenAddress);
}
if (opts.multisigContractAddress) {
qs.push('multisigContractAddress=' + opts.multisigContractAddress);
qs.push('network=' + this.credentials.network);
}
this.request.get('/v3/wallets/?' + qs.join('&'), (err, result) => {
if (err)
return cb(err);
if (result.wallet.status == 'pending') {
var c = this.credentials;
result.wallet.secret = API._buildSecret(c.walletId, c.walletPrivKey, c.coin, c.network);
}
this._processStatus(result);
return cb(err, result);
});
}
getPreferences(cb) {
$.checkState(this.credentials, 'Failed state: this.credentials at <getPreferences()>');
$.checkArgument(cb);
this.request.get('/v1/preferences/', (err, preferences) => {
if (err)
return cb(err);
return cb(null, preferences);
});
}
savePreferences(preferences, cb) {
$.checkState(this.credentials, 'Failed state: this.credentials at <savePreferences()>');
$.checkArgument(cb);
this.request.put('/v1/preferences/', preferences, cb);
}
fetchPayPro(opts, cb) {
$.checkArgument(opts).checkArgument(opts.payProUrl);
paypro_1.PayPro.get({
url: opts.payProUrl,
coin: this.credentials.coin || 'btc',
network: this.credentials.network || 'livenet',
request: this.request
}, (err, paypro) => {
if (err)
return cb(err);
return cb(null, paypro);
});
}
getUtxos(opts, cb) {
$.checkState(this.credentials && this.credentials.isComplete(), 'Failed state: this.credentials at <getUtxos()>');
opts = opts || {};
let url = '/v1/utxos/';
if (opts.addresses) {
url +=
'?' +
querystring_1.default.stringify({
addresses: [].concat(opts.addresses).join(',')
});
}
this.request.get(url, cb);
}
getCoinsForTx(opts, cb) {
$.checkState(this.credentials && this.credentials.isComplete(), 'Failed state: this.credentials at <getCoinsForTx()>');
$.checkArgument(opts && (opts.coin || opts.chain) && opts.network && opts.txId, 'Missing required parameter(s)');
opts.chain = opts.chain || opts.coin;
let url = '/v1/txcoins/';
url +=
'?' +
querystring_1.default.stringify({
coin: opts.chain,
network: opts.network,
txId: opts.txId
});
this.request.get(url, cb);
}
_getCreateTxProposalArgs(opts) {
const args = JSON.parse(JSON.stringify(opts));
args.message = API._encryptMessage(opts.message, this.credentials.sharedEncryptingKey) || null;
args.payProUrl = opts.payProUrl || null;
args.isTokenSwap = opts.isTokenSwap || null;
args.replaceTxByFee = opts.replaceTxByFee || null;
for (const o of args.outputs) {
o.message = API._encryptMessage(o.message, this.credentials.sharedEncryptingKey) || null;
}
return args;
}
createTxProposal(opts, cb, baseUrl) {
$.checkState(this.credentials && this.credentials.isComplete(), 'Failed state: this.credentials at <createTxProposal()>');
$.checkState(this.credentials.sharedEncryptingKey);
$.checkArgument(opts);
if (!opts.signingMethod && this.credentials.coin == 'bch') {
opts.signingMethod = 'schnorr';
}
var args = this._getCreateTxProposalArgs(opts);
baseUrl = baseUrl || '/v3/txproposals/';
this.request.post(baseUrl, args, (err, txp) => {
if (err)
return cb(err);
this._processTxps(txp);
if (!verifier_1.Verifier.checkProposalCreation(args, txp, this.credentials.sharedEncryptingKey)) {
return cb(new errors_1.Errors.SERVER_COMPROMISED());
}
return cb(null, txp);
});
}
publishTxProposal(opts, cb) {
$.checkState(this.credentials && this.credentials.isComplete(), 'Failed state: this.credentials at <publishTxProposal()>');
$.checkArgument(opts === null || opts === void 0 ? void 0 : opts.txp, 'No txp was given to publish');
$.checkState(parseInt(opts.txp.version) >= 3);
var t = common_1.Utils.buildTx(opts.txp);
var hash = t.uncheckedSerialize();
var args = {
proposalSignature: common_1.Utils.signMessage(hash, this.credentials.requestPrivKey)
};
var url = '/v2/txproposals/' + opts.txp.id + '/publish/';
this.request.post(url, args, (err, txp) => {
if (err)
return cb(err);
this._processTxps(txp);
return cb(null, txp);
});
}
createAddress(opts, cb) {
$.checkState(this.credentials && this.credentials.isComplete(), 'Failed state: this.credentials at <createAddress()>');
if (!cb) {
cb = opts;
opts = {};
log_1.default.warn('DEPRECATED WARN: createAddress should receive 2 parameters.');
}
if (!this._checkKeyDerivation())
return cb(new Error('Cannot create new address for this wallet'));
opts = opts || {};
this.request.post('/v4/addresses/', opts, (err, address) => {
if (err)
return cb(err);
if (!verifier_1.Verifier.checkAddress(this.credentials, address)) {
return cb(new errors_1.Errors.SERVER_COMPROMISED());
}
return cb(null, address);
});
}
getMainAddresses(opts, cb) {
$.checkState(this.credentials && this.credentials.isComplete());
opts = opts || {};
var args = [];
if (opts.limit)
args.push('limit=' + opts.limit);
if (opts.reverse)
args.push('reverse=1');
var qs = '';
if (args.length > 0) {
qs = '?' + args.join('&');
}
var url = '/v1/addresses/' + qs;
this.request.get(url, (err, addresses) => {
if (err)
return cb(err);
if (!opts.doNotVerify) {
const fake = (addresses || []).some(address => !verifier_1.Verifier.checkAddress(this.credentials, address));
if (fake)
return cb(new errors_1.Errors.SERVER_COMPROMISED());
}
return cb(null, addresses);
});
}
getBalance(opts, cb) {
if (!cb) {
cb = opts;
opts = {};
log_1.default.warn('DEPRECATED WARN: getBalance should receive 2 parameters.');
}
opts = opts || {};
$.checkState(this.credentials && this.credentials.isComplete(), 'Failed state: this.credentials at <getBalance()>');
opts.chain = opts.chain || opts.coin;
var args = [];
if (opts.coin) {
args.push('coin=' + opts.coin);
}
if (opts.tokenAddress) {
args.push('tokenAddress=' + opts.tokenAddress);
}
if (opts.multisigContractAddress) {
args.push('multisigContractAddress=' + opts.multisigContractAddress);
}
var qs = '';
if (args.length > 0) {
qs = '?' + args.join('&');
}
var url = '/v1/balance/' + qs;
this.request.get(url, cb);
}
getTxProposals(opts, cb) {
$.checkState(this.credentials && this.credentials.isComplete(), 'Failed state: this.credentials at <getTxProposals()>');
opts = opts || {};
this.request.get('/v2/txproposals/', (err, txps) => {
if (err)
return cb(err);
this._processTxps(txps);
async_1.default.every(txps, (txp, acb) => {
if (opts.doNotVerify)
return acb(true);
this.getPayProV2(txp)
.then(paypro => {
var isLegit = verifier_1.Verifier.checkTxProposal(this.credentials, txp, {
paypro
});
return acb(isLegit);
})
.catch(err => {
return acb(err);
});
}, isLegit => {
if (!isLegit)
return cb(new errors_1.Errors.SERVER_COMPROMISED());
var result;
if (opts.forAirGapped) {
result = {
txps: JSON.parse(JSON.stringify(txps)),
encryptedPkr: opts.doNotEncryptPkr
? null
: common_1.Utils.encryptMessage(JSON.stringify(this.credentials.publicKeyRing), this.credentials.personalEncryptingKey),
unencryptedPkr: opts.doNotEncryptPkr
? JSON.stringify(this.credentials.publicKeyRing)
: null,
m: this.credentials.m,
n: this.credentials.n
};
}
else {
result = txps;
}
return cb(null, result);
});
});
}
getPayPro(txp, cb) {
if (!txp.payProUrl || this.doNotVerifyPayPro)
return cb();
paypro_1.PayPro.get({
url: txp.payProUrl,
coin: txp.coin || 'btc',
network: txp.network || 'livenet',
request: this.request
}, (err, paypro) => {
if (err)
return cb(new Error('Could not fetch invoice:' + (err.message ? err.message : err)));
return cb(null, paypro);
});
}
getPayProV2(txp) {
if (!txp.payProUrl || this.doNotVerifyPayPro)
return Promise.resolve();
const chain = txp.chain || common_1.Utils.getChain(txp.coin);
const currency = common_1.Utils.getCurrencyCodeFromCoinAndChain(txp.coin, chain);
const payload = {
address: txp.from
};
return payproV2_1.PayProV2.selectPaymentOption({
paymentUrl: txp.payProUrl,
chain,
currency,
payload
});
}
pushSignatures(txp, signatures, cb, baseUrl) {
$.checkState(this.credentials && this.credentials.isComplete(), 'Failed state: this.credentials at <pushSignatures()>');
$.checkArgument(txp.creatorId);
if (!(signatures === null || signatures === void 0 ? void 0 : signatures.length)) {
return cb('No signatures to push. Sign the transaction with Key first');
}
this.getPayProV2(txp)
.then(paypro => {
const isLegit = verifier_1.Verifier.checkTxProposal(this.credentials, txp, { paypro });
if (!isLegit)
return cb(new errors_1.Errors.SERVER_COMPROMISED());
baseUrl = baseUrl || '/v2/txproposals/';
const url = baseUrl + txp.id + '/signatures/';
const args = { signatures };
this.request.post(url, args, (err, txp) => {
if (err)
return cb(err);
this._processTxps(txp);
return cb(null, txp);
});
})
.catch(err => {
return cb(err);
});
}
createAdvertisement(opts, cb) {
var url = '/v1/advertisements/';
let args = opts;
this.request.post(url, args, (err, createdAd) => {
if (err) {
return cb(err);
}
return cb(null, createdAd);
});
}
getAdvertisements(opts, cb) {
var url = '/v1/advertisements/';
if (opts.testing === true) {
url = '/v1/advertisements/' + '?testing=