Browse Source

Merge branch 'master' into issue-573

psbt
Marcos Rodriguez 5 years ago
parent
commit
22c3603ff1
  1. 197
      BlueComponents.js
  2. 4
      MainBottomTabs.js
  3. 2
      android/app/build.gradle
  4. 18
      ios/BlueWallet.xcodeproj/project.pbxproj
  5. 5
      ios/BlueWallet/BlueWallet.entitlements
  6. 2
      ios/BlueWallet/Info.plist
  7. 5
      ios/BlueWalletWatch Extension/BlueWalletWatch Extension.entitlements
  8. 56
      ios/BlueWalletWatch Extension/ComplicationController.swift
  9. 17
      ios/BlueWalletWatch Extension/Info.plist
  10. 6
      ios/BlueWalletWatch Extension/Objects/WalletGradient.swift
  11. 32
      ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Circular.imageset/Contents.json
  12. BIN
      ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Circular.imageset/circular38mm@2x.png
  13. BIN
      ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Circular.imageset/circular40mm@2x.png
  14. BIN
      ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Circular.imageset/circular42mm@2x.png
  15. BIN
      ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Circular.imageset/circular44mm@2x.png
  16. 48
      ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Contents.json
  17. 32
      ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Extra Large.imageset/Contents.json
  18. BIN
      ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Extra Large.imageset/extra-large38mm@2x.png
  19. BIN
      ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Extra Large.imageset/extra-large40mm@2x.png
  20. BIN
      ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Extra Large.imageset/extra-large42mm@2x.png
  21. BIN
      ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Extra Large.imageset/extra-large44mm@2x.png
  22. 30
      ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Graphic Bezel.imageset/Contents.json
  23. BIN
      ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Graphic Bezel.imageset/graphic-bezel40mm@2x.png
  24. BIN
      ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Graphic Bezel.imageset/graphic-bezel44mm@2x.png
  25. 30
      ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Graphic Circular.imageset/Contents.json
  26. BIN
      ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Graphic Circular.imageset/graphic-circular40mm@2x.png
  27. BIN
      ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Graphic Circular.imageset/graphic-circular44mm@2x.png
  28. 30
      ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Graphic Corner.imageset/Contents.json
  29. BIN
      ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Graphic Corner.imageset/graphic-corner40mm@2x.png
  30. BIN
      ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Graphic Corner.imageset/graphic-corner44mm@2x.png
  31. 28
      ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Graphic Large Rectangular.imageset/Contents.json
  32. 32
      ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Modular.imageset/Contents.json
  33. BIN
      ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Modular.imageset/modular38mm@2x.png
  34. BIN
      ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Modular.imageset/modular40mm@2x.png
  35. BIN
      ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Modular.imageset/modular42mm@2x.png
  36. BIN
      ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Modular.imageset/modular44mm@2x.png
  37. 32
      ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/Contents.json
  38. BIN
      ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/utility38mm@2x.png
  39. BIN
      ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/utility40mm@2x.png
  40. BIN
      ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/utility42mm@2x.png
  41. BIN
      ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/utility44mm@2x.png
  42. 0
      ios/BlueWalletWatch/Assets.xcassets/walletHDSegwitNative.imageset/Contents.json
  43. 0
      ios/BlueWalletWatch/Assets.xcassets/walletHDSegwitNative.imageset/mask.png
  44. 0
      ios/BlueWalletWatch/Assets.xcassets/walletHDSegwitNative.imageset/mask@2x.png
  45. 0
      ios/BlueWalletWatch/Assets.xcassets/walletHDSegwitNative.imageset/mask@3x.png
  46. 2
      ios/BlueWalletWatch/Info.plist
  47. 6
      ios/Podfile.lock
  48. 10
      ios/fastlane/metadata/en-US/release_notes.txt
  49. 8
      models/networkTransactionFees.js
  50. 6
      package-lock.json
  51. 3
      package.json
  52. 2
      screen/lnd/lndCreateInvoice.js
  53. 7
      screen/lnd/lndViewAdditionalInvoiceInformation.js
  54. 14
      screen/lnd/lndViewInvoice.js
  55. 11
      screen/receive/details.js
  56. 8
      screen/receive/receiveAmount.js
  57. 2
      screen/selftest.js
  58. 40
      screen/transactions/CPFP.js
  59. 8
      screen/transactions/transactionStatus.js
  60. 4
      screen/wallets/add.js
  61. 49
      screen/wallets/details.js
  62. 8
      screen/wallets/export.js
  63. 22
      screen/wallets/import.js
  64. 78
      screen/wallets/marketplace.js
  65. 23
      screen/wallets/transactions.js
  66. 6
      screen/wallets/xpub.js
  67. 8
      tests/integration/HDWallet.test.js

197
BlueComponents.js

@ -18,6 +18,7 @@ import {
Image,
Keyboard,
SafeAreaView,
InteractionManager,
InputAccessoryView,
Clipboard,
Platform,
@ -33,6 +34,7 @@ import WalletGradient from './class/walletGradient';
import ToolTip from 'react-native-tooltip';
import { BlurView } from '@react-native-community/blur';
import showPopupMenu from 'react-native-popup-menu-android';
import NetworkTransactionFees, { NetworkTransactionFeeType } from './models/networkTransactionFees';
const LocalQRCode = require('@remobile/react-native-qrcode-local-image');
let loc = require('./loc/');
/** @type {AppStorage} */
@ -102,7 +104,6 @@ export class BitcoinButton extends Component {
borderRadius: 5,
backgroundColor: (this.props.active && BlueApp.settings.hdbackgroundColor) || BlueApp.settings.brandingColor,
// eslint-disable-next-line
width: this.props.style.width,
minWidth: this.props.style.width,
// eslint-disable-next-line
minHeight: this.props.style.height,
@ -140,7 +141,6 @@ export class LightningButton extends Component {
borderRadius: 5,
backgroundColor: (this.props.active && BlueApp.settings.lnbackgroundColor) || BlueApp.settings.brandingColor,
// eslint-disable-next-line
width: this.props.style.width,
minWidth: this.props.style.width,
// eslint-disable-next-line
minHeight: this.props.style.height,
@ -303,7 +303,7 @@ export class BlueWalletNavigationHeader extends Component {
)}
</TouchableOpacity>
{this.state.wallet.type === LightningCustodianWallet.type && (
<TouchableOpacity onPress={() => this.props.navigation.navigate('ManageFunds', { fromWallet: this.state.wallet })}>
<TouchableOpacity onPress={() => NavigationService.navigate('ManageFunds', { fromWallet: this.state.wallet })}>
<View
style={{
marginTop: 14,
@ -561,10 +561,6 @@ export class BlueFormMultiInput extends Component {
};
}
onSelectionChange = ({ nativeEvent: { selection, text } }) => {
this.setState({ selection: { start: selection.end, end: selection.end } });
};
render() {
return (
<TextInput
@ -587,8 +583,6 @@ export class BlueFormMultiInput extends Component {
spellCheck={false}
{...this.props}
selectTextOnFocus={false}
onSelectionChange={this.onSelectionChange}
selection={this.state.selection}
keyboardType={Platform.OS === 'android' ? 'visible-password' : 'default'}
/>
);
@ -850,13 +844,47 @@ export class BlueDismissKeyboardInputAccessory extends Component {
alignItems: 'center',
}}
>
<BlueButtonLink title="Done" onPress={Keyboard.dismiss} />
<BlueButtonLink title="Done" onPress={() => Keyboard.dismiss()} />
</View>
</InputAccessoryView>
);
}
}
export class BlueDoneAndDismissKeyboardInputAccessory extends Component {
static InputAccessoryViewID = 'BlueDoneAndDismissKeyboardInputAccessory';
onPasteTapped = async () => {
const clipboard = await Clipboard.getString();
this.props.onPasteTapped(clipboard);
};
render() {
const inputView = (
<View
style={{
backgroundColor: '#eef0f4',
height: 44,
flex: 1,
flexDirection: 'row',
justifyContent: 'flex-end',
alignItems: 'center',
}}
>
<BlueButtonLink title="Clear" onPress={this.props.onClearTapped} />
<BlueButtonLink title="Paste" onPress={this.onPasteTapped} />
<BlueButtonLink title="Done" onPress={() => Keyboard.dismiss()} />
</View>
);
if (Platform.OS === 'ios') {
return <InputAccessoryView nativeID={BlueDoneAndDismissKeyboardInputAccessory.InputAccessoryViewID}>{inputView}</InputAccessoryView>;
} else {
return <KeyboardAvoidingView style={{ height: 44 }}>{inputView}</KeyboardAvoidingView>;
}
}
}
export class BlueLoading extends Component {
render() {
return (
@ -1334,6 +1362,8 @@ export class BlueTransactionListItem extends Component {
itemPriceUnit: BitcoinUnit.BTC,
};
state = { transactionTimeToReadable: '...' };
txMemo = () => {
if (BlueApp.tx_metadata[this.props.item.hash] && BlueApp.tx_metadata[this.props.item.hash]['memo']) {
return BlueApp.tx_metadata[this.props.item.hash]['memo'];
@ -1495,11 +1525,18 @@ export class BlueTransactionListItem extends Component {
}
};
componentDidMount() {
InteractionManager.runAfterInteractions(() => {
const transactionTimeToReadable = loc.transactionTimeToReadable(this.props.item.received);
this.setState({ transactionTimeToReadable });
});
}
render() {
return (
<BlueListItem
avatar={this.avatar()}
title={loc.transactionTimeToReadable(this.props.item.received)}
title={this.state.transactionTimeToReadable}
subtitle={this.subtitle()}
onPress={this.onPress}
badge={{
@ -1956,6 +1993,132 @@ export class BlueAddressInput extends Component {
}
}
export class BlueReplaceFeeSuggestions extends Component {
static propTypes = {
onFeeSelected: PropTypes.func.isRequired,
transactionMinimum: PropTypes.number.isRequired,
};
static defaultProps = {
onFeeSelected: undefined,
transactionMinimum: 1,
};
state = { networkFees: undefined, selectedFeeType: NetworkTransactionFeeType.FAST, customFeeValue: 0 };
async componentDidMount() {
const networkFees = await NetworkTransactionFees.recommendedFees();
this.setState({ networkFees });
}
onFeeSelected = selectedFeeType => {
if (selectedFeeType !== NetworkTransactionFeeType.CUSTOM) {
Keyboard.dismiss();
}
if (selectedFeeType === NetworkTransactionFeeType.FAST) {
this.props.onFeeSelected(this.state.networkFees.fastestFee);
this.setState({ selectedFeeType }, () => this.props.onFeeSelected(this.state.networkFees.fastestFee));
} else if (selectedFeeType === NetworkTransactionFeeType.MEDIUM) {
this.setState({ selectedFeeType }, () => this.props.onFeeSelected(this.state.networkFees.halfHourFee));
} else if (selectedFeeType === NetworkTransactionFeeType.SLOW) {
this.setState({ selectedFeeType }, () => this.props.onFeeSelected(this.state.networkFees.hourFee));
} else if (selectedFeeType === NetworkTransactionFeeType.CUSTOM) {
this.props.onFeeSelected(this.state.customFeeValue);
}
};
onCustomFeeTextChange = customFee => {
this.setState({ customFeeValue: Number(customFee), selectedFeeType: NetworkTransactionFeeType.CUSTOM }, () => {
this.onFeeSelected(NetworkTransactionFeeType.CUSTOM);
});
};
render() {
return (
<View>
{this.state.networkFees && (
<>
<BlueText>Suggestions</BlueText>
<TouchableOpacity onPress={() => this.onFeeSelected(NetworkTransactionFeeType.FAST)}>
<BlueListItem
title={'Fast'}
rightTitle={`${this.state.networkFees.fastestFee} sat/b`}
{...(this.state.selectedFeeType === NetworkTransactionFeeType.FAST
? { rightIcon: <Icon name="check" type="font-awesome" color="#0c2550" /> }
: { hideChevron: true })}
/>
</TouchableOpacity>
<TouchableOpacity onPress={() => this.onFeeSelected(NetworkTransactionFeeType.MEDIUM)}>
<BlueListItem
title={'Medium'}
rightTitle={`${this.state.networkFees.halfHourFee} sat/b`}
{...(this.state.selectedFeeType === NetworkTransactionFeeType.MEDIUM
? { rightIcon: <Icon name="check" type="font-awesome" color="#0c2550" /> }
: { hideChevron: true })}
/>
</TouchableOpacity>
<TouchableOpacity onPress={() => this.onFeeSelected(NetworkTransactionFeeType.SLOW)}>
<BlueListItem
title={'Slow'}
rightTitle={`${this.state.networkFees.hourFee} sat/b`}
{...(this.state.selectedFeeType === NetworkTransactionFeeType.SLOW
? { rightIcon: <Icon name="check" type="font-awesome" color="#0c2550" /> }
: { hideChevron: true })}
/>
</TouchableOpacity>
</>
)}
<TouchableOpacity onPress={() => this.customTextInput.focus()}>
<View style={{ flexDirection: 'row', justifyContent: 'space-between', marginLeft: 18, marginRight: 18, alignItems: 'center' }}>
<Text style={{ color: BlueApp.settings.foregroundColor, fontSize: 16, fontWeight: '500' }}>Custom</Text>
<View
style={{
flexDirection: 'row',
minHeight: 44,
height: 44,
minWidth: 48,
alignItems: 'center',
justifyContent: 'flex-end',
marginVertical: 8,
}}
>
<TextInput
onChangeText={this.onCustomFeeTextChange}
keyboardType={'numeric'}
value={this.state.customFeeValue}
ref={ref => (this.customTextInput = ref)}
maxLength={9}
style={{
borderColor: '#d2d2d2',
borderBottomColor: '#d2d2d2',
borderWidth: 1.0,
borderBottomWidth: 0.5,
borderRadius: 4,
minHeight: 33,
maxWidth: 100,
minWidth: 44,
backgroundColor: '#f5f5f5',
textAlign: 'right',
}}
onFocus={() => this.onCustomFeeTextChange(this.state.customFeeValue)}
defaultValue={`${this.props.transactionMinimum}`}
placeholder="Custom sat/b"
inputAccessoryViewID={BlueDismissKeyboardInputAccessory.InputAccessoryViewID}
/>
<Text style={{ color: BlueApp.settings.alternativeTextColor, marginHorizontal: 8 }}>sat/b</Text>
{this.state.selectedFeeType === NetworkTransactionFeeType.CUSTOM && <Icon name="check" type="font-awesome" color="#0c2550" />}
</View>
<BlueDismissKeyboardInputAccessory />
</View>
</TouchableOpacity>
<BlueText>
The total fee rate (satoshi per byte) you want to pay should be higher than {this.props.transactionMinimum} sat/byte
</BlueText>
</View>
);
}
}
export class BlueBitcoinAmount extends Component {
static propTypes = {
isLoading: PropTypes.bool,
@ -1988,12 +2151,24 @@ export class BlueBitcoinAmount extends Component {
{...this.props}
keyboardType="numeric"
onChangeText={text => {
text = text.trim();
text = text.replace(',', '.');
const split = text.split('.');
if (split.length >= 2) {
text = `${parseInt(split[0], 10)}.${split[1]}`;
} else {
text = `${parseInt(split[0], 10)}`;
}
text = this.props.unit === BitcoinUnit.BTC ? text.replace(/[^0-9.]/g, '') : text.replace(/[^0-9]/g, '');
text = text.replace(/(\..*)\./g, '$1');
if (text.startsWith('.')) {
text = '0.';
}
text = text.replace(/(0{1,}.)\./g, '$1');
if (this.props.unit !== BitcoinUnit.BTC) {
text = text.replace(/[^0-9.]/g, '');
}
this.props.onChangeText(text);
}}
onBlur={() => {

4
MainBottomTabs.js

@ -19,6 +19,7 @@ import WalletDetails from './screen/wallets/details';
import WalletExport from './screen/wallets/export';
import WalletXpub from './screen/wallets/xpub';
import BuyBitcoin from './screen/wallets/buyBitcoin';
import Marketplace from './screen/wallets/marketplace';
import scanQrWif from './screen/wallets/scanQrWif';
import ReorderWallets from './screen/wallets/reorderWallets';
import SelectWallet from './screen/wallets/selectWallet';
@ -249,6 +250,9 @@ const MainBottomTabs = createStackNavigator(
BuyBitcoin: {
screen: BuyBitcoin,
},
Marketplace: {
screen: Marketplace,
},
//
SendDetails: {
screen: CreateTransactionStackNavigator,

2
android/app/build.gradle

@ -118,7 +118,7 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "4.4.1"
versionName "4.5.0"
multiDexEnabled true
missingDimensionStrategy 'react-native-camera', 'general'
}

18
ios/BlueWallet.xcodeproj/project.pbxproj

@ -23,6 +23,7 @@
2D16E6881FA4F8E400B85C8A /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2D16E6891FA4F8E400B85C8A /* libReact.a */; };
2DCD954D1E0B4F2C00145EB5 /* BlueWalletTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* BlueWalletTests.m */; };
3208E93922F63279007F5A27 /* AppCenter-Config.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3208E93822F63279007F5A27 /* AppCenter-Config.plist */; };
32F0A29A2311DBB20095C559 /* ComplicationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32F0A2992311DBB20095C559 /* ComplicationController.swift */; };
34582CAA4AD140F7B80C961A /* libTcpSockets.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9DF4E6C040764E4BA1ACC1EB /* libTcpSockets.a */; };
34CC55B441594DBB95AD1B50 /* Octicons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = E8E8CE89B3D142C6A8A56C34 /* Octicons.ttf */; };
398DED6337DF58F0ECFD8F2E /* libPods-BlueWalletTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 70089FECE936F9A0AC45B7CE /* libPods-BlueWalletTests.a */; };
@ -148,6 +149,9 @@
2D16E6891FA4F8E400B85C8A /* libReact.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libReact.a; sourceTree = BUILT_PRODUCTS_DIR; };
2FCC2CD6FF4448229D0CE0F3 /* MaterialCommunityIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = MaterialCommunityIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/MaterialCommunityIcons.ttf"; sourceTree = "<group>"; };
3208E93822F63279007F5A27 /* AppCenter-Config.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "AppCenter-Config.plist"; sourceTree = "<group>"; };
32F0A24F2310B0700095C559 /* BlueWalletWatch Extension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "BlueWalletWatch Extension.entitlements"; sourceTree = "<group>"; };
32F0A2502310B0910095C559 /* BlueWallet.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = BlueWallet.entitlements; path = BlueWallet/BlueWallet.entitlements; sourceTree = "<group>"; };
32F0A2992311DBB20095C559 /* ComplicationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ComplicationController.swift; sourceTree = "<group>"; };
334051161886419EA186F4BA /* FontAwesome.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = FontAwesome.ttf; path = "../node_modules/react-native-vector-icons/Fonts/FontAwesome.ttf"; sourceTree = "<group>"; };
3703B10AAB374CF896CCC2EA /* libBVLinearGradient.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libBVLinearGradient.a; sourceTree = "<group>"; };
3F7F1B8332C6439793D55B45 /* EvilIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = EvilIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf"; sourceTree = "<group>"; };
@ -308,6 +312,7 @@
13B07FAE1A68108700A75B9A /* BlueWallet */ = {
isa = PBXGroup;
children = (
32F0A2502310B0910095C559 /* BlueWallet.entitlements */,
3208E93822F63279007F5A27 /* AppCenter-Config.plist */,
008F07F21AC5B25A0029DE68 /* main.jsbundle */,
13B07FAF1A68108700A75B9A /* AppDelegate.h */,
@ -422,8 +427,10 @@
B40D4E40225841ED00428FCC /* BlueWalletWatch Extension */ = {
isa = PBXGroup;
children = (
32F0A24F2310B0700095C559 /* BlueWalletWatch Extension.entitlements */,
B43D03242258474500FBAA95 /* Objects */,
B40D4E672258426B00428FCC /* KeychainSwiftDistrib.swift */,
32F0A2992311DBB20095C559 /* ComplicationController.swift */,
B40D4E43225841ED00428FCC /* ExtensionDelegate.swift */,
B40D4E45225841ED00428FCC /* NotificationController.swift */,
B40D4E552258425400428FCC /* InterfaceController.swift */,
@ -618,6 +625,11 @@
13B07F861A680F5B00A75B9A = {
DevelopmentTeam = A7W54YZ4WU;
ProvisioningStyle = Manual;
SystemCapabilities = {
com.apple.Keychain = {
enabled = 0;
};
};
};
2D02E47A1E0B4A5D006451C7 = {
CreatedOnToolsVersion = 8.2.1;
@ -639,6 +651,11 @@
CreatedOnToolsVersion = 10.2;
DevelopmentTeam = A7W54YZ4WU;
ProvisioningStyle = Manual;
SystemCapabilities = {
com.apple.Keychain = {
enabled = 0;
};
};
};
};
};
@ -941,6 +958,7 @@
files = (
B43D037C225847C500FBAA95 /* Wallet.swift in Sources */,
B43D037A225847C500FBAA95 /* Transaction.swift in Sources */,
32F0A29A2311DBB20095C559 /* ComplicationController.swift in Sources */,
B40D4E602258425500428FCC /* SpecifyInterfaceController.swift in Sources */,
B43D0379225847C500FBAA95 /* WatchDataSource.swift in Sources */,
B40D4E46225841ED00428FCC /* NotificationController.swift in Sources */,

5
ios/BlueWallet/BlueWallet.entitlements

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict/>
</plist>

2
ios/BlueWallet/Info.plist

@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>4.4.1</string>
<string>4.5.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>

5
ios/BlueWalletWatch Extension/BlueWalletWatch Extension.entitlements

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict/>
</plist>

56
ios/BlueWalletWatch Extension/ComplicationController.swift

@ -0,0 +1,56 @@
//
// ComplicationController.swift
// T WatchKit Extension
//
// Created by Marcos Rodriguez on 8/24/19.
// Copyright © 2019 Marcos Rodriguez. All rights reserved.
//
import ClockKit
class ComplicationController: NSObject, CLKComplicationDataSource {
// MARK: - Timeline Configuration
func getSupportedTimeTravelDirections(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTimeTravelDirections) -> Void) {
handler([.forward, .backward])
}
func getTimelineStartDate(for complication: CLKComplication, withHandler handler: @escaping (Date?) -> Void) {
handler(nil)
}
func getTimelineEndDate(for complication: CLKComplication, withHandler handler: @escaping (Date?) -> Void) {
handler(nil)
}
func getPrivacyBehavior(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationPrivacyBehavior) -> Void) {
handler(.showOnLockScreen)
}
// MARK: - Timeline Population
func getCurrentTimelineEntry(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTimelineEntry?) -> Void) {
// Call the handler with the current timeline entry
handler(nil)
}
func getTimelineEntries(for complication: CLKComplication, before date: Date, limit: Int, withHandler handler: @escaping ([CLKComplicationTimelineEntry]?) -> Void) {
// Call the handler with the timeline entries prior to the given date
handler(nil)
}
func getTimelineEntries(for complication: CLKComplication, after date: Date, limit: Int, withHandler handler: @escaping ([CLKComplicationTimelineEntry]?) -> Void) {
// Call the handler with the timeline entries after to the given date
handler(nil)
}
// MARK: - Placeholder Templates
func getLocalizableSampleTemplate(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTemplate?) -> Void) {
// This method will be called once per supported complication, and the results will be cached
handler(nil)
}
}

17
ios/BlueWalletWatch Extension/Info.plist

@ -17,9 +17,24 @@
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>4.4.1</string>
<string>4.5.0</string>
<key>CFBundleVersion</key>
<string>239</string>
<key>CLKComplicationPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).ComplicationController</string>
<key>CLKComplicationSupportedFamilies</key>
<array>
<string>CLKComplicationFamilyCircularSmall</string>
<string>CLKComplicationFamilyExtraLarge</string>
<string>CLKComplicationFamilyGraphicBezel</string>
<string>CLKComplicationFamilyGraphicCircular</string>
<string>CLKComplicationFamilyGraphicCorner</string>
<string>CLKComplicationFamilyModularLarge</string>
<string>CLKComplicationFamilyModularSmall</string>
<string>CLKComplicationFamilyUtilitarianLarge</string>
<string>CLKComplicationFamilyUtilitarianSmall</string>
<string>CLKComplicationFamilyUtilitarianSmallFlat</string>
</array>
<key>LSApplicationCategoryType</key>
<string></string>
<key>NSExtension</key>

6
ios/BlueWalletWatch Extension/Objects/WalletGradient.swift

@ -12,15 +12,15 @@ enum WalletGradient: String {
case SegwitHD = "HDsegwitP2SH"
case Segwit = "segwitP2SH"
case LightningCustodial = "lightningCustodianWallet"
case ACINQStrike = "LightningACINQ"
case SegwitNative = "HDsegwitBech32"
case WatchOnly = "watchOnly"
var imageString: String{
switch self {
case .Segwit:
return "wallet"
case .ACINQStrike:
return "walletACINQ"
case .SegwitNative:
return "walletHDSegwitNative"
case .SegwitHD:
return "walletHD"
case .WatchOnly:

32
ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Circular.imageset/Contents.json

@ -0,0 +1,32 @@
{
"images" : [
{
"idiom" : "watch",
"screen-width" : "<=145",
"filename" : "circular38mm@2x.png",
"scale" : "2x"
},
{
"screen-width" : ">161",
"scale" : "2x",
"idiom" : "watch",
"filename" : "circular40mm@2x.png"
},
{
"scale" : "2x",
"idiom" : "watch",
"filename" : "circular42mm@2x.png",
"screen-width" : ">145"
},
{
"filename" : "circular44mm@2x.png",
"scale" : "2x",
"idiom" : "watch",
"screen-width" : ">183"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

BIN
ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Circular.imageset/circular38mm@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Circular.imageset/circular40mm@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Circular.imageset/circular42mm@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Circular.imageset/circular44mm@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

48
ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Contents.json

@ -0,0 +1,48 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
},
"assets" : [
{
"idiom" : "watch",
"role" : "circular",
"filename" : "Circular.imageset"
},
{
"idiom" : "watch",
"filename" : "Modular.imageset",
"role" : "modular"
},
{
"idiom" : "watch",
"filename" : "Utilitarian.imageset",
"role" : "utilitarian"
},
{
"idiom" : "watch",
"role" : "extra-large",
"filename" : "Extra Large.imageset"
},
{
"role" : "graphic-corner",
"idiom" : "watch",
"filename" : "Graphic Corner.imageset"
},
{
"filename" : "Graphic Circular.imageset",
"role" : "graphic-circular",
"idiom" : "watch"
},
{
"idiom" : "watch",
"filename" : "Graphic Bezel.imageset",
"role" : "graphic-bezel"
},
{
"idiom" : "watch",
"role" : "graphic-large-rectangular",
"filename" : "Graphic Large Rectangular.imageset"
}
]
}

32
ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Extra Large.imageset/Contents.json

@ -0,0 +1,32 @@
{
"images" : [
{
"filename" : "extra-large38mm@2x.png",
"screen-width" : "<=145",
"idiom" : "watch",
"scale" : "2x"
},
{
"screen-width" : ">161",
"filename" : "extra-large40mm@2x.png",
"idiom" : "watch",
"scale" : "2x"
},
{
"screen-width" : ">145",
"idiom" : "watch",
"filename" : "extra-large42mm@2x.png",
"scale" : "2x"
},
{
"idiom" : "watch",
"scale" : "2x",
"screen-width" : ">183",
"filename" : "extra-large44mm@2x.png"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

BIN
ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Extra Large.imageset/extra-large38mm@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

BIN
ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Extra Large.imageset/extra-large40mm@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Extra Large.imageset/extra-large42mm@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Extra Large.imageset/extra-large44mm@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

30
ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Graphic Bezel.imageset/Contents.json

@ -0,0 +1,30 @@
{
"images" : [
{
"idiom" : "watch",
"scale" : "2x",
"screen-width" : "<=145"
},
{
"idiom" : "watch",
"filename" : "graphic-bezel40mm@2x.png",
"screen-width" : ">161",
"scale" : "2x"
},
{
"idiom" : "watch",
"scale" : "2x",
"screen-width" : ">145"
},
{
"idiom" : "watch",
"filename" : "graphic-bezel44mm@2x.png",
"screen-width" : ">183",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

BIN
ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Graphic Bezel.imageset/graphic-bezel40mm@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Graphic Bezel.imageset/graphic-bezel44mm@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

30
ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Graphic Circular.imageset/Contents.json

@ -0,0 +1,30 @@
{
"images" : [
{
"idiom" : "watch",
"scale" : "2x",
"screen-width" : "<=145"
},
{
"idiom" : "watch",
"filename" : "graphic-circular40mm@2x.png",
"screen-width" : ">161",
"scale" : "2x"
},
{
"idiom" : "watch",
"scale" : "2x",
"screen-width" : ">145"
},
{
"idiom" : "watch",
"filename" : "graphic-circular44mm@2x.png",
"screen-width" : ">183",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

BIN
ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Graphic Circular.imageset/graphic-circular40mm@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Graphic Circular.imageset/graphic-circular44mm@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

30
ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Graphic Corner.imageset/Contents.json

@ -0,0 +1,30 @@
{
"images" : [
{
"idiom" : "watch",
"scale" : "2x",
"screen-width" : "<=145"
},
{
"idiom" : "watch",
"filename" : "graphic-corner40mm@2x.png",
"screen-width" : ">161",
"scale" : "2x"
},
{
"idiom" : "watch",
"scale" : "2x",
"screen-width" : ">145"
},
{
"idiom" : "watch",
"filename" : "graphic-corner44mm@2x.png",
"screen-width" : ">183",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

BIN
ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Graphic Corner.imageset/graphic-corner40mm@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Graphic Corner.imageset/graphic-corner44mm@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

28
ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Graphic Large Rectangular.imageset/Contents.json

@ -0,0 +1,28 @@
{
"images" : [
{
"idiom" : "watch",
"scale" : "2x",
"screen-width" : "<=145"
},
{
"idiom" : "watch",
"scale" : "2x",
"screen-width" : ">161"
},
{
"idiom" : "watch",
"scale" : "2x",
"screen-width" : ">145"
},
{
"idiom" : "watch",
"scale" : "2x",
"screen-width" : ">183"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

32
ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Modular.imageset/Contents.json

@ -0,0 +1,32 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
},
"images" : [
{
"screen-width" : "<=145",
"scale" : "2x",
"idiom" : "watch",
"filename" : "modular38mm@2x.png"
},
{
"screen-width" : ">161",
"scale" : "2x",
"filename" : "modular40mm@2x.png",
"idiom" : "watch"
},
{
"scale" : "2x",
"idiom" : "watch",
"filename" : "modular42mm@2x.png",
"screen-width" : ">145"
},
{
"filename" : "modular44mm@2x.png",
"screen-width" : ">183",
"idiom" : "watch",
"scale" : "2x"
}
]
}

BIN
ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Modular.imageset/modular38mm@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Modular.imageset/modular40mm@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Modular.imageset/modular42mm@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Modular.imageset/modular44mm@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

32
ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/Contents.json

@ -0,0 +1,32 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
},
"images" : [
{
"scale" : "2x",
"filename" : "utility38mm@2x.png",
"screen-width" : "<=145",
"idiom" : "watch"
},
{
"idiom" : "watch",
"scale" : "2x",
"screen-width" : ">161",
"filename" : "utility40mm@2x.png"
},
{
"scale" : "2x",
"idiom" : "watch",
"filename" : "utility42mm@2x.png",
"screen-width" : ">145"
},
{
"idiom" : "watch",
"screen-width" : ">183",
"filename" : "utility44mm@2x.png",
"scale" : "2x"
}
]
}

BIN
ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/utility38mm@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/utility40mm@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/utility42mm@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
ios/BlueWalletWatch/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/utility44mm@2x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

0
ios/BlueWalletWatch/Assets.xcassets/walletACINQ.imageset/Contents.json → ios/BlueWalletWatch/Assets.xcassets/walletHDSegwitNative.imageset/Contents.json

0
ios/BlueWalletWatch/Assets.xcassets/walletACINQ.imageset/mask.png → ios/BlueWalletWatch/Assets.xcassets/walletHDSegwitNative.imageset/mask.png

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

0
ios/BlueWalletWatch/Assets.xcassets/walletACINQ.imageset/mask@2x.png → ios/BlueWalletWatch/Assets.xcassets/walletHDSegwitNative.imageset/mask@2x.png

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

0
ios/BlueWalletWatch/Assets.xcassets/walletACINQ.imageset/mask@3x.png → ios/BlueWalletWatch/Assets.xcassets/walletHDSegwitNative.imageset/mask@3x.png

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

2
ios/BlueWalletWatch/Info.plist

@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>4.4.1</string>
<string>4.5.0</string>
<key>CFBundleVersion</key>
<string>239</string>
<key>UISupportedInterfaceOrientations</key>

6
ios/Podfile.lock

@ -33,6 +33,8 @@ PODS:
- DoubleConversion
- glog
- glog (0.3.5)
- RCTSystemSetting (1.7.2):
- React
- React (0.60.5):
- React-Core (= 0.60.5)
- React-DevSupport (= 0.60.5)
@ -163,6 +165,7 @@ DEPENDENCIES:
- EFQRCode (~> 5.0.0)
- Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`)
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
- RCTSystemSetting (from `../node_modules/react-native-system-setting`)
- React (from `../node_modules/react-native/`)
- React-Core (from `../node_modules/react-native/React`)
- React-cxxreact (from `../node_modules/react-native/ReactCommon/cxxreact`)
@ -226,6 +229,8 @@ EXTERNAL SOURCES:
:podspec: "../node_modules/react-native/third-party-podspecs/Folly.podspec"
glog:
:podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec"
RCTSystemSetting:
:path: "../node_modules/react-native-system-setting"
React:
:path: "../node_modules/react-native/"
React-Core:
@ -315,6 +320,7 @@ SPEC CHECKSUMS:
EFQRCode: 07437cfbce3a1e497397a4f3d766c980d8972608
Folly: 30e7936e1c45c08d884aa59369ed951a8e68cf51
glog: 1f3da668190260b06b429bb211bfbee5cd790c28
RCTSystemSetting: 9279ff44c49bb4fb0a5d335a0851852c8f3eda99
React: 53c53c4d99097af47cf60594b8706b4e3321e722
React-Core: ba421f6b4f4cbe2fb17c0b6fc675f87622e78a64
React-cxxreact: 8384287780c4999351ad9b6e7a149d9ed10a2395

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

@ -1,6 +1,16 @@
Always backup your keys! While your coins are safe, bugs happen, and app
could sometimes lose your wallets (fixed).
v4.4.1
======
* FIX: crash on ManageFunds
* ADD: marketplace for regular onchain btc wallets
* ADD: WatchApp Complication.
* FIX: Wallet gradient for Bech32 on watch app
* FIX: v3.9.8 fails to fill value #470
* FIX: Don't allow more than one leading zero
v4.4.0
======

8
models/networkTransactionFees.js

@ -3,6 +3,14 @@ import BigNumber from 'bignumber.js';
let loc = require('../loc');
const BlueElectrum = require('../BlueElectrum');
export const NetworkTransactionFeeType = Object.freeze({
FAST: 'Fast',
MEDIUM: 'MEDIUM',
SLOW: 'SLOW',
CUSTOM: 'CUSTOM',
});
export class NetworkTransactionFee {
static StorageKey = 'NetworkTransactionFee';

6
package-lock.json

@ -1,6 +1,6 @@
{
"name": "BlueWallet",
"version": "4.4.1",
"version": "4.5.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -10885,6 +10885,10 @@
"resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-9.5.1.tgz",
"integrity": "sha512-cRGfomzG/5LEwuJ6ct3m5yccckeI9aj8BNYwDPVxOeJ84LuJuvk5OqcjlYNeEzOWmWiH+QrFXfpLH1ag04bUeQ=="
},
"react-native-system-setting": {
"version": "git+https://github.com/marcosrdz/react-native-system-setting.git#81e4fff3257d2efd0c0f3bccb17e11e94aaa2509",
"from": "git+https://github.com/marcosrdz/react-native-system-setting.git"
},
"react-native-tab-view": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/react-native-tab-view/-/react-native-tab-view-1.4.1.tgz",

3
package.json

@ -1,6 +1,6 @@
{
"name": "BlueWallet",
"version": "4.4.1",
"version": "4.5.0",
"devDependencies": {
"@babel/core": "^7.5.0",
"@babel/runtime": "^7.5.1",
@ -108,6 +108,7 @@
"react-native-snap-carousel": "3.8.0",
"react-native-sortable-list": "0.0.23",
"react-native-svg": "9.5.1",
"react-native-system-setting": "git+https://github.com/marcosrdz/react-native-system-setting.git",
"react-native-tcp": "3.3.1",
"react-native-tooltip": "git+https://github.com/marcosrdz/react-native-tooltip.git",
"react-native-vector-icons": "6.6.0",

2
screen/lnd/lndCreateInvoice.js

@ -64,7 +64,7 @@ export default class LNDCreateInvoice extends Component {
throw new Error('Reply from server: ' + reply.reason);
}
}
await BlueApp.saveToDisk();
this.props.navigation.navigate('LNDViewInvoice', {
invoice: invoiceRequest,
fromWallet: this.state.fromWallet,

7
screen/lnd/lndViewAdditionalInvoiceInformation.js

@ -12,6 +12,7 @@ import {
} from '../../BlueComponents';
import PropTypes from 'prop-types';
import QRCode from 'react-native-qrcode-svg';
import SystemSetting from 'react-native-system-setting';
/** @type {AppStorage} */
let BlueApp = require('../../BlueApp');
const loc = require('../../loc');
@ -33,6 +34,12 @@ export default class LNDViewAdditionalInvoiceInformation extends Component {
return;
}
this.setState({ walletInfo: fromWallet.info_raw, addressText: fromWallet.info_raw.uris[0] });
await SystemSetting.saveBrightness();
await SystemSetting.setAppBrightness(1.0);
}
async componentWillUnmount() {
await SystemSetting.restoreBrightness();
}
render() {

14
screen/lnd/lndViewInvoice.js

@ -14,6 +14,7 @@ import PropTypes from 'prop-types';
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
import { Icon } from 'react-native-elements';
import QRCode from 'react-native-qrcode-svg';
import SystemSetting from 'react-native-system-setting';
/** @type {AppStorage} */
let BlueApp = require('../../BlueApp');
const loc = require('../../loc');
@ -43,7 +44,7 @@ export default class LNDViewInvoice extends Component {
qrCodeHeight: height > width ? width - 20 : width / 2,
};
this.fetchInvoiceInterval = undefined;
BackHandler.addEventListener('hardwareBackPress', this.handleBackButton.bind(this));
BackHandler.addEventListener('hardwareBackPress', this.handleBackButton);
}
async componentDidMount() {
@ -62,6 +63,8 @@ export default class LNDViewInvoice extends Component {
if (typeof updatedUserInvoice !== 'undefined') {
this.setState({ invoice: updatedUserInvoice, isLoading: false, addressText: updatedUserInvoice.payment_request });
await SystemSetting.saveBrightness();
await SystemSetting.setAppBrightness(1.0);
if (updatedUserInvoice.ispaid) {
// we fetched the invoice, and it is paid :-)
this.setState({ isFetchingInvoices: false });
@ -90,16 +93,17 @@ export default class LNDViewInvoice extends Component {
}, 3000);
}
componentWillUnmount() {
async componentWillUnmount() {
clearInterval(this.fetchInvoiceInterval);
this.fetchInvoiceInterval = undefined;
BackHandler.removeEventListener('hardwareBackPress', this.handleBackButton.bind(this));
BackHandler.removeEventListener('hardwareBackPress', this.handleBackButton);
await SystemSetting.restoreBrightness();
}
handleBackButton() {
handleBackButton = () => {
this.props.navigation.goBack(null);
return true;
}
};
onLayout = () => {
const { height } = Dimensions.get('window');

11
screen/receive/details.js

@ -15,6 +15,8 @@ import {
import PropTypes from 'prop-types';
import Privacy from '../../Privacy';
import Share from 'react-native-share';
import { ScrollView } from 'react-native-gesture-handler';
import SystemSetting from 'react-native-system-setting';
/** @type {AppStorage} */
let BlueApp = require('../../BlueApp');
let loc = require('../../loc');
@ -80,19 +82,22 @@ export default class ReceiveDetails extends Component {
}
InteractionManager.runAfterInteractions(async () => {
await SystemSetting.saveBrightness();
await SystemSetting.setAppBrightness(1.0);
const bip21encoded = bip21.encode(this.state.address);
this.setState({ bip21encoded });
});
}
componentWillUnmount() {
async componentWillUnmount() {
Privacy.disableBlur();
await SystemSetting.restoreBrightness();
}
render() {
return (
<SafeBlueArea style={{ flex: 1 }}>
<View style={{ flex: 1, justifyContent: 'space-between' }}>
<ScrollView contentContainerStyle={{ justifyContent: 'space-between' }}>
<View style={{ marginTop: 32, alignItems: 'center', paddingHorizontal: 16 }}>
{this.state.bip21encoded === undefined ? (
<View style={{ alignItems: 'center', width: 300, height: 300 }}>
@ -147,7 +152,7 @@ export default class ReceiveDetails extends Component {
/>
</View>
</View>
</View>
</ScrollView>
</SafeBlueArea>
);
}

8
screen/receive/receiveAmount.js

@ -13,6 +13,7 @@ import {
} from '../../BlueComponents';
import PropTypes from 'prop-types';
import Privacy from '../../Privacy';
import SystemSetting from 'react-native-system-setting';
/** @type {AppStorage} */
let BlueApp = require('../../BlueApp');
let loc = require('../../loc');
@ -48,12 +49,15 @@ export default class ReceiveAmount extends Component {
};
}
componentDidMount() {
async componentDidMount() {
Privacy.enableBlur();
await SystemSetting.saveBrightness();
await SystemSetting.setAppBrightness(1.0);
}
componentWillUnmount() {
async componentWillUnmount() {
Privacy.disableBlur();
await SystemSetting.restoreBrightness();
}
determineSize = () => {

2
screen/selftest.js

@ -258,7 +258,7 @@ export default class Selftest extends Component {
//
let hd3 = new HDSegwitP2SHWallet();
hd3._xpub = 'ypub6WaPs4JNUvfJoNyZZvRCYTutJiKcYJSiiDCEUNwwvGr76XkaXDuR5gVVcKE1NUxw7Sn2MWLSLrfF2nnk455GmWfK9fUYDXvWba7Zz1E5iX2';
hd3._xpub = 'ypub6Wb82D7F38b48uzRVyTwydMCPcos4njzygPRCJ4x1enm6EA5YUthtWgJUPYiFTs7Sk53q8rJ9d1SJ2fBNqsyhjUTDR7gyF1SXbBnaa9xcQj';
await hd3.fetchBalance();
if (hd3.getBalance() !== 26000) throw new Error('Could not fetch HD balance');
await hd3.fetchTransactions();

40
screen/transactions/CPFP.js

@ -1,7 +1,16 @@
/* global alert */
import React, { Component } from 'react';
import { ActivityIndicator, View, TextInput, TouchableOpacity, Linking, Clipboard } from 'react-native';
import { BlueSpacing20, BlueButton, SafeBlueArea, BlueCard, BlueText, BlueSpacing, BlueNavigationStyle } from '../../BlueComponents';
import {
BlueSpacing20,
BlueReplaceFeeSuggestions,
BlueButton,
SafeBlueArea,
BlueCard,
BlueText,
BlueSpacing,
BlueNavigationStyle,
} from '../../BlueComponents';
import PropTypes from 'prop-types';
import { HDSegwitBech32Transaction, HDSegwitBech32Wallet } from '../../class';
import { Icon, Text } from 'react-native-elements';
@ -216,34 +225,9 @@ export default class CPFP extends Component {
<BlueCard style={{ alignItems: 'center', flex: 1 }}>
<BlueText>{text}</BlueText>
<BlueSpacing20 />
<View
style={{
flexDirection: 'row',
borderColor: '#d2d2d2',
borderBottomColor: '#d2d2d2',
borderWidth: 1.0,
borderBottomWidth: 0.5,
backgroundColor: '#f5f5f5',
minHeight: 44,
height: 44,
alignItems: 'center',
marginVertical: 8,
borderRadius: 4,
}}
>
<TextInput
onChangeText={text => this.setState({ newFeeRate: text })}
keyboardType={'numeric'}
placeholder={'total fee rate (satoshi per byte) you want to pay'}
value={this.state.newFeeRate + ''}
style={{ flex: 1, minHeight: 33, marginHorizontal: 8 }}
/>
</View>
<BlueText>Should be higher than {this.state.feeRate} sat/byte</BlueText>
<BlueReplaceFeeSuggestions onFeeSelected={fee => this.setState({ newFeeRate: fee })} transactionMinimum={this.state.feeRate} />
<BlueSpacing />
<BlueButton onPress={() => this.createTransaction()} title="Create" />
<BlueButton disabled={this.state.newFeeRate <= this.state.feeRate} onPress={() => this.createTransaction()} title="Create" />
</BlueCard>
</SafeBlueArea>
);

8
screen/transactions/transactionStatus.js

@ -314,7 +314,6 @@ export default class TransactionsStatus extends Component {
}
title="Bump Fee"
/>
<BlueSpacing20 />
</React.Fragment>
);
}
@ -324,13 +323,12 @@ export default class TransactionsStatus extends Component {
return (
<React.Fragment>
<ActivityIndicator />
<BlueSpacing20 />
</React.Fragment>
);
} else if (this.state.isRBFCancelPossible === buttonStatus.possible) {
return (
<React.Fragment>
<TouchableOpacity style={{ marginVertical: 48 }}>
<TouchableOpacity style={{ marginVertical: 16 }}>
<Text
onPress={() =>
this.props.navigation.navigate('RBFCancel', {
@ -338,7 +336,7 @@ export default class TransactionsStatus extends Component {
wallet: this.state.wallet,
})
}
style={{ color: '#d0021b', fontSize: 15, fontWeight: '500' }}
style={{ color: '#d0021b', fontSize: 15, fontWeight: '500', textAlign: 'center' }}
>
{loc.send.details.cancel}
</Text>
@ -349,7 +347,7 @@ export default class TransactionsStatus extends Component {
})()}
<TouchableOpacity
style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'center' }}
style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'center', marginBottom: 16 }}
onPress={() => this.props.navigation.navigate('TransactionDetails', { hash: this.state.tx.hash })}
>
<Text style={{ color: '#9aa0aa', fontSize: 14, marginRight: 8 }}>{loc.send.create.details.toLowerCase()}</Text>

4
screen/wallets/add.js

@ -138,7 +138,7 @@ export default class WalletsAdd extends Component {
});
}}
style={{
width: 141,
width: '45%',
height: 88,
}}
/>
@ -155,7 +155,7 @@ export default class WalletsAdd extends Component {
});
}}
style={{
width: 141,
width: '45%',
height: 88,
}}
/>

49
screen/wallets/details.js

@ -10,6 +10,7 @@ import { HDSegwitP2SHWallet } from '../../class/hd-segwit-p2sh-wallet';
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
import { HDSegwitBech32Wallet } from '../../class';
let EV = require('../../events');
let prompt = require('../../prompt');
/** @type {AppStorage} */
let BlueApp = require('../../BlueApp');
let loc = require('../../loc');
@ -65,6 +66,32 @@ export default class WalletDetails extends Component {
});
}
async presentWalletHasBalanceAlert() {
ReactNativeHapticFeedback.trigger('notificationWarning', { ignoreAndroidSystemSettings: false });
const walletBalanceConfirmation = await prompt(
'Wallet Balance',
`This wallet has a balance. Before proceeding, please be aware that you will not be able to recover the funds without this wallet's seed phrase. In order to avoid accidental removal this wallet, please enter your wallet's balance of ${this.state.wallet.getBalance()} satoshis.`,
true,
'plain-text',
);
if (Number(walletBalanceConfirmation) === this.state.wallet.getBalance()) {
this.props.navigation.setParams({ isLoading: true });
this.setState({ isLoading: true }, async () => {
BlueApp.deleteWallet(this.state.wallet);
ReactNativeHapticFeedback.trigger('notificationSuccess', { ignoreAndroidSystemSettings: false });
await BlueApp.saveToDisk();
EV(EV.enum.TRANSACTIONS_COUNT_CHANGED);
EV(EV.enum.WALLETS_COUNT_CHANGED);
this.props.navigation.navigate('Wallets');
});
} else {
ReactNativeHapticFeedback.trigger('notificationError', { ignoreAndroidSystemSettings: false });
this.setState({ isLoading: false }, async () => {
alert("The provided balance amount does not match this wallet's balance. Please, try again");
});
}
}
render() {
if (this.state.isLoading) {
return (
@ -190,15 +217,19 @@ export default class WalletDetails extends Component {
{
text: loc.wallets.details.yes_delete,
onPress: async () => {
this.props.navigation.setParams({ isLoading: true });
this.setState({ isLoading: true }, async () => {
BlueApp.deleteWallet(this.state.wallet);
ReactNativeHapticFeedback.trigger('notificationSuccess', { ignoreAndroidSystemSettings: false });
await BlueApp.saveToDisk();
EV(EV.enum.TRANSACTIONS_COUNT_CHANGED);
EV(EV.enum.WALLETS_COUNT_CHANGED);
this.props.navigation.navigate('Wallets');
});
if (this.state.wallet.getBalance() > 0) {
this.presentWalletHasBalanceAlert();
} else {
this.props.navigation.setParams({ isLoading: true });
this.setState({ isLoading: true }, async () => {
BlueApp.deleteWallet(this.state.wallet);
ReactNativeHapticFeedback.trigger('notificationSuccess', { ignoreAndroidSystemSettings: false });
await BlueApp.saveToDisk();
EV(EV.enum.TRANSACTIONS_COUNT_CHANGED);
EV(EV.enum.WALLETS_COUNT_CHANGED);
this.props.navigation.navigate('Wallets');
});
}
},
style: 'destructive',
},

8
screen/wallets/export.js

@ -4,6 +4,7 @@ import QRCode from 'react-native-qrcode-svg';
import { BlueSpacing20, SafeBlueArea, BlueNavigationStyle, BlueText } from '../../BlueComponents';
import PropTypes from 'prop-types';
import Privacy from '../../Privacy';
import SystemSetting from 'react-native-system-setting';
/** @type {AppStorage} */
let BlueApp = require('../../BlueApp');
let loc = require('../../loc');
@ -36,15 +37,18 @@ export default class WalletExport extends Component {
};
}
componentDidMount() {
async componentDidMount() {
Privacy.enableBlur();
this.setState({
isLoading: false,
});
await SystemSetting.saveBrightness();
await SystemSetting.setAppBrightness(1.0);
}
componentWillUnmount() {
async componentWillUnmount() {
Privacy.disableBlur();
await SystemSetting.restoreBrightness();
}
onLayout = () => {

22
screen/wallets/import.js

@ -9,12 +9,13 @@ import {
HDSegwitBech32Wallet,
} from '../../class';
import React, { Component } from 'react';
import { KeyboardAvoidingView, Dimensions, View, TouchableWithoutFeedback, Keyboard } from 'react-native';
import { KeyboardAvoidingView, Platform, Dimensions, View, TouchableWithoutFeedback, Keyboard } from 'react-native';
import {
BlueFormMultiInput,
BlueButtonLink,
BlueFormLabel,
BlueLoading,
BlueDoneAndDismissKeyboardInputAccessory,
BlueButton,
SafeBlueArea,
BlueSpacing20,
@ -41,6 +42,7 @@ export default class WalletsImport extends Component {
super(props);
this.state = {
isLoading: true,
isToolbarVisibleForAndroid: false,
};
}
@ -255,10 +257,28 @@ export default class WalletsImport extends Component {
<BlueFormMultiInput
value={this.state.label}
placeholder=""
contextMenuHidden
onChangeText={text => {
this.setLabel(text);
}}
inputAccessoryViewID={BlueDoneAndDismissKeyboardInputAccessory.InputAccessoryViewID}
onFocus={() => this.setState({ isToolbarVisibleForAndroid: true })}
onBlur={() => this.setState({ isToolbarVisibleForAndroid: false })}
/>
{Platform.select({
ios: (
<BlueDoneAndDismissKeyboardInputAccessory
onClearTapped={() => this.setState({ label: '' }, () => Keyboard.dismiss())}
onPasteTapped={text => this.setState({ label: text }, () => Keyboard.dismiss())}
/>
),
android: this.state.isToolbarVisibleForAndroid && (
<BlueDoneAndDismissKeyboardInputAccessory
onClearTapped={() => this.setState({ label: '' }, () => Keyboard.dismiss())}
onPasteTapped={text => this.setState({ label: text }, () => Keyboard.dismiss())}
/>
),
})}
</KeyboardAvoidingView>
</TouchableWithoutFeedback>

78
screen/wallets/marketplace.js

@ -0,0 +1,78 @@
import React, { Component } from 'react';
import { BackHandler } from 'react-native';
import { WebView } from 'react-native-webview';
import { BlueLoading, BlueNavigationStyle } from '../../BlueComponents';
import PropTypes from 'prop-types';
export default class Marketplace extends Component {
static navigationOptions = ({ navigation }) => ({
...BlueNavigationStyle(navigation, true),
title: 'Marketplace',
headerLeft: null,
});
constructor(props) {
super(props);
if (!props.navigation.getParam('fromWallet')) throw new Error('Invalid param');
let fromWallet = props.navigation.getParam('fromWallet');
this.state = {
url: '',
fromWallet,
canGoBack: false,
};
BackHandler.addEventListener('hardwareBackPress', this.handleBackButton.bind(this));
}
async componentDidMount() {
let address;
if (this.state.fromWallet && this.state.fromWallet.getAddressAsync) {
address = await this.state.fromWallet.getAddressAsync();
} else if (this.state.fromWallet && this.state.fromWallet.getAddress) {
address = this.state.fromWallet.getAddress();
}
let url = 'https://bluewallet.io/marketplace-btc/?address=' + address; // default
this.setState({
url,
});
}
componentWillUnmount = () => {
BackHandler.removeEventListener('hardwareBackPress', this.handleBackButton.bind(this));
};
handleBackButton() {
this.state.canGoBack ? this.webview.goBack() : this.props.navigation.goBack(null);
return true;
}
_onNavigationStateChange = webViewState => {
this.setState({ canGoBack: webViewState.canGoBack });
};
render() {
if (this.state.isLoading) {
return <BlueLoading />;
}
return (
<WebView
ref={ref => (this.webview = ref)}
onNavigationStateChange={this._onNavigationStateChange}
source={{
uri: this.state.url,
}}
/>
);
}
}
Marketplace.propTypes = {
navigation: PropTypes.shape({
getParam: PropTypes.func,
navigate: PropTypes.func,
goBack: PropTypes.func,
}),
};

23
screen/wallets/transactions.js

@ -8,6 +8,7 @@ import { BlueSendButtonIcon, BlueReceiveButtonIcon, BlueTransactionListItem, Blu
import { Icon } from 'react-native-elements';
import { LightningCustodianWallet } from '../../class';
import Handoff from 'react-native-handoff';
import { ScrollView } from 'react-native-gesture-handler';
/** @type {AppStorage} */
let BlueApp = require('../../BlueApp');
let loc = require('../../loc');
@ -50,6 +51,7 @@ export default class WalletTransactions extends Component {
const wallet = props.navigation.getParam('wallet');
this.props.navigation.setParams({ wallet: wallet, isLoading: true });
this.state = {
showMarketplace: true,
isLoading: true,
showShowFlatListRefreshControl: false,
wallet: wallet,
@ -172,7 +174,7 @@ export default class WalletTransactions extends Component {
renderListFooterComponent = () => {
// if not all txs rendered - display indicator
return (this.getTransactions(Infinity).length > this.state.limit && <ActivityIndicator />) || <View />;
return (this.getTransactions(Infinity).length > this.state.limit && <ActivityIndicator style={{ marginVertical: 20 }} />) || <View />;
};
renderListHeaderComponent = () => {
@ -229,16 +231,19 @@ export default class WalletTransactions extends Component {
wallet={this.state.wallet}
onWalletUnitChange={wallet =>
InteractionManager.runAfterInteractions(async () => {
this.setState({ wallet }, () => BlueApp.saveToDisk());
this.setState({ wallet }, () => InteractionManager.runAfterInteractions(() => BlueApp.saveToDisk()));
})
}
/>
<View style={{ flex: 1, backgroundColor: '#FFFFFF' }}>
{this.state.wallet.type === LightningCustodianWallet.type && (
{this.state.showMarketplace && (
<TouchableOpacity
onPress={() => {
console.log('navigating to LappBrowser');
navigate('LappBrowser', { fromSecret: this.state.wallet.getSecret(), fromWallet: this.state.wallet });
if (this.state.wallet.type === LightningCustodianWallet.type) {
navigate('LappBrowser', { fromSecret: this.state.wallet.getSecret(), fromWallet: this.state.wallet });
} else {
navigate('Marketplace', { fromWallet: this.state.wallet });
}
}}
>
<View
@ -246,11 +251,9 @@ export default class WalletTransactions extends Component {
margin: 16,
backgroundColor: '#f2f2f2',
borderRadius: 9,
minWidth: 343,
minHeight: 49,
justifyContent: 'center',
alignItems: 'center',
alignSelf: 'center',
}}
>
<Text style={{ color: '#062453', fontSize: 18 }}>marketplace</Text>
@ -277,7 +280,7 @@ export default class WalletTransactions extends Component {
ListHeaderComponent={this.renderListHeaderComponent}
ListFooterComponent={this.renderListFooterComponent}
ListEmptyComponent={
<View style={{ top: 50, minHeight: 200, paddingHorizontal: 16 }}>
<ScrollView style={{ minHeight: 100 }} contentContainerStyle={{ flex: 1, justifyContent: 'center', paddingHorizontal: 16 }}>
<Text
numberOfLines={0}
style={{
@ -319,7 +322,7 @@ export default class WalletTransactions extends Component {
{loc.wallets.list.tap_here_to_buy}
</Text>
)}
</View>
</ScrollView>
}
refreshControl={
<RefreshControl onRefresh={() => this.refreshTransactions()} refreshing={this.state.showShowFlatListRefreshControl} />
@ -327,6 +330,7 @@ export default class WalletTransactions extends Component {
data={this.state.dataSource}
keyExtractor={this._keyExtractor}
renderItem={this.renderItem}
contentInset={{ top: 0, left: 0, bottom: 90, right: 0 }}
/>
</View>
<View
@ -338,7 +342,6 @@ export default class WalletTransactions extends Component {
bottom: 30,
borderRadius: 30,
minHeight: 48,
flex: 0.84,
overflow: 'hidden',
}}
>

6
screen/wallets/xpub.js

@ -4,6 +4,7 @@ import QRCode from 'react-native-qrcode-svg';
import { BlueSpacing20, SafeBlueArea, BlueText, BlueNavigationStyle, BlueCopyTextToClipboard } from '../../BlueComponents';
import PropTypes from 'prop-types';
import Privacy from '../../Privacy';
import SystemSetting from 'react-native-system-setting';
/** @type {AppStorage} */
let BlueApp = require('../../BlueApp');
let loc = require('../../loc');
@ -43,9 +44,12 @@ export default class WalletXpub extends Component {
this.setState({
isLoading: false,
});
await SystemSetting.saveBrightness();
await SystemSetting.setAppBrightness(1.0);
}
componentWillUnmount() {
async componentWillUnmount() {
await SystemSetting.restoreBrightness();
Privacy.disableBlur();
}

8
tests/integration/HDWallet.test.js

@ -146,7 +146,7 @@ it('HD (BIP49) can create TX', async () => {
let txhex = hd.createTx(hd.utxo, 0.000014, 0.000001, '3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK');
assert.strictEqual(
txhex,
'01000000000101e141f756746932f869c7323d941f26e6a1a6817143b97250a51f8c08510547a900000000171600140808fb749f01203cd063e72a96379e40b0c858f5ffffffff02780500000000000017a914a3a65daca3064280ae072b9d6773c027b30abace87b45f00000000000017a91451c2c75a20e796ffaff35b453cac8b3a1247ff408702483045022100ecb2edfd434ddf0c773aaed965dfb7e5480d972fe304465be1e35fe756753068022041791f9e2884687198b465846b70b2bacd618e3b6941539053d05f312ea3cae30121039faa75979e2a2a89f8f520c7338f9f586f37b0ca4644779c9e7c2359912b625900000000',
'0100000000010187c9acd9d5714845343b18abaa26cb83299be2487c22da9c0e270f241b4d9cfe0000000017160014a239b6a0cbc7aadc2e77643de36306a6167fad15ffffffff02780500000000000017a914a3a65daca3064280ae072b9d6773c027b30abace87b45f00000000000017a9140acff2c37ed45110baece4bb9d4dcc0c6309dbbd8702483045022100f489dfbd372b66348a25f6e9ba1b5eb88a3646efcd75ef1211c96cf46eed692c0220416ac99a94c5f4a076588291d9857fc5b854e02404d69635dc35e82fde3ecd9701210202ac3bd159e54dc31e65842ad5f9a10b4eb024e83864a319b27de65ee08b2a3900000000',
);
txhex = hd.createTx(hd.utxo, 0.000005, 0.000001, '3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK');
@ -185,21 +185,21 @@ it('HD (BIP49) can create TX', async () => {
txid: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
vout: 0,
amount: 26000,
address: '3GgPyzKfWXiaXMbJ9LeEVGetvEXdrX9Ecj',
address: '39SpCj47M88ajRBTbkfaKRgpaX7FTLQJz5',
wif: 'L3fg5Jb6tJDVMvoG2boP4u3CxjX1Er3e7Z4zDALQdGgVLLE8zVUr',
},
{
txid: 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',
vout: 0,
amount: 26000,
address: '3GgPyzKfWXiaXMbJ9LeEVGetvEXdrX9Ecj',
address: '39SpCj47M88ajRBTbkfaKRgpaX7FTLQJz5',
wif: 'L3fg5Jb6tJDVMvoG2boP4u3CxjX1Er3e7Z4zDALQdGgVLLE8zVUr',
},
{
txid: 'cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc',
vout: 0,
amount: 26000,
address: '3GgPyzKfWXiaXMbJ9LeEVGetvEXdrX9Ecj',
address: '39SpCj47M88ajRBTbkfaKRgpaX7FTLQJz5',
wif: 'L3fg5Jb6tJDVMvoG2boP4u3CxjX1Er3e7Z4zDALQdGgVLLE8zVUr',
},
];

Loading…
Cancel
Save