copay-crown
Version:
A Secure Crown Wallet
389 lines (338 loc) • 13 kB
text/typescript
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { TranslateService } from '@ngx-translate/core';
import { Events, NavController, NavParams } from 'ionic-angular';
import { Logger } from '../../../providers/logger/logger';
// Pages
import { TabsPage } from '../../tabs/tabs';
// Providers
import { BwcProvider } from '../../../providers/bwc/bwc';
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 { PopupProvider } from '../../../providers/popup/popup';
import { ProfileProvider } from '../../../providers/profile/profile';
import { WalletProvider } from '../../../providers/wallet/wallet';
export class ImportWalletPage {
private derivationPathByDefault: string;
private derivationPathForTestnet: string;
private importForm: FormGroup;
private reader: FileReader;
private defaults: any;
private errors: any;
public importErr: boolean;
public fromOnboarding: boolean;
public formFile: any;
public showAdvOpts: boolean;
public selectedTab: string;
public isCordova: boolean;
public isSafari: boolean;
public isIOS: boolean;
public file: File;
public testnetEnabled: boolean;
constructor(
private navCtrl: NavController,
private navParams: NavParams,
private form: FormBuilder,
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,
private translate: TranslateService,
private events: Events
) {
this.reader = new FileReader();
this.defaults = this.configProvider.getDefaults();
this.errors = bwcProvider.getErrors();
this.isCordova = this.platformProvider.isCordova;
this.isSafari = this.platformProvider.isSafari;
this.isIOS = this.platformProvider.isIOS;
this.importErr = false;
this.fromOnboarding = this.navParams.data.fromOnboarding;
this.selectedTab = 'words';
this.derivationPathByDefault = this.derivationPathHelperProvider.default;
this.derivationPathForTestnet = this.derivationPathHelperProvider.defaultTestnet;
this.showAdvOpts = false;
this.formFile = null;
this.importForm = this.form.group({
words: [null, Validators.required],
backupText: [null],
mnemonicPassword: [null],
file: [null],
filePassword: [null],
derivationPath: [this.derivationPathByDefault, Validators.required],
testnet: [false],
bwsURL: [this.defaults.bws.url],
coin: [this.navParams.data.coin ? this.navParams.data.coin : 'btc']
});
}
ionViewWillEnter() {
if (this.navParams.data.code) {
this.processWalletInfo(this.navParams.data.code);
}
}
selectTab(tab: string) {
this.selectedTab = tab;
switch (tab) {
case 'words':
this.file = null;
this.formFile = null;
this.importForm.get('words').setValidators([Validators.required]);
this.importForm.get('filePassword').clearValidators();
if (this.isCordova || this.isSafari) this.importForm.get('backupText').clearValidators();
else this.importForm.get('file').clearValidators();
break;
case 'file':
if (this.isCordova || this.isSafari) this.importForm.get('backupText').setValidators([Validators.required]);
else this.importForm.get('file').setValidators([Validators.required]);
this.importForm.get('filePassword').setValidators([Validators.required]);
this.importForm.get('words').clearValidators();
break;
default:
this.importForm.get('words').clearValidators();
this.importForm.get('file').clearValidators();
this.importForm.get('filePassword').clearValidators();
break;
}
this.importForm.get('words').updateValueAndValidity();
this.importForm.get('file').updateValueAndValidity();
this.importForm.get('filePassword').updateValueAndValidity();
this.importForm.get('backupText').updateValueAndValidity();
}
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
let title = this.translate.instant('Error');
let subtitle = this.translate.instant('Incorrect code format');
this.popupProvider.ionicAlert(title, subtitle);
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) {
let title = this.translate.instant('Error');
let subtitle = this.translate.instant('Password required. Make sure to enter your password in advanced options');
this.popupProvider.ionicAlert(title, subtitle);
}
this.testnetEnabled = info.network == 'testnet' ? true : false;
this.importForm.controls['derivationPath'].setValue(info.derivationPath);
this.importForm.controls['words'].setValue(info.data);
}
public setDerivationPath(): void {
let path = this.testnetEnabled ? this.derivationPathForTestnet : this.derivationPathByDefault;
this.importForm.controls['derivationPath'].setValue(path);
}
private importBlob(str: string, opts: any): void {
let str2: string;
let err: any = null;
try {
str2 = this.bwcProvider.getSJCL().decrypt(this.importForm.value.filePassword, str);
} catch (e) {
err = this.translate.instant('Could not decrypt file, check your password');
this.logger.warn(e);
};
if (err) {
let title = this.translate.instant('Error');
this.popupProvider.ionicAlert(title, err);
return;
}
this.onGoingProcessProvider.set('importingWallet');
opts.compressed = null;
opts.password = null;
setTimeout(() => {
this.profileProvider.importWallet(str2, opts).then((wallet: any) => {
this.onGoingProcessProvider.clear();
this.finish(wallet);
}).catch((err: any) => {
this.onGoingProcessProvider.clear();
let title = this.translate.instant('Error');
this.popupProvider.ionicAlert(title, err);
return;
});
}, 100);
}
private finish(wallet: any): void {
this.walletProvider.updateRemotePreferences(wallet).then(() => {
this.profileProvider.setBackupFlag(wallet.credentials.walletId);
this.events.publish('status:updated');
if (this.fromOnboarding) {
this.profileProvider.setOnboardingCompleted().then(() => {
this.profileProvider.setDisclaimerAccepted().catch((err: any) => {
this.logger.error(err);
});
}).catch((err: any) => {
this.logger.error(err);
});
this.navCtrl.setRoot(TabsPage);
this.navCtrl.popToRoot();
}
else {
this.navCtrl.popToRoot().then(() => {
this.navCtrl.parent.select(0);
});
}
}).catch((err: any) => {
this.logger.warn(err);
});
}
private importExtendedPrivateKey(xPrivKey, opts) {
this.onGoingProcessProvider.set('importingWallet');
setTimeout(() => {
this.profileProvider.importExtendedPrivateKey(xPrivKey, opts).then((wallet: any) => {
this.onGoingProcessProvider.clear();
this.finish(wallet);
}).catch((err: any) => {
if (err instanceof this.errors.NOT_AUTHORIZED) {
this.importErr = true;
} else {
let title = this.translate.instant('Error');
this.popupProvider.ionicAlert(title, err);
}
this.onGoingProcessProvider.clear();
return;
});
}, 100);
}
private importMnemonic(words: string, opts: any): void {
this.onGoingProcessProvider.set('importingWallet');
setTimeout(() => {
this.profileProvider.importMnemonic(words, opts).then((wallet: any) => {
this.onGoingProcessProvider.clear();
this.finish(wallet);
}).catch((err: any) => {
if (err instanceof this.errors.NOT_AUTHORIZED) {
this.importErr = true;
} else {
let title = this.translate.instant('Error');
this.popupProvider.ionicAlert(title, err);
}
this.onGoingProcessProvider.clear();
return;
});
}, 100);
}
import() {
if (this.selectedTab === 'file') {
this.importFromFile();
} else {
this.importFromMnemonic();
}
}
public importFromFile(): void {
if (!this.importForm.valid) {
let title = this.translate.instant('Error');
let subtitle = this.translate.instant('There is an error in the form');
this.popupProvider.ionicAlert(title, subtitle);
return;
}
let backupFile = this.file;
let backupText = this.importForm.value.backupText;
if (!backupFile && !backupText) {
let title = this.translate.instant('Error');
let subtitle = this.translate.instant('Please, select your backup file');
this.popupProvider.ionicAlert(title, subtitle);
return;
}
if (backupFile) {
this.reader.readAsBinaryString(backupFile);
} else {
let opts: any = {};
opts.bwsurl = this.importForm.value.bwsurl;
opts.coin = this.importForm.value.coin;
this.importBlob(backupText, opts);
}
}
public importFromMnemonic(): void {
if (!this.importForm.valid) {
let title = this.translate.instant('Error');
let subtitle = this.translate.instant('There is an error in the form');
this.popupProvider.ionicAlert(title, subtitle);
return;
}
let opts: any = {};
if (this.importForm.value.bwsurl)
opts.bwsurl = this.importForm.value.bwsurl;
let pathData: any = this.derivationPathHelperProvider.parse(this.importForm.value.derivationPath);
if (!pathData) {
let title = this.translate.instant('Error');
let subtitle = this.translate.instant('Invalid derivation path');
this.popupProvider.ionicAlert(title, subtitle);
return;
}
opts.account = pathData.account;
opts.networkName = pathData.networkName;
opts.derivationStrategy = pathData.derivationStrategy;
opts.coin = this.importForm.value.coin;
let words: string = this.importForm.value.words || null;
if (!words) {
let title = this.translate.instant('Error');
let subtitle = this.translate.instant('Please enter the recovery phrase');
this.popupProvider.ionicAlert(title, subtitle);
return;
} else if (words.indexOf('xprv') == 0 || words.indexOf('tprv') == 0) {
return this.importExtendedPrivateKey(words, opts);
} else {
let wordList: any[] = words.split(/[\u3000\s]+/);
if ((wordList.length % 3) != 0) {
let title = this.translate.instant('Error');
let subtitle = this.translate.instant('Wrong number of recovery words:');
this.popupProvider.ionicAlert(title, subtitle + ' ' + wordList.length);
return;
}
}
opts.passphrase = this.importForm.value.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.formFile = $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.importForm.value.bwsurl;
opts.coin = this.importForm.value.coin;
this.importBlob(evt.target.result, opts);
}
}
}
public openScanner(): void {
if (this.navParams.data.fromScan) {
this.navCtrl.popToRoot({ animate: false });
} else {
this.navCtrl.popToRoot({ animate: false }).then(() => {
this.navCtrl.parent.select(2);
});
}
}
}