Browse Source

Merge pull request #11 from sansegkh/master

merged from master
pulltorefresh
San Segkhoonthod 6 years ago
committed by GitHub
parent
commit
bb39995f11
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      .buckconfig
  2. 73
      .flowconfig
  3. 1
      .gitattributes
  4. 55
      .gitignore
  5. 2
      App.js
  6. 46
      App.test.js
  7. 16
      App2.test.js
  8. 237
      BlueComponents.js
  9. 144
      HDWallet.test.js
  10. 42
      LightningCustodianWallet.test.js
  11. 21
      NavigationService.js
  12. 18
      android/app/BUCK
  13. 97
      android/app/app.iml
  14. 49
      android/app/build.gradle
  15. 19
      android/app/build_defs.bzl
  16. 11
      android/app/src/main/AndroidManifest.xml
  17. BIN
      android/app/src/main/assets/fonts/FontAwesome5_Brands.ttf
  18. BIN
      android/app/src/main/assets/fonts/FontAwesome5_Regular.ttf
  19. BIN
      android/app/src/main/assets/fonts/FontAwesome5_Solid.ttf
  20. 71
      android/app/src/main/java/com/bluewallet/MainApplication.java
  21. 0
      android/app/src/main/java/io/bluewallet/bluewallet/MainActivity.java
  22. 1
      android/app/src/main/java/io/bluewallet/bluewallet/MainApplication.java
  23. 92
      android/app/src/main/java/io/bluewallet/bluewallet/MainApplication.java
  24. 2
      android/app/src/main/res/values/strings.xml
  25. 23
      android/build.gradle
  26. 93
      android/build/intermediates/lint-cache/maven.google/com/android/support/group-index.xml
  27. 120
      android/build/intermediates/lint-cache/maven.google/master-index.xml
  28. 2
      android/gradle/wrapper/gradle-wrapper.properties
  29. 2
      android/metadata/en-US/full_description.txt
  30. 18
      android/settings.gradle
  31. 5
      app.json
  32. 3
      babel.config.js
  33. 12
      class/lightning-custodian-wallet.js
  34. 79
      class/walletGradient.js
  35. 4
      events.js
  36. 1355
      ios/BlueWallet.xcodeproj/project.pbxproj
  37. 2
      ios/BlueWallet/AppDelegate.h
  38. 17
      ios/BlueWallet/AppDelegate.m
  39. 10
      ios/BlueWallet/Info.plist
  40. 2
      ios/BlueWallet/main.m
  41. 2
      ios/BlueWalletTests/BlueWalletTests.m
  42. 2
      ios/BlueWalletTests/Info.plist
  43. 2
      ios/fastlane/metadata/en-US/description.txt
  44. 2
      ios/fastlane/metadata/es-ES/description.txt
  45. 2
      ios/fastlane/metadata/pt-BR/description.txt
  46. 2
      ios/fastlane/metadata/pt-PT/description.txt
  47. 2
      loc/cs_CZ.js
  48. 2
      loc/da_DK.js
  49. 2
      loc/de_DE.js
  50. 2
      loc/en.js
  51. 2
      loc/es.js
  52. 2
      loc/fr_FR.js
  53. 54
      loc/hr_HR.js
  54. 7
      loc/index.js
  55. 2
      loc/nl_NL.js
  56. 2
      loc/pt_BR.js
  57. 2
      loc/pt_PT.js
  58. 2
      models/fiatUnit.js
  59. 5463
      package-lock.json
  60. 59
      package.json
  61. 1
      patches/fix_mangle.sh
  62. 16
      patches/minifier.js.patch
  63. 448
      screen/lnd/browser.js
  64. 6
      screen/lnd/lndCreateInvoice.js
  65. 36
      screen/lnd/lndViewAdditionalInvoiceInformation.js
  66. 48
      screen/lnd/lndViewInvoice.js
  67. 82
      screen/lnd/scanLndInvoice.js
  68. 36
      screen/receive/details.js
  69. 58
      screen/receive/receiveAmount.js
  70. 118
      screen/send/details.js
  71. 27
      screen/send/scanQrAddress.js
  72. 6
      screen/settings/about.js
  73. 2
      screen/settings/language.js
  74. 6
      screen/settings/lightningSettings.js
  75. 2
      screen/transactions/details.js
  76. 3
      screen/wallets/add.js
  77. 34
      screen/wallets/buyBitcoin.js
  78. 2
      screen/wallets/details.js
  79. 9
      screen/wallets/list.js
  80. 40
      screen/wallets/reorderWallets.js
  81. 40
      screen/wallets/selectWallet.js
  82. 30
      screen/wallets/transactions.js
  83. 2
      screen/wallets/walletMigrate.js
  84. 17
      screen/wallets/xpub.js

6
.buckconfig

@ -0,0 +1,6 @@
[android]
target = Google Inc.:Google APIs:23
[maven_repositories]
central = https://repo1.maven.org/maven2

73
.flowconfig

@ -2,74 +2,69 @@
; We fork some components by platform
.*/*[.]android.js
; Ignore templates for 'react-native init'
<PROJECT_ROOT>/node_modules/react-native/local-cli/templates/.*
; Ignore RN jest
<PROJECT_ROOT>/node_modules/react-native/jest/.*
; Ignore RNTester
<PROJECT_ROOT>/node_modules/react-native/RNTester/.*
; Ignore the website subdir
<PROJECT_ROOT>/node_modules/react-native/website/.*
; Ignore the Dangerfile
<PROJECT_ROOT>/node_modules/react-native/danger/dangerfile.js
; Ignore Fbemitter
<PROJECT_ROOT>/node_modules/fbemitter/.*
; Ignore "BUCK" generated dirs
<PROJECT_ROOT>/node_modules/react-native/\.buckd/
<PROJECT_ROOT>/\.buckd/
; Ignore unexpected extra "@providesModule"
.*/node_modules/.*/node_modules/fbjs/.*
; Ignore duplicate module providers
; For RN Apps installed via npm, "Libraries" folder is inside
; "node_modules/react-native" but in the source repo it is in the root
.*/Libraries/react-native/React.js
; Ignore polyfills
<PROJECT_ROOT>/node_modules/react-native/Libraries/polyfills/.*
.*/Libraries/polyfills/.*
; Ignore various node_modules
<PROJECT_ROOT>/node_modules/react-native-gesture-handler/.*
<PROJECT_ROOT>/node_modules/expo/.*
<PROJECT_ROOT>/node_modules/react-navigation/.*
<PROJECT_ROOT>/node_modules/xdl/.*
<PROJECT_ROOT>/node_modules/reqwest/.*
<PROJECT_ROOT>/node_modules/metro-bundler/.*
; Ignore metro
.*/node_modules/metro/.*
[include]
[libs]
node_modules/react-native/Libraries/react-native/react-native-interface.js
node_modules/react-native/flow/
node_modules/expo/flow/
node_modules/react-native/flow-github/
[options]
emoji=true
module.system=haste
esproposal.optional_chaining=enable
esproposal.nullish_coalescing=enable
module.file_ext=.js
module.file_ext=.jsx
module.file_ext=.json
module.file_ext=.ios.js
module.system=haste
module.system.haste.use_name_reducers=true
# get basename
module.system.haste.name_reducers='^.*/\([a-zA-Z0-9$_.-]+\.js\(\.flow\)?\)$' -> '\1'
# strip .js or .js.flow suffix
module.system.haste.name_reducers='^\(.*\)\.js\(\.flow\)?$' -> '\1'
# strip .ios suffix
module.system.haste.name_reducers='^\(.*\)\.ios$' -> '\1'
module.system.haste.name_reducers='^\(.*\)\.android$' -> '\1'
module.system.haste.name_reducers='^\(.*\)\.native$' -> '\1'
module.system.haste.paths.blacklist=.*/__tests__/.*
module.system.haste.paths.blacklist=.*/__mocks__/.*
module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/Animated/src/polyfills/.*
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/Libraries/.*
munge_underscores=true
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
module.file_ext=.js
module.file_ext=.jsx
module.file_ext=.json
module.file_ext=.native.js
suppress_type=$FlowIssue
suppress_type=$FlowFixMe
suppress_type=$FlowFixMeProps
suppress_type=$FlowFixMeState
suppress_type=$FixMe
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(5[0-6]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native_oss[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(5[0-6]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native_oss[a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
unsafe.enable_getters_and_setters=true
[version]
^0.56.0
^0.85.0

1
.gitattributes

@ -0,0 +1 @@
*.pbxproj -text

55
.gitignore

@ -1,29 +1,10 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.
# dependencies
/node_modules
node_modules/
# misc
.env.local
.env.development.local
.env.test.local
.env.production.local
.idea/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
class/constants.js
# OSX# OSX
# OSX
#
.DS_Store
# Xcode
#
build/
ios/build/*
*.pbxuser
!default.pbxuser
*.mode1v3
@ -39,13 +20,37 @@ DerivedData
*.hmap
*.ipa
*.xcuserstate
#
project.xcworkspace
# Android
# Android/IntelliJ
#
android/local.properties
android/app/bluewallet-release-key.keystore
build/
.idea
.gradle
local.properties
*.iml
build/
# node.js
#
node_modules/
npm-debug.log
yarn-error.log
# BUCK
buck-out/
\.buckd/
*.keystore
# fastlane
#
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
# screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/
*/fastlane/report.xml
*/fastlane/Preview.html
*/fastlane/screenshots
# Bundle artifact
*.jsbundle

2
App.js

@ -2,6 +2,7 @@ import React from 'react';
import { Linking } from 'react-native';
import { NavigationActions } from 'react-navigation';
import MainBottomTabs from './MainBottomTabs';
import NavigationService from './NavigationService';
export default class App extends React.Component {
navigator = null;
@ -53,6 +54,7 @@ export default class App extends React.Component {
<MainBottomTabs
ref={nav => {
this.navigator = nav;
NavigationService.setTopLevelNavigator(nav);
}}
/>
);

46
App.test.js

@ -57,7 +57,7 @@ describe('unit - LegacyWallet', function() {
let b = LegacyWallet.fromJson(key);
assert(key === JSON.stringify(b));
assert.equal(key, JSON.stringify(b));
assert.strictEqual(key, JSON.stringify(b));
});
it('can validate addresses', () => {
@ -118,8 +118,8 @@ it('Appstorage - loadFromDisk works', async () => {
let Storage2 = new AppStorage();
await Storage2.loadFromDisk();
assert.equal(Storage2.wallets.length, 1);
assert.equal(Storage2.wallets[0].getLabel(), 'testlabel');
assert.strictEqual(Storage2.wallets.length, 1);
assert.strictEqual(Storage2.wallets[0].getLabel(), 'testlabel');
let isEncrypted = await Storage2.storageIsEncrypted();
assert.ok(!isEncrypted);
@ -146,7 +146,7 @@ it('Appstorage - encryptStorage & load encrypted storage works', async () => {
assert.ok(!isEncrypted);
await Storage.encryptStorage('password');
isEncrypted = await Storage.storageIsEncrypted();
assert.equal(Storage.cachedPassword, 'password');
assert.strictEqual(Storage.cachedPassword, 'password');
assert.ok(isEncrypted);
// saved, now trying to load, using good password
@ -156,8 +156,8 @@ it('Appstorage - encryptStorage & load encrypted storage works', async () => {
assert.ok(isEncrypted);
let loadResult = await Storage2.loadFromDisk('password');
assert.ok(loadResult);
assert.equal(Storage2.wallets.length, 1);
assert.equal(Storage2.wallets[0].getLabel(), 'testlabel');
assert.strictEqual(Storage2.wallets.length, 1);
assert.strictEqual(Storage2.wallets[0].getLabel(), 'testlabel');
// now trying to load, using bad password
@ -166,7 +166,7 @@ it('Appstorage - encryptStorage & load encrypted storage works', async () => {
assert.ok(isEncrypted);
loadResult = await Storage2.loadFromDisk('passwordBAD');
assert.ok(!loadResult);
assert.equal(Storage2.wallets.length, 0);
assert.strictEqual(Storage2.wallets.length, 0);
// now, trying case with adding data after decrypt.
// saveToDisk should be handled correctly
@ -176,14 +176,14 @@ it('Appstorage - encryptStorage & load encrypted storage works', async () => {
assert.ok(isEncrypted);
loadResult = await Storage2.loadFromDisk('password');
assert.ok(loadResult);
assert.equal(Storage2.wallets.length, 1);
assert.equal(Storage2.wallets[0].getLabel(), 'testlabel');
assert.strictEqual(Storage2.wallets.length, 1);
assert.strictEqual(Storage2.wallets[0].getLabel(), 'testlabel');
w = new SegwitP2SHWallet();
w.setLabel('testlabel2');
await w.generate();
Storage2.wallets.push(w);
assert.equal(Storage2.wallets.length, 2);
assert.equal(Storage2.wallets[1].getLabel(), 'testlabel2');
assert.strictEqual(Storage2.wallets.length, 2);
assert.strictEqual(Storage2.wallets[1].getLabel(), 'testlabel2');
await Storage2.saveToDisk();
// saved to encrypted storage after load. next load should be successfull
Storage2 = new AppStorage();
@ -191,15 +191,15 @@ it('Appstorage - encryptStorage & load encrypted storage works', async () => {
assert.ok(isEncrypted);
loadResult = await Storage2.loadFromDisk('password');
assert.ok(loadResult);
assert.equal(Storage2.wallets.length, 2);
assert.equal(Storage2.wallets[0].getLabel(), 'testlabel');
assert.equal(Storage2.wallets[1].getLabel(), 'testlabel2');
assert.strictEqual(Storage2.wallets.length, 2);
assert.strictEqual(Storage2.wallets[0].getLabel(), 'testlabel');
assert.strictEqual(Storage2.wallets[1].getLabel(), 'testlabel2');
// next, adding new `fake` storage which should be unlocked with `fake` password
let createFakeStorageResult = await Storage2.createFakeStorage('fakePassword');
assert.ok(createFakeStorageResult);
assert.equal(Storage2.wallets.length, 0);
assert.equal(Storage2.cachedPassword, 'fakePassword');
assert.strictEqual(Storage2.wallets.length, 0);
assert.strictEqual(Storage2.cachedPassword, 'fakePassword');
w = new SegwitP2SHWallet();
w.setLabel('fakewallet');
await w.generate();
@ -210,14 +210,14 @@ it('Appstorage - encryptStorage & load encrypted storage works', async () => {
let Storage3 = new AppStorage();
loadResult = await Storage3.loadFromDisk('password');
assert.ok(loadResult);
assert.equal(Storage3.wallets.length, 2);
assert.equal(Storage3.wallets[0].getLabel(), 'testlabel');
assert.strictEqual(Storage3.wallets.length, 2);
assert.strictEqual(Storage3.wallets[0].getLabel(), 'testlabel');
// fake:
Storage3 = new AppStorage();
loadResult = await Storage3.loadFromDisk('fakePassword');
assert.ok(loadResult);
assert.equal(Storage3.wallets.length, 1);
assert.equal(Storage3.wallets[0].getLabel(), 'fakewallet');
assert.strictEqual(Storage3.wallets.length, 1);
assert.strictEqual(Storage3.wallets[0].getLabel(), 'fakewallet');
});
it('Wallet can fetch UTXO', async () => {
@ -245,7 +245,7 @@ it('Wallet can fetch TXs', async () => {
let w = new LegacyWallet();
w._address = '12eQ9m4sgAwTSQoNXkRABKhCXCsjm2jdVG';
await w.fetchTransactions();
assert.equal(w.getTransactions().length, 2);
assert.strictEqual(w.getTransactions().length, 2);
let tx0 = w.getTransactions()[0];
let txExpected = {
@ -296,7 +296,7 @@ it('Wallet can fetch TXs', async () => {
delete txExpected.confirmations;
delete tx0.preference; // that bs is not always the same
delete txExpected.preference;
assert.deepEqual(tx0, txExpected);
assert.deepStrictEqual(tx0, txExpected);
});
describe('currency', () => {
@ -321,7 +321,7 @@ describe('currency', () => {
await currency.setPrefferedCurrency(FiatUnit.EUR);
await currency.startUpdater();
let preferred = await currency.getPreferredCurrency();
assert.equal(preferred.endPointKey, 'EUR');
assert.strictEqual(preferred.endPointKey, 'EUR');
cur = JSON.parse(AsyncStorage.storageCache[AppStorage.EXCHANGE_RATES]);
assert.ok(cur['BTC_EUR'] > 0);
});

16
App2.test.js

@ -14,7 +14,10 @@ it('bip38 decodes', async () => {
{ N: 1, r: 8, p: 8 }, // using non-default parameters to speed it up (not-bip38 compliant)
);
assert.equal(wif.encode(0x80, decryptedKey.privateKey, decryptedKey.compressed), '5KN7MzqK5wt2TP1fQCYyHBtDrXdJuXbUzm4A9rKAteGu3Qi5CVR');
assert.strictEqual(
wif.encode(0x80, decryptedKey.privateKey, decryptedKey.compressed),
'5KN7MzqK5wt2TP1fQCYyHBtDrXdJuXbUzm4A9rKAteGu3Qi5CVR',
);
});
it('bip38 decodes slow', async () => {
@ -29,7 +32,10 @@ it('bip38 decodes slow', async () => {
let encryptedKey = '6PnU5voARjBBykwSddwCdcn6Eu9EcsK24Gs5zWxbJbPZYW7eiYQP8XgKbN';
let decryptedKey = await bip38.decrypt(encryptedKey, 'qwerty', status => process.stdout.write(parseInt(status.percent) + '%\r'));
assert.equal(wif.encode(0x80, decryptedKey.privateKey, decryptedKey.compressed), 'KxqRtpd9vFju297ACPKHrGkgXuberTveZPXbRDiQ3MXZycSQYtjc');
assert.strictEqual(
wif.encode(0x80, decryptedKey.privateKey, decryptedKey.compressed),
'KxqRtpd9vFju297ACPKHrGkgXuberTveZPXbRDiQ3MXZycSQYtjc',
);
});
describe('Watch only wallet', () => {
@ -46,16 +52,16 @@ describe('Watch only wallet', () => {
w.setSecret('167zK5iZrs1U6piDqubD3FjRqUTM2CZnb8');
await w.fetchTransactions();
assert.equal(w.getTransactions().length, 233);
assert.strictEqual(w.getTransactions().length, 233);
w = new WatchOnlyWallet();
w.setSecret('1BiJW1jyUaxcJp2JWwbPLPzB1toPNWTFJV');
await w.fetchTransactions();
assert.equal(w.getTransactions().length, 2);
assert.strictEqual(w.getTransactions().length, 2);
// fetch again and make sure no duplicates
await w.fetchTransactions();
assert.equal(w.getTransactions().length, 2);
assert.strictEqual(w.getTransactions().length, 2);
});
it('can fetch complex TXs', async () => {

237
BlueComponents.js

@ -1,4 +1,5 @@
/* eslint react/prop-types: 0 */
/* global alert */
/** @type {AppStorage} */
import React, { Component } from 'react';
import Ionicons from 'react-native-vector-icons/Ionicons';
@ -10,23 +11,25 @@ import {
Animated,
ActivityIndicator,
View,
UIManager,
StyleSheet,
Dimensions,
Image,
SafeAreaView,
Clipboard,
Platform,
LayoutAnimation,
TextInput,
} from 'react-native';
import LinearGradient from 'react-native-linear-gradient';
import { WatchOnlyWallet, LegacyWallet } from './class';
import { LightningCustodianWallet } from './class';
import Carousel from 'react-native-snap-carousel';
import DeviceInfo from 'react-native-device-info';
import { HDLegacyP2PKHWallet } from './class/hd-legacy-p2pkh-wallet';
import { HDLegacyBreadwalletWallet } from './class/hd-legacy-breadwallet-wallet';
import { HDSegwitP2SHWallet } from './class/hd-segwit-p2sh-wallet';
import { LightningCustodianWallet } from './class/lightning-custodian-wallet';
import { BitcoinUnit } from './models/bitcoinUnits';
import NavigationService from './NavigationService';
import ImagePicker from 'react-native-image-picker';
import WalletGradient from './class/walletGradient';
const LocalQRCode = require('@remobile/react-native-qrcode-local-image');
let loc = require('./loc/');
/** @type {AppStorage} */
let BlueApp = require('./BlueApp');
@ -41,30 +44,29 @@ if (aspectRatio > 1.6) {
export class BlueButton extends Component {
render() {
// eslint-disable-next-line
this.props.buttonStyle = this.props.buttonStyle || {};
const backgroundColor = this.props.disabled ? '#99a0ab' : '#ccddf9';
return (
<Button
activeOpacity={0.1}
delayPressIn={0}
{...this.props}
<TouchableOpacity
style={{
flex: 1,
borderWidth: 0.7,
borderColor: 'transparent',
}}
buttonStyle={Object.assign(
{
backgroundColor: '#ccddf9',
backgroundColor: this.props.hasOwnProperty('backgroundColor') ? this.props.backgroundColor : backgroundColor,
minHeight: 45,
height: 45,
borderWidth: 0,
maxHeight: 45,
borderRadius: 25,
},
this.props.buttonStyle,
)}
color="#0c2550"
/>
minWidth: width / 1.5,
justifyContent: 'center',
alignItems: 'center',
}}
{...this.props}
>
<View style={{ flexDirection: 'row', justifyContent: 'center', alignItems: 'center' }}>
{this.props.icon && <Icon name={this.props.icon.name} type={this.props.icon.type} color={this.props.icon.color} />}
{this.props.title && <Text style={{ marginHorizontal: 8, fontSize: 16, color: '#0c2550' }}>{this.props.title}</Text>}
</View>
</TouchableOpacity>
);
}
}
@ -193,6 +195,60 @@ export const BlueCopyToClipboardButton = ({ stringToCopy }) => {
);
};
export class BlueCopyTextToClipboard extends Component {
static propTypes = {
text: PropTypes.string,
};
static defaultProps = {
text: '',
};
state = { hasTappedText: false };
constructor() {
super();
if (Platform.OS === 'android') UIManager.setLayoutAnimationEnabledExperimental && UIManager.setLayoutAnimationEnabledExperimental(true);
}
copyToClipboard = () => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.spring, () => {
Clipboard.setString(this.props.text);
setTimeout(() => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.spring);
this.setState({ hasTappedText: false });
}, 1000);
});
this.setState({ hasTappedText: true });
};
render() {
return (
<View style={{ justifyContent: 'center', alignItems: 'center', paddingHorizontal: 16 }}>
<TouchableOpacity onPress={this.copyToClipboard} disabled={this.state.hasTappedText}>
<Text style={styleCopyTextToClipboard.address} numberOfLines={0}>
{this.props.text}
</Text>
{this.state.hasTappedText && (
<Text style={styleCopyTextToClipboard.address} numberOfLines={0}>
{loc.wallets.xpub.copiedToClipboard}
</Text>
)}
</TouchableOpacity>
</View>
);
}
}
const styleCopyTextToClipboard = StyleSheet.create({
address: {
marginVertical: 32,
fontSize: 15,
color: '#9aa0aa',
textAlign: 'center',
},
});
export class SafeBlueArea extends Component {
render() {
return (
@ -215,13 +271,12 @@ export class BlueText extends Component {
render() {
return (
<Text
style={Object.assign(
{
style={{
color: BlueApp.settings.foregroundColor,
},
// eslint-disable-next-line
this.props.style,
)}
...this.props.style,
}}
{...this.props}
/>
);
@ -883,7 +938,7 @@ export class NewWalletPanel extends Component {
style={{ marginVertical: 17 }}
>
<LinearGradient
colors={['#eef0f4', '#eef0f4']}
colors={WalletGradient.createWallet}
style={{
padding: 15,
borderRadius: 10,
@ -964,39 +1019,6 @@ export class WalletsCarousel extends Component {
);
}
let gradient1 = '#65ceef';
let gradient2 = '#68bbe1';
if (WatchOnlyWallet.type === item.type) {
gradient1 = '#7d7d7d';
gradient2 = '#4a4a4a';
}
if (LegacyWallet.type === item.type) {
gradient1 = '#40fad1';
gradient2 = '#15be98';
}
if (HDLegacyP2PKHWallet.type === item.type) {
gradient1 = '#e36dfa';
gradient2 = '#bd10e0';
}
if (HDLegacyBreadwalletWallet.type === item.type) {
gradient1 = '#fe6381';
gradient2 = '#f99c42';
}
if (HDSegwitP2SHWallet.type === item.type) {
gradient1 = '#c65afb';
gradient2 = '#9053fe';
}
if (LightningCustodianWallet.type === item.type) {
gradient1 = '#f1be07';
gradient2 = '#f79056';
}
return (
<Animated.View
style={{ paddingRight: 10, marginVertical: 17, transform: [{ scale: scaleValue }] }}
@ -1010,13 +1032,13 @@ export class WalletsCarousel extends Component {
onLongPress={WalletsCarousel.handleLongPress}
onPress={() => {
if (WalletsCarousel.handleClick) {
WalletsCarousel.handleClick(index, [gradient1, gradient2]);
WalletsCarousel.handleClick(index);
}
}}
>
<LinearGradient
shadowColor="#000000"
colors={[gradient1, gradient2]}
colors={WalletGradient.gradientsFor(item.type)}
style={{
padding: 15,
borderRadius: 10,
@ -1111,6 +1133,94 @@ export class WalletsCarousel extends Component {
}
}
export class BlueAddressInput extends Component {
static propTypes = {
isLoading: PropTypes.bool,
onChangeText: PropTypes.func,
onBarScanned: PropTypes.func,
address: PropTypes.string,
};
static defaultProps = {
isLoading: false,
address: '',
};
render() {
return (
<View
style={{
flexDirection: 'row',
borderColor: '#d2d2d2',
borderBottomColor: '#d2d2d2',
borderWidth: 1.0,
borderBottomWidth: 0.5,
backgroundColor: '#f5f5f5',
minHeight: 44,
height: 44,
marginHorizontal: 20,
alignItems: 'center',
marginVertical: 8,
borderRadius: 4,
}}
>
<TextInput
onChangeText={text => {
this.props.onChangeText(text);
}}
placeholder={loc.send.details.address}
numberOfLines={1}
value={this.props.address}
style={{ flex: 1, marginHorizontal: 8, minHeight: 33 }}
editable={!this.props.isLoading}
/>
<TouchableOpacity
disabled={this.props.isLoading}
onPress={() => {
ImagePicker.showImagePicker(
{
title: null,
mediaType: 'photo',
takePhotoButtonTitle: null,
customButtons: [{ name: 'navigatetoQRScan', title: 'Use Camera' }],
},
response => {
if (response.customButton) {
NavigationService.navigate('ScanQrAddress', { onBarScanned: this.props.onBarScanned });
} else if (response.uri) {
const uri = response.uri.toString().replace('file://', '');
LocalQRCode.decode(uri, (error, result) => {
if (!error) {
this.props.onBarScanned(result);
} else {
alert('The selected image does not contain a QR Code.');
}
});
}
},
);
}}
style={{
width: 75,
height: 36,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
backgroundColor: '#bebebe',
borderRadius: 4,
paddingVertical: 4,
paddingHorizontal: 8,
marginHorizontal: 4,
}}
>
<Icon name="qrcode" size={22} type="font-awesome" color="#FFFFFF" />
<Text style={{ color: '#FFFFFF' }}>{loc.send.details.scan}</Text>
</TouchableOpacity>
</View>
);
}
}
export class BlueBitcoinAmount extends Component {
static propTypes = {
isLoading: PropTypes.bool,
@ -1148,6 +1258,7 @@ export class BlueBitcoinAmount extends Component {
ref={textInput => (this.textInput = textInput)}
editable={!this.props.isLoading && !this.props.disabled}
value={amount}
autoFocus={this.props.pointerEvents !== 'none'}
placeholderTextColor={this.props.disabled ? '#99a0ab' : '#0f5cc0'}
style={{
color: this.props.disabled ? '#99a0ab' : '#0f5cc0',

144
HDWallet.test.js

@ -6,13 +6,13 @@ let bitcoin = require('bitcoinjs-lib');
it('can convert witness to address', () => {
let address = SegwitP2SHWallet.witnessToAddress('035c618df829af694cb99e664ce1b34f80ad2c3b49bcd0d9c0b1836c66b2d25fd8');
assert.equal(address, '34ZVGb3gT8xMLT6fpqC6dNVqJtJmvdjbD7');
assert.strictEqual(address, '34ZVGb3gT8xMLT6fpqC6dNVqJtJmvdjbD7');
address = SegwitBech32Wallet.witnessToAddress('035c618df829af694cb99e664ce1b34f80ad2c3b49bcd0d9c0b1836c66b2d25fd8');
assert.equal(address, 'bc1quhnve8q4tk3unhmjts7ymxv8cd6w9xv8wy29uv');
assert.strictEqual(address, 'bc1quhnve8q4tk3unhmjts7ymxv8cd6w9xv8wy29uv');
address = SegwitBech32Wallet.scriptPubKeyToAddress('00144d757460da5fcaf84cc22f3847faaa1078e84f6a');
assert.equal(address, 'bc1qf46hgcx6tl90snxz9uuy0742zpuwsnm27ysdh7');
assert.strictEqual(address, 'bc1qf46hgcx6tl90snxz9uuy0742zpuwsnm27ysdh7');
});
it('can create a Segwit HD (BIP49)', async function() {
@ -21,39 +21,39 @@ it('can create a Segwit HD (BIP49)', async function() {
'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();
hd.setSecret(mnemonic);
assert.equal('3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK', hd._getExternalAddressByIndex(0));
assert.equal('35p5LwCAE7mH2css7onyQ1VuS1jgWtQ4U3', hd._getExternalAddressByIndex(1));
assert.equal('32yn5CdevZQLk3ckuZuA8fEKBco8mEkLei', hd._getInternalAddressByIndex(0));
assert.equal(true, hd.validateMnemonic());
assert.strictEqual('3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK', hd._getExternalAddressByIndex(0));
assert.strictEqual('35p5LwCAE7mH2css7onyQ1VuS1jgWtQ4U3', hd._getExternalAddressByIndex(1));
assert.strictEqual('32yn5CdevZQLk3ckuZuA8fEKBco8mEkLei', hd._getInternalAddressByIndex(0));
assert.strictEqual(true, hd.validateMnemonic());
await hd.fetchBalance();
assert.equal(hd.getBalance(), 0);
assert.strictEqual(hd.getBalance(), 0);
assert.ok(hd._lastTxFetch === 0);
await hd.fetchTransactions();
assert.ok(hd._lastTxFetch > 0);
assert.equal(hd.transactions.length, 4);
assert.strictEqual(hd.transactions.length, 4);
assert.equal('L4MqtwJm6hkbACLG4ho5DF8GhcXdLEbbvpJnbzA9abfD6RDpbr2m', hd._getExternalWIFByIndex(0));
assert.equal(
assert.strictEqual('L4MqtwJm6hkbACLG4ho5DF8GhcXdLEbbvpJnbzA9abfD6RDpbr2m', hd._getExternalWIFByIndex(0));
assert.strictEqual(
'ypub6WhHmKBmHNjcrUVNCa3sXduH9yxutMipDcwiKW31vWjcMbfhQHjXdyx4rqXbEtVgzdbhFJ5mZJWmfWwnP4Vjzx97admTUYKQt6b9D7jjSCp',
hd.getXpub(),
);
// checking that internal pointer and async address getter return the same address
let freeAddress = await hd.getAddressAsync();
assert.equal(hd._getExternalAddressByIndex(hd.next_free_address_index), freeAddress);
assert.strictEqual(hd._getExternalAddressByIndex(hd.next_free_address_index), freeAddress);
let freeChangeAddress = await hd.getChangeAddressAsync();
assert.equal(hd._getInternalAddressByIndex(hd.next_free_change_address_index), freeChangeAddress);
assert.strictEqual(hd._getInternalAddressByIndex(hd.next_free_change_address_index), freeChangeAddress);
});
it('Segwit HD (BIP49) can generate addressess only via ypub', async function() {
let ypub = 'ypub6WhHmKBmHNjcrUVNCa3sXduH9yxutMipDcwiKW31vWjcMbfhQHjXdyx4rqXbEtVgzdbhFJ5mZJWmfWwnP4Vjzx97admTUYKQt6b9D7jjSCp';
let hd = new HDSegwitP2SHWallet();
hd._xpub = ypub;
assert.equal('3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK', hd._getExternalAddressByIndex(0));
assert.equal('35p5LwCAE7mH2css7onyQ1VuS1jgWtQ4U3', hd._getExternalAddressByIndex(1));
assert.equal('32yn5CdevZQLk3ckuZuA8fEKBco8mEkLei', hd._getInternalAddressByIndex(0));
assert.strictEqual('3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK', hd._getExternalAddressByIndex(0));
assert.strictEqual('35p5LwCAE7mH2css7onyQ1VuS1jgWtQ4U3', hd._getExternalAddressByIndex(1));
assert.strictEqual('32yn5CdevZQLk3ckuZuA8fEKBco8mEkLei', hd._getInternalAddressByIndex(0));
});
it('can generate Segwit HD (BIP49)', async () => {
@ -88,56 +88,56 @@ it('HD (BIP49) can create TX', async () => {
await hd.getChangeAddressAsync(); // to refresh internal pointer to next free address
await hd.getAddressAsync(); // to refresh internal pointer to next free address
let txhex = hd.createTx(hd.utxo, 0.000014, 0.000001, '3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK');
assert.equal(
assert.strictEqual(
txhex,
'010000000001029d98d81fe2b596fd79e845fa9f38d7e0b6fb73303c40fac604d04df1fa137aee00000000171600142f18e8406c9d210f30c901b24e5feeae78784eb7ffffffff67fb86f310df24e508d40fce9511c7fde4dd4ee91305fd08a074279a70e2cd22000000001716001468dde644410cc789d91a7f36b823f38369755a1cffffffff02780500000000000017a914a3a65daca3064280ae072b9d6773c027b30abace87dc0500000000000017a914850f4dbc255654de2c12c6f6d79cf9cb756cad038702483045022100dc8390a9fd34c31259fa47f9fc182f20d991110ecfd5b58af1cf542fe8de257a022004c2d110da7b8c4127675beccc63b46fd65c706951f090fd381fa3b21d3c5c08012102edd141c5a27a726dda66be10a38b0fd3ccbb40e7c380034aaa43a1656d5f4dd60247304402207c0aef8313d55e72474247daad955979f62e56d1cbac5f2d14b8b022c6ce112602205d9aa3804f04624b12ab8a5ab0214b529c531c2f71c27c6f18aba6502a6ea0a80121030db3c49461a5e539e97bab62ab2b8f88151d1c2376493cf73ef1d02ef60637fd00000000',
);
txhex = hd.createTx(hd.utxo, 0.000005, 0.000001, '3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK');
var tx = bitcoin.Transaction.fromHex(txhex);
assert.equal(tx.ins.length, 1);
assert.equal(tx.outs.length, 2);
assert.equal(tx.outs[0].value, 500);
assert.equal(tx.outs[1].value, 400);
assert.strictEqual(tx.ins.length, 1);
assert.strictEqual(tx.outs.length, 2);
assert.strictEqual(tx.outs[0].value, 500);
assert.strictEqual(tx.outs[1].value, 400);
let chunksIn = bitcoin.script.decompile(tx.outs[0].script);
let toAddress = bitcoin.address.fromOutputScript(chunksIn);
chunksIn = bitcoin.script.decompile(tx.outs[1].script);
let changeAddress = bitcoin.address.fromOutputScript(chunksIn);
assert.equal('3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK', toAddress);
assert.equal(hd._getInternalAddressByIndex(hd.next_free_change_address_index), changeAddress);
assert.strictEqual('3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK', toAddress);
assert.strictEqual(hd._getInternalAddressByIndex(hd.next_free_change_address_index), changeAddress);
//
txhex = hd.createTx(hd.utxo, 0.000015, 0.000001, '3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK');
tx = bitcoin.Transaction.fromHex(txhex);
assert.equal(tx.ins.length, 2);
assert.equal(tx.outs.length, 2);
assert.strictEqual(tx.ins.length, 2);
assert.strictEqual(tx.outs.length, 2);
//
txhex = hd.createTx(hd.utxo, 0.00025, 0.00001, '3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK');
tx = bitcoin.Transaction.fromHex(txhex);
assert.equal(tx.ins.length, 7);
assert.equal(tx.outs.length, 1);
assert.strictEqual(tx.ins.length, 7);
assert.strictEqual(tx.outs.length, 1);
chunksIn = bitcoin.script.decompile(tx.outs[0].script);
toAddress = bitcoin.address.fromOutputScript(chunksIn);
assert.equal('3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK', toAddress);
assert.strictEqual('3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK', toAddress);
// checking that change amount is at least 3x of fee, otherwise screw the change, just add it to fee.
// theres 0.00003 on UTXOs, lets transfer (0.00003 - 100sat), soo fee is equal to change (100 sat)
// which throws @dust error if broadcasted
txhex = hd.createTx(hd.utxo, 0.000028, 0.000001, '3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK');
tx = bitcoin.Transaction.fromHex(txhex);
assert.equal(tx.ins.length, 2);
assert.equal(tx.outs.length, 1); // only 1 output, which means change is neglected
assert.equal(tx.outs[0].value, 2800);
assert.strictEqual(tx.ins.length, 2);
assert.strictEqual(tx.outs.length, 1); // only 1 output, which means change is neglected
assert.strictEqual(tx.outs[0].value, 2800);
});
it('Segwit HD (BIP49) can fetch UTXO', async function() {
let hd = new HDSegwitP2SHWallet();
hd.usedAddresses = ['1Ez69SnzzmePmZX3WpEzMKTrcBF2gpNQ55', '1BiTCHeYzJNMxBLFCMkwYXNdFEdPJP53ZV']; // hacking internals
await hd.fetchUtxo();
assert.equal(hd.utxo.length, 11);
assert.strictEqual(hd.utxo.length, 11);
assert.ok(typeof hd.utxo[0].confirmations === 'number');
assert.ok(hd.utxo[0].txid);
assert.ok(hd.utxo[0].vout);
@ -168,7 +168,7 @@ it('can work with malformed mnemonic', () => {
hd = new HDSegwitP2SHWallet();
hd.setSecret(mnemonic);
let seed2 = hd.getMnemonicToSeedHex();
assert.equal(seed1, seed2);
assert.strictEqual(seed1, seed2);
assert.ok(hd.validateMnemonic());
});
@ -182,28 +182,28 @@ it('can create a Legacy HD (BIP44)', async function() {
let mnemonic = process.env.HD_MNEMONIC_BREAD;
let hd = new HDLegacyP2PKHWallet();
hd.setSecret(mnemonic);
assert.equal(hd.validateMnemonic(), true);
assert.equal(hd._getExternalAddressByIndex(0), '12eQ9m4sgAwTSQoNXkRABKhCXCsjm2jdVG');
assert.equal(hd._getExternalAddressByIndex(1), '1QDCFcpnrZ4yrAQxmbvSgeUC9iZZ8ehcR5');
assert.equal(hd._getInternalAddressByIndex(0), '1KZjqYHm7a1DjhjcdcjfQvYfF2h6PqatjX');
assert.equal(hd._getInternalAddressByIndex(1), '13CW9WWBsWpDUvLtbFqYziWBWTYUoQb4nU');
assert.equal(
assert.strictEqual(hd.validateMnemonic(), true);
assert.strictEqual(hd._getExternalAddressByIndex(0), '12eQ9m4sgAwTSQoNXkRABKhCXCsjm2jdVG');
assert.strictEqual(hd._getExternalAddressByIndex(1), '1QDCFcpnrZ4yrAQxmbvSgeUC9iZZ8ehcR5');
assert.strictEqual(hd._getInternalAddressByIndex(0), '1KZjqYHm7a1DjhjcdcjfQvYfF2h6PqatjX');
assert.strictEqual(hd._getInternalAddressByIndex(1), '13CW9WWBsWpDUvLtbFqYziWBWTYUoQb4nU');
assert.strictEqual(
hd.getXpub(),
'xpub6CQdfC3v9gU86eaSn7AhUFcBVxiGhdtYxdC5Cw2vLmFkfth2KXCMmYcPpvZviA89X6DXDs4PJDk5QVL2G2xaVjv7SM4roWHr1gR4xB3Z7Ps',
);
assert.equal(hd._getExternalWIFByIndex(0), 'L1hqNoJ26YuCdujMBJfWBNfgf4Jo7AcKFvcNcKLoMtoJDdDtRq7Q');
assert.equal(hd._getExternalWIFByIndex(1), 'KyyH4h59iatJWwFfiYPnYkw39SP7cBwydC3xzszsBBXHpfwz9cKb');
assert.equal(hd._getInternalWIFByIndex(0), 'Kx3QkrfemEEV49Mj5oWfb4bsWymboPdstta7eN3kAzop9apxYEFP');
assert.equal(hd._getInternalWIFByIndex(1), 'Kwfg1EDjFapN9hgwafdNPEH22z3vkd4gtG785vXXjJ6uvVWAJGtr');
assert.strictEqual(hd._getExternalWIFByIndex(0), 'L1hqNoJ26YuCdujMBJfWBNfgf4Jo7AcKFvcNcKLoMtoJDdDtRq7Q');
assert.strictEqual(hd._getExternalWIFByIndex(1), 'KyyH4h59iatJWwFfiYPnYkw39SP7cBwydC3xzszsBBXHpfwz9cKb');
assert.strictEqual(hd._getInternalWIFByIndex(0), 'Kx3QkrfemEEV49Mj5oWfb4bsWymboPdstta7eN3kAzop9apxYEFP');
assert.strictEqual(hd._getInternalWIFByIndex(1), 'Kwfg1EDjFapN9hgwafdNPEH22z3vkd4gtG785vXXjJ6uvVWAJGtr');
await hd.fetchBalance();
assert.equal(hd.balance, 0);
assert.strictEqual(hd.balance, 0);
assert.ok(hd._lastTxFetch === 0);
await hd.fetchTransactions();
assert.ok(hd._lastTxFetch > 0);
assert.equal(hd.transactions.length, 4);
assert.equal(hd.next_free_address_index, 1);
assert.equal(hd.next_free_change_address_index, 1);
assert.strictEqual(hd.transactions.length, 4);
assert.strictEqual(hd.next_free_address_index, 1);
assert.strictEqual(hd.next_free_change_address_index, 1);
for (let tx of hd.getTransactions()) {
assert.ok(tx.value === 1000 || tx.value === 1377 || tx.value === -1377 || tx.value === -1000);
@ -211,17 +211,17 @@ it('can create a Legacy HD (BIP44)', async function() {
// checking that internal pointer and async address getter return the same address
let freeAddress = await hd.getAddressAsync();
assert.equal(hd._getExternalAddressByIndex(hd.next_free_address_index), freeAddress);
assert.strictEqual(hd._getExternalAddressByIndex(hd.next_free_address_index), freeAddress);
});
it('Legacy HD (BIP44) can generate addressess based on xpub', async function() {
let xpub = 'xpub6CQdfC3v9gU86eaSn7AhUFcBVxiGhdtYxdC5Cw2vLmFkfth2KXCMmYcPpvZviA89X6DXDs4PJDk5QVL2G2xaVjv7SM4roWHr1gR4xB3Z7Ps';
let hd = new HDLegacyP2PKHWallet();
hd._xpub = xpub;
assert.equal(hd._getExternalAddressByIndex(0), '12eQ9m4sgAwTSQoNXkRABKhCXCsjm2jdVG');
assert.equal(hd._getInternalAddressByIndex(0), '1KZjqYHm7a1DjhjcdcjfQvYfF2h6PqatjX');
assert.equal(hd._getExternalAddressByIndex(1), '1QDCFcpnrZ4yrAQxmbvSgeUC9iZZ8ehcR5');
assert.equal(hd._getInternalAddressByIndex(1), '13CW9WWBsWpDUvLtbFqYziWBWTYUoQb4nU');
assert.strictEqual(hd._getExternalAddressByIndex(0), '12eQ9m4sgAwTSQoNXkRABKhCXCsjm2jdVG');
assert.strictEqual(hd._getInternalAddressByIndex(0), '1KZjqYHm7a1DjhjcdcjfQvYfF2h6PqatjX');
assert.strictEqual(hd._getExternalAddressByIndex(1), '1QDCFcpnrZ4yrAQxmbvSgeUC9iZZ8ehcR5');
assert.strictEqual(hd._getInternalAddressByIndex(1), '13CW9WWBsWpDUvLtbFqYziWBWTYUoQb4nU');
});
it('Legacy HD (BIP44) can create TX', async () => {
@ -239,38 +239,38 @@ it('Legacy HD (BIP44) can create TX', async () => {
await hd.getAddressAsync(); // to refresh internal pointer to next free address
let txhex = hd.createTx(hd.utxo, 0.0008, 0.000005, '3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK');
assert.equal(
assert.strictEqual(
txhex,
'01000000045fbc74110c2d6fcf4d1161a59913fbcd2b6ab3c5a9eb4d0dc0859515cbc8654f030000006b4830450221009be5dbe37db5a8409ddce3570140c95d162a07651b1e48cf39a6a741892adc53022061a25b8024d8f3cb1b94f264245de0c6e9a103ea557ddeb66245b40ec8e9384b012102ad7b2216f3a2b38d56db8a7ee5c540fd12c4bbb7013106eff78cc2ace65aa002ffffffff5fbc74110c2d6fcf4d1161a59913fbcd2b6ab3c5a9eb4d0dc0859515cbc8654f000000006a47304402207106e9fa4e2e35d351fbccc9c0fad3356d85d0cd35a9d7e9cbcefce5440da1e5022073c1905b5927447378c0f660e62900c1d4b2691730799458889fb87d86f5159101210316e84a2556f30a199541633f5dda6787710ccab26771b7084f4c9e1104f47667ffffffff5fbc74110c2d6fcf4d1161a59913fbcd2b6ab3c5a9eb4d0dc0859515cbc8654f020000006a4730440220250b15094096c4d4fe6793da8e45fa118ed057cc2759a480c115e76e23590791022079cdbdc9e630d713395602071e2837ecc1d192a36a24d8ec71bc51d5e62b203b01210316e84a2556f30a199541633f5dda6787710ccab26771b7084f4c9e1104f47667ffffffff5fbc74110c2d6fcf4d1161a59913fbcd2b6ab3c5a9eb4d0dc0859515cbc8654f010000006b483045022100879da610e6ed12c84d55f12baf3bf6222d59b5282502b3c7f4db1d22152c16900220759a1c88583cbdaf7fde21c273ad985dfdf94a2fa85e42ee41dcea2fd69136fd012102ad7b2216f3a2b38d56db8a7ee5c540fd12c4bbb7013106eff78cc2ace65aa002ffffffff02803801000000000017a914a3a65daca3064280ae072b9d6773c027b30abace872c4c0000000000001976a9146ee5e3e66dc73587a3a2d77a1a6c8554fae21b8a88ac00000000',
);
var tx = bitcoin.Transaction.fromHex(txhex);
assert.equal(tx.ins.length, 4);
assert.equal(tx.outs.length, 2);
assert.equal(tx.outs[0].value, 80000); // payee
assert.equal(tx.outs[1].value, 19500); // change
assert.strictEqual(tx.ins.length, 4);
assert.strictEqual(tx.outs.length, 2);
assert.strictEqual(tx.outs[0].value, 80000); // payee
assert.strictEqual(tx.outs[1].value, 19500); // change
let chunksIn = bitcoin.script.decompile(tx.outs[0].script);
let toAddress = bitcoin.address.fromOutputScript(chunksIn);
chunksIn = bitcoin.script.decompile(tx.outs[1].script);
let changeAddress = bitcoin.address.fromOutputScript(chunksIn);
assert.equal('3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK', toAddress);
assert.equal(hd._getInternalAddressByIndex(hd.next_free_change_address_index), changeAddress);
assert.strictEqual('3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK', toAddress);
assert.strictEqual(hd._getInternalAddressByIndex(hd.next_free_change_address_index), changeAddress);
// checking that change amount is at least 3x of fee, otherwise screw the change, just add it to fee.
// theres 0.001 on UTXOs, lets transfer (0.001 - 100sat), soo fee is equal to change (100 sat)
// which throws @dust error if broadcasted
txhex = hd.createTx(hd.utxo, 0.000998, 0.000001, '3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK');
tx = bitcoin.Transaction.fromHex(txhex);
assert.equal(tx.ins.length, 4);
assert.equal(tx.outs.length, 1); // only 1 output, which means change is neglected
assert.equal(tx.outs[0].value, 99800);
assert.strictEqual(tx.ins.length, 4);
assert.strictEqual(tx.outs.length, 1); // only 1 output, which means change is neglected
assert.strictEqual(tx.outs[0].value, 99800);
});
it('Legacy HD (BIP44) can fetch UTXO', async function() {
let hd = new HDLegacyP2PKHWallet();
hd.usedAddresses = ['1Ez69SnzzmePmZX3WpEzMKTrcBF2gpNQ55', '1BiTCHeYzJNMxBLFCMkwYXNdFEdPJP53ZV']; // hacking internals
await hd.fetchUtxo();
assert.equal(hd.utxo.length, 11);
assert.strictEqual(hd.utxo.length, 11);
assert.ok(typeof hd.utxo[0].confirmations === 'number');
assert.ok(hd.utxo[0].txid);
assert.ok(hd.utxo[0].vout);
@ -290,31 +290,31 @@ it('HD breadwallet works', async function() {
let hdBread = new HDLegacyBreadwalletWallet();
hdBread.setSecret(process.env.HD_MNEMONIC_BREAD);
assert.equal(hdBread.validateMnemonic(), true);
assert.equal(hdBread._getExternalAddressByIndex(0), '1ARGkNMdsBE36fJhddSwf8PqBXG3s4d2KU');
assert.equal(hdBread._getInternalAddressByIndex(0), '1JLvA5D7RpWgChb4A5sFcLNrfxYbyZdw3V');
assert.strictEqual(hdBread.validateMnemonic(), true);
assert.strictEqual(hdBread._getExternalAddressByIndex(0), '1ARGkNMdsBE36fJhddSwf8PqBXG3s4d2KU');
assert.strictEqual(hdBread._getInternalAddressByIndex(0), '1JLvA5D7RpWgChb4A5sFcLNrfxYbyZdw3V');
assert.equal(
assert.strictEqual(
hdBread.getXpub(),
'xpub68nLLEi3KERQY7jyznC9PQSpSjmekrEmN8324YRCXayMXaavbdEJsK4gEcX2bNf9vGzT4xRks9utZ7ot1CTHLtdyCn9udvv1NWvtY7HXroh',
);
await hdBread.fetchBalance();
assert.equal(hdBread.balance, 0);
assert.strictEqual(hdBread.balance, 0);
assert.ok(hdBread._lastTxFetch === 0);
await hdBread.fetchTransactions();
assert.ok(hdBread._lastTxFetch > 0);
assert.equal(hdBread.transactions.length, 177);
assert.strictEqual(hdBread.transactions.length, 177);
for (let tx of hdBread.getTransactions()) {
assert.ok(tx.confirmations);
}
assert.equal(hdBread.next_free_address_index, 10);
assert.equal(hdBread.next_free_change_address_index, 118);
assert.strictEqual(hdBread.next_free_address_index, 10);
assert.strictEqual(hdBread.next_free_change_address_index, 118);
// checking that internal pointer and async address getter return the same address
let freeAddress = await hdBread.getAddressAsync();
assert.equal(hdBread._getExternalAddressByIndex(hdBread.next_free_address_index), freeAddress);
assert.strictEqual(hdBread._getExternalAddressByIndex(hdBread.next_free_address_index), freeAddress);
});
it('can convert blockchain.info TX to blockcypher TX format', () => {

42
LightningCustodianWallet.test.js

@ -152,7 +152,7 @@ describe('LightningCustodianWallet', () => {
}
await l2.fetchTransactions();
assert.equal(l2.transactions_raw.length, txLen + 1);
assert.strictEqual(l2.transactions_raw.length, txLen + 1);
// transactions became more after paying an invoice
// now, trying to pay duplicate invoice
@ -165,7 +165,7 @@ describe('LightningCustodianWallet', () => {
}
assert.ok(caughtError);
await l2.fetchTransactions();
assert.equal(l2.transactions_raw.length, txLen + 1);
assert.strictEqual(l2.transactions_raw.length, txLen + 1);
// havent changed since last time
end = +new Date();
if ((end - start) / 1000 > 9) {
@ -191,21 +191,21 @@ describe('LightningCustodianWallet', () => {
await lNew.createAccount(true);
await lNew.authorize();
await lNew.fetchBalance();
assert.equal(lNew.balance, 0);
assert.strictEqual(lNew.balance, 0);
let invoices = await lNew.getUserInvoices();
let invoice = await lNew.addInvoice(1, 'test memo');
let invoices2 = await lNew.getUserInvoices();
assert.equal(invoices2.length, invoices.length + 1);
assert.strictEqual(invoices2.length, invoices.length + 1);
assert.ok(invoices2[0].ispaid === false);
assert.ok(invoices2[0].description);
assert.equal(invoices2[0].description, 'test memo');
assert.strictEqual(invoices2[0].description, 'test memo');
assert.ok(invoices2[0].payment_request);
assert.ok(invoices2[0].timestamp);
assert.ok(invoices2[0].expire_time);
assert.equal(invoices2[0].amt, 1);
assert.strictEqual(invoices2[0].amt, 1);
for (let inv of invoices2) {
assert.equal(inv.type, 'user_invoice');
assert.strictEqual(inv.type, 'user_invoice');
}
await lOld.fetchBalance();
@ -225,11 +225,11 @@ describe('LightningCustodianWallet', () => {
await lOld.fetchBalance();
await lNew.fetchBalance();
assert.equal(oldBalance - lOld.balance, 1);
assert.equal(lNew.balance, 1);
assert.strictEqual(oldBalance - lOld.balance, 1);
assert.strictEqual(lNew.balance, 1);
await lOld.fetchTransactions();
assert.equal(lOld.transactions_raw.length, txLen + 1, 'internal invoice should also produce record in payer`s tx list');
assert.strictEqual(lOld.transactions_raw.length, txLen + 1, 'internal invoice should also produce record in payer`s tx list');
let newTx = lOld.transactions_raw.slice().pop();
assert.ok(typeof newTx.fee !== 'undefined');
assert.ok(newTx.value);
@ -244,8 +244,8 @@ describe('LightningCustodianWallet', () => {
await lNew.payInvoice(invoice);
await lOld.fetchBalance();
await lNew.fetchBalance();
assert.equal(lOld.balance - oldBalance, 1);
assert.equal(lNew.balance, 0);
assert.strictEqual(lOld.balance - oldBalance, 1);
assert.strictEqual(lNew.balance, 0);
// now, paying same internal invoice. should fail:
@ -261,16 +261,16 @@ describe('LightningCustodianWallet', () => {
assert.ok(coughtError);
await lOld.fetchTransactions();
assert.equal(txLen, lOld.transactions_raw.length, 'tx count should not be changed');
assert.equal(invLen, (await lNew.getUserInvoices()).length, 'invoices count should not be changed');
assert.strictEqual(txLen, lOld.transactions_raw.length, 'tx count should not be changed');
assert.strictEqual(invLen, (await lNew.getUserInvoices()).length, 'invoices count should not be changed');
// testing how limiting works:
assert.equal(lNew.user_invoices_raw.length, 1);
assert.strictEqual(lNew.user_invoices_raw.length, 1);
await lNew.addInvoice(666, 'test memo 2');
invoices = await lNew.getUserInvoices(1);
assert.equal(invoices.length, 2);
assert.equal(invoices[0].amt, 1);
assert.equal(invoices[1].amt, 666);
assert.strictEqual(invoices.length, 2);
assert.strictEqual(invoices[0].amt, 1);
assert.strictEqual(invoices[1].amt, 666);
});
it('can pay free amount (tip) invoice', async function() {
@ -319,7 +319,7 @@ describe('LightningCustodianWallet', () => {
let decoded = await l2.decodeInvoice(invoice);
assert.ok(decoded.payment_hash);
assert.ok(decoded.description);
assert.equal(+decoded.num_satoshis, 0);
assert.strictEqual(+decoded.num_satoshis, 0);
await l2.checkRouteInvoice(invoice);
@ -342,10 +342,10 @@ describe('LightningCustodianWallet', () => {
}
await l2.fetchTransactions();
assert.equal(l2.transactions_raw.length, txLen + 1);
assert.strictEqual(l2.transactions_raw.length, txLen + 1);
// transactions became more after paying an invoice
await l2.fetchBalance();
assert.equal(oldBalance - l2.balance, 3);
assert.strictEqual(oldBalance - l2.balance, 3);
});
});

21
NavigationService.js

@ -0,0 +1,21 @@
import { NavigationActions } from 'react-navigation';
let _navigator;
function setTopLevelNavigator(navigatorRef) {
_navigator = navigatorRef;
}
function navigate(routeName, params) {
_navigator.dispatch(
NavigationActions.navigate({
routeName,
params,
}),
);
}
export default {
navigate,
setTopLevelNavigator,
};

18
android/app/BUCK

@ -8,23 +8,13 @@
# - `buck install -r android/app` - compile, install and run application
#
load(":build_defs.bzl", "create_aar_targets", "create_jar_targets")
lib_deps = []
for jarfile in glob(['libs/*.jar']):
name = 'jars__' + jarfile[jarfile.rindex('/') + 1: jarfile.rindex('.jar')]
lib_deps.append(':' + name)
prebuilt_jar(
name = name,
binary_jar = jarfile,
)
create_aar_targets(glob(["libs/*.aar"]))
for aarfile in glob(['libs/*.aar']):
name = 'aars__' + aarfile[aarfile.rindex('/') + 1: aarfile.rindex('.aar')]
lib_deps.append(':' + name)
android_prebuilt_aar(
name = name,
aar = aarfile,
)
create_jar_targets(glob(["libs/*.jar"]))
android_library(
name = "all-libs",

97
android/app/app.iml

@ -23,12 +23,11 @@
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7">
<output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
<output-test url="file://$MODULE_DIR$/build/intermediates/classes/test/debug" />
<output url="file://$MODULE_DIR$/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes" />
<output-test url="file://$MODULE_DIR$/build/intermediates/javac/debugUnitTest/compileDebugUnitTestJavaWithJavac/classes" />
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
@ -36,7 +35,6 @@
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" />
@ -85,18 +83,47 @@
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/shaders" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/build/generated/not_namespaced_r_class_sources" />
<excludeFolder url="file://$MODULE_DIR$/build/generated/source/r" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/annotation_processor_list" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/apk_list" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/build-info" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/builds" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/check-libraries" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/check-manifest" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/checkDebugClasspath" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/checkReleaseClasspath" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/compatible_screen_manifest" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-runtime-classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-verifier" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant-run-apk" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant_run_main_apk_resources" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant_run_merged_manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant_run_split_apk_resources" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaPrecompile" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/javac" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jniLibs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/linked_res_for_bundle" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifest-checker" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/merged_assets" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/merged_manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/module_bundle" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/prebuild" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/processed_res" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/reload-dex" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/resources" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/shader_assets" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/shaders" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/split-apk" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/split_list" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/splits-support" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/transforms" />
@ -105,59 +132,53 @@
</content>
<orderEntry type="jdk" jdkName="Android API 27 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Gradle: com.facebook.fresco:fbcore-1.10.0" level="project" />
<orderEntry type="library" name="Gradle: com.facebook.fresco:fresco-1.10.0" level="project" />
<orderEntry type="library" name="Gradle: com.facebook.fresco:drawee-1.10.0" level="project" />
<orderEntry type="library" name="Gradle: org.webkit:android-jsc:r174650@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-vector-drawable:27.1.1@aar" level="project" />
<orderEntry type="library" name="Gradle: com.facebook.fresco:fresco:1.10.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.facebook.react:react-native:0.57.8@aar" level="project" />
<orderEntry type="library" name="Gradle: android.arch.lifecycle:livedata-core:1.1.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-core-utils:27.1.1@aar" level="project" />
<orderEntry type="library" name="Gradle: android.arch.lifecycle:runtime:1.1.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.facebook.fresco:fbcore:1.10.0@aar" level="project" />
<orderEntry type="library" name="Gradle: android.arch.lifecycle:common:1.1.0@jar" level="project" />
<orderEntry type="library" name="Gradle: com.facebook.fresco:imagepipeline-okhttp3-1.10.0" level="project" />
<orderEntry type="library" name="Gradle: com.google.android.gms:play-services-analytics-impl-16.0.5" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-annotations:27.1.1@jar" level="project" />
<orderEntry type="library" name="Gradle: com.google.android.gms:play-services-tasks-16.0.1" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:animated-vector-drawable-27.1.1" level="project" />
<orderEntry type="library" name="Gradle: android.arch.lifecycle:viewmodel:1.1.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.facebook.fresco:drawee:1.10.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp:3.11.0@jar" level="project" />
<orderEntry type="library" name="Gradle: com.google.android.gms:play-services-ads-identifier-16.0.0" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-compat-27.1.1" level="project" />
<orderEntry type="library" name="Gradle: com.parse.bolts:bolts-tasks:1.4.0@jar" level="project" />
<orderEntry type="library" name="Gradle: android.arch.lifecycle:viewmodel-1.1.0" level="project" />
<orderEntry type="library" name="Gradle: com.google.android.gms:play-services-measurement-base-16.0.4" level="project" />
<orderEntry type="library" name="Gradle: com.google.android.gms:play-services-analytics-16.0.5" level="project" />
<orderEntry type="library" name="Gradle: com.google.android.gms:play-services-stats-16.0.1" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:exifinterface:28.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-v4:27.1.1@aar" level="project" />
<orderEntry type="library" name="Gradle: android.arch.core:runtime:1.1.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.squareup.okhttp3:okhttp-urlconnection:3.11.0@jar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-vector-drawable-27.1.1" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-core-ui-27.1.1" level="project" />
<orderEntry type="library" name="Gradle: com.google.android.gms:play-services-basement-16.0.1" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-media-compat-26.1.0" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-core-utils-27.1.1" level="project" />
<orderEntry type="library" name="Gradle: org.webkit:android-jsc-r174650" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-v4-26.1.0" level="project" />
<orderEntry type="library" name="Gradle: com.google.android.gms:play-services-tagmanager-v4-impl-16.0.5" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:animated-vector-drawable:27.1.1@aar" level="project" />
<orderEntry type="library" name="Gradle: com.facebook.fresco:imagepipeline-okhttp3:1.10.0@aar" level="project" />
<orderEntry type="library" name="Gradle: com.facebook.fresco:imagepipeline-base:1.10.0@aar" level="project" />
<orderEntry type="library" name="Gradle: io.sentry:sentry:1.7.5@jar" level="project" />
<orderEntry type="library" name="Gradle: com.google.android.gms:play-services-base-16.0.1" level="project" />
<orderEntry type="library" name="Gradle: com.squareup.okio:okio:1.14.0@jar" level="project" />
<orderEntry type="library" name="Gradle: javax.inject:javax.inject:1@jar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-fragment-27.1.1" level="project" />
<orderEntry type="library" name="Gradle: android.arch.core:runtime-1.1.0" level="project" />
<orderEntry type="library" name="Gradle: com.facebook.soloader:soloader-0.5.1" level="project" />
<orderEntry type="library" name="Gradle: com.facebook.react:react-native-0.57.5" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-core-ui:27.1.1@aar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-compat:27.1.1@aar" level="project" />
<orderEntry type="library" name="Gradle: com.facebook.infer.annotation:infer-annotation:0.11.2@jar" level="project" />
<orderEntry type="library" name="Gradle: com.facebook.fresco:imagepipeline:1.10.0@aar" level="project" />
<orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.24@jar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:appcompat-v7-27.1.1" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-media-compat:27.1.1@aar" level="project" />
<orderEntry type="library" name="Gradle: com.google.code.findbugs:jsr305:3.0.2@jar" level="project" />
<orderEntry type="library" name="Gradle: android.arch.lifecycle:livedata-core-1.1.0" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-fragment:27.1.1@aar" level="project" />
<orderEntry type="library" name="Gradle: io.sentry:sentry-android:1.7.5@jar" level="project" />
<orderEntry type="library" name="Gradle: com.facebook.fresco:imagepipeline-1.10.0" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:support-annotations:28.0.0@jar" level="project" />
<orderEntry type="library" name="Gradle: com.android.support:appcompat-v7:27.1.1@aar" level="project" />
<orderEntry type="library" name="Gradle: android.arch.core:common:1.1.0@jar" level="project" />
<orderEntry type="library" name="Gradle: com.fasterxml.jackson.core:jackson-core:2.8.7@jar" level="project" />
<orderEntry type="library" name="Gradle: com.facebook.fresco:imagepipeline-base-1.10.0" level="project" />
<orderEntry type="library" name="Gradle: android.arch.lifecycle:runtime-1.1.0" level="project" />
<orderEntry type="library" name="Gradle: com.facebook.soloader:soloader:0.5.1@aar" level="project" />
<orderEntry type="module" module-name="react-native-webview" />
<orderEntry type="module" module-name="react-native-linear-gradient" />
<orderEntry type="module" module-name="react-native-svg" />
<orderEntry type="module" module-name="react-native-sentry" />
<orderEntry type="module" module-name="react-native-google-analytics-bridge" />
<orderEntry type="module" module-name="react-native-haptic-feedback" />
<orderEntry type="module" module-name="react-native-gesture-handler" />
<orderEntry type="module" module-name="react-native-fs" />
<orderEntry type="module" module-name="react-native-prompt-android" />
<orderEntry type="module" module-name="react-native-linear-gradient" />
<orderEntry type="module" module-name="react-native-vector-icons" />
<orderEntry type="module" module-name="react-native-svg" />
<orderEntry type="module" module-name="react-native-device-info" />
<orderEntry type="module" module-name="react-native-randombytes" />
<orderEntry type="module" module-name="react-native-camera" />

49
android/app/build.gradle

@ -77,7 +77,6 @@ project.ext.react = [
]
apply from: "../../node_modules/react-native/react.gradle"
apply from: "../../node_modules/react-native-sentry/sentry.gradle"
/**
* Set this to true to create two separate APKs instead of one:
@ -102,22 +101,12 @@ android {
applicationId "io.bluewallet.bluewallet"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 18
versionName "3.6.0"
versionCode 1
versionName "3.7.0"
ndk {
abiFilters "armeabi-v7a", "x86"
}
}
signingConfigs {
release {
if (project.hasProperty('MYAPP_RELEASE_STORE_FILE')) {
storeFile file(MYAPP_RELEASE_STORE_FILE)
storePassword MYAPP_RELEASE_STORE_PASSWORD
keyAlias MYAPP_RELEASE_KEY_ALIAS
keyPassword MYAPP_RELEASE_KEY_PASSWORD
}
}
}
splits {
abi {
reset()
@ -130,7 +119,6 @@ android {
release {
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
signingConfig signingConfigs.release
}
}
// applicationVariants are e.g. debug, release
@ -138,7 +126,7 @@ android {
variant.outputs.each { output ->
// For each separate APK per architecture, set a unique version code as described here:
// http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits
def versionCodes = ["armeabi-v7a":1, "x86":2]
def versionCodes = ["armeabi-v7a":1, "x86":2, "arm64-v8a": 3]
def abi = output.getFilter(OutputFile.ABI)
if (abi != null) { // null for the universal-debug, universal-release variants
output.versionCodeOverride =
@ -149,22 +137,21 @@ android {
}
dependencies {
compile project(':react-native-webview')
compile project(':react-native-camera')
compile project(':react-native-fs')
compile project(':react-native-gesture-handler')
compile project(':react-native-vector-icons')
compile project(':react-native-svg')
compile project(':react-native-sentry')
compile project(':react-native-randombytes')
compile project(':react-native-prompt-android')
compile project(':react-native-linear-gradient')
compile project(':react-native-haptic-feedback')
compile project(':react-native-google-analytics-bridge')
compile project(':react-native-device-info')
implementation (project(':react-native-camera')) {
exclude group: "com.android.support"
}
implementation project(':@remobile_react-native-qrcode-local-image')
implementation project(':react-native-image-picker')
implementation project(':react-native-webview')
implementation project(':react-native-svg')
implementation project(':react-native-vector-icons')
implementation project(':react-native-sentry')
implementation project(':react-native-randombytes')
implementation project(':react-native-prompt-android')
implementation project(':react-native-linear-gradient')
implementation project(':react-native-haptic-feedback')
implementation project(':react-native-google-analytics-bridge')
implementation project(':react-native-gesture-handler')
implementation project(':react-native-fs')
implementation project(':react-native-device-info')
implementation project(':react-native-camera')
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
implementation "com.facebook.react:react-native:+" // From node_modules

19
android/app/build_defs.bzl

@ -0,0 +1,19 @@
"""Helper definitions to glob .aar and .jar targets"""
def create_aar_targets(aarfiles):
for aarfile in aarfiles:
name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")]
lib_deps.append(":" + name)
android_prebuilt_aar(
name = name,
aar = aarfile,
)
def create_jar_targets(jarfiles):
for jarfile in jarfiles:
name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")]
lib_deps.append(":" + name)
prebuilt_jar(
name = name,
binary_jar = jarfile,
)

11
android/app/src/main/AndroidManifest.xml

@ -3,12 +3,12 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.CAMERA"/>
<application
android:name=".MainApplication"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:allowBackup="false"
android:theme="@style/AppTheme">
<activity
@ -20,13 +20,6 @@
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="bitcoin" />
<data android:scheme="lightning" />
</intent-filter>
</activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
</application>

BIN
android/app/src/main/assets/fonts/FontAwesome5_Brands.ttf

Binary file not shown.

BIN
android/app/src/main/assets/fonts/FontAwesome5_Regular.ttf

Binary file not shown.

BIN
android/app/src/main/assets/fonts/FontAwesome5_Solid.ttf

Binary file not shown.

71
android/app/src/main/java/com/bluewallet/MainApplication.java

@ -1,71 +0,0 @@
package io.bluewallet.bluewallet;
import android.app.Application;
import com.facebook.react.ReactApplication;
import com.reactnativecommunity.webview.RNCWebViewPackage;
import com.oblador.vectoricons.VectorIconsPackage;
import com.horcrux.svg.SvgPackage;
import io.sentry.RNSentryPackage;
import com.bitgo.randombytes.RandomBytesPackage;
import im.shimo.react.prompt.RNPromptPackage;
import com.BV.LinearGradient.LinearGradientPackage;
import com.mkuczera.RNReactNativeHapticFeedbackPackage;
import com.idehub.GoogleAnalyticsBridge.GoogleAnalyticsBridgePackage;
import com.learnium.RNDeviceInfo.RNDeviceInfo;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.soloader.SoLoader;
import org.reactnative.camera.RNCameraPackage;
import com.swmansion.gesturehandler.react.RNGestureHandlerPackage;
import com.rnfs.RNFSPackage;
import java.util.Arrays;
import java.util.List;
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new RNCWebViewPackage(),
new RNFSPackage() ,
new VectorIconsPackage(),
new SvgPackage(),
new RNSentryPackage(),
new RandomBytesPackage(),
new RNPromptPackage(),
new LinearGradientPackage(),
new RNReactNativeHapticFeedbackPackage(),
new GoogleAnalyticsBridgePackage(),
new RNDeviceInfo(),
new RNCameraPackage(),
new RNGestureHandlerPackage()
);
}
@Override
protected String getJSMainModuleName() {
return "index";
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
@Override
public void onCreate() {
super.onCreate();
SoLoader.init(this, /* native exopackage */ false);
}
}

0
android/app/src/main/java/com/bluewallet/MainActivity.java → android/app/src/main/java/io/bluewallet/bluewallet/MainActivity.java

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

@ -1 +0,0 @@
../../../../../../../.././android/app/src/main/java/com/bluewallet/MainApplication.java

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

@ -0,0 +1,92 @@
package io.bluewallet.bluewallet;
import android.app.Application;
import com.facebook.react.ReactApplication;
import com.remobile.qrcodeLocalImage.RCTQRCodeLocalImagePackage;
import com.imagepicker.ImagePickerPackage;
import com.reactnativecommunity.webview.RNCWebViewPackage;
import io.sentry.RNSentryPackage;
import com.bitgo.randombytes.RandomBytesPackage;
import im.shimo.react.prompt.RNPromptPackage;
import com.BV.LinearGradient.LinearGradientPackage;
import com.mkuczera.RNReactNativeHapticFeedbackPackage;
import com.idehub.GoogleAnalyticsBridge.GoogleAnalyticsBridgePackage;
import com.swmansion.gesturehandler.react.RNGestureHandlerPackage;
import com.rnfs.RNFSPackage;
import com.learnium.RNDeviceInfo.RNDeviceInfo;
import org.reactnative.camera.RNCameraPackage;
import io.sentry.RNSentryPackage;
import com.bitgo.randombytes.RandomBytesPackage;
import im.shimo.react.prompt.RNPromptPackage;
import com.BV.LinearGradient.LinearGradientPackage;
import com.mkuczera.RNReactNativeHapticFeedbackPackage;
import com.idehub.GoogleAnalyticsBridge.GoogleAnalyticsBridgePackage;
import com.reactnativecommunity.webview.RNCWebViewPackage;
import com.oblador.vectoricons.VectorIconsPackage;
import com.horcrux.svg.SvgPackage;
import io.sentry.RNSentryPackage;
import com.bitgo.randombytes.RandomBytesPackage;
import im.shimo.react.prompt.RNPromptPackage;
import com.BV.LinearGradient.LinearGradientPackage;
import com.mkuczera.RNReactNativeHapticFeedbackPackage;
import com.idehub.GoogleAnalyticsBridge.GoogleAnalyticsBridgePackage;
import com.learnium.RNDeviceInfo.RNDeviceInfo;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.soloader.SoLoader;
import org.reactnative.camera.RNCameraPackage;
import com.swmansion.gesturehandler.react.RNGestureHandlerPackage;
import com.rnfs.RNFSPackage;
import java.util.Arrays;
import java.util.List;
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new RCTQRCodeLocalImagePackage(),
new ImagePickerPackage(),
new RNCWebViewPackage(),
new RNSentryPackage(),
new RandomBytesPackage(),
new RNPromptPackage(),
new RNReactNativeHapticFeedbackPackage(),
new GoogleAnalyticsBridgePackage(),
new RNDeviceInfo(),
new LinearGradientPackage(),
new RNFSPackage() ,
new VectorIconsPackage(),
new SvgPackage(),
new RNCameraPackage(),
new RNGestureHandlerPackage()
);
}
@Override
protected String getJSMainModuleName() {
return "index";
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
@Override
public void onCreate() {
super.onCreate();
SoLoader.init(this, /* native exopackage */ false);
}
}

2
android/app/src/main/res/values/strings.xml

@ -1,3 +1,3 @@
<resources>
<string name="app_name">Blue Wallet</string>
<string name="app_name">BlueWallet</string>
</resources>

23
android/build.gradle

@ -2,18 +2,18 @@
buildscript {
ext {
buildToolsVersion = "27.0.3"
buildToolsVersion = "28.0.2"
minSdkVersion = 16
compileSdkVersion = 27
targetSdkVersion = 26
supportLibVersion = "27.1.1"
compileSdkVersion = 28
targetSdkVersion = 27
supportLibVersion = "28.0.0"
}
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.4'
classpath 'com.android.tools.build:gradle:3.2.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@ -34,17 +34,6 @@ allprojects {
task wrapper(type: Wrapper) {
gradleVersion = '4.4'
gradleVersion = '4.7'
distributionUrl = distributionUrl.replace("bin", "all")
}
subprojects {
project.configurations.all {
resolutionStrategy.eachDependency { details ->
if (details.requested.group == 'com.android.support'
&& !details.requested.name.contains('multidex') ) {
details.useVersion "26.1.0"
}
}
}
}

93
android/build/intermediates/lint-cache/maven.google/com/android/support/group-index.xml

@ -1,93 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<com.android.support>
<support-compat versions="24.2.0,24.2.1,25.0.0,25.0.1,25.1.0,25.1.1,25.2.0,25.3.0,25.3.1,25.4.0,26.0.0-alpha1,26.0.0-beta1,26.0.0-beta2,26.0.0,26.0.1,26.0.2,26.1.0,27.0.0,27.0.1,27.0.2,27.1.0,27.1.1,28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<leanback-v17 versions="21.0.0,21.0.2,21.0.3,22.0.0,22.1.0,22.1.1,22.2.0,22.2.1,23.0.0,23.0.1,23.1.0,23.1.1,23.2.0,23.2.1,23.3.0,23.4.0,24.0.0-alpha1,24.0.0-alpha2,24.0.0-beta1,24.0.0,24.1.0,24.1.1,24.2.0,24.2.1,25.0.0,25.0.1,25.1.0,25.1.1,25.2.0,25.3.0,25.3.1,25.4.0,26.0.0-alpha1,26.0.0-beta1,26.0.0-beta2,26.0.0,26.0.1,26.0.2,26.1.0,27.0.0,27.0.1,27.0.2,27.1.0,27.1.1,28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<recommendation versions="23.0.1,23.1.0,23.1.1,23.2.0,23.2.1,23.3.0,23.4.0,24.0.0-alpha1,24.0.0-alpha2,24.0.0-beta1,24.0.0,24.1.0,24.1.1,24.2.0,24.2.1,25.0.0,25.0.1,25.1.0,25.1.1,25.2.0,25.3.0,25.3.1,25.4.0,26.0.0-alpha1,26.0.0-beta1,26.0.0-beta2,26.0.0,26.0.1,26.0.2,26.1.0,27.0.0,27.0.1,27.0.2,27.1.0,27.1.1,28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<support-tv-provider versions="26.0.0-alpha1,26.0.0-beta1,26.0.0-beta2,26.0.0,26.0.1,26.0.2,26.1.0,27.0.0,27.0.1,27.0.2,27.1.0,27.1.1,28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<support-vector-drawable versions="23.2.0,23.2.1,23.3.0,23.4.0,24.0.0-alpha1,24.0.0-alpha2,24.0.0-beta1,24.0.0,24.1.0,24.1.1,24.2.0,24.2.1,25.0.0,25.0.1,25.1.0,25.1.1,25.2.0,25.3.0,25.3.1,25.4.0,26.0.0-alpha1,26.0.0-beta1,26.0.0-beta2,26.0.0,26.0.1,26.0.2,26.1.0,27.0.0,27.0.1,27.0.2,27.1.0,27.1.1,28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<recyclerview-v7 versions="21.0.0,21.0.2,21.0.3,22.0.0,22.1.0,22.1.1,22.2.0,22.2.1,23.0.0,23.0.1,23.1.0,23.1.1,23.2.0,23.2.1,23.3.0,23.4.0,24.0.0-alpha1,24.0.0-alpha2,24.0.0-beta1,24.0.0,24.1.0,24.1.1,24.2.0,24.2.1,25.0.0,25.0.1,25.1.0,25.1.1,25.2.0,25.3.0,25.3.1,25.4.0,26.0.0-alpha1,26.0.0-beta1,26.0.0-beta2,26.0.0,26.0.1,26.0.2,26.1.0,27.0.0,27.0.1,27.0.2,27.1.0,27.1.1,28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<preference-leanback-v17 versions="23.0.0,23.0.1,23.1.0,23.1.1,23.2.0,23.2.1,23.3.0,23.4.0,24.0.0-alpha1,24.0.0-alpha2,24.0.0-beta1,24.0.0,24.1.0,24.1.1,24.2.0,24.2.1,25.0.0,25.0.1,25.1.0,25.1.1,25.2.0,25.3.0,25.3.1,25.4.0,26.0.0-alpha1,26.0.0-beta1,26.0.0-beta2,26.0.0,26.0.1,26.0.2,26.1.0,27.0.0,27.0.1,27.0.2,27.1.0,27.1.1,28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<preference-v14 versions="23.0.0,23.0.1,23.1.0,23.1.1,23.2.0,23.2.1,23.3.0,23.4.0,24.0.0-alpha1,24.0.0-alpha2,24.0.0-beta1,24.0.0,24.1.0,24.1.1,24.2.0,24.2.1,25.0.0,25.0.1,25.1.0,25.1.1,25.2.0,25.3.0,25.3.1,25.4.0,26.0.0-alpha1,26.0.0-beta1,26.0.0-beta2,26.0.0,26.0.1,26.0.2,26.1.0,27.0.0,27.0.1,27.0.2,27.1.0,27.1.1,28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<percent versions="22.2.0,23.0.0,23.0.1,23.1.0,23.1.1,23.2.0,23.2.1,23.3.0,23.4.0,24.0.0-alpha1,24.0.0-alpha2,24.0.0-beta1,24.0.0,24.1.0,24.1.1,24.2.0,24.2.1,25.0.0,25.0.1,25.1.0,25.1.1,25.2.0,25.3.0,25.3.1,25.4.0,26.0.0-alpha1,26.0.0-beta1,26.0.0-beta2,26.0.0,26.0.1,26.0.2,26.1.0,27.0.0,27.0.1,27.0.2,27.1.0,27.1.1,28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<support-media-compat versions="24.2.0,24.2.1,25.0.0,25.0.1,25.1.0,25.1.1,25.2.0,25.3.0,25.3.1,25.4.0,26.0.0-alpha1,26.0.0-beta1,26.0.0-beta2,26.0.0,26.0.1,26.0.2,26.1.0,27.0.0,27.0.1,27.0.2,27.1.0,27.1.1,28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<cardview-v7 versions="21.0.0,21.0.2,21.0.3,22.0.0,22.1.0,22.1.1,22.2.0,22.2.1,23.0.0,23.0.1,23.1.0,23.1.1,23.2.0,23.2.1,23.3.0,23.4.0,24.0.0-alpha1,24.0.0-alpha2,24.0.0-beta1,24.0.0,24.1.0,24.1.1,24.2.0,24.2.1,25.0.0,25.0.1,25.1.0,25.1.1,25.2.0,25.3.0,25.3.1,25.4.0,26.0.0-alpha1,26.0.0-beta1,26.0.0-beta2,26.0.0,26.0.1,26.0.2,26.1.0,27.0.0,27.0.1,27.0.2,27.1.0,27.1.1,28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<wearable versions="26.0.0-alpha1"/>
<exifinterface versions="25.1.0,25.1.1,25.2.0,25.3.0,25.3.1,25.4.0,26.0.0-alpha1,26.0.0-beta1,26.0.0-beta2,26.0.0,26.0.1,26.0.2,26.1.0,27.0.0,27.0.1,27.0.2,27.1.0,27.1.1,28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<support-annotations versions="19.1.0,20.0.0,21.0.0,21.0.2,21.0.3,22.0.0,22.1.0,22.1.1,22.2.0,22.2.1,23.0.0,23.0.1,23.1.0,23.1.1,23.2.0,23.2.1,23.3.0,23.4.0,24.0.0-alpha1,24.0.0-alpha2,24.0.0-beta1,24.0.0,24.1.0,24.1.1,24.2.0,24.2.1,25.0.0,25.0.1,25.1.0,25.1.1,25.2.0,25.3.0,25.3.1,25.4.0,26.0.0-alpha1,26.0.0-beta1,26.0.0-beta2,26.0.0,26.0.1,26.0.2,26.1.0,27.0.0,27.0.1,27.0.2,27.1.0,27.1.1,28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<appcompat-v7 versions="18.0.0,19.0.0,19.0.1,19.1.0,20.0.0,21.0.0,21.0.2,21.0.3,22.0.0,22.1.0,22.1.1,22.2.0,22.2.1,23.0.0,23.0.1,23.1.0,23.1.1,23.2.0,23.2.1,23.3.0,23.4.0,24.0.0-alpha1,24.0.0-alpha2,24.0.0-beta1,24.0.0,24.1.0,24.1.1,24.2.0,24.2.1,25.0.0,25.0.1,25.1.0,25.1.1,25.2.0,25.3.0,25.3.1,25.4.0,26.0.0-alpha1,26.0.0-beta1,26.0.0-beta2,26.0.0,26.0.1,26.0.2,26.1.0,27.0.0,27.0.1,27.0.2,27.1.0,27.1.1,28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<palette-v7 versions="21.0.0,21.0.2,21.0.3,22.0.0,22.1.0,22.1.1,22.2.0,22.2.1,23.0.0,23.0.1,23.1.0,23.1.1,23.2.0,23.2.1,23.3.0,23.4.0,24.0.0-alpha1,24.0.0-alpha2,24.0.0-beta1,24.0.0,24.1.0,24.1.1,24.2.0,24.2.1,25.0.0,25.0.1,25.1.0,25.1.1,25.2.0,25.3.0,25.3.1,25.4.0,26.0.0-alpha1,26.0.0-beta1,26.0.0-beta2,26.0.0,26.0.1,26.0.2,26.1.0,27.0.0,27.0.1,27.0.2,27.1.0,27.1.1,28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<multidex-instrumentation versions="1.0.0,1.0.1,1.0.2,1.0.3"/>
<multidex versions="1.0.0,1.0.1,1.0.2,1.0.3"/>
<mediarouter-v7 versions="18.0.0,19.0.0,19.0.1,19.1.0,20.0.0,21.0.0,21.0.2,21.0.3,22.0.0,22.1.0,22.1.1,22.2.0,22.2.1,23.0.0,23.0.1,23.1.0,23.1.1,23.2.0,23.2.1,23.3.0,23.4.0,24.0.0-alpha1,24.0.0-alpha2,24.0.0-beta1,24.0.0,24.1.0,24.1.1,24.2.0,24.2.1,25.0.0,25.0.1,25.2.0,25.3.0,25.3.1,25.4.0,26.0.0-alpha1,26.0.0-beta1,26.0.0-beta2,26.0.0,26.0.1,26.0.2,26.1.0,27.0.0,27.0.1,27.0.2,27.1.0,27.1.1,28.0.0-alpha1,28.0.0-alpha3,28.0.0-alpha4,28.0.0-alpha5,28.0.0-beta01,28.0.0"/>
<preference-v7 versions="23.0.0,23.0.1,23.1.0,23.1.1,23.2.0,23.2.1,23.3.0,23.4.0,24.0.0-alpha1,24.0.0-alpha2,24.0.0-beta1,24.0.0,24.1.0,24.1.1,24.2.0,24.2.1,25.0.0,25.0.1,25.1.0,25.1.1,25.2.0,25.3.0,25.3.1,25.4.0,26.0.0-alpha1,26.0.0-beta1,26.0.0-beta2,26.0.0,26.0.1,26.0.2,26.1.0,27.0.0,27.0.1,27.0.2,27.1.0,27.1.1,28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<support-dynamic-animation versions="25.3.0,25.3.1,25.4.0,26.0.0-beta1,26.0.0-beta2,26.0.0,26.0.1,26.0.2,26.1.0,27.0.0,27.0.1,27.0.2,27.1.0,27.1.1,28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<support-fragment versions="24.2.0,24.2.1,25.0.0,25.0.1,25.1.0,25.1.1,25.2.0,25.3.0,25.3.1,25.4.0,26.0.0-alpha1,26.0.0-beta1,26.0.0-beta2,26.0.0,26.0.1,26.0.2,26.1.0,27.0.0,27.0.1,27.0.2,27.1.0,27.1.1,28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<design versions="22.2.0,22.2.1,23.0.0,23.0.1,23.1.0,23.1.1,23.2.0,23.2.1,23.3.0,23.4.0,24.0.0-alpha1,24.0.0-alpha2,24.0.0-beta1,24.0.0,24.1.0,24.1.1,24.2.0,24.2.1,25.0.0,25.0.1,25.1.0,25.1.1,25.2.0,25.3.0,25.3.1,25.4.0,26.0.0-alpha1,26.0.0-beta1,26.0.0-beta2,26.0.0,26.0.1,26.0.2,26.1.0,27.0.0,27.0.1,27.0.2,27.1.0,27.1.1,28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<transition versions="24.2.0,24.2.1,25.0.0,25.0.1,25.1.0,25.1.1,25.2.0,25.3.0,25.3.1,25.4.0,26.0.0-alpha1,26.0.0-beta1,26.0.0-beta2,26.0.0,26.0.1,26.0.2,26.1.0,27.0.0,27.0.1,27.0.2,27.1.0,27.1.1,28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<customtabs versions="23.0.0,23.0.1,23.1.0,23.1.1,23.2.0,23.2.1,23.3.0,23.4.0,24.0.0-alpha1,24.0.0-alpha2,24.0.0-beta1,24.0.0,24.1.0,24.1.1,24.2.0,24.2.1,25.0.0,25.0.1,25.1.0,25.1.1,25.2.0,25.3.0,25.3.1,25.4.0,26.0.0-alpha1,26.0.0-beta1,26.0.0-beta2,26.0.0,26.0.1,26.0.2,26.1.0,27.0.0,27.0.1,27.0.2,27.1.0,27.1.1,28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<support-core-ui versions="24.2.0,24.2.1,25.0.0,25.0.1,25.1.0,25.1.1,25.2.0,25.3.0,25.3.1,25.4.0,26.0.0-alpha1,26.0.0-beta1,26.0.0-beta2,26.0.0,26.0.1,26.0.2,26.1.0,27.0.0,27.0.1,27.0.2,27.1.0,27.1.1,28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<gridlayout-v7 versions="13.0.0,18.0.0,19.0.0,19.0.1,19.1.0,20.0.0,21.0.0,21.0.2,21.0.3,22.0.0,22.1.0,22.1.1,22.2.0,22.2.1,23.0.0,23.0.1,23.1.0,23.1.1,23.2.0,23.2.1,23.3.0,23.4.0,24.0.0-alpha1,24.0.0-alpha2,24.0.0-beta1,24.0.0,24.1.0,24.1.1,24.2.0,24.2.1,25.0.0,25.0.1,25.1.0,25.1.1,25.2.0,25.3.0,25.3.1,25.4.0,26.0.0-alpha1,26.0.0-beta1,26.0.0-beta2,26.0.0,26.0.1,26.0.2,26.1.0,27.0.0,27.0.1,27.0.2,27.1.0,27.1.1,28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<animated-vector-drawable versions="23.2.0,23.2.1,23.3.0,23.4.0,24.0.0-alpha1,24.0.0-alpha2,24.0.0-beta1,24.0.0,24.1.0,24.1.1,24.2.0,24.2.1,25.0.0,25.0.1,25.1.0,25.1.1,25.2.0,25.3.0,25.3.1,25.4.0,26.0.0-alpha1,26.0.0-beta1,26.0.0-beta2,26.0.0,26.0.1,26.0.2,26.1.0,27.0.0,27.0.1,27.0.2,27.1.0,27.1.1,28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<support-core-utils versions="24.2.0,24.2.1,25.0.0,25.0.1,25.1.0,25.1.1,25.2.0,25.3.0,25.3.1,25.4.0,26.0.0-alpha1,26.0.0-beta1,26.0.0-beta2,26.0.0,26.0.1,26.0.2,26.1.0,27.0.0,27.0.1,27.0.2,27.1.0,27.1.1,28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<support-v13 versions="13.0.0,18.0.0,19.0.0,19.0.1,19.1.0,20.0.0,21.0.0,21.0.2,21.0.3,22.0.0,22.1.0,22.1.1,22.2.0,22.2.1,23.0.0,23.0.1,23.1.0,23.1.1,23.2.0,23.2.1,23.3.0,23.4.0,24.0.0-alpha1,24.0.0-alpha2,24.0.0-beta1,24.0.0,24.1.0,24.1.1,24.2.0,24.2.1,25.0.0,25.0.1,25.1.0,25.1.1,25.2.0,25.3.0,25.3.1,25.4.0,26.0.0-alpha1,26.0.0-beta1,26.0.0-beta2,26.0.0,26.0.1,26.0.2,26.1.0,27.0.0,27.0.1,27.0.2,27.1.0,27.1.1,28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<instantvideo versions="26.0.0-alpha1"/>
<support-v4 versions="13.0.0,18.0.0,19.0.0,19.0.1,19.1.0,20.0.0,21.0.0,21.0.2,21.0.3,22.0.0,22.1.0,22.1.1,22.2.0,22.2.1,23.0.0,23.0.1,23.1.0,23.1.1,23.2.0,23.2.1,23.3.0,23.4.0,24.0.0-alpha1,24.0.0-alpha2,24.0.0-beta1,24.0.0,24.1.0,24.1.1,24.2.0,24.2.1,25.0.0,25.0.1,25.1.0,25.1.1,25.2.0,25.3.0,25.3.1,25.4.0,26.0.0-alpha1,26.0.0-beta1,26.0.0-beta2,26.0.0,26.0.1,26.0.2,26.1.0,27.0.0,27.0.1,27.0.2,27.1.0,27.1.1,28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<support-emoji versions="26.0.0-beta1,26.0.0-beta2,26.0.0,26.0.1,26.0.2,26.1.0,27.0.0,27.0.1,27.0.2,27.1.0,27.1.1,28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<wear versions="26.0.0-beta1,26.0.0-beta2,26.0.0,26.0.1,26.0.2,26.1.0,27.0.0,27.0.1,27.0.2,27.1.0,27.1.1,28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<support-emoji-appcompat versions="26.0.0-beta1,26.0.0-beta2,26.0.0,26.0.1,26.0.2,26.1.0,27.0.0,27.0.1,27.0.2,27.1.0,27.1.1,28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<support-emoji-bundled versions="26.0.0-beta1,26.0.0-beta2,26.0.0,26.0.1,26.0.2,26.1.0,27.0.0,27.0.1,27.0.2,27.1.0,27.1.1,28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<support-content versions="27.0.0,27.0.1,27.0.2,27.1.0,27.1.1,28.0.0-alpha1"/>
<design-bottomnavigation versions="28.0.0-alpha1"/>
<design-button versions="28.0.0-alpha1"/>
<design-circularreveal-cardview versions="28.0.0-alpha1"/>
<design-bottomappbar versions="28.0.0-alpha1"/>
<design-card versions="28.0.0-alpha1"/>
<design-shape versions="28.0.0-alpha1"/>
<design-drawable versions="28.0.0-alpha1"/>
<design-bottomsheet versions="28.0.0-alpha1"/>
<design-floatingactionbutton versions="28.0.0-alpha1"/>
<design-circularreveal-coordinatorlayout versions="28.0.0-alpha1"/>
<design-textfield versions="28.0.0-alpha1"/>
<design-stateful versions="28.0.0-alpha1"/>
<design-circularreveal versions="28.0.0-alpha1"/>
<design-expandable versions="28.0.0-alpha1"/>
<design-navigation versions="28.0.0-alpha1"/>
<design-dialog versions="28.0.0-alpha1"/>
<design-canvas versions="28.0.0-alpha1"/>
<design-tabs versions="28.0.0-alpha1"/>
<design-chip versions="28.0.0-alpha1"/>
<design-snackbar versions="28.0.0-alpha1"/>
<design-theme versions="28.0.0-alpha1"/>
<design-math versions="28.0.0-alpha1"/>
<design-transformation versions="28.0.0-alpha1"/>
<design-widget versions="28.0.0-alpha1"/>
<design-animation versions="28.0.0-alpha1"/>
<design-typography versions="28.0.0-alpha1"/>
<design-color versions="28.0.0-alpha1"/>
<design-internal versions="28.0.0-alpha1"/>
<design-resources versions="28.0.0-alpha1"/>
<design-ripple versions="28.0.0-alpha1"/>
<coordinatorlayout versions="28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<collections versions="28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<slidingpanelayout versions="28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<asynclayoutinflater versions="28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<slices-view versions="28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<recyclerview-selection versions="28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<viewpager versions="28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<cursoradapter versions="28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<localbroadcastmanager versions="28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<heifwriter versions="28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<customview versions="28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<print versions="28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<slices-builders versions="28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<interpolator versions="28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<slices-core versions="28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<loader versions="28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<swiperefreshlayout versions="28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<drawerlayout versions="28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<documentfile versions="28.0.0-alpha1,28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<webkit versions="28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<car versions="28.0.0-alpha3,28.0.0-alpha4,28.0.0-alpha5"/>
<versionedparcelable versions="28.0.0-alpha3,28.0.0-beta01,28.0.0-rc01,28.0.0-rc02,28.0.0"/>
<media2 versions="28.0.0-alpha01,28.0.0-alpha02,28.0.0-alpha03"/>
</com.android.support>

120
android/build/intermediates/lint-cache/maven.google/master-index.xml

@ -1,120 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
<com.android.support.constraint/>
<com.android.databinding/>
<com.android.support/>
<com.android.support.test/>
<com.android.support.test.janktesthelper/>
<com.android.support.test.uiautomator/>
<com.android.support.test.espresso/>
<android.arch.persistence.room/>
<android.arch.lifecycle/>
<android.arch.core/>
<com.google.android.instantapps/>
<com.google.android.instantapps.thirdpartycompat/>
<com.android.java.tools.build/>
<com.android.tools/>
<com.android.tools.layoutlib/>
<com.android.tools.ddms/>
<com.android.tools.external.com-intellij/>
<com.android.tools.build/>
<com.android.tools.analytics-library/>
<com.android.tools.internal.build.test/>
<com.android.tools.lint/>
<com.android.tools.external.org-jetbrains/>
<com.android.support.test.espresso.idling/>
<com.android.support.test.services/>
<com.google.firebase/>
<com.google.android.gms/>
<com.google.gms/>
<android.arch.paging/>
<com.crashlytics.sdk.android/>
<io.fabric.sdk.android/>
<android.arch.persistence/>
<com.google.android.wearable/>
<com.google.android.support/>
<com.android.installreferrer/>
<com.google.ar/>
<androidx.core/>
<com.google.android.things/>
<com.android.tools.build.jetifier/>
<tools.base.build-system.debug/>
<androidx.databinding/>
<androidx.constraintlayout/>
<org.chromium.net/>
<com.google.android.play/>
<androidx.multidex/>
<com.google.android.material/>
<androidx.test.services/>
<androidx.test.janktesthelper/>
<androidx.test/>
<androidx.test.espresso/>
<androidx.test.espresso.idling/>
<androidx.test.uiautomator/>
<androidx.room/>
<androidx.paging/>
<androidx.lifecycle/>
<androidx.sqlite/>
<androidx.arch.core/>
<android.arch.work/>
<android.arch.navigation/>
<androidx.mediarouter/>
<androidx.percentlayout/>
<androidx.emoji/>
<androidx.cardview/>
<androidx.preference/>
<androidx.wear/>
<androidx.legacy/>
<androidx.documentfile/>
<androidx.car/>
<androidx.swiperefreshlayout/>
<androidx.leanback/>
<androidx.appcompat/>
<androidx.customview/>
<androidx.gridlayout/>
<androidx.vectordrawable/>
<androidx.heifwriter/>
<androidx.transition/>
<androidx.print/>
<androidx.viewpager/>
<androidx.annotation/>
<androidx.exifinterface/>
<androidx.dynamicanimation/>
<androidx.browser/>
<androidx.localbroadcastmanager/>
<androidx.asynclayoutinflater/>
<androidx.contentpager/>
<androidx.slidingpanelayout/>
<androidx.cursoradapter/>
<androidx.media/>
<androidx.loader/>
<androidx.interpolator/>
<androidx.coordinatorlayout/>
<androidx.fragment/>
<androidx.tvprovider/>
<androidx.slice/>
<androidx.collection/>
<androidx.recommendation/>
<androidx.drawerlayout/>
<androidx.recyclerview/>
<androidx.webkit/>
<androidx.palette/>
<com.google.ar.sceneform/>
<com.google.ar.sceneform.ux/>
<androidx.test.ext/>
<com.google.android.ads.consent/>
<androidx.versionedparcelable/>
<androidx.media2/>
<com.google.ads.afsn/>
<com.google.android.ads/>
<androidx.biometric/>
<androidx.concurrent/>
<androidx.activity/>
<com.android.tools.apkparser/>
<com.android.tools.pixelprobe/>
<androidx.textclassifier/>
<androidx.remotecallback/>
<com.android.tools.chunkio/>
<com.android.tools.fakeadbserver/>
<androidx.savedstate/>
</metadata>

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.4-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.7-all.zip

2
android/metadata/en-US/full_description.txt

@ -1,6 +1,6 @@
Store, send and receive bitcoin with the wallet focus on security and simplicity.
On Blue Wallet you own you private keys.
On BlueWallet you own you private keys.
You can instantly transact with anyone in the world and transform the financial system right from your pocket.

18
android/settings.gradle

@ -1,16 +1,14 @@
rootProject.name = 'BlueWallet'
include ':@remobile_react-native-qrcode-local-image'
project(':@remobile_react-native-qrcode-local-image').projectDir = new File(rootProject.projectDir, '../node_modules/@remobile/react-native-qrcode-local-image/android')
include ':react-native-image-picker'
project(':react-native-image-picker').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-image-picker/android')
include ':react-native-webview'
project(':react-native-webview').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webview/android')
include ':react-native-camera'
project(':react-native-camera').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-camera/android')
include ':react-native-fs'
project(':react-native-fs').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fs/android')
include ':react-native-gesture-handler'
project(':react-native-gesture-handler').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-gesture-handler/android')
include ':react-native-vector-icons'
project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android')
include ':react-native-svg'
project(':react-native-svg').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-svg/android')
include ':react-native-vector-icons'
project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android')
include ':react-native-sentry'
project(':react-native-sentry').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-sentry/android')
include ':react-native-randombytes'
@ -23,6 +21,10 @@ include ':react-native-haptic-feedback'
project(':react-native-haptic-feedback').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-haptic-feedback/android')
include ':react-native-google-analytics-bridge'
project(':react-native-google-analytics-bridge').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-google-analytics-bridge/android')
include ':react-native-gesture-handler'
project(':react-native-gesture-handler').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-gesture-handler/android')
include ':react-native-fs'
project(':react-native-fs').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fs/android')
include ':react-native-device-info'
project(':react-native-device-info').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-device-info/android')
include ':react-native-camera'

5
app.json

@ -1,7 +1,4 @@
{
"displayName": "Blue Wallet",
"name": "BlueWallet",
"ios": {
"buildNumber": "118"
}
"displayName": "BlueWallet"
}

3
babel.config.js

@ -0,0 +1,3 @@
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
};

12
class/lightning-custodian-wallet.js

@ -7,8 +7,8 @@ export class LightningCustodianWallet extends LegacyWallet {
static type = 'lightningCustodianWallet';
static typeReadable = 'Lightning';
constructor() {
super();
constructor(props) {
super(props);
this.setBaseURI(); // no args to init with default value
this.init();
this.refresh_token = '';
@ -49,13 +49,13 @@ export class LightningCustodianWallet extends LegacyWallet {
}
timeToRefreshBalance() {
// lndhub calls are cheap, so why not refresh constantly
return true;
// only manual refresh for now
return false;
}
timeToRefreshTransaction() {
// lndhub calls are cheap, so why not refresh the list constantly
return true;
// only manual refresh for now
return false;
}
static fromJson(param) {

79
class/walletGradient.js

@ -0,0 +1,79 @@
import { LegacyWallet } from './legacy-wallet';
import { HDSegwitP2SHWallet } from './hd-segwit-p2sh-wallet';
import { LightningCustodianWallet } from './lightning-custodian-wallet';
import { HDLegacyBreadwalletWallet } from './hd-legacy-breadwallet-wallet';
import { HDLegacyP2PKHWallet } from './hd-legacy-p2pkh-wallet';
import { WatchOnlyWallet } from './watch-only-wallet';
export default class WalletGradient {
static defaultGradients = ['#65ceef', '#68bbe1'];
static watchOnlyWallet = ['#7d7d7d', '#4a4a4a'];
static legacyWallet = ['#40fad1', '#15be98'];
static hdLegacyP2PKHWallet = ['#e36dfa', '#bd10e0'];
static hdLegacyBreadWallet = ['#fe6381', '#f99c42'];
static hdSegwitP2SHWallet = ['#c65afb', '#9053fe'];
static lightningCustodianWallet = ['#f1be07', '#f79056'];
static createWallet = ['#eef0f4', '#eef0f4'];
static gradientsFor(type) {
let gradient;
switch (type) {
case WatchOnlyWallet.type:
gradient = WalletGradient.watchOnlyWallet;
break;
case LegacyWallet.type:
gradient = WalletGradient.legacyWallet;
break;
case HDLegacyP2PKHWallet.type:
gradient = WalletGradient.hdLegacyP2PKHWallet;
break;
case HDLegacyBreadwalletWallet.type:
gradient = WalletGradient.hdLegacyBreadWallet;
break;
case HDSegwitP2SHWallet.type:
gradient = WalletGradient.hdSegwitP2SHWallet;
break;
case LightningCustodianWallet.type:
gradient = WalletGradient.lightningCustodianWallet;
break;
case 'CreateWallet':
gradient = WalletGradient.createWallet;
break;
default:
gradient = WalletGradient.defaultGradients;
break;
}
return gradient;
}
static headerColorFor(type) {
let gradient;
switch (type) {
case WatchOnlyWallet.type:
gradient = WalletGradient.watchOnlyWallet;
break;
case LegacyWallet.type:
gradient = WalletGradient.legacyWallet;
break;
case HDLegacyP2PKHWallet.type:
gradient = WalletGradient.hdLegacyP2PKHWallet;
break;
case HDLegacyBreadwalletWallet.type:
gradient = WalletGradient.hdLegacyBreadWallet;
break;
case HDSegwitP2SHWallet.type:
gradient = WalletGradient.hdSegwitP2SHWallet;
break;
case LightningCustodianWallet.type:
gradient = WalletGradient.lightningCustodianWallet;
break;
case 'CreateWallet':
gradient = WalletGradient.createWallet;
break;
default:
gradient = WalletGradient.defaultGradients;
break;
}
return gradient[0];
}
}

4
events.js

@ -36,10 +36,6 @@ EV.enum = {
// changed (usually for current wallet)
REMOTE_TRANSACTIONS_COUNT_CHANGED: 'REMOTE_TRANSACTIONS_COUNT_CHANGED',
// emitted when QR scanner scanned address that should be used in CREATE TRANSACTION screen
// thus, previous screen (CREATE TRANSACTION screen) will update it's input content
CREATE_TRANSACTION_NEW_DESTINATION_ADDRESS: 'CREATE_TRANSACTION_NEW_DESTINATION_ADDRESS',
// RECEIVE_ADDRESS_CHANGED: 'RECEIVE_ADDRESS_CHANGED',
};

1355
ios/BlueWallet.xcodeproj/project.pbxproj

File diff suppressed because it is too large

2
ios/BlueWallet/AppDelegate.h

@ -1,5 +1,5 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

17
ios/BlueWallet/AppDelegate.m

@ -1,5 +1,5 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
@ -23,14 +23,12 @@
NSURL *jsCodeLocation;
jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
moduleName:@"BlueWallet"
initialProperties:nil
launchOptions:launchOptions];
[RNSentry installWithRootView:rootView];
rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
rootView.backgroundColor = [UIColor blackColor];
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [UIViewController new];
@ -40,11 +38,8 @@ RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
return YES;
}
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
return [RCTLinkingManager application:application openURL:url
sourceApplication:sourceApplication annotation:annotation];
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
return [RCTLinkingManager application:app openURL:url options:options];
}
@end

10
ios/BlueWallet/Info.plist

@ -5,7 +5,7 @@
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>Blue Wallet</string>
<string>BlueWallet</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>3.6.0</string>
<string>3.7.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
@ -56,7 +56,7 @@
<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>NSMotionUsageDescription</key>
@ -64,9 +64,11 @@
<key>NSPhotoLibraryAddUsageDescription</key>
<string>This alert should not show up as we do not require this data</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This alert should not show up as we do not require this data</string>
<string>In order to import an image for scanning, we need your permission to access your photo library.</string>
<key>NSSpeechRecognitionUsageDescription</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>UIAppFonts</key>
<array>
<string>AntDesign.ttf</string>

2
ios/BlueWallet/main.m

@ -1,5 +1,5 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

2
ios/BlueWalletTests/BlueWalletTests.m

@ -1,5 +1,5 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.

2
ios/BlueWalletTests/Info.plist

@ -7,7 +7,7 @@
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>

2
ios/fastlane/metadata/en-US/description.txt

@ -1,6 +1,6 @@
Store, send and receive bitcoin with the wallet focus on security and simplicity.
On Blue Wallet you own you private keys. A Bitcoin wallet focused on us the users.
On BlueWallet you own you private keys. A Bitcoin wallet focused on us the users.
You can instantly transact with anyone in the world and transform the financial system right from your pocket.

2
ios/fastlane/metadata/es-ES/description.txt

@ -1,6 +1,6 @@
Store, send and receive bitcoin with the wallet focus on security and simplicity.
On Blue Wallet you own you private keys. A Bitcoin wallet focused on us the users.
On BlueWallet you own you private keys. A Bitcoin wallet focused on us the users.
You can instantly transact with anyone in the world and transform the financial system right from your pocket.

2
ios/fastlane/metadata/pt-BR/description.txt

@ -1,6 +1,6 @@
Guardar, enviar e receber bitcoin com uma carteira focada na segurança e simplicidade.
Na Blue Wallet você possui as suas chaves privadas. Uma carteira Bitcoin focada nos usuários.
Na BlueWallet você possui as suas chaves privadas. Uma carteira Bitcoin focada nos usuários.
Você pode instantaneamente transacionar com qualquer pessoa no mundo e transformar o sistema financeiro diretamente do seu bolso.

2
ios/fastlane/metadata/pt-PT/description.txt

@ -1,6 +1,6 @@
Guardar, enviar e receber bitcoin com uma carteira focada na segurança e simplicidade.
Na Blue Wallet você possui as suas chaves privadas. Uma carteira Bitcoin focada nos usuários.
Na BlueWallet você possui as suas chaves privadas. Uma carteira Bitcoin focada nos usuários.
Você pode instantaneamente transacionar com qualquer pessoa no mundo e transformar o sistema financeiro diretamente do seu bolso.

2
loc/cs_CZ.js

@ -12,7 +12,7 @@ module.exports = {
options: 'možnosti',
createBitcoinWallet: 'In order to use a Lightning wallet, a Bitcoin wallet is needed to fund it. Would you like to continue anyway?',
list: {
app_name: 'Blue Wallet',
app_name: 'BlueWallet',
title: 'peněženky',
header: 'Peněženka reprezentuje pár tajného (privátního) klíče a adresy' + 'kterou můžete sdílet, abyste získali mince',
add: 'Přidat peněženku',

2
loc/da_DK.js

@ -12,7 +12,7 @@ module.exports = {
options: 'valgmuligheder',
createBitcoinWallet: 'In order to use a Lightning wallet, a Bitcoin wallet is needed to fund it. Would you like to continue anyway?',
list: {
app_name: 'Blue Wallet',
app_name: 'BlueWallet',
title: 'wallets',
header: 'En wallet består af par af hemmelige (private nøgler) og en adresse' + 'som du kan dele med andre for at modtage coins.',
add: 'Tilføj Wallet',

2
loc/de_DE.js

@ -13,7 +13,7 @@ module.exports = {
createBitcoinWallet:
'In order to use a Lightning wallet, a Bitcoin wallet is needed in order to fund it. Please, create or import a Bitcoin wallet.',
list: {
app_name: 'Blue Wallet',
app_name: 'BlueWallet',
title: 'Wallets',
header:
'Eine Wallet (Brieftasche) spiegelt ein Paar von kryptographischen Schlüssel wider. Einen geheimen und eine Adresse als öffentlichen Schlüssel. Letztern kann man zum Erhalt von Bitcoin teilen.',

2
loc/en.js

@ -13,7 +13,7 @@ module.exports = {
createBitcoinWallet:
'You currently do not have a Bitcoin wallet. In order to fund a Lightning wallet, a Bitcoin wallet needs to be created or imported. Would you like to continue anyway?',
list: {
app_name: 'Blue Wallet',
app_name: 'BlueWallet',
title: 'wallets',
header: 'A wallet represents a pair of a secret (private key) and an address' + 'you can share to receive coins.',
add: 'Add Wallet',

2
loc/es.js

@ -13,7 +13,7 @@ module.exports = {
createBitcoinWallet:
'In order to use a Lightning wallet, a Bitcoin wallet is needed in order to fund it. Would you like to continue anyway?',
list: {
app_name: 'Blue Wallet',
app_name: 'BlueWallet',
title: 'billeteras',
header: 'Un Monedero esta representado con secreto (clave privada) y una dirección' + 'que puedes compartir para recibir monedas.',
add: 'Añadir Carterqa',

2
loc/fr_FR.js

@ -13,7 +13,7 @@ module.exports = {
createBitcoinWallet: 'In order to use a Lightning wallet, a Bitcoin wallet is needed to fund it. Would you like to continue anyway?',
list: {
app_name: 'Blue Wallet',
app_name: 'BlueWallet',
title: 'portefeuilles',
header:
'Un portefeuille represente une paire de clées (publique/privée) et une adresse que vous pouvez partager pour recevoir des transactions.',

54
loc/hr_HR.js

@ -4,16 +4,16 @@ module.exports = {
enter_password: 'Unesi lozinku',
bad_password: 'Kriva lozinka, pokušaj ponovo',
never: 'nikad',
continue: 'Continue',
ok: 'OK',
continue: 'Nastavi',
ok: 'U redu',
},
wallets: {
select_wallet: 'Odaberi volet',
options: 'opcije',
list: {
app_name: 'Blue Wallet',
app_name: 'BlueWallet',
title: 'Voleti',
header: 'Volet je par privatnog ključa (tajna!) i javne adrese' + 'koju slobodno možete dijeliti kada primate novce.',
header: 'Volet je par privatnog ključa (tajna!) i javne adrese ' + 'koju slobodno možete dijeliti kada primate novce.',
add: 'Dodaj volet',
create_a_wallet: 'Stvori novi volet',
create_a_wallet1: 'Ne košta ništa i možete',
@ -33,10 +33,10 @@ module.exports = {
create: 'Stvori',
label_new_segwit: 'Novi SegWit',
label_new_lightning: 'Novi Lightning',
wallet_name: 'ime voleta',
wallet_type: 'tip',
wallet_name: 'ime voleta:',
wallet_type: 'tip:',
or: 'ili',
import_wallet: 'Unesi volet',
import_wallet: 'Unesi vanjski volet',
imported: 'Unešeno',
coming_soon: 'Dolazi uskoro',
lightning: 'Lightning',
@ -53,10 +53,10 @@ module.exports = {
yes_delete: 'Da, briši',
no_cancel: 'Ne, otiaži',
delete: 'Obriši',
save: 'Pohrani',
save: 'Spremi',
delete_this_wallet: 'Obriši ovaj volet',
export_backup: 'Izvoz / bekap',
buy_bitcoin: 'Kupi Bitcoin',
buy_bitcoin: 'Kupovina Bitcoina',
show_xpub: 'Prikaži voletov XPUB',
},
export: {
@ -147,22 +147,22 @@ module.exports = {
satoshi_per_byte: 'Satoshi / byte',
memo: 'Bilješka',
broadcast: 'Objavi',
not_enough_fee: 'Trošak slanja je premal. Povećaj ga.',
not_enough_fee: 'Trošak slanja je premalen. Povećaj ga.',
},
},
receive: {
header: 'Primanje',
header: 'Primi',
details: {
title: 'Pokaži ovu adresu platitelju',
share: 'pokaži',
share: 'podijeli',
copiedToClipboard: 'Kopirano u međuspremnik.',
label: 'Opis',
create: 'Create',
create: 'Stvori',
setAmount: 'Odredi iznos za primiti',
},
},
buyBitcoin: {
header: 'Kupi Bitcoin',
header: 'Kupovina Bitcoina',
tap_your_address: 'Klikni na adresu za spremanje u međuspremnik:',
copied: 'Spremljeno u međuspremnik!',
},
@ -176,26 +176,26 @@ module.exports = {
retype_password: 'Ponovi lozinku',
passwords_do_not_match: 'Lozinke su različite',
encrypt_storage: 'Kriptiraj spremnik',
lightning_settings: 'Lightning settings',
lightning_settings: 'Lightning postavke',
lightning_settings_explain:
'To connect to your own LND node please install LndHub' +
' and put its URL here in settings. Leave blank to use default ' +
'Za spajanje na tvoj vlastiti LND čvor trebaš instalirati LndHub' +
' i upisati njegov URL ovdje. Ostavi prazno za standardni ' +
'ndHub\n (lndhub.io)',
save: 'save',
about: 'Iznos',
save: 'Spremi',
about: 'Informacije',
language: 'Jezik',
currency: 'Valuta',
},
plausibledeniability: {
title: 'Fejk volet',
help:
'U iznimnim okolnostima netko gadan (pa još ako drži oklagiju) te' +
'može neljubazno pritisnuti da mu otkriješ lozinku za ovaj volet.' +
'BlueWallet ti čuva leđa buraz. Nemaš brige. Gledaj.' +
'Stvoriti ćemo dupli volet sa drugačijom lozinkom. Haha, žišku?' +
'Pa kad se ovaj počne pjeniti a ti vidiš da je vrag odnio šalu' +
'ti mu podvali ovaj drugi volet. Eto mu ga. Nek si cucla.',
help2: 'Novi spremnik će biti posve funkcionalan, možeš pohraniti koliko' + 'misliš da je potrebno da izgleda uvjerljivo.',
'Pazi. Netko gadan te može u iznimnim okolnostima (pljačka, prijevremeni izbori, itd.) ' +
'brutalno pritisnuti da mu otkriješ lozinku za svoj volet. ' +
'BlueWallet ti čuva leđa buraz. Nemaš brige. Gledaj, ' +
'stvoriti ćemo fejk volet sa drugačijom lozinkom. Haha, žišku? ' +
'Pa kad se ovaj počne pjeniti, a ti vidiš da je vrag odnio šalu, ' +
'samo mu podvali lozinku za ovaj drugi volet. Eto mu ga. Nek si cucla. ',
help2: 'Novi spremnik će biti posve funkcionalan, možeš pohraniti koliko ' + 'misliš da je potrebno da izgleda uvjerljivo.',
create_fake_storage: 'Stvori fejk enkriptirani spremnik',
go_back: 'Povratak',
create_password: 'Unesi lozinku',
@ -212,6 +212,6 @@ module.exports = {
refill: 'Dopuni',
withdraw: 'Isprazni',
expired: 'Isteklo',
sameWalletAsInvoiceError: 'Ne možeš platiti račun s istim voletom s kojim si račun stvorio, ono.',
sameWalletAsInvoiceError: 'Buraz! Ne možeš platiti račun s istim voletom s kojim si račun stvorio, ono.',
},
};

7
loc/index.js

@ -156,6 +156,13 @@ strings.transactionTimeToReadable = time => {
return dayjs(time).fromNow();
};
strings.transactionTimeToReadableToFuture = time => {
if (time === 0) {
return strings._.never;
}
return dayjs(time).toNow();
};
function removeTrailingZeros(value) {
value = value.toString();

2
loc/nl_NL.js

@ -13,7 +13,7 @@ module.exports = {
createBitcoinWallet:
'Om een Lightning-portemonnee te kunnen gebruiken, is een Bitcoin-portemonnee nodig om deze te financieren. Wil je toch doorgaan?',
list: {
app_name: 'Blue Wallet',
app_name: 'BlueWallet',
title: 'portemonnees',
header: 'Een portemonnee vertegenwoordigt een geheime (privésleutel) en een adres' + 'dat u kunt delen om munten te ontvangen.',
add: 'Portemonnee toevoegen',

2
loc/pt_BR.js

@ -14,7 +14,7 @@ module.exports = {
list: {
tabBarLabel: 'Carteiras',
app_name: 'Blue Wallet',
app_name: 'BlueWallet',
title: 'carteiras',
header: 'Uma carteira representa um par composto de uma chave privada e um endereço que você pode .',
add: 'adicionar wallet',

2
loc/pt_PT.js

@ -13,7 +13,7 @@ module.exports = {
createBitcoinWallet: 'In order to use a Lightning wallet, a Bitcoin wallet is needed to fund it. Would you like to continue anyway?',
list: {
app_name: 'Blue Wallet',
app_name: 'BlueWallet',
title: 'wallets',
header: 'Uma wallet representa um par entre um segredo (chave privada) e um endereço' + 'que pode partilhar para receber Bitcoin.',
add: 'adicionar wallet',

2
models/fiatUnit.js

@ -1,6 +1,7 @@
export const FiatUnit = Object.freeze({
USD: { endPointKey: 'USD', symbol: '$', locale: 'en-US' },
AUD: { endPointKey: 'AUD', symbol: '$', locale: 'en-AU' },
BRL: { endPointKey: 'BRL', symbol: 'R$', locale: 'pt-BR' },
CAD: { endPointKey: 'CAD', symbol: '$', locale: 'en-CA' },
CZK: { endPointKey: 'CZK', symbol: 'Kč', locale: 'cs-CZ' },
CNY: { endPointKey: 'CNY', symbol: '¥', locale: 'zh-CN' },
@ -9,6 +10,7 @@ export const FiatUnit = Object.freeze({
HRK: { endPointKey: 'HRK', symbol: 'HRK', locale: 'hr-HR' },
INR: { endPointKey: 'INR', symbol: '₹', locale: 'hi-HN' },
JPY: { endPointKey: 'JPY', symbol: '¥', locale: 'ja-JP' },
MXN: { endPointKey: 'MXN', symbol: '$', locale: 'es-MX' },
PLN: { endPointKey: 'PLN', symbol: 'zł', locale: 'pl-PL' },
RUB: { endPointKey: 'RUB', symbol: '₽', locale: 'ru-RU' },
SGD: { endPointKey: 'SGD', symbol: 'S$', locale: 'zh-SG' },

5463
package-lock.json

File diff suppressed because it is too large

59
package.json

@ -1,24 +1,24 @@
{
"name": "BlueWallet",
"version": "3.6.0",
"version": "3.7.0",
"devDependencies": {
"babel-eslint": "^8.2.6",
"babel-jest": "23.6.0",
"eslint": "^4.19.1",
"eslint-plugin-babel": "^4.1.2",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-node": "^6.0.1",
"eslint-plugin-promise": "^3.8.0",
"babel-eslint": "^10.0.1",
"babel-jest": "^24.0.0",
"eslint": "^5.12.1",
"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-react": "^7.12.3",
"jest": "23.6.0",
"metro-react-native-babel-preset": "^0.49.1",
"jest": "^24.0.0",
"metro-react-native-babel-preset": "^0.51.1",
"prettier-eslint-cli": "^4.7.1",
"react-test-renderer": "^16.7.0",
"rn-nodeify": "github:tradle/rn-nodeify"
},
"scripts": {
"prepare": "./patches/fix_mangle.sh; git apply patches/minifier.js.patch; git apply patches/minify.js.patch; git apply patches/transaction_builder.js.patch; git apply ./patches/transaction.js.patch; test -f sentry.sh && ./sentry.sh || true",
"clean": "rm -r -f node_modules/",
"prepare": "./patches/fix_mangle.sh; git apply patches/minifier.js.patch; git apply patches/minify.js.patch; git apply patches/transaction_builder.js.patch; git apply ./patches/transaction.js.patch",
"clean": "cd android/; ./gradlew clean; cd ..; rm -r -f /tmp/metro-cache/; rm -r -f node_modules/; npm cache clean --force; npm i; npm start -- --reset-cache",
"start": "node node_modules/react-native/local-cli/cli.js start",
"android": "react-native run-android",
"ios": "react-native run-ios",
@ -35,8 +35,9 @@
}
},
"dependencies": {
"@babel/preset-env": "^7.2.3",
"bignumber.js": "^7.0.0",
"@babel/preset-env": "^7.3.1",
"@remobile/react-native-qrcode-local-image": "^1.0.4",
"bignumber.js": "^8.0.2",
"bip21": "^2.0.2",
"bip39": "^2.5.0",
"bitcoinjs-lib": "^3.3.2",
@ -44,52 +45,54 @@
"buffer-reverse": "^1.0.1",
"crypto-js": "^3.1.9-1",
"dayjs": "^1.8.0",
"eslint-config-prettier": "^2.10.0",
"eslint-config-prettier": "^3.6.0",
"eslint-config-standard": "^12.0.0",
"eslint-config-standard-react": "^7.0.2",
"eslint-plugin-prettier": "^2.6.2",
"eslint-plugin-prettier": "^3.0.1",
"eslint-plugin-standard": "^4.0.0",
"frisbee": "^1.6.4",
"intl": "^1.2.5",
"mocha": "^5.2.0",
"node-libs-react-native": "^1.0.1",
"path-browserify": "0.0.0",
"prettier": "^1.14.2",
"prettier": "^1.16.1",
"process": "^0.11.10",
"prop-types": "^15.6.2",
"react": "^16.7.0",
"react-localization": "^1.0.10",
"react-native": "^0.57.8",
"react-native-camera": "^1.8.0",
"react-native": "^0.58.1",
"react-native-camera": "^1.9.2",
"react-native-custom-qr-codes": "^2.0.0",
"react-native-device-info": "^0.24.3",
"react-native-device-info": "^0.25.1",
"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.12",
"react-native-google-analytics-bridge": "^6.1.2",
"react-native-gesture-handler": "^1.0.15",
"react-native-google-analytics-bridge": "^7.0.0",
"react-native-haptic-feedback": "^1.4.2",
"react-native-image-picker": "^0.28.0",
"react-native-level-fs": "^3.0.1",
"react-native-linear-gradient": "^2.5.3",
"react-native-modal": "^7.0.2",
"react-native-permissions": "^1.1.1",
"react-native-prompt-android": "^0.3.4",
"react-native-qrcode": "^0.2.7",
"react-native-randombytes": "^3.5.1",
"react-native-randombytes": "^3.5.2",
"react-native-rate": "^1.1.6",
"react-native-sentry": "^0.40.2",
"react-native-snap-carousel": "^3.7.4",
"react-native-snap-carousel": "^3.7.5",
"react-native-sortable-list": "0.0.22",
"react-native-svg": "^8.0.10",
"react-native-vector-icons": "^6.0.2",
"react-native-webview": "2.8.0",
"react-native-svg": "^9.0.4",
"react-native-vector-icons": "^6.2.0",
"react-native-webview": "^3.2.1",
"react-native-wkwebview-reborn": "^2.0.0",
"react-navigation": "^3.0.9",
"react-test-render": "^1.1.1",
"readable-stream": "^1.1.14",
"request-promise-native": "^1.0.5",
"secure-random": "^1.1.1",
"stream-browserify": "^1.0.0",
"util": "^0.10.4",
"util": "^0.11.1",
"wif": "^2.0.1"
},
"react-native": {

1
patches/fix_mangle.sh

@ -2,3 +2,4 @@
grep -rl "mangle: { toplevel: true }" ./node_modules/ | xargs sed -i '' -e "s/mangle: { toplevel: true }/mangle: false/g" || true
grep -rl "mangle: {toplevel: true}" ./node_modules/ | xargs sed -i '' -e "s/mangle: {toplevel: true}/mangle: false/g" || true
grep -rl "BASE_MAP.fill(255)" ./node_modules/ | xargs sed -i '' -e "s/BASE_MAP.fill(255)/for (let c = 0 ; c< 256; c++) BASE_MAP[c] = 255;/g" || true
echo fix_mangle.sh done

16
patches/minifier.js.patch

@ -1,15 +1,15 @@
diff --git a/node_modules/metro-minify-uglify/src/minifier.js b/node_modules/metro-minify-uglify/src/minifier.js
index 021df4c..dcea186 100644
index b703ee4..fadc077 100644
--- a/node_modules/metro-minify-uglify/src/minifier.js
+++ b/node_modules/metro-minify-uglify/src/minifier.js
@@ -44,9 +44,7 @@ function minify(_ref) {
reserved = _ref.reserved,
@@ -67,9 +67,7 @@ function minify(_ref) {
config = _ref.config;
const options = _extends({}, config, {
- mangle: _extends({}, config.mangle, {
const options = _objectSpread({}, config, {
- mangle: _objectSpread({}, config.mangle, {
- reserved
- }),
+ mangle: false, // !!!!!!!!!!!!!!!!
sourceMap: _extends({}, config.sourceMap, {
+ mangle: false, // !!!!!!!!!!!!!!!!!!!!!!!!
sourceMap: _objectSpread({}, config.sourceMap, {
content: map
})

448
screen/lnd/browser.js

@ -1,6 +1,7 @@
import React, { Component } from 'react';
import { TouchableOpacity, ActivityIndicator, View, Alert, Dimensions } from 'react-native';
import { TouchableOpacity, ActivityIndicator, View, Platform, Alert, Dimensions } from 'react-native';
import { WebView } from 'react-native-webview';
import WKWebView from 'react-native-wkwebview-reborn';
import { BlueNavigationStyle, SafeBlueArea } from '../../BlueComponents';
import { FormInput } from 'react-native-elements';
import Ionicons from 'react-native-vector-icons/Ionicons';
@ -17,7 +18,7 @@ let bluewalletResponses = {};
// eslint-disable-next-line
var webln = {
enable: function() {
window.postMessage('enable');
window.postMessage(JSON.stringify({ enable: true }));
return new Promise(function(resolve, reject) {
resolve(true);
});
@ -66,166 +67,8 @@ var webln = {
/// /////////////////
/// /////////////////
export default class Browser extends Component {
static navigationOptions = ({ navigation }) => ({
...BlueNavigationStyle(navigation, true),
title: 'Lapp Browser',
headerLeft: null,
});
constructor(props) {
super(props);
if (!props.navigation.getParam('fromSecret')) throw new Error('Invalid param');
if (!props.navigation.getParam('fromWallet')) throw new Error('Invalid param');
this.state = {
url: 'https://bluewallet.io/marketplace/',
pageIsLoading: false,
fromSecret: props.navigation.getParam('fromSecret'),
fromWallet: props.navigation.getParam('fromWallet'),
};
}
render() {
return (
<SafeBlueArea>
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<TouchableOpacity
onPress={() => {
this.webview.goBack();
}}
>
<Ionicons
name={'ios-arrow-round-back'}
size={36}
style={{
color: 'red',
backgroundColor: 'transparent',
paddingLeft: 10,
}}
/>
</TouchableOpacity>
<FormInput
inputStyle={{ color: '#0c2550', maxWidth: width - 150, fontSize: 16 }}
containerStyle={{
maxWidth: width - 150,
borderColor: '#d2d2d2',
borderWidth: 0.5,
backgroundColor: '#f5f5f5',
}}
value={this.state.url}
/>
<TouchableOpacity
onPress={() => {
this.setState({ url: 'https://bluewallet.io/marketplace/' });
}}
>
<Ionicons
name={'ios-home'}
size={36}
style={{
color: 'red',
backgroundColor: 'transparent',
}}
/>
</TouchableOpacity>
<TouchableOpacity
onPress={() => {
this.webview.reload();
}}
>
{(!this.state.pageIsLoading && (
<Ionicons
name={'ios-sync'}
size={36}
style={{
color: 'red',
backgroundColor: 'transparent',
paddingLeft: 15,
}}
/>
)) || (
<View style={{ paddingLeft: 20 }}>
<ActivityIndicator />
</View>
)}
</TouchableOpacity>
</View>
<WebView
ref={ref => (this.webview = ref)}
source={{ uri: this.state.url }}
mixedContentMode={'compatibility'}
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 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 },
);
}
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 },
);
}
}}
injectedJavaScript={`
let alreadyInjected = false;
const injectedParadise = `
/* rules:
no 'let', only 'var'
@ -251,7 +94,7 @@ bluewalletResponses = {};
webln = {
enable : function () {
window.postMessage('enable');
window.postMessage(JSON.stringify({'enable': true}));
return new Promise(function(resolve, reject){
resolve(true);
})
@ -327,6 +170,7 @@ function tryToPay(invoice) {
searching for all bolt11 manually */
setInterval(function() {
window.postMessage('interval');
var searchText = "lnbc";
@ -362,14 +206,288 @@ setInterval(function() {
}, 1000);
`}
`;
export default class Browser extends Component {
static navigationOptions = ({ navigation }) => ({
...BlueNavigationStyle(navigation, true),
title: 'Lapp Browser',
headerLeft: null,
});
constructor(props) {
super(props);
if (!props.navigation.getParam('fromSecret')) throw new Error('Invalid param');
if (!props.navigation.getParam('fromWallet')) throw new Error('Invalid param');
this.state = {
url: 'https://bluewallet.io/marketplace/',
pageIsLoading: false,
fromSecret: props.navigation.getParam('fromSecret'),
fromWallet: props.navigation.getParam('fromWallet'),
};
}
renderWebView = () => {
if (Platform.OS === 'android') {
return (
<WebView
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 => {
this.setState({ pageIsLoading: true });
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}
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 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 },
);
}
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 });
}}
/>
);
}
};
render() {
return (
<SafeBlueArea>
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<TouchableOpacity
onPress={() => {
this.webview.goBack();
}}
>
<Ionicons
name={'ios-arrow-round-back'}
size={36}
style={{
color: 'red',
backgroundColor: 'transparent',
paddingLeft: 10,
}}
/>
</TouchableOpacity>
<FormInput
inputStyle={{ color: '#0c2550', maxWidth: width - 150, fontSize: 16 }}
containerStyle={{
maxWidth: width - 150,
borderColor: '#d2d2d2',
borderWidth: 0.5,
backgroundColor: '#f5f5f5',
}}
value={this.state.url}
/>
<TouchableOpacity
onPress={() => {
processedInvoices = {};
this.setState({ url: 'https://bluewallet.io/marketplace/' });
}}
>
<Ionicons
name={'ios-home'}
size={36}
style={{
color: this.state.weblnEnabled ? 'green' : 'red',
backgroundColor: 'transparent',
}}
/>
</TouchableOpacity>
<TouchableOpacity
onPress={() => {
let reloadUrl = this.state.url;
this.setState({ url: 'about:blank' });
processedInvoices = {};
setTimeout(() => this.setState({ url: reloadUrl }), 500);
// this.webview.reload();
}}
>
{(!this.state.pageIsLoading && (
<Ionicons
name={'ios-sync'}
size={36}
style={{
color: 'red',
backgroundColor: 'transparent',
paddingLeft: 15,
}}
/>
)) || (
<View style={{ paddingLeft: 20 }}>
<ActivityIndicator />
</View>
)}
</TouchableOpacity>
</View>
{this.renderWebView()}
</SafeBlueArea>
);
}

6
screen/lnd/lndCreateInvoice.js

@ -51,11 +51,7 @@ export default class LNDCreateInvoice extends Component {
{this.state.isLoading ? (
<ActivityIndicator />
) : (
<BlueButton
disabled={!(this.state.description.length > 0 && this.state.amount > 0)}
onPress={() => this.createInvoice()}
title={loc.send.details.create}
/>
<BlueButton disabled={!this.state.amount > 0} onPress={() => this.createInvoice()} title={loc.send.details.create} />
)}
</View>
);

36
screen/lnd/lndViewAdditionalInvoiceInformation.js

@ -1,7 +1,15 @@
/* global alert */
import React, { Component } from 'react';
import { Animated, StyleSheet, View, TouchableOpacity, Clipboard, Share } from 'react-native';
import { BlueLoading, SafeBlueArea, BlueButton, BlueNavigationStyle, BlueText, BlueSpacing20 } from '../../BlueComponents';
import { View, Share } from 'react-native';
import {
BlueLoading,
BlueCopyTextToClipboard,
SafeBlueArea,
BlueButton,
BlueNavigationStyle,
BlueText,
BlueSpacing20,
} from '../../BlueComponents';
import PropTypes from 'prop-types';
import { QRCode } from 'react-native-custom-qr-codes';
/** @type {AppStorage} */
@ -16,13 +24,6 @@ export default class LNDViewAdditionalInvoiceInformation extends Component {
state = { walletInfo: undefined };
copyToClipboard = () => {
this.setState({ addressText: loc.receive.details.copiedToClipboard }, () => {
Clipboard.setString(this.state.walletInfo.uris[0]);
setTimeout(() => this.setState({ addressText: this.state.walletInfo.uris[0] }), 1000);
});
};
async componentDidMount() {
const fromWallet = this.props.navigation.getParam('fromWallet');
try {
@ -56,13 +57,9 @@ export default class LNDViewAdditionalInvoiceInformation extends Component {
/>
<BlueSpacing20 />
<BlueText>Open direct channel with this node:</BlueText>
<TouchableOpacity onPress={this.copyToClipboard}>
<Animated.Text style={styles.address} numberOfLines={0}>
{this.state.addressText}
</Animated.Text>
</TouchableOpacity>
<BlueCopyTextToClipboard text={this.state.walletInfo.uris[0]} />
</View>
<View style={{ marginBottom: 24 }}>
<View style={{ marginBottom: 25 }}>
<BlueButton
icon={{
name: 'share-alternative',
@ -83,15 +80,6 @@ export default class LNDViewAdditionalInvoiceInformation extends Component {
}
}
const styles = StyleSheet.create({
address: {
marginVertical: 32,
fontSize: 15,
color: '#9aa0aa',
textAlign: 'center',
},
});
LNDViewAdditionalInvoiceInformation.propTypes = {
navigation: PropTypes.shape({
goBack: PropTypes.function,

48
screen/lnd/lndViewInvoice.js

@ -1,6 +1,14 @@
import React, { Component } from 'react';
import { Animated, StyleSheet, View, TouchableOpacity, Clipboard, Dimensions, Share, ScrollView, BackHandler } from 'react-native';
import { BlueLoading, BlueText, SafeBlueArea, BlueButton, BlueNavigationStyle, BlueSpacing20 } from '../../BlueComponents';
import { View, Dimensions, Share, ScrollView, BackHandler } from 'react-native';
import {
BlueLoading,
BlueText,
SafeBlueArea,
BlueButton,
BlueCopyTextToClipboard,
BlueNavigationStyle,
BlueSpacing20,
} from '../../BlueComponents';
import PropTypes from 'prop-types';
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
import { Icon } from 'react-native-elements';
@ -59,7 +67,7 @@ export default class LNDViewInvoice extends Component {
ReactNativeHapticFeedback.trigger('notificationSuccess', false);
clearInterval(this.fetchInvoiceInterval);
this.fetchInvoiceInterval = undefined;
EV(EV.enum.TRANSACTIONS_COUNT_CHANGED);
EV(EV.enum.REMOTE_TRANSACTIONS_COUNT_CHANGED); // remote because we want to refetch from server tx list and balance
} else {
const currentDate = new Date();
const now = (currentDate.getTime() / 1000) | 0;
@ -88,16 +96,10 @@ export default class LNDViewInvoice extends Component {
}
handleBackButton() {
this.props.navigation.dismiss();
return true;
}
copyToClipboard = () => {
this.setState({ addressText: loc.receive.details.copiedToClipboard }, () => {
Clipboard.setString(this.state.invoice.payment_request);
setTimeout(() => this.setState({ addressText: this.state.invoice.payment_request }), 1000);
});
};
onLayout = () => {
const { height } = Dimensions.get('window');
this.setState({ qrCodeHeight: height > width ? width - 20 : width / 2 });
@ -171,7 +173,6 @@ export default class LNDViewInvoice extends Component {
}
}
}
// Invoice has not expired, nor has it been paid for.
return (
<SafeBlueArea>
@ -181,12 +182,11 @@ export default class LNDViewInvoice extends Component {
flex: 1,
alignItems: 'center',
marginTop: 8,
paddingHorizontal: 16,
justifyContent: 'space-between',
}}
onLayout={this.onLayout}
>
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center', paddingHorizontal: 16 }}>
<QRFast
value={typeof this.state.invoice === 'object' ? invoice.payment_request : invoice}
fgColor={BlueApp.settings.brandingColor}
@ -197,12 +197,10 @@ export default class LNDViewInvoice extends Component {
<BlueSpacing20 />
{invoice && invoice.amt && <BlueText>Please pay {invoice.amt} sats</BlueText>}
{invoice && invoice.description && <BlueText>For: {invoice.description}</BlueText>}
<TouchableOpacity onPress={this.copyToClipboard}>
<Animated.Text style={styles.address} numberOfLines={0}>
{this.state.addressText}
</Animated.Text>
</TouchableOpacity>
{invoice && invoice.hasOwnProperty('description') && invoice.description.length > 0 && (
<BlueText>For: {invoice.description}</BlueText>
)}
<BlueCopyTextToClipboard text={this.state.invoice.payment_request} />
<BlueButton
icon={{
@ -217,8 +215,9 @@ export default class LNDViewInvoice extends Component {
}}
title={loc.receive.details.share}
/>
<BlueSpacing20 />
<BlueButton
buttonStyle={{ backgroundColor: 'white' }}
backgroundColor="#FFFFFF"
icon={{
name: 'info',
type: 'entypo',
@ -235,15 +234,6 @@ export default class LNDViewInvoice extends Component {
}
}
const styles = StyleSheet.create({
address: {
marginVertical: 32,
fontSize: 15,
color: '#9aa0aa',
textAlign: 'center',
},
});
LNDViewInvoice.propTypes = {
navigation: PropTypes.shape({
goBack: PropTypes.function,

82
screen/lnd/scanLndInvoice.js

@ -1,16 +1,22 @@
/* global alert */
import React from 'react';
import { Text, Dimensions, ActivityIndicator, View, TouchableOpacity, TouchableWithoutFeedback, TextInput, Keyboard } from 'react-native';
import { Icon } from 'react-native-elements';
import { Text, ActivityIndicator, View, TouchableWithoutFeedback, Keyboard } from 'react-native';
import PropTypes from 'prop-types';
import { BlueSpacing20, BlueButton, SafeBlueArea, BlueCard, BlueNavigationStyle, BlueBitcoinAmount } from '../../BlueComponents';
import {
BlueSpacing20,
BlueButton,
SafeBlueArea,
BlueCard,
BlueNavigationStyle,
BlueAddressInput,
BlueBitcoinAmount,
} from '../../BlueComponents';
import { LightningCustodianWallet } from '../../class/lightning-custodian-wallet';
import { BitcoinUnit } from '../../models/bitcoinUnits';
/** @type {AppStorage} */
let BlueApp = require('../../BlueApp');
let EV = require('../../events');
let loc = require('../../loc');
const { width } = Dimensions.get('window');
export default class ScanLndInvoice extends React.Component {
static navigationOptions = ({ navigation }) => ({
@ -59,20 +65,12 @@ export default class ScanLndInvoice extends React.Component {
}
async componentDidMount() {
EV(
EV.enum.CREATE_TRANSACTION_NEW_DESTINATION_ADDRESS,
data => {
this.processInvoice(data);
},
true,
);
if (this.props.navigation.state.params.uri) {
this.processTextForInvoice(this.props.navigation.getParam('uri'));
}
}
processInvoice(data) {
processInvoice = data => {
this.setState({ isLoading: true }, async () => {
if (this.ignoreRead) return;
this.ignoreRead = true;
@ -85,6 +83,12 @@ export default class ScanLndInvoice extends React.Component {
return this.props.navigation.goBack();
}
// handling BIP21 w/BOLT11 support
let ind = data.indexOf('lightning=');
if (ind !== -1) {
data = data.substring(ind + 10).split('&')[0];
}
data = data.replace('LIGHTNING:', '').replace('lightning:', '');
console.log(data);
@ -116,7 +120,7 @@ export default class ScanLndInvoice extends React.Component {
alert(Err.message);
}
});
}
};
async pay() {
if (!this.state.hasOwnProperty('decoded')) {
@ -206,53 +210,15 @@ export default class ScanLndInvoice extends React.Component {
/>
<BlueSpacing20 />
<BlueCard>
<View
style={{
flexDirection: 'row',
borderColor: '#d2d2d2',
borderBottomColor: '#d2d2d2',
borderWidth: 1.0,
borderBottomWidth: 0.5,
backgroundColor: '#f5f5f5',
minHeight: 44,
height: 44,
marginHorizontal: 20,
alignItems: 'center',
marginVertical: 8,
borderRadius: 4,
}}
>
<TextInput
<BlueAddressInput
onChangeText={text => {
this.setState({ destination: text });
this.processTextForInvoice(text);
}}
placeholder={loc.wallets.details.destination}
numberOfLines={1}
value={this.state.destination}
style={{ flex: 1, marginHorizontal: 8, minHeight: 33, height: 33 }}
editable={!this.state.isLoading}
onBarScanned={this.processInvoice}
address={this.state.destination}
isLoading={this.state.isLoading}
/>
<TouchableOpacity
disabled={this.state.isLoading}
onPress={() => this.props.navigation.navigate('ScanQrAddress')}
style={{
width: 75,
height: 36,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
backgroundColor: '#bebebe',
borderRadius: 4,
paddingVertical: 4,
paddingHorizontal: 8,
marginHorizontal: 4,
}}
>
<Icon name="qrcode" size={22} type="font-awesome" color="#FFFFFF" />
<Text style={{ color: '#FFFFFF' }}>{loc.send.details.scan}</Text>
</TouchableOpacity>
</View>
<View
style={{
flexDirection: 'row',
@ -269,7 +235,7 @@ export default class ScanLndInvoice extends React.Component {
{this.state.expiresIn !== undefined && (
<Text style={{ color: '#81868e', fontSize: 12, left: 20, top: 10 }}>Expires in: {this.state.expiresIn}</Text>
)}
</BlueCard>
<BlueSpacing20 />
<BlueSpacing20 />
{this.state.isLoading ? (
<View>
@ -283,13 +249,13 @@ export default class ScanLndInvoice extends React.Component {
color: BlueApp.settings.buttonTextColor,
}}
title={'Pay'}
buttonStyle={{ width: 150, left: (width - 150) / 2 - 20 }}
onPress={() => {
this.pay();
}}
disabled={this.shouldDisablePayButton()}
/>
)}
</BlueCard>
</SafeBlueArea>
</TouchableWithoutFeedback>
);

36
screen/receive/details.js

@ -1,8 +1,16 @@
import React, { Component } from 'react';
import { Animated, StyleSheet, View, TouchableOpacity, Clipboard, Share } from 'react-native';
import { View, Share } from 'react-native';
import { QRCode } from 'react-native-custom-qr-codes';
import bip21 from 'bip21';
import { BlueLoading, SafeBlueArea, BlueButton, BlueButtonLink, BlueNavigationStyle, is } from '../../BlueComponents';
import {
BlueLoading,
SafeBlueArea,
BlueCopyTextToClipboard,
BlueButton,
BlueButtonLink,
BlueNavigationStyle,
is,
} from '../../BlueComponents';
import PropTypes from 'prop-types';
/** @type {AppStorage} */
let BlueApp = require('../../BlueApp');
@ -70,13 +78,6 @@ export default class ReceiveDetails extends Component {
}
}
copyToClipboard = () => {
this.setState({ addressText: loc.receive.details.copiedToClipboard }, () => {
Clipboard.setString(this.state.address);
setTimeout(() => this.setState({ addressText: this.state.address }), 1000);
});
};
render() {
console.log('render() receive/details, address,secret=', this.state.address, ',', this.state.secret);
if (this.state.isLoading) {
@ -94,13 +95,9 @@ export default class ReceiveDetails extends Component {
backgroundColor={BlueApp.settings.brandingColor}
logo={require('../../img/qr-code.png')}
/>
<TouchableOpacity onPress={this.copyToClipboard}>
<Animated.Text style={styles.address} numberOfLines={0}>
{this.state.addressText}
</Animated.Text>
</TouchableOpacity>
<BlueCopyTextToClipboard text={this.state.addressText} />
</View>
<View style={{ marginBottom: 24, alignItems: 'center' }}>
<View style={{ flex: 0.2, marginBottom: 24, alignItems: 'center' }}>
<BlueButtonLink
title={loc.receive.details.setAmount}
onPress={() => {
@ -129,15 +126,6 @@ export default class ReceiveDetails extends Component {
}
}
const styles = StyleSheet.create({
address: {
marginVertical: 32,
fontSize: 15,
color: '#9aa0aa',
textAlign: 'center',
},
});
ReceiveDetails.propTypes = {
navigation: PropTypes.shape({
goBack: PropTypes.function,

58
screen/receive/receiveAmount.js

@ -1,21 +1,17 @@
import React, { Component } from 'react';
import {
StyleSheet,
View,
Share,
TextInput,
KeyboardAvoidingView,
Clipboard,
Animated,
TouchableOpacity,
Platform,
Dimensions,
ScrollView,
} from 'react-native';
import { View, Share, TextInput, KeyboardAvoidingView, Platform, Dimensions, ScrollView } from 'react-native';
import { QRCode as QRSlow } from 'react-native-custom-qr-codes';
import QRFast from 'react-native-qrcode';
import bip21 from 'bip21';
import { SafeBlueArea, BlueButton, BlueNavigationStyle, BlueBitcoinAmount, BlueText } from '../../BlueComponents';
import {
SafeBlueArea,
BlueCard,
BlueButton,
BlueNavigationStyle,
BlueBitcoinAmount,
BlueText,
BlueCopyTextToClipboard,
} from '../../BlueComponents';
import PropTypes from 'prop-types';
/** @type {AppStorage} */
let BlueApp = require('../../BlueApp');
@ -52,13 +48,6 @@ export default class ReceiveAmount extends Component {
};
}
copyToClipboard = () => {
this.setState({ addressText: loc.receive.details.copiedToClipboard }, () => {
Clipboard.setString(this.state.bip21);
setTimeout(() => this.setState({ addressText: this.state.address }), 1000);
});
};
determineSize = () => {
if (width > 312) {
return width - 48;
@ -94,8 +83,9 @@ export default class ReceiveAmount extends Component {
editable={!this.state.isLoading}
/>
</View>
<BlueCard>
<BlueButton
title={loc.receive.create}
title={loc.receive.details.create}
onPress={() => {
this.setState({
amountSet: true,
@ -103,6 +93,7 @@ export default class ReceiveAmount extends Component {
});
}}
/>
</BlueCard>
</View>
);
}
@ -132,12 +123,8 @@ export default class ReceiveAmount extends Component {
/>
)}
</View>
<View style={{ marginBottom: 24, alignItems: 'center', justifyContent: 'space-between' }}>
<TouchableOpacity onPress={this.copyToClipboard}>
<Animated.Text style={styles.address} numberOfLines={0}>
{this.state.bip21}
</Animated.Text>
</TouchableOpacity>
<View style={{ alignItems: 'center', justifyContent: 'space-between' }}>
<BlueCopyTextToClipboard text={this.state.bip21} />
</View>
</View>
);
@ -157,11 +144,8 @@ export default class ReceiveAmount extends Component {
{this.state.amountSet ? this.renderWithSetAmount() : this.renderDefault()}
</KeyboardAvoidingView>
{this.state.amountSet && (
<BlueCard>
<BlueButton
buttonStyle={{
alignSelf: 'center',
marginBottom: 24,
}}
icon={{
name: 'share-alternative',
type: 'entypo',
@ -174,6 +158,7 @@ export default class ReceiveAmount extends Component {
}}
title={loc.receive.details.share}
/>
</BlueCard>
)}
</View>
</ScrollView>
@ -181,12 +166,3 @@ export default class ReceiveAmount extends Component {
);
}
}
const styles = StyleSheet.create({
address: {
marginVertical: 32,
fontSize: 15,
color: '#9aa0aa',
textAlign: 'center',
},
});

118
screen/send/details.js

@ -14,7 +14,7 @@ import {
Text,
} from 'react-native';
import { Icon } from 'react-native-elements';
import { BlueNavigationStyle, BlueButton, BlueBitcoinAmount } from '../../BlueComponents';
import { BlueNavigationStyle, BlueButton, BlueBitcoinAmount, BlueAddressInput } from '../../BlueComponents';
import PropTypes from 'prop-types';
import Modal from 'react-native-modal';
import NetworkTransactionFees, { NetworkTransactionFee } from '../../models/networkTransactionFees';
@ -23,7 +23,6 @@ import { BitcoinUnit } from '../../models/bitcoinUnits';
import { HDLegacyP2PKHWallet, HDSegwitP2SHWallet } from '../../class';
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
const bip21 = require('bip21');
let EV = require('../../events');
let BigNumber = require('bignumber.js');
/** @type {AppStorage} */
let BlueApp = require('../../BlueApp');
@ -82,13 +81,15 @@ export default class SendDetails extends Component {
};
}
async componentDidMount() {
EV(EV.enum.CREATE_TRANSACTION_NEW_DESTINATION_ADDRESS, data => {
processAddressData = data => {
this.setState(
{ isLoading: false },
{ isLoading: true },
() => {
data = data.replace('bitcoin:', '');
if (btcAddressRx.test(data) || data.indexOf('bc1') === 0) {
if (BitcoinBIP70TransactionDecode.matchesPaymentURL(data)) {
this.processBIP70Invoice(data);
} else {
const dataWithoutSchema = data.replace('bitcoin:', '');
if (btcAddressRx.test(dataWithoutSchema) || dataWithoutSchema.indexOf('bc1') === 0) {
this.setState({
address: data,
bip70TransactionExpiration: null,
@ -97,11 +98,15 @@ export default class SendDetails extends Component {
} else {
let address, options;
try {
if (!data.toLowerCase().startsWith('bitcoin:')) {
data = `bitcoin:${data}`;
}
const decoded = bip21.decode(data);
address = decoded.address;
options = decoded.options;
} catch (Err) {
console.log(Err);
} catch (error) {
console.log(error);
this.setState({ isLoading: false });
}
console.log(options);
if (btcAddressRx.test(address)) {
@ -112,14 +117,15 @@ export default class SendDetails extends Component {
bip70TransactionExpiration: null,
isLoading: false,
});
} else if (BitcoinBIP70TransactionDecode.matchesPaymentURL(data)) {
this.processBIP70Invoice(data);
}
}
}
},
true,
);
});
};
async componentDidMount() {
let recommendedFees = await NetworkTransactionFees.recommendedFees().catch(response => {
this.setState({
fee: response.halfHourFee,
@ -140,13 +146,25 @@ export default class SendDetails extends Component {
if (BitcoinBIP70TransactionDecode.matchesPaymentURL(this.props.navigation.state.params.uri)) {
this.processBIP70Invoice(this.props.navigation.state.params.uri);
} else {
try {
const { address, amount, memo } = this.decodeBitcoinUri(this.props.navigation.getParam('uri'));
this.setState({ address, amount, memo });
} catch (error) {
console.log(error);
alert('Error: Unable to decode Bitcoin address');
}
}
}
}
}
decodeBitcoinUri(uri) {
try {
let amount = '';
let parsedBitcoinUri = null;
let address = '';
let memo = '';
parsedBitcoinUri = bip21.decode(this.props.navigation.state.params.uri);
parsedBitcoinUri = bip21.decode(uri);
address = parsedBitcoinUri.hasOwnProperty('address') ? parsedBitcoinUri.address : address;
if (parsedBitcoinUri.hasOwnProperty('options')) {
if (parsedBitcoinUri.options.hasOwnProperty('amount')) {
@ -156,13 +174,9 @@ export default class SendDetails extends Component {
memo = parsedBitcoinUri.options.label || memo;
}
}
this.setState({ address, amount, memo });
} catch (error) {
console.log(error);
alert('Error: Unable to decode Bitcoin address');
}
}
}
return { address, amount, memo };
} catch (_) {
return undefined;
}
}
@ -497,56 +511,27 @@ export default class SendDetails extends Component {
amount={this.state.amount}
onChangeText={text => this.setState({ amount: text })}
/>
<View
style={{
flexDirection: 'row',
borderColor: '#d2d2d2',
borderBottomColor: '#d2d2d2',
borderWidth: 1.0,
borderBottomWidth: 0.5,
backgroundColor: '#f5f5f5',
minHeight: 44,
height: 44,
marginHorizontal: 20,
alignItems: 'center',
marginVertical: 8,
borderRadius: 4,
}}
>
<TextInput
<BlueAddressInput
onChangeText={text => {
if (!this.processBIP70Invoice(text)) {
this.setState({ address: text.replace(' ', ''), isLoading: false, bip70TransactionExpiration: null });
this.setState({
address: text.trim().replace('bitcoin:', ''),
isLoading: false,
bip70TransactionExpiration: null,
});
} else {
this.setState({ address: text.replace(' ', ''), isLoading: false, bip70TransactionExpiration: null });
try {
const { address, amount, memo } = this.decodeBitcoinUri(text);
this.setState({ address, amount, memo, isLoading: false, bip70TransactionExpiration: null });
} catch (_) {
this.setState({ address: text.trim(), isLoading: false, bip70TransactionExpiration: null });
}
}
}}
placeholder={loc.send.details.address}
numberOfLines={1}
value={this.state.address}
style={{ flex: 1, marginHorizontal: 8, minHeight: 33 }}
editable={!this.state.isLoading}
onBarScanned={this.processAddressData}
address={this.state.address}
isLoading={this.state.isLoading}
/>
<TouchableOpacity
disabled={this.state.isLoading}
onPress={() => this.props.navigation.navigate('ScanQrAddress')}
style={{
width: 75,
height: 36,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
backgroundColor: '#bebebe',
borderRadius: 4,
paddingVertical: 4,
paddingHorizontal: 8,
marginHorizontal: 4,
}}
>
<Icon name="qrcode" size={22} type="font-awesome" color="#FFFFFF" />
<Text style={{ color: '#FFFFFF' }}>{loc.send.details.scan}</Text>
</TouchableOpacity>
</View>
<View
hide={!this.state.showMemoRow}
style={{
@ -635,8 +620,9 @@ const styles = StyleSheet.create({
SendDetails.propTypes = {
navigation: PropTypes.shape({
goBack: PropTypes.function,
goBack: PropTypes.func,
navigate: PropTypes.func,
getParam: PropTypes.func,
state: PropTypes.shape({
params: PropTypes.shape({
address: PropTypes.string,

27
screen/send/scanQrAddress.js

@ -1,11 +1,9 @@
/* global alert */
import React from 'react';
import { ActivityIndicator, Image, View, TouchableOpacity } from 'react-native';
import PropTypes from 'prop-types';
import Camera from 'react-native-camera';
import Permissions from 'react-native-permissions';
import { SafeBlueArea } from '../../BlueComponents';
let EV = require('../../events');
export default class CameraExample extends React.Component {
static navigationOptions = {
@ -17,18 +15,14 @@ export default class CameraExample extends React.Component {
hasCameraPermission: null,
};
async onBarCodeScanned(ret) {
if (this.ignoreRead) return;
this.ignoreRead = true;
setTimeout(() => {
this.ignoreRead = false;
}, 2000);
this.props.navigation.goBack();
EV(EV.enum.CREATE_TRANSACTION_NEW_DESTINATION_ADDRESS, ret.data);
onBarCodeScanned(ret) {
console.warn(ret);
const onBarScanned = this.props.navigation.getParam('onBarScanned');
onBarScanned(ret.data);
this.props.navigation.goBack(null);
} // end
async componentDidMount() {
componentDidMount() {
Permissions.request('camera').then(response => {
// Response is one of: 'authorized', 'denied', 'restricted', or 'undetermined'
this.setState({ hasCameraPermission: response === 'authorized' });
@ -48,13 +42,11 @@ export default class CameraExample extends React.Component {
if (hasCameraPermission === null) {
return <View />;
} else if (hasCameraPermission === false) {
alert('BlueWallet does not have permission to use your camera.');
this.props.navigation.goBack(null);
return <View />;
} else {
return (
<SafeBlueArea style={{ flex: 1 }}>
<Camera style={{ flex: 1 }} onBarCodeRead={ret => this.onBarCodeScanned(ret)}>
<Camera style={{ flex: 1, justifyContent: 'space-between' }} onBarCodeRead={ret => this.onBarCodeScanned(ret)}>
<TouchableOpacity
style={{ width: 40, height: 80, padding: 14, marginTop: 32 }}
onPress={() => this.props.navigation.goBack(null)}
@ -70,7 +62,8 @@ export default class CameraExample extends React.Component {
CameraExample.propTypes = {
navigation: PropTypes.shape({
goBack: PropTypes.function,
dismiss: PropTypes.function,
goBack: PropTypes.func,
dismiss: PropTypes.func,
getParam: PropTypes.func,
}),
};

6
screen/settings/about.js

@ -51,7 +51,6 @@ export default class About extends Component {
<BlueTextCentered h4>Always backup your keys</BlueTextCentered>
<BlueSpacing20 />
</BlueCard>
<BlueButton
icon={{
@ -109,15 +108,14 @@ export default class About extends Component {
};
Rate.rate(options, success => {
if (success) {
console.warn('User Rated.');
console.log('User Rated.');
}
});
}}
title="Rate Blue Wallet"
title="Rate BlueWallet"
/>
<BlueSpacing20 />
<BlueCard>
<BlueText h3>Built with awesome:</BlueText>
<BlueSpacing20 />
<BlueText h4>* React Native</BlueText>

2
screen/settings/language.js

@ -78,7 +78,7 @@ export default class Language extends Component {
renderItem={this.renderItem}
/>
<BlueCard>
<BlueText>When selecting a new language, restarting Blue Wallet may be required for the change to take effect.</BlueText>
<BlueText>When selecting a new language, restarting BlueWallet may be required for the change to take effect.</BlueText>
</BlueCard>
</SafeBlueArea>
);

6
screen/settings/lightningSettings.js

@ -3,6 +3,7 @@ import { AsyncStorage, View, TextInput, Linking } from 'react-native';
import { AppStorage } from '../../class';
import { BlueLoading, BlueSpacing20, BlueButton, SafeBlueArea, BlueCard, BlueNavigationStyle, BlueText } from '../../BlueComponents';
import PropTypes from 'prop-types';
import { Button } from 'react-native-elements';
import { LightningCustodianWallet } from '../../class/lightning-custodian-wallet';
/** @type {AppStorage} */
let BlueApp = require('../../BlueApp');
@ -56,7 +57,7 @@ export default class LightningSettings extends Component {
<BlueText>{loc.settings.lightning_settings_explain}</BlueText>
</BlueCard>
<BlueButton
<Button
icon={{
name: 'mark-github',
type: 'octicon',
@ -67,6 +68,7 @@ export default class LightningSettings extends Component {
Linking.openURL('https://github.com/BlueWallet/LndHub');
}}
title="github.com/BlueWallet/LndHub"
color={BlueApp.settings.buttonTextColor}
buttonStyle={{
backgroundColor: '#FFFFFF',
}}
@ -92,7 +94,7 @@ export default class LightningSettings extends Component {
value={this.state.URI}
onChangeText={text => this.setState({ URI: text })}
numberOfLines={1}
style={{ flex: 1, marginHorizontal: 8, minHeight: 33, height: 33 }}
style={{ flex: 1, marginHorizontal: 8, minHeight: 36, height: 36 }}
editable={!this.state.isLoading}
underlineColorAndroid="transparent"
/>

2
screen/transactions/details.js

@ -176,7 +176,7 @@ export default class TransactionsDetails extends Component {
</React.Fragment>
)}
{this.state.tx.hasOwnProperty('block_height') && (
{this.state.tx.hasOwnProperty('block_height') && this.state.block_height > 0 && (
<React.Fragment>
<BlueText style={{ fontSize: 16, fontWeight: '500', marginBottom: 4 }}>Block Height</BlueText>
<BlueText style={{ marginBottom: 26, color: 'grey' }}>{this.state.tx.block_height}</BlueText>

3
screen/wallets/add.js

@ -182,9 +182,6 @@ export default class WalletsAdd extends Component {
{!this.state.isLoading ? (
<BlueButton
title={loc.wallets.add.create}
buttonStyle={{
width: width / 1.5,
}}
onPress={() => {
this.setState(
{ isLoading: true },

34
screen/wallets/buyBitcoin.js

@ -1,6 +1,14 @@
import React, { Component } from 'react';
import { Animated, Linking, StyleSheet, View, TouchableOpacity, Clipboard } from 'react-native';
import { BlueNavigationStyle, BlueLoading, SafeBlueArea, BlueButton, BlueText, BlueSpacing40 } from '../../BlueComponents';
import { Linking, View } from 'react-native';
import {
BlueNavigationStyle,
BlueCopyTextToClipboard,
BlueLoading,
SafeBlueArea,
BlueButton,
BlueText,
BlueSpacing40,
} from '../../BlueComponents';
import PropTypes from 'prop-types';
/** @type {AppStorage} */
let BlueApp = require('../../BlueApp');
@ -58,13 +66,6 @@ export default class BuyBitcoin extends Component {
}
}
copyToClipboard = () => {
this.setState({ addressText: loc.buyBitcoin.copied }, () => {
Clipboard.setString(this.state.address);
setTimeout(() => this.setState({ addressText: this.state.address }), 1000);
});
};
render() {
console.log('render() receive/details, address,secret=', this.state.address, ',', this.state.secret);
if (this.state.isLoading) {
@ -77,11 +78,7 @@ export default class BuyBitcoin extends Component {
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center', paddingHorizontal: 16 }}>
<BlueText>{loc.buyBitcoin.tap_your_address}</BlueText>
<TouchableOpacity onPress={this.copyToClipboard}>
<Animated.Text style={styles.address} numberOfLines={0}>
{this.state.addressText}
</Animated.Text>
</TouchableOpacity>
<BlueCopyTextToClipboard text={this.state.addressText} />
<BlueButton
icon={{
@ -108,15 +105,6 @@ export default class BuyBitcoin extends Component {
}
}
const styles = StyleSheet.create({
address: {
marginVertical: 32,
fontSize: 15,
color: '#9aa0aa',
textAlign: 'center',
},
});
BuyBitcoin.propTypes = {
navigation: PropTypes.shape({
goBack: PropTypes.function,

2
screen/wallets/details.js

@ -120,7 +120,6 @@ export default class WalletDetails extends Component {
{loc.wallets.details.type.toLowerCase()}
</Text>
<Text style={{ color: '#81868e', fontWeight: '500', fontSize: 14 }}>{this.state.wallet.typeReadable}</Text>
</BlueCard>
<View>
<BlueSpacing20 />
<BlueButton
@ -202,6 +201,7 @@ export default class WalletDetails extends Component {
<Text style={{ color: '#d0021b', fontSize: 15, fontWeight: '500' }}>{loc.wallets.details.delete}</Text>
</TouchableOpacity>
</View>
</BlueCard>
</View>
</TouchableWithoutFeedback>
</SafeBlueArea>

9
screen/wallets/list.js

@ -19,6 +19,7 @@ import { Icon } from 'react-native-elements';
import { NavigationEvents } from 'react-navigation';
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
import PropTypes from 'prop-types';
import WalletGradient from '../../class/walletGradient';
let EV = require('../../events');
let A = require('../../analytics');
/** @type {AppStorage} */
@ -121,13 +122,13 @@ export default class WalletsList extends Component {
return '';
}
handleClick(index, gradients) {
handleClick(index) {
console.log('click', index);
let wallet = BlueApp.wallets[index];
if (wallet) {
this.props.navigation.navigate('WalletTransactions', {
wallet: wallet,
gradients: gradients,
headerColor: WalletGradient.headerColorFor(wallet.type),
});
} else {
// if its out of index - this must be last card with incentive to create wallet
@ -298,8 +299,8 @@ export default class WalletsList extends Component {
<BlueHeaderDefaultMain leftText={loc.wallets.list.title} onNewWalletPress={() => this.props.navigation.navigate('AddWallet')} />
<WalletsCarousel
data={this.state.wallets}
handleClick={(index, headerColor) => {
this.handleClick(index, headerColor);
handleClick={index => {
this.handleClick(index);
}}
handleLongPress={this.handleLongPress}
onSnapToItem={index => {

40
screen/wallets/reorderWallets.js

@ -4,12 +4,9 @@ import { SafeBlueArea, BlueNavigationStyle } from '../../BlueComponents';
import SortableList from 'react-native-sortable-list';
import LinearGradient from 'react-native-linear-gradient';
import PropTypes from 'prop-types';
import { WatchOnlyWallet, LegacyWallet } from '../../class';
import { HDLegacyP2PKHWallet } from '../../class/hd-legacy-p2pkh-wallet';
import { HDLegacyBreadwalletWallet } from '../../class/hd-legacy-breadwallet-wallet';
import { HDSegwitP2SHWallet } from '../../class/hd-segwit-p2sh-wallet';
import { LightningCustodianWallet } from '../../class/lightning-custodian-wallet';
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
import WalletGradient from '../../class/walletGradient';
let EV = require('../../events');
/** @type {AppStorage} */
let BlueApp = require('../../BlueApp');
@ -67,39 +64,6 @@ export default class ReorderWallets extends Component {
}
item = item.data;
let gradient1 = '#65ceef';
let gradient2 = '#68bbe1';
if (WatchOnlyWallet.type === item.type) {
gradient1 = '#7d7d7d';
gradient2 = '#4a4a4a';
}
if (LegacyWallet.type === item.type) {
gradient1 = '#40fad1';
gradient2 = '#15be98';
}
if (HDLegacyP2PKHWallet.type === item.type) {
gradient1 = '#e36dfa';
gradient2 = '#bd10e0';
}
if (HDLegacyBreadwalletWallet.type === item.type) {
gradient1 = '#fe6381';
gradient2 = '#f99c42';
}
if (HDSegwitP2SHWallet.type === item.type) {
gradient1 = '#c65afb';
gradient2 = '#9053fe';
}
if (LightningCustodianWallet.type === item.type) {
gradient1 = '#f1be07';
gradient2 = '#f79056';
}
return (
<View
shadowOpacity={40 / 100}
@ -109,7 +73,7 @@ export default class ReorderWallets extends Component {
>
<LinearGradient
shadowColor="#000000"
colors={[gradient1, gradient2]}
colors={WalletGradient.gradientsFor(item.type)}
style={{
padding: 15,
borderRadius: 10,

40
screen/wallets/selectWallet.js

@ -3,12 +3,9 @@ import { View, ActivityIndicator, Image, Text, TouchableOpacity, FlatList } from
import { SafeBlueArea, BlueNavigationStyle, BlueText, BlueSpacing20 } from '../../BlueComponents';
import LinearGradient from 'react-native-linear-gradient';
import PropTypes from 'prop-types';
import { WatchOnlyWallet, LegacyWallet } from '../../class';
import { HDLegacyP2PKHWallet } from '../../class/hd-legacy-p2pkh-wallet';
import { HDLegacyBreadwalletWallet } from '../../class/hd-legacy-breadwallet-wallet';
import { HDSegwitP2SHWallet } from '../../class/hd-segwit-p2sh-wallet';
import { LightningCustodianWallet } from '../../class/lightning-custodian-wallet';
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
import WalletGradient from '../../class/walletGradient';
/** @type {AppStorage} */
let BlueApp = require('../../BlueApp');
let loc = require('../../loc');
@ -36,39 +33,6 @@ export default class SelectWallet extends Component {
}
_renderItem = ({ item }) => {
let gradient1 = '#65ceef';
let gradient2 = '#68bbe1';
if (WatchOnlyWallet.type === item.type) {
gradient1 = '#7d7d7d';
gradient2 = '#4a4a4a';
}
if (LegacyWallet.type === item.type) {
gradient1 = '#40fad1';
gradient2 = '#15be98';
}
if (HDLegacyP2PKHWallet.type === item.type) {
gradient1 = '#e36dfa';
gradient2 = '#bd10e0';
}
if (HDLegacyBreadwalletWallet.type === item.type) {
gradient1 = '#fe6381';
gradient2 = '#f99c42';
}
if (HDSegwitP2SHWallet.type === item.type) {
gradient1 = '#c65afb';
gradient2 = '#9053fe';
}
if (LightningCustodianWallet.type === item.type) {
gradient1 = '#f1be07';
gradient2 = '#f79056';
}
return (
<TouchableOpacity
onPress={() => {
@ -84,7 +48,7 @@ export default class SelectWallet extends Component {
>
<LinearGradient
shadowColor="#000000"
colors={[gradient1, gradient2]}
colors={WalletGradient.gradientsFor(item.type)}
style={{
padding: 15,
borderRadius: 10,

30
screen/wallets/transactions.js

@ -20,6 +20,7 @@ import {
import { Icon } from 'react-native-elements';
import { BitcoinUnit } from '../../models/bitcoinUnits';
import { LightningCustodianWallet } from '../../class';
import WalletGradient from '../../class/walletGradient';
/** @type {AppStorage} */
let BlueApp = require('../../BlueApp');
let loc = require('../../loc');
@ -41,7 +42,7 @@ export default class WalletTransactions extends Component {
</TouchableOpacity>
),
headerStyle: {
backgroundColor: navigation.getParam('gradients')[0] || '#65ceef',
backgroundColor: navigation.getParam('headerColor'),
borderBottomWidth: 0,
elevation: 0,
shadowRadius: 0,
@ -145,12 +146,17 @@ export default class WalletTransactions extends Component {
setTimeout(async function() {
// more responsive
let noErr = true;
let smthChanged = false;
try {
/** @type {LegacyWallet} */
let wallet = that.state.wallet;
const oldBalance = wallet.getBalance();
await wallet.fetchBalance();
if (oldBalance !== wallet.getBalance()) smthChanged = true;
let start = +new Date();
const oldTxLen = wallet.getTransactions().length;
await wallet.fetchTransactions();
if (oldTxLen !== wallet.getTransactions().length) smthChanged = true;
if (wallet.fetchPendingTransactions) {
await wallet.fetchPendingTransactions();
}
@ -163,7 +169,8 @@ export default class WalletTransactions extends Component {
noErr = false;
console.warn(err);
}
if (noErr) {
if (noErr && smthChanged) {
console.log('saving to disk');
await BlueApp.saveToDisk(); // caching
EV(EV.enum.TRANSACTIONS_COUNT_CHANGED); // let other components know they should redraw
}
@ -195,9 +202,8 @@ export default class WalletTransactions extends Component {
}
renderWalletHeader = () => {
const gradients = this.props.navigation.getParam('gradients') || ['#65ceef', '#68bbe1'];
return (
<LinearGradient colors={[gradients[0], gradients[1]]} style={{ padding: 15, minHeight: 164 }}>
<LinearGradient colors={WalletGradient.gradientsFor(this.state.wallet.type)} style={{ padding: 15, minHeight: 164 }}>
<Image
source={
(LightningCustodianWallet.type === this.state.wallet.type && require('../../img/lnd-shape.png')) ||
@ -366,27 +372,26 @@ export default class WalletTransactions extends Component {
{(() => {
if (this.state.showManageFundsSmallButton) {
return (
<React.Fragment>
<View style={{ justifyContent: 'space-between', alignContent: 'center', flexDirection: 'row', marginVertical: 8 }}>
<TouchableOpacity
style={{ alignSelf: 'flex-start', left: 10, top: 15, flexDirection: 'row' }}
style={{ left: 10, flexDirection: 'row', flex: 1, alignItems: 'center' }}
onPress={() => {
console.log('navigating to LappBrowser');
navigate('LappBrowser', { fromSecret: this.state.wallet.getSecret(), fromWallet: this.state.wallet });
}}
>
<BlueText style={{ fontWeight: '600', fontSize: 16 }}>{'marketplace'}</BlueText>
<BlueText style={{ fontWeight: '600', fontSize: 16 }}>marketplace</BlueText>
<Icon
style={{ position: 'relative' }}
name="shopping-cart"
type="font-awesome"
size={14}
color={BlueApp.settings.foregroundColor}
iconStyle={{ left: 5 }}
iconStyle={{ left: 5, top: 2 }}
/>
</TouchableOpacity>
<TouchableOpacity
style={{ alignSelf: 'flex-end', right: 10, top: -5, flexDirection: 'row' }}
style={{ marginRight: 10, flexDirection: 'row', alignItems: 'center' }}
onPress={() => {
console.log('navigating to', this.state.wallet.getLabel());
navigate('ManageFunds', { fromWallet: this.state.wallet });
@ -394,15 +399,14 @@ export default class WalletTransactions extends Component {
>
<BlueText style={{ fontWeight: '600', fontSize: 16 }}>{loc.lnd.title}</BlueText>
<Icon
style={{ position: 'relative' }}
name="link"
type="font-awesome"
size={14}
color={BlueApp.settings.foregroundColor}
iconStyle={{ left: 5, transform: [{ rotate: '90deg' }] }}
iconStyle={{ left: 5, top: 2, transform: [{ rotate: '90deg' }] }}
/>
</TouchableOpacity>
</React.Fragment>
</View>
);
}
})()}

2
screen/wallets/walletMigrate.js

@ -82,7 +82,7 @@ export default class WalletMigrate extends Component {
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignContent: 'center' }}>
<View style={{ flex: 1, justifyContent: 'center', alignContent: 'center', backgroundColor: '#ffffff' }}>
<ActivityIndicator />
</View>
);

17
screen/wallets/xpub.js

@ -1,7 +1,7 @@
import React, { Component } from 'react';
import { Dimensions, Platform, ActivityIndicator, View, Clipboard, Animated, TouchableOpacity } from 'react-native';
import { Dimensions, Platform, ActivityIndicator, View } from 'react-native';
import { QRCode as QRSlow } from 'react-native-custom-qr-codes';
import { BlueSpacing20, SafeBlueArea, BlueText, BlueNavigationStyle } from '../../BlueComponents';
import { BlueSpacing20, SafeBlueArea, BlueText, BlueNavigationStyle, BlueCopyTextToClipboard } from '../../BlueComponents';
import PropTypes from 'prop-types';
const QRFast = require('react-native-qrcode');
/** @type {AppStorage} */
@ -49,13 +49,6 @@ export default class WalletXpub extends Component {
}, 1000);
}
copyToClipboard = () => {
this.setState({ xpubText: loc.wallets.xpub.copiedToClipboard }, () => {
Clipboard.setString(this.state.xpub);
setTimeout(() => this.setState({ xpubText: this.state.xpub }), 1000);
});
};
onLayout = () => {
const { height } = Dimensions.get('window');
this.setState({ qrCodeHeight: height > width ? width - 40 : width / 2 });
@ -110,11 +103,7 @@ export default class WalletXpub extends Component {
}
})()}
<BlueSpacing20 />
<TouchableOpacity onPress={this.copyToClipboard}>
<Animated.Text style={{ paddingHorizontal: 8, textAlign: 'center' }} numberOfLines={0}>
{this.state.xpubText}
</Animated.Text>
</TouchableOpacity>
<BlueCopyTextToClipboard text={this.state.xpubText} />
</View>
</SafeBlueArea>
);

Loading…
Cancel
Save