diff --git a/.eslintrc.js b/.eslintrc.js
index aa2b7b50ef..f353b562b6 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -2,7 +2,7 @@ module.exports = {
extends: ["@react-native-community", "plugin:prettier/recommended"],
overrides: [
{
- "files": ["e2e/*.spec.js", "e2e/init.js"],
+ "files": ["e2e/*.spec.js", "e2e/init.js", "e2e/e2eUtils.js"],
"rules": {
"no-undef": "off"
}
@@ -22,6 +22,7 @@ module.exports = {
},
},
rules: {
+ "no-bitwise": "off",
"comma-dangle": ["error", "never"],
"object-curly-spacing": ["error", "always"],
"quotes": ["error", "single", { "avoidEscape": true }],
diff --git a/android/app/src/main/assets/fonts/RobotoMono-Regular.ttf b/android/app/src/main/assets/fonts/RobotoMono-Regular.ttf
new file mode 100644
index 0000000000..5919b5d1bf
Binary files /dev/null and b/android/app/src/main/assets/fonts/RobotoMono-Regular.ttf differ
diff --git a/e2e/e2eUtils.js b/e2e/e2eUtils.js
new file mode 100644
index 0000000000..22f3f32500
--- /dev/null
+++ b/e2e/e2eUtils.js
@@ -0,0 +1,57 @@
+// Copyright 2015-2019 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+'use strict';
+
+import testIDs from './testIDs';
+const { IdentityPin } = testIDs;
+
+export const testTap = async buttonId => await element(by.id(buttonId)).tap();
+
+export const testVisible = async componentId =>
+ await expect(element(by.id(componentId))).toBeVisible();
+
+export const testExist = async componentId =>
+ await expect(element(by.id(componentId))).toExist();
+
+export const testNotExist = async componentId =>
+ await expect(element(by.id(componentId))).toNotExist();
+
+export const testNotVisible = async componentId =>
+ await expect(element(by.id(componentId))).toBeNotVisible();
+
+export const tapBack = async () =>
+ await element(by.id(testIDs.Header.headerBackButton))
+ .atIndex(0)
+ .tap();
+
+export const testInput = async (inputId, inputText) => {
+ await element(by.id(inputId)).typeText(inputText);
+ await element(by.id(inputId)).tapReturnKey();
+};
+
+export const testScrollAndTap = async (buttonId, screenId) => {
+ await waitFor(element(by.id(buttonId)))
+ .toBeVisible()
+ .whileElement(by.id(screenId))
+ .scroll(100, 'down');
+ await testTap(buttonId);
+};
+
+export const testUnlockPin = async pinCode => {
+ await testInput(IdentityPin.unlockPinInput, pinCode);
+ await testTap(IdentityPin.unlockPinButton);
+};
diff --git a/e2e/firstTest.spec.js b/e2e/firstTest.spec.js
deleted file mode 100644
index 8ac8de3f36..0000000000
--- a/e2e/firstTest.spec.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import testIDs from './testIDs';
-
-describe('Load test', () => {
- it('should have account list screen', async () => {
- await expect(element(by.id(testIDs.TacScreen.tacView))).toBeVisible();
- await element(by.id(testIDs.TacScreen.agreePrivacyButton)).tap();
- await element(by.id(testIDs.TacScreen.agreeTacButton)).tap();
- await element(by.id(testIDs.TacScreen.nextButton)).tap();
- await expect(
- element(by.id(testIDs.AccountListScreen.accountList))
- ).toBeVisible();
- });
-});
diff --git a/e2e/identityManipulation.spec.js b/e2e/identityManipulation.spec.js
new file mode 100644
index 0000000000..4d9a4687d5
--- /dev/null
+++ b/e2e/identityManipulation.spec.js
@@ -0,0 +1,143 @@
+// Copyright 2015-2019 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+'use strict';
+
+import testIDs from './testIDs';
+import {
+ tapBack,
+ testExist,
+ testInput,
+ testNotExist,
+ testNotVisible,
+ testScrollAndTap,
+ testTap,
+ testUnlockPin,
+ testVisible
+} from './e2eUtils';
+
+const {
+ TacScreen,
+ AccountNetworkChooser,
+ IdentitiesSwitch,
+ IdentityManagement,
+ IdentityNew,
+ IdentityBackup,
+ IdentityPin,
+ PathDerivation,
+ PathDetail,
+ PathsList,
+ SignedTx,
+ TxDetails
+} = testIDs;
+
+const pinCode = '123456';
+const mockIdentityName = 'mockIdentity';
+const substrateNetworkButtonIndex = AccountNetworkChooser.networkButton + '2'; //Need change if network list changes
+const fundingPath = '//funding/0';
+const mockSeedPhrase =
+ 'split cradle example drum veteran swear cruel pizza guilt surface mansion film grant benefit educate marble cargo ignore bind include advance grunt exile grow';
+
+const testSetUpDefaultPath = async () => {
+ await testInput(IdentityPin.setPin, pinCode);
+ await testInput(IdentityPin.confirmPin, pinCode);
+ await testTap(IdentityPin.submitButton);
+ await testVisible(AccountNetworkChooser.chooserScreen);
+ await testScrollAndTap(
+ substrateNetworkButtonIndex,
+ testIDs.AccountNetworkChooser.chooserScreen
+ );
+ await testUnlockPin(pinCode);
+ await testExist(PathsList.pathCard + '//kusama_CC2//default');
+};
+
+describe('Load test', async () => {
+ beforeAll(async () => {
+ if (device.getPlatform() === 'ios') {
+ await device.clearKeychain();
+ }
+ await device.launchApp({ permissions: { camera: 'YES' } });
+ });
+
+ it('should have account list screen', async () => {
+ await testVisible(TacScreen.tacView);
+ await testTap(TacScreen.agreePrivacyButton);
+ await testTap(TacScreen.agreeTacButton);
+ await testTap(TacScreen.nextButton);
+ await testVisible(AccountNetworkChooser.noAccountScreen);
+ });
+
+ it('recover a identity with seed phrase', async () => {
+ await testTap(AccountNetworkChooser.recoverButton);
+ await testVisible(IdentityNew.seedInput);
+ await element(by.id(IdentityNew.seedInput)).typeText(mockSeedPhrase);
+ await testInput(IdentityNew.nameInput, mockIdentityName);
+ await testTap(IdentityNew.recoverButton);
+ await testSetUpDefaultPath();
+ });
+
+ it('create a new identity with default substrate account', async () => {
+ await tapBack();
+ await testTap(IdentitiesSwitch.toggleButton);
+ await testTap(IdentitiesSwitch.addIdentityButton);
+ await testNotVisible(IdentityNew.seedInput);
+ await testTap(IdentityNew.createButton);
+ await testVisible(IdentityBackup.seedText);
+ await testTap(IdentityBackup.nextButton);
+ await element(by.text('Proceed')).tap();
+ await testSetUpDefaultPath();
+ });
+
+ it('derive a new key', async () => {
+ await testTap(PathsList.deriveButton);
+ await testInput(PathDerivation.nameInput, 'first one');
+ await testInput(PathDerivation.pathInput, fundingPath);
+ await testTap(PathDerivation.deriveButton);
+ await testUnlockPin(pinCode);
+ await testExist(PathsList.pathCard + `//kusama_CC2${fundingPath}`);
+ });
+
+ it('delete a path', async () => {
+ await tapBack();
+ await testTap(AccountNetworkChooser.networkButton + '0');
+ await testTap(PathsList.pathCard + `//kusama_CC2${fundingPath}`);
+ await testTap(PathDetail.popupMenuButton);
+ await testTap(PathDetail.deleteButton);
+ await element(by.text('Delete')).tap();
+ await testUnlockPin(pinCode);
+ await testNotExist(PathsList.pathCard + `//kusama_CC2${fundingPath}`);
+ });
+
+ it('should sign the transaction', async () => {
+ await tapBack();
+ await testTap(AccountNetworkChooser.scanButton);
+ await testScrollAndTap(TxDetails.signButton, TxDetails.scrollScreen);
+ await testUnlockPin(pinCode);
+ await testVisible(SignedTx.qrView);
+ });
+
+ it('delete identity', async () => {
+ await element(by.id(IdentitiesSwitch.toggleButton))
+ .atIndex(0)
+ .tap();
+ await testTap(IdentitiesSwitch.manageIdentityButton);
+ await testTap(IdentityManagement.popupMenuButton);
+ await testTap(IdentityManagement.deleteButton);
+ await element(by.text('Delete')).tap();
+ await testUnlockPin(pinCode);
+ await testVisible(IdentitiesSwitch.modal);
+ });
+});
diff --git a/e2e/init.js b/e2e/init.js
index 7a5ed06347..5d04e50487 100644
--- a/e2e/init.js
+++ b/e2e/init.js
@@ -1,3 +1,21 @@
+// Copyright 2015-2019 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+'use strict';
+
const detox = require('detox');
const config = require('../package.json').detox;
const adapter = require('detox/runners/jest/adapter');
@@ -12,7 +30,7 @@ jasmine.getEnv().addReporter(adapter);
jasmine.getEnv().addReporter(specReporter);
beforeAll(async () => {
- await detox.init(config);
+ await detox.init(config, { launchApp: false });
});
beforeEach(async () => {
diff --git a/e2e/mock.js b/e2e/mock.js
new file mode 100644
index 0000000000..49ca453424
--- /dev/null
+++ b/e2e/mock.js
@@ -0,0 +1,36 @@
+// Copyright 2015-2019 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+'use strict';
+
+import { NETWORK_LIST, SubstrateNetworkKeys } from '../src/constants';
+
+export const signingTestIdentityPath = `//${NETWORK_LIST[SubstrateNetworkKeys.KUSAMA].pathID}//default`;
+
+const setRemarkExtrinsicKusama =
+ '47900000100005301023c36776005aec2f32a34c109dc791a82edef980eec3be80da938ac9bcc68217220170000010c11111165030000fa030000e3777fa922cafbff200cadeaea1a76bd7898ad5b89f7848999058b50e715f636dbb5aefb451e26bd64faf476301f980437d87c0d88dec1a8c7a3eb3cc82e9bbb0ec';
+
+export const createMockSignRequest = () => ({
+ bounds: {
+ height: 1440,
+ origin: [],
+ width: 1920
+ },
+ data: '',
+ rawData: setRemarkExtrinsicKusama,
+ target: 319,
+ type: 'QR_CODE'
+});
diff --git a/e2e/testIDs.js b/e2e/testIDs.js
index 48d607c51d..c04bcb30a7 100644
--- a/e2e/testIDs.js
+++ b/e2e/testIDs.js
@@ -1,12 +1,93 @@
+// Copyright 2015-2019 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+'use strict';
+
const testIDs = {
AccountListScreen: {
accountList: 'accountList'
},
+ AccountNetworkChooser: {
+ addNewNetworkButton: 'anc_add_button',
+ chooserScreen: 'anc_chooser_scree',
+ createButton: 'anc_create_button',
+ networkButton: 'anc_network_button',
+ noAccountScreen: 'anc_no_account_screen',
+ recoverButton: 'anc_recover_button',
+ scanButton: 'anc_scan_button',
+ showExistedButton: 'anc_show_existed'
+ },
+ Header: {
+ headerBackButton: 'header_back_button'
+ },
+ IdentitiesSwitch: {
+ addIdentityButton: 'identities_switch_add_identity',
+ manageIdentityButton: 'identities_switch_manager_button',
+ modal: 'identity_switch_modal',
+ toggleButton: 'identities_switch_toggle_button'
+ },
+ IdentityBackup: {
+ nextButton: 'identity_backup_next',
+ seedText: 'identity_backup_seed'
+ },
+ IdentityManagement: {
+ deleteButton: 'identity_management_delete_button',
+ popupMenuButton: 'identity_management_popup_menu'
+ },
+ IdentityNew: {
+ createButton: 'identity_new_create_button',
+ nameInput: 'identity_new_name_input',
+ recoverButton: 'identity_new_recover_button',
+ seedInput: 'identity_new_seed_input'
+ },
+ IdentityPin: {
+ confirmPin: 'identity_pin_confirm',
+ scrollScreen: 'identity_pin_scroll',
+ setPin: 'identity_pin_set',
+ submitButton: 'identity_submit_button',
+ unlockPinButton: 'identity_unlock_pin_button',
+ unlockPinInput: 'identity_unlock_pin_input'
+ },
+ PathDerivation: {
+ deriveButton: 'path_derivation_derive_button',
+ nameInput: 'path_derivation_name_input',
+ pathInput: 'path_derivation_path_input'
+ },
+ PathDetail: {
+ deleteButton: 'path_detail_delete_button',
+ popupMenuButton: 'path_detail_popup_menu_button'
+ },
+ PathsList: {
+ deriveButton: 'path_list_derive_button',
+ pathCard: 'path_list_path_card',
+ scanButton: 'path_list_scan_button'
+ },
+ SignedMessage: {},
+ SignedTx: {
+ qrView: 'signed_tx_qr_view'
+ },
TacScreen: {
agreePrivacyButton: 'tac_privacy',
agreeTacButton: 'tac_agree',
nextButton: 'tac_next',
tacView: 'tac_view'
+ },
+ TxDetails: {
+ scrollScreen: 'tx_details_scroll',
+ signButton: 'tx_details_sign_button'
}
};
diff --git a/ios/NativeSigner.xcodeproj/project.pbxproj b/ios/NativeSigner.xcodeproj/project.pbxproj
index 369899772b..39498aa6a3 100644
--- a/ios/NativeSigner.xcodeproj/project.pbxproj
+++ b/ios/NativeSigner.xcodeproj/project.pbxproj
@@ -5,7 +5,6 @@
};
objectVersion = 46;
objects = {
-
/* Begin PBXBuildFile section */
00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */; };
00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; };
@@ -45,6 +44,7 @@
5DC40D98E51D492C8FF692B5 /* Manifold-CF-Light.otf in Resources */ = {isa = PBXBuildFile; fileRef = 31AAF2CB51C04377BFC79634 /* Manifold-CF-Light.otf */; };
6686E5A219254E3D8880285E /* Octicons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = F2FB2DDD90964B3BB1FC813C /* Octicons.ttf */; };
6701864923270B1100A14061 /* assets in Resources */ = {isa = PBXBuildFile; fileRef = 6701864823270B1100A14061 /* assets */; };
+ 67E3BED3237C17A6007882FA /* RobotoMono-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 67E3BEA2237C17A6007882FA /* RobotoMono-Regular.ttf */; };
6FBA1E2F42104B26A94F3EE0 /* Ionicons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 84D1117E71B04C839F00F619 /* Ionicons.ttf */; };
740B033F930D4E6FB906D8C2 /* MaterialCommunityIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = CB5C4A7F0E8B4E4E98E06EFD /* MaterialCommunityIcons.ttf */; };
768CDFD4C47549429C87DEDD /* Entypo.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 6A7AB3BD3C1A4E0EB3C4B3B6 /* Entypo.ttf */; };
@@ -477,6 +477,7 @@
5F744F56289845F0A1085BBB /* Roboto-MediumItalic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Roboto-MediumItalic.ttf"; path = "../res/fonts/Roboto-MediumItalic.ttf"; sourceTree = ""; };
624E6C0FF4A64A97A7D51BEF /* Manifold-CF-Bold.otf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Manifold-CF-Bold.otf"; path = "../res/fonts/Manifold-CF-Bold.otf"; sourceTree = ""; };
6701864823270B1100A14061 /* assets */ = {isa = PBXFileReference; lastKnownFileType = folder; path = assets; sourceTree = ""; };
+ 67E3BEA2237C17A6007882FA /* RobotoMono-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "RobotoMono-Regular.ttf"; sourceTree = ""; };
6A7AB3BD3C1A4E0EB3C4B3B6 /* Entypo.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Entypo.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Entypo.ttf"; sourceTree = ""; };
6C53A63D96B24FDD95AF1C97 /* Fontisto.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Fontisto.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Fontisto.ttf"; sourceTree = ""; };
7733AB637FF54DE5B174F42C /* Zocial.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Zocial.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Zocial.ttf"; sourceTree = ""; };
@@ -765,6 +766,7 @@
53880E8FF84C419EB11ACA5C /* Roboto-Light.ttf */,
8C99AB759A004CEB88AC4455 /* Roboto-LightItalic.ttf */,
E3DA81F74A0847378E71E280 /* Roboto-Medium.ttf */,
+ 67E3BEA2237C17A6007882FA /* RobotoMono-Regular.ttf */,
5F744F56289845F0A1085BBB /* Roboto-MediumItalic.ttf */,
5CD421FF72D947B0AA9EBABB /* Roboto-Regular.ttf */,
020454D0D3C74A398EC7F440 /* Roboto-Thin.ttf */,
@@ -1415,6 +1417,7 @@
7A7137D6EEDE40C184F30C15 /* Roboto-Regular.ttf in Resources */,
16614F66487241CE933918B8 /* Roboto-Thin.ttf in Resources */,
0CFC8839F5B54F888E6C01DE /* Roboto-ThinItalic.ttf in Resources */,
+ 67E3BED3237C17A6007882FA /* RobotoMono-Regular.ttf in Resources */,
3C31DDCB4CD0465084344D5F /* Manifold-CF-Bold.otf in Resources */,
AD0B6F7EACB74BA7A42D2A2E /* Manifold-CF-Demi-Bold.otf in Resources */,
F1569210427145DEBBB5B898 /* Manifold-CF-Extra-Bold.otf in Resources */,
diff --git a/ios/NativeSigner/Info.plist b/ios/NativeSigner/Info.plist
index 0da84f6d22..a93fa124c6 100644
--- a/ios/NativeSigner/Info.plist
+++ b/ios/NativeSigner/Info.plist
@@ -43,6 +43,7 @@
LaunchScreen
UIAppFonts
+ RobotoMono-Regular.ttf
Roboto-Black.ttf
Roboto-BlackItalic.ttf
Roboto-Bold.ttf
diff --git a/ios/RobotoMono-Regular.ttf b/ios/RobotoMono-Regular.ttf
new file mode 100644
index 0000000000..5919b5d1bf
Binary files /dev/null and b/ios/RobotoMono-Regular.ttf differ
diff --git a/jest.config.js b/jest.config.js
index 4c729261f3..8cbfc1cafc 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -19,7 +19,7 @@ const { defaults } = require('jest-config');
module.exports = {
moduleFileExtensions: [...defaults.moduleFileExtensions, 'ts', 'tsx'],
preset: 'react-native',
- roots: ['/src'],
+ roots: ['/specs'],
setupFiles: ['/jest-setup.js'],
testEnvironment: 'node',
testPathIgnorePatterns: ['/node_modules/'],
diff --git a/package.json b/package.json
index c13b68100b..369a46746b 100644
--- a/package.json
+++ b/package.json
@@ -22,11 +22,11 @@
"start": "NODE_OPTIONS=--max_old_space_size=8192 react-native start",
"test": "jest",
"test-rust": "cd ./rust/signer && cargo test && cd ../..",
- "build-e2e:android": "detox build -c android.emu.debug",
- "test-e2e:android": "detox test -c android.emu.debug",
+ "build-e2e:android": "detox build -c android.emu.debug -l info",
+ "test-e2e:android": "detox test -c android.emu.debug -l info --noStackTrace",
"e2e:android": "yarn run build-e2e:android && yarn run test-e2e:android",
- "build-e2e:ios": "detox build -c ios.sim.debug",
- "test-e2e:ios": "detox test -c ios.sim.debug",
+ "build-e2e:ios": "detox build -c ios.sim.debug -l info",
+ "test-e2e:ios": "detox test -c ios.sim.debug -l info --noStackTrace",
"e2e:ios": "yarn run build-e2e:ios && yarn run test-e2e:ios"
},
"husky": {
@@ -51,6 +51,7 @@
"react": "^16.9.0",
"react-native": "0.60.5",
"react-native-camera": "^3.4.0",
+ "react-native-elements": "^1.2.6",
"react-native-gesture-handler": "^1.4.1",
"react-native-keyboard-aware-scroll-view": "^0.9.1",
"react-native-markdown-renderer": "^3.2.8",
@@ -75,7 +76,7 @@
"babel-eslint": "10.0.3",
"babel-jest": "^24.9.0",
"babel-plugin-rewrite-require": "^1.14.5",
- "detox": "^14.5.0",
+ "detox": "^14.7.0",
"eslint": "^6.3.0",
"eslint-config-prettier": "^6.2.0",
"eslint-plugin-prettier": "^3.1.0",
diff --git a/res/fonts/RobotoMono-Regular.ttf b/res/fonts/RobotoMono-Regular.ttf
new file mode 100755
index 0000000000..5919b5d1bf
Binary files /dev/null and b/res/fonts/RobotoMono-Regular.ttf differ
diff --git a/res/img/card_shadow-1.png b/res/img/card_shadow-1.png
new file mode 100644
index 0000000000..a6a031fb25
Binary files /dev/null and b/res/img/card_shadow-1.png differ
diff --git a/res/img/card_shadow.png b/res/img/card_shadow.png
new file mode 100644
index 0000000000..ad2454ea2d
Binary files /dev/null and b/res/img/card_shadow.png differ
diff --git a/res/img/card_shadow@2x.png b/res/img/card_shadow@2x.png
new file mode 100644
index 0000000000..5bcac89be7
Binary files /dev/null and b/res/img/card_shadow@2x.png differ
diff --git a/icon.png b/res/img/icon.png
similarity index 100%
rename from icon.png
rename to res/img/icon.png
diff --git a/res/img/logos/eth-classic.png b/res/img/logos/eth-classic.png
new file mode 100644
index 0000000000..7d38a69f2e
Binary files /dev/null and b/res/img/logos/eth-classic.png differ
diff --git a/res/img/logos/eth-classic@2x.png b/res/img/logos/eth-classic@2x.png
new file mode 100644
index 0000000000..b8f66d0d0d
Binary files /dev/null and b/res/img/logos/eth-classic@2x.png differ
diff --git a/res/img/logos/eth-classic@3x.png b/res/img/logos/eth-classic@3x.png
new file mode 100644
index 0000000000..e5919749d5
Binary files /dev/null and b/res/img/logos/eth-classic@3x.png differ
diff --git a/res/img/logos/eth.png b/res/img/logos/eth.png
new file mode 100644
index 0000000000..2c063d2d2c
Binary files /dev/null and b/res/img/logos/eth.png differ
diff --git a/res/img/logos/eth@2x.png b/res/img/logos/eth@2x.png
new file mode 100644
index 0000000000..a20e4f6d47
Binary files /dev/null and b/res/img/logos/eth@2x.png differ
diff --git a/res/img/logos/eth@3x.png b/res/img/logos/eth@3x.png
new file mode 100644
index 0000000000..8c31019dd1
Binary files /dev/null and b/res/img/logos/eth@3x.png differ
diff --git a/res/img/logos/kusama.png b/res/img/logos/kusama.png
new file mode 100644
index 0000000000..c66cff1180
Binary files /dev/null and b/res/img/logos/kusama.png differ
diff --git a/res/img/logos/kusama@2x.png b/res/img/logos/kusama@2x.png
new file mode 100644
index 0000000000..bebf413075
Binary files /dev/null and b/res/img/logos/kusama@2x.png differ
diff --git a/res/img/logos/kusama@3x.png b/res/img/logos/kusama@3x.png
new file mode 100644
index 0000000000..b8c8e5276e
Binary files /dev/null and b/res/img/logos/kusama@3x.png differ
diff --git a/res/img/logos/substrate-dev.png b/res/img/logos/substrate-dev.png
new file mode 100644
index 0000000000..e7ae85095d
Binary files /dev/null and b/res/img/logos/substrate-dev.png differ
diff --git a/res/img/logos/substrate-dev@2x.png b/res/img/logos/substrate-dev@2x.png
new file mode 100644
index 0000000000..fa8b637a39
Binary files /dev/null and b/res/img/logos/substrate-dev@2x.png differ
diff --git a/res/img/logos/substrate-dev@3x.png b/res/img/logos/substrate-dev@3x.png
new file mode 100644
index 0000000000..c088856522
Binary files /dev/null and b/res/img/logos/substrate-dev@3x.png differ
diff --git a/src/util/checksum.spec.js b/specs/util/checksum.spec.js
similarity index 94%
rename from src/util/checksum.spec.js
rename to specs/util/checksum.spec.js
index 001f404ad2..ba6b7d287f 100644
--- a/src/util/checksum.spec.js
+++ b/specs/util/checksum.spec.js
@@ -14,7 +14,9 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see .
-import { checksummedAddress } from './checksum';
+'use strict';
+
+import { checksummedAddress } from '../../src/util/checksum';
describe('checksum', () => {
it('create a proper checksum of an address', () => {
diff --git a/src/util/decoders.spec.js b/specs/util/decoders.spec.js
similarity index 96%
rename from src/util/decoders.spec.js
rename to specs/util/decoders.spec.js
index fb40f8cfd7..0e0de67e11 100644
--- a/src/util/decoders.spec.js
+++ b/specs/util/decoders.spec.js
@@ -27,7 +27,10 @@ import Call from '@polkadot/types/primitive/Generic/Call';
import { u8aConcat } from '@polkadot/util';
import { checkAddress, decodeAddress } from '@polkadot/util-crypto';
-import { SUBSTRATE_NETWORK_LIST, SubstrateNetworkKeys } from '../constants';
+import {
+ SUBSTRATE_NETWORK_LIST,
+ SubstrateNetworkKeys
+} from '../../src/constants';
import {
constructDataFromBytes,
rawDataToU8A,
@@ -35,9 +38,9 @@ import {
hexToAscii,
decodeToString,
isJsonString
-} from './decoders';
-import { isAscii } from './strings';
-import kusamaData from './static-kusama';
+} from '../../src/util/decoders';
+import { isAscii } from '../../src/util/strings';
+import kusamaData from '../../src/util/static-kusama';
const SUBSTRATE_ID = new Uint8Array([0x53]);
const CRYPTO_SR25519 = new Uint8Array([0x01]);
@@ -97,6 +100,7 @@ const SIGN_TX_TEST = u8aConcat(
new GenericExtrinsicPayload(SIGNER_PAYLOAD_TEST, { version: 4 }).toU8a()
);
+/* eslint-disable-next-line jest/no-disabled-tests */
describe.skip('sanity check', () => {
it('sanity check address is kusama', () => {
expect(checkAddress(KUSAMA_ADDRESS, 2)).toEqual([true, null]);
diff --git a/specs/util/identitiesUtils.spec.js b/specs/util/identitiesUtils.spec.js
new file mode 100644
index 0000000000..ffebfb98dd
--- /dev/null
+++ b/specs/util/identitiesUtils.spec.js
@@ -0,0 +1,179 @@
+// Copyright 2015-2019 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+'use strict';
+
+import {
+ deserializeIdentities,
+ getAvailableNetworkKeys,
+ getPathName,
+ groupPaths,
+ serializeIdentities
+} from '../../src/util/identitiesUtils';
+import {
+ EthereumNetworkKeys,
+ SubstrateNetworkKeys,
+ UnknownNetworkKeys
+} from '../../src/constants';
+
+const accountIdFunding1 = 'address1',
+ accountIdFunding2 = 'address2',
+ accountIdSoft = 'address3',
+ accountIdPolkadot = 'address5',
+ accountIdStaking = 'address4',
+ accountIdEthereum = 'address6',
+ accountIdDefault = 'addressDefault',
+ paths = [
+ '//kusama_CC2//default',
+ '//kusama_CC2//funding/1',
+ '//kusama_CC2/softKey1',
+ '//kusama_CC2//funding/2',
+ '//kusama_CC2//staking/1',
+ '//polkadot//default',
+ '1'
+ ],
+ metaDefault = {
+ accountId: accountIdDefault,
+ createdAt: 1571068850409,
+ name: '',
+ updatedAt: 1571078850509
+ },
+ metaFunding1 = {
+ accountId: accountIdFunding1,
+ createdAt: 1571068850409,
+ name: 'funding account1',
+ updatedAt: 1571078850509
+ },
+ metaFunding2 = {
+ accountId: accountIdFunding2,
+ createdAt: 1571068850409,
+ name: '',
+ updatedAt: 1571078850509
+ },
+ metaStaking = {
+ accountId: accountIdStaking,
+ createdAt: 1571068850409,
+ name: '',
+ updatedAt: 1571078850509
+ },
+ metaPolkadot = {
+ accountId: accountIdPolkadot,
+ createdAt: 1573142786972,
+ name: 'PolkadotFirst',
+ updatedAt: 1573142786972
+ },
+ metaEthereum = {
+ accountId: accountIdEthereum,
+ createdAt: 1573142786972,
+ name: 'Eth account',
+ updatedAt: 1573142786972
+ },
+ metaSoftKey = {
+ accountId: accountIdSoft,
+ createdAt: 1573142786972,
+ name: '',
+ updatedAt: 1573142786972
+ };
+const accountIdsMap = new Map([
+ [accountIdDefault, paths[0]],
+ [accountIdFunding1, paths[1]],
+ [accountIdSoft, paths[2]],
+ [accountIdFunding2, paths[3]],
+ [accountIdStaking, paths[4]],
+ [accountIdPolkadot, paths[5]],
+ [accountIdEthereum, paths[6]]
+]);
+const metaMap = new Map([
+ [paths[0], metaDefault],
+ [paths[1], metaFunding1],
+ [paths[2], metaSoftKey],
+ [paths[3], metaStaking],
+ [paths[4], metaFunding2],
+ [paths[5], metaPolkadot],
+ [paths[6], metaEthereum]
+]);
+const testIdentities = [
+ {
+ accountIds: accountIdsMap,
+ derivationPassword: '',
+ encryptedSeedPhrase: 'yyyy',
+ meta: metaMap,
+ name: 'identity1'
+ },
+ {
+ accountIds: accountIdsMap,
+ derivationPassword: '',
+ encryptedSeedPhrase: 'xxxx',
+ meta: metaMap,
+ name: 'identity2'
+ }
+];
+
+describe('IdentitiesUtils', () => {
+ it('works with serialize and deserialize', () => {
+ const serializedJson = serializeIdentities(testIdentities);
+ const originItem = deserializeIdentities(serializedJson);
+ expect(originItem).toEqual(testIdentities);
+ });
+
+ it('regroup the paths', () => {
+ const kusamaPaths = paths.slice(0, 5);
+ const groupResult = groupPaths(kusamaPaths);
+ expect(groupResult).toEqual([
+ {
+ paths: [paths[0]],
+ title: '//default'
+ },
+ {
+ paths: [paths[2]],
+ title: '/softKey1'
+ },
+ {
+ paths: [paths[4]],
+ title: '//staking'
+ },
+ {
+ paths: [paths[1], paths[3]],
+ title: '//funding'
+ }
+ ]);
+ });
+
+ it('get the path name', () => {
+ const expectNames = [
+ 'default',
+ 'funding account1',
+ 'softKey1',
+ 'funding2',
+ 'staking1',
+ 'PolkadotFirst',
+ 'Eth account'
+ ];
+ paths.forEach((path, index) => {
+ const name = getPathName(path, testIdentities[0]);
+ expect(name).toEqual(expectNames[index]);
+ });
+ });
+
+ it('get the correspond networkKeys', () => {
+ const networkKeys = getAvailableNetworkKeys(testIdentities[0]);
+ expect(networkKeys).toEqual([
+ EthereumNetworkKeys.FRONTIER,
+ SubstrateNetworkKeys.KUSAMA,
+ UnknownNetworkKeys.UNKNOWN
+ ]);
+ });
+});
diff --git a/src/util/suri.spec.js b/specs/util/suri.spec.js
similarity index 97%
rename from src/util/suri.spec.js
rename to specs/util/suri.spec.js
index 2e7f416176..1f1502b5bf 100644
--- a/src/util/suri.spec.js
+++ b/specs/util/suri.spec.js
@@ -16,7 +16,11 @@
'use strict';
-import { constructSURI, parseDerivationPath, parseSURI } from './suri';
+import {
+ constructSURI,
+ parseDerivationPath,
+ parseSURI
+} from '../../src/util/suri';
describe('derivation path', () => {
describe('parsing', () => {
diff --git a/src/util/units.spec.js b/specs/util/units.spec.js
similarity index 97%
rename from src/util/units.spec.js
rename to specs/util/units.spec.js
index ae543ffca1..04c2cf1b50 100644
--- a/src/util/units.spec.js
+++ b/specs/util/units.spec.js
@@ -22,8 +22,8 @@ import { GenericCall, Metadata } from '@polkadot/types';
import Call from '@polkadot/types/primitive/Generic/Call';
import { formatBalance } from '@polkadot/util';
-import kusamaData from './static-kusama';
-import { fromWei } from './units';
+import kusamaData from '../../src/util/static-kusama';
+import { fromWei } from '../../src/util/units';
describe('units', () => {
describe('ethereum', () => {
diff --git a/src/App.js b/src/App.js
index cecde3cfa2..cc9390e02e 100644
--- a/src/App.js
+++ b/src/App.js
@@ -20,11 +20,12 @@ import '../shim';
import '@polkadot/types/injector';
-import React, { Component } from 'react';
-import { Platform, StatusBar, YellowBox } from 'react-native';
+import React, { Component, PureComponent } from 'react';
+import { Platform, StatusBar, View, YellowBox } from 'react-native';
import {
createAppContainer,
createStackNavigator,
+ createSwitchNavigator,
HeaderBackButton,
withNavigation
} from 'react-navigation';
@@ -34,22 +35,29 @@ import { MenuProvider } from 'react-native-popup-menu';
import '../shim';
import Background from './components/Background';
import colors from './colors';
-import fonts from './fonts';
import HeaderLeftHome from './components/HeaderLeftHome';
import SecurityHeader from './components/SecurityHeader';
import '../ReactotronConfig';
import About from './screens/About';
-import AccountBackup from './screens/AccountBackup';
+import LegacyAccountBackup from './screens/LegacyAccountBackup';
import AccountDetails from './screens/AccountDetails';
import AccountEdit from './screens/AccountEdit';
-import AccountList from './screens/AccountList';
import AccountNetworkChooser from './screens/AccountNetworkChooser';
import AccountNew from './screens/AccountNew';
import AccountPin from './screens/AccountPin';
import AccountRecover from './screens/AccountRecover';
import { AccountUnlock, AccountUnlockAndSign } from './screens/AccountUnlock';
+import LegacyAccountList from './screens/LegacyAccountList';
import Loading from './screens/Loading';
+import IdentityBackup from './screens/IdentityBackup';
+import IdentityManagement from './screens/IdentityManagement';
+import IdentityNew from './screens/IdentityNew';
+import IdentityPin from './screens/IdentityPin';
import MessageDetails from './screens/MessageDetails';
+import PathDerivation from './screens/PathDerivation';
+import PathDetails from './screens/PathDetails';
+import PathsList from './screens/PathsList';
+import PathManagement from './screens/PathManagement';
import PrivacyPolicy from './screens/PrivacyPolicy';
import QrScanner from './screens/QrScanner';
import Security from './screens/Security';
@@ -57,15 +65,17 @@ import SignedMessage from './screens/SignedMessage';
import SignedTx from './screens/SignedTx';
import TermsAndConditions from './screens/TermsAndConditions';
import TxDetails from './screens/TxDetails';
+import LegacyNetworkChooser from './screens/LegacyNetworkChooser';
+import testIDs from '../e2e/testIDs';
const getLaunchArgs = props => {
if (Platform.OS === 'ios') {
if (props.launchArgs && props.launchArgs.includes('-detoxServer')) {
- global.inTest = true;
+ return (global.inTest = true);
}
} else {
if (props.launchArgs && props.launchArgs.hasOwnProperty('detoxServer')) {
- global.inTest = true;
+ return (global.inTest = true);
}
}
global.inTest = false;
@@ -88,7 +98,7 @@ export default class App extends Component {
return (
-
+
@@ -97,130 +107,145 @@ export default class App extends Component {
}
}
-const globalStackNavigationOptions = {
- headerBackTitleStyle: {
- fontFamily: fonts.semiBold,
- fontSize: 20
- },
- headerRight: ,
- headerStyle: {
- backgroundColor: colors.bg,
- borderBottomColor: colors.bg_text_sec,
- borderBottomWidth: 0.5,
- height: 60,
- paddingBottom: 0,
- paddingTop: 0
- },
- headerTintColor: colors.card_bg,
- headerTitleStyle: {
- display: 'none'
- }
+const globalStackNavigationOptions = ({ navigation }) => {
+ const isFirstScreen = navigation.dangerouslyGetParent().state.index === 0;
+ return {
+ headerBackTitleStyle: {
+ color: colors.bg_text_sec
+ },
+ headerLeft: isFirstScreen ? : ,
+ headerRight: ,
+ headerStyle: {
+ backgroundColor: colors.bg,
+ borderBottomColor: colors.bg,
+ borderBottomWidth: 0,
+ elevation: 0,
+ height: 60,
+ paddingBottom: 0,
+ paddingTop: 0
+ },
+ headerTintColor: colors.bg_text_sec,
+ headerTitleStyle: {
+ display: 'none'
+ }
+ };
};
-// A workaround for https://github.com/react-navigation/react-navigation/issues/88
-const SecurityHeaderBackButton = withNavigation(
- class _HeaderBackButton extends Component {
+const HeaderLeftWithBack = withNavigation(
+ class _HeaderBackButton extends PureComponent {
render() {
const { navigation } = this.props;
return (
- navigation.goBack(null)}
- />
+
+ navigation.goBack()}
+ />
+
);
}
}
);
/* eslint-disable sort-keys */
-const Screens = createStackNavigator(
+const tocAndPrivacyPolicyScreens = {
+ TermsAndConditions: {
+ navigationOptions: {
+ headerRight: null
+ },
+ screen: TermsAndConditions
+ },
+ PrivacyPolicy: {
+ navigationOptions: {
+ headerRight: null
+ },
+ screen: PrivacyPolicy
+ }
+};
+
+const Screens = createSwitchNavigator(
{
Loading: {
screen: Loading
},
- Security: {
- screen: createStackNavigator(
- {
- Security: {
- navigationOptions: {
- headerLeft: ,
- headerRight: null
- },
- screen: Security
- }
- },
- {
- defaultNavigationOptions: globalStackNavigationOptions,
- headerMode: 'screen'
- }
- )
- },
- TocAndPrivacyPolicy: {
- screen: createStackNavigator(
- {
- TermsAndConditions: {
- navigationOptions: {
- headerLeft:
- },
- screen: TermsAndConditions
- },
- PrivacyPolicy: {
- screen: PrivacyPolicy
- }
- },
- {
- defaultNavigationOptions: globalStackNavigationOptions,
- initialRouteParams: {
- isWelcome: true
- }
- }
- )
- },
+ TocAndPrivacyPolicy: createStackNavigator(tocAndPrivacyPolicyScreens, {
+ defaultNavigationOptions: globalStackNavigationOptions
+ }),
Welcome: {
screen: createStackNavigator(
{
- AccountList: {
- navigationOptions: {
- headerLeft:
- },
- screen: AccountList
+ AccountNetworkChooser: {
+ screen: AccountNetworkChooser
+ },
+ AccountPin: {
+ screen: AccountPin
+ },
+ AccountUnlock: {
+ screen: AccountUnlock
},
About: {
screen: About
},
- AccountBackup: {
- screen: AccountBackup
- },
AccountDetails: {
screen: AccountDetails
},
AccountEdit: {
screen: AccountEdit
},
- AccountNetworkChooser: {
- screen: AccountNetworkChooser
- },
AccountNew: {
screen: AccountNew
},
- AccountPin: {
- screen: AccountPin
- },
AccountRecover: {
screen: AccountRecover
},
- AccountUnlock: {
- screen: AccountUnlock
- },
AccountUnlockAndSign: {
screen: AccountUnlockAndSign
},
+ LegacyAccountBackup: {
+ screen: LegacyAccountBackup
+ },
+ LegacyAccountList: {
+ screen: LegacyAccountList
+ },
+ LegacyNetworkChooser: {
+ screen: LegacyNetworkChooser
+ },
+ IdentityBackup: {
+ screen: IdentityBackup
+ },
+ IdentityManagement: {
+ screen: IdentityManagement
+ },
+ IdentityNew: {
+ screen: IdentityNew
+ },
+ IdentityPin: {
+ screen: IdentityPin
+ },
MessageDetails: {
screen: MessageDetails
},
+ PathDerivation: {
+ screen: PathDerivation
+ },
+ PathDetails: {
+ screen: PathDetails
+ },
+ PathsList: {
+ screen: PathsList
+ },
+ PathManagement: {
+ screen: PathManagement
+ },
QrScanner: {
screen: QrScanner
},
@@ -232,13 +257,17 @@ const Screens = createStackNavigator(
},
TxDetails: {
screen: TxDetails
- }
+ },
+ Security: {
+ navigationOptions: {
+ headerRight: null
+ },
+ screen: Security
+ },
+ ...tocAndPrivacyPolicyScreens
},
{
- defaultNavigationOptions: globalStackNavigationOptions,
- initialRouteParams: {
- isWelcome: true
- }
+ defaultNavigationOptions: globalStackNavigationOptions
}
)
}
diff --git a/src/colors.js b/src/colors.js
index 912cef597a..379d6621ae 100644
--- a/src/colors.js
+++ b/src/colors.js
@@ -14,14 +14,19 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see .
+'use strict';
+
export default {
- bg: '#1D1D1D',
- bg_alert: '#ED332B',
- bg_button: '#48acf0',
- bg_text: '#FFFFFF',
- bg_text_sec: '#B4B5B0',
+ bg: '#1B1B1B',
+ bg_alert: '#EB5757',
+ bg_button: '#48A5C2',
+ bg_text: '#EEE',
+ bg_text_sec: '#AAA',
bg_warning: '#FAE265',
- card_bg: '#F9F9F9',
- card_bg_text_sec: '#B4B5B0',
- card_text: '#1A1A1A'
+ card_bg: 'rgba(255, 255, 255, 0.15)',
+ card_bgSolid: '#323232',
+ card_bg_text_sec: '#999',
+ card_text: '#1A1A1A',
+ label_text: '#48A5C2',
+ label_text_sec: '#A8CDD9'
};
diff --git a/src/components/AccountCard.js b/src/components/AccountCard.js
index fe15e75737..b9c18d541c 100644
--- a/src/components/AccountCard.js
+++ b/src/components/AccountCard.js
@@ -16,89 +16,116 @@
// @flow
+'use strict';
+
import PropTypes from 'prop-types';
import React from 'react';
import { StyleSheet, Text, View, ViewPropTypes } from 'react-native';
+import Icon from 'react-native-vector-icons/MaterialIcons';
+import Separator from '../components/Separator';
import AccountIcon from './AccountIcon';
import Address from './Address';
-import colors from '../colors';
import { NETWORK_LIST, NetworkProtocols } from '../constants';
-import fonts from '../fonts';
+import fontStyles from '../fontStyles';
import TouchableItem from './TouchableItem';
+import colors from '../colors';
+import { getAddressFromAccountId } from '../util/identitiesUtils';
+import { AccountPrefixedTitle } from './AccountPrefixedTitle';
export default class AccountCard extends React.PureComponent {
static propTypes = {
- address: PropTypes.string.isRequired,
+ accountId: PropTypes.string,
+ isAdd: PropTypes.bool,
networkKey: PropTypes.string,
onPress: PropTypes.func,
seedType: PropTypes.string,
style: ViewPropTypes.style,
- title: PropTypes.string
+ testID: PropTypes.string,
+ title: PropTypes.string,
+ titlePrefix: PropTypes.string
};
static defaultProps = {
onPress: () => {},
- title: 'no name'
+ title: 'No name'
};
render() {
- const { address, networkKey, onPress, seedType, style } = this.props;
+ const {
+ accountId,
+ isAdd,
+ isNetworkCard,
+ networkKey,
+ networkColor,
+ onPress,
+ seedType,
+ style,
+ testID,
+ titlePrefix
+ } = this.props;
let { title } = this.props;
title = title.length ? title : AccountCard.defaultProps.title;
const seedTypeDisplay = seedType || '';
const network =
NETWORK_LIST[networkKey] || NETWORK_LIST[NetworkProtocols.UNKNOWN];
+ const extractAddress = getAddressFromAccountId(accountId);
+
return (
-
-
+
+
+ {isAdd ? (
+
+
+
+ ) : (
-
-
- {title}
-
-
-
+ )}
+
+ {!isNetworkCard && (
+
+
+ {`${network.title}${seedTypeDisplay} `}
+
+
+ )}
+
+ {accountId !== '' && (
+
+ )}
-
- {seedTypeDisplay}
-
-
- {network.title}
-
-
+ />
);
@@ -106,45 +133,25 @@ export default class AccountCard extends React.PureComponent {
}
const styles = StyleSheet.create({
- body: {
- paddingBottom: 20
- },
content: {
- backgroundColor: colors.card_bg,
+ alignItems: 'center',
flexDirection: 'row',
- padding: 10
+ paddingLeft: 16
},
desc: {
flex: 1,
flexDirection: 'column',
justifyContent: 'space-between',
- paddingLeft: 10
+ paddingLeft: 16
},
footer: {
- backgroundColor: '#977CF6',
- flexDirection: 'row',
- justifyContent: 'space-between',
- padding: 5
- },
- footerNetwork: {
- color: colors.card_bg,
- fontFamily: fonts.semiBold
- },
- footerSeedType: {
- color: colors.card_bg,
- fontFamily: fonts.regular
+ alignSelf: 'stretch',
+ height: 88,
+ marginLeft: 8,
+ width: 8
},
icon: {
- height: 47,
- width: 47
- },
- secondaryText: {
- color: colors.bg_text_sec,
- fontFamily: fonts.semiBold,
- fontSize: 14
- },
- titleText: {
- fontFamily: fonts.semiBold,
- fontSize: 20
+ height: 40,
+ width: 40
}
});
diff --git a/src/components/AccountIcon.js b/src/components/AccountIcon.js
index bb3cc55887..7e8dc8fd83 100644
--- a/src/components/AccountIcon.js
+++ b/src/components/AccountIcon.js
@@ -19,7 +19,7 @@
import Identicon from '@polkadot/reactnative-identicon';
import PropTypes from 'prop-types';
import React, { useEffect, useState } from 'react';
-import { Image } from 'react-native';
+import { Image, View } from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons';
import colors from '../colors';
@@ -29,37 +29,53 @@ import { blockiesIcon } from '../util/native';
export default function AccountIcon(props) {
AccountIcon.propTypes = {
address: PropTypes.string.isRequired,
+ network: PropTypes.object,
protocol: PropTypes.string.isRequired
};
- const { address, protocol, style } = props;
+ const { address, protocol, style, network } = props;
const [ethereumIconUri, setEthereumIconUri] = useState('');
useEffect(() => {
+ const loadEthereumIcon = function(ethereumAddress) {
+ blockiesIcon('0x' + ethereumAddress)
+ .then(uri => {
+ setEthereumIconUri(uri);
+ })
+ .catch(console.error);
+ };
+
if (protocol === NetworkProtocols.ETHEREUM) {
loadEthereumIcon(address);
}
- }, [protocol, address]);
-
- const loadEthereumIcon = function(ethereumAddress) {
- blockiesIcon('0x' + ethereumAddress)
- .then(uri => {
- setEthereumIconUri(uri);
- })
- .catch(console.error);
- };
+ }, [address, protocol]);
+ if (address === '') {
+ return (
+
+
+
+ );
+ }
if (protocol === NetworkProtocols.SUBSTRATE) {
- return ;
+ return ;
} else if (protocol === NetworkProtocols.ETHEREUM && ethereumIconUri) {
return (
);
} else {
// if there's no protocol or it's unknown we return a warning
- return ;
+ return ;
}
}
diff --git a/src/components/AccountIconChooser.js b/src/components/AccountIconChooser.js
index 4695ce0aa5..88858232e7 100644
--- a/src/components/AccountIconChooser.js
+++ b/src/components/AccountIconChooser.js
@@ -16,6 +16,8 @@
// @flow
+'use strict';
+
import React from 'react';
import {
FlatList,
diff --git a/src/components/AccountPrefixedTitle.js b/src/components/AccountPrefixedTitle.js
new file mode 100644
index 0000000000..96cac91d73
--- /dev/null
+++ b/src/components/AccountPrefixedTitle.js
@@ -0,0 +1,50 @@
+// Copyright 2015-2019 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+'use strict';
+
+import React from 'react';
+import { Text, View } from 'react-native';
+import PropTypes from 'prop-types';
+
+import fontStyles from '../fontStyles';
+import colors from '../colors';
+
+AccountPrefixedTitle.propTypes = {
+ title: PropTypes.string.isRequired,
+ titlePrefix: PropTypes.string
+};
+
+export function AccountPrefixedTitle({ titlePrefix, title }) {
+ return (
+
+ {titlePrefix && (
+
+ {titlePrefix}
+
+ )}
+
+ {title}
+
+
+ );
+}
diff --git a/src/components/AccountSeed.js b/src/components/AccountSeed.js
index c5f68a102f..e12991db1c 100644
--- a/src/components/AccountSeed.js
+++ b/src/components/AccountSeed.js
@@ -20,6 +20,7 @@ import React, { Component } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import colors from '../colors';
import fonts from '../fonts';
+import fontStyles from '../fontStyles';
import PARITY_WORDS from '../../res/parity_wordlist.json';
import BIP39_WORDS from '../../res/bip39_wordlist.json';
import TextInput from './TextInput';
@@ -132,7 +133,8 @@ export default class AccountSeed extends Component {
return (
{prefix}
- {result}
+ {address}
);
}
diff --git a/src/components/Background.js b/src/components/Background.js
index 24659109a4..98bda8ce63 100644
--- a/src/components/Background.js
+++ b/src/components/Background.js
@@ -14,6 +14,8 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see .
+'use strict';
+
import React from 'react';
import { StyleSheet, View } from 'react-native';
import colors from '../colors';
diff --git a/src/components/Button.js b/src/components/Button.js
index 0bdf1ded47..e4cd43bbd3 100644
--- a/src/components/Button.js
+++ b/src/components/Button.js
@@ -16,6 +16,8 @@
// @flow
+'use strict';
+
import PropTypes from 'prop-types';
import React from 'react';
import {
@@ -28,7 +30,7 @@ import {
ViewPropTypes
} from 'react-native';
import colors from '../colors';
-import fonts from '../fonts';
+import fontStyles from '../fontStyles';
export default class Button extends React.PureComponent<{
title: string,
@@ -40,6 +42,8 @@ export default class Button extends React.PureComponent<{
static propTypes = {
disabled: PropTypes.bool,
onPress: PropTypes.func.isRequired,
+ onlyText: PropTypes.bool,
+ small: PropTypes.bool,
style: ViewPropTypes.style,
textStyles: Text.propTypes.style,
title: PropTypes.string.isRequired
@@ -50,14 +54,25 @@ export default class Button extends React.PureComponent<{
onPress,
title,
disabled,
+ small,
textStyles,
+ onlyText,
buttonStyles,
- testID
+ testID,
+ style
} = this.props;
- const finalTextStyles = [styles.text, textStyles];
+ const finalTextStyles = [textStyles];
const finalButtonStyles = [styles.button, buttonStyles];
+ if (small) {
+ finalTextStyles.push(fontStyles.t_important);
+ finalButtonStyles.push(styles.buttonSmall);
+ }
+ if (onlyText) {
+ finalTextStyles.push({ color: colors.bg_button });
+ finalButtonStyles.push(styles.buttonOnlyText);
+ }
if (disabled) {
finalTextStyles.push(styles.textDisabled);
finalButtonStyles.push(styles.buttonDisabled);
@@ -72,8 +87,8 @@ export default class Button extends React.PureComponent<{
onPress={onPress}
testID={testID}
>
-
-
+
+
{title}
@@ -86,21 +101,28 @@ const styles = StyleSheet.create({
button: {
alignItems: 'center',
backgroundColor: colors.bg_button,
+ borderRadius: 60,
elevation: 4,
- height: 60,
- justifyContent: 'center'
+ height: 56,
+ justifyContent: 'center',
+ marginHorizontal: 8,
+ marginVertical: 8,
+ paddingHorizontal: 64
},
buttonDisabled: {
- backgroundColor: '#dfdfdf',
+ backgroundColor: colors.card_bgSolid,
elevation: 0
},
- text: {
- color: 'white',
- fontFamily: fonts.bold,
- fontSize: 20,
- padding: 8
+ buttonOnlyText: {
+ backgroundColor: colors.bg,
+ elevation: 0,
+ paddingHorizontal: 0
+ },
+ buttonSmall: {
+ height: 48,
+ paddingHorizontal: 48
},
textDisabled: {
- color: '#a1a1a1'
+ color: colors.card_bg
}
});
diff --git a/src/components/ButtonIcon.js b/src/components/ButtonIcon.js
new file mode 100644
index 0000000000..b001eed1c7
--- /dev/null
+++ b/src/components/ButtonIcon.js
@@ -0,0 +1,145 @@
+// Copyright 2015-2019 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+// @flow
+
+'use strict';
+
+import React, { useState } from 'react';
+import {
+ Platform,
+ TouchableNativeFeedback,
+ TouchableOpacity,
+ View,
+ Text
+} from 'react-native';
+import { Icon } from 'react-native-elements';
+import AntIcon from 'react-native-vector-icons/AntDesign';
+import colors from '../colors';
+
+const ButtonIcon = props => {
+ const {
+ dropdown = false,
+ renderDropdownElement,
+ iconName,
+ iconType,
+ iconColor,
+ onPress,
+ iconBgStyle,
+ iconSize,
+ testID,
+ textStyle,
+ title,
+ style
+ } = props;
+
+ const size = iconSize || 28;
+
+ const styles = {
+ dropdownView: {
+ marginRight: 8,
+ marginTop: size / -12,
+ marginVertical: 8
+ },
+ generalView: {
+ display: 'flex',
+ flexDirection: 'row',
+ paddingVertical: 8
+ },
+ iconTitleView: {
+ alignItems: 'center',
+ display: 'flex',
+ flexDirection: 'row',
+ marginLeft: 8
+ },
+ iconTitleViewContainer: {
+ flex: dropdown && title ? 1 : 0
+ },
+ iconView: {
+ backgroundColor: colors.card_bg,
+ borderRadius: size,
+ height: size,
+ paddingLeft: 3,
+ paddingTop: size / 8,
+ width: size
+ },
+ title: {
+ marginLeft: 8
+ }
+ };
+
+ const Touchable =
+ Platform.OS === 'android' ? TouchableNativeFeedback : TouchableOpacity;
+
+ const renderIcon = () => {
+ if (iconType === 'antdesign') {
+ return (
+
+ );
+ }
+ return (
+
+ );
+ };
+ const [isDropdownOpen, setIsDropsdownOpen] = useState(false);
+
+ return (
+ <>
+
+
+
+ {renderIcon()}
+ {!!title && {title}}
+
+
+
+
+ {dropdown && (
+
+ setIsDropsdownOpen(!isDropdownOpen)}>
+
+
+
+
+
+ )}
+
+ {isDropdownOpen && renderDropdownElement()}
+ >
+ );
+};
+
+export default ButtonIcon;
diff --git a/src/components/ButtonMainAction.js b/src/components/ButtonMainAction.js
new file mode 100644
index 0000000000..883362e31c
--- /dev/null
+++ b/src/components/ButtonMainAction.js
@@ -0,0 +1,74 @@
+// Copyright 2015-2019 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+// @flow
+
+'use strict';
+
+import PropTypes from 'prop-types';
+import React from 'react';
+import Button from './Button';
+import { View } from 'react-native';
+
+export default class ButtonMainAction extends React.PureComponent {
+ static propTypes = {
+ bottom: PropTypes.bool,
+ disabled: PropTypes.bool,
+ onPress: PropTypes.func,
+ testID: PropTypes.string,
+ title: PropTypes.string
+ };
+ render() {
+ const { onPress, title, testID, bottom, disabled, style } = this.props;
+ const finalViewStyles = [styles.body];
+ const finalButtonStyles = [styles.button];
+
+ if (bottom === false) {
+ finalViewStyles.push(styles.p_relative);
+ finalButtonStyles.push(styles.p_relative);
+ }
+
+ return (
+
+
+
+ );
+ }
+}
+
+const styles = {
+ body: {
+ bottom: 0,
+ height: 120,
+ position: 'absolute',
+ width: '100%'
+ },
+ button: {
+ alignSelf: 'center',
+ elevation: 2,
+ position: 'absolute'
+ },
+ p_relative: {
+ bottom: 0,
+ marginTop: 32,
+ position: 'relative'
+ }
+};
diff --git a/src/components/ButtonNewDerivation.js b/src/components/ButtonNewDerivation.js
new file mode 100644
index 0000000000..6e23f79bfa
--- /dev/null
+++ b/src/components/ButtonNewDerivation.js
@@ -0,0 +1,63 @@
+// Copyright 2015-2019 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+// @flow
+
+'use strict';
+
+import PropTypes from 'prop-types';
+import React from 'react';
+import Button from './Button';
+
+import { StyleSheet } from 'react-native';
+import colors from '../colors';
+import fontStyles from '../fontStyles';
+
+export default class ButtonNewDerivation extends React.PureComponent {
+ static propTypes = {
+ onPress: PropTypes.func
+ };
+ render() {
+ const { onPress, title, testID } = this.props;
+ return (
+
+ );
+ }
+}
+
+const styles = StyleSheet.create({
+ button: {
+ alignItems: 'center',
+ backgroundColor: 'transparent',
+ borderColor: colors.card_bgSolid,
+ borderRadius: 3,
+ borderWidth: 1,
+ elevation: 0,
+ height: 56,
+ marginBottom: 125,
+ marginTop: 24
+ },
+ text: {
+ letterSpacing: -0.4,
+ opacity: 0.4
+ }
+});
diff --git a/src/components/Card.js b/src/components/Card.js
deleted file mode 100644
index e6dd53d5ae..0000000000
--- a/src/components/Card.js
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright 2015-2019 Parity Technologies (UK) Ltd.
-// This file is part of Parity.
-
-// Parity is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Parity is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Parity. If not, see .
-
-// @flow
-
-import PropTypes from 'prop-types';
-import React from 'react';
-import {
- Image,
- Platform,
- StyleSheet,
- Text,
- TouchableNativeFeedback,
- TouchableOpacity,
- View,
- ViewPropTypes
-} from 'react-native';
-import colors from '../colors';
-
-export default class Card extends React.PureComponent<{
- title: string,
- secondaryText?: ?string,
- labelText?: ?string,
- footerStyle?: ?StyleSheet.Styles,
- style: ?StyleSheet.Styles,
- onPress: () => any
-}> {
- static propTypes = {
- footerStyle: ViewPropTypes.style,
- labelText: PropTypes.string,
- onPress: PropTypes.func,
- secondaryText: PropTypes.string,
- style: ViewPropTypes.style,
- title: PropTypes.string.isRequired
- };
-
- render() {
- const {
- title,
- secondaryText,
- labelText,
- footerStyle,
- style,
- onPress
- } = this.props;
-
- const finalBodyStyle = [style.body, footerStyle];
- const finalContentStyle = [style.content];
- const finalFooterStyle = [styles.footer, footerStyle];
- const finalTitleTextStyle = [styles.titleText];
- const finalSecondaryTextStyle = [styles.secondaryText];
- const finalFooterTextStyle = [styles.footerText];
-
- const Touchable =
- Platform.OS === 'android' ? TouchableNativeFeedback : TouchableOpacity;
- return (
-
-
-
-
-
- {title}
- {secondaryText}
-
-
-
- {labelText}
-
-
-
- );
- }
-}
-
-const styles = StyleSheet.create({
- body: {},
- content: {
- backgroundColor: colors.card_bg,
- padding: 10
- },
- footer: {},
- footerText: {},
- image: {
- height: 80,
- width: 80
- },
- secondaryText: {},
- titleText: {}
-});
diff --git a/src/components/CompatibleCard.js b/src/components/CompatibleCard.js
new file mode 100644
index 0000000000..2b353491c5
--- /dev/null
+++ b/src/components/CompatibleCard.js
@@ -0,0 +1,45 @@
+// Copyright 2015-2019 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+'use strict';
+
+import PropTypes from 'prop-types';
+import AccountCard from './AccountCard';
+import PathCard from './PathCard';
+import React from 'react';
+
+const CompatibleCard = ({ account, accountsStore, titlePrefix }) =>
+ account.isLegacy === false ? (
+
+ ) : (
+
+ );
+
+CompatibleCard.propTypes = {
+ account: PropTypes.object.isRequired,
+ accountsStore: PropTypes.object.isRequired,
+ titlePrefix: PropTypes.string
+};
+
+export default CompatibleCard;
diff --git a/src/components/DerivationPathField.js b/src/components/DerivationPathField.js
index 8c322ebd3f..bb0d5279eb 100644
--- a/src/components/DerivationPathField.js
+++ b/src/components/DerivationPathField.js
@@ -22,10 +22,12 @@ import Icon from 'react-native-vector-icons/MaterialIcons';
import { parseDerivationPath } from '../util/suri';
import TextInput from './TextInput';
+import colors from '../colors';
+import fontStyles from '../fontStyles';
export default function DerivationPathField(props) {
const { onChange, styles } = props;
- const [showAdvancedField, setShowAdvancedField] = useState(false);
+ const [showAdvancedField, setShowAdvancedField] = useState(true);
const [isValidPath, setIsValidPath] = useState(true);
const toggleShowAdvancedField = () => {
@@ -34,10 +36,7 @@ export default function DerivationPathField(props) {
return (
<>
-
+
ADVANCED
@@ -71,7 +70,12 @@ export default function DerivationPathField(props) {
}
}}
placeholder="optional derivation path"
- style={isValidPath ? ownStyles.validInput : ownStyles.invalidInput}
+ style={[
+ fontStyles.h2,
+ ownStyles.input,
+ ownStyles.test,
+ isValidPath ? {} : ownStyles.invalidInput
+ ]}
/>
)}
>
@@ -84,9 +88,6 @@ const ownStyles = StyleSheet.create({
paddingTop: 20
},
invalidInput: {
- backgroundColor: '#fee3e3'
- },
- validInput: {
- backgroundColor: '#e4fee4'
+ borderBottomColor: colors.bg_alert
}
});
diff --git a/src/components/HeaderLeftHome.js b/src/components/HeaderLeftHome.js
index bc255002d2..ba67b82e90 100644
--- a/src/components/HeaderLeftHome.js
+++ b/src/components/HeaderLeftHome.js
@@ -1,44 +1,62 @@
+// Copyright 2015-2019 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+'use strict';
+
import React from 'react';
import { Image, StyleSheet, Text, View } from 'react-native';
import colors from '../colors';
import fonts from '../fonts';
+import iconLogo from '../../res/img/icon.png';
export default class HeaderLeftHome extends React.PureComponent {
render() {
return (
this.props.onPress && this.props.onPress()}
+ style={[
+ {
+ alignItems: 'center',
+ flexDirection: 'row',
+ marginTop: -10,
+ paddingLeft: 12
+ },
+ this.props.style
+ ]}
>
-
- parity
+
+ parity
+ signer
);
}
}
const styles = StyleSheet.create({
- headerStyle: {
- alignItems: 'center',
- backgroundColor: colors.bg,
- borderBottomColor: colors.bg_text_sec,
- borderBottomWidth: 0.5,
- flexDirection: 'row',
- height: 60
- },
headerTextLeft: {
color: colors.bg_text,
- flex: 1,
- fontFamily: fonts.regular,
- fontSize: 25,
- paddingLeft: 4
+ fontFamily: fonts.light,
+ fontSize: 14,
+ marginRight: 2,
+ marginTop: 15
},
logo: {
- height: 42,
- width: 42
+ height: 24,
+ width: 24
+ },
+ t_bold: {
+ fontFamily: fonts.semiBold
}
});
diff --git a/src/components/IdentitiesSwitch.js b/src/components/IdentitiesSwitch.js
new file mode 100644
index 0000000000..3d8ea6fcd7
--- /dev/null
+++ b/src/components/IdentitiesSwitch.js
@@ -0,0 +1,314 @@
+// Copyright 2015-2019 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+'use strict';
+
+import React, { useState } from 'react';
+import { FlatList, Modal, View, TouchableWithoutFeedback } from 'react-native';
+import { withNavigation, ScrollView } from 'react-navigation';
+
+import ButtonIcon from './ButtonIcon';
+import colors from '../colors';
+import fontStyles from '../fontStyles';
+import Separator from './Separator';
+import { withAccountStore } from '../util/HOC';
+import { getIdentityName } from '../util/identitiesUtils';
+import testIDs from '../../e2e/testIDs';
+
+function IdentitiesSwitch({ navigation, accounts }) {
+ const defaultVisible = navigation.getParam('isSwitchOpen', false);
+ const [visible, setVisible] = useState(defaultVisible);
+ const { currentIdentity, identities } = accounts.state;
+
+ const closeModalAndNavigate = (screenName, params) => {
+ setVisible(false);
+ navigation.navigate(screenName, params);
+ };
+
+ const onIdentitySelectedAndNavigate = async (
+ identity,
+ screenName,
+ params
+ ) => {
+ await accounts.selectIdentity(identity);
+ closeModalAndNavigate(screenName, params);
+ };
+
+ const renderIdentityOptions = identity => {
+ return (
+ <>
+
+ onIdentitySelectedAndNavigate(identity, 'IdentityManagement')
+ }
+ iconBgStyle={styles.i_arrowBg}
+ iconType="antdesign"
+ iconName="arrowright"
+ iconSize={18}
+ testID={testIDs.IdentitiesSwitch.manageIdentityButton}
+ textStyle={fontStyles.t_regular}
+ style={styles.i_arrowStyle}
+ />
+
+ onIdentitySelectedAndNavigate(identity, 'IdentityBackup', {
+ isNew: false
+ })
+ }
+ iconBgStyle={styles.i_arrowBg}
+ iconType="antdesign"
+ iconName="arrowright"
+ iconSize={18}
+ textStyle={fontStyles.t_regular}
+ style={styles.i_arrowStyle}
+ />
+ >
+ );
+ };
+
+ const renderCurrentIdentityCard = () => {
+ if (!currentIdentity) return;
+
+ const currentIdentityTitle = getIdentityName(currentIdentity, identities);
+
+ return (
+ <>
+
+ onIdentitySelectedAndNavigate(
+ currentIdentity,
+ 'AccountNetworkChooser'
+ )
+ }
+ iconType="antdesign"
+ iconName="user"
+ iconSize={40}
+ style={{ paddingLeft: 16 }}
+ textStyle={fontStyles.h1}
+ />
+ {renderIdentityOptions(currentIdentity)}
+
+ >
+ );
+ };
+
+ const renderSettings = () => {
+ return (
+ <>
+ closeModalAndNavigate('About')}
+ iconType="antdesign"
+ iconName="info"
+ iconSize={24}
+ textStyle={fontStyles.t_big}
+ style={styles.indentedButton}
+ />
+
+ closeModalAndNavigate('TermsAndConditions', {
+ disableButtons: true
+ })
+ }
+ iconBgStyle={styles.i_arrowBg}
+ iconType="antdesign"
+ iconName="arrowright"
+ iconSize={18}
+ textStyle={fontStyles.t_regular}
+ style={styles.i_arrowStyle}
+ />
+ closeModalAndNavigate('PrivacyPolicy')}
+ iconBgStyle={styles.i_arrowBg}
+ iconType="antdesign"
+ iconName="arrowright"
+ iconSize={18}
+ textStyle={fontStyles.t_regular}
+ style={styles.i_arrowStyle}
+ />
+ >
+ );
+ };
+
+ const renderNonSelectedIdentity = ({ item, index }) => {
+ const identity = item;
+ const title = identity.name || `identity_${index.toString()}`;
+
+ return (
+ renderIdentityOptions(identity)}
+ title={title}
+ onPress={() =>
+ onIdentitySelectedAndNavigate(identity, 'AccountNetworkChooser')
+ }
+ iconType="antdesign"
+ iconName="user"
+ iconSize={24}
+ style={styles.indentedButton}
+ textStyle={fontStyles.h2}
+ />
+ );
+ };
+
+ const renderIdentities = () => {
+ // if no identity or the only one we have is the selected one
+
+ if (!identities.length || (identities.length === 1 && currentIdentity))
+ return;
+
+ const identitiesToShow = currentIdentity
+ ? identities.filter(
+ identity => identity.encryptedSeed !== currentIdentity.encryptedSeed
+ )
+ : identities;
+
+ return (
+ <>
+
+ item.encryptedSeed}
+ style={{ paddingVertical: identities.length > 5 ? 8 : 0 }}
+ />
+
+ {identities.length > 5 && (
+
+ )}
+ >
+ );
+ };
+
+ return (
+
+ setVisible(!visible)}
+ iconName="user"
+ iconType="antdesign"
+ iconBgStyle={{ backgroundColor: 'transparent' }}
+ testID={testIDs.IdentitiesSwitch.toggleButton}
+ />
+
+ setVisible(false)}
+ >
+ setVisible(false)}
+ >
+ setVisible(false)}
+ >
+
+ {renderCurrentIdentityCard()}
+ {renderIdentities()}
+ {accounts.getAccounts().size > 0 && (
+ <>
+ closeModalAndNavigate('LegacyAccountList')}
+ iconName="solution1"
+ iconType="antdesign"
+ iconSize={24}
+ textStyle={fontStyles.t_big}
+ style={styles.indentedButton}
+ />
+
+ >
+ )}
+
+ closeModalAndNavigate('IdentityNew')}
+ iconName="plus"
+ iconType="antdesign"
+ iconSize={24}
+ textStyle={fontStyles.t_big}
+ style={styles.indentedButton}
+ />
+
+
+ {__DEV__ && (
+
+ closeModalAndNavigate('AccountNew')}
+ iconName="plus"
+ iconType="antdesign"
+ iconSize={24}
+ textStyle={fontStyles.t_big}
+ style={styles.indentedButton}
+ />
+
+
+ )}
+
+ {renderSettings()}
+
+
+
+
+
+ );
+}
+
+const styles = {
+ card: {
+ backgroundColor: colors.bg,
+ borderRadius: 5,
+ paddingBottom: 16,
+ paddingTop: 8
+ },
+ container: {
+ backgroundColor: 'rgba(0,0,0,0.8)',
+ flex: 1,
+ justifyContent: 'center',
+ marginTop: -24,
+ paddingLeft: 16,
+ paddingRight: 16
+ },
+ i_arrowBg: {
+ backgroundColor: 'rgba(0,0,0,0)',
+ marginRight: -3
+ },
+ i_arrowStyle: {
+ opacity: 0.7,
+ paddingBottom: 6,
+ paddingLeft: 64,
+ paddingTop: 0
+ },
+ indentedButton: {
+ paddingLeft: 32
+ }
+};
+
+export default withAccountStore(withNavigation(IdentitiesSwitch));
diff --git a/src/components/KeyboardScrollView.js b/src/components/KeyboardScrollView.js
index 0fa3cc774e..f257a42b8d 100644
--- a/src/components/KeyboardScrollView.js
+++ b/src/components/KeyboardScrollView.js
@@ -1,8 +1,26 @@
+// Copyright 2015-2019 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+'use strict';
+
import React from 'react';
import { Keyboard, Platform } from 'react-native';
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
-class KeyboardScrollView extends React.Component {
+class KeyboardScrollView extends React.PureComponent {
render() {
const defaultProps = { enableAutomaticScroll: true };
return Platform.select({
diff --git a/src/components/Markdown.js b/src/components/Markdown.js
index d93e87cb1e..7fb38f2c1f 100644
--- a/src/components/Markdown.js
+++ b/src/components/Markdown.js
@@ -16,6 +16,8 @@
// @flow
+'use strict';
+
import React from 'react';
import { StyleSheet } from 'react-native';
import { default as MarkdownRender } from 'react-native-markdown-renderer';
@@ -28,7 +30,7 @@ export default class Markdown extends React.PureComponent {
.
+
+'use strict';
+
+import React from 'react';
+import { StyleSheet, Text, View } from 'react-native';
+
+import fontStyles from '../fontStyles';
+import { hexToAscii, isAscii } from '../util/strings';
+import colors from '../colors';
+
+export default function MessageDetailsCard({ isHash, message, data, style }) {
+ return (
+
+ {isHash ? 'Hash' : 'Message'}
+ {isHash ? (
+ {message}
+ ) : (
+
+ {isAscii(message) ? hexToAscii(message) : data}
+
+ )}
+
+ );
+}
+
+const styles = StyleSheet.create({
+ hashText: {
+ ...fontStyles.t_codeS,
+ backgroundColor: colors.label_text,
+ color: colors.bg,
+ marginBottom: 20,
+ paddingHorizontal: 8
+ },
+ messageContainer: {
+ marginTop: 16
+ },
+ messageText: {
+ ...fontStyles.t_code,
+ color: colors.label_text,
+ lineHeight: 26,
+ marginBottom: 20,
+ minHeight: 120,
+ padding: 10
+ }
+});
diff --git a/src/components/PathCard.js b/src/components/PathCard.js
new file mode 100644
index 0000000000..5d12ebbc3d
--- /dev/null
+++ b/src/components/PathCard.js
@@ -0,0 +1,172 @@
+// Copyright 2015-2019 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+'use strict';
+
+import React from 'react';
+import PropTypes from 'prop-types';
+import { StyleSheet, Text, View } from 'react-native';
+import AntIcon from 'react-native-vector-icons/AntDesign';
+import {
+ getAccountIdWithPath,
+ getAddressFromAccountId,
+ getNetworkKeyByPath,
+ getPathName
+} from '../util/identitiesUtils';
+import { NETWORK_LIST } from '../constants';
+import Separator from '../components/Separator';
+import AccountIcon from './AccountIcon';
+import Address from './Address';
+import colors from '../colors';
+import fontStyles from '../fontStyles';
+import TouchableItem from './TouchableItem';
+import { AccountPrefixedTitle } from './AccountPrefixedTitle';
+
+PathCard.propTypes = {
+ identity: PropTypes.object.isRequired,
+ name: PropTypes.string,
+ onPress: PropTypes.func,
+ path: PropTypes.string.isRequired,
+ testID: PropTypes.string,
+ titlePrefix: PropTypes.string
+};
+
+export default function PathCard({
+ onPress,
+ identity,
+ path,
+ name,
+ testID,
+ titlePrefix
+}) {
+ const isNotEmptyName = name && name !== '';
+ const pathName = isNotEmptyName ? name : getPathName(path, identity);
+ const accountId = getAccountIdWithPath(path, identity);
+
+ const networkKey = getNetworkKeyByPath(path);
+ const network = NETWORK_LIST[networkKey];
+ const extractAddress = getAddressFromAccountId(accountId, network.protocol);
+
+ const nonSubstrateCard = (
+
+
+
+
+
+
+
+ {network.title}
+
+
+
+
+
+
+
+
+ );
+ const substrateDerivationCard = (
+
+
+
+
+
+
+
+
+ {path}
+
+ {extractAddress !== '' && (
+
+ {extractAddress}
+
+ )}
+
+
+
+
+ );
+
+ return network.protocol === 'substrate'
+ ? substrateDerivationCard
+ : nonSubstrateCard;
+}
+
+const styles = StyleSheet.create({
+ body: {
+ flexDirection: 'column',
+ marginBottom: 10
+ },
+ content: {
+ alignItems: 'center',
+ flexDirection: 'row',
+ paddingLeft: 16
+ },
+ contentDer: {
+ backgroundColor: colors.card_bg,
+ paddingVertical: 8
+ },
+ desc: {
+ flex: 1,
+ flexDirection: 'column',
+ justifyContent: 'space-between',
+ paddingLeft: 16
+ },
+ footer: {
+ alignSelf: 'stretch',
+ backgroundColor: '#977CF6',
+ height: 100,
+ marginLeft: 8,
+ width: 8
+ },
+ icon: {
+ height: 40,
+ width: 40
+ }
+});
diff --git a/src/components/PayloadDetailsCard.js b/src/components/PayloadDetailsCard.js
index aa27f34c17..979f7f42ae 100644
--- a/src/components/PayloadDetailsCard.js
+++ b/src/components/PayloadDetailsCard.js
@@ -16,6 +16,8 @@
// @flow
+'use strict';
+
import { GenericCall, getTypeRegistry, Metadata } from '@polkadot/types';
import Call from '@polkadot/types/primitive/Generic/Call';
import { formatBalance } from '@polkadot/util';
@@ -23,18 +25,19 @@ import { decodeAddress, encodeAddress } from '@polkadot/util-crypto';
import PropTypes from 'prop-types';
import React, { useEffect, useState } from 'react';
-import { Alert, StyleSheet, Text, View, ViewPropTypes } from 'react-native';
+import { StyleSheet, Text, View, ViewPropTypes } from 'react-native';
-import fonts from '../fonts';
import colors from '../colors';
import { SUBSTRATE_NETWORK_LIST, SubstrateNetworkKeys } from '../constants';
import kusamaMetadata from '../util/static-kusama';
import substrateDevMetadata from '../util/static-substrate';
import { shortString } from '../util/strings';
+import fontStyles from '../fontStyles';
+import { alertDecodeError } from '../util/alertUtils';
export default class PayloadDetailsCard extends React.PureComponent {
static propTypes = {
- description: PropTypes.string.isRequired,
+ description: PropTypes.string,
payload: PropTypes.object,
prefix: PropTypes.number.isRequired,
signature: PropTypes.string,
@@ -94,18 +97,18 @@ export default class PayloadDetailsCard extends React.PureComponent {
return (
- {description}
+ {!!description && {description}}
{!!payload && (
-
+
)}
{!!signature && (
-
+
Signature
{signature}
@@ -157,12 +158,12 @@ function ExtrinsicPart({ label, fallback, prefix, value }) {
let methodArgs = {};
// todo: clean this up
- function formatArgs(callInstance, methodArgs, depth) {
+ function formatArgs(callInstance, callMethodArgs, depth) {
const { args, meta, methodName, sectionName } = callInstance;
let paramArgKvArray = [];
if (!meta.args.length) {
const sectionMethod = `${sectionName}.${methodName}`;
- methodArgs[sectionMethod] = null;
+ callMethodArgs[sectionMethod] = null;
return;
}
@@ -183,7 +184,7 @@ function ExtrinsicPart({ label, fallback, prefix, value }) {
prefix
);
} else if (args[i] instanceof Call) {
- argument = formatArgs(args[i], methodArgs, depth++); // go deeper into the nested calls
+ argument = formatArgs(args[i], callMethodArgs, depth++); // go deeper into the nested calls
} else if (
args[i].toRawType() === 'Vec' ||
args[i].toRawType() === 'Vec'
@@ -202,23 +203,14 @@ function ExtrinsicPart({ label, fallback, prefix, value }) {
const param = meta.args[i].name.toString();
const sectionMethod = `${sectionName}.${methodName}`;
paramArgKvArray.push([param, argument]);
- methodArgs[sectionMethod] = paramArgKvArray;
+ callMethodArgs[sectionMethod] = paramArgKvArray;
}
}
formatArgs(call, methodArgs, 0);
setFormattedCallArgs(methodArgs);
} catch (e) {
- Alert.alert(
- 'Could not decode method with available metadata.',
- 'Signing something you do not understand is inherently unsafe. Do not sign this extrinsic unless you know what you are doing, or update Parity Signer to be able to decode this message. If you are not sure, or you are using the latest version, please open an issue on github.com/paritytech/parity-signer.',
- [
- {
- style: 'default',
- text: 'Okay'
- }
- ]
- );
+ alertDecodeError();
setUseFallBack(true);
}
}
@@ -238,13 +230,9 @@ function ExtrinsicPart({ label, fallback, prefix, value }) {
const renderEraDetails = () => {
if (period && phase) {
return (
-
-
- phase:
- {phase}
- period:
- {period}
-
+
+ phase: {phase}
+ period: {period}
);
} else {
@@ -253,8 +241,7 @@ function ExtrinsicPart({ label, fallback, prefix, value }) {
style={{
display: 'flex',
flexDirection: 'row',
- flexWrap: 'wrap',
- padding: 5
+ flexWrap: 'wrap'
}}
>
Immortal Era
@@ -286,20 +273,22 @@ function ExtrinsicPart({ label, fallback, prefix, value }) {
return (
-
+
Call {sectionMethod} with
the following arguments:
{paramArgs ? (
paramArgs.map(([param, arg]) => (
- {param}:
-
+
+ {' { '}
+ {param}:{' '}
{arg && arg.length > 50
? shortString(arg)
: arg instanceof Array
? arg.join(', ')
- : arg}
+ : arg}{' '}
+ {'}'}
))
@@ -324,9 +313,7 @@ function ExtrinsicPart({ label, fallback, prefix, value }) {
return (
-
+
{label}
{label === 'Method' && !useFallback ? (
renderMethodDetails()
@@ -346,55 +333,38 @@ function ExtrinsicPart({ label, fallback, prefix, value }) {
const styles = StyleSheet.create({
body: {
- backgroundColor: colors.card_bg,
- flexDirection: 'column',
- padding: 20,
- paddingTop: 10
+ marginTop: 8
},
callDetails: {
- alignItems: 'flex-start',
- display: 'flex',
- flexDirection: 'column',
- justifyContent: 'flex-start',
- paddingLeft: 5,
- width: '100%'
+ marginBottom: 4
},
era: {
- alignItems: 'flex-end',
- display: 'flex',
- flexDirection: 'row',
- justifyContent: 'space-around'
+ flexDirection: 'row'
},
- icon: {
- height: 47,
- width: 47
+ extrinsicContainer: {
+ paddingTop: 16
},
label: {
- backgroundColor: colors.bg,
- color: colors.card_bg,
- fontFamily: fonts.bold,
- fontSize: 20,
+ ...fontStyles.t_label,
+ backgroundColor: colors.label_text,
+ marginBottom: 10,
+ paddingLeft: 8,
textAlign: 'left'
},
secondaryText: {
- color: colors.card_text,
- fontFamily: fonts.semiBold,
- fontSize: 14,
- paddingLeft: 8,
+ ...fontStyles.t_codeS,
+ color: colors.label_text,
+ paddingHorizontal: 8,
textAlign: 'left'
},
subLabel: {
- backgroundColor: null,
- color: colors.card_text,
- fontFamily: fonts.bold,
- fontSize: 14,
- paddingLeft: 5,
+ ...fontStyles.t_codeS,
+ color: colors.label_text,
+ paddingLeft: 8,
textAlign: 'left'
},
titleText: {
- color: colors.card_text,
- fontFamily: fonts.bold,
- fontSize: 14,
- textAlign: 'center'
+ ...fontStyles.t_codeS,
+ color: colors.label_text_sec
}
});
diff --git a/src/components/PopupMenu.js b/src/components/PopupMenu.js
index 91ab8137c8..e78f3876c7 100644
--- a/src/components/PopupMenu.js
+++ b/src/components/PopupMenu.js
@@ -30,9 +30,14 @@ import fonts from '../fonts';
export default class PopupMenu extends React.PureComponent {
render() {
- const { onSelect, menuTriggerIconName, menuItems } = this.props;
+ const { onSelect, menuTriggerIconName, menuItems, testID } = this.props;
const menuTriggerIcon = (
-
+
);
return (
@@ -41,7 +46,10 @@ export default class PopupMenu extends React.PureComponent {
{menuItems.map((menuItem, index) => (
-
+
{menuItem.text}
diff --git a/src/components/QrView.js b/src/components/QrView.js
index a3d3908d82..77dd965651 100644
--- a/src/components/QrView.js
+++ b/src/components/QrView.js
@@ -43,17 +43,24 @@ export default function QrView(props) {
}
displayQrCode(props.data);
- }, [props]);
+ }, [props.data]);
const { width: deviceWidth } = Dimensions.get('window');
- let size = props.size || deviceWidth - 80;
- let flexBasis = props.height || deviceWidth - 40;
+ let size = props.size || deviceWidth - 64;
+ let flexBasis = props.height || deviceWidth - 32;
return (
diff --git a/src/components/ScreenHeading.js b/src/components/ScreenHeading.js
new file mode 100644
index 0000000000..5becaed0c1
--- /dev/null
+++ b/src/components/ScreenHeading.js
@@ -0,0 +1,149 @@
+// Copyright 2015-2019 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+// @flow
+
+'use strict';
+
+import PropTypes from 'prop-types';
+import React from 'react';
+import { View, Text } from 'react-native';
+import AntIcon from 'react-native-vector-icons/AntDesign';
+import fontStyles from '../fontStyles';
+import fonts from '../fonts';
+import ButtonIcon from './ButtonIcon';
+import { Icon } from 'react-native-elements';
+import colors from '../colors';
+
+export default class ScreenHeading extends React.PureComponent {
+ static propTypes = {
+ big: PropTypes.bool,
+ onPress: PropTypes.func,
+ small: PropTypes.bool,
+ subtitle: PropTypes.string,
+ title: PropTypes.string
+ };
+ render() {
+ const {
+ big,
+ title,
+ small,
+ subtitle,
+ subtitleL,
+ subtitleIcon,
+ error,
+ onPress,
+ iconName,
+ iconType
+ } = this.props;
+ const finalViewStyles = [styles.body];
+ const finalTextStyles = [fontStyles.h1, styles.t_center];
+ const finalSubtitleStyle = [fontStyles.t_codeS];
+ const finalSubtitleIconStyle = [styles.subtitleIcon];
+
+ if (big) {
+ finalViewStyles.push(styles.bodyL);
+ finalTextStyles.push(styles.t_left);
+ finalSubtitleIconStyle.push({ justifyContent: 'flex-start' });
+ } else if (small) {
+ finalViewStyles.push(styles.bodyL);
+ finalTextStyles.push([fontStyles.h2, styles.t_left, styles.t_normal]);
+ }
+
+ if (error) {
+ finalSubtitleStyle.push(styles.t_error);
+ }
+ if (subtitleL) {
+ finalSubtitleStyle.push({ textAlign: 'left' });
+ }
+
+ const renderSubtitle = () => {
+ if (!subtitle) return;
+ return (
+
+ {renderSubtitleIcon()}
+ {subtitle}
+
+ );
+ };
+ const renderSubtitleIcon = () => {
+ if (!subtitleIcon) return;
+ return ;
+ };
+
+ const renderBack = () => {
+ if (!onPress) return;
+ return (
+
+ );
+ };
+ const renderIcon = () => {
+ if (!iconName) return;
+ return (
+
+
+
+ );
+ };
+
+ return (
+
+ {title}
+ {renderSubtitle()}
+ {renderBack()}
+ {renderIcon()}
+
+ );
+ }
+}
+
+const styles = {
+ body: {
+ marginBottom: 16,
+ paddingHorizontal: 16
+ },
+ bodyL: {
+ paddingLeft: 72,
+ paddingRight: 16
+ },
+ icon: {
+ marginLeft: 5,
+ position: 'absolute'
+ },
+ subtitleIcon: {
+ alignItems: 'center',
+ flexDirection: 'row',
+ justifyContent: 'center'
+ },
+ t_center: {
+ textAlign: 'center'
+ },
+ t_error: {
+ color: colors.bg_alert
+ },
+ t_left: {
+ textAlign: 'left'
+ },
+ t_normal: {
+ fontFamily: fonts.roboto
+ }
+};
diff --git a/src/components/SecurityHeader.js b/src/components/SecurityHeader.js
index c48cfcf274..649f5f1181 100644
--- a/src/components/SecurityHeader.js
+++ b/src/components/SecurityHeader.js
@@ -14,64 +14,48 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see .
+'use strict';
+
import NetInfo from '@react-native-community/netinfo';
-import React from 'react';
-import { StyleSheet, Text, View } from 'react-native';
-import Icon from 'react-native-vector-icons/MaterialIcons';
+import React, { useEffect, useState } from 'react';
+import { View } from 'react-native';
import { withNavigation } from 'react-navigation';
import colors from '../colors';
-import fonts from '../fonts';
-import TouchableItem from './TouchableItem';
-
-class SecurityHeader extends React.Component {
- state = {
- isConnected: false
- };
-
- componentDidMount() {
- this.unsubscribe = NetInfo.addEventListener(state => {
- this.setState({ isConnected: state.isConnected });
- });
- }
-
- componentWillUnmount() {
- this.unsubscribe();
- }
-
- render() {
- const { isConnected } = this.state;
-
- if (!isConnected) {
- return null;
- }
-
- return (
- this.props.navigation.navigate('Security')}>
-
-
- Not Secure
-
-
- );
- }
+import IdentitiesSwitch from '../components/IdentitiesSwitch';
+import ButtonIcon from './ButtonIcon';
+
+function SecurityHeader({ navigation }) {
+ const [isConnected, setIsConnected] = useState(false);
+
+ useEffect(
+ () =>
+ NetInfo.addEventListener(state => {
+ setIsConnected(state.isConnected);
+ }),
+ []
+ );
+
+ return (
+
+ {isConnected && (
+ navigation.navigate('Security')}
+ iconName="shield-off"
+ iconType="feather"
+ iconColor={colors.bg_alert}
+ iconBgStyle={{ backgroundColor: 'transparent', marginTop: -3 }}
+ />
+ )}
+
+
+ );
}
-const styles = StyleSheet.create({
- headerSecureIcon: {
- color: colors.bg_alert,
- fontFamily: fonts.bold,
- fontSize: 20,
- marginLeft: 0,
- paddingRight: 5
- },
- headerTextRight: {
- color: colors.bg_alert,
- fontFamily: fonts.bold,
- fontSize: 17,
- marginLeft: 0,
- paddingRight: 14
- }
-});
-
export default withNavigation(SecurityHeader);
diff --git a/src/components/Separator.js b/src/components/Separator.js
new file mode 100644
index 0000000000..e46c3ae015
--- /dev/null
+++ b/src/components/Separator.js
@@ -0,0 +1,63 @@
+// Copyright 2015-2019 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+'use strict';
+
+import PropTypes from 'prop-types';
+import React from 'react';
+import { View, ViewPropTypes, Image } from 'react-native';
+
+import shadowImage from '../../res/img/card_shadow.png';
+
+export default class Separator extends React.PureComponent {
+ static propTypes = {
+ shadow: PropTypes.bool,
+ shadowStyle: ViewPropTypes.style,
+ style: ViewPropTypes.style
+ };
+
+ render() {
+ const { shadow, shadowStyle, style } = this.props;
+
+ return (
+
+ {shadow && (
+
+ )}
+
+ );
+ }
+}
diff --git a/src/components/TextInput.js b/src/components/TextInput.js
index a6f7ba3afa..2847071919 100644
--- a/src/components/TextInput.js
+++ b/src/components/TextInput.js
@@ -14,15 +14,27 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see .
+'use strict';
+
+import PropTypes from 'prop-types';
import React from 'react';
-import { StyleSheet, TextInput as TextInputOrigin } from 'react-native';
+import {
+ StyleSheet,
+ TextInput as TextInputOrigin,
+ View,
+ Text
+} from 'react-native';
+import fontStyles from '../fontStyles';
import colors from '../colors';
-import fonts from '../fonts';
export default class TextInput extends React.PureComponent {
static defaultProps = {
focus: false
};
+ static propTypes = {
+ fixedPrefix: PropTypes.string,
+ style: PropTypes.oneOfType([PropTypes.object, PropTypes.array])
+ };
// Methods:
focus() {
@@ -34,30 +46,68 @@ export default class TextInput extends React.PureComponent {
focus && this.focus();
}
+ renderLabel() {
+ const { label } = this.props;
+ if (!label) return;
+ return (
+ {label}
+ );
+ }
+
render() {
+ const { fixedPrefix, style, error } = this.props;
+ const finalInputStyles = [styles.input];
+ if (error) {
+ finalInputStyles.push(styles.input_error);
+ }
+
return (
- {
- this.input = input;
- }}
- keyboardAppearance="dark"
- underlineColorAndroid="transparent"
- {...this.props}
- style={[styles.input, this.props.style]}
- />
+
+ {this.renderLabel()}
+
+ {fixedPrefix && (
+
+ {fixedPrefix}
+
+ )}
+ {
+ this.input = input;
+ }}
+ keyboardAppearance="dark"
+ underlineColorAndroid="transparent"
+ {...this.props}
+ style={[fontStyles.h2, finalInputStyles, style]}
+ placeholderTextColor={colors.card_bg_text_sec}
+ />
+
+
);
}
}
const styles = StyleSheet.create({
+ body: {
+ marginVertical: 8,
+ paddingHorizontal: 16
+ },
input: {
- alignItems: 'center',
- backgroundColor: colors.card_bg,
- elevation: 4,
- fontFamily: fonts.regular,
- fontSize: 24,
- height: 60,
- justifyContent: 'center',
- paddingHorizontal: 18
+ borderBottomColor: colors.card_bg_text_sec,
+ borderBottomWidth: 0.8,
+ flex: 1,
+ height: 40,
+ padding: 0,
+ paddingTop: 8
+ },
+ inputFixed: {
+ color: '#888',
+ flex: 0,
+ paddingTop: 11.5
+ },
+ input_error: {
+ borderBottomColor: colors.bg_alert
+ },
+ viewStyle: {
+ flexDirection: 'row'
}
});
diff --git a/src/components/TouchableItem.js b/src/components/TouchableItem.js
index 37d4efc303..f71d8f9f8f 100644
--- a/src/components/TouchableItem.js
+++ b/src/components/TouchableItem.js
@@ -1,3 +1,19 @@
+// Copyright 2015-2019 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
/**
* TouchableItem renders a touchable that looks native on both iOS and Android.
*
@@ -7,6 +23,9 @@
* On iOS you can pass the props of TouchableOpacity, on Android pass the props
* of TouchableNativeFeedback.
*/
+
+'use strict';
+
import React from 'react';
import {
Platform,
@@ -17,7 +36,7 @@ import {
const ANDROID_VERSION_LOLLIPOP = 21;
-export default class TouchableItem extends React.Component {
+export default class TouchableItem extends React.PureComponent {
static defaultProps = {
borderless: false,
pressColor: 'rgba(0, 0, 0, .32)'
diff --git a/src/components/TxDetailsCard.js b/src/components/TxDetailsCard.js
index 8e9afb5c8e..25f7b253d4 100644
--- a/src/components/TxDetailsCard.js
+++ b/src/components/TxDetailsCard.js
@@ -16,6 +16,8 @@
// @flow
+'use strict';
+
import PropTypes from 'prop-types';
import React from 'react';
import { StyleSheet, Text, View, ViewPropTypes } from 'react-native';
@@ -67,14 +69,14 @@ function Amount({ style, value, gas, gasPrice }) {
- {value}
+ {value}
ETH
.
+
+'use strict';
+
import colors from './colors';
export const NetworkProtocols = Object.freeze({
@@ -21,6 +39,7 @@ export const EthereumNetworkKeys = Object.freeze({
KOVAN: '42',
CLASSIC: '61'
});
+
/* eslint-enable sort-keys */
// genesisHash is used as Network key for Substrate networks
@@ -36,32 +55,36 @@ const unknownNetworkBase = {
[UnknownNetworkKeys.UNKNOWN]: {
color: colors.bg_alert,
protocol: NetworkProtocols.UNKNOWN,
- secondaryColor: colors.card_bg,
+ secondaryColor: colors.card_bgSolid,
title: 'Unknown network'
}
};
const substrateNetworkBase = {
[SubstrateNetworkKeys.KUSAMA]: {
- color: '#4C4646',
+ color: '#e6007a',
decimals: 12,
genesisHash: SubstrateNetworkKeys.KUSAMA,
+ logo: require('../res/img/logos/kusama.png'),
+ pathId: 'kusama_CC2',
prefix: 2,
title: 'Kusama CC2',
unit: 'KSM'
},
[SubstrateNetworkKeys.KUSAMA_DEV]: {
- color: '#4C4646',
+ color: '#A60037',
decimals: 12,
genesisHash: SubstrateNetworkKeys.KUSAMA_DEV,
+ pathId: 'kusama_dev',
prefix: 2,
title: 'Kusama Development',
unit: 'KSM'
},
[SubstrateNetworkKeys.SUBSTRATE_DEV]: {
color: '#ff8c00',
- decimals: 0,
+ decimals: 12,
genesisHash: SubstrateNetworkKeys.SUBSTRATE_DEV,
+ pathId: 'substrate_dev',
prefix: 42,
title: 'Substrate Development',
unit: 'UNIT'
@@ -78,15 +101,16 @@ const substrateNetworkBase = {
const ethereumNetworkBase = {
[EthereumNetworkKeys.FRONTIER]: {
- color: '#977CF6',
+ color: '#64A2F4',
ethereumChainId: EthereumNetworkKeys.FRONTIER,
- secondaryColor: colors.card_bg,
+ secondaryColor: colors.card_bgSolid,
title: 'Ethereum'
},
[EthereumNetworkKeys.CLASSIC]: {
- color: '#8C7166',
+ color: '#319C7C',
ethereumChainId: EthereumNetworkKeys.CLASSIC,
- secondaryColor: colors.card_bg,
+ logo: require('../res/img/logos/eth-classic.png'),
+ secondaryColor: colors.card_bgSolid,
title: 'Ethereum Classic'
},
[EthereumNetworkKeys.ROPSTEN]: {
@@ -104,15 +128,17 @@ const ethereumNetworkBase = {
};
const ethereumDefaultValues = {
- color: '#F2E265',
+ color: '#2968C7',
+ logo: require('../res/img/logos/eth.png'),
protocol: NetworkProtocols.ETHEREUM,
secondaryColor: colors.card_text
};
const substrateDefaultValues = {
color: '#4C4646',
+ logo: require('../res/img/logos/substrate-dev.png'),
protocol: NetworkProtocols.SUBSTRATE,
- secondaryColor: colors.card_bg
+ secondaryColor: colors.card_bgSolid
};
function setDefault(networkBase, defaultProps) {
@@ -143,5 +169,3 @@ export const NETWORK_LIST = Object.freeze(
UNKNOWN_NETWORK
)
);
-
-export const TX_DETAILS_MSG = 'After signing and publishing you will have sent';
diff --git a/src/fontStyles.js b/src/fontStyles.js
new file mode 100644
index 0000000000..e253299dc0
--- /dev/null
+++ b/src/fontStyles.js
@@ -0,0 +1,86 @@
+// Copyright 2015-2019 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+'use strict';
+
+import fonts from './fonts';
+import colors from './colors';
+export default {
+ h1: {
+ color: colors.bg_text,
+ fontFamily: fonts.robotoBold,
+ fontSize: 22
+ },
+ h2: {
+ color: colors.bg_text,
+ fontFamily: fonts.robotoMedium,
+ fontSize: 18
+ },
+ quote: {
+ color: colors.bg_text,
+ fontFamily: fonts.robotoLight,
+ fontSize: 28
+ },
+ t_big: {
+ color: colors.bg_text,
+ fontFamily: fonts.roboto,
+ fontSize: 16
+ },
+ t_code: {
+ color: colors.bg_text_sec,
+ fontFamily: fonts.robotoMono,
+ fontSize: 18
+ },
+ t_codeS: {
+ color: colors.bg_text_sec,
+ fontFamily: fonts.robotoMono,
+ fontSize: 11
+ },
+ t_important: {
+ color: colors.bg_text,
+ fontFamily: fonts.robotoBold,
+ fontSize: 13
+ },
+ t_label: {
+ backgroundColor: colors.label_text,
+ color: colors.card_text,
+ fontFamily: fonts.robotoMedium,
+ fontSize: 13
+ },
+ t_prefix: {
+ color: colors.bg_text,
+ fontFamily: fonts.roboto,
+ fontSize: 14,
+ textTransform: 'uppercase'
+ },
+ t_regular: {
+ color: colors.bg_text,
+ fontFamily: fonts.robotoRegular,
+ fontSize: 12
+ },
+ t_seed: {
+ borderColor: colors.card_bg,
+ borderWidth: 0.8,
+ color: colors.bg_button,
+ fontFamily: fonts.light,
+ fontSize: 20,
+ letterSpacing: 0.1,
+ lineHeight: 26,
+ minHeight: 140,
+ paddingHorizontal: 16,
+ paddingVertical: 10
+ }
+};
diff --git a/src/fonts.js b/src/fonts.js
index bb974b3c58..92944998e6 100644
--- a/src/fonts.js
+++ b/src/fonts.js
@@ -14,8 +14,16 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see .
+'use strict';
+
export default {
bold: 'ManifoldCF-Bold',
+ light: 'ManifoldCF-Light',
regular: 'Manifold CF',
+ roboto: 'Roboto-regular',
+ robotoBold: 'Roboto-Bold',
+ robotoLight: 'Roboto-Light',
+ robotoMedium: 'Roboto-Medium',
+ robotoMono: 'RobotoMono-Regular',
semiBold: 'ManifoldCF-DemiBold'
};
diff --git a/src/screens/About.js b/src/screens/About.js
index 0ec27ffa07..5c1dc937f7 100644
--- a/src/screens/About.js
+++ b/src/screens/About.js
@@ -23,11 +23,6 @@ import fonts from '../fonts';
import packageJson from '../../package.json';
export default class About extends React.PureComponent {
- static navigationOptions = {
- headerBackTitle: 'Back',
- title: 'About'
- };
-
render() {
return (
@@ -104,7 +99,7 @@ const styles = StyleSheet.create({
paddingBottom: 15
},
text: {
- color: colors.card_bg,
+ color: colors.bg_text_sec,
fontFamily: fonts.regular,
fontSize: 14,
marginBottom: 20
@@ -115,13 +110,6 @@ const styles = StyleSheet.create({
fontSize: 18,
paddingBottom: 20
},
- titleTop: {
- color: colors.bg_text_sec,
- fontFamily: fonts.bold,
- fontSize: 24,
- paddingBottom: 20,
- textAlign: 'center'
- },
top: {
flex: 1
}
diff --git a/src/screens/AccountBackup.js b/src/screens/AccountBackup.js
deleted file mode 100644
index 5c4f349942..0000000000
--- a/src/screens/AccountBackup.js
+++ /dev/null
@@ -1,243 +0,0 @@
-// Copyright 2015-2019 Parity Technologies (UK) Ltd.
-// This file is part of Parity.
-
-// Parity is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Parity is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Parity. If not, see .
-
-'use strict';
-
-import React from 'react';
-import {
- Alert,
- AppState,
- Clipboard,
- ScrollView,
- StyleSheet,
- Text,
- View
-} from 'react-native';
-import { Subscribe } from 'unstated';
-
-import colors from '../colors';
-import fonts from '../fonts';
-import AccountCard from '../components/AccountCard';
-import Background from '../components/Background';
-import Button from '../components/Button';
-import TouchableItem from '../components/TouchableItem';
-import DerivationPasswordVerify from '../components/DerivationPasswordVerify';
-import AccountsStore from '../stores/AccountsStore';
-import { NetworkProtocols, NETWORK_LIST } from '../constants';
-
-export default class AccountBackup extends React.PureComponent {
- static navigationOptions = {
- title: 'Account Backup'
- };
- render() {
- return (
-
- {accounts => }
-
- );
- }
-}
-
-class AccountBackupView extends React.PureComponent {
- constructor(...args) {
- super(...args);
- this.handleAppStateChange = this.handleAppStateChange.bind(this);
- }
-
- componentDidMount() {
- AppState.addEventListener('change', this.handleAppStateChange);
- }
-
- handleAppStateChange = nextAppState => {
- if (nextAppState === 'inactive') {
- this.props.navigation.goBack();
- }
- };
-
- componentWillUnmount() {
- const { accounts } = this.props;
- const selectedKey = accounts.getSelectedKey();
-
- if (selectedKey) {
- accounts.lockAccount(selectedKey);
- }
-
- AppState.removeEventListener('change', this._handleAppStateChange);
- }
-
- render() {
- const { accounts, navigation } = this.props;
- const { navigate } = navigation;
- const isNew = navigation.getParam('isNew');
- const {
- address,
- derivationPassword,
- derivationPath,
- name,
- networkKey,
- seed,
- seedPhrase
- } = isNew ? accounts.getNew() : accounts.getSelected();
- const protocol =
- (NETWORK_LIST[networkKey] && NETWORK_LIST[networkKey].protocol) || '';
-
- return (
-
-
- BACKUP ACCOUNT
-
-
- RECOVERY PHRASE
-
- Write these words down on paper. Keep the backup paper safe. These
- words allow anyone to recover this account and access its funds.
-
-
- {
- // only allow the copy of the recovery phrase in dev environment
- if (__DEV__) {
- Alert.alert(
- 'Write this recovery phrase on paper',
- `It is not recommended to transfer or store a recovery phrase digitally and unencrypted. Anyone in possession of this recovery phrase is able to spend funds from this account.
- `,
- [
- {
- onPress: () => {
- if (protocol === NetworkProtocols.SUBSTRATE) {
- Clipboard.setString(`${seedPhrase}${derivationPath}`);
- } else {
- Clipboard.setString(seed);
- }
- },
- style: 'default',
- text: 'Copy anyway'
- },
- {
- style: 'cancel',
- text: 'Cancel'
- }
- ]
- );
- }
- }}
- >
- {seedPhrase || seed}
-
- {!!derivationPath && (
- {derivationPath}
- )}
- {!!derivationPassword && (
-
- )}
- {isNew && (
-
- );
- }
-}
-
-const styles = StyleSheet.create({
- body: {
- backgroundColor: colors.bg,
- flex: 1,
- flexDirection: 'column',
- padding: 20
- },
- bodyContainer: {
- flex: 1,
- flexDirection: 'column',
- justifyContent: 'space-between'
- },
- bodyContent: {
- paddingBottom: 40
- },
- bottom: {
- flexBasis: 50,
- paddingBottom: 15
- },
- deleteButton: {
- backgroundColor: colors.bg_alert
- },
- derivationText: {
- backgroundColor: colors.card_bg,
- fontFamily: fonts.regular,
- fontSize: 20,
- lineHeight: 26,
- marginTop: 20,
- minHeight: 30,
- padding: 10
- },
- hintText: {
- color: colors.bg_text_sec,
- fontFamily: fonts.bold,
- fontSize: 12,
- paddingBottom: 20,
- textAlign: 'center'
- },
- nextStep: {
- marginTop: 20
- },
- seedText: {
- backgroundColor: colors.card_bg,
- fontFamily: fonts.regular,
- fontSize: 20,
- lineHeight: 26,
- minHeight: 160,
- padding: 10
- },
- title: {
- color: colors.bg_text_sec,
- fontFamily: fonts.bold,
- fontSize: 18,
- paddingBottom: 20
- },
- titleTop: {
- color: colors.bg_text_sec,
- fontFamily: fonts.bold,
- fontSize: 24,
- paddingBottom: 20,
- textAlign: 'center'
- },
- top: {
- flex: 1
- }
-});
diff --git a/src/screens/AccountDetails.js b/src/screens/AccountDetails.js
index c7aa27366e..59cbe96878 100644
--- a/src/screens/AccountDetails.js
+++ b/src/screens/AccountDetails.js
@@ -17,10 +17,8 @@
'use strict';
import React from 'react';
-import { Alert, ScrollView, StyleSheet, Text, View } from 'react-native';
-import { NavigationActions, StackActions } from 'react-navigation';
+import { ScrollView, StyleSheet, Text, View } from 'react-native';
import { Subscribe } from 'unstated';
-
import colors from '../colors';
import fonts from '../fonts';
import AccountCard from '../components/AccountCard';
@@ -29,12 +27,14 @@ import AccountsStore from '../stores/AccountsStore';
import TxStore from '../stores/TxStore';
import PopupMenu from '../components/PopupMenu';
import { NETWORK_LIST, NetworkProtocols } from '../constants';
-
-export default class AccountDetails extends React.Component {
- static navigationOptions = {
- title: 'Account Details'
- };
-
+import { alertDeleteAccount } from '../util/alertUtils';
+import {
+ navigateToLandingPage,
+ navigateToLegacyAccountList
+} from '../util/navigationHelpers';
+import fontStyles from '../fontStyles';
+
+export default class AccountDetails extends React.PureComponent {
render() {
return (
@@ -51,43 +51,25 @@ export default class AccountDetails extends React.Component {
}
}
-class AccountDetailsView extends React.Component {
+class AccountDetailsView extends React.PureComponent {
constructor(props) {
super(props);
}
onDelete = () => {
- const accounts = this.props.accounts;
+ const { accounts, navigation } = this.props;
const selected = accounts.getSelected();
const selectedKey = accounts.getSelectedKey();
- Alert.alert(
- 'Delete Account',
- `Do you really want to delete ${selected.name ||
- selected.address ||
- 'this account'}?
-This account can only be recovered with its associated recovery phrase.`,
- [
- {
- onPress: () => {
- accounts.deleteAccount(selectedKey);
- const resetAction = StackActions.reset({
- actions: [
- NavigationActions.navigate({ routeName: 'AccountList' })
- ],
- index: 0, // FIXME workaround for now, use SwitchNavigator later: https://github.com/react-navigation/react-navigation/issues/1127#issuecomment-295841343
- key: undefined
- });
- this.props.navigation.dispatch(resetAction);
- },
- style: 'destructive',
- text: 'Delete'
- },
- {
- style: 'cancel',
- text: 'Cancel'
+ alertDeleteAccount(
+ selected.name || selected.address || 'this account',
+ async () => {
+ await accounts.deleteAccount(selectedKey);
+ if (accounts.getAccounts().size === 0) {
+ return navigateToLandingPage(navigation);
}
- ]
+ navigateToLegacyAccountList(navigation);
+ }
);
};
@@ -137,35 +119,37 @@ This account can only be recovered with its associated recovery phrase.`,
NetworkProtocols.UNKNOWN;
return (
-
-
- ACCOUNT
-
-
+
+
+
+ Public Address
+
+
+
-
+
{protocol !== NetworkProtocols.UNKNOWN ? (
) : (
@@ -179,31 +163,25 @@ This account can only be recovered with its associated recovery phrase.`,
const styles = StyleSheet.create({
body: {
+ alignContent: 'flex-start',
backgroundColor: colors.bg,
flex: 1,
- flexDirection: 'column',
- padding: 20
- },
- bodyContent: {
- paddingBottom: 40
+ paddingBottom: 40,
+ paddingTop: 8
},
deleteText: {
color: colors.bg_alert
},
header: {
- alignItems: 'center',
flexDirection: 'row',
- justifyContent: 'center',
- paddingBottom: 20
+ paddingBottom: 24,
+ paddingLeft: 72,
+ paddingRight: 19
},
menuView: {
alignItems: 'flex-end',
flex: 1
},
- qr: {
- backgroundColor: colors.card_bg,
- marginTop: 20
- },
title: {
color: colors.bg_text_sec,
flexDirection: 'column',
diff --git a/src/screens/AccountEdit.js b/src/screens/AccountEdit.js
index 60d706d0a0..766fce680c 100644
--- a/src/screens/AccountEdit.js
+++ b/src/screens/AccountEdit.js
@@ -17,19 +17,19 @@
'use strict';
import React from 'react';
-import { ScrollView, StyleSheet, Text } from 'react-native';
+import { ScrollView, StyleSheet } from 'react-native';
import { Subscribe } from 'unstated';
import colors from '../colors';
-import fonts from '../fonts';
import AccountCard from '../components/AccountCard';
import TextInput from '../components/TextInput';
import AccountsStore from '../stores/AccountsStore';
-export default class AccountEdit extends React.PureComponent {
- static navigationOptions = {
- title: 'Edit Account'
- };
+const onNameInput = async (accounts, name) => {
+ await accounts.updateSelectedAccount({ name });
+ await accounts.save(accounts.getSelectedKey(), accounts.getSelected());
+};
+export default class AccountEdit extends React.PureComponent {
constructor(props) {
super(props);
}
@@ -45,26 +45,16 @@ export default class AccountEdit extends React.PureComponent {
}
return (
-
- EDIT ACCOUNT
+
- ACCOUNT NAME
{
- accounts.updateSelectedAccount({ name });
- await accounts.save(
- accounts.getSelectedKey(),
- accounts.getSelected()
- );
- }}
+ onChangeText={name => onNameInput(accounts, name)}
value={selected.name}
placeholder="New name"
/>
@@ -78,43 +68,9 @@ export default class AccountEdit extends React.PureComponent {
const styles = StyleSheet.create({
body: {
+ alignContent: 'flex-start',
backgroundColor: colors.bg,
flex: 1,
- flexDirection: 'column',
- overflow: 'hidden'
- },
- bodyContainer: {
- flex: 1,
- padding: 20
- },
- bottom: {
- flexBasis: 50,
- paddingBottom: 15
- },
- deleteButton: {
- backgroundColor: 'red'
- },
- hintText: {
- color: colors.bg_text_sec,
- fontFamily: fonts.bold,
- fontSize: 12,
- paddingTop: 20,
- textAlign: 'center'
- },
- title: {
- color: colors.bg_text_sec,
- fontFamily: fonts.bold,
- fontSize: 18,
- paddingBottom: 20
- },
- titleTop: {
- color: colors.bg_text_sec,
- fontFamily: fonts.bold,
- fontSize: 24,
- paddingBottom: 20,
- textAlign: 'center'
- },
- top: {
- flex: 1
+ paddingBottom: 40
}
});
diff --git a/src/screens/AccountList.js b/src/screens/AccountList.js
deleted file mode 100644
index d6fb2035a4..0000000000
--- a/src/screens/AccountList.js
+++ /dev/null
@@ -1,190 +0,0 @@
-// Copyright 2015-2019 Parity Technologies (UK) Ltd.
-// This file is part of Parity.
-
-// Parity is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Parity is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Parity. If not, see .
-
-'use strict';
-
-import PropTypes from 'prop-types';
-import React from 'react';
-import { FlatList, StyleSheet, Text, View } from 'react-native';
-import { Subscribe } from 'unstated';
-
-import colors from '../colors';
-import AccountCard from '../components/AccountCard';
-import Background from '../components/Background';
-import Button from '../components/Button';
-import PopupMenu from '../components/PopupMenu';
-import fonts from '../fonts';
-import AccountsStore from '../stores/AccountsStore';
-import testIDs from '../../e2e/testIDs';
-
-export default class AccountList extends React.PureComponent {
- static navigationOptions = {
- title: 'Accounts'
- };
-
- render() {
- return (
-
- {accounts => {
- return (
- {
- accounts.select(key);
- this.props.navigation.navigate('AccountDetails');
- }}
- />
- );
- }}
-
- );
- }
-}
-
-class AccountListView extends React.PureComponent {
- static propTypes = {
- accounts: PropTypes.object.isRequired,
- onAccountSelected: PropTypes.func.isRequired
- };
-
- constructor(props) {
- super(props);
- }
-
- showOnboardingMessage = () => {
- const { navigate } = this.props.navigation;
- const createLink = (text, navigation) => (
- navigate(navigation)}>
- {text}
-
- );
-
- return (
-
-
- No account yet?{'\n'}
- {createLink('Create', 'AccountNew')} or{' '}
- {createLink('recover', 'AccountRecover')} an account to get started.
-
-
- );
- };
-
- render() {
- const { accounts, navigation, onAccountSelected } = this.props;
- const hasNoAccount = accounts.length < 1;
- const { navigate } = navigation;
-
- return (
-
-
-
- ACCOUNTS
-
- navigate(value)}
- menuTriggerIconName={'add'}
- menuItems={[
- { text: 'New Account', value: 'AccountNew' },
- { text: 'Recover Account', value: 'AccountRecover' },
- { text: 'About', value: 'About' }
- ]}
- />
-
-
- {hasNoAccount && this.showOnboardingMessage()}
- {
- this.list = list;
- }}
- style={styles.content}
- data={[...accounts.entries()]}
- keyExtractor={([key]) => key}
- ItemSeparatorComponent={() => }
- renderItem={({ item: [accountKey, account] }) => {
- return (
- {
- onAccountSelected(accountKey);
- }}
- style={{ paddingBottom: null }}
- title={account.name}
- />
- );
- }}
- enableEmptySections
- />
- {!hasNoAccount && (
-
-
- )}
-
- );
- }
-}
-
-const styles = StyleSheet.create({
- body: {
- backgroundColor: colors.bg,
- flex: 1,
- flexDirection: 'column',
- padding: 20
- },
- bottom: {
- marginTop: 20
- },
- content: {
- flex: 1
- },
- header: {
- alignItems: 'center',
- flexDirection: 'row',
- justifyContent: 'center',
- paddingBottom: 20
- },
- link: {
- textDecorationLine: 'underline'
- },
- menuView: {
- alignItems: 'flex-end',
- flex: 1
- },
- onboardingText: {
- color: colors.bg_text_sec,
- fontFamily: fonts.regular,
- fontSize: 20
- },
- onboardingWrapper: {
- alignItems: 'flex-end',
- flex: 1,
- flexDirection: 'row'
- },
- title: {
- color: colors.bg_text_sec,
- flexDirection: 'column',
- fontFamily: fonts.bold,
- fontSize: 18,
- justifyContent: 'center'
- }
-});
diff --git a/src/screens/AccountNetworkChooser.js b/src/screens/AccountNetworkChooser.js
index 13272904f8..8bc06571e5 100644
--- a/src/screens/AccountNetworkChooser.js
+++ b/src/screens/AccountNetworkChooser.js
@@ -16,118 +16,241 @@
'use strict';
-import React from 'react';
-import { ScrollView, StyleSheet, Text } from 'react-native';
-import { Subscribe } from 'unstated';
+import React, { useState } from 'react';
+import { ScrollView, StyleSheet, Text, View } from 'react-native';
+import { withNavigation } from 'react-navigation';
+
import colors from '../colors';
-import fonts from '../fonts';
-import TouchableItem from '../components/TouchableItem';
+import AccountCard from '../components/AccountCard';
+import Button from '../components/Button';
import {
NETWORK_LIST,
UnknownNetworkKeys,
- SubstrateNetworkKeys
+ SubstrateNetworkKeys,
+ NetworkProtocols
} from '../constants';
-import AccountsStore from '../stores/AccountsStore';
-import { empty } from '../util/account';
+import { navigateToPathsList, unlockSeed } from '../util/navigationHelpers';
+import { withAccountStore } from '../util/HOC';
+import { alertPathDerivationError } from '../util/alertUtils';
+import {
+ getAvailableNetworkKeys,
+ getPathsWithSubstrateNetwork
+} from '../util/identitiesUtils';
+import testIDs from '../../e2e/testIDs';
+import ButtonMainAction from '../components/ButtonMainAction';
+import ScreenHeading from '../components/ScreenHeading';
+import Separator from '../components/Separator';
+import fontStyles from '../fontStyles';
-export default class AccountNetworkChooser extends React.PureComponent {
- static navigationOptions = {
- headerBackTitle: 'Back',
- title: 'Choose a network'
- };
- render() {
+function AccountNetworkChooser({ navigation, accounts }) {
+ const isNew = navigation.getParam('isNew', false);
+ const [shouldShowMoreNetworks, setShouldShowMoreNetworks] = useState(false);
+ const excludedNetworks = [UnknownNetworkKeys.UNKNOWN];
+ if (!__DEV__) {
+ excludedNetworks.push(SubstrateNetworkKeys.SUBSTRATE_DEV);
+ excludedNetworks.push(SubstrateNetworkKeys.KUSAMA_DEV);
+ }
+ const { identities, currentIdentity, loaded } = accounts.state;
+ const hasLegacyAccount = accounts.getAccounts().size !== 0;
+
+ const TextButton = ({ text, isRecover }) => (
+ navigation.navigate('IdentityNew', { isRecover })}
+ >
+ {text}
+
+ );
+
+ const showOnboardingMessage = () => {
return (
-
- {accounts => (
-
- )}
-
+
+
+
+ or
+
+ your identity to get started.
+ {hasLegacyAccount && (
+
+
);
- }
-}
+ };
+
+ const sortNetworkKeys = ([, networkParams]) =>
+ networkParams.protocol !== NetworkProtocols.SUBSTRATE ? 1 : -1;
+
+ const getNetworkKeys = ([networkKey]) => {
+ const availableNetworks = getAvailableNetworkKeys(
+ currentIdentity || identities[0]
+ );
+ if (excludedNetworks.includes(networkKey)) return false;
+ if (isNew) return true;
+ if (shouldShowMoreNetworks) {
+ return !availableNetworks.includes(networkKey);
+ }
+ return availableNetworks.includes(networkKey);
+ };
+
+ const onDerivationFinished = (derivationSucceed, networkKey) => {
+ if (derivationSucceed) {
+ navigateToPathsList(navigation, networkKey);
+ } else {
+ alertPathDerivationError();
+ }
+ };
+
+ const deriveSubstrateDefault = async (networkKey, networkParams) => {
+ const { prefix, pathId } = networkParams;
+ const seed = await unlockSeed(navigation);
+ const derivationSucceed = await accounts.deriveNewPath(
+ `//${pathId}//default`,
+ seed,
+ prefix,
+ networkKey,
+ 'Default'
+ );
+ onDerivationFinished(derivationSucceed, networkKey);
+ };
+
+ const deriveEthereumAccount = async networkKey => {
+ const seed = await unlockSeed(navigation);
+ const derivationSucceed = await accounts.deriveEthereumAccount(
+ seed,
+ networkKey
+ );
+ onDerivationFinished(derivationSucceed, networkKey);
+ };
-class AccountNetworkChooserView extends React.PureComponent {
- render() {
- const { navigation } = this.props;
- const { accounts } = this.props;
- const excludedNetworks = [UnknownNetworkKeys.UNKNOWN];
+ const renderShowMoreButton = () => {
+ if (isNew) return;
+ if (!shouldShowMoreNetworks) {
+ return (
+ <>
+ setShouldShowMoreNetworks(true)}
+ title="Add Network Account"
+ networkColor={colors.bg}
+ />
+
+ >
+ );
+ }
+ };
- if (!__DEV__) {
- excludedNetworks.push(SubstrateNetworkKeys.SUBSTRATE_DEV);
- excludedNetworks.push(SubstrateNetworkKeys.KUSAMA_DEV);
+ const renderScreenHeading = () => {
+ if (isNew) {
+ return ;
+ } else if (shouldShowMoreNetworks) {
+ return (
+ setShouldShowMoreNetworks(false)}
+ />
+ );
}
+ };
+ const renderScanButton = () => {
+ if (isNew) return;
+ else if (shouldShowMoreNetworks) return;
return (
-
- CHOOSE NETWORK
- {Object.entries(NETWORK_LIST)
- .filter(([networkKey]) => !excludedNetworks.includes(networkKey))
- .map(([networkKey, networkParams]) => (
- {
- accounts.updateNew(empty('', networkKey));
- navigation.goBack();
- }}
- >
-
- {networkParams.title}
-
-
- ))}
-
+ navigation.navigate('QrScanner')}
+ />
);
- }
+ };
+
+ const onNetworkChosen = async (networkKey, networkParams) => {
+ if (isNew) {
+ if (networkParams.protocol === NetworkProtocols.SUBSTRATE) {
+ await deriveSubstrateDefault(networkKey, networkParams);
+ } else {
+ await deriveEthereumAccount(networkKey);
+ }
+ } else {
+ const paths = Array.from(currentIdentity.meta.keys());
+ const listedPaths = getPathsWithSubstrateNetwork(paths, networkKey);
+ if (networkParams.protocol === NetworkProtocols.SUBSTRATE) {
+ if (listedPaths.length === 0)
+ return navigation.navigate('PathDerivation', {
+ networkKey
+ });
+ } else if (!paths.includes(networkKey)) {
+ return await deriveEthereumAccount(networkKey);
+ }
+ navigation.navigate('PathsList', { networkKey });
+ }
+ };
+
+ if (!loaded) return ;
+
+ if (identities.length === 0) return showOnboardingMessage();
+
+ const networkList = Object.entries(NETWORK_LIST).filter(getNetworkKeys);
+ networkList.sort(sortNetworkKeys);
+
+ return (
+
+ {renderScreenHeading()}
+
+ {networkList.map(([networkKey, networkParams], index) => (
+ onNetworkChosen(networkKey, networkParams)}
+ title={networkParams.title}
+ />
+ ))}
+ {renderShowMoreButton()}
+
+ {renderScanButton()}
+
+ );
}
+export default withAccountStore(withNavigation(AccountNetworkChooser));
+
const styles = StyleSheet.create({
body: {
backgroundColor: colors.bg,
flex: 1,
- flexDirection: 'column',
- overflow: 'hidden'
- },
- bottom: {
- flexBasis: 50,
- paddingBottom: 15
- },
- card: {
- backgroundColor: colors.card_bg,
- padding: 20
- },
- cardText: {
- color: colors.card_text,
- fontFamily: fonts.bold,
- fontSize: 20
- },
- title: {
- color: colors.bg_text_sec,
- fontFamily: fonts.bold,
- fontSize: 18,
- paddingBottom: 20
- },
- titleTop: {
- color: colors.bg_text_sec,
- fontFamily: fonts.bold,
- fontSize: 24,
- paddingBottom: 20,
- textAlign: 'center'
+ flexDirection: 'column'
},
- top: {
- flex: 1
+ onboardingWrapper: {
+ alignItems: 'center',
+ flexDirection: 'row',
+ flexWrap: 'wrap'
}
});
diff --git a/src/screens/AccountNew.js b/src/screens/AccountNew.js
index 1e6670f9a9..8597e36cbe 100644
--- a/src/screens/AccountNew.js
+++ b/src/screens/AccountNew.js
@@ -16,191 +16,168 @@
'use strict';
-import React from 'react';
+import React, { useEffect, useReducer } from 'react';
import { StyleSheet, Text, View } from 'react-native';
-import { Subscribe } from 'unstated';
-
+import { withNavigation } from 'react-navigation';
import colors from '../colors';
import AccountIconChooser from '../components/AccountIconChooser';
import Background from '../components/Background';
import Button from '../components/Button';
import DerivationPathField from '../components/DerivationPathField';
import KeyboardScrollView from '../components/KeyboardScrollView';
-import NetworkButton from '../components/NetworkButton';
import TextInput from '../components/TextInput';
import { NETWORK_LIST, NetworkProtocols } from '../constants';
import fonts from '../fonts';
-import AccountsStore from '../stores/AccountsStore';
-import { empty, validateSeed } from '../util/account';
+import { emptyAccount, validateSeed } from '../util/account';
import { constructSURI } from '../util/suri';
-
-export default class AccountNew extends React.Component {
- static navigationOptions = {
- headerBackTitle: 'Back',
- title: 'New Account'
+import AccountCard from '../components/AccountCard';
+import { withAccountStore } from '../util/HOC';
+
+function AccountNew({ accounts, navigation }) {
+ const initialState = {
+ derivationPassword: '',
+ derivationPath: '',
+ isDerivationPathValid: true,
+ selectedAccount: undefined,
+ selectedNetwork: undefined
};
- render() {
- return (
-
- {accounts => }
-
- );
- }
-}
-
-class AccountNewView extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- derivationPassword: '',
- derivationPath: '',
- isDerivationPathValid: true,
- selectedAccount: undefined,
- selectedNetwork: undefined
- };
- }
+ const reducer = (state, delta) => ({ ...state, ...delta });
+ const [state, updateState] = useReducer(reducer, initialState);
- componentWillUnmount = function() {
- // called when the user goes back, or finishes the whole process
- this.props.accounts.updateNew(empty());
- };
+ useEffect(() => {
+ accounts.updateNew(emptyAccount());
+ }, [accounts, accounts.updateNew]);
- static getDerivedStateFromProps(nextProps, prevState) {
- const selectedAccount = nextProps.accounts.getNew();
+ useEffect(() => {
+ const selectedAccount = accounts.state.newAccount;
const selectedNetwork = NETWORK_LIST[selectedAccount.networkKey];
-
- return {
- derivationPassword: prevState.derivationPassword,
- derivationPath: prevState.derivationPath,
- selectedAccount,
- selectedNetwork
- };
- }
-
- render() {
- const { accounts, navigation } = this.props;
- const {
- derivationPassword,
- derivationPath,
- isDerivationPathValid,
+ updateState({
selectedAccount,
selectedNetwork
- } = this.state;
- const { address, name, seed, validBip39Seed } = selectedAccount;
- const isSubstrate = selectedNetwork.protocol === NetworkProtocols.SUBSTRATE;
-
- if (!selectedAccount) {
- return null;
- }
-
- return (
+ });
+ }, [accounts.state.newAccount]);
+
+ const {
+ derivationPassword,
+ derivationPath,
+ isDerivationPathValid,
+ selectedAccount,
+ selectedNetwork
+ } = state;
+ if (!selectedAccount) return null;
+
+ const { address, name, seed, validBip39Seed } = selectedAccount;
+ const isSubstrate = selectedNetwork.protocol === NetworkProtocols.SUBSTRATE;
+
+ return (
+
+
+
+ CREATE ACCOUNT
+ NETWORK
+
+ navigation.navigate('LegacyNetworkChooser')}
+ />
-
-
-
- CREATE ACCOUNT
- NETWORK
-
- ICON & ADDRESS
- {
- if (newAddress && isBip39 && newSeed) {
- if (isSubstrate) {
- try {
- const suri = constructSURI({
- derivePath: derivationPath,
- password: derivationPassword,
- phrase: newSeed
- });
+ ICON & ADDRESS
+ {
+ if (newAddress && isBip39 && newSeed) {
+ if (isSubstrate) {
+ try {
+ const suri = constructSURI({
+ derivePath: derivationPath,
+ password: derivationPassword,
+ phrase: newSeed
+ });
- accounts.updateNew({
- address: newAddress,
- derivationPassword,
- derivationPath,
- seed: suri,
- seedPhrase: newSeed,
- validBip39Seed: isBip39
- });
- } catch (e) {
- console.error(e);
- }
- } else {
- // Ethereum account
- accounts.updateNew({
- address: newAddress,
- seed: newSeed,
- validBip39Seed: isBip39
- });
- }
- } else {
accounts.updateNew({
- address: '',
- seed: '',
- validBip39Seed: false
- });
- }
- }}
- network={selectedNetwork}
- value={address && address}
- />
- NAME
- accounts.updateNew({ name })}
- value={name}
- placeholder="Enter a new account name"
- />
- {isSubstrate && (
- {
- this.setState({
+ address: newAddress,
derivationPassword,
derivationPath,
- isDerivationPathValid
+ seed: suri,
+ seedPhrase: newSeed,
+ validBip39Seed: isBip39
});
- }}
- styles={styles}
- />
- )}
-
-
-
- Next, you will be asked to backup your account, get a pen and some
- paper.
-
-
-
+ } else {
+ accounts.updateNew({
+ address: '',
+ seed: '',
+ validBip39Seed: false
+ });
+ }
+ }}
+ network={selectedNetwork}
+ value={address && address}
+ />
+ NAME
+ accounts.updateNew({ name: input })}
+ value={name}
+ placeholder="Enter a new account name"
+ />
+ {isSubstrate && (
+ {
+ updateState({
+ derivationPassword: newDerivationPath.derivationPassword,
+ derivationPath: newDerivationPath.derivationPath,
+ isDerivationPathValid: newDerivationPath.isDerivationPathValid
+ });
+ }}
+ styles={styles}
+ />
+ )}
+
+
+ Next, you will be asked to backup your account, get a pen and some
+ paper.
+
+
- );
- }
+
+ );
}
+export default withAccountStore(withNavigation(AccountNew));
+
const styles = StyleSheet.create({
body: {
backgroundColor: colors.bg,
flex: 1,
- overflow: 'hidden'
+ overflow: 'hidden',
+ padding: 16
},
bodyContainer: {
flex: 1,
@@ -224,8 +201,7 @@ const styles = StyleSheet.create({
title: {
color: colors.bg_text_sec,
fontFamily: fonts.bold,
- fontSize: 18,
- paddingBottom: 20
+ fontSize: 18
},
titleTop: {
color: colors.bg_text_sec,
@@ -233,8 +209,5 @@ const styles = StyleSheet.create({
fontSize: 24,
paddingBottom: 20,
textAlign: 'center'
- },
- top: {
- flex: 1
}
});
diff --git a/src/screens/AccountPin.js b/src/screens/AccountPin.js
index f8b990c9d4..6230eb6c76 100644
--- a/src/screens/AccountPin.js
+++ b/src/screens/AccountPin.js
@@ -16,7 +16,7 @@
'use strict';
-import React, { Component } from 'react';
+import React, { PureComponent } from 'react';
import { StyleSheet, Text } from 'react-native';
import { NavigationActions, StackActions } from 'react-navigation';
import { Subscribe } from 'unstated';
@@ -27,6 +27,7 @@ import Button from '../components/Button';
import TextInput from '../components/TextInput';
import AccountsStore from '../stores/AccountsStore';
import KeyboardScrollView from '../components/KeyboardScrollView';
+import { navigateToLegacyAccountList } from '../util/navigationHelpers';
export default class AccountPin extends React.PureComponent {
render() {
@@ -54,40 +55,31 @@ class AccountPinView extends React.PureComponent {
async submit() {
const { accounts, navigation } = this.props;
+ const { pin, confirmation } = this.state;
const accountCreation = navigation.getParam('isNew');
- const { pin } = this.state;
const account = accountCreation
? accounts.getNew()
: accounts.getSelected();
- if (
- this.state.pin.length >= 6 &&
- this.state.pin === this.state.confirmation
- ) {
+ if (pin.length >= 6 && pin === confirmation) {
if (accountCreation) {
await accounts.submitNew(pin);
- const resetAction = StackActions.reset({
- actions: [NavigationActions.navigate({ routeName: 'AccountList' })],
- index: 0, // FIXME workaround for now, use SwitchNavigator later: https://github.com/react-navigation/react-navigation/issues/1127#issuecomment-295841343
- key: undefined
- });
- this.props.navigation.dispatch(resetAction);
+ return navigateToLegacyAccountList(navigation);
} else {
await accounts.save(accounts.getSelectedKey(), account, pin);
const resetAction = StackActions.reset({
actions: [
- NavigationActions.navigate({ routeName: 'AccountList' }),
+ NavigationActions.navigate({ routeName: 'LegacyAccountList' }),
NavigationActions.navigate({ routeName: 'AccountDetails' })
],
index: 1, // FIXME workaround for now, use SwitchNavigator later: https://github.com/react-navigation/react-navigation/issues/1127#issuecomment-295841343
key: undefined
});
- this.props.navigation.dispatch(resetAction);
+ navigation.dispatch(resetAction);
}
} else {
- if (this.state.pin.length < 6) {
+ if (pin.length < 6) {
this.setState({ pinTooShort: true });
- } else if (this.state.pin !== this.state.confirmation)
- this.setState({ pinMismatch: true });
+ } else if (pin !== confirmation) this.setState({ pinMismatch: true });
}
}
@@ -156,7 +148,7 @@ class AccountPinView extends React.PureComponent {
}
}
-class PinInput extends Component {
+class PinInput extends PureComponent {
render() {
return (
@@ -60,7 +53,7 @@ export default class AccountRecover extends React.Component {
}
}
-class AccountRecoverView extends React.Component {
+class AccountRecoverView extends React.PureComponent {
constructor(...args) {
super(...args);
@@ -162,7 +155,7 @@ class AccountRecoverView extends React.Component {
componentWillUnmount = function() {
// called when the user goes back, or finishes the whole recovery process
- this.props.accounts.updateNew(empty());
+ this.props.accounts.updateNew(emptyAccount());
};
componentDidUpdate(_, prevState) {
@@ -173,12 +166,26 @@ class AccountRecoverView extends React.Component {
}
}
- toggleAdvancedField = () => {
- this.setState({ showAdvancedField: !this.state.showAdvancedField });
+ onAccountRecover = () => {
+ const { navigation } = this.props;
+ const { seedPhrase, validBip39Seed } = this.state.selectedAccount;
+ const validation = validateSeed(seedPhrase, validBip39Seed);
+
+ if (!validation.valid) {
+ if (validation.accountRecoveryAllowed) {
+ return alertInvalidSeedRecovery(`${validation.reason}`, navigation);
+ } else {
+ return alertErrorWithMessage(`${validation.reason}`, 'Back');
+ }
+ }
+
+ navigation.navigate('AccountPin', {
+ isNew: true
+ });
};
render() {
- const { accounts, navigation } = this.props;
+ const { accounts } = this.props;
const {
derivationPassword,
derivationPath,
@@ -211,7 +218,7 @@ class AccountRecoverView extends React.Component {
ACCOUNT NAME
accounts.updateNew({ name })}
+ onChangeText={input => accounts.updateNew({ name: input })}
value={name}
placeholder="Enter an account name"
/>
@@ -224,37 +231,32 @@ class AccountRecoverView extends React.Component {
findNodeHandle(event.target)
);
}}
- ref={this._seed}
valid={
validateSeed(seedPhrase, validBip39Seed).valid ||
(isSubstrate && address)
}
- onChangeText={seedPhrase => {
+ onChangeText={newSeedPhrase => {
this.debouncedAddressGeneration(
- seedPhrase,
+ newSeedPhrase,
derivationPath,
derivationPassword
);
- this.setState({ seedPhrase });
+ this.setState({ seedPhrase: newSeedPhrase });
}}
value={this.state.seedPhrase}
/>
{isSubstrate && (
{
+ onChange={newDerivationPath => {
this.debouncedAddressGeneration(
seedPhrase,
- derivationPath,
- derivationPassword
+ newDerivationPath.derivationPath,
+ newDerivationPath.derivationPassword
);
this.setState({
- derivationPassword,
- derivationPath,
- isDerivationPathValid
+ derivationPassword: newDerivationPath.derivationPassword,
+ derivationPath: newDerivationPath.derivationPath,
+ isDerivationPathValid: newDerivationPath.isDerivationPathValid
});
}}
styles={styles}
@@ -262,7 +264,7 @@ class AccountRecoverView extends React.Component {
)}
{
- const validation = validateSeed(seedPhrase, validBip39Seed);
-
- if (!validation.valid) {
- if (validation.accountRecoveryAllowed) {
- return Alert.alert('Warning', `${validation.reason}`, [
- {
- onPress: () => {
- navigation.navigate('AccountPin', {
- isNew: true,
- isWelcome: navigation.getParam('isWelcome')
- });
- },
- style: 'default',
- text: 'I understand the risks'
- },
- {
- style: 'cancel',
- text: 'Back'
- }
- ]);
- } else {
- return Alert.alert('Error', `${validation.reason}`, [
- {
- style: 'cancel',
- text: 'Back'
- }
- ]);
- }
- }
-
- navigation.navigate('AccountPin', {
- isNew: true,
- isWelcome: navigation.getParam('isWelcome')
- });
- }}
+ onPress={() => this.onAccountRecover()}
/>
diff --git a/src/screens/AccountUnlock.js b/src/screens/AccountUnlock.js
index 9dfef70225..0b811d87ad 100644
--- a/src/screens/AccountUnlock.js
+++ b/src/screens/AccountUnlock.js
@@ -18,12 +18,13 @@
import PropTypes from 'prop-types';
import React from 'react';
-import { StyleSheet, Text, View } from 'react-native';
+import { StyleSheet, View } from 'react-native';
import { NavigationActions, StackActions } from 'react-navigation';
import { Subscribe } from 'unstated';
import colors from '../colors';
-import fonts from '../fonts';
+import fontStyles from '../fontStyles';
import Background from '../components/Background';
+import ScreenHeading from '../components/ScreenHeading';
import TextInput from '../components/TextInput';
import AccountsStore from '../stores/AccountsStore';
import ScannerStore from '../stores/ScannerStore';
@@ -41,7 +42,7 @@ export class AccountUnlockAndSign extends React.PureComponent {
accounts={accounts}
checkPin={async pin => {
try {
- await scannerStore.signData(pin);
+ await scannerStore.signDataLegacy(pin);
return true;
} catch (e) {
return false;
@@ -50,7 +51,9 @@ export class AccountUnlockAndSign extends React.PureComponent {
navigate={() => {
const resetAction = StackActions.reset({
actions: [
- NavigationActions.navigate({ routeName: 'AccountList' }),
+ NavigationActions.navigate({
+ routeName: 'LegacyAccountList'
+ }),
NavigationActions.navigate({ routeName: next })
],
index: 1, // FIXME workaround for now, use SwitchNavigator later: https://github.com/react-navigation/react-navigation/issues/1127#issuecomment-295841343
@@ -65,10 +68,10 @@ export class AccountUnlockAndSign extends React.PureComponent {
}
}
-export class AccountUnlock extends React.Component {
+export class AccountUnlock extends React.PureComponent {
render() {
const { navigation } = this.props;
- const next = navigation.getParam('next', 'AccountList');
+ const next = navigation.getParam('next', 'LegacyAccountList');
return (
@@ -88,7 +91,9 @@ export class AccountUnlock extends React.Component {
} else {
const resetAction = StackActions.reset({
actions: [
- NavigationActions.navigate({ routeName: 'AccountList' }),
+ NavigationActions.navigate({
+ routeName: 'LegacyAccountList'
+ }),
NavigationActions.navigate({ routeName: 'AccountDetails' }),
NavigationActions.navigate({ routeName: next })
],
@@ -126,10 +131,13 @@ class AccountUnlockView extends React.PureComponent {
return (
- UNLOCK ACCOUNT
- {this.showErrorMessage()}
- PIN
+
{
this.setState({ pin: pin });
if (pin.length < 4) {
@@ -162,7 +170,7 @@ function PinInput(props) {
numberOfLines={1}
returnKeyType="next"
secureTextEntry
- style={styles.pinInput}
+ style={[fontStyles.t_seed, styles.pinInput]}
{...props}
/>
);
@@ -172,30 +180,13 @@ const styles = StyleSheet.create({
body: {
backgroundColor: colors.bg,
flex: 1,
- overflow: 'hidden',
- padding: 20
- },
- errorText: {
- color: colors.bg_alert,
- fontFamily: fonts.bold,
- fontSize: 12,
- paddingBottom: 20,
- textAlign: 'center'
+ overflow: 'hidden'
},
pinInput: {
- marginBottom: 20
- },
- title: {
- color: colors.bg_text_sec,
- fontFamily: fonts.bold,
- fontSize: 18,
- paddingBottom: 10
- },
- titleTop: {
- color: colors.bg_text_sec,
- fontFamily: fonts.bold,
- fontSize: 24,
- paddingBottom: 20,
- textAlign: 'center'
+ borderBottomColor: colors.bg_button,
+ borderColor: colors.bg_button,
+ minHeight: 48,
+ paddingLeft: 10,
+ paddingRight: 10
}
});
diff --git a/src/screens/IdentityBackup.js b/src/screens/IdentityBackup.js
new file mode 100644
index 0000000000..ee2812705d
--- /dev/null
+++ b/src/screens/IdentityBackup.js
@@ -0,0 +1,110 @@
+// Copyright 2015-2019 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+'use strict';
+
+import React, { useEffect, useState } from 'react';
+
+import { words } from '../util/native';
+import { ScrollView, StyleSheet, Text, View } from 'react-native';
+import TouchableItem from '../components/TouchableItem';
+import colors from '../colors';
+import fontStyles from '../fontStyles';
+import ButtonMainAction from '../components/ButtonMainAction';
+import { withNavigation } from 'react-navigation';
+import {
+ navigateToNewIdentityNetwork,
+ setPin,
+ unlockSeed
+} from '../util/navigationHelpers';
+import { withAccountStore } from '../util/HOC';
+import testIDs from '../../e2e/testIDs';
+import ScreenHeading from '../components/ScreenHeading';
+import { alertBackupDone, alertCopyBackupPhrase } from '../util/alertUtils';
+
+function IdentityBackup({ navigation, accounts }) {
+ const [seedPhrase, setSeedPhrase] = useState('');
+ const isNew = navigation.getParam('isNew', false);
+ const onBackupDone = async () => {
+ const pin = await setPin(navigation);
+ await accounts.saveNewIdentity(seedPhrase, pin);
+ setSeedPhrase('');
+ navigateToNewIdentityNetwork(navigation);
+ };
+
+ useEffect(() => {
+ const setSeedPhraseAsync = async () => {
+ if (isNew) {
+ setSeedPhrase(await words());
+ } else {
+ const backupSeedPhrase = await unlockSeed(navigation);
+ navigation.pop();
+ setSeedPhrase(backupSeedPhrase);
+ }
+ };
+
+ setSeedPhraseAsync();
+ return () => {
+ setSeedPhrase('');
+ };
+ }, [isNew, navigation]);
+
+ return (
+
+
+
+ {
+ // only allow the copy of the recovery phrase in dev environment
+ if (__DEV__) {
+ alertCopyBackupPhrase(seedPhrase);
+ }
+ }}
+ >
+
+ {seedPhrase}
+
+
+ {isNew && (
+ alertBackupDone(onBackupDone)}
+ />
+ )}
+
+ );
+}
+
+export default withAccountStore(withNavigation(IdentityBackup));
+
+const styles = StyleSheet.create({
+ body: {
+ backgroundColor: colors.bg,
+ flex: 1,
+ flexDirection: 'column',
+ padding: 16
+ }
+});
diff --git a/src/screens/IdentityManagement.js b/src/screens/IdentityManagement.js
new file mode 100644
index 0000000000..013e1d74f4
--- /dev/null
+++ b/src/screens/IdentityManagement.js
@@ -0,0 +1,109 @@
+// Copyright 2015-2019 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+'use strict';
+
+import React from 'react';
+import { withNavigation } from 'react-navigation';
+import { ScrollView, StyleSheet, View } from 'react-native';
+
+import { withAccountStore } from '../util/HOC';
+import TextInput from '../components/TextInput';
+import { navigateToLandingPage, unlockSeed } from '../util/navigationHelpers';
+import {
+ alertDeleteIdentity,
+ alertIdentityDeletionError
+} from '../util/alertUtils';
+import testIDs from '../../e2e/testIDs';
+import ScreenHeading from '../components/ScreenHeading';
+import colors from '../colors';
+import PopupMenu from '../components/PopupMenu';
+
+function IdentityManagement({ accounts, navigation }) {
+ const { currentIdentity } = accounts.state;
+ if (!currentIdentity) return null;
+
+ const onOptionSelect = value => {
+ if (value === 'PathDelete') {
+ alertDeleteIdentity(async () => {
+ await unlockSeed(navigation);
+ const deleteSucceed = await accounts.deleteCurrentIdentity();
+ if (deleteSucceed) {
+ navigateToLandingPage(navigation, true);
+ } else {
+ alertIdentityDeletionError();
+ }
+ });
+ } else {
+ navigation.navigate('IdentityBackup', { isNew: false });
+ }
+ };
+
+ return (
+
+
+
+
+
+ accounts.updateIdentityName(name)}
+ value={currentIdentity.name}
+ placeholder="Enter a new identity name"
+ focus
+ />
+
+ );
+}
+
+export default withAccountStore(withNavigation(IdentityManagement));
+
+const styles = StyleSheet.create({
+ body: {
+ backgroundColor: colors.bg,
+ flex: 1,
+ flexDirection: 'column'
+ },
+ deleteText: {
+ color: colors.bg_alert
+ },
+ header: {
+ flexDirection: 'row',
+ paddingBottom: 24,
+ paddingLeft: 16,
+ paddingRight: 16
+ },
+ menuView: {
+ alignItems: 'flex-end',
+ flex: 1,
+ position: 'absolute',
+ right: 16,
+ top: 5
+ }
+});
diff --git a/src/screens/IdentityNew.js b/src/screens/IdentityNew.js
new file mode 100644
index 0000000000..079ccd5347
--- /dev/null
+++ b/src/screens/IdentityNew.js
@@ -0,0 +1,152 @@
+// Copyright 2015-2019 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+'use strict';
+
+import React, { useEffect, useState } from 'react';
+import { StyleSheet, View } from 'react-native';
+import { withNavigation } from 'react-navigation';
+
+import Button from '../components/Button';
+import TextInput from '../components/TextInput';
+import { emptyIdentity } from '../util/identitiesUtils';
+import colors from '../colors';
+import fonts from '../fonts';
+import { withAccountStore } from '../util/HOC';
+import { validateSeed } from '../util/account';
+import AccountSeed from '../components/AccountSeed';
+import {
+ navigateToNewIdentityNetwork,
+ setPin
+} from '../util/navigationHelpers';
+import { alertIdentityCreationError } from '../util/alertUtils';
+import testIDs from '../../e2e/testIDs';
+import ScreenHeading from '../components/ScreenHeading';
+import KeyboardScrollView from '../components/KeyboardScrollView';
+
+function IdentityNew({ accounts, navigation }) {
+ const isRecoverDefaultValue = navigation.getParam('isRecover', false);
+ const [isRecover, setIsRecover] = useState(isRecoverDefaultValue);
+ const [seedPhrase, setSeedPhrase] = useState('');
+
+ useEffect(() => {
+ const clearNewIdentity = () => accounts.updateNewIdentity(emptyIdentity());
+ clearNewIdentity();
+ return clearNewIdentity;
+ }, [accounts]);
+
+ const updateName = name => {
+ accounts.updateNewIdentity({ name });
+ };
+
+ const onRecoverIdentity = async () => {
+ const pin = await setPin(navigation);
+ try {
+ await accounts.saveNewIdentity(seedPhrase, pin);
+ setSeedPhrase('');
+ navigateToNewIdentityNetwork(navigation);
+ } catch (e) {
+ alertIdentityCreationError();
+ }
+ };
+
+ const onCreateNewIdentity = () => {
+ setSeedPhrase('');
+ navigation.navigate('IdentityBackup', {
+ isNew: true
+ });
+ };
+
+ const renderRecoverView = () => (
+ <>
+
+
+
+ >
+ );
+
+ const renderCreateView = () => (
+
+ setIsRecover(true)}
+ small={true}
+ onlyText={true}
+ />
+
+
+ );
+
+ return (
+
+
+
+ {isRecover ? renderRecoverView() : renderCreateView()}
+
+ );
+}
+
+export default withAccountStore(withNavigation(IdentityNew));
+
+const styles = StyleSheet.create({
+ body: {
+ backgroundColor: colors.bg,
+ flex: 1,
+ overflow: 'hidden'
+ },
+ btnBox: {
+ alignContent: 'center',
+ flexDirection: 'row',
+ flexWrap: 'wrap',
+ justifyContent: 'space-around',
+ marginTop: 32
+ },
+ title: {
+ color: colors.bg_text_sec,
+ fontFamily: fonts.bold,
+ fontSize: 18
+ }
+});
diff --git a/src/screens/IdentityPin.js b/src/screens/IdentityPin.js
new file mode 100644
index 0000000000..f42520a69b
--- /dev/null
+++ b/src/screens/IdentityPin.js
@@ -0,0 +1,207 @@
+// Copyright 2015-2019 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+'use strict';
+
+import React, { useState } from 'react';
+import { StyleSheet } from 'react-native';
+import { withNavigation } from 'react-navigation';
+import colors from '../colors';
+import Background from '../components/Background';
+import ButtonMainAction from '../components/ButtonMainAction';
+import TextInput from '../components/TextInput';
+import KeyboardScrollView from '../components/KeyboardScrollView';
+import { withAccountStore } from '../util/HOC';
+import testIDs from '../../e2e/testIDs';
+import ScreenHeading from '../components/ScreenHeading';
+import fontStyles from '../fontStyles';
+import { onlyNumberRegex } from '../util/regex';
+import { unlockIdentitySeed } from '../util/identitiesUtils';
+
+export default withAccountStore(withNavigation(IdentityPin));
+
+function IdentityPin({ navigation, accounts }) {
+ const initialState = {
+ confirmation: '',
+ focusConfirmation: false,
+ pin: '',
+ pinMismatch: false,
+ pinTooShort: false
+ };
+ const [state, setState] = useState(initialState);
+ const updateState = delta => setState({ ...state, ...delta });
+ const isUnlock = navigation.getParam('isUnlock', false);
+
+ const submit = async () => {
+ const isIdentityCreation = navigation.getParam('isNew');
+ const { pin, confirmation } = state;
+ if (pin.length >= 6 && pin === confirmation) {
+ if (isIdentityCreation) {
+ const resolve = navigation.getParam('resolve');
+ setState(initialState);
+ resolve(pin);
+ }
+ } else {
+ if (pin.length < 6) {
+ updateState({ pinTooShort: true });
+ } else if (pin !== confirmation) updateState({ pinMismatch: true });
+ }
+ };
+
+ const testPin = async () => {
+ const { pin } = state;
+ if (pin.length >= 6) {
+ try {
+ const identity =
+ navigation.getParam('identity') || accounts.state.currentIdentity;
+ const resolve = navigation.getParam('resolve');
+ const seed = await unlockIdentitySeed(pin, identity);
+ setState(initialState);
+ resolve(seed);
+ } catch (e) {
+ updateState({ pin: '', pinMismatch: true });
+ //TODO record error times;
+ }
+ } else {
+ updateState({ pinTooShort: true });
+ }
+ };
+
+ const showHintOrError = () => {
+ if (state.pinTooShort) {
+ return ' Your pin must be at least 6 digits long!';
+ } else if (state.pinMismatch) {
+ return isUnlock ? ' Pin code is wrong!' : " Pin codes don't match!";
+ }
+ return ' Choose a PIN code with 6 or more digits';
+ };
+
+ const onPinInputChange = (stateName, pinInput) => {
+ if (onlyNumberRegex.test(pinInput)) {
+ updateState({
+ pinMismatch: false,
+ pinTooShort: false,
+ [stateName]: pinInput
+ });
+ }
+ };
+
+ const renderPinInput = () =>
+ isUnlock ? (
+ <>
+
+ onPinInputChange('pin', pin)}
+ value={state.pin}
+ />
+
+ >
+ ) : (
+ <>
+
+
+ updateState({ focusConfirmation: false })}
+ onSubmitEditing={() => {
+ updateState({ focusConfirmation: true });
+ }}
+ onChangeText={pin => onPinInputChange('pin', pin)}
+ value={state.pin}
+ />
+
+ onPinInputChange('confirmation', confirmation)
+ }
+ value={state.confirmation}
+ />
+
+ >
+ );
+
+ return (
+
+
+ {renderPinInput()}
+
+ );
+}
+
+function PinInput(props) {
+ return (
+
+ );
+}
+
+const styles = StyleSheet.create({
+ body: {
+ backgroundColor: colors.bg,
+ flex: 1,
+ overflow: 'hidden'
+ },
+ pinInput: {
+ borderBottomColor: colors.bg_button,
+ borderColor: colors.bg_button,
+ minHeight: 48,
+ paddingLeft: 10,
+ paddingRight: 10
+ }
+});
diff --git a/src/screens/LegacyAccountBackup.js b/src/screens/LegacyAccountBackup.js
new file mode 100644
index 0000000000..c4410db2f3
--- /dev/null
+++ b/src/screens/LegacyAccountBackup.js
@@ -0,0 +1,149 @@
+// Copyright 2015-2019 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+'use strict';
+
+import React, { useEffect } from 'react';
+import { AppState, ScrollView, StyleSheet, Text, View } from 'react-native';
+import { withNavigation } from 'react-navigation';
+
+import colors from '../colors';
+import fonts from '../fonts';
+import fontStyles from '../fontStyles';
+import AccountCard from '../components/AccountCard';
+import Background from '../components/Background';
+import Button from '../components/Button';
+import ScreenHeading from '../components/ScreenHeading';
+import TouchableItem from '../components/TouchableItem';
+import DerivationPasswordVerify from '../components/DerivationPasswordVerify';
+import { withAccountStore } from '../util/HOC';
+import { NetworkProtocols, NETWORK_LIST } from '../constants';
+import { alertBackupDone, alertCopyBackupPhrase } from '../util/alertUtils';
+
+function LegacyAccountBackup({ navigation, accounts }) {
+ useEffect(() => {
+ const handleAppStateChange = nextAppState => {
+ if (nextAppState === 'inactive') {
+ navigation.goBack();
+ }
+ };
+
+ AppState.addEventListener('change', handleAppStateChange);
+ return () => {
+ const selectedKey = accounts.getSelectedKey();
+
+ if (selectedKey) {
+ accounts.lockAccount(selectedKey);
+ }
+
+ AppState.removeEventListener('change', handleAppStateChange);
+ };
+ }, [navigation, accounts]);
+
+ const { navigate } = navigation;
+ const isNew = navigation.getParam('isNew');
+ const {
+ address,
+ derivationPassword,
+ derivationPath,
+ name,
+ networkKey,
+ seed,
+ seedPhrase
+ } = isNew ? accounts.getNew() : accounts.getSelected();
+ const protocol =
+ (NETWORK_LIST[networkKey] && NETWORK_LIST[networkKey].protocol) ||
+ NetworkProtocols.UNKNOWN;
+
+ return (
+
+
+
+
+
+
+ {
+ // only allow the copy of the recovery phrase in dev environment
+ if (__DEV__) {
+ if (protocol === NetworkProtocols.SUBSTRATE) {
+ alertCopyBackupPhrase(`${seedPhrase}${derivationPath}`);
+ } else {
+ alertCopyBackupPhrase(seed);
+ }
+ }
+ }}
+ >
+ {seedPhrase || seed}
+
+ {!!derivationPath && (
+ {derivationPath}
+ )}
+ {!!derivationPassword && (
+
+ )}
+ {isNew && (
+ {
+ alertBackupDone(() => {
+ navigate('AccountPin', { isNew });
+ });
+ }}
+ />
+ )}
+
+
+ );
+}
+
+export default withAccountStore(withNavigation(LegacyAccountBackup));
+
+const styles = StyleSheet.create({
+ body: {
+ alignContent: 'flex-start',
+ backgroundColor: colors.bg,
+ flex: 1,
+ paddingBottom: 40,
+ paddingTop: 24
+ },
+ bodyContent: {
+ padding: 16
+ },
+ derivationText: {
+ backgroundColor: colors.card_bg,
+ fontFamily: fonts.regular,
+ fontSize: 20,
+ lineHeight: 26,
+ marginTop: 20,
+ minHeight: 30,
+ padding: 10
+ },
+ nextStep: {
+ marginTop: 20
+ },
+ title: {
+ color: colors.bg_text_sec,
+ fontFamily: fonts.bold,
+ fontSize: 18,
+ paddingBottom: 20
+ }
+});
diff --git a/src/screens/LegacyAccountList.js b/src/screens/LegacyAccountList.js
new file mode 100644
index 0000000000..e0a09e4ae8
--- /dev/null
+++ b/src/screens/LegacyAccountList.js
@@ -0,0 +1,77 @@
+// Copyright 2015-2019 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+'use strict';
+
+import React from 'react';
+import { FlatList, StyleSheet, View } from 'react-native';
+import { withNavigation } from 'react-navigation';
+
+import colors from '../colors';
+import AccountCard from '../components/AccountCard';
+import Background from '../components/Background';
+import testIDs from '../../e2e/testIDs';
+import ButtonMainAction from '../components/ButtonMainAction';
+import { withAccountStore } from '../util/HOC';
+
+function LegacyAccountList({ navigation, accounts }) {
+ const onAccountSelected = async key => {
+ await accounts.select(key);
+ navigation.navigate('AccountDetails');
+ };
+ const accountsMap = accounts.getAccounts();
+
+ return (
+
+
+ key}
+ renderItem={({ item: [accountKey, account] }) => {
+ return (
+ onAccountSelected(accountKey)}
+ style={{ paddingBottom: null }}
+ title={account.name}
+ />
+ );
+ }}
+ enableEmptySections
+ />
+ navigation.navigate('QrScanner')}
+ />
+
+ );
+}
+
+export default withAccountStore(withNavigation(LegacyAccountList));
+
+const styles = StyleSheet.create({
+ body: {
+ backgroundColor: colors.bg,
+ flex: 1,
+ flexDirection: 'column'
+ },
+ content: {
+ flex: 1
+ }
+});
diff --git a/src/screens/LegacyNetworkChooser.js b/src/screens/LegacyNetworkChooser.js
new file mode 100644
index 0000000000..b2bb665926
--- /dev/null
+++ b/src/screens/LegacyNetworkChooser.js
@@ -0,0 +1,133 @@
+// Copyright 2015-2019 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+'use strict';
+
+import React from 'react';
+import { ScrollView, StyleSheet, Text } from 'react-native';
+import { Subscribe } from 'unstated';
+import colors from '../colors';
+import fonts from '../fonts';
+import TouchableItem from '../components/TouchableItem';
+import {
+ NETWORK_LIST,
+ UnknownNetworkKeys,
+ SubstrateNetworkKeys
+} from '../constants';
+import AccountsStore from '../stores/AccountsStore';
+import { emptyAccount } from '../util/account';
+
+export default class LegacyNetworkChooser extends React.PureComponent {
+ static navigationOptions = {
+ headerBackTitle: 'Back',
+ title: 'Choose a network'
+ };
+ render() {
+ return (
+
+ {accounts => (
+
+ )}
+
+ );
+ }
+}
+
+class LegacyNetworkChooserView extends React.PureComponent {
+ render() {
+ const { navigation } = this.props;
+ const { accounts } = this.props;
+ const excludedNetworks = [UnknownNetworkKeys.UNKNOWN];
+
+ if (!__DEV__) {
+ excludedNetworks.push(SubstrateNetworkKeys.SUBSTRATE_DEV);
+ excludedNetworks.push(SubstrateNetworkKeys.KUSAMA_DEV);
+ }
+
+ return (
+
+ CHOOSE NETWORK
+ {Object.entries(NETWORK_LIST)
+ .filter(([networkKey]) => !excludedNetworks.includes(networkKey))
+ .map(([networkKey, networkParams]) => (
+ {
+ accounts.updateNew(emptyAccount('', networkKey));
+ navigation.goBack();
+ }}
+ >
+
+ {networkParams.title}
+
+
+ ))}
+
+ );
+ }
+}
+
+const styles = StyleSheet.create({
+ body: {
+ backgroundColor: colors.bg,
+ flex: 1,
+ flexDirection: 'column',
+ overflow: 'hidden'
+ },
+ bottom: {
+ flexBasis: 50,
+ paddingBottom: 15
+ },
+ card: {
+ backgroundColor: colors.card_bg,
+ padding: 20
+ },
+ cardText: {
+ color: colors.card_text,
+ fontFamily: fonts.bold,
+ fontSize: 20
+ },
+ title: {
+ color: colors.bg_text_sec,
+ fontFamily: fonts.bold,
+ fontSize: 18,
+ paddingBottom: 20
+ },
+ titleTop: {
+ color: colors.bg_text_sec,
+ fontFamily: fonts.bold,
+ fontSize: 24,
+ paddingBottom: 20,
+ textAlign: 'center'
+ },
+ top: {
+ flex: 1
+ }
+});
diff --git a/src/screens/Loading.js b/src/screens/Loading.js
index 64294df3bf..0d377e4d68 100644
--- a/src/screens/Loading.js
+++ b/src/screens/Loading.js
@@ -18,10 +18,9 @@
import React from 'react';
import { StyleSheet, View } from 'react-native';
-import { NavigationActions, StackActions } from 'react-navigation';
import colors from '../colors';
-import { accountId } from '../util/account';
+import { generateAccountId } from '../util/account';
import {
loadAccounts,
loadToCAndPPConfirmation,
@@ -29,41 +28,15 @@ import {
} from '../util/db';
export default class Loading extends React.PureComponent {
- static navigationOptions = {
- headerBackTitle: 'Back',
- title: 'Add Account'
- };
-
async componentDidMount() {
const tocPP = await loadToCAndPPConfirmation();
- const firstScreen = 'Welcome';
- const firstScreenActions = StackActions.reset({
- actions: [NavigationActions.navigate({ routeName: firstScreen })],
- index: 0,
- key: null
- });
- let tocActions;
-
+ const { navigate } = this.props.navigation;
if (!tocPP) {
this.migrateAccounts();
-
- tocActions = StackActions.reset({
- actions: [
- NavigationActions.navigate({
- params: {
- firstScreenActions
- },
- routeName: 'TocAndPrivacyPolicy'
- })
- ],
- index: 0
- });
+ navigate('TocAndPrivacyPolicy');
} else {
- tocActions = firstScreenActions;
+ navigate('Welcome');
}
-
- await loadAccounts();
- this.props.navigation.dispatch(tocActions);
}
async migrateAccounts() {
@@ -87,7 +60,7 @@ export default class Loading extends React.PureComponent {
accounts.forEach(account => {
try {
- saveAccount(accountId(account), account);
+ saveAccount(generateAccountId(account), account);
} catch (e) {
console.error(e);
}
diff --git a/src/screens/MessageDetails.js b/src/screens/MessageDetails.js
index bba2ea91bf..19e55c0988 100644
--- a/src/screens/MessageDetails.js
+++ b/src/screens/MessageDetails.js
@@ -20,7 +20,7 @@ import { GenericExtrinsicPayload } from '@polkadot/types';
import { isU8a, u8aToHex } from '@polkadot/util';
import PropTypes from 'prop-types';
import React from 'react';
-import { Alert, ScrollView, StyleSheet, Text } from 'react-native';
+import { ScrollView, StyleSheet, Text } from 'react-native';
import { Subscribe } from 'unstated';
import colors from '../colors';
import {
@@ -28,47 +28,65 @@ import {
NetworkProtocols,
SUBSTRATE_NETWORK_LIST
} from '../constants';
-import fonts from '../fonts';
-import AccountCard from '../components/AccountCard';
import Background from '../components/Background';
import Button from '../components/Button';
import PayloadDetailsCard from '../components/PayloadDetailsCard';
import ScannerStore from '../stores/ScannerStore';
-import { hexToAscii, isAscii } from '../util/strings';
+import AccountsStore from '../stores/AccountsStore';
+import { navigateToSignedMessage, unlockSeed } from '../util/navigationHelpers';
+import fontStyles from '../fontStyles';
+import MessageDetailsCard from '../components/MessageDetailsCard';
+import { alertMultipart } from '../util/alertUtils';
+import CompatibleCard from '../components/CompatibleCard';
+import { getIdentityFromSender } from '../util/identitiesUtils';
export default class MessageDetails extends React.PureComponent {
- static navigationOptions = {
- headerBackTitle: 'Transaction details',
- title: 'Transaction Details'
- };
+ async onSignMessage(scannerStore, accountsStore, sender) {
+ try {
+ if (sender.isLegacy) {
+ return this.props.navigation.navigate('AccountUnlockAndSign', {
+ next: 'SignedMessage'
+ });
+ }
+ const senderIdentity = getIdentityFromSender(
+ sender,
+ accountsStore.state.identities
+ );
+ const seed = await unlockSeed(this.props.navigation, senderIdentity);
+ await scannerStore.signDataWithSeed(
+ seed,
+ NETWORK_LIST[sender.networkKey].protocol
+ );
+ return navigateToSignedMessage(this.props.navigation);
+ } catch (e) {
+ scannerStore.setErrorMsg(e.message);
+ }
+ }
+
render() {
return (
-
- {scannerStore => {
+
+ {(scannerStore, accountsStore) => {
const dataToSign = scannerStore.getDataToSign();
const message = scannerStore.getMessage();
+ const sender = scannerStore.getSender();
if (dataToSign) {
return (
{
- try {
- this.props.navigation.navigate('AccountUnlockAndSign', {
- next: 'SignedMessage'
- });
- } catch (e) {
- scannerStore.setErrorMsg(e.message);
- }
- }}
+ onNext={() =>
+ this.onSignMessage(scannerStore, accountsStore, sender)
+ }
/>
);
} else {
@@ -91,7 +109,15 @@ export class MessageDetailsView extends React.PureComponent {
};
render() {
- const { dataToSign, isHash, message, onNext, prehash, sender } = this.props;
+ const {
+ accountsStore,
+ dataToSign,
+ isHash,
+ message,
+ onNext,
+ prehash,
+ sender
+ } = this.props;
const isEthereum =
NETWORK_LIST[sender.networkKey].protocol === NetworkProtocols.ETHEREUM;
@@ -104,53 +130,26 @@ export class MessageDetailsView extends React.PureComponent {
style={styles.body}
>
- SIGN MESSAGE
- FROM ACCOUNT
-
+ Sign Message
+ From Account
+
{!isEthereum && prehash && prefix ? (
) : null}
- {isHash ? (
- HASH
- ) : (
- MESSAGE
- )}
-
- {isHash
- ? message
- : isAscii(message)
- ? hexToAscii(message)
- : dataToSign}
-
+
{
- isHash
- ? Alert.alert(
- 'Warning',
- 'The payload of the transaction you are signing is too big to be decoded. Not seeing what you are signing is inherently unsafe. If possible, contact the developer of the application generating the transaction to ask for multipart support.',
- [
- {
- onPress: () => onNext(),
- text: 'I take the risk'
- },
- {
- style: 'cancel',
- text: 'Cancel'
- }
- ]
- )
- : onNext();
+ isHash ? alertMultipart(onNext) : onNext();
}}
/>
@@ -186,25 +185,13 @@ const styles = StyleSheet.create({
deleteText: {
textAlign: 'right'
},
- message: {
- backgroundColor: colors.card_bg,
- fontFamily: fonts.regular,
- fontSize: 20,
- lineHeight: 26,
- marginBottom: 20,
- minHeight: 120,
- padding: 10
- },
+
title: {
- color: colors.bg_text_sec,
- fontFamily: fonts.bold,
- fontSize: 18,
+ ...fontStyles.h2,
paddingBottom: 20
},
topTitle: {
- color: colors.bg_text_sec,
- fontFamily: fonts.bold,
- fontSize: 24,
+ ...fontStyles.h1,
paddingBottom: 20,
textAlign: 'center'
},
diff --git a/src/screens/PathDerivation.js b/src/screens/PathDerivation.js
new file mode 100644
index 0000000000..626a8eaee5
--- /dev/null
+++ b/src/screens/PathDerivation.js
@@ -0,0 +1,117 @@
+// Copyright 2015-2019 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+'use strict';
+
+import React, { useState } from 'react';
+import { withNavigation } from 'react-navigation';
+import { withAccountStore } from '../util/HOC';
+import { StyleSheet, Text, View } from 'react-native';
+import TextInput from '../components/TextInput';
+import ButtonMainAction from '../components/ButtonMainAction';
+import { validateDerivedPath } from '../util/identitiesUtils';
+import { navigateToPathsList, unlockSeed } from '../util/navigationHelpers';
+import { NETWORK_LIST, UnknownNetworkKeys } from '../constants';
+import { alertPathDerivationError } from '../util/alertUtils';
+import testIDs from '../../e2e/testIDs';
+import Separator from '../components/Separator';
+import ScreenHeading from '../components/ScreenHeading';
+import colors from '../colors';
+import PathCard from '../components/PathCard';
+import KeyboardScrollView from '../components/KeyboardScrollView';
+
+function PathDerivation({ accounts, navigation }) {
+ const networkKey = navigation.getParam(
+ 'networkKey',
+ UnknownNetworkKeys.UNKNOWN
+ );
+
+ const [derivationPath, setDerivationPath] = useState('');
+ const [keyPairsName, setKeyPairsName] = useState('');
+ const [isPathValid, setIsPathValid] = useState(true);
+ const existedNetworkPath = `//${NETWORK_LIST[networkKey].pathId}`;
+ const completePath = `${existedNetworkPath}${derivationPath}`;
+
+ const onPathDerivation = async () => {
+ if (!validateDerivedPath(derivationPath)) {
+ return setIsPathValid(false);
+ }
+ const seed = await unlockSeed(navigation);
+ const derivationSucceed = await accounts.deriveNewPath(
+ completePath,
+ seed,
+ NETWORK_LIST[networkKey].prefix,
+ networkKey,
+ keyPairsName
+ );
+ if (derivationSucceed) {
+ navigateToPathsList(navigation, networkKey);
+ } else {
+ setIsPathValid(false);
+ alertPathDerivationError();
+ }
+ };
+
+ return (
+
+
+
+ {!isPathValid && Invalid Path}
+
+ setKeyPairsName(keyParisName)}
+ />
+
+
+ onPathDerivation()}
+ />
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ backgroundColor: colors.bg,
+ flex: 1
+ }
+});
+
+export default withAccountStore(withNavigation(PathDerivation));
diff --git a/src/screens/PathDetails.js b/src/screens/PathDetails.js
new file mode 100644
index 0000000000..05fe6b7d40
--- /dev/null
+++ b/src/screens/PathDetails.js
@@ -0,0 +1,120 @@
+// Copyright 2015-2019 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+'use strict';
+
+import React from 'react';
+import { withAccountStore } from '../util/HOC';
+import { withNavigation } from 'react-navigation';
+import { ScrollView, StyleSheet, View } from 'react-native';
+import PathCard from '../components/PathCard';
+import PopupMenu from '../components/PopupMenu';
+import ScreenHeading from '../components/ScreenHeading';
+import colors from '../colors';
+import QrView from '../components/QrView';
+import {
+ getAccountIdWithPath,
+ getNetworkKeyByPath,
+ isSubstratePath
+} from '../util/identitiesUtils';
+import { UnknownNetworkKeys } from '../constants';
+import { alertDeleteAccount, alertPathDeletionError } from '../util/alertUtils';
+import { navigateToPathsList, unlockSeed } from '../util/navigationHelpers';
+import testIDs from '../../e2e/testIDs';
+
+export function PathDetailsView({ accounts, navigation, path, networkKey }) {
+ const { currentIdentity } = accounts.state;
+ const address = getAccountIdWithPath(path, currentIdentity);
+
+ const onOptionSelect = value => {
+ if (value === 'PathDelete') {
+ alertDeleteAccount('this key pairs', async () => {
+ await unlockSeed(navigation);
+ const deleteSucceed = await accounts.deletePath(path);
+ if (deleteSucceed) {
+ isSubstratePath(path)
+ ? navigateToPathsList(navigation, networkKey)
+ : navigation.navigate('AccountNetworkChooser');
+ } else {
+ alertPathDeletionError();
+ }
+ });
+ } else {
+ navigation.navigate('PathManagement', { path });
+ }
+ };
+
+ return (
+
+
+
+
+
+
+
+ {networkKey !== UnknownNetworkKeys.UNKNOWN && address !== '' && (
+
+ )}
+
+
+ );
+}
+
+function PathDetails({ accounts, navigation }) {
+ const path = navigation.getParam('path', '');
+ const networkKey = getNetworkKeyByPath(path);
+ return (
+
+ );
+}
+
+const styles = StyleSheet.create({
+ body: {
+ backgroundColor: colors.bg,
+ flex: 1,
+ flexDirection: 'column'
+ },
+ deleteText: {
+ color: colors.bg_alert
+ },
+ menuView: {
+ alignItems: 'flex-end',
+ flex: 1,
+ position: 'absolute',
+ right: 16,
+ top: 5
+ }
+});
+
+export default withAccountStore(withNavigation(PathDetails));
diff --git a/src/screens/PathManagement.js b/src/screens/PathManagement.js
new file mode 100644
index 0000000000..b6022a2c47
--- /dev/null
+++ b/src/screens/PathManagement.js
@@ -0,0 +1,46 @@
+// Copyright 2015-2019 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+'use strict';
+
+import React from 'react';
+import { withAccountStore } from '../util/HOC';
+import { withNavigation } from 'react-navigation';
+import { ScrollView } from 'react-native';
+import TextInput from '../components/TextInput';
+import PathCard from '../components/PathCard';
+import colors from '../colors';
+
+function PathManagement({ accounts, navigation }) {
+ const path = navigation.getParam('path', '');
+ const { currentIdentity } = accounts.state;
+ const pathName = currentIdentity.meta.get(path).name;
+
+ return (
+
+
+ accounts.updatePathName(path, name)}
+ value={pathName}
+ placeholder="Enter a new identity name"
+ focus={true}
+ />
+
+ );
+}
+
+export default withAccountStore(withNavigation(PathManagement));
diff --git a/src/screens/PathsList.js b/src/screens/PathsList.js
new file mode 100644
index 0000000000..1047cebf29
--- /dev/null
+++ b/src/screens/PathsList.js
@@ -0,0 +1,183 @@
+// Copyright 2015-2019 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+'use strict';
+
+import React, { useMemo } from 'react';
+import { ScrollView, StyleSheet, Text, View } from 'react-native';
+
+import {
+ NETWORK_LIST,
+ NetworkProtocols,
+ UnknownNetworkKeys
+} from '../constants';
+import { withAccountStore } from '../util/HOC';
+import { withNavigation } from 'react-navigation';
+import {
+ getPathsWithSubstrateNetwork,
+ groupPaths,
+ removeSlash
+} from '../util/identitiesUtils';
+import ButtonNewDerivation from '../components/ButtonNewDerivation';
+import PathCard from '../components/PathCard';
+import { PathDetailsView } from './PathDetails';
+import testIDs from '../../e2e/testIDs';
+
+import Separator from '../components/Separator';
+import fontStyles from '../fontStyles';
+import colors from '../colors';
+import ButtonMainAction from '../components/ButtonMainAction';
+import ScreenHeading from '../components/ScreenHeading';
+
+function PathsList({ accounts, navigation }) {
+ const networkKey = navigation.getParam(
+ 'networkKey',
+ UnknownNetworkKeys.UNKNOWN
+ );
+
+ const { currentIdentity } = accounts.state;
+ const isSubstratePaths =
+ NETWORK_LIST[networkKey].protocol === NetworkProtocols.SUBSTRATE;
+ const pathsGroups = useMemo(() => {
+ if (!currentIdentity || !isSubstratePaths) return null;
+ const paths = Array.from(currentIdentity.meta.keys());
+ const listedPaths = getPathsWithSubstrateNetwork(paths, networkKey);
+ return groupPaths(listedPaths);
+ }, [currentIdentity, isSubstratePaths, networkKey]);
+
+ if (!currentIdentity) return null;
+ if (!isSubstratePaths) {
+ return (
+
+ );
+ }
+
+ const { navigate } = navigation;
+
+ const renderSinglePath = pathsGroup => {
+ const path = pathsGroup.paths[0];
+ return (
+ navigate('PathDetails', { path })}
+ />
+ );
+ };
+
+ const renderGroupPaths = pathsGroup => (
+
+
+
+
+
+
+ {removeSlash(pathsGroup.title)}
+
+
+ {NETWORK_LIST[networkKey].pathId}
+ {pathsGroup.title}
+
+
+
+ {/**/}
+ {/* navigation.navigate('PathDerivation', { networkKey })*/}
+ {/* }*/}
+ {/*/>*/}
+
+
+ {pathsGroup.paths.map(path => (
+
+ navigate('PathDetails', { path })}
+ />
+
+ ))}
+
+ );
+ return (
+
+
+
+ {pathsGroups.map(pathsGroup =>
+ pathsGroup.paths.length === 1
+ ? renderSinglePath(pathsGroup)
+ : renderGroupPaths(pathsGroup)
+ )}
+ navigation.navigate('PathDerivation', { networkKey })}
+ />
+
+ navigation.navigate('QrScanner')}
+ />
+
+ );
+}
+
+export default withAccountStore(withNavigation(PathsList));
+
+const styles = StyleSheet.create({
+ body: {
+ backgroundColor: colors.bg,
+ flex: 1,
+ flexDirection: 'column'
+ }
+});
diff --git a/src/screens/PrivacyPolicy.js b/src/screens/PrivacyPolicy.js
index ee35a96be7..6d9cf5daf5 100644
--- a/src/screens/PrivacyPolicy.js
+++ b/src/screens/PrivacyPolicy.js
@@ -20,19 +20,13 @@ import React from 'react';
import { ScrollView, StyleSheet, View } from 'react-native';
import privacyPolicy from '../../docs/privacy-policy.md';
import colors from '../colors';
-import fonts from '../fonts';
import Markdown from '../components/Markdown';
export default class PrivacyPolicy extends React.PureComponent {
- static navigationOptions = {
- headerBackTitle: 'Back',
- title: 'Privacy policy'
- };
-
render() {
return (
-
+
{privacyPolicy}
@@ -45,33 +39,6 @@ const styles = StyleSheet.create({
backgroundColor: colors.bg,
flex: 1,
flexDirection: 'column',
- overflow: 'hidden',
- padding: 20
- },
- bottom: {
- flexBasis: 50,
- paddingBottom: 15
- },
- text: {
- color: colors.card_bg,
- fontFamily: fonts.regular,
- fontSize: 14,
- marginTop: 10
- },
- title: {
- color: colors.bg_text_sec,
- fontFamily: fonts.bold,
- fontSize: 18,
- paddingBottom: 20
- },
- titleTop: {
- color: colors.bg_text_sec,
- fontFamily: fonts.bold,
- fontSize: 24,
- fontWeight: 'bold',
- textAlign: 'center'
- },
- top: {
- flex: 1
+ overflow: 'hidden'
}
});
diff --git a/src/screens/QrScanner.js b/src/screens/QrScanner.js
index 68da8f8328..fc2192a1eb 100644
--- a/src/screens/QrScanner.js
+++ b/src/screens/QrScanner.js
@@ -19,7 +19,7 @@
'use strict';
import PropTypes from 'prop-types';
-import React from 'react';
+import React, { useEffect } from 'react';
import { Alert, Button, StyleSheet, Text, View } from 'react-native';
import { RNCamera } from 'react-native-camera';
import { Subscribe } from 'unstated';
@@ -29,13 +29,10 @@ import fonts from '../fonts';
import AccountsStore from '../stores/AccountsStore';
import ScannerStore from '../stores/ScannerStore';
import { isAddressString, isJsonString, rawDataToU8A } from '../util/decoders';
+import ScreenHeading from '../components/ScreenHeading';
+import { createMockSignRequest } from '../../e2e/mock';
export default class Scanner extends React.PureComponent {
- static navigationOptions = {
- headerBackTitle: 'Scanner',
- title: 'Transaction Details'
- };
-
constructor(props) {
super(props);
this.state = { enableScan: true };
@@ -64,6 +61,7 @@ export default class Scanner extends React.PureComponent {
isMultipart={scannerStore.getTotalFramesCount() > 1}
missedFrames={scannerStore.getMissedFrames()}
navigation={this.props.navigation}
+ accountStore={accountsStore}
scannerStore={scannerStore}
totalFramesCount={scannerStore.getTotalFramesCount()}
onBarCodeRead={async txRequestData => {
@@ -104,7 +102,6 @@ export default class Scanner extends React.PureComponent {
this.props.navigation.navigate('MessageDetails');
}
}
- ('');
} catch (e) {
return this.showErrorMessage(
scannerStore,
@@ -121,95 +118,85 @@ export default class Scanner extends React.PureComponent {
}
}
-export class QrScannerView extends React.Component {
- constructor(props) {
- super(props);
- this.setBusySubscription = null;
- this.setReadySubscription = null;
- }
-
- static propTypes = {
- onBarCodeRead: PropTypes.func.isRequired
- };
+QrScannerView.propTypes = {
+ onBarCodeRead: PropTypes.func.isRequired
+};
- componentDidMount() {
- this.setBusySubscription = this.props.navigation.addListener(
- 'willFocus',
- () => {
- this.props.scannerStore.setReady();
- }
- );
- this.setReadySubscription = this.props.navigation.addListener(
- 'didBlur',
- () => {
- this.props.scannerStore.setBusy();
- }
- );
+export function QrScannerView({
+ navigation,
+ scannerStore,
+ accountStore,
+ ...props
+}) {
+ if (global.inTest) {
+ props.onBarCodeRead(createMockSignRequest());
}
- componentWillUnmount() {
- this.setBusySubscription.remove();
- this.setReadySubscription.remove();
- }
+ useEffect(() => {
+ const setBusySubscription = navigation.addListener('willFocus', () => {
+ scannerStore.setReady();
+ });
+ const setReadySubscription = navigation.addListener('didBlur', () => {
+ scannerStore.setBusy();
+ });
+ return () => {
+ setBusySubscription.remove();
+ setReadySubscription.remove();
+ scannerStore.setReady();
+ };
+ }, [navigation, scannerStore]);
- render() {
- const missedFrames = this.props.scannerStore.getMissedFrames();
- const missedFramesMessage = missedFrames && missedFrames.join(', ');
+ const missedFrames = scannerStore.getMissedFrames();
+ const missedFramesMessage = missedFrames && missedFrames.join(', ');
- if (this.props.scannerStore.isBusy()) {
- return ;
- }
- return (
-
-
-
- SCANNER
+ if (scannerStore.isBusy()) {
+ return ;
+ }
+ return (
+
+
+
+
+
+
+
+
+
+
+ {props.isMultipart ? (
+
+
+ Scanning Multipart Data, Please Hold Still...
+
+
+ {props.completedFramesCount} / {props.totalFramesCount} Completed.
+
+ scannerStore.clearMultipartProgress()}
+ style={styles.descSecondary}
+ title="Start Over"
+ />
-
-
-
-
+ ) : (
+
+ Scan QR Code
+ To Sign a New Transaction
- {this.props.isMultipart ? (
-
-
- Scanning Multipart Data, Please Hold Still...
-
-
- {this.props.completedFramesCount} /{' '}
- {this.props.totalFramesCount} Completed.
-
- this.props.scannerStore.clearMultipartProgress()}
- style={styles.descSecondary}
- title="Start Over"
- />
-
- ) : (
-
- Scan QR Code
-
- To Sign a New Transaction
-
-
- )}
- {missedFrames && missedFrames.length >= 1 ? (
-
-
- You missed the following frames: {missedFramesMessage}
-
-
- ) : (
- undefined
- )}
-
-
- );
- }
+ )}
+ {missedFrames && missedFrames.length >= 1 && (
+
+
+ You missed the following frames: {missedFramesMessage}
+
+
+ )}
+
+
+ );
}
const text = {
@@ -273,12 +260,6 @@ const styles = StyleSheet.create({
alignItems: 'center',
justifyContent: 'center'
},
- titleTop: {
- color: colors.bg_text,
- fontFamily: fonts.bold,
- fontSize: 26,
- textAlign: 'center'
- },
top: {
alignItems: 'center',
backgroundColor: 'rgba(0, 0, 0, 0.5)',
diff --git a/src/screens/Security.js b/src/screens/Security.js
index 4a624df170..9c1c033a56 100644
--- a/src/screens/Security.js
+++ b/src/screens/Security.js
@@ -14,35 +14,29 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see .
+'use strict';
+
import React from 'react';
-import { ScrollView, StyleSheet, Text, View } from 'react-native';
-import Icon from 'react-native-vector-icons/MaterialIcons';
+import { ScrollView, StyleSheet } from 'react-native';
import colors from '../colors';
-import fonts from '../fonts';
+import ScreenHeading from '../components/ScreenHeading';
export default class Security extends React.PureComponent {
render() {
return (
-
-
-
- NOT SECURE
-
-
- A device is considered not secure if it has access to the internet or
+
+
);
}
@@ -54,29 +48,5 @@ const styles = StyleSheet.create({
flex: 1,
flexDirection: 'column',
overflow: 'hidden'
- },
- card: {
- alignItems: 'center',
- backgroundColor: colors.card_bg,
- flex: 1,
- flexDirection: 'row',
- padding: 20
- },
- cardText: {
- color: colors.card_bg,
- fontFamily: fonts.bold,
- fontSize: 22
- },
- text: {
- color: colors.card_bg,
- fontFamily: fonts.regular,
- fontSize: 14,
- marginBottom: 20
- },
- title: {
- color: colors.bg_text_sec,
- fontFamily: fonts.bold,
- fontSize: 18,
- paddingBottom: 20
}
});
diff --git a/src/screens/SignedMessage.js b/src/screens/SignedMessage.js
index 5bcd2fc389..2ed54405b5 100644
--- a/src/screens/SignedMessage.js
+++ b/src/screens/SignedMessage.js
@@ -16,64 +16,43 @@
'use strict';
-import PropTypes from 'prop-types';
-import React from 'react';
-import { ScrollView, StyleSheet, Text, View } from 'react-native';
-import { Subscribe } from 'unstated';
+import React, { useEffect } from 'react';
+import { ScrollView, StyleSheet, Text } from 'react-native';
import colors from '../colors';
-import fonts from '../fonts';
import QrView from '../components/QrView';
-import ScannerStore from '../stores/ScannerStore';
-import { hexToAscii, isAscii } from '../util/strings';
+import { withScannerStore } from '../util/HOC';
+import fontStyles from '../fontStyles';
+import MessageDetailsCard from '../components/MessageDetailsCard';
-export default class SignedMessage extends React.PureComponent {
- render() {
- return (
-
- {scannerStore => {
- return (
-
- );
- }}
-
- );
- }
-}
+export function SignedMessage({ scanner }) {
+ const data = scanner.getSignedTxData();
+ const isHash = scanner.getIsHash();
+ const message = scanner.getMessage();
-export class SignedMessageView extends React.PureComponent {
- static propTypes = {
- data: PropTypes.string.isRequired, // post sign
- isHash: PropTypes.bool,
- message: PropTypes.string // pre sign
- };
+ useEffect(
+ () =>
+ function() {
+ scanner.cleanup();
+ },
+ [scanner]
+ );
- render() {
- const { data, isHash, message } = this.props;
-
- return (
-
- SCAN SIGNATURE
-
-
-
- {!isHash && 'MESSAGE'}
- {isHash ? (
- HASH
- ) : (
- MESSAGE
- )}
-
- {isHash ? message : isAscii(message) ? hexToAscii(message) : data}
-
-
- );
- }
+ return (
+
+ Signed Message
+
+
+
+ );
}
+export default withScannerStore(SignedMessage);
+
const styles = StyleSheet.create({
body: {
backgroundColor: colors.bg,
@@ -81,30 +60,11 @@ const styles = StyleSheet.create({
flexDirection: 'column',
overflow: 'hidden'
},
- message: {
- backgroundColor: colors.card_bg,
- fontFamily: fonts.regular,
- fontSize: 20,
- lineHeight: 26,
- marginBottom: 20,
- minHeight: 120,
- padding: 10
- },
- qr: {
- backgroundColor: colors.card_bg,
- marginBottom: 20
- },
- title: {
- color: colors.bg_text_sec,
- fontFamily: fonts.bold,
- fontSize: 18,
- paddingBottom: 20
+ messageDetail: {
+ paddingHorizontal: 20
},
topTitle: {
- color: colors.bg_text_sec,
- fontFamily: fonts.bold,
- fontSize: 24,
- paddingBottom: 20,
+ ...fontStyles.h1,
textAlign: 'center'
}
});
diff --git a/src/screens/SignedTx.js b/src/screens/SignedTx.js
index 43831d5018..968101bead 100644
--- a/src/screens/SignedTx.js
+++ b/src/screens/SignedTx.js
@@ -16,65 +16,49 @@
'use strict';
-import PropTypes from 'prop-types';
-import React from 'react';
+import React, { useEffect } from 'react';
import { ScrollView, StyleSheet, Text, View } from 'react-native';
-import { Subscribe } from 'unstated';
import colors from '../colors';
-import AccountCard from '../components/AccountCard';
import PayloadDetailsCard from '../components/PayloadDetailsCard';
import TxDetailsCard from '../components/TxDetailsCard';
import QrView from '../components/QrView';
import {
NETWORK_LIST,
NetworkProtocols,
- SUBSTRATE_NETWORK_LIST,
- TX_DETAILS_MSG
+ SUBSTRATE_NETWORK_LIST
} from '../constants';
-import fonts from '../fonts';
-import AccountsStore from '../stores/AccountsStore';
-import ScannerStore from '../stores/ScannerStore';
+import testIDs from '../../e2e/testIDs';
+import { withAccountAndScannerStore } from '../util/HOC';
+import fontStyles from '../fontStyles';
+import CompatibleCard from '../components/CompatibleCard';
-export default class SignedTx extends React.PureComponent {
- render() {
- return (
-
- {(scanner, accounts) => {
- return (
-
- );
- }}
-
- );
- }
-}
+export function SignedTx({ scanner, accounts }) {
+ const { gas, gasPrice, value } = scanner.getTx();
+ const data = scanner.getSignedTxData();
+ const recipient = scanner.getRecipient();
+ const sender = scanner.getSender();
-export class SignedTxView extends React.PureComponent {
- static propTypes = {
- data: PropTypes.string.isRequired,
- gas: PropTypes.string,
- gasPrice: PropTypes.string,
- recipient: PropTypes.object,
- sender: PropTypes.object,
- value: PropTypes.string
- };
+ useEffect(
+ () =>
+ function() {
+ scanner.cleanup();
+ },
+ [scanner]
+ );
- render() {
- const { data, gas, gasPrice, recipient, sender, value } = this.props;
+ return (
+
+ Scan Signature
+
+
+
- return (
-
- SCAN SIGNATURE
-
-
-
- TRANSACTION DETAILS
+ Transaction Details
+
{NETWORK_LIST[sender.networkKey].protocol ===
NetworkProtocols.ETHEREUM ? (
@@ -85,12 +69,8 @@ export class SignedTxView extends React.PureComponent {
gas={gas}
gasPrice={gasPrice}
/>
- RECIPIENT
-
+ Recipient
+
) : (
)}
-
- );
- }
+
+
+ );
}
+export default withAccountAndScannerStore(SignedTx);
+
+const TX_DETAILS_MSG = 'After signing and publishing you will have sent';
+
const styles = StyleSheet.create({
body: {
+ alignContent: 'flex-start',
backgroundColor: colors.bg,
flex: 1,
- flexDirection: 'column',
- overflow: 'hidden'
+ paddingTop: 24
},
qr: {
- backgroundColor: colors.card_bg,
marginBottom: 20
},
title: {
- color: colors.bg_text_sec,
- fontFamily: fonts.bold,
- fontSize: 18,
+ ...fontStyles.h2,
+ marginHorizontal: 20,
paddingBottom: 20
},
topTitle: {
- color: colors.bg_text_sec,
- fontFamily: fonts.bold,
- fontSize: 24,
+ ...fontStyles.h1,
paddingBottom: 20,
textAlign: 'center'
}
diff --git a/src/screens/TermsAndConditions.js b/src/screens/TermsAndConditions.js
index a438291998..c56a677d95 100644
--- a/src/screens/TermsAndConditions.js
+++ b/src/screens/TermsAndConditions.js
@@ -21,7 +21,7 @@ import { ScrollView, StyleSheet, Text, View } from 'react-native';
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
import toc from '../../docs/terms-and-conditions.md';
import colors from '../colors';
-import fonts from '../fonts';
+import fontStyles from '../fontStyles';
import Button from '../components/Button';
import Markdown from '../components/Markdown';
import TouchableItem from '../components/TouchableItem';
@@ -29,11 +29,6 @@ import { saveToCAndPPConfirmation } from '../util/db';
import testIDs from '../../e2e/testIDs';
export default class TermsAndConditions extends React.PureComponent {
- static navigationOptions = {
- headerBackTitle: 'Back',
- title: 'Terms and conditions'
- };
-
state = {
ppAgreement: false,
tocAgreement: false
@@ -42,72 +37,86 @@ export default class TermsAndConditions extends React.PureComponent {
render() {
const { navigation } = this.props;
const { tocAgreement, ppAgreement } = this.state;
+ const disableButtons = navigation.getParam('disableButtons', false);
+
+ const onConfirm = async () => {
+ await saveToCAndPPConfirmation();
+ navigation.navigate('Welcome');
+ };
+
return (
-
+
{toc}
- {
- this.setState({ tocAgreement: !tocAgreement });
- }}
- >
-
-
-
- {' I agree to the terms and conditions'}
-
-
- {
- this.setState({ ppAgreement: !ppAgreement });
- }}
- >
-
+ {!disableButtons && (
+
+ {
+ this.setState({ tocAgreement: !tocAgreement });
+ }}
+ >
+
-
- {' I agree to the '}
-
+ {' I agree to the terms and conditions'}
+
+
+ {
- navigation.navigate('PrivacyPolicy');
+ this.setState({ ppAgreement: !ppAgreement });
}}
>
- privacy policy
-
-
-
+
+
+
+ {' I agree to the '}
+ {
+ navigation.navigate('PrivacyPolicy');
+ }}
+ >
+ privacy policy
+
+
+
- {
- const firstScreenActions = navigation.getParam(
- 'firstScreenActions'
- );
- await saveToCAndPPConfirmation();
- navigation.dispatch(firstScreenActions);
- }}
- />
+
+
+ )}
);
}
@@ -118,33 +127,13 @@ const styles = StyleSheet.create({
backgroundColor: colors.bg,
flex: 1,
flexDirection: 'column',
- overflow: 'hidden',
- padding: 20
- },
- bottom: {
- flexBasis: 50,
- paddingBottom: 15
- },
- text: {
- color: colors.card_bg,
- fontFamily: fonts.regular,
- fontSize: 14,
- marginTop: 10
- },
- title: {
- color: colors.bg_text_sec,
- fontFamily: fonts.bold,
- fontSize: 18,
- paddingBottom: 20
+ overflow: 'hidden'
},
- titleTop: {
+ icon: {
color: colors.bg_text_sec,
- fontFamily: fonts.bold,
- fontSize: 24,
- paddingBottom: 20,
- textAlign: 'center'
+ fontSize: 30
},
- top: {
+ scrollView: {
flex: 1
}
});
diff --git a/src/screens/TxDetails.js b/src/screens/TxDetails.js
index 5d67d2b78a..990ef39e87 100644
--- a/src/screens/TxDetails.js
+++ b/src/screens/TxDetails.js
@@ -18,7 +18,7 @@
import PropTypes from 'prop-types';
import React from 'react';
-import { ScrollView, StyleSheet, Text } from 'react-native';
+import { ScrollView, StyleSheet, Text, View } from 'react-native';
import { Subscribe } from 'unstated';
import colors from '../colors';
@@ -27,45 +27,64 @@ import {
NetworkProtocols,
SUBSTRATE_NETWORK_LIST
} from '../constants';
-import fonts from '../fonts';
-import AccountCard from '../components/AccountCard';
import Background from '../components/Background';
-import Button from '../components/Button';
+import ButtonMainAction from '../components/ButtonMainAction';
+import ScreenHeading from '../components/ScreenHeading';
import TxDetailsCard from '../components/TxDetailsCard';
import AccountsStore from '../stores/AccountsStore';
import ScannerStore from '../stores/ScannerStore';
import PayloadDetailsCard from '../components/PayloadDetailsCard';
+import { navigateToSignedTx, unlockSeed } from '../util/navigationHelpers';
import { GenericExtrinsicPayload } from '@polkadot/types';
+import testIDs from '../../e2e/testIDs';
+import fontStyles from '../fontStyles';
+import CompatibleCard from '../components/CompatibleCard';
+import { getIdentityFromSender } from '../util/identitiesUtils';
export default class TxDetails extends React.PureComponent {
- static navigationOptions = {
- headerBackTitle: 'Transaction details',
- title: 'Transaction Details'
- };
+ async onSignTx(scannerStore, accountsStore, sender) {
+ try {
+ if (sender.isLegacy) {
+ return this.props.navigation.navigate('AccountUnlockAndSign', {
+ next: 'SignedTx'
+ });
+ }
+ const senderIdentity = getIdentityFromSender(
+ sender,
+ accountsStore.state.identities
+ );
+ const seed = await unlockSeed(this.props.navigation, senderIdentity);
+ await scannerStore.signDataWithSeed(
+ seed,
+ NETWORK_LIST[sender.networkKey].protocol
+ );
+ return navigateToSignedTx(this.props.navigation);
+ } catch (e) {
+ scannerStore.setErrorMsg(e.message);
+ }
+ }
+
render() {
return (
- {scannerStore => {
+ {(scannerStore, accountsStore) => {
const txRequest = scannerStore.getTXRequest();
-
+ const sender = scannerStore.getSender();
if (txRequest) {
const tx = scannerStore.getTx();
return (
{
- try {
- this.props.navigation.navigate('AccountUnlockAndSign');
- } catch (e) {
- scannerStore.setErrorMsg(e.message);
- }
- }}
+ onNext={() =>
+ this.onSignTx(scannerStore, accountsStore, sender)
+ }
/>
);
} else {
@@ -94,11 +113,12 @@ export class TxDetailsView extends React.PureComponent {
render() {
const {
// dataToSign,
+ accountsStore,
gas,
gasPrice,
prehash,
- recipient,
sender,
+ recipient,
value,
onNext
} = this.props;
@@ -109,101 +129,76 @@ export class TxDetailsView extends React.PureComponent {
!isEthereum && SUBSTRATE_NETWORK_LIST[sender.networkKey].prefix;
return (
-
-
- SIGN TRANSACTION
- FROM ACCOUNT
-
+
- TRANSACTION DETAILS
-
- {isEthereum ? (
-
-
- RECIPIENT
-
+
+ {`You are about to confirm sending the following ${
+ isEthereum ? 'transaction' : 'extrinsic'
+ }`}
+
+
+
+
-
- ) : (
-
- )}
-
-
+
+ Recipient
+
+
+ ) : (
+
+ )}
+
+
+ onNext()}
/>
-
+
);
}
}
const styles = StyleSheet.create({
- actionButtonContainer: {
- flex: 1
- },
- actionsContainer: {
- flex: 1,
- flexDirection: 'row'
- },
- address: {
- flex: 1
- },
body: {
+ alignContent: 'flex-start',
backgroundColor: colors.bg,
- flex: 1,
- flexDirection: 'column',
- overflow: 'hidden',
- padding: 20
+ flex: 1
},
bodyContent: {
- paddingBottom: 40
+ marginVertical: 16,
+ paddingHorizontal: 20
},
- changePinText: {
- color: 'green',
- textAlign: 'left'
- },
- deleteText: {
- textAlign: 'right'
+ marginBottom: {
+ marginBottom: 16
},
title: {
- color: colors.bg_text_sec,
- fontFamily: fonts.bold,
- fontSize: 18,
- paddingBottom: 20
- },
- topTitle: {
- color: colors.bg_text_sec,
- fontFamily: fonts.bold,
- fontSize: 24,
- paddingBottom: 20,
- textAlign: 'center'
- },
- transactionDetails: {
- backgroundColor: colors.card_bg,
- flex: 1
- },
- wrapper: {
- borderRadius: 5
+ ...fontStyles.t_regular,
+ paddingBottom: 8
}
});
diff --git a/src/stores/AccountsStore.js b/src/stores/AccountsStore.js
index 1426d252f1..4093442f3a 100644
--- a/src/stores/AccountsStore.js
+++ b/src/stores/AccountsStore.js
@@ -18,39 +18,42 @@
import { Container } from 'unstated';
-import { accountId, empty } from '../util/account';
+import {
+ emptyAccount,
+ extractAddressFromAccountId,
+ generateAccountId
+} from '../util/account';
import {
loadAccounts,
saveAccount,
- deleteAccount as deleteDbAccount
+ deleteAccount as deleteDbAccount,
+ saveIdentities,
+ loadIdentities
} from '../util/db';
-import { parseSURI } from '../util/suri';
-import { decryptData, encryptData } from '../util/native';
-
-export type Account = {
- address: string,
- createdAt: number,
- derivationPassword: string,
- derivationPath: string, // doesn't contain the ///password
- encryptedSeed: string,
- name: string,
- networkKey: string,
- seed: string, //this is the SURI (seedPhrase + /soft//hard///password derivation)
- seedPhrase: string, //contains only the BIP39 words, no derivation path
- updatedAt: number,
- validBip39Seed: boolean
-};
-
-type AccountsState = {
- accounts: Map,
- newAccount: Account,
- selectedKey: string
-};
-
-export default class AccountsStore extends Container {
+import { constructSURI, parseSURI } from '../util/suri';
+import {
+ brainWalletAddress,
+ decryptData,
+ encryptData,
+ substrateAddress
+} from '../util/native';
+import { NETWORK_LIST } from '../constants';
+import type { AccountsStoreState } from './types';
+import {
+ deepCopyIdentities,
+ deepCopyIdentity,
+ emptyIdentity,
+ getNetworkKeyByPath
+} from '../util/identitiesUtils';
+
+export default class AccountsStore extends Container {
state = {
accounts: new Map(),
- newAccount: empty(),
+ currentIdentity: null,
+ identities: [],
+ loaded: false,
+ newAccount: emptyAccount(),
+ newIdentity: emptyIdentity(),
selectedKey: ''
};
@@ -60,7 +63,7 @@ export default class AccountsStore extends Container {
}
async select(accountKey) {
- this.setState({ selectedKey: accountKey });
+ await this.setState({ selectedKey: accountKey });
}
updateNew(accountUpdate) {
@@ -75,38 +78,59 @@ export default class AccountsStore extends Container {
async submitNew(pin) {
const account = this.state.newAccount;
+ if (!account.seed) return;
- // only save a new account if the seed isn't empty
- if (account.seed) {
- const accountKey = accountId(account);
+ const accountKey = generateAccountId(account);
+ await this.save(accountKey, account, pin);
- await this.save(accountKey, account, pin);
- this.setState({
- accounts: this.state.accounts.set(accountKey, account),
- newAccount: empty()
- });
- }
+ this.setState({
+ accounts: this.state.accounts.set(accountKey, account),
+ newAccount: emptyAccount()
+ });
+ }
+
+ async deriveEthereumAccount(seed, networkKey) {
+ const networkParams = NETWORK_LIST[networkKey];
+ const ethereumAddress = await brainWalletAddress(seed);
+ if (ethereumAddress === '') return false;
+ const { ethereumChainId } = networkParams;
+ const accountId = generateAccountId({
+ address: ethereumAddress.address,
+ networkKey
+ });
+ const updatedCurrentIdentity = deepCopyIdentity(this.state.currentIdentity);
+ if (updatedCurrentIdentity.meta.has(ethereumChainId)) return false;
+ updatedCurrentIdentity.meta.set(ethereumChainId, {
+ accountId,
+ createdAt: new Date().getTime(),
+ name: '',
+ updatedAt: new Date().getTime()
+ });
+ updatedCurrentIdentity.accountIds.set(accountId, ethereumChainId);
+ return await this.updateCurrentIdentity(updatedCurrentIdentity);
}
- updateAccount(accountKey, updatedAccount) {
+ async updateAccount(accountKey, updatedAccount) {
const accounts = this.state.accounts;
const account = accounts.get(accountKey);
if (account && updatedAccount) {
- this.setState({
+ await this.setState({
accounts: accounts.set(accountKey, { ...account, ...updatedAccount })
});
}
}
- updateSelectedAccount(updatedAccount) {
- this.updateAccount(this.state.selectedKey, updatedAccount);
+ async updateSelectedAccount(updatedAccount) {
+ await this.updateAccount(this.state.selectedKey, updatedAccount);
}
async refreshList() {
- loadAccounts().then(accounts => {
- this.setState({ accounts });
- });
+ const accounts = await loadAccounts();
+ const identities = await loadIdentities();
+ let { currentIdentity } = this.state;
+ if (identities.length > 0) currentIdentity = identities[0];
+ this.setState({ accounts, currentIdentity, identities, loaded: true });
}
async save(accountKey, account, pin = null) {
@@ -118,7 +142,6 @@ export default class AccountsStore extends Container {
const accountToSave = this.deleteSensitiveData(account);
- accountToSave.updatedAt = new Date().getTime();
await saveAccount(accountKey, accountToSave);
} catch (e) {
console.error(e);
@@ -178,35 +201,75 @@ export default class AccountsStore extends Container {
}
}
- async checkPinForSelected(pin) {
- const account = this.getSelected();
-
- if (account && account.encryptedSeed) {
- return await decryptData(account.encryptedSeed, pin);
- } else {
- return false;
+ getAccountWithoutCaseSensitive(accountId) {
+ let findLegacyAccount = null;
+ for (const [key, value] of this.state.accounts) {
+ if (key.toLowerCase() === accountId.toLowerCase()) {
+ findLegacyAccount = value;
+ break;
+ }
}
+ return findLegacyAccount;
}
- getById(account) {
- return (
- this.state.accounts.get(accountId(account)) ||
- empty(account.address, account.networkKey)
- );
+ async getById({ address, networkKey }) {
+ const accountId = generateAccountId({ address, networkKey });
+ const legacyAccount = this.getAccountWithoutCaseSensitive(accountId);
+ if (legacyAccount) return { ...legacyAccount, isLegacy: true };
+ const derivedAccount = await this.getAccountFromIdentity(accountId);
+ if (derivedAccount) return { ...derivedAccount, isLegacy: false };
+ return emptyAccount(address, networkKey);
}
- getByAddress(address) {
+ async getAccountFromIdentity(accountIdOrAddress) {
+ const isAccountId = accountIdOrAddress.split(':').length > 1;
+ let targetPath = null;
+ let targetIdentity = null;
+ for (const identity of this.state.identities) {
+ const searchList = Array.from(identity.accountIds.entries());
+ for (const [accountId, path] of searchList) {
+ const searchAccountIdOrAddress = isAccountId
+ ? accountId
+ : extractAddressFromAccountId(accountId);
+ const found =
+ accountId.indexOf('ethereum:') === 0
+ ? searchAccountIdOrAddress.toLowerCase() ===
+ accountIdOrAddress.toLowerCase()
+ : searchAccountIdOrAddress === accountIdOrAddress;
+ if (found) {
+ targetPath = path;
+ targetIdentity = identity;
+ break;
+ }
+ }
+ }
+
+ if (!targetPath || !targetIdentity) return false;
+ await this.setState({ currentIdentity: targetIdentity });
+
+ const metaData = targetIdentity.meta.get(targetPath);
+ const networkKey = getNetworkKeyByPath(targetPath);
+ return {
+ ...metaData,
+ encryptedSeed: targetIdentity.encryptedSeed,
+ isBip39: true,
+ isLegacy: false,
+ networkKey,
+ path: targetPath
+ };
+ }
+
+ async getAccountByAddress(address) {
if (!address) {
return false;
}
for (let v of this.state.accounts.values()) {
if (v.address.toLowerCase() === address.toLowerCase()) {
- return v;
+ return { ...v, isLegacy: true };
}
}
-
- return false;
+ return await this.getAccountFromIdentity(address);
}
getSelected() {
@@ -220,4 +283,149 @@ export default class AccountsStore extends Container {
getAccounts() {
return this.state.accounts;
}
+
+ getIdentityByAccountId(accountId) {
+ return this.state.identities.find(identity =>
+ identity.accountIds.has(accountId)
+ );
+ }
+
+ getNewIdentity() {
+ return this.state.newIdentity;
+ }
+
+ async saveNewIdentity(seedPhrase, pin) {
+ const updatedIdentity = deepCopyIdentity(this.state.newIdentity);
+ updatedIdentity.encryptedSeed = await encryptData(seedPhrase, pin);
+ const newIdentities = this.state.identities.concat(updatedIdentity);
+ this.setState({
+ currentIdentity: updatedIdentity,
+ identities: newIdentities,
+ newIdentity: emptyIdentity()
+ });
+ await saveIdentities(newIdentities);
+ }
+
+ async selectIdentity(identity) {
+ await this.setState({ currentIdentity: identity });
+ }
+
+ updateNewIdentity(identityUpdate) {
+ this.setState({
+ newIdentity: { ...this.state.newIdentity, ...identityUpdate }
+ });
+ }
+
+ async updateCurrentIdentity(updatedIdentity) {
+ try {
+ await this.setState({
+ currentIdentity: updatedIdentity
+ });
+ await this._updateIdentitiesWithCurrentIdentity();
+ } catch (e) {
+ console.warn('derive new Path error', e);
+ return false;
+ }
+ return true;
+ }
+
+ async _updateIdentitiesWithCurrentIdentity() {
+ const newIdentities = deepCopyIdentities(this.state.identities);
+ const identityIndex = newIdentities.findIndex(
+ identity =>
+ identity.encryptedSeed === this.state.currentIdentity.encryptedSeed
+ );
+ newIdentities.splice(identityIndex, 1, this.state.currentIdentity);
+ this.setState({ identities: newIdentities });
+ await saveIdentities(newIdentities);
+ }
+
+ async updateIdentityName(name) {
+ const updatedCurrentIdentity = deepCopyIdentity(this.state.currentIdentity);
+ updatedCurrentIdentity.name = name;
+ try {
+ await this.setState({ currentIdentity: updatedCurrentIdentity });
+ await this._updateIdentitiesWithCurrentIdentity();
+ } catch (e) {
+ console.warn('update identity name error', e);
+ }
+ }
+
+ async updatePathName(path, name) {
+ const updatedCurrentIdentity = deepCopyIdentity(this.state.currentIdentity);
+ const updatedPathMeta = Object.assign(
+ {},
+ updatedCurrentIdentity.meta.get(path),
+ { name }
+ );
+ updatedCurrentIdentity.meta.set(path, updatedPathMeta);
+ try {
+ await this.setState({ currentIdentity: updatedCurrentIdentity });
+ await this._updateIdentitiesWithCurrentIdentity();
+ } catch (e) {
+ console.warn('update path name error', e);
+ }
+ }
+
+ async deriveNewPath(newPath, seed, prefix, networkKey, name) {
+ const updatedCurrentIdentity = deepCopyIdentity(this.state.currentIdentity);
+ const suri = constructSURI({
+ derivePath: newPath,
+ password: '',
+ phrase: seed
+ });
+ let address = '';
+ try {
+ address = await substrateAddress(suri, prefix);
+ } catch (e) {
+ return false;
+ }
+ if (address === '') return false;
+ if (updatedCurrentIdentity.meta.has(newPath)) return false;
+ const accountId = generateAccountId({ address, networkKey });
+ updatedCurrentIdentity.meta.set(newPath, {
+ accountId,
+ createdAt: new Date().getTime(),
+ name,
+ updatedAt: new Date().getTime()
+ });
+ updatedCurrentIdentity.accountIds.set(accountId, newPath);
+ return await this.updateCurrentIdentity(updatedCurrentIdentity);
+ }
+
+ async deletePath(path) {
+ const updatedCurrentIdentity = deepCopyIdentity(this.state.currentIdentity);
+ const { accountId } = updatedCurrentIdentity.meta.get(path);
+ updatedCurrentIdentity.meta.delete(path);
+ updatedCurrentIdentity.accountIds.delete(accountId);
+ try {
+ await this.setState({
+ currentIdentity: updatedCurrentIdentity
+ });
+ await this._updateIdentitiesWithCurrentIdentity();
+ } catch (e) {
+ console.warn('derive new Path error', e);
+ return false;
+ }
+ return true;
+ }
+
+ async deleteCurrentIdentity() {
+ try {
+ const newIdentities = deepCopyIdentities(this.state.identities);
+ const identityIndex = newIdentities.findIndex(
+ identity =>
+ identity.encryptedSeed === this.state.currentIdentity.encryptedSeed
+ );
+ newIdentities.splice(identityIndex, 1);
+ this.setState({
+ currentIdentity: null,
+ identities: newIdentities
+ });
+ await saveIdentities(newIdentities);
+ return true;
+ } catch (e) {
+ return false;
+ }
+ }
}
diff --git a/src/stores/ScannerStore.js b/src/stores/ScannerStore.js
index d927f47d24..bdc9b58658 100644
--- a/src/stores/ScannerStore.js
+++ b/src/stores/ScannerStore.js
@@ -48,7 +48,8 @@ import {
asciiToHex,
encodeNumber
} from '../util/decoders';
-import { Account } from './AccountsStore';
+import { Account } from './types';
+import { constructSURI } from '../util/suri';
type TXRequest = Object;
@@ -145,7 +146,7 @@ export default class ScannerStore extends Container {
return;
}
- if (accountsStore.getByAddress(parsedData.data.account)) {
+ if (await accountsStore.getAccountByAddress(parsedData.data.account)) {
this.setState({
unsignedData: parsedData
});
@@ -156,7 +157,7 @@ export default class ScannerStore extends Container {
for (let i = 0; i < networks.length; i++) {
let key = networks[i];
- let account = accountsStore.getByAddress(
+ let account = await accountsStore.getAccountByAddress(
encodeAddress(
decodeAddress(parsedData.data.account),
SUBSTRATE_NETWORK_LIST[key].prefix
@@ -257,13 +258,14 @@ export default class ScannerStore extends Container {
!missedFrames.includes(frame)
) {
// enumerate all the frames between (current)frame and latestFrame
- const missedFrames = Array.from(new Array(missedFramesRange), (_, i) =>
- mod(i + latestFrame, totalFrameCount)
+ const updatedMissedFrames = Array.from(
+ new Array(missedFramesRange),
+ (_, i) => mod(i + latestFrame, totalFrameCount)
);
const dedupMissedFrames = new Set([
...this.state.missedFrames,
- ...missedFrames
+ ...updatedMissedFrames
]);
this.setState({
@@ -318,11 +320,11 @@ export default class ScannerStore extends Container {
dataToSign = await ethSign(message);
}
- const sender = accountsStore.getByAddress(address);
+ const sender = await accountsStore.getAccountByAddress(address);
- if (!sender || !sender.encryptedSeed) {
+ if (!sender) {
throw new Error(
- `No private key found for ${address} found in your signer key storage.`
+ `No private key found for ${address} in your signer key storage.`
);
}
@@ -363,20 +365,20 @@ export default class ScannerStore extends Container {
? tx.ethereumChainId
: txRequest.data.data.genesisHash.toHex();
- const sender = accountsStore.getById({
+ const sender = await accountsStore.getById({
address: txRequest.data.account,
networkKey
});
const networkTitle = NETWORK_LIST[networkKey].title;
- if (!sender || !sender.encryptedSeed) {
+ if (!sender) {
throw new Error(
`No private key found for account ${txRequest.data.account} found in your signer key storage for the ${networkTitle} chain.`
);
}
- const recipient = accountsStore.getById({
+ const recipient = await accountsStore.getById({
address: isEthereum ? tx.action : txRequest.data.account,
networkKey
});
@@ -400,17 +402,15 @@ export default class ScannerStore extends Container {
return true;
}
- async signData(pin = '1') {
+ async signDataWithSuri(suri) {
const { dataToSign, isHash, sender } = this.state;
- const seed = await decryptData(sender.encryptedSeed, pin);
const isEthereum =
NETWORK_LIST[sender.networkKey].protocol === NetworkProtocols.ETHEREUM;
let signedData;
-
if (isEthereum) {
- signedData = await brainWalletSign(seed, dataToSign);
+ signedData = await brainWalletSign(suri, dataToSign);
} else {
let signable;
@@ -423,26 +423,39 @@ export default class ScannerStore extends Container {
} else if (isAscii(dataToSign)) {
signable = hexStripPrefix(asciiToHex(dataToSign));
}
-
- let signed = await substrateSign(seed, signable);
+ let signed = await substrateSign(suri, signable);
signed = '0x' + signed;
-
// TODO: tweak the first byte if and when sig type is not sr25519
const sig = u8aConcat(SIG_TYPE_SR25519, hexToU8a(signed));
-
signedData = u8aToHex(sig, -1, false); // the false doesn't add 0x
}
-
this.setState({ signedData });
}
+ async signDataWithSeed(seed, protocol) {
+ if (protocol === NetworkProtocols.SUBSTRATE) {
+ const suri = constructSURI({
+ derivePath: this.state.sender.path,
+ password: '',
+ phrase: seed
+ });
+ await this.signDataWithSuri(suri);
+ } else {
+ await this.signDataWithSuri(seed);
+ }
+ }
+
+ async signDataLegacy(pin = '1') {
+ const { sender } = this.state;
+ const suri = await decryptData(sender.encryptedSeed, pin);
+ await this.signDataWithSuri(suri);
+ }
+
cleanup() {
return new Promise(resolve => {
- const prehash = this.state.prehash;
this.setState(
{
- ...DEFAULT_STATE,
- prehash
+ ...DEFAULT_STATE
},
resolve
);
diff --git a/src/stores/types.js b/src/stores/types.js
new file mode 100644
index 0000000000..5ea7bce5f9
--- /dev/null
+++ b/src/stores/types.js
@@ -0,0 +1,54 @@
+// Copyright 2015-2019 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+// @flow
+
+export type Account = {
+ address: string,
+ createdAt: number,
+ derivationPassword: string,
+ derivationPath: string, // doesn't contain the ///password
+ encryptedSeed: string,
+ name: string,
+ networkKey: string,
+ seed: string, //this is the SURI (seedPhrase + /soft//hard///password derivation)
+ seedPhrase: string, //contains only the BIP39 words, no derivation path
+ updatedAt: number,
+ validBip39Seed: boolean
+};
+
+type AccountMeta = {
+ address: string,
+ createdAt: number,
+ name: ?string,
+ updatedAt: number
+};
+
+export type Identity = {
+ encryptedSeedPhrase: string,
+ derivationPassword: string,
+ meta: Map,
+ accountIds: Map,
+ name: string
+};
+
+export type AccountsStoreState = {
+ identities: [Identity],
+ accounts: Map,
+ newAccount: Account,
+ newIdentity: ?Identity,
+ selectedKey: string
+};
diff --git a/src/util/HOC.js b/src/util/HOC.js
new file mode 100644
index 0000000000..14a1b09fcd
--- /dev/null
+++ b/src/util/HOC.js
@@ -0,0 +1,42 @@
+// Copyright 2015-2019 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+'use strict';
+
+import React from 'react';
+import { Subscribe } from 'unstated';
+import AccountsStore from '../stores/AccountsStore';
+import ScannerStore from '../stores/ScannerStore';
+
+export const withAccountStore = WrappedComponent => props => (
+
+ {accounts => }
+
+);
+
+export const withScannerStore = WrappedComponent => props => (
+
+ {scanner => }
+
+);
+
+export const withAccountAndScannerStore = WrappedComponent => props => (
+
+ {(scanner, accounts) => (
+
+ )}
+
+);
diff --git a/src/util/account.js b/src/util/account.js
index 7ec2e1f003..4ddd83f148 100644
--- a/src/util/account.js
+++ b/src/util/account.js
@@ -16,13 +16,15 @@
// @flow
+'use strict';
+
import {
NetworkProtocols,
NETWORK_LIST,
SubstrateNetworkKeys
} from '../constants';
-export function accountId({ address, networkKey }) {
+export function generateAccountId({ address, networkKey }) {
if (
typeof address !== 'string' ||
address.length === 0 ||
@@ -41,11 +43,23 @@ export function accountId({ address, networkKey }) {
if (protocol === NetworkProtocols.SUBSTRATE) {
return `${protocol}:${address}:${genesisHash}`;
} else {
- return `${protocol}:0x${address.toLowerCase()}@${ethereumChainId}`;
+ return `${protocol}:0x${address}@${ethereumChainId}`;
}
}
-export function empty(address = '', networkKey = SubstrateNetworkKeys.KUSAMA) {
+export const extractAddressFromAccountId = id => {
+ const withoutNetwork = id.split(':')[1];
+ const address = withoutNetwork.split('@')[0];
+ if (address.indexOf('0x') !== -1) {
+ return address.slice(2);
+ }
+ return address;
+};
+
+export function emptyAccount(
+ address = '',
+ networkKey = SubstrateNetworkKeys.KUSAMA
+) {
return {
address: address,
createdAt: new Date().getTime(),
@@ -62,7 +76,7 @@ export function empty(address = '', networkKey = SubstrateNetworkKeys.KUSAMA) {
}
export function validateSeed(seed, validBip39Seed) {
- if (seed.length === 0) {
+ if (!seed || seed.length === 0) {
return {
accountRecoveryAllowed: false,
reason: 'A seed phrase is required.',
diff --git a/src/util/alertUtils.js b/src/util/alertUtils.js
new file mode 100644
index 0000000000..443b648d74
--- /dev/null
+++ b/src/util/alertUtils.js
@@ -0,0 +1,144 @@
+// Copyright 2015-2019 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+'use strict';
+
+import { Alert, Clipboard } from 'react-native';
+
+export const alertErrorWithMessage = (message, buttonText) =>
+ Alert.alert('Error', message, [
+ {
+ style: 'Cancel',
+ text: buttonText
+ }
+ ]);
+
+export const alertIdentityCreationError = () =>
+ alertErrorWithMessage("Can't create Identity from the seed", 'Try again');
+
+export const alertPathDerivationError = () =>
+ alertErrorWithMessage("Can't Derive Key pairs from the seed", 'Try again');
+
+export const alertPathDeletionError = () =>
+ alertErrorWithMessage("Can't delete Key pairs.", 'Try again');
+
+export const alertIdentityDeletionError = () =>
+ alertErrorWithMessage("Can't delete Identity.", 'Try again');
+
+const buildAlertDeleteButtons = onDelete => [
+ {
+ onPress: () => {
+ onDelete();
+ },
+ style: 'destructive',
+ text: 'Delete'
+ },
+ {
+ style: 'cancel',
+ text: 'Cancel'
+ }
+];
+
+export const alertDeleteAccount = (accountName, onDelete) => {
+ Alert.alert(
+ 'Delete Key Pairs',
+ `Do you really want to delete ${accountName}?
+This account can only be recovered with its associated recovery phrase.`,
+ buildAlertDeleteButtons(onDelete)
+ );
+};
+
+export const alertDeleteIdentity = onDelete => {
+ Alert.alert(
+ 'Delete Identity',
+ `Do you really want to delete this Identity and all the related key pairs?
+This identity can only be recovered with its associated recovery phrase.`,
+ buildAlertDeleteButtons(onDelete)
+ );
+};
+
+export const alertCopyBackupPhrase = seedPhrase =>
+ Alert.alert(
+ 'Write this recovery phrase on paper',
+ 'It is not recommended to transfer or store a recovery phrase digitally and unencrypted. Anyone in possession of this recovery phrase is able to spend funds from this account.',
+ [
+ {
+ onPress: () => {
+ Clipboard.setString(seedPhrase);
+ },
+ style: 'default',
+ text: 'Copy anyway'
+ },
+ {
+ style: 'cancel',
+ text: 'Cancel'
+ }
+ ]
+ );
+
+const alertRisks = (message, onPress) =>
+ Alert.alert('Warning', message, [
+ {
+ onPress,
+ style: 'default',
+ text: 'I understand the risks'
+ },
+ {
+ style: 'cancel',
+ text: 'Back'
+ }
+ ]);
+
+export const alertInvalidSeedRecovery = (message, navigation) =>
+ alertRisks(message, () => {
+ navigation.navigate('AccountPin', {
+ isNew: true
+ });
+ });
+
+export const alertMultipart = onNext =>
+ alertRisks(
+ 'The payload of the transaction you are signing is too big to be decoded. Not seeing what you are signing is inherently unsafe. If possible, contact the developer of the application generating the transaction to ask for multipart support.',
+ onNext
+ );
+
+export const alertDecodeError = () =>
+ Alert.alert(
+ 'Could not decode method with available metadata.',
+ 'Signing something you do not understand is inherently unsafe. Do not sign this extrinsic unless you know what you are doing, or update Parity Signer to be able to decode this message. If you are not sure, or you are using the latest version, please open an issue on github.com/paritytech/parity-signer.',
+ [
+ {
+ style: 'default',
+ text: 'Okay'
+ }
+ ]
+ );
+
+export const alertBackupDone = onPress =>
+ Alert.alert(
+ 'Important',
+ "Make sure you've backed up this recovery phrase. It is the only way to restore your account in case of device failure/lost.",
+ [
+ {
+ onPress,
+ text: 'Proceed'
+ },
+ {
+ style: 'cancel',
+ text: 'Cancel'
+ }
+ ]
+ );
diff --git a/src/util/array.js b/src/util/array.js
index bf1be73fcd..759939406a 100644
--- a/src/util/array.js
+++ b/src/util/array.js
@@ -1,3 +1,21 @@
+// Copyright 2015-2019 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+'use strict';
+
/**
* Default comparator, should work for strings and numbers
*/
@@ -34,7 +52,6 @@ export function binarySearch(array, item, compare = defaultCompare) {
let max = array.length - 1;
while (min <= max) {
- /* eslint-disable-next-line no-bitwise */
let guess = (min + max) >> 1; // fast integer division by 2
const result = compare(item, array[guess]);
diff --git a/src/util/db.js b/src/util/db.js
index a56522cce3..11b6a69a10 100644
--- a/src/util/db.js
+++ b/src/util/db.js
@@ -18,7 +18,13 @@
import { AsyncStorage } from 'react-native';
import SecureStorage from 'react-native-secure-storage';
-import { accountId } from './account';
+import { generateAccountId } from './account';
+import { deserializeIdentities, serializeIdentities } from './identitiesUtils';
+
+const currentAccountsStore = {
+ keychainService: 'accounts_v3',
+ sharedPreferencesName: 'accounts_v3'
+};
export async function loadAccounts(version = 3) {
if (!SecureStorage) {
@@ -43,13 +49,41 @@ export async function loadAccounts(version = 3) {
});
}
-const accountsStore = {
- keychainService: 'accounts_v3',
- sharedPreferencesName: 'accounts_v3'
+const identitiesStore = {
+ keychainService: 'parity_signer_identities',
+ sharedPreferencesName: 'parity_signer_identities'
+};
+const identityStorageLabel = 'identities_v4';
+
+export async function loadIdentities(version = 3) {
+ function handleError(e) {
+ console.warn('loading identities error', e);
+ return [];
+ }
+ try {
+ // TODO to be deleted before merging, used for clean the keychain.
+ // await SecureStorage.deleteItem(identityStorageLabel, identitiesStore);
+ const identities = await SecureStorage.getItem(
+ identityStorageLabel,
+ identitiesStore
+ );
+ if (!identities) return [];
+ return deserializeIdentities(identities);
+ } catch (e) {
+ handleError(e);
+ }
+}
+
+export const saveIdentities = identities => {
+ SecureStorage.setItem(
+ identityStorageLabel,
+ serializeIdentities(identities),
+ identitiesStore
+ );
};
function accountTxsKey({ address, networkKey }) {
- return 'account_txs_' + accountId({ address, networkKey });
+ return 'account_txs_' + generateAccountId({ address, networkKey });
}
function txKey(hash) {
@@ -57,13 +91,13 @@ function txKey(hash) {
}
export const deleteAccount = async accountKey =>
- SecureStorage.deleteItem(accountKey, accountsStore);
+ SecureStorage.deleteItem(accountKey, currentAccountsStore);
export const saveAccount = (accountKey, account) =>
SecureStorage.setItem(
accountKey,
JSON.stringify(account, null, 0),
- accountsStore
+ currentAccountsStore
);
export async function saveTx(tx) {
diff --git a/src/util/debounce.js b/src/util/debounce.js
index 5982832e33..b58e236d7e 100644
--- a/src/util/debounce.js
+++ b/src/util/debounce.js
@@ -1,3 +1,21 @@
+// Copyright 2015-2019 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+'use strict';
+
/**
* Creates and returns a new debounced version of the passed function that will
* postpone its execution until after wait milliseconds have elapsed since
diff --git a/src/util/decoders.js b/src/util/decoders.js
index 177cc08479..461eb94b35 100644
--- a/src/util/decoders.js
+++ b/src/util/decoders.js
@@ -16,6 +16,8 @@
// @flow
+'use strict';
+
import { GenericExtrinsicPayload } from '@polkadot/types';
import {
hexStripPrefix,
@@ -175,7 +177,6 @@ export async function constructDataFromBytes(bytes, multipartComplete = false) {
data.action = isOversized ? 'signData' : 'signTransaction';
data.oversized = isOversized;
data.isHash = isOversized;
- '';
data.data.data = isOversized
? await blake2b(
u8aToHex(extrinsicPayload.toU8a(true), -1, false)
diff --git a/src/util/identitiesUtils.js b/src/util/identitiesUtils.js
new file mode 100644
index 0000000000..d5993e91d3
--- /dev/null
+++ b/src/util/identitiesUtils.js
@@ -0,0 +1,193 @@
+// Copyright 2015-2019 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+'use strict';
+
+import {
+ NETWORK_LIST,
+ NetworkProtocols,
+ UnknownNetworkKeys
+} from '../constants';
+import { pathsRegex } from './regex';
+import { decryptData } from './native';
+import { parseSURI } from './suri';
+
+//walk around to fix the regular expression support for positive look behind;
+export const removeSlash = str => str.replace(/\//g, '');
+
+const extractPathId = path => {
+ const matchNetworkPath = path.match(pathsRegex.networkPath);
+ if (!matchNetworkPath) return null;
+ return removeSlash(matchNetworkPath[0]);
+};
+
+export const extractSubPathName = path => {
+ const pathFragments = path.match(pathsRegex.allPath);
+ if (!pathFragments || pathFragments.length <= 1) return '';
+ return removeSlash(pathFragments.slice(1).join(''));
+};
+
+export const isSubstratePath = path => path.split('//')[1] !== undefined;
+
+export function emptyIdentity() {
+ return {
+ accountIds: new Map(),
+ derivationPassword: '',
+ encryptedSeedPhrase: '',
+ meta: new Map(),
+ name: ''
+ };
+}
+
+export const serializeIdentity = identity =>
+ Object.entries(identity).reduce((newIdentity, entry) => {
+ let [key, value] = entry;
+ if (value instanceof Map) {
+ newIdentity[key] = Array.from(value.entries());
+ } else {
+ newIdentity[key] = value;
+ }
+ return newIdentity;
+ }, {});
+
+export const deserializeIdentity = identityJSON =>
+ Object.entries(identityJSON).reduce((newIdentity, entry) => {
+ let [key, value] = entry;
+ if (value instanceof Array) {
+ newIdentity[key] = new Map(value);
+ } else {
+ newIdentity[key] = value;
+ }
+ return newIdentity;
+ }, {});
+
+export const serializeIdentities = identities => {
+ const identitiesWithObject = identities.map(serializeIdentity);
+ return JSON.stringify(identitiesWithObject);
+};
+
+export const deserializeIdentities = identitiesJSON => {
+ const identitiesWithObject = JSON.parse(identitiesJSON);
+ return identitiesWithObject.map(deserializeIdentity);
+};
+
+export const deepCopyIdentities = identities =>
+ deserializeIdentities(serializeIdentities(identities));
+export const deepCopyIdentity = identity =>
+ deserializeIdentity(serializeIdentity(identity));
+
+export const getPathsWithSubstrateNetwork = (paths, networkKey) =>
+ paths.filter(path => extractPathId(path) === NETWORK_LIST[networkKey].pathId);
+
+export const getNetworkKeyByPath = path => {
+ if (!isSubstratePath(path) && NETWORK_LIST.hasOwnProperty(path)) {
+ return path;
+ }
+ const pathId = extractPathId(path);
+ if (!pathId) return UnknownNetworkKeys.UNKNOWN;
+
+ const networkKeyIndex = Object.values(NETWORK_LIST).findIndex(
+ networkParams => networkParams.pathId === pathId
+ );
+ if (networkKeyIndex !== -1) return Object.keys(NETWORK_LIST)[networkKeyIndex];
+
+ return UnknownNetworkKeys.UNKNOWN;
+};
+
+export const getIdentityFromSender = (sender, identities) =>
+ identities.find(i => i.encryptedSeed === sender.encryptedSeed);
+
+export const getAccountIdWithPath = (path, identity) => {
+ const pathMeta = identity.meta.get(path);
+ if (pathMeta && pathMeta.accountId) return pathMeta.accountId;
+ return '';
+};
+
+export const unlockIdentitySeed = async (pin, identity) => {
+ const { encryptedSeed } = identity;
+ const seed = await decryptData(encryptedSeed, pin);
+ const { phrase } = parseSURI(seed);
+ return phrase;
+};
+
+export const getAvailableNetworkKeys = identity => {
+ const accountIdsList = Array.from(identity.accountIds.values());
+ const networkKeysSet = accountIdsList.reduce((networksSet, path) => {
+ let networkKey;
+ if (isSubstratePath(path)) {
+ networkKey = getNetworkKeyByPath(path);
+ } else {
+ networkKey = path;
+ }
+ return { ...networksSet, [networkKey]: true };
+ }, {});
+ return Object.keys(networkKeysSet);
+};
+
+export const getAddressFromAccountId = (accountId, protocol) => {
+ if (!accountId) return '';
+ if (protocol === NetworkProtocols.SUBSTRATE) {
+ return accountId.split(':')[1] || accountId;
+ } else {
+ const withoutPrefix = accountId.split(':')[1] || accountId;
+ const withOut0x = withoutPrefix.split('0x')[1] || accountId;
+ return withOut0x.split('@')[0];
+ }
+};
+
+export const validateDerivedPath = derivedPath =>
+ pathsRegex.validateDerivedPath.test(derivedPath);
+
+export const getIdentityName = (identity, identities) => {
+ if (identity.name) return identity.name;
+ const identityIndex = identities.findIndex(
+ i => i.encryptedSeed === identity.encryptedSeed
+ );
+ return `Identity_${identityIndex}`;
+};
+
+export const getPathName = (path, lookUpIdentity) => {
+ if (
+ lookUpIdentity &&
+ lookUpIdentity.meta.has(path) &&
+ lookUpIdentity.meta.get(path).name !== ''
+ ) {
+ return lookUpIdentity.meta.get(path).name;
+ }
+ if (!isSubstratePath(path)) {
+ return 'No name';
+ }
+ return extractSubPathName(path);
+};
+
+export const groupPaths = paths => {
+ const unSortedPaths = paths.reduce((groupedPath, path) => {
+ const pathId = extractPathId(path) || '';
+ const subPath = path.slice(pathId.length + 2);
+
+ const groupName = subPath.match(pathsRegex.firstPath)[0];
+
+ const existedItem = groupedPath.find(p => p.title === groupName);
+ if (existedItem) {
+ existedItem.paths.push(path);
+ existedItem.paths.sort();
+ } else {
+ groupedPath.push({ paths: [path], title: groupName });
+ }
+ return groupedPath;
+ }, []);
+ return unSortedPaths.sort((a, b) => a.paths.length - b.paths.length);
+};
diff --git a/src/util/navigationHelpers.js b/src/util/navigationHelpers.js
new file mode 100644
index 0000000000..7d208db10f
--- /dev/null
+++ b/src/util/navigationHelpers.js
@@ -0,0 +1,120 @@
+// Copyright 2015-2019 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+'use strict';
+
+import { NavigationActions, StackActions } from 'react-navigation';
+
+export const setPin = async navigation =>
+ new Promise(resolve => {
+ navigation.navigate('IdentityPin', { isNew: true, resolve });
+ });
+
+export const unlockSeed = async (navigation, identity) =>
+ new Promise(resolve => {
+ navigation.navigate('IdentityPin', { identity, isUnlock: true, resolve });
+ });
+
+export const navigateToPathsList = (navigation, networkKey) => {
+ const resetAction = StackActions.reset({
+ actions: [
+ NavigationActions.navigate({
+ isNew: false,
+ routeName: 'AccountNetworkChooser'
+ }),
+ NavigationActions.navigate({
+ params: { networkKey },
+ routeName: 'PathsList'
+ })
+ ],
+ index: 1,
+ key: undefined
+ });
+ navigation.dispatch(resetAction);
+};
+
+export const navigateToLandingPage = (navigation, isSwitchOpen) => {
+ const resetAction = StackActions.reset({
+ actions: [
+ NavigationActions.navigate({
+ params: { isSwitchOpen },
+ routeName: 'AccountNetworkChooser'
+ })
+ ],
+ index: 0,
+ key: undefined
+ });
+ navigation.dispatch(resetAction);
+};
+
+export const navigateToNewIdentityNetwork = navigation => {
+ const resetAction = StackActions.reset({
+ actions: [
+ NavigationActions.navigate({
+ params: { isNew: true },
+ routeName: 'AccountNetworkChooser'
+ })
+ ],
+ index: 0,
+ key: undefined
+ });
+ navigation.dispatch(resetAction);
+};
+
+export const navigateToSignedMessage = navigation => {
+ const resetAction = StackActions.reset({
+ actions: [
+ NavigationActions.navigate({
+ isNew: false,
+ routeName: 'AccountNetworkChooser'
+ }),
+ NavigationActions.navigate({
+ params: { isNew: true },
+ routeName: 'SignedMessage'
+ })
+ ],
+ index: 1,
+ key: undefined
+ });
+ navigation.dispatch(resetAction);
+};
+
+export const navigateToSignedTx = navigation => {
+ const resetAction = StackActions.reset({
+ actions: [
+ NavigationActions.navigate({
+ isNew: false,
+ routeName: 'AccountNetworkChooser'
+ }),
+ NavigationActions.navigate({
+ params: { isNew: true },
+ routeName: 'SignedTx'
+ })
+ ],
+ index: 1,
+ key: undefined
+ });
+ navigation.dispatch(resetAction);
+};
+
+export const navigateToLegacyAccountList = navigation => {
+ const resetAction = StackActions.reset({
+ actions: [NavigationActions.navigate({ routeName: 'LegacyAccountList' })],
+ index: 0,
+ key: undefined
+ });
+ navigation.dispatch(resetAction);
+};
diff --git a/src/util/numbers.js b/src/util/numbers.js
index aa65643709..946fcdbdd4 100644
--- a/src/util/numbers.js
+++ b/src/util/numbers.js
@@ -14,6 +14,8 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see .
+'use strict';
+
/*
* @dev Modulo division that wraps on negative numbers because Javascript % does not by default.
*/
diff --git a/src/util/regex.js b/src/util/regex.js
new file mode 100644
index 0000000000..e4516723e0
--- /dev/null
+++ b/src/util/regex.js
@@ -0,0 +1,26 @@
+// Copyright 2015-2019 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+'use strict';
+
+export const pathsRegex = {
+ allPath: /(\/|\/\/)[\w-.]+(?=(\/?))/g,
+ firstPath: /(\/|\/\/)[\w-.]+(?=(\/)?)/,
+ networkPath: /(\/|\/\/)[\w-.]+(?=(\/)?)/,
+ validateDerivedPath: /^(\/\/?[\w-.]+)+$/
+};
+
+export const onlyNumberRegex = /^\d+$|^$/;
diff --git a/src/util/static-kusama.js b/src/util/static-kusama.js
index 3e2021261f..7008b6d0e0 100644
--- a/src/util/static-kusama.js
+++ b/src/util/static-kusama.js
@@ -14,6 +14,8 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see .
+'use strict';
+
const meta =
'0x6d657461086c1853797374656d00011c2866696c6c5f626c6f636b0004210120412062696720646973706174636820746861742077696c6c20646973616c6c6f7720616e79206f74686572207472616e73616374696f6e20746f20626520696e636c756465642e1872656d61726b041c5f72656d61726b144279746573046c204d616b6520736f6d65206f6e2d636861696e2072656d61726b2e387365745f686561705f7061676573041470616765730c75363404fc2053657420746865206e756d626572206f6620706167657320696e2074686520576562417373656d626c7920656e7669726f6e6d656e74277320686561702e207365745f636f6465040c6e657714427974657304482053657420746865206e657720636f64652e2c7365745f73746f7261676504146974656d73345665633c4b657956616c75653e046c2053657420736f6d65206974656d73206f662073746f726167652e306b696c6c5f73746f7261676504106b657973205665633c4b65793e0478204b696c6c20736f6d65206974656d732066726f6d2073746f726167652e2c6b696c6c5f70726566697804187072656669780c4b6579041501204b696c6c20616c6c2073746f72616765206974656d7320776974682061206b657920746861742073746172747320776974682074686520676976656e207072656669782e0000006052616e646f6d6e657373436f6c6c656374697665466c6970000000000010426162650001000000002454696d657374616d700001040c736574040c6e6f773c436f6d706163743c4d6f6d656e743e245820536574207468652063757272656e742074696d652e00590120546869732063616c6c2073686f756c6420626520696e766f6b65642065786163746c79206f6e63652070657220626c6f636b2e2049742077696c6c2070616e6963206174207468652066696e616c697a6174696f6ed82070686173652c20696620746869732063616c6c206861736e2774206265656e20696e766f6b656420627920746861742074696d652e004501205468652074696d657374616d702073686f756c642062652067726561746572207468616e207468652070726576696f7573206f6e652062792074686520616d6f756e74207370656369666965642062794420604d696e696d756d506572696f64602e00d820546865206469737061746368206f726967696e20666f7220746869732063616c6c206d7573742062652060496e686572656e74602e0000001c496e64696365730001000000002042616c616e63657300010c207472616e736665720810646573741c416464726573731476616c756540436f6d706163743c42616c616e63653e5cd8205472616e7366657220736f6d65206c697175696420667265652062616c616e636520746f20616e6f74686572206163636f756e742e00090120607472616e73666572602077696c6c207365742074686520604672656542616c616e636560206f66207468652073656e64657220616e642072656365697665722e21012049742077696c6c2064656372656173652074686520746f74616c2069737375616e6365206f66207468652073797374656d2062792074686520605472616e73666572466565602e1501204966207468652073656e6465722773206163636f756e742069732062656c6f7720746865206578697374656e7469616c206465706f736974206173206120726573756c74b4206f6620746865207472616e736665722c20746865206163636f756e742077696c6c206265207265617065642e00190120546865206469737061746368206f726967696e20666f7220746869732063616c6c206d75737420626520605369676e65646020627920746865207472616e736163746f722e002c2023203c7765696768743e3101202d20446570656e64656e74206f6e20617267756d656e747320627574206e6f7420637269746963616c2c20676976656e2070726f70657220696d706c656d656e746174696f6e7320666f72cc202020696e70757420636f6e6669672074797065732e205365652072656c617465642066756e6374696f6e732062656c6f772e6901202d20497420636f6e7461696e732061206c696d69746564206e756d626572206f6620726561647320616e642077726974657320696e7465726e616c6c7920616e64206e6f20636f6d706c657820636f6d7075746174696f6e2e004c2052656c617465642066756e6374696f6e733a0051012020202d2060656e737572655f63616e5f77697468647261776020697320616c776179732063616c6c656420696e7465726e616c6c792062757420686173206120626f756e64656420636f6d706c65786974792e2d012020202d205472616e7366657272696e672062616c616e63657320746f206163636f756e7473207468617420646964206e6f74206578697374206265666f72652077696c6c206361757365d420202020202060543a3a4f6e4e65774163636f756e743a3a6f6e5f6e65775f6163636f756e746020746f2062652063616c6c65642edc2020202d2052656d6f76696e6720656e6f7567682066756e64732066726f6d20616e206163636f756e742077696c6c20747269676765725901202020202060543a3a4475737452656d6f76616c3a3a6f6e5f756e62616c616e6365646020616e642060543a3a4f6e4672656542616c616e63655a65726f3a3a6f6e5f667265655f62616c616e63655f7a65726f602e00302023203c2f7765696768743e2c7365745f62616c616e63650c0c77686f1c41646472657373206e65775f6672656540436f6d706163743c42616c616e63653e306e65775f726573657276656440436f6d706163743c42616c616e63653e349420536574207468652062616c616e636573206f66206120676976656e206163636f756e742e00210120546869732077696c6c20616c74657220604672656542616c616e63656020616e642060526573657276656442616c616e63656020696e2073746f726167652e2069742077696c6c090120616c736f2064656372656173652074686520746f74616c2069737375616e6365206f66207468652073797374656d202860546f74616c49737375616e636560292e190120496620746865206e65772066726565206f722072657365727665642062616c616e63652069732062656c6f7720746865206578697374656e7469616c206465706f7369742ce82069742077696c6c20726573657420746865206163636f756e74206e6f6e636520286073797374656d3a3a4163636f756e744e6f6e636560292e00b420546865206469737061746368206f726967696e20666f7220746869732063616c6c2069732060726f6f74602e002c2023203c7765696768743e80202d20496e646570656e64656e74206f662074686520617267756d656e74732ec4202d20436f6e7461696e732061206c696d69746564206e756d626572206f6620726561647320616e64207772697465732e302023203c2f7765696768743e38666f7263655f7472616e736665720c18736f757263651c4164647265737310646573741c416464726573731476616c756540436f6d706163743c42616c616e63653e0851012045786163746c7920617320607472616e73666572602c2065786365707420746865206f726967696e206d75737420626520726f6f7420616e642074686520736f75726365206163636f756e74206d61792062652c207370656369666965642e000000485472616e73616374696f6e5061796d656e74000000000028417574686f7273686970000104287365745f756e636c657304286e65775f756e636c65732c5665633c4865616465723e04642050726f76696465206120736574206f6620756e636c65732e0000001c5374616b696e6700013c10626f6e640c28636f6e74726f6c6c65721c416464726573731476616c756548436f6d706163743c42616c616e63654f663e1470617965654452657761726444657374696e6174696f6e3c65012054616b6520746865206f726967696e206163636f756e74206173206120737461736820616e64206c6f636b207570206076616c756560206f66206974732062616c616e63652e2060636f6e74726f6c6c6572602077696c6c8420626520746865206163636f756e74207468617420636f6e74726f6c732069742e003101206076616c756560206d757374206265206d6f7265207468616e2074686520606d696e696d756d5f62616c616e636560207370656369666965642062792060543a3a43757272656e6379602e00250120546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f20627920746865207374617368206163636f756e742e002c2023203c7765696768743ed4202d20496e646570656e64656e74206f662074686520617267756d656e74732e204d6f64657261746520636f6d706c65786974792e20202d204f2831292e68202d20546872656520657874726120444220656e74726965732e006d01204e4f54453a2054776f206f66207468652073746f726167652077726974657320286053656c663a3a626f6e646564602c206053656c663a3a7061796565602920617265205f6e657665725f20636c65616e656420756e6c65737325012074686520606f726967696e602066616c6c732062656c6f77205f6578697374656e7469616c206465706f7369745f20616e6420676574732072656d6f76656420617320647573742e302023203c2f7765696768743e28626f6e645f657874726104386d61785f6164646974696f6e616c48436f6d706163743c42616c616e63654f663e3865012041646420736f6d6520657874726120616d6f756e742074686174206861766520617070656172656420696e207468652073746173682060667265655f62616c616e63656020696e746f207468652062616c616e63652075703420666f72207374616b696e672e00510120557365207468697320696620746865726520617265206164646974696f6e616c2066756e647320696e20796f7572207374617368206163636f756e74207468617420796f75207769736820746f20626f6e642e650120556e6c696b65205b60626f6e64605d206f72205b60756e626f6e64605d20746869732066756e6374696f6e20646f6573206e6f7420696d706f736520616e79206c696d69746174696f6e206f6e2074686520616d6f756e744c20746861742063616e2062652061646465642e00550120546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f206279207468652073746173682c206e6f742074686520636f6e74726f6c6c65722e002c2023203c7765696768743ee8202d20496e646570656e64656e74206f662074686520617267756d656e74732e20496e7369676e69666963616e7420636f6d706c65786974792e20202d204f2831292e40202d204f6e6520444220656e7472792e302023203c2f7765696768743e18756e626f6e64041476616c756548436f6d706163743c42616c616e63654f663e5c5501205363686564756c65206120706f7274696f6e206f662074686520737461736820746f20626520756e6c6f636b656420726561647920666f72207472616e73666572206f75742061667465722074686520626f6e64010120706572696f6420656e64732e2049662074686973206c656176657320616e20616d6f756e74206163746976656c7920626f6e646564206c657373207468616e250120543a3a43757272656e63793a3a6d696e696d756d5f62616c616e636528292c207468656e20697420697320696e6372656173656420746f207468652066756c6c20616d6f756e742e004901204f6e63652074686520756e6c6f636b20706572696f6420697320646f6e652c20796f752063616e2063616c6c206077697468647261775f756e626f6e6465646020746f2061637475616c6c79206d6f7665c0207468652066756e6473206f7574206f66206d616e6167656d656e7420726561647920666f72207472616e736665722e003d01204e6f206d6f7265207468616e2061206c696d69746564206e756d626572206f6620756e6c6f636b696e67206368756e6b73202873656520604d41585f554e4c4f434b494e475f4348554e4b5360293d012063616e20636f2d657869737473206174207468652073616d652074696d652e20496e207468617420636173652c205b6043616c6c3a3a77697468647261775f756e626f6e646564605d206e656564fc20746f2062652063616c6c656420666972737420746f2072656d6f766520736f6d65206f6620746865206368756e6b732028696620706f737369626c65292e00550120546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f2062792074686520636f6e74726f6c6c65722c206e6f74207468652073746173682e00982053656520616c736f205b6043616c6c3a3a77697468647261775f756e626f6e646564605d2e002c2023203c7765696768743e4101202d20496e646570656e64656e74206f662074686520617267756d656e74732e204c696d697465642062757420706f74656e7469616c6c79206578706c6f697461626c6520636f6d706c65786974792e98202d20436f6e7461696e732061206c696d69746564206e756d626572206f662072656164732e6501202d20456163682063616c6c20287265717569726573207468652072656d61696e646572206f662074686520626f6e6465642062616c616e636520746f2062652061626f766520606d696e696d756d5f62616c616e63656029710120202077696c6c2063617573652061206e657720656e74727920746f20626520696e73657274656420696e746f206120766563746f722028604c65646765722e756e6c6f636b696e676029206b65707420696e2073746f726167652ea501202020546865206f6e6c792077617920746f20636c65616e207468652061666f72656d656e74696f6e65642073746f72616765206974656d20697320616c736f20757365722d636f6e74726f6c6c656420766961206077697468647261775f756e626f6e646564602e40202d204f6e6520444220656e7472792e28203c2f7765696768743e4477697468647261775f756e626f6e64656400402d012052656d6f766520616e7920756e6c6f636b6564206368756e6b732066726f6d207468652060756e6c6f636b696e67602071756575652066726f6d206f7572206d616e6167656d656e742e003501205468697320657373656e7469616c6c7920667265657320757020746861742062616c616e636520746f206265207573656420627920746865207374617368206163636f756e7420746f20646f4c2077686174657665722069742077616e74732e00550120546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f2062792074686520636f6e74726f6c6c65722c206e6f74207468652073746173682e006c2053656520616c736f205b6043616c6c3a3a756e626f6e64605d2e002c2023203c7765696768743e5501202d20436f756c6420626520646570656e64656e74206f6e2074686520606f726967696e6020617267756d656e7420616e6420686f77206d7563682060756e6c6f636b696e6760206368756e6b732065786973742e45012020497420696d706c6965732060636f6e736f6c69646174655f756e6c6f636b656460207768696368206c6f6f7073206f76657220604c65646765722e756e6c6f636b696e67602c207768696368206973f42020696e6469726563746c7920757365722d636f6e74726f6c6c65642e20536565205b60756e626f6e64605d20666f72206d6f72652064657461696c2e7901202d20436f6e7461696e732061206c696d69746564206e756d626572206f662072656164732c20796574207468652073697a65206f6620776869636820636f756c64206265206c61726765206261736564206f6e20606c6564676572602ec8202d2057726974657320617265206c696d6974656420746f2074686520606f726967696e60206163636f756e74206b65792e302023203c2f7765696768743e2076616c6964617465041470726566733856616c696461746f7250726566732ce8204465636c617265207468652064657369726520746f2076616c696461746520666f7220746865206f726967696e20636f6e74726f6c6c65722e00dc20456666656374732077696c6c2062652066656c742061742074686520626567696e6e696e67206f6620746865206e657874206572612e00550120546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f2062792074686520636f6e74726f6c6c65722c206e6f74207468652073746173682e002c2023203c7765696768743ee8202d20496e646570656e64656e74206f662074686520617267756d656e74732e20496e7369676e69666963616e7420636f6d706c65786974792e98202d20436f6e7461696e732061206c696d69746564206e756d626572206f662072656164732ec8202d2057726974657320617265206c696d6974656420746f2074686520606f726967696e60206163636f756e74206b65792e302023203c2f7765696768743e206e6f6d696e617465041c74617267657473305665633c416464726573733e2c1101204465636c617265207468652064657369726520746f206e6f6d696e6174652060746172676574736020666f7220746865206f726967696e20636f6e74726f6c6c65722e00dc20456666656374732077696c6c2062652066656c742061742074686520626567696e6e696e67206f6620746865206e657874206572612e00550120546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f2062792074686520636f6e74726f6c6c65722c206e6f74207468652073746173682e002c2023203c7765696768743e2501202d20546865207472616e73616374696f6e277320636f6d706c65786974792069732070726f706f7274696f6e616c20746f207468652073697a65206f66206074617267657473602c982077686963682069732063617070656420617420604d41585f4e4f4d494e4154494f4e53602ed8202d20426f74682074686520726561647320616e642077726974657320666f6c6c6f7720612073696d696c6172207061747465726e2e302023203c2f7765696768743e146368696c6c002cc8204465636c617265206e6f2064657369726520746f206569746865722076616c6964617465206f72206e6f6d696e6174652e00dc20456666656374732077696c6c2062652066656c742061742074686520626567696e6e696e67206f6620746865206e657874206572612e00550120546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f2062792074686520636f6e74726f6c6c65722c206e6f74207468652073746173682e002c2023203c7765696768743ee8202d20496e646570656e64656e74206f662074686520617267756d656e74732e20496e7369676e69666963616e7420636f6d706c65786974792e54202d20436f6e7461696e73206f6e6520726561642ec8202d2057726974657320617265206c696d6974656420746f2074686520606f726967696e60206163636f756e74206b65792e302023203c2f7765696768743e247365745f7061796565041470617965654452657761726444657374696e6174696f6e2cb8202852652d2973657420746865207061796d656e742074617267657420666f72206120636f6e74726f6c6c65722e00dc20456666656374732077696c6c2062652066656c742061742074686520626567696e6e696e67206f6620746865206e657874206572612e00550120546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f2062792074686520636f6e74726f6c6c65722c206e6f74207468652073746173682e002c2023203c7765696768743ee8202d20496e646570656e64656e74206f662074686520617267756d656e74732e20496e7369676e69666963616e7420636f6d706c65786974792e98202d20436f6e7461696e732061206c696d69746564206e756d626572206f662072656164732ec8202d2057726974657320617265206c696d6974656420746f2074686520606f726967696e60206163636f756e74206b65792e302023203c2f7765696768743e387365745f636f6e74726f6c6c65720428636f6e74726f6c6c65721c416464726573732c90202852652d297365742074686520636f6e74726f6c6c6572206f6620612073746173682e00dc20456666656374732077696c6c2062652066656c742061742074686520626567696e6e696e67206f6620746865206e657874206572612e00550120546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f206279207468652073746173682c206e6f742074686520636f6e74726f6c6c65722e002c2023203c7765696768743ee8202d20496e646570656e64656e74206f662074686520617267756d656e74732e20496e7369676e69666963616e7420636f6d706c65786974792e98202d20436f6e7461696e732061206c696d69746564206e756d626572206f662072656164732ec8202d2057726974657320617265206c696d6974656420746f2074686520606f726967696e60206163636f756e74206b65792e302023203c2f7765696768743e4c7365745f76616c696461746f725f636f756e74040c6e657730436f6d706163743c7533323e04802054686520696465616c206e756d626572206f662076616c696461746f72732e34666f7263655f6e6f5f657261730014b020466f72636520746865726520746f206265206e6f206e6577206572617320696e646566696e6974656c792e002c2023203c7765696768743e40202d204e6f20617267756d656e74732e302023203c2f7765696768743e34666f7263655f6e65775f65726100184d0120466f72636520746865726520746f2062652061206e6577206572612061742074686520656e64206f6620746865206e6578742073657373696f6e2e20416674657220746869732c2069742077696c6c206265a020726573657420746f206e6f726d616c20286e6f6e2d666f7263656429206265686176696f75722e002c2023203c7765696768743e40202d204e6f20617267756d656e74732e302023203c2f7765696768743e447365745f696e76756c6e657261626c6573042876616c696461746f7273385665633c4163636f756e7449643e04cc20536574207468652076616c696461746f72732077686f2063616e6e6f7420626520736c61736865642028696620616e79292e34666f7263655f756e7374616b6504147374617368244163636f756e744964040d0120466f72636520612063757272656e74207374616b657220746f206265636f6d6520636f6d706c6574656c7920756e7374616b65642c20696d6d6564696174656c792e50666f7263655f6e65775f6572615f616c776179730014050120466f72636520746865726520746f2062652061206e6577206572612061742074686520656e64206f662073657373696f6e7320696e646566696e6974656c792e002c2023203c7765696768743e50202d204f6e652073746f72616765207772697465302023203c2f7765696768743e000000204f6666656e6365730001000000001c53657373696f6e000104207365745f6b65797308106b657973104b6579731470726f6f6614427974657328e42053657473207468652073657373696f6e206b6579287329206f66207468652066756e6374696f6e2063616c6c657220746f20606b6579602e210120416c6c6f777320616e206163636f756e7420746f20736574206974732073657373696f6e206b6579207072696f7220746f206265636f6d696e6720612076616c696461746f722ec4205468697320646f65736e27742074616b652065666665637420756e74696c20746865206e6578742073657373696f6e2e00d420546865206469737061746368206f726967696e206f6620746869732066756e6374696f6e206d757374206265207369676e65642e002c2023203c7765696768743e88202d204f286c6f67206e2920696e206e756d626572206f66206163636f756e74732e58202d204f6e6520657874726120444220656e7472792e302023203c2f7765696768743e0000003c46696e616c697479547261636b65720001042866696e616c5f68696e74041068696e7450436f6d706163743c426c6f636b4e756d6265723e08f42048696e7420746861742074686520617574686f72206f66207468697320626c6f636b207468696e6b732074686520626573742066696e616c697a65646c20626c6f636b2069732074686520676976656e206e756d6265722e0000001c4772616e647061000104487265706f72745f6d69736265686176696f72041c5f7265706f72741442797465730464205265706f727420736f6d65206d69736265686176696f722e00000020496d4f6e6c696e6500010424686561727462656174082468656172746265617424486561727462656174247369676e6174757265245369676e6174757265000000002444656d6f63726163790001441c70726f706f7365082070726f706f73616c2050726f706f73616c1476616c756548436f6d706163743c42616c616e63654f663e18a02050726f706f736520612073656e73697469766520616374696f6e20746f2062652074616b656e2e002c2023203c7765696768743e20202d204f2831292e80202d2054776f204442206368616e6765732c206f6e6520444220656e7472792e302023203c2f7765696768743e187365636f6e64042070726f706f73616c48436f6d706163743c50726f70496e6465783e18a02050726f706f736520612073656e73697469766520616374696f6e20746f2062652074616b656e2e002c2023203c7765696768743e20202d204f2831292e40202d204f6e6520444220656e7472792e302023203c2f7765696768743e10766f746508247265665f696e64657860436f6d706163743c5265666572656e64756d496e6465783e10766f746510566f74651c350120566f746520696e2061207265666572656e64756d2e2049662060766f74652e69735f6179652829602c2074686520766f746520697320746f20656e616374207468652070726f706f73616c3bbc206f7468657277697365206974206973206120766f746520746f206b65657020746865207374617475732071756f2e002c2023203c7765696768743e20202d204f2831292e7c202d204f6e65204442206368616e67652c206f6e6520444220656e7472792e302023203c2f7765696768743e2870726f78795f766f746508247265665f696e64657860436f6d706163743c5265666572656e64756d496e6465783e10766f746510566f74651c510120566f746520696e2061207265666572656e64756d206f6e20626568616c66206f6620612073746173682e2049662060766f74652e69735f6179652829602c2074686520766f746520697320746f20656e616374f8207468652070726f706f73616c3b20206f7468657277697365206974206973206120766f746520746f206b65657020746865207374617475732071756f2e002c2023203c7765696768743e20202d204f2831292e7c202d204f6e65204442206368616e67652c206f6e6520444220656e7472792e302023203c2f7765696768743e40656d657267656e63795f63616e63656c04247265665f696e6465783c5265666572656e64756d496e646578085101205363686564756c6520616e20656d657267656e63792063616e63656c6c6174696f6e206f662061207265666572656e64756d2e2043616e6e6f742068617070656e20747769636520746f207468652073616d6530207265666572656e64756d2e4065787465726e616c5f70726f706f7365042070726f706f73616c2050726f706f73616c083101205363686564756c652061207265666572656e64756d20746f206265207461626c6564206f6e6365206974206973206c6567616c20746f207363686564756c6520616e2065787465726e616c30207265666572656e64756d2e6465787465726e616c5f70726f706f73655f6d616a6f72697479042070726f706f73616c2050726f706f73616c145901205363686564756c652061206d616a6f726974792d63617272696573207265666572656e64756d20746f206265207461626c6564206e657874206f6e6365206974206973206c6567616c20746f207363686564756c656020616e2065787465726e616c207265666572656e64756d2e004d0120556e6c696b65206065787465726e616c5f70726f706f7365602c20626c61636b6c697374696e6720686173206e6f20656666656374206f6e207468697320616e64206974206d6179207265706c61636520619c207072652d7363686564756c6564206065787465726e616c5f70726f706f7365602063616c6c2e6065787465726e616c5f70726f706f73655f64656661756c74042070726f706f73616c2050726f706f73616c144901205363686564756c652061206e656761746976652d7475726e6f75742d62696173207265666572656e64756d20746f206265207461626c6564206e657874206f6e6365206974206973206c6567616c20746f84207363686564756c6520616e2065787465726e616c207265666572656e64756d2e004d0120556e6c696b65206065787465726e616c5f70726f706f7365602c20626c61636b6c697374696e6720686173206e6f20656666656374206f6e207468697320616e64206974206d6179207265706c61636520619c207072652d7363686564756c6564206065787465726e616c5f70726f706f7365602063616c6c2e28666173745f747261636b0c3470726f706f73616c5f68617368104861736834766f74696e675f706572696f642c426c6f636b4e756d6265721464656c61792c426c6f636b4e756d626572245101205363686564756c65207468652063757272656e746c792065787465726e616c6c792d70726f706f736564206d616a6f726974792d63617272696573207265666572656e64756d20746f206265207461626c6564650120696d6d6564696174656c792e204966207468657265206973206e6f2065787465726e616c6c792d70726f706f736564207265666572656e64756d2063757272656e746c792c206f72206966207468657265206973206f6e65ec20627574206974206973206e6f742061206d616a6f726974792d63617272696573207265666572656e64756d207468656e206974206661696c732e00f8202d206070726f706f73616c5f68617368603a205468652068617368206f66207468652063757272656e742065787465726e616c2070726f706f73616c2e6101202d2060766f74696e675f706572696f64603a2054686520706572696f64207468617420697320616c6c6f77656420666f7220766f74696e67206f6e20746869732070726f706f73616c2e20496e6372656173656420746f9820202060456d657267656e6379566f74696e67506572696f646020696620746f6f206c6f772e5501202d206064656c6179603a20546865206e756d626572206f6620626c6f636b20616674657220766f74696e672068617320656e64656420696e20617070726f76616c20616e6420746869732073686f756c64206265bc202020656e61637465642e205468697320646f65736e277420686176652061206d696e696d756d20616d6f756e742e347665746f5f65787465726e616c043470726f706f73616c5f68617368104861736804bc205665746f20616e6420626c61636b6c697374207468652065787465726e616c2070726f706f73616c20686173682e4463616e63656c5f7265666572656e64756d04247265665f696e64657860436f6d706163743c5265666572656e64756d496e6465783e04542052656d6f76652061207265666572656e64756d2e3463616e63656c5f7175657565640c107768656e50436f6d706163743c426c6f636b4e756d6265723e14776869636830436f6d706163743c7533323e107768617460436f6d706163743c5265666572656e64756d496e6465783e04a02043616e63656c20612070726f706f73616c2071756575656420666f7220656e6163746d656e742e247365745f70726f7879041470726f7879244163636f756e7449641498205370656369667920612070726f78792e2043616c6c6564206279207468652073746173682e002c2023203c7765696768743e58202d204f6e6520657874726120444220656e7472792e302023203c2f7765696768743e3072657369676e5f70726f787900149820436c656172207468652070726f78792e2043616c6c6564206279207468652070726f78792e002c2023203c7765696768743e40202d204f6e6520444220636c6561722e302023203c2f7765696768743e3072656d6f76655f70726f7879041470726f7879244163636f756e744964149820436c656172207468652070726f78792e2043616c6c6564206279207468652073746173682e002c2023203c7765696768743e40202d204f6e6520444220636c6561722e302023203c2f7765696768743e2064656c65676174650808746f244163636f756e74496428636f6e76696374696f6e28436f6e76696374696f6e143c2044656c656761746520766f74652e002c2023203c7765696768743e58202d204f6e6520657874726120444220656e7472792e302023203c2f7765696768743e28756e64656c656761746500144420556e64656c656761746520766f74652e002c2023203c7765696768743e20202d204f2831292e302023203c2f7765696768743e0000001c436f756e63696c0001102c7365745f6d656d62657273042c6e65775f6d656d62657273385665633c4163636f756e7449643e105101205365742074686520636f6c6c6563746976652773206d656d62657273686970206d616e75616c6c7920746f20606e65775f6d656d62657273602e204265206e69636520746f2074686520636861696e20616e645c2070726f76696465206974207072652d736f727465642e005820526571756972657320726f6f74206f726967696e2e1c65786563757465042070726f706f73616c2050726f706f73616c0cf420446973706174636820612070726f706f73616c2066726f6d2061206d656d626572207573696e672074686520604d656d62657260206f726967696e2e00ac204f726967696e206d7573742062652061206d656d626572206f662074686520636f6c6c6563746976652e1c70726f706f736508247468726573686f6c6450436f6d706163743c4d656d626572436f756e743e2070726f706f73616c2050726f706f73616c102c2023203c7765696768743e90202d20426f756e6465642073746f7261676520726561647320616e64207772697465732eb8202d20417267756d656e7420607468726573686f6c6460206861732062656172696e67206f6e207765696768742e302023203c2f7765696768743e10766f74650c2070726f706f73616c104861736814696e64657858436f6d706163743c50726f706f73616c496e6465783e1c617070726f766510626f6f6c102c2023203c7765696768743e8c202d20426f756e6465642073746f72616765207265616420616e64207772697465732e5501202d2057696c6c20626520736c696768746c792068656176696572206966207468652070726f706f73616c20697320617070726f766564202f20646973617070726f7665642061667465722074686520766f74652e302023203c2f7765696768743e00000048546563686e6963616c436f6d6d69747465650001102c7365745f6d656d62657273042c6e65775f6d656d62657273385665633c4163636f756e7449643e105101205365742074686520636f6c6c6563746976652773206d656d62657273686970206d616e75616c6c7920746f20606e65775f6d656d62657273602e204265206e69636520746f2074686520636861696e20616e645c2070726f76696465206974207072652d736f727465642e005820526571756972657320726f6f74206f726967696e2e1c65786563757465042070726f706f73616c2050726f706f73616c0cf420446973706174636820612070726f706f73616c2066726f6d2061206d656d626572207573696e672074686520604d656d62657260206f726967696e2e00ac204f726967696e206d7573742062652061206d656d626572206f662074686520636f6c6c6563746976652e1c70726f706f736508247468726573686f6c6450436f6d706163743c4d656d626572436f756e743e2070726f706f73616c2050726f706f73616c102c2023203c7765696768743e90202d20426f756e6465642073746f7261676520726561647320616e64207772697465732eb8202d20417267756d656e7420607468726573686f6c6460206861732062656172696e67206f6e207765696768742e302023203c2f7765696768743e10766f74650c2070726f706f73616c104861736814696e64657858436f6d706163743c50726f706f73616c496e6465783e1c617070726f766510626f6f6c102c2023203c7765696768743e8c202d20426f756e6465642073746f72616765207265616420616e64207772697465732e5501202d2057696c6c20626520736c696768746c792068656176696572206966207468652070726f706f73616c20697320617070726f766564202f20646973617070726f7665642061667465722074686520766f74652e302023203c2f7765696768743e00000044456c656374696f6e7350687261676d656e00011410766f74650814766f746573385665633c4163636f756e7449643e1476616c756548436f6d706163743c42616c616e63654f663e3c050120566f746520666f72206120736574206f662063616e6469646174657320666f7220746865207570636f6d696e6720726f756e64206f6620656c656374696f6e2e0050205468652060766f746573602073686f756c643a482020202d206e6f7420626520656d7074792eac2020202d206265206c657373207468616e20746865206e756d626572206f662063616e646964617465732e005d012055706f6e20766f74696e672c206076616c75656020756e697473206f66206077686f6027732062616c616e6365206973206c6f636b656420616e64206120626f6e6420616d6f756e742069732072657365727665642e5d012049742069732074686520726573706f6e736962696c697479206f66207468652063616c6c657220746f206e6f7420706c61636520616c6c206f662074686569722062616c616e636520696e746f20746865206c6f636ba020616e64206b65657020736f6d6520666f722066757274686572207472616e73616374696f6e732e002c2023203c7765696768743e2c2023232323205374617465302052656164733a204f283129c8205772697465733a204f28562920676976656e2060566020766f7465732e205620697320626f756e6465642062792031362e302023203c2f7765696768743e3072656d6f76655f766f746572001c21012052656d6f766520606f726967696e60206173206120766f7465722e20546869732072656d6f76657320746865206c6f636b20616e642072657475726e732074686520626f6e642e002c2023203c7765696768743e2c2023232323205374617465302052656164733a204f28312934205772697465733a204f283129302023203c2f7765696768743e507265706f72745f646566756e63745f766f74657204187461726765741c41646472657373345d01205265706f727420607461726765746020666f72206265696e6720616e20646566756e637420766f7465722e20496e2063617365206f6620612076616c6964207265706f72742c20746865207265706f727465722069735d012072657761726465642062792074686520626f6e6420616d6f756e74206f662060746172676574602e204f74686572776973652c20746865207265706f7274657220697473656c662069732072656d6f76656420616e645c20746865697220626f6e6420697320736c61736865642e0088204120646566756e637420766f74657220697320646566696e656420746f2062653a4d012020202d206120766f7465722077686f73652063757272656e74207375626d697474656420766f7465732061726520616c6c20696e76616c69642e20692e652e20616c6c206f66207468656d20617265206e6fb420202020206c6f6e67657220612063616e646964617465206e6f7220616e20616374697665206d656d6265722e002c2023203c7765696768743e2c202323232320537461746515012052656164733a204f284e4c6f674d2920676976656e204d2063757272656e742063616e6469646174657320616e64204e20766f74657320666f722060746172676574602e34205772697465733a204f283129302023203c2f7765696768743e407375626d69745f63616e646964616379003478205375626d6974206f6e6573656c6620666f722063616e6469646163792e006420412063616e6469646174652077696c6c206569746865723aec2020202d204c6f73652061742074686520656e64206f6620746865207465726d20616e6420666f7266656974207468656972206465706f7369742e2d012020202d2057696e20616e64206265636f6d652061206d656d6265722e204d656d626572732077696c6c206576656e7475616c6c7920676574207468656972207374617368206261636b2e55012020202d204265636f6d6520612072756e6e65722d75702e2052756e6e6572732d75707320617265207265736572766564206d656d6265727320696e2063617365206f6e65206765747320666f72636566756c6c7934202020202072656d6f7665642e002c2023203c7765696768743e2c20232323232053746174658c2052656164733a204f284c6f674e2920476976656e204e2063616e646964617465732e34205772697465733a204f283129302023203c2f7765696768743e3472656d6f76655f6d656d626572040c77686f1c416464726573733021012052656d6f7665206120706172746963756c6172206d656d6265722066726f6d20746865207365742e20546869732069732065666665637469766520696d6d6564696174656c792e00590120496620612072756e6e65722d757020697320617661696c61626c652c207468656e2074686520626573742072756e6e65722d75702077696c6c2062652072656d6f76656420616e64207265706c6163657320746865f4206f7574676f696e67206d656d6265722e204f74686572776973652c2061206e65772070687261676d656e20726f756e6420697320737461727465642e004501204e6f74652074686174207468697320646f6573206e6f7420616666656374207468652064657369676e6174656420626c6f636b206e756d626572206f6620746865206e65787420656c656374696f6e2e002c2023203c7765696768743e2c2023232323205374617465582052656164733a204f28646f5f70687261676d656e295c205772697465733a204f28646f5f70687261676d656e29302023203c2f7765696768743e0000004c546563686e6963616c4d656d62657273686970000110286164645f6d656d626572040c77686f244163636f756e7449640c7c204164642061206d656d626572206077686f6020746f20746865207365742e00b4204d6179206f6e6c792062652063616c6c65642066726f6d20604164644f726967696e60206f7220726f6f742e3472656d6f76655f6d656d626572040c77686f244163636f756e7449640c902052656d6f76652061206d656d626572206077686f602066726f6d20746865207365742e00c0204d6179206f6e6c792062652063616c6c65642066726f6d206052656d6f76654f726967696e60206f7220726f6f742e2c737761705f6d656d626572081872656d6f7665244163636f756e7449640c616464244163636f756e7449640cc02053776170206f7574206f6e65206d656d626572206072656d6f76656020666f7220616e6f746865722060616464602e00b8204d6179206f6e6c792062652063616c6c65642066726f6d2060537761704f726967696e60206f7220726f6f742e3472657365745f6d656d62657273041c6d656d62657273385665633c4163636f756e7449643e105901204368616e676520746865206d656d6265727368697020746f2061206e6577207365742c20646973726567617264696e6720746865206578697374696e67206d656d626572736869702e204265206e69636520616e646c207061737320606d656d6265727360207072652d736f727465642e00bc204d6179206f6e6c792062652063616c6c65642066726f6d206052657365744f726967696e60206f7220726f6f742e00000020547265617375727900010c3470726f706f73655f7370656e64081476616c756548436f6d706163743c42616c616e63654f663e2c62656e65666963696172791c41646472657373242d012050757420666f727761726420612073756767657374696f6e20666f72207370656e64696e672e2041206465706f7369742070726f706f7274696f6e616c20746f207468652076616c7565350120697320726573657276656420616e6420736c6173686564206966207468652070726f706f73616c2069732072656a65637465642e2049742069732072657475726e6564206f6e636520746865542070726f706f73616c20697320617761726465642e002c2023203c7765696768743e20202d204f2831292e64202d204c696d697465642073746f726167652072656164732e94202d204f6e65204442206368616e67652c206f6e6520657874726120444220656e7472792e302023203c2f7765696768743e3c72656a6563745f70726f706f73616c042c70726f706f73616c5f696458436f6d706163743c50726f706f73616c496e6465783e1cfc2052656a65637420612070726f706f736564207370656e642e20546865206f726967696e616c206465706f7369742077696c6c20626520736c61736865642e002c2023203c7765696768743e20202d204f2831292e64202d204c696d697465642073746f726167652072656164732e40202d204f6e6520444220636c6561722e302023203c2f7765696768743e40617070726f76655f70726f706f73616c042c70726f706f73616c5f696458436f6d706163743c50726f706f73616c496e6465783e205d0120417070726f766520612070726f706f73616c2e2041742061206c617465722074696d652c207468652070726f706f73616c2077696c6c20626520616c6c6f636174656420746f207468652062656e6566696369617279ac20616e6420746865206f726967696e616c206465706f7369742077696c6c2062652072657475726e65642e002c2023203c7765696768743e20202d204f2831292e64202d204c696d697465642073746f726167652072656164732e44202d204f6e65204442206368616e67652e302023203c2f7765696768743e00000018436c61696d7300010414636c61696d081064657374244163636f756e74496448657468657265756d5f7369676e61747572653845636473615369676e61747572650438204d616b65206120636c61696d2e0000002850617261636861696e73000104247365745f686561647304146865616473585665633c417474657374656443616e6469646174653e0415012050726f766964652063616e64696461746520726563656970747320666f722070617261636861696e732c20696e20617363656e64696e67206f726465722062792069642e000000304174746573746174696f6e73000104446d6f72655f6174746573746174696f6e7304145f6d6f7265404d6f72654174746573746174696f6e730415012050726f766964652063616e64696461746520726563656970747320666f722070617261636861696e732c20696e20617363656e64696e67206f726465722062792069642e00000014536c6f74730001182c6e65775f61756374696f6e08206475726174696f6e50436f6d706163743c426c6f636b4e756d6265723e486c656173655f706572696f645f696e64657858436f6d706163743c4c65617365506572696f644f663e1458204372656174652061206e65772061756374696f6e2e00550120546869732063616e206f6e6c792068617070656e207768656e2074686572652069736e277420616c726561647920616e2061756374696f6e20696e2070726f677265737320616e64206d6179206f6e6c7920626529012063616c6c65642062792074686520726f6f74206f726967696e2e20416363657074732074686520606475726174696f6e60206f6620746869732061756374696f6e20616e64207468655d0120606c656173655f706572696f645f696e64657860206f662074686520696e697469616c206c6561736520706572696f64206f662074686520666f757220746861742061726520746f2062652061756374696f6e65642e0c626964140c73756238436f6d706163743c53756249643e3461756374696f6e5f696e64657854436f6d706163743c41756374696f6e496e6465783e2866697273745f736c6f7458436f6d706163743c4c65617365506572696f644f663e246c6173745f736c6f7458436f6d706163743c4c65617365506572696f644f663e18616d6f756e7448436f6d706163743c42616c616e63654f663e404d01204d616b652061206e6577206269642066726f6d20616e206163636f756e742028696e636c7564696e6720612070617261636861696e206163636f756e742920666f72206465706c6f79696e672061206e65772c2070617261636861696e2e005d01204d756c7469706c652073696d756c74616e656f757320626964732066726f6d207468652073616d65206269646465722061726520616c6c6f776564206f6e6c79206173206c6f6e6720617320616c6c2061637469766541012062696473206f7665726c61702065616368206f746865722028692e652e20617265206d757475616c6c79206578636c7573697665292e20426964732063616e6e6f742062652072656461637465642e005901202d20607375626020697320746865207375622d6269646465722049442c20616c6c6f77696e6720666f72206d756c7469706c6520636f6d706574696e67206269647320746f206265206d6164652062792028616e64742066756e64656420627929207468652073616d65206163636f756e742e5101202d206061756374696f6e5f696e646578602069732074686520696e646578206f66207468652061756374696f6e20746f20626964206f6e2e2053686f756c64206a757374206265207468652070726573656e746c2076616c7565206f66206041756374696f6e436f756e746572602e4d01202d206066697273745f736c6f746020697320746865206669727374206c6561736520706572696f6420696e646578206f66207468652072616e676520746f20626964206f6e2e2054686973206973207468650d01206162736f6c757465206c6561736520706572696f6420696e6465782076616c75652c206e6f7420616e2061756374696f6e2d7370656369666963206f66667365742e4501202d20606c6173745f736c6f746020697320746865206c617374206c6561736520706572696f6420696e646578206f66207468652072616e676520746f20626964206f6e2e2054686973206973207468650d01206162736f6c757465206c6561736520706572696f6420696e6465782076616c75652c206e6f7420616e2061756374696f6e2d7370656369666963206f66667365742e4d01202d2060616d6f756e74602069732074686520616d6f756e7420746f2062696420746f2062652068656c64206173206465706f73697420666f72207468652070617261636861696e2073686f756c6420746865cc206269642077696e2e205468697320616d6f756e742069732068656c64207468726f7567686f7574207468652072616e67652e246269645f72656e6577103461756374696f6e5f696e64657854436f6d706163743c41756374696f6e496e6465783e2866697273745f736c6f7458436f6d706163743c4c65617365506572696f644f663e246c6173745f736c6f7458436f6d706163743c4c65617365506572696f644f663e18616d6f756e7448436f6d706163743c42616c616e63654f663e3c5101204d616b652061206e6577206269642066726f6d20612070617261636861696e206163636f756e7420666f722072656e6577696e67207468617420287072652d6578697374696e67292070617261636861696e2e00a820546865206f726967696e202a6d7573742a20626520612070617261636861696e206163636f756e742e005d01204d756c7469706c652073696d756c74616e656f757320626964732066726f6d207468652073616d65206269646465722061726520616c6c6f776564206f6e6c79206173206c6f6e6720617320616c6c2061637469766541012062696473206f7665726c61702065616368206f746865722028692e652e20617265206d757475616c6c79206578636c7573697665292e20426964732063616e6e6f742062652072656461637465642e005101202d206061756374696f6e5f696e646578602069732074686520696e646578206f66207468652061756374696f6e20746f20626964206f6e2e2053686f756c64206a757374206265207468652070726573656e746c2076616c7565206f66206041756374696f6e436f756e746572602e4d01202d206066697273745f736c6f746020697320746865206669727374206c6561736520706572696f6420696e646578206f66207468652072616e676520746f20626964206f6e2e2054686973206973207468650d01206162736f6c757465206c6561736520706572696f6420696e6465782076616c75652c206e6f7420616e2061756374696f6e2d7370656369666963206f66667365742e4501202d20606c6173745f736c6f746020697320746865206c617374206c6561736520706572696f6420696e646578206f66207468652072616e676520746f20626964206f6e2e2054686973206973207468650d01206162736f6c757465206c6561736520706572696f6420696e6465782076616c75652c206e6f7420616e2061756374696f6e2d7370656369666963206f66667365742e4d01202d2060616d6f756e74602069732074686520616d6f756e7420746f2062696420746f2062652068656c64206173206465706f73697420666f72207468652070617261636861696e2073686f756c6420746865cc206269642077696e2e205468697320616d6f756e742069732068656c64207468726f7567686f7574207468652072616e67652e3c7365745f6f6666626f617264696e670410646573741c4164647265737314c82053657420746865206f66662d626f617264696e6720696e666f726d6174696f6e20666f7220612070617261636861696e2e00a820546865206f726967696e202a6d7573742a20626520612070617261636861696e206163636f756e742e002101202d20606465737460206973207468652064657374696e6174696f6e206163636f756e7420746f2072656365697665207468652070617261636861696e2773206465706f7369742e3c6669785f6465706c6f795f64617461100c73756238436f6d706163743c53756249643e1c706172615f69643c436f6d706163743c5061726149643e24636f64655f68617368104861736844696e697469616c5f686561645f646174611442797465731c2d012053657420746865206465706c6f7920696e666f726d6174696f6e20666f722061207375636365737366756c2062696420746f206465706c6f792061206e65772070617261636861696e2e00c8202d20606f726967696e60206d75737420626520746865207375636365737366756c20626964646572206163636f756e742eb0202d20607375626020697320746865207375622d626964646572204944206f6620746865206269646465722e0101202d2060706172615f696460206973207468652070617261636861696e20494420616c6c6f7474656420746f207468652077696e6e696e67206269646465722e1d01202d2060636f64655f6861736860206973207468652068617368206f66207468652070617261636861696e2773205761736d2076616c69646174696f6e2066756e6374696f6e2ef0202d2060696e697469616c5f686561645f6461746160206973207468652070617261636861696e277320696e697469616c206865616420646174612e54656c61626f726174655f6465706c6f795f64617461081c706172615f69643c436f6d706163743c5061726149643e10636f64651442797465733074204e6f74652061206e65772070617261636861696e277320636f64652e004d012054686973206d7573742062652063616c6c656420616674657220606669785f6465706c6f795f646174616020616e642060636f646560206d7573742062652074686520707265696d616765206f6620746865c42060636f64655f68617368602070617373656420746865726520666f72207468652073616d652060706172615f6964602e0061012054686973206d61792062652063616c6c6564206265666f7265206f722061667465722074686520626567696e6e696e67206f66207468652070617261636861696e2773206669727374206c6561736520706572696f642e45012049662063616c6c6564206265666f7265207468656e207468652070617261636861696e2077696c6c206265636f6d65206163746976652061742074686520666972737420626c6f636b206f66206974736501207374617274696e67206c6561736520706572696f642e2049662061667465722c207468656e2069742077696c6c206265636f6d652061637469766520696d6d6564696174656c7920616674657220746869732063616c6c2e006c202d20605f6f726967696e6020697320697272656c6576616e742efc202d2060706172615f696460206973207468652070617261636861696e2049442077686f736520636f64652077696c6c20626520656c61626f72617465642e1501202d2060636f6465602069732074686520707265696d616765206f662074686520726567697374657265642060636f64655f6861736860206f662060706172615f6964602e0000002452656769737472617200011c3472656769737465725f70617261100869643c436f6d706163743c5061726149643e10696e666f2050617261496e666f10636f646514427974657344696e697469616c5f686561645f64617461144279746573089820526567697374657220612070617261636861696e207769746820676976656e20636f64652e8c204661696c7320696620676976656e20494420697320616c726561647920757365642e3c646572656769737465725f70617261040869643c436f6d706163743c5061726149643e0494204465726567697374657220612070617261636861696e207769746820676976656e206964407365745f7468726561645f636f756e740414636f756e740c75333214410120526573657420746865206e756d626572206f6620706172617468726561647320746861742063616e2070617920746f206265207363686564756c656420696e20612073696e676c6520626c6f636b2e0098202d2060636f756e74603a20546865206e756d626572206f662070617261746872656164732e0084204d7573742062652063616c6c65642066726f6d20526f6f74206f726967696e2e4c72656769737465725f706172617468726561640810636f646514427974657344696e697469616c5f686561645f6461746114427974657310a42052656769737465722061207061726174687265616420666f7220696d6d656469617465207573652e004d01204d7573742062652073656e742066726f6d2061205369676e6564206f726967696e20746861742069732061626c6520746f206861766520506172617468726561644465706f7369742072657365727665642e39012060636f64656020616e642060696e697469616c5f686561645f646174616020617265207573656420746f20696e697469616c697a6520746865207061726174687265616427732073746174652e4473656c6563745f706172617468726561640c0c5f69643c436f6d706163743c5061726149643e245f636f6c6c61746f7228436f6c6c61746f724964285f686561645f68617368104861736814050120506c61636520612062696420666f722061207061726174687265616420746f2062652070726f6772657373656420696e20746865206e65787420626c6f636b2e00410120546869732069732061206b696e64206f66207370656369616c207472616e73616374696f6e20746861742073686f756c642062792068656176696c79207072696f726974697a656420696e207468655d01207472616e73616374696f6e20706f6f6c206163636f7264696e6720746f20746865206076616c7565603b206f6e6c792060546872656164436f756e7460206f66207468656d206d61792062652070726573656e7465645420696e20616e792073696e676c6520626c6f636b2e54646572656769737465725f70617261746872656164001cc820446572656769737465722061207061726174687265616420616e6420726574726965766520746865206465706f7369742e002101204d7573742062652073656e742066726f6d2061206050617261636861696e60206f726967696e2077686963682069732063757272656e746c79206120706172617468726561642e00590120456e737572652074686174206265666f72652063616c6c696e672074686973207468617420616e792066756e647320796f752077616e7420656d70746965642066726f6d20746865207061726174687265616427734501206163636f756e74206973206d6f766564206f75743b20616674657220746869732069742077696c6c20626520696d706f737369626c6520746f207265747269657665207468656d2028776974686f75746820676f7665726e616e636520696e74657276656e74696f6e292e107377617004146f746865723c436f6d706163743c5061726149643e206501205377617020612070617261636861696e207769746820616e6f746865722070617261636861696e206f7220706172617468726561642e20546865206f726967696e206d7573742062652061206050617261636861696e602e65012054686520737761702077696c6c2068617070656e206f6e6c7920696620746865726520697320616c726561647920616e206f70706f7369746520737761702070656e64696e672e204966207468657265206973206e6f742c5d012074686520737761702077696c6c2062652073746f72656420696e207468652070656e64696e67207377617073206d61702c20726561647920666f722061206c6174657220636f6e6669726d61746f727920737761702e00610120546865206050617261496460732072656d61696e206d617070656420746f207468652073616d652068656164206461746120616e6420636f646520736f2065787465726e616c20636f64652063616e2072656c79206f6e410120605061726149646020746f2062652061206c6f6e672d7465726d206964656e746966696572206f662061206e6f74696f6e616c202270617261636861696e222e20486f77657665722c2074686569725901207363686564756c696e6720696e666f2028692e652e2077686574686572207468657927726520612070617261746872656164206f722070617261636861696e292c2061756374696f6e20696e666f726d6174696f6e9820616e64207468652061756374696f6e206465706f736974206172652073776974636865642e000000105375646f00010c107375646f042070726f706f73616c2050726f706f73616c2839012041757468656e7469636174657320746865207375646f206b657920616e64206469737061746368657320612066756e6374696f6e2063616c6c20776974682060526f6f7460206f726967696e2e00d020546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f2e002c2023203c7765696768743e20202d204f2831292e64202d204c696d697465642073746f726167652072656164732e60202d204f6e6520444220777269746520286576656e74292ed4202d20556e6b6e6f776e20776569676874206f662064657269766174697665206070726f706f73616c6020657865637574696f6e2e302023203c2f7765696768743e1c7365745f6b6579040c6e65771c416464726573732475012041757468656e74696361746573207468652063757272656e74207375646f206b657920616e6420736574732074686520676976656e204163636f756e7449642028606e6577602920617320746865206e6577207375646f206b65792e00d020546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f2e002c2023203c7765696768743e20202d204f2831292e64202d204c696d697465642073746f726167652072656164732e44202d204f6e65204442206368616e67652e302023203c2f7765696768743e1c7375646f5f6173080c77686f1c416464726573732070726f706f73616c2050726f706f73616c2c51012041757468656e7469636174657320746865207375646f206b657920616e64206469737061746368657320612066756e6374696f6e2063616c6c207769746820605369676e656460206f726967696e2066726f6d44206120676976656e206163636f756e742e00d020546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f2e002c2023203c7765696768743e20202d204f2831292e64202d204c696d697465642073746f726167652072656164732e60202d204f6e6520444220777269746520286576656e74292ed4202d20556e6b6e6f776e20776569676874206f662064657269766174697665206070726f706f73616c6020657865637574696f6e2e302023203c2f7765696768743e000000144e69636b73000110207365745f6e616d6504106e616d65144279746573405d012053657420616e206163636f756e742773206e616d652e20546865206e616d652073686f756c642062652061205554462d382d656e636f64656420737472696e6720627920636f6e76656e74696f6e2c2074686f7567684c20776520646f6e277420636865636b2069742e00610120546865206e616d65206d6179206e6f74206265206d6f7265207468616e2060543a3a4d61784c656e677468602062797465732c206e6f72206c657373207468616e2060543a3a4d696e4c656e677468602062797465732e005d0120496620746865206163636f756e7420646f65736e277420616c726561647920686176652061206e616d652c207468656e206120666565206f6620605265736572766174696f6e466565602069732072657365727665644020696e20746865206163636f756e742e00d020546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f2e002c2023203c7765696768743e20202d204f2831292e84202d204174206d6f7374206f6e652062616c616e6365206f7065726174696f6e2e68202d204f6e652073746f7261676520726561642f77726974652e34202d204f6e65206576656e742e302023203c2f7765696768743e28636c6561725f6e616d650028510120436c65617220616e206163636f756e742773206e616d6520616e642072657475726e20746865206465706f7369742e204661696c7320696620746865206163636f756e7420776173206e6f74206e616d65642e00d020546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f2e002c2023203c7765696768743e20202d204f2831292e64202d204f6e652062616c616e6365206f7065726174696f6e2e68202d204f6e652073746f7261676520726561642f77726974652e34202d204f6e65206576656e742e302023203c2f7765696768743e246b696c6c5f6e616d6504187461726765741c4164647265737334e42052656d6f766520616e206163636f756e742773206e616d6520616e642074616b6520636861726765206f6620746865206465706f7369742e004901204661696c73206966206077686f6020686173206e6f74206265656e206e616d65642e20546865206465706f736974206973206465616c742077697468207468726f7567682060543a3a536c6173686564604c20696d62616c616e63652068616e646c65722e00310120546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f526f6f745f206f72206d617463682060543a3a466f7263654f726967696e602e002c2023203c7765696768743e20202d204f2831292edc202d204f6e6520756e62616c616e6365642068616e646c6572202870726f6261626c7920612062616c616e6365207472616e736665722968202d204f6e652073746f7261676520726561642f77726974652e34202d204f6e65206576656e742e302023203c2f7765696768743e28666f7263655f6e616d6508187461726765741c41646472657373106e616d6514427974657330c82053657420612074686972642d7061727479206163636f756e742773206e616d652077697468206e6f206465706f7369742e00a0204e6f206c656e67746820636865636b696e6720697320646f6e65206f6e20746865206e616d652e00310120546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f526f6f745f206f72206d617463682060543a3a466f7263654f726967696e602e002c2023203c7765696768743e20202d204f2831292e84202d204174206d6f7374206f6e652062616c616e6365206f7065726174696f6e2e68202d204f6e652073746f7261676520726561642f77726974652e34202d204f6e65206576656e742e302023203c2f7765696768743e000000';
diff --git a/src/util/static-substrate.js b/src/util/static-substrate.js
index 05a234f5eb..eb2ff141c9 100644
--- a/src/util/static-substrate.js
+++ b/src/util/static-substrate.js
@@ -14,6 +14,8 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see .
+'use strict';
+
const meta =
'0x6d65746108601853797374656d00011c2866696c6c5f626c6f636b0004210120412062696720646973706174636820746861742077696c6c20646973616c6c6f7720616e79206f74686572207472616e73616374696f6e20746f20626520696e636c756465642e1872656d61726b041c5f72656d61726b144279746573046c204d616b6520736f6d65206f6e2d636861696e2072656d61726b2e387365745f686561705f7061676573041470616765730c75363404fc2053657420746865206e756d626572206f6620706167657320696e2074686520576562417373656d626c7920656e7669726f6e6d656e74277320686561702e207365745f636f6465040c6e657714427974657304482053657420746865206e657720636f64652e2c7365745f73746f7261676504146974656d73345665633c4b657956616c75653e046c2053657420736f6d65206974656d73206f662073746f726167652e306b696c6c5f73746f7261676504106b657973205665633c4b65793e0478204b696c6c20736f6d65206974656d732066726f6d2073746f726167652e2c6b696c6c5f70726566697804187072656669780c4b6579041501204b696c6c20616c6c2073746f72616765206974656d7320776974682061206b657920746861742073746172747320776974682074686520676976656e207072656669782e0000001c5574696c697479000104146261746368041463616c6c73245665633c43616c6c3e04b02053656e642061206261746368206f662064697370617463682063616c6c7320286f6e6c7920726f6f74292e00000010426162650001000000002454696d657374616d700001040c736574040c6e6f773c436f6d706163743c4d6f6d656e743e245820536574207468652063757272656e742074696d652e00590120546869732063616c6c2073686f756c6420626520696e766f6b65642065786163746c79206f6e63652070657220626c6f636b2e2049742077696c6c2070616e6963206174207468652066696e616c697a6174696f6ed82070686173652c20696620746869732063616c6c206861736e2774206265656e20696e766f6b656420627920746861742074696d652e004501205468652074696d657374616d702073686f756c642062652067726561746572207468616e207468652070726576696f7573206f6e652062792074686520616d6f756e74207370656369666965642062794420604d696e696d756d506572696f64602e00d820546865206469737061746368206f726967696e20666f7220746869732063616c6c206d7573742062652060496e686572656e74602e00000028417574686f7273686970000104287365745f756e636c657304286e65775f756e636c65732c5665633c4865616465723e04642050726f76696465206120736574206f6620756e636c65732e0000001c496e64696365730001000000002042616c616e63657300010c207472616e736665720810646573741c416464726573731476616c756540436f6d706163743c42616c616e63653e5cd8205472616e7366657220736f6d65206c697175696420667265652062616c616e636520746f20616e6f74686572206163636f756e742e00090120607472616e73666572602077696c6c207365742074686520604672656542616c616e636560206f66207468652073656e64657220616e642072656365697665722e21012049742077696c6c2064656372656173652074686520746f74616c2069737375616e6365206f66207468652073797374656d2062792074686520605472616e73666572466565602e1501204966207468652073656e6465722773206163636f756e742069732062656c6f7720746865206578697374656e7469616c206465706f736974206173206120726573756c74b4206f6620746865207472616e736665722c20746865206163636f756e742077696c6c206265207265617065642e00190120546865206469737061746368206f726967696e20666f7220746869732063616c6c206d75737420626520605369676e65646020627920746865207472616e736163746f722e002c2023203c7765696768743e3101202d20446570656e64656e74206f6e20617267756d656e747320627574206e6f7420637269746963616c2c20676976656e2070726f70657220696d706c656d656e746174696f6e7320666f72cc202020696e70757420636f6e6669672074797065732e205365652072656c617465642066756e6374696f6e732062656c6f772e6901202d20497420636f6e7461696e732061206c696d69746564206e756d626572206f6620726561647320616e642077726974657320696e7465726e616c6c7920616e64206e6f20636f6d706c657820636f6d7075746174696f6e2e004c2052656c617465642066756e6374696f6e733a0051012020202d2060656e737572655f63616e5f77697468647261776020697320616c776179732063616c6c656420696e7465726e616c6c792062757420686173206120626f756e64656420636f6d706c65786974792e2d012020202d205472616e7366657272696e672062616c616e63657320746f206163636f756e7473207468617420646964206e6f74206578697374206265666f72652077696c6c206361757365d420202020202060543a3a4f6e4e65774163636f756e743a3a6f6e5f6e65775f6163636f756e746020746f2062652063616c6c65642edc2020202d2052656d6f76696e6720656e6f7567682066756e64732066726f6d20616e206163636f756e742077696c6c20747269676765725901202020202060543a3a4475737452656d6f76616c3a3a6f6e5f756e62616c616e6365646020616e642060543a3a4f6e4672656542616c616e63655a65726f3a3a6f6e5f667265655f62616c616e63655f7a65726f602e00302023203c2f7765696768743e2c7365745f62616c616e63650c0c77686f1c41646472657373206e65775f6672656540436f6d706163743c42616c616e63653e306e65775f726573657276656440436f6d706163743c42616c616e63653e349420536574207468652062616c616e636573206f66206120676976656e206163636f756e742e00210120546869732077696c6c20616c74657220604672656542616c616e63656020616e642060526573657276656442616c616e63656020696e2073746f726167652e2069742077696c6c090120616c736f2064656372656173652074686520746f74616c2069737375616e6365206f66207468652073797374656d202860546f74616c49737375616e636560292e190120496620746865206e65772066726565206f722072657365727665642062616c616e63652069732062656c6f7720746865206578697374656e7469616c206465706f7369742ce82069742077696c6c20726573657420746865206163636f756e74206e6f6e636520286073797374656d3a3a4163636f756e744e6f6e636560292e00b420546865206469737061746368206f726967696e20666f7220746869732063616c6c2069732060726f6f74602e002c2023203c7765696768743e80202d20496e646570656e64656e74206f662074686520617267756d656e74732ec4202d20436f6e7461696e732061206c696d69746564206e756d626572206f6620726561647320616e64207772697465732e302023203c2f7765696768743e38666f7263655f7472616e736665720c18736f757263651c4164647265737310646573741c416464726573731476616c756540436f6d706163743c42616c616e63653e0851012045786163746c7920617320607472616e73666572602c2065786365707420746865206f726967696e206d75737420626520726f6f7420616e642074686520736f75726365206163636f756e74206d61792062652c207370656369666965642e000000485472616e73616374696f6e5061796d656e7400000000001c5374616b696e6700013810626f6e640c28636f6e74726f6c6c65721c416464726573731476616c756548436f6d706163743c42616c616e63654f663e1470617965654452657761726444657374696e6174696f6e3c65012054616b6520746865206f726967696e206163636f756e74206173206120737461736820616e64206c6f636b207570206076616c756560206f66206974732062616c616e63652e2060636f6e74726f6c6c6572602077696c6c8420626520746865206163636f756e74207468617420636f6e74726f6c732069742e003101206076616c756560206d757374206265206d6f7265207468616e2074686520606d696e696d756d5f62616c616e636560207370656369666965642062792060543a3a43757272656e6379602e00250120546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f20627920746865207374617368206163636f756e742e002c2023203c7765696768743ed4202d20496e646570656e64656e74206f662074686520617267756d656e74732e204d6f64657261746520636f6d706c65786974792e20202d204f2831292e68202d20546872656520657874726120444220656e74726965732e006d01204e4f54453a2054776f206f66207468652073746f726167652077726974657320286053656c663a3a626f6e646564602c206053656c663a3a7061796565602920617265205f6e657665725f20636c65616e656420756e6c65737325012074686520606f726967696e602066616c6c732062656c6f77205f6578697374656e7469616c206465706f7369745f20616e6420676574732072656d6f76656420617320647573742e302023203c2f7765696768743e28626f6e645f657874726104386d61785f6164646974696f6e616c48436f6d706163743c42616c616e63654f663e3865012041646420736f6d6520657874726120616d6f756e742074686174206861766520617070656172656420696e207468652073746173682060667265655f62616c616e63656020696e746f207468652062616c616e63652075703420666f72207374616b696e672e00510120557365207468697320696620746865726520617265206164646974696f6e616c2066756e647320696e20796f7572207374617368206163636f756e74207468617420796f75207769736820746f20626f6e642e650120556e6c696b65205b60626f6e64605d206f72205b60756e626f6e64605d20746869732066756e6374696f6e20646f6573206e6f7420696d706f736520616e79206c696d69746174696f6e206f6e2074686520616d6f756e744c20746861742063616e2062652061646465642e00550120546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f206279207468652073746173682c206e6f742074686520636f6e74726f6c6c65722e002c2023203c7765696768743ee8202d20496e646570656e64656e74206f662074686520617267756d656e74732e20496e7369676e69666963616e7420636f6d706c65786974792e20202d204f2831292e40202d204f6e6520444220656e7472792e302023203c2f7765696768743e18756e626f6e64041476616c756548436f6d706163743c42616c616e63654f663e5c5501205363686564756c65206120706f7274696f6e206f662074686520737461736820746f20626520756e6c6f636b656420726561647920666f72207472616e73666572206f75742061667465722074686520626f6e64010120706572696f6420656e64732e2049662074686973206c656176657320616e20616d6f756e74206163746976656c7920626f6e646564206c657373207468616e250120543a3a43757272656e63793a3a6d696e696d756d5f62616c616e636528292c207468656e20697420697320696e6372656173656420746f207468652066756c6c20616d6f756e742e004901204f6e63652074686520756e6c6f636b20706572696f6420697320646f6e652c20796f752063616e2063616c6c206077697468647261775f756e626f6e6465646020746f2061637475616c6c79206d6f7665c0207468652066756e6473206f7574206f66206d616e6167656d656e7420726561647920666f72207472616e736665722e003d01204e6f206d6f7265207468616e2061206c696d69746564206e756d626572206f6620756e6c6f636b696e67206368756e6b73202873656520604d41585f554e4c4f434b494e475f4348554e4b5360293d012063616e20636f2d657869737473206174207468652073616d652074696d652e20496e207468617420636173652c205b6043616c6c3a3a77697468647261775f756e626f6e646564605d206e656564fc20746f2062652063616c6c656420666972737420746f2072656d6f766520736f6d65206f6620746865206368756e6b732028696620706f737369626c65292e00550120546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f2062792074686520636f6e74726f6c6c65722c206e6f74207468652073746173682e00982053656520616c736f205b6043616c6c3a3a77697468647261775f756e626f6e646564605d2e002c2023203c7765696768743e4101202d20496e646570656e64656e74206f662074686520617267756d656e74732e204c696d697465642062757420706f74656e7469616c6c79206578706c6f697461626c6520636f6d706c65786974792e98202d20436f6e7461696e732061206c696d69746564206e756d626572206f662072656164732e6501202d20456163682063616c6c20287265717569726573207468652072656d61696e646572206f662074686520626f6e6465642062616c616e636520746f2062652061626f766520606d696e696d756d5f62616c616e63656029710120202077696c6c2063617573652061206e657720656e74727920746f20626520696e73657274656420696e746f206120766563746f722028604c65646765722e756e6c6f636b696e676029206b65707420696e2073746f726167652ea501202020546865206f6e6c792077617920746f20636c65616e207468652061666f72656d656e74696f6e65642073746f72616765206974656d20697320616c736f20757365722d636f6e74726f6c6c656420766961206077697468647261775f756e626f6e646564602e40202d204f6e6520444220656e7472792e28203c2f7765696768743e4477697468647261775f756e626f6e64656400402d012052656d6f766520616e7920756e6c6f636b6564206368756e6b732066726f6d207468652060756e6c6f636b696e67602071756575652066726f6d206f7572206d616e6167656d656e742e003501205468697320657373656e7469616c6c7920667265657320757020746861742062616c616e636520746f206265207573656420627920746865207374617368206163636f756e7420746f20646f4c2077686174657665722069742077616e74732e00550120546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f2062792074686520636f6e74726f6c6c65722c206e6f74207468652073746173682e006c2053656520616c736f205b6043616c6c3a3a756e626f6e64605d2e002c2023203c7765696768743e5501202d20436f756c6420626520646570656e64656e74206f6e2074686520606f726967696e6020617267756d656e7420616e6420686f77206d7563682060756e6c6f636b696e6760206368756e6b732065786973742e45012020497420696d706c6965732060636f6e736f6c69646174655f756e6c6f636b656460207768696368206c6f6f7073206f76657220604c65646765722e756e6c6f636b696e67602c207768696368206973f42020696e6469726563746c7920757365722d636f6e74726f6c6c65642e20536565205b60756e626f6e64605d20666f72206d6f72652064657461696c2e7901202d20436f6e7461696e732061206c696d69746564206e756d626572206f662072656164732c20796574207468652073697a65206f6620776869636820636f756c64206265206c61726765206261736564206f6e20606c6564676572602ec8202d2057726974657320617265206c696d6974656420746f2074686520606f726967696e60206163636f756e74206b65792e302023203c2f7765696768743e2076616c6964617465041470726566733856616c696461746f7250726566732ce8204465636c617265207468652064657369726520746f2076616c696461746520666f7220746865206f726967696e20636f6e74726f6c6c65722e00dc20456666656374732077696c6c2062652066656c742061742074686520626567696e6e696e67206f6620746865206e657874206572612e00550120546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f2062792074686520636f6e74726f6c6c65722c206e6f74207468652073746173682e002c2023203c7765696768743ee8202d20496e646570656e64656e74206f662074686520617267756d656e74732e20496e7369676e69666963616e7420636f6d706c65786974792e98202d20436f6e7461696e732061206c696d69746564206e756d626572206f662072656164732ec8202d2057726974657320617265206c696d6974656420746f2074686520606f726967696e60206163636f756e74206b65792e302023203c2f7765696768743e206e6f6d696e617465041c74617267657473305665633c416464726573733e2c1101204465636c617265207468652064657369726520746f206e6f6d696e6174652060746172676574736020666f7220746865206f726967696e20636f6e74726f6c6c65722e00dc20456666656374732077696c6c2062652066656c742061742074686520626567696e6e696e67206f6620746865206e657874206572612e00550120546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f2062792074686520636f6e74726f6c6c65722c206e6f74207468652073746173682e002c2023203c7765696768743e2501202d20546865207472616e73616374696f6e277320636f6d706c65786974792069732070726f706f7274696f6e616c20746f207468652073697a65206f66206074617267657473602c982077686963682069732063617070656420617420604d41585f4e4f4d494e4154494f4e53602ed8202d20426f74682074686520726561647320616e642077726974657320666f6c6c6f7720612073696d696c6172207061747465726e2e302023203c2f7765696768743e146368696c6c002cc8204465636c617265206e6f2064657369726520746f206569746865722076616c6964617465206f72206e6f6d696e6174652e00dc20456666656374732077696c6c2062652066656c742061742074686520626567696e6e696e67206f6620746865206e657874206572612e00550120546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f2062792074686520636f6e74726f6c6c65722c206e6f74207468652073746173682e002c2023203c7765696768743ee8202d20496e646570656e64656e74206f662074686520617267756d656e74732e20496e7369676e69666963616e7420636f6d706c65786974792e54202d20436f6e7461696e73206f6e6520726561642ec8202d2057726974657320617265206c696d6974656420746f2074686520606f726967696e60206163636f756e74206b65792e302023203c2f7765696768743e247365745f7061796565041470617965654452657761726444657374696e6174696f6e2cb8202852652d2973657420746865207061796d656e742074617267657420666f72206120636f6e74726f6c6c65722e00dc20456666656374732077696c6c2062652066656c742061742074686520626567696e6e696e67206f6620746865206e657874206572612e00550120546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f2062792074686520636f6e74726f6c6c65722c206e6f74207468652073746173682e002c2023203c7765696768743ee8202d20496e646570656e64656e74206f662074686520617267756d656e74732e20496e7369676e69666963616e7420636f6d706c65786974792e98202d20436f6e7461696e732061206c696d69746564206e756d626572206f662072656164732ec8202d2057726974657320617265206c696d6974656420746f2074686520606f726967696e60206163636f756e74206b65792e302023203c2f7765696768743e387365745f636f6e74726f6c6c65720428636f6e74726f6c6c65721c416464726573732c90202852652d297365742074686520636f6e74726f6c6c6572206f6620612073746173682e00dc20456666656374732077696c6c2062652066656c742061742074686520626567696e6e696e67206f6620746865206e657874206572612e00550120546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f206279207468652073746173682c206e6f742074686520636f6e74726f6c6c65722e002c2023203c7765696768743ee8202d20496e646570656e64656e74206f662074686520617267756d656e74732e20496e7369676e69666963616e7420636f6d706c65786974792e98202d20436f6e7461696e732061206c696d69746564206e756d626572206f662072656164732ec8202d2057726974657320617265206c696d6974656420746f2074686520606f726967696e60206163636f756e74206b65792e302023203c2f7765696768743e4c7365745f76616c696461746f725f636f756e74040c6e657730436f6d706163743c7533323e04802054686520696465616c206e756d626572206f662076616c696461746f72732e34666f7263655f6e6f5f657261730014b020466f72636520746865726520746f206265206e6f206e6577206572617320696e646566696e6974656c792e002c2023203c7765696768743e40202d204e6f20617267756d656e74732e302023203c2f7765696768743e34666f7263655f6e65775f65726100184d0120466f72636520746865726520746f2062652061206e6577206572612061742074686520656e64206f6620746865206e6578742073657373696f6e2e20416674657220746869732c2069742077696c6c206265a020726573657420746f206e6f726d616c20286e6f6e2d666f7263656429206265686176696f75722e002c2023203c7765696768743e40202d204e6f20617267756d656e74732e302023203c2f7765696768743e447365745f696e76756c6e657261626c6573042876616c696461746f7273385665633c4163636f756e7449643e04cc20536574207468652076616c696461746f72732077686f2063616e6e6f7420626520736c61736865642028696620616e79292e34666f7263655f756e7374616b6504147374617368244163636f756e744964040d0120466f72636520612063757272656e74207374616b657220746f206265636f6d6520636f6d706c6574656c7920756e7374616b65642c20696d6d6564696174656c792e0000001c53657373696f6e000104207365745f6b65797308106b657973104b6579731470726f6f6614427974657328e42053657473207468652073657373696f6e206b6579287329206f66207468652066756e6374696f6e2063616c6c657220746f20606b6579602e210120416c6c6f777320616e206163636f756e7420746f20736574206974732073657373696f6e206b6579207072696f7220746f206265636f6d696e6720612076616c696461746f722ec4205468697320646f65736e27742074616b652065666665637420756e74696c20746865206e6578742073657373696f6e2e00d420546865206469737061746368206f726967696e206f6620746869732066756e6374696f6e206d757374206265207369676e65642e002c2023203c7765696768743e88202d204f286c6f67206e2920696e206e756d626572206f66206163636f756e74732e58202d204f6e6520657874726120444220656e7472792e302023203c2f7765696768743e0000002444656d6f63726163790001441c70726f706f7365082070726f706f73616c2050726f706f73616c1476616c756548436f6d706163743c42616c616e63654f663e18a02050726f706f736520612073656e73697469766520616374696f6e20746f2062652074616b656e2e002c2023203c7765696768743e20202d204f2831292e80202d2054776f204442206368616e6765732c206f6e6520444220656e7472792e302023203c2f7765696768743e187365636f6e64042070726f706f73616c48436f6d706163743c50726f70496e6465783e18a02050726f706f736520612073656e73697469766520616374696f6e20746f2062652074616b656e2e002c2023203c7765696768743e20202d204f2831292e40202d204f6e6520444220656e7472792e302023203c2f7765696768743e10766f746508247265665f696e64657860436f6d706163743c5265666572656e64756d496e6465783e10766f746510566f74651c350120566f746520696e2061207265666572656e64756d2e2049662060766f74652e69735f6179652829602c2074686520766f746520697320746f20656e616374207468652070726f706f73616c3bbc206f7468657277697365206974206973206120766f746520746f206b65657020746865207374617475732071756f2e002c2023203c7765696768743e20202d204f2831292e7c202d204f6e65204442206368616e67652c206f6e6520444220656e7472792e302023203c2f7765696768743e2870726f78795f766f746508247265665f696e64657860436f6d706163743c5265666572656e64756d496e6465783e10766f746510566f74651c510120566f746520696e2061207265666572656e64756d206f6e20626568616c66206f6620612073746173682e2049662060766f74652e69735f6179652829602c2074686520766f746520697320746f20656e616374f8207468652070726f706f73616c3b20206f7468657277697365206974206973206120766f746520746f206b65657020746865207374617475732071756f2e002c2023203c7765696768743e20202d204f2831292e7c202d204f6e65204442206368616e67652c206f6e6520444220656e7472792e302023203c2f7765696768743e40656d657267656e63795f63616e63656c04247265665f696e6465783c5265666572656e64756d496e646578085101205363686564756c6520616e20656d657267656e63792063616e63656c6c6174696f6e206f662061207265666572656e64756d2e2043616e6e6f742068617070656e20747769636520746f207468652073616d6530207265666572656e64756d2e4065787465726e616c5f70726f706f7365042070726f706f73616c2050726f706f73616c083101205363686564756c652061207265666572656e64756d20746f206265207461626c6564206f6e6365206974206973206c6567616c20746f207363686564756c6520616e2065787465726e616c30207265666572656e64756d2e6465787465726e616c5f70726f706f73655f6d616a6f72697479042070726f706f73616c2050726f706f73616c145901205363686564756c652061206d616a6f726974792d63617272696573207265666572656e64756d20746f206265207461626c6564206e657874206f6e6365206974206973206c6567616c20746f207363686564756c656020616e2065787465726e616c207265666572656e64756d2e004d0120556e6c696b65206065787465726e616c5f70726f706f7365602c20626c61636b6c697374696e6720686173206e6f20656666656374206f6e207468697320616e64206974206d6179207265706c61636520619c207072652d7363686564756c6564206065787465726e616c5f70726f706f7365602063616c6c2e6065787465726e616c5f70726f706f73655f64656661756c74042070726f706f73616c2050726f706f73616c144901205363686564756c652061206e656761746976652d7475726e6f75742d62696173207265666572656e64756d20746f206265207461626c6564206e657874206f6e6365206974206973206c6567616c20746f84207363686564756c6520616e2065787465726e616c207265666572656e64756d2e004d0120556e6c696b65206065787465726e616c5f70726f706f7365602c20626c61636b6c697374696e6720686173206e6f20656666656374206f6e207468697320616e64206974206d6179207265706c61636520619c207072652d7363686564756c6564206065787465726e616c5f70726f706f7365602063616c6c2e28666173745f747261636b0c3470726f706f73616c5f68617368104861736834766f74696e675f706572696f642c426c6f636b4e756d6265721464656c61792c426c6f636b4e756d626572245101205363686564756c65207468652063757272656e746c792065787465726e616c6c792d70726f706f736564206d616a6f726974792d63617272696573207265666572656e64756d20746f206265207461626c6564650120696d6d6564696174656c792e204966207468657265206973206e6f2065787465726e616c6c792d70726f706f736564207265666572656e64756d2063757272656e746c792c206f72206966207468657265206973206f6e65ec20627574206974206973206e6f742061206d616a6f726974792d63617272696573207265666572656e64756d207468656e206974206661696c732e00f8202d206070726f706f73616c5f68617368603a205468652068617368206f66207468652063757272656e742065787465726e616c2070726f706f73616c2e6101202d2060766f74696e675f706572696f64603a2054686520706572696f64207468617420697320616c6c6f77656420666f7220766f74696e67206f6e20746869732070726f706f73616c2e20496e6372656173656420746f9820202060456d657267656e6379566f74696e67506572696f646020696620746f6f206c6f772e5501202d206064656c6179603a20546865206e756d626572206f6620626c6f636b20616674657220766f74696e672068617320656e64656420696e20617070726f76616c20616e6420746869732073686f756c64206265bc202020656e61637465642e205468697320646f65736e277420686176652061206d696e696d756d20616d6f756e742e347665746f5f65787465726e616c043470726f706f73616c5f68617368104861736804bc205665746f20616e6420626c61636b6c697374207468652065787465726e616c2070726f706f73616c20686173682e4463616e63656c5f7265666572656e64756d04247265665f696e64657860436f6d706163743c5265666572656e64756d496e6465783e04542052656d6f76652061207265666572656e64756d2e3463616e63656c5f7175657565640c107768656e50436f6d706163743c426c6f636b4e756d6265723e14776869636830436f6d706163743c7533323e107768617460436f6d706163743c5265666572656e64756d496e6465783e04a02043616e63656c20612070726f706f73616c2071756575656420666f7220656e6163746d656e742e247365745f70726f7879041470726f7879244163636f756e7449641498205370656369667920612070726f78792e2043616c6c6564206279207468652073746173682e002c2023203c7765696768743e58202d204f6e6520657874726120444220656e7472792e302023203c2f7765696768743e3072657369676e5f70726f787900149820436c656172207468652070726f78792e2043616c6c6564206279207468652070726f78792e002c2023203c7765696768743e40202d204f6e6520444220636c6561722e302023203c2f7765696768743e3072656d6f76655f70726f7879041470726f7879244163636f756e744964149820436c656172207468652070726f78792e2043616c6c6564206279207468652073746173682e002c2023203c7765696768743e40202d204f6e6520444220636c6561722e302023203c2f7765696768743e2064656c65676174650808746f244163636f756e74496428636f6e76696374696f6e28436f6e76696374696f6e143c2044656c656761746520766f74652e002c2023203c7765696768743e58202d204f6e6520657874726120444220656e7472792e302023203c2f7765696768743e28756e64656c656761746500144420556e64656c656761746520766f74652e002c2023203c7765696768743e20202d204f2831292e302023203c2f7765696768743e0000001c436f756e63696c0001102c7365745f6d656d62657273042c6e65775f6d656d62657273385665633c4163636f756e7449643e105101205365742074686520636f6c6c6563746976652773206d656d62657273686970206d616e75616c6c7920746f20606e65775f6d656d62657273602e204265206e69636520746f2074686520636861696e20616e645c2070726f76696465206974207072652d736f727465642e005820526571756972657320726f6f74206f726967696e2e1c65786563757465042070726f706f73616c2050726f706f73616c0cf420446973706174636820612070726f706f73616c2066726f6d2061206d656d626572207573696e672074686520604d656d62657260206f726967696e2e00ac204f726967696e206d7573742062652061206d656d626572206f662074686520636f6c6c6563746976652e1c70726f706f736508247468726573686f6c6450436f6d706163743c4d656d626572436f756e743e2070726f706f73616c2050726f706f73616c102c2023203c7765696768743e90202d20426f756e6465642073746f7261676520726561647320616e64207772697465732eb8202d20417267756d656e7420607468726573686f6c6460206861732062656172696e67206f6e207765696768742e302023203c2f7765696768743e10766f74650c2070726f706f73616c104861736814696e64657858436f6d706163743c50726f706f73616c496e6465783e1c617070726f766510626f6f6c102c2023203c7765696768743e8c202d20426f756e6465642073746f72616765207265616420616e64207772697465732e5501202d2057696c6c20626520736c696768746c792068656176696572206966207468652070726f706f73616c20697320617070726f766564202f20646973617070726f7665642061667465722074686520766f74652e302023203c2f7765696768743e00000048546563686e6963616c436f6d6d69747465650001102c7365745f6d656d62657273042c6e65775f6d656d62657273385665633c4163636f756e7449643e105101205365742074686520636f6c6c6563746976652773206d656d62657273686970206d616e75616c6c7920746f20606e65775f6d656d62657273602e204265206e69636520746f2074686520636861696e20616e645c2070726f76696465206974207072652d736f727465642e005820526571756972657320726f6f74206f726967696e2e1c65786563757465042070726f706f73616c2050726f706f73616c0cf420446973706174636820612070726f706f73616c2066726f6d2061206d656d626572207573696e672074686520604d656d62657260206f726967696e2e00ac204f726967696e206d7573742062652061206d656d626572206f662074686520636f6c6c6563746976652e1c70726f706f736508247468726573686f6c6450436f6d706163743c4d656d626572436f756e743e2070726f706f73616c2050726f706f73616c102c2023203c7765696768743e90202d20426f756e6465642073746f7261676520726561647320616e64207772697465732eb8202d20417267756d656e7420607468726573686f6c6460206861732062656172696e67206f6e207765696768742e302023203c2f7765696768743e10766f74650c2070726f706f73616c104861736814696e64657858436f6d706163743c50726f706f73616c496e6465783e1c617070726f766510626f6f6c102c2023203c7765696768743e8c202d20426f756e6465642073746f72616765207265616420616e64207772697465732e5501202d2057696c6c20626520736c696768746c792068656176696572206966207468652070726f706f73616c20697320617070726f766564202f20646973617070726f7665642061667465722074686520766f74652e302023203c2f7765696768743e00000024456c656374696f6e7300011c10766f74650814766f746573385665633c4163636f756e7449643e1476616c756548436f6d706163743c42616c616e63654f663e3c050120566f746520666f72206120736574206f662063616e6469646174657320666f7220746865207570636f6d696e6720726f756e64206f6620656c656374696f6e2e0050205468652060766f746573602073686f756c643a482020202d206e6f7420626520656d7074792eac2020202d206265206c657373207468616e20746865206e756d626572206f662063616e646964617465732e005d012055706f6e20766f74696e672c206076616c75656020756e697473206f66206077686f6027732062616c616e6365206973206c6f636b656420616e64206120626f6e6420616d6f756e742069732072657365727665642e5d012049742069732074686520726573706f6e736962696c697479206f66207468652063616c6c657220746f206e6f7420706c61636520616c6c206f662074686569722062616c616e636520696e746f20746865206c6f636ba020616e64206b65657020736f6d6520666f722066757274686572207472616e73616374696f6e732e002c2023203c7765696768743e2c2023232323205374617465302052656164733a204f283129c8205772697465733a204f28562920676976656e2060566020766f7465732e205620697320626f756e6465642062792031362e302023203c2f7765696768743e3072656d6f76655f766f746572001c21012052656d6f766520606f726967696e60206173206120766f7465722e20546869732072656d6f76657320746865206c6f636b20616e642072657475726e732074686520626f6e642e002c2023203c7765696768743e2c2023232323205374617465302052656164733a204f28312934205772697465733a204f283129302023203c2f7765696768743e507265706f72745f646566756e63745f766f74657204187461726765741c41646472657373345d01205265706f727420607461726765746020666f72206265696e6720616e20646566756e637420766f7465722e20496e2063617365206f6620612076616c6964207265706f72742c20746865207265706f727465722069735d012072657761726465642062792074686520626f6e6420616d6f756e74206f662060746172676574602e204f74686572776973652c20746865207265706f7274657220697473656c662069732072656d6f76656420616e645c20746865697220626f6e6420697320736c61736865642e0088204120646566756e637420766f74657220697320646566696e656420746f2062653a4d012020202d206120766f7465722077686f73652063757272656e74207375626d697474656420766f7465732061726520616c6c20696e76616c69642e20692e652e20616c6c206f66207468656d20617265206e6fb420202020206c6f6e67657220612063616e646964617465206e6f7220616e20616374697665206d656d6265722e002c2023203c7765696768743e2c202323232320537461746515012052656164733a204f284e4c6f674d2920676976656e204d2063757272656e742063616e6469646174657320616e64204e20766f74657320666f722060746172676574602e34205772697465733a204f283129302023203c2f7765696768743e407375626d69745f63616e646964616379003478205375626d6974206f6e6573656c6620666f722063616e6469646163792e006420412063616e6469646174652077696c6c206569746865723aec2020202d204c6f73652061742074686520656e64206f6620746865207465726d20616e6420666f7266656974207468656972206465706f7369742e2d012020202d2057696e20616e64206265636f6d652061206d656d6265722e204d656d626572732077696c6c206576656e7475616c6c7920676574207468656972207374617368206261636b2e55012020202d204265636f6d6520612072756e6e65722d75702e2052756e6e6572732d75707320617265207265736572766564206d656d6265727320696e2063617365206f6e65206765747320666f72636566756c6c7934202020202072656d6f7665642e002c2023203c7765696768743e2c20232323232053746174658c2052656164733a204f284c6f674e2920476976656e204e2063616e646964617465732e34205772697465733a204f283129302023203c2f7765696768743e607365745f646573697265645f6d656d6265725f636f756e740414636f756e7430436f6d706163743c7533323e1c610120536574207468652064657369726564206d656d62657220636f756e742e204368616e6765732077696c6c206265206566666563746976652061742074686520626567696e6e696e67206f66206e65787420726f756e642e002c2023203c7765696768743e2c2023232323205374617465302052656164733a204f28312934205772697465733a204f283129302023203c2f7765696768743e3472656d6f76655f6d656d626572040c77686f1c416464726573733021012052656d6f7665206120706172746963756c6172206d656d6265722066726f6d20746865207365742e20546869732069732065666665637469766520696d6d6564696174656c792e00590120496620612072756e6e65722d757020697320617661696c61626c652c207468656e2074686520626573742072756e6e65722d75702077696c6c2062652072656d6f76656420616e64207265706c6163657320746865f4206f7574676f696e67206d656d6265722e204f74686572776973652c2061206e65772070687261676d656e20726f756e6420697320737461727465642e004501204e6f74652074686174207468697320646f6573206e6f7420616666656374207468652064657369676e6174656420626c6f636b206e756d626572206f6620746865206e65787420656c656374696f6e2e002c2023203c7765696768743e2c2023232323205374617465582052656164733a204f28646f5f70687261676d656e295c205772697465733a204f28646f5f70687261676d656e29302023203c2f7765696768743e447365745f7465726d5f6475726174696f6e0414636f756e7450436f6d706163743c426c6f636b4e756d6265723e1c49012053657420746865206475726174696f6e206f662065616368207465726d2e20546869732077696c6c2061666665637420746865206e65787420656c656374696f6e277320626c6f636b206e756d6265722e002c2023203c7765696768743e2c2023232323205374617465302052656164733a204f28312934205772697465733a204f283129302023203c2f7765696768743e0000004c546563686e6963616c4d656d62657273686970000110286164645f6d656d626572040c77686f244163636f756e7449640c7c204164642061206d656d626572206077686f6020746f20746865207365742e00b4204d6179206f6e6c792062652063616c6c65642066726f6d20604164644f726967696e60206f7220726f6f742e3472656d6f76655f6d656d626572040c77686f244163636f756e7449640c902052656d6f76652061206d656d626572206077686f602066726f6d20746865207365742e00c0204d6179206f6e6c792062652063616c6c65642066726f6d206052656d6f76654f726967696e60206f7220726f6f742e2c737761705f6d656d626572081872656d6f7665244163636f756e7449640c616464244163636f756e7449640cc02053776170206f7574206f6e65206d656d626572206072656d6f76656020666f7220616e6f746865722060616464602e00b8204d6179206f6e6c792062652063616c6c65642066726f6d2060537761704f726967696e60206f7220726f6f742e3472657365745f6d656d62657273041c6d656d62657273385665633c4163636f756e7449643e105901204368616e676520746865206d656d6265727368697020746f2061206e6577207365742c20646973726567617264696e6720746865206578697374696e67206d656d626572736869702e204265206e69636520616e646c207061737320606d656d6265727360207072652d736f727465642e00bc204d6179206f6e6c792062652063616c6c65642066726f6d206052657365744f726967696e60206f7220726f6f742e0000003c46696e616c697479547261636b65720001042866696e616c5f68696e74041068696e7450436f6d706163743c426c6f636b4e756d6265723e08f42048696e7420746861742074686520617574686f72206f66207468697320626c6f636b207468696e6b732074686520626573742066696e616c697a65646c20626c6f636b2069732074686520676976656e206e756d6265722e0000001c4772616e647061000104487265706f72745f6d69736265686176696f72041c5f7265706f72741442797465730464205265706f727420736f6d65206d69736265686176696f722e00000020547265617375727900010c3470726f706f73655f7370656e64081476616c756548436f6d706163743c42616c616e63654f663e2c62656e65666963696172791c41646472657373242d012050757420666f727761726420612073756767657374696f6e20666f72207370656e64696e672e2041206465706f7369742070726f706f7274696f6e616c20746f207468652076616c7565350120697320726573657276656420616e6420736c6173686564206966207468652070726f706f73616c2069732072656a65637465642e2049742069732072657475726e6564206f6e636520746865542070726f706f73616c20697320617761726465642e002c2023203c7765696768743e20202d204f2831292e64202d204c696d697465642073746f726167652072656164732e94202d204f6e65204442206368616e67652c206f6e6520657874726120444220656e7472792e302023203c2f7765696768743e3c72656a6563745f70726f706f73616c042c70726f706f73616c5f696458436f6d706163743c50726f706f73616c496e6465783e1cfc2052656a65637420612070726f706f736564207370656e642e20546865206f726967696e616c206465706f7369742077696c6c20626520736c61736865642e002c2023203c7765696768743e20202d204f2831292e64202d204c696d697465642073746f726167652072656164732e40202d204f6e6520444220636c6561722e302023203c2f7765696768743e40617070726f76655f70726f706f73616c042c70726f706f73616c5f696458436f6d706163743c50726f706f73616c496e6465783e205d0120417070726f766520612070726f706f73616c2e2041742061206c617465722074696d652c207468652070726f706f73616c2077696c6c20626520616c6c6f636174656420746f207468652062656e6566696369617279ac20616e6420746865206f726967696e616c206465706f7369742077696c6c2062652072657475726e65642e002c2023203c7765696768743e20202d204f2831292e64202d204c696d697465642073746f726167652072656164732e44202d204f6e65204442206368616e67652e302023203c2f7765696768743e00000024436f6e7472616374730001143c7570646174655f7363686564756c6504207363686564756c65205363686564756c650cb4205570646174657320746865207363686564756c6520666f72206d65746572696e6720636f6e7472616374732e000d0120546865207363686564756c65206d7573742068617665206120677265617465722076657273696f6e207468616e207468652073746f726564207363686564756c652e207075745f636f646508246761735f6c696d697430436f6d706163743c4761733e10636f6465144279746573085d012053746f7265732074686520676976656e2062696e617279205761736d20636f646520696e746f2074686520636861696e27732073746f7261676520616e642072657475726e73206974732060636f646568617368602ed420596f752063616e20696e7374616e746961746520636f6e747261637473206f6e6c7920776974682073746f72656420636f64652e1063616c6c1010646573741c416464726573731476616c756548436f6d706163743c42616c616e63654f663e246761735f6c696d697430436f6d706163743c4761733e10646174611442797465731c0901204d616b657320612063616c6c20746f20616e206163636f756e742c206f7074696f6e616c6c79207472616e7366657272696e6720736f6d652062616c616e63652e002901202a20496620746865206163636f756e74206973206120736d6172742d636f6e7472616374206163636f756e742c20746865206173736f63696174656420636f64652077696c6c206265b020657865637574656420616e6420616e792076616c75652077696c6c206265207472616e736665727265642e1901202a20496620746865206163636f756e74206973206120726567756c6172206163636f756e742c20616e792076616c75652077696c6c206265207472616e736665727265642e4901202a204966206e6f206163636f756e742065786973747320616e64207468652063616c6c2076616c7565206973206e6f74206c657373207468616e20606578697374656e7469616c5f6465706f736974602c1501206120726567756c6172206163636f756e742077696c6c206265206372656174656420616e6420616e792076616c75652077696c6c206265207472616e736665727265642e2c696e7374616e74696174651024656e646f776d656e7448436f6d706163743c42616c616e63654f663e246761735f6c696d697430436f6d706163743c4761733e24636f64655f6861736820436f646548617368106461746114427974657328bd0120496e7374616e7469617465732061206e657720636f6e74726163742066726f6d207468652060636f646568617368602067656e65726174656420627920607075745f636f6465602c206f7074696f6e616c6c79207472616e7366657272696e6720736f6d652062616c616e63652e009820496e7374616e74696174696f6e20697320657865637574656420617320666f6c6c6f77733a004101202d205468652064657374696e6174696f6e206164647265737320697320636f6d7075746564206261736564206f6e207468652073656e64657220616e642068617368206f662074686520636f64652e0501202d2054686520736d6172742d636f6e7472616374206163636f756e7420697320637265617465642061742074686520636f6d707574656420616464726573732e6d01202d20546865206063746f725f636f64656020697320657865637574656420696e2074686520636f6e74657874206f6620746865206e65776c792d63726561746564206163636f756e742e204275666665722072657475726e65645d0120202061667465722074686520657865637574696f6e206973207361766564206173207468652060636f646560206f6620746865206163636f756e742e205468617420636f64652077696c6c20626520696e766f6b6564a820202075706f6e20616e792063616c6c2072656365697665642062792074686973206163636f756e742e7c202d2054686520636f6e747261637420697320696e697469616c697a65642e3c636c61696d5f737572636861726765081064657374244163636f756e744964286175785f73656e646572444f7074696f6e3c4163636f756e7449643e14710120416c6c6f777320626c6f636b2070726f64756365727320746f20636c61696d206120736d616c6c2072657761726420666f72206576696374696e67206120636f6e74726163742e204966206120626c6f636b2070726f64756365721501206661696c7320746f20646f20736f2c206120726567756c61722075736572732077696c6c20626520616c6c6f77656420746f20636c61696d20746865207265776172642e00390120496620636f6e7472616374206973206e6f742065766963746564206173206120726573756c74206f6620746869732063616c6c2c206e6f20616374696f6e73206172652074616b656e20616e64ac207468652073656e646572206973206e6f7420656c696769626c6520666f7220746865207265776172642e000000105375646f00010c107375646f042070726f706f73616c2050726f706f73616c2839012041757468656e7469636174657320746865207375646f206b657920616e64206469737061746368657320612066756e6374696f6e2063616c6c20776974682060526f6f7460206f726967696e2e00d020546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f2e002c2023203c7765696768743e20202d204f2831292e64202d204c696d697465642073746f726167652072656164732e60202d204f6e6520444220777269746520286576656e74292ed4202d20556e6b6e6f776e20776569676874206f662064657269766174697665206070726f706f73616c6020657865637574696f6e2e302023203c2f7765696768743e1c7365745f6b6579040c6e65771c416464726573732475012041757468656e74696361746573207468652063757272656e74207375646f206b657920616e6420736574732074686520676976656e204163636f756e7449642028606e6577602920617320746865206e6577207375646f206b65792e00d020546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f2e002c2023203c7765696768743e20202d204f2831292e64202d204c696d697465642073746f726167652072656164732e44202d204f6e65204442206368616e67652e302023203c2f7765696768743e1c7375646f5f6173080c77686f1c416464726573732070726f706f73616c2050726f706f73616c2c51012041757468656e7469636174657320746865207375646f206b657920616e64206469737061746368657320612066756e6374696f6e2063616c6c207769746820605369676e656460206f726967696e2066726f6d44206120676976656e206163636f756e742e00d020546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f2e002c2023203c7765696768743e20202d204f2831292e64202d204c696d697465642073746f726167652072656164732e60202d204f6e6520444220777269746520286576656e74292ed4202d20556e6b6e6f776e20776569676874206f662064657269766174697665206070726f706f73616c6020657865637574696f6e2e302023203c2f7765696768743e00000020496d4f6e6c696e6500010424686561727462656174082468656172746265617424486561727462656174247369676e6174757265245369676e61747572650000000048417574686f72697479446973636f76657279000100000000204f6666656e6365730001000000006052616e646f6d6e657373436f6c6c656374697665466c6970000100000000';
diff --git a/src/util/strings.js b/src/util/strings.js
index a4d3e3585f..35cf7936fa 100644
--- a/src/util/strings.js
+++ b/src/util/strings.js
@@ -16,10 +16,13 @@
// @flow
+'use strict';
+
/*
* @dev Check if input is in Ascii table.
*/
export function isAscii(data) {
+ /* eslint-disable-next-line no-control-regex */
return /^[\x00-\x7F]*$/.test(data);
}
diff --git a/src/util/suri.js b/src/util/suri.js
index e43d700442..6c41f0899b 100644
--- a/src/util/suri.js
+++ b/src/util/suri.js
@@ -1,3 +1,21 @@
+// Copyright 2015-2019 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+'use strict';
+
/**
* @typedef {Object} SURIObject
* @property {string} phrase - The valid bip39 seed phrase
diff --git a/src/util/transaction.js b/src/util/transaction.js
index b8e7882c2b..0dcd22c586 100644
--- a/src/util/transaction.js
+++ b/src/util/transaction.js
@@ -14,6 +14,8 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see .
+'use strict';
+
import { rlpItem } from './native';
import { fromWei } from './units';
diff --git a/src/util/units.js b/src/util/units.js
index 6d748268b0..1605b9e0d7 100644
--- a/src/util/units.js
+++ b/src/util/units.js
@@ -14,6 +14,8 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see .
+'use strict';
+
import BigNumber from 'bignumber.js';
/* eslint-disable sort-keys */
diff --git a/yarn.lock b/yarn.lock
index fb90f19410..b872c9e54f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1359,6 +1359,22 @@
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.2.tgz#0e58ae66773d7fd7c372a493aff740878ec9ceaa"
integrity sha512-f8JzJNWVhKtc9dg/dyDNfliTKNOJSLa7Oht/ElZdF/UbMUmAH3rLmAk3ODNjw0mZajDEgatA03tRjB4+Dp/tzA==
+"@types/react-native-vector-icons@^6.4.4":
+ version "6.4.4"
+ resolved "https://registry.yarnpkg.com/@types/react-native-vector-icons/-/react-native-vector-icons-6.4.4.tgz#f81dcc371b74a9c408ce12f95b8f494d7543146b"
+ integrity sha512-G1Iry/8i23IPjZzNjydMt/WcjV+T1Xu3cTXDwSsP9lpKu0bA0j+c7AACJ1aIka8HVnWXS41NoZnKkHImO0SMkw==
+ dependencies:
+ "@types/react" "*"
+ "@types/react-native" "*"
+
+"@types/react-native@*":
+ version "0.60.22"
+ resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.60.22.tgz#ba199a441cb0612514244ffb1d0fe6f04c878575"
+ integrity sha512-LTXMKEyGA+x4kadmjujX6yAgpcaZutJ01lC7zLJWCULaZg7Qw5/3iOQpwIJRUcOc+a8A2RR7rSxplehVf9IuhA==
+ dependencies:
+ "@types/prop-types" "*"
+ "@types/react" "*"
+
"@types/react-native@>=0.50.0":
version "0.60.14"
resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.60.14.tgz#76b07b97127a563ec0c996848925f81fcee1320b"
@@ -2425,7 +2441,7 @@ color-support@^1.1.3:
resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2"
integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==
-color@^3.1.2:
+color@^3.1.0, color@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/color/-/color-3.1.2.tgz#68148e7f85d41ad7649c5fa8c8106f098d229e10"
integrity sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg==
@@ -2774,7 +2790,7 @@ deep-is@~0.1.3:
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=
-deepmerge@^3.2.0:
+deepmerge@^3.1.0, deepmerge@^3.2.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-3.3.0.tgz#d3c47fd6f3a93d517b14426b0628a17b0125f5f7"
integrity sha512-GRQOafGHwMHpjPx9iCvTgpu9NojZ49q794EEL94JVEw6VaeA8XTUyBKvAkOOjBX9oJNiV6G3P+T+tihFjo2TqA==
@@ -2858,10 +2874,10 @@ detect-newline@^2.1.0:
resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2"
integrity sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=
-detox@^14.5.0:
- version "14.5.0"
- resolved "https://registry.yarnpkg.com/detox/-/detox-14.5.0.tgz#cc96b41a2b76a029ef26be861fb04f69fe70c965"
- integrity sha512-6LH/Aw/0pnf5llCY5rZICxVK1uoAmV+OZzGPQogpE3ZqxPkViFkg/SHsQqOn1M1Iswia7SzfSgu2KfKMNFXvRA==
+detox@^14.7.0:
+ version "14.7.1"
+ resolved "https://registry.yarnpkg.com/detox/-/detox-14.7.1.tgz#14aa533078f88407238a41a53ac0556fadcaf39f"
+ integrity sha512-XEkficJ5GlMVTbQzJAiuoGVGasgJb5/uFzeuhPrNW7oZ/ar4toOEp57tu95YteFvLEI1ikXe6j7Rp0shAbZKjg==
dependencies:
"@babel/core" "^7.4.5"
bunyan "^1.8.12"
@@ -4017,7 +4033,7 @@ hoist-non-react-statics@^2.3.1, hoist-non-react-statics@^2.5.0:
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47"
integrity sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==
-hoist-non-react-statics@^3.0.1, hoist-non-react-statics@^3.3.0:
+hoist-non-react-statics@^3.0.1, hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz#b09178f0122184fb95acf525daaecb4d8f45958b"
integrity sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA==
@@ -5164,7 +5180,7 @@ lodash.unescape@4.0.1:
resolved "https://registry.yarnpkg.com/lodash.unescape/-/lodash.unescape-4.0.1.tgz#bf2249886ce514cda112fae9218cdc065211fc9c"
integrity sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=
-lodash@4.x.x, lodash@^4.0.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.5, lodash@^4.3.0, lodash@^4.6.1:
+lodash@4.x.x, lodash@^4.0.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.3.0, lodash@^4.6.1:
version "4.17.15"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
@@ -6128,7 +6144,7 @@ open@^6.2.0:
dependencies:
is-wsl "^1.1.0"
-opencollective-postinstall@^2.0.2:
+opencollective-postinstall@^2.0.0, opencollective-postinstall@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.2.tgz#5657f1bede69b6e33a45939b061eb53d3c6c3a89"
integrity sha512-pVOEP16TrAO2/fjej1IdOyupJY8KDUM1CvsaScRbw6oddvpQoOfGk4ywha0HKKVAD6RkW4x6Q+tNBwhf3Bgpuw==
@@ -6774,6 +6790,20 @@ react-native-crypto@^2.0.1:
public-encrypt "^4.0.0"
randomfill "^1.0.3"
+react-native-elements@^1.2.6:
+ version "1.2.6"
+ resolved "https://registry.yarnpkg.com/react-native-elements/-/react-native-elements-1.2.6.tgz#fda0f49ddf87abdb287de1ec9bc057c103430455"
+ integrity sha512-f0HRW41qb2x8MJUjR9b5FtRX9gnychtZ3P4O7AX3HV2pxx5c6pFqE+Q38mv8vMDU0QB9X+l/iz6bapbTd9HCvA==
+ dependencies:
+ "@types/react-native-vector-icons" "^6.4.4"
+ color "^3.1.0"
+ deepmerge "^3.1.0"
+ hoist-non-react-statics "^3.1.0"
+ opencollective-postinstall "^2.0.0"
+ prop-types "^15.7.2"
+ react-native-ratings "^6.3.0"
+ react-native-status-bar-height "^2.2.0"
+
react-native-fit-image@^1.5.2:
version "1.5.4"
resolved "https://registry.yarnpkg.com/react-native-fit-image/-/react-native-fit-image-1.5.4.tgz#73d2fccc7ad902cf2ffcd008a2a74749ad50134a"
@@ -6828,6 +6858,14 @@ react-native-randombytes@^3.5.1, react-native-randombytes@^3.5.3:
buffer "^4.9.1"
sjcl "^1.0.3"
+react-native-ratings@^6.3.0:
+ version "6.5.0"
+ resolved "https://registry.yarnpkg.com/react-native-ratings/-/react-native-ratings-6.5.0.tgz#a1606ccba3c5b54eec8e6cfa4765a45cf0e4ab8d"
+ integrity sha512-YMcfQ7UQCmXGEc/WPlukHSHs5yvckTwjq5fTRk1FG8gaO7fZCNygEUGPuw4Dbvvp3IlsCUn0bOQd63RYsb7NDQ==
+ dependencies:
+ lodash "^4.17.4"
+ prop-types "^15.5.10"
+
react-native-safe-area-view@^0.14.1:
version "0.14.8"
resolved "https://registry.yarnpkg.com/react-native-safe-area-view/-/react-native-safe-area-view-0.14.8.tgz#ef33c46ff8164ae77acad48c3039ec9c34873e5b"
@@ -6846,6 +6884,11 @@ react-native-safe-area-view@^0.14.1:
version "1.0.0"
resolved "git+https://github.com/paritytech/react-native-secure-storage.git#3263537d637ddfc67e243b40ba50e5d05367d20b"
+react-native-status-bar-height@^2.2.0:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/react-native-status-bar-height/-/react-native-status-bar-height-2.4.0.tgz#de8cee4bb733a196167210d2d0bc1fa10acba3e3"
+ integrity sha512-pWvZFlyIHiuxLugLioq97vXiaGSovFXEyxt76wQtbq0gxv4dGXMPqYow46UmpwOgeJpBhqL1E0EKxnfJRrFz5w==
+
react-native-svg@^9.12.0:
version "9.13.2"
resolved "https://registry.yarnpkg.com/react-native-svg/-/react-native-svg-9.13.2.tgz#72a2e089f69af99c491586a95baf71009c263a51"