diff --git a/loc/sl_SI.json b/loc/sl_SI.json index 9c9b3de32..528f7590d 100644 --- a/loc/sl_SI.json +++ b/loc/sl_SI.json @@ -12,6 +12,9 @@ "dont_allow": "Ne dovoli", "yes": "Da", "no": "Ne", + "save": "Shrani", + "seed": "Seme", + "wallet_key": "Ključ denarnice", "invalid_animated_qr_code_fragment" : "Neveljaven del animirane QR kode, prosimo poskusite ponovno", "file_saved": "Datoteka ({filePath}) je bila shranjena v mapo Prenosi." }, @@ -317,7 +320,7 @@ "transactions_count": "število transakcij" }, "wallets": { - "add_bitcoin_explain": "Simple and powerful Bitcoin wallet", + "add_bitcoin_explain": "Preprosta in zmogljiva Bitcoin denarnica", "add_bitcoin": "Bitcoin", "add_create": "Ustvari", "add_entropy_generated": "{gen} bajtov ustvarjene entropije", @@ -326,7 +329,7 @@ "add_import_wallet": "Uvozi denarnico", "import_file": "Uvozi datoteko", "add_lightning": "Lightning", - "add_lightning_explain": "For spending with instant transactions", + "add_lightning_explain": "Za hitre vsakodnevne transakcije", "add_lndhub": "Povežite se s svojim LNDHub-om", "add_lndhub_error": "Podan naslov vozlišča ni veljavno vozlišče LNDHub.", "add_lndhub_placeholder": "naslov vašega vozlišča", @@ -391,10 +394,11 @@ "xpub_title": "XPUB denarnice" }, "multisig": { - "multisig_vault": "Vault", - "multisig_vault_explain": "Best security for large amounts", + "multisig_vault": "Trezor", + "multisig_vault_explain": "Največja varnost za višje zneske", "provide_signature": "Vnesite podpis", "vault_key": "Ključ trezorja {number}", + "required_keys_out_of_total": "Zahtevani ključi od vseh", "fee": "Omrežnina: {number}", "fee_btc": "{number} BTC", "confirm": "Potrditev", @@ -404,6 +408,41 @@ "scan_or_import_file": "Skenirajte ali uvozite datoteko", "export_coordination_setup": "izvoz koordinacijskih nastavitev", "cosign_this_transaction": "Sopodpis te transakcije?", - "co_sign_transaction": "Sopodpis QR-airgapped transakcije" + "lets_start": "Začnimo", + "create": "Ustvari", + "provide_key": "Vnesite ključ", + "native_segwit_title": "Najb. praksa", + "wrapped_segwit_title": "Najb. združljivost", + "legacy_title": "Zastarelo", + "co_sign_transaction": "Sopodpis QR-airgapped transakcije", + "what_is_vault": "Trezor je", + "what_is_vault_numberOfWallets": " {m}-od-{n} multisig ", + "what_is_vault_wallet": "denarnica", + "vault_advanced_customize": "Nastavitve trezorja...", + "needs": "Zahtevana sta", + "what_is_vault_description_number_of_vault_keys": " {m} ključa trezorja, ", + "what_is_vault_description_to_spend": "tretji pa\npredstavlja rezervo.", + "quorum": "{m} od {n} kvorum", + "quorum_header": "Kvorum", + "of": "od", + "wallet_type": "Tip denarnice", + "view_key": "prikaži", + "invalid_mnemonics": "Zdi se, da to mnemonično seme ni veljavno", + "invalid_cosigner": "Neveljavni podatki sopodpisnika", + "invalid_cosigner_format": "Nepravilen sopodpisnik: to ni sopodpisnik za {format} obliko", + "create_new_key": "Ustvari novega", + "scan_or_open_file": "Skenirajte ali odprite datoteko", + "i_have_mnemonics": "Za ta ključ imam seme...", + "please_write_down_mnemonics": "Prosimo, zapišite si seznam besed (mnemonično seme) na list papirja. Lahko si zapišete tudi pozneje.", + "i_wrote_it_down": "V redu, sem si zapisal", + "type_your_mnemonics": "Vnesite seme za uvoz obstoječega ključa trezorja", + "this_is_cosigners_xpub": "To je xpub sopodpisnika, pripravljen za uvoz v drugo denarnico. Varno ga lahko delite.", + "wallet_key_created": "Ključ trezorja je bil ustvarjen. Vzemite si trenutek, ter zapišite seznam besed (mnemonično seme) na list papirja.", + "are_you_sure_seed_will_be_lost": "Ali ste prepričani? Če nimate varnostne kopije, bo vaše mnemonično seme izgubljeno", + "forget_this_seed": "Pozabi to seme in uporabi xpub", + "invalid_fingerprint": "Prstni odtis (fingerprint) tega semena se ne ujema s sopodpisnikovim", + "view_edit_cosigners": "Prikaži/uredi sopodpisnike", + "this_cosigner_is_already_imported": "Ta sopodpisnik je že uvožen", + "view_edit_cosigners_title": "Urejanje sopodpisnikov" } } diff --git a/screen/send/ScanQRCode.js b/screen/send/ScanQRCode.js index c2565d4b5..65ee2f528 100644 --- a/screen/send/ScanQRCode.js +++ b/screen/send/ScanQRCode.js @@ -70,6 +70,7 @@ const styles = StyleSheet.create({ position: 'absolute', }, backdoorInputWrapper: { position: 'absolute', left: '5%', top: '0%', width: '90%', height: '70%', backgroundColor: 'white' }, + progressWrapper: { position: 'absolute', right: '0%', top: '0%', backgroundColor: 'white' }, backdoorInput: { height: '50%', marginTop: 5, @@ -95,6 +96,8 @@ const ScanQRCode = () => { const isFocused = useIsFocused(); const [cameraStatus, setCameraStatus] = useState(RNCamera.Constants.CameraStatus.PENDING_AUTHORIZATION); const [backdoorPressed, setBackdoorPressed] = useState(0); + const [urTotal, setUrTotal] = useState(0); + const [urHave, setUrHave] = useState(0); const [backdoorText, setBackdoorText] = useState(''); const [backdoorVisible, setBackdoorVisible] = useState(false); const [animatedQRCodeData, setAnimatedQRCodeData] = useState({}); @@ -111,6 +114,8 @@ const ScanQRCode = () => { try { const [index, total] = extractSingleWorkload(ur); animatedQRCodeData[index + 'of' + total] = ur; + setUrTotal(total); + setUrHave(Object.values(animatedQRCodeData).length); if (Object.values(animatedQRCodeData).length === total) { const payload = decodeUR(Object.values(animatedQRCodeData)); // lets look inside that data @@ -264,6 +269,14 @@ const ScanQRCode = () => { )} + {urTotal > 0 && ( + + + {urHave} / {urTotal} + + + )} + {backdoorVisible && ( Provide QR code contents manually: @@ -291,6 +304,8 @@ const ScanQRCode = () => { // this might be a json string (for convenience - in case there are "\n" in there) } catch (_) { data = backdoorText; + } finally { + setBackdoorText(''); } if (data) onBarCodeRead({ data }); diff --git a/screen/send/confirm.js b/screen/send/confirm.js index 7d130fea2..dc18163a0 100644 --- a/screen/send/confirm.js +++ b/screen/send/confirm.js @@ -9,16 +9,6 @@ import { BitcoinUnit } from '../../models/bitcoinUnits'; import PropTypes from 'prop-types'; import ReactNativeHapticFeedback from 'react-native-haptic-feedback'; import Biometric from '../../class/biometrics'; -import { - HDLegacyElectrumSeedP2PKHWallet, - HDLegacyP2PKHWallet, - HDSegwitBech32Wallet, - HDSegwitP2SHWallet, - HDLegacyBreadwalletWallet, - LegacyWallet, - SegwitP2SHWallet, - SegwitBech32Wallet, -} from '../../class'; import loc, { formatBalance, formatBalanceWithoutSuffix } from '../../loc'; import { BlueCurrentTheme } from '../../components/themes'; import Notifications from '../../blue_modules/notifications'; @@ -89,21 +79,7 @@ export default class Confirm extends Component { } } - // wallets that support new createTransaction() instead of deprecated createTx() - if ( - [ - HDSegwitBech32Wallet.type, - HDSegwitP2SHWallet.type, - HDLegacyP2PKHWallet.type, - HDLegacyBreadwalletWallet.type, - HDLegacyElectrumSeedP2PKHWallet.type, - LegacyWallet.type, - SegwitP2SHWallet.type, - SegwitBech32Wallet.type, - ].includes(this.state.fromWallet.type) - ) { - amount = formatBalanceWithoutSuffix(amount, BitcoinUnit.BTC, false); - } + amount = formatBalanceWithoutSuffix(amount, BitcoinUnit.BTC, false); this.context.fetchAndSaveWalletTransactions(this.state.fromWallet.getID()); this.props.navigation.navigate('Success', { diff --git a/screen/wallets/addMultisig.js b/screen/wallets/addMultisig.js index 9f59cd0da..92e68e90d 100644 --- a/screen/wallets/addMultisig.js +++ b/screen/wallets/addMultisig.js @@ -42,7 +42,7 @@ const WalletsAddMultisig = () => { color: colors.alternativeTextColor, }, selectedItem: { - backgroundColor: colors.buttonDisabledTextColor, + backgroundColor: colors.elevated, }, deSelectedItem: { backgroundColor: 'transparent', @@ -146,21 +146,21 @@ const WalletsAddMultisig = () => { onPress={setFormatP2wsh} title={`${loc.multisig.native_segwit_title} (${MultisigHDWallet.FORMAT_P2WSH})`} checkmark={isP2wsh()} - containerStyle={[styles.borderRadius6, isP2wsh() ? stylesHook.selectedItem : stylesHook.deSelectedItem]} + containerStyle={[styles.borderRadius6, styles.item, isP2wsh() ? stylesHook.selectedItem : stylesHook.deSelectedItem]} /> @@ -239,6 +239,9 @@ const styles = StyleSheet.create({ justifyContent: 'flex-end', margin: 0, }, + item: { + paddingHorizontal: 0, + }, descriptionContainer: { alignContent: 'center', justifyContent: 'center', diff --git a/screen/wallets/addMultisigStep2.js b/screen/wallets/addMultisigStep2.js index 80caafa29..26844d84c 100644 --- a/screen/wallets/addMultisigStep2.js +++ b/screen/wallets/addMultisigStep2.js @@ -1,5 +1,5 @@ /* global alert */ -import React, { useRef, useState } from 'react'; +import React, { useContext, useRef, useState } from 'react'; import { ActivityIndicator, FlatList, @@ -32,7 +32,6 @@ import Modal from 'react-native-modal'; import { getSystemName } from 'react-native-device-info'; import ImagePicker from 'react-native-image-picker'; import ScanQRCode from '../send/ScanQRCode'; -import WalletImport from '../../class/wallet-import'; import QRCode from 'react-native-qrcode-svg'; import { SquareButton } from '../../components/SquareButton'; import { SafeAreaView } from 'react-native-safe-area-context'; @@ -43,13 +42,17 @@ import MultipleStepsListItem, { import Clipboard from '@react-native-community/clipboard'; import showPopupMenu from 'react-native-popup-menu-android'; import ToolTip from 'react-native-tooltip'; +import ReactNativeHapticFeedback from 'react-native-haptic-feedback'; +import { BlueStorageContext } from '../../blue_modules/storage-context'; +const A = require('../../blue_modules/analytics'); const fs = require('../../blue_modules/fs'); const isDesktop = getSystemName() === 'Mac OS X'; const LocalQRCode = require('@remobile/react-native-qrcode-local-image'); const staticCache = {}; const WalletsAddMultisigStep2 = () => { + const { addWallet, saveToDisk, setNewWalletAdded } = useContext(BlueStorageContext); const { colors } = useTheme(); const navigation = useNavigation(); @@ -125,7 +128,7 @@ const WalletsAddMultisigStep2 = () => { }, }); - const onCreate = () => { + const onCreate = async () => { setIsLoading(true); const w = new MultisigHDWallet(); w.setM(m); @@ -149,7 +152,14 @@ const WalletsAddMultisigStep2 = () => { w.addCosigner(cc[0], cc[1], cc[2]); } w.setLabel('Multisig Vault'); - WalletImport._saveWallet(w); + await w.fetchBalance(); + + addWallet(w); + await saveToDisk(); + setNewWalletAdded(true); + A(A.ENUM.CREATED_WALLET); + ReactNativeHapticFeedback.trigger('notificationSuccess', { ignoreAndroidSystemSettings: false }); + navigation.dangerouslyGetParent().pop(); }; diff --git a/screen/wallets/import.js b/screen/wallets/import.js index fad69981e..ed06fcb00 100644 --- a/screen/wallets/import.js +++ b/screen/wallets/import.js @@ -234,7 +234,7 @@ const WalletsImport = () => { onPress={importButtonPressed} /> - + {Platform.select({ diff --git a/tests/e2e/bluewallet.spec.js b/tests/e2e/bluewallet.spec.js index 5ca8bcf45..71f813ca2 100644 --- a/tests/e2e/bluewallet.spec.js +++ b/tests/e2e/bluewallet.spec.js @@ -551,6 +551,41 @@ describe('BlueWallet UI Tests', () => { expect(element(by.id('TransactionValue'))).toHaveText('0.0001'); expect(element(by.id('TransactionAddress'))).toHaveText('BC1QH6TF004TY7Z7UN2V5NTU4MKF630545GVHS45U7'); }); + + it('can import multisig setup from UR (ver1) QRs 2 frames', async () => { + await yo('WalletsList'); + await element(by.id('WalletsList')).swipe('left', 'fast', 1); // in case emu screen is small and it doesnt fit + // going to Import Wallet screen and importing mnemonic + await element(by.id('CreateAWallet')).tap(); + await element(by.id('ImportWallet')).tap(); + await element(by.id('ScanImport')).tap(); + + const urs = [ + 'UR:BYTES/1OF2/J8RX04F2WJ9SSY577U30R55ELM4LUCJCXJVJTD60SYV9A286Q0AQH7QXL6/TYQMJGEQGFK82E2HV9KXCET5YPXH2MR5D9EKJEEQWDJHGATSYPNXJMR9PG3JQARGD9EJQENFD3JJQCM0DE6XZ6TWWVSX7MNV0YS8QATZD35KXGRTV4UHXGRPDEJZQ6TNYPEKZEN9YP6X7Z3RYPJXJUM5WF5KYAT5V5SXZMT0DENJQCM0WD5KWMN9WFES5GC2FESK6EF6YPXH2MR5D9EKJEEQ2ESH2MR5PFGX7MRFVDUN5GPJYPHKVGPJPFZX2UNFWESHG6T0DCAZQMF0XSUZWTESYUHNQFE0XGNS53N0WFKKZAP6YPGRY46NFQ9Q53PNXAZ5Z3PC8QAZQKNSW43RWDRFDFCXV6Z92F9YU6NGGD94S5NNWP2XGNZ22C6K2M69D4F4YKNYFPC5GANS', + 'UR:BYTES/2OF2/J8RX04F2WJ9SSY577U30R55ELM4LUCJCXJVJTD60SYV9A286Q0AQH7QXL6/8944VARY2EZHJ62CDVMHQKRC2F3XVKN629M8X3ZXWPNYGJZ9FPT8G4NS0Q6YG73EG3R42468DCE9S6E40FRN2AF5X4G4GNTNT9FNYAN2DA5YU5G2PGCNVWZYGSMRQVE6YPD8QATZXU6K6S298PZK57TC2DAX772SD4RKUEP4G5MY672YXAQ5C36WDEJ8YA2HWC6NY7RS0F5K6KJ3FD6KKAMKG4N9S4ZGW9K5SWRWVF3XXDNRVDGR2APJV9XNXMTHWVEHQJ6E2DHYKUZTF4XHJARYVF8Y2KJX24UYK7N6W3V5VNFC2PHQ5ZSJDYL5T', + ]; + + await waitFor(element(by.id('UrProgressBar'))).toBeNotVisible(); + + for (const ur of urs) { + // tapping 10 times invisible button is a backdoor: + for (let c = 0; c <= 10; c++) { + await element(by.id('ScanQrBackdoorButton')).tap(); + } + await element(by.id('scanQrBackdoorInput')).replaceText(ur); + await element(by.id('scanQrBackdoorOkButton')).tap(); + await waitFor(element(by.id('UrProgressBar'))).toBeVisible(); + } + + if (process.env.TRAVIS) await sleep(60000); + await sup('OK', 3 * 61000); // waiting for wallet import + await element(by.text('OK')).tap(); + // ok, wallet imported + + // lets go inside wallet + const expectedWalletLabel = 'Multisig Vault'; + await element(by.text(expectedWalletLabel)).tap(); + }); }); async function sleep(ms) { diff --git a/tests/integration/multisig-hd-wallet.test.js b/tests/integration/multisig-hd-wallet.test.js index fd1be9ec8..a80bf11f0 100644 --- a/tests/integration/multisig-hd-wallet.test.js +++ b/tests/integration/multisig-hd-wallet.test.js @@ -46,6 +46,6 @@ describe('multisig-hd-wallet', () => { await w.fetchBalance(); await w.fetchTransactions(); - assert.strictEqual(w.getTransactions().length, 5); + assert.ok(w.getTransactions().length >= 6); }); });