Browse Source

Merge pull request #23 from sansegkh/master

merged from master
singleaddress
San Segkhoonthod 6 years ago
committed by GitHub
parent
commit
526fae6020
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      .gitattributes
  2. 6
      App.test.js
  3. 2
      App2.test.js
  4. 2
      BlueApp.js
  5. 188
      BlueComponents.js
  6. 29
      BlueElectrum.js
  7. 83
      Electrum.test.js
  8. 41
      HDWallet.test.js
  9. 65
      LightningCustodianWallet.test.js
  10. 4
      android/app/build.gradle
  11. 2
      android/app/src/main/java/io/bluewallet/bluewallet/MainApplication.java
  12. 4
      android/build.gradle
  13. 2
      android/gradle/wrapper/gradle-wrapper.properties
  14. 2
      android/settings.gradle
  15. 51
      class/abstract-hd-wallet.js
  16. 18
      class/app-storage.js
  17. 132
      class/hd-segwit-p2sh-wallet.js
  18. 144
      ios/BlueWallet.xcodeproj/project.pbxproj
  19. 6
      ios/BlueWallet/Info.plist
  20. 32
      ios/fastlane/metadata/en-US/release_notes.txt
  21. 3
      loc/cs_CZ.js
  22. 3
      loc/da_DK.js
  23. 3
      loc/de_DE.js
  24. 226
      loc/el.js
  25. 3
      loc/en.js
  26. 3
      loc/es.js
  27. 226
      loc/fi_FI.js
  28. 3
      loc/fr_FR.js
  29. 3
      loc/hr_HR.js
  30. 3
      loc/id_ID.js
  31. 99
      loc/index.js
  32. 8
      loc/it.js
  33. 2
      loc/jp_JP.js
  34. 224
      loc/nb_NO.js
  35. 3
      loc/nl_NL.js
  36. 3
      loc/pt_BR.js
  37. 3
      loc/pt_PT.js
  38. 3
      loc/ru.js
  39. 224
      loc/sv_SE.js
  40. 3
      loc/th_TH.js
  41. 224
      loc/tr_TR.js
  42. 3
      loc/ua.js
  43. 3
      loc/zh_cn.js
  44. 6941
      package-lock.json
  45. 66
      package.json
  46. 305
      screen/lnd/browser.js
  47. 2
      screen/plausibledeniability.js
  48. 9
      screen/selftest.js
  49. 2
      screen/send/details.js
  50. 8
      screen/settings/about.js
  51. 2
      screen/settings/encryptStorage.js
  52. 7
      screen/settings/language.js
  53. 4
      screen/settings/lightningSettings.js
  54. 2
      screen/transactions/RBF-create.js
  55. 23
      screen/wallets/list.js
  56. 10
      screen/wallets/scanQrWif.js
  57. 9
      screen/wallets/transactions.js
  58. 24
      security-alert.js

1
.gitattributes

@ -1 +1,2 @@
*.pbxproj -text
*.patch -text

6
App.test.js

@ -75,7 +75,7 @@ it('BlueHeader works', () => {
expect(rendered).toBeTruthy();
});
it('Settings work', () => {
it.skip('Settings work', () => {
const rendered = TestRenderer.create(<Settings />).toJSON();
expect(rendered).toBeTruthy();
});
@ -231,12 +231,12 @@ it('Wallet can fetch UTXO', async () => {
it('Wallet can fetch balance', async () => {
jasmine.DEFAULT_TIMEOUT_INTERVAL = 30000;
let w = new LegacyWallet();
w._address = '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa'; // hack internals
w._address = '115fUy41sZkAG14CmdP1VbEKcNRZJWkUWG'; // hack internals
assert.ok(w.getBalance() === 0);
assert.ok(w.getUnconfirmedBalance() === 0);
assert.ok(w._lastBalanceFetch === 0);
await w.fetchBalance();
assert.ok(w.getBalance() > 0);
assert.ok(w.getBalance() === 0.18262);
assert.ok(w.getUnconfirmedBalance() === 0);
assert.ok(w._lastBalanceFetch > 0);
});

2
App2.test.js

@ -21,7 +21,7 @@ it('bip38 decodes', async () => {
});
it('bip38 decodes slow', async () => {
if (process.env.USER === 'burn' || process.env.USER === 'igor') {
if (process.env.USER === 'burn' || process.env.USER === 'igor' || process.env.USER === 'overtorment') {
// run only on circleCI
return;
}

2
BlueApp.js

@ -29,8 +29,6 @@ async function startAndDecrypt(retry) {
console.log('loaded from disk');
EV(EV.enum.WALLETS_COUNT_CHANGED);
EV(EV.enum.TRANSACTIONS_COUNT_CHANGED);
let securityAlert = require('./security-alert');
await securityAlert.start();
// now, lets try to fetch balance and txs for first wallet if it is time for it
/* let hadToRefresh = false;
let noErr = true;

188
BlueComponents.js

@ -45,11 +45,11 @@ if (aspectRatio > 1.6) {
export class BlueButton extends Component {
render() {
let backgroundColor = this.props.backgroundColor ? this.props.backgroundColor : '#ccddf9';
let fontColor = '#0c2550';
let backgroundColor = this.props.backgroundColor ? this.props.backgroundColor : BlueApp.settings.buttonBackgroundColor;
let fontColor = BlueApp.settings.buttonTextColor;
if (this.props.hasOwnProperty('disabled') && this.props.disabled === true) {
backgroundColor = '#eef0f4';
fontColor = '#9aa0aa';
backgroundColor = BlueApp.settings.buttonDisabledBackgroundColor;
fontColor = BlueApp.settings.buttonDisabledTextColor;
}
let buttonWidth = width / 1.5;
if (this.props.hasOwnProperty('noMinWidth')) {
@ -93,10 +93,10 @@ export class BitcoinButton extends Component {
<View
style={{
// eslint-disable-next-line
borderColor: (this.props.active && BlueApp.settings.foregroundColor) || '#d2d2d2',
borderColor: (this.props.active && BlueApp.settings.foregroundColor) || BlueApp.settings.inputBorderColor,
borderWidth: 0.5,
borderRadius: 5,
backgroundColor: '#f5f5f5',
backgroundColor: BlueApp.settings.inputBackgroundColor,
// eslint-disable-next-line
width: this.props.style.width,
// eslint-disable-next-line
@ -104,8 +104,18 @@ export class BitcoinButton extends Component {
}}
>
<View style={{ paddingTop: 30 }}>
<Icon name="btc" size={32} type="font-awesome" color={(this.props.active && BlueApp.settings.foregroundColor) || '#d2d2d2'} />
<Text style={{ textAlign: 'center', color: (this.props.active && BlueApp.settings.foregroundColor) || '#d2d2d2' }}>
<Icon
name="btc"
size={32}
type="font-awesome"
color={(this.props.active && BlueApp.settings.foregroundColor) || BlueApp.settings.inputBorderColor}
/>
<Text
style={{
textAlign: 'center',
color: (this.props.active && BlueApp.settings.foregroundColor) || BlueApp.settings.inputBorderColor,
}}
>
{loc.wallets.add.bitcoin}
</Text>
</View>
@ -127,10 +137,10 @@ export class LightningButton extends Component {
<View
style={{
// eslint-disable-next-line
borderColor: (this.props.active && BlueApp.settings.foregroundColor) || '#d2d2d2',
borderColor: (this.props.active && BlueApp.settings.foregroundColor) || BlueApp.settings.inputBorderColor,
borderWidth: 0.5,
borderRadius: 5,
backgroundColor: '#f5f5f5',
backgroundColor: BlueApp.settings.inputBackgroundColor,
// eslint-disable-next-line
width: this.props.style.width,
// eslint-disable-next-line
@ -138,8 +148,18 @@ export class LightningButton extends Component {
}}
>
<View style={{ paddingTop: 30 }}>
<Icon name="bolt" size={32} type="font-awesome" color={(this.props.active && BlueApp.settings.foregroundColor) || '#d2d2d2'} />
<Text style={{ textAlign: 'center', color: (this.props.active && BlueApp.settings.foregroundColor) || '#d2d2d2' }}>
<Icon
name="bolt"
size={32}
type="font-awesome"
color={(this.props.active && BlueApp.settings.foregroundColor) || BlueApp.settings.inputBorderColor}
/>
<Text
style={{
textAlign: 'center',
color: (this.props.active && BlueApp.settings.foregroundColor) || BlueApp.settings.inputBorderColor,
}}
>
{loc.wallets.add.lightning}
</Text>
</View>
@ -161,7 +181,7 @@ export class BlueButtonLink extends Component {
}}
{...this.props}
>
<Text style={{ color: '#0c2550', textAlign: 'center', fontSize: 16 }}>{this.props.title}</Text>
<Text style={{ color: BlueApp.settings.foregroundColor, textAlign: 'center', fontSize: 16 }}>{this.props.title}</Text>
</TouchableOpacity>
);
}
@ -169,15 +189,15 @@ export class BlueButtonLink extends Component {
export const BlueNavigationStyle = (navigation, withNavigationCloseButton = false, customCloseButtonFunction = undefined) => ({
headerStyle: {
backgroundColor: '#FFFFFF',
backgroundColor: BlueApp.settings.brandingColor,
borderBottomWidth: 0,
elevation: 0,
},
headerTitleStyle: {
fontWeight: '600',
color: '#0c2550',
color: BlueApp.settings.foregroundColor,
},
headerTintColor: '#0c2550',
headerTintColor: BlueApp.settings.foregroundColor,
headerRight: withNavigationCloseButton ? (
<TouchableOpacity
style={{ width: 40, height: 40, padding: 14 }}
@ -309,7 +329,7 @@ export class BlueListItem extends Component {
fontSize: 16,
fontWeight: '500',
}}
subtitleStyle={{ color: '#9aa0aa' }}
subtitleStyle={{ color: BlueApp.settings.alternativeTextColor }}
subtitleNumberOfLines={1}
{...this.props}
/>
@ -331,11 +351,11 @@ export class BlueFormInput extends Component {
inputStyle={{ color: BlueApp.settings.foregroundColor, maxWidth: width - 105 }}
containerStyle={{
marginTop: 5,
borderColor: '#d2d2d2',
borderBottomColor: '#d2d2d2',
borderColor: BlueApp.settings.inputBorderColor,
borderBottomColor: BlueApp.settings.inputBorderColor,
borderWidth: 0.5,
borderBottomWidth: 0.5,
backgroundColor: '#f5f5f5',
backgroundColor: BlueApp.settings.inputBackgroundColor,
}}
/>
);
@ -363,11 +383,11 @@ export class BlueFormMultiInput extends Component {
style={{
marginTop: 5,
marginHorizontal: 20,
borderColor: '#d2d2d2',
borderBottomColor: '#d2d2d2',
borderColor: BlueApp.settings.inputBorderColor,
borderBottomColor: BlueApp.settings.inputBorderColor,
borderWidth: 0.5,
borderBottomWidth: 0.5,
backgroundColor: '#f5f5f5',
backgroundColor: BlueApp.settings.inputBackgroundColor,
height: 200,
color: BlueApp.settings.foregroundColor,
}}
@ -379,7 +399,6 @@ export class BlueFormMultiInput extends Component {
onSelectionChange={this.onSelectionChange}
selection={this.state.selection}
keyboardType={Platform.OS === 'android' ? 'visible-password' : 'default'}
contextMenuHidden
/>
);
}
@ -397,11 +416,11 @@ export class BlueFormInputAddress extends Component {
}}
containerStyle={{
marginTop: 5,
borderColor: '#d2d2d2',
borderBottomColor: '#d2d2d2',
borderColor: BlueApp.settings.inputBorderColor,
borderBottomColor: BlueApp.settings.inputBorderColor,
borderWidth: 0.5,
borderBottomWidth: 0.5,
backgroundColor: '#f5f5f5',
backgroundColor: BlueApp.settings.inputBackgroundColor,
}}
/>
);
@ -427,9 +446,9 @@ export class BlueHeader extends Component {
export class BlueHeaderDefaultSub extends Component {
render() {
return (
<SafeAreaView style={{ backgroundColor: '#FFFFFF' }}>
<SafeAreaView style={{ backgroundColor: BlueApp.settings.brandingColor }}>
<Header
backgroundColor="#FFFFFF"
backgroundColor={BlueApp.settings.brandingColor}
outerContainerStyles={{
borderBottomColor: 'transparent',
borderBottomWidth: 0,
@ -474,10 +493,10 @@ export class BlueHeaderDefaultSub extends Component {
export class BlueHeaderDefaultMain extends Component {
render() {
return (
<SafeAreaView style={{ backgroundColor: '#FFFFFF' }}>
<SafeAreaView style={{ backgroundColor: BlueApp.settings.brandingColor }}>
<Header
{...this.props}
backgroundColor="#FFFFFF"
backgroundColor={BlueApp.settings.brandingColor}
outerContainerStyles={{
borderBottomColor: 'transparent',
borderBottomWidth: 0,
@ -583,7 +602,7 @@ export class BlueUseAllFundsButton extends Component {
return (
<InputAccessoryView nativeID={BlueUseAllFundsButton.InputAccessoryViewID}>
<View style={{ flex: 1, flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }}>
<Text style={{ color: '#9aa0aa', fontSize: 16, marginHorizontal: 8 }}>
<Text style={{ color: BlueApp.settings.alternativeTextColor, fontSize: 16, marginHorizontal: 8 }}>
Total: {this.props.wallet.getBalance()} {BitcoinUnit.BTC}
</Text>
<BlueButtonLink title="Use All" onPress={this.props.onUseAllPressed} />
@ -726,7 +745,14 @@ export class BlueTransactionIncomingIcon extends Component {
<View {...this.props}>
<View style={stylesBlueIcon.boxIncoming}>
<View style={stylesBlueIcon.ballIncoming}>
<Icon {...this.props} name="arrow-down" size={16} type="font-awesome" color="#37c0a1" iconStyle={{ left: 0, top: 8 }} />
<Icon
{...this.props}
name="arrow-down"
size={16}
type="font-awesome"
color={BlueApp.settings.incomingForegroundColor}
iconStyle={{ left: 0, top: 8 }}
/>
</View>
</View>
</View>
@ -761,7 +787,14 @@ export class BlueTransactionExpiredIcon extends Component {
<View {...this.props}>
<View style={stylesBlueIcon.boxIncoming}>
<View style={stylesBlueIcon.ballOutgoingWithoutRotate}>
<Icon {...this.props} name="hourglass-end" size={16} type="font-awesome" color="#d0021b" iconStyle={{ left: 0, top: 6 }} />
<Icon
{...this.props}
name="hourglass-end"
size={16}
type="font-awesome"
color={BlueApp.settings.outgoingForegroundColor}
iconStyle={{ left: 0, top: 6 }}
/>
</View>
</View>
</View>
@ -780,7 +813,7 @@ export class BlueTransactionOnchainIcon extends Component {
name="link"
size={16}
type="font-awesome"
color="#37c0a1"
color={BlueApp.settings.incomingForegroundColor}
iconStyle={{ left: 0, top: 7, transform: [{ rotate: '-45deg' }] }}
/>
</View>
@ -796,7 +829,14 @@ export class BlueTransactionOffchainIcon extends Component {
<View {...this.props}>
<View style={stylesBlueIcon.boxIncoming}>
<View style={stylesBlueIcon.ballOutgoingWithoutRotate}>
<Icon {...this.props} name="bolt" size={16} type="font-awesome" color="#d0021b" iconStyle={{ left: 0, top: 7 }} />
<Icon
{...this.props}
name="bolt"
size={16}
type="font-awesome"
color={BlueApp.settings.outgoingForegroundColor}
iconStyle={{ left: 0, top: 7 }}
/>
</View>
</View>
</View>
@ -810,7 +850,14 @@ export class BlueTransactionOffchainIncomingIcon extends Component {
<View {...this.props}>
<View style={stylesBlueIcon.boxIncoming}>
<View style={stylesBlueIcon.ballIncomingWithoutRotate}>
<Icon {...this.props} name="bolt" size={16} type="font-awesome" color="#37c0a1" iconStyle={{ left: 0, top: 7 }} />
<Icon
{...this.props}
name="bolt"
size={16}
type="font-awesome"
color={BlueApp.settings.incomingForegroundColor}
iconStyle={{ left: 0, top: 7 }}
/>
</View>
</View>
</View>
@ -824,7 +871,14 @@ export class BlueTransactionOutgoingIcon extends Component {
<View {...this.props}>
<View style={stylesBlueIcon.boxIncoming}>
<View style={stylesBlueIcon.ballOutgoing}>
<Icon {...this.props} name="arrow-down" size={16} type="font-awesome" color="#d0021b" iconStyle={{ left: 0, top: 8 }} />
<Icon
{...this.props}
name="arrow-down"
size={16}
type="font-awesome"
color={BlueApp.settings.outgoingForegroundColor}
iconStyle={{ left: 0, top: 8 }}
/>
</View>
</View>
</View>
@ -842,7 +896,7 @@ export class BlueReceiveButtonIcon extends Component {
style={{
flex: 1,
minWidth: 130,
backgroundColor: '#ccddf9',
backgroundColor: BlueApp.settings.buttonBackgroundColor,
}}
>
<View style={{ flex: 1, flexDirection: 'row', alignItems: 'center', justifyContent: 'center' }}>
@ -857,11 +911,11 @@ export class BlueReceiveButtonIcon extends Component {
marginBottom: -11,
}}
>
<Icon {...this.props} name="arrow-down" size={16} type="font-awesome" color="#2f5fb3" />
<Icon {...this.props} name="arrow-down" size={16} type="font-awesome" color={BlueApp.settings.buttonAlternativeTextColor} />
</View>
<Text
style={{
color: '#2f5fb3',
color: BlueApp.settings.buttonAlternativeTextColor,
fontSize: (isIpad && 10) || 16,
fontWeight: '500',
left: 5,
@ -885,7 +939,7 @@ export class BlueSendButtonIcon extends Component {
style={{
flex: 1,
minWidth: 130,
backgroundColor: '#ccddf9',
backgroundColor: BlueApp.settings.buttonBackgroundColor,
alignItems: 'center',
}}
>
@ -900,11 +954,11 @@ export class BlueSendButtonIcon extends Component {
marginBottom: 11,
}}
>
<Icon {...this.props} name="arrow-down" size={16} type="font-awesome" color="#2f5fb3" />
<Icon {...this.props} name="arrow-down" size={16} type="font-awesome" color={BlueApp.settings.buttonAlternativeTextColor} />
</View>
<Text
style={{
color: '#2f5fb3',
color: BlueApp.settings.buttonAlternativeTextColor,
fontSize: (isIpad && 10) || 16,
fontWeight: '500',
backgroundColor: 'transparent',
@ -927,7 +981,7 @@ export class ManageFundsBigButton extends Component {
style={{
flex: 1,
width: 168,
backgroundColor: '#ccddf9',
backgroundColor: BlueApp.settings.buttonBackgroundColor,
}}
>
<View style={{ flex: 1, flexDirection: 'row', alignItems: 'center', justifyContent: 'center' }}>
@ -940,11 +994,11 @@ export class ManageFundsBigButton extends Component {
transform: [{ rotate: '90deg' }],
}}
>
<Icon {...this.props} name="link" size={16} type="font-awesome" color="#2f5fb3" />
<Icon {...this.props} name="link" size={16} type="font-awesome" color={BlueApp.settings.buttonAlternativeTextColor} />
</View>
<Text
style={{
color: '#2f5fb3',
color: BlueApp.settings.buttonAlternativeTextColor,
fontSize: (isIpad && 10) || 16,
fontWeight: '500',
backgroundColor: 'transparent',
@ -1018,7 +1072,7 @@ export class NewWalletPanel extends Component {
backgroundColor: 'transparent',
fontWeight: 'bold',
fontSize: 20,
color: '#9aa0aa',
color: BlueApp.settings.alternativeTextColor,
}}
>
{loc.wallets.list.create_a_wallet}
@ -1028,7 +1082,7 @@ export class NewWalletPanel extends Component {
style={{
backgroundColor: 'transparent',
fontSize: 13,
color: '#9aa0aa',
color: BlueApp.settings.alternativeTextColor,
}}
>
{loc.wallets.list.create_a_wallet1}
@ -1037,7 +1091,7 @@ export class NewWalletPanel extends Component {
style={{
backgroundColor: 'transparent',
fontSize: 13,
color: '#9aa0aa',
color: BlueApp.settings.alternativeTextColor,
}}
>
{loc.wallets.list.create_a_wallet2}
@ -1091,7 +1145,7 @@ export class BlueTransactionListItem extends Component {
rowTitleStyle = () => {
const item = this.props.item;
let color = '#37c0a1';
let color = BlueApp.settings.successColor;
if (item.type === 'user_invoice' || item.type === 'payment_request') {
const currentDate = new Date();
@ -1099,12 +1153,12 @@ export class BlueTransactionListItem extends Component {
const invoiceExpiration = item.timestamp + item.expire_time;
if (invoiceExpiration > now) {
color = '#37c0a1';
color = BlueApp.settings.successColor;
} else if (invoiceExpiration < now) {
if (item.ispaid) {
color = '#37c0a1';
color = BlueApp.settings.successColor;
} else {
color = '#FF0000';
color = BlueApp.settings.failedColor;
}
}
} else if (item.value / 100000000 < 0) {
@ -1487,7 +1541,7 @@ export class WalletsCarousel extends Component {
}}
>
<LinearGradient
shadowColor="#000000"
shadowColor={BlueApp.settings.shadowColor}
colors={WalletGradient.gradientsFor(item.type)}
style={{
padding: 15,
@ -1513,7 +1567,7 @@ export class WalletsCarousel extends Component {
style={{
backgroundColor: 'transparent',
fontSize: 19,
color: '#fff',
color: BlueApp.settings.inverseForegroundColor,
}}
>
{item.getLabel()}
@ -1525,7 +1579,7 @@ export class WalletsCarousel extends Component {
backgroundColor: 'transparent',
fontWeight: 'bold',
fontSize: 36,
color: '#fff',
color: BlueApp.settings.inverseForegroundColor,
}}
>
{loc.formatBalance(Number(item.getBalance()), item.getPreferredBalanceUnit(), true)}
@ -1536,7 +1590,7 @@ export class WalletsCarousel extends Component {
style={{
backgroundColor: 'transparent',
fontSize: 13,
color: '#fff',
color: BlueApp.settings.inverseForegroundColor,
}}
>
{loc.wallets.list.latest_transaction}
@ -1547,7 +1601,7 @@ export class WalletsCarousel extends Component {
backgroundColor: 'transparent',
fontWeight: 'bold',
fontSize: 16,
color: '#fff',
color: BlueApp.settings.inverseForegroundColor,
}}
>
{loc.transactionTimeToReadable(item.getLatestTransactionTime())}
@ -1603,11 +1657,11 @@ export class BlueAddressInput extends Component {
<View
style={{
flexDirection: 'row',
borderColor: '#d2d2d2',
borderBottomColor: '#d2d2d2',
borderColor: BlueApp.settings.inputBorderColor,
borderBottomColor: BlueApp.settings.inputBorderColor,
borderWidth: 1.0,
borderBottomWidth: 0.5,
backgroundColor: '#f5f5f5',
backgroundColor: BlueApp.settings.inputBackgroundColor,
minHeight: 44,
height: 44,
marginHorizontal: 20,
@ -1668,8 +1722,8 @@ export class BlueAddressInput extends Component {
marginHorizontal: 4,
}}
>
<Icon name="qrcode" size={22} type="font-awesome" color="#FFFFFF" />
<Text style={{ color: '#FFFFFF' }}>{loc.send.details.scan}</Text>
<Icon name="qrcode" size={22} type="font-awesome" color={BlueApp.settings.inverseForegroundColor} />
<Text style={{ color: BlueApp.settings.inverseForegroundColor }}>{loc.send.details.scan}</Text>
</TouchableOpacity>
</View>
);
@ -1713,16 +1767,16 @@ export class BlueBitcoinAmount extends Component {
ref={textInput => (this.textInput = textInput)}
editable={!this.props.isLoading && !this.props.disabled}
value={amount}
placeholderTextColor={this.props.disabled ? '#99a0ab' : '#0f5cc0'}
placeholderTextColor={this.props.disabled ? BlueApp.settings.buttonDisabledTextColor : BlueApp.settings.alternativeTextColor2}
style={{
color: this.props.disabled ? '#99a0ab' : '#0f5cc0',
color: this.props.disabled ? BlueApp.settings.buttonDisabledTextColor : BlueApp.settings.alternativeTextColor2,
fontSize: 36,
fontWeight: '600',
}}
/>
<Text
style={{
color: this.props.disabled ? '#99a0ab' : '#0f5cc0',
color: this.props.disabled ? BlueApp.settings.buttonDisabledTextColor : BlueApp.settings.alternativeTextColor2,
fontSize: 16,
marginHorizontal: 4,
paddingBottom: 6,

29
BlueElectrum.js

@ -5,6 +5,17 @@ let reverse = require('buffer-reverse');
const storageKey = 'ELECTRUM_PEERS';
const defaultPeer = { host: 'electrum.coinucopia.io', tcp: 50001 };
const hardcodedPeers = [
{ host: 'noveltybobble.coinjoined.com', tcp: '50001' },
{ host: 'electrum.be', tcp: '50001' },
// { host: 'node.ispol.sk', tcp: '50001' }, // down
{ host: '139.162.14.142', tcp: '50001' },
// { host: 'electrum.coinucopia.io', tcp: '50001' }, // SLOW
{ host: 'Bitkoins.nl', tcp: '50001' },
{ host: 'fullnode.coinkite.com', tcp: '50001' },
// { host: 'preperfect.eleCTruMioUS.com', tcp: '50001' }, // down
{ host: 'electrum1.bluewallet.io', tcp: '50001' },
];
let mainClient = false;
let mainConnected = false;
@ -16,9 +27,9 @@ async function connectMain() {
mainClient = new ElectrumClient(usingPeer.tcp, usingPeer.host, 'tcp');
await mainClient.connect();
const ver = await mainClient.server_version('2.7.11', '1.2');
console.log('connected to ', ver);
let peers = await mainClient.serverPeers_subscribe();
if (peers && peers.length > 0) {
console.log('connected to ', ver);
mainConnected = true;
AsyncStorage.setItem(storageKey, JSON.stringify(peers));
}
@ -29,6 +40,9 @@ async function connectMain() {
if (!mainConnected) {
console.log('retry');
mainClient.keepAlive = () => {}; // dirty hack to make it stop reconnecting
mainClient.reconnect = () => {}; // dirty hack to make it stop reconnecting
mainClient.close();
setTimeout(connectMain, 5000);
}
}
@ -42,17 +56,6 @@ connectMain();
* @returns {Promise<{tcp, host}|*>}
*/
async function getRandomHardcodedPeer() {
let hardcodedPeers = [
{ host: 'node.ispol.sk', tcp: '50001' },
{ host: 'electrum.vom-stausee.de', tcp: '50001' },
{ host: 'orannis.com', tcp: '50001' },
{ host: '139.162.14.142', tcp: '50001' },
{ host: 'daedalus.bauerj.eu', tcp: '50001' },
{ host: 'electrum.eff.ro', tcp: '50001' },
{ host: 'electrum.anduck.net', tcp: '50001' },
{ host: 'mooo.not.fyi', tcp: '50011' },
{ host: 'electrum.coinucopia.io', tcp: '50001' },
];
return hardcodedPeers[(hardcodedPeers.length * Math.random()) | 0];
}
@ -189,6 +192,8 @@ module.exports.forceDisconnect = () => {
mainClient.close();
};
module.exports.hardcodedPeers = hardcodedPeers;
/*

83
Electrum.test.js

@ -1,53 +1,68 @@
/* global it, describe, jasmine */
/* global it, describe, afterAll, beforeAll, jasmine */
global.net = require('net');
let BlueElectrum = require('./BlueElectrum');
let assert = require('assert');
jasmine.DEFAULT_TIMEOUT_INTERVAL = 150 * 1000;
afterAll(() => {
// after all tests we close socket so the test suite can actually terminate
return BlueElectrum.forceDisconnect();
});
beforeAll(async () => {
// awaiting for Electrum to be connected. For RN Electrum would naturally connect
// while app starts up, but for tests we need to wait for it
try {
await BlueElectrum.waitTillConnected();
} catch (Err) {
console.log('failed to connect to Electrum:', Err);
process.exit(1);
}
});
describe('Electrum', () => {
it('ElectrumClient can connect and query', async () => {
jasmine.DEFAULT_TIMEOUT_INTERVAL = 100 * 1000;
const ElectrumClient = require('electrum-client');
let bitcoin = require('bitcoinjs-lib');
// let bitcoin = require('bitcoinjs-lib');
const peer = { host: 'electrum.coinucopia.io', ssl: 50002, tcp: 50001, pruning: null, http: null, https: null };
console.log('begin connection:', JSON.stringify(peer));
let mainClient = new ElectrumClient(peer.tcp, peer.host, 'tcp');
for (let peer of BlueElectrum.hardcodedPeers) {
let mainClient = new ElectrumClient(peer.tcp, peer.host, 'tcp');
try {
await mainClient.connect();
const ver = await mainClient.server_version('2.7.11', '1.2');
console.log('connected to ', ver);
} catch (e) {
console.log('bad connection:', JSON.stringify(peer));
throw new Error();
}
try {
await mainClient.connect();
await mainClient.server_version('2.7.11', '1.2');
} catch (e) {
mainClient.reconnect = mainClient.keepAlive = () => {}; // dirty hack to make it stop reconnecting
mainClient.close();
throw new Error('bad connection: ' + JSON.stringify(peer));
}
let addr4elect = 'bc1qwqdg6squsna38e46795at95yu9atm8azzmyvckulcc7kytlcckxswvvzej';
let script = bitcoin.address.toOutputScript(addr4elect);
let hash = bitcoin.crypto.sha256(script);
let reversedHash = Buffer.from(hash.reverse());
let balance = await mainClient.blockchainScripthash_getBalance(reversedHash.toString('hex'));
assert.ok(balance.confirmed > 0);
let addr4elect = 'bc1qwqdg6squsna38e46795at95yu9atm8azzmyvckulcc7kytlcckxswvvzej';
let script = bitcoin.address.toOutputScript(addr4elect);
let hash = bitcoin.crypto.sha256(script);
let reversedHash = Buffer.from(hash.reverse());
let start = +new Date();
let balance = await mainClient.blockchainScripthash_getBalance(reversedHash.toString('hex'));
let end = +new Date();
console.warn(peer.host, 'took', (end - start) / 1000, 'seconds to fetch balance');
assert.ok(balance.confirmed > 0);
addr4elect = '3GCvDBAktgQQtsbN6x5DYiQCMmgZ9Yk8BK';
script = bitcoin.address.toOutputScript(addr4elect);
hash = bitcoin.crypto.sha256(script);
reversedHash = Buffer.from(hash.reverse());
balance = await mainClient.blockchainScripthash_getBalance(reversedHash.toString('hex'));
assert.ok(balance.confirmed === 51432);
addr4elect = '3GCvDBAktgQQtsbN6x5DYiQCMmgZ9Yk8BK';
script = bitcoin.address.toOutputScript(addr4elect);
hash = bitcoin.crypto.sha256(script);
reversedHash = Buffer.from(hash.reverse());
balance = await mainClient.blockchainScripthash_getBalance(reversedHash.toString('hex'));
assert.ok(balance.confirmed === 51432);
// let peers = await mainClient.serverPeers_subscribe();
// console.log(peers);
mainClient.keepAlive = () => {}; // dirty hack to make it stop reconnecting
mainClient.reconnect = () => {}; // dirty hack to make it stop reconnecting
mainClient.close();
// setTimeout(()=>process.exit(), 3000);
// let peers = await mainClient.serverPeers_subscribe();
// console.log(peers);
mainClient.reconnect = mainClient.keepAlive = () => {}; // dirty hack to make it stop reconnecting
mainClient.close();
}
});
it('BlueElectrum works', async function() {
let address = '3GCvDBAktgQQtsbN6x5DYiQCMmgZ9Yk8BK';
await BlueElectrum.waitTillConnected();
let balance = await BlueElectrum.getBalanceByAddress(address);
assert.strictEqual(balance.confirmed, 51432);
assert.strictEqual(balance.unconfirmed, 0);
@ -59,7 +74,5 @@ describe('Electrum', () => {
assert.ok(tx.tx_hash);
assert.ok(tx.height);
}
BlueElectrum.forceDisconnect();
});
});

41
HDWallet.test.js

@ -5,6 +5,7 @@ let assert = require('assert');
let bitcoin = require('bitcoinjs-lib');
global.net = require('net'); // needed by Electrum client. For RN it is proviced in shim.js
let BlueElectrum = require('./BlueElectrum'); // so it connects ASAP
jasmine.DEFAULT_TIMEOUT_INTERVAL = 300 * 1000;
afterAll(() => {
// after all tests we close socket so the test suite can actually terminate
@ -14,7 +15,12 @@ afterAll(() => {
beforeAll(async () => {
// awaiting for Electrum to be connected. For RN Electrum would naturally connect
// while app starts up, but for tests we need to wait for it
await BlueElectrum.waitTillConnected();
try {
await BlueElectrum.waitTillConnected();
} catch (Err) {
console.log('failed to connect to Electrum:', Err);
process.exit(2);
}
});
it('can convert witness to address', () => {
@ -29,7 +35,6 @@ it('can convert witness to address', () => {
});
it('can create a Segwit HD (BIP49)', async function() {
jasmine.DEFAULT_TIMEOUT_INTERVAL = 30 * 1000;
let mnemonic =
'honey risk juice trip orient galaxy win situate shoot anchor bounce remind horse traffic exotic since escape mimic ramp skin judge owner topple erode';
let hd = new HDSegwitP2SHWallet();
@ -60,6 +65,34 @@ it('can create a Segwit HD (BIP49)', async function() {
assert.strictEqual(hd._getInternalAddressByIndex(hd.next_free_change_address_index), freeChangeAddress);
});
it('HD (BIP49) can work with a gap', async function() {
jasmine.DEFAULT_TIMEOUT_INTERVAL = 300 * 1000;
let hd = new HDSegwitP2SHWallet();
hd._xpub = 'ypub6XRzrn3HB1tjhhvrHbk1vnXCecZEdXohGzCk3GXwwbDoJ3VBzZ34jNGWbC6WrS7idXrYjjXEzcPDX5VqnHEnuNf5VAXgLfSaytMkJ2rwVqy'; // has gap
await hd.fetchBalance();
// for (let c = 0; c < 5; c++) {
// console.log('internal', c, hd._getInternalAddressByIndex(c));
// }
// for (let c = 0; c < 5; c++) {
// console.log('external', c, hd._getExternalAddressByIndex(c));
// }
await hd.fetchTransactions();
console.log('hd.transactions.length=', hd.transactions.length);
assert.ok(hd.transactions.length >= 3);
});
it('Segwit HD (BIP49) can batch fetch many txs', async function() {
jasmine.DEFAULT_TIMEOUT_INTERVAL = 240 * 1000;
let hd = new HDSegwitP2SHWallet();
hd._xpub = 'ypub6WZ2c7YJ1SQ1rBYftwMqwV9bBmybXzETFxWmkzMz25bCf6FkDdXjNgR7zRW8JGSnoddNdUH7ZQS7JeQAddxdGpwgPskcsXFcvSn1JdGVcPQ'; // cant fetch txs
await hd.fetchBalance();
await hd.fetchTransactions();
assert.ok(hd.transactions.length > 0);
console.log('hd.transactions.length=', hd.transactions.length);
});
it('Segwit HD (BIP49) can generate addressess only via ypub', function() {
let ypub = 'ypub6WhHmKBmHNjcrUVNCa3sXduH9yxutMipDcwiKW31vWjcMbfhQHjXdyx4rqXbEtVgzdbhFJ5mZJWmfWwnP4Vjzx97admTUYKQt6b9D7jjSCp';
let hd = new HDSegwitP2SHWallet();
@ -92,7 +125,6 @@ it('HD (BIP49) can create TX', async () => {
console.error('process.env.HD_MNEMONIC not set, skipped');
return;
}
jasmine.DEFAULT_TIMEOUT_INTERVAL = 90 * 1000;
let hd = new HDSegwitP2SHWallet();
hd.setSecret(process.env.HD_MNEMONIC);
assert.ok(hd.validateMnemonic());
@ -167,7 +199,6 @@ it('Segwit HD (BIP49) can fetch balance with many used addresses in hierarchy',
return;
}
jasmine.DEFAULT_TIMEOUT_INTERVAL = 90 * 1000;
let hd = new HDSegwitP2SHWallet();
hd.setSecret(process.env.HD_MNEMONIC_BIP49_MANY_TX);
assert.ok(hd.validateMnemonic());
@ -215,7 +246,6 @@ it('can create a Legacy HD (BIP44)', async function() {
return;
}
jasmine.DEFAULT_TIMEOUT_INTERVAL = 30 * 1000;
let mnemonic = process.env.HD_MNEMONIC_BREAD;
let hd = new HDLegacyP2PKHWallet();
hd.setSecret(mnemonic);
@ -266,7 +296,6 @@ it('Legacy HD (BIP44) can create TX', async () => {
console.error('process.env.HD_MNEMONIC not set, skipped');
return;
}
jasmine.DEFAULT_TIMEOUT_INTERVAL = 90 * 1000;
let hd = new HDLegacyP2PKHWallet();
hd.setSecret(process.env.HD_MNEMONIC);
assert.ok(hd.validateMnemonic());

65
LightningCustodianWallet.test.js

@ -349,7 +349,7 @@ describe('LightningCustodianWallet', () => {
assert.strictEqual(oldBalance - l2.balance, 3);
});
it('cant pay negative free amount', async () => {
it('cant create zemo amt invoices yet', async () => {
let l1 = new LightningCustodianWallet();
jasmine.DEFAULT_TIMEOUT_INTERVAL = 100 * 1000;
assert.ok(l1.refill_addressess.length === 0);
@ -367,16 +367,71 @@ describe('LightningCustodianWallet', () => {
assert.ok(l1._access_token_created_ts > 0);
assert.ok(l1.balance === 0);
let invoice = await l1.addInvoice(0, 'zero amt inv');
let err = false;
try {
await l1.addInvoice(0, 'zero amt inv');
} catch (_) {
err = true;
}
assert.ok(err);
});
it('cant pay negative free amount', async () => {
jasmine.DEFAULT_TIMEOUT_INTERVAL = 100 * 1000;
if (!process.env.BLITZHUB) {
console.error('process.env.BLITZHUB not set, skipped');
return;
}
// fetchig invoice from tippin.me :
const api = new Frisbee({
baseURI: 'https://tippin.me',
});
const res = await api.post('/lndreq/newinvoice.php', {
headers: {
Origin: 'https://tippin.me',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'en-GB,en-US;q=0.9,en;q=0.8',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
Accept: 'application/json, text/javascript, */*; q=0.01',
},
body: 'userid=1188&username=overtorment&istaco=0&customAmnt=0&customMemo=',
});
let json;
let invoice;
if (res && res.body && (json = JSON.parse(res.body)) && json.message) {
invoice = json.message;
} else {
throw new Error('tippin.me problem: ' + JSON.stringify(res));
}
let l2 = new LightningCustodianWallet();
l2.setSecret(process.env.BLITZHUB);
await l2.authorize();
await l2.fetchTransactions();
await l2.fetchBalance();
let oldBalance = +l2.balance;
let txLen = l2.transactions_raw.length;
let decoded = await l2.decodeInvoice(invoice);
assert.ok(decoded.payment_hash);
assert.ok(decoded.description);
assert.strictEqual(+decoded.num_satoshis, 0);
await l2.checkRouteInvoice(invoice);
let error = false;
try {
await l1.payInvoice(invoice, -1);
await l2.payInvoice(invoice, -1);
} catch (Err) {
error = true;
}
await l1.fetchBalance();
assert.strictEqual(l1.balance, 0);
assert.ok(error);
await l2.fetchBalance();
assert.strictEqual(l2.balance, oldBalance);
await l2.fetchTransactions();
assert.strictEqual(l2.transactions_raw.length, txLen);
});
});

4
android/app/build.gradle

@ -102,11 +102,12 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "3.8.3"
versionName "3.9.4"
ndk {
abiFilters "armeabi-v7a", "x86"
}
multiDexEnabled true
missingDimensionStrategy 'react-native-camera', 'general'
}
splits {
abi {
@ -138,6 +139,7 @@ android {
}
dependencies {
implementation project(':@react-native-community_slider')
implementation project(':react-native-obscure')
implementation project(':react-native-tcp')
implementation project(':@remobile_react-native-qrcode-local-image')

2
android/app/src/main/java/io/bluewallet/bluewallet/MainApplication.java

@ -3,6 +3,7 @@ package io.bluewallet.bluewallet;
import android.app.Application;
import com.facebook.react.ReactApplication;
import com.reactnativecommunity.slider.ReactSliderPackage;
import com.diegofhg.obscure.ObscurePackage;
import com.peel.react.TcpSocketsModule;
import com.remobile.qrcodeLocalImage.RCTQRCodeLocalImagePackage;
@ -57,6 +58,7 @@ public class MainApplication extends Application implements ReactApplication {
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new ReactSliderPackage(),
new ObscurePackage(),
new TcpSocketsModule(),
new RCTQRCodeLocalImagePackage(),

4
android/build.gradle

@ -2,7 +2,7 @@
buildscript {
ext {
buildToolsVersion = "28.0.2"
buildToolsVersion = "28.0.3"
minSdkVersion = 16
compileSdkVersion = 28
targetSdkVersion = 27
@ -13,7 +13,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
classpath 'com.android.tools.build:gradle:3.3.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files

2
android/gradle/wrapper/gradle-wrapper.properties

@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.7-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip

2
android/settings.gradle

@ -1,4 +1,6 @@
rootProject.name = 'BlueWallet'
include ':@react-native-community_slider'
project(':@react-native-community_slider').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/slider/android')
include ':react-native-obscure'
project(':react-native-obscure').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-obscure/android')
include ':react-native-tcp'

51
class/abstract-hd-wallet.js

@ -1,6 +1,5 @@
import { LegacyWallet } from './legacy-wallet';
import Frisbee from 'frisbee';
import { WatchOnlyWallet } from './watch-only-wallet';
const bip39 = require('bip39');
const BigNumber = require('bignumber.js');
const bitcoin = require('bitcoinjs-lib');
@ -19,6 +18,7 @@ export class AbstractHDWallet extends LegacyWallet {
this._xpub = ''; // cache
this.usedAddresses = [];
this._address_to_wif_cache = {};
this.gap_limit = 3;
}
generate() {
@ -33,8 +33,11 @@ export class AbstractHDWallet extends LegacyWallet {
// need to reformat txs, as we are expected to return them in blockcypher format,
// but they are from blockchain.info actually (for all hd wallets)
let uniq = {};
let txs = [];
for (let tx of this.transactions) {
if (uniq[tx.hash]) continue;
uniq[tx.hash] = 1;
txs.push(AbstractHDWallet.convertTx(tx));
}
@ -111,12 +114,10 @@ export class AbstractHDWallet extends LegacyWallet {
if (this.next_free_address_index + c < 0) continue;
let address = this._getExternalAddressByIndex(this.next_free_address_index + c);
this.external_addresses_cache[this.next_free_address_index + c] = address; // updating cache just for any case
let WatchWallet = new WatchOnlyWallet();
WatchWallet.setSecret(address);
await WatchWallet.fetchTransactions();
if (WatchWallet.transactions.length === 0) {
let txs = await BlueElectrum.getTransactionsByAddress(address);
if (txs.length === 0) {
// found free address
freeAddress = WatchWallet.getAddress();
freeAddress = address;
this.next_free_address_index += c; // now points to _this one_
break;
}
@ -146,12 +147,10 @@ export class AbstractHDWallet extends LegacyWallet {
if (this.next_free_change_address_index + c < 0) continue;
let address = this._getInternalAddressByIndex(this.next_free_change_address_index + c);
this.internal_addresses_cache[this.next_free_change_address_index + c] = address; // updating cache just for any case
let WatchWallet = new WatchOnlyWallet();
WatchWallet.setSecret(address);
await WatchWallet.fetchTransactions();
if (WatchWallet.transactions.length === 0) {
let txs = await BlueElectrum.getTransactionsByAddress(address);
if (txs.length === 0) {
// found free address
freeAddress = WatchWallet.getAddress();
freeAddress = address;
this.next_free_change_address_index += c; // now points to _this one_
break;
}
@ -347,8 +346,6 @@ export class AbstractHDWallet extends LegacyWallet {
async fetchBalance() {
try {
// doing binary search for last used externa address
let that = this;
// refactor me
@ -371,8 +368,6 @@ export class AbstractHDWallet extends LegacyWallet {
return binarySearchIterationForInternalAddress(index, maxUsedIndex, minUnusedIndex, depth + 1);
}
this.next_free_change_address_index = await binarySearchIterationForInternalAddress(100);
// refactor me
// eslint-disable-next-line
async function binarySearchIterationForExternalAddress(index, maxUsedIndex = 0, minUnusedIndex = 100500100, depth = 0) {
@ -393,14 +388,34 @@ export class AbstractHDWallet extends LegacyWallet {
return binarySearchIterationForExternalAddress(index, maxUsedIndex, minUnusedIndex, depth + 1);
}
this.next_free_address_index = await binarySearchIterationForExternalAddress(100);
if (this.next_free_change_address_index === 0 && this.next_free_address_index === 0) {
// assuming that this is freshly imported/created wallet, with no internal variables set
// wild guess - its completely empty wallet:
let completelyEmptyWallet = false;
let txs = await BlueElectrum.getTransactionsByAddress(that._getInternalAddressByIndex(0));
if (txs.length === 0) {
let txs2 = await BlueElectrum.getTransactionsByAddress(that._getExternalAddressByIndex(0));
if (txs2.length === 0) {
// yep, completely empty wallet
completelyEmptyWallet = true;
}
}
// wrong guess. will have to rescan
if (!completelyEmptyWallet) {
// so doing binary search for last used address:
this.next_free_change_address_index = await binarySearchIterationForInternalAddress(100);
this.next_free_address_index = await binarySearchIterationForExternalAddress(100);
}
}
this.usedAddresses = [];
// generating all involved addresses:
for (let c = 0; c < this.next_free_address_index; c++) {
for (let c = 0; c < this.next_free_address_index + this.gap_limit; c++) {
this.usedAddresses.push(this._getExternalAddressByIndex(c));
}
for (let c = 0; c < this.next_free_change_address_index; c++) {
for (let c = 0; c < this.next_free_change_address_index + this.gap_limit; c++) {
this.usedAddresses.push(this._getInternalAddressByIndex(c));
}

18
class/app-storage.js

@ -26,8 +26,24 @@ export class AppStorage {
this.settings = {
brandingColor: '#ffffff',
foregroundColor: '#0c2550',
buttonBackground: '#ffffff',
buttonBackgroundColor: '#ccddf9',
buttonTextColor: '#0c2550',
buttonAlternativeTextColor: '#2f5fb3',
buttonDisabledBackgroundColor: '#eef0f4',
buttonDisabledTextColor: '#9aa0aa',
inputBorderColor: '#d2d2d2',
inputBackgroundColor: '#f5f5f5',
alternativeTextColor: '#9aa0aa',
alternativeTextColor2: '#0f5cc0',
buttonBlueBackgroundColor: '#ccddf9',
incomingBackgroundColor: '#d2f8d6',
incomingForegroundColor: '#37c0a1',
outgoingBackgroundColor: '#f8d2d2',
outgoingForegroundColor: '#d0021b',
successColor: '#37c0a1',
failedColor: '#ff0000',
shadowColor: '#000000',
inverseForegroundColor: '#ffffff',
};
}

132
class/hd-segwit-p2sh-wallet.js

@ -148,83 +148,103 @@ export class HDSegwitP2SHWallet extends AbstractHDWallet {
return this._xpub;
}
/**
* @inheritDoc
*/
async fetchTransactions() {
try {
if (this.usedAddresses.length === 0) {
// just for any case, refresh balance (it refreshes internal `this.usedAddresses`)
await this.fetchBalance();
}
async _getTransactionsBatch(addresses) {
const api = new Frisbee({ baseURI: 'https://blockchain.info' });
let transactions = [];
let offset = 0;
let addresses = this.usedAddresses.join('|');
addresses += '|' + this._getExternalAddressByIndex(this.next_free_address_index);
addresses += '|' + this._getInternalAddressByIndex(this.next_free_change_address_index);
while (1) {
let response = await api.get('/multiaddr?active=' + addresses + '&n=100&offset=' + offset);
const api = new Frisbee({ baseURI: 'https://blockchain.info' });
this.transactions = [];
let offset = 0;
while (1) {
let response = await api.get('/multiaddr?active=' + addresses + '&n=100&offset=' + offset);
if (response && response.body) {
if (response.body.txs && response.body.txs.length === 0) {
break;
}
if (response && response.body) {
if (response.body.txs && response.body.txs.length === 0) {
break;
}
this._lastTxFetch = +new Date();
this._lastTxFetch = +new Date();
// processing TXs and adding to internal memory
if (response.body.txs) {
for (let tx of response.body.txs) {
let value = 0;
// processing TXs and adding to internal memory
if (response.body.txs) {
for (let tx of response.body.txs) {
let value = 0;
for (let input of tx.inputs) {
// ----- INPUTS
for (let input of tx.inputs) {
// ----- INPUTS
if (input.prev_out && input.prev_out.addr && this.weOwnAddress(input.prev_out.addr)) {
// this is outgoing from us
value -= input.prev_out.value;
}
if (input.prev_out && input.prev_out.addr && this.weOwnAddress(input.prev_out.addr)) {
// this is outgoing from us
value -= input.prev_out.value;
}
}
for (let output of tx.out) {
// ----- OUTPUTS
for (let output of tx.out) {
// ----- OUTPUTS
if (output.addr && this.weOwnAddress(output.addr)) {
// this is incoming to us
value += output.value;
}
if (output.addr && this.weOwnAddress(output.addr)) {
// this is incoming to us
value += output.value;
}
}
tx.value = value; // new BigNumber(value).div(100000000).toString() * 1;
if (response.body.hasOwnProperty('info')) {
if (response.body.info.latest_block.height && tx.block_height) {
tx.confirmations = response.body.info.latest_block.height - tx.block_height + 1;
} else {
tx.confirmations = 0;
}
tx.value = value; // new BigNumber(value).div(100000000).toString() * 1;
if (response.body.hasOwnProperty('info')) {
if (response.body.info.latest_block.height && tx.block_height) {
tx.confirmations = response.body.info.latest_block.height - tx.block_height + 1;
} else {
tx.confirmations = 0;
}
this.transactions.push(tx);
} else {
tx.confirmations = 0;
}
transactions.push(tx);
}
if (response.body.txs.length < 100) {
// this fetch yilded less than page size, thus requesting next batch makes no sense
break;
}
} else {
break; // error ?
if (response.body.txs.length < 100) {
// this fetch yilded less than page size, thus requesting next batch makes no sense
break;
}
} else {
throw new Error('Could not fetch transactions from API: ' + response.err); // breaks here
break; // error ?
}
} else {
throw new Error('Could not fetch transactions from API: ' + response.err); // breaks here
}
offset += 100;
}
return transactions;
}
offset += 100;
/**
* @inheritDoc
*/
async fetchTransactions() {
try {
if (this.usedAddresses.length === 0) {
// just for any case, refresh balance (it refreshes internal `this.usedAddresses`)
await this.fetchBalance();
}
this.transactions = [];
let addresses4batch = [];
for (let addr of this.usedAddresses) {
addresses4batch.push(addr);
if (addresses4batch.length >= 45) {
let addresses = addresses4batch.join('|');
let transactions = await this._getTransactionsBatch(addresses);
this.transactions = this.transactions.concat(transactions);
addresses4batch = [];
}
}
// final batch
for (let c = 0; c <= this.gap_limit; c++) {
addresses4batch.push(this._getExternalAddressByIndex(this.next_free_address_index + c));
addresses4batch.push(this._getInternalAddressByIndex(this.next_free_change_address_index + c));
}
let addresses = addresses4batch.join('|');
let transactions = await this._getTransactionsBatch(addresses);
this.transactions = this.transactions.concat(transactions);
} catch (err) {
console.warn(err);
}

144
ios/BlueWallet.xcodeproj/project.pbxproj

@ -14,8 +14,8 @@
00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; };
00E356F31AD99517003FC87E /* BlueWalletTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* BlueWalletTests.m */; };
01AB943FA0794E91B65F0BFE /* libRCTPrivacySnapshot.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FC63C7054F1C4FDFB7A830E5 /* libRCTPrivacySnapshot.a */; };
02DEC1C9F61B405E8E357B2E /* libRCTWKWebView.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E7078D2FED444DA4B0BD57F9 /* libRCTWKWebView.a */; };
034FE828CEF14A6CBCF9073E /* libRNFS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8637D4B5E14D443A9031DA95 /* libRNFS.a */; };
09C0911C2C014C8C87F192C9 /* libRNCSlider.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B4D3235A177F4580BA52F2F9 /* libRNCSlider.a */; };
0AF37AC0E67044038B49FB3B /* SimpleLineIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 4D746BBE67E84684848246E2 /* SimpleLineIcons.ttf */; };
0B2C4EBFB4CB4960AAD777BC /* Entypo.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 44BC9E3EE0E9476A830CCCB9 /* Entypo.ttf */; };
11D1A2F320CAFA9E000508D9 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */; };
@ -67,6 +67,7 @@
B058E2132B704E9E874BDB29 /* libRNRandomBytes-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 253243E162CE4822BF3A3B7D /* libRNRandomBytes-tvOS.a */; };
B1102FDCF41C4D008352748B /* libRNReactNativeHapticFeedback.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AB2325650CE04F018697ACFE /* libRNReactNativeHapticFeedback.a */; };
B44D665E562B4F289F09D327 /* libRNSVG-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9F1F51A83D044F3BB26A35FC /* libRNSVG-tvOS.a */; };
B4E923F92251B32100ABC850 /* libRNCWebView.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B40FE68321FAD78F005D5578 /* libRNCWebView.a */; };
C1056BF235EE4E23AAF21975 /* libRCTQRCodeLocalImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B642AFB13483418CAB6FF25E /* libRCTQRCodeLocalImage.a */; };
C10C13E4CC4445C5861B1A3A /* libRNDeviceInfo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FD7977067E1A496F94D8B1B7 /* libRNDeviceInfo.a */; };
C41EC263DBE649299C99B9A5 /* libRNRate.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BBA99996E6FA4B49ACE0BEFA /* libRNRate.a */; };
@ -514,13 +515,6 @@
remoteGlobalIDString = 134814201AA4EA6300B7C361;
remoteInfo = RNCWebView;
};
B40FE6DC21FAD7A8005D5578 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = E7173EC6B95B4981AD3D4C70 /* RCTWKWebView.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 0974579A1D2A440A000D9368;
remoteInfo = RCTWKWebView;
};
B4327EF221FC137D00F7ADFA /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 1A03CFBC35DD4AC28FA4A619 /* RNImagePicker.xcodeproj */;
@ -549,6 +543,20 @@
remoteGlobalIDString = 134814201AA4EA6300B7C361;
remoteInfo = TcpSockets;
};
B4E923C32251B1CF00ABC850 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 178483985D8A4250A4794DA7 /* RNGestureHandler.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = B5C32A36220C603B000FFB8D;
remoteInfo = "RNGestureHandler-tvOS";
};
B4E923D52251B1CF00ABC850 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = C41AE1DD23384D5CA09A0F90 /* RNCSlider.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 28C79A09220DC4CC0061DE82;
remoteInfo = RNCSlider;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
@ -618,11 +626,13 @@
A9166D490AEF4938BD6621CF /* Feather.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Feather.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Feather.ttf"; sourceTree = "<group>"; };
AB2325650CE04F018697ACFE /* libRNReactNativeHapticFeedback.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNReactNativeHapticFeedback.a; sourceTree = "<group>"; };
ADBDB91F1DFEBF0600ED6528 /* RCTBlob.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTBlob.xcodeproj; path = "../node_modules/react-native/Libraries/Blob/RCTBlob.xcodeproj"; sourceTree = "<group>"; };
B4D3235A177F4580BA52F2F9 /* libRNCSlider.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNCSlider.a; sourceTree = "<group>"; };
B642AFB13483418CAB6FF25E /* libRCTQRCodeLocalImage.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRCTQRCodeLocalImage.a; sourceTree = "<group>"; };
B9D9B3A7B2CB4255876B67AF /* libz.tbd */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
BBA99996E6FA4B49ACE0BEFA /* libRNRate.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNRate.a; sourceTree = "<group>"; };
BCBEC3BDE968405183D1ABAD /* RNCamera.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNCamera.xcodeproj; path = "../node_modules/react-native-camera/ios/RNCamera.xcodeproj"; sourceTree = "<group>"; };
C0B8F0536B07482281FA173E /* ReactNativePermissions.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = ReactNativePermissions.xcodeproj; path = "../node_modules/react-native-permissions/ios/ReactNativePermissions.xcodeproj"; sourceTree = "<group>"; };
C41AE1DD23384D5CA09A0F90 /* RNCSlider.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNCSlider.xcodeproj; path = "../node_modules/@react-native-community/slider/ios/RNCSlider.xcodeproj"; sourceTree = "<group>"; };
C4496FB303574862B40A878A /* AntDesign.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = AntDesign.ttf; path = "../node_modules/react-native-vector-icons/Fonts/AntDesign.ttf"; sourceTree = "<group>"; };
CA741BA794714D3F80251AC9 /* Ionicons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Ionicons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Ionicons.ttf"; sourceTree = "<group>"; };
CD746B955C55410793BB72C0 /* libRNGestureHandler.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNGestureHandler.a; sourceTree = "<group>"; };
@ -633,8 +643,6 @@
D6EC5B694E664FD7B02EDD2F /* libRNSentry.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNSentry.a; sourceTree = "<group>"; };
E432C66239704518B4C8719B /* RCTGoogleAnalyticsBridge.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RCTGoogleAnalyticsBridge.xcodeproj; path = "../node_modules/react-native-google-analytics-bridge/ios/RCTGoogleAnalyticsBridge/RCTGoogleAnalyticsBridge.xcodeproj"; sourceTree = "<group>"; };
E6B44173A8854B6D85D7F933 /* libRNVectorIcons-tvOS.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = "libRNVectorIcons-tvOS.a"; sourceTree = "<group>"; };
E7078D2FED444DA4B0BD57F9 /* libRCTWKWebView.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRCTWKWebView.a; sourceTree = "<group>"; };
E7173EC6B95B4981AD3D4C70 /* RCTWKWebView.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RCTWKWebView.xcodeproj; path = "../node_modules/react-native-wkwebview-reborn/ios/RCTWKWebView.xcodeproj"; sourceTree = "<group>"; };
E8E8CE89B3D142C6A8A56C34 /* Octicons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Octicons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Octicons.ttf"; sourceTree = "<group>"; };
EAEF0F27730C4742B0F3AB99 /* RNRate.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNRate.xcodeproj; path = "../node_modules/react-native-rate/ios/RNRate.xcodeproj"; sourceTree = "<group>"; };
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
@ -660,6 +668,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
B4E923F92251B32100ABC850 /* libRNCWebView.a in Frameworks */,
ED297163215061F000B7C4FE /* JavaScriptCore.framework in Frameworks */,
ADBDB9381DFEBF1600ED6528 /* libRCTBlob.a in Frameworks */,
11D1A2F320CAFA9E000508D9 /* libRCTAnimation.a in Frameworks */,
@ -690,13 +699,13 @@
C70F52A820614622A16EAF23 /* libRNSentry.a in Frameworks */,
6D9E44529B3C463AB9E6CA39 /* libRNVectorIcons.a in Frameworks */,
1FE70B15FB724CE3927C7541 /* libRNSVG.a in Frameworks */,
02DEC1C9F61B405E8E357B2E /* libRCTWKWebView.a in Frameworks */,
A6E5EEC7A4B54F5A9C9D92FC /* libRNImagePicker.a in Frameworks */,
C1056BF235EE4E23AAF21975 /* libRCTQRCodeLocalImage.a in Frameworks */,
34582CAA4AD140F7B80C961A /* libTcpSockets.a in Frameworks */,
C98BD93429374073A736EFBD /* libToolTipMenu.a in Frameworks */,
16763847ED654C79A4B4BBD5 /* ToolTipMenuTests.xctest in Frameworks */,
01AB943FA0794E91B65F0BFE /* libRCTPrivacySnapshot.a in Frameworks */,
09C0911C2C014C8C87F192C9 /* libRNCSlider.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -931,12 +940,12 @@
9EA3788F4C6643B7B0182587 /* RNVectorIcons.xcodeproj */,
3BC85BBCB16D42A4BAC73161 /* RNSVG.xcodeproj */,
2509F6D4DBD14FECBAD3EAC6 /* RNCWebView.xcodeproj */,
E7173EC6B95B4981AD3D4C70 /* RCTWKWebView.xcodeproj */,
1A03CFBC35DD4AC28FA4A619 /* RNImagePicker.xcodeproj */,
7EA61BC8FF6E4AD2A67F1557 /* RCTQRCodeLocalImage.xcodeproj */,
910283A2A9EB4D00902DE78E /* TcpSockets.xcodeproj */,
151034F9C66E464B8A6581DA /* ToolTipMenu.xcodeproj */,
CEF75317A8A047AB85BF0FC9 /* RCTPrivacySnapshot.xcodeproj */,
C41AE1DD23384D5CA09A0F90 /* RNCSlider.xcodeproj */,
);
name = Libraries;
sourceTree = "<group>";
@ -1016,13 +1025,13 @@
94565BFC6A0C4235B3EC7B01 /* libRNSVG.a */,
9F1F51A83D044F3BB26A35FC /* libRNSVG-tvOS.a */,
A7C4B1FDAD264618BAF8C335 /* libRNCWebView.a */,
E7078D2FED444DA4B0BD57F9 /* libRCTWKWebView.a */,
FC98DC24A81A463AB8B2E6B1 /* libRNImagePicker.a */,
B642AFB13483418CAB6FF25E /* libRCTQRCodeLocalImage.a */,
9DF4E6C040764E4BA1ACC1EB /* libTcpSockets.a */,
90F86BC5194548CA87D729A9 /* libToolTipMenu.a */,
8448882949434D41A054C0B2 /* ToolTipMenuTests.xctest */,
FC63C7054F1C4FDFB7A830E5 /* libRCTPrivacySnapshot.a */,
B4D3235A177F4580BA52F2F9 /* libRNCSlider.a */,
);
name = "Recovered References";
sourceTree = "<group>";
@ -1074,6 +1083,7 @@
isa = PBXGroup;
children = (
B40FE56C21FAD229005D5578 /* libRNGestureHandler.a */,
B4E923C42251B1CF00ABC850 /* libRNGestureHandler-tvOS.a */,
);
name = Products;
sourceTree = "<group>";
@ -1146,14 +1156,6 @@
name = Products;
sourceTree = "<group>";
};
B40FE6D921FAD7A7005D5578 /* Products */ = {
isa = PBXGroup;
children = (
B40FE6DD21FAD7A8005D5578 /* libRCTWKWebView.a */,
);
name = Products;
sourceTree = "<group>";
};
B4327EEF21FC137D00F7ADFA /* Products */ = {
isa = PBXGroup;
children = (
@ -1186,6 +1188,14 @@
name = Products;
sourceTree = "<group>";
};
B4E923D22251B1CF00ABC850 /* Products */ = {
isa = PBXGroup;
children = (
B4E923D62251B1CF00ABC850 /* libRNCSlider.a */,
);
name = Products;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -1296,6 +1306,7 @@
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
English,
en,
Base,
);
@ -1363,10 +1374,6 @@
ProductGroup = 139FDEE71B06529A00C62182 /* Products */;
ProjectRef = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */;
},
{
ProductGroup = B40FE6D921FAD7A7005D5578 /* Products */;
ProjectRef = E7173EC6B95B4981AD3D4C70 /* RCTWKWebView.xcodeproj */;
},
{
ProductGroup = 146834001AC3E56700842450 /* Products */;
ProjectRef = 146833FF1AC3E56700842450 /* React.xcodeproj */;
@ -1379,6 +1386,10 @@
ProductGroup = B40FE53D21FAD229005D5578 /* Products */;
ProjectRef = BCBEC3BDE968405183D1ABAD /* RNCamera.xcodeproj */;
},
{
ProductGroup = B4E923D22251B1CF00ABC850 /* Products */;
ProjectRef = C41AE1DD23384D5CA09A0F90 /* RNCSlider.xcodeproj */;
},
{
ProductGroup = B40FE67F21FAD78F005D5578 /* Products */;
ProjectRef = 2509F6D4DBD14FECBAD3EAC6 /* RNCWebView.xcodeproj */;
@ -1856,13 +1867,6 @@
remoteRef = B40FE68221FAD78F005D5578 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
B40FE6DD21FAD7A8005D5578 /* libRCTWKWebView.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTWKWebView.a;
remoteRef = B40FE6DC21FAD7A8005D5578 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
B4327EF321FC137D00F7ADFA /* libRNImagePicker.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
@ -1891,6 +1895,20 @@
remoteRef = B47720652202510900DD0E81 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
B4E923C42251B1CF00ABC850 /* libRNGestureHandler-tvOS.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libRNGestureHandler-tvOS.a";
remoteRef = B4E923C32251B1CF00ABC850 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
B4E923D62251B1CF00ABC850 /* libRNCSlider.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRNCSlider.a;
remoteRef = B4E923D52251B1CF00ABC850 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
/* End PBXReferenceProxy section */
/* Begin PBXResourcesBuildPhase section */
@ -2061,22 +2079,17 @@
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
"$(SRCROOT)/../node_modules/react-native-svg/ios/**",
"$(SRCROOT)/../node_modules/react-native-webview/ios",
"$(SRCROOT)/../node_modules/react-native-wkwebview-reborn/ios/RCTWKWebView",
"$(SRCROOT)/../node_modules/react-native-image-picker/ios",
"$(SRCROOT)/../node_modules/@remobile/react-native-qrcode-local-image/ios/RCTQRCodeLocalImage",
"$(SRCROOT)/../node_modules/react-native-tcp/ios/**",
"$(SRCROOT)/../node_modules/react-native-tooltip/ToolTipMenu",
"$(SRCROOT)/../node_modules/react-native-privacy-snapshot/RCTPrivacySnapshot",
"$(SRCROOT)/../node_modules/@react-native-community/slider/ios",
);
INFOPLIST_FILE = BlueWalletTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = (
"-ObjC",
"-lc++",
@ -2109,22 +2122,17 @@
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
"$(SRCROOT)/../node_modules/react-native-svg/ios/**",
"$(SRCROOT)/../node_modules/react-native-webview/ios",
"$(SRCROOT)/../node_modules/react-native-wkwebview-reborn/ios/RCTWKWebView",
"$(SRCROOT)/../node_modules/react-native-image-picker/ios",
"$(SRCROOT)/../node_modules/@remobile/react-native-qrcode-local-image/ios/RCTQRCodeLocalImage",
"$(SRCROOT)/../node_modules/react-native-tcp/ios/**",
"$(SRCROOT)/../node_modules/react-native-tooltip/ToolTipMenu",
"$(SRCROOT)/../node_modules/react-native-privacy-snapshot/RCTPrivacySnapshot",
"$(SRCROOT)/../node_modules/@react-native-community/slider/ios",
);
INFOPLIST_FILE = BlueWalletTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = (
"-ObjC",
"-lc++",
@ -2158,15 +2166,15 @@
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
"$(SRCROOT)/../node_modules/react-native-svg/ios/**",
"$(SRCROOT)/../node_modules/react-native-webview/ios",
"$(SRCROOT)/../node_modules/react-native-wkwebview-reborn/ios/RCTWKWebView",
"$(SRCROOT)/../node_modules/react-native-image-picker/ios",
"$(SRCROOT)/../node_modules/@remobile/react-native-qrcode-local-image/ios/RCTQRCodeLocalImage",
"$(SRCROOT)/../node_modules/react-native-tcp/ios/**",
"$(SRCROOT)/../node_modules/react-native-tooltip/ToolTipMenu",
"$(SRCROOT)/../node_modules/react-native-privacy-snapshot/RCTPrivacySnapshot",
"$(SRCROOT)/../node_modules/@react-native-community/slider/ios",
);
INFOPLIST_FILE = BlueWallet/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
OTHER_LDFLAGS = (
"$(inherited)",
@ -2202,15 +2210,15 @@
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
"$(SRCROOT)/../node_modules/react-native-svg/ios/**",
"$(SRCROOT)/../node_modules/react-native-webview/ios",
"$(SRCROOT)/../node_modules/react-native-wkwebview-reborn/ios/RCTWKWebView",
"$(SRCROOT)/../node_modules/react-native-image-picker/ios",
"$(SRCROOT)/../node_modules/@remobile/react-native-qrcode-local-image/ios/RCTQRCodeLocalImage",
"$(SRCROOT)/../node_modules/react-native-tcp/ios/**",
"$(SRCROOT)/../node_modules/react-native-tooltip/ToolTipMenu",
"$(SRCROOT)/../node_modules/react-native-privacy-snapshot/RCTPrivacySnapshot",
"$(SRCROOT)/../node_modules/@react-native-community/slider/ios",
);
INFOPLIST_FILE = BlueWallet/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
OTHER_LDFLAGS = (
"$(inherited)",
@ -2253,21 +2261,16 @@
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
"$(SRCROOT)/../node_modules/react-native-svg/ios/**",
"$(SRCROOT)/../node_modules/react-native-webview/ios",
"$(SRCROOT)/../node_modules/react-native-wkwebview-reborn/ios/RCTWKWebView",
"$(SRCROOT)/../node_modules/react-native-image-picker/ios",
"$(SRCROOT)/../node_modules/@remobile/react-native-qrcode-local-image/ios/RCTQRCodeLocalImage",
"$(SRCROOT)/../node_modules/react-native-tcp/ios/**",
"$(SRCROOT)/../node_modules/react-native-tooltip/ToolTipMenu",
"$(SRCROOT)/../node_modules/react-native-privacy-snapshot/RCTPrivacySnapshot",
"$(SRCROOT)/../node_modules/@react-native-community/slider/ios",
);
INFOPLIST_FILE = "BlueWallet-tvOS/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = (
"-ObjC",
"-lc++",
@ -2309,21 +2312,16 @@
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
"$(SRCROOT)/../node_modules/react-native-svg/ios/**",
"$(SRCROOT)/../node_modules/react-native-webview/ios",
"$(SRCROOT)/../node_modules/react-native-wkwebview-reborn/ios/RCTWKWebView",
"$(SRCROOT)/../node_modules/react-native-image-picker/ios",
"$(SRCROOT)/../node_modules/@remobile/react-native-qrcode-local-image/ios/RCTQRCodeLocalImage",
"$(SRCROOT)/../node_modules/react-native-tcp/ios/**",
"$(SRCROOT)/../node_modules/react-native-tooltip/ToolTipMenu",
"$(SRCROOT)/../node_modules/react-native-privacy-snapshot/RCTPrivacySnapshot",
"$(SRCROOT)/../node_modules/@react-native-community/slider/ios",
);
INFOPLIST_FILE = "BlueWallet-tvOS/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = (
"-ObjC",
"-lc++",
@ -2364,21 +2362,16 @@
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
"$(SRCROOT)/../node_modules/react-native-svg/ios/**",
"$(SRCROOT)/../node_modules/react-native-webview/ios",
"$(SRCROOT)/../node_modules/react-native-wkwebview-reborn/ios/RCTWKWebView",
"$(SRCROOT)/../node_modules/react-native-image-picker/ios",
"$(SRCROOT)/../node_modules/@remobile/react-native-qrcode-local-image/ios/RCTQRCodeLocalImage",
"$(SRCROOT)/../node_modules/react-native-tcp/ios/**",
"$(SRCROOT)/../node_modules/react-native-tooltip/ToolTipMenu",
"$(SRCROOT)/../node_modules/react-native-privacy-snapshot/RCTPrivacySnapshot",
"$(SRCROOT)/../node_modules/@react-native-community/slider/ios",
);
INFOPLIST_FILE = "BlueWallet-tvOSTests/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = (
"-ObjC",
"-lc++",
@ -2419,21 +2412,16 @@
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
"$(SRCROOT)/../node_modules/react-native-svg/ios/**",
"$(SRCROOT)/../node_modules/react-native-webview/ios",
"$(SRCROOT)/../node_modules/react-native-wkwebview-reborn/ios/RCTWKWebView",
"$(SRCROOT)/../node_modules/react-native-image-picker/ios",
"$(SRCROOT)/../node_modules/@remobile/react-native-qrcode-local-image/ios/RCTQRCodeLocalImage",
"$(SRCROOT)/../node_modules/react-native-tcp/ios/**",
"$(SRCROOT)/../node_modules/react-native-tooltip/ToolTipMenu",
"$(SRCROOT)/../node_modules/react-native-privacy-snapshot/RCTPrivacySnapshot",
"$(SRCROOT)/../node_modules/@react-native-community/slider/ios",
);
INFOPLIST_FILE = "BlueWallet-tvOSTests/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = (
"-ObjC",
"-lc++",

6
ios/BlueWallet/Info.plist

@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>3.8.3</string>
<string>3.9.4</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
@ -56,9 +56,11 @@
<key>NSCalendarsUsageDescription</key>
<string>This alert should not show up as we do not require this data</string>
<key>NSCameraUsageDescription</key>
<string>In order to quickly scan the recipient's address, we need your permission to use the camera to scan their QR Code.</string>
<string>In order to quickly scan the recipient&apos;s address, we need your permission to use the camera to scan their QR Code.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>This alert should not show up as we do not require this data</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>This alert should not show up as we do not require this data</string>
<key>NSMicrophoneUsageDescription</key>
<string>This alert should not show up as we do not require this data</string>
<key>NSMotionUsageDescription</key>

32
ios/fastlane/metadata/en-US/release_notes.txt

@ -1,3 +1,35 @@
v3.9.2
======
* ADD: Swedish translation
* FIX: fetch tx history for large HD wallets (closes #416)
* FIX: HD wallets not can handle small gaps in hierarchy (closes #388)
* FIX: JP
* REF: better Electrum support
v3.9.0
======
* FIX: all wallets are refreshed when app launches
* FIX: Send BTC should ignore non-onchain wallets* FIX: all wallets are refreshed when app launches
* FIX: correct qr scan when importing lndhub qr backup
* ADD: Finnish translation
* ADD: Turkish translation
* ADD: Greek (EL) localization
* ADD: norwegian translation
v3.8.3
======
* REF: better support for different lndhub uris
* FIX: Send BTC should ignore non-onchain wallets
* FIX: Deeplink would fail sometimes on cold start (#382)
* FIX: Fixed an issue when inserting an address after amount.
* FIX: UI fixes for transactions list
* ADD: Haptic feedbacks on error alerts
* ADD: Read clipboard for transactions
* ADD: Italian language
v3.8.2
======

3
loc/cs_CZ.js

@ -22,6 +22,9 @@ module.exports = {
latest_transaction: 'poslední transakce',
empty_txs1: 'Zde budou zobrazeny vaše transakce,',
empty_txs2: 'zatím žádné',
empty_txs1_lightning:
'Lightning wallet should be used for your daily transactions. Fees are unfairly cheap and speed is blazing fast.',
empty_txs2_lightning: '\nTo start using it tap on "manage funds" and topup your balance.',
tap_here_to_buy: 'Klikněte zde pro zakoupení Bitcoinu',
},
reorder: {

3
loc/da_DK.js

@ -22,6 +22,9 @@ module.exports = {
latest_transaction: 'seneste transaktion',
empty_txs1: 'Dine transaktioner vil blive vist her,',
empty_txs2: 'ingen endnu',
empty_txs1_lightning:
'Lightning wallet should be used for your daily transactions. Fees are unfairly cheap and speed is blazing fast.',
empty_txs2_lightning: '\nTo start using it tap on "manage funds" and topup your balance.',
tap_here_to_buy: 'Tryk her for at købe Bitcoin',
},
reorder: {

3
loc/de_DE.js

@ -24,6 +24,9 @@ module.exports = {
latest_transaction: 'Lezte Transaktion',
empty_txs1: 'Deine Transaktionen erscheinen hier',
empty_txs2: 'Noch keine Transaktionen',
empty_txs1_lightning:
'Lightning wallet should be used for your daily transactions. Fees are unfairly cheap and speed is blazing fast.',
empty_txs2_lightning: '\nTo start using it tap on "manage funds" and topup your balance.',
tap_here_to_buy: 'Klicke hier, um Bitcoin zu kaufen',
},
reorder: {

226
loc/el.js

@ -0,0 +1,226 @@
module.exports = {
_: {
storage_is_encrypted: 'Το αρχείο σου είναι κρυπτογραφημένο. Χρειάζεται ένας κωδικός για να αποκρυπτογραφηθεί.',
enter_password: 'Εισήγαγε κωδικό',
bad_password: 'Λάθος κωδικός, δοκίμασε ξανά',
never: 'ποτέ',
continue: 'Συνέχισε',
ok: 'Εντάξει',
},
wallets: {
select_wallet: 'Διάλεξε Πορτοφόλι',
options: 'επιλογές',
createBitcoinWallet:
'Δεν έχεις πορτοφόλι Bitcoin. Για να βάλεις χρήματα στο πορτοφόλι Lightning, πρέπει πρώτα να δημιουργήσεις ή να εισάγεις ένα πορτοφόλι Bitcoin. Θα ήθελες να προχωρήσεις ούτως ή άλλως;',
list: {
app_name: 'BlueWallet',
title: 'πορτοφόλια',
header:
'Ένα πορτοφόλι αποτελείται από ένα μυστικό (το ιδιωτικό κλειδί) και μια διεύθυνση' +
'την οποία μπορείς να δώσεις σε άλλους για να σε πληρώσουν σε αυτήν.',
add: 'Πρόσθεσε Πορτοφόλι',
create_a_wallet: 'Δημιούργησε ένα πορτοφόλι',
create_a_wallet1: 'Είναι δωρεάν και μπορείς να',
create_a_wallet2: 'δημιουργήσεις όσα θέλεις',
latest_transaction: 'τελευταία συναλλαγή',
empty_txs1: 'Οι συναλλαγές θα εμφανιστούν εδώ,',
empty_txs2: 'καμία συναλλαγή',
empty_txs1_lightning:
'Lightning wallet should be used for your daily transactions. Fees are unfairly cheap and speed is blazing fast.',
empty_txs2_lightning: '\nTo start using it tap on "manage funds" and topup your balance.',
tap_here_to_buy: 'Πάτησε εδώ για να αγοράσεις Bitcoin',
},
reorder: {
title: 'Άλλαξε την σειρά των Πορτοφολιών',
},
add: {
title: 'Πρόσθεσε πορτοφόλι',
description:
'Μπορείς να σκανάρεις ένα χάρτινο πορτοφόλι (σε WIF - Wallet Import Format), ή να δημιουργήσεις ένα νέο πορτοφόλι. Υποστηρίζουμε πορτοφόλια τύπου Segwit.',
scan: 'Σκάναρε',
create: 'Δημιούργησε',
label_new_segwit: 'Νεό πορτοφόλι τύπου SegWit',
label_new_lightning: 'Νέο πορτοφόλι Lightning',
wallet_name: 'όνομα',
wallet_type: 'τύπος',
or: 'ή',
import_wallet: 'Εισήγαγε πορτοφόλι',
imported: 'Εισήχθηκε',
coming_soon: 'Σύντομα',
lightning: 'Lightning',
bitcoin: 'Bitcoin',
},
details: {
title: 'Πορτοφόλι',
address: 'Διεύθυνση',
type: 'Τύπος',
label: 'Ετικέτα',
destination: 'προορισμός',
description: 'περιγραφή',
are_you_sure: 'Είσαι σίγουρος;',
yes_delete: 'Ναι, διέγραψε',
no_cancel: 'Όχι, ακύρωσε',
delete: 'Διέγραψε',
save: 'Σώσε',
delete_this_wallet: 'Διέγραψε το πορτοφόλι',
export_backup: 'Εξήγαγε / δημιούργησε αντίγραφο ασφαλείας',
buy_bitcoin: 'Αγόρασε Bitcoin',
show_xpub: 'Δείξε το XPUB του πορτοφολιού',
},
export: {
title: 'Εξαγωγή πορτοφολιού',
},
xpub: {
title: 'XPUB του πορτοφολιού',
copiedToClipboard: 'Αντιγράφηκε στο clipboard.',
},
import: {
title: 'Εισαγωγή',
explanation:
'Γράψε εδώ το μνημονικό (φράση), το ιδιωτικό κλειδί, το WIF, ή ό,τι άλλο έχεις. Το BlueWallet θα προσπαθήσει να μαντέψει το σωστό format και να εισάγει το πορτοφόλι',
imported: 'Εισήχθη',
error: 'Η εισαγωγή απέτυχε. Παρακαλούμε σιγουρευτείτε ότι τα δεδομένα που εισάγετε είναι σωστά.',
success: 'Επιτυχία',
do_import: 'Εισήγαγε',
scan_qr: 'ή θέλεις θα σκανάρεις ένα QR code;',
},
scanQrWif: {
go_back: 'Πίσω',
cancel: 'Ακύρωσε',
decoding: 'Αποκωδικοποίηση',
input_password: 'Βάλε τον κωδικό',
password_explain: 'Αυτό είναι ένα κρυπτογραφημένο ιδιωτικό κλειδί τύπου BIP38',
bad_password: 'Λάθος κωδικός',
wallet_already_exists: 'Αυτό το πορτοφόλι υπάρχει ήδη',
bad_wif: 'Λάθος WIF',
imported_wif: 'Εισήχθη το WIF ',
with_address: ' με διεύθυνση ',
imported_segwit: 'Εισήχθη SegWit',
imported_legacy: 'Εισήχθη πορτοφόλιο παλαιού τύπου (Legacy)',
imported_watchonly: 'Εισήχθη πορτοφόλι παρακολούθησης (Watch-only)',
},
},
transactions: {
list: {
tabBarLabel: 'Συναλλαγές',
title: 'συναλλαγές',
description: 'Λίστα των εισερχόμενων και εξερχόμενων συναλλαγών όλων των πορτοφολιών σου',
conf: 'conf',
},
details: {
title: 'Συναλλαγή',
from: 'Εισερχόμενες διευθύνσεις',
to: 'Εξερχόμενες διευθύνσεις',
copy: 'Αντέγραψε',
transaction_details: 'Λεπτομέρειες συναλλαγής',
show_in_block_explorer: 'Δείξε στον block explorer',
},
},
send: {
header: 'Στείλε',
details: {
title: 'δημιούργησε συναλλαγή',
amount_field_is_not_valid: 'Το ποσό δεν είναι σωστό',
fee_field_is_not_valid: 'Τα έξοδα συναλλαγής δεν είναι σωστά',
address_field_is_not_valid: 'Η διεύθυνση δεν είναι σωστή',
total_exceeds_balance: 'Δεν έχετε αρκετό υπόλοιπο για να στείλετε αυτό το ποσό.',
create_tx_error: 'Σφάλμα στην δημιουργία της συναλλαγής. Σιγουρευτείτε ότι η διεύθυνση είναι σωστή.',
address: 'διεύθυνση',
amount_placeholder: 'ποσό πληρωμής (σε BTC)',
fee_placeholder: 'συν έξοδα συναλλαγής (σε BTC)',
note_placeholder: 'Σημείωση',
cancel: 'Ακύρωση',
scan: 'Σκάναρε',
send: 'Στείλε',
create: 'Δημιούργησε',
remaining_balance: 'Υπόλοιπο',
},
confirm: {
header: 'Επικύρωση',
sendNow: 'Στείλε τώρα',
},
success: {
done: 'Ολοκληρώθηκε',
},
create: {
details: 'Λεπτομέρειες',
title: 'δημιούργησε συναλλαγή',
error: 'Σφάλμα στη δημιουργία συναλλαγής. Λάθος διεύθυνση ή ποσό συναλλαγής;',
go_back: 'Πίσω',
this_is_hex: 'Αυτή είναι η υπογεγραμμένη συναλλαγή σε μορφή hex και είναι έτοιμη για αποστολή στο δίκτυο.',
to: 'Προς',
amount: 'Ποσό',
fee: 'Έξοδα',
tx_size: 'Μέγεθος συναλλαγής',
satoshi_per_byte: 'Satoshi ανά byte',
memo: 'Σημείωση',
broadcast: 'Στείλε στο δίκτυο',
not_enough_fee: 'Τα έξοδα συναλλαγής δεν είναι αρκετά. Αυξήστε τα.',
},
},
receive: {
header: 'Λήψη',
details: {
title: 'Δώσε αυτήν τη διεύθυνση στον πληρωτή',
share: 'Δώσε',
copiedToClipboard: 'Αντιγράφηκε στο clipboard.',
label: 'Περιγραφή',
create: 'Δημιούργησε',
setAmount: 'Λάβε με ποσό',
},
},
buyBitcoin: {
header: 'Αγόρασε Bitcoin',
tap_your_address: 'Πάτησε στην διεύθυνσή σου για να αντιγραφεί στο clipboard:',
copied: 'Αντιγράφηκε στο Clipboard!',
},
settings: {
header: 'ρυθμίσεις',
plausible_deniability: 'Εύλογη δυνατότητα άρνησης...',
storage_not_encrypted: 'Το αρχείο δεν είναι κρυπτογραφημένο',
storage_encrypted: 'Το αρχείο είναι κρυπτογραφημένο',
password: 'Κωδικός',
password_explain: 'Δώσε ένα κωδικό για την κρυπτογράφηση του αρχείου',
retype_password: 'Ξαναδώσε τον κωδικό',
passwords_do_not_match: 'Οι κωδικοί δεν είναι ίδιοι',
encrypt_storage: 'Κρυπτογράφησε το αρχείο',
lightning_settings: 'Ρυθμίσεις Lightning',
lightning_settings_explain:
'Για να συνδεθείτε στον δικό σας κόμβο LND παρακαλούμε εγκαταστήστε το LndHub' +
' και βάλτε το URL του εδώ. Αφήστε το κενό για να χρησιμοποιήσετε το LNDHub της BlueWallet (lndhub.io). Αφού σώσετε τις ρυθμίσεις τυχόν νέα πορτοφόλια που θα δημιουργήσετε θα συνδεθούν στο LNDHub που επιλέξατε.',
save: 'Σώσε',
about: 'Σχετικά',
language: 'Γλώσσα',
currency: 'Νόμισμα',
},
plausibledeniability: {
title: 'Εύλογη δυνατότητα άρνησης',
help:
'Μπορεί κάποια στιγμή να υποχρεωθείτε να αποκαλύψετε τον ' +
'κωδικό σας. Για να προστατέψετε τα χρήματά σας, το BlueWallet μπορεί να δημιουργήσει ένα εναλλακτικό ' +
'κρυπτογραφημένο αρχείο με διαφορετικό κωδικό. Εάν σας υποχρεώσουν, ' +
'μπορείτε να αποκαλύψετε αυτόν τον δεύτερο κωδικό. Κάποιος που θα τον ' +
'βάλει στο BlueWallet θα δει ένα μόνο ένα ψεύτικο αρχείο που μοιάζει ' +
'κανονικό, προστατεύοντας έτσι το κανονικό σας αρχείο και ' +
'τα χρήματά σας.',
help2: 'Το νέο αρχείο θα είναι πλήρως λειτουργικό, και μπορείτε να βάλετε εκεί ' + 'κάποια ελάχιστα χρήματα για να μοιάζει αληθινό.',
create_fake_storage: 'Δημιούργησε ένα ψεύτικο κρυπτογραφημένο αρχείο',
go_back: 'Πίσω',
create_password: 'Δώσε έναν κωδικό',
create_password_explanation: 'Ο κωδικός του ψεύτικου αρχείου δεν πρέπει να είναι ίδιος με τον κωδικό του πραγματικού αρχείου',
password_should_not_match: 'Ο κωδικός του ψεύτικου αρχείου δεν πρέπει να είναι ίδιος με τον κωδικό του πραγματικού αρχείου',
retype_password: 'Ξαναδώσε τον κωδικό',
passwords_do_not_match: 'Οι κωδικοί δεν είναι ίδιοι, δοκίμασε ξανά',
success: 'Επιτυχία',
},
lnd: {
title: 'Διαχείριση χρημάτων',
choose_source_wallet: 'Διάλεξε ένα πορτοφόλι',
refill_lnd_balance: 'Γέμισε το πορτοφόλι Lightning',
refill: 'Γέμισμα πορτοφολιού',
withdraw: 'Ανάληψη',
expired: 'Έληξε',
placeholder: 'Τιμολόγιο',
sameWalletAsInvoiceError: 'Δεν μπορείς να εξοφλήσεις ένα τιμολόγιο από το ίδιο πορτοφόλι με το οποίο δημιουργήθηκε.',
},
};

3
loc/en.js

@ -23,6 +23,9 @@ module.exports = {
latest_transaction: 'latest transaction',
empty_txs1: 'Your transactions will appear here,',
empty_txs2: 'none at the moment',
empty_txs1_lightning:
'Lightning wallet should be used for your daily transactions. Fees are unfairly cheap and speed is blazing fast.',
empty_txs2_lightning: '\nTo start using it tap on "manage funds" and topup your balance.',
tap_here_to_buy: 'Tap here to buy Bitcoin',
},
reorder: {

3
loc/es.js

@ -23,6 +23,9 @@ module.exports = {
latest_transaction: 'última transacción',
empty_txs1: 'Sus transacciones aparecerán aquí,',
empty_txs2: 'ninguno por el momento.',
empty_txs1_lightning:
'Lightning wallet should be used for your daily transactions. Fees are unfairly cheap and speed is blazing fast.',
empty_txs2_lightning: '\nTo start using it tap on "manage funds" and topup your balance.',
tap_here_to_buy: 'Tap here to buy Bitcoin',
},
reorder: {

226
loc/fi_FI.js

@ -0,0 +1,226 @@
module.exports = {
_: {
storage_is_encrypted: 'Tallennustilasi on salattu. Salasana vaaditaan sen purkamiseksi',
enter_password: 'Anna salasana',
bad_password: 'Väärä salasana, yritä uudelleen',
never: 'ei koskaan',
continue: 'Jatka',
ok: 'OK',
},
wallets: {
select_wallet: 'Valitse Lompakko',
options: 'valinnat',
createBitcoinWallet:
'Sinulla ei tällä hetkellä ole Bitcoin lompakkoa. Rahoittaaksesi Lightning lompakkoa, Bitcoin lompakko tulee tuoda tai luoda. Haluatko kuitenkin jatkaa?',
list: {
app_name: 'BlueWallet',
title: 'lompakot',
header:
'Lompakko koostuu salaisesta avaimesta (private key) sekä julkisesta osoitteesta' +
',jonka voit jakaa vastaanottaaksesi kolikoita.',
add: 'Lisää Lompakko',
create_a_wallet: 'Luo lompakko',
create_a_wallet1: 'Se on ilmaista ja voit luoda',
create_a_wallet2: 'niin monta kuin haluat',
latest_transaction: 'viimeisin siirto',
empty_txs1: 'Siirtosi näkyvät tässä,',
empty_txs2: 'ei siirtoja',
empty_txs1_lightning:
'Lightning wallet should be used for your daily transactions. Fees are unfairly cheap and speed is blazing fast.',
empty_txs2_lightning: '\nTo start using it tap on "manage funds" and topup your balance.',
tap_here_to_buy: 'Napsauta tästä ostaaksesi Bitcoinia',
},
reorder: {
title: 'Järjestele Lompakot',
},
add: {
title: 'lisää lompakko',
description:
'Voit joko skannata paperisen lompakon (WIF - Wallet Import Format), tai luoda uuden. Segwit lompakkoja tuetaan oletuksena.',
scan: 'Skannaa',
create: 'Luo',
label_new_segwit: 'Uusi SegWit',
label_new_lightning: 'Uusi Lightning',
wallet_name: 'nimi',
wallet_type: 'tyyppi',
or: 'tai',
import_wallet: 'Tuo lompakko',
imported: 'Tuotu',
coming_soon: 'Tulossa',
lightning: 'Lightning',
bitcoin: 'Bitcoin',
},
details: {
title: 'Lompakko',
address: 'Osoite',
type: 'Tyyppi',
label: 'Etiketti',
destination: 'määränpää',
description: 'kuvaus',
are_you_sure: 'Oletko varma?',
yes_delete: 'Kyllä, poista',
no_cancel: 'En, peruuta',
delete: 'Poista',
save: 'Tallenna',
delete_this_wallet: 'Poista tämä lompakko',
export_backup: 'Vie / varmuuskopioi',
buy_bitcoin: 'Osta Bitcoinia',
show_xpub: 'Näytä lompakon XPUB',
},
export: {
title: 'lompakon vienti',
},
xpub: {
title: 'lompakon XPUB',
copiedToClipboard: 'Kopioitu leikepöydälle.',
},
import: {
title: 'tuo',
explanation:
'Kirjoita tähän muistisanasi, private key, WIF tai jotain mitä sinulla on. BlueWallet tekee parhaansa arvatakseen oikean muodon ja tuo lompakkosi',
imported: 'Tuotu',
error: 'Tuonti epäonnistui. Varmista, että annettu tieto on oikein',
success: 'Onnistui',
do_import: 'Tuo',
scan_qr: 'tai skannaa QR-koodi?',
},
scanQrWif: {
go_back: 'Mene Takaisin',
cancel: 'Peruuta',
decoding: 'Dekoodataan',
input_password: 'Anna salasana',
password_explain: 'Tämä on BIP38 salattu private key',
bad_password: 'Väärä salasana',
wallet_already_exists: 'Lompakko on jo olemassa',
bad_wif: 'Väärä WIF',
imported_wif: 'WIF tuotu ',
with_address: ' osoitteella ',
imported_segwit: 'SegWit tuotu',
imported_legacy: 'Legacy tuotu',
imported_watchonly: 'Watch-only tuotu',
},
},
transactions: {
list: {
tabBarLabel: 'Siirrot',
title: 'siirrot',
description: 'Lompakkojesi saapuvat ja lähtevät siirrot',
conf: 'conf',
},
details: {
title: 'Siirto',
from: 'Input',
to: 'Output',
copy: 'Kopioi',
transaction_details: 'Siirron tiedot',
show_in_block_explorer: 'Näytä lohkoketjuselaimessa',
},
},
send: {
header: 'Lähetä',
details: {
title: 'luo siirto',
amount_field_is_not_valid: 'Määrä ei kelpaa',
fee_field_is_not_valid: 'Siirtokulu ei kelpaa',
address_field_is_not_valid: 'Osoite ei kelpaa',
total_exceeds_balance: 'Lähetettävä summa ylittää katteen',
create_tx_error: 'Virhe siirron luonnissa. Varmista, että osoite on oikein.',
address: 'osoite',
amount_placeholder: 'lähetettävä summa (BTC)',
fee_placeholder: 'plus siirtokulu (BTC)',
note_placeholder: 'muistiinpano',
cancel: 'Peruuta',
scan: 'Skannaa',
send: 'Lähetä',
create: 'Luo',
remaining_balance: 'Jäljellä oleva kate',
},
confirm: {
header: 'Vahvista',
sendNow: 'Lähetä nyt',
},
success: {
done: 'Valmis',
},
create: {
details: 'Tiedot',
title: 'luo siirto',
error: 'Virhe siirron luonnissa. Väärä osoite tai lähetettävä summa?',
go_back: 'Mene Takaisin',
this_is_hex: 'Tämä on siirron hex, allekirjoitettu ja valmis kuulutettavaksi verkkoon.',
to: 'Vastaanottaja',
amount: 'Summa',
fee: 'Kulu',
tx_size: 'TX koko',
satoshi_per_byte: 'Satoshia per tavu',
memo: 'Memo',
broadcast: 'Kuuluta',
not_enough_fee: 'Kulu ei riitä. Korota kulua.',
},
},
receive: {
header: 'Vastaanota',
details: {
title: 'Jaa tämä osoite maksajalle',
share: 'jaa',
copiedToClipboard: 'Kopioitu leikepöydälle.',
label: 'Selite',
create: 'Luo',
setAmount: 'Vastaanotettava summa',
},
},
buyBitcoin: {
header: 'Osta Bitcoinia',
tap_your_address: 'Napsauta osoitettasi kopioidaksesi sen leikepöydälle:',
copied: 'Kopioitu leikepöydälle!',
},
settings: {
header: 'asetukset',
plausible_deniability: 'Uskottava kiistettävyys...',
storage_not_encrypted: 'Tallennustila: salaamaton',
storage_encrypted: 'Tallennustila: salattu',
password: 'Salasana',
password_explain: 'Luo salasana, jota käytät tallennustilan salauksen purkamiseen',
retype_password: 'Salasana uudelleen',
passwords_do_not_match: 'Salasanat eivät täsmää',
encrypt_storage: 'Salaa tallennustila',
lightning_settings: 'Lightning asetukset',
lightning_settings_explain:
'Yhdistääksesi omaan LND noodiin, asenna LndHub' +
' ja laita sen URL tänne. Jätä tyhjäksi käyttääksesi BlueWalletin LNDHubia (lndhub.io). Muutosten tallentamisen jälkeen luodut lompakot yhdistävät annettuun LNDHubiin.',
save: 'Tallenna',
about: 'Tietoa',
language: 'Kieli',
currency: 'Valuutta',
},
plausibledeniability: {
title: 'Uskottava Kiistettävyys',
help:
'Joissain tilanteissa, saatat olla pakotettu kertomaan ' +
'salasanasi. Pitääksesi kolikkosi turvassa, BlueWallet voi luoda toisen ' +
'salatun tallennustilan, toisella salasanalla. Paineen alla, ' +
'voit kertoa tämän salasanan kolmannelle osapuolelle. Annettaessa ' +
'BlueWalletiin, se avaa uuden väärennetyn tallennustilan. Se näyttää ' +
'oikealta kolmannelle osapuolelle, mutta pitää oikean tallennustilasi ' +
'kolikkoineen turvassa.',
help2: 'Uusi tallennustila näyttää täysin toimivalta, ja voit säilyttää ' + 'pieniä summia siellä, jotta se näyttää uskottavalta.',
create_fake_storage: 'Luo väärennetty tallennustila',
go_back: 'Mene Takaisin',
create_password: 'Luo salasana',
create_password_explanation: 'Väärennetyn tallennustilan salasanan ei tule täsmätä oikean tallennustilan salasanan kanssa',
password_should_not_match: 'Väärennetyn tallennustilan salasanan ei tule täsmätä oikean tallennustilan salasanan kanssa',
retype_password: 'Salasana uudelleen',
passwords_do_not_match: 'Salasanat eivät täsmää, yritä uudelleen',
success: 'Onnistui',
},
lnd: {
title: 'hallinnoi varoja',
choose_source_wallet: 'Valitse lähdelompakko',
refill_lnd_balance: 'Täytä Lightning lompakon katetta',
refill: 'Täytä',
withdraw: 'Nosta',
expired: 'Erääntynyt',
placeholder: 'Lasku',
sameWalletAsInvoiceError: 'Et voi maksaa laskua samalla lompakolla, jolla se on luotu.',
},
};

3
loc/fr_FR.js

@ -24,6 +24,9 @@ module.exports = {
latest_transaction: 'dernière transaction',
empty_txs1: 'Vos transactions apparaîtront ici,',
empty_txs2: 'Aucune pour le moment',
empty_txs1_lightning:
'Lightning wallet should be used for your daily transactions. Fees are unfairly cheap and speed is blazing fast.',
empty_txs2_lightning: '\nTo start using it tap on "manage funds" and topup your balance.',
tap_here_to_buy: 'Cliquez ici pour acheter du Bitcoin',
},
reorder: {

3
loc/hr_HR.js

@ -21,6 +21,9 @@ module.exports = {
latest_transaction: 'posljednja transakcija',
empty_txs1: 'Vaše transakcije će se pojaviti ovdje',
empty_txs2: 'trenutno nema nijedne',
empty_txs1_lightning:
'Lightning wallet should be used for your daily transactions. Fees are unfairly cheap and speed is blazing fast.',
empty_txs2_lightning: '\nTo start using it tap on "manage funds" and topup your balance.',
tap_here_to_buy: 'Klikni ovdje za kupnju Bitkoina',
},
reorder: {

3
loc/id_ID.js

@ -23,6 +23,9 @@ module.exports = {
latest_transaction: 'transaksi terbaru',
empty_txs1: 'Transaksimu akan muncul di sini,',
empty_txs2: 'saat ini tidak ada transaksi',
empty_txs1_lightning:
'Lightning wallet should be used for your daily transactions. Fees are unfairly cheap and speed is blazing fast.',
empty_txs2_lightning: '\nTo start using it tap on "manage funds" and topup your balance.',
tap_here_to_buy: 'Tap di sini untuk membeli bitcoin',
},
reorder: {

99
loc/index.js

@ -17,6 +17,9 @@ dayjs.extend(relativeTime);
strings.setLanguage(lang);
let localeForDayJSAvailable = true;
switch (lang) {
case 'el':
require('dayjs/locale/el');
break;
case 'it':
require('dayjs/locale/it');
break;
@ -29,6 +32,9 @@ dayjs.extend(relativeTime);
case 'es':
require('dayjs/locale/es');
break;
case 'fi_fi':
require('dayjs/locale/fi');
break;
case 'fr_fr':
require('dayjs/locale/fr');
break;
@ -62,6 +68,15 @@ dayjs.extend(relativeTime);
case 'id_id':
require('dayjs/locale/id');
break;
case 'sv_se':
require('dayjs/locale/sv');
break;
case 'nb_no':
require('dayjs/locale/nb');
break;
case 'tr_tr':
require('dayjs/locale/tr');
break;
default:
localeForDayJSAvailable = false;
break;
@ -69,85 +84,6 @@ dayjs.extend(relativeTime);
if (localeForDayJSAvailable) {
dayjs.locale(lang.split('_')[0]);
}
return;
}
if (Localization.getCurrentLocaleAsync) {
let locale = await Localization.getCurrentLocaleAsync();
if (locale) {
console.log('current locale:', locale);
if (
locale === 'en' ||
locale === 'ru' ||
locale === 'ua' ||
locale === 'es' ||
locale === 'it' ||
locale === 'fr-fr' ||
locale === 'pt-br' ||
locale === 'pt-pt' ||
locale === 'jp-JP' ||
locale === 'de-de' ||
locale === 'cs-cz' ||
locale === 'th-th' ||
locale === 'da-dk' ||
locale === 'nl-nl' ||
locale === 'hr-hr' ||
locale === 'id-id' ||
locale === 'zh-cn'
) {
switch (locale) {
case 'it':
require('dayjs/locale/it');
break;
case 'zh-cn':
require('dayjs/locale/zh-cn');
break;
case 'ru':
require('dayjs/locale/ru');
break;
case 'es':
require('dayjs/locale/es');
break;
case 'fr-fr':
require('dayjs/locale/fr');
break;
case 'pt-br':
require('dayjs/locale/pt-br');
break;
case 'pt-pt':
require('dayjs/locale/pt');
break;
case 'jp-JP':
require('dayjs/locale/ja');
break;
case 'de-de':
require('dayjs/locale/de');
break;
case 'th-th':
require('dayjs/locale/th');
break;
case 'da-dk':
require('dayjs/locale/da');
break;
case 'nl-nl':
require('dayjs/locale/nl');
break;
case 'hr-hr':
require('dayjs/locale/hr');
break;
case 'id-id':
require('dayjs/locale/id');
break;
default:
break;
}
dayjs.locale(locale.split('-')[0]);
locale = locale.replace('-', '_');
strings.setLanguage(locale);
} else {
strings.setLanguage('en');
}
}
}
})();
@ -158,6 +94,7 @@ strings = new Localization({
pt_pt: require('./pt_PT.js'),
es: require('./es.js'),
it: require('./it.js'),
el: require('./el.js'),
ua: require('./ua.js'),
jp_jp: require('./jp_JP.js'),
de_de: require('./de_DE.js'),
@ -165,10 +102,14 @@ strings = new Localization({
cs_cz: require('./cs_CZ.js'),
th_th: require('./th_TH.js'),
nl_nl: require('./nl_NL.js'),
fi_fi: require('./fi_FI.js'),
fr_fr: require('./fr_FR.js'),
hr_hr: require('./hr_HR.js'),
id_id: require('./id_ID.js'),
zh_cn: require('./zh_cn.js'),
sv_se: require('./sv_SE.js'),
nb_no: require('./nb_NO.js'),
tr_tr: require('./tr_TR.js'),
});
strings.saveLanguage = lang => AsyncStorage.setItem(AppStorage.LANG, lang);

8
loc/it.js

@ -16,7 +16,8 @@ module.exports = {
app_name: 'BlueWallet',
title: 'Portafogli',
header:
'Un portafoglio rappresenta la coppia fra un segreto (chiave privata) e un indirizzo' + 'che puoi condividere per ricevere Bitcoin.',
'Un portafoglio rappresenta la coppia fra un segreto (chiave privata) e un indirizzo' +
'che puoi condividere per ricevere Bitcoin.',
add: 'Aggiungi Portafoglio',
create_a_wallet: 'Crea un portafoglio',
create_a_wallet1: 'È gratuito e puoi crearne',
@ -24,6 +25,9 @@ module.exports = {
latest_transaction: 'Transazioni recenti',
empty_txs1: 'Le tue transazioni appariranno qui,',
empty_txs2: 'Nessuna transazione',
empty_txs1_lightning:
'Lightning wallet should be used for your daily transactions. Fees are unfairly cheap and speed is blazing fast.',
empty_txs2_lightning: '\nTo start using it tap on "manage funds" and topup your balance.',
tap_here_to_buy: 'Clicca qui per comprare Bitcoin',
},
reorder: {
@ -33,7 +37,7 @@ module.exports = {
title: 'Aggiungi Portafoglio',
description:
'Puoi scansionare il Backup di un Paper-Wallet (in WIF - Wallet Import Format), o creare un nuovo portafoglio. I portafogli Segwit sono supportati di default.',
scan: 'Scansiona',
scan: 'Scan',
create: 'Crea',
label_new_segwit: 'Nuovo SegWit',
label_new_lightning: 'Nuovo Lightning',

2
loc/jp_JP.js

@ -23,6 +23,8 @@ module.exports = {
latest_transaction: '最新の取引',
empty_txs1: 'ここに取引が表示されます',
empty_txs2: '現在は何もありません',
empty_txs1_lightning: 'Lightning ウォレットを日常の取引にご利用ください。手数料は安く、送金はあっという間に完了します。',
empty_txs2_lightning: '\n利用を開始するには"資金の管理"をタップしてウォレットへ送金してください。',
tap_here_to_buy: 'Bitcoin を購入するにはここをタップ',
},
reorder: {

224
loc/nb_NO.js

@ -0,0 +1,224 @@
module.exports = {
_: {
storage_is_encrypted: 'Lagringen din er kryptert. Passord er nødvendig for å dekryptere det',
enter_password: 'Oppgi passord',
bad_password: 'Feil passord, prøv igjen',
never: 'aldri',
continue: 'Fortsett',
ok: 'OK',
},
wallets: {
select_wallet: 'Velg Lommebok',
options: 'innstillinger',
createBitcoinWallet:
'Du har ingen Bitcoin-lommebok. For å finansiere en Lightning-lommebok, må en Bitcoin-lommebok være opprettet eller importert. Vil du fortsette uansett?',
list: {
app_name: 'BlueWallet',
title: 'lommebøker',
header: 'En lommebok representerer en hemmelighet (privat nøkkel) og en adresse ' + ' du kan dele for å motta kryptovaluta.',
add: 'Legg til lommebok',
create_a_wallet: 'Lag en lommebok',
create_a_wallet1: 'Det er gratis, og du kan lage',
create_a_wallet2: 'så mange du vil',
latest_transaction: 'siste transaksjonen',
empty_txs1: 'Dine transaksjoner vil vises her,',
empty_txs2: 'ingen for øyeblikket',
empty_txs1_lightning:
'Lightning wallet should be used for your daily transactions. Fees are unfairly cheap and speed is blazing fast.',
empty_txs2_lightning: '\nTo start using it tap on "manage funds" and topup your balance.',
tap_here_to_buy: 'Trykk her for å kjøpe Bitcoin',
},
reorder: {
title: 'Omorganisere lommebøker',
},
add: {
title: 'legg til lommebok',
description:
'Du kan enten skanne en backup papirlommebok (i WIF - Wallet Import Format), eller opprett en ny lommebok. Segwit lommebøker støttes som standard.',
scan: 'Skann',
create: 'Lag',
label_new_segwit: 'Ny SegWit',
label_new_lightning: 'Ny Lightning',
wallet_name: 'navn',
wallet_type: 'type',
or: 'eller',
import_wallet: 'Importer lommeboken',
imported: 'Importert',
coming_soon: 'Kommer snart',
lightning: 'Lightning',
bitcoin: 'Bitcoin',
},
details: {
title: 'Lommebok',
address: 'Adresse',
type: 'Type',
label: 'Merkelapp',
destination: 'mål',
description: 'beskrivelse',
are_you_sure: 'Er du sikker?',
yes_delete: 'Ja, slett',
no_cancel: 'Nei, avbryt',
delete: 'Slett',
save: 'Lagre',
delete_this_wallet: 'Slett denne lommeboken',
export_backup: 'Eksporter / backup',
buy_bitcoin: 'Kjøp Bitcoin',
show_xpub: 'Vis lommebok XPUB',
},
export: {
title: 'lommebok eksport',
},
xpub: {
title: 'lommebok XPUB',
copiedToClipboard: 'Kopiert til utklippstavlen.',
},
import: {
title: 'importer',
explanation:
'Skriv her din mnemonic, private nøkkel, WIF, eller hva som helst annet. BlueWallet vil gjøre sitt beste for å gjette riktig format og importere lommeboken din',
imported: 'importert',
error: 'Kunne ikke importere. Vennligst vær sikker på at de oppgitte dataene er gyldige.',
success: 'Suksess',
do_import: 'Importer',
scan_qr: 'eller skann QR-kode i stedet?',
},
scanQrWif: {
go_back: 'Gå tilbake',
cancel: 'Avbryt',
decoding: 'Dekoder',
input_password: 'Input passord',
password_explain: 'Dette er BIP38 kryptert privat nøkkel',
bad_password: 'Feil passord',
wallet_already_exists: 'En slik lommebok eksisterer allerede',
bad_wif: 'Dårlig WIF',
imported_wif: 'Importert WIF',
with_address: ' med adresse',
imported_segwit: 'Importert SegWit',
imported_legacy: 'Importert Legacy',
imported_watchonly: 'Importert Watch-only',
},
},
transactions: {
list: {
tabBarLabel: 'Transaksjoner',
title: 'transaksjoner',
description: 'En liste over inngående eller utgående transaksjoner fra lommebokene dine',
conf: 'conf',
},
details: {
title: 'Transaksjon',
from: 'Input',
to: 'Produksjon',
copy: 'Kopiere',
transaction_details: 'Transaksjonsdetaljer',
show_in_block_explorer: 'Vis i blokkutforsker',
},
},
send: {
header: 'Sende',
details: {
title: 'opprett transaksjon',
amount_field_is_not_valid: 'Beløp feltet er ikke gyldig',
fee_field_is_not_valid: 'Avgiftsfeltet er ikke gyldig',
address_field_is_not_valid: 'Adressefeltet er ikke gyldig',
total_exceeds_balance: 'Sendingsbeløpet overstiger den tilgjengelige saldoen.',
create_tx_error: 'Det oppsto en feil under opprettelse av transaksjonen. Vennligst vær sikker på at adressen er gyldig.',
address: 'adresse',
amount_placeholder: 'mengde å sende (i BTC)',
fee_placeholder: 'pluss transaksjonsgebyr (i BTC)',
note_placeholder: 'notat til meg selv',
cancel: 'Avbryt',
scan: 'Skanne',
send: 'Sende',
create: 'Lag',
remaining_balance: 'Gjenværende balanse',
},
confirm: {
header: 'Bekrefte',
sendNow: 'Send nå',
},
success: {
done: 'Ferdig',
},
create: {
details: 'Detaljer',
title: 'opprett transaksjon',
error: 'Feil ved å opprette transaksjon. Ugyldig adresse eller send beløp?',
go_back: 'Gå tilbake',
this_is_hex: 'Dette er transaksjonsheks, signert og klar til å sendes til nettverket.',
to: 'Til',
amount: 'Beløp',
fee: 'Avgift',
tx_size: 'TX-størrelse',
satoshi_per_byte: 'Satoshi per byte',
memo: 'Memo',
broadcast: 'Kringkaste',
not_enough_fee: 'Ikke nok avgift. Øk avgiften',
},
},
receive: {
header: 'Motta',
details: {
title: 'Del denne adressen med betaleren',
share: 'dele',
copiedToClipboard: 'Kopiert til utklippstavlen.',
label: 'Beskrivelse',
create: 'Lag',
setAmount: 'Motta med beløp',
},
},
buyBitcoin: {
header: 'Kjøp Bitcoin',
tap_your_address: 'Trykk på adressen din for å kopiere den til utklippstavlen:',
copied: 'Kopiert til utklippstavlen!',
},
settings: {
header: 'innstillinger',
plausible_deniability: 'Troverdig denibilitet ...',
storage_not_encrypted: 'Lagring: ikke kryptert',
storage_encrypted: 'Lagring: kryptert',
password: 'Passord',
password_explain: 'Opprett passordet du vil bruke til å dekryptere lagringen',
retype_password: 'Skriv inn passordet på nytt',
passwords_do_not_match: 'passordene er ikke like',
encrypt_storage: 'Krypter lagring',
lightning_settings: 'Lightning innstillinger',
lightning_settings_explain:
'For å koble til din egen LND-node, vennligst installer LndHub' +
' og legg URLen her i innstillinger. La feltet være tomt for å bruke BlueWallets LNDHub (lndhub.io). Lommebøker opprettet etter lagring av endringer, vil koble til den angitte LNDHub.',
save: 'Lagre',
about: 'Om',
language: 'Språk',
currency: 'Valuta',
},
plausibledeniability: {
title: 'Troverdighet benektelse',
help:
'Under visse omstendigheter kan du bli tvunget til å avsløre en' +
'passord. For å holde mynten din trygg, kan BlueWallet opprette en annen' +
'kryptert lagring, med et annet passord. Under press,' +
'Du kan oppgi dette passordet til en tredjepart. Hvis inntastet i' +
'BlueWallet, det vil låse opp ny "falsk" lagring. Dette vil virke' +
'troverdig overfor en tredje part, men vil i hemmelighet beholde ' +
'hovedlageret trygt.',
help2: 'Ny lagring vil være fullt funksjonell, og du kan lagre en mindre sum der' + ' , slik at det ser mer troverdig ut.',
create_fake_storage: 'Lag falsk kryptert lagring',
go_back: 'Gå tilbake',
create_password: 'Lag et passord',
create_password_explanation: 'Passord for falsk lagring må ikke matche passord for hovedlager',
password_should_not_match: 'Passord for falsk lagring må ikke matche passord for hovedlager',
retype_password: 'Skriv inn passordet på nytt',
passwords_do_not_match: 'Passordene stemmer ikke overens, prøv igjen',
success: 'Vellykket',
},
lnd: {
title: 'administrere midler',
choose_source_wallet: 'Velg en kilde lommebok',
refill_lnd_balance: 'Refill Lightning lommebokbalanse',
refill: 'Fylle på',
withdraw: 'Ta ut',
expired: 'Utløpt',
placeholder: 'Faktura',
sameWalletAsInvoiceError: 'Du kan ikke betale en faktura med samme lommebok som brukes til å lage den.',
},
};

3
loc/nl_NL.js

@ -23,6 +23,9 @@ module.exports = {
latest_transaction: 'laatste transactie',
empty_txs1: 'Uw transacties verschijnen hier,',
empty_txs2: 'geen transacties op dit moment',
empty_txs1_lightning:
'Lightning wallet should be used for your daily transactions. Fees are unfairly cheap and speed is blazing fast.',
empty_txs2_lightning: '\nTo start using it tap on "manage funds" and topup your balance.',
tap_here_to_buy: 'Klik hier om Bitcoin te kopen',
},
reorder: {

3
loc/pt_BR.js

@ -24,6 +24,9 @@ module.exports = {
latest_transaction: 'última transação',
empty_txs1: 'Suas transações aparecerão aqui,',
empty_txs2: 'nenhuma no momento',
empty_txs1_lightning:
'Lightning wallet should be used for your daily transactions. Fees are unfairly cheap and speed is blazing fast.',
empty_txs2_lightning: '\nTo start using it tap on "manage funds" and topup your balance.',
tap_here_to_buy: 'Toque aqui para comprar Bitcoin',
},
reorder: {

3
loc/pt_PT.js

@ -23,6 +23,9 @@ module.exports = {
latest_transaction: 'últimas transacções',
empty_txs1: 'As suas transacções aparecerão aqui,',
empty_txs2: 'nenhuma de momento',
empty_txs1_lightning:
'Lightning wallet should be used for your daily transactions. Fees are unfairly cheap and speed is blazing fast.',
empty_txs2_lightning: '\nTo start using it tap on "manage funds" and topup your balance.',
tap_here_to_buy: 'Tap here to buy Bitcoin',
},
reorder: {

3
loc/ru.js

@ -23,6 +23,9 @@ module.exports = {
latest_transaction: 'Последняя транзакция',
empty_txs1: 'Список транзакций пока пуст',
empty_txs2: ' ',
empty_txs1_lightning:
'Lightning wallet should be used for your daily transactions. Fees are unfairly cheap and speed is blazing fast.',
empty_txs2_lightning: '\nTo start using it tap on "manage funds" and topup your balance.',
tap_here_to_buy: 'Купить Bitcoin',
},
reorder: {

224
loc/sv_SE.js

@ -0,0 +1,224 @@
module.exports = {
_: {
storage_is_encrypted: 'Lagringen är krypterad. Lösenords krävs för att dekryptera',
enter_password: 'Ange lösenord',
bad_password: 'Felaktigt lösenord, försök igen',
never: 'aldrig',
continue: 'Fortsätt',
ok: 'OK',
},
wallets: {
select_wallet: 'Välj plånbok',
options: 'inställningar',
createBitcoinWallet:
'Du har ännu ingen Bitcoinplånbok. För att kunna sätta in pengar i en Lightningplånbok behöver en Bitcoinplånbok skapas eller importeras. Vill du fortsätta ändå?',
list: {
app_name: 'BlueWallet',
title: 'plånböcker',
header: 'En plånbok representerar ett par av en privat nyckel samt en adress' + 'som du kan dela med andra för att ta emot coins',
add: 'Ny Plånbok',
create_a_wallet: 'Ny plånbok',
create_a_wallet1: 'Det är gratis och du kan skapa',
create_a_wallet2: 'hur många du vill',
latest_transaction: 'senaste transaktion',
empty_txs1: 'Dina transaktioner kommer att visas här',
empty_txs2: 'men än så länge finns inga!',
empty_txs1_lightning:
'Lightningplånboken ska användas för dagliga småtransaktioner. Avgifterna är minimala och transaktioner sker direkt.',
empty_txs2_lightning: '\nFör att komma igång klicka på "sätt in / ta ut" ovan och sätt in dina första bitcoin.',
tap_here_to_buy: 'Tryck här för att köpa bitcoin',
},
reorder: {
title: 'Sortera plånböcker',
},
add: {
title: 'ny plånbok',
description:
'Du kan antingen skanna in en backup (i WIF - Wallet Import Format) eller skapa en ny plånbok. Segwit-plånböcker stöds som standard.',
scan: 'Skanna',
create: 'Skapa',
label_new_segwit: 'Ny SegWit',
label_new_lightning: 'Ny Lightning',
wallet_name: 'namn',
wallet_type: 'typ',
or: 'eller',
import_wallet: 'Importera plånbok',
imported: 'Importerad',
coming_soon: 'Kommer snart',
lightning: 'Lightning',
bitcoin: 'Bitcoin',
},
details: {
title: 'Plånbok',
address: 'Adress',
type: 'Typ',
label: 'Etikett',
description: 'beskrivning',
are_you_sure: 'Är du säker?',
yes_delete: 'Ja, ta bort',
no_cancel: 'Nej, avbryt',
delete: 'Radera',
save: 'Spara',
delete_this_wallet: 'Radera denna plånbok',
export_backup: 'Exportera / ta backup',
buy_bitcoin: 'Köp bitcoin',
show_xpub: 'Visa plånbokens XPUB',
},
export: {
title: 'exportera plånbok',
},
xpub: {
title: 'plånbokens XPUB',
copiedToClipboard: 'Kopierad till urklipp',
},
import: {
title: 'import',
explanation:
'Skriv in dina ord, din privata nyckel, WIF, eller vad du kan tänkas ha. BlueWallet kommer att göra sitt bästa för att gissa formatet och importera plånboken',
imported: 'Importerad',
error: 'Import misslyckades. Kontrollera att informationen du matade in är korrekt.',
success: 'Import lyckad!',
do_import: 'Importera',
scan_qr: 'eller skanna QR-kod istället?',
},
scanQrWif: {
go_back: 'Tillbaka',
cancel: 'Avbryt',
decoding: 'Avkodar',
input_password: 'Ange lösenord',
password_explain: 'Detta är BIP38-krypterad privat nyckel',
bad_password: 'Felaktigt lösenord',
wallet_already_exists: 'En sådan plånbok existerar redan',
bad_wif: 'Felaktigt WIF',
imported_wif: 'Importerade WIF ',
with_address: ' med adress ',
imported_segwit: 'Importerade SegWit',
imported_legacy: 'Importerade Legacy',
imported_watchonly: 'Importerade Watch-only',
},
},
transactions: {
list: {
tabBarLabel: 'Transaktioner',
title: 'transaktioner',
description: 'Lista över dina plånböckers inkommande och utgående transaktioner',
conf: 'konf',
},
details: {
title: 'Transaktion',
from: 'Input',
to: 'Output',
copy: 'Kopiera',
transaction_details: 'Transactionsdetaljer',
show_in_block_explorer: 'Visa i block explorer',
},
},
send: {
header: 'Skicka',
details: {
title: 'skapa transaktion',
amount_field_is_not_valid: 'Angivet belopp är inte giltigt',
fee_field_is_not_valid: 'Angiven avgift är inte giltig',
address_field_is_not_valid: 'Angiven adress är inte giltig',
total_exceeds_balance: 'Beloppet överstiger plånbokens tillgängliga belopp',
create_tx_error: 'Något gick fel när transaktionen skulle skapas. Kontrollera att adressen är giltig.',
address: 'adress',
amount_placeholder: 'belopp att skicka (i BTC)',
fee_placeholder: 'plus avgift (i BTC)',
note_placeholder: 'egen notering',
cancel: 'Avbryt',
scan: 'Skanna',
send: 'Skicka',
create: 'Skapa',
remaining_balance: 'Återstående saldo',
},
confirm: {
header: 'Bekräfta',
sendNow: 'Skicka nu',
},
success: {
done: 'Klart!',
},
create: {
details: 'Detaljer',
title: 'skapa transaktion',
error: 'Något gick fel när transaktionen skulle skapas. Felaktig adress eller belopp?',
go_back: 'Tillbaka',
this_is_hex: 'Detta är transaktionens hex, signerad och redo att skickas ut på nätverket.',
to: 'Till',
amount: 'Belopp',
fee: 'Avgift',
tx_size: 'Transaktionsstorlek',
satoshi_per_byte: 'Satoshi per byte',
memo: 'Memo',
broadcast: 'Publicera',
not_enough_fee: 'För låg avgift. Öka avgiften',
},
},
receive: {
header: 'Ta emot',
details: {
title: 'Dela den här adressen med betalaren',
share: 'dela',
copiedToClipboard: 'Kopierad till urklipp.',
label: 'Beskrivning',
create: 'Skapa',
setAmount: 'Ta emot med belopp',
},
},
buyBitcoin: {
header: 'Köp bitcoin',
tap_your_address: 'Tryck på adressen för att kopiera den till urklipp:',
copied: 'Kopierad till urklipp!',
},
settings: {
header: 'inställningar',
plausible_deniability: 'Trovärdigt förnekande...',
storage_not_encrypted: 'Lagring: ej krypterad',
storage_encrypted: 'Lagring: krypterad',
password: 'Lösenord',
password_explain: 'Skapa ett lösenord som du kommer att använda vid dekryptering',
retype_password: 'Ange lösenord igen',
passwords_do_not_match: 'Lösenorden är olika!',
encrypt_storage: 'Kryptera lagring',
lightning_settings: 'Lightning Network',
lightning_settings_explain:
'För att ansluta till din egen LND-nod, installera LndHub' +
" och mata in dess URL nedan. Lämna blankt för att använda BlueWallet's LNDHub (lndhub.io). Plånböcker skapade efter att inställningarna sparats kommer att använda den angivna LNDHub:en",
save: 'Spara',
about: 'Om',
language: 'Språk',
currency: 'Valuta',
},
plausibledeniability: {
title: 'Trovärdigt förnekande',
help:
'Under vissa omständigheter kan du bli tvingad att uppge ditt ' +
'lösenord. För att se till att dina pengar är säkra kan BlueWallet skapa ytterligare en ' +
'krypterad lagringsyta, med ett annat lösenord. Vid tvång ' +
'kan du uppge detta alternativa lösenord. När det matas in i ' +
"BlueWallet så kommer det att låsa upp din 'fejkade' lagringsyta. Det kommer att se " +
'ut precis som vanligt men i själva verket är dina pengar i säkert förvar på din ' +
'primära lagringsyta.',
help2:
'Den alternativa lagringsytan kommer att vara fullt fungerade och du kan eventuellt spara en mindre summa där för att den ska verka mer trovärdig.',
create_fake_storage: 'Skapa fejkad lagringsyta',
go_back: 'Tillbaka',
create_password: 'Skapa ett lösenord',
create_password_explanation: 'Lösenordet för den fejkade lagringsytan får inte vara samma som ditt huvudlösenord',
password_should_not_match: 'Lösenordet för den fejkade lagringsytan får inte vara samma som ditt huvudlösenord',
retype_password: 'Ange lösenord igen',
passwords_do_not_match: 'Lösenorden är olika!',
success: 'Fejkad lagringsyta skapad!',
},
lnd: {
title: 'sätt in / ta ut',
choose_source_wallet: 'Choose a source wallet',
refill_lnd_balance: 'Refill Lightning wallet balance',
refill: 'Sätt in',
withdraw: 'Ta ut',
expired: 'Förfallen',
placeholder: 'Faktura',
sameWalletAsInvoiceError: 'Du kan inte betala en faktura från samma plånbok som användes för att skapa den.',
},
};

3
loc/th_TH.js

@ -23,6 +23,9 @@ module.exports = {
latest_transaction: 'ธุรกรรมล่าสุด',
empty_txs1: 'ธุรกรรมจะปรากฏที่นี่,',
empty_txs2: 'ไม่มี ณ ขณะนี้',
empty_txs1_lightning:
'Lightning wallet should be used for your daily transactions. Fees are unfairly cheap and speed is blazing fast.',
empty_txs2_lightning: '\nTo start using it tap on "manage funds" and topup your balance.',
tap_here_to_buy: 'กดที่นี่เพื่อซื้อบิตคอยน์',
},
reorder: {

224
loc/tr_TR.js

@ -0,0 +1,224 @@
module.exports = {
_: {
storage_is_encrypted: 'Your storage is encrypted. Password is required to decrypt it',
enter_password: 'Şifre gir',
bad_password: 'Hatalı şifre, tekrar deneyin',
never: 'asla',
continue: 'Devam et',
ok: 'Tamam',
},
wallets: {
select_wallet: 'Cüzdan Seç',
options: 'seçenekler',
createBitcoinWallet:
'Şu anda Bitcoin cüzdanınız yok. Lightning cüzdanına yükleme yapmak için Bitcoin cüzdanı oluşturmak veya içeri yüklemek gerekir. Yine de devam etmek istiyor musunuz?',
list: {
app_name: 'BlueWallet',
title: 'cüzdanlar',
header: 'Cüzdan biri gizli, biri halka açık olan bir çift anahtar ve koin almak için' + ' paylaşabileceğiniz bir adrestir.',
add: 'Cüzdan Ekle',
create_a_wallet: 'Cüzdan oluştur',
create_a_wallet1: 'Oluşturması bedava ve',
create_a_wallet2: 'istediğiniz kadar oluşturabilirsiniz',
latest_transaction: 'en son işlem',
empty_txs1: 'İşlemleriniz burada görünür,',
empty_txs2: 'şu anda hiç yok',
empty_txs1_lightning:
'Lightning wallet should be used for your daily transactions. Fees are unfairly cheap and speed is blazing fast.',
empty_txs2_lightning: '\nTo start using it tap on "manage funds" and topup your balance.',
tap_here_to_buy: 'Bitcoin almak için buraya dokunun',
},
reorder: {
title: 'Cüzdanları Sırala',
},
add: {
title: 'cüzdan ekle',
description:
'Kağıt cüzdan (WIF - Wallet Import Format olarak) tarayarak içeri yükleyebilirsiniz veya cüzdan oluşturabilirsiniz. Segwit cüzdanlar standard olarak desteklenir.',
scan: 'Tara',
create: 'Oluştur',
label_new_segwit: 'Yeni SegWit',
label_new_lightning: 'Yeni Lightning',
wallet_name: 'isim',
wallet_type: 'tip',
or: 'veya',
import_wallet: 'Cüzdan İçeri Yükle',
imported: 'Yüklendi',
coming_soon: 'Yakında',
lightning: 'Lightning',
bitcoin: 'Bitcoin',
},
details: {
title: 'Cüzdan',
address: 'Adres',
type: 'Tip',
label: 'Etiket',
destination: 'hedef',
description: 'tanım',
are_you_sure: 'Emin misiniz?',
yes_delete: 'Evet, sil',
no_cancel: 'Hayır, vazgeç',
delete: 'Sil',
save: 'Kaydet',
delete_this_wallet: 'Bu cüzdanı sil',
export_backup: 'Dışa yükle / yedekle',
buy_bitcoin: 'Bitcoin satın al',
show_xpub: 'Cüzdan XPUB göster',
},
export: {
title: 'cüzdan yedekle',
},
xpub: {
title: 'cüzdan XPUB',
copiedToClipboard: 'Panoya kopyalandı',
},
import: {
title: 'içeri yükle',
explanation:
'Buraya cüzdan yedek cümlenizi, gizli anahtarınızı, WIF veya diğer bilginizi yazın. BlueWallet elinden gelen en iyi tahmini yaparak cüzdanınızı içeri aktarmaya çalışacak.',
imported: 'İçeri aktarıldı',
error: 'İçeri aktarma başarısız oldu. Lütfen girilen bilginin doğru olduğundan emin olun.',
success: 'Başarılı',
do_import: 'İçe Aktar',
scan_qr: 'veya QR kod tara?',
},
scanQrWif: {
go_back: 'Geri Git',
cancel: 'Vazgeç',
decoding: 'Deşifre ediliyor',
input_password: 'Şifre gir',
password_explain: 'Bu BIP38 şifreli gizli anahtar',
bad_password: 'Hatalı şifre',
wallet_already_exists: 'Böyle bir cüzdan zaten var',
bad_wif: 'Hatalı WIF',
imported_wif: 'İçeri aktarılmış WIF ',
with_address: ' adres ile ',
imported_segwit: 'İçeri Aktarılmış SegWit',
imported_legacy: 'İçeri Aktarılmış Eski Tip',
imported_watchonly: 'İçeri Aktarılmış Sadece-izleme',
},
},
transactions: {
list: {
tabBarLabel: 'İşlemler',
title: 'işlemler',
description: 'Cüzdanlarınıza gelen ve giden işlemlerin bir listesi',
conf: 'onay',
},
details: {
title: 'İşlem',
from: 'Girdi',
to: 'Çıktı',
copy: 'Kopya',
transaction_details: 'İşlem detayları',
show_in_block_explorer: 'Blok gezgininde göster',
},
},
send: {
header: 'Gönder',
details: {
title: 'işlem oluştur',
amount_field_is_not_valid: 'Miktar alanı geçerli değil',
fee_field_is_not_valid: 'Ücret alanı geçerli değil',
address_field_is_not_valid: 'Adres alanı geçerli değil',
total_exceeds_balance: 'Gönderme miktarı mevcut bakiyeyi aşıyor.',
create_tx_error: 'İşlem oluşturulurken bir hata oluştu. Lütfen adresin geçerli olduğundan emin olun.',
address: 'adres',
amount_placeholder: 'gönderilecek miktar (BTC cinsinden)',
fee_placeholder: 'artı işlem ücreti (BTC cinsinden)',
note_placeholder: 'kendime not',
cancel: 'Vazgeç',
scan: 'Tara',
send: 'Gönder',
create: 'Oluştur',
remaining_balance: 'Kalan bakiye',
},
confirm: {
header: 'Onayla',
sendNow: 'Şimdi gönder',
},
success: {
done: 'Tamam',
},
create: {
details: 'Detaylar',
title: 'işlem oluştur',
error: 'İşlem yaratma hatası. Geçersiz adres veya gönderim miktarı?',
go_back: 'Geri dön',
this_is_hex: 'Bu işlemin hexi, imzalanmış ve ağa yayınlanmaya hazır.',
to: 'Kime',
amount: 'Miktar',
fee: 'Ücret',
tx_size: 'TX boyutu',
satoshi_per_byte: 'Bayt başına Satoshi',
memo: 'Not',
broadcast: 'Yayınla',
not_enough_fee: 'Yetersiz ücret. Ücreti arttırın',
},
},
receive: {
header: 'Al',
details: {
title: 'Bu adresi ödeyenle paylaş',
share: 'paylaş',
copiedToClipboard: 'Panoya kopyalandı.',
label: 'Açıklama',
create: 'Oluştur',
setAmount: 'Miktar ile al',
},
},
buyBitcoin: {
header: 'Bitcoin Satın al',
tap_your_address: 'Panoya kopyalamak için adresinize dokunun:',
copied: 'Panoya kopyalandı!',
},
settings: {
header: 'ayarlar',
plausible_deniability: 'Makul ret...',
storage_not_encrypted: 'Depolama: şifreli değil',
storage_encrypted: 'Depolama: şifreli',
password: 'Şifre',
password_explain: 'Depolamanın şifresini çözmek için kullanacağınız şifreyi oluşturun',
retype_password: 'Şifrenizi yeniden girin',
passwords_do_not_match: 'Şifreler eşleşmedi',
encrypt_storage: 'Depolamayı şifrele',
lightning_settings: 'Lightning Ayarları',
lightning_settings_explain:
'Kendi LND düğümünüze bağlanmak için lütfen LndHubı yükleyin.' +
" ve URL’sini buraya, ayarlara yazın. BlueWallet'in LNDHub (lndhub.io) programını kullanmak için boş bırakın. Değişiklikleri kaydettikten sonra oluşturulan cüzdanlar belirtilen LNDHub'a bağlanacaktır.",
save: 'Kaydet',
about: 'Hakkında',
language: 'Dil',
currency: 'Para Birimi',
},
plausibledeniability: {
title: 'Makul Ret',
help:
'Bazı koşullar altında, şifrenizi açıklamanız gerekebilir. ' +
'Paralarınızı güvende tutmak için, BlueWallet başka bir şifre ile ' +
'şifreli depolama alanı yaratabilir. Baskı altında, ' +
'Bu şifreyi 3. bir tarafa söyleyebilirsiniz. Girilirse ' +
"BlueWallet, yeni 'sahte' bir depolamanın kilidini açacaktır. Bu 3. şahıslara " +
'normal görünecektir, ancak paraların olduğu ana depolama alanınızı gizlice saklamaya ' +
'devam edecektir.',
help2: 'Yeni depolama alanı tamamen işlevsel olacak ve ufak ' + 'bir miktar tutarsanız daha inanılır görünecektir.',
create_fake_storage: 'Sahte şifreli depolama oluşturun',
go_back: 'Geri Dön',
create_password: 'Şifre oluştur',
create_password_explanation: 'Sahte depolama şifreniz, ana depolama şifrenizle aynı olmamalıdır.',
password_should_not_match: 'Sahte depolama şifreniz, ana depolama şifrenizle aynı olmamalıdır',
retype_password: 'Şifrenizi yeniden yazın',
passwords_do_not_match: 'Şifreler eşleşmedi, tekrar dene',
success: 'Başarılı',
},
lnd: {
title: 'paraları yönet',
choose_source_wallet: 'Kaynak bir cüzdan seçin',
refill_lnd_balance: 'Lightning cüzdana bakiye yükle',
refill: 'Yükle',
withdraw: 'Çek',
expired: 'Süresi doldu',
placeholder: 'Fatura',
sameWalletAsInvoiceError: 'Bir faturayı, oluştururken kullandığınız cüzdan ile ödeyemezsiniz.',
},
};

3
loc/ua.js

@ -23,6 +23,9 @@ module.exports = {
latest_transaction: 'остання транзакція',
empty_txs1: "Транзакціі з'являться тут,",
empty_txs2: 'поки що жодноі',
empty_txs1_lightning:
'Lightning wallet should be used for your daily transactions. Fees are unfairly cheap and speed is blazing fast.',
empty_txs2_lightning: '\nTo start using it tap on "manage funds" and topup your balance.',
tap_here_to_buy: 'Tap here to buy Bitcoin',
},
reorder: {

3
loc/zh_cn.js

@ -22,6 +22,9 @@ module.exports = {
latest_transaction: '最近的转账',
empty_txs1: '你的转账信息将展示在这里',
empty_txs2: '当前无信息',
empty_txs1_lightning:
'Lightning wallet should be used for your daily transactions. Fees are unfairly cheap and speed is blazing fast.',
empty_txs2_lightning: '\nTo start using it tap on "manage funds" and topup your balance.',
tap_here_to_buy: '点击购买比特币',
},
reorder: {

6941
package-lock.json

File diff suppressed because it is too large

66
package.json

@ -1,19 +1,19 @@
{
"name": "BlueWallet",
"version": "3.8.3",
"version": "3.9.4",
"devDependencies": {
"babel-eslint": "^10.0.1",
"babel-jest": "^24.0.0",
"eslint": "^5.13.0",
"babel-jest": "^24.7.1",
"eslint": "^5.16.0",
"eslint-plugin-babel": "^5.3.0",
"eslint-plugin-import": "^2.15.0",
"eslint-plugin-node": "^8.0.1",
"eslint-plugin-promise": "^4.0.1",
"eslint-plugin-promise": "^4.1.1",
"eslint-plugin-react": "^7.12.3",
"jest": "^24.0.0",
"metro-react-native-babel-preset": "^0.51.1",
"jest": "^24.7.1",
"metro-react-native-babel-preset": "^0.53.1",
"prettier-eslint-cli": "^4.7.1",
"react-test-renderer": "^16.7.0",
"react-test-renderer": "^16.8.6",
"rn-nodeify": "github:tradle/rn-nodeify"
},
"scripts": {
@ -36,45 +36,46 @@
}
},
"dependencies": {
"@babel/preset-env": "7.3.1",
"@babel/preset-env": "7.4.3",
"@react-native-community/slider": "1.0.4",
"@remobile/react-native-qrcode-local-image": "1.0.4",
"bignumber.js": "8.0.2",
"bignumber.js": "8.1.1",
"bip21": "2.0.2",
"bip39": "2.5.0",
"bitcoinjs-lib": "3.3.2",
"buffer": "5.2.1",
"buffer-reverse": "1.0.1",
"crypto-js": "3.1.9-1",
"dayjs": "1.8.6",
"dayjs": "1.8.12",
"electrum-client": "git+https://github.com/Overtorment/node-electrum-client.git",
"eslint-config-prettier": "4.0.0",
"eslint-config-prettier": "4.1.0",
"eslint-config-standard": "12.0.0",
"eslint-config-standard-react": "7.0.2",
"eslint-plugin-prettier": "3.0.1",
"eslint-plugin-standard": "4.0.0",
"frisbee": "2.0.5",
"frisbee": "2.0.8",
"intl": "1.2.5",
"mocha": "5.2.0",
"node-libs-react-native": "1.0.1",
"node-libs-react-native": "1.0.3",
"path-browserify": "1.0.0",
"prettier": "1.16.3",
"prettier": "1.16.4",
"process": "0.11.10",
"prop-types": "15.6.2",
"react": "16.7.0",
"react-localization": "1.0.10",
"react-native": "0.58.6",
"react-native-camera": "1.10.0",
"react-native-device-info": "^0.26.4",
"prop-types": "15.7.2",
"react": "16.8.6",
"react-localization": "1.0.13",
"react-native": "0.59.3",
"react-native-camera": "2.2.1",
"react-native-device-info": "1.4.2",
"react-native-elements": "0.19.0",
"react-native-flexi-radio-button": "0.2.2",
"react-native-fs": "2.13.3",
"react-native-gesture-handler": "1.0.15",
"react-native-google-analytics-bridge": "7.0.0",
"react-native-haptic-feedback": "^1.6.0",
"react-native-image-picker": "0.28.0",
"react-native-gesture-handler": "1.1.0",
"react-native-google-analytics-bridge": "7.1.0",
"react-native-haptic-feedback": "1.6.0",
"react-native-image-picker": "0.28.1",
"react-native-level-fs": "3.0.1",
"react-native-linear-gradient": "2.5.3",
"react-native-modal": "7.0.2",
"react-native-linear-gradient": "2.5.4",
"react-native-modal": "9.0.0",
"react-native-obscure": "1.2.1",
"react-native-permissions": "1.1.1",
"react-native-popup-menu-android": "1.0.3",
@ -83,18 +84,17 @@
"react-native-qrcode-svg": "5.1.2",
"react-native-randombytes": "3.5.2",
"react-native-rate": "1.1.6",
"react-native-sentry": "0.41.1",
"react-native-sentry": "0.42.0",
"react-native-snap-carousel": "3.7.5",
"react-native-sortable-list": "0.0.22",
"react-native-svg": "9.1.1",
"react-native-svg": "9.3.7",
"react-native-tcp": "3.3.0",
"react-native-tooltip": "git+https://github.com/marcosrdz/react-native-tooltip.git",
"react-native-vector-icons": "6.2.0",
"react-native-webview": "4.1.0",
"react-native-wkwebview-reborn": "2.0.0",
"react-navigation": "^3.3.2",
"react-native-vector-icons": "6.4.2",
"react-native-webview": "5.6.2",
"react-navigation": "3.6.1",
"react-test-render": "1.1.1",
"readable-stream": "3.1.1",
"readable-stream": "3.3.0",
"secure-random": "1.1.1",
"stream-browserify": "2.0.2",
"util": "0.11.1",

305
screen/lnd/browser.js

@ -1,7 +1,6 @@
import React, { Component } from 'react';
import { TouchableOpacity, ActivityIndicator, TextInput, Keyboard, BackHandler, View, Platform, Alert } from 'react-native';
import { TouchableOpacity, ActivityIndicator, TextInput, Keyboard, BackHandler, View, Alert } from 'react-native';
import { WebView } from 'react-native-webview';
import WKWebView from 'react-native-wkwebview-reborn';
import { BlueNavigationStyle, SafeBlueArea } from '../../BlueComponents';
import Ionicons from 'react-native-vector-icons/Ionicons';
import PropTypes from 'prop-types';
@ -16,19 +15,19 @@ let bluewalletResponses = {};
// eslint-disable-next-line
var webln = {
enable: function() {
window.postMessage(JSON.stringify({ enable: true }));
window.ReactNativeWebView.postMessage(JSON.stringify({ enable: true }));
return new Promise(function(resolve, reject) {
resolve(true);
});
},
getInfo: function() {
window.postMessage('getInfo');
window.ReactNativeWebView.postMessage('getInfo');
return new Promise(function(resolve, reject) {
reject(new Error('not implemented'));
});
},
sendPayment: function(paymentRequest) {
window.postMessage(JSON.stringify({ sendPayment: paymentRequest }));
window.ReactNativeWebView.postMessage(JSON.stringify({ sendPayment: paymentRequest }));
return new Promise(function(resolve, reject) {
/* nop. intentionally, forever hang promise.
lapp page usually asynchroniously checks payment itself, via ajax,
@ -38,7 +37,7 @@ var webln = {
},
makeInvoice: function(RequestInvoiceArgs) {
var id = Math.random();
window.postMessage(JSON.stringify({ makeInvoice: RequestInvoiceArgs, id: id }));
window.ReactNativeWebView.postMessage(JSON.stringify({ makeInvoice: RequestInvoiceArgs, id: id }));
return new Promise(function(resolve, reject) {
var interval = setInterval(function() {
if (bluewalletResponses[id]) {
@ -49,13 +48,13 @@ var webln = {
});
},
signMessage: function() {
window.postMessage('signMessage');
window.ReactNativeWebView.postMessage('signMessage');
return new Promise(function(resolve, reject) {
reject(new Error('not implemented'));
});
},
verifyMessage: function() {
window.postMessage('verifyMessage');
window.ReactNativeWebView.postMessage('verifyMessage');
return new Promise(function(resolve, reject) {
reject(new Error('not implemented'));
});
@ -92,19 +91,19 @@ bluewalletResponses = {};
webln = {
enable : function () {
window.postMessage(JSON.stringify({'enable': true}));
window.ReactNativeWebView.postMessage(JSON.stringify({'enable': true}));
return new Promise(function(resolve, reject){
resolve(true);
})
},
getInfo : function () {
window.postMessage('getInfo');
window.ReactNativeWebView.postMessage('getInfo');
return new Promise(function(resolve, reject){
reject('not implemented');
})
},
sendPayment: function(paymentRequest) {
window.postMessage(JSON.stringify({ sendPayment: paymentRequest }));
window.ReactNativeWebView.postMessage(JSON.stringify({ sendPayment: paymentRequest }));
return new Promise(function(resolve, reject) {
/* nop. intentionally, forever hang promise.
lapp page usually asynchroniously checks payment itself, via ajax,
@ -114,7 +113,7 @@ webln = {
},
makeInvoice: function (RequestInvoiceArgs) {
var id = Math.random();
window.postMessage(JSON.stringify({makeInvoice: RequestInvoiceArgs, id: id}));
window.ReactNativeWebView.postMessage(JSON.stringify({makeInvoice: RequestInvoiceArgs, id: id}));
return new Promise(function(resolve, reject) {
var interval = setInterval(function () {
if (bluewalletResponses[id]) {
@ -125,13 +124,13 @@ webln = {
});
},
signMessage: function () {
window.postMessage('signMessage');
window.ReactNativeWebView.postMessage('signMessage');
return new Promise(function(resolve, reject){
reject('not implemented');
})
},
verifyMessage: function () {
window.postMessage('verifyMessage');
window.ReactNativeWebView.postMessage('verifyMessage');
return new Promise(function(resolve, reject){
reject('not implemented');
})
@ -143,10 +142,10 @@ webln = {
/* listening to events that might come from RN: */
document.addEventListener("message", function(event) {
window.postMessage("inside webview, received post message: " + event.data);
window.ReactNativeWebView.postMessage("inside webview, received post message: " + event.detail);
var json;
try {
json = JSON.parse(event.data);
json = JSON.parse(event.detail);
} catch (_) {}
if (json && json.bluewalletResponse) {
@ -161,14 +160,14 @@ document.addEventListener("message", function(event) {
function tryToPay(invoice) {
window.postMessage(JSON.stringify({sendPayment:invoice}));
window.ReactNativeWebView.postMessage(JSON.stringify({sendPayment:invoice}));
}
/* for non-webln compatible pages we do it oldschool,
searching for all bolt11 manually */
setInterval(function() {
window.postMessage('interval');
window.ReactNativeWebView.postMessage('interval');
var searchText = "lnbc";
@ -243,193 +242,107 @@ export default class Browser extends Component {
};
renderWebView = () => {
if (Platform.OS === 'android') {
return (
<WebView
onNavigationStateChange={this._onNavigationStateChange}
ref={ref => (this.webview = ref)}
source={{ uri: this.state.url }}
onMessage={e => {
// this is a handler which receives messages sent from within the browser
console.log('---- message from the bus:', e.nativeEvent.data);
let json = false;
try {
json = JSON.parse(e.nativeEvent.data);
} catch (_) {}
// message from browser has ln invoice
if (json && json.sendPayment) {
// checking that already asked about this invoice:
if (processedInvoices[json.sendPayment]) {
return;
} else {
// checking that we do not trigger alert too often:
if (+new Date() - lastTimeTriedToPay < 3000) {
return;
}
lastTimeTriedToPay = +new Date();
//
processedInvoices[json.sendPayment] = 1;
}
Alert.alert(
'Page',
'This page asks for permission to pay an invoice',
[
{ text: 'Cancel', onPress: () => console.log('Cancel Pressed'), style: 'cancel' },
{
text: 'Pay',
onPress: () => {
console.log('OK Pressed');
this.props.navigation.navigate({
routeName: 'ScanLndInvoice',
params: {
uri: json.sendPayment,
fromSecret: this.state.fromSecret,
},
});
},
},
],
{ cancelable: false },
);
}
if (json && json.makeInvoice) {
let amount = Math.max(+json.makeInvoice.minimumAmount, +json.makeInvoice.maximumAmount, +json.makeInvoice.defaultAmount);
Alert.alert(
'Page',
'This page wants to pay you ' + amount + ' sats (' + json.makeInvoice.defaultMemo + ')',
[
{ text: 'No thanks', onPress: () => console.log('Cancel Pressed'), style: 'cancel' },
{
text: 'Accept',
onPress: async () => {
/** @type {LightningCustodianWallet} */
const fromWallet = this.state.fromWallet;
const payreq = await fromWallet.addInvoice(amount, json.makeInvoice.defaultMemo || ' ');
this.webview.postMessage(JSON.stringify({ bluewalletResponse: { paymentRequest: payreq }, id: json.id }));
},
},
],
{ cancelable: false },
);
}
if (json && json.enable) {
console.log('webln enabled');
this.setState({ weblnEnabled: true });
}
}}
onLoadStart={e => {
alreadyInjected = false;
console.log('load start');
this.setState({ pageIsLoading: true, weblnEnabled: false });
}}
onLoadEnd={e => {
console.log('load end');
this.setState({ url: e.nativeEvent.url, pageIsLoading: false });
}}
onLoadProgress={e => {
console.log('progress:', e.nativeEvent.progress);
if (!alreadyInjected && e.nativeEvent.progress > 0.5) {
this.webview.injectJavaScript(injectedParadise);
alreadyInjected = true;
console.log('injected');
}
}}
/>
);
} else if (Platform.OS === 'ios') {
return (
<WKWebView
ref={ref => (this.webview = ref)}
source={{ uri: this.state.url }}
injectJavaScript={injectedParadise}
onNavigationStateChange={this._onNavigationStateChange}
onMessage={e => {
// this is a handler which receives messages sent from within the browser
console.log('---- message from the bus:', e.nativeEvent.data);
let json = false;
try {
json = JSON.parse(e.nativeEvent.data);
} catch (_) {}
// message from browser has ln invoice
if (json && json.sendPayment) {
return (
<WebView
onNavigationStateChange={this._onNavigationStateChange}
ref={ref => (this.webview = ref)}
source={{ uri: this.state.url }}
onMessage={e => {
// this is a handler which receives messages sent from within the browser
console.log('---- message from the bus:', e.nativeEvent.data);
let json = false;
try {
json = JSON.parse(e.nativeEvent.data);
} catch (_) {}
// message from browser has ln invoice
if (json && json.sendPayment) {
// checking that already asked about this invoice:
if (processedInvoices[json.sendPayment]) {
return;
} else {
// checking that we do not trigger alert too often:
if (+new Date() - lastTimeTriedToPay < 3000) {
return;
}
lastTimeTriedToPay = +new Date();
// checking that already asked about this invoice:
if (processedInvoices[json.sendPayment]) {
return;
} else {
processedInvoices[json.sendPayment] = 1;
}
Alert.alert(
'Page',
'This page asks for permission to pay an invoice',
[
{ text: 'Cancel', onPress: () => console.log('Cancel Pressed'), style: 'cancel' },
{
text: 'Pay',
onPress: () => {
console.log('OK Pressed');
this.props.navigation.navigate({
routeName: 'ScanLndInvoice',
params: {
uri: json.sendPayment,
fromSecret: this.state.fromSecret,
},
});
},
},
],
{ cancelable: false },
);
//
processedInvoices[json.sendPayment] = 1;
}
if (json && json.makeInvoice) {
let amount = Math.max(+json.makeInvoice.minimumAmount, +json.makeInvoice.maximumAmount, +json.makeInvoice.defaultAmount);
Alert.alert(
'Page',
'This page wants to pay you ' + amount + ' sats (' + json.makeInvoice.defaultMemo + ')',
[
{ text: 'No thanks', onPress: () => console.log('Cancel Pressed'), style: 'cancel' },
{
text: 'Accept',
onPress: async () => {
/** @type {LightningCustodianWallet} */
const fromWallet = this.state.fromWallet;
const payreq = await fromWallet.addInvoice(amount, json.makeInvoice.defaultMemo || ' ');
this.webview.postMessage(JSON.stringify({ bluewalletResponse: { paymentRequest: payreq }, id: json.id }));
},
Alert.alert(
'Page',
'This page asks for permission to pay an invoice',
[
{ text: 'Cancel', onPress: () => console.log('Cancel Pressed'), style: 'cancel' },
{
text: 'Pay',
onPress: () => {
console.log('OK Pressed');
this.props.navigation.navigate({
routeName: 'ScanLndInvoice',
params: {
uri: json.sendPayment,
fromSecret: this.state.fromSecret,
},
});
},
],
{ cancelable: false },
);
}
if (json && json.enable) {
console.log('webln enabled');
this.setState({ weblnEnabled: true });
}
}}
onLoadStart={e => {
alreadyInjected = false;
console.log('load start');
this.setState({ pageIsLoading: true, weblnEnabled: false });
}}
onLoadEnd={e => {
console.log('load end');
this.setState({ url: e.nativeEvent.url, pageIsLoading: false });
}}
/>
);
}
},
],
{ cancelable: false },
);
}
if (json && json.makeInvoice) {
let amount = Math.max(+json.makeInvoice.minimumAmount, +json.makeInvoice.maximumAmount, +json.makeInvoice.defaultAmount);
Alert.alert(
'Page',
'This page wants to pay you ' + amount + ' sats (' + json.makeInvoice.defaultMemo + ')',
[
{ text: 'No thanks', onPress: () => console.log('Cancel Pressed'), style: 'cancel' },
{
text: 'Accept',
onPress: async () => {
/** @type {LightningCustodianWallet} */
const fromWallet = this.state.fromWallet;
const payreq = await fromWallet.addInvoice(amount, json.makeInvoice.defaultMemo || ' ');
// this.webview.postMessage(JSON.stringify({ bluewalletResponse: { paymentRequest: payreq }, id: json.id }));
// Since webview.postMessage is removed from webview, we inject javascript that will manually triger document
// event; note how data is passed in 'detail', not 'data'
let jsonstr = JSON.stringify({ bluewalletResponse: { paymentRequest: payreq }, id: json.id });
this.webview.injectJavaScript("document.dispatchEvent( new CustomEvent('message', { detail: '" + jsonstr + "' }) );");
},
},
],
{ cancelable: false },
);
}
if (json && json.enable) {
console.log('webln enabled');
this.setState({ weblnEnabled: true });
}
}}
onLoadStart={e => {
alreadyInjected = false;
console.log('load start');
this.setState({ pageIsLoading: true, weblnEnabled: false });
}}
onLoadEnd={e => {
console.log('load end');
this.setState({ url: e.nativeEvent.url, pageIsLoading: false });
}}
onLoadProgress={e => {
console.log('progress:', e.nativeEvent.progress);
if (!alreadyInjected && e.nativeEvent.progress > 0.5) {
this.webview.injectJavaScript(injectedParadise);
alreadyInjected = true;
console.log('injected');
}
}}
/>
);
};
render() {
return (
<SafeBlueArea>

2
screen/plausibledeniability.js

@ -48,7 +48,7 @@ export default class PlausibleDeniability extends Component {
<BlueButton
icon={{
name: 'shield',
type: 'octicon',
type: 'font-awesome',
color: BlueApp.settings.buttonTextColor,
}}
title={loc.plausibledeniability.create_fake_storage}

9
screen/selftest.js

@ -252,6 +252,15 @@ export default class Selftest extends Component {
if (!hd2.validateMnemonic()) {
throw new Error('mnemonic phrase validation not ok');
}
//
let hd3 = new HDSegwitP2SHWallet();
hd3._xpub = 'ypub6XRSuTABFst6pd8BuTmjSwkDya7HrCRqmtsNmtrh1gyrEZwe24GcjJf6jk6nhhenZpLsm6sDHx2BXwnCQQtjF63FbpNyVEkmngKFQF11aph';
await hd3.fetchBalance();
if (hd3.getBalance() !== 0.00026) throw new Error('Could not fetch HD balance');
await hd3.fetchTransactions();
if (hd3.transactions.length !== 7) throw new Error('Could not fetch HD transactions');
} else {
console.warn('skipping RN-specific test');
}

2
screen/send/details.js

@ -11,7 +11,6 @@ import {
TouchableWithoutFeedback,
StyleSheet,
Platform,
Slider,
AsyncStorage,
Text,
} from 'react-native';
@ -24,6 +23,7 @@ import {
BlueDismissKeyboardInputAccessory,
BlueLoading,
} from '../../BlueComponents';
import Slider from '@react-native-community/slider';
import PropTypes from 'prop-types';
import Modal from 'react-native-modal';
import NetworkTransactionFees, { NetworkTransactionFee } from '../../models/networkTransactionFees';

8
screen/settings/about.js

@ -54,8 +54,8 @@ export default class About extends Component {
<BlueButton
icon={{
name: 'mark-github',
type: 'octicon',
name: 'github',
type: 'font-awesome',
color: BlueApp.settings.buttonTextColor,
}}
onPress={() => {
@ -93,8 +93,8 @@ export default class About extends Component {
<BlueButton
icon={{
name: 'thumbsup',
type: 'octicon',
name: 'thumbs-up',
type: 'font-awesome',
color: BlueApp.settings.buttonTextColor,
}}
onPress={() => {

2
screen/settings/encryptStorage.js

@ -58,7 +58,7 @@ export default class EncryptStorage extends Component {
<BlueButton
icon={{
name: 'shield',
type: 'octicon',
type: 'font-awesome',
color: BlueApp.settings.buttonTextColor,
}}
onPress={async () => {

7
screen/settings/language.js

@ -22,9 +22,11 @@ export default class Language extends Component {
{ label: 'Chinese (ZH)', value: 'zh_cn' },
{ label: 'Croatian (HR)', value: 'hr_hr' },
{ label: 'Danish (DK)', value: 'da_dk' },
{ label: 'Norsk (NB)', value: 'nb_no' },
{ label: 'Deutsch (DE)', value: 'de_de' },
{ label: 'Español (ES)', value: 'es' },
{ label: 'Italian (IT)', value: 'it' },
{ label: 'Italiano (IT)', value: 'it' },
{ label: 'Suomi (FI)', value: 'fi_fi' },
{ label: 'Français (FR)', value: 'fr_fr' },
{ label: 'Indonesia (ID)', value: 'id_id' },
{ label: '日本語 (JP)', value: 'jp_jp' },
@ -32,8 +34,11 @@ export default class Language extends Component {
{ label: 'Portuguese (BR)', value: 'pt_br' },
{ label: 'Portuguese (PT)', value: 'pt_pt' },
{ label: 'Русский', value: 'ru' },
{ label: 'Svenska (SE)', value: 'sv_se' },
{ label: 'Thai (TH)', value: 'th_th' },
{ label: 'Українська', value: 'ua' },
{ label: 'Ελληνικά (EL)', value: 'el' },
{ label: 'Türkçe (TR)', value: 'tr_tr' },
],
};
}

4
screen/settings/lightningSettings.js

@ -59,8 +59,8 @@ export default class LightningSettings extends Component {
<Button
icon={{
name: 'mark-github',
type: 'octicon',
name: 'github',
type: 'font-awesome',
color: BlueApp.settings.buttonTextColor,
backgroundColor: '#FFFFFF',
}}

2
screen/transactions/RBF-create.js

@ -225,7 +225,7 @@ export default class SendCreate extends Component {
{this.state.isLoading ? (
<ActivtyIndicator />
) : (
<BlueButton icon={{ name: 'megaphone', type: 'octicon' }} onPress={() => this.broadcast()} title="Broadcast" />
<BlueButton icon={{ name: 'bullhorn', type: 'font-awesome' }} onPress={() => this.broadcast()} title="Broadcast" />
)}
</SafeBlueArea>
);

23
screen/wallets/list.js

@ -11,6 +11,7 @@ let A = require('../../analytics');
/** @type {AppStorage} */
let BlueApp = require('../../BlueApp');
let loc = require('../../loc');
let BlueElectrum = require('../../BlueElectrum');
export default class WalletsList extends Component {
static navigationOptions = ({ navigation }) => ({
@ -24,7 +25,7 @@ export default class WalletsList extends Component {
style={{ marginHorizontal: 16, width: 40, height: 40, justifyContent: 'center', alignItems: 'flex-end' }}
onPress={() => navigation.navigate('Settings')}
>
<Icon name="kebab-horizontal" size={22} type="octicon" color={BlueApp.settings.foregroundColor} />
<Icon size={22} name="ellipsis-h" type="font-awesome" color={BlueApp.settings.foregroundColor} />
</TouchableOpacity>
),
});
@ -46,6 +47,26 @@ export default class WalletsList extends Component {
componentDidMount() {
this.redrawScreen();
// the idea is that upon wallet launch we will refresh
// all balances and all transactions here:
InteractionManager.runAfterInteractions(async () => {
let noErr = true;
try {
await BlueElectrum.waitTillConnected();
let balanceStart = +new Date();
await BlueApp.fetchWalletBalances();
let balanceEnd = +new Date();
console.log('fetch all wallet balances took', (balanceEnd - balanceStart) / 1000, 'sec');
let start = +new Date();
await BlueApp.fetchWalletTransactions();
let end = +new Date();
console.log('fetch all wallet txs took', (end - start) / 1000, 'sec');
} catch (_) {
noErr = false;
}
if (noErr) this.redrawScreen();
});
}
/**

10
screen/wallets/scanQrWif.js

@ -28,12 +28,12 @@ export default class ScanQrWif extends React.Component {
};
async onBarCodeScanned(ret) {
this.setState({ isLoading: true });
if (+new Date() - this.lastTimeIveBeenHere < 6000) {
this.lastTimeIveBeenHere = +new Date();
return;
}
this.lastTimeIveBeenHere = +new Date();
this.setState({ isLoading: true });
console.log('onBarCodeScanned', ret);
if (ret.data[0] === '6') {
@ -129,6 +129,12 @@ export default class ScanQrWif extends React.Component {
this.setState({ isLoading: true });
let lnd = new LightningCustodianWallet();
lnd.setSecret(ret.data);
if (ret.data.includes('@')) {
const split = ret.data.split('@');
lnd.setBaseURI(split[1]);
lnd.setSecret(split[0]);
}
try {
await lnd.authorize();
await lnd.fetchTransactions();
@ -249,7 +255,7 @@ export default class ScanQrWif extends React.Component {
>
<BlueText>{this.state.message}</BlueText>
<BlueButton
icon={{ name: 'stop', type: 'octicon' }}
icon={{ name: 'ban', type: 'font-awesome' }}
onPress={async () => {
this.setState({ message: false });
shold_stop_bip38 = true; // eslint-disable-line

9
screen/wallets/transactions.js

@ -41,7 +41,7 @@ export default class WalletTransactions extends Component {
})
}
>
<Icon name="kebab-horizontal" size={22} type="octicon" color="#FFFFFF" />
<Icon name="ellipsis-h" type="font-awesome" size={22} color="#FFFFFF" />
</TouchableOpacity>
),
headerStyle: {
@ -405,9 +405,7 @@ export default class WalletTransactions extends Component {
textAlign: 'center',
}}
>
{(this.isLightning() &&
'Lightning wallet should be used for your daily transactions. Fees are unfairly cheap and speed is blazing fast.') ||
loc.wallets.list.empty_txs1}
{(this.isLightning() && loc.wallets.list.empty_txs1_lightning) || loc.wallets.list.empty_txs1}
</Text>
<Text
style={{
@ -416,8 +414,7 @@ export default class WalletTransactions extends Component {
textAlign: 'center',
}}
>
{(this.isLightning() && '\nTo start using it tap on "manage funds" and topup your balance') ||
loc.wallets.list.empty_txs2}
{(this.isLightning() && loc.wallets.list.empty_txs2_lightning) || loc.wallets.list.empty_txs2}
</Text>
<Text />

24
security-alert.js

@ -1,24 +0,0 @@
import { AsyncStorage, Platform, Alert } from 'react-native';
async function start() {
const key = 'security_alert_num_times';
let times = await AsyncStorage.getItem(key);
times = times || 0;
console.log({ times });
if (times < 3 && Platform.OS === 'ios') {
Alert.alert(
'Security alert',
'If you used BlueWallet prior to version 3.1.0 you are advised to re-create wallets ' +
'and transfer all funds from old wallets to new ones, as older versions might have security issues',
[
{ text: 'Remind me later', onPress: () => AsyncStorage.setItem(key, '0') },
{ text: 'Ok', onPress: () => AsyncStorage.setItem(key, parseInt(times) + 1 + '') },
],
{ cancelable: false },
);
}
}
module.exports.start = start;
Loading…
Cancel
Save