mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-01-19 05:45:15 +01:00
ADD: main view SCAN button can now import watch-only wallets (closes #2817)
This commit is contained in:
parent
dc17b1d935
commit
c84fc9a384
@ -1,4 +1,4 @@
|
|||||||
import { AppStorage, LightningCustodianWallet } from './';
|
import { AppStorage, LightningCustodianWallet, WatchOnlyWallet } from './';
|
||||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||||
import RNFS from 'react-native-fs';
|
import RNFS from 'react-native-fs';
|
||||||
import url from 'url';
|
import url from 'url';
|
||||||
@ -178,6 +178,17 @@ class DeeplinkSchemaMatch {
|
|||||||
params: Azteco.getParamsFromUrl(event.url),
|
params: Azteco.getParamsFromUrl(event.url),
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
} else if (new WatchOnlyWallet().setSecret(event.url).init().valid()) {
|
||||||
|
completionHandler([
|
||||||
|
'AddWalletRoot',
|
||||||
|
{
|
||||||
|
screen: 'ImportWallet',
|
||||||
|
params: {
|
||||||
|
triggerImport: true,
|
||||||
|
label: event.url,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
} else {
|
} else {
|
||||||
const urlObject = url.parse(event.url, true); // eslint-disable-line node/no-deprecated-api
|
const urlObject = url.parse(event.url, true); // eslint-disable-line node/no-deprecated-api
|
||||||
(async () => {
|
(async () => {
|
||||||
|
@ -56,13 +56,15 @@ export class WatchOnlyWallet extends LegacyWallet {
|
|||||||
* this method creates appropriate HD wallet class, depending on whether we have xpub, ypub or zpub
|
* this method creates appropriate HD wallet class, depending on whether we have xpub, ypub or zpub
|
||||||
* as a property of `this`, and in case such property exists - it recreates it and copies data from old one.
|
* as a property of `this`, and in case such property exists - it recreates it and copies data from old one.
|
||||||
* this is needed after serialization/save/load/deserialization procedure.
|
* this is needed after serialization/save/load/deserialization procedure.
|
||||||
|
*
|
||||||
|
* @return {WatchOnlyWallet} this
|
||||||
*/
|
*/
|
||||||
init() {
|
init() {
|
||||||
let hdWalletInstance;
|
let hdWalletInstance;
|
||||||
if (this.secret.startsWith('xpub')) hdWalletInstance = new HDLegacyP2PKHWallet();
|
if (this.secret.startsWith('xpub')) hdWalletInstance = new HDLegacyP2PKHWallet();
|
||||||
else if (this.secret.startsWith('ypub')) hdWalletInstance = new HDSegwitP2SHWallet();
|
else if (this.secret.startsWith('ypub')) hdWalletInstance = new HDSegwitP2SHWallet();
|
||||||
else if (this.secret.startsWith('zpub')) hdWalletInstance = new HDSegwitBech32Wallet();
|
else if (this.secret.startsWith('zpub')) hdWalletInstance = new HDSegwitBech32Wallet();
|
||||||
else return;
|
else return this;
|
||||||
hdWalletInstance._xpub = this.secret;
|
hdWalletInstance._xpub = this.secret;
|
||||||
if (this._hdWalletInstance) {
|
if (this._hdWalletInstance) {
|
||||||
// now, porting all properties from old object to new one
|
// now, porting all properties from old object to new one
|
||||||
@ -75,6 +77,8 @@ export class WatchOnlyWallet extends LegacyWallet {
|
|||||||
delete hdWalletInstance._node0;
|
delete hdWalletInstance._node0;
|
||||||
}
|
}
|
||||||
this._hdWalletInstance = hdWalletInstance;
|
this._hdWalletInstance = hdWalletInstance;
|
||||||
|
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareForSerialization() {
|
prepareForSerialization() {
|
||||||
|
@ -4,13 +4,14 @@
|
|||||||
*
|
*
|
||||||
* @param navigateFunc {function}
|
* @param navigateFunc {function}
|
||||||
* @param currentScreenName {string}
|
* @param currentScreenName {string}
|
||||||
|
* @param showFileImportButton {boolean}
|
||||||
*
|
*
|
||||||
* @return {Promise<string>}
|
* @return {Promise<string>}
|
||||||
*/
|
*/
|
||||||
module.exports = function (navigateFunc, currentScreenName) {
|
module.exports = function scanQrHelper(navigateFunc, currentScreenName, showFileImportButton = true) {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
const params = {};
|
const params = {};
|
||||||
params.showFileImportButton = true;
|
params.showFileImportButton = !!showFileImportButton;
|
||||||
|
|
||||||
params.onBarScanned = function (data) {
|
params.onBarScanned = function (data) {
|
||||||
setTimeout(() => resolve(data.data || data), 1);
|
setTimeout(() => resolve(data.data || data), 1);
|
||||||
|
@ -315,17 +315,9 @@ const ScanQRCode = () => {
|
|||||||
testID="scanQrBackdoorOkButton"
|
testID="scanQrBackdoorOkButton"
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
setBackdoorVisible(false);
|
setBackdoorVisible(false);
|
||||||
let data;
|
setBackdoorText('');
|
||||||
try {
|
|
||||||
data = JSON.parse(backdoorText);
|
|
||||||
// this might be a json string (for convenience - in case there are "\n" in there)
|
|
||||||
} catch (_) {
|
|
||||||
data = backdoorText;
|
|
||||||
} finally {
|
|
||||||
setBackdoorText('');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data) onBarCodeRead({ data });
|
if (backdoorText) onBarCodeRead({ data: backdoorText });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
@ -23,6 +23,7 @@ const WalletsImport = () => {
|
|||||||
const [isToolbarVisibleForAndroid, setIsToolbarVisibleForAndroid] = useState(false);
|
const [isToolbarVisibleForAndroid, setIsToolbarVisibleForAndroid] = useState(false);
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const label = (route.params && route.params.label) || '';
|
const label = (route.params && route.params.label) || '';
|
||||||
|
const triggerImport = (route.params && route.params.triggerImport) || false;
|
||||||
const [importText, setImportText] = useState(label);
|
const [importText, setImportText] = useState(label);
|
||||||
const navigation = useNavigation();
|
const navigation = useNavigation();
|
||||||
const { colors } = useTheme();
|
const { colors } = useTheme();
|
||||||
@ -50,6 +51,11 @@ const WalletsImport = () => {
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (triggerImport) importButtonPressed();
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
const importButtonPressed = () => {
|
const importButtonPressed = () => {
|
||||||
if (importText.trim().length === 0) {
|
if (importText.trim().length === 0) {
|
||||||
return;
|
return;
|
||||||
|
@ -31,6 +31,7 @@ import { isCatalyst, isMacCatalina, isTablet } from '../../blue_modules/environm
|
|||||||
import BlueClipboard from '../../blue_modules/clipboard';
|
import BlueClipboard from '../../blue_modules/clipboard';
|
||||||
import navigationStyle from '../../components/navigationStyle';
|
import navigationStyle from '../../components/navigationStyle';
|
||||||
|
|
||||||
|
const scanqrHelper = require('../../helpers/scan-qr');
|
||||||
const A = require('../../blue_modules/analytics');
|
const A = require('../../blue_modules/analytics');
|
||||||
const fs = require('../../blue_modules/fs');
|
const fs = require('../../blue_modules/fs');
|
||||||
const WalletsListSections = { CAROUSEL: 'CAROUSEL', LOCALTRADER: 'LOCALTRADER', TRANSACTIONS: 'TRANSACTIONS' };
|
const WalletsListSections = { CAROUSEL: 'CAROUSEL', LOCALTRADER: 'LOCALTRADER', TRANSACTIONS: 'TRANSACTIONS' };
|
||||||
@ -343,18 +344,12 @@ const WalletsList = () => {
|
|||||||
if (isMacCatalina) {
|
if (isMacCatalina) {
|
||||||
fs.showActionSheet({ anchor: walletActionButtonsRef.current }).then(onBarScanned);
|
fs.showActionSheet({ anchor: walletActionButtonsRef.current }).then(onBarScanned);
|
||||||
} else {
|
} else {
|
||||||
navigate('ScanQRCodeRoot', {
|
scanqrHelper(navigate, routeName, false).then(onBarScanned);
|
||||||
screen: 'ScanQRCode',
|
|
||||||
params: {
|
|
||||||
launchedBy: routeName,
|
|
||||||
onBarScanned,
|
|
||||||
showFileImportButton: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onBarScanned = value => {
|
const onBarScanned = value => {
|
||||||
|
if (!value) return;
|
||||||
DeeplinkSchemaMatch.navigationRouteFor({ url: value }, completionValue => {
|
DeeplinkSchemaMatch.navigationRouteFor({ url: value }, completionValue => {
|
||||||
ReactNativeHapticFeedback.trigger('impactLight', { ignoreAndroidSystemSettings: false });
|
ReactNativeHapticFeedback.trigger('impactLight', { ignoreAndroidSystemSettings: false });
|
||||||
navigate(...completionValue);
|
navigate(...completionValue);
|
||||||
@ -381,14 +376,7 @@ const WalletsList = () => {
|
|||||||
if (buttonIndex === 1) {
|
if (buttonIndex === 1) {
|
||||||
fs.showImagePickerAndReadImage().then(onBarScanned);
|
fs.showImagePickerAndReadImage().then(onBarScanned);
|
||||||
} else if (buttonIndex === 2) {
|
} else if (buttonIndex === 2) {
|
||||||
navigate('ScanQRCodeRoot', {
|
scanqrHelper(navigate, routeName, false).then(onBarScanned);
|
||||||
screen: 'ScanQRCode',
|
|
||||||
params: {
|
|
||||||
launchedBy: routeName,
|
|
||||||
onBarScanned,
|
|
||||||
showFileImportButton: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else if (buttonIndex === 3) {
|
} else if (buttonIndex === 3) {
|
||||||
copyFromClipboard();
|
copyFromClipboard();
|
||||||
}
|
}
|
||||||
@ -408,15 +396,7 @@ const WalletsList = () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: loc.wallets.list_long_scan,
|
text: loc.wallets.list_long_scan,
|
||||||
onPress: () =>
|
onPress: () => scanqrHelper(navigate, routeName, false).then(onBarScanned),
|
||||||
navigate('ScanQRCodeRoot', {
|
|
||||||
screen: 'ScanQRCode',
|
|
||||||
params: {
|
|
||||||
launchedBy: routeName,
|
|
||||||
onBarScanned,
|
|
||||||
showFileImportButton: false,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
if (!isClipboardEmpty) {
|
if (!isClipboardEmpty) {
|
||||||
|
@ -202,6 +202,81 @@ describe('unit - DeepLinkSchemaMatch', function () {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
argument: {
|
||||||
|
url: require('fs').readFileSync('./tests/unit/fixtures/skeleton-cobo.txt', 'ascii'),
|
||||||
|
},
|
||||||
|
expected: [
|
||||||
|
'AddWalletRoot',
|
||||||
|
{
|
||||||
|
screen: 'ImportWallet',
|
||||||
|
params: {
|
||||||
|
triggerImport: true,
|
||||||
|
label: require('fs').readFileSync('./tests/unit/fixtures/skeleton-cobo.txt', 'ascii'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
argument: {
|
||||||
|
url: require('fs').readFileSync('./tests/unit/fixtures/skeleton-coldcard.txt', 'ascii'),
|
||||||
|
},
|
||||||
|
expected: [
|
||||||
|
'AddWalletRoot',
|
||||||
|
{
|
||||||
|
screen: 'ImportWallet',
|
||||||
|
params: {
|
||||||
|
triggerImport: true,
|
||||||
|
label: require('fs').readFileSync('./tests/unit/fixtures/skeleton-coldcard.txt', 'ascii'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
argument: {
|
||||||
|
url: require('fs').readFileSync('./tests/unit/fixtures/skeleton-electrum.txt', 'ascii'),
|
||||||
|
},
|
||||||
|
expected: [
|
||||||
|
'AddWalletRoot',
|
||||||
|
{
|
||||||
|
screen: 'ImportWallet',
|
||||||
|
params: {
|
||||||
|
triggerImport: true,
|
||||||
|
label: require('fs').readFileSync('./tests/unit/fixtures/skeleton-electrum.txt', 'ascii'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
argument: {
|
||||||
|
url: require('fs').readFileSync('./tests/unit/fixtures/skeleton-walletdescriptor.txt', 'ascii'),
|
||||||
|
},
|
||||||
|
expected: [
|
||||||
|
'AddWalletRoot',
|
||||||
|
{
|
||||||
|
screen: 'ImportWallet',
|
||||||
|
params: {
|
||||||
|
triggerImport: true,
|
||||||
|
label: require('fs').readFileSync('./tests/unit/fixtures/skeleton-walletdescriptor.txt', 'ascii'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
argument: {
|
||||||
|
url: 'zpub6rFDtF1nuXZ9PUL4XzKURh3vJBW6Kj6TUrYL4qPtFNtDXtcTVfiqjQDyrZNwjwzt5HS14qdqo3Co2282Lv3Re6Y5wFZxAVuMEpeygnnDwfx',
|
||||||
|
},
|
||||||
|
expected: [
|
||||||
|
'AddWalletRoot',
|
||||||
|
{
|
||||||
|
screen: 'ImportWallet',
|
||||||
|
params: {
|
||||||
|
triggerImport: true,
|
||||||
|
label: 'zpub6rFDtF1nuXZ9PUL4XzKURh3vJBW6Kj6TUrYL4qPtFNtDXtcTVfiqjQDyrZNwjwzt5HS14qdqo3Co2282Lv3Re6Y5wFZxAVuMEpeygnnDwfx',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const asyncNavigationRouteFor = async function (event) {
|
const asyncNavigationRouteFor = async function (event) {
|
||||||
|
1
tests/unit/fixtures/skeleton-cobo.txt
Normal file
1
tests/unit/fixtures/skeleton-cobo.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"ExtPubKey":"zpub6rcabYFcdr41zyUNRWRyHYs2Sm86E5XV8RjjRzTFYsiCngteeZnkwaF2xuhjmM6kpHjuNpFW42BMhzPmFwXt48e1FhddMB7xidZzN4SF24K","MasterFingerprint":"5271c071","CoboVaultFirmwareVersion":"1.2.4(BTC-Only)"}
|
1
tests/unit/fixtures/skeleton-coldcard.txt
Normal file
1
tests/unit/fixtures/skeleton-coldcard.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"keystore": {"ckcc_xpub": "xpub661MyMwAqRbcGmUDQVKxmhEESB5xTk8hbsdTSV3Pmhm3HE9Fj3s45R9Y8LwyaQWjXXPytZjuhTKSyCBPeNrB1VVWQq1HCvjbEZ27k44oNmg", "xpub": "zpub6rFDtF1nuXZ9PUL4XzKURh3vJBW6Kj6TUrYL4qPtFNtDXtcTVfiqjQDyrZNwjwzt5HS14qdqo3Co2282Lv3Re6Y5wFZxAVuMEpeygnnDwfx", "label": "Coldcard Import 168DD603", "ckcc_xfp": 64392470, "type": "hardware", "hw_type": "coldcard", "derivation": "m/84'/0'/0'"}, "wallet_type": "standard", "use_encryption": false, "seed_version": 17}
|
1
tests/unit/fixtures/skeleton-electrum.txt
Normal file
1
tests/unit/fixtures/skeleton-electrum.txt
Normal file
File diff suppressed because one or more lines are too long
1
tests/unit/fixtures/skeleton-walletdescriptor.txt
Normal file
1
tests/unit/fixtures/skeleton-walletdescriptor.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
[8cce63f8/84h/0h/0h]zpub6s2RJ9qAEBW8Abhojs6LyDzF7gttcDr6EsR3Umu2aptZBb45e734rGtt4KqsCMmNyR1EEzUU2ugdVYez2VywQvAbBjUSKn8ho4Zk2c5otkk
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user