mirror of https://github.com/BTCPrivate/copay.git
Feat: Import wallet
This commit is contained in:
parent
07f06b22a7
commit
595971e907
|
@ -23,7 +23,7 @@
|
|||
<div *ngIf="selectedTab == 'file'">
|
||||
<ion-item>
|
||||
<ion-label stacked>Choose a backup file from your computer</ion-label>
|
||||
<ion-input type="file" [(ngModel)]="formData.file" formControlName="file"></ion-input>
|
||||
<ion-input type="file" accept="json" [(ngModel)]="formData.file" (change)="fileChangeEvent($event)" formControlName="file"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
|
@ -34,7 +34,7 @@
|
|||
|
||||
<ion-item-divider color="light"></ion-item-divider>
|
||||
|
||||
<ion-item (click)="showAdvOpts = !showAdvOpts">
|
||||
<ion-item (click)="toggleShowAdvOpts()">
|
||||
<ion-label *ngIf="!showAdvOpts">Show advanced options</ion-label>
|
||||
<ion-label *ngIf="showAdvOpts">Hide advanced options</ion-label>
|
||||
</ion-item>
|
||||
|
|
|
@ -1,40 +1,69 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { NavController, NavParams } from 'ionic-angular';
|
||||
import { Validators, FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { Logger } from '@nsalaun/ng-logger';
|
||||
|
||||
// Pages
|
||||
import { HomePage } from '../../../pages/home/home';
|
||||
|
||||
// Providers
|
||||
import { BwcProvider } from '../../../providers/bwc/bwc';
|
||||
import { WalletProvider } from '../../../providers/wallet/wallet';
|
||||
import { DerivationPathHelperProvider } from '../../../providers/derivation-path-helper/derivation-path-helper';
|
||||
import { ConfigProvider } from '../../../providers/config/config';
|
||||
import { DerivationPathHelperProvider } from '../../../providers/derivation-path-helper/derivation-path-helper';
|
||||
import { OnGoingProcessProvider } from '../../../providers/on-going-process/on-going-process';
|
||||
import { PlatformProvider } from '../../../providers/platform/platform';
|
||||
import { ProfileProvider } from '../../../providers/profile/profile';
|
||||
import { PopupProvider } from '../../../providers/popup/popup';
|
||||
import { WalletProvider } from '../../../providers/wallet/wallet';
|
||||
|
||||
@Component({
|
||||
selector: 'page-import-wallet',
|
||||
templateUrl: 'import-wallet.html'
|
||||
})
|
||||
export class ImportWalletPage implements OnInit {
|
||||
public fromOnboarding: boolean;
|
||||
public formData: any;
|
||||
public showAdvOpts: boolean;
|
||||
public selectedTab: string;
|
||||
public seedOptions: any;
|
||||
|
||||
private derivationPathByDefault: string;
|
||||
private derivationPathForTestnet: string;
|
||||
private importForm: FormGroup;
|
||||
private reader: FileReader;
|
||||
private defaults: any;
|
||||
private config: any;
|
||||
private errors: any;
|
||||
private importErr: boolean;
|
||||
|
||||
public fromOnboarding: boolean;
|
||||
public formData: any;
|
||||
public showAdvOpts: boolean;
|
||||
public selectedTab: string;
|
||||
public enableCash: boolean = false;
|
||||
public isCordova: boolean;
|
||||
public file: File;
|
||||
|
||||
constructor(
|
||||
public navCtrl: NavController,
|
||||
private navCtrl: NavController,
|
||||
private navParams: NavParams,
|
||||
private form: FormBuilder,
|
||||
private bwc: BwcProvider,
|
||||
private pathHelper: DerivationPathHelperProvider,
|
||||
private bwcProvider: BwcProvider,
|
||||
private derivationPathHelperProvider: DerivationPathHelperProvider,
|
||||
private walletProvider: WalletProvider,
|
||||
private configProvider: ConfigProvider,
|
||||
private popupProvider: PopupProvider,
|
||||
private platformProvider: PlatformProvider,
|
||||
private logger: Logger,
|
||||
private onGoingProcessProvider: OnGoingProcessProvider,
|
||||
private profileProvider: ProfileProvider
|
||||
) {
|
||||
this.reader = new FileReader();
|
||||
this.defaults = configProvider.getDefaults();
|
||||
this.config = configProvider.get();
|
||||
this.errors = bwcProvider.getErrors();
|
||||
|
||||
this.isCordova = this.platformProvider.isCordova;
|
||||
this.importErr = false;
|
||||
this.fromOnboarding = this.navParams.data.fromOnboarding;
|
||||
this.selectedTab = 'words';
|
||||
this.derivationPathByDefault = this.pathHelper.default;
|
||||
this.derivationPathForTestnet = this.pathHelper.defaultTestnet;
|
||||
this.derivationPathByDefault = this.derivationPathHelperProvider.default;
|
||||
this.derivationPathForTestnet = this.derivationPathHelperProvider.defaultTestnet;
|
||||
this.showAdvOpts = false;
|
||||
this.formData = {
|
||||
words: null,
|
||||
|
@ -43,8 +72,14 @@ export class ImportWalletPage implements OnInit {
|
|||
filePassword: null,
|
||||
derivationPath: this.derivationPathByDefault,
|
||||
testnet: false,
|
||||
bwsURL: this.configProvider.get()['bws']['url'],
|
||||
bwsURL: this.defaults.bws.url,
|
||||
coin: this.navParams.data.coin ? this.navParams.data.coin : null
|
||||
};
|
||||
|
||||
if (this.config.cashSupport) this.enableCash = true;
|
||||
|
||||
if (this.navParams.data.code)
|
||||
this.processWalletInfo(this.navParams.data.code);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
|
@ -85,19 +120,259 @@ export class ImportWalletPage implements OnInit {
|
|||
this.importForm.get('filePassword').updateValueAndValidity();
|
||||
}
|
||||
|
||||
setDerivationPath() {
|
||||
this.formData.derivationPath = this.formData.testnet ? this.derivationPathForTestnet : this.derivationPathByDefault;
|
||||
}
|
||||
|
||||
normalizeMnemonic(words: string) {
|
||||
if (!words || !words.indexOf) return words;
|
||||
var isJA = words.indexOf('\u3000') > -1;
|
||||
var wordList = words.split(/[\u3000\s]+/);
|
||||
|
||||
return wordList.join(isJA ? '\u3000' : ' ');
|
||||
}
|
||||
|
||||
private processWalletInfo(code: string): void {
|
||||
if (!code) return;
|
||||
|
||||
this.importErr = false;
|
||||
let parsedCode = code.split('|');
|
||||
|
||||
if (parsedCode.length != 5) {
|
||||
/// Trying to import a malformed wallet export QR code
|
||||
this.popupProvider.ionicAlert('Error', 'Incorrect code format'); //TODO gettextcatalog
|
||||
return;
|
||||
}
|
||||
|
||||
let info = {
|
||||
type: parsedCode[0],
|
||||
data: parsedCode[1],
|
||||
network: parsedCode[2],
|
||||
derivationPath: parsedCode[3],
|
||||
hasPassphrase: parsedCode[4] == 'true' ? true : false
|
||||
};
|
||||
|
||||
if (info.type == '1' && info.hasPassphrase)
|
||||
this.popupProvider.ionicAlert('Error', 'Password required. Make sure to enter your password in advanced options'); //TODO gettextcatalog
|
||||
|
||||
this.formData.derivationPath = info.derivationPath;
|
||||
this.formData.testnetEnabled = info.network == 'testnet' ? true : false;
|
||||
this.formData.words = info.data;
|
||||
}
|
||||
|
||||
public switchTestnetOff(): void {
|
||||
this.formData.testnetEnabled = false;
|
||||
this.setDerivationPath();
|
||||
}
|
||||
|
||||
private setDerivationPath(): void {
|
||||
this.formData.derivationPath = this.formData.testnet ? this.derivationPathForTestnet : this.derivationPathByDefault;
|
||||
}
|
||||
|
||||
private importBlob(str: string, opts: any): void {
|
||||
let str2: string;
|
||||
let err: any = null;
|
||||
try {
|
||||
str2 = this.bwcProvider.getSJCL().decrypt(this.formData.filePassword, str);
|
||||
} catch (e) {
|
||||
err = 'Could not decrypt file, check your password'; //TODO gettextcatalog
|
||||
this.logger.warn(e);
|
||||
};
|
||||
|
||||
if (err) {
|
||||
this.popupProvider.ionicAlert('Error', err); //TODO gettextcatalog
|
||||
return;
|
||||
}
|
||||
|
||||
this.onGoingProcessProvider.set('importingWallet', true);
|
||||
opts.compressed = null;
|
||||
opts.password = null;
|
||||
|
||||
setTimeout(() => {
|
||||
this.profileProvider.importWallet(str2, opts).then((wallet: any) => {
|
||||
this.onGoingProcessProvider.set('importingWallet', false);
|
||||
this.finish(wallet);
|
||||
}).catch((err: any) => {
|
||||
this.onGoingProcessProvider.set('importingWallet', false);
|
||||
this.popupProvider.ionicAlert('Error', err); //TODO gettextcatalog
|
||||
return;
|
||||
});
|
||||
}, 100);
|
||||
}
|
||||
|
||||
private finish(wallet: any): void {
|
||||
this.walletProvider.updateRemotePreferences(wallet).then(() => {
|
||||
this.profileProvider.setBackupFlag(wallet.credentials.walletId);
|
||||
if (this.fromOnboarding) {
|
||||
this.profileProvider.setDisclaimerAccepted().catch((err: any) => {
|
||||
this.logger.error(err);
|
||||
});
|
||||
}
|
||||
this.navCtrl.setRoot(HomePage);
|
||||
this.navCtrl.popToRoot();
|
||||
}).catch((err: any) => {
|
||||
this.logger.warn(err);
|
||||
});
|
||||
}
|
||||
|
||||
private importExtendedPrivateKey(xPrivKey, opts) {
|
||||
this.onGoingProcessProvider.set('importingWallet', true);
|
||||
setTimeout(() => {
|
||||
this.profileProvider.importExtendedPrivateKey(xPrivKey, opts).then((wallet: any) => {
|
||||
this.onGoingProcessProvider.set('importingWallet', false);
|
||||
this.finish(wallet);
|
||||
}).catch((err: any) => {
|
||||
this.onGoingProcessProvider.set('importingWallet', false);
|
||||
if (err instanceof this.errors.NOT_AUTHORIZED) {
|
||||
this.importErr = true;
|
||||
} else {
|
||||
this.popupProvider.ionicAlert('Error', err); // TODO: gettextcatalog
|
||||
}
|
||||
return;
|
||||
});
|
||||
}, 100);
|
||||
}
|
||||
|
||||
/*
|
||||
IMPORT FROM PUBLIC KEY - PENDING TODO from v1
|
||||
|
||||
var _importExtendedPublicKey = function(xPubKey, opts) {
|
||||
ongoingProcess.set('importingWallet', true);
|
||||
$timeout(function() {
|
||||
profileService.importExtendedPublicKey(opts, function(err, walletId) {
|
||||
ongoingProcess.set('importingWallet', false);
|
||||
if (err) {
|
||||
$scope.error = err;
|
||||
return $timeout(function() {
|
||||
$scope.$apply();
|
||||
});
|
||||
}
|
||||
|
||||
profileService.setBackupFlag(walletId);
|
||||
if ($stateParams.fromOnboarding) {
|
||||
profileService.setDisclaimerAccepted(function(err) {
|
||||
if (err) $log.error(err);
|
||||
});
|
||||
}
|
||||
|
||||
$state.go('tabs.home');
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
*/
|
||||
|
||||
private importMnemonic(words: string, opts: any): void {
|
||||
this.onGoingProcessProvider.set('importingWallet', true);
|
||||
setTimeout(() => {
|
||||
this.profileProvider.importMnemonic(words, opts).then((wallet: any) => {
|
||||
this.onGoingProcessProvider.set('importingWallet', false);
|
||||
this.finish(wallet);
|
||||
}).catch((err: any) => {
|
||||
if (err instanceof this.errors.NOT_AUTHORIZED) {
|
||||
this.importErr = true;
|
||||
} else {
|
||||
this.popupProvider.ionicAlert('Error', err); // TODO: gettextcatalog
|
||||
}
|
||||
});
|
||||
}, 100);
|
||||
}
|
||||
|
||||
|
||||
import() {
|
||||
console.log(this.formData);
|
||||
if (this.selectedTab === 'file') {
|
||||
this.importFromFile();
|
||||
} else {
|
||||
this.importFromMnemonic();
|
||||
}
|
||||
}
|
||||
|
||||
public importFromFile(): void {
|
||||
if (!this.importForm.valid) {
|
||||
this.popupProvider.ionicAlert('Error', 'There is an error in the form'); // TODO: gettextcatalog
|
||||
return;
|
||||
}
|
||||
|
||||
let backupFile = this.file;
|
||||
let backupText = this.formData.backupText;
|
||||
let password = this.formData.filePassword;
|
||||
|
||||
if (!backupFile && !backupText) {
|
||||
this.popupProvider.ionicAlert('Error', 'Please, select your backup file'); // TODO: gettextcatalog
|
||||
return;
|
||||
}
|
||||
|
||||
if (backupFile) {
|
||||
this.reader.readAsBinaryString(backupFile);
|
||||
} else {
|
||||
let opts: any = {};
|
||||
opts.bwsurl = this.formData.bwsurl;
|
||||
opts.coin = this.formData.coin;
|
||||
this.importBlob(backupText, opts);
|
||||
}
|
||||
}
|
||||
|
||||
public importFromMnemonic(): void {
|
||||
if (!this.importForm.valid) {
|
||||
this.popupProvider.ionicAlert('Error', 'There is an error in the form'); // TODO: gettextcatalog
|
||||
return;
|
||||
}
|
||||
|
||||
let opts: any = {};
|
||||
|
||||
if (this.formData.bwsurl)
|
||||
opts.bwsurl = this.formData.bwsurl;
|
||||
|
||||
let pathData: any = this.derivationPathHelperProvider.parse(this.formData.derivationPath);
|
||||
|
||||
if (!pathData) {
|
||||
this.popupProvider.ionicAlert('Error', 'Invalid derivation path'); // TODO: gettextcatalog
|
||||
return;
|
||||
}
|
||||
|
||||
opts.account = pathData.account;
|
||||
opts.networkName = pathData.networkName;
|
||||
opts.derivationStrategy = pathData.derivationStrategy;
|
||||
opts.coin = this.formData.coin;
|
||||
|
||||
let words: string = this.formData.words || null;
|
||||
|
||||
if (!words) {
|
||||
this.popupProvider.ionicAlert('Error', 'Please enter the recovery phrase');
|
||||
return;
|
||||
} else if (words.indexOf('xprv') == 0 || words.indexOf('tprv') == 0) {
|
||||
return this.importExtendedPrivateKey(words, opts);
|
||||
} else if (words.indexOf('xpub') == 0 || words.indexOf('tpuv') == 0) {
|
||||
//return this.importExtendedPublicKey(words, opts); TODO
|
||||
return this.logger.warn("TODO: this.importExtendedPublicKey(words, opts)");
|
||||
} else {
|
||||
let wordList: Array<any> = words.split(/[\u3000\s]+/);
|
||||
|
||||
if ((wordList.length % 3) != 0) {
|
||||
this.popupProvider.ionicAlert('Error', 'Wrong number of recovery words: ' + wordList.length);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
opts.passphrase = this.formData.passphrase || null;
|
||||
this.importMnemonic(words, opts);
|
||||
}
|
||||
|
||||
public toggleShowAdvOpts(): void {
|
||||
this.showAdvOpts = !this.showAdvOpts;
|
||||
}
|
||||
|
||||
public fileChangeEvent($event: any) {
|
||||
this.file = $event.target ? $event.target.files[0] : $event.srcElement.files[0];
|
||||
this.formData.file = $event.target.value;
|
||||
this.getFile();
|
||||
}
|
||||
|
||||
private getFile() {
|
||||
// If we use onloadend, we need to check the readyState.
|
||||
this.reader.onloadend = (evt: any) => {
|
||||
if (evt.target.readyState == 2) { // DONE == 2
|
||||
let opts: any = {};
|
||||
opts.bwsurl = this.formData.bwsurl;
|
||||
opts.coin = this.formData.coin;
|
||||
this.importBlob(evt.target.result, opts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ export class OnGoingProcessProvider {
|
|||
content: processName
|
||||
});
|
||||
this.loading.present();
|
||||
|
||||
}
|
||||
else {
|
||||
this.loading.dismiss();
|
||||
|
|
|
@ -709,9 +709,9 @@ export class WalletProvider {
|
|||
});
|
||||
}
|
||||
|
||||
public updateRemotePreferences(clients: any, prefs: any): Promise<any> {
|
||||
public updateRemotePreferences(clients: any, prefs?: any): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
prefs = prefs || {};
|
||||
prefs = prefs ? prefs : {};
|
||||
|
||||
if (!lodash.isArray(clients))
|
||||
clients = [clients];
|
||||
|
@ -723,13 +723,16 @@ export class WalletProvider {
|
|||
this.logger.debug('Saving remote preferences', wallet.credentials.walletName, prefs);
|
||||
|
||||
wallet.savePreferences(prefs, (err: any) => {
|
||||
|
||||
if (err) {
|
||||
this.popupProvider.ionicAlert(this.bwcErrorProvider.msg(err, 'Could not save preferences on the server')); //TODO Gettextcatalog
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
updateRemotePreferencesFor(clients, prefs);
|
||||
updateRemotePreferencesFor(clients, prefs).then(() => {
|
||||
return resolve();
|
||||
}).catch((err: any) => {
|
||||
return reject(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
@ -740,7 +743,7 @@ export class WalletProvider {
|
|||
|
||||
//prefs.email (may come from arguments)
|
||||
prefs.email = config.emailNotifications.email;
|
||||
prefs.language = "English" // This line was hardcoded - TODO: prefs.language = uxLanguage.getCurrentLanguage();
|
||||
prefs.language = "en" // This line was hardcoded - TODO: prefs.language = uxLanguage.getCurrentLanguage();
|
||||
// prefs.unit = walletSettings.unitCode; // TODO: remove, not used
|
||||
|
||||
updateRemotePreferencesFor(lodash.clone(clients), prefs).then(() => {
|
||||
|
|
Loading…
Reference in New Issue