mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-03-26 08:55:56 +01:00
Merge branch 'master' into limpbrains-sss
This commit is contained in:
commit
b9195d3a49
62 changed files with 1478 additions and 491 deletions
|
@ -44,7 +44,6 @@ import Lnurl from './class/lnurl';
|
|||
import { BlueStorageContext } from './blue_modules/storage-context';
|
||||
import ToolTipMenu from './components/TooltipMenu';
|
||||
|
||||
/** @type {AppStorage} */
|
||||
const { height, width } = Dimensions.get('window');
|
||||
const aspectRatio = height / width;
|
||||
let isIpad;
|
||||
|
|
|
@ -15,6 +15,7 @@ import EncryptStorage from './screen/settings/encryptStorage';
|
|||
import PlausibleDeniability from './screen/plausibledeniability';
|
||||
import LightningSettings from './screen/settings/lightningSettings';
|
||||
import ElectrumSettings from './screen/settings/electrumSettings';
|
||||
import TorSettings from './screen/settings/torSettings';
|
||||
import Tools from './screen/settings/tools';
|
||||
import GeneralSettings from './screen/settings/GeneralSettings';
|
||||
import NetworkSettings from './screen/settings/NetworkSettings';
|
||||
|
@ -153,6 +154,7 @@ const WalletsRoot = () => {
|
|||
/>
|
||||
<WalletsStack.Screen name="LightningSettings" component={LightningSettings} options={LightningSettings.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="ElectrumSettings" component={ElectrumSettings} options={ElectrumSettings.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="TorSettings" component={TorSettings} options={TorSettings.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="SettingsPrivacy" component={SettingsPrivacy} options={SettingsPrivacy.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="Tools" component={Tools} options={Tools.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="LNDViewInvoice" component={LNDViewInvoice} options={LNDViewInvoice.navigationOptions(theme)} />
|
||||
|
|
|
@ -7,7 +7,6 @@ import { SafeAreaView } from 'react-native-safe-area-context';
|
|||
import { StackActions, useNavigation, useRoute } from '@react-navigation/native';
|
||||
import { BlueStorageContext } from './blue_modules/storage-context';
|
||||
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||
/** @type {AppStorage} */
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
|
|
|
@ -6,12 +6,12 @@ import {
|
|||
useInstalled,
|
||||
transferCurrentComplicationUserInfo,
|
||||
} from 'react-native-watch-connectivity';
|
||||
import { InteractionManager } from 'react-native';
|
||||
import { Chain } from './models/bitcoinUnits';
|
||||
import loc, { formatBalance, transactionTimeToReadable } from './loc';
|
||||
import { BlueStorageContext } from './blue_modules/storage-context';
|
||||
import Notifications from './blue_modules/notifications';
|
||||
import { FiatUnit } from './models/fiatUnit';
|
||||
import { MultisigHDWallet } from './class';
|
||||
|
||||
function WatchConnectivity() {
|
||||
const { walletsInitialized, wallets, fetchWalletTransactions, saveToDisk, txMetadata, preferredFiatCurrency } = useContext(
|
||||
|
@ -106,7 +106,7 @@ function WatchConnectivity() {
|
|||
}
|
||||
};
|
||||
|
||||
const sendWalletsToWatch = () => {
|
||||
const sendWalletsToWatch = async () => {
|
||||
if (!Array.isArray(wallets)) {
|
||||
console.log('No Wallets set to sync with Watch app. Exiting...');
|
||||
return;
|
||||
|
@ -116,100 +116,102 @@ function WatchConnectivity() {
|
|||
return;
|
||||
}
|
||||
|
||||
return InteractionManager.runAfterInteractions(async () => {
|
||||
const walletsToProcess = [];
|
||||
const walletsToProcess = [];
|
||||
|
||||
for (const wallet of wallets) {
|
||||
let receiveAddress;
|
||||
if (wallet.getAddressAsync) {
|
||||
if (wallet.chain === Chain.ONCHAIN) {
|
||||
try {
|
||||
receiveAddress = await wallet.getAddressAsync();
|
||||
} catch (_) {}
|
||||
if (!receiveAddress) {
|
||||
// either sleep expired or getAddressAsync threw an exception
|
||||
receiveAddress = wallet._getExternalAddressByIndex(wallet.next_free_address_index);
|
||||
}
|
||||
} else if (wallet.chain === Chain.OFFCHAIN) {
|
||||
try {
|
||||
await wallet.getAddressAsync();
|
||||
receiveAddress = wallet.getAddress();
|
||||
} catch (_) {}
|
||||
if (!receiveAddress) {
|
||||
// either sleep expired or getAddressAsync threw an exception
|
||||
receiveAddress = wallet.getAddress();
|
||||
}
|
||||
for (const wallet of wallets) {
|
||||
let receiveAddress;
|
||||
if (wallet.getAddressAsync) {
|
||||
if (wallet.chain === Chain.ONCHAIN) {
|
||||
try {
|
||||
receiveAddress = await wallet.getAddressAsync();
|
||||
} catch (_) {}
|
||||
if (!receiveAddress) {
|
||||
// either sleep expired or getAddressAsync threw an exception
|
||||
receiveAddress = wallet._getExternalAddressByIndex(wallet.next_free_address_index);
|
||||
}
|
||||
} else if (wallet.chain === Chain.OFFCHAIN) {
|
||||
try {
|
||||
await wallet.getAddressAsync();
|
||||
receiveAddress = wallet.getAddress();
|
||||
} catch (_) {}
|
||||
if (!receiveAddress) {
|
||||
// either sleep expired or getAddressAsync threw an exception
|
||||
receiveAddress = wallet.getAddress();
|
||||
}
|
||||
}
|
||||
const transactions = wallet.getTransactions(10);
|
||||
const watchTransactions = [];
|
||||
for (const transaction of transactions) {
|
||||
let type = 'pendingConfirmation';
|
||||
let memo = '';
|
||||
let amount = 0;
|
||||
}
|
||||
const transactions = wallet.getTransactions(10);
|
||||
const watchTransactions = [];
|
||||
for (const transaction of transactions) {
|
||||
let type = 'pendingConfirmation';
|
||||
let memo = '';
|
||||
let amount = 0;
|
||||
|
||||
if ('confirmations' in transaction && !(transaction.confirmations > 0)) {
|
||||
if ('confirmations' in transaction && !(transaction.confirmations > 0)) {
|
||||
type = 'pendingConfirmation';
|
||||
} else if (transaction.type === 'user_invoice' || transaction.type === 'payment_request') {
|
||||
const currentDate = new Date();
|
||||
const now = (currentDate.getTime() / 1000) | 0;
|
||||
const invoiceExpiration = transaction.timestamp + transaction.expire_time;
|
||||
|
||||
if (invoiceExpiration > now) {
|
||||
type = 'pendingConfirmation';
|
||||
} else if (transaction.type === 'user_invoice' || transaction.type === 'payment_request') {
|
||||
const currentDate = new Date();
|
||||
const now = (currentDate.getTime() / 1000) | 0;
|
||||
const invoiceExpiration = transaction.timestamp + transaction.expire_time;
|
||||
|
||||
if (invoiceExpiration > now) {
|
||||
type = 'pendingConfirmation';
|
||||
} else if (invoiceExpiration < now) {
|
||||
if (transaction.ispaid) {
|
||||
type = 'received';
|
||||
} else {
|
||||
type = 'sent';
|
||||
}
|
||||
}
|
||||
} else if (transaction.value / 100000000 < 0) {
|
||||
type = 'sent';
|
||||
} else {
|
||||
type = 'received';
|
||||
}
|
||||
if (transaction.type === 'user_invoice' || transaction.type === 'payment_request') {
|
||||
amount = isNaN(transaction.value) ? '0' : amount;
|
||||
const currentDate = new Date();
|
||||
const now = (currentDate.getTime() / 1000) | 0;
|
||||
const invoiceExpiration = transaction.timestamp + transaction.expire_time;
|
||||
|
||||
if (invoiceExpiration > now) {
|
||||
amount = formatBalance(transaction.value, wallet.getPreferredBalanceUnit(), true).toString();
|
||||
} else if (invoiceExpiration < now) {
|
||||
if (transaction.ispaid) {
|
||||
amount = formatBalance(transaction.value, wallet.getPreferredBalanceUnit(), true).toString();
|
||||
} else {
|
||||
amount = loc.lnd.expired;
|
||||
}
|
||||
} else if (invoiceExpiration < now) {
|
||||
if (transaction.ispaid) {
|
||||
type = 'received';
|
||||
} else {
|
||||
type = 'sent';
|
||||
}
|
||||
}
|
||||
} else if (transaction.value / 100000000 < 0) {
|
||||
type = 'sent';
|
||||
} else {
|
||||
type = 'received';
|
||||
}
|
||||
if (transaction.type === 'user_invoice' || transaction.type === 'payment_request') {
|
||||
amount = isNaN(transaction.value) ? '0' : amount;
|
||||
const currentDate = new Date();
|
||||
const now = (currentDate.getTime() / 1000) | 0;
|
||||
const invoiceExpiration = transaction.timestamp + transaction.expire_time;
|
||||
|
||||
if (invoiceExpiration > now) {
|
||||
amount = formatBalance(transaction.value, wallet.getPreferredBalanceUnit(), true).toString();
|
||||
} else if (invoiceExpiration < now) {
|
||||
if (transaction.ispaid) {
|
||||
amount = formatBalance(transaction.value, wallet.getPreferredBalanceUnit(), true).toString();
|
||||
} else {
|
||||
amount = loc.lnd.expired;
|
||||
}
|
||||
} else {
|
||||
amount = formatBalance(transaction.value, wallet.getPreferredBalanceUnit(), true).toString();
|
||||
}
|
||||
if (txMetadata[transaction.hash] && txMetadata[transaction.hash].memo) {
|
||||
memo = txMetadata[transaction.hash].memo;
|
||||
} else if (transaction.memo) {
|
||||
memo = transaction.memo;
|
||||
}
|
||||
const watchTX = { type, amount, memo, time: transactionTimeToReadable(transaction.received) };
|
||||
watchTransactions.push(watchTX);
|
||||
} else {
|
||||
amount = formatBalance(transaction.value, wallet.getPreferredBalanceUnit(), true).toString();
|
||||
}
|
||||
walletsToProcess.push({
|
||||
label: wallet.getLabel(),
|
||||
balance: formatBalance(Number(wallet.getBalance()), wallet.getPreferredBalanceUnit(), true),
|
||||
type: wallet.type,
|
||||
preferredBalanceUnit: wallet.getPreferredBalanceUnit(),
|
||||
receiveAddress: receiveAddress,
|
||||
transactions: watchTransactions,
|
||||
xpub: wallet.getXpub() ? wallet.getXpub() : wallet.getSecret(),
|
||||
hideBalance: wallet.hideBalance,
|
||||
});
|
||||
if (txMetadata[transaction.hash] && txMetadata[transaction.hash].memo) {
|
||||
memo = txMetadata[transaction.hash].memo;
|
||||
} else if (transaction.memo) {
|
||||
memo = transaction.memo;
|
||||
}
|
||||
const watchTX = { type, amount, memo, time: transactionTimeToReadable(transaction.received) };
|
||||
watchTransactions.push(watchTX);
|
||||
}
|
||||
updateApplicationContext({ wallets: walletsToProcess, randomID: Math.floor(Math.random() * 11) });
|
||||
});
|
||||
|
||||
const walletInformation = {
|
||||
label: wallet.getLabel(),
|
||||
balance: formatBalance(Number(wallet.getBalance()), wallet.getPreferredBalanceUnit(), true),
|
||||
type: wallet.type,
|
||||
preferredBalanceUnit: wallet.getPreferredBalanceUnit(),
|
||||
receiveAddress: receiveAddress,
|
||||
transactions: watchTransactions,
|
||||
hideBalance: wallet.hideBalance,
|
||||
};
|
||||
if (wallet.chain === Chain.ONCHAIN && wallet.type !== MultisigHDWallet.type) {
|
||||
walletInformation.push({ xpub: wallet.getXpub() ? wallet.getXpub() : wallet.getSecret() });
|
||||
}
|
||||
walletsToProcess.push(walletInformation);
|
||||
}
|
||||
updateApplicationContext({ wallets: walletsToProcess, randomID: Math.floor(Math.random() * 11) });
|
||||
};
|
||||
|
||||
return null;
|
||||
|
|
18
__mocks__/react-native-tor.js
vendored
Normal file
18
__mocks__/react-native-tor.js
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
/* global jest */
|
||||
|
||||
export const startIfNotStarted = jest.fn(async (key, value, callback) => {
|
||||
return 666;
|
||||
});
|
||||
|
||||
|
||||
export const get = jest.fn();
|
||||
export const post = jest.fn();
|
||||
export const deleteMock = jest.fn();
|
||||
export const stopIfRunning = jest.fn();
|
||||
export const getDaemonStatus = jest.fn();
|
||||
|
||||
const mock = jest.fn().mockImplementation(() => {
|
||||
return { startIfNotStarted, get, post, delete: deleteMock, stopIfRunning, getDaemonStatus };
|
||||
});
|
||||
|
||||
export default mock;
|
|
@ -177,6 +177,7 @@ android {
|
|||
|
||||
dependencies {
|
||||
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||
implementation files("../../node_modules/react-native-tor/android/libs/sifir_android.aar")
|
||||
//noinspection GradleDynamicVersion
|
||||
|
||||
androidTestImplementation('com.wix:detox:+') {
|
||||
|
|
4
android/app/proguard-rules.pro
vendored
4
android/app/proguard-rules.pro
vendored
|
@ -8,3 +8,7 @@
|
|||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
-keep class com.sifir.** { *;}
|
||||
-keep interface com.sifir.** { *;}
|
||||
-keep enum com.sifir.** { *;}
|
|
@ -14,6 +14,7 @@
|
|||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:allowBackup="false"
|
||||
android:extractNativeLibs="true"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:usesCleartextTraffic="true"
|
||||
android:supportsRtl="true"
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
buildscript {
|
||||
ext {
|
||||
minSdkVersion = 21
|
||||
minSdkVersion = 26
|
||||
supportLibVersion = "28.0.0"
|
||||
buildToolsVersion = "29.0.2"
|
||||
compileSdkVersion = 29
|
||||
|
|
|
@ -13,7 +13,7 @@ if [ -f $FILENAME ]; then
|
|||
APTZ=`curl "https://$APPETIZE@api.appetize.io/v1/apps" -F "file=@$FILENAME" -F "platform=android"`
|
||||
echo Apptezize response:
|
||||
echo $APTZ
|
||||
APPURL=`node -e "let e = JSON.parse('$APTZ'); console.log(e.publicURL);"`
|
||||
APPURL=`node -e "let e = JSON.parse('$APTZ'); console.log(e.publicURL + '?device=pixel4');"`
|
||||
echo App url: $APPURL
|
||||
PR=`node scripts/appcenter-post-build-get-pr-number.js`
|
||||
echo PR: $PR
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* global alert */
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
import { Platform, Alert } from 'react-native';
|
||||
import { Alert } from 'react-native';
|
||||
import { AppStorage, LegacyWallet, SegwitBech32Wallet, SegwitP2SHWallet } from '../class';
|
||||
import DefaultPreference from 'react-native-default-preference';
|
||||
import RNWidgetCenter from 'react-native-widget-center';
|
||||
|
@ -9,6 +9,35 @@ const bitcoin = require('bitcoinjs-lib');
|
|||
const ElectrumClient = require('electrum-client');
|
||||
const reverse = require('buffer-reverse');
|
||||
const BigNumber = require('bignumber.js');
|
||||
const torrific = require('../blue_modules/torrific');
|
||||
const Realm = require('realm');
|
||||
|
||||
let _realm;
|
||||
async function _getRealm() {
|
||||
if (_realm) return _realm;
|
||||
|
||||
const password = bitcoin.crypto.sha256(Buffer.from('fyegjitkyf[eqjnc.lf')).toString('hex');
|
||||
const buf = Buffer.from(password + password, 'hex');
|
||||
const encryptionKey = Int8Array.from(buf);
|
||||
const path = 'electrumcache.realm';
|
||||
|
||||
const schema = [
|
||||
{
|
||||
name: 'Cache',
|
||||
primaryKey: 'cache_key',
|
||||
properties: {
|
||||
cache_key: { type: 'string', indexed: true },
|
||||
cache_value: 'string', // stringified json
|
||||
},
|
||||
},
|
||||
];
|
||||
_realm = await Realm.open({
|
||||
schema,
|
||||
path,
|
||||
encryptionKey,
|
||||
});
|
||||
return _realm;
|
||||
}
|
||||
|
||||
const storageKey = 'ELECTRUM_PEERS';
|
||||
const defaultPeer = { host: 'electrum1.bluewallet.io', ssl: '443' };
|
||||
|
@ -48,11 +77,19 @@ async function connectMain() {
|
|||
usingPeer = savedPeer;
|
||||
}
|
||||
|
||||
await DefaultPreference.setName('group.io.bluewallet.bluewallet');
|
||||
try {
|
||||
await DefaultPreference.setName('group.io.bluewallet.bluewallet');
|
||||
await DefaultPreference.set(AppStorage.ELECTRUM_HOST, usingPeer.host);
|
||||
await DefaultPreference.set(AppStorage.ELECTRUM_TCP_PORT, usingPeer.tcp);
|
||||
await DefaultPreference.set(AppStorage.ELECTRUM_SSL_PORT, usingPeer.ssl);
|
||||
if (usingPeer.host.endsWith('onion')) {
|
||||
const randomPeer = await getRandomHardcodedPeer();
|
||||
await DefaultPreference.set(AppStorage.ELECTRUM_HOST, randomPeer.host);
|
||||
await DefaultPreference.set(AppStorage.ELECTRUM_TCP_PORT, randomPeer.tcp);
|
||||
await DefaultPreference.set(AppStorage.ELECTRUM_SSL_PORT, randomPeer.ssl);
|
||||
} else {
|
||||
await DefaultPreference.set(AppStorage.ELECTRUM_HOST, usingPeer.host);
|
||||
await DefaultPreference.set(AppStorage.ELECTRUM_TCP_PORT, usingPeer.tcp);
|
||||
await DefaultPreference.set(AppStorage.ELECTRUM_SSL_PORT, usingPeer.ssl);
|
||||
}
|
||||
|
||||
RNWidgetCenter.reloadAllTimelines();
|
||||
} catch (e) {
|
||||
// Must be running on Android
|
||||
|
@ -61,19 +98,26 @@ async function connectMain() {
|
|||
|
||||
try {
|
||||
console.log('begin connection:', JSON.stringify(usingPeer));
|
||||
mainClient = new ElectrumClient(usingPeer.ssl || usingPeer.tcp, usingPeer.host, usingPeer.ssl ? 'tls' : 'tcp');
|
||||
mainClient = new ElectrumClient(
|
||||
usingPeer.host.endsWith('.onion') ? torrific : global.net,
|
||||
global.tls,
|
||||
usingPeer.ssl || usingPeer.tcp,
|
||||
usingPeer.host,
|
||||
usingPeer.ssl ? 'tls' : 'tcp',
|
||||
);
|
||||
mainClient.onError = function (e) {
|
||||
if (Platform.OS === 'android' && mainConnected) {
|
||||
// android sockets are buggy and dont always issue CLOSE event, which actually makes the persistence code to reconnect.
|
||||
// so lets do it manually, but only if we were previously connected (mainConnected), otherwise theres other
|
||||
console.log('electrum mainClient.onError():', e.message);
|
||||
if (mainConnected) {
|
||||
// most likely got a timeout from electrum ping. lets reconnect
|
||||
// but only if we were previously connected (mainConnected), otherwise theres other
|
||||
// code which does connection retries
|
||||
mainClient.close();
|
||||
mainConnected = false;
|
||||
setTimeout(connectMain, 500);
|
||||
// dropping `mainConnected` flag ensures there wont be reconnection race condition if several
|
||||
// errors triggered
|
||||
console.log('reconnecting after socket error');
|
||||
return;
|
||||
setTimeout(connectMain, usingPeer.host.endsWith('.onion') ? 4000 : 500);
|
||||
}
|
||||
mainConnected = false;
|
||||
};
|
||||
const ver = await mainClient.initElectrum({ client: 'bluewallet', version: '1.4' });
|
||||
if (ver && ver[0]) {
|
||||
|
@ -104,6 +148,7 @@ async function connectMain() {
|
|||
if (connectionAttempt >= 5) {
|
||||
presentNetworkErrorAlert(usingPeer);
|
||||
} else {
|
||||
console.log('reconnection attempt #', connectionAttempt);
|
||||
setTimeout(connectMain, 500);
|
||||
}
|
||||
}
|
||||
|
@ -249,8 +294,8 @@ module.exports.getConfig = async function () {
|
|||
return {
|
||||
host: mainClient.host,
|
||||
port: mainClient.port,
|
||||
status: mainClient.status ? 1 : 0,
|
||||
serverName,
|
||||
connected: mainClient.timeLastCall !== 0 && mainClient.status,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -453,15 +498,38 @@ module.exports.multiGetHistoryByAddress = async function (addresses, batchsize)
|
|||
return ret;
|
||||
};
|
||||
|
||||
module.exports.multiGetTransactionByTxid = async function (txids, batchsize, verbose) {
|
||||
module.exports.multiGetTransactionByTxid = async function (txids, batchsize, verbose = true) {
|
||||
batchsize = batchsize || 45;
|
||||
// this value is fine-tuned so althrough wallets in test suite will occasionally
|
||||
// throw 'response too large (over 1,000,000 bytes', test suite will pass
|
||||
verbose = verbose !== false;
|
||||
if (!mainClient) throw new Error('Electrum client is not connected');
|
||||
const ret = {};
|
||||
txids = [...new Set(txids)]; // deduplicate just for any case
|
||||
|
||||
// lets try cache first:
|
||||
const realm = await _getRealm();
|
||||
const cacheKeySuffix = verbose ? '_verbose' : '_non_verbose';
|
||||
const keysCacheMiss = [];
|
||||
for (const txid of txids) {
|
||||
const jsonString = realm.objectForPrimaryKey('Cache', txid + cacheKeySuffix); // search for a realm object with a primary key
|
||||
if (jsonString && jsonString.cache_value) {
|
||||
try {
|
||||
ret[txid] = JSON.parse(jsonString.cache_value);
|
||||
} catch (error) {
|
||||
console.log(error, 'cache failed to parse', jsonString.cache_value);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ret[txid]) keysCacheMiss.push(txid);
|
||||
}
|
||||
|
||||
if (keysCacheMiss.length === 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
txids = keysCacheMiss;
|
||||
// end cache
|
||||
|
||||
const chunks = splitIntoChunks(txids, batchsize);
|
||||
for (const chunk of chunks) {
|
||||
let results = [];
|
||||
|
@ -505,6 +573,23 @@ module.exports.multiGetTransactionByTxid = async function (txids, batchsize, ver
|
|||
}
|
||||
}
|
||||
|
||||
// saving cache:
|
||||
realm.write(() => {
|
||||
for (const txid of Object.keys(ret)) {
|
||||
if (verbose && (!ret[txid].confirmations || ret[txid].confirmations < 7)) continue;
|
||||
// dont cache immature txs, but only for 'verbose', since its fully decoded tx jsons. non-verbose are just plain
|
||||
// strings txhex
|
||||
realm.create(
|
||||
'Cache',
|
||||
{
|
||||
cache_key: txid + cacheKeySuffix,
|
||||
cache_value: JSON.stringify(ret[txid]),
|
||||
},
|
||||
Realm.UpdateMode.Modified,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
|
@ -522,18 +607,19 @@ module.exports.waitTillConnected = async function () {
|
|||
waitTillConnectedInterval = setInterval(() => {
|
||||
if (mainConnected) {
|
||||
clearInterval(waitTillConnectedInterval);
|
||||
resolve(true);
|
||||
return resolve(true);
|
||||
}
|
||||
|
||||
if (wasConnectedAtLeastOnce && mainClient.status === 1) {
|
||||
clearInterval(waitTillConnectedInterval);
|
||||
mainConnected = true;
|
||||
resolve(true);
|
||||
return resolve(true);
|
||||
}
|
||||
|
||||
if (retriesCounter++ >= 30) {
|
||||
if (wasConnectedAtLeastOnce && retriesCounter++ >= 30) {
|
||||
// `wasConnectedAtLeastOnce` needed otherwise theres gona be a race condition with the code that connects
|
||||
// electrum during app startup
|
||||
clearInterval(waitTillConnectedInterval);
|
||||
connectionAttempt = 0;
|
||||
presentNetworkErrorAlert();
|
||||
reject(new Error('Waiting for Electrum connection timeout'));
|
||||
}
|
||||
|
@ -600,7 +686,12 @@ module.exports.calcEstimateFeeFromFeeHistorgam = function (numberOfBlocks, feeHi
|
|||
};
|
||||
|
||||
module.exports.estimateFees = async function () {
|
||||
const histogram = await mainClient.mempool_getFeeHistogram();
|
||||
let histogram;
|
||||
try {
|
||||
histogram = await Promise.race([mainClient.mempool_getFeeHistogram(), new Promise(resolve => setTimeout(resolve, 5000))]);
|
||||
} catch (_) {}
|
||||
|
||||
if (!histogram) throw new Error('timeout while getting mempool_getFeeHistogram');
|
||||
|
||||
// fetching what electrum (which uses bitcoin core) thinks about fees:
|
||||
const _fast = await module.exports.estimateFee(1);
|
||||
|
@ -685,13 +776,20 @@ module.exports.calculateBlockTime = function (height) {
|
|||
* @returns {Promise<boolean>} Whether provided host:port is a valid electrum server
|
||||
*/
|
||||
module.exports.testConnection = async function (host, tcpPort, sslPort) {
|
||||
const client = new ElectrumClient(sslPort || tcpPort, host, sslPort ? 'tls' : 'tcp');
|
||||
const client = new ElectrumClient(
|
||||
host.endsWith('.onion') ? torrific : global.net,
|
||||
global.tls,
|
||||
sslPort || tcpPort,
|
||||
host,
|
||||
sslPort ? 'tls' : 'tcp',
|
||||
);
|
||||
|
||||
client.onError = () => {}; // mute
|
||||
let timeoutId = false;
|
||||
try {
|
||||
const rez = await Promise.race([
|
||||
new Promise(resolve => {
|
||||
timeoutId = setTimeout(() => resolve('timeout'), 3000);
|
||||
timeoutId = setTimeout(() => resolve('timeout'), host.endsWith('.onion') ? 21000 : 5000);
|
||||
}),
|
||||
client.connect(),
|
||||
]);
|
||||
|
@ -714,6 +812,7 @@ module.exports.forceDisconnect = () => {
|
|||
};
|
||||
|
||||
module.exports.hardcodedPeers = hardcodedPeers;
|
||||
module.exports.getRandomHardcodedPeer = getRandomHardcodedPeer;
|
||||
|
||||
const splitIntoChunks = function (arr, chunkSize) {
|
||||
const groups = [];
|
||||
|
|
|
@ -61,7 +61,6 @@ async function updateExchangeRate() {
|
|||
try {
|
||||
rate = await getFiatRate(preferredFiatCurrency.endPointKey);
|
||||
} catch (Err) {
|
||||
console.warn(Err);
|
||||
const lastSavedExchangeRate = JSON.parse(await AsyncStorage.getItem(AppStorage.EXCHANGE_RATES));
|
||||
exchangeRates['BTC_' + preferredFiatCurrency.endPointKey] = lastSavedExchangeRate['BTC_' + preferredFiatCurrency.endPointKey] * 1;
|
||||
return;
|
||||
|
|
|
@ -106,7 +106,7 @@ const showImagePickerAndReadImage = () => {
|
|||
},
|
||||
response => {
|
||||
if (response.uri) {
|
||||
const uri = Platform.OS === 'ios' ? response.uri.toString().replace('file://', '') : response.path.toString();
|
||||
const uri = response.uri.toString().replace('file://', '');
|
||||
LocalQRCode.decode(uri, (error, result) => {
|
||||
if (!error) {
|
||||
resolve(result);
|
||||
|
@ -130,7 +130,7 @@ const takePhotoWithImagePickerAndReadPhoto = () => {
|
|||
},
|
||||
response => {
|
||||
if (response.uri) {
|
||||
const uri = Platform.OS === 'ios' ? response.uri.toString().replace('file://', '') : response.path.toString();
|
||||
const uri = response.uri.toString().replace('file://', '');
|
||||
LocalQRCode.decode(uri, (error, result) => {
|
||||
if (!error) {
|
||||
resolve(result);
|
||||
|
@ -174,7 +174,7 @@ const showFilePickerAndReadFile = async function () {
|
|||
|
||||
if (res?.type === DocumentPicker.types.images || res?.type?.startsWith('image/')) {
|
||||
return new Promise(resolve => {
|
||||
const uri = Platform.OS === 'ios' ? res.uri.toString().replace('file://', '') : res.uri;
|
||||
const uri = res.uri.toString().replace('file://', '');
|
||||
LocalQRCode.decode(decodeURI(uri), (error, result) => {
|
||||
if (!error) {
|
||||
resolve({ data: result, uri: decodeURI(res.uri) });
|
||||
|
|
287
blue_modules/torrific.js
Normal file
287
blue_modules/torrific.js
Normal file
|
@ -0,0 +1,287 @@
|
|||
import Tor from 'react-native-tor';
|
||||
const tor = Tor();
|
||||
|
||||
/**
|
||||
* TOR wrapper mimicking Frisbee interface
|
||||
*/
|
||||
class Torsbee {
|
||||
baseURI = '';
|
||||
|
||||
static _testConn;
|
||||
static _resolveReference;
|
||||
static _rejectReference;
|
||||
|
||||
constructor(opts) {
|
||||
opts = opts || {};
|
||||
this.baseURI = opts.baseURI || this.baseURI;
|
||||
}
|
||||
|
||||
async get(path, options) {
|
||||
console.log('TOR: starting...');
|
||||
const socksProxy = await tor.startIfNotStarted();
|
||||
console.log('TOR: started', await tor.getDaemonStatus(), 'on local port', socksProxy);
|
||||
if (path.startsWith('/') && this.baseURI.endsWith('/')) {
|
||||
// oy vey, duplicate slashes
|
||||
path = path.substr(1);
|
||||
}
|
||||
|
||||
const response = {};
|
||||
try {
|
||||
const uri = this.baseURI + path;
|
||||
console.log('TOR: requesting', uri);
|
||||
const torResponse = await tor.get(uri, options?.headers || {}, true);
|
||||
response.originalResponse = torResponse;
|
||||
|
||||
if (options?.headers['Content-Type'] === 'application/json' && torResponse.json) {
|
||||
response.body = torResponse.json;
|
||||
} else {
|
||||
response.body = Buffer.from(torResponse.b64Data, 'base64').toString();
|
||||
}
|
||||
} catch (error) {
|
||||
response.err = error;
|
||||
console.warn(error);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
async post(path, options) {
|
||||
console.log('TOR: starting...');
|
||||
const socksProxy = await tor.startIfNotStarted();
|
||||
console.log('TOR: started', await tor.getDaemonStatus(), 'on local port', socksProxy);
|
||||
if (path.startsWith('/') && this.baseURI.endsWith('/')) {
|
||||
// oy vey, duplicate slashes
|
||||
path = path.substr(1);
|
||||
}
|
||||
|
||||
const uri = this.baseURI + path;
|
||||
console.log('TOR: posting to', uri);
|
||||
|
||||
const response = {};
|
||||
try {
|
||||
const torResponse = await tor.post(uri, JSON.stringify(options?.body || {}), options?.headers || {}, true);
|
||||
response.originalResponse = torResponse;
|
||||
|
||||
if (options?.headers['Content-Type'] === 'application/json' && torResponse.json) {
|
||||
response.body = torResponse.json;
|
||||
} else {
|
||||
response.body = Buffer.from(torResponse.b64Data, 'base64').toString();
|
||||
}
|
||||
} catch (error) {
|
||||
response.err = error;
|
||||
console.warn(error);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
testSocket() {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.constructor._resolveReference = resolve;
|
||||
this.constructor._rejectReference = reject;
|
||||
(async () => {
|
||||
console.log('testSocket...');
|
||||
try {
|
||||
if (!this.constructor._testConn) {
|
||||
// no test conenctino exists, creating it...
|
||||
await tor.startIfNotStarted();
|
||||
const target = 'v7gtzf7nua6hdmb2wtqaqioqmesdb4xrlly4zwr7bvayxv2bpg665pqd.onion:50001';
|
||||
this.constructor._testConn = await tor.createTcpConnection({ target }, (data, err) => {
|
||||
if (err) {
|
||||
return this.constructor._rejectReference(new Error(err));
|
||||
}
|
||||
const json = JSON.parse(data);
|
||||
if (!json || typeof json.result === 'undefined')
|
||||
return this.constructor._rejectReference(new Error('Unexpected response from TOR socket: ' + JSON.stringify(json)));
|
||||
|
||||
// conn.close();
|
||||
// instead of closing connect, we will actualy re-cyce existing test connection as we
|
||||
// saved it into `this.constructor.testConn`
|
||||
this.constructor._resolveReference();
|
||||
});
|
||||
|
||||
await this.constructor._testConn.write(
|
||||
`{ "id": 1, "method": "blockchain.scripthash.get_balance", "params": ["716decbe1660861c3d93906cb1d98ee68b154fd4d23aed9783859c1271b52a9c"] }\n`,
|
||||
);
|
||||
} else {
|
||||
// test connectino exists, so we are reusing it
|
||||
await this.constructor._testConn.write(
|
||||
`{ "id": 1, "method": "blockchain.scripthash.get_balance", "params": ["716decbe1660861c3d93906cb1d98ee68b154fd4d23aed9783859c1271b52a9c"] }\n`,
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
this.constructor._rejectReference(error);
|
||||
}
|
||||
})();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for react-native-tor mimicking Socket class from NET package
|
||||
*/
|
||||
class TorSocket {
|
||||
constructor() {
|
||||
this._socket = false;
|
||||
this._listeners = {};
|
||||
}
|
||||
|
||||
setTimeout() {}
|
||||
|
||||
setEncoding() {}
|
||||
|
||||
setKeepAlive() {}
|
||||
|
||||
setNoDelay() {}
|
||||
|
||||
on(event, listener) {
|
||||
this._listeners[event] = this._listeners[event] || [];
|
||||
this._listeners[event].push(listener);
|
||||
}
|
||||
|
||||
removeListener(event, listener) {
|
||||
this._listeners[event] = this._listeners[event] || [];
|
||||
const newListeners = [];
|
||||
|
||||
let found = false;
|
||||
for (const savedListener of this._listeners[event]) {
|
||||
// eslint-disable-next-line eqeqeq
|
||||
if (savedListener == listener) {
|
||||
// found our listener
|
||||
found = true;
|
||||
// we just skip it
|
||||
} else {
|
||||
// other listeners should go back to original array
|
||||
newListeners.push(savedListener);
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
this._listeners[event] = newListeners;
|
||||
} else {
|
||||
// something went wrong, lets just cleanup all listeners
|
||||
this._listeners[event] = [];
|
||||
}
|
||||
}
|
||||
|
||||
connect(port, host, callback) {
|
||||
console.log('connecting TOR socket...', host, port);
|
||||
(async () => {
|
||||
console.log('starting tor...');
|
||||
try {
|
||||
await tor.startIfNotStarted();
|
||||
} catch (e) {
|
||||
console.warn('Could not bootstrap TOR', e);
|
||||
await tor.stopIfRunning();
|
||||
this._passOnEvent('error', 'Could not bootstrap TOR');
|
||||
return false;
|
||||
}
|
||||
console.log('started tor');
|
||||
const iWillConnectISwear = tor.createTcpConnection({ target: host + ':' + port, connectionTimeout: 15000 }, (data, err) => {
|
||||
if (err) {
|
||||
console.log('TOR socket onData error: ', err);
|
||||
// this._passOnEvent('error', err);
|
||||
return;
|
||||
}
|
||||
this._passOnEvent('data', data);
|
||||
});
|
||||
|
||||
try {
|
||||
this._socket = await Promise.race([iWillConnectISwear, new Promise(resolve => setTimeout(resolve, 21000))]);
|
||||
} catch (e) {}
|
||||
|
||||
if (!this._socket) {
|
||||
console.log('connecting TOR socket failed'); // either sleep expired or connect threw an exception
|
||||
await tor.stopIfRunning();
|
||||
this._passOnEvent('error', 'connecting TOR socket failed');
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log('TOR socket connected:', host, port);
|
||||
setTimeout(() => {
|
||||
this._passOnEvent('connect', true);
|
||||
callback();
|
||||
}, 1000);
|
||||
})();
|
||||
}
|
||||
|
||||
_passOnEvent(event, data) {
|
||||
this._listeners[event] = this._listeners[event] || [];
|
||||
for (const savedListener of this._listeners[event]) {
|
||||
savedListener(data);
|
||||
}
|
||||
}
|
||||
|
||||
emit(event, data) {}
|
||||
|
||||
end() {
|
||||
console.log('trying to close TOR socket');
|
||||
if (this._socket && this._socket.close) {
|
||||
console.log('trying to close TOR socket SUCCESS');
|
||||
return this._socket.close();
|
||||
}
|
||||
}
|
||||
|
||||
destroy() {}
|
||||
|
||||
write(data) {
|
||||
if (this._socket && this._socket.write) {
|
||||
try {
|
||||
return this._socket.write(data);
|
||||
} catch (error) {
|
||||
console.log('this._socket.write() failed so we are issuing ERROR event', error);
|
||||
this._passOnEvent('error', error);
|
||||
}
|
||||
} else {
|
||||
console.log('TOR socket write error, socket not connected');
|
||||
this._passOnEvent('error', 'TOR socket not connected');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.getDaemonStatus = async () => {
|
||||
try {
|
||||
return await tor.getDaemonStatus();
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.stopIfRunning = async () => {
|
||||
try {
|
||||
Torsbee._testConn = false;
|
||||
return await tor.stopIfRunning();
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.startIfNotStarted = async () => {
|
||||
try {
|
||||
return await tor.startIfNotStarted();
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.testSocket = async () => {
|
||||
const c = new Torsbee();
|
||||
return c.testSocket();
|
||||
};
|
||||
|
||||
module.exports.testHttp = async () => {
|
||||
const api = new Torsbee({
|
||||
baseURI: 'http://explorerzydxu5ecjrkwceayqybizmpjjznk5izmitf2modhcusuqlid.onion:80/',
|
||||
});
|
||||
const torResponse = await api.get('/api/tx/a84dbcf0d2550f673dda9331eea7cab86b645fd6e12049755c4b47bd238adce9', {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
const json = torResponse.body;
|
||||
if (json.txid !== 'a84dbcf0d2550f673dda9331eea7cab86b645fd6e12049755c4b47bd238adce9')
|
||||
throw new Error('TOR failure, got ' + JSON.stringify(torResponse));
|
||||
};
|
||||
|
||||
module.exports.Torsbee = Torsbee;
|
||||
module.exports.Socket = TorSocket;
|
|
@ -43,16 +43,6 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
return ret + (this.getUnconfirmedBalance() < 0 ? this.getUnconfirmedBalance() : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
timeToRefreshTransaction() {
|
||||
for (const tx of this.getTransactions()) {
|
||||
if (tx.confirmations < 7) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @inheritDoc
|
||||
|
|
|
@ -37,6 +37,13 @@ export class AbstractWallet {
|
|||
this._utxoMetadata = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {number} Timestamp (millisecsec) of when last transactions were fetched from the network
|
||||
*/
|
||||
getLastTxFetch() {
|
||||
return this._lastTxFetch;
|
||||
}
|
||||
|
||||
getID() {
|
||||
return createHash('sha256').update(this.getSecret()).digest().toString('hex');
|
||||
}
|
||||
|
@ -198,6 +205,8 @@ export class AbstractWallet {
|
|||
}
|
||||
this.secret = parsedSecret.keystore.xpub;
|
||||
this.masterFingerprint = masterFingerprint;
|
||||
|
||||
if (parsedSecret.keystore.type === 'hardware') this.use_with_hardware_wallet = true;
|
||||
}
|
||||
// It is a Cobo Vault Hardware Wallet
|
||||
if (parsedSecret && parsedSecret.ExtPubKey && parsedSecret.MasterFingerprint) {
|
||||
|
@ -205,6 +214,7 @@ export class AbstractWallet {
|
|||
const mfp = Buffer.from(parsedSecret.MasterFingerprint, 'hex').reverse().toString('hex');
|
||||
this.masterFingerprint = parseInt(mfp, 16);
|
||||
this.setLabel('Cobo Vault ' + parsedSecret.MasterFingerprint);
|
||||
if (parsedSecret.CoboVaultFirmwareVersion) this.use_with_hardware_wallet = true;
|
||||
}
|
||||
} catch (_) {}
|
||||
return this;
|
||||
|
|
|
@ -37,7 +37,7 @@ export class LegacyWallet extends AbstractWallet {
|
|||
*/
|
||||
timeToRefreshTransaction() {
|
||||
for (const tx of this.getTransactions()) {
|
||||
if (tx.confirmations < 7) {
|
||||
if (tx.confirmations < 7 && this._lastTxFetch < +new Date() - 5 * 60 * 1000) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import { LegacyWallet } from './legacy-wallet';
|
|||
import Frisbee from 'frisbee';
|
||||
import bolt11 from 'bolt11';
|
||||
import { BitcoinUnit, Chain } from '../../models/bitcoinUnits';
|
||||
const torrific = require('../../blue_modules/torrific');
|
||||
|
||||
export class LightningCustodianWallet extends LegacyWallet {
|
||||
static type = 'lightningCustodianWallet';
|
||||
|
@ -77,6 +78,12 @@ export class LightningCustodianWallet extends LegacyWallet {
|
|||
this._api = new Frisbee({
|
||||
baseURI: this.baseURI,
|
||||
});
|
||||
|
||||
if (this.baseURI.indexOf('.onion') !== -1) {
|
||||
this._api = new torrific.Torsbee({
|
||||
baseURI: this.baseURI,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
accessTokenExpired() {
|
||||
|
@ -581,9 +588,14 @@ export class LightningCustodianWallet extends LegacyWallet {
|
|||
}
|
||||
|
||||
static async isValidNodeAddress(address) {
|
||||
const apiCall = new Frisbee({
|
||||
baseURI: address,
|
||||
});
|
||||
const isTor = address.indexOf('.onion') !== -1;
|
||||
const apiCall = isTor
|
||||
? new torrific.Torsbee({
|
||||
baseURI: address,
|
||||
})
|
||||
: new Frisbee({
|
||||
baseURI: address,
|
||||
});
|
||||
const response = await apiCall.get('/getinfo', {
|
||||
headers: {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
|
|
|
@ -2,6 +2,7 @@ import { LegacyWallet } from './legacy-wallet';
|
|||
import { HDSegwitP2SHWallet } from './hd-segwit-p2sh-wallet';
|
||||
import { HDLegacyP2PKHWallet } from './hd-legacy-p2pkh-wallet';
|
||||
import { HDSegwitBech32Wallet } from './hd-segwit-bech32-wallet';
|
||||
|
||||
const bitcoin = require('bitcoinjs-lib');
|
||||
const HDNode = require('bip32');
|
||||
|
||||
|
@ -15,10 +16,26 @@ export class WatchOnlyWallet extends LegacyWallet {
|
|||
this.masterFingerprint = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
getLastTxFetch() {
|
||||
if (this._hdWalletInstance) return this._hdWalletInstance.getLastTxFetch();
|
||||
return super.getLastTxFetch();
|
||||
}
|
||||
|
||||
timeToRefreshTransaction() {
|
||||
if (this._hdWalletInstance) return this._hdWalletInstance.timeToRefreshTransaction();
|
||||
return super.timeToRefreshTransaction();
|
||||
}
|
||||
|
||||
timeToRefreshBalance() {
|
||||
if (this._hdWalletInstance) return this._hdWalletInstance.timeToRefreshBalance();
|
||||
return super.timeToRefreshBalance();
|
||||
}
|
||||
|
||||
allowSend() {
|
||||
return (
|
||||
this.useWithHardwareWalletEnabled() && this._hdWalletInstance instanceof HDSegwitBech32Wallet && this._hdWalletInstance.allowSend()
|
||||
);
|
||||
return this.useWithHardwareWalletEnabled() && this.isHd() && this._hdWalletInstance.allowSend();
|
||||
}
|
||||
|
||||
allowSignVerifyMessage() {
|
||||
|
@ -165,10 +182,10 @@ export class WatchOnlyWallet extends LegacyWallet {
|
|||
* @see HDSegwitBech32Wallet.createTransaction
|
||||
*/
|
||||
createTransaction(utxos, targets, feeRate, changeAddress, sequence) {
|
||||
if (this._hdWalletInstance instanceof HDSegwitBech32Wallet) {
|
||||
if (this._hdWalletInstance && this.isHd()) {
|
||||
return this._hdWalletInstance.createTransaction(utxos, targets, feeRate, changeAddress, sequence, true, this.getMasterFingerprint());
|
||||
} else {
|
||||
throw new Error('Not a zpub watch-only wallet, cant create PSBT (or just not initialized)');
|
||||
throw new Error('Not a HD watch-only wallet, cant create PSBT (or just not initialized)');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -153,6 +153,9 @@ class AmountInput extends Component {
|
|||
}
|
||||
text = rez;
|
||||
}
|
||||
if (text.startsWith('0') && !(text.includes('.') || text.includes(','))) {
|
||||
text = text.replace(/^(0+)/g, '');
|
||||
}
|
||||
text = text.replace(/[^\d.,-]/g, ''); // remove all but numbers, dots & commas
|
||||
text = text.replace(/(\..*)\./g, '$1');
|
||||
}
|
||||
|
|
|
@ -95,7 +95,7 @@
|
|||
6DEB4C3B254FBF4800E9F9AA /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DEB4C3A254FBF4800E9F9AA /* Colors.swift */; };
|
||||
6DEB4C3C254FBF4800E9F9AA /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DEB4C3A254FBF4800E9F9AA /* Colors.swift */; };
|
||||
6DF25A9F249DB97E001D06F5 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6DF25A9E249DB97E001D06F5 /* LaunchScreen.storyboard */; };
|
||||
6DFC807024EA0B6C007B8700 /* EFQRCode in Frameworks */ = {isa = PBXBuildFile; productRef = 6DFC806F24EA0B6C007B8700 /* EFQRCode */; };
|
||||
6DFC807024EA0B6C007B8700 /* BuildFile in Frameworks */ = {isa = PBXBuildFile; productRef = 6DFC806F24EA0B6C007B8700 /* SwiftPackageProductDependency */; };
|
||||
6DFC807224EA2FA9007B8700 /* ViewQRCodefaceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DFC807124EA2FA9007B8700 /* ViewQRCodefaceController.swift */; };
|
||||
764B49B1420D4AEB8109BF62 /* libsqlite3.0.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 7B468CC34D5B41F3950078EF /* libsqlite3.0.tbd */; };
|
||||
782F075B5DD048449E2DECE9 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = B9D9B3A7B2CB4255876B67AF /* libz.tbd */; };
|
||||
|
@ -496,7 +496,7 @@
|
|||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
6DFC807024EA0B6C007B8700 /* EFQRCode in Frameworks */,
|
||||
6DFC807024EA0B6C007B8700 /* BuildFile in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -996,7 +996,7 @@
|
|||
);
|
||||
name = "BlueWalletWatch Extension";
|
||||
packageProductDependencies = (
|
||||
6DFC806F24EA0B6C007B8700 /* EFQRCode */,
|
||||
6DFC806F24EA0B6C007B8700 /* SwiftPackageProductDependency */,
|
||||
);
|
||||
productName = "BlueWalletWatch Extension";
|
||||
productReference = B40D4E3C225841ED00428FCC /* BlueWalletWatch Extension.appex */;
|
||||
|
@ -1089,7 +1089,7 @@
|
|||
);
|
||||
mainGroup = 83CBB9F61A601CBA00E9B192;
|
||||
packageReferences = (
|
||||
6DFC806E24EA0B6C007B8700 /* XCRemoteSwiftPackageReference "EFQRCode" */,
|
||||
6DFC806E24EA0B6C007B8700 /* RemoteSwiftPackageReference */,
|
||||
);
|
||||
productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */;
|
||||
projectDirPath = "";
|
||||
|
@ -1205,6 +1205,24 @@
|
|||
shellPath = /bin/sh;
|
||||
shellScript = "export SENTRY_PROPERTIES=sentry.properties\nexport NODE_BINARY=node\n../node_modules/@sentry/cli/bin/sentry-cli react-native xcode ../node_modules/react-native/scripts/react-native-xcode.sh\n";
|
||||
};
|
||||
1D2D05C5A2622FFEA3FB0BEB /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-BlueWallet/Pods-BlueWallet-frameworks.sh",
|
||||
"${PODS_ROOT}/../../node_modules/react-native-tor/ios/Libsifir_ios.framework",
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Libsifir_ios.framework",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-BlueWallet/Pods-BlueWallet-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
2130DE983D1D45AC8FC45F7E /* Upload Debug Symbols to Sentry */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
@ -1613,6 +1631,7 @@
|
|||
CURRENT_PROJECT_VERSION = 310;
|
||||
DEAD_CODE_STRIPPING = NO;
|
||||
DEVELOPMENT_TEAM = A7W54YZ4WU;
|
||||
ENABLE_BITCODE = NO;
|
||||
"ENABLE_HARDENED_RUNTIME[sdk=macosx*]" = YES;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"$(inherited)",
|
||||
|
@ -1621,7 +1640,7 @@
|
|||
);
|
||||
HEADER_SEARCH_PATHS = "$(inherited)";
|
||||
INFOPLIST_FILE = BlueWallet/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.1;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
|
@ -1661,10 +1680,11 @@
|
|||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 310;
|
||||
DEVELOPMENT_TEAM = A7W54YZ4WU;
|
||||
ENABLE_BITCODE = NO;
|
||||
"ENABLE_HARDENED_RUNTIME[sdk=macosx*]" = YES;
|
||||
HEADER_SEARCH_PATHS = "$(inherited)";
|
||||
INFOPLIST_FILE = BlueWallet/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.1;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
|
@ -2513,7 +2533,7 @@
|
|||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCRemoteSwiftPackageReference section */
|
||||
6DFC806E24EA0B6C007B8700 /* XCRemoteSwiftPackageReference "EFQRCode" */ = {
|
||||
6DFC806E24EA0B6C007B8700 /* RemoteSwiftPackageReference */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/EFPrefix/EFQRCode.git";
|
||||
requirement = {
|
||||
|
@ -2524,9 +2544,9 @@
|
|||
/* End XCRemoteSwiftPackageReference section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
6DFC806F24EA0B6C007B8700 /* EFQRCode */ = {
|
||||
6DFC806F24EA0B6C007B8700 /* SwiftPackageProductDependency */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 6DFC806E24EA0B6C007B8700 /* XCRemoteSwiftPackageReference "EFQRCode" */;
|
||||
package = 6DFC806E24EA0B6C007B8700 /* RemoteSwiftPackageReference */;
|
||||
productName = EFQRCode;
|
||||
};
|
||||
/* End XCSwiftPackageProductDependency section */
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
platform :ios, '10.0'
|
||||
platform :ios, '11.1'
|
||||
workspace 'BlueWallet'
|
||||
require_relative '../node_modules/react-native/scripts/react_native_pods'
|
||||
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
|
||||
|
|
|
@ -271,11 +271,11 @@ PODS:
|
|||
- React-Core
|
||||
- react-native-safe-area-context (3.2.0):
|
||||
- React-Core
|
||||
- react-native-slider (3.0.3):
|
||||
- React
|
||||
- react-native-tcp-socket (3.7.1):
|
||||
- CocoaAsyncSocket
|
||||
- React
|
||||
- react-native-tor (0.1.7):
|
||||
- React
|
||||
- react-native-webview (11.3.1):
|
||||
- React-Core
|
||||
- react-native-widget-center (0.0.4):
|
||||
|
@ -384,7 +384,7 @@ PODS:
|
|||
- RNSentry (2.4.0):
|
||||
- React-Core
|
||||
- Sentry (= 6.1.4)
|
||||
- RNShare (5.1.6):
|
||||
- RNShare (5.1.7):
|
||||
- React-Core
|
||||
- RNSVG (12.1.0):
|
||||
- React
|
||||
|
@ -453,8 +453,8 @@ DEPENDENCIES:
|
|||
- react-native-is-catalyst (from `../node_modules/react-native-is-catalyst`)
|
||||
- react-native-randombytes (from `../node_modules/react-native-randombytes`)
|
||||
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
|
||||
- "react-native-slider (from `../node_modules/@react-native-community/slider`)"
|
||||
- react-native-tcp-socket (from `../node_modules/react-native-tcp-socket`)
|
||||
- react-native-tor (from `../node_modules/react-native-tor`)
|
||||
- react-native-webview (from `../node_modules/react-native-webview`)
|
||||
- react-native-widget-center (from `../node_modules/react-native-widget-center`)
|
||||
- React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`)
|
||||
|
@ -572,10 +572,10 @@ EXTERNAL SOURCES:
|
|||
:path: "../node_modules/react-native-randombytes"
|
||||
react-native-safe-area-context:
|
||||
:path: "../node_modules/react-native-safe-area-context"
|
||||
react-native-slider:
|
||||
:path: "../node_modules/@react-native-community/slider"
|
||||
react-native-tcp-socket:
|
||||
:path: "../node_modules/react-native-tcp-socket"
|
||||
react-native-tor:
|
||||
:path: "../node_modules/react-native-tor"
|
||||
react-native-webview:
|
||||
:path: "../node_modules/react-native-webview"
|
||||
react-native-widget-center:
|
||||
|
@ -705,8 +705,8 @@ SPEC CHECKSUMS:
|
|||
react-native-is-catalyst: 52ee70e0123c82419dd4ce47dc4cc94b22467512
|
||||
react-native-randombytes: 45ae693012df24c9a07a5e578b3b567c01468581
|
||||
react-native-safe-area-context: e471852c5ed67eea4b10c5d9d43c1cebae3b231d
|
||||
react-native-slider: b733e17fdd31186707146debf1f04b5d94aa1a93
|
||||
react-native-tcp-socket: 96a4f104cdcc9c6621aafe92937f163d88447c5b
|
||||
react-native-tor: 4f389f5719dad633542b57ea32744e954730e7ef
|
||||
react-native-webview: 30f048378c6cee522ed9bbbedbc34acb21e58188
|
||||
react-native-widget-center: 0f81d17beb163e7fb5848b06754d7d277fe7d99a
|
||||
React-RCTActionSheet: 89a0ca9f4a06c1f93c26067af074ccdce0f40336
|
||||
|
@ -740,7 +740,7 @@ SPEC CHECKSUMS:
|
|||
RNScreens: f7ad633b2e0190b77b6a7aab7f914fad6f198d8d
|
||||
RNSecureKeyStore: f1ad870e53806453039f650720d2845c678d89c8
|
||||
RNSentry: b0d55027200c96f52e26b9bfb20296d47fc5051d
|
||||
RNShare: 2db8b0346a4859f83d15528602ed5b6120a0452a
|
||||
RNShare: 503c37af86611beadccba46156fcc42170c9fb42
|
||||
RNSVG: ce9d996113475209013317e48b05c21ee988d42e
|
||||
RNVectorIcons: bc69e6a278b14842063605de32bec61f0b251a59
|
||||
RNWatch: e4c5d19506c94506860032fb68aedd5991beb985
|
||||
|
@ -750,6 +750,6 @@ SPEC CHECKSUMS:
|
|||
Yoga: 4bd86afe9883422a7c4028c00e34790f560923d6
|
||||
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
|
||||
|
||||
PODFILE CHECKSUM: 070fb164a2dfb94605ea7862fd9b1c25e95ff046
|
||||
PODFILE CHECKSUM: 8fb37a9b658fd4d511d97e40f40e2d511092b261
|
||||
|
||||
COCOAPODS: 1.10.1
|
||||
|
|
|
@ -17,7 +17,6 @@ var numberFormatter: NumberFormatter {
|
|||
formatter.numberStyle = .decimal
|
||||
formatter.maximumFractionDigits = 0
|
||||
formatter.locale = Locale.current
|
||||
formatter.groupingSeparator = " "
|
||||
return formatter
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ struct WidgetDataStore: Codable {
|
|||
let numberFormatter = NumberFormatter()
|
||||
numberFormatter.locale = Locale(identifier: WidgetAPI.getUserPreferredCurrencyLocale())
|
||||
numberFormatter.numberStyle = .currency
|
||||
numberFormatter.usesSignificantDigits = true
|
||||
numberFormatter.maximumFractionDigits = 0
|
||||
numberFormatter.minimumFractionDigits = 0
|
||||
numberFormatter.currencySymbol = ""
|
||||
|
|
178
loc/cy.json
178
loc/cy.json
|
@ -1,13 +1,23 @@
|
|||
{
|
||||
"_": {
|
||||
"bad_password": "Cyfrinair anghywir. Tria eto. ",
|
||||
"cancel": "Canslo",
|
||||
"continue": "Parhau",
|
||||
"enter_password": "Cyfrinair",
|
||||
"never": "byth",
|
||||
"of": "{nifer} o {gyfanswm}",
|
||||
"never": "Byth",
|
||||
"of": "{number} o {total}",
|
||||
"ok": "Iawn",
|
||||
"storage_is_encrypted": "Mae'r storfa wedi encryptio. Mae angen Cyfrinair i'w ddad-gryptio. ",
|
||||
"yes": "Ie"
|
||||
"allow": "Caniatau",
|
||||
"dont_allow": "Peidio Caniatau",
|
||||
"yes": "Ie",
|
||||
"no": "Na",
|
||||
"save": "Safio",
|
||||
"seed": "Hadyn",
|
||||
"success": "Llwyddiant",
|
||||
"wallet_key": "Allwedd waled",
|
||||
"discard_changes": "Gwaredu newidiadau?",
|
||||
"discard_changes_detail": "Dydi'r newidiadau heb gael eu safio. Wyt ti dal eisiau eu gwaredu a gadael y sgrîn?"
|
||||
},
|
||||
"azteco": {
|
||||
"success": "Llwyddiant"
|
||||
|
@ -21,27 +31,185 @@
|
|||
"error": "Camgymeriad",
|
||||
"network": "Camgymeriad rhwydwaith"
|
||||
},
|
||||
"hodl": {
|
||||
"are_you_sure_you_want_to_logout": "Wyt ti'n sicr am logio allan o Hodl Hodl?",
|
||||
"cont_address_escrow": "Escrow",
|
||||
"cont_address_to": "At",
|
||||
"cont_buying": "Prynnu",
|
||||
"cont_cancel": "Canslo cytundeb",
|
||||
"cont_cancel_q": "Wyt ti'n sicr am ganslo'r cytundeb hwn?",
|
||||
"cont_cancel_y": "Ie, canslo'r cytundeb",
|
||||
"cont_chat": "Agor sgwrs efo'r aelod arall",
|
||||
"cont_how": "Sut i dalu",
|
||||
"cont_no": "Does gennyt ddim cytundebau yn agored.",
|
||||
"cont_paid": "Marcio'r gytundeb wedi talu.",
|
||||
"cont_paid_e": "Gwna hyn ddim ond os wyt wedi gyrru arian i'r gwerthwr drwy'r modd talu fel a gytunwyd",
|
||||
"cont_paid_q": "Wyt ti'n sicr am farcio'r gytundeb yma wedi talu?",
|
||||
"cont_selling": "Gwerthu",
|
||||
"cont_st_completed": "Wedi cwblhau!",
|
||||
"cont_st_in_progress_buyer": "Mae'r arian mewn escrow. Tala'r gwerthwr.",
|
||||
"cont_st_paid_enought": "Mae'r Bitcoins mewn escrow. Tala'r gwerthwr drwy'r modd talu fel a gytunwyd.",
|
||||
"cont_st_paid_waiting": "Aros am y gwerthwr i ryddhau'r arian o'r escrow",
|
||||
"cont_st_waiting": "Aros am y gwerthwr i roi bitcoins mewn escrow",
|
||||
"cont_title": "Fy Nghytundebau",
|
||||
"filter_any": "Unriw",
|
||||
"filter_buying": "Prynnu",
|
||||
"filter_country_global": "Cynigion O'r Byd",
|
||||
"filter_currency": "Arian",
|
||||
"filter_iambuying": "Dwi'n prynnu bitcoin",
|
||||
"filter_iamselling": "Dwi'n gwerthu bitcoin",
|
||||
"filter_method": "Modd Talu",
|
||||
"filter_search": "Chwilio",
|
||||
"filter_selling": "Gwerthu",
|
||||
"item_minmax": "Isafswm/Uchafswm",
|
||||
"local_trader": "Masnachwr Lleol",
|
||||
"local_trader_new": "Newydd",
|
||||
"login": "Logio Fewn",
|
||||
"logout": "Logio Allan",
|
||||
"mycont": "Fy Nghytundebau",
|
||||
"offer_accept": "Derbyn cynnig",
|
||||
"offer_choosemethod": "Dewis Modd Talu",
|
||||
"offer_minmax": "Isafswm/Uchafswm",
|
||||
"offer_minutes": "Isafswm",
|
||||
"offer_promt_fiat": "Faint o {currency} wyt ti eisiau prynnu?",
|
||||
"offer_promt_fiat_e": "Er engraifft, 100",
|
||||
"offer_window": "Ffenest",
|
||||
"p2p": "Prynnu Bitcoin ar Gyfnewid Person-i-Berson"
|
||||
},
|
||||
"lnd": {
|
||||
"errorInvoiceExpired": "Anfoneb wedi gorffen",
|
||||
"exchange": "Cyfnewid",
|
||||
"expired": "Gorffen",
|
||||
"expiredLow": "Gorffen",
|
||||
"expiresIn": "Yn Gorffen: {time}",
|
||||
"payButton": "Talu",
|
||||
"placeholder": "Anfoneb",
|
||||
"potentialFee": "Ffi Tebygol: {fee}",
|
||||
"refill": "Ail-lenwi",
|
||||
"refill_card": "Ail-lenwi efo cerdyn banc",
|
||||
"refill_create": "Er mwyn parhau, ffurfia waled Bitcoin i'w ail-lenwi. ",
|
||||
"refill_external": "Ail-lenwi efo Waled Allanol",
|
||||
"refill_lnd_balance": "Ail-lenwi balans waled Mellten",
|
||||
"sameWalletAsInvoiceError": "Does dim modd talu anfoneb efo'r un waled ddefnyddwyd i'w greu.",
|
||||
"title": "Rheoli cyllid"
|
||||
"title": "Rheoli Cyllid"
|
||||
},
|
||||
"lndViewInvoice": {
|
||||
"additional_info": "Gwybodaeth Ychwanegol",
|
||||
"for": "Ar Gyfer:",
|
||||
"lightning_invoice": "Anfoneb Mellten",
|
||||
"has_been_paid": "Mae'r anfoneb yma wedi cael ei dalu",
|
||||
"open_direct_channel": "Agor sianel uniongyrchol efo'r nodyn hwn:",
|
||||
"please_pay": "Talu"
|
||||
"please_pay": "Talu",
|
||||
"wasnt_paid_and_expired": "Ni chafodd yr anfoneb ei thalu, ac mae wedi gorffen."
|
||||
},
|
||||
"plausibledeniability": {
|
||||
"create_fake_storage": "Creu storfa wedi encryptio",
|
||||
"create_password": "Creu cyfrinair",
|
||||
"create_password_explanation": "Ni ddyliai'r cyfrinair ar gyfer y storfa ffug fod yr un cyfrinair ag ar gyfer y brif storfa"
|
||||
},
|
||||
"pleasebackup": {
|
||||
"title": "Mae dy waled wedi cael ei chreu."
|
||||
},
|
||||
"receive": {
|
||||
"details_create": "Creu",
|
||||
"details_label": "Disgrifiad",
|
||||
"details_setAmount": "Derbyn efo swm",
|
||||
"details_share": "Rhannu",
|
||||
"header": "Derbyn"
|
||||
},
|
||||
"send": {
|
||||
"broadcastButton": "Darlledu",
|
||||
"broadcastError": "Camgymeriad",
|
||||
"broadcastPending": "Disgwyl",
|
||||
"broadcastSuccess": "Llwyddiant",
|
||||
"confirm_header": "Cadarnhau",
|
||||
"confirm_sendNow": "Anfon rwan",
|
||||
"create_amount": "Swm",
|
||||
"create_broadcast": "Darlledu",
|
||||
"create_copy": "Copïo a darlledu'n hwyrach",
|
||||
"create_details": "Manylion",
|
||||
"create_fee": "Ffi",
|
||||
"create_to": "At",
|
||||
"create_tx_size": "Maint Trafodyn",
|
||||
"details_address": "Cyfeiriad",
|
||||
"details_adv_full": "Defnyddio Balans Llawn",
|
||||
"details_adv_full_sure": "Wyt ti'n sicr dy fod eisiau defnyddio balans llawn dy walet ar gyfer y trafodyn hwn?",
|
||||
"details_adv_import": "Mewnforio Trafodyn",
|
||||
"details_create": "Creu Anfoneb",
|
||||
"details_next": "Nesaf",
|
||||
"details_note_placeholder": "Hunan Nodyn",
|
||||
"details_scan": "Sganio",
|
||||
"details_wallet_before_tx": "Cyn creu trafodyn, mae angen adio waled Bitcoin.",
|
||||
"dynamic_next": "Nesaf",
|
||||
"dynamic_prev": "Blaenorol",
|
||||
"dynamic_start": "Dechrau",
|
||||
"dynamic_stop": "Stopio",
|
||||
"fee_fast": "Cyflym",
|
||||
"fee_medium": "Canolig",
|
||||
"fee_slow": "Araf",
|
||||
"header": "Anfon",
|
||||
"input_clear": "Clirio",
|
||||
"input_done": "Wedi Cwblhau",
|
||||
"input_paste": "Pastio",
|
||||
"input_total": "Cyfanswm:",
|
||||
"permission_camera_message": "Angen dy ganiatad i ddefnyddio dy gamera.",
|
||||
"permission_camera_title": "Caniatad i ddefnyddio'r camera",
|
||||
"psbt_sign": "Arwyddo trafodyn",
|
||||
"open_settings": "Agor Gosodiadau"
|
||||
},
|
||||
"settings": {
|
||||
"electrum_clear": "Clirio",
|
||||
"encrypt_decrypt": "Dadgryptio'r Storfa",
|
||||
"encrypt_title": "Diogelwch",
|
||||
"encrypt_tstorage": "Storfa",
|
||||
"encrypt_use": "Defnydd {type}",
|
||||
"general": "Cyffredinol",
|
||||
"header": "Gosodiadau",
|
||||
"language": "Iaith",
|
||||
"language_isRTL": "Mae angen ail ddechrau BlueWallet i'r newidiadau iaith gael effaith.",
|
||||
"lightning_saved": "Mae dy newidiadau wedi cael eu safio'n llwyddianus.",
|
||||
"lightning_settings": "Gosodiadau Mellten",
|
||||
"network": "Rhwydwaith",
|
||||
"network_broadcast": "Darlledu Trafodyn",
|
||||
"notifications": "Hysbysebion",
|
||||
"password": "Cyfrinair",
|
||||
"password_explain": "Creu'r cyfrinair y byddi'n defnyddio i ddadgryptio'r storfa.",
|
||||
"passwords_do_not_match": "Cyfrineiriau ddim yn cyfateb.",
|
||||
"privacy": "Prefiatrwydd",
|
||||
"retype_password": "Ail-deipio cyfrinair",
|
||||
"save": "Safio",
|
||||
"saved": "Wedi Safio",
|
||||
"success_transaction_broadcasted": "Llwyddiant! Mae dy drafodyn wedi cael ei ddarlledu!"
|
||||
},
|
||||
"transactions": {
|
||||
"cpfp_create": "Creu",
|
||||
"details_balance_hide": "Cuddio Balans",
|
||||
"details_balance_show": "Dangos Balans",
|
||||
"details_copy": "Copïo",
|
||||
"details_from": "Mewnbwn",
|
||||
"details_inputs": "Mewnbynau",
|
||||
"details_outputs": "Allbynau",
|
||||
"details_received": "Derbynwyd",
|
||||
"details_title": "Trafodyn",
|
||||
"details_to": "Allbwn",
|
||||
"details_transaction_details": "Manylion Trafodyn",
|
||||
"list_title": "Trafodion"
|
||||
},
|
||||
"units": {
|
||||
"MAX": "Mwyafswm"
|
||||
},
|
||||
"addresses": {
|
||||
"sign_title": "Arwyddo/Gwirio neges",
|
||||
"sign_sign": "Arwyddo",
|
||||
"sign_verify": "Gwirio",
|
||||
"sign_signature_correct": "Gwirio Wedi Llwyddo!",
|
||||
"sign_signature_incorrect": "Gwirio Wedi Methu!",
|
||||
"sign_placeholder_address": "Cyfeiriad",
|
||||
"sign_placeholder_message": "Neges",
|
||||
"sign_placeholder_signature": "Llofnod",
|
||||
"address_balance": "Balans: {balance} sats",
|
||||
"addresses_title": "Cyfeiriadau",
|
||||
"type_change": "Newid",
|
||||
"type_receive": "Derbyn"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -136,9 +136,9 @@
|
|||
"ask": "Hast Du die Wiederherstellungs-Phrase deines Wallets gesichert? Ohne Sie kannst Du nicht mehr auf deine bitcoin zugreifen und sie wären für immer verloren, sollte dein Gerät verloren oder kaputt gehen.",
|
||||
"ask_no": "Nein, habe ich nicht.",
|
||||
"ask_yes": "Ja, habe ich.",
|
||||
"ok": "Ja, ich habe sie aufgeschrieben!",
|
||||
"ok_lnd": "Die Sicherung ist erstellt.",
|
||||
"text": "Nimm Dir Zeit die mnemonische Phrase zur späteren Wiederherstellung auf Papier zu schreiben. Die Wörter sind dein Backup im Fall eines Geräteverlustes.",
|
||||
"ok": "Ok, ich habe sie notiert.",
|
||||
"ok_lnd": "OK. Die Sicherung ist erstellt.",
|
||||
"text": "Nimm Dir Zeit die mnemonischen Wörter auf ein Papier zu schreiben.\nDie Wörter sind dein Backup zur Wallet-Wiederherstellung.",
|
||||
"text_lnd": "Zur Wiederherstellung des Wallet im Verlustfall bitte dieses Wallet-Backup sichern. ",
|
||||
"text_lnd2": "Diese Wallet wird durch BlueWallet verwaltet.",
|
||||
"title": "Dein Wallet ist erstellt."
|
||||
|
@ -276,6 +276,7 @@
|
|||
"electrum_history": "Serverhistorie",
|
||||
"electrum_reset_to_default": "Sollen die Electrum-Einstellungen wirklich auf die Standardwerte zurückgesetzt werden?",
|
||||
"electrum_clear": "Löschen",
|
||||
"tor_supported": "TOR-Unterstützung",
|
||||
"encrypt_decrypt": "Speicher entschlüsseln",
|
||||
"encrypt_decrypt_q": "Willst du die Speicherverschlüsselung wirklich aufheben? Hiermit wird dein Wallet ohne Passwortschutz direkt benutzbar. ",
|
||||
"encrypt_del_uninstall": "Bei Deinstallation löschen",
|
||||
|
@ -296,6 +297,7 @@
|
|||
"lightning_error_lndhub_uri": "Keine gültige LndHub URI",
|
||||
"lightning_saved": "Deine Änderungen wurden gespeichert.",
|
||||
"lightning_settings": "Lightning-Einstellungen",
|
||||
"tor_settings": "TOR-Einstellungen",
|
||||
"lightning_settings_explain": "Zur Verbindung mit einem eigenen LND-Knoten ein LNDHub installieren und dessen URL hier eingeben. Das Feld leer lassen, um den LNDHub (lndhub.io) von BlueWallet zur verwenden. Nach dem Speichern werden sich neu erstellte Wallets mit dem angegebenen LNDHub verbinden.",
|
||||
"network": "Netzwerk",
|
||||
"network_broadcast": "Transaktion publizieren",
|
||||
|
@ -565,15 +567,23 @@
|
|||
"sign_title": "Meldung signieren/verifizieren",
|
||||
"sign_help": "Kryptografische Signatur für eine Bitcoin-Adresse erstellen oder verifizieren",
|
||||
"sign_sign": "Signieren",
|
||||
"sign_sign_submit": "Signieren und übertragen",
|
||||
"sign_verify": "Verifizieren",
|
||||
"sign_signature_correct": "Verifizierung erfolgreich!",
|
||||
"sign_signature_incorrect": "Verifizierung fehlgeschlagen!",
|
||||
"sign_placeholder_address": "Adresse",
|
||||
"sign_placeholder_message": "Meldung",
|
||||
"sign_placeholder_signature": "Signatur",
|
||||
"sign_aopp_title": "AOPP",
|
||||
"sign_aopp_confirm": "Senden der signierten Nachricht nach {hostname}?",
|
||||
"address_balance": "Guthaben: {balance} sats",
|
||||
"addresses_title": "Adressen",
|
||||
"type_change": "Wechsel",
|
||||
"type_receive": "Empfang"
|
||||
},
|
||||
"aopp": {
|
||||
"title": "Adresse wählen",
|
||||
"send_success": "Signatur erfolgreich gesendet",
|
||||
"send_error": "Fehler beim Senden der Signatur"
|
||||
}
|
||||
}
|
||||
|
|
10
loc/en.json
10
loc/en.json
|
@ -136,12 +136,12 @@
|
|||
"ask": "Have you saved your wallet’s backup phrase? This backup phrase is required to access your funds in case you lose this device. Without the backup phrase, your funds will be permanently lost.",
|
||||
"ask_no": "No, I have not",
|
||||
"ask_yes": "Yes, I have",
|
||||
"ok": "OK, I wrote this down.",
|
||||
"ok_lnd": "OK, I have saved it.",
|
||||
"text": "Please take a moment to write down this mnemonic phrase on a piece of paper. It’s your backup you can use to restore the wallet on another device.",
|
||||
"ok": "OK, I wrote it down",
|
||||
"ok_lnd": "OK, I have saved it",
|
||||
"text": "Please take a moment to write down this mnemonic phrase on a piece of paper.\nIt’s your backup and you can use it to recover the wallet.",
|
||||
"text_lnd": "Please save this wallet backup. It allows you to restore the wallet in case of loss.",
|
||||
"text_lnd2": "This wallet is hosted by BlueWallet.",
|
||||
"title": "Your wallet has been created."
|
||||
"title": "Your wallet has been created"
|
||||
},
|
||||
"receive": {
|
||||
"details_create": "Create",
|
||||
|
@ -276,6 +276,7 @@
|
|||
"electrum_history": "Server history",
|
||||
"electrum_reset_to_default": "Are you sure to want to reset your Electrum settings to default?",
|
||||
"electrum_clear": "Clear",
|
||||
"tor_supported": "Tor supported",
|
||||
"encrypt_decrypt": "Decrypt Storage",
|
||||
"encrypt_decrypt_q": "Are you sure you want to decrypt your storage? This will allow your wallets to be accessed without a password.",
|
||||
"encrypt_del_uninstall": "Delete if BlueWallet is uninstalled",
|
||||
|
@ -296,6 +297,7 @@
|
|||
"lightning_error_lndhub_uri": "Not a valid LNDHub URI",
|
||||
"lightning_saved": "Your changes have been saved successfully.",
|
||||
"lightning_settings": "Lightning Settings",
|
||||
"tor_settings": "Tor Settings",
|
||||
"lightning_settings_explain": "To connect to your own LND node, please install LNDHub and put its URL here in settings. Leave blank to use BlueWallet’s LNDHub (lndhub.io). Wallets created after saving changes will connect to the specified LNDHub.",
|
||||
"network": "Network",
|
||||
"network_broadcast": "Broadcast Transaction",
|
||||
|
|
|
@ -136,12 +136,12 @@
|
|||
"ask": "¿Has guardado la frase de respaldo de tu billetera? Esta frase de respaldo es necesaria para acceder a tus fondos en caso de que pierdas este dispositivo. Sin la frase de respaldo, tus fondos se perderán permanentemente.",
|
||||
"ask_no": "No, no tengo",
|
||||
"ask_yes": "Sí, tengo",
|
||||
"ok": "OK, escribí esto.",
|
||||
"ok_lnd": "OK, lo he guardado.",
|
||||
"text": "Toma un momento para escribir esta frase mnemotécnica en una hoja de papel. Es tu copia de seguridad que puedes usar para restaurar la billetera en otro dispositivo.",
|
||||
"ok": "OK, lo escribí",
|
||||
"ok_lnd": "OK, lo he guardado",
|
||||
"text": "Tómate un momento para escribir esta frase mnemotécnica en una hoja de papel.\nEs tu copia de seguridad y puedes usarla para recuperar la billetera.",
|
||||
"text_lnd": "Guarda esta copia de seguridad de la billetera. Te permite restaurar la billetera en caso de pérdida.",
|
||||
"text_lnd2": "Esta billetera está alojada en BlueWallet.",
|
||||
"title": "Tu Billetera está creada."
|
||||
"title": "Tu billetera ha sido creada"
|
||||
},
|
||||
"receive": {
|
||||
"details_create": "Crear",
|
||||
|
@ -276,6 +276,7 @@
|
|||
"electrum_history": "Historial del servidor",
|
||||
"electrum_reset_to_default": "¿Estás seguro de querer restablecer la configuración de Electrum a los valores predeterminados?",
|
||||
"electrum_clear": "Limpiar",
|
||||
"tor_supported": "TOR soportado",
|
||||
"encrypt_decrypt": "Descifrar Almacenamiento",
|
||||
"encrypt_decrypt_q": "¿Estás seguro de que deseas descifrar tu almacenamiento? Esto permitirá acceder a tus billeteras sin una contraseña.",
|
||||
"encrypt_del_uninstall": "Eliminar si BlueWallet es desinstalada",
|
||||
|
@ -296,6 +297,7 @@
|
|||
"lightning_error_lndhub_uri": "URI LndHub no válida",
|
||||
"lightning_saved": "Tus cambios han sido guardados correctamente.",
|
||||
"lightning_settings": "Configuración de Lightning",
|
||||
"tor_settings": "Ajustes de TOR",
|
||||
"lightning_settings_explain": "Para conectarse a tu propio nodo LND, instala LNDHub y pon su URL aquí en la configuración. Déjalo en blanco para usar LNDHub de BlueWallet (lndhub.io). Las carteras creadas después de guardar los cambios se conectarán al LNDHub especificado.",
|
||||
"network": "Red",
|
||||
"network_broadcast": "Publicar transacción",
|
||||
|
@ -573,7 +575,7 @@
|
|||
"sign_placeholder_message": "Mensaje",
|
||||
"sign_placeholder_signature": "Firma",
|
||||
"sign_aopp_title": "AOPP",
|
||||
"sign_aopp_confirm": "¿Quieres enviar un mensaje firmado a\n{hostname}?",
|
||||
"sign_aopp_confirm": "¿Quieres enviar un mensaje firmado a {hostname}?",
|
||||
"address_balance": "Saldo: {balance} sats",
|
||||
"addresses_title": "Direcciones",
|
||||
"type_change": "Cambio",
|
||||
|
|
21
loc/fa.json
21
loc/fa.json
|
@ -136,9 +136,9 @@
|
|||
"ask": "آیا کلمههای پشتیبان کیف پول خود را ذخیره کردهاید؟ درصورت ازدستدادن این دستگاه، این کلمههای پشتیبان برای دسترسی به دارایی شما لازم هستند. بدون کلمههای پشتیبان، دارایی شما برای همیشه ازدست خواهد رفت.",
|
||||
"ask_no": "خیر، نکردهام",
|
||||
"ask_yes": "بله، کردهام",
|
||||
"ok": "خب، این را نوشتم.",
|
||||
"ok": "خب، آن را نوشتم.",
|
||||
"ok_lnd": "خب، آن را ذخیره کردم.",
|
||||
"text": "لطفاً درنگ کرده و این عبارت یادیار (mnemonic phrase) را روی یک تکه کاغذ یادداشت کنید. این کلمههای پشتیبان شما هستند که میتوانید از آنها برای بازیابی کیف پول در دستگاه دیگری استفاده کنید.",
|
||||
"text": "لطفاً درنگ کرده و این عبارت یادیار (mnemonic phrase) را روی یک تکه کاغذ یادداشت کنید. این کلمهها نسخهٔ پشتیبان شما هستند، و میتوانید از آنها برای بازیابی کیف پول در دستگاه دیگری استفاده کنید.",
|
||||
"text_lnd": "لطفاً این نسخهٔ پشتیبان کیف پول را ذخیره کنید. با داشتن آن قادر خواهید بود درصورت ازدسترفتن کیف پول آن را بازیابی کنید.",
|
||||
"text_lnd2": "این کیف پول توسط BlueWallet میزبانی میشود.",
|
||||
"title": "کیف پول شما ایجاد شد."
|
||||
|
@ -276,6 +276,7 @@
|
|||
"electrum_history": "تاریخچهٔ سرورها",
|
||||
"electrum_reset_to_default": "آیا مطمئن هستید که میخواهید تنظیمات الکترام را به حالت پیشفرض بازنشانی کنید؟",
|
||||
"electrum_clear": "پاککردن",
|
||||
"tor_supported": "پشتیبانی از تور",
|
||||
"encrypt_decrypt": "رمزگشایی فضای ذخیرهسازی",
|
||||
"encrypt_decrypt_q": "آیا مطمئن هستید که میخواهید فضای ذخیرهسازی خود را رمزگشایی کنید؟ این کار اجازه میدهد تا کیف پولهای شما بدون گذرواژه قابلدسترسی باشند.",
|
||||
"encrypt_del_uninstall": "درصورت لغو نصب BlueWallet، حذف شود",
|
||||
|
@ -296,6 +297,7 @@
|
|||
"lightning_error_lndhub_uri": "یوآرآی LNDHub معتبر نیست",
|
||||
"lightning_saved": "تغییرات شما با موفقیت ذخیره شدند.",
|
||||
"lightning_settings": "تنظیمات لایتنینگ",
|
||||
"tor_settings": "تنظیمات تور",
|
||||
"lightning_settings_explain": "برای اتصال به گره LND خود، لطفاً LNDHub را نصب کرده و آدرس آن را اینجا در تنظیمات قرار دهید. برای استفاده از LNDHub برنامهٔ BlueWallet (به آدرس lndhub.io)، خالی بگذارید. کیف پولهای ایجادشده بعد از ذخیرهٔ تغییرات به LNDHub مشخصشده متصل خواهند شد.",
|
||||
"network": "شبکه",
|
||||
"network_broadcast": "انتشار تراکنش",
|
||||
|
@ -407,6 +409,7 @@
|
|||
"details_no_cancel": "خیر، لغو کن",
|
||||
"details_save": "ذخیره",
|
||||
"details_show_xpub": "نمایش XPUB کیف پول",
|
||||
"details_show_addresses": "نمایش آدرسها",
|
||||
"details_title": "کیف پول",
|
||||
"details_type": "نوع",
|
||||
"details_use_with_hardware_wallet": "استفاده همراه با کیف پول سختافزاری",
|
||||
|
@ -564,11 +567,23 @@
|
|||
"sign_title": "امضا/صحتسنجی پیام",
|
||||
"sign_help": "اینجا شما میتوانید امضای رمزنگارانهای را از روی یک آدرس بیتکوین تولید یا صحتسنجی کنید.",
|
||||
"sign_sign": "امضاکردن",
|
||||
"sign_sign_submit": "امضا و ارسال",
|
||||
"sign_verify": "صحتسنجی",
|
||||
"sign_signature_correct": "صحتسنجی موفقیتآمیز بود!",
|
||||
"sign_signature_incorrect": "صحتسنجی با شکست مواجه شد!",
|
||||
"sign_placeholder_address": "آدرس",
|
||||
"sign_placeholder_message": "پیام",
|
||||
"sign_placeholder_signature": "امضا"
|
||||
"sign_placeholder_signature": "امضا",
|
||||
"sign_aopp_title": "پروتکل اثبات مالکیت آدرس (AOPP)",
|
||||
"sign_aopp_confirm": "آیا مایل به ارسال پیام امضاشده به {hostname} هستید؟",
|
||||
"address_balance": "موجودی: {balance} ساتوشی",
|
||||
"addresses_title": "آدرسها",
|
||||
"type_change": "باقیمانده",
|
||||
"type_receive": "دریافت"
|
||||
},
|
||||
"aopp": {
|
||||
"title": "انتخاب آدرس",
|
||||
"send_success": "امضا با موفقیت ارسال شد.",
|
||||
"send_error": "خطا در ارسال امضا"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -138,10 +138,10 @@
|
|||
"ask_yes": "Oui",
|
||||
"ok": "OK, je l'ai notée !",
|
||||
"ok_lnd": "OK, je l'ai sauvegardée.",
|
||||
"text": "Veuillez prendre quelques instants pour écrire cette phrase de sauvegarde sur papier. Il s'agit de votre unique moyen de restaurer l'accès à votre portefeuille depuis un autre appareil.",
|
||||
"text": "Veuillez prendre un moment pour noter cette phrase de sauvegarde sur papier.\nC'est votre backup et vous pouvez l'utiliser pour restaurer votre portefeuille.",
|
||||
"text_lnd": "Veuillez sauvegarder cette sauvegarde de portefeuille. Elle vous permettra de restaurer le portefeuille en cas de perte.",
|
||||
"text_lnd2": "Ce portefeuille est hébergé par Bluewallet.",
|
||||
"title": "Votre portefeuille est créé"
|
||||
"title": "Votre portefeuille a été créé"
|
||||
},
|
||||
"receive": {
|
||||
"details_create": "Créer",
|
||||
|
@ -276,6 +276,7 @@
|
|||
"electrum_history": "Historique de serveur",
|
||||
"electrum_reset_to_default": "Etes-vous sûr de vouloir réinitialiser vos paramètres Electrum ?",
|
||||
"electrum_clear": "Effacer",
|
||||
"tor_supported": "TOR supporté",
|
||||
"encrypt_decrypt": "Déchiffrer le stockage",
|
||||
"encrypt_decrypt_q": "Etes-vous sûr de vouloir déchiffrer le stockage ? L'accès à vos portefeuilles pourra alors se faire sans mot de passe.",
|
||||
"encrypt_del_uninstall": "Supprimer si BlueWallet est désinstallé",
|
||||
|
@ -292,10 +293,11 @@
|
|||
"groundcontrol_explanation": "GroundControl est un serveur de notifications push pour les portefeuilles Bitcoin gratuit et open source. Vous pouvez installer votre propre serveur GroundControl et insérer ici son URL pour ne pas reposer sur l'infrastructure de BlueWallet. Laissez vide pour conserver l'option par défaut.",
|
||||
"header": "réglages",
|
||||
"language": "Langue",
|
||||
"language_restart": "Lors de la sélection d'un nouvelle langue, il peut être nécessaire de redémarrer BlueWallet pour que le changement prenne effet.",
|
||||
"language_isRTL": "Il est nécessaire de redémarrer BlueWallet pour que le changement de langue prenne effet.",
|
||||
"lightning_error_lndhub_uri": "URI LndHub invalide",
|
||||
"lightning_saved": "Les changements ont bien été enregistrés",
|
||||
"lightning_settings": "Lightning settings",
|
||||
"tor_settings": "Paramètres TOR",
|
||||
"lightning_settings_explain": "To connect to your own LND node please install LndHub and put its URL here in settings. Leave blank to use default ndHub\n (lndhub.io)",
|
||||
"network": "Réseau",
|
||||
"network_broadcast": "Diffuser une transaction",
|
||||
|
@ -322,7 +324,8 @@
|
|||
"success_transaction_broadcasted" : "Succès! Votre transaction a été difusée!",
|
||||
"total_balance": "Solde total",
|
||||
"total_balance_explanation": "Afficher le solde total de tous vos portefeuilles sur l'écran d'accueil.",
|
||||
"widgets": "Widgets"
|
||||
"widgets": "Widgets",
|
||||
"tools": "Outils"
|
||||
},
|
||||
"notifications": {
|
||||
"would_you_like_to_receive_notifications": "Voulez vous recevoir les notifications quand vous recevez des paiements entrants ?",
|
||||
|
@ -406,6 +409,7 @@
|
|||
"details_no_cancel": "Non, annuler",
|
||||
"details_save": "Enregistrer",
|
||||
"details_show_xpub": "Afficher XPUB du portefeuille",
|
||||
"details_show_addresses": "Montrer les adresses",
|
||||
"details_title": "Portefeuille",
|
||||
"details_type": "Type",
|
||||
"details_use_with_hardware_wallet": "Utiliser avec un portefeuille matériel",
|
||||
|
@ -563,11 +567,23 @@
|
|||
"sign_title": "Signe/vérifie le message",
|
||||
"sign_help": "Ici vous pouvez créer ou vérifier une signature cryptographique basée sur une adresse bitcoin",
|
||||
"sign_sign": "Signe",
|
||||
"sign_sign_submit": "Signer et envoyer",
|
||||
"sign_verify": "Vérifie",
|
||||
"sign_signature_correct": "Succès de la vérification",
|
||||
"sign_signature_incorrect": "Échec de la vérification",
|
||||
"sign_placeholder_address": "Adresse",
|
||||
"sign_placeholder_message": "Message",
|
||||
"sign_placeholder_signature": "Signature"
|
||||
"sign_placeholder_signature": "Signature",
|
||||
"sign_aopp_title": "AOPP",
|
||||
"sign_aopp_confirm": "Voulez-vous envoyer un message signé à {hostname} ?",
|
||||
"address_balance": "Solde : {balance} sats",
|
||||
"addresses_title": "Adresses",
|
||||
"type_change": "Monnaie",
|
||||
"type_receive": "Réception"
|
||||
},
|
||||
"aopp": {
|
||||
"title": "Sélectionner l'adresse",
|
||||
"send_success": "Signature envoyée avec succès",
|
||||
"send_error": "Erreur lors de l'envoi de la signature"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -474,11 +474,11 @@
|
|||
"native_segwit_title": "נוהג מומלץ",
|
||||
"wrapped_segwit_title": "תאימות גבוהה",
|
||||
"co_sign_transaction": "חתימה על העברה",
|
||||
"what_is_vault": "כספת היא ארנק",
|
||||
"what_is_vault": "כספת היא ארנק ",
|
||||
"what_is_vault_numberOfWallets": "רב-חתימות {m}-מתוך-{n}",
|
||||
"what_is_vault_wallet": ".",
|
||||
"vault_advanced_customize": "הגדרות כספת...",
|
||||
"needs": "דרישות",
|
||||
"needs": "נדרשים ",
|
||||
"what_is_vault_description_number_of_vault_keys": "{m} מפתחות כספת",
|
||||
"what_is_vault_description_to_spend": "לבזבוז ובשלישי תוכלו \nלהשתמש כגיבוי.",
|
||||
"what_is_vault_description_to_spend_other": "לבזבוז.",
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
"no": "いいえ",
|
||||
"save": "保存",
|
||||
"seed": "シード",
|
||||
"success": "成功",
|
||||
"wallet_key": "ウォレットキー",
|
||||
"invalid_animated_qr_code_fragment": "無効なアニメーションQRCodeフラグメントです。再度お試しください。",
|
||||
"file_saved": "ファイル ({filePath}) はダウンロードフォルダに保存されました。",
|
||||
|
@ -179,12 +180,14 @@
|
|||
"details_amount_field_is_not_valid": "金額欄が正しくありません",
|
||||
"details_amount_field_is_less_than_minimum_amount_sat": "指定した金額が少なすぎます。500sats以上の金額を入力してください。",
|
||||
"details_create": "作成",
|
||||
"details_error_decode": "ビットコインアドレスを復号できません",
|
||||
"details_fee_field_is_not_valid": "手数料欄が正しくありません",
|
||||
"details_next": "次",
|
||||
"details_no_signed_tx": "選択したファイルには、インポート可能なトランザクションが含まれていません。",
|
||||
"details_note_placeholder": "ラベル",
|
||||
"details_scan": "読取り",
|
||||
"details_total_exceeds_balance": "送金額が利用可能残額を超えています。",
|
||||
"details_unrecognized_file_format": "認識できないファイルフォーマット",
|
||||
"details_wallet_before_tx": "トランザクションを作成する前に、まずはビットコインウォレットを追加する必要があります。",
|
||||
"dynamic_init": "初期化中",
|
||||
"dynamic_next": "次",
|
||||
|
@ -485,5 +488,15 @@
|
|||
"BTC": "BTC",
|
||||
"sat_byte": "sat/byte",
|
||||
"sats": "sats"
|
||||
},
|
||||
"addresses": {
|
||||
"addresses_title": "アドレス",
|
||||
"type_change": "チェンジ",
|
||||
"type_receive": "受取り"
|
||||
},
|
||||
"aopp": {
|
||||
"title": "アドレス選択",
|
||||
"send_success": "署名は正常に送信されました",
|
||||
"send_error": "署名送信エラー"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
export const AvailableLanguages = Object.freeze([
|
||||
{ label: 'English', value: 'en' },
|
||||
{ label: 'Afrikaans (AFR)', value: 'zar_afr' },
|
||||
{ label: 'Arabic (AR)', value: 'ar', isRTL: true },
|
||||
{ label: 'العربية (AR)', value: 'ar', isRTL: true },
|
||||
{ label: 'Български (BG)', value: 'bg_bg' },
|
||||
{ label: 'Català (CA)', value: 'ca' },
|
||||
{ label: 'Chinese (TW)', value: 'zh_tw' },
|
||||
|
|
18
loc/pl.json
18
loc/pl.json
|
@ -136,12 +136,12 @@
|
|||
"ask": "Czy zapisałeś już słowa klucze? Są one niezbędne, by dysponować środkami, w przypadku zgubienia tego urządzenia. Bez słów kluczy, twoje środki przepadną na zawsze,",
|
||||
"ask_no": "Nie mam",
|
||||
"ask_yes": "Mam",
|
||||
"ok": "OK, zanotowałem!",
|
||||
"ok_lnd": "OK, zachowałem.",
|
||||
"text": "Proszę poświęć chwilę, żeby zapisać to wyrażenie mnemoniczne na kartce papieru, To twoja kopia zapasowa, możesz użyć jej, aby odtworzyć portfel na innym urządzeniu.",
|
||||
"ok": "OK, zapisałem",
|
||||
"ok_lnd": "OK, zapisałem ",
|
||||
"text": "Poświęć whilę by zapisać tę frazę mnemoniczną na kartce papieru\nTo Twoja kopia zapasowa, którą możesz użyć później by odtworzyć portfel.",
|
||||
"text_lnd": "Proszę, poświęć chwilę, żeby zapisać to uwierzytelnienie LNDHub. To twoja kopia zapasowa, możesz użyć jej, aby odtworzyć portfel na innym urządzeniu.",
|
||||
"text_lnd2": "Ten portfel jest obsługiwany przez BlueWallet.",
|
||||
"title": "Twój portfel został utworzony ..."
|
||||
"title": "Twój portfel został utworzony"
|
||||
},
|
||||
"receive": {
|
||||
"details_create": "Stwórz",
|
||||
|
@ -276,6 +276,7 @@
|
|||
"electrum_history": "Historia serwerów",
|
||||
"electrum_reset_to_default": "Czy na pewno ustawić domyślne ustawienia Electrum?",
|
||||
"electrum_clear": "Wyczyść",
|
||||
"tor_supported": "TOR obsługiwany",
|
||||
"encrypt_decrypt": "Odszyfruj Schowek",
|
||||
"encrypt_decrypt_q": "Czy jesteś pewien, że chcesz odszyfrować schowek? To pozwoli na dostęp do twoich portfeli bez hasła.",
|
||||
"encrypt_del_uninstall": "Usuń, jeżeli BlueWallet jest odinstalowany.",
|
||||
|
@ -296,6 +297,7 @@
|
|||
"lightning_error_lndhub_uri": "Niepoprawny LndHub URI",
|
||||
"lightning_saved": "Wprowadzone przez ciebie zmiany zostały pomyślnie zachowane.",
|
||||
"lightning_settings": "Ustawienia Lightning",
|
||||
"tor_settings": "Ustawienia TOR",
|
||||
"lightning_settings_explain": "Aby podłączyć się do własnego node LND, zainstaluj najperw LndHub i wklej jego URL tutaj w ustawieniach. Zostaw puste by użyć LNDHub'a należącego do BlueWallet (lndhub.io). Portfele stworzone po zapisaniu zmian połączą się ze wskazanym LNDHub.",
|
||||
"network": "Sieć",
|
||||
"network_broadcast": "Rozgłoś transakcje",
|
||||
|
@ -565,15 +567,23 @@
|
|||
"sign_title": "Podpisz/Zweryfikuj wiadomość",
|
||||
"sign_help": "Tutaj możesz utworzyć lub zweryfikować kryptograficzny podpis w oparciu o adres Bitcoin",
|
||||
"sign_sign": "Podpisz",
|
||||
"sign_sign_submit": "Podpisz i Wyślij",
|
||||
"sign_verify": "Weryfikuj",
|
||||
"sign_signature_correct": "Weryfikacja pomyślna!",
|
||||
"sign_signature_incorrect": "Weryfikacja nie powiodła się!",
|
||||
"sign_placeholder_address": "Adres",
|
||||
"sign_placeholder_message": "Wiadomość",
|
||||
"sign_placeholder_signature": "Podpis",
|
||||
"sign_aopp_title": "AOPP",
|
||||
"sign_aopp_confirm": "Czy chcesz wysłać podpisaną wiadomość do [hostname]?",
|
||||
"address_balance": "Saldo: {balance} sats",
|
||||
"addresses_title": "Adresy",
|
||||
"type_change": "Reszta",
|
||||
"type_receive": "Otrzymaj"
|
||||
},
|
||||
"aopp": {
|
||||
"title": "Wybierz adres",
|
||||
"send_success": "Podpis wysłany z powodzeniem",
|
||||
"send_error": "Błąd wysyłania podpisu"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -407,6 +407,7 @@
|
|||
"details_no_cancel": "Não, cancelar",
|
||||
"details_save": "Salvar",
|
||||
"details_show_xpub": "Ver XPUB",
|
||||
"details_show_addresses": "Mostrar endereço",
|
||||
"details_title": "Carteira",
|
||||
"details_type": "Tipo",
|
||||
"details_use_with_hardware_wallet": "Usar com carteira de hardware",
|
||||
|
@ -564,11 +565,23 @@
|
|||
"sign_title": "Assinar/Verificar mensagem",
|
||||
"sign_help": "Aqui você pode criar ou verificar uma assinatura criptográfica utilizando um endereço de Bitcoin",
|
||||
"sign_sign": "Assinar",
|
||||
"sign_sign_submit": "Assinar e Enviar",
|
||||
"sign_verify": "Verificar",
|
||||
"sign_signature_correct": "Verificada com sucesso!",
|
||||
"sign_signature_incorrect": "Falha na verificação!",
|
||||
"sign_placeholder_address": "Endereço",
|
||||
"sign_placeholder_message": "Mensagem",
|
||||
"sign_placeholder_signature": "Assinatura"
|
||||
"sign_placeholder_signature": "Assinatura",
|
||||
"sign_aopp_title": "",
|
||||
"sign_aopp_confirm": "Você deseja enviar a mensagem assinada para {hostname}?",
|
||||
"address_balance": "Balanço: {balance} sats",
|
||||
"addresses_title": "Endereços",
|
||||
"type_change": "Alterar",
|
||||
"type_receive": "Receber"
|
||||
},
|
||||
"aopp": {
|
||||
"title": "Selecionar endereço",
|
||||
"send_success": "Assinatura enviada com sucesso",
|
||||
"send_error": "Erro no envio da assinatura"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
"seed": "Seed",
|
||||
"success": "Succes",
|
||||
"wallet_key": "Cheia portofelului",
|
||||
"invalid_animated_qr_code_fragment" : "Fragment animat de QRCode invalid. Încearcă din nou.",
|
||||
"invalid_animated_qr_code_fragment": "Fragment animat de QRCode invalid. Încearcă din nou.",
|
||||
"file_saved": "Fișierul ({filePath}) a fost salvat în folderul Downloads.",
|
||||
"discard_changes": "Renunți la modificări?",
|
||||
"discard_changes_detail": "Ai modificări nesalvate. Sigur vrei să renunți la ele și să părăsești ecranul?"
|
||||
|
@ -292,7 +292,7 @@
|
|||
"groundcontrol_explanation": "GroundControl este un server de notificări gratuit, open-source, pentru portofele Bitcoin. Poți instala propriul server GroundControl și să-i pui URL-ul aici pentru a nu te baza pe infrastructura BlueWallet. Lasă necompletat pentru a folosi serverul implicit al GroundControl.",
|
||||
"header": "Setări",
|
||||
"language": "Limbă",
|
||||
"language_restart": "Cînd selectezi o nouă limbă, e posibil să fie nevoie de restartarea BlueWallet pentru ca modificarea să-și facă efectul. ",
|
||||
"language_isRTL": "E nevoie de restartarea BlueWallet pentru ca orientarea lingvistică să-și facă efectul.",
|
||||
"lightning_error_lndhub_uri": "Nu este un URI valid LNDHub",
|
||||
"lightning_saved": "Modificările tale au fost salvate cu succes.",
|
||||
"lightning_settings": "Setări Lightning",
|
||||
|
@ -302,7 +302,7 @@
|
|||
"network_electrum": "Server Electrum",
|
||||
"not_a_valid_uri": "Nu este un URI valid.",
|
||||
"notifications": "Notificări",
|
||||
"open_link_in_explorer" : "Deschide link în explorer",
|
||||
"open_link_in_explorer": "Deschide link în explorer",
|
||||
"password": "Parolă",
|
||||
"password_explain": "Creează parola pe care o vei folosi pentru decriptarea spațiului de stocare.",
|
||||
"passwords_do_not_match": "Parolele nu se potrivesc.",
|
||||
|
@ -319,7 +319,7 @@
|
|||
"selfTest": "Auto-test",
|
||||
"save": "Salvează",
|
||||
"saved": "Salvat",
|
||||
"success_transaction_broadcasted" : "Realizat cu succes! Tranzacția ta a fost difuzată!",
|
||||
"success_transaction_broadcasted": "Realizat cu succes! Tranzacția ta a fost difuzată!",
|
||||
"total_balance": "Balanță totală",
|
||||
"total_balance_explanation": "Afișează balanța totală a tuturor portofelelor tale pe widget-urile ecranului tău principal.",
|
||||
"widgets": "Widget-uri"
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
"no": "Ne",
|
||||
"save": "Shrani",
|
||||
"seed": "Seme",
|
||||
"success": "Uspešno",
|
||||
"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.",
|
||||
|
@ -135,9 +136,9 @@
|
|||
"ask": "Ali ste shranili varnostno kopijo (seznam besed) vaše denarnice? Varnostna kopija je potrebna za dostop do vaših sredstev v primeru izgube naprave. Brez varnostne kopije bodo vaša sredstva trajno izgubljena.",
|
||||
"ask_no": "Ne, nisem",
|
||||
"ask_yes": "Da, sem",
|
||||
"ok": "V redu, sem si zapisal!",
|
||||
"ok_lnd": "V redu, sem shranil.",
|
||||
"text": "Prosimo zapišite si seznam besed (mnemonično seme) na list papirja. To je varnostna kopija, ki jo lahko uporabite za obnovitev denarnice na drugi napravi.",
|
||||
"ok": "V redu, sem si zapisal",
|
||||
"ok_lnd": "V redu, sem shranil",
|
||||
"text": "Prosimo zapišite si seznam besed (mnemonično seme) na list papirja.\nTo je varnostna kopija, ki jo lahko uporabite za obnovitev denarnice.",
|
||||
"text_lnd": "Shranite varnostno kopijo te denarnice. Omogoča vam obnovitev denarnice v primeru izgube te naprave.",
|
||||
"text_lnd2": "Ta denarnica uporablja gostovanje BlueWallet.",
|
||||
"title": "Vaša denarnica je ustvarjena"
|
||||
|
@ -174,7 +175,6 @@
|
|||
"details_address_field_is_not_valid": "Naslov ni veljaven",
|
||||
"details_adv_fee_bump": "Omogoči Povečanje Omrežnine",
|
||||
"details_adv_full": "Uporabi Celotno Stanje",
|
||||
"details_adv_full_remove": "Drugi prejemniki bodo odstranjeni iz te transakcije.",
|
||||
"details_adv_full_sure": "Ali ste prepričani, da želite za to transakcijo uporabiti celotno stanje denarnice?",
|
||||
"details_adv_import": "Uvozi transakcijo",
|
||||
"details_amount_field_is_not_valid": "Znesek ni veljaven",
|
||||
|
@ -183,15 +183,12 @@
|
|||
"details_error_decode": "Ni mogoče dekodirati Bitcoin naslova",
|
||||
"details_fee_field_is_not_valid": "Omrežnina ni veljavna",
|
||||
"details_next": "Naprej",
|
||||
"details_no_maximum": "Izbrana denarnica ne podpira samodejnega izračuna največjega stanja. Ali ste prepričani, da želite izbrati to denarnico?",
|
||||
"details_no_multiple": "Izbrana denarnica ne podpira pošiljanja več prejemnikom. Ali ste prepričani, da želite izbrati to denarnico?",
|
||||
"details_no_signed_tx": "Izbrana datoteka ne vsebuje transakcije, ki jo je mogoče uvoziti.",
|
||||
"details_note_placeholder": "lastna opomba",
|
||||
"details_scan": "Skeniraj",
|
||||
"details_total_exceeds_balance": "Znesek presega razpoložljivo stanje.",
|
||||
"details_unrecognized_file_format": "Neprepoznana oblika datoteke",
|
||||
"details_wallet_before_tx": "Pred ustvarjanjem transakcije, morate dodati Bitcoin denarnico.",
|
||||
"details_wallet_selection": "Izbira Denarnice",
|
||||
"dynamic_init": "Inicializacija",
|
||||
"dynamic_next": "Naprej",
|
||||
"dynamic_prev": "Nazaj",
|
||||
|
@ -279,6 +276,7 @@
|
|||
"electrum_history": "Zgodovina strežnikov",
|
||||
"electrum_reset_to_default": "Ali ste prepričani, da želite ponastaviti nastavitve Electrum strežnika na privzeto?",
|
||||
"electrum_clear": "Počisti",
|
||||
"tor_supported": "TOR podprt",
|
||||
"encrypt_decrypt": "Dešifriraj Shrambo",
|
||||
"encrypt_decrypt_q": "Ali ste prepričani, da želite dešifrirati shrambo? To bo omogočilo dostop do vaših denarnic brez gesla.",
|
||||
"encrypt_del_uninstall": "Izbriši, če je BlueWallet odstranjen",
|
||||
|
@ -295,10 +293,11 @@
|
|||
"groundcontrol_explanation": "GroundControl je brezplačen odprtokoden strežnik potisnih obvestil za bitcoin denarnice. Da se ne zanašate na BlueWallet infrastrukturo, lahko namestite svoj strežnik GroundControl in tukaj dodate njegov URL. Pustite prazno, za uporabo privzetega.",
|
||||
"header": "Nastavitve",
|
||||
"language": "Jezik",
|
||||
"language_restart": "Pri izbiri novega jezika bo morda potrebno ponovno zagnati BlueWallet, da bo sprememba začela veljati.",
|
||||
"language_isRTL": "Za spremembo orientacije pisave je potreben ponovni zagon aplikacije.",
|
||||
"lightning_error_lndhub_uri": "Neveljaven LndHub URI",
|
||||
"lightning_saved": "Spremembe so bile uspešno shranjene",
|
||||
"lightning_settings": "Lightning Nastavitve",
|
||||
"tor_settings": "TOR Nastavitve",
|
||||
"lightning_settings_explain": "Za povezavo z lastnim LND vozliščem, prosimo namestite LndHub in tukaj vnesite URL vozlišča. Pustite prazno za uporabo BlueWallet LNDHub (lndhub.io). Denarnice ustvarjene po potrditvi sprememb bodo povezane z novim LNDHub-om.",
|
||||
"network": "Omrežje",
|
||||
"network_broadcast": "Objavi transakcijo",
|
||||
|
@ -325,7 +324,8 @@
|
|||
"success_transaction_broadcasted" : "Vaša transakcija je bila objavljena!",
|
||||
"total_balance": "Skupno stanje",
|
||||
"total_balance_explanation": "Prikaži skupno stanje vseh denarnic na pripomočkih na domačem zaslonu.",
|
||||
"widgets": "Pripomočki"
|
||||
"widgets": "Pripomočki",
|
||||
"tools": "Orodja"
|
||||
},
|
||||
"notifications": {
|
||||
"would_you_like_to_receive_notifications": "Želite prikaz obvestil ob prejemu plačila?",
|
||||
|
@ -409,6 +409,7 @@
|
|||
"details_no_cancel": "Ne, prekliči",
|
||||
"details_save": "Shrani",
|
||||
"details_show_xpub": "Prikaži XPUB denarnice",
|
||||
"details_show_addresses": "Prikaži naslove",
|
||||
"details_title": "Denarnica",
|
||||
"details_type": "Tip",
|
||||
"details_use_with_hardware_wallet": "Uporaba s strojno denarnico",
|
||||
|
@ -561,5 +562,28 @@
|
|||
"MAX": "MAX",
|
||||
"sat_byte": "sat/bajt",
|
||||
"sats": "sats"
|
||||
},
|
||||
"addresses": {
|
||||
"sign_title": "Podpiši / Preveri sporočilo",
|
||||
"sign_help": "Ustvarite ali preverite kriptografski podpis na podlagi Bitcoin naslova",
|
||||
"sign_sign": "Podpiši",
|
||||
"sign_sign_submit": "Podpiši in Oddaj",
|
||||
"sign_verify": "Preveri",
|
||||
"sign_signature_correct": "Preverjanje uspešno!",
|
||||
"sign_signature_incorrect": "Preverjanje neuspešno!",
|
||||
"sign_placeholder_address": "Naslov",
|
||||
"sign_placeholder_message": "Sporočilo",
|
||||
"sign_placeholder_signature": "Podpis",
|
||||
"sign_aopp_title": "AOPP",
|
||||
"sign_aopp_confirm": "Ali želite podpisano sporočilo poslati na {hostname}?",
|
||||
"address_balance": "Stanje: {balance} sats",
|
||||
"addresses_title": "Naslovi",
|
||||
"type_change": "Vračilo",
|
||||
"type_receive": "Prejemni"
|
||||
},
|
||||
"aopp": {
|
||||
"title": "Izberite Naslov",
|
||||
"send_success": "Podpis uspešno poslan",
|
||||
"send_error": "Napaka pri pošiljanju podpisa"
|
||||
}
|
||||
}
|
||||
|
|
91
package-lock.json
generated
91
package-lock.json
generated
|
@ -5408,9 +5408,9 @@
|
|||
}
|
||||
},
|
||||
"@react-native-async-storage/async-storage": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-1.14.1.tgz",
|
||||
"integrity": "sha512-UkLUox2q5DKNYB6IMUzsuwrTJeXGLySvtQlnrqd3fd+96JErCT4X3xD+W1cvQjes0nm0LbaELbwObKc+Tea7wA==",
|
||||
"version": "1.15.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-1.15.1.tgz",
|
||||
"integrity": "sha512-rRQ3Zsz1DIzxVlRVsE4nRNLWyv4hXmt/Ecwan+ypKwp16+SUTsfdX7F/W0xu4FpUwQDFCjl7MAS9pKV2VEPwDg==",
|
||||
"requires": {
|
||||
"deep-assign": "^3.0.0"
|
||||
}
|
||||
|
@ -5812,11 +5812,6 @@
|
|||
"invariant": "^2.2.4"
|
||||
}
|
||||
},
|
||||
"@react-native-community/slider": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-community/slider/-/slider-3.0.3.tgz",
|
||||
"integrity": "sha512-8IeHfDwJ9/CTUwFs6x90VlobV3BfuPgNLjTgC6dRZovfCWigaZwVNIFFJnHBakK3pW2xErAPwhdvNR4JeNoYbw=="
|
||||
},
|
||||
"@react-navigation/core": {
|
||||
"version": "5.15.2",
|
||||
"resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-5.15.2.tgz",
|
||||
|
@ -6080,6 +6075,11 @@
|
|||
"@sinonjs/commons": "^1.7.0"
|
||||
}
|
||||
},
|
||||
"@types/async": {
|
||||
"version": "3.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/async/-/async-3.2.6.tgz",
|
||||
"integrity": "sha512-ZkrXnZLC1mc4b9QLKaSrsxV4oxTRs10OI2kgSApT8G0v1jrmqppSHUVQ15kLorzsFBTjvf7OKF4kAibuuNQ+xA=="
|
||||
},
|
||||
"@types/babel__core": {
|
||||
"version": "7.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.9.tgz",
|
||||
|
@ -9224,8 +9224,8 @@
|
|||
"integrity": "sha512-rQItBTFnol20HaaLm26UgSUduX7iGerwW7pEYX17MB1tI6LzFajiLV7iZ7LVcUcsN/7HrZUoCLrBauChy/IqEg=="
|
||||
},
|
||||
"electrum-client": {
|
||||
"version": "git+ssh://git@github.com/BlueWallet/rn-electrum-client.git#f9a827e724a5a2e578fdfdb483f83793af55b030",
|
||||
"from": "electrum-client@git+https://github.com/BlueWallet/rn-electrum-client.git#f9a827e724a5a2e578fdfdb483f83793af55b030"
|
||||
"version": "git+https://github.com/BlueWallet/rn-electrum-client.git#99ebcc649d91a8dc39bea7964b02dd9ead464aa4",
|
||||
"from": "git+https://github.com/BlueWallet/rn-electrum-client.git#99ebcc649d91a8dc39bea7964b02dd9ead464aa4"
|
||||
},
|
||||
"electrum-mnemonic": {
|
||||
"version": "2.0.0",
|
||||
|
@ -16926,6 +16926,11 @@
|
|||
"minimist": "^1.2.5"
|
||||
}
|
||||
},
|
||||
"mockdate": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/mockdate/-/mockdate-3.0.5.tgz",
|
||||
"integrity": "sha512-iniQP4rj1FhBdBYS/+eQv7j1tadJ9lJtdzgOpvsOHng/GbcDh2Fhdeq+ZRldrPYdXvCyfFUmFeEwEGXZB5I/AQ=="
|
||||
},
|
||||
"moment": {
|
||||
"version": "2.29.1",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
|
||||
|
@ -18639,9 +18644,9 @@
|
|||
"from": "react-native-blue-crypto@git+https://github.com/Overtorment/react-native-blue-crypto.git"
|
||||
},
|
||||
"react-native-camera": {
|
||||
"version": "3.43.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-camera/-/react-native-camera-3.43.1.tgz",
|
||||
"integrity": "sha512-hglDMBpJyqxwlyElYjeY5zTj838P5LWUQcOpcO8zKh+w6waPyDi1F4vUd/5KHjQ4hPt0xJXXYuxo0IRGo5hyHQ==",
|
||||
"version": "3.43.2",
|
||||
"resolved": "https://registry.npmjs.org/react-native-camera/-/react-native-camera-3.43.2.tgz",
|
||||
"integrity": "sha512-/5rY4+yu//nxvdhLkGnDQ3GgFH2ELX+w2ETWn4aKrg/o5ihP4w1K0tmePyMr//f9T4ioIzfWloGpBhz1eUi74g==",
|
||||
"requires": {
|
||||
"prop-types": "^15.6.2"
|
||||
}
|
||||
|
@ -18906,11 +18911,38 @@
|
|||
}
|
||||
},
|
||||
"react-native-reanimated": {
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-1.13.2.tgz",
|
||||
"integrity": "sha512-O+WhgxSjOIzcVdAAvx+h2DY331Ek1knKlaq+jsNLpC1fhRy9XTdOObovgob/aF2ve9uJfPEawCx8381g/tUJZQ==",
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-2.0.1.tgz",
|
||||
"integrity": "sha512-Wg/mEdI8xMRDQHYkgNGztJDjAcx1EFR5OMMtXrLSMmT0qzqcRWcVZgDHBN2MEAJqem/HkPAoOFutWzibwvinVg==",
|
||||
"requires": {
|
||||
"fbjs": "^1.0.0"
|
||||
"@babel/plugin-transform-object-assign": "^7.10.4",
|
||||
"fbjs": "^3.0.0",
|
||||
"mockdate": "^3.0.2",
|
||||
"string-hash-64": "^1.0.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"fbjs": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.0.tgz",
|
||||
"integrity": "sha512-dJd4PiDOFuhe7vk4F80Mba83Vr2QuK86FoxtgPmzBqEJahncp+13YCmfoa53KHCo6OnlXLG7eeMWPfB5CrpVKg==",
|
||||
"requires": {
|
||||
"cross-fetch": "^3.0.4",
|
||||
"fbjs-css-vars": "^1.0.0",
|
||||
"loose-envify": "^1.0.0",
|
||||
"object-assign": "^4.1.0",
|
||||
"promise": "^7.1.1",
|
||||
"setimmediate": "^1.0.5",
|
||||
"ua-parser-js": "^0.7.18"
|
||||
}
|
||||
},
|
||||
"promise": {
|
||||
"version": "7.3.1",
|
||||
"resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
|
||||
"integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
|
||||
"requires": {
|
||||
"asap": "~2.0.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-native-safe-area-context": {
|
||||
|
@ -18994,6 +19026,22 @@
|
|||
"version": "git+ssh://git@github.com/BlueWallet/react-native-tooltip.git#d369e7ece09e4dec73873f1cfeac83e9d35294a6",
|
||||
"from": "react-native-tooltip@git+https://github.com/BlueWallet/react-native-tooltip.git#d369e7ece09e4dec73873f1cfeac83e9d35294a6"
|
||||
},
|
||||
"react-native-tor": {
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/react-native-tor/-/react-native-tor-0.1.7.tgz",
|
||||
"integrity": "sha512-jIIo4at/cD/Hy6y/Ae+509XeJ/47AvAoRBJBOV0CsmpAh6WacBmIrkiEBv1SUqh2pkbXG6YGn3CRJFowbc5UYw==",
|
||||
"requires": {
|
||||
"@types/async": "^3.2.6",
|
||||
"async": "^3.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"async": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz",
|
||||
"integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-native-vector-icons": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-vector-icons/-/react-native-vector-icons-7.1.0.tgz",
|
||||
|
@ -19834,10 +19882,6 @@
|
|||
"object-assign": "^4.1.1"
|
||||
}
|
||||
},
|
||||
"scryptsy": {
|
||||
"version": "file:blue_modules/scryptsy",
|
||||
"integrity": "sha512-1CdSqHQowJBnMAFyPEBRfqag/YP9OF394FV+4YREIJX4ljD7OxvQRDayyoyyCk+senRjSkP6VnUNQmVQqB6g7w=="
|
||||
},
|
||||
"secp256k1": {
|
||||
"version": "3.8.0",
|
||||
"resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-3.8.0.tgz",
|
||||
|
@ -20390,6 +20434,11 @@
|
|||
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
|
||||
"integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY="
|
||||
},
|
||||
"string-hash-64": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/string-hash-64/-/string-hash-64-1.0.3.tgz",
|
||||
"integrity": "sha512-D5OKWKvDhyVWWn2x5Y9b+37NUllks34q1dCDhk/vYcso9fmhs+Tl3KR/gE4v5UNj2UA35cnX4KdVVGkG1deKqw=="
|
||||
},
|
||||
"string-length": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.1.tgz",
|
||||
|
|
10
package.json
10
package.json
|
@ -71,12 +71,11 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@babel/preset-env": "7.12.1",
|
||||
"@react-native-async-storage/async-storage": "1.14.1",
|
||||
"@react-native-async-storage/async-storage": "1.15.1",
|
||||
"@react-native-clipboard/clipboard": "1.7.0",
|
||||
"@react-native-community/blur": "3.6.0",
|
||||
"@react-native-community/masked-view": "0.1.10",
|
||||
"@react-native-community/push-notification-ios": "1.8.0",
|
||||
"@react-native-community/slider": "3.0.3",
|
||||
"@react-navigation/drawer": "5.12.4",
|
||||
"@react-navigation/native": "5.9.3",
|
||||
"@react-navigation/stack": "5.14.3",
|
||||
|
@ -103,7 +102,7 @@
|
|||
"dayjs": "1.10.4",
|
||||
"detox": "18.8.1",
|
||||
"ecurve": "1.0.6",
|
||||
"electrum-client": "git+https://github.com/BlueWallet/rn-electrum-client.git#f9a827e724a5a2e578fdfdb483f83793af55b030",
|
||||
"electrum-client": "git+https://github.com/BlueWallet/rn-electrum-client.git#99ebcc649d91a8dc39bea7964b02dd9ead464aa4",
|
||||
"electrum-mnemonic": "2.0.0",
|
||||
"eslint-config-prettier": "6.14.0",
|
||||
"eslint-config-standard": "14.1.1",
|
||||
|
@ -126,7 +125,7 @@
|
|||
"react-localization": "1.0.16",
|
||||
"react-native": "0.63.4",
|
||||
"react-native-blue-crypto": "git+https://github.com/Overtorment/react-native-blue-crypto.git",
|
||||
"react-native-camera": "3.43.1",
|
||||
"react-native-camera": "3.43.2",
|
||||
"react-native-crypto": "2.2.0",
|
||||
"react-native-default-preference": "1.4.3",
|
||||
"react-native-device-info": "8.0.7",
|
||||
|
@ -156,7 +155,7 @@
|
|||
"react-native-quick-actions": "0.3.13",
|
||||
"react-native-randombytes": "3.6.0",
|
||||
"react-native-rate": "1.2.4",
|
||||
"react-native-reanimated": "1.13.2",
|
||||
"react-native-reanimated": "2.0.1",
|
||||
"react-native-safe-area-context": "3.2.0",
|
||||
"react-native-screens": "2.18.1",
|
||||
"react-native-secure-key-store": "git+https://github.com/BlueWallet/react-native-secure-key-store.git#4828fd1a67d12e4c0e21eee0bee673fde75e6f9a",
|
||||
|
@ -166,6 +165,7 @@
|
|||
"react-native-svg": "12.1.0",
|
||||
"react-native-tcp-socket": "3.7.1",
|
||||
"react-native-tooltip": "git+https://github.com/BlueWallet/react-native-tooltip.git#d369e7ece09e4dec73873f1cfeac83e9d35294a6",
|
||||
"react-native-tor": "0.1.7",
|
||||
"react-native-vector-icons": "7.1.0",
|
||||
"react-native-watch-connectivity": "1.0.3",
|
||||
"react-native-webview": "11.3.1",
|
||||
|
|
|
@ -18,12 +18,12 @@ import {
|
|||
BlueLoading,
|
||||
BlueCopyTextToClipboard,
|
||||
BlueButton,
|
||||
SecondButton,
|
||||
BlueButtonLink,
|
||||
is,
|
||||
BlueText,
|
||||
BlueSpacing20,
|
||||
BlueAlertWalletExportReminder,
|
||||
BlueCard,
|
||||
} from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import BottomModal from '../../components/BottomModal';
|
||||
|
@ -91,21 +91,30 @@ const ReceiveDetails = () => {
|
|||
root: {
|
||||
flex: 1,
|
||||
backgroundColor: colors.elevated,
|
||||
},
|
||||
scroll: {
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
scrollBody: {
|
||||
flexGrow: 3,
|
||||
marginTop: 32,
|
||||
alignItems: 'center',
|
||||
paddingHorizontal: 16,
|
||||
},
|
||||
share: {
|
||||
justifyContent: 'center',
|
||||
paddingVertical: 16,
|
||||
alignItems: 'center',
|
||||
flexGrow: 1,
|
||||
marginBottom: 8,
|
||||
},
|
||||
link: {
|
||||
marginVertical: 16,
|
||||
paddingHorizontal: 32,
|
||||
},
|
||||
amount: {
|
||||
color: colors.foregroundColor,
|
||||
fontWeight: '600',
|
||||
fontSize: 36,
|
||||
textAlign: 'center',
|
||||
paddingBottom: 24,
|
||||
},
|
||||
label: {
|
||||
color: colors.foregroundColor,
|
||||
|
@ -119,10 +128,6 @@ const ReceiveDetails = () => {
|
|||
height: 300,
|
||||
backgroundColor: colors.elevated,
|
||||
},
|
||||
share: {
|
||||
marginBottom: 24,
|
||||
marginHorizontal: 16,
|
||||
},
|
||||
modalButton: {
|
||||
backgroundColor: colors.modalButton,
|
||||
paddingVertical: 14,
|
||||
|
@ -147,7 +152,7 @@ const ReceiveDetails = () => {
|
|||
};
|
||||
const renderReceiveDetails = () => {
|
||||
return (
|
||||
<ScrollView style={styles.root} contentContainerStyle={styles.scroll} keyboardShouldPersistTaps="always">
|
||||
<ScrollView contentContainerStyle={styles.root} keyboardShouldPersistTaps="always">
|
||||
<View style={styles.scrollBody}>
|
||||
{isCustom && (
|
||||
<>
|
||||
|
@ -187,10 +192,15 @@ const ReceiveDetails = () => {
|
|||
<BlueCopyTextToClipboard text={isCustom ? bip21encoded : address} />
|
||||
</View>
|
||||
<View style={styles.share}>
|
||||
<BlueButtonLink testID="SetCustomAmountButton" title={loc.receive.details_setAmount} onPress={showCustomAmountModal} />
|
||||
<View>
|
||||
<SecondButton onPress={handleShareButtonPressed} title={loc.receive.details_share} />
|
||||
</View>
|
||||
<BlueCard>
|
||||
<BlueButtonLink
|
||||
style={styles.link}
|
||||
testID="SetCustomAmountButton"
|
||||
title={loc.receive.details_setAmount}
|
||||
onPress={showCustomAmountModal}
|
||||
/>
|
||||
<BlueButton onPress={handleShareButtonPressed} title={loc.receive.details_share} />
|
||||
</BlueCard>
|
||||
</View>
|
||||
{renderCustomAmountModal()}
|
||||
</ScrollView>
|
||||
|
@ -212,7 +222,7 @@ const ReceiveDetails = () => {
|
|||
try {
|
||||
newAddress = await Promise.race([wallet.getAddressAsync(), sleep(1000)]);
|
||||
} catch (_) {}
|
||||
if (address === undefined) {
|
||||
if (newAddress === undefined) {
|
||||
// either sleep expired or getAddressAsync threw an exception
|
||||
console.warn('either sleep expired or getAddressAsync threw an exception');
|
||||
newAddress = wallet._getExternalAddressByIndex(wallet.getNextFreeAddressIndex());
|
||||
|
@ -224,7 +234,7 @@ const ReceiveDetails = () => {
|
|||
await Promise.race([wallet.getAddressAsync(), sleep(1000)]);
|
||||
newAddress = wallet.getAddress();
|
||||
} catch (_) {}
|
||||
if (address === undefined) {
|
||||
if (newAddress === undefined) {
|
||||
// either sleep expired or getAddressAsync threw an exception
|
||||
console.warn('either sleep expired or getAddressAsync threw an exception');
|
||||
newAddress = wallet.getAddress();
|
||||
|
|
|
@ -17,6 +17,7 @@ const bitcoin = require('bitcoinjs-lib');
|
|||
const BlueCrypto = require('react-native-blue-crypto');
|
||||
const encryption = require('../blue_modules/encryption');
|
||||
const BlueElectrum = require('../blue_modules/BlueElectrum');
|
||||
const torrific = require('../blue_modules/torrific');
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
center: {
|
||||
|
@ -53,6 +54,13 @@ export default class Selftest extends Component {
|
|||
|
||||
//
|
||||
|
||||
if (typeof navigator !== 'undefined' && navigator.product === 'ReactNative') {
|
||||
await torrific.testHttp();
|
||||
await torrific.testSocket();
|
||||
} else {
|
||||
// skipping RN-specific test'
|
||||
}
|
||||
|
||||
if (typeof navigator !== 'undefined' && navigator.product === 'ReactNative') {
|
||||
await BlueElectrum.ping();
|
||||
await BlueElectrum.waitTillConnected();
|
||||
|
|
|
@ -215,7 +215,7 @@ const ScanQRCode = () => {
|
|||
setIsLoading(false);
|
||||
} else {
|
||||
if (response.uri) {
|
||||
const uri = Platform.OS === 'ios' ? response.uri.toString().replace('file://', '') : response.uri;
|
||||
const uri = response.uri.toString().replace('file://', '');
|
||||
LocalQRCode.decode(uri, (error, result) => {
|
||||
if (!error) {
|
||||
onBarCodeRead({ data: result });
|
||||
|
|
|
@ -15,10 +15,12 @@ import loc, { formatBalance, formatBalanceWithoutSuffix } from '../../loc';
|
|||
import { BlueCurrentTheme } from '../../components/themes';
|
||||
import Notifications from '../../blue_modules/notifications';
|
||||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
import { Psbt } from 'bitcoinjs-lib';
|
||||
const currency = require('../../blue_modules/currency');
|
||||
const BlueElectrum = require('../../blue_modules/BlueElectrum');
|
||||
const Bignumber = require('bignumber.js');
|
||||
const bitcoin = require('bitcoinjs-lib');
|
||||
const torrific = require('../../blue_modules/torrific');
|
||||
|
||||
export default class Confirm extends Component {
|
||||
static contextType = BlueStorageContext;
|
||||
|
@ -69,11 +71,38 @@ export default class Confirm extends Component {
|
|||
} else {
|
||||
const wallet = new PayjoinTransaction(this.state.psbt, txHex => this.broadcast(txHex), this.state.fromWallet);
|
||||
const paymentScript = this.getPaymentScript();
|
||||
const payjoinClient = new PayjoinClient({
|
||||
paymentScript,
|
||||
wallet,
|
||||
payjoinUrl: this.state.payjoinUrl,
|
||||
});
|
||||
let payjoinClient;
|
||||
if (this.state.payjoinUrl.includes('.onion')) {
|
||||
console.warn('trying TOR....');
|
||||
const payjoinUrl = this.state.payjoinUrl;
|
||||
// working through TOR - crafting custom requester that will handle TOR http request
|
||||
const customPayjoinRequester = {
|
||||
requestPayjoin: async function (psbt: Psbt) {
|
||||
console.warn('requesting payjoin with psbt:', psbt.toBase64());
|
||||
const api = new torrific.Torsbee();
|
||||
const torResponse = await api.post(payjoinUrl, {
|
||||
headers: {
|
||||
'Content-Type': 'text/plain',
|
||||
},
|
||||
body: psbt.toBase64(),
|
||||
});
|
||||
console.warn('got torResponse.body');
|
||||
if (!torResponse.body) throw new Error('TOR failure, got ' + JSON.stringify(torResponse));
|
||||
return Psbt.fromBase64(torResponse.body);
|
||||
},
|
||||
};
|
||||
payjoinClient = new PayjoinClient({
|
||||
paymentScript,
|
||||
wallet,
|
||||
payjoinRequester: customPayjoinRequester,
|
||||
});
|
||||
} else {
|
||||
payjoinClient = new PayjoinClient({
|
||||
paymentScript,
|
||||
wallet,
|
||||
payjoinUrl: this.state.payjoinUrl,
|
||||
});
|
||||
}
|
||||
await payjoinClient.run();
|
||||
const payjoinPsbt = wallet.getPayjoinPsbt();
|
||||
if (payjoinPsbt) {
|
||||
|
|
|
@ -443,7 +443,7 @@ const SendDetails = () => {
|
|||
const changeAddress = await getChangeAddressAsync();
|
||||
const requestedSatPerByte = Number(feeRate);
|
||||
const lutxo = utxo || wallet.getUtxo();
|
||||
console.log({ requestedSatPerByte, utxo });
|
||||
console.log({ requestedSatPerByte, lutxo: lutxo.length });
|
||||
|
||||
const targets = [];
|
||||
for (const transaction of addresses) {
|
||||
|
@ -1005,10 +1005,10 @@ const SendDetails = () => {
|
|||
switch={{ value: isTransactionReplaceable, onValueChange: onReplaceableFeeSwitchValueChanged }}
|
||||
/>
|
||||
)}
|
||||
{wallet.type === WatchOnlyWallet.type && wallet.isHd() && wallet.getSecret().startsWith('zpub') && (
|
||||
{wallet.type === WatchOnlyWallet.type && wallet.isHd() && (
|
||||
<BlueListItem title={loc.send.details_adv_import} hideChevron component={TouchableOpacity} onPress={importTransaction} />
|
||||
)}
|
||||
{wallet.type === WatchOnlyWallet.type && wallet.isHd() && wallet.getSecret().startsWith('zpub') && (
|
||||
{wallet.type === WatchOnlyWallet.type && wallet.isHd() && (
|
||||
<BlueListItem
|
||||
testID="ImportQrTransactionButton"
|
||||
title={loc.send.details_adv_import + ' (QR)'}
|
||||
|
|
|
@ -37,7 +37,6 @@ import loc from '../../loc';
|
|||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
import Notifications from '../../blue_modules/notifications';
|
||||
const BlueElectrum = require('../../blue_modules/BlueElectrum');
|
||||
/** @type {AppStorage} */
|
||||
const bitcoin = require('bitcoinjs-lib');
|
||||
const fs = require('../../blue_modules/fs');
|
||||
|
||||
|
|
|
@ -13,6 +13,10 @@ const NetworkSettings = () => {
|
|||
navigate('ElectrumSettings');
|
||||
};
|
||||
|
||||
const navigateToTorSettings = () => {
|
||||
navigate('TorSettings');
|
||||
};
|
||||
|
||||
const navigateToLightningSettings = () => {
|
||||
navigate('LightningSettings');
|
||||
};
|
||||
|
@ -30,6 +34,7 @@ const NetworkSettings = () => {
|
|||
chevron
|
||||
/>
|
||||
)}
|
||||
<BlueListItem title={loc.settings.tor_settings} onPress={navigateToTorSettings} testID="TorSettings" chevron />
|
||||
</ScrollView>
|
||||
</SafeBlueArea>
|
||||
);
|
||||
|
|
|
@ -5,15 +5,7 @@ import { Icon } from 'react-native-elements';
|
|||
import { getApplicationName, getVersion, getBundleId, getBuildNumber, getUniqueId } from 'react-native-device-info';
|
||||
import Rate, { AndroidMarket } from 'react-native-rate';
|
||||
|
||||
import {
|
||||
BlueButton,
|
||||
BlueCard,
|
||||
BlueListItem,
|
||||
BlueSpacing20,
|
||||
BlueTextCentered,
|
||||
SafeBlueArea,
|
||||
BlueCopyToClipboardButton,
|
||||
} from '../../BlueComponents';
|
||||
import { BlueButton, BlueCard, BlueListItem, BlueSpacing20, BlueTextCentered, BlueCopyToClipboardButton } from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import loc from '../../loc';
|
||||
|
||||
|
@ -114,113 +106,111 @@ const About = () => {
|
|||
};
|
||||
|
||||
return (
|
||||
<SafeBlueArea>
|
||||
<ScrollView testID="AboutScrollView">
|
||||
<BlueCard>
|
||||
<View style={styles.center}>
|
||||
<Image style={styles.logo} source={require('../../img/bluebeast.png')} />
|
||||
<Text style={styles.textFree}>{loc.settings.about_free}</Text>
|
||||
<Text style={styles.textBackup}>{loc.settings.about_backup}</Text>
|
||||
<BlueButton onPress={handleOnRatePress} title={loc.settings.about_review + ' ⭐🙏'} />
|
||||
</View>
|
||||
</BlueCard>
|
||||
<BlueListItem
|
||||
leftIcon={{
|
||||
name: 'twitter',
|
||||
type: 'font-awesome',
|
||||
color: '#1da1f2',
|
||||
}}
|
||||
onPress={handleOnTwitterPress}
|
||||
title={loc.settings.about_sm_twitter}
|
||||
/>
|
||||
<BlueListItem
|
||||
leftIcon={{
|
||||
name: 'telegram',
|
||||
type: 'font-awesome',
|
||||
color: '#0088cc',
|
||||
}}
|
||||
onPress={handleOnTelegramPress}
|
||||
title={loc.settings.about_sm_telegram}
|
||||
/>
|
||||
<BlueListItem
|
||||
leftIcon={{
|
||||
name: 'discord',
|
||||
type: 'font-awesome-5',
|
||||
color: '#7289da',
|
||||
}}
|
||||
onPress={handleOnDiscordPress}
|
||||
title={loc.settings.about_sm_discord}
|
||||
/>
|
||||
<BlueCard>
|
||||
<View style={styles.buildWith}>
|
||||
<BlueSpacing20 />
|
||||
|
||||
<BlueTextCentered>{loc.settings.about_awesome} 👍</BlueTextCentered>
|
||||
<BlueSpacing20 />
|
||||
<BlueTextCentered>React Native</BlueTextCentered>
|
||||
<BlueTextCentered>bitcoinjs-lib</BlueTextCentered>
|
||||
<BlueTextCentered>Nodejs</BlueTextCentered>
|
||||
<BlueTextCentered>Electrum server</BlueTextCentered>
|
||||
<BlueSpacing20 />
|
||||
|
||||
<TouchableOpacity onPress={handleOnGithubPress} style={styles.buttonLink}>
|
||||
<Icon size={22} name="github" type="font-awesome-5" color={colors.foregroundColor} />
|
||||
<Text style={styles.textLink}>{loc.settings.about_sm_github}</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</BlueCard>
|
||||
<BlueListItem
|
||||
leftIcon={{
|
||||
name: 'book',
|
||||
type: 'font-awesome',
|
||||
color: '#9AA0AA',
|
||||
}}
|
||||
chevron
|
||||
onPress={handleOnReleaseNotesPress}
|
||||
title={loc.settings.about_release_notes}
|
||||
/>
|
||||
<BlueListItem
|
||||
leftIcon={{
|
||||
name: 'law',
|
||||
type: 'octicon',
|
||||
color: colors.foregroundColor,
|
||||
}}
|
||||
chevron
|
||||
onPress={handleOnLicensingPress}
|
||||
title={loc.settings.about_license}
|
||||
/>
|
||||
<BlueListItem
|
||||
leftIcon={{
|
||||
name: 'flask',
|
||||
type: 'font-awesome',
|
||||
color: '#FC0D44',
|
||||
}}
|
||||
chevron
|
||||
onPress={handleOnSelfTestPress}
|
||||
testID="RunSelfTestButton"
|
||||
title={loc.settings.about_selftest}
|
||||
/>
|
||||
<BlueSpacing20 />
|
||||
<BlueSpacing20 />
|
||||
<BlueTextCentered>
|
||||
{getApplicationName()} ver {getVersion()} (build {getBuildNumber()})
|
||||
</BlueTextCentered>
|
||||
<BlueTextCentered>{new Date(getBuildNumber() * 1000).toGMTString()}</BlueTextCentered>
|
||||
<BlueTextCentered>{getBundleId()}</BlueTextCentered>
|
||||
<BlueTextCentered>
|
||||
w, h = {width}, {height}
|
||||
</BlueTextCentered>
|
||||
<BlueTextCentered>Unique ID: {getUniqueId()}</BlueTextCentered>
|
||||
<View style={styles.copyToClipboard}>
|
||||
<BlueCopyToClipboardButton
|
||||
stringToCopy={(Platform.OS === 'android' ? 'user.id:' : 'app.device:') + getUniqueId()}
|
||||
displayText={loc.transactions.details_copy}
|
||||
/>
|
||||
<ScrollView testID="AboutScrollView" contentInsetAdjustmentBehavior="automatic">
|
||||
<BlueCard>
|
||||
<View style={styles.center}>
|
||||
<Image style={styles.logo} source={require('../../img/bluebeast.png')} />
|
||||
<Text style={styles.textFree}>{loc.settings.about_free}</Text>
|
||||
<Text style={styles.textBackup}>{loc.settings.about_backup}</Text>
|
||||
<BlueButton onPress={handleOnRatePress} title={loc.settings.about_review + ' ⭐🙏'} />
|
||||
</View>
|
||||
<BlueSpacing20 />
|
||||
<BlueSpacing20 />
|
||||
</ScrollView>
|
||||
</SafeBlueArea>
|
||||
</BlueCard>
|
||||
<BlueListItem
|
||||
leftIcon={{
|
||||
name: 'twitter',
|
||||
type: 'font-awesome',
|
||||
color: '#1da1f2',
|
||||
}}
|
||||
onPress={handleOnTwitterPress}
|
||||
title={loc.settings.about_sm_twitter}
|
||||
/>
|
||||
<BlueListItem
|
||||
leftIcon={{
|
||||
name: 'telegram',
|
||||
type: 'font-awesome',
|
||||
color: '#0088cc',
|
||||
}}
|
||||
onPress={handleOnTelegramPress}
|
||||
title={loc.settings.about_sm_telegram}
|
||||
/>
|
||||
<BlueListItem
|
||||
leftIcon={{
|
||||
name: 'discord',
|
||||
type: 'font-awesome-5',
|
||||
color: '#7289da',
|
||||
}}
|
||||
onPress={handleOnDiscordPress}
|
||||
title={loc.settings.about_sm_discord}
|
||||
/>
|
||||
<BlueCard>
|
||||
<View style={styles.buildWith}>
|
||||
<BlueSpacing20 />
|
||||
|
||||
<BlueTextCentered>{loc.settings.about_awesome} 👍</BlueTextCentered>
|
||||
<BlueSpacing20 />
|
||||
<BlueTextCentered>React Native</BlueTextCentered>
|
||||
<BlueTextCentered>bitcoinjs-lib</BlueTextCentered>
|
||||
<BlueTextCentered>Nodejs</BlueTextCentered>
|
||||
<BlueTextCentered>Electrum server</BlueTextCentered>
|
||||
<BlueSpacing20 />
|
||||
|
||||
<TouchableOpacity onPress={handleOnGithubPress} style={styles.buttonLink}>
|
||||
<Icon size={22} name="github" type="font-awesome-5" color={colors.foregroundColor} />
|
||||
<Text style={styles.textLink}>{loc.settings.about_sm_github}</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</BlueCard>
|
||||
<BlueListItem
|
||||
leftIcon={{
|
||||
name: 'book',
|
||||
type: 'font-awesome',
|
||||
color: '#9AA0AA',
|
||||
}}
|
||||
chevron
|
||||
onPress={handleOnReleaseNotesPress}
|
||||
title={loc.settings.about_release_notes}
|
||||
/>
|
||||
<BlueListItem
|
||||
leftIcon={{
|
||||
name: 'law',
|
||||
type: 'octicon',
|
||||
color: colors.foregroundColor,
|
||||
}}
|
||||
chevron
|
||||
onPress={handleOnLicensingPress}
|
||||
title={loc.settings.about_license}
|
||||
/>
|
||||
<BlueListItem
|
||||
leftIcon={{
|
||||
name: 'flask',
|
||||
type: 'font-awesome',
|
||||
color: '#FC0D44',
|
||||
}}
|
||||
chevron
|
||||
onPress={handleOnSelfTestPress}
|
||||
testID="RunSelfTestButton"
|
||||
title={loc.settings.about_selftest}
|
||||
/>
|
||||
<BlueSpacing20 />
|
||||
<BlueSpacing20 />
|
||||
<BlueTextCentered>
|
||||
{getApplicationName()} ver {getVersion()} (build {getBuildNumber()})
|
||||
</BlueTextCentered>
|
||||
<BlueTextCentered>{new Date(getBuildNumber() * 1000).toGMTString()}</BlueTextCentered>
|
||||
<BlueTextCentered>{getBundleId()}</BlueTextCentered>
|
||||
<BlueTextCentered>
|
||||
w, h = {width}, {height}
|
||||
</BlueTextCentered>
|
||||
<BlueTextCentered>Unique ID: {getUniqueId()}</BlueTextCentered>
|
||||
<View style={styles.copyToClipboard}>
|
||||
<BlueCopyToClipboardButton
|
||||
stringToCopy={(Platform.OS === 'android' ? 'user.id:' : 'app.device:') + getUniqueId()}
|
||||
displayText={loc.transactions.details_copy}
|
||||
/>
|
||||
</View>
|
||||
<BlueSpacing20 />
|
||||
<BlueSpacing20 />
|
||||
</ScrollView>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -220,9 +220,11 @@ export default class ElectrumSettings extends Component {
|
|||
<BlueCard>
|
||||
<BlueText style={styles.status}>{loc.settings.electrum_status}</BlueText>
|
||||
<View style={styles.connectWrap}>
|
||||
<View style={[styles.container, this.state.config.status === 1 ? styles.containerConnected : styles.containerDisconnected]}>
|
||||
<BlueText style={this.state.config.status === 1 ? styles.textConnected : styles.textDisconnected}>
|
||||
{this.state.config.status === 1 ? loc.settings.electrum_connected : loc.settings.electrum_connected_not}
|
||||
<View
|
||||
style={[styles.container, this.state.config.connected === 1 ? styles.containerConnected : styles.containerDisconnected]}
|
||||
>
|
||||
<BlueText style={this.state.config.connected === 1 ? styles.textConnected : styles.textDisconnected}>
|
||||
{this.state.config.connected === 1 ? loc.settings.electrum_connected : loc.settings.electrum_connected_not}
|
||||
</BlueText>
|
||||
</View>
|
||||
</View>
|
||||
|
@ -287,6 +289,7 @@ export default class ElectrumSettings extends Component {
|
|||
testID="SSLPortInput"
|
||||
/>
|
||||
</View>
|
||||
<BlueText style={styles.torSupported}>{loc.settings.tor_supported}</BlueText>
|
||||
<BlueSpacing20 />
|
||||
<BlueButtonLink title={loc.wallets.import_scan_qr} onPress={this.importScan} />
|
||||
<BlueSpacing20 />
|
||||
|
@ -402,4 +405,7 @@ const styles = StyleSheet.create({
|
|||
borderBottomColor: BlueCurrentTheme.colors.formBorder,
|
||||
borderBottomWidth: 1,
|
||||
},
|
||||
torSupported: {
|
||||
color: '#81868e',
|
||||
},
|
||||
});
|
||||
|
|
|
@ -4,7 +4,7 @@ import { FlatList, StyleSheet } from 'react-native';
|
|||
import { useNavigation, useTheme } from '@react-navigation/native';
|
||||
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import { SafeBlueArea, BlueListItem, BlueLoading } from '../../BlueComponents';
|
||||
import { BlueListItem } from '../../BlueComponents';
|
||||
import loc from '../../loc';
|
||||
import { AvailableLanguages } from '../../loc/languages';
|
||||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
|
@ -17,7 +17,6 @@ const styles = StyleSheet.create({
|
|||
|
||||
const Language = () => {
|
||||
const { setLanguage, language } = useContext(BlueStorageContext);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [selectedLanguage, setSelectedLanguage] = useState(loc.getLanguage());
|
||||
const { setOptions } = useNavigation();
|
||||
const { colors } = useTheme();
|
||||
|
@ -26,9 +25,6 @@ const Language = () => {
|
|||
backgroundColor: colors.background,
|
||||
},
|
||||
});
|
||||
useEffect(() => {
|
||||
setIsLoading(false);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setOptions({ title: loc.settings.language });
|
||||
|
@ -54,18 +50,15 @@ const Language = () => {
|
|||
);
|
||||
};
|
||||
|
||||
return isLoading ? (
|
||||
<BlueLoading />
|
||||
) : (
|
||||
<SafeBlueArea>
|
||||
<FlatList
|
||||
style={[styles.flex, stylesHook.flex]}
|
||||
keyExtractor={(_item, index) => `${index}`}
|
||||
data={AvailableLanguages}
|
||||
renderItem={renderItem}
|
||||
initialNumToRender={25}
|
||||
/>
|
||||
</SafeBlueArea>
|
||||
return (
|
||||
<FlatList
|
||||
style={[styles.flex, stylesHook.flex]}
|
||||
keyExtractor={(_item, index) => `${index}`}
|
||||
data={AvailableLanguages}
|
||||
renderItem={renderItem}
|
||||
initialNumToRender={25}
|
||||
contentInsetAdjustmentBehavior="automatic"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -37,6 +37,9 @@ const styles = StyleSheet.create({
|
|||
backgroundColor: 'transparent',
|
||||
flexDirection: I18nManager.isRTL ? 'row-reverse' : 'row',
|
||||
},
|
||||
torSupported: {
|
||||
color: '#81868e',
|
||||
},
|
||||
});
|
||||
|
||||
const LightningSettings = () => {
|
||||
|
@ -144,6 +147,8 @@ const LightningSettings = () => {
|
|||
/>
|
||||
</View>
|
||||
|
||||
<BlueText style={[styles.torSupported]}>{loc.settings.tor_supported}</BlueText>
|
||||
<BlueSpacing20 />
|
||||
<BlueButtonLink title={loc.wallets.import_scan_qr} testID="ImportScan" onPress={importScan} />
|
||||
<BlueSpacing20 />
|
||||
{isLoading ? <BlueLoading /> : <BlueButton testID="Save" onPress={save} title={loc.settings.save} />}
|
||||
|
|
94
screen/settings/torSettings.js
Normal file
94
screen/settings/torSettings.js
Normal file
|
@ -0,0 +1,94 @@
|
|||
/* global alert */
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { View, StyleSheet } from 'react-native';
|
||||
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import { BlueButton, BlueCard, BlueLoading, BlueSpacing20, BlueText, SafeBlueArea } from '../../BlueComponents';
|
||||
import loc from '../../loc';
|
||||
|
||||
const torrific = require('../../blue_modules/torrific');
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
torSupported: {
|
||||
color: '#81868e',
|
||||
},
|
||||
});
|
||||
|
||||
const TorSettings = () => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [daemonStatus, setDaemonStatus] = useState('');
|
||||
|
||||
const updateStatus = async () => {
|
||||
const status = await torrific.getDaemonStatus();
|
||||
setDaemonStatus(status);
|
||||
};
|
||||
|
||||
const startIfNotStarted = async () => {
|
||||
await torrific.startIfNotStarted();
|
||||
};
|
||||
|
||||
const stopIfRunning = async () => {
|
||||
await torrific.stopIfRunning();
|
||||
};
|
||||
|
||||
const testSocket = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
await torrific.testSocket();
|
||||
alert('OK');
|
||||
} catch (error) {
|
||||
alert(error.message);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const testHttp = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
await torrific.testHttp();
|
||||
alert('OK');
|
||||
} catch (error) {
|
||||
alert(error.message);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(updateStatus, 1000);
|
||||
return () => {
|
||||
clearInterval(interval);
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<View style={[styles.root]}>
|
||||
<BlueLoading />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SafeBlueArea>
|
||||
<BlueCard>
|
||||
<BlueText>Daemon Status: {daemonStatus}</BlueText>
|
||||
</BlueCard>
|
||||
<BlueCard>
|
||||
<BlueButton title="start" onPress={startIfNotStarted} />
|
||||
<BlueSpacing20 />
|
||||
<BlueButton title="stop" onPress={stopIfRunning} />
|
||||
<BlueSpacing20 />
|
||||
<BlueButton title="test socket" onPress={testSocket} />
|
||||
<BlueSpacing20 />
|
||||
<BlueButton title="test http" onPress={testHttp} />
|
||||
</BlueCard>
|
||||
</SafeBlueArea>
|
||||
);
|
||||
};
|
||||
|
||||
TorSettings.navigationOptions = navigationStyle({}, opts => ({ ...opts, title: loc.settings.tor_settings }));
|
||||
|
||||
export default TorSettings;
|
|
@ -3,7 +3,6 @@ import { Image, View, Text, ScrollView, StyleSheet } from 'react-native';
|
|||
import { useTheme } from '@react-navigation/native';
|
||||
import { SafeBlueArea, BlueLoading } from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
/** @type {AppStorage} */
|
||||
import loc from '../../loc';
|
||||
|
||||
const WalletsAddMultisigHelp = () => {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useCallback, useState, useContext } from 'react';
|
||||
import React, { useCallback, useState, useContext, useRef, useEffect } from 'react';
|
||||
import { ActivityIndicator, FlatList, StyleSheet, View, StatusBar } from 'react-native';
|
||||
import { useFocusEffect, useNavigation, useRoute, useTheme } from '@react-navigation/native';
|
||||
import Privacy from '../../blue_modules/Privacy';
|
||||
|
@ -49,6 +49,8 @@ const WalletAddresses = () => {
|
|||
|
||||
const { walletID } = useRoute().params;
|
||||
|
||||
const addressList = useRef();
|
||||
|
||||
const wallet = wallets.find(w => w.getID() === walletID);
|
||||
|
||||
const balanceUnit = wallet.getPreferredBalanceUnit();
|
||||
|
@ -65,6 +67,12 @@ const WalletAddresses = () => {
|
|||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (showAddresses) {
|
||||
addressList.current.scrollToIndex({ animated: false, index: 0 });
|
||||
}
|
||||
}, [showAddresses]);
|
||||
|
||||
const getAddresses = () => {
|
||||
const addressList = [];
|
||||
|
||||
|
@ -108,30 +116,22 @@ const WalletAddresses = () => {
|
|||
return <AddressItem {...item} balanceUnit={balanceUnit} onPress={() => navigateToReceive(item)} />;
|
||||
};
|
||||
|
||||
const render = () => {
|
||||
if (showAddresses) {
|
||||
return (
|
||||
<View style={stylesHook.root}>
|
||||
<StatusBar barStyle="default" />
|
||||
<FlatList
|
||||
style={stylesHook.root}
|
||||
data={addresses}
|
||||
initialNumToRender={20}
|
||||
contentInsetAdjustmentBehavior="automatic"
|
||||
renderItem={renderRow}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={[stylesHook.root, styles.loading]}>
|
||||
<ActivityIndicator />
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
return render();
|
||||
return (
|
||||
<View style={[styles.root, stylesHook.root]}>
|
||||
<StatusBar barStyle="default" />
|
||||
<FlatList
|
||||
contentContainerStyle={stylesHook.root}
|
||||
ref={addressList}
|
||||
data={addresses}
|
||||
extraData={addresses}
|
||||
initialNumToRender={40}
|
||||
renderItem={renderRow}
|
||||
ListEmptyComponent={<ActivityIndicator />}
|
||||
centerContent={!showAddresses}
|
||||
contentInsetAdjustmentBehavior="automatic"
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
WalletAddresses.navigationOptions = navigationStyle({
|
||||
|
@ -144,22 +144,4 @@ const styles = StyleSheet.create({
|
|||
root: {
|
||||
flex: 1,
|
||||
},
|
||||
loading: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
},
|
||||
loadMoreButton: {
|
||||
borderRadius: 9,
|
||||
minHeight: 49,
|
||||
paddingHorizontal: 8,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
alignSelf: 'auto',
|
||||
flexGrow: 1,
|
||||
marginHorizontal: 16,
|
||||
},
|
||||
loadMoreText: {
|
||||
fontSize: 16,
|
||||
},
|
||||
});
|
||||
|
|
|
@ -106,6 +106,12 @@ const styles = StyleSheet.create({
|
|||
fontWeight: '500',
|
||||
textAlign: 'center',
|
||||
},
|
||||
row: {
|
||||
flexDirection: 'row',
|
||||
},
|
||||
secondrow: {
|
||||
marginLeft: 16,
|
||||
},
|
||||
});
|
||||
|
||||
const WalletDetails = () => {
|
||||
|
@ -116,6 +122,8 @@ const WalletDetails = () => {
|
|||
const wallet = useRef(wallets.find(w => w.getID() === walletID)).current;
|
||||
const [walletName, setWalletName] = useState(wallet.getLabel());
|
||||
const [useWithHardwareWallet, setUseWithHardwareWallet] = useState(wallet.useWithHardwareWalletEnabled());
|
||||
const { isAdancedModeEnabled } = useContext(BlueStorageContext);
|
||||
const [isAdvancedModeEnabledRender, setIsAdvancedModeEnabledRender] = useState(false);
|
||||
const [hideTransactionsInWalletsList, setHideTransactionsInWalletsList] = useState(!wallet.getHideTransactionsInWalletsList());
|
||||
const { goBack, navigate, setOptions, popToTop } = useNavigation();
|
||||
const { colors } = useTheme();
|
||||
|
@ -144,7 +152,7 @@ const WalletDetails = () => {
|
|||
setIsLoading(true);
|
||||
if (walletName.trim().length > 0) {
|
||||
wallet.setLabel(walletName.trim());
|
||||
if (wallet.type === WatchOnlyWallet.type && wallet.getSecret().startsWith('zpub')) {
|
||||
if (wallet.type === WatchOnlyWallet.type && wallet.isHd()) {
|
||||
wallet.setUseWithHardwareWalletEnabled(useWithHardwareWallet);
|
||||
}
|
||||
wallet.setHideTransactionsInWalletsList(!hideTransactionsInWalletsList);
|
||||
|
@ -161,6 +169,7 @@ const WalletDetails = () => {
|
|||
};
|
||||
|
||||
useEffect(() => {
|
||||
isAdancedModeEnabled().then(setIsAdvancedModeEnabledRender);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
setOptions({
|
||||
headerRight: () => (
|
||||
|
@ -476,13 +485,6 @@ const WalletDetails = () => {
|
|||
</>
|
||||
)}
|
||||
|
||||
{!!wallet.getDerivationPath() && (
|
||||
<>
|
||||
<Text style={[styles.textLabel2, stylesHook.textLabel2]}>{loc.wallets.details_derivation_path}</Text>
|
||||
<BlueText>{wallet.getDerivationPath()}</BlueText>
|
||||
</>
|
||||
)}
|
||||
|
||||
{wallet.type === LightningCustodianWallet.type && (
|
||||
<>
|
||||
<Text style={[styles.textLabel1, stylesHook.textLabel1]}>{loc.wallets.details_connected_to.toLowerCase()}</Text>
|
||||
|
@ -513,7 +515,7 @@ const WalletDetails = () => {
|
|||
</>
|
||||
|
||||
<View>
|
||||
{wallet.type === WatchOnlyWallet.type && wallet.getSecret().startsWith('zpub') && (
|
||||
{wallet.type === WatchOnlyWallet.type && wallet.isHd() && (
|
||||
<>
|
||||
<BlueSpacing10 />
|
||||
<Text style={[styles.textLabel2, stylesHook.textLabel2]}>{loc.wallets.details_advanced.toLowerCase()}</Text>
|
||||
|
@ -523,12 +525,22 @@ const WalletDetails = () => {
|
|||
</View>
|
||||
</>
|
||||
)}
|
||||
{isAdvancedModeEnabledRender && (
|
||||
<View style={styles.row}>
|
||||
{wallet.allowMasterFingerprint() && (
|
||||
<View>
|
||||
<Text style={[styles.textLabel2, stylesHook.textLabel2]}>{loc.wallets.details_master_fingerprint.toLowerCase()}</Text>
|
||||
<BlueText>{wallet.getMasterFingerprintHex()}</BlueText>
|
||||
</View>
|
||||
)}
|
||||
|
||||
{wallet.allowMasterFingerprint() && (
|
||||
<>
|
||||
<Text style={[styles.textLabel2, stylesHook.textLabel2]}>{loc.wallets.details_master_fingerprint.toLowerCase()}</Text>
|
||||
<BlueText>{wallet.getMasterFingerprintHex()}</BlueText>
|
||||
</>
|
||||
{!!wallet.getDerivationPath() && (
|
||||
<View style={styles.secondrow}>
|
||||
<Text style={[styles.textLabel2, stylesHook.textLabel2]}>{loc.wallets.details_derivation_path}</Text>
|
||||
<BlueText>{wallet.getDerivationPath()}</BlueText>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</BlueCard>
|
||||
|
|
|
@ -2,7 +2,7 @@ import React, { useEffect, useState, useCallback, useContext } from 'react';
|
|||
import { ActivityIndicator, View, BackHandler, Text, ScrollView, StyleSheet, StatusBar, I18nManager } from 'react-native';
|
||||
import { useNavigation, useRoute, useTheme } from '@react-navigation/native';
|
||||
|
||||
import { BlueSpacing20, SafeBlueArea, BlueText, BlueButton } from '../../BlueComponents';
|
||||
import { SafeBlueArea, BlueButton } from '../../BlueComponents';
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import Privacy from '../../blue_modules/Privacy';
|
||||
import loc from '../../loc';
|
||||
|
@ -71,15 +71,15 @@ const PleaseBackup = () => {
|
|||
</View>
|
||||
) : (
|
||||
<SafeBlueArea style={stylesHook.flex}>
|
||||
<StatusBar barStyle="default" />
|
||||
<ScrollView testID="PleaseBackupScrollView">
|
||||
<StatusBar barStyle="light-content" />
|
||||
<ScrollView contentContainerStyle={styles.flex} testID="PleaseBackupScrollView">
|
||||
<View style={styles.please}>
|
||||
<BlueText style={[styles.successText, stylesHook.successText]}>{loc.pleasebackup.success}</BlueText>
|
||||
<BlueText style={[styles.pleaseText, stylesHook.pleaseText]}>{loc.pleasebackup.text}</BlueText>
|
||||
|
||||
<Text style={[styles.pleaseText, stylesHook.pleaseText]}>{loc.pleasebackup.text}</Text>
|
||||
</View>
|
||||
<View style={styles.list}>
|
||||
<View style={styles.secret}>{renderSecret()}</View>
|
||||
|
||||
<BlueSpacing20 />
|
||||
</View>
|
||||
<View style={styles.bottom}>
|
||||
<BlueButton testID="PleasebackupOk" onPress={handleBackButton} title={loc.pleasebackup.ok} />
|
||||
</View>
|
||||
</ScrollView>
|
||||
|
@ -103,6 +103,10 @@ const styles = StyleSheet.create({
|
|||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
},
|
||||
flex: {
|
||||
flex: 1,
|
||||
justifyContent: 'space-around',
|
||||
},
|
||||
word: {
|
||||
marginRight: 8,
|
||||
marginBottom: 8,
|
||||
|
@ -115,19 +119,29 @@ const styles = StyleSheet.create({
|
|||
wortText: {
|
||||
fontWeight: 'bold',
|
||||
textAlign: 'left',
|
||||
fontSize: 17,
|
||||
},
|
||||
please: {
|
||||
alignItems: 'center',
|
||||
flexGrow: 1,
|
||||
paddingHorizontal: 16,
|
||||
},
|
||||
list: {
|
||||
flexGrow: 8,
|
||||
paddingHorizontal: 16,
|
||||
},
|
||||
bottom: {
|
||||
flexGrow: 2,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
successText: {
|
||||
textAlign: 'center',
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
pleaseText: {
|
||||
paddingBottom: 10,
|
||||
paddingRight: 0,
|
||||
paddingLeft: 0,
|
||||
marginVertical: 16,
|
||||
fontSize: 16,
|
||||
fontWeight: '500',
|
||||
},
|
||||
secret: {
|
||||
flexWrap: 'wrap',
|
||||
|
|
|
@ -149,7 +149,7 @@ const WalletTransactions = () => {
|
|||
|
||||
// refresh transactions if it never hasn't been done. It could be a fresh imported wallet
|
||||
useEffect(() => {
|
||||
if (wallet._lastTxFetch === 0) {
|
||||
if (wallet.getLastTxFetch() === 0) {
|
||||
refreshTransactions();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
|
@ -463,7 +463,7 @@ const WalletTransactions = () => {
|
|||
},
|
||||
response => {
|
||||
if (response.uri) {
|
||||
const uri = Platform.OS === 'ios' ? response.uri.toString().replace('file://', '') : response.uri;
|
||||
const uri = response.uri.toString().replace('file://', '');
|
||||
LocalQRCode.decode(uri, (error, result) => {
|
||||
if (!error) {
|
||||
onBarCodeRead({ data: result });
|
||||
|
@ -482,35 +482,31 @@ const WalletTransactions = () => {
|
|||
|
||||
const sendButtonPress = () => {
|
||||
if (wallet.chain === Chain.OFFCHAIN) {
|
||||
navigate('ScanLndInvoiceRoot', { screen: 'ScanLndInvoice', params: { walletID: wallet.getID() } });
|
||||
} else {
|
||||
if (wallet.type === WatchOnlyWallet.type && wallet.isHd() && wallet.getSecret().startsWith('zpub')) {
|
||||
if (wallet.useWithHardwareWalletEnabled()) {
|
||||
navigateToSendScreen();
|
||||
} else {
|
||||
Alert.alert(
|
||||
loc.wallets.details_title,
|
||||
loc.transactions.enable_offline_signing,
|
||||
[
|
||||
{
|
||||
text: loc._.ok,
|
||||
onPress: async () => {
|
||||
wallet.setUseWithHardwareWalletEnabled(true);
|
||||
await saveToDisk();
|
||||
navigateToSendScreen();
|
||||
},
|
||||
style: 'default',
|
||||
},
|
||||
|
||||
{ text: loc._.cancel, onPress: () => {}, style: 'cancel' },
|
||||
],
|
||||
{ cancelable: false },
|
||||
);
|
||||
}
|
||||
} else {
|
||||
navigateToSendScreen();
|
||||
}
|
||||
return navigate('ScanLndInvoiceRoot', { screen: 'ScanLndInvoice', params: { walletID: wallet.getID() } });
|
||||
}
|
||||
|
||||
if (wallet.type === WatchOnlyWallet.type && wallet.isHd() && !wallet.useWithHardwareWalletEnabled()) {
|
||||
return Alert.alert(
|
||||
loc.wallets.details_title,
|
||||
loc.transactions.enable_offline_signing,
|
||||
[
|
||||
{
|
||||
text: loc._.ok,
|
||||
onPress: async () => {
|
||||
wallet.setUseWithHardwareWalletEnabled(true);
|
||||
await saveToDisk();
|
||||
navigateToSendScreen();
|
||||
},
|
||||
style: 'default',
|
||||
},
|
||||
|
||||
{ text: loc._.cancel, onPress: () => {}, style: 'cancel' },
|
||||
],
|
||||
{ cancelable: false },
|
||||
);
|
||||
}
|
||||
|
||||
navigateToSendScreen();
|
||||
};
|
||||
|
||||
const sendButtonLongPress = async () => {
|
||||
|
@ -697,7 +693,7 @@ const WalletTransactions = () => {
|
|||
}
|
||||
/>
|
||||
)}
|
||||
{(wallet.allowSend() || (wallet.type === WatchOnlyWallet.type && wallet.isHd() && wallet.getSecret().startsWith('zpub'))) && (
|
||||
{(wallet.allowSend() || (wallet.type === WatchOnlyWallet.type && wallet.isHd())) && (
|
||||
<FButton
|
||||
onLongPress={sendButtonLongPress}
|
||||
onPress={sendButtonPress}
|
||||
|
|
|
@ -824,7 +824,7 @@ describe('BlueWallet UI Tests', () => {
|
|||
await helperImportWallet(
|
||||
'zpub6rDWXE4wbwefeCrHWehXJheXnti5F9PbpamDUeB5eFbqaY89x3jq86JADBuXpnJnSvRVwqkaTnyMaZERUg4BpxD9V4tSZfKeYh1ozPdL1xK',
|
||||
'Imported Watch-only',
|
||||
'0.00030666 BTC',
|
||||
'0 BTC', // it used to be 0.00030666 till someone stole it from git history kek
|
||||
);
|
||||
|
||||
await element(by.id('ReceiveButton')).tap();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const bitcoin = require('bitcoinjs-lib');
|
||||
global.net = require('net');
|
||||
global.tls = require('tls');
|
||||
const net = require('net');
|
||||
const tls = require('tls');
|
||||
|
||||
const assert = require('assert');
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 150 * 1000;
|
||||
|
@ -19,7 +19,7 @@ describe('ElectrumClient', () => {
|
|||
const ElectrumClient = require('electrum-client');
|
||||
|
||||
for (const peer of hardcodedPeers) {
|
||||
const mainClient = new ElectrumClient(peer.ssl || peer.tcp, peer.host, peer.ssl ? 'tls' : 'tcp');
|
||||
const mainClient = new ElectrumClient(net, tls, peer.ssl || peer.tcp, peer.host, peer.ssl ? 'tls' : 'tcp');
|
||||
|
||||
try {
|
||||
await mainClient.connect();
|
||||
|
|
|
@ -167,7 +167,7 @@ describe('LightningCustodianWallet', () => {
|
|||
},
|
||||
body: '{"amount": "0.01", "currency": "USD"}',
|
||||
});
|
||||
if (!res.body.data || !res.body.data.lightning_invoice || !res.body.data.lightning_invoice.payreq) {
|
||||
if (!res.body || !res.body.data || !res.body.data.lightning_invoice || !res.body.data.lightning_invoice.payreq) {
|
||||
throw new Error('Opennode problem');
|
||||
}
|
||||
|
||||
|
|
|
@ -95,6 +95,8 @@ jest.mock('react-native-haptic-feedback', () => ({}));
|
|||
|
||||
const realmInstanceMock = {
|
||||
close: function () {},
|
||||
write: function () {},
|
||||
objectForPrimaryKey: function () { return {}; },
|
||||
objects: function () {
|
||||
const wallets = {
|
||||
filtered: function () {
|
||||
|
|
|
@ -19,6 +19,7 @@ describe('Watch only wallet', () => {
|
|||
[secret.toUpperCase()],
|
||||
);
|
||||
assert.strictEqual(w.isHd(), false);
|
||||
assert.ok(!w.useWithHardwareWalletEnabled());
|
||||
}
|
||||
|
||||
w.setSecret('not valid');
|
||||
|
@ -35,6 +36,7 @@ describe('Watch only wallet', () => {
|
|||
assert.strictEqual(w.getMasterFingerprint(), false);
|
||||
assert.strictEqual(w.getMasterFingerprintHex(), '00000000');
|
||||
assert.ok(w.isXpubValid(), w.secret);
|
||||
assert.ok(!w.useWithHardwareWalletEnabled());
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -60,7 +62,59 @@ describe('Watch only wallet', () => {
|
|||
assert.ok(!w.valid());
|
||||
});
|
||||
|
||||
it('can create PSBT base64 without signature for HW wallet', async () => {
|
||||
it('can create PSBT base64 without signature for HW wallet xpub', async () => {
|
||||
const w = new WatchOnlyWallet();
|
||||
w.setSecret('xpub6CQdfC3v9gU86eaSn7AhUFcBVxiGhdtYxdC5Cw2vLmFkfth2KXCMmYcPpvZviA89X6DXDs4PJDk5QVL2G2xaVjv7SM4roWHr1gR4xB3Z7Ps');
|
||||
w.init();
|
||||
const changeAddress = '1KZjqYHm7a1DjhjcdcjfQvYfF2h6PqatjX';
|
||||
// hardcoding so we wont have to call w.getChangeAddressAsync()
|
||||
const utxos = [
|
||||
{
|
||||
height: 530926,
|
||||
value: 1000,
|
||||
address: '12eQ9m4sgAwTSQoNXkRABKhCXCsjm2jdVG',
|
||||
txid: 'd0432027a86119c63a0be8fa453275c2333b59067f1e559389cd3e0e377c8b96',
|
||||
vout: 1,
|
||||
txhex:
|
||||
'0100000001b630ac364a04b83548994ded4705b98316b2d1fe18b9fffa2627be9eef11bf60000000006b48304502210096e68d94d374e3a688ed2e6605289f81172540abaab5f6cc431c231919860746022075ee4e64c867ed9d369d01a9b35d8b1689a821be8d729fff7fb3dfcc75d16f6401210281d2e40ba6422fc97b61fd5643bee83dd749d8369339edc795d7b3f00e96c681fdffffff02ef020000000000001976a914e4271ef9e9a03a89b981c73d3d6936d2f6fccc0688ace8030000000000001976a914120ad7854152901ebeb269acb6cef20e71b3cf5988acea190800',
|
||||
},
|
||||
];
|
||||
// hardcoding utxo so we wont have to call w.fetchUtxo() and w.getUtxo()
|
||||
|
||||
const { psbt } = await w.createTransaction(utxos, [{ address: '1QDCFcpnrZ4yrAQxmbvSgeUC9iZZ8ehcR5' }], 1, changeAddress);
|
||||
|
||||
assert.strictEqual(
|
||||
psbt.toBase64(),
|
||||
'cHNidP8BAFUCAAAAAZaLfDcOPs2Jk1UefwZZOzPCdTJF+ugLOsYZYagnIEPQAQAAAAAAAACAASgDAAAAAAAAGXapFP6ZRvxlaU5S/9HQFr1i2lsgp58AiKwAAAAAAAEA4gEAAAABtjCsNkoEuDVImU3tRwW5gxay0f4Yuf/6Jie+nu8Rv2AAAAAAa0gwRQIhAJbmjZTTdOOmiO0uZgUon4EXJUCrqrX2zEMcIxkZhgdGAiB17k5kyGftnTadAamzXYsWiaghvo1yn/9/s9/MddFvZAEhAoHS5AumQi/Je2H9VkO+6D3XSdg2kzntx5XXs/AOlsaB/f///wLvAgAAAAAAABl2qRTkJx756aA6ibmBxz09aTbS9vzMBois6AMAAAAAAAAZdqkUEgrXhUFSkB6+smmsts7yDnGzz1mIrOoZCAAiBgPGm5BfckKzaIEi8GlRM5oe4A2mUvbsxlJ+pmMhRsrOYhgAAAAALAAAgAAAAIAAAACAAAAAAAAAAAAAAA==',
|
||||
);
|
||||
});
|
||||
|
||||
it('can create PSBT base64 without signature for HW wallet ypub', async () => {
|
||||
const w = new WatchOnlyWallet();
|
||||
w.setSecret('ypub6XRzrn3HB1tjhhvrHbk1vnXCecZEdXohGzCk3GXwwbDoJ3VBzZ34jNGWbC6WrS7idXrYjjXEzcPDX5VqnHEnuNf5VAXgLfSaytMkJ2rwVqy');
|
||||
w.init();
|
||||
const changeAddress = '333R1N8zst8bK7xMtqBndmwcd288qxEBmr';
|
||||
// hardcoding so we wont have to call w.getChangeAddressAsync()
|
||||
const utxos = [
|
||||
{
|
||||
height: 566299,
|
||||
value: 250000,
|
||||
address: '37EX3KrmopubWPLB8Y8NR36wXs7icu2kjQ',
|
||||
txid: '786f05d0c531c4bb399ab8cf406b2f118504280bd015e26e4ff9539f8201d4f4',
|
||||
vout: 0,
|
||||
},
|
||||
];
|
||||
// hardcoding utxo so we wont have to call w.fetchUtxo() and w.getUtxo()
|
||||
|
||||
const { psbt } = await w.createTransaction(utxos, [{ address: '398qz3BtNG8DABpEGa2VkHBcficxkgeKvX' }], 1, changeAddress);
|
||||
|
||||
assert.strictEqual(
|
||||
psbt.toBase64(),
|
||||
'cHNidP8BAFMCAAAAAfTUAYKfU/lPbuIV0AsoBIURL2tAz7iaObvEMcXQBW94AAAAAAAAAACAAdDPAwAAAAAAF6kUUatl8TFvnlvB8H/KsqbnR6kpUluHAAAAAAABASCQ0AMAAAAAABepFDzN1E7LDjAMNARzCHsU4rXqBf55hwEEFgAUG3vPJhyWYtt/ikPpOCW6jCqkmxsiBgLHMhb0QhE8eyJBnE9syGAtMehGmHe1sxpm+TlxjgFXERgAAAAAMQAAgAAAAIAAAACAAAAAAAAAAAAAAA==',
|
||||
);
|
||||
});
|
||||
|
||||
it('can create PSBT base64 without signature for HW wallet zpub', async () => {
|
||||
const w = new WatchOnlyWallet();
|
||||
w.setSecret('zpub6rjLjQVqVnj7crz9E4QWj4WgczmEseJq22u2B6k2HZr6NE2PQx3ZYg8BnbjN9kCfHymSeMd2EpwpM5iiz5Nrb3TzvddxW2RMcE3VXdVaXHk');
|
||||
// zpub provided by Stepan @ CryptoAdvance
|
||||
|
@ -102,6 +156,7 @@ describe('Watch only wallet', () => {
|
|||
);
|
||||
assert.strictEqual(w.getMasterFingerprint(), 64392470);
|
||||
assert.strictEqual(w.getMasterFingerprintHex(), '168dd603');
|
||||
assert.ok(w.useWithHardwareWalletEnabled());
|
||||
|
||||
const utxos = [
|
||||
{
|
||||
|
@ -140,6 +195,7 @@ describe('Watch only wallet', () => {
|
|||
);
|
||||
assert.strictEqual(w.getMasterFingerprint(), 64392470);
|
||||
assert.strictEqual(w.getMasterFingerprintHex(), '168dd603');
|
||||
assert.ok(w.useWithHardwareWalletEnabled());
|
||||
|
||||
const utxos = [
|
||||
{
|
||||
|
@ -179,6 +235,7 @@ describe('Watch only wallet', () => {
|
|||
assert.strictEqual(w.getMasterFingerprint(), 1908437330);
|
||||
assert.strictEqual(w.getMasterFingerprintHex(), '5271c071');
|
||||
assert.strictEqual(w.getLabel(), 'Cobo Vault 5271c071');
|
||||
assert.ok(w.useWithHardwareWalletEnabled());
|
||||
});
|
||||
|
||||
it('can import zpub with master fingerprint', async () => {
|
||||
|
@ -192,6 +249,7 @@ describe('Watch only wallet', () => {
|
|||
);
|
||||
assert.strictEqual(w.getMasterFingerprint(), 4167290508);
|
||||
assert.strictEqual(w.getMasterFingerprintHex(), '8cce63f8');
|
||||
assert.ok(!w.useWithHardwareWalletEnabled());
|
||||
});
|
||||
|
||||
it('can combine signed PSBT and prepare it for broadcast', async () => {
|
||||
|
|
Loading…
Add table
Reference in a new issue