mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-02-22 15:04:50 +01:00
Merge branch 'master' into ops
This commit is contained in:
commit
aa8234b1d6
118 changed files with 14305 additions and 6735 deletions
|
@ -71,5 +71,5 @@ untyped-import
|
||||||
untyped-type-import
|
untyped-type-import
|
||||||
|
|
||||||
[version]
|
[version]
|
||||||
^0.113.0
|
^0.122.0
|
||||||
|
|
12
.travis.yml
12
.travis.yml
|
@ -89,15 +89,3 @@ script:
|
||||||
- npm run e2e:release-test || npm run e2e:release-test
|
- npm run e2e:release-test || npm run e2e:release-test
|
||||||
|
|
||||||
after_failure: ./tests/e2e/upload-artifacts.sh
|
after_failure: ./tests/e2e/upload-artifacts.sh
|
||||||
|
|
||||||
before_cache:
|
|
||||||
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
|
|
||||||
- rm -r -f node_modules/
|
|
||||||
- curl "${GRAVIS}.clean_gradle_cache.sh" --output ~/.clean_gradle_cache.sh
|
|
||||||
- bash ~/.clean_gradle_cache.sh > /dev/null
|
|
||||||
|
|
||||||
cache:
|
|
||||||
directories:
|
|
||||||
- $HOME/.gradle/caches/
|
|
||||||
- $HOME/.gradle/wrapper/
|
|
||||||
- node_modules/
|
|
||||||
|
|
|
@ -1,33 +1,35 @@
|
||||||
/* eslint react/prop-types: "off", react-native/no-inline-styles: "off" */
|
/* eslint react/prop-types: "off", react-native/no-inline-styles: "off" */
|
||||||
/* global alert */
|
/* global alert */
|
||||||
import React, { Component, useState } from 'react';
|
import React, { Component, useState, useMemo, useCallback } from 'react';
|
||||||
import Ionicons from 'react-native-vector-icons/Ionicons';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Icon, Input, Text, Header, ListItem } from 'react-native-elements';
|
import { Icon, Input, Text, Header, ListItem, Avatar } from 'react-native-elements';
|
||||||
import {
|
import {
|
||||||
|
ActivityIndicator,
|
||||||
|
Alert,
|
||||||
|
Animated,
|
||||||
|
Dimensions,
|
||||||
|
FlatList,
|
||||||
|
Image,
|
||||||
|
InputAccessoryView,
|
||||||
|
Keyboard,
|
||||||
|
KeyboardAvoidingView,
|
||||||
|
PixelRatio,
|
||||||
|
Platform,
|
||||||
|
PlatformColor,
|
||||||
|
SafeAreaView,
|
||||||
|
StyleSheet,
|
||||||
|
Switch,
|
||||||
|
TextInput,
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
TouchableWithoutFeedback,
|
TouchableWithoutFeedback,
|
||||||
Animated,
|
|
||||||
Alert,
|
|
||||||
ActivityIndicator,
|
|
||||||
View,
|
|
||||||
KeyboardAvoidingView,
|
|
||||||
UIManager,
|
UIManager,
|
||||||
StyleSheet,
|
useWindowDimensions,
|
||||||
Dimensions,
|
View,
|
||||||
Image,
|
|
||||||
Keyboard,
|
|
||||||
SafeAreaView,
|
|
||||||
InputAccessoryView,
|
|
||||||
Platform,
|
|
||||||
FlatList,
|
|
||||||
TextInput,
|
|
||||||
PixelRatio,
|
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import Clipboard from '@react-native-community/clipboard';
|
import Clipboard from '@react-native-community/clipboard';
|
||||||
import LinearGradient from 'react-native-linear-gradient';
|
import LinearGradient from 'react-native-linear-gradient';
|
||||||
import ActionSheet from './screen/ActionSheet';
|
import ActionSheet from './screen/ActionSheet';
|
||||||
import { LightningCustodianWallet, PlaceholderWallet } from './class';
|
import { LightningCustodianWallet, MultisigHDWallet, PlaceholderWallet } from './class';
|
||||||
import Carousel from 'react-native-snap-carousel';
|
import Carousel from 'react-native-snap-carousel';
|
||||||
import { BitcoinUnit } from './models/bitcoinUnits';
|
import { BitcoinUnit } from './models/bitcoinUnits';
|
||||||
import * as NavigationService from './NavigationService';
|
import * as NavigationService from './NavigationService';
|
||||||
|
@ -60,57 +62,25 @@ if (aspectRatio > 1.6) {
|
||||||
} else {
|
} else {
|
||||||
isIpad = true;
|
isIpad = true;
|
||||||
}
|
}
|
||||||
|
// eslint-disable-next-line no-unused-expressions
|
||||||
|
Platform.OS === 'android' ? (ActivityIndicator.defaultProps.color = PlatformColor('?attr/colorControlActivated')) : null;
|
||||||
|
|
||||||
export class BlueButton extends Component {
|
export const BlueButton = props => {
|
||||||
render() {
|
|
||||||
let backgroundColor = this.props.backgroundColor ? this.props.backgroundColor : BlueCurrentTheme.colors.mainColor;
|
|
||||||
let fontColor = BlueCurrentTheme.colors.buttonTextColor;
|
|
||||||
if (this.props.disabled === true) {
|
|
||||||
backgroundColor = BlueCurrentTheme.colors.buttonDisabledBackgroundColor;
|
|
||||||
fontColor = BlueCurrentTheme.colors.buttonDisabledTextColor;
|
|
||||||
}
|
|
||||||
let buttonWidth = this.props.width ? this.props.width : width / 1.5;
|
|
||||||
if ('noMinWidth' in this.props) {
|
|
||||||
buttonWidth = 0;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<TouchableOpacity
|
|
||||||
style={{
|
|
||||||
flex: 1,
|
|
||||||
borderWidth: 0.7,
|
|
||||||
borderColor: 'transparent',
|
|
||||||
backgroundColor: backgroundColor,
|
|
||||||
minHeight: 45,
|
|
||||||
height: 45,
|
|
||||||
maxHeight: 45,
|
|
||||||
borderRadius: 25,
|
|
||||||
minWidth: buttonWidth,
|
|
||||||
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: fontColor }}>{this.props.title}</Text>}
|
|
||||||
</View>
|
|
||||||
</TouchableOpacity>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const BlueButtonHook = props => {
|
|
||||||
const { colors } = useTheme();
|
const { colors } = useTheme();
|
||||||
|
const { width } = useWindowDimensions();
|
||||||
|
|
||||||
let backgroundColor = props.backgroundColor ? props.backgroundColor : colors.mainColor;
|
let backgroundColor = props.backgroundColor ? props.backgroundColor : colors.mainColor;
|
||||||
let fontColor = colors.buttonTextColor;
|
let fontColor = colors.buttonTextColor;
|
||||||
if (props.disabled === true) {
|
if (props.disabled === true) {
|
||||||
backgroundColor = colors.buttonDisabledBackgroundColor;
|
backgroundColor = colors.buttonDisabledBackgroundColor;
|
||||||
fontColor = colors.buttonDisabledTextColor;
|
fontColor = colors.buttonDisabledTextColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
let buttonWidth = props.width ? props.width : width / 1.5;
|
let buttonWidth = props.width ? props.width : width / 1.5;
|
||||||
if ('noMinWidth' in props) {
|
if ('noMinWidth' in props) {
|
||||||
buttonWidth = 0;
|
buttonWidth = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={{
|
style={{
|
||||||
|
@ -136,17 +106,13 @@ export const BlueButtonHook = props => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export class SecondButton extends Component {
|
export const BlueButtonHook = props => {
|
||||||
render() {
|
const { colors } = useTheme();
|
||||||
let backgroundColor = this.props.backgroundColor ? this.props.backgroundColor : BlueCurrentTheme.colors.buttonBlueBackgroundColor;
|
let backgroundColor = props.backgroundColor ? props.backgroundColor : colors.mainColor;
|
||||||
let fontColor = BlueCurrentTheme.colors.buttonTextColor;
|
let fontColor = colors.buttonTextColor;
|
||||||
if (this.props.disabled === true) {
|
if (props.disabled === true) {
|
||||||
backgroundColor = BlueCurrentTheme.colors.buttonDisabledBackgroundColor;
|
backgroundColor = colors.buttonDisabledBackgroundColor;
|
||||||
fontColor = BlueCurrentTheme.colors.buttonDisabledTextColor;
|
fontColor = colors.buttonDisabledTextColor;
|
||||||
}
|
|
||||||
let buttonWidth = this.props.width ? this.props.width : width / 1.5;
|
|
||||||
if ('noMinWidth' in this.props) {
|
|
||||||
buttonWidth = 0;
|
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
|
@ -159,7 +125,42 @@ export class SecondButton extends Component {
|
||||||
height: 45,
|
height: 45,
|
||||||
maxHeight: 45,
|
maxHeight: 45,
|
||||||
borderRadius: 25,
|
borderRadius: 25,
|
||||||
minWidth: buttonWidth,
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
}}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<View style={{ flexDirection: 'row', justifyContent: 'center', alignItems: 'center' }}>
|
||||||
|
{props.icon && <Icon name={props.icon.name} type={props.icon.type} color={props.icon.color} />}
|
||||||
|
{props.title && <Text style={{ marginHorizontal: 8, fontSize: 16, color: fontColor }}>{props.title}</Text>}
|
||||||
|
</View>
|
||||||
|
</TouchableOpacity>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export class SecondButton extends Component {
|
||||||
|
render() {
|
||||||
|
let backgroundColor = this.props.backgroundColor ? this.props.backgroundColor : BlueCurrentTheme.colors.buttonBlueBackgroundColor;
|
||||||
|
let fontColor = BlueCurrentTheme.colors.buttonTextColor;
|
||||||
|
if (this.props.disabled === true) {
|
||||||
|
backgroundColor = BlueCurrentTheme.colors.buttonDisabledBackgroundColor;
|
||||||
|
fontColor = BlueCurrentTheme.colors.buttonDisabledTextColor;
|
||||||
|
}
|
||||||
|
// let buttonWidth = this.props.width ? this.props.width : width / 1.5;
|
||||||
|
// if ('noMinWidth' in this.props) {
|
||||||
|
// buttonWidth = 0;
|
||||||
|
// }
|
||||||
|
return (
|
||||||
|
<TouchableOpacity
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
borderWidth: 0.7,
|
||||||
|
borderColor: 'transparent',
|
||||||
|
backgroundColor: backgroundColor,
|
||||||
|
minHeight: 45,
|
||||||
|
height: 45,
|
||||||
|
maxHeight: 45,
|
||||||
|
borderRadius: 25,
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
}}
|
}}
|
||||||
|
@ -190,7 +191,7 @@ export const BitcoinButton = props => {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<View style={{ marginTop: 16, marginLeft: 16, marginBottom: 16 }}>
|
<View style={{ margin: 16 }}>
|
||||||
<Text style={{ color: colors.hdborderColor, fontWeight: 'bold' }}>{loc.wallets.add_bitcoin}</Text>
|
<Text style={{ color: colors.hdborderColor, fontWeight: 'bold' }}>{loc.wallets.add_bitcoin}</Text>
|
||||||
</View>
|
</View>
|
||||||
<Image
|
<Image
|
||||||
|
@ -218,7 +219,7 @@ export const LightningButton = props => {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<View style={{ marginTop: 16, marginLeft: 16, marginBottom: 16 }}>
|
<View style={{ margin: 16 }}>
|
||||||
<Text style={{ color: colors.lnborderColor, fontWeight: 'bold' }}>{loc.wallets.add_lightning}</Text>
|
<Text style={{ color: colors.lnborderColor, fontWeight: 'bold' }}>{loc.wallets.add_lightning}</Text>
|
||||||
</View>
|
</View>
|
||||||
<Image
|
<Image
|
||||||
|
@ -339,9 +340,16 @@ export class BlueWalletNavigationHeader extends Component {
|
||||||
style={{ padding: 15, minHeight: 140, justifyContent: 'center' }}
|
style={{ padding: 15, minHeight: 140, justifyContent: 'center' }}
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
source={
|
source={(() => {
|
||||||
(LightningCustodianWallet.type === this.state.wallet.type && require('./img/lnd-shape.png')) || require('./img/btc-shape.png')
|
switch (this.state.wallet.type) {
|
||||||
|
case LightningCustodianWallet.type:
|
||||||
|
return require('./img/lnd-shape.png');
|
||||||
|
case MultisigHDWallet.type:
|
||||||
|
return require('./img/vault-shape.png');
|
||||||
|
default:
|
||||||
|
return require('./img/btc-shape.png');
|
||||||
}
|
}
|
||||||
|
})()}
|
||||||
style={{
|
style={{
|
||||||
width: 99,
|
width: 99,
|
||||||
height: 94,
|
height: 94,
|
||||||
|
@ -406,9 +414,9 @@ export class BlueWalletNavigationHeader extends Component {
|
||||||
marginBottom: 10,
|
marginBottom: 10,
|
||||||
backgroundColor: 'rgba(255,255,255,0.2)',
|
backgroundColor: 'rgba(255,255,255,0.2)',
|
||||||
borderRadius: 9,
|
borderRadius: 9,
|
||||||
minWidth: 119,
|
|
||||||
minHeight: 39,
|
minHeight: 39,
|
||||||
width: 119,
|
alignSelf: 'flex-start',
|
||||||
|
paddingHorizontal: 12,
|
||||||
height: 39,
|
height: 39,
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
@ -699,60 +707,56 @@ export const BlueTextCenteredHooks = props => {
|
||||||
return <Text {...props} style={{ color: colors.foregroundColor, textAlign: 'center' }} />;
|
return <Text {...props} style={{ color: colors.foregroundColor, textAlign: 'center' }} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const BlueListItem = React.memo(props => (
|
export const BlueListItem = React.memo(props => {
|
||||||
<ListItem
|
|
||||||
testID={props.testID}
|
|
||||||
bottomDivider
|
|
||||||
containerStyle={{
|
|
||||||
backgroundColor: 'transparent',
|
|
||||||
borderBottomColor: BlueCurrentTheme.colors.lightBorder,
|
|
||||||
paddingTop: 16,
|
|
||||||
paddingBottom: 16,
|
|
||||||
}}
|
|
||||||
titleStyle={{
|
|
||||||
color: props.disabled ? BlueCurrentTheme.colors.buttonDisabledTextColor : BlueCurrentTheme.colors.foregroundColor,
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: '500',
|
|
||||||
}}
|
|
||||||
subtitleStyle={{ flexWrap: 'wrap', color: BlueCurrentTheme.colors.alternativeTextColor, fontWeight: '400', fontSize: 14 }}
|
|
||||||
subtitleNumberOfLines={1}
|
|
||||||
titleNumberOfLines={0}
|
|
||||||
Component={TouchableOpacity}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
));
|
|
||||||
|
|
||||||
export const BlueListItemHooks = props => {
|
|
||||||
const { colors } = useTheme();
|
const { colors } = useTheme();
|
||||||
return (
|
return (
|
||||||
<ListItem
|
<ListItem
|
||||||
testID={props.testID}
|
containerStyle={props.containerStyle ?? { backgroundColor: 'transparent' }}
|
||||||
|
Component={props.Component ?? TouchableOpacity}
|
||||||
bottomDivider
|
bottomDivider
|
||||||
containerStyle={{
|
testID={props.testID}
|
||||||
backgroundColor: 'transparent',
|
onPress={props.onPress}
|
||||||
borderBottomColor: colors.lightBorder,
|
>
|
||||||
paddingTop: 16,
|
{props.leftAvatar && <Avatar>{props.leftAvatar}</Avatar>}
|
||||||
paddingBottom: 16,
|
{props.leftIcon && <Avatar icon={props.leftIcon} />}
|
||||||
}}
|
<ListItem.Content>
|
||||||
titleStyle={{
|
<ListItem.Title
|
||||||
|
style={{
|
||||||
color: props.disabled ? colors.buttonDisabledTextColor : colors.foregroundColor,
|
color: props.disabled ? colors.buttonDisabledTextColor : colors.foregroundColor,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: '500',
|
fontWeight: '500',
|
||||||
}}
|
}}
|
||||||
rightTitleStyle={{ flexWrap: 'wrap', color: colors.alternativeTextColor, fontWeight: '400', fontSize: 14 }}
|
numberOfLines={0}
|
||||||
subtitleStyle={{ flexWrap: 'wrap', color: colors.alternativeTextColor, fontWeight: '400', fontSize: 14 }}
|
>
|
||||||
subtitleNumberOfLines={1}
|
{props.title}
|
||||||
titleNumberOfLines={0}
|
</ListItem.Title>
|
||||||
Component={TouchableOpacity}
|
{props.subtitle && (
|
||||||
{...props}
|
<ListItem.Subtitle
|
||||||
/>
|
numberOfLines={1}
|
||||||
|
style={{ flexWrap: 'wrap', color: colors.alternativeTextColor, fontWeight: '400', fontSize: 14 }}
|
||||||
|
>
|
||||||
|
{props.subtitle}
|
||||||
|
</ListItem.Subtitle>
|
||||||
|
)}
|
||||||
|
</ListItem.Content>
|
||||||
|
<ListItem.Content right>
|
||||||
|
{props.rightTitle && (
|
||||||
|
<ListItem.Title style={props.rightTitleStyle} numberOfLines={0} right>
|
||||||
|
{props.rightTitle}
|
||||||
|
</ListItem.Title>
|
||||||
|
)}
|
||||||
|
</ListItem.Content>
|
||||||
|
{props.chevron && <ListItem.Chevron />}
|
||||||
|
{props.rightIcon && <Avatar icon={props.rightIcon} />}
|
||||||
|
{props.switch && <Switch {...props.switch} />}
|
||||||
|
</ListItem>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
export const BlueFormLabel = props => {
|
export const BlueFormLabel = props => {
|
||||||
const { colors } = useTheme();
|
const { colors } = useTheme();
|
||||||
|
|
||||||
return <Text {...props} style={{ color: colors.foregroundColor, fontWeight: '400', marginLeft: 20 }} />;
|
return <Text {...props} style={{ color: colors.foregroundColor, fontWeight: '400', marginHorizontal: 20 }} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export class BlueFormInput extends Component {
|
export class BlueFormInput extends Component {
|
||||||
|
@ -902,95 +906,37 @@ export const BlueHeaderDefaultSubHooks = props => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const BlueHeaderDefaultMainHooks = props => {
|
export const BlueHeaderDefaultMain = props => {
|
||||||
const { colors } = useTheme();
|
const { colors } = useTheme();
|
||||||
|
const { isDrawerList } = props;
|
||||||
return (
|
return (
|
||||||
<Header
|
<Header
|
||||||
{...props}
|
|
||||||
leftComponent={{
|
leftComponent={{
|
||||||
text: props.leftText,
|
text: props.leftText,
|
||||||
style: {
|
style: {
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
fontSize: 34,
|
fontSize: 34,
|
||||||
color: colors.foregroundColor,
|
color: colors.foregroundColor,
|
||||||
|
paddingHorizontal: 4,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
leftContainerStyle={{
|
placement="left"
|
||||||
minWidth: '70%',
|
containerStyle={{
|
||||||
height: 80,
|
borderTopColor: isDrawerList ? colors.elevated : colors.background,
|
||||||
|
borderBottomColor: isDrawerList ? colors.elevated : colors.background,
|
||||||
|
maxHeight: 44,
|
||||||
|
height: 44,
|
||||||
|
paddingTop: 0,
|
||||||
|
marginBottom: 8,
|
||||||
}}
|
}}
|
||||||
bottomDivider={false}
|
bottomDivider={false}
|
||||||
topDivider={false}
|
topDivider={false}
|
||||||
containerStyle={{
|
backgroundColor={isDrawerList ? colors.elevated : colors.background}
|
||||||
height: 44,
|
rightComponent={<BluePlusIcon onPress={props.onNewWalletPress} Component={TouchableOpacity} />}
|
||||||
flexDirection: 'row',
|
|
||||||
backgroundColor: colors.elevatated,
|
|
||||||
borderTopColor: colors.elevatated,
|
|
||||||
borderBottomColor: colors.elevatated,
|
|
||||||
borderBottomWidth: 0,
|
|
||||||
}}
|
|
||||||
rightComponent={
|
|
||||||
props.onNewWalletPress && (
|
|
||||||
<TouchableOpacity
|
|
||||||
onPress={props.onNewWalletPress}
|
|
||||||
style={{
|
|
||||||
height: 100,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<BluePlusIcon />
|
|
||||||
</TouchableOpacity>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export class BlueHeaderDefaultMain extends Component {
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<SafeAreaView style={{ paddingVertical: 8, paddingHorizontal: 4, backgroundColor: BlueCurrentTheme.colors.background }}>
|
|
||||||
<Header
|
|
||||||
{...this.props}
|
|
||||||
leftComponent={{
|
|
||||||
text: this.props.leftText,
|
|
||||||
style: {
|
|
||||||
fontWeight: 'bold',
|
|
||||||
fontSize: 34,
|
|
||||||
color: BlueCurrentTheme.colors.foregroundColor,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
leftContainerStyle={{
|
|
||||||
minWidth: '70%',
|
|
||||||
height: 80,
|
|
||||||
}}
|
|
||||||
bottomDivider={false}
|
|
||||||
topDivider={false}
|
|
||||||
containerStyle={{
|
|
||||||
height: 44,
|
|
||||||
flexDirection: 'row',
|
|
||||||
backgroundColor: BlueCurrentTheme.colors.background,
|
|
||||||
borderTopColor: BlueCurrentTheme.colors.background,
|
|
||||||
borderBottomColor: BlueCurrentTheme.colors.background,
|
|
||||||
borderBottomWidth: 0,
|
|
||||||
}}
|
|
||||||
rightComponent={
|
|
||||||
this.props.onNewWalletPress && (
|
|
||||||
<TouchableOpacity
|
|
||||||
onPress={this.props.onNewWalletPress}
|
|
||||||
style={{
|
|
||||||
height: 100,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<BluePlusIcon />
|
|
||||||
</TouchableOpacity>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</SafeAreaView>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class BlueSpacing extends Component {
|
export class BlueSpacing extends Component {
|
||||||
render() {
|
render() {
|
||||||
return <View {...this.props} style={{ height: 60 }} />;
|
return <View {...this.props} style={{ height: 60 }} />;
|
||||||
|
@ -1268,23 +1214,12 @@ export const BluePlusIcon = props => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<View {...props} style={stylesBlueIcon.container}>
|
<Avatar
|
||||||
<View style={stylesBlueIcon.box1}>
|
rounded
|
||||||
<View style={[stylesBlueIcon.ball, stylesBlueIconHooks.ball]}>
|
containerStyle={[stylesBlueIcon.ball, stylesBlueIconHooks.ball]}
|
||||||
<Ionicons
|
icon={{ name: 'add', size: 22, type: 'ionicons', color: colors.foregroundColor }}
|
||||||
{...props}
|
{...props}
|
||||||
name="ios-add"
|
|
||||||
size={26}
|
|
||||||
style={{
|
|
||||||
color: colors.foregroundColor,
|
|
||||||
backgroundColor: 'transparent',
|
|
||||||
left: 8,
|
|
||||||
top: 1,
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1632,7 +1567,7 @@ export const NewWalletPanel = props => {
|
||||||
minHeight: Platform.OS === 'ios' ? 164 : 181,
|
minHeight: Platform.OS === 'ios' ? 164 : 181,
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
alignItems: 'flex-start',
|
alignItems: 'flex-start',
|
||||||
backgroundColor: WalletGradient.createWallet,
|
backgroundColor: WalletGradient.createWallet(),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Text
|
<Text
|
||||||
|
@ -1673,15 +1608,27 @@ export const NewWalletPanel = props => {
|
||||||
export const BlueTransactionListItem = React.memo(({ item, itemPriceUnit = BitcoinUnit.BTC, timeElapsed }) => {
|
export const BlueTransactionListItem = React.memo(({ item, itemPriceUnit = BitcoinUnit.BTC, timeElapsed }) => {
|
||||||
const [subtitleNumberOfLines, setSubtitleNumberOfLines] = useState(1);
|
const [subtitleNumberOfLines, setSubtitleNumberOfLines] = useState(1);
|
||||||
const { colors } = useTheme();
|
const { colors } = useTheme();
|
||||||
|
const containerStyle = useMemo(
|
||||||
|
() => ({
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
borderBottomColor: colors.lightBorder,
|
||||||
|
paddingTop: 16,
|
||||||
|
paddingBottom: 16,
|
||||||
|
}),
|
||||||
|
[colors.lightBorder],
|
||||||
|
);
|
||||||
|
|
||||||
const txMemo = () => {
|
const title = useMemo(() => transactionTimeToReadable(item.received), [item.received]);
|
||||||
if (BlueApp.tx_metadata[item.hash] && BlueApp.tx_metadata[item.hash].memo) {
|
const txMemo = BlueApp.tx_metadata[item.hash]?.memo ?? '';
|
||||||
return BlueApp.tx_metadata[item.hash].memo;
|
const subtitle = useMemo(() => {
|
||||||
}
|
let sub = item.confirmations < 7 ? loc.formatString(loc.transactions.list_conf, { number: item.confirmations }) : '';
|
||||||
return '';
|
if (sub !== '') sub += ' ';
|
||||||
};
|
sub += txMemo;
|
||||||
|
if (item.memo) sub += item.memo;
|
||||||
|
return sub || null;
|
||||||
|
}, [txMemo, item.confirmations, item.memo]);
|
||||||
|
|
||||||
const rowTitle = () => {
|
const rowTitle = useMemo(() => {
|
||||||
if (item.type === 'user_invoice' || item.type === 'payment_request') {
|
if (item.type === 'user_invoice' || item.type === 'payment_request') {
|
||||||
if (isNaN(item.value)) {
|
if (isNaN(item.value)) {
|
||||||
item.value = '0';
|
item.value = '0';
|
||||||
|
@ -1702,9 +1649,9 @@ export const BlueTransactionListItem = React.memo(({ item, itemPriceUnit = Bitco
|
||||||
} else {
|
} else {
|
||||||
return formatBalanceWithoutSuffix(item.value && item.value, itemPriceUnit, true).toString();
|
return formatBalanceWithoutSuffix(item.value && item.value, itemPriceUnit, true).toString();
|
||||||
}
|
}
|
||||||
};
|
}, [item, itemPriceUnit]);
|
||||||
|
|
||||||
const rowTitleStyle = () => {
|
const rowTitleStyle = useMemo(() => {
|
||||||
let color = colors.successColor;
|
let color = colors.successColor;
|
||||||
|
|
||||||
if (item.type === 'user_invoice' || item.type === 'payment_request') {
|
if (item.type === 'user_invoice' || item.type === 'payment_request') {
|
||||||
|
@ -1726,15 +1673,15 @@ export const BlueTransactionListItem = React.memo(({ item, itemPriceUnit = Bitco
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
fontWeight: '600',
|
color,
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
color: color,
|
fontWeight: '600',
|
||||||
textAlign: 'right',
|
textAlign: 'right',
|
||||||
width: 96,
|
width: 96,
|
||||||
};
|
};
|
||||||
};
|
}, [item, colors.foregroundColor, colors.successColor]);
|
||||||
|
|
||||||
const avatar = () => {
|
const avatar = useMemo(() => {
|
||||||
// is it lightning refill tx?
|
// is it lightning refill tx?
|
||||||
if (item.category === 'receive' && item.confirmations < 3) {
|
if (item.category === 'receive' && item.confirmations < 3) {
|
||||||
return (
|
return (
|
||||||
|
@ -1800,13 +1747,9 @@ export const BlueTransactionListItem = React.memo(({ item, itemPriceUnit = Bitco
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
}, [item]);
|
||||||
|
|
||||||
const subtitle = () => {
|
const onPress = useCallback(async () => {
|
||||||
return (item.confirmations < 7 ? loc.transactions.list_conf + ': ' + item.confirmations + ' ' : '') + txMemo() + (item.memo || '');
|
|
||||||
};
|
|
||||||
|
|
||||||
const onPress = async () => {
|
|
||||||
if (item.hash) {
|
if (item.hash) {
|
||||||
NavigationService.navigate('TransactionStatus', { hash: item.hash });
|
NavigationService.navigate('TransactionStatus', { hash: item.hash });
|
||||||
} else if (item.type === 'user_invoice' || item.type === 'payment_request' || item.type === 'paid_invoice') {
|
} else if (item.type === 'user_invoice' || item.type === 'payment_request' || item.type === 'paid_invoice') {
|
||||||
|
@ -1829,7 +1772,7 @@ export const BlueTransactionListItem = React.memo(({ item, itemPriceUnit = Bitco
|
||||||
NavigationService.navigate('ScanLndInvoiceRoot', {
|
NavigationService.navigate('ScanLndInvoiceRoot', {
|
||||||
screen: 'LnurlPaySuccess',
|
screen: 'LnurlPaySuccess',
|
||||||
params: {
|
params: {
|
||||||
paymentHash: paymentHash,
|
paymentHash,
|
||||||
justPaid: false,
|
justPaid: false,
|
||||||
fromWalletID: lightningWallet[0].getID(),
|
fromWalletID: lightningWallet[0].getID(),
|
||||||
},
|
},
|
||||||
|
@ -1844,28 +1787,31 @@ export const BlueTransactionListItem = React.memo(({ item, itemPriceUnit = Bitco
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}, [item]);
|
||||||
|
|
||||||
const onLongPress = () => {
|
const onLongPress = useCallback(() => {
|
||||||
if (subtitleNumberOfLines === 1) {
|
if (subtitleNumberOfLines === 1) {
|
||||||
setSubtitleNumberOfLines(0);
|
setSubtitleNumberOfLines(0);
|
||||||
}
|
}
|
||||||
};
|
}, [subtitleNumberOfLines]);
|
||||||
|
|
||||||
|
const subtitleProps = useMemo(() => ({ numberOfLines: subtitleNumberOfLines }), [subtitleNumberOfLines]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={{ marginHorizontal: 4 }}>
|
<View style={{ marginHorizontal: 4 }}>
|
||||||
<BlueListItem
|
<BlueListItem
|
||||||
leftAvatar={avatar()}
|
leftAvatar={avatar}
|
||||||
title={transactionTimeToReadable(item.received)}
|
title={title}
|
||||||
titleNumberOfLines={subtitleNumberOfLines}
|
titleNumberOfLines={subtitleNumberOfLines}
|
||||||
subtitle={subtitle()}
|
subtitle={subtitle}
|
||||||
subtitleProps={{ numberOfLines: subtitleNumberOfLines }}
|
subtitleProps={subtitleProps}
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
onLongPress={onLongPress}
|
onLongPress={onLongPress}
|
||||||
chevron={false}
|
chevron={false}
|
||||||
Component={TouchableOpacity}
|
Component={TouchableOpacity}
|
||||||
rightTitle={rowTitle()}
|
rightTitle={rowTitle}
|
||||||
rightTitleStyle={rowTitleStyle()}
|
rightTitleStyle={rowTitleStyle}
|
||||||
|
containerStyle={containerStyle}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
@ -2005,7 +1951,16 @@ const WalletCarouselItem = ({ item, index, onPress, handleLongPress, isSelectedW
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
source={(LightningCustodianWallet.type === item.type && require('./img/lnd-shape.png')) || require('./img/btc-shape.png')}
|
source={(() => {
|
||||||
|
switch (item.type) {
|
||||||
|
case LightningCustodianWallet.type:
|
||||||
|
return require('./img/lnd-shape.png');
|
||||||
|
case MultisigHDWallet.type:
|
||||||
|
return require('./img/vault-shape.png');
|
||||||
|
default:
|
||||||
|
return require('./img/btc-shape.png');
|
||||||
|
}
|
||||||
|
})()}
|
||||||
style={{
|
style={{
|
||||||
width: 99,
|
width: 99,
|
||||||
height: 94,
|
height: 94,
|
||||||
|
@ -2093,7 +2048,8 @@ export class WalletsCarousel extends Component {
|
||||||
};
|
};
|
||||||
|
|
||||||
snapToItem = item => {
|
snapToItem = item => {
|
||||||
this.walletsCarousel.current.snapToItem(item);
|
// eslint-disable-next-line no-unused-expressions
|
||||||
|
this.walletsCarousel?.current?.snapToItem(item);
|
||||||
};
|
};
|
||||||
|
|
||||||
onLayout = () => {
|
onLayout = () => {
|
||||||
|
@ -2250,6 +2206,7 @@ export class BlueAddressInput extends Component {
|
||||||
{...this.props}
|
{...this.props}
|
||||||
/>
|
/>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
|
testID="BlueAddressInputScanQrButton"
|
||||||
disabled={this.props.isLoading}
|
disabled={this.props.isLoading}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
Keyboard.dismiss();
|
Keyboard.dismiss();
|
||||||
|
@ -2745,6 +2702,7 @@ const tabsStyles = StyleSheet.create({
|
||||||
borderBottomWidth: 1,
|
borderBottomWidth: 1,
|
||||||
},
|
},
|
||||||
tabRoot: {
|
tabRoot: {
|
||||||
|
flex: 1,
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
borderColor: 'white',
|
borderColor: 'white',
|
||||||
|
@ -2753,7 +2711,7 @@ const tabsStyles = StyleSheet.create({
|
||||||
});
|
});
|
||||||
|
|
||||||
export const BlueTabs = ({ active, onSwitch, tabs }) => (
|
export const BlueTabs = ({ active, onSwitch, tabs }) => (
|
||||||
<View style={tabsStyles.root}>
|
<View style={[tabsStyles.root, isIpad && { marginBottom: 30 }]}>
|
||||||
{tabs.map((Tab, i) => (
|
{tabs.map((Tab, i) => (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
key={i}
|
key={i}
|
||||||
|
@ -2764,7 +2722,6 @@ export const BlueTabs = ({ active, onSwitch, tabs }) => (
|
||||||
borderColor: BlueCurrentTheme.colors.buttonAlternativeTextColor,
|
borderColor: BlueCurrentTheme.colors.buttonAlternativeTextColor,
|
||||||
borderBottomWidth: 2,
|
borderBottomWidth: 2,
|
||||||
},
|
},
|
||||||
{ width: width / tabs.length },
|
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<Tab active={active === i} />
|
<Tab active={active === i} />
|
||||||
|
|
|
@ -27,6 +27,7 @@ import PleaseBackupLNDHub from './screen/wallets/pleaseBackupLNDHub';
|
||||||
import ImportWallet from './screen/wallets/import';
|
import ImportWallet from './screen/wallets/import';
|
||||||
import WalletDetails from './screen/wallets/details';
|
import WalletDetails from './screen/wallets/details';
|
||||||
import WalletExport from './screen/wallets/export';
|
import WalletExport from './screen/wallets/export';
|
||||||
|
import ExportMultisigCoordinationSetup from './screen/wallets/exportMultisigCoordinationSetup';
|
||||||
import WalletXpub from './screen/wallets/xpub';
|
import WalletXpub from './screen/wallets/xpub';
|
||||||
import BuyBitcoin from './screen/wallets/buyBitcoin';
|
import BuyBitcoin from './screen/wallets/buyBitcoin';
|
||||||
import HodlHodl from './screen/wallets/hodlHodl';
|
import HodlHodl from './screen/wallets/hodlHodl';
|
||||||
|
@ -53,6 +54,7 @@ import ScanQRCode from './screen/send/ScanQRCode';
|
||||||
import SendCreate from './screen/send/create';
|
import SendCreate from './screen/send/create';
|
||||||
import Confirm from './screen/send/confirm';
|
import Confirm from './screen/send/confirm';
|
||||||
import PsbtWithHardwareWallet from './screen/send/psbtWithHardwareWallet';
|
import PsbtWithHardwareWallet from './screen/send/psbtWithHardwareWallet';
|
||||||
|
import PsbtMultisig from './screen/send/psbtMultisig';
|
||||||
import Success from './screen/send/success';
|
import Success from './screen/send/success';
|
||||||
import Broadcast from './screen/send/broadcast';
|
import Broadcast from './screen/send/broadcast';
|
||||||
|
|
||||||
|
@ -79,7 +81,11 @@ const defaultScreenOptions =
|
||||||
...TransitionPresets.ModalPresentationIOS,
|
...TransitionPresets.ModalPresentationIOS,
|
||||||
gestureResponseDistance: { vertical: Dimensions.get('window').height, horizontal: 50 },
|
gestureResponseDistance: { vertical: Dimensions.get('window').height, horizontal: 50 },
|
||||||
})
|
})
|
||||||
: undefined;
|
: {
|
||||||
|
gestureEnabled: true,
|
||||||
|
cardOverlayEnabled: true,
|
||||||
|
...TransitionPresets.ScaleFromCenterAndroid,
|
||||||
|
};
|
||||||
const defaultStackScreenOptions =
|
const defaultStackScreenOptions =
|
||||||
Platform.OS === 'ios'
|
Platform.OS === 'ios'
|
||||||
? {
|
? {
|
||||||
|
@ -88,11 +94,15 @@ const defaultStackScreenOptions =
|
||||||
cardStyle: { backgroundColor: '#FFFFFF' },
|
cardStyle: { backgroundColor: '#FFFFFF' },
|
||||||
headerStatusBarHeight: 10,
|
headerStatusBarHeight: 10,
|
||||||
}
|
}
|
||||||
: undefined;
|
: {
|
||||||
|
gestureEnabled: true,
|
||||||
|
cardOverlayEnabled: true,
|
||||||
|
...TransitionPresets.ScaleFromCenterAndroid,
|
||||||
|
};
|
||||||
|
|
||||||
const WalletsStack = createStackNavigator();
|
const WalletsStack = createStackNavigator();
|
||||||
const WalletsRoot = () => (
|
const WalletsRoot = () => (
|
||||||
<WalletsStack.Navigator>
|
<WalletsStack.Navigator {...(Platform.OS === 'android' ? { screenOptions: defaultScreenOptions } : null)}>
|
||||||
<WalletsStack.Screen name="WalletsList" component={WalletsList} options={WalletsList.navigationOptions} />
|
<WalletsStack.Screen name="WalletsList" component={WalletsList} options={WalletsList.navigationOptions} />
|
||||||
<WalletsStack.Screen name="WalletTransactions" component={WalletTransactions} options={WalletTransactions.navigationOptions} />
|
<WalletsStack.Screen name="WalletTransactions" component={WalletTransactions} options={WalletTransactions.navigationOptions} />
|
||||||
<WalletsStack.Screen name="WalletDetails" component={WalletDetails} options={WalletDetails.navigationOptions} />
|
<WalletsStack.Screen name="WalletDetails" component={WalletDetails} options={WalletDetails.navigationOptions} />
|
||||||
|
@ -167,17 +177,8 @@ const SendDetailsRoot = () => (
|
||||||
component={PsbtWithHardwareWallet}
|
component={PsbtWithHardwareWallet}
|
||||||
options={PsbtWithHardwareWallet.navigationOptions}
|
options={PsbtWithHardwareWallet.navigationOptions}
|
||||||
/>
|
/>
|
||||||
<SendDetailsStack.Screen
|
<SendDetailsStack.Screen name="CreateTransaction" component={SendCreate} options={SendCreate.navigationOptions} />
|
||||||
name="CreateTransaction"
|
<SendDetailsStack.Screen name="PsbtMultisig" component={PsbtMultisig} options={PsbtMultisig.navigationOptions} />
|
||||||
component={SendCreate}
|
|
||||||
options={{
|
|
||||||
headerStyle: {
|
|
||||||
backgroundColor: '#FFFFFF',
|
|
||||||
borderBottomWidth: 0,
|
|
||||||
},
|
|
||||||
headerTintColor: '#0c2550',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<SendDetailsStack.Screen name="Success" component={Success} options={Success.navigationOptions} />
|
<SendDetailsStack.Screen name="Success" component={Success} options={Success.navigationOptions} />
|
||||||
<SendDetailsStack.Screen name="SelectWallet" component={SelectWallet} options={SelectWallet.navigationOptions} />
|
<SendDetailsStack.Screen name="SelectWallet" component={SelectWallet} options={SelectWallet.navigationOptions} />
|
||||||
</SendDetailsStack.Navigator>
|
</SendDetailsStack.Navigator>
|
||||||
|
@ -263,11 +264,12 @@ function DrawerRoot() {
|
||||||
const dimensions = useWindowDimensions();
|
const dimensions = useWindowDimensions();
|
||||||
const isLargeScreen = Platform.OS === 'android' ? isTablet() : dimensions.width >= Dimensions.get('screen').width / 3 && isTablet();
|
const isLargeScreen = Platform.OS === 'android' ? isTablet() : dimensions.width >= Dimensions.get('screen').width / 3 && isTablet();
|
||||||
const drawerStyle = { width: '0%' };
|
const drawerStyle = { width: '0%' };
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Drawer.Navigator
|
<Drawer.Navigator
|
||||||
drawerStyle={isLargeScreen ? null : drawerStyle}
|
drawerStyle={isLargeScreen ? null : drawerStyle}
|
||||||
drawerType={isLargeScreen ? 'permanent' : null}
|
drawerType={isLargeScreen ? 'permanent' : null}
|
||||||
drawerContent={props => <DrawerList {...props} />}
|
drawerContent={props => (isLargeScreen ? <DrawerList {...props} /> : null)}
|
||||||
>
|
>
|
||||||
<Drawer.Screen name="Navigation" component={Navigation} options={{ headerShown: false, gestureEnabled: false }} />
|
<Drawer.Screen name="Navigation" component={Navigation} options={{ headerShown: false, gestureEnabled: false }} />
|
||||||
</Drawer.Navigator>
|
</Drawer.Navigator>
|
||||||
|
@ -304,6 +306,11 @@ const Navigation = () => (
|
||||||
|
|
||||||
{/* screens */}
|
{/* screens */}
|
||||||
<RootStack.Screen name="WalletExport" component={WalletExport} options={WalletExport.navigationOptions} />
|
<RootStack.Screen name="WalletExport" component={WalletExport} options={WalletExport.navigationOptions} />
|
||||||
|
<RootStack.Screen
|
||||||
|
name="ExportMultisigCoordinationSetup"
|
||||||
|
component={ExportMultisigCoordinationSetup}
|
||||||
|
options={ExportMultisigCoordinationSetup.navigationOptions}
|
||||||
|
/>
|
||||||
<RootStack.Screen name="WalletXpub" component={WalletXpub} options={WalletXpub.navigationOptions} />
|
<RootStack.Screen name="WalletXpub" component={WalletXpub} options={WalletXpub.navigationOptions} />
|
||||||
<RootStack.Screen name="BuyBitcoin" component={BuyBitcoin} options={BuyBitcoin.navigationOptions} />
|
<RootStack.Screen name="BuyBitcoin" component={BuyBitcoin} options={BuyBitcoin.navigationOptions} />
|
||||||
<RootStack.Screen name="Marketplace" component={Marketplace} options={Marketplace.navigationOptions} />
|
<RootStack.Screen name="Marketplace" component={Marketplace} options={Marketplace.navigationOptions} />
|
||||||
|
@ -315,7 +322,7 @@ const Navigation = () => (
|
||||||
name="ScanQRCodeRoot"
|
name="ScanQRCodeRoot"
|
||||||
component={ScanQRCodeRoot}
|
component={ScanQRCodeRoot}
|
||||||
options={{
|
options={{
|
||||||
...TransitionPresets.ModalTransition,
|
...(Platform.OS === 'ios' ? TransitionPresets.ModalTransition : TransitionPresets.ScaleFromCenterAndroid),
|
||||||
headerShown: false,
|
headerShown: false,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -69,7 +69,7 @@ The above command will build the app and install it. Once you launch the app it
|
||||||
* To run on iOS:
|
* To run on iOS:
|
||||||
|
|
||||||
```
|
```
|
||||||
npx podinstall
|
npx pod-install
|
||||||
npm start
|
npm start
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -127,12 +127,16 @@ android {
|
||||||
targetCompatibility JavaVersion.VERSION_1_8
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lintOptions {
|
||||||
|
abortOnError false
|
||||||
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "io.bluewallet.bluewallet"
|
applicationId "io.bluewallet.bluewallet"
|
||||||
minSdkVersion rootProject.ext.minSdkVersion
|
minSdkVersion rootProject.ext.minSdkVersion
|
||||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||||
versionCode 1
|
versionCode 1
|
||||||
versionName "5.6.0"
|
versionName "5.6.2"
|
||||||
multiDexEnabled true
|
multiDexEnabled true
|
||||||
missingDimensionStrategy 'react-native-camera', 'general'
|
missingDimensionStrategy 'react-native-camera', 'general'
|
||||||
testBuildType System.getProperty('testBuildType', 'debug') // This will later be used to control the test apk build type
|
testBuildType System.getProperty('testBuildType', 'debug') // This will later be used to control the test apk build type
|
||||||
|
@ -155,13 +159,6 @@ android {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
packagingOptions {
|
|
||||||
pickFirst "lib/armeabi-v7a/libc++_shared.so"
|
|
||||||
pickFirst "lib/arm64-v8a/libc++_shared.so"
|
|
||||||
pickFirst "lib/x86/libc++_shared.so"
|
|
||||||
pickFirst "lib/x86_64/libc++_shared.so"
|
|
||||||
}
|
|
||||||
|
|
||||||
// applicationVariants are e.g. debug, release
|
// applicationVariants are e.g. debug, release
|
||||||
applicationVariants.all { variant ->
|
applicationVariants.all { variant ->
|
||||||
variant.outputs.each { output ->
|
variant.outputs.each { output ->
|
||||||
|
|
|
@ -2,11 +2,11 @@
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
ext {
|
ext {
|
||||||
buildToolsVersion = "28.0.3"
|
|
||||||
minSdkVersion = 18
|
minSdkVersion = 18
|
||||||
compileSdkVersion = 28
|
|
||||||
targetSdkVersion = 28
|
|
||||||
supportLibVersion = "28.0.0"
|
supportLibVersion = "28.0.0"
|
||||||
|
buildToolsVersion = "29.0.2"
|
||||||
|
compileSdkVersion = 29
|
||||||
|
targetSdkVersion = 29
|
||||||
googlePlayServicesVersion = "16.+"
|
googlePlayServicesVersion = "16.+"
|
||||||
firebaseVersion = "17.3.4"
|
firebaseVersion = "17.3.4"
|
||||||
firebaseMessagingVersion = "20.2.1"
|
firebaseMessagingVersion = "20.2.1"
|
||||||
|
|
|
@ -31,4 +31,4 @@ org.gradle.configureondemand=true
|
||||||
org.gradle.jvmargs=-Xmx4g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
org.gradle.jvmargs=-Xmx4g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||||
|
|
||||||
# Version of flipper SDK to use with React Native
|
# Version of flipper SDK to use with React Native
|
||||||
FLIPPER_VERSION=0.33.1
|
FLIPPER_VERSION=0.54.0
|
30
android/gradlew
vendored
30
android/gradlew
vendored
|
@ -154,19 +154,19 @@ if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||||
else
|
else
|
||||||
eval `echo args$i`="\"$arg\""
|
eval `echo args$i`="\"$arg\""
|
||||||
fi
|
fi
|
||||||
i=$((i+1))
|
i=`expr $i + 1`
|
||||||
done
|
done
|
||||||
case $i in
|
case $i in
|
||||||
(0) set -- ;;
|
0) set -- ;;
|
||||||
(1) set -- "$args0" ;;
|
1) set -- "$args0" ;;
|
||||||
(2) set -- "$args0" "$args1" ;;
|
2) set -- "$args0" "$args1" ;;
|
||||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
3) set -- "$args0" "$args1" "$args2" ;;
|
||||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -175,15 +175,9 @@ save () {
|
||||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||||
echo " "
|
echo " "
|
||||||
}
|
}
|
||||||
APP_ARGS=$(save "$@")
|
APP_ARGS=`save "$@"`
|
||||||
|
|
||||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||||
|
|
||||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
|
||||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
|
||||||
cd "$(dirname "$0")"
|
|
||||||
fi
|
|
||||||
|
|
||||||
exec "$JAVACMD" "$@"
|
exec "$JAVACMD" "$@"
|
||||||
|
|
|
@ -521,15 +521,27 @@ module.exports.calculateBlockTime = function (height) {
|
||||||
*/
|
*/
|
||||||
module.exports.testConnection = async function (host, tcpPort, sslPort) {
|
module.exports.testConnection = async function (host, tcpPort, sslPort) {
|
||||||
const client = new ElectrumClient(sslPort || tcpPort, host, sslPort ? 'tls' : 'tcp');
|
const client = new ElectrumClient(sslPort || tcpPort, host, sslPort ? 'tls' : 'tcp');
|
||||||
|
client.onError = () => {}; // mute
|
||||||
|
let timeoutId = false;
|
||||||
try {
|
try {
|
||||||
await client.connect();
|
const rez = await Promise.race([
|
||||||
|
new Promise(resolve => {
|
||||||
|
timeoutId = setTimeout(() => resolve('timeout'), 3000);
|
||||||
|
}),
|
||||||
|
client.connect(),
|
||||||
|
]);
|
||||||
|
if (rez === 'timeout') return false;
|
||||||
|
|
||||||
await client.server_version('2.7.11', '1.4');
|
await client.server_version('2.7.11', '1.4');
|
||||||
await client.server_ping();
|
await client.server_ping();
|
||||||
client.close();
|
|
||||||
return true;
|
return true;
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
return false;
|
} finally {
|
||||||
|
if (timeoutId) clearTimeout(timeoutId);
|
||||||
|
client.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.forceDisconnect = () => {
|
module.exports.forceDisconnect = () => {
|
||||||
|
|
109
blue_modules/fs.js
Normal file
109
blue_modules/fs.js
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
/* global alert */
|
||||||
|
import { PermissionsAndroid, Platform } from 'react-native';
|
||||||
|
import RNFS from 'react-native-fs';
|
||||||
|
import Share from 'react-native-share';
|
||||||
|
import loc from '../loc';
|
||||||
|
import { getSystemName } from 'react-native-device-info';
|
||||||
|
import DocumentPicker from 'react-native-document-picker';
|
||||||
|
|
||||||
|
const isDesktop = getSystemName() === 'Mac OS X';
|
||||||
|
|
||||||
|
const writeFileAndExport = async function (filename, contents) {
|
||||||
|
if (Platform.OS === 'ios') {
|
||||||
|
const filePath = RNFS.TemporaryDirectoryPath + `/${filename}`;
|
||||||
|
await RNFS.writeFile(filePath, contents);
|
||||||
|
Share.open({
|
||||||
|
url: 'file://' + filePath,
|
||||||
|
saveToFiles: isDesktop,
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.log(error);
|
||||||
|
// alert(error.message);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
RNFS.unlink(filePath);
|
||||||
|
});
|
||||||
|
} else if (Platform.OS === 'android') {
|
||||||
|
const granted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE, {
|
||||||
|
title: loc.send.permission_storage_title,
|
||||||
|
message: loc.send.permission_storage_message,
|
||||||
|
buttonNeutral: loc.send.permission_storage_later,
|
||||||
|
buttonNegative: loc._.cancel,
|
||||||
|
buttonPositive: loc._.ok,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
|
||||||
|
console.log('Storage Permission: Granted');
|
||||||
|
const filePath = RNFS.DownloadDirectoryPath + `/${filename}`;
|
||||||
|
await RNFS.writeFile(filePath, contents);
|
||||||
|
alert(loc.formatString(loc._.file_saved, { filePath: filename }));
|
||||||
|
} else {
|
||||||
|
console.log('Storage Permission: Denied');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens & reads *.psbt files, and returns base64 psbt. FALSE if something went wrong (wont throw).
|
||||||
|
*
|
||||||
|
* @returns {Promise<string|boolean>} Base64 PSBT
|
||||||
|
*/
|
||||||
|
const openSignedTransaction = async function () {
|
||||||
|
try {
|
||||||
|
const res = await DocumentPicker.pick({
|
||||||
|
type: Platform.OS === 'ios' ? ['io.bluewallet.psbt', 'io.bluewallt.psbt.txn'] : [DocumentPicker.types.allFiles],
|
||||||
|
});
|
||||||
|
|
||||||
|
return await _readPsbtFileIntoBase64(res.uri);
|
||||||
|
} catch (err) {
|
||||||
|
if (!DocumentPicker.isCancel(err)) {
|
||||||
|
alert(loc.send.details_no_signed_tx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const _readPsbtFileIntoBase64 = async function (uri) {
|
||||||
|
const base64 = await RNFS.readFile(uri, 'base64');
|
||||||
|
const stringData = Buffer.from(base64, 'base64').toString(); // decode from base64
|
||||||
|
if (stringData.startsWith('psbt')) {
|
||||||
|
// file was binary, but outer code expects base64 psbt, so we return base64 we got from rn-fs;
|
||||||
|
// most likely produced by Electrum-desktop
|
||||||
|
return base64;
|
||||||
|
} else {
|
||||||
|
// file was a text file, having base64 psbt in there. so we basically have double base64encoded string
|
||||||
|
// thats why we are returning string that was decoded once;
|
||||||
|
// most likely produced by Coldcard
|
||||||
|
return stringData;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const showFilePickerAndReadFile = async function () {
|
||||||
|
try {
|
||||||
|
const res = await DocumentPicker.pick({
|
||||||
|
type:
|
||||||
|
Platform.OS === 'ios'
|
||||||
|
? ['io.bluewallet.psbt', 'io.bluewallet.psbt.txn', DocumentPicker.types.plainText, 'public.json']
|
||||||
|
: [DocumentPicker.types.allFiles],
|
||||||
|
});
|
||||||
|
|
||||||
|
let file = false;
|
||||||
|
if (res.uri.toLowerCase().endsWith('.psbt')) {
|
||||||
|
// this is either binary file from ElectrumDesktop OR string file with base64 string in there
|
||||||
|
file = await _readPsbtFileIntoBase64(res.uri);
|
||||||
|
} else {
|
||||||
|
file = await RNFS.readFile(res.uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { data: file, uri: res.uri };
|
||||||
|
} catch (err) {
|
||||||
|
if (!DocumentPicker.isCancel(err)) {
|
||||||
|
return { data: false, uri: false };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.writeFileAndExport = writeFileAndExport;
|
||||||
|
module.exports.openSignedTransaction = openSignedTransaction;
|
||||||
|
module.exports.showFilePickerAndReadFile = showFilePickerAndReadFile;
|
|
@ -14,6 +14,7 @@ import {
|
||||||
LightningCustodianWallet,
|
LightningCustodianWallet,
|
||||||
HDLegacyElectrumSeedP2PKHWallet,
|
HDLegacyElectrumSeedP2PKHWallet,
|
||||||
HDSegwitElectrumSeedP2WPKHWallet,
|
HDSegwitElectrumSeedP2WPKHWallet,
|
||||||
|
MultisigHDWallet,
|
||||||
} from './';
|
} from './';
|
||||||
import DeviceQuickActions from './quick-actions';
|
import DeviceQuickActions from './quick-actions';
|
||||||
import { AbstractHDElectrumWallet } from './wallets/abstract-hd-electrum-wallet';
|
import { AbstractHDElectrumWallet } from './wallets/abstract-hd-electrum-wallet';
|
||||||
|
@ -297,6 +298,9 @@ export class AppStorage {
|
||||||
case HDSegwitElectrumSeedP2WPKHWallet.type:
|
case HDSegwitElectrumSeedP2WPKHWallet.type:
|
||||||
unserializedWallet = HDSegwitElectrumSeedP2WPKHWallet.fromJson(key);
|
unserializedWallet = HDSegwitElectrumSeedP2WPKHWallet.fromJson(key);
|
||||||
break;
|
break;
|
||||||
|
case MultisigHDWallet.type:
|
||||||
|
unserializedWallet = MultisigHDWallet.fromJson(key);
|
||||||
|
break;
|
||||||
case LightningCustodianWallet.type: {
|
case LightningCustodianWallet.type: {
|
||||||
/** @type {LightningCustodianWallet} */
|
/** @type {LightningCustodianWallet} */
|
||||||
unserializedWallet = LightningCustodianWallet.fromJson(key);
|
unserializedWallet = LightningCustodianWallet.fromJson(key);
|
||||||
|
@ -433,7 +437,7 @@ export class AppStorage {
|
||||||
const realm = await this.getRealm();
|
const realm = await this.getRealm();
|
||||||
for (const key of this.wallets) {
|
for (const key of this.wallets) {
|
||||||
if (typeof key === 'boolean' || key.type === PlaceholderWallet.type) continue;
|
if (typeof key === 'boolean' || key.type === PlaceholderWallet.type) continue;
|
||||||
if (key.prepareForSerialization) key.prepareForSerialization();
|
key.prepareForSerialization();
|
||||||
const keyCloned = Object.assign({}, key); // stripped-down version of a wallet to save to secure keystore
|
const keyCloned = Object.assign({}, key); // stripped-down version of a wallet to save to secure keystore
|
||||||
if (key._hdWalletInstance) keyCloned._hdWalletInstance = Object.assign({}, key._hdWalletInstance);
|
if (key._hdWalletInstance) keyCloned._hdWalletInstance = Object.assign({}, key._hdWalletInstance);
|
||||||
this.offloadWalletToRealm(realm, key);
|
this.offloadWalletToRealm(realm, key);
|
||||||
|
|
|
@ -59,12 +59,12 @@ export default class Biometric {
|
||||||
static async unlockWithBiometrics() {
|
static async unlockWithBiometrics() {
|
||||||
const isDeviceBiometricCapable = await Biometric.isDeviceBiometricCapable();
|
const isDeviceBiometricCapable = await Biometric.isDeviceBiometricCapable();
|
||||||
if (isDeviceBiometricCapable) {
|
if (isDeviceBiometricCapable) {
|
||||||
try {
|
return new Promise(resolve => {
|
||||||
const isConfirmed = await FingerprintScanner.authenticate({ description: 'Please confirm your identity.', fallbackEnabled: true });
|
FingerprintScanner.authenticate({ description: 'Please confirm your identity.', fallbackEnabled: true })
|
||||||
return isConfirmed;
|
.then(() => resolve(true))
|
||||||
} catch (_e) {
|
.catch(() => resolve(false))
|
||||||
return false;
|
.finally(() => FingerprintScanner.release());
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,9 +40,8 @@ class DeeplinkSchemaMatch {
|
||||||
if (event.url.toLowerCase().startsWith('bluewallet:bitcoin:') || event.url.toLowerCase().startsWith('bluewallet:lightning:')) {
|
if (event.url.toLowerCase().startsWith('bluewallet:bitcoin:') || event.url.toLowerCase().startsWith('bluewallet:lightning:')) {
|
||||||
event.url = event.url.substring(11);
|
event.url = event.url.substring(11);
|
||||||
}
|
}
|
||||||
|
if (DeeplinkSchemaMatch.isPossiblySignedPSBTFile(event.url)) {
|
||||||
if (DeeplinkSchemaMatch.isPossiblyPSBTFile(event.url)) {
|
RNFS.readFile(decodeURI(event.url))
|
||||||
RNFS.readFile(event.url)
|
|
||||||
.then(file => {
|
.then(file => {
|
||||||
if (file) {
|
if (file) {
|
||||||
completionHandler([
|
completionHandler([
|
||||||
|
@ -203,13 +202,23 @@ class DeeplinkSchemaMatch {
|
||||||
}
|
}
|
||||||
|
|
||||||
static isTXNFile(filePath) {
|
static isTXNFile(filePath) {
|
||||||
return filePath.toLowerCase().startsWith('file:') && filePath.toLowerCase().endsWith('.txn');
|
return (
|
||||||
|
(filePath.toLowerCase().startsWith('file:') || filePath.toLowerCase().startsWith('content:')) &&
|
||||||
|
filePath.toLowerCase().endsWith('.txn')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static isPossiblySignedPSBTFile(filePath) {
|
||||||
|
return (
|
||||||
|
(filePath.toLowerCase().startsWith('file:') || filePath.toLowerCase().startsWith('content:')) &&
|
||||||
|
filePath.toLowerCase().endsWith('-signed.psbt')
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static isPossiblyPSBTFile(filePath) {
|
static isPossiblyPSBTFile(filePath) {
|
||||||
return (
|
return (
|
||||||
(filePath.toLowerCase().startsWith('file:') || filePath.toLowerCase().startsWith('content:')) &&
|
(filePath.toLowerCase().startsWith('file:') || filePath.toLowerCase().startsWith('content:')) &&
|
||||||
filePath.toLowerCase().endsWith('-signed.psbt')
|
filePath.toLowerCase().endsWith('.psbt')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -314,6 +323,31 @@ class DeeplinkSchemaMatch {
|
||||||
static bip21encode() {
|
static bip21encode() {
|
||||||
return bip21.encode.apply(bip21, arguments);
|
return bip21.encode.apply(bip21, arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static decodeBitcoinUri(uri) {
|
||||||
|
let amount = '';
|
||||||
|
let parsedBitcoinUri = null;
|
||||||
|
let address = uri || '';
|
||||||
|
let memo = '';
|
||||||
|
let payjoinUrl = '';
|
||||||
|
try {
|
||||||
|
parsedBitcoinUri = DeeplinkSchemaMatch.bip21decode(uri);
|
||||||
|
address = 'address' in parsedBitcoinUri ? parsedBitcoinUri.address : address;
|
||||||
|
if ('options' in parsedBitcoinUri) {
|
||||||
|
if ('amount' in parsedBitcoinUri.options) {
|
||||||
|
amount = parsedBitcoinUri.options.amount.toString();
|
||||||
|
amount = parsedBitcoinUri.options.amount;
|
||||||
|
}
|
||||||
|
if ('label' in parsedBitcoinUri.options) {
|
||||||
|
memo = parsedBitcoinUri.options.label || memo;
|
||||||
|
}
|
||||||
|
if ('pj' in parsedBitcoinUri.options) {
|
||||||
|
payjoinUrl = parsedBitcoinUri.options.pj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (_) {}
|
||||||
|
return { address, amount, memo, payjoinUrl };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DeeplinkSchemaMatch;
|
export default DeeplinkSchemaMatch;
|
||||||
|
|
|
@ -214,6 +214,32 @@ export class HDSegwitBech32Transaction {
|
||||||
return { fee, feeRate, targets, changeAmount, utxos, unconfirmedUtxos };
|
return { fee, feeRate, targets, changeAmount, utxos, unconfirmedUtxos };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We get _all_ our UTXOs (even spent kek),
|
||||||
|
* and see if each input in this transaction's UTXO is in there. If its not there - its an unknown
|
||||||
|
* input, we dont own it (possibly a payjoin transaction), and we cant do RBF
|
||||||
|
*
|
||||||
|
* @returns {Promise<boolean>}
|
||||||
|
*/
|
||||||
|
async thereAreUnknownInputsInTx() {
|
||||||
|
if (!this._wallet) throw new Error('Wallet required for this method');
|
||||||
|
if (!this._txDecoded) await this._fetchTxhexAndDecode();
|
||||||
|
|
||||||
|
const spentUtxos = this._wallet.getDerivedUtxoFromOurTransaction(true);
|
||||||
|
for (const inp of this._txDecoded.ins) {
|
||||||
|
const txidInUtxo = reverse(inp.hash).toString('hex');
|
||||||
|
|
||||||
|
let found = false;
|
||||||
|
for (const spentU of spentUtxos) {
|
||||||
|
if (spentU.txid === txidInUtxo && spentU.vout === inp.index) found = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if all outputs belong to us, that
|
* Checks if all outputs belong to us, that
|
||||||
* means we already canceled this tx and we can only bump fees
|
* means we already canceled this tx and we can only bump fees
|
||||||
|
@ -224,6 +250,8 @@ export class HDSegwitBech32Transaction {
|
||||||
if (!this._wallet) throw new Error('Wallet required for this method');
|
if (!this._wallet) throw new Error('Wallet required for this method');
|
||||||
if (!this._txDecoded) await this._fetchTxhexAndDecode();
|
if (!this._txDecoded) await this._fetchTxhexAndDecode();
|
||||||
|
|
||||||
|
if (await this.thereAreUnknownInputsInTx()) return false;
|
||||||
|
|
||||||
// if theres at least one output we dont own - we can cancel this transaction!
|
// if theres at least one output we dont own - we can cancel this transaction!
|
||||||
for (const outp of this._txDecoded.outs) {
|
for (const outp of this._txDecoded.outs) {
|
||||||
if (!this._wallet.weOwnAddress(SegwitBech32Wallet.scriptPubKeyToAddress(outp.script))) return true;
|
if (!this._wallet.weOwnAddress(SegwitBech32Wallet.scriptPubKeyToAddress(outp.script))) return true;
|
||||||
|
@ -232,6 +260,15 @@ export class HDSegwitBech32Transaction {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async canBumpTx() {
|
||||||
|
if (!this._wallet) throw new Error('Wallet required for this method');
|
||||||
|
if (!this._txDecoded) await this._fetchTxhexAndDecode();
|
||||||
|
|
||||||
|
if (await this.thereAreUnknownInputsInTx()) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an RBF transaction that can replace previous one and basically cancel it (rewrite
|
* Creates an RBF transaction that can replace previous one and basically cancel it (rewrite
|
||||||
* output to the one our wallet controls). Note, this cannot add more utxo in RBF transaction if
|
* output to the one our wallet controls). Note, this cannot add more utxo in RBF transaction if
|
||||||
|
|
|
@ -14,3 +14,4 @@ export * from './hd-segwit-bech32-transaction';
|
||||||
export * from './wallets/placeholder-wallet';
|
export * from './wallets/placeholder-wallet';
|
||||||
export * from './wallets/hd-legacy-electrum-seed-p2pkh-wallet';
|
export * from './wallets/hd-legacy-electrum-seed-p2pkh-wallet';
|
||||||
export * from './wallets/hd-segwit-electrum-seed-p2wpkh-wallet';
|
export * from './wallets/hd-segwit-electrum-seed-p2wpkh-wallet';
|
||||||
|
export * from './wallets/multisig-hd-wallet';
|
||||||
|
|
85
class/payjoin-transaction.js
Normal file
85
class/payjoin-transaction.js
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
/* global alert */
|
||||||
|
import * as bitcoin from 'bitcoinjs-lib';
|
||||||
|
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||||
|
|
||||||
|
const delay = milliseconds => new Promise(resolve => setTimeout(resolve, milliseconds));
|
||||||
|
|
||||||
|
// Implements IPayjoinClientWallet
|
||||||
|
// https://github.com/bitcoinjs/payjoin-client/blob/master/ts_src/wallet.ts
|
||||||
|
export default class PayjoinTransaction {
|
||||||
|
constructor(psbt, broadcast, wallet) {
|
||||||
|
this._psbt = psbt;
|
||||||
|
this._broadcast = broadcast;
|
||||||
|
this._wallet = wallet;
|
||||||
|
this._payjoinPsbt = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPsbt() {
|
||||||
|
// Nasty hack to get this working for now
|
||||||
|
const unfinalized = this._psbt.clone();
|
||||||
|
unfinalized.data.inputs.forEach((input, index) => {
|
||||||
|
delete input.finalScriptWitness;
|
||||||
|
|
||||||
|
const address = bitcoin.address.fromOutputScript(input.witnessUtxo.script);
|
||||||
|
const wif = this._wallet._getWifForAddress(address);
|
||||||
|
const keyPair = bitcoin.ECPair.fromWIF(wif);
|
||||||
|
|
||||||
|
unfinalized.signInput(index, keyPair);
|
||||||
|
});
|
||||||
|
|
||||||
|
return unfinalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Doesnt conform to spec but needed for user-facing wallet software to find out txid of payjoined transaction
|
||||||
|
*
|
||||||
|
* @returns {boolean|Psbt}
|
||||||
|
*/
|
||||||
|
getPayjoinPsbt() {
|
||||||
|
return this._payjoinPsbt;
|
||||||
|
}
|
||||||
|
|
||||||
|
async signPsbt(payjoinPsbt) {
|
||||||
|
// Do this without relying on private methods
|
||||||
|
payjoinPsbt.data.inputs.forEach((input, index) => {
|
||||||
|
const address = bitcoin.address.fromOutputScript(input.witnessUtxo.script);
|
||||||
|
try {
|
||||||
|
const wif = this._wallet._getWifForAddress(address);
|
||||||
|
const keyPair = bitcoin.ECPair.fromWIF(wif);
|
||||||
|
payjoinPsbt.signInput(index, keyPair).finalizeInput(index);
|
||||||
|
} catch (e) {}
|
||||||
|
});
|
||||||
|
|
||||||
|
this._payjoinPsbt = payjoinPsbt;
|
||||||
|
return this._payjoinPsbt;
|
||||||
|
}
|
||||||
|
|
||||||
|
async broadcastTx(txHex) {
|
||||||
|
try {
|
||||||
|
const result = await this._broadcast(txHex);
|
||||||
|
if (!result) {
|
||||||
|
throw new Error(`Broadcast failed`);
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
} catch (e) {
|
||||||
|
return 'Error: ' + e.message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async scheduleBroadcastTx(txHex, milliseconds) {
|
||||||
|
delay(milliseconds).then(async () => {
|
||||||
|
const result = await this.broadcastTx(txHex);
|
||||||
|
if (result === '') {
|
||||||
|
// TODO: Improve the wording of this error message
|
||||||
|
ReactNativeHapticFeedback.trigger('notificationError', { ignoreAndroidSystemSettings: false });
|
||||||
|
alert('Something was wrong with the payjoin transaction, the original transaction sucessfully broadcast.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async isOwnOutputScript(outputScript) {
|
||||||
|
const address = bitcoin.address.fromOutputScript(outputScript);
|
||||||
|
|
||||||
|
return this._wallet.weOwnAddress(address);
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,7 +9,8 @@ import { PlaceholderWallet } from './wallets/placeholder-wallet';
|
||||||
import { SegwitBech32Wallet } from './wallets/segwit-bech32-wallet';
|
import { SegwitBech32Wallet } from './wallets/segwit-bech32-wallet';
|
||||||
import { HDLegacyElectrumSeedP2PKHWallet } from './wallets/hd-legacy-electrum-seed-p2pkh-wallet';
|
import { HDLegacyElectrumSeedP2PKHWallet } from './wallets/hd-legacy-electrum-seed-p2pkh-wallet';
|
||||||
import { HDSegwitElectrumSeedP2WPKHWallet } from './wallets/hd-segwit-electrum-seed-p2wpkh-wallet';
|
import { HDSegwitElectrumSeedP2WPKHWallet } from './wallets/hd-segwit-electrum-seed-p2wpkh-wallet';
|
||||||
import { BlueCurrentTheme } from '../components/themes';
|
import { MultisigHDWallet } from './wallets/multisig-hd-wallet';
|
||||||
|
import { useTheme } from '@react-navigation/native';
|
||||||
|
|
||||||
export default class WalletGradient {
|
export default class WalletGradient {
|
||||||
static hdSegwitP2SHWallet = ['#65ceef', '#68bbe1'];
|
static hdSegwitP2SHWallet = ['#65ceef', '#68bbe1'];
|
||||||
|
@ -19,9 +20,15 @@ export default class WalletGradient {
|
||||||
static legacyWallet = ['#40fad1', '#15be98'];
|
static legacyWallet = ['#40fad1', '#15be98'];
|
||||||
static hdLegacyP2PKHWallet = ['#e36dfa', '#bd10e0'];
|
static hdLegacyP2PKHWallet = ['#e36dfa', '#bd10e0'];
|
||||||
static hdLegacyBreadWallet = ['#fe6381', '#f99c42'];
|
static hdLegacyBreadWallet = ['#fe6381', '#f99c42'];
|
||||||
|
static multisigHdWallet = ['#1ce6eb', '#296fc5', '#3500A2'];
|
||||||
static defaultGradients = ['#c65afb', '#9053fe'];
|
static defaultGradients = ['#c65afb', '#9053fe'];
|
||||||
static lightningCustodianWallet = ['#f1be07', '#f79056'];
|
static lightningCustodianWallet = ['#f1be07', '#f79056'];
|
||||||
static createWallet = BlueCurrentTheme.colors.lightButton;
|
|
||||||
|
static createWallet = () => {
|
||||||
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||||
|
const { colors } = useTheme();
|
||||||
|
return colors.lightButton;
|
||||||
|
};
|
||||||
|
|
||||||
static gradientsFor(type) {
|
static gradientsFor(type) {
|
||||||
let gradient;
|
let gradient;
|
||||||
|
@ -55,6 +62,9 @@ export default class WalletGradient {
|
||||||
case SegwitBech32Wallet.type:
|
case SegwitBech32Wallet.type:
|
||||||
gradient = WalletGradient.segwitBech32Wallet;
|
gradient = WalletGradient.segwitBech32Wallet;
|
||||||
break;
|
break;
|
||||||
|
case MultisigHDWallet.type:
|
||||||
|
gradient = WalletGradient.multisigHdWallet;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
gradient = WalletGradient.defaultGradients;
|
gradient = WalletGradient.defaultGradients;
|
||||||
break;
|
break;
|
||||||
|
@ -88,6 +98,9 @@ export default class WalletGradient {
|
||||||
case SegwitBech32Wallet.type:
|
case SegwitBech32Wallet.type:
|
||||||
gradient = WalletGradient.segwitBech32Wallet;
|
gradient = WalletGradient.segwitBech32Wallet;
|
||||||
break;
|
break;
|
||||||
|
case MultisigHDWallet.type:
|
||||||
|
gradient = WalletGradient.multisigHdWallet;
|
||||||
|
break;
|
||||||
case LightningCustodianWallet.type:
|
case LightningCustodianWallet.type:
|
||||||
gradient = WalletGradient.lightningCustodianWallet;
|
gradient = WalletGradient.lightningCustodianWallet;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -13,6 +13,7 @@ import {
|
||||||
SegwitBech32Wallet,
|
SegwitBech32Wallet,
|
||||||
HDLegacyElectrumSeedP2PKHWallet,
|
HDLegacyElectrumSeedP2PKHWallet,
|
||||||
HDSegwitElectrumSeedP2WPKHWallet,
|
HDSegwitElectrumSeedP2WPKHWallet,
|
||||||
|
MultisigHDWallet,
|
||||||
} from '.';
|
} from '.';
|
||||||
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||||
import loc from '../loc';
|
import loc from '../loc';
|
||||||
|
@ -34,7 +35,9 @@ export default class WalletImport {
|
||||||
*/
|
*/
|
||||||
static async _saveWallet(w, additionalProperties) {
|
static async _saveWallet(w, additionalProperties) {
|
||||||
try {
|
try {
|
||||||
const wallet = BlueApp.getWallets().some(wallet => wallet.getSecret() === w.secret && wallet.type !== PlaceholderWallet.type);
|
const wallet = BlueApp.getWallets().some(
|
||||||
|
wallet => (wallet.getSecret() === w.secret || wallet.getID() === w.getID()) && wallet.type !== PlaceholderWallet.type,
|
||||||
|
);
|
||||||
if (wallet) {
|
if (wallet) {
|
||||||
alert('This wallet has been previously imported.');
|
alert('This wallet has been previously imported.');
|
||||||
WalletImport.removePlaceholderWallet();
|
WalletImport.removePlaceholderWallet();
|
||||||
|
@ -97,6 +100,7 @@ export default class WalletImport {
|
||||||
const placeholderWallet = WalletImport.addPlaceholderWallet(importText);
|
const placeholderWallet = WalletImport.addPlaceholderWallet(importText);
|
||||||
// Plan:
|
// Plan:
|
||||||
// -2. check if BIP38 encrypted
|
// -2. check if BIP38 encrypted
|
||||||
|
// -1a. check if multisig
|
||||||
// -1. check lightning custodian
|
// -1. check lightning custodian
|
||||||
// 0. check if its HDSegwitBech32Wallet (BIP84)
|
// 0. check if its HDSegwitBech32Wallet (BIP84)
|
||||||
// 1. check if its HDSegwitP2SHWallet (BIP49)
|
// 1. check if its HDSegwitP2SHWallet (BIP49)
|
||||||
|
@ -125,6 +129,18 @@ export default class WalletImport {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// is it multisig?
|
||||||
|
try {
|
||||||
|
const ms = new MultisigHDWallet();
|
||||||
|
ms.setSecret(importText);
|
||||||
|
if (ms.getN() > 0 && ms.getM() > 0) {
|
||||||
|
await ms.fetchBalance();
|
||||||
|
return WalletImport._saveWallet(ms);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
|
||||||
// is it lightning custodian?
|
// is it lightning custodian?
|
||||||
if (importText.indexOf('blitzhub://') !== -1 || importText.indexOf('lndhub://') !== -1) {
|
if (importText.indexOf('blitzhub://') !== -1 || importText.indexOf('lndhub://') !== -1) {
|
||||||
const lnd = new LightningCustodianWallet();
|
const lnd = new LightningCustodianWallet();
|
||||||
|
|
|
@ -553,27 +553,33 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
||||||
async _fetchBalance() {
|
async _fetchBalance() {
|
||||||
// probing future addressess in hierarchy whether they have any transactions, in case
|
// probing future addressess in hierarchy whether they have any transactions, in case
|
||||||
// our 'next free addr' pointers are lagging behind
|
// our 'next free addr' pointers are lagging behind
|
||||||
let tryAgain = false;
|
// for that we are gona batch fetch history for all addresses between last used and last used + gap_limit
|
||||||
let txs = await BlueElectrum.getTransactionsByAddress(
|
|
||||||
this._getExternalAddressByIndex(this.next_free_address_index + this.gap_limit - 1),
|
const lagAddressesToFetch = [];
|
||||||
);
|
for (let c = this.next_free_address_index; c < this.next_free_address_index + this.gap_limit; c++) {
|
||||||
if (txs.length > 0) {
|
lagAddressesToFetch.push(this._getExternalAddressByIndex(c));
|
||||||
|
}
|
||||||
|
for (let c = this.next_free_change_address_index; c < this.next_free_change_address_index + this.gap_limit; c++) {
|
||||||
|
lagAddressesToFetch.push(this._getInternalAddressByIndex(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
const txs = await BlueElectrum.multiGetHistoryByAddress(lagAddressesToFetch); // <------ electrum call
|
||||||
|
|
||||||
|
for (let c = this.next_free_address_index; c < this.next_free_address_index + this.gap_limit; c++) {
|
||||||
|
const address = this._getExternalAddressByIndex(c);
|
||||||
|
if (txs[address] && Array.isArray(txs[address]) && txs[address].length > 0) {
|
||||||
// whoa, someone uses our wallet outside! better catch up
|
// whoa, someone uses our wallet outside! better catch up
|
||||||
this.next_free_address_index += this.gap_limit;
|
this.next_free_address_index = c + 1;
|
||||||
tryAgain = true;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
txs = await BlueElectrum.getTransactionsByAddress(
|
for (let c = this.next_free_change_address_index; c < this.next_free_change_address_index + this.gap_limit; c++) {
|
||||||
this._getInternalAddressByIndex(this.next_free_change_address_index + this.gap_limit - 1),
|
const address = this._getInternalAddressByIndex(c);
|
||||||
);
|
if (txs[address] && Array.isArray(txs[address]) && txs[address].length > 0) {
|
||||||
if (txs.length > 0) {
|
// whoa, someone uses our wallet outside! better catch up
|
||||||
this.next_free_change_address_index += this.gap_limit;
|
this.next_free_change_address_index = c + 1;
|
||||||
tryAgain = true;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: refactor me ^^^ can be batched in single call. plus not just couple of addresses, but all between [ next_free .. (next_free + gap_limit) ]
|
|
||||||
|
|
||||||
if (tryAgain) return this._fetchBalance();
|
|
||||||
|
|
||||||
// next, business as usuall. fetch balances
|
// next, business as usuall. fetch balances
|
||||||
|
|
||||||
|
@ -674,8 +680,9 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
||||||
|
|
||||||
addressess = [...new Set(addressess)]; // deduplicate just for any case
|
addressess = [...new Set(addressess)]; // deduplicate just for any case
|
||||||
|
|
||||||
|
const fetchedUtxo = await BlueElectrum.multiGetUtxoByAddress(addressess);
|
||||||
this._utxo = [];
|
this._utxo = [];
|
||||||
for (const arr of Object.values(await BlueElectrum.multiGetUtxoByAddress(addressess))) {
|
for (const arr of Object.values(fetchedUtxo)) {
|
||||||
this._utxo = this._utxo.concat(arr);
|
this._utxo = this._utxo.concat(arr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -712,15 +719,26 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
||||||
return this._utxo;
|
return this._utxo;
|
||||||
}
|
}
|
||||||
|
|
||||||
getDerivedUtxoFromOurTransaction() {
|
getDerivedUtxoFromOurTransaction(returnSpentUtxoAsWell = false) {
|
||||||
const utxos = [];
|
const utxos = [];
|
||||||
|
|
||||||
|
// its faster to pre-build hashmap of owned addresses than to query `this.weOwnAddress()`, which in turn
|
||||||
|
// iterates over all addresses in hierarchy
|
||||||
|
const ownedAddressesHashmap = {};
|
||||||
|
for (let c = 0; c < this.next_free_address_index + 1; c++) {
|
||||||
|
ownedAddressesHashmap[this._getExternalAddressByIndex(c)] = true;
|
||||||
|
}
|
||||||
|
for (let c = 0; c < this.next_free_change_address_index + 1; c++) {
|
||||||
|
ownedAddressesHashmap[this._getInternalAddressByIndex(c)] = true;
|
||||||
|
}
|
||||||
|
|
||||||
for (const tx of this.getTransactions()) {
|
for (const tx of this.getTransactions()) {
|
||||||
for (const output of tx.outputs) {
|
for (const output of tx.outputs) {
|
||||||
let address = false;
|
let address = false;
|
||||||
if (output.scriptPubKey && output.scriptPubKey.addresses && output.scriptPubKey.addresses[0]) {
|
if (output.scriptPubKey && output.scriptPubKey.addresses && output.scriptPubKey.addresses[0]) {
|
||||||
address = output.scriptPubKey.addresses[0];
|
address = output.scriptPubKey.addresses[0];
|
||||||
}
|
}
|
||||||
if (this.weOwnAddress(address)) {
|
if (ownedAddressesHashmap[address]) {
|
||||||
const value = new BigNumber(output.value).multipliedBy(100000000).toNumber();
|
const value = new BigNumber(output.value).multipliedBy(100000000).toNumber();
|
||||||
utxos.push({
|
utxos.push({
|
||||||
txid: tx.txid,
|
txid: tx.txid,
|
||||||
|
@ -730,13 +748,15 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
||||||
value,
|
value,
|
||||||
amount: value,
|
amount: value,
|
||||||
confirmations: tx.confirmations,
|
confirmations: tx.confirmations,
|
||||||
wif: this._getWifForAddress(address),
|
wif: false,
|
||||||
height: BlueElectrum.estimateCurrentBlockheight() - tx.confirmations,
|
height: BlueElectrum.estimateCurrentBlockheight() - tx.confirmations,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (returnSpentUtxoAsWell) return utxos;
|
||||||
|
|
||||||
// got all utxos we ever had. lets filter out the ones that are spent:
|
// got all utxos we ever had. lets filter out the ones that are spent:
|
||||||
const ret = [];
|
const ret = [];
|
||||||
for (const utxo of utxos) {
|
for (const utxo of utxos) {
|
||||||
|
@ -748,7 +768,11 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!spent) ret.push(utxo);
|
if (!spent) {
|
||||||
|
// filling WIFs only for legit unspent UTXO, as it is a slow operation
|
||||||
|
utxo.wif = this._getWifForAddress(utxo.address);
|
||||||
|
ret.push(utxo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -26,6 +26,10 @@ export class AbstractHDWallet extends LegacyWallet {
|
||||||
return this.next_free_address_index;
|
return this.next_free_address_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getNextFreeChangeAddressIndex() {
|
||||||
|
return this.next_free_change_address_index;
|
||||||
|
}
|
||||||
|
|
||||||
prepareForSerialization() {
|
prepareForSerialization() {
|
||||||
// deleting structures that cant be serialized
|
// deleting structures that cant be serialized
|
||||||
delete this._node0;
|
delete this._node0;
|
||||||
|
@ -93,7 +97,7 @@ export class AbstractHDWallet extends LegacyWallet {
|
||||||
if (!freeAddress) {
|
if (!freeAddress) {
|
||||||
// could not find in cycle above, give up
|
// could not find in cycle above, give up
|
||||||
freeAddress = this._getExternalAddressByIndex(this.next_free_address_index + c); // we didnt check this one, maybe its free
|
freeAddress = this._getExternalAddressByIndex(this.next_free_address_index + c); // we didnt check this one, maybe its free
|
||||||
this.next_free_address_index += c + 1; // now points to the one _after_
|
this.next_free_address_index += c; // now points to this one
|
||||||
}
|
}
|
||||||
this._address = freeAddress;
|
this._address = freeAddress;
|
||||||
return freeAddress;
|
return freeAddress;
|
||||||
|
@ -130,8 +134,8 @@ export class AbstractHDWallet extends LegacyWallet {
|
||||||
|
|
||||||
if (!freeAddress) {
|
if (!freeAddress) {
|
||||||
// could not find in cycle above, give up
|
// could not find in cycle above, give up
|
||||||
freeAddress = this._getExternalAddressByIndex(this.next_free_address_index + c); // we didnt check this one, maybe its free
|
freeAddress = this._getInternalAddressByIndex(this.next_free_change_address_index + c); // we didnt check this one, maybe its free
|
||||||
this.next_free_address_index += c + 1; // now points to the one _after_
|
this.next_free_change_address_index += c; // now points to this one
|
||||||
}
|
}
|
||||||
this._address = freeAddress;
|
this._address = freeAddress;
|
||||||
return freeAddress;
|
return freeAddress;
|
||||||
|
|
|
@ -115,6 +115,10 @@ export class AbstractWallet {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
allowPayJoin() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
weOwnAddress(address) {
|
weOwnAddress(address) {
|
||||||
throw Error('not implemented');
|
throw Error('not implemented');
|
||||||
}
|
}
|
||||||
|
@ -215,6 +219,10 @@ export class AbstractWallet {
|
||||||
return new Promise(resolve => resolve(this.getAddress()));
|
return new Promise(resolve => resolve(this.getAddress()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getChangeAddressAsync() {
|
||||||
|
return new Promise(resolve => resolve(this.getAddress()));
|
||||||
|
}
|
||||||
|
|
||||||
useWithHardwareWalletEnabled() {
|
useWithHardwareWalletEnabled() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -260,4 +268,6 @@ export class AbstractWallet {
|
||||||
|
|
||||||
return b58.encode(data);
|
return b58.encode(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prepareForSerialization() {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,6 +71,7 @@ export class HDLegacyBreadwalletWallet extends HDLegacyP2PKHWallet {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_getWIFByIndex(internal, index) {
|
_getWIFByIndex(internal, index) {
|
||||||
|
if (!this.secret) return false;
|
||||||
const mnemonic = this.secret;
|
const mnemonic = this.secret;
|
||||||
const seed = bip39.mnemonicToSeed(mnemonic);
|
const seed = bip39.mnemonicToSeed(mnemonic);
|
||||||
const root = bitcoinjs.bip32.fromSeed(seed);
|
const root = bitcoinjs.bip32.fromSeed(seed);
|
||||||
|
|
|
@ -61,6 +61,7 @@ export class HDLegacyElectrumSeedP2PKHWallet extends HDLegacyP2PKHWallet {
|
||||||
}
|
}
|
||||||
|
|
||||||
_getWIFByIndex(internal, index) {
|
_getWIFByIndex(internal, index) {
|
||||||
|
if (!this.secret) return false;
|
||||||
const root = bitcoin.bip32.fromSeed(mn.mnemonicToSeedSync(this.secret, MNEMONIC_TO_SEED_OPTS));
|
const root = bitcoin.bip32.fromSeed(mn.mnemonicToSeedSync(this.secret, MNEMONIC_TO_SEED_OPTS));
|
||||||
const path = `m/${internal ? 1 : 0}/${index}`;
|
const path = `m/${internal ? 1 : 0}/${index}`;
|
||||||
const child = root.derivePath(path);
|
const child = root.derivePath(path);
|
||||||
|
|
|
@ -48,6 +48,7 @@ export class HDLegacyP2PKHWallet extends AbstractHDElectrumWallet {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_getWIFByIndex(internal, index) {
|
_getWIFByIndex(internal, index) {
|
||||||
|
if (!this.secret) return false;
|
||||||
const mnemonic = this.secret;
|
const mnemonic = this.secret;
|
||||||
const seed = bip39.mnemonicToSeed(mnemonic);
|
const seed = bip39.mnemonicToSeed(mnemonic);
|
||||||
|
|
||||||
|
|
|
@ -28,4 +28,8 @@ export class HDSegwitBech32Wallet extends AbstractHDElectrumWallet {
|
||||||
allowRBF() {
|
allowRBF() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
allowPayJoin() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,7 @@ export class HDSegwitElectrumSeedP2WPKHWallet extends HDSegwitBech32Wallet {
|
||||||
}
|
}
|
||||||
|
|
||||||
_getWIFByIndex(internal, index) {
|
_getWIFByIndex(internal, index) {
|
||||||
|
if (!this.secret) return false;
|
||||||
const root = bitcoin.bip32.fromSeed(mn.mnemonicToSeedSync(this.secret, MNEMONIC_TO_SEED_OPTS));
|
const root = bitcoin.bip32.fromSeed(mn.mnemonicToSeedSync(this.secret, MNEMONIC_TO_SEED_OPTS));
|
||||||
const path = `m/0'/${internal ? 1 : 0}/${index}`;
|
const path = `m/0'/${internal ? 1 : 0}/${index}`;
|
||||||
const child = root.derivePath(path);
|
const child = root.derivePath(path);
|
||||||
|
|
|
@ -29,6 +29,7 @@ export class HDSegwitP2SHWallet extends AbstractHDElectrumWallet {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_getWIFByIndex(internal, index) {
|
_getWIFByIndex(internal, index) {
|
||||||
|
if (!this.secret) return false;
|
||||||
const mnemonic = this.secret;
|
const mnemonic = this.secret;
|
||||||
const seed = bip39.mnemonicToSeed(mnemonic);
|
const seed = bip39.mnemonicToSeed(mnemonic);
|
||||||
const root = bitcoin.bip32.fromSeed(seed);
|
const root = bitcoin.bip32.fromSeed(seed);
|
||||||
|
|
|
@ -401,10 +401,4 @@ export class LegacyWallet extends AbstractWallet {
|
||||||
allowSendMax() {
|
allowSendMax() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getChangeAddressAsync() {
|
|
||||||
return new Promise(resolve => {
|
|
||||||
resolve(this.getAddress());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
897
class/wallets/multisig-hd-wallet.js
Normal file
897
class/wallets/multisig-hd-wallet.js
Normal file
|
@ -0,0 +1,897 @@
|
||||||
|
import { AbstractHDElectrumWallet } from './abstract-hd-electrum-wallet';
|
||||||
|
import bip39 from 'bip39';
|
||||||
|
import b58 from 'bs58check';
|
||||||
|
import { decodeUR } from 'bc-ur';
|
||||||
|
const BlueElectrum = require('../../blue_modules/BlueElectrum');
|
||||||
|
const coinSelectAccumulative = require('coinselect/accumulative');
|
||||||
|
const coinSelectSplit = require('coinselect/split');
|
||||||
|
const HDNode = require('bip32');
|
||||||
|
const bitcoin = require('bitcoinjs-lib');
|
||||||
|
const createHash = require('create-hash');
|
||||||
|
const reverse = require('buffer-reverse');
|
||||||
|
|
||||||
|
export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
||||||
|
static type = 'HDmultisig';
|
||||||
|
static typeReadable = 'Multisig Vault';
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this._m = 0; // minimum required signatures so spend (m out of n)
|
||||||
|
this._cosigners = []; // array of xpubs or mnemonic seeds
|
||||||
|
this._cosignersFingerprints = []; // array of according fingerprints (if any provided)
|
||||||
|
this._cosignersCustomPaths = []; // array of according paths (if any provided)
|
||||||
|
this._derivationPath = '';
|
||||||
|
this._isNativeSegwit = false;
|
||||||
|
this._isWrappedSegwit = false;
|
||||||
|
this._isLegacy = false;
|
||||||
|
this.gap_limit = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
isLegacy() {
|
||||||
|
return this._isLegacy;
|
||||||
|
}
|
||||||
|
|
||||||
|
isNativeSegwit() {
|
||||||
|
return this._isNativeSegwit;
|
||||||
|
}
|
||||||
|
|
||||||
|
isWrappedSegwit() {
|
||||||
|
return this._isWrappedSegwit;
|
||||||
|
}
|
||||||
|
|
||||||
|
setWrappedSegwit() {
|
||||||
|
this._isWrappedSegwit = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
setNativeSegwit() {
|
||||||
|
this._isNativeSegwit = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLegacy() {
|
||||||
|
this._isLegacy = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
setM(m) {
|
||||||
|
this._m = m;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {number} How many minumim signatures required to authorize a spend
|
||||||
|
*/
|
||||||
|
getM() {
|
||||||
|
return this._m;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {number} Total count of cosigners
|
||||||
|
*/
|
||||||
|
getN() {
|
||||||
|
return this._cosigners.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
setDerivationPath(path) {
|
||||||
|
this._derivationPath = path;
|
||||||
|
switch (this._derivationPath) {
|
||||||
|
case "m/48'/0'/0'/2'":
|
||||||
|
this._isNativeSegwit = true;
|
||||||
|
break;
|
||||||
|
case "m/48'/0'/0'/1'":
|
||||||
|
this._isWrappedSegwit = true;
|
||||||
|
break;
|
||||||
|
case "m/45'":
|
||||||
|
this._isLegacy = true;
|
||||||
|
break;
|
||||||
|
case "m/44'":
|
||||||
|
this._isLegacy = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getDerivationPath() {
|
||||||
|
return this._derivationPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
getCustomDerivationPathForCosigner(index) {
|
||||||
|
if (index === 0) throw new Error('cosigners indexation starts from 1');
|
||||||
|
return this._cosignersCustomPaths[index - 1] || this.getDerivationPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
getCosigner(index) {
|
||||||
|
if (index === 0) throw new Error('cosigners indexation starts from 1');
|
||||||
|
return this._cosigners[index - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFingerprint(index) {
|
||||||
|
if (index === 0) throw new Error('cosigners fingerprints indexation starts from 1');
|
||||||
|
return this._cosignersFingerprints[index - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
getCosignerForFingerprint(fp) {
|
||||||
|
const index = this._cosignersFingerprints.indexOf(fp);
|
||||||
|
return this._cosigners[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
static isXpubValid(key) {
|
||||||
|
let xpub;
|
||||||
|
|
||||||
|
try {
|
||||||
|
xpub = super._zpubToXpub(key);
|
||||||
|
HDNode.fromBase58(xpub);
|
||||||
|
return true;
|
||||||
|
} catch (_) {}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param key {string} Either xpub or mnemonic phrase
|
||||||
|
* @param fingerprint {string} Fingerprint for cosigner that is added as xpub
|
||||||
|
* @param path {string} Custom path (if any) for cosigner that is added as mnemonics
|
||||||
|
*/
|
||||||
|
addCosigner(key, fingerprint, path) {
|
||||||
|
if (MultisigHDWallet.isXpubString(key) && !fingerprint) {
|
||||||
|
throw new Error('fingerprint is required when adding cosigner as xpub (watch-only)');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path && !this.constructor.isPathValid(path)) {
|
||||||
|
throw new Error('path is not valid');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!MultisigHDWallet.isXpubString(key)) {
|
||||||
|
// mnemonics. lets derive fingerprint
|
||||||
|
if (!bip39.validateMnemonic(key)) throw new Error('Not a valid mnemonic phrase');
|
||||||
|
fingerprint = MultisigHDWallet.seedToFingerprint(key);
|
||||||
|
} else {
|
||||||
|
if (!MultisigHDWallet.isXpubValid(key)) throw new Error('Not a valid xpub: ' + key);
|
||||||
|
}
|
||||||
|
|
||||||
|
const index = this._cosigners.length;
|
||||||
|
this._cosigners[index] = key;
|
||||||
|
if (fingerprint) this._cosignersFingerprints[index] = fingerprint.toUpperCase();
|
||||||
|
if (path) this._cosignersCustomPaths[index] = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stored cosigner can be EITHER xpub (or Zpub or smth), OR mnemonic phrase. This method converts it to xpub
|
||||||
|
*
|
||||||
|
* @param cosigner {string} Zpub (or similar) or mnemonic seed
|
||||||
|
* @returns {string} xpub
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_getXpubFromCosigner(cosigner) {
|
||||||
|
let xpub = cosigner;
|
||||||
|
if (!MultisigHDWallet.isXpubString(cosigner)) {
|
||||||
|
const index = this._cosigners.indexOf(cosigner);
|
||||||
|
xpub = MultisigHDWallet.seedToXpub(cosigner, this._cosignersCustomPaths[index] || this._derivationPath);
|
||||||
|
}
|
||||||
|
return this.constructor._zpubToXpub(xpub);
|
||||||
|
}
|
||||||
|
|
||||||
|
_getExternalAddressByIndex(index) {
|
||||||
|
if (!this._m) throw new Error('m is not set');
|
||||||
|
index = +index;
|
||||||
|
if (this.external_addresses_cache[index]) return this.external_addresses_cache[index]; // cache hit
|
||||||
|
|
||||||
|
const address = this._getAddressFromNode(0, index);
|
||||||
|
this.external_addresses_cache[index] = address;
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
_getAddressFromNode(nodeIndex, index) {
|
||||||
|
const pubkeys = [];
|
||||||
|
let cosignerIndex = 0;
|
||||||
|
for (const cosigner of this._cosigners) {
|
||||||
|
this._nodes = this._nodes || [];
|
||||||
|
this._nodes[nodeIndex] = this._nodes[nodeIndex] || [];
|
||||||
|
let _node;
|
||||||
|
|
||||||
|
if (!this._nodes[nodeIndex][cosignerIndex]) {
|
||||||
|
const xpub = this._getXpubFromCosigner(cosigner);
|
||||||
|
const hdNode = HDNode.fromBase58(xpub);
|
||||||
|
_node = hdNode.derive(nodeIndex);
|
||||||
|
} else {
|
||||||
|
_node = this._nodes[nodeIndex][cosignerIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
pubkeys.push(_node.derive(index).publicKey);
|
||||||
|
cosignerIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.isWrappedSegwit()) {
|
||||||
|
const { address } = bitcoin.payments.p2sh({
|
||||||
|
redeem: bitcoin.payments.p2wsh({
|
||||||
|
redeem: bitcoin.payments.p2ms({ m: this._m, pubkeys: MultisigHDWallet.sortBuffers(pubkeys) }),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
return address;
|
||||||
|
} else if (this.isNativeSegwit()) {
|
||||||
|
const { address } = bitcoin.payments.p2wsh({
|
||||||
|
redeem: bitcoin.payments.p2ms({ m: this._m, pubkeys: MultisigHDWallet.sortBuffers(pubkeys) }),
|
||||||
|
});
|
||||||
|
|
||||||
|
return address;
|
||||||
|
} else if (this.isLegacy()) {
|
||||||
|
const { address } = bitcoin.payments.p2sh({
|
||||||
|
redeem: bitcoin.payments.p2ms({ m: this._m, pubkeys: MultisigHDWallet.sortBuffers(pubkeys) }),
|
||||||
|
});
|
||||||
|
|
||||||
|
return address;
|
||||||
|
} else {
|
||||||
|
throw new Error('Dont know how to make address');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_getInternalAddressByIndex(index) {
|
||||||
|
if (!this._m) throw new Error('m is not set');
|
||||||
|
index = +index;
|
||||||
|
if (this.internal_addresses_cache[index]) return this.internal_addresses_cache[index]; // cache hit
|
||||||
|
|
||||||
|
const address = this._getAddressFromNode(1, index);
|
||||||
|
this.internal_addresses_cache[index] = address;
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
static seedToXpub(mnemonic, path) {
|
||||||
|
const seed = bip39.mnemonicToSeed(mnemonic);
|
||||||
|
const root = bitcoin.bip32.fromSeed(seed);
|
||||||
|
|
||||||
|
const child = root.derivePath(path).neutered();
|
||||||
|
this._xpub = child.toBase58();
|
||||||
|
|
||||||
|
return this._xpub;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mnemonic {string} Mnemonic seed phrase
|
||||||
|
* @returns {string} Hex string of fingerprint derived from mnemonics. Always has lenght of 8 chars and correct leading zeroes
|
||||||
|
*/
|
||||||
|
static seedToFingerprint(mnemonic) {
|
||||||
|
const seed = bip39.mnemonicToSeed(mnemonic);
|
||||||
|
const root = bitcoin.bip32.fromSeed(seed);
|
||||||
|
let hex = root.fingerprint.toString('hex');
|
||||||
|
while (hex.length < 8) hex = '0' + hex; // leading zeroes
|
||||||
|
return hex.toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns xpub with correct prefix accodting to this objects set derivation path, for example 'Zpub' (with
|
||||||
|
* capital Z) for bech32 multisig
|
||||||
|
* @see https://github.com/satoshilabs/slips/blob/master/slip-0132.md
|
||||||
|
*
|
||||||
|
* @param xpub {string} Any kind of xpub, including zpub etc since we are only swapping the prefix bytes
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
convertXpubToMultisignatureXpub(xpub) {
|
||||||
|
let data = b58.decode(xpub);
|
||||||
|
data = data.slice(4);
|
||||||
|
if (this.isNativeSegwit()) {
|
||||||
|
return b58.encode(Buffer.concat([Buffer.from('02aa7ed3', 'hex'), data]));
|
||||||
|
} else if (this.isWrappedSegwit()) {
|
||||||
|
return b58.encode(Buffer.concat([Buffer.from('0295b43f', 'hex'), data]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return xpub;
|
||||||
|
}
|
||||||
|
|
||||||
|
static isXpubString(xpub) {
|
||||||
|
return ['xpub', 'ypub', 'zpub', 'Ypub', 'Zpub'].includes(xpub.substring(0, 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts fingerprint that is stored as a deciman number to hex string (all caps)
|
||||||
|
*
|
||||||
|
* @param xfp {number} For example 64392470
|
||||||
|
* @returns {string} For example 168DD603
|
||||||
|
*/
|
||||||
|
static ckccXfp2fingerprint(xfp) {
|
||||||
|
let masterFingerprintHex = Number(xfp).toString(16);
|
||||||
|
while (masterFingerprintHex.length < 8) masterFingerprintHex = '0' + masterFingerprintHex; // conversion without explicit zero might result in lost byte
|
||||||
|
|
||||||
|
// poor man's little-endian conversion:
|
||||||
|
// ¯\_(ツ)_/¯
|
||||||
|
return (
|
||||||
|
masterFingerprintHex[6] +
|
||||||
|
masterFingerprintHex[7] +
|
||||||
|
masterFingerprintHex[4] +
|
||||||
|
masterFingerprintHex[5] +
|
||||||
|
masterFingerprintHex[2] +
|
||||||
|
masterFingerprintHex[3] +
|
||||||
|
masterFingerprintHex[0] +
|
||||||
|
masterFingerprintHex[1]
|
||||||
|
).toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
getXpub() {
|
||||||
|
return this.getSecret(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
getSecret(coordinationSetup = false) {
|
||||||
|
let ret = '# BlueWallet Multisig setup file\n';
|
||||||
|
if (coordinationSetup) ret += '# this file contains only public keys and is safe to\n# distribute among cosigners\n';
|
||||||
|
if (!coordinationSetup) ret += '# this file may contain private information\n';
|
||||||
|
ret += '#\n';
|
||||||
|
ret += 'Name: ' + this.getLabel() + '\n';
|
||||||
|
ret += 'Policy: ' + this.getM() + ' of ' + this.getN() + '\n';
|
||||||
|
|
||||||
|
let hasCustomPaths = 0;
|
||||||
|
for (let index = 0; index < this.getN(); index++) {
|
||||||
|
if (this._cosignersCustomPaths[index]) hasCustomPaths++;
|
||||||
|
}
|
||||||
|
|
||||||
|
let printedGlobalDerivation = false;
|
||||||
|
if (hasCustomPaths !== this.getN()) {
|
||||||
|
printedGlobalDerivation = true;
|
||||||
|
ret += 'Derivation: ' + this.getDerivationPath() + '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.isNativeSegwit()) {
|
||||||
|
ret += 'Format: P2WSH\n';
|
||||||
|
} else if (this.isWrappedSegwit()) {
|
||||||
|
ret += 'Format: P2WSH-P2SH\n';
|
||||||
|
} else if (this.isLegacy()) {
|
||||||
|
ret += 'Format: P2SH\n';
|
||||||
|
} else {
|
||||||
|
ret += 'Format: unknown\n';
|
||||||
|
}
|
||||||
|
ret += '\n';
|
||||||
|
|
||||||
|
for (let index = 0; index < this.getN(); index++) {
|
||||||
|
if (
|
||||||
|
this._cosignersCustomPaths[index] &&
|
||||||
|
((printedGlobalDerivation && this._cosignersCustomPaths[index] !== this.getDerivationPath()) || !printedGlobalDerivation)
|
||||||
|
) {
|
||||||
|
ret += '# derivation: ' + this._cosignersCustomPaths[index] + '\n';
|
||||||
|
// if we printed global derivation and this cosigned _has_ derivation and its different from global - we print it ;
|
||||||
|
// or we print it if cosigner _has_ some derivation set and we did not print global
|
||||||
|
}
|
||||||
|
if (this.constructor.isXpubString(this._cosigners[index])) {
|
||||||
|
ret += this._cosignersFingerprints[index] + ': ' + this._cosigners[index] + '\n';
|
||||||
|
} else {
|
||||||
|
if (coordinationSetup) {
|
||||||
|
const xpub = this.convertXpubToMultisignatureXpub(
|
||||||
|
MultisigHDWallet.seedToXpub(this._cosigners[index], this._cosignersCustomPaths[index] || this._derivationPath),
|
||||||
|
);
|
||||||
|
const fingerprint = MultisigHDWallet.seedToFingerprint(this._cosigners[index]);
|
||||||
|
ret += fingerprint + ': ' + xpub + '\n';
|
||||||
|
} else {
|
||||||
|
ret += 'seed: ' + this._cosigners[index] + '\n';
|
||||||
|
ret += '# warning! sensitive information, do not disclose ^^^ \n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret += '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
setSecret(secret) {
|
||||||
|
if (secret.toUpperCase().startsWith('UR:BYTES')) {
|
||||||
|
const decoded = decodeUR([secret]);
|
||||||
|
const b = Buffer.from(decoded, 'hex');
|
||||||
|
secret = b.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// is it Coldcard json file?
|
||||||
|
let json;
|
||||||
|
try {
|
||||||
|
json = JSON.parse(secret);
|
||||||
|
} catch (_) {}
|
||||||
|
if (json && json.xfp && json.p2wsh_deriv && json.p2wsh) {
|
||||||
|
this.addCosigner(json.p2wsh, json.xfp); // technically we dont need deriv (json.p2wsh_deriv), since cosigner is already an xpub
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// is it electrum json?
|
||||||
|
if (json && json.wallet_type) {
|
||||||
|
const mofn = json.wallet_type.split('of');
|
||||||
|
this.setM(parseInt(mofn[0].trim()));
|
||||||
|
const n = parseInt(mofn[1].trim());
|
||||||
|
for (let c = 1; c <= n; c++) {
|
||||||
|
const cosignerData = json['x' + c + '/'];
|
||||||
|
if (cosignerData) {
|
||||||
|
const fingerprint =
|
||||||
|
(cosignerData.ckcc_xfp
|
||||||
|
? MultisigHDWallet.ckccXfp2fingerprint(cosignerData.ckcc_xfp)
|
||||||
|
: cosignerData.root_fingerprint?.toUpperCase()) || '00000000';
|
||||||
|
if (cosignerData.seed) {
|
||||||
|
// TODO: support electrum's bip32
|
||||||
|
}
|
||||||
|
this.addCosigner(cosignerData.xpub, fingerprint, cosignerData.derivation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.getCosigner(1).startsWith('Zpub')) this.setNativeSegwit();
|
||||||
|
if (this.getCosigner(1).startsWith('Ypub')) this.setWrappedSegwit();
|
||||||
|
if (this.getCosigner(1).startsWith('xpub')) this.setLegacy();
|
||||||
|
}
|
||||||
|
|
||||||
|
// coldcard & cobo txt format:
|
||||||
|
let customPathForCurrentCosigner = false;
|
||||||
|
for (const line of secret.split('\n')) {
|
||||||
|
const [key, value] = line.split(':');
|
||||||
|
|
||||||
|
switch (key) {
|
||||||
|
case 'Name':
|
||||||
|
this.setLabel(value.trim());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'Policy':
|
||||||
|
this.setM(parseInt(value.trim().split('of')[0].trim()));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'Derivation':
|
||||||
|
this.setDerivationPath(value.trim());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'Format':
|
||||||
|
switch (value.trim()) {
|
||||||
|
case 'P2WSH':
|
||||||
|
this.setNativeSegwit();
|
||||||
|
break;
|
||||||
|
case 'P2WSH-P2SH':
|
||||||
|
this.setWrappedSegwit();
|
||||||
|
break;
|
||||||
|
case 'P2SH':
|
||||||
|
this.setLegacy();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (key && value && MultisigHDWallet.isXpubString(value.trim())) {
|
||||||
|
this.addCosigner(value.trim(), key, customPathForCurrentCosigner);
|
||||||
|
} else if (key.replace('#', '').trim() === 'derivation') {
|
||||||
|
customPathForCurrentCosigner = value.trim();
|
||||||
|
} else if (key === 'seed') {
|
||||||
|
this.addCosigner(value.trim(), false, customPathForCurrentCosigner);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// is it wallet descriptor?
|
||||||
|
// @see https://github.com/bitcoin/bitcoin/blob/master/doc/descriptors.md
|
||||||
|
// @see https://github.com/Fonta1n3/FullyNoded/blob/master/Docs/Wallets/Wallet-Export-Spec.md
|
||||||
|
if (secret.indexOf('sortedmulti(') !== -1 && json.descriptor) {
|
||||||
|
if (json.label) this.setLabel(json.label);
|
||||||
|
if (json.descriptor.startsWith('wsh(')) {
|
||||||
|
this.setNativeSegwit();
|
||||||
|
}
|
||||||
|
if (json.descriptor.startsWith('sh(')) {
|
||||||
|
this.setLegacy();
|
||||||
|
}
|
||||||
|
if (json.descriptor.startsWith('sh(wsh(')) {
|
||||||
|
this.setLegacy();
|
||||||
|
}
|
||||||
|
|
||||||
|
const s2 = json.descriptor.substr(json.descriptor.indexOf('sortedmulti(') + 12);
|
||||||
|
const s3 = s2.split(',');
|
||||||
|
const m = parseInt(s3[0]);
|
||||||
|
if (m) this.setM(m);
|
||||||
|
|
||||||
|
for (let c = 1; c < s3.length; c++) {
|
||||||
|
const re = /\[([^\]]+)\](.*)/;
|
||||||
|
const m = s3[c].match(re);
|
||||||
|
if (m && m.length === 3) {
|
||||||
|
let hexFingerprint = m[1].split('/')[0];
|
||||||
|
if (hexFingerprint.length === 8) {
|
||||||
|
hexFingerprint = Buffer.from(hexFingerprint, 'hex').reverse().toString('hex');
|
||||||
|
}
|
||||||
|
|
||||||
|
const path = 'm/' + m[1].split('/').slice(1).join('/').replace(/[h]/g, "'");
|
||||||
|
let xpub = m[2];
|
||||||
|
if (xpub.indexOf('/') !== -1) {
|
||||||
|
xpub = xpub.substr(0, xpub.indexOf('/'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// console.warn('m[2] = ', m[2], {hexFingerprint, path, xpub});
|
||||||
|
this.addCosigner(xpub, hexFingerprint.toUpperCase(), path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// is it caravan?
|
||||||
|
if (json && json.network === 'mainnet' && json.quorum) {
|
||||||
|
this.setM(+json.quorum.requiredSigners);
|
||||||
|
if (json.name) this.setLabel(json.name);
|
||||||
|
|
||||||
|
switch (json.addressType.toLowerCase()) {
|
||||||
|
case 'P2SH':
|
||||||
|
this.setLegacy();
|
||||||
|
break;
|
||||||
|
case 'P2SH-P2WSH':
|
||||||
|
this.setWrappedSegwit();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
case 'P2WSH':
|
||||||
|
this.setNativeSegwit();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const pk of json.extendedPublicKeys) {
|
||||||
|
const path = this.constructor.isPathValid(json.bip32Path) ? json.bip32Path : "m/1'";
|
||||||
|
// wtf, where caravan stores fingerprints..?
|
||||||
|
this.addCosigner(pk.xpub, '00000000', path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.getLabel()) this.setLabel('Multisig vault');
|
||||||
|
}
|
||||||
|
|
||||||
|
_getDerivationPathByAddressWithCustomPath(address, customPathPrefix) {
|
||||||
|
const path = customPathPrefix || this._derivationPath;
|
||||||
|
for (let c = 0; c < this.next_free_address_index + this.gap_limit; c++) {
|
||||||
|
if (this._getExternalAddressByIndex(c) === address) return path + '/0/' + c;
|
||||||
|
}
|
||||||
|
for (let c = 0; c < this.next_free_change_address_index + this.gap_limit; c++) {
|
||||||
|
if (this._getInternalAddressByIndex(c) === address) return path + '/1/' + c;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_getWifForAddress(address) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_getPubkeyByAddress(address) {
|
||||||
|
throw new Error('Not applicable in multisig');
|
||||||
|
}
|
||||||
|
|
||||||
|
_getDerivationPathByAddress(address) {
|
||||||
|
throw new Error('Not applicable in multisig');
|
||||||
|
}
|
||||||
|
|
||||||
|
_addPsbtInput(psbt, input, sequence, masterFingerprintBuffer) {
|
||||||
|
const bip32Derivation = []; // array per each pubkey thats gona be used
|
||||||
|
const pubkeys = [];
|
||||||
|
for (let c = 0; c < this._cosigners.length; c++) {
|
||||||
|
const cosigner = this._cosigners[c];
|
||||||
|
const path = this._getDerivationPathByAddressWithCustomPath(input.address, this._cosignersCustomPaths[c] || this._derivationPath);
|
||||||
|
// ^^ path resembles _custom path_, if provided by user during setup, otherwise default path for wallet type gona be used
|
||||||
|
const masterFingerprint = Buffer.from(this._cosignersFingerprints[c], 'hex');
|
||||||
|
|
||||||
|
const xpub = this._getXpubFromCosigner(cosigner);
|
||||||
|
const hdNode0 = HDNode.fromBase58(xpub);
|
||||||
|
const splt = path.split('/');
|
||||||
|
const internal = +splt[splt.length - 2];
|
||||||
|
const index = +splt[splt.length - 1];
|
||||||
|
const _node0 = hdNode0.derive(internal);
|
||||||
|
const pubkey = _node0.derive(index).publicKey;
|
||||||
|
pubkeys.push(pubkey);
|
||||||
|
|
||||||
|
bip32Derivation.push({
|
||||||
|
masterFingerprint,
|
||||||
|
path,
|
||||||
|
pubkey,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.isNativeSegwit()) {
|
||||||
|
const p2wsh = bitcoin.payments.p2wsh({
|
||||||
|
redeem: bitcoin.payments.p2ms({ m: this._m, pubkeys: MultisigHDWallet.sortBuffers(pubkeys) }),
|
||||||
|
});
|
||||||
|
const witnessScript = p2wsh.redeem.output;
|
||||||
|
|
||||||
|
psbt.addInput({
|
||||||
|
hash: input.txId,
|
||||||
|
index: input.vout,
|
||||||
|
sequence,
|
||||||
|
bip32Derivation,
|
||||||
|
witnessUtxo: {
|
||||||
|
script: p2wsh.output,
|
||||||
|
value: input.value,
|
||||||
|
},
|
||||||
|
witnessScript,
|
||||||
|
// hw wallets now require passing the whole previous tx as Buffer, as if it was non-segwit input, to mitigate
|
||||||
|
// some hw wallets attack vector
|
||||||
|
nonWitnessUtxo: Buffer.from(input.txhex, 'hex'),
|
||||||
|
});
|
||||||
|
} else if (this.isWrappedSegwit()) {
|
||||||
|
const p2shP2wsh = bitcoin.payments.p2sh({
|
||||||
|
redeem: bitcoin.payments.p2wsh({
|
||||||
|
redeem: bitcoin.payments.p2ms({ m: this._m, pubkeys: MultisigHDWallet.sortBuffers(pubkeys) }),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
const witnessScript = p2shP2wsh.redeem.redeem.output;
|
||||||
|
const redeemScript = p2shP2wsh.redeem.output;
|
||||||
|
|
||||||
|
psbt.addInput({
|
||||||
|
hash: input.txId,
|
||||||
|
index: input.vout,
|
||||||
|
sequence,
|
||||||
|
bip32Derivation,
|
||||||
|
witnessUtxo: {
|
||||||
|
script: p2shP2wsh.output,
|
||||||
|
value: input.value,
|
||||||
|
},
|
||||||
|
witnessScript,
|
||||||
|
redeemScript,
|
||||||
|
// hw wallets now require passing the whole previous tx as Buffer, as if it was non-segwit input, to mitigate
|
||||||
|
// some hw wallets attack vector
|
||||||
|
nonWitnessUtxo: Buffer.from(input.txhex, 'hex'),
|
||||||
|
});
|
||||||
|
} else if (this.isLegacy()) {
|
||||||
|
const p2sh = bitcoin.payments.p2sh({
|
||||||
|
redeem: bitcoin.payments.p2ms({ m: this._m, pubkeys: MultisigHDWallet.sortBuffers(pubkeys) }),
|
||||||
|
});
|
||||||
|
const redeemScript = p2sh.redeem.output;
|
||||||
|
psbt.addInput({
|
||||||
|
hash: input.txId,
|
||||||
|
index: input.vout,
|
||||||
|
sequence,
|
||||||
|
bip32Derivation,
|
||||||
|
redeemScript,
|
||||||
|
nonWitnessUtxo: Buffer.from(input.txhex, 'hex'),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw new Error('Dont know how to add input');
|
||||||
|
}
|
||||||
|
|
||||||
|
return psbt;
|
||||||
|
}
|
||||||
|
|
||||||
|
_getOutputDataForChange(outputData) {
|
||||||
|
const bip32Derivation = []; // array per each pubkey thats gona be used
|
||||||
|
const pubkeys = [];
|
||||||
|
for (let c = 0; c < this._cosigners.length; c++) {
|
||||||
|
const cosigner = this._cosigners[c];
|
||||||
|
const path = this._getDerivationPathByAddressWithCustomPath(
|
||||||
|
outputData.address,
|
||||||
|
this._cosignersCustomPaths[c] || this._derivationPath,
|
||||||
|
);
|
||||||
|
// ^^ path resembles _custom path_, if provided by user during setup, otherwise default path for wallet type gona be used
|
||||||
|
const masterFingerprint = Buffer.from(this._cosignersFingerprints[c], 'hex');
|
||||||
|
|
||||||
|
const xpub = this._getXpubFromCosigner(cosigner);
|
||||||
|
const hdNode0 = HDNode.fromBase58(xpub);
|
||||||
|
const splt = path.split('/');
|
||||||
|
const internal = +splt[splt.length - 2];
|
||||||
|
const index = +splt[splt.length - 1];
|
||||||
|
const _node0 = hdNode0.derive(internal);
|
||||||
|
const pubkey = _node0.derive(index).publicKey;
|
||||||
|
pubkeys.push(pubkey);
|
||||||
|
|
||||||
|
bip32Derivation.push({
|
||||||
|
masterFingerprint,
|
||||||
|
path,
|
||||||
|
pubkey,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
outputData.bip32Derivation = bip32Derivation;
|
||||||
|
|
||||||
|
if (this.isLegacy()) {
|
||||||
|
const p2sh = bitcoin.payments.p2ms({ m: this._m, pubkeys: MultisigHDWallet.sortBuffers(pubkeys) });
|
||||||
|
outputData.redeemScript = p2sh.output;
|
||||||
|
} else if (this.isWrappedSegwit()) {
|
||||||
|
const p2shP2wsh = bitcoin.payments.p2sh({
|
||||||
|
redeem: bitcoin.payments.p2wsh({
|
||||||
|
redeem: bitcoin.payments.p2ms({ m: this._m, pubkeys: MultisigHDWallet.sortBuffers(pubkeys) }),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
outputData.witnessScript = p2shP2wsh.redeem.redeem.output;
|
||||||
|
outputData.redeemScript = p2shP2wsh.redeem.output;
|
||||||
|
} else if (this.isNativeSegwit()) {
|
||||||
|
// not needed by coldcard, apparently..?
|
||||||
|
const p2wsh = bitcoin.payments.p2wsh({
|
||||||
|
redeem: bitcoin.payments.p2ms({ m: this._m, pubkeys: MultisigHDWallet.sortBuffers(pubkeys) }),
|
||||||
|
});
|
||||||
|
outputData.witnessScript = p2wsh.redeem.output;
|
||||||
|
} else {
|
||||||
|
throw new Error('dont know how to add change output');
|
||||||
|
}
|
||||||
|
|
||||||
|
return outputData;
|
||||||
|
}
|
||||||
|
|
||||||
|
howManySignaturesCanWeMake() {
|
||||||
|
let howManyPrivKeysWeGot = 0;
|
||||||
|
for (const cosigner of this._cosigners) {
|
||||||
|
if (!MultisigHDWallet.isXpubString(cosigner)) howManyPrivKeysWeGot++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return howManyPrivKeysWeGot;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
createTransaction(utxos, targets, feeRate, changeAddress, sequence, skipSigning = false, masterFingerprint) {
|
||||||
|
if (targets.length === 0) throw new Error('No destination provided');
|
||||||
|
if (this.howManySignaturesCanWeMake() === 0) skipSigning = true;
|
||||||
|
|
||||||
|
if (!changeAddress) throw new Error('No change address provided');
|
||||||
|
sequence = sequence || AbstractHDElectrumWallet.defaultRBFSequence;
|
||||||
|
|
||||||
|
let algo = coinSelectAccumulative;
|
||||||
|
if (targets.length === 1 && targets[0] && !targets[0].value) {
|
||||||
|
// we want to send MAX
|
||||||
|
algo = coinSelectSplit;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { inputs, outputs, fee } = algo(utxos, targets, feeRate);
|
||||||
|
|
||||||
|
// .inputs and .outputs will be undefined if no solution was found
|
||||||
|
if (!inputs || !outputs) {
|
||||||
|
throw new Error('Not enough balance. Try sending smaller amount');
|
||||||
|
}
|
||||||
|
|
||||||
|
let psbt = new bitcoin.Psbt();
|
||||||
|
|
||||||
|
let c = 0;
|
||||||
|
inputs.forEach(input => {
|
||||||
|
c++;
|
||||||
|
psbt = this._addPsbtInput(psbt, input, sequence);
|
||||||
|
});
|
||||||
|
|
||||||
|
outputs.forEach(output => {
|
||||||
|
// if output has no address - this is change output
|
||||||
|
let change = false;
|
||||||
|
if (!output.address) {
|
||||||
|
change = true;
|
||||||
|
output.address = changeAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
let outputData = {
|
||||||
|
address: output.address,
|
||||||
|
value: output.value,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (change) {
|
||||||
|
outputData = this._getOutputDataForChange(outputData);
|
||||||
|
}
|
||||||
|
|
||||||
|
psbt.addOutput(outputData);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!skipSigning) {
|
||||||
|
for (let cc = 0; cc < c; cc++) {
|
||||||
|
for (const cosigner of this._cosigners) {
|
||||||
|
if (!MultisigHDWallet.isXpubString(cosigner)) {
|
||||||
|
// ok this is a mnemonic, lets try to sign
|
||||||
|
const seed = bip39.mnemonicToSeed(cosigner);
|
||||||
|
const hdRoot = bitcoin.bip32.fromSeed(seed);
|
||||||
|
psbt.signInputHD(cc, hdRoot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let tx;
|
||||||
|
if (!skipSigning && this.howManySignaturesCanWeMake() >= this.getM()) {
|
||||||
|
tx = psbt.finalizeAllInputs().extractTransaction();
|
||||||
|
}
|
||||||
|
return { tx, inputs, outputs, fee, psbt };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://github.com/bitcoin/bips/blob/master/bip-0067.mediawiki
|
||||||
|
*
|
||||||
|
* @param bufArr {Array.<Buffer>}
|
||||||
|
* @returns {Array.<Buffer>}
|
||||||
|
*/
|
||||||
|
static sortBuffers(bufArr) {
|
||||||
|
return bufArr.sort(Buffer.compare);
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareForSerialization() {
|
||||||
|
// deleting structures that cant be serialized
|
||||||
|
delete this._nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
static isPathValid(path) {
|
||||||
|
const root = bitcoin.bip32.fromSeed(Buffer.alloc(32));
|
||||||
|
try {
|
||||||
|
root.derivePath(path);
|
||||||
|
return true;
|
||||||
|
} catch (_) {}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
allowSend() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchUtxo() {
|
||||||
|
await super.fetchUtxo();
|
||||||
|
// now we need to fetch txhash for each input as required by PSBT
|
||||||
|
const txhexes = await BlueElectrum.multiGetTransactionByTxid(
|
||||||
|
this.getUtxo().map(x => x.txid),
|
||||||
|
50,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
const newUtxos = [];
|
||||||
|
for (const u of this.getUtxo()) {
|
||||||
|
if (txhexes[u.txid]) u.txhex = txhexes[u.txid];
|
||||||
|
newUtxos.push(u);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newUtxos;
|
||||||
|
}
|
||||||
|
|
||||||
|
getID() {
|
||||||
|
const string2hash = [...this._cosigners].sort().join(',') + ';' + [...this._cosignersFingerprints].sort().join(',');
|
||||||
|
return createHash('sha256').update(string2hash).digest().toString('hex');
|
||||||
|
}
|
||||||
|
|
||||||
|
calculateFeeFromPsbt(psbt) {
|
||||||
|
let goesIn = 0;
|
||||||
|
const cacheUtxoAmounts = {};
|
||||||
|
for (const inp of psbt.data.inputs) {
|
||||||
|
if (inp.witnessUtxo && inp.witnessUtxo.value) {
|
||||||
|
// segwit input
|
||||||
|
goesIn += inp.witnessUtxo.value;
|
||||||
|
} else if (inp.nonWitnessUtxo) {
|
||||||
|
// non-segwit input
|
||||||
|
// lets parse this transaction and cache how much each input was worth
|
||||||
|
const inputTx = bitcoin.Transaction.fromHex(inp.nonWitnessUtxo);
|
||||||
|
let index = 0;
|
||||||
|
for (const out of inputTx.outs) {
|
||||||
|
cacheUtxoAmounts[inputTx.getId() + ':' + index] = out.value;
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (goesIn === 0) {
|
||||||
|
// means we failed to get amounts that go in previously, so lets use utxo amounts cache we've build
|
||||||
|
// from non-segwit inputs
|
||||||
|
for (const inp of psbt.txInputs) {
|
||||||
|
const cacheKey = reverse(inp.hash).toString('hex') + ':' + inp.index;
|
||||||
|
if (cacheUtxoAmounts[cacheKey]) goesIn += cacheUtxoAmounts[cacheKey];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let goesOut = 0;
|
||||||
|
for (const output of psbt.txOutputs) {
|
||||||
|
goesOut += output.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return goesIn - goesOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
calculateHowManySignaturesWeHaveFromPsbt(psbt) {
|
||||||
|
let sigsHave = 0;
|
||||||
|
for (const inp of psbt.data.inputs) {
|
||||||
|
sigsHave = Math.max(sigsHave, inp.partialSig?.length || 0);
|
||||||
|
if (inp.finalScriptSig || inp.finalScriptWitness) sigsHave = this.getM(); // hacky, but it means we have enough
|
||||||
|
// He who knows that enough is enough will always have enough. Lao Tzu
|
||||||
|
}
|
||||||
|
|
||||||
|
return sigsHave;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to signs passed psbt object (by reference). If there are enough signatures - tries to finalize psbt
|
||||||
|
* and returns Transaction (ready to extract hex)
|
||||||
|
*
|
||||||
|
* @param psbt {Psbt}
|
||||||
|
* @returns {{ tx: Transaction }}
|
||||||
|
*/
|
||||||
|
cosignPsbt(psbt) {
|
||||||
|
for (let cc = 0; cc < psbt.inputCount; cc++) {
|
||||||
|
for (const cosigner of this._cosigners) {
|
||||||
|
if (!MultisigHDWallet.isXpubString(cosigner)) {
|
||||||
|
// ok this is a mnemonic, lets try to sign
|
||||||
|
const seed = bip39.mnemonicToSeed(cosigner);
|
||||||
|
const hdRoot = bitcoin.bip32.fromSeed(seed);
|
||||||
|
try {
|
||||||
|
psbt.signInputHD(cc, hdRoot);
|
||||||
|
} catch (_) {} // protects agains duplicate cosignings
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let tx = false;
|
||||||
|
if (this.calculateHowManySignaturesWeHaveFromPsbt(psbt) >= this.getM()) {
|
||||||
|
tx = psbt.finalizeAllInputs().extractTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
return { tx };
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,11 @@ export class PlaceholderWallet extends AbstractWallet {
|
||||||
this._isFailure = false;
|
this._isFailure = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setSecret(newSecret) {
|
||||||
|
// so TRY AGAIN when something goes wrong during import has more consistent prefilled text
|
||||||
|
this.secret = newSecret;
|
||||||
|
}
|
||||||
|
|
||||||
allowSend() {
|
allowSend() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,16 +120,26 @@ export class WatchOnlyWallet extends LegacyWallet {
|
||||||
throw new Error('Not initialized');
|
throw new Error('Not initialized');
|
||||||
}
|
}
|
||||||
|
|
||||||
async _getExternalAddressByIndex(index) {
|
_getExternalAddressByIndex(index) {
|
||||||
if (this._hdWalletInstance) return this._hdWalletInstance._getExternalAddressByIndex(index);
|
if (this._hdWalletInstance) return this._hdWalletInstance._getExternalAddressByIndex(index);
|
||||||
throw new Error('Not initialized');
|
throw new Error('Not initialized');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_getInternalAddressByIndex(index) {
|
||||||
|
if (this._hdWalletInstance) return this._hdWalletInstance._getInternalAddressByIndex(index);
|
||||||
|
throw new Error('Not initialized');
|
||||||
|
}
|
||||||
|
|
||||||
getNextFreeAddressIndex() {
|
getNextFreeAddressIndex() {
|
||||||
if (this._hdWalletInstance) return this._hdWalletInstance.next_free_address_index;
|
if (this._hdWalletInstance) return this._hdWalletInstance.next_free_address_index;
|
||||||
throw new Error('Not initialized');
|
throw new Error('Not initialized');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getNextFreeChangeAddressIndex() {
|
||||||
|
if (this._hdWalletInstance) return this._hdWalletInstance.next_free_change_address_index;
|
||||||
|
throw new Error('Not initialized');
|
||||||
|
}
|
||||||
|
|
||||||
async getChangeAddressAsync() {
|
async getChangeAddressAsync() {
|
||||||
if (this._hdWalletInstance) return this._hdWalletInstance.getChangeAddressAsync();
|
if (this._hdWalletInstance) return this._hdWalletInstance.getChangeAddressAsync();
|
||||||
throw new Error('Not initialized');
|
throw new Error('Not initialized');
|
||||||
|
|
180
components/DynamicQRCode.js
Normal file
180
components/DynamicQRCode.js
Normal file
|
@ -0,0 +1,180 @@
|
||||||
|
/* eslint react/prop-types: "off", react-native/no-inline-styles: "off" */
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { Text } from 'react-native-elements';
|
||||||
|
import { Dimensions, StyleSheet, TouchableOpacity, View } from 'react-native';
|
||||||
|
import { encodeUR } from 'bc-ur/dist';
|
||||||
|
import QRCode from 'react-native-qrcode-svg';
|
||||||
|
import { BlueCurrentTheme } from '../components/themes';
|
||||||
|
import { BlueSpacing20 } from '../BlueComponents';
|
||||||
|
import loc from '../loc';
|
||||||
|
|
||||||
|
const { height, width } = Dimensions.get('window');
|
||||||
|
|
||||||
|
export class DynamicQRCode extends Component {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
const qrCodeHeight = height > width ? width - 40 : width / 3;
|
||||||
|
const qrCodeMaxHeight = 370;
|
||||||
|
this.state = {
|
||||||
|
index: 0,
|
||||||
|
total: 0,
|
||||||
|
qrCodeHeight: Math.min(qrCodeHeight, qrCodeMaxHeight),
|
||||||
|
intervalHandler: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fragments = [];
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const { value, capacity = 800, hideControls = true } = this.props;
|
||||||
|
this.fragments = encodeUR(value, capacity);
|
||||||
|
this.setState(
|
||||||
|
{
|
||||||
|
total: this.fragments.length,
|
||||||
|
hideControls,
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
this.startAutoMove();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
moveToNextFragment = () => {
|
||||||
|
const { index, total } = this.state;
|
||||||
|
if (index === total - 1) {
|
||||||
|
this.setState({
|
||||||
|
index: 0,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.setState(state => ({
|
||||||
|
index: state.index + 1,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
startAutoMove = () => {
|
||||||
|
if (!this.state.intervalHandler)
|
||||||
|
this.setState(() => ({
|
||||||
|
intervalHandler: setInterval(this.moveToNextFragment, 500),
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
stopAutoMove = () => {
|
||||||
|
clearInterval(this.state.intervalHandler);
|
||||||
|
this.setState(() => ({
|
||||||
|
intervalHandler: null,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
moveToPreviousFragment = () => {
|
||||||
|
const { index, total } = this.state;
|
||||||
|
if (index > 0) {
|
||||||
|
this.setState(state => ({
|
||||||
|
index: state.index - 1,
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
this.setState(state => ({
|
||||||
|
index: total - 1,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const currentFragment = this.fragments[this.state.index];
|
||||||
|
|
||||||
|
if (!currentFragment) {
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
<Text>{loc.send.dynamic_init}</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={animatedQRCodeStyle.container}>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={animatedQRCodeStyle.qrcodeContainer}
|
||||||
|
onPress={() => {
|
||||||
|
this.setState(prevState => ({ hideControls: !prevState.hideControls }));
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<QRCode
|
||||||
|
value={currentFragment.toUpperCase()}
|
||||||
|
size={this.state.qrCodeHeight}
|
||||||
|
color="#000000"
|
||||||
|
logoBackgroundColor={BlueCurrentTheme.colors.brandingColor}
|
||||||
|
backgroundColor="#FFFFFF"
|
||||||
|
ecl="L"
|
||||||
|
/>
|
||||||
|
</TouchableOpacity>
|
||||||
|
|
||||||
|
{!this.state.hideControls && (
|
||||||
|
<View style={animatedQRCodeStyle.container}>
|
||||||
|
<BlueSpacing20 />
|
||||||
|
<View>
|
||||||
|
<Text style={animatedQRCodeStyle.text}>
|
||||||
|
{loc.formatString(loc._.of, { number: this.state.index + 1, total: this.state.total })}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
<BlueSpacing20 />
|
||||||
|
<View style={animatedQRCodeStyle.controller}>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={[animatedQRCodeStyle.button, { width: '25%', alignItems: 'flex-start' }]}
|
||||||
|
onPress={this.moveToPreviousFragment}
|
||||||
|
>
|
||||||
|
<Text style={animatedQRCodeStyle.text}>{loc.send.dynamic_prev}</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={[animatedQRCodeStyle.button, { width: '50%' }]}
|
||||||
|
onPress={this.state.intervalHandler ? this.stopAutoMove : this.startAutoMove}
|
||||||
|
>
|
||||||
|
<Text style={animatedQRCodeStyle.text}>{this.state.intervalHandler ? loc.send.dynamic_stop : loc.send.dynamic_start}</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={[animatedQRCodeStyle.button, { width: '25%', alignItems: 'flex-end' }]}
|
||||||
|
onPress={this.moveToNextFragment}
|
||||||
|
>
|
||||||
|
<Text style={animatedQRCodeStyle.text}>{loc.send.dynamic_next}</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const animatedQRCodeStyle = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
qrcodeContainer: {
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
borderWidth: 6,
|
||||||
|
borderRadius: 8,
|
||||||
|
borderColor: '#FFFFFF',
|
||||||
|
margin: 6,
|
||||||
|
},
|
||||||
|
controller: {
|
||||||
|
width: '90%',
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
borderRadius: 25,
|
||||||
|
height: 45,
|
||||||
|
paddingHorizontal: 18,
|
||||||
|
},
|
||||||
|
button: {
|
||||||
|
alignItems: 'center',
|
||||||
|
height: 45,
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
fontSize: 14,
|
||||||
|
color: BlueCurrentTheme.colors.foregroundColor,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
},
|
||||||
|
});
|
118
components/FloatButtons.js
Normal file
118
components/FloatButtons.js
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
import React, { useState, useRef } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { View, Text, TouchableOpacity, StyleSheet, Dimensions, PixelRatio } from 'react-native';
|
||||||
|
|
||||||
|
import { BlueCurrentTheme } from './themes';
|
||||||
|
|
||||||
|
const BORDER_RADIUS = 30;
|
||||||
|
const PADDINGS = 8;
|
||||||
|
const ICON_MARGIN = 7;
|
||||||
|
|
||||||
|
const cStyles = StyleSheet.create({
|
||||||
|
root: {
|
||||||
|
position: 'absolute',
|
||||||
|
alignSelf: 'center',
|
||||||
|
height: '6.3%',
|
||||||
|
minHeight: 44,
|
||||||
|
},
|
||||||
|
rootPre: {
|
||||||
|
bottom: -1000,
|
||||||
|
},
|
||||||
|
rootPost: {
|
||||||
|
bottom: 30,
|
||||||
|
borderRadius: BORDER_RADIUS,
|
||||||
|
flexDirection: 'row',
|
||||||
|
overflow: 'hidden',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const FContainer = ({ children }) => {
|
||||||
|
const [newWidth, setNewWidth] = useState();
|
||||||
|
const layoutCalculated = useRef(false);
|
||||||
|
|
||||||
|
const onLayout = event => {
|
||||||
|
if (layoutCalculated.current) return;
|
||||||
|
const maxWidth = Dimensions.get('window').width - BORDER_RADIUS - 20;
|
||||||
|
const { width } = event.nativeEvent.layout;
|
||||||
|
const withPaddings = Math.ceil(width + PADDINGS * 2);
|
||||||
|
const len = React.Children.toArray(children).filter(Boolean).length;
|
||||||
|
let newWidth = withPaddings * len > maxWidth ? Math.floor(maxWidth / len) : withPaddings;
|
||||||
|
if (len === 1 && newWidth < 90) newWidth = 90; // to add Paddings for lonely small button, like Scan on main screen
|
||||||
|
setNewWidth(newWidth);
|
||||||
|
layoutCalculated.current = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View onLayout={onLayout} style={[cStyles.root, newWidth ? cStyles.rootPost : cStyles.rootPre]}>
|
||||||
|
{newWidth
|
||||||
|
? React.Children.toArray(children)
|
||||||
|
.filter(Boolean)
|
||||||
|
.map((c, index, array) =>
|
||||||
|
React.cloneElement(c, {
|
||||||
|
width: newWidth,
|
||||||
|
key: index,
|
||||||
|
first: index === 0,
|
||||||
|
last: index === array.length - 1,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
: children}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
FContainer.propTypes = {
|
||||||
|
children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.element), PropTypes.element]),
|
||||||
|
};
|
||||||
|
|
||||||
|
const buttonFontSize =
|
||||||
|
PixelRatio.roundToNearestPixel(Dimensions.get('window').width / 26) > 22
|
||||||
|
? 22
|
||||||
|
: PixelRatio.roundToNearestPixel(Dimensions.get('window').width / 26);
|
||||||
|
|
||||||
|
const bStyles = StyleSheet.create({
|
||||||
|
root: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
overflow: 'hidden',
|
||||||
|
backgroundColor: BlueCurrentTheme.colors.buttonBackgroundColor,
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
color: BlueCurrentTheme.colors.buttonAlternativeTextColor,
|
||||||
|
fontSize: buttonFontSize,
|
||||||
|
fontWeight: '600',
|
||||||
|
marginLeft: ICON_MARGIN,
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const FButton = ({ text, icon, width, first, last, ...props }) => {
|
||||||
|
const style = {};
|
||||||
|
if (width) {
|
||||||
|
const paddingLeft = first ? BORDER_RADIUS / 2 : PADDINGS;
|
||||||
|
const paddingRight = last ? BORDER_RADIUS / 2 : PADDINGS;
|
||||||
|
style.paddingRight = paddingRight;
|
||||||
|
style.paddingLeft = paddingLeft;
|
||||||
|
style.width = width + paddingRight + paddingLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TouchableOpacity style={[bStyles.root, style]} {...props}>
|
||||||
|
<View style={bStyles.icon}>{icon}</View>
|
||||||
|
<Text numberOfLines={1} style={bStyles.text}>
|
||||||
|
{text}
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
FButton.propTypes = {
|
||||||
|
text: PropTypes.string,
|
||||||
|
icon: PropTypes.element,
|
||||||
|
width: PropTypes.number,
|
||||||
|
first: PropTypes.bool,
|
||||||
|
last: PropTypes.bool,
|
||||||
|
};
|
38
components/SquareButton.js
Normal file
38
components/SquareButton.js
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
/* eslint react/prop-types: "off", react-native/no-inline-styles: "off" */
|
||||||
|
import React from 'react';
|
||||||
|
import { TouchableOpacity, View, Text } from 'react-native';
|
||||||
|
import { Icon } from 'react-native-elements';
|
||||||
|
import { useTheme } from '@react-navigation/native';
|
||||||
|
|
||||||
|
export const SquareButton = props => {
|
||||||
|
const { colors } = useTheme();
|
||||||
|
let backgroundColor = props.backgroundColor ? props.backgroundColor : colors.buttonBlueBackgroundColor;
|
||||||
|
let fontColor = colors.buttonTextColor;
|
||||||
|
if (props.disabled === true) {
|
||||||
|
backgroundColor = colors.buttonDisabledBackgroundColor;
|
||||||
|
fontColor = colors.buttonDisabledTextColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TouchableOpacity
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
borderWidth: 0.7,
|
||||||
|
borderColor: 'transparent',
|
||||||
|
backgroundColor: backgroundColor,
|
||||||
|
minHeight: 50,
|
||||||
|
height: 50,
|
||||||
|
maxHeight: 50,
|
||||||
|
borderRadius: 10,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
}}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<View style={{ flexDirection: 'row', justifyContent: 'center', alignItems: 'center' }}>
|
||||||
|
{props.icon && <Icon name={props.icon.name} type={props.icon.type} color={props.icon.color} />}
|
||||||
|
{props.title && <Text style={{ marginHorizontal: 8, fontSize: 16, color: fontColor }}>{props.title}</Text>}
|
||||||
|
</View>
|
||||||
|
</TouchableOpacity>
|
||||||
|
);
|
||||||
|
};
|
|
@ -54,6 +54,8 @@ export const BlueDefaultTheme = {
|
||||||
mainColor: '#CFDCF6',
|
mainColor: '#CFDCF6',
|
||||||
success: '#ccddf9',
|
success: '#ccddf9',
|
||||||
successCheck: '#0f5cc0',
|
successCheck: '#0f5cc0',
|
||||||
|
msSuccessBG: '#37c0a1',
|
||||||
|
msSuccessCheck: '#ffffff',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -96,6 +98,8 @@ export const BlueDarkTheme = {
|
||||||
buttonBlueBackgroundColor: '#202020',
|
buttonBlueBackgroundColor: '#202020',
|
||||||
scanLabel: 'rgba(255,255,255,.2)',
|
scanLabel: 'rgba(255,255,255,.2)',
|
||||||
labelText: '#ffffff',
|
labelText: '#ffffff',
|
||||||
|
msSuccessBG: '#8EFFE5',
|
||||||
|
msSuccessCheck: '#000000',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
BIN
img/vault-shape.png
Normal file
BIN
img/vault-shape.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.6 KiB |
10
index.js
10
index.js
|
@ -1,15 +1,7 @@
|
||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import './shim.js';
|
import './shim.js';
|
||||||
import { AppRegistry, YellowBox } from 'react-native';
|
import { AppRegistry } from 'react-native';
|
||||||
import App from './App';
|
import App from './App';
|
||||||
YellowBox.ignoreWarnings([
|
|
||||||
'Require cycle',
|
|
||||||
'Non-serializable values were',
|
|
||||||
"Can't perform a React state update",
|
|
||||||
'{"code":404',
|
|
||||||
'React has detected a change in the order of Hooks',
|
|
||||||
]);
|
|
||||||
|
|
||||||
const A = require('./blue_modules/analytics');
|
const A = require('./blue_modules/analytics');
|
||||||
|
|
||||||
if (!Error.captureStackTrace) {
|
if (!Error.captureStackTrace) {
|
||||||
|
|
|
@ -939,43 +939,12 @@
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
);
|
);
|
||||||
inputPaths = (
|
inputFileListPaths = (
|
||||||
"${PODS_ROOT}/Target Support Files/Pods-BlueWallet/Pods-BlueWallet-resources.sh",
|
"${PODS_ROOT}/Target Support Files/Pods-BlueWallet/Pods-BlueWallet-resources-${CONFIGURATION}-input-files.xcfilelist",
|
||||||
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/AntDesign.ttf",
|
|
||||||
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Entypo.ttf",
|
|
||||||
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf",
|
|
||||||
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Feather.ttf",
|
|
||||||
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome.ttf",
|
|
||||||
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Brands.ttf",
|
|
||||||
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Regular.ttf",
|
|
||||||
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Solid.ttf",
|
|
||||||
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Fontisto.ttf",
|
|
||||||
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Foundation.ttf",
|
|
||||||
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Ionicons.ttf",
|
|
||||||
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/MaterialCommunityIcons.ttf",
|
|
||||||
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/MaterialIcons.ttf",
|
|
||||||
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Octicons.ttf",
|
|
||||||
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf",
|
|
||||||
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Zocial.ttf",
|
|
||||||
);
|
);
|
||||||
name = "[CP] Copy Pods Resources";
|
name = "[CP] Copy Pods Resources";
|
||||||
outputPaths = (
|
outputFileListPaths = (
|
||||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AntDesign.ttf",
|
"${PODS_ROOT}/Target Support Files/Pods-BlueWallet/Pods-BlueWallet-resources-${CONFIGURATION}-output-files.xcfilelist",
|
||||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Entypo.ttf",
|
|
||||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EvilIcons.ttf",
|
|
||||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Feather.ttf",
|
|
||||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome.ttf",
|
|
||||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Brands.ttf",
|
|
||||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Regular.ttf",
|
|
||||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Solid.ttf",
|
|
||||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Fontisto.ttf",
|
|
||||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Foundation.ttf",
|
|
||||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Ionicons.ttf",
|
|
||||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialCommunityIcons.ttf",
|
|
||||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialIcons.ttf",
|
|
||||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Octicons.ttf",
|
|
||||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SimpleLineIcons.ttf",
|
|
||||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Zocial.ttf",
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
|
@ -1281,6 +1250,7 @@
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(PROJECT_DIR)",
|
"$(PROJECT_DIR)",
|
||||||
);
|
);
|
||||||
|
MARKETING_VERSION = 5.6.2;
|
||||||
OTHER_LDFLAGS = (
|
OTHER_LDFLAGS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"-ObjC",
|
"-ObjC",
|
||||||
|
@ -1320,6 +1290,7 @@
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(PROJECT_DIR)",
|
"$(PROJECT_DIR)",
|
||||||
);
|
);
|
||||||
|
MARKETING_VERSION = 5.6.2;
|
||||||
OTHER_LDFLAGS = (
|
OTHER_LDFLAGS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"-ObjC",
|
"-ObjC",
|
||||||
|
@ -1575,6 +1546,7 @@
|
||||||
CODE_SIGN_ENTITLEMENTS = "TodayExtension/BlueWallet - Bitcoin Price.entitlements";
|
CODE_SIGN_ENTITLEMENTS = "TodayExtension/BlueWallet - Bitcoin Price.entitlements";
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 239;
|
||||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
DEVELOPMENT_TEAM = A7W54YZ4WU;
|
DEVELOPMENT_TEAM = A7W54YZ4WU;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
|
@ -1611,6 +1583,7 @@
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
|
CURRENT_PROJECT_VERSION = 239;
|
||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
DEVELOPMENT_TEAM = A7W54YZ4WU;
|
DEVELOPMENT_TEAM = A7W54YZ4WU;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#import "RNQuickActionManager.h"
|
#import "RNQuickActionManager.h"
|
||||||
#import <UserNotifications/UserNotifications.h>
|
#import <UserNotifications/UserNotifications.h>
|
||||||
#import <RNCPushNotificationIOS.h>
|
#import <RNCPushNotificationIOS.h>
|
||||||
|
#ifdef FB_SONARKIT_ENABLED
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
#import <FlipperKit/FlipperClient.h>
|
#import <FlipperKit/FlipperClient.h>
|
||||||
#import <FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h>
|
#import <FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h>
|
||||||
|
@ -31,13 +32,13 @@ static void InitializeFlipper(UIApplication *application) {
|
||||||
[client start];
|
[client start];
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
@implementation AppDelegate
|
@implementation AppDelegate
|
||||||
|
|
||||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#ifdef FB_SONARKIT_ENABLED
|
||||||
InitializeFlipper(application);
|
InitializeFlipper(application);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,34 @@
|
||||||
<string>io.bluewallet.psbt.txn</string>
|
<string>io.bluewallet.psbt.txn</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleTypeIconFiles</key>
|
||||||
|
<array/>
|
||||||
|
<key>CFBundleTypeName</key>
|
||||||
|
<string>TXT</string>
|
||||||
|
<key>CFBundleTypeRole</key>
|
||||||
|
<string>Editor</string>
|
||||||
|
<key>LSHandlerRank</key>
|
||||||
|
<string>Owner</string>
|
||||||
|
<key>LSItemContentTypes</key>
|
||||||
|
<array>
|
||||||
|
<string>io.bluewallet.txt</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleTypeIconFiles</key>
|
||||||
|
<array/>
|
||||||
|
<key>CFBundleTypeName</key>
|
||||||
|
<string>JSON</string>
|
||||||
|
<key>CFBundleTypeRole</key>
|
||||||
|
<string>Editor</string>
|
||||||
|
<key>LSHandlerRank</key>
|
||||||
|
<string>Owner</string>
|
||||||
|
<key>LSItemContentTypes</key>
|
||||||
|
<array>
|
||||||
|
<string>io.bluewallet.json</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>$(EXECUTABLE_NAME)</string>
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
@ -101,7 +129,7 @@
|
||||||
<key>NSCalendarsUsageDescription</key>
|
<key>NSCalendarsUsageDescription</key>
|
||||||
<string>This alert should not show up as we do not require this data</string>
|
<string>This alert should not show up as we do not require this data</string>
|
||||||
<key>NSCameraUsageDescription</key>
|
<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's address, we need your permission to use the camera to scan their QR Code.</string>
|
||||||
<key>NSFaceIDUsageDescription</key>
|
<key>NSFaceIDUsageDescription</key>
|
||||||
<string>In order to use FaceID please confirm your permission.</string>
|
<string>In order to use FaceID please confirm your permission.</string>
|
||||||
<key>NSLocationAlwaysUsageDescription</key>
|
<key>NSLocationAlwaysUsageDescription</key>
|
||||||
|
@ -241,6 +269,44 @@
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>UTTypeConformsTo</key>
|
||||||
|
<array>
|
||||||
|
<string>public.plain-text</string>
|
||||||
|
</array>
|
||||||
|
<key>UTTypeDescription</key>
|
||||||
|
<string>Text File</string>
|
||||||
|
<key>UTTypeIconFiles</key>
|
||||||
|
<array/>
|
||||||
|
<key>UTTypeIdentifier</key>
|
||||||
|
<string>io.bluewallet.txt</string>
|
||||||
|
<key>UTTypeTagSpecification</key>
|
||||||
|
<dict>
|
||||||
|
<key>public.filename-extension</key>
|
||||||
|
<array>
|
||||||
|
<string>txt</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>UTTypeConformsTo</key>
|
||||||
|
<array>
|
||||||
|
<string>public.json</string>
|
||||||
|
</array>
|
||||||
|
<key>UTTypeDescription</key>
|
||||||
|
<string>JSON File</string>
|
||||||
|
<key>UTTypeIconFiles</key>
|
||||||
|
<array/>
|
||||||
|
<key>UTTypeIdentifier</key>
|
||||||
|
<string>io.bluewallet.json</string>
|
||||||
|
<key>UTTypeTagSpecification</key>
|
||||||
|
<dict>
|
||||||
|
<key>public.filename-extension</key>
|
||||||
|
<array>
|
||||||
|
<string>json</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>XPC!</string>
|
<string>XPC!</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>5.6.0</string>
|
<string>5.6.2</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>239</string>
|
<string>239</string>
|
||||||
<key>CLKComplicationPrincipalClass</key>
|
<key>CLKComplicationPrincipalClass</key>
|
||||||
|
|
|
@ -14,6 +14,7 @@ enum WalletGradient: String {
|
||||||
case LightningCustodial = "lightningCustodianWallet"
|
case LightningCustodial = "lightningCustodianWallet"
|
||||||
case SegwitNative = "HDsegwitBech32"
|
case SegwitNative = "HDsegwitBech32"
|
||||||
case WatchOnly = "watchOnly"
|
case WatchOnly = "watchOnly"
|
||||||
|
case MultiSig = "HDmultisig"
|
||||||
|
|
||||||
var imageString: String{
|
var imageString: String{
|
||||||
switch self {
|
switch self {
|
||||||
|
@ -27,6 +28,8 @@ enum WalletGradient: String {
|
||||||
return "walletWatchOnly"
|
return "walletWatchOnly"
|
||||||
case .LightningCustodial:
|
case .LightningCustodial:
|
||||||
return "walletLightningCustodial"
|
return "walletLightningCustodial"
|
||||||
|
case .MultiSig:
|
||||||
|
return "watchMultisig"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
23
ios/BlueWalletWatch/Assets.xcassets/watchMultisig.imageset/Contents.json
vendored
Normal file
23
ios/BlueWalletWatch/Assets.xcassets/watchMultisig.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "multisig-watch.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "multisig-watch@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "multisig-watch@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
ios/BlueWalletWatch/Assets.xcassets/watchMultisig.imageset/multisig-watch.png
vendored
Normal file
BIN
ios/BlueWalletWatch/Assets.xcassets/watchMultisig.imageset/multisig-watch.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
BIN
ios/BlueWalletWatch/Assets.xcassets/watchMultisig.imageset/multisig-watch@2x.png
vendored
Normal file
BIN
ios/BlueWalletWatch/Assets.xcassets/watchMultisig.imageset/multisig-watch@2x.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
BIN
ios/BlueWalletWatch/Assets.xcassets/watchMultisig.imageset/multisig-watch@3x.png
vendored
Normal file
BIN
ios/BlueWalletWatch/Assets.xcassets/watchMultisig.imageset/multisig-watch@3x.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 49 KiB |
|
@ -17,7 +17,7 @@
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>5.6.0</string>
|
<string>5.6.2</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>239</string>
|
<string>239</string>
|
||||||
<key>UISupportedInterfaceOrientations</key>
|
<key>UISupportedInterfaceOrientations</key>
|
||||||
|
|
81
ios/Podfile
81
ios/Podfile
|
@ -1,91 +1,18 @@
|
||||||
platform :ios, '10.0'
|
platform :ios, '10.0'
|
||||||
workspace 'BlueWallet'
|
workspace 'BlueWallet'
|
||||||
|
require_relative '../node_modules/react-native/scripts/react_native_pods'
|
||||||
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
|
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
|
||||||
|
|
||||||
def add_flipper_pods!(versions = {})
|
|
||||||
versions['Flipper'] ||= '~> 0.37.0'
|
|
||||||
versions['DoubleConversion'] ||= '1.1.7'
|
|
||||||
versions['Flipper-Folly'] ||= '~> 2.1'
|
|
||||||
versions['Flipper-Glog'] ||= '0.3.6'
|
|
||||||
versions['Flipper-PeerTalk'] ||= '~> 0.0.4'
|
|
||||||
versions['Flipper-RSocket'] ||= '~> 1.0'
|
|
||||||
|
|
||||||
pod 'FlipperKit', versions['Flipper'], :configuration => 'Debug'
|
|
||||||
pod 'FlipperKit/FlipperKitLayoutPlugin', versions['Flipper'], :configuration => 'Debug'
|
|
||||||
pod 'FlipperKit/SKIOSNetworkPlugin', versions['Flipper'], :configuration => 'Debug'
|
|
||||||
pod 'FlipperKit/FlipperKitUserDefaultsPlugin', versions['Flipper'], :configuration => 'Debug'
|
|
||||||
pod 'FlipperKit/FlipperKitReactPlugin', versions['Flipper'], :configuration => 'Debug'
|
|
||||||
|
|
||||||
# List all transitive dependencies for FlipperKit pods
|
|
||||||
# to avoid them being linked in Release builds
|
|
||||||
pod 'Flipper', versions['Flipper'], :configuration => 'Debug'
|
|
||||||
pod 'Flipper-DoubleConversion', versions['DoubleConversion'], :configuration => 'Debug'
|
|
||||||
pod 'Flipper-Folly', versions['Flipper-Folly'], :configuration => 'Debug'
|
|
||||||
pod 'Flipper-Glog', versions['Flipper-Glog'], :configuration => 'Debug'
|
|
||||||
pod 'Flipper-PeerTalk', versions['Flipper-PeerTalk'], :configuration => 'Debug'
|
|
||||||
pod 'Flipper-RSocket', versions['Flipper-RSocket'], :configuration => 'Debug'
|
|
||||||
pod 'FlipperKit/Core', versions['Flipper'], :configuration => 'Debug'
|
|
||||||
pod 'FlipperKit/CppBridge', versions['Flipper'], :configuration => 'Debug'
|
|
||||||
pod 'FlipperKit/FBCxxFollyDynamicConvert', versions['Flipper'], :configuration => 'Debug'
|
|
||||||
pod 'FlipperKit/FBDefines', versions['Flipper'], :configuration => 'Debug'
|
|
||||||
pod 'FlipperKit/FKPortForwarding', versions['Flipper'], :configuration => 'Debug'
|
|
||||||
pod 'FlipperKit/FlipperKitHighlightOverlay', versions['Flipper'], :configuration => 'Debug'
|
|
||||||
pod 'FlipperKit/FlipperKitLayoutTextSearchable', versions['Flipper'], :configuration => 'Debug'
|
|
||||||
pod 'FlipperKit/FlipperKitNetworkPlugin', versions['Flipper'], :configuration => 'Debug'
|
|
||||||
end
|
|
||||||
|
|
||||||
# Post Install processing for Flipper
|
|
||||||
def flipper_post_install(installer)
|
|
||||||
installer.pods_project.targets.each do |target|
|
|
||||||
if target.name == 'YogaKit'
|
|
||||||
target.build_configurations.each do |config|
|
|
||||||
config.build_settings['SWIFT_VERSION'] = '4.1'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
target 'BlueWallet' do
|
target 'BlueWallet' do
|
||||||
# Pods for RnDiffApp
|
config = use_native_modules!
|
||||||
pod 'FBLazyVector', :path => "../node_modules/react-native/Libraries/FBLazyVector"
|
|
||||||
pod 'FBReactNativeSpec', :path => "../node_modules/react-native/Libraries/FBReactNativeSpec"
|
|
||||||
pod 'RCTRequired', :path => "../node_modules/react-native/Libraries/RCTRequired"
|
|
||||||
pod 'RCTTypeSafety', :path => "../node_modules/react-native/Libraries/TypeSafety"
|
|
||||||
pod 'React', :path => '../node_modules/react-native/'
|
|
||||||
pod 'React-Core', :path => '../node_modules/react-native/'
|
|
||||||
pod 'React-CoreModules', :path => '../node_modules/react-native/React/CoreModules'
|
|
||||||
pod 'React-Core/DevSupport', :path => '../node_modules/react-native/'
|
|
||||||
pod 'React-RCTActionSheet', :path => '../node_modules/react-native/Libraries/ActionSheetIOS'
|
|
||||||
pod 'React-RCTAnimation', :path => '../node_modules/react-native/Libraries/NativeAnimation'
|
|
||||||
pod 'React-RCTBlob', :path => '../node_modules/react-native/Libraries/Blob'
|
|
||||||
pod 'React-RCTImage', :path => '../node_modules/react-native/Libraries/Image'
|
|
||||||
pod 'React-RCTLinking', :path => '../node_modules/react-native/Libraries/LinkingIOS'
|
|
||||||
pod 'React-RCTNetwork', :path => '../node_modules/react-native/Libraries/Network'
|
|
||||||
pod 'React-RCTSettings', :path => '../node_modules/react-native/Libraries/Settings'
|
|
||||||
pod 'React-RCTText', :path => '../node_modules/react-native/Libraries/Text'
|
|
||||||
pod 'React-RCTVibration', :path => '../node_modules/react-native/Libraries/Vibration'
|
|
||||||
pod 'React-Core/RCTWebSocket', :path => '../node_modules/react-native/'
|
|
||||||
|
|
||||||
pod 'React-cxxreact', :path => '../node_modules/react-native/ReactCommon/cxxreact'
|
use_react_native!(:path => config["reactNativePath"])
|
||||||
pod 'React-jsi', :path => '../node_modules/react-native/ReactCommon/jsi'
|
|
||||||
pod 'React-jsiexecutor', :path => '../node_modules/react-native/ReactCommon/jsiexecutor'
|
|
||||||
pod 'React-jsinspector', :path => '../node_modules/react-native/ReactCommon/jsinspector'
|
|
||||||
pod 'ReactCommon/callinvoker', :path => "../node_modules/react-native/ReactCommon"
|
|
||||||
pod 'ReactCommon/turbomodule/core', :path => "../node_modules/react-native/ReactCommon"
|
|
||||||
pod 'Yoga', :path => '../node_modules/react-native/ReactCommon/yoga', :modular_headers => true
|
|
||||||
|
|
||||||
pod 'DoubleConversion', :podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
|
|
||||||
pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec'
|
|
||||||
pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec'
|
|
||||||
|
|
||||||
|
|
||||||
use_native_modules!
|
|
||||||
|
|
||||||
# Enables Flipper.
|
# Enables Flipper.
|
||||||
#
|
#
|
||||||
# Note that if you have use_frameworks! enabled, Flipper will not work and
|
# Note that if you have use_frameworks! enabled, Flipper will not work and
|
||||||
# you should disable these next few lines.
|
# you should disable these next few lines.
|
||||||
add_flipper_pods!
|
use_flipper!
|
||||||
post_install do |installer|
|
post_install do |installer|
|
||||||
flipper_post_install(installer)
|
flipper_post_install(installer)
|
||||||
end
|
end
|
||||||
|
|
545
ios/Podfile.lock
545
ios/Podfile.lock
|
@ -5,15 +5,15 @@ PODS:
|
||||||
- CocoaAsyncSocket (7.6.4)
|
- CocoaAsyncSocket (7.6.4)
|
||||||
- CocoaLibEvent (1.0.0)
|
- CocoaLibEvent (1.0.0)
|
||||||
- DoubleConversion (1.1.6)
|
- DoubleConversion (1.1.6)
|
||||||
- FBLazyVector (0.62.2)
|
- FBLazyVector (0.63.3)
|
||||||
- FBReactNativeSpec (0.62.2):
|
- FBReactNativeSpec (0.63.3):
|
||||||
- Folly (= 2018.10.22.00)
|
- Folly (= 2020.01.13.00)
|
||||||
- RCTRequired (= 0.62.2)
|
- RCTRequired (= 0.63.3)
|
||||||
- RCTTypeSafety (= 0.62.2)
|
- RCTTypeSafety (= 0.63.3)
|
||||||
- React-Core (= 0.62.2)
|
- React-Core (= 0.63.3)
|
||||||
- React-jsi (= 0.62.2)
|
- React-jsi (= 0.63.3)
|
||||||
- ReactCommon/turbomodule/core (= 0.62.2)
|
- ReactCommon/turbomodule/core (= 0.63.3)
|
||||||
- Flipper (0.37.0):
|
- Flipper (0.54.0):
|
||||||
- Flipper-Folly (~> 2.2)
|
- Flipper-Folly (~> 2.2)
|
||||||
- Flipper-RSocket (~> 1.1)
|
- Flipper-RSocket (~> 1.1)
|
||||||
- Flipper-DoubleConversion (1.1.7)
|
- Flipper-DoubleConversion (1.1.7)
|
||||||
|
@ -27,44 +27,44 @@ PODS:
|
||||||
- Flipper-PeerTalk (0.0.4)
|
- Flipper-PeerTalk (0.0.4)
|
||||||
- Flipper-RSocket (1.1.0):
|
- Flipper-RSocket (1.1.0):
|
||||||
- Flipper-Folly (~> 2.2)
|
- Flipper-Folly (~> 2.2)
|
||||||
- FlipperKit (0.37.0):
|
- FlipperKit (0.54.0):
|
||||||
- FlipperKit/Core (= 0.37.0)
|
- FlipperKit/Core (= 0.54.0)
|
||||||
- FlipperKit/Core (0.37.0):
|
- FlipperKit/Core (0.54.0):
|
||||||
- Flipper (~> 0.37.0)
|
- Flipper (~> 0.54.0)
|
||||||
- FlipperKit/CppBridge
|
- FlipperKit/CppBridge
|
||||||
- FlipperKit/FBCxxFollyDynamicConvert
|
- FlipperKit/FBCxxFollyDynamicConvert
|
||||||
- FlipperKit/FBDefines
|
- FlipperKit/FBDefines
|
||||||
- FlipperKit/FKPortForwarding
|
- FlipperKit/FKPortForwarding
|
||||||
- FlipperKit/CppBridge (0.37.0):
|
- FlipperKit/CppBridge (0.54.0):
|
||||||
- Flipper (~> 0.37.0)
|
- Flipper (~> 0.54.0)
|
||||||
- FlipperKit/FBCxxFollyDynamicConvert (0.37.0):
|
- FlipperKit/FBCxxFollyDynamicConvert (0.54.0):
|
||||||
- Flipper-Folly (~> 2.2)
|
- Flipper-Folly (~> 2.2)
|
||||||
- FlipperKit/FBDefines (0.37.0)
|
- FlipperKit/FBDefines (0.54.0)
|
||||||
- FlipperKit/FKPortForwarding (0.37.0):
|
- FlipperKit/FKPortForwarding (0.54.0):
|
||||||
- CocoaAsyncSocket (~> 7.6)
|
- CocoaAsyncSocket (~> 7.6)
|
||||||
- Flipper-PeerTalk (~> 0.0.4)
|
- Flipper-PeerTalk (~> 0.0.4)
|
||||||
- FlipperKit/FlipperKitHighlightOverlay (0.37.0)
|
- FlipperKit/FlipperKitHighlightOverlay (0.54.0)
|
||||||
- FlipperKit/FlipperKitLayoutPlugin (0.37.0):
|
- FlipperKit/FlipperKitLayoutPlugin (0.54.0):
|
||||||
- FlipperKit/Core
|
- FlipperKit/Core
|
||||||
- FlipperKit/FlipperKitHighlightOverlay
|
- FlipperKit/FlipperKitHighlightOverlay
|
||||||
- FlipperKit/FlipperKitLayoutTextSearchable
|
- FlipperKit/FlipperKitLayoutTextSearchable
|
||||||
- YogaKit (~> 1.18)
|
- YogaKit (~> 1.18)
|
||||||
- FlipperKit/FlipperKitLayoutTextSearchable (0.37.0)
|
- FlipperKit/FlipperKitLayoutTextSearchable (0.54.0)
|
||||||
- FlipperKit/FlipperKitNetworkPlugin (0.37.0):
|
- FlipperKit/FlipperKitNetworkPlugin (0.54.0):
|
||||||
- FlipperKit/Core
|
- FlipperKit/Core
|
||||||
- FlipperKit/FlipperKitReactPlugin (0.37.0):
|
- FlipperKit/FlipperKitReactPlugin (0.54.0):
|
||||||
- FlipperKit/Core
|
- FlipperKit/Core
|
||||||
- FlipperKit/FlipperKitUserDefaultsPlugin (0.37.0):
|
- FlipperKit/FlipperKitUserDefaultsPlugin (0.54.0):
|
||||||
- FlipperKit/Core
|
- FlipperKit/Core
|
||||||
- FlipperKit/SKIOSNetworkPlugin (0.37.0):
|
- FlipperKit/SKIOSNetworkPlugin (0.54.0):
|
||||||
- FlipperKit/Core
|
- FlipperKit/Core
|
||||||
- FlipperKit/FlipperKitNetworkPlugin
|
- FlipperKit/FlipperKitNetworkPlugin
|
||||||
- Folly (2018.10.22.00):
|
- Folly (2020.01.13.00):
|
||||||
- boost-for-react-native
|
- boost-for-react-native
|
||||||
- DoubleConversion
|
- DoubleConversion
|
||||||
- Folly/Default (= 2018.10.22.00)
|
- Folly/Default (= 2020.01.13.00)
|
||||||
- glog
|
- glog
|
||||||
- Folly/Default (2018.10.22.00):
|
- Folly/Default (2020.01.13.00):
|
||||||
- boost-for-react-native
|
- boost-for-react-native
|
||||||
- DoubleConversion
|
- DoubleConversion
|
||||||
- glog
|
- glog
|
||||||
|
@ -81,169 +81,172 @@ PODS:
|
||||||
- OpenSSL-Universal/Static (1.0.2.19)
|
- OpenSSL-Universal/Static (1.0.2.19)
|
||||||
- PasscodeAuth (1.0.0):
|
- PasscodeAuth (1.0.0):
|
||||||
- React
|
- React
|
||||||
- RCTRequired (0.62.2)
|
- RCTRequired (0.63.3)
|
||||||
- RCTTypeSafety (0.62.2):
|
- RCTTypeSafety (0.63.3):
|
||||||
- FBLazyVector (= 0.62.2)
|
- FBLazyVector (= 0.63.3)
|
||||||
- Folly (= 2018.10.22.00)
|
- Folly (= 2020.01.13.00)
|
||||||
- RCTRequired (= 0.62.2)
|
- RCTRequired (= 0.63.3)
|
||||||
- React-Core (= 0.62.2)
|
- React-Core (= 0.63.3)
|
||||||
- React (0.62.2):
|
- React (0.63.3):
|
||||||
- React-Core (= 0.62.2)
|
- React-Core (= 0.63.3)
|
||||||
- React-Core/DevSupport (= 0.62.2)
|
- React-Core/DevSupport (= 0.63.3)
|
||||||
- React-Core/RCTWebSocket (= 0.62.2)
|
- React-Core/RCTWebSocket (= 0.63.3)
|
||||||
- React-RCTActionSheet (= 0.62.2)
|
- React-RCTActionSheet (= 0.63.3)
|
||||||
- React-RCTAnimation (= 0.62.2)
|
- React-RCTAnimation (= 0.63.3)
|
||||||
- React-RCTBlob (= 0.62.2)
|
- React-RCTBlob (= 0.63.3)
|
||||||
- React-RCTImage (= 0.62.2)
|
- React-RCTImage (= 0.63.3)
|
||||||
- React-RCTLinking (= 0.62.2)
|
- React-RCTLinking (= 0.63.3)
|
||||||
- React-RCTNetwork (= 0.62.2)
|
- React-RCTNetwork (= 0.63.3)
|
||||||
- React-RCTSettings (= 0.62.2)
|
- React-RCTSettings (= 0.63.3)
|
||||||
- React-RCTText (= 0.62.2)
|
- React-RCTText (= 0.63.3)
|
||||||
- React-RCTVibration (= 0.62.2)
|
- React-RCTVibration (= 0.63.3)
|
||||||
- React-Core (0.62.2):
|
- React-callinvoker (0.63.3)
|
||||||
- Folly (= 2018.10.22.00)
|
- React-Core (0.63.3):
|
||||||
|
- Folly (= 2020.01.13.00)
|
||||||
- glog
|
- glog
|
||||||
- React-Core/Default (= 0.62.2)
|
- React-Core/Default (= 0.63.3)
|
||||||
- React-cxxreact (= 0.62.2)
|
- React-cxxreact (= 0.63.3)
|
||||||
- React-jsi (= 0.62.2)
|
- React-jsi (= 0.63.3)
|
||||||
- React-jsiexecutor (= 0.62.2)
|
- React-jsiexecutor (= 0.63.3)
|
||||||
- Yoga
|
- Yoga
|
||||||
- React-Core/CoreModulesHeaders (0.62.2):
|
- React-Core/CoreModulesHeaders (0.63.3):
|
||||||
- Folly (= 2018.10.22.00)
|
- Folly (= 2020.01.13.00)
|
||||||
- glog
|
- glog
|
||||||
- React-Core/Default
|
- React-Core/Default
|
||||||
- React-cxxreact (= 0.62.2)
|
- React-cxxreact (= 0.63.3)
|
||||||
- React-jsi (= 0.62.2)
|
- React-jsi (= 0.63.3)
|
||||||
- React-jsiexecutor (= 0.62.2)
|
- React-jsiexecutor (= 0.63.3)
|
||||||
- Yoga
|
- Yoga
|
||||||
- React-Core/Default (0.62.2):
|
- React-Core/Default (0.63.3):
|
||||||
- Folly (= 2018.10.22.00)
|
- Folly (= 2020.01.13.00)
|
||||||
- glog
|
- glog
|
||||||
- React-cxxreact (= 0.62.2)
|
- React-cxxreact (= 0.63.3)
|
||||||
- React-jsi (= 0.62.2)
|
- React-jsi (= 0.63.3)
|
||||||
- React-jsiexecutor (= 0.62.2)
|
- React-jsiexecutor (= 0.63.3)
|
||||||
- Yoga
|
- Yoga
|
||||||
- React-Core/DevSupport (0.62.2):
|
- React-Core/DevSupport (0.63.3):
|
||||||
- Folly (= 2018.10.22.00)
|
- Folly (= 2020.01.13.00)
|
||||||
- glog
|
- glog
|
||||||
- React-Core/Default (= 0.62.2)
|
- React-Core/Default (= 0.63.3)
|
||||||
- React-Core/RCTWebSocket (= 0.62.2)
|
- React-Core/RCTWebSocket (= 0.63.3)
|
||||||
- React-cxxreact (= 0.62.2)
|
- React-cxxreact (= 0.63.3)
|
||||||
- React-jsi (= 0.62.2)
|
- React-jsi (= 0.63.3)
|
||||||
- React-jsiexecutor (= 0.62.2)
|
- React-jsiexecutor (= 0.63.3)
|
||||||
- React-jsinspector (= 0.62.2)
|
- React-jsinspector (= 0.63.3)
|
||||||
- Yoga
|
- Yoga
|
||||||
- React-Core/RCTActionSheetHeaders (0.62.2):
|
- React-Core/RCTActionSheetHeaders (0.63.3):
|
||||||
- Folly (= 2018.10.22.00)
|
- Folly (= 2020.01.13.00)
|
||||||
- glog
|
- glog
|
||||||
- React-Core/Default
|
- React-Core/Default
|
||||||
- React-cxxreact (= 0.62.2)
|
- React-cxxreact (= 0.63.3)
|
||||||
- React-jsi (= 0.62.2)
|
- React-jsi (= 0.63.3)
|
||||||
- React-jsiexecutor (= 0.62.2)
|
- React-jsiexecutor (= 0.63.3)
|
||||||
- Yoga
|
- Yoga
|
||||||
- React-Core/RCTAnimationHeaders (0.62.2):
|
- React-Core/RCTAnimationHeaders (0.63.3):
|
||||||
- Folly (= 2018.10.22.00)
|
- Folly (= 2020.01.13.00)
|
||||||
- glog
|
- glog
|
||||||
- React-Core/Default
|
- React-Core/Default
|
||||||
- React-cxxreact (= 0.62.2)
|
- React-cxxreact (= 0.63.3)
|
||||||
- React-jsi (= 0.62.2)
|
- React-jsi (= 0.63.3)
|
||||||
- React-jsiexecutor (= 0.62.2)
|
- React-jsiexecutor (= 0.63.3)
|
||||||
- Yoga
|
- Yoga
|
||||||
- React-Core/RCTBlobHeaders (0.62.2):
|
- React-Core/RCTBlobHeaders (0.63.3):
|
||||||
- Folly (= 2018.10.22.00)
|
- Folly (= 2020.01.13.00)
|
||||||
- glog
|
- glog
|
||||||
- React-Core/Default
|
- React-Core/Default
|
||||||
- React-cxxreact (= 0.62.2)
|
- React-cxxreact (= 0.63.3)
|
||||||
- React-jsi (= 0.62.2)
|
- React-jsi (= 0.63.3)
|
||||||
- React-jsiexecutor (= 0.62.2)
|
- React-jsiexecutor (= 0.63.3)
|
||||||
- Yoga
|
- Yoga
|
||||||
- React-Core/RCTImageHeaders (0.62.2):
|
- React-Core/RCTImageHeaders (0.63.3):
|
||||||
- Folly (= 2018.10.22.00)
|
- Folly (= 2020.01.13.00)
|
||||||
- glog
|
- glog
|
||||||
- React-Core/Default
|
- React-Core/Default
|
||||||
- React-cxxreact (= 0.62.2)
|
- React-cxxreact (= 0.63.3)
|
||||||
- React-jsi (= 0.62.2)
|
- React-jsi (= 0.63.3)
|
||||||
- React-jsiexecutor (= 0.62.2)
|
- React-jsiexecutor (= 0.63.3)
|
||||||
- Yoga
|
- Yoga
|
||||||
- React-Core/RCTLinkingHeaders (0.62.2):
|
- React-Core/RCTLinkingHeaders (0.63.3):
|
||||||
- Folly (= 2018.10.22.00)
|
- Folly (= 2020.01.13.00)
|
||||||
- glog
|
- glog
|
||||||
- React-Core/Default
|
- React-Core/Default
|
||||||
- React-cxxreact (= 0.62.2)
|
- React-cxxreact (= 0.63.3)
|
||||||
- React-jsi (= 0.62.2)
|
- React-jsi (= 0.63.3)
|
||||||
- React-jsiexecutor (= 0.62.2)
|
- React-jsiexecutor (= 0.63.3)
|
||||||
- Yoga
|
- Yoga
|
||||||
- React-Core/RCTNetworkHeaders (0.62.2):
|
- React-Core/RCTNetworkHeaders (0.63.3):
|
||||||
- Folly (= 2018.10.22.00)
|
- Folly (= 2020.01.13.00)
|
||||||
- glog
|
- glog
|
||||||
- React-Core/Default
|
- React-Core/Default
|
||||||
- React-cxxreact (= 0.62.2)
|
- React-cxxreact (= 0.63.3)
|
||||||
- React-jsi (= 0.62.2)
|
- React-jsi (= 0.63.3)
|
||||||
- React-jsiexecutor (= 0.62.2)
|
- React-jsiexecutor (= 0.63.3)
|
||||||
- Yoga
|
- Yoga
|
||||||
- React-Core/RCTSettingsHeaders (0.62.2):
|
- React-Core/RCTSettingsHeaders (0.63.3):
|
||||||
- Folly (= 2018.10.22.00)
|
- Folly (= 2020.01.13.00)
|
||||||
- glog
|
- glog
|
||||||
- React-Core/Default
|
- React-Core/Default
|
||||||
- React-cxxreact (= 0.62.2)
|
- React-cxxreact (= 0.63.3)
|
||||||
- React-jsi (= 0.62.2)
|
- React-jsi (= 0.63.3)
|
||||||
- React-jsiexecutor (= 0.62.2)
|
- React-jsiexecutor (= 0.63.3)
|
||||||
- Yoga
|
- Yoga
|
||||||
- React-Core/RCTTextHeaders (0.62.2):
|
- React-Core/RCTTextHeaders (0.63.3):
|
||||||
- Folly (= 2018.10.22.00)
|
- Folly (= 2020.01.13.00)
|
||||||
- glog
|
- glog
|
||||||
- React-Core/Default
|
- React-Core/Default
|
||||||
- React-cxxreact (= 0.62.2)
|
- React-cxxreact (= 0.63.3)
|
||||||
- React-jsi (= 0.62.2)
|
- React-jsi (= 0.63.3)
|
||||||
- React-jsiexecutor (= 0.62.2)
|
- React-jsiexecutor (= 0.63.3)
|
||||||
- Yoga
|
- Yoga
|
||||||
- React-Core/RCTVibrationHeaders (0.62.2):
|
- React-Core/RCTVibrationHeaders (0.63.3):
|
||||||
- Folly (= 2018.10.22.00)
|
- Folly (= 2020.01.13.00)
|
||||||
- glog
|
- glog
|
||||||
- React-Core/Default
|
- React-Core/Default
|
||||||
- React-cxxreact (= 0.62.2)
|
- React-cxxreact (= 0.63.3)
|
||||||
- React-jsi (= 0.62.2)
|
- React-jsi (= 0.63.3)
|
||||||
- React-jsiexecutor (= 0.62.2)
|
- React-jsiexecutor (= 0.63.3)
|
||||||
- Yoga
|
- Yoga
|
||||||
- React-Core/RCTWebSocket (0.62.2):
|
- React-Core/RCTWebSocket (0.63.3):
|
||||||
- Folly (= 2018.10.22.00)
|
- Folly (= 2020.01.13.00)
|
||||||
- glog
|
- glog
|
||||||
- React-Core/Default (= 0.62.2)
|
- React-Core/Default (= 0.63.3)
|
||||||
- React-cxxreact (= 0.62.2)
|
- React-cxxreact (= 0.63.3)
|
||||||
- React-jsi (= 0.62.2)
|
- React-jsi (= 0.63.3)
|
||||||
- React-jsiexecutor (= 0.62.2)
|
- React-jsiexecutor (= 0.63.3)
|
||||||
- Yoga
|
- Yoga
|
||||||
- React-CoreModules (0.62.2):
|
- React-CoreModules (0.63.3):
|
||||||
- FBReactNativeSpec (= 0.62.2)
|
- FBReactNativeSpec (= 0.63.3)
|
||||||
- Folly (= 2018.10.22.00)
|
- Folly (= 2020.01.13.00)
|
||||||
- RCTTypeSafety (= 0.62.2)
|
- RCTTypeSafety (= 0.63.3)
|
||||||
- React-Core/CoreModulesHeaders (= 0.62.2)
|
- React-Core/CoreModulesHeaders (= 0.63.3)
|
||||||
- React-RCTImage (= 0.62.2)
|
- React-jsi (= 0.63.3)
|
||||||
- ReactCommon/turbomodule/core (= 0.62.2)
|
- React-RCTImage (= 0.63.3)
|
||||||
- React-cxxreact (0.62.2):
|
- ReactCommon/turbomodule/core (= 0.63.3)
|
||||||
|
- React-cxxreact (0.63.3):
|
||||||
- boost-for-react-native (= 1.63.0)
|
- boost-for-react-native (= 1.63.0)
|
||||||
- DoubleConversion
|
- DoubleConversion
|
||||||
- Folly (= 2018.10.22.00)
|
- Folly (= 2020.01.13.00)
|
||||||
- glog
|
- glog
|
||||||
- React-jsinspector (= 0.62.2)
|
- React-callinvoker (= 0.63.3)
|
||||||
- React-jsi (0.62.2):
|
- React-jsinspector (= 0.63.3)
|
||||||
|
- React-jsi (0.63.3):
|
||||||
- boost-for-react-native (= 1.63.0)
|
- boost-for-react-native (= 1.63.0)
|
||||||
- DoubleConversion
|
- DoubleConversion
|
||||||
- Folly (= 2018.10.22.00)
|
- Folly (= 2020.01.13.00)
|
||||||
- glog
|
- glog
|
||||||
- React-jsi/Default (= 0.62.2)
|
- React-jsi/Default (= 0.63.3)
|
||||||
- React-jsi/Default (0.62.2):
|
- React-jsi/Default (0.63.3):
|
||||||
- boost-for-react-native (= 1.63.0)
|
- boost-for-react-native (= 1.63.0)
|
||||||
- DoubleConversion
|
- DoubleConversion
|
||||||
- Folly (= 2018.10.22.00)
|
- Folly (= 2020.01.13.00)
|
||||||
- glog
|
- glog
|
||||||
- React-jsiexecutor (0.62.2):
|
- React-jsiexecutor (0.63.3):
|
||||||
- DoubleConversion
|
- DoubleConversion
|
||||||
- Folly (= 2018.10.22.00)
|
- Folly (= 2020.01.13.00)
|
||||||
- glog
|
- glog
|
||||||
- React-cxxreact (= 0.62.2)
|
- React-cxxreact (= 0.63.3)
|
||||||
- React-jsi (= 0.62.2)
|
- React-jsi (= 0.63.3)
|
||||||
- React-jsinspector (0.62.2)
|
- React-jsinspector (0.63.3)
|
||||||
- react-native-blue-crypto (1.0.0):
|
- react-native-blue-crypto (1.0.0):
|
||||||
- React
|
- React
|
||||||
- react-native-blur (0.8.0):
|
- react-native-blur (0.8.0):
|
||||||
|
@ -262,8 +265,8 @@ PODS:
|
||||||
- React
|
- React
|
||||||
- react-native-geolocation (2.0.2):
|
- react-native-geolocation (2.0.2):
|
||||||
- React
|
- React
|
||||||
- react-native-image-picker (2.3.3):
|
- react-native-image-picker (2.3.4):
|
||||||
- React
|
- React-Core
|
||||||
- react-native-randombytes (3.5.3):
|
- react-native-randombytes (3.5.3):
|
||||||
- React
|
- React
|
||||||
- react-native-safe-area-context (3.1.8):
|
- react-native-safe-area-context (3.1.8):
|
||||||
|
@ -273,67 +276,68 @@ PODS:
|
||||||
- react-native-tcp-socket (3.7.1):
|
- react-native-tcp-socket (3.7.1):
|
||||||
- CocoaAsyncSocket
|
- CocoaAsyncSocket
|
||||||
- React
|
- React
|
||||||
- react-native-webview (10.8.3):
|
- react-native-webview (10.9.2):
|
||||||
- React
|
- React-Core
|
||||||
- React-RCTActionSheet (0.62.2):
|
- React-RCTActionSheet (0.63.3):
|
||||||
- React-Core/RCTActionSheetHeaders (= 0.62.2)
|
- React-Core/RCTActionSheetHeaders (= 0.63.3)
|
||||||
- React-RCTAnimation (0.62.2):
|
- React-RCTAnimation (0.63.3):
|
||||||
- FBReactNativeSpec (= 0.62.2)
|
- FBReactNativeSpec (= 0.63.3)
|
||||||
- Folly (= 2018.10.22.00)
|
- Folly (= 2020.01.13.00)
|
||||||
- RCTTypeSafety (= 0.62.2)
|
- RCTTypeSafety (= 0.63.3)
|
||||||
- React-Core/RCTAnimationHeaders (= 0.62.2)
|
- React-Core/RCTAnimationHeaders (= 0.63.3)
|
||||||
- ReactCommon/turbomodule/core (= 0.62.2)
|
- React-jsi (= 0.63.3)
|
||||||
- React-RCTBlob (0.62.2):
|
- ReactCommon/turbomodule/core (= 0.63.3)
|
||||||
- FBReactNativeSpec (= 0.62.2)
|
- React-RCTBlob (0.63.3):
|
||||||
- Folly (= 2018.10.22.00)
|
- FBReactNativeSpec (= 0.63.3)
|
||||||
- React-Core/RCTBlobHeaders (= 0.62.2)
|
- Folly (= 2020.01.13.00)
|
||||||
- React-Core/RCTWebSocket (= 0.62.2)
|
- React-Core/RCTBlobHeaders (= 0.63.3)
|
||||||
- React-jsi (= 0.62.2)
|
- React-Core/RCTWebSocket (= 0.63.3)
|
||||||
- React-RCTNetwork (= 0.62.2)
|
- React-jsi (= 0.63.3)
|
||||||
- ReactCommon/turbomodule/core (= 0.62.2)
|
- React-RCTNetwork (= 0.63.3)
|
||||||
- React-RCTImage (0.62.2):
|
- ReactCommon/turbomodule/core (= 0.63.3)
|
||||||
- FBReactNativeSpec (= 0.62.2)
|
- React-RCTImage (0.63.3):
|
||||||
- Folly (= 2018.10.22.00)
|
- FBReactNativeSpec (= 0.63.3)
|
||||||
- RCTTypeSafety (= 0.62.2)
|
- Folly (= 2020.01.13.00)
|
||||||
- React-Core/RCTImageHeaders (= 0.62.2)
|
- RCTTypeSafety (= 0.63.3)
|
||||||
- React-RCTNetwork (= 0.62.2)
|
- React-Core/RCTImageHeaders (= 0.63.3)
|
||||||
- ReactCommon/turbomodule/core (= 0.62.2)
|
- React-jsi (= 0.63.3)
|
||||||
- React-RCTLinking (0.62.2):
|
- React-RCTNetwork (= 0.63.3)
|
||||||
- FBReactNativeSpec (= 0.62.2)
|
- ReactCommon/turbomodule/core (= 0.63.3)
|
||||||
- React-Core/RCTLinkingHeaders (= 0.62.2)
|
- React-RCTLinking (0.63.3):
|
||||||
- ReactCommon/turbomodule/core (= 0.62.2)
|
- FBReactNativeSpec (= 0.63.3)
|
||||||
- React-RCTNetwork (0.62.2):
|
- React-Core/RCTLinkingHeaders (= 0.63.3)
|
||||||
- FBReactNativeSpec (= 0.62.2)
|
- React-jsi (= 0.63.3)
|
||||||
- Folly (= 2018.10.22.00)
|
- ReactCommon/turbomodule/core (= 0.63.3)
|
||||||
- RCTTypeSafety (= 0.62.2)
|
- React-RCTNetwork (0.63.3):
|
||||||
- React-Core/RCTNetworkHeaders (= 0.62.2)
|
- FBReactNativeSpec (= 0.63.3)
|
||||||
- ReactCommon/turbomodule/core (= 0.62.2)
|
- Folly (= 2020.01.13.00)
|
||||||
- React-RCTSettings (0.62.2):
|
- RCTTypeSafety (= 0.63.3)
|
||||||
- FBReactNativeSpec (= 0.62.2)
|
- React-Core/RCTNetworkHeaders (= 0.63.3)
|
||||||
- Folly (= 2018.10.22.00)
|
- React-jsi (= 0.63.3)
|
||||||
- RCTTypeSafety (= 0.62.2)
|
- ReactCommon/turbomodule/core (= 0.63.3)
|
||||||
- React-Core/RCTSettingsHeaders (= 0.62.2)
|
- React-RCTSettings (0.63.3):
|
||||||
- ReactCommon/turbomodule/core (= 0.62.2)
|
- FBReactNativeSpec (= 0.63.3)
|
||||||
- React-RCTText (0.62.2):
|
- Folly (= 2020.01.13.00)
|
||||||
- React-Core/RCTTextHeaders (= 0.62.2)
|
- RCTTypeSafety (= 0.63.3)
|
||||||
- React-RCTVibration (0.62.2):
|
- React-Core/RCTSettingsHeaders (= 0.63.3)
|
||||||
- FBReactNativeSpec (= 0.62.2)
|
- React-jsi (= 0.63.3)
|
||||||
- Folly (= 2018.10.22.00)
|
- ReactCommon/turbomodule/core (= 0.63.3)
|
||||||
- React-Core/RCTVibrationHeaders (= 0.62.2)
|
- React-RCTText (0.63.3):
|
||||||
- ReactCommon/turbomodule/core (= 0.62.2)
|
- React-Core/RCTTextHeaders (= 0.63.3)
|
||||||
- ReactCommon/callinvoker (0.62.2):
|
- React-RCTVibration (0.63.3):
|
||||||
|
- FBReactNativeSpec (= 0.63.3)
|
||||||
|
- Folly (= 2020.01.13.00)
|
||||||
|
- React-Core/RCTVibrationHeaders (= 0.63.3)
|
||||||
|
- React-jsi (= 0.63.3)
|
||||||
|
- ReactCommon/turbomodule/core (= 0.63.3)
|
||||||
|
- ReactCommon/turbomodule/core (0.63.3):
|
||||||
- DoubleConversion
|
- DoubleConversion
|
||||||
- Folly (= 2018.10.22.00)
|
- Folly (= 2020.01.13.00)
|
||||||
- glog
|
- glog
|
||||||
- React-cxxreact (= 0.62.2)
|
- React-callinvoker (= 0.63.3)
|
||||||
- ReactCommon/turbomodule/core (0.62.2):
|
- React-Core (= 0.63.3)
|
||||||
- DoubleConversion
|
- React-cxxreact (= 0.63.3)
|
||||||
- Folly (= 2018.10.22.00)
|
- React-jsi (= 0.63.3)
|
||||||
- glog
|
|
||||||
- React-Core (= 0.62.2)
|
|
||||||
- React-cxxreact (= 0.62.2)
|
|
||||||
- React-jsi (= 0.62.2)
|
|
||||||
- ReactCommon/callinvoker (= 0.62.2)
|
|
||||||
- ReactNativePrivacySnapshot (1.0.0):
|
- ReactNativePrivacySnapshot (1.0.0):
|
||||||
- React
|
- React
|
||||||
- RealmJS (6.1.0):
|
- RealmJS (6.1.0):
|
||||||
|
@ -341,28 +345,28 @@ PODS:
|
||||||
- React
|
- React
|
||||||
- RemobileReactNativeQrcodeLocalImage (1.0.4):
|
- RemobileReactNativeQrcodeLocalImage (1.0.4):
|
||||||
- React
|
- React
|
||||||
- RNCAsyncStorage (1.12.0):
|
- RNCAsyncStorage (1.12.1):
|
||||||
- React
|
- React-Core
|
||||||
- RNCClipboard (1.2.3):
|
- RNCClipboard (1.4.0):
|
||||||
- React
|
- React-Core
|
||||||
- RNCMaskedView (0.1.10):
|
- RNCMaskedView (0.1.10):
|
||||||
- React
|
- React
|
||||||
- RNCPushNotificationIOS (1.5.0):
|
- RNCPushNotificationIOS (1.5.0):
|
||||||
- React
|
- React
|
||||||
- RNDefaultPreference (1.4.3):
|
- RNDefaultPreference (1.4.3):
|
||||||
- React
|
- React
|
||||||
- RNDeviceInfo (6.0.3):
|
- RNDeviceInfo (6.2.0):
|
||||||
- React-Core
|
- React-Core
|
||||||
- RNFS (2.16.6):
|
- RNFS (2.16.6):
|
||||||
- React
|
- React
|
||||||
- RNGestureHandler (1.7.0):
|
- RNGestureHandler (1.8.0):
|
||||||
- React
|
- React
|
||||||
- RNHandoff (0.0.3):
|
- RNHandoff (0.0.3):
|
||||||
- React
|
- React
|
||||||
- RNInAppBrowser (3.4.0):
|
- RNInAppBrowser (3.4.0):
|
||||||
- React
|
- React
|
||||||
- RNLocalize (1.4.0):
|
- RNLocalize (1.4.2):
|
||||||
- React
|
- React-Core
|
||||||
- RNQuickAction (0.3.13):
|
- RNQuickAction (0.3.13):
|
||||||
- React
|
- React
|
||||||
- RNRate (1.2.4):
|
- RNRate (1.2.4):
|
||||||
|
@ -375,11 +379,11 @@ PODS:
|
||||||
- React
|
- React
|
||||||
- RNSecureKeyStore (1.0.0):
|
- RNSecureKeyStore (1.0.0):
|
||||||
- React
|
- React
|
||||||
- RNSentry (1.8.0):
|
- RNSentry (1.8.2):
|
||||||
- React
|
- React
|
||||||
- Sentry (~> 5.2.0)
|
- Sentry (~> 5.2.0)
|
||||||
- RNShare (3.7.0):
|
- RNShare (4.0.2):
|
||||||
- React
|
- React-Core
|
||||||
- RNSVG (12.1.0):
|
- RNSVG (12.1.0):
|
||||||
- React
|
- React
|
||||||
- RNVectorIcons (6.6.0):
|
- RNVectorIcons (6.6.0):
|
||||||
|
@ -400,25 +404,25 @@ DEPENDENCIES:
|
||||||
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
|
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
|
||||||
- FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
|
- FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
|
||||||
- FBReactNativeSpec (from `../node_modules/react-native/Libraries/FBReactNativeSpec`)
|
- FBReactNativeSpec (from `../node_modules/react-native/Libraries/FBReactNativeSpec`)
|
||||||
- Flipper (~> 0.37.0)
|
- Flipper (~> 0.54.0)
|
||||||
- Flipper-DoubleConversion (= 1.1.7)
|
- Flipper-DoubleConversion (= 1.1.7)
|
||||||
- Flipper-Folly (~> 2.1)
|
- Flipper-Folly (~> 2.2)
|
||||||
- Flipper-Glog (= 0.3.6)
|
- Flipper-Glog (= 0.3.6)
|
||||||
- Flipper-PeerTalk (~> 0.0.4)
|
- Flipper-PeerTalk (~> 0.0.4)
|
||||||
- Flipper-RSocket (~> 1.0)
|
- Flipper-RSocket (~> 1.1)
|
||||||
- FlipperKit (~> 0.37.0)
|
- FlipperKit (~> 0.54.0)
|
||||||
- FlipperKit/Core (~> 0.37.0)
|
- FlipperKit/Core (~> 0.54.0)
|
||||||
- FlipperKit/CppBridge (~> 0.37.0)
|
- FlipperKit/CppBridge (~> 0.54.0)
|
||||||
- FlipperKit/FBCxxFollyDynamicConvert (~> 0.37.0)
|
- FlipperKit/FBCxxFollyDynamicConvert (~> 0.54.0)
|
||||||
- FlipperKit/FBDefines (~> 0.37.0)
|
- FlipperKit/FBDefines (~> 0.54.0)
|
||||||
- FlipperKit/FKPortForwarding (~> 0.37.0)
|
- FlipperKit/FKPortForwarding (~> 0.54.0)
|
||||||
- FlipperKit/FlipperKitHighlightOverlay (~> 0.37.0)
|
- FlipperKit/FlipperKitHighlightOverlay (~> 0.54.0)
|
||||||
- FlipperKit/FlipperKitLayoutPlugin (~> 0.37.0)
|
- FlipperKit/FlipperKitLayoutPlugin (~> 0.54.0)
|
||||||
- FlipperKit/FlipperKitLayoutTextSearchable (~> 0.37.0)
|
- FlipperKit/FlipperKitLayoutTextSearchable (~> 0.54.0)
|
||||||
- FlipperKit/FlipperKitNetworkPlugin (~> 0.37.0)
|
- FlipperKit/FlipperKitNetworkPlugin (~> 0.54.0)
|
||||||
- FlipperKit/FlipperKitReactPlugin (~> 0.37.0)
|
- FlipperKit/FlipperKitReactPlugin (~> 0.54.0)
|
||||||
- FlipperKit/FlipperKitUserDefaultsPlugin (~> 0.37.0)
|
- FlipperKit/FlipperKitUserDefaultsPlugin (~> 0.54.0)
|
||||||
- FlipperKit/SKIOSNetworkPlugin (~> 0.37.0)
|
- FlipperKit/SKIOSNetworkPlugin (~> 0.54.0)
|
||||||
- Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`)
|
- Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`)
|
||||||
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
|
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
|
||||||
- lottie-ios (from `../node_modules/lottie-ios`)
|
- lottie-ios (from `../node_modules/lottie-ios`)
|
||||||
|
@ -427,6 +431,7 @@ DEPENDENCIES:
|
||||||
- RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`)
|
- RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`)
|
||||||
- RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`)
|
- RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`)
|
||||||
- React (from `../node_modules/react-native/`)
|
- React (from `../node_modules/react-native/`)
|
||||||
|
- React-callinvoker (from `../node_modules/react-native/ReactCommon/callinvoker`)
|
||||||
- React-Core (from `../node_modules/react-native/`)
|
- React-Core (from `../node_modules/react-native/`)
|
||||||
- React-Core/DevSupport (from `../node_modules/react-native/`)
|
- React-Core/DevSupport (from `../node_modules/react-native/`)
|
||||||
- React-Core/RCTWebSocket (from `../node_modules/react-native/`)
|
- React-Core/RCTWebSocket (from `../node_modules/react-native/`)
|
||||||
|
@ -456,7 +461,6 @@ DEPENDENCIES:
|
||||||
- React-RCTSettings (from `../node_modules/react-native/Libraries/Settings`)
|
- React-RCTSettings (from `../node_modules/react-native/Libraries/Settings`)
|
||||||
- React-RCTText (from `../node_modules/react-native/Libraries/Text`)
|
- React-RCTText (from `../node_modules/react-native/Libraries/Text`)
|
||||||
- React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`)
|
- React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`)
|
||||||
- ReactCommon/callinvoker (from `../node_modules/react-native/ReactCommon`)
|
|
||||||
- ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`)
|
- ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`)
|
||||||
- ReactNativePrivacySnapshot (from `../node_modules/react-native-privacy-snapshot`)
|
- ReactNativePrivacySnapshot (from `../node_modules/react-native-privacy-snapshot`)
|
||||||
- RealmJS (from `../node_modules/realm`)
|
- RealmJS (from `../node_modules/realm`)
|
||||||
|
@ -528,6 +532,8 @@ EXTERNAL SOURCES:
|
||||||
:path: "../node_modules/react-native/Libraries/TypeSafety"
|
:path: "../node_modules/react-native/Libraries/TypeSafety"
|
||||||
React:
|
React:
|
||||||
:path: "../node_modules/react-native/"
|
:path: "../node_modules/react-native/"
|
||||||
|
React-callinvoker:
|
||||||
|
:path: "../node_modules/react-native/ReactCommon/callinvoker"
|
||||||
React-Core:
|
React-Core:
|
||||||
:path: "../node_modules/react-native/"
|
:path: "../node_modules/react-native/"
|
||||||
React-CoreModules:
|
React-CoreModules:
|
||||||
|
@ -644,84 +650,85 @@ SPEC CHECKSUMS:
|
||||||
BVLinearGradient: e3aad03778a456d77928f594a649e96995f1c872
|
BVLinearGradient: e3aad03778a456d77928f594a649e96995f1c872
|
||||||
CocoaAsyncSocket: 694058e7c0ed05a9e217d1b3c7ded962f4180845
|
CocoaAsyncSocket: 694058e7c0ed05a9e217d1b3c7ded962f4180845
|
||||||
CocoaLibEvent: 2fab71b8bd46dd33ddb959f7928ec5909f838e3f
|
CocoaLibEvent: 2fab71b8bd46dd33ddb959f7928ec5909f838e3f
|
||||||
DoubleConversion: 5805e889d232975c086db112ece9ed034df7a0b2
|
DoubleConversion: cde416483dac037923206447da6e1454df403714
|
||||||
FBLazyVector: 4aab18c93cd9546e4bfed752b4084585eca8b245
|
FBLazyVector: 878b59e31113e289e275165efbe4b54fa614d43d
|
||||||
FBReactNativeSpec: 5465d51ccfeecb7faa12f9ae0024f2044ce4044e
|
FBReactNativeSpec: 7da9338acfb98d4ef9e5536805a0704572d33c2f
|
||||||
Flipper: 1670db365568191bd123a0c905b834e77ba9e3d3
|
Flipper: be611d4b742d8c87fbae2ca5f44603a02539e365
|
||||||
Flipper-DoubleConversion: 38631e41ef4f9b12861c67d17cb5518d06badc41
|
Flipper-DoubleConversion: 38631e41ef4f9b12861c67d17cb5518d06badc41
|
||||||
Flipper-Folly: c12092ea368353b58e992843a990a3225d4533c3
|
Flipper-Folly: c12092ea368353b58e992843a990a3225d4533c3
|
||||||
Flipper-Glog: 1dfd6abf1e922806c52ceb8701a3599a79a200a6
|
Flipper-Glog: 1dfd6abf1e922806c52ceb8701a3599a79a200a6
|
||||||
Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9
|
Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9
|
||||||
Flipper-RSocket: 64e7431a55835eb953b0bf984ef3b90ae9fdddd7
|
Flipper-RSocket: 64e7431a55835eb953b0bf984ef3b90ae9fdddd7
|
||||||
FlipperKit: afd4259ef9eadeeb2d30250b37d95cb3b6b97a69
|
FlipperKit: ab353d41aea8aae2ea6daaf813e67496642f3d7d
|
||||||
Folly: 30e7936e1c45c08d884aa59369ed951a8e68cf51
|
Folly: b73c3869541e86821df3c387eb0af5f65addfab4
|
||||||
GCDWebServer: 2c156a56c8226e2d5c0c3f208a3621ccffbe3ce4
|
GCDWebServer: 2c156a56c8226e2d5c0c3f208a3621ccffbe3ce4
|
||||||
glog: 1f3da668190260b06b429bb211bfbee5cd790c28
|
glog: 40a13f7840415b9a77023fbcae0f1e6f43192af3
|
||||||
lottie-ios: 48fac6be217c76937e36e340e2d09cf7b10b7f5f
|
lottie-ios: 48fac6be217c76937e36e340e2d09cf7b10b7f5f
|
||||||
lottie-react-native: 1fb4ce21d6ad37dab8343eaff8719df76035bd93
|
lottie-react-native: 1fb4ce21d6ad37dab8343eaff8719df76035bd93
|
||||||
OpenSSL-Universal: 8b48cc0d10c1b2923617dfe5c178aa9ed2689355
|
OpenSSL-Universal: 8b48cc0d10c1b2923617dfe5c178aa9ed2689355
|
||||||
PasscodeAuth: 1cc99b13d8e4de4716d7e2b4069af2f1a9de30b2
|
PasscodeAuth: 1cc99b13d8e4de4716d7e2b4069af2f1a9de30b2
|
||||||
RCTRequired: cec6a34b3ac8a9915c37e7e4ad3aa74726ce4035
|
RCTRequired: 48884c74035a0b5b76dbb7a998bd93bcfc5f2047
|
||||||
RCTTypeSafety: 93006131180074cffa227a1075802c89a49dd4ce
|
RCTTypeSafety: edf4b618033c2f1c5b7bc3d90d8e085ed95ba2ab
|
||||||
React: 29a8b1a02bd764fb7644ef04019270849b9a7ac3
|
React: f36e90f3ceb976546e97df3403e37d226f79d0e3
|
||||||
React-Core: b12bffb3f567fdf99510acb716ef1abd426e0e05
|
React-callinvoker: 18874f621eb96625df7a24a7dc8d6e07391affcd
|
||||||
React-CoreModules: 4a9b87bbe669d6c3173c0132c3328e3b000783d0
|
React-Core: ac3d816b8e3493970153f4aaf0cff18af0bb95e6
|
||||||
React-cxxreact: e65f9c2ba0ac5be946f53548c1aaaee5873a8103
|
React-CoreModules: 4016d3a4e518bcfc4f5a51252b5a05692ca6f0e1
|
||||||
React-jsi: b6dc94a6a12ff98e8877287a0b7620d365201161
|
React-cxxreact: ffc9129013b87cb36cf3f30a86695a3c397b0f99
|
||||||
React-jsiexecutor: 1540d1c01bb493ae3124ed83351b1b6a155db7da
|
React-jsi: df07aa95b39c5be3e41199921509bfa929ed2b9d
|
||||||
React-jsinspector: 512e560d0e985d0e8c479a54a4e5c147a9c83493
|
React-jsiexecutor: b56c03e61c0dd5f5801255f2160a815f4a53d451
|
||||||
|
React-jsinspector: 8e68ffbfe23880d3ee9bafa8be2777f60b25cbe2
|
||||||
react-native-blue-crypto: 23f1558ad3d38d7a2edb7e2f6ed1bc520ed93e56
|
react-native-blue-crypto: 23f1558ad3d38d7a2edb7e2f6ed1bc520ed93e56
|
||||||
react-native-blur: cad4d93b364f91e7b7931b3fa935455487e5c33c
|
react-native-blur: cad4d93b364f91e7b7931b3fa935455487e5c33c
|
||||||
react-native-camera: fc1296181b2d393ea698164869070c96d8625129
|
react-native-camera: fc1296181b2d393ea698164869070c96d8625129
|
||||||
react-native-document-picker: c5752781fbc0c126c627c1549b037c139444a4cf
|
react-native-document-picker: c5752781fbc0c126c627c1549b037c139444a4cf
|
||||||
react-native-fingerprint-scanner: c68136ca57e3704d7bdf5faa554ea535ce15b1d0
|
react-native-fingerprint-scanner: c68136ca57e3704d7bdf5faa554ea535ce15b1d0
|
||||||
react-native-geolocation: cbd9d6bd06bac411eed2671810f454d4908484a8
|
react-native-geolocation: cbd9d6bd06bac411eed2671810f454d4908484a8
|
||||||
react-native-image-picker: a6c3d644751a388b0fc8b56822ff7cbd398a3008
|
react-native-image-picker: 32d1ad2c0024ca36161ae0d5c2117e2d6c441f11
|
||||||
react-native-randombytes: 991545e6eaaf700b4ee384c291ef3d572e0b2ca8
|
react-native-randombytes: 991545e6eaaf700b4ee384c291ef3d572e0b2ca8
|
||||||
react-native-safe-area-context: 01158a92c300895d79dee447e980672dc3fb85a6
|
react-native-safe-area-context: 01158a92c300895d79dee447e980672dc3fb85a6
|
||||||
react-native-slider: b733e17fdd31186707146debf1f04b5d94aa1a93
|
react-native-slider: b733e17fdd31186707146debf1f04b5d94aa1a93
|
||||||
react-native-tcp-socket: 96a4f104cdcc9c6621aafe92937f163d88447c5b
|
react-native-tcp-socket: 96a4f104cdcc9c6621aafe92937f163d88447c5b
|
||||||
react-native-webview: 162c2f2b14555cb524ac0e3b422a9b66ebceefee
|
react-native-webview: c51f73be304c61d359ec3e7c5e4e8f2c977fd360
|
||||||
React-RCTActionSheet: f41ea8a811aac770e0cc6e0ad6b270c644ea8b7c
|
React-RCTActionSheet: 53ea72699698b0b47a6421cb1c8b4ab215a774aa
|
||||||
React-RCTAnimation: 49ab98b1c1ff4445148b72a3d61554138565bad0
|
React-RCTAnimation: 1befece0b5183c22ae01b966f5583f42e69a83c2
|
||||||
React-RCTBlob: a332773f0ebc413a0ce85942a55b064471587a71
|
React-RCTBlob: 0b284339cbe4b15705a05e2313a51c6d8b51fa40
|
||||||
React-RCTImage: e70be9b9c74fe4e42d0005f42cace7981c994ac3
|
React-RCTImage: d1756599ebd4dc2cb19d1682fe67c6b976658387
|
||||||
React-RCTLinking: c1b9739a88d56ecbec23b7f63650e44672ab2ad2
|
React-RCTLinking: 9af0a51c6d6a4dd1674daadafffc6d03033a6d18
|
||||||
React-RCTNetwork: 73138b6f45e5a2768ad93f3d57873c2a18d14b44
|
React-RCTNetwork: 332c83929cc5eae0b3bbca4add1d668e1fc18bda
|
||||||
React-RCTSettings: 6e3738a87e21b39a8cb08d627e68c44acf1e325a
|
React-RCTSettings: d6953772cfd55f2c68ad72b7ef29efc7ec49f773
|
||||||
React-RCTText: fae545b10cfdb3d247c36c56f61a94cfd6dba41d
|
React-RCTText: 65a6de06a7389098ce24340d1d3556015c38f746
|
||||||
React-RCTVibration: 4356114dbcba4ce66991096e51a66e61eda51256
|
React-RCTVibration: 8e9fb25724a0805107fc1acc9075e26f814df454
|
||||||
ReactCommon: ed4e11d27609d571e7eee8b65548efc191116eb3
|
ReactCommon: 4167844018c9ed375cc01a843e9ee564399e53c3
|
||||||
ReactNativePrivacySnapshot: cc295e45dc22810e9ff2c93380d643de20a77015
|
ReactNativePrivacySnapshot: cc295e45dc22810e9ff2c93380d643de20a77015
|
||||||
RealmJS: c8645e0d65b676780f7e6c393d327527a2eb15e8
|
RealmJS: c8645e0d65b676780f7e6c393d327527a2eb15e8
|
||||||
RemobileReactNativeQrcodeLocalImage: 57aadc12896b148fb5e04bc7c6805f3565f5c3fa
|
RemobileReactNativeQrcodeLocalImage: 57aadc12896b148fb5e04bc7c6805f3565f5c3fa
|
||||||
RNCAsyncStorage: 3eea36d9460c5159b592f9ecbe5a77f8aca98006
|
RNCAsyncStorage: cb9a623793918c6699586281f0b51cbc38f046f9
|
||||||
RNCClipboard: 5f3218dcdc28405aa2ae72b78e388f150b826dd3
|
RNCClipboard: ce9b77f2881948e9e04af84bd70262ab37adb1c1
|
||||||
RNCMaskedView: f5c7d14d6847b7b44853f7acb6284c1da30a3459
|
RNCMaskedView: f5c7d14d6847b7b44853f7acb6284c1da30a3459
|
||||||
RNCPushNotificationIOS: 8025ff0b610d7b28d29ddc1b619cd55814362e4c
|
RNCPushNotificationIOS: 8025ff0b610d7b28d29ddc1b619cd55814362e4c
|
||||||
RNDefaultPreference: 21816c0a6f61a2829ccc0cef034392e9b509ee5f
|
RNDefaultPreference: 21816c0a6f61a2829ccc0cef034392e9b509ee5f
|
||||||
RNDeviceInfo: 910ef2129aff229d8ebd1214bc5f0148017b51d1
|
RNDeviceInfo: 980848feea8d74412b16f2e3e8758c8294d63ca2
|
||||||
RNFS: 2bd9eb49dc82fa9676382f0585b992c424cd59df
|
RNFS: 2bd9eb49dc82fa9676382f0585b992c424cd59df
|
||||||
RNGestureHandler: b6b359bb800ae399a9c8b27032bdbf7c18f08a08
|
RNGestureHandler: 7a5833d0f788dbd107fbb913e09aa0c1ff333c39
|
||||||
RNHandoff: d3b0754cca3a6bcd9b25f544f733f7f033ccf5fa
|
RNHandoff: d3b0754cca3a6bcd9b25f544f733f7f033ccf5fa
|
||||||
RNInAppBrowser: 6097fbc6b09051b40a6a9ec22caf7af40b115ec0
|
RNInAppBrowser: 6097fbc6b09051b40a6a9ec22caf7af40b115ec0
|
||||||
RNLocalize: fc27ee5878ce5a3af73873fb2d8e866e0d1e6d84
|
RNLocalize: 4071198b59b461f3b74eebc5fee8c50f13e39e79
|
||||||
RNQuickAction: 6d404a869dc872cde841ad3147416a670d13fa93
|
RNQuickAction: 6d404a869dc872cde841ad3147416a670d13fa93
|
||||||
RNRate: 2b31dad120cd1b78e33c6034808561c386a3dddf
|
RNRate: 2b31dad120cd1b78e33c6034808561c386a3dddf
|
||||||
RNReactNativeHapticFeedback: 22c5ecf474428766c6b148f96f2ff6155cd7225e
|
RNReactNativeHapticFeedback: 22c5ecf474428766c6b148f96f2ff6155cd7225e
|
||||||
RNReanimated: 89f5e0a04d1dd52fbf27e7e7030d8f80a646a3fc
|
RNReanimated: 89f5e0a04d1dd52fbf27e7e7030d8f80a646a3fc
|
||||||
RNScreens: 0e91da98ab26d5d04c7b59a9b6bd694124caf88c
|
RNScreens: 0e91da98ab26d5d04c7b59a9b6bd694124caf88c
|
||||||
RNSecureKeyStore: f1ad870e53806453039f650720d2845c678d89c8
|
RNSecureKeyStore: f1ad870e53806453039f650720d2845c678d89c8
|
||||||
RNSentry: 1e27aeee260eaa833d5c22a4401be47d7b15c4b0
|
RNSentry: cff88174a0b2e0289b2efe5be1abba1f6c54269d
|
||||||
RNShare: a1d5064df7a0ebe778d001869b3f0a124bf0a491
|
RNShare: 7a7277f3c313652422d9de072ac50714dff5e8a4
|
||||||
RNSVG: ce9d996113475209013317e48b05c21ee988d42e
|
RNSVG: ce9d996113475209013317e48b05c21ee988d42e
|
||||||
RNVectorIcons: 0bb4def82230be1333ddaeee9fcba45f0b288ed4
|
RNVectorIcons: 0bb4def82230be1333ddaeee9fcba45f0b288ed4
|
||||||
RNWatch: d56d00be49131ee454bb5a4a574f18506c8949e4
|
RNWatch: d56d00be49131ee454bb5a4a574f18506c8949e4
|
||||||
Sentry: 8fa58a051237554f22507fb483b9a1de0171a2dc
|
Sentry: 8fa58a051237554f22507fb483b9a1de0171a2dc
|
||||||
ToolTipMenu: 4d89d95ddffd7539230bdbe02ee51bbde362e37e
|
ToolTipMenu: 4d89d95ddffd7539230bdbe02ee51bbde362e37e
|
||||||
Yoga: 3ebccbdd559724312790e7742142d062476b698e
|
Yoga: 7d13633d129fd179e01b8953d38d47be90db185a
|
||||||
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
|
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
|
||||||
|
|
||||||
PODFILE CHECKSUM: e9c5efd531ca5ac67a4b743a179eeefb322cf387
|
PODFILE CHECKSUM: 845139ceee01fe141fab93749d578abee11b11c9
|
||||||
|
|
||||||
COCOAPODS: 1.10.0.beta.2
|
COCOAPODS: 1.9.3
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15505" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="M4Y-Lb-cyx">
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097.3" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="M4Y-Lb-cyx">
|
||||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15509"/>
|
<deployment identifier="iOS"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
|
||||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
|
<capability name="Stack View standard spacing" minToolsVersion="9.0"/>
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<scenes>
|
<scenes>
|
||||||
|
@ -15,59 +17,49 @@
|
||||||
<rect key="frame" x="0.0" y="0.0" width="320" height="100"/>
|
<rect key="frame" x="0.0" y="0.0" width="320" height="100"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Bitcoin Price" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="aaf-Pc-Y9i">
|
|
||||||
<rect key="frame" x="16" y="8" width="288" height="21"/>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstAttribute="height" constant="21" id="fON-Nf-oBQ"/>
|
|
||||||
</constraints>
|
|
||||||
<fontDescription key="fontDescription" style="UICTFontTextStyleHeadline"/>
|
|
||||||
<nil key="textColor"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalHuggingPriority="251" text="..." textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bcB-MD-aJf">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalHuggingPriority="251" text="..." textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bcB-MD-aJf">
|
||||||
<rect key="frame" x="104" y="73" width="200" height="15"/>
|
<rect key="frame" x="104" y="73.5" width="200" height="14.5"/>
|
||||||
<fontDescription key="fontDescription" style="UICTFontTextStyleCaption1"/>
|
<fontDescription key="fontDescription" style="UICTFontTextStyleCaption1"/>
|
||||||
<nil key="textColor"/>
|
<nil key="textColor"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Last Updated:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vU4-uK-6ow">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Last Updated:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vU4-uK-6ow">
|
||||||
<rect key="frame" x="16" y="73" width="80" height="15"/>
|
<rect key="frame" x="16" y="73.5" width="80" height="14.5"/>
|
||||||
<fontDescription key="fontDescription" style="UICTFontTextStyleCaption1"/>
|
<fontDescription key="fontDescription" style="UICTFontTextStyleCaption1"/>
|
||||||
<nil key="textColor"/>
|
<nil key="textColor"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="USD" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lkL-gv-1a1">
|
<stackView opaque="NO" contentMode="scaleToFill" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="U8g-sL-Cl4">
|
||||||
<rect key="frame" x="16" y="40" width="35" height="33"/>
|
<rect key="frame" x="16" y="5.5" width="83" height="20.5"/>
|
||||||
|
<subviews>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="USD" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lkL-gv-1a1">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="61" height="20.5"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="35" id="4Nq-zK-gi3"/>
|
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="35" id="4Nq-zK-gi3"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
|
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="17"/>
|
||||||
|
<nil key="textColor"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalHuggingPriority="251" text="..." textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bEQ-e6-Puo">
|
||||||
|
<rect key="frame" x="69" y="0.0" width="14" height="20.5"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
<nil key="textColor"/>
|
<nil key="textColor"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalHuggingPriority="251" text="..." textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bEQ-e6-Puo">
|
</subviews>
|
||||||
<rect key="frame" x="59" y="46.5" width="14" height="20.5"/>
|
</stackView>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
<stackView opaque="NO" contentMode="scaleToFill" spacingType="standard" translatesAutoresizingMaskIntoConstraints="NO" id="2BS-g5-Fog">
|
||||||
<nil key="textColor"/>
|
<rect key="frame" x="16" y="34" width="83" height="33"/>
|
||||||
<nil key="highlightedColor"/>
|
<subviews>
|
||||||
</label>
|
|
||||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="arrow.up" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="eST-DU-WIK">
|
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="arrow.up" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="eST-DU-WIK">
|
||||||
<rect key="frame" x="221" y="47.5" width="17" height="18"/>
|
<rect key="frame" x="0.0" y="1" width="17" height="31"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="width" constant="17" id="gkK-pz-TDJ"/>
|
<constraint firstAttribute="width" relation="lessThanOrEqual" constant="17" id="gkK-pz-TDJ"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
</imageView>
|
</imageView>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="..." textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gm7-vT-KrH" userLabel="...">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="from" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="aqr-Mt-cor">
|
||||||
<rect key="frame" x="290" y="40" width="14" height="33"/>
|
<rect key="frame" x="25" y="0.0" width="36" height="33"/>
|
||||||
<constraints>
|
|
||||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="14" id="KoT-51-551"/>
|
|
||||||
</constraints>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
|
||||||
<nil key="textColor"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="from" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="aqr-Mt-cor">
|
|
||||||
<rect key="frame" x="246" y="40" width="36" height="33"/>
|
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="height" constant="33" id="u9N-WC-Os2"/>
|
<constraint firstAttribute="height" constant="33" id="u9N-WC-Os2"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
|
@ -75,30 +67,30 @@
|
||||||
<nil key="textColor"/>
|
<nil key="textColor"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="..." textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gm7-vT-KrH" userLabel="...">
|
||||||
|
<rect key="frame" x="69" y="0.0" width="14" height="33"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="14" id="KoT-51-551"/>
|
||||||
|
</constraints>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
|
<nil key="textColor"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
</subviews>
|
||||||
|
</stackView>
|
||||||
</subviews>
|
</subviews>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstItem="aqr-Mt-cor" firstAttribute="top" secondItem="aaf-Pc-Y9i" secondAttribute="bottom" constant="11" id="0ca-1C-JqG"/>
|
<constraint firstItem="gm7-vT-KrH" firstAttribute="leading" secondItem="bEQ-e6-Puo" secondAttribute="leading" id="1nN-8G-TB7"/>
|
||||||
<constraint firstItem="ssy-KU-ocm" firstAttribute="trailing" secondItem="aaf-Pc-Y9i" secondAttribute="trailing" constant="16" id="197-jr-Kn5"/>
|
|
||||||
<constraint firstItem="eST-DU-WIK" firstAttribute="centerY" secondItem="aqr-Mt-cor" secondAttribute="centerY" id="2yd-pY-y1Y"/>
|
|
||||||
<constraint firstItem="bcB-MD-aJf" firstAttribute="leading" secondItem="vU4-uK-6ow" secondAttribute="trailing" constant="8" id="5bB-Zv-Yeq"/>
|
<constraint firstItem="bcB-MD-aJf" firstAttribute="leading" secondItem="vU4-uK-6ow" secondAttribute="trailing" constant="8" id="5bB-Zv-Yeq"/>
|
||||||
<constraint firstItem="lkL-gv-1a1" firstAttribute="leading" secondItem="ssy-KU-ocm" secondAttribute="leading" constant="16" id="Bgx-xM-CSS"/>
|
|
||||||
<constraint firstItem="ssy-KU-ocm" firstAttribute="bottom" secondItem="bcB-MD-aJf" secondAttribute="bottom" constant="12" id="EPP-OS-3b6"/>
|
<constraint firstItem="ssy-KU-ocm" firstAttribute="bottom" secondItem="bcB-MD-aJf" secondAttribute="bottom" constant="12" id="EPP-OS-3b6"/>
|
||||||
<constraint firstItem="vU4-uK-6ow" firstAttribute="leading" secondItem="ssy-KU-ocm" secondAttribute="leading" constant="16" id="EkD-jp-arv"/>
|
<constraint firstItem="vU4-uK-6ow" firstAttribute="leading" secondItem="ssy-KU-ocm" secondAttribute="leading" constant="16" id="EkD-jp-arv"/>
|
||||||
<constraint firstItem="gm7-vT-KrH" firstAttribute="centerY" secondItem="aqr-Mt-cor" secondAttribute="centerY" id="Eo2-n1-zbi"/>
|
<constraint firstItem="U8g-sL-Cl4" firstAttribute="leading" secondItem="vU4-uK-6ow" secondAttribute="leading" id="INV-Y4-WjP"/>
|
||||||
|
<constraint firstItem="2BS-g5-Fog" firstAttribute="top" secondItem="U8g-sL-Cl4" secondAttribute="bottom" constant="8" id="JRm-Qe-7EW"/>
|
||||||
<constraint firstItem="ssy-KU-ocm" firstAttribute="bottom" secondItem="vU4-uK-6ow" secondAttribute="bottom" constant="12" id="JSh-ZE-k1H"/>
|
<constraint firstItem="ssy-KU-ocm" firstAttribute="bottom" secondItem="vU4-uK-6ow" secondAttribute="bottom" constant="12" id="JSh-ZE-k1H"/>
|
||||||
<constraint firstItem="bcB-MD-aJf" firstAttribute="centerY" secondItem="vU4-uK-6ow" secondAttribute="centerY" id="MUL-tE-LmX"/>
|
<constraint firstItem="bcB-MD-aJf" firstAttribute="centerY" secondItem="vU4-uK-6ow" secondAttribute="centerY" id="MUL-tE-LmX"/>
|
||||||
<constraint firstItem="bEQ-e6-Puo" firstAttribute="leading" secondItem="lkL-gv-1a1" secondAttribute="trailing" constant="8" id="Ml2-4o-Yqk"/>
|
<constraint firstItem="2BS-g5-Fog" firstAttribute="leading" secondItem="vU4-uK-6ow" secondAttribute="leading" id="ThK-uE-6nD"/>
|
||||||
<constraint firstItem="ssy-KU-ocm" firstAttribute="trailing" secondItem="gm7-vT-KrH" secondAttribute="trailing" constant="16" id="OLV-lQ-T8a"/>
|
<constraint firstItem="vU4-uK-6ow" firstAttribute="top" secondItem="2BS-g5-Fog" secondAttribute="bottom" constant="6.5" id="Wib-ev-GFn"/>
|
||||||
<constraint firstItem="gm7-vT-KrH" firstAttribute="centerY" secondItem="bEQ-e6-Puo" secondAttribute="centerY" id="Rle-PT-j9m"/>
|
|
||||||
<constraint firstItem="gm7-vT-KrH" firstAttribute="leading" secondItem="aqr-Mt-cor" secondAttribute="trailing" constant="8" id="Rtu-ah-AvP"/>
|
|
||||||
<constraint firstItem="gm7-vT-KrH" firstAttribute="firstBaseline" secondItem="aqr-Mt-cor" secondAttribute="firstBaseline" id="YIV-xq-qlw"/>
|
|
||||||
<constraint firstItem="bEQ-e6-Puo" firstAttribute="centerY" secondItem="lkL-gv-1a1" secondAttribute="centerY" id="Ys3-7f-RIc"/>
|
|
||||||
<constraint firstItem="aaf-Pc-Y9i" firstAttribute="leading" secondItem="S3S-Oj-5AN" secondAttribute="leading" constant="16" id="a1b-Yq-aZb"/>
|
|
||||||
<constraint firstItem="aaf-Pc-Y9i" firstAttribute="top" secondItem="S3S-Oj-5AN" secondAttribute="top" constant="8" id="aIo-h1-w4F"/>
|
|
||||||
<constraint firstItem="lkL-gv-1a1" firstAttribute="top" secondItem="aaf-Pc-Y9i" secondAttribute="bottom" constant="11" id="dET-8J-W4K"/>
|
|
||||||
<constraint firstItem="ssy-KU-ocm" firstAttribute="trailing" secondItem="bcB-MD-aJf" secondAttribute="trailing" constant="16" id="kkD-VZ-BAt"/>
|
<constraint firstItem="ssy-KU-ocm" firstAttribute="trailing" secondItem="bcB-MD-aJf" secondAttribute="trailing" constant="16" id="kkD-VZ-BAt"/>
|
||||||
<constraint firstItem="vU4-uK-6ow" firstAttribute="firstBaseline" secondItem="lkL-gv-1a1" secondAttribute="baseline" constant="16" symbolType="layoutAnchor" id="lml-Hc-8Sv"/>
|
|
||||||
<constraint firstItem="aqr-Mt-cor" firstAttribute="leading" secondItem="eST-DU-WIK" secondAttribute="trailing" constant="8" id="t4j-U9-sOm"/>
|
|
||||||
</constraints>
|
</constraints>
|
||||||
<viewLayoutGuide key="safeArea" id="ssy-KU-ocm"/>
|
<viewLayoutGuide key="safeArea" id="ssy-KU-ocm"/>
|
||||||
</view>
|
</view>
|
||||||
|
@ -120,6 +112,6 @@
|
||||||
</scene>
|
</scene>
|
||||||
</scenes>
|
</scenes>
|
||||||
<resources>
|
<resources>
|
||||||
<image name="arrow.up" catalog="system" width="60" height="64"/>
|
<image name="arrow.up" catalog="system" width="120" height="128"/>
|
||||||
</resources>
|
</resources>
|
||||||
</document>
|
</document>
|
||||||
|
|
|
@ -17,9 +17,9 @@
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>5.6.0</string>
|
<string>5.6.2</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1</string>
|
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||||
<key>NSExtension</key>
|
<key>NSExtension</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSExtensionMainStoryboard</key>
|
<key>NSExtensionMainStoryboard</key>
|
||||||
|
|
|
@ -31,12 +31,17 @@ class TodayViewController: UIViewController, NCWidgetProviding {
|
||||||
} else {
|
} else {
|
||||||
setLastPriceOutletsHidden(isHidden: true)
|
setLastPriceOutletsHidden(isHidden: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if #available(iOSApplicationExtension 13.0, *) {
|
||||||
|
} else{
|
||||||
|
self.lastPriceArrowImage.removeFromSuperview()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setLastPriceOutletsHidden(isHidden: Bool) {
|
func setLastPriceOutletsHidden(isHidden: Bool) {
|
||||||
lastPrice.isHidden = isHidden
|
lastPrice.isHidden = isHidden
|
||||||
lastPriceFromLabel.isHidden = isHidden
|
lastPriceFromLabel.isHidden = isHidden
|
||||||
lastPriceArrowImage.isHidden = isHidden
|
lastPriceArrowImage?.isHidden = isHidden
|
||||||
}
|
}
|
||||||
|
|
||||||
func processRateAndLastUpdate(todayStore: TodayDataStore) {
|
func processRateAndLastUpdate(todayStore: TodayDataStore) {
|
||||||
|
|
|
@ -1,40 +1,40 @@
|
||||||
A Bitcoin wallet that allows you to store, send Bitcoin, receive Bitcoin and buy Bitcoin with focus on security and simplicity.
|
Eine Bitcoin-Wallet, die das Speichern, Überweisen, Empfangen und Kaufen von Bitcoin erlaubt und dabei den Fokus auf Sicherheit und Einfachheit legt.
|
||||||
|
|
||||||
On BlueWallet, a bitcoin wallet you own your private keys. A Bitcoin wallet made by Bitcoin users for the community.
|
Auf BlueWallet bist Du im Besitz der privaten Schlüssel. Eine Bitcoin-Wallet, die von Bitcoin-Benutzern für die Gemeinschaft erstellt wurde.
|
||||||
|
|
||||||
You can instantly transact with anyone in the world and transform the financial system right from your pocket.
|
Du kannst sofort mit jedem in der Welt Geschäfte machen und das Finanzsystem direkt aus der Tasche heraus reformieren.
|
||||||
|
|
||||||
Create for free unlimited number of bitcoin wallets or import your existing wallet. It's simple and fast.
|
Erstelle kostenlos eine unbegrenzte Anzahl von Bitcoin-Wallets oder importiere Deine vorhandene Wallet. Es ist einfach und schnell.
|
||||||
|
|
||||||
_____
|
_____
|
||||||
|
|
||||||
Folgendes ist enthalten:
|
Folgendes ist enthalten:
|
||||||
|
|
||||||
|
|
||||||
1 - Security by design
|
1 - Konzeptionsintegrierte Sicherheit
|
||||||
|
|
||||||
Free/Libre Open Source Software
|
Quelloffene Software
|
||||||
MIT licensed, you can build it and run it on your own! Made with ReactNative
|
MIT lizenziert. Sie können es selbst kompilieren und betreiben! Erstellt mit ReactNative.
|
||||||
|
|
||||||
Plausible deniability
|
Plausible Bestreitbarkeit
|
||||||
Password which decrypts fake bitcoin wallets if you are forced to disclose your access
|
Passwort, das falsche Bitcoin-Wallet entschlüsselt, falls Du gezwungen bist, Deinen Zugang preiszugeben
|
||||||
|
|
||||||
Full encryption
|
Vollständige Verschlüsselung
|
||||||
On top of the iOS multi-layer encryption, we encrypt everything with added passwords
|
Zusätzlich zur mehrschichtigen iOS-Verschlüsselung verschlüsseln wir alles mit zusätzlichen Passwörtern
|
||||||
|
|
||||||
Full node
|
Full Node
|
||||||
Connect to your Bitcoin full node through Electrum
|
Verbindung zu Deinem Bitcoin Full Node über Electrum
|
||||||
|
|
||||||
Cold Storage
|
Cold Storage
|
||||||
Connect to your hardware wallet and keep your coins in Cold storage
|
Verbinde Dich mit Deiner Hardware-Wallet und verwahre Deine Coins sicher im Cold Storage
|
||||||
|
|
||||||
2 - Focused on your experience
|
2 - Fokus auf einfacher Bedienbarkeit
|
||||||
|
|
||||||
Be in control
|
Behalte die Kontrolle
|
||||||
Private keys never leave your device.
|
Private Schlüssel verlassen niemals Dein Gerät.
|
||||||
You control your private keys
|
Du kontrollierst Deine privaten Schlüssel
|
||||||
|
|
||||||
Flexible fees
|
Definierbare Transaktionsgebühren
|
||||||
Starting from 1 Satoshi. Defined by you, the user
|
Starting from 1 Satoshi. Defined by you, the user
|
||||||
|
|
||||||
Replace-By-Fee
|
Replace-By-Fee
|
||||||
|
|
|
@ -1,3 +1,24 @@
|
||||||
|
v5.6.1
|
||||||
|
======
|
||||||
|
|
||||||
|
* ADD: payjoin support
|
||||||
|
* FIX: rare crash on startup (electrum server malformed response)
|
||||||
|
* FIX: rare freezes on send screen
|
||||||
|
* FIX: bitcoin price widget content overlap
|
||||||
|
* FIX: biometrics listener release for some devices
|
||||||
|
* FIX: locales pt_BR, pt_PT, ru, sl_SI, ja_JP
|
||||||
|
* FIX: add margin for RTL languages
|
||||||
|
* FIX: Missing (NT) before $ sign
|
||||||
|
|
||||||
|
v.5.6.0
|
||||||
|
=======
|
||||||
|
|
||||||
|
* FIX: some transactions displayed with 0 value
|
||||||
|
* FIX: PSBT with HW wallets flow
|
||||||
|
* FIX: rare crash on watch-only receive button
|
||||||
|
* FIX: RBF cancel style
|
||||||
|
* REF: updated languages sl_SI, de_DE, fi_FI, es_ES
|
||||||
|
|
||||||
v5.5.9
|
v5.5.9
|
||||||
=======
|
=======
|
||||||
|
|
||||||
|
@ -67,33 +88,3 @@ v5.5.7
|
||||||
* FIX: Background had wrong color during loading phase
|
* FIX: Background had wrong color during loading phase
|
||||||
* REF: speeded up large wallets (>3k txs)
|
* REF: speeded up large wallets (>3k txs)
|
||||||
* REF: speedup onchain wallet creation
|
* REF: speedup onchain wallet creation
|
||||||
|
|
||||||
v5.5.6
|
|
||||||
======
|
|
||||||
|
|
||||||
* ADD: Camera Permission authorization view
|
|
||||||
* FIX: recieve button for watch-only wallets
|
|
||||||
* FIX: could not scan animated QR signed psbt
|
|
||||||
* FIX: updated 'fi_FI' language.
|
|
||||||
|
|
||||||
v5.5.5
|
|
||||||
======
|
|
||||||
|
|
||||||
* FIX: scan Cobo vault signed transaction QR
|
|
||||||
|
|
||||||
v5.5.4
|
|
||||||
======
|
|
||||||
|
|
||||||
* ADD: handling push notification open
|
|
||||||
* ADD: View Wallet xPub (Apple Watch)
|
|
||||||
* ADD: COP Fiat
|
|
||||||
* FIX: Invoice were not being sent (Apple Watch)
|
|
||||||
* FIX: Disable some Watch app elements when app is not reachable
|
|
||||||
* FIX: Show loading indicator when processing file or qrcode image
|
|
||||||
* FIX: Button size for large devices
|
|
||||||
* FIX: better handling of electrum disconnect
|
|
||||||
* FIX: disable push notifications in settings
|
|
||||||
* FIX: Font-Color in Bump-Fee Input Field "Custom" is not adapted for dark mode
|
|
||||||
* FIX: QRCode border in LND Backup screen
|
|
||||||
* FIX: Animated QRCode border. Change save path to Downloads folder
|
|
||||||
* FIX: sk_SK language updates
|
|
||||||
|
|
|
@ -293,7 +293,7 @@
|
||||||
"details_to": "Ausgehend",
|
"details_to": "Ausgehend",
|
||||||
"details_transaction_details": "Transaktionsdetails",
|
"details_transaction_details": "Transaktionsdetails",
|
||||||
"enable_hw": "Dieses Wallet nutzt keine Hardware-Wallet. Möchtest Du die Verwendung einer Hardware-Wallet aktivieren?",
|
"enable_hw": "Dieses Wallet nutzt keine Hardware-Wallet. Möchtest Du die Verwendung einer Hardware-Wallet aktivieren?",
|
||||||
"list_conf": "Konf",
|
"list_conf": "Bestätigungen: {number}",
|
||||||
"list_title": "Transaktionen",
|
"list_title": "Transaktionen",
|
||||||
"rbf_explain": "BlueWallet ersetzt diese Transaktion zur Verringerung der Transaktionszeit durch eine mit höherer Gebühr. (RBF - Replace By Fee)",
|
"rbf_explain": "BlueWallet ersetzt diese Transaktion zur Verringerung der Transaktionszeit durch eine mit höherer Gebühr. (RBF - Replace By Fee)",
|
||||||
"rbf_title": "TRX-Gebühr erhöhen (RBF)",
|
"rbf_title": "TRX-Gebühr erhöhen (RBF)",
|
||||||
|
|
28
loc/en.json
28
loc/en.json
|
@ -8,7 +8,10 @@
|
||||||
"of": "{number} of {total}",
|
"of": "{number} of {total}",
|
||||||
"ok": "OK",
|
"ok": "OK",
|
||||||
"storage_is_encrypted": "Your storage is encrypted. Password is required to decrypt it",
|
"storage_is_encrypted": "Your storage is encrypted. Password is required to decrypt it",
|
||||||
"yes": "Yes"
|
"yes": "Yes",
|
||||||
|
"no": "No",
|
||||||
|
"invalid_animated_qr_code_fragment" : "Invalid animated QRCode fragment, please try again",
|
||||||
|
"file_saved": "File ({filePath}) has been saved in your Downloads folder ."
|
||||||
},
|
},
|
||||||
"azteco": {
|
"azteco": {
|
||||||
"codeIs": "Your voucher code is",
|
"codeIs": "Your voucher code is",
|
||||||
|
@ -170,13 +173,13 @@
|
||||||
"details_next": "Next",
|
"details_next": "Next",
|
||||||
"details_no_maximum": "The selected wallet does not support automatic maximum balance calculation. Are you sure to want to select this wallet?",
|
"details_no_maximum": "The selected wallet does not support automatic maximum balance calculation. Are you sure to want to select this wallet?",
|
||||||
"details_no_multiple": "The selected wallet does not support sending Bitcoin to multiple recipients. Are you sure to want to select this wallet?",
|
"details_no_multiple": "The selected wallet does not support sending Bitcoin to multiple recipients. Are you sure to want to select this wallet?",
|
||||||
"details_no_signed_tx": "The selected file does not contain a signed transaction that can be imported.",
|
"details_no_signed_tx": "The selected file does not contain a transaction that can be imported.",
|
||||||
"details_note_placeholder": "note to self",
|
"details_note_placeholder": "note to self",
|
||||||
"details_scan": "Scan",
|
"details_scan": "Scan",
|
||||||
"details_total_exceeds_balance": "The sending amount exceeds the available balance.",
|
"details_total_exceeds_balance": "The sending amount exceeds the available balance.",
|
||||||
"details_wallet_before_tx": "Before creating a transaction, you must first add a Bitcoin wallet.",
|
"details_wallet_before_tx": "Before creating a transaction, you must first add a Bitcoin wallet.",
|
||||||
"details_wallet_selection": "Wallet Selection",
|
"details_wallet_selection": "Wallet Selection",
|
||||||
"dynamic_init": "Initialing",
|
"dynamic_init": "Initializing",
|
||||||
"dynamic_next": "Next",
|
"dynamic_next": "Next",
|
||||||
"dynamic_prev": "Previous",
|
"dynamic_prev": "Previous",
|
||||||
"dynamic_start": "Start",
|
"dynamic_start": "Start",
|
||||||
|
@ -209,7 +212,8 @@
|
||||||
"qr_error_no_qrcode": "The selected image does not contain a QR Code.",
|
"qr_error_no_qrcode": "The selected image does not contain a QR Code.",
|
||||||
"qr_error_no_wallet": "The selected file does not contain a wallet that can be imported.",
|
"qr_error_no_wallet": "The selected file does not contain a wallet that can be imported.",
|
||||||
"success_done": "Done",
|
"success_done": "Done",
|
||||||
"txSaved": "The transaction file ({filePath}) has been saved in your Downloads folder ."
|
"txSaved": "The transaction file ({filePath}) has been saved in your Downloads folder .",
|
||||||
|
"problem_with_psbt": "Problem with PSBT"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"about": "About",
|
"about": "About",
|
||||||
|
@ -293,7 +297,7 @@
|
||||||
"details_to": "Output",
|
"details_to": "Output",
|
||||||
"details_transaction_details": "Transaction details",
|
"details_transaction_details": "Transaction details",
|
||||||
"enable_hw": "This wallet is not being used in conjunction with a hardwarde wallet. Would you like to enable hardware wallet use?",
|
"enable_hw": "This wallet is not being used in conjunction with a hardwarde wallet. Would you like to enable hardware wallet use?",
|
||||||
"list_conf": "conf",
|
"list_conf": "conf: {number}",
|
||||||
"list_title": "transactions",
|
"list_title": "transactions",
|
||||||
"rbf_explain": "We will replace this transaction with the one with a higher fee, so it should be mined faster. This is called RBF - Replace By Fee.",
|
"rbf_explain": "We will replace this transaction with the one with a higher fee, so it should be mined faster. This is called RBF - Replace By Fee.",
|
||||||
"rbf_title": "Bump fee (RBF)",
|
"rbf_title": "Bump fee (RBF)",
|
||||||
|
@ -371,5 +375,19 @@
|
||||||
"select_wallet": "Select Wallet",
|
"select_wallet": "Select Wallet",
|
||||||
"xpub_copiedToClipboard": "Copied to clipboard.",
|
"xpub_copiedToClipboard": "Copied to clipboard.",
|
||||||
"xpub_title": "wallet XPUB"
|
"xpub_title": "wallet XPUB"
|
||||||
|
},
|
||||||
|
"multisig": {
|
||||||
|
"provide_signature": "Provide signature",
|
||||||
|
"vault_key": "Vault key {number}",
|
||||||
|
"fee": "Fee: {number}",
|
||||||
|
"fee_btc": "{number} BTC",
|
||||||
|
"confirm": "Confirm",
|
||||||
|
"header": "Send",
|
||||||
|
"share": "Share",
|
||||||
|
"how_many_signatures_can_bluewallet_make": "how many signatures can bluewallet make",
|
||||||
|
"scan_or_import_file": "Scan or import file",
|
||||||
|
"export_coordination_setup": "export coordination setup",
|
||||||
|
"cosign_this_transaction": "Co-sign this transaction?",
|
||||||
|
"co_sign_transaction": "Co-sign transaction"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
24
loc/es.json
24
loc/es.json
|
@ -94,7 +94,7 @@
|
||||||
"refill_external": "Recargar con una cartera externa",
|
"refill_external": "Recargar con una cartera externa",
|
||||||
"refill_lnd_balance": "Rellenar la cartera de Lightning",
|
"refill_lnd_balance": "Rellenar la cartera de Lightning",
|
||||||
"sameWalletAsInvoiceError": "No puedes pagar una factura con la misma cartera que usaste para crearla.",
|
"sameWalletAsInvoiceError": "No puedes pagar una factura con la misma cartera que usaste para crearla.",
|
||||||
"title": "manejar fondos"
|
"title": "Administrar fondos"
|
||||||
},
|
},
|
||||||
"lndViewInvoice": {
|
"lndViewInvoice": {
|
||||||
"additional_info": "Información adicional",
|
"additional_info": "Información adicional",
|
||||||
|
@ -170,7 +170,7 @@
|
||||||
"details_next": "Siguiente",
|
"details_next": "Siguiente",
|
||||||
"details_no_maximum": "La cartera seleccionada no permite el cálculo automático del saldo máximo. ¿Estás seguro de querer seleccionar esta cartera?",
|
"details_no_maximum": "La cartera seleccionada no permite el cálculo automático del saldo máximo. ¿Estás seguro de querer seleccionar esta cartera?",
|
||||||
"details_no_multiple": "La cartera seleccionada no admite el envío de bitcoin a varios destinatarios. ¿Estás seguro de querer seleccionar esta cartera?",
|
"details_no_multiple": "La cartera seleccionada no admite el envío de bitcoin a varios destinatarios. ¿Estás seguro de querer seleccionar esta cartera?",
|
||||||
"details_no_signed_tx": "El archivo seleccionado no contiene una transacción firmada que se pueda importar.",
|
"details_no_signed_tx": "El archivo seleccionado no contiene una transacción que se pueda importar.",
|
||||||
"details_note_placeholder": "nota personal",
|
"details_note_placeholder": "nota personal",
|
||||||
"details_scan": "Escanear",
|
"details_scan": "Escanear",
|
||||||
"details_total_exceeds_balance": "El monto excede el balance disponible.",
|
"details_total_exceeds_balance": "El monto excede el balance disponible.",
|
||||||
|
@ -184,20 +184,20 @@
|
||||||
"fee_10m": "10m",
|
"fee_10m": "10m",
|
||||||
"fee_1d": "1d",
|
"fee_1d": "1d",
|
||||||
"fee_3h": "3h",
|
"fee_3h": "3h",
|
||||||
"fee_custom": "Custom",
|
"fee_custom": "Personalizada",
|
||||||
"fee_fast": "Fast",
|
"fee_fast": "Rápida",
|
||||||
"fee_medium": "Medium",
|
"fee_medium": "Media",
|
||||||
"fee_replace_min": "The total fee rate (satoshi per byte) you want to pay should be higher than {min} sat/byte",
|
"fee_replace_min": "La comisión (en satoshis por byte) tiene que ser mayor que {min} sat/byte",
|
||||||
"fee_satbyte": "in sat/byte",
|
"fee_satbyte": "en sat/bye",
|
||||||
"fee_slow": "Slow",
|
"fee_slow": "Lenta",
|
||||||
"header": "Enviar",
|
"header": "Enviar",
|
||||||
"input_clear": "Borrar",
|
"input_clear": "Borrar",
|
||||||
"input_done": "Completado",
|
"input_done": "Completado",
|
||||||
"input_paste": "Pegar",
|
"input_paste": "Pegar",
|
||||||
"input_total": "Total:",
|
"input_total": "Total:",
|
||||||
"open_settings": "Abrir configuración",
|
|
||||||
"permission_camera_message": "Necesitamos permiso para usar la cámara",
|
"permission_camera_message": "Necesitamos permiso para usar la cámara",
|
||||||
"permission_camera_title": "Permiso para usar la cámara",
|
"permission_camera_title": "Permiso para usar la cámara",
|
||||||
|
"open_settings": "Abrir configuración",
|
||||||
"permission_storage_later": "Pregúntame luego",
|
"permission_storage_later": "Pregúntame luego",
|
||||||
"permission_storage_message": "BlueWallet necesita permiso para acceder a su almacenamiento para poder guardar esta transacción.",
|
"permission_storage_message": "BlueWallet necesita permiso para acceder a su almacenamiento para poder guardar esta transacción.",
|
||||||
"permission_storage_title": "Permiso para acceder al almacenamiento",
|
"permission_storage_title": "Permiso para acceder al almacenamiento",
|
||||||
|
@ -308,6 +308,7 @@
|
||||||
"add_entropy_provide": "Entropía mediante el lanzamiento de dados",
|
"add_entropy_provide": "Entropía mediante el lanzamiento de dados",
|
||||||
"add_entropy_remain": "{gen} bytes of entropía generada. Los {rem} bytes restantes serán obtenidos del generador de números aleatorios.",
|
"add_entropy_remain": "{gen} bytes of entropía generada. Los {rem} bytes restantes serán obtenidos del generador de números aleatorios.",
|
||||||
"add_import_wallet": "Importar cartera",
|
"add_import_wallet": "Importar cartera",
|
||||||
|
"import_file": "Importar archivo",
|
||||||
"add_lightning": "Lightning",
|
"add_lightning": "Lightning",
|
||||||
"add_lndhub": "Conecta a tu LDNHub",
|
"add_lndhub": "Conecta a tu LDNHub",
|
||||||
"add_lndhub_error": "La dirección proporcionada no es válida para un nodo LNDHub.",
|
"add_lndhub_error": "La dirección proporcionada no es válida para un nodo LNDHub.",
|
||||||
|
@ -341,7 +342,6 @@
|
||||||
"import_do_import": "Importar",
|
"import_do_import": "Importar",
|
||||||
"import_error": "Error al importar. Por favor, asegúrate de que los datos introducidos son correctos.",
|
"import_error": "Error al importar. Por favor, asegúrate de que los datos introducidos son correctos.",
|
||||||
"import_explanation": "Escriba aquí mnemotécnica, clave privada, WIF o cualquier cosa que tenga. BlueWallet hará todo lo posible para adivinar el formato correcto e importar su billetera.",
|
"import_explanation": "Escriba aquí mnemotécnica, clave privada, WIF o cualquier cosa que tenga. BlueWallet hará todo lo posible para adivinar el formato correcto e importar su billetera.",
|
||||||
"import_file": "Importar archivo",
|
|
||||||
"import_imported": "Importado",
|
"import_imported": "Importado",
|
||||||
"import_scan_qr": "Escanear o importar un archivo",
|
"import_scan_qr": "Escanear o importar un archivo",
|
||||||
"import_success": "Tu cartera ha sido importada.",
|
"import_success": "Tu cartera ha sido importada.",
|
||||||
|
@ -353,7 +353,7 @@
|
||||||
"list_empty_txs1": "Tus transacciones aparecerán aquí",
|
"list_empty_txs1": "Tus transacciones aparecerán aquí",
|
||||||
"list_empty_txs1_lightning": "Usa carteras Lighting para tus transacciones diarias. Tienen tasas muy bajas y una velocidad de vértigo.",
|
"list_empty_txs1_lightning": "Usa carteras Lighting para tus transacciones diarias. Tienen tasas muy bajas y una velocidad de vértigo.",
|
||||||
"list_empty_txs2": "Empieza con tu cartera",
|
"list_empty_txs2": "Empieza con tu cartera",
|
||||||
"list_empty_txs2_lightning": "\nPara comenzar a usarlo, toque \"manejar fondos\" y recargue su saldo.",
|
"list_empty_txs2_lightning": "\nPara comenzar a usarlo, toca en \"Administrar fondos\" y añade algo de salgo.",
|
||||||
"list_header": "Una cartera representa un par de llaves, una privada y una que puedes compartir para recibir fondos.",
|
"list_header": "Una cartera representa un par de llaves, una privada y una que puedes compartir para recibir fondos.",
|
||||||
"list_import_error": "Error al intentar importar esta cartera.",
|
"list_import_error": "Error al intentar importar esta cartera.",
|
||||||
"list_import_problem": "Ha ocurrido un problema al importar esta cartera",
|
"list_import_problem": "Ha ocurrido un problema al importar esta cartera",
|
||||||
|
@ -361,6 +361,7 @@
|
||||||
"list_long_choose": "Elegir foto",
|
"list_long_choose": "Elegir foto",
|
||||||
"list_long_clipboard": "Copiar del portapapeles",
|
"list_long_clipboard": "Copiar del portapapeles",
|
||||||
"list_long_scan": "Escanear código QR",
|
"list_long_scan": "Escanear código QR",
|
||||||
|
"take_photo": "Hacer una foto",
|
||||||
"list_tap_here_to_buy": "Tap here to buy Bitcoin",
|
"list_tap_here_to_buy": "Tap here to buy Bitcoin",
|
||||||
"list_title": "Carteras",
|
"list_title": "Carteras",
|
||||||
"list_tryagain": "Inténtalo otra vez",
|
"list_tryagain": "Inténtalo otra vez",
|
||||||
|
@ -368,7 +369,6 @@
|
||||||
"select_no_bitcoin": "No hay carteras de Bitcoin disponibles.",
|
"select_no_bitcoin": "No hay carteras de Bitcoin disponibles.",
|
||||||
"select_no_bitcoin_exp": "Es necesaria una cartera de Bitcoin para recargar una Cartera de Lighting. Por favor, cree o importe una.",
|
"select_no_bitcoin_exp": "Es necesaria una cartera de Bitcoin para recargar una Cartera de Lighting. Por favor, cree o importe una.",
|
||||||
"select_wallet": "Selecciona una cartera",
|
"select_wallet": "Selecciona una cartera",
|
||||||
"take_photo": "Hacer una foto",
|
|
||||||
"xpub_copiedToClipboard": "Copiado a portapapeles.",
|
"xpub_copiedToClipboard": "Copiado a portapapeles.",
|
||||||
"xpub_title": "XPUB de la cartera"
|
"xpub_title": "XPUB de la cartera"
|
||||||
}
|
}
|
||||||
|
|
183
loc/fi_fi.json
183
loc/fi_fi.json
|
@ -7,7 +7,7 @@
|
||||||
"never": "ei koskaan",
|
"never": "ei koskaan",
|
||||||
"of": "{number} / {total}",
|
"of": "{number} / {total}",
|
||||||
"ok": "OK",
|
"ok": "OK",
|
||||||
"storage_is_encrypted": "Tallennustilasi on salattu. Salasana vaaditaan sen purkamiseksi",
|
"storage_is_encrypted": "Tallennustilasi on salattu. Sen purkamiseksi vaaditaan salasana",
|
||||||
"yes": "Kyllä"
|
"yes": "Kyllä"
|
||||||
},
|
},
|
||||||
"azteco": {
|
"azteco": {
|
||||||
|
@ -16,12 +16,12 @@
|
||||||
"errorSomething": "Jotain meni pieleen. Onko tämä kuponki edelleen voimassa?",
|
"errorSomething": "Jotain meni pieleen. Onko tämä kuponki edelleen voimassa?",
|
||||||
"redeem": "Lunastus lompakkoon",
|
"redeem": "Lunastus lompakkoon",
|
||||||
"redeemButton": "Lunastus",
|
"redeemButton": "Lunastus",
|
||||||
"success": "Onnistunut",
|
"success": "Onnistui",
|
||||||
"title": "Lunasta Azte.co kuponki"
|
"title": "Lunasta Azte.co kuponki"
|
||||||
},
|
},
|
||||||
"entropy": {
|
"entropy": {
|
||||||
"save": "Tallenna",
|
"save": "Tallenna",
|
||||||
"title": "Satunnaisuus",
|
"title": "Entropia",
|
||||||
"undo": "Kumoa"
|
"undo": "Kumoa"
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
|
@ -31,7 +31,7 @@
|
||||||
},
|
},
|
||||||
"hodl": {
|
"hodl": {
|
||||||
"are_you_sure_you_want_to_logout": "Haluatko varmasti kirjautua ulos HodlHodl-palvelusta?",
|
"are_you_sure_you_want_to_logout": "Haluatko varmasti kirjautua ulos HodlHodl-palvelusta?",
|
||||||
"cont_address_escrow": "Sulkutili",
|
"cont_address_escrow": "Escrow",
|
||||||
"cont_address_to": "Vastaanottaja",
|
"cont_address_to": "Vastaanottaja",
|
||||||
"cont_buying": "ostaminen",
|
"cont_buying": "ostaminen",
|
||||||
"cont_cancel": "Peruuta sopimus",
|
"cont_cancel": "Peruuta sopimus",
|
||||||
|
@ -40,19 +40,19 @@
|
||||||
"cont_chat": "Avaa keskustelu vastapuolen kanssa",
|
"cont_chat": "Avaa keskustelu vastapuolen kanssa",
|
||||||
"cont_how": "Kuinka maksaa",
|
"cont_how": "Kuinka maksaa",
|
||||||
"cont_no": "Sinulla ei ole meneillään sopimuksia",
|
"cont_no": "Sinulla ei ole meneillään sopimuksia",
|
||||||
"cont_paid": "Merkitse sopimus maksetuksi",
|
"cont_paid": "Merkitse sopimus Maksettu",
|
||||||
"cont_paid_e": "Tee tämä vain, jos lähetit varoja myyjälle sovitulla maksutavalla",
|
"cont_paid_e": "Tee tämä vain, jos lähetit varoja myyjälle sovitulla maksutavalla",
|
||||||
"cont_paid_q": "Haluatko varmasti merkitä tämän sopimuksen maksettuksi?",
|
"cont_paid_q": "Haluatko varmasti merkitä tämän sopimuksen maksetuksi?",
|
||||||
"cont_selling": "myynti",
|
"cont_selling": "myynti",
|
||||||
"cont_st_completed": "Valmista!",
|
"cont_st_completed": "Valmista!",
|
||||||
"cont_st_in_progress_buyer": "Kolikot ovat sulkutilillä, ole hyvä ja maksa myyjälle",
|
"cont_st_in_progress_buyer": "Kolikot ovat escrow:ssa, ole hyvä ja maksa myyjälle",
|
||||||
"cont_st_paid_enought": "Bitcoinit ovat sulkutilillä! Ole hyvä ja maksa myyjälle\nsovitun maksutavan kautta",
|
"cont_st_paid_enought": "Bitcoinit ovat escrow:ssa! Ole hyvä ja maksa myyjälle\nsovitun maksutavan kautta",
|
||||||
"cont_st_paid_waiting": "Odotetaan myyjän vapauttavan kolikot sulkutilistä",
|
"cont_st_paid_waiting": "Odotetaan myyjän vapauttavan kolikot escrow:sta",
|
||||||
"cont_st_waiting": "Odotetaan myyjän tallettavan bitcoineja sulkutiliin...",
|
"cont_st_waiting": "Odotetaan myyjän tallettavan bitcoineja escrow:iin...",
|
||||||
"cont_title": "Minun sopimukset",
|
"cont_title": "Sopimukseni",
|
||||||
"filter_any": "Mikä tahansa",
|
"filter_any": "Mikä tahansa",
|
||||||
"filter_buying": "Ostaminen",
|
"filter_buying": "Ostaminen",
|
||||||
"filter_country_global": "Globaalit tarjoukset",
|
"filter_country_global": "Maailmanlaajuiset tarjoukset",
|
||||||
"filter_country_near": "Lähellä minua",
|
"filter_country_near": "Lähellä minua",
|
||||||
"filter_currency": "Valuutta",
|
"filter_currency": "Valuutta",
|
||||||
"filter_detail": "Tiedot",
|
"filter_detail": "Tiedot",
|
||||||
|
@ -63,17 +63,17 @@
|
||||||
"filter_search": "Etsi",
|
"filter_search": "Etsi",
|
||||||
"filter_selling": "Myynti",
|
"filter_selling": "Myynti",
|
||||||
"item_minmax": "Minimi/Maximi",
|
"item_minmax": "Minimi/Maximi",
|
||||||
"item_nooffers": "Ei tarjouksia. Yritä muuttaa \"Lähellä minua\" globaaleiksi tarjouksiksi!",
|
"item_nooffers": "Ei tarjouksia. Yritä muuttaa \"Lähellä minua\" kansainvälisiksi tarjouksiksi!",
|
||||||
"item_rating": "{rating} kaupat",
|
"item_rating": "{rating} vaihdot",
|
||||||
"item_rating_no": "Ei arvosanaa",
|
"item_rating_no": "Ei luokitusta",
|
||||||
"login": "Kirjaudu sisään",
|
"login": "Kirjaudu sisään",
|
||||||
"mycont": "Sopimukseni",
|
"mycont": "Sopimukseni",
|
||||||
"offer_accept": "Hyväksy tarjous",
|
"offer_accept": "Hyväksy tarjous",
|
||||||
"offer_account_finish": "Näyttää siltä, että et vienyt loppuun tilin luomista HodlHodl-palvelussa, haluatko lopettaa asennuksen nyt?",
|
"offer_account_finish": "Näyttää siltä, että et vienyt loppuun tilin luomista HodlHodl-palvelussa, haluatko päättää asennuksen nyt?",
|
||||||
"offer_choosemethod": "Valitse maksutapa",
|
"offer_choosemethod": "Valitse maksutapa",
|
||||||
"offer_confirmations": "vahvistukset",
|
"offer_confirmations": "vahvistukset",
|
||||||
"offer_minmax": "minimi/maximi",
|
"offer_minmax": "min / max",
|
||||||
"offer_minutes": "minimi",
|
"offer_minutes": "min",
|
||||||
"offer_promt_fiat": "Kuinka paljon {currency} haluat ostaa?",
|
"offer_promt_fiat": "Kuinka paljon {currency} haluat ostaa?",
|
||||||
"offer_promt_fiat_e": "Esimerkiksi 100",
|
"offer_promt_fiat_e": "Esimerkiksi 100",
|
||||||
"offer_window": "ikkuna",
|
"offer_window": "ikkuna",
|
||||||
|
@ -92,7 +92,7 @@
|
||||||
"refill_card": "Täytä pankkikortilla",
|
"refill_card": "Täytä pankkikortilla",
|
||||||
"refill_create": "Jatka luomalla Bitcoin-lompakko, jolla voit täyttää sen.",
|
"refill_create": "Jatka luomalla Bitcoin-lompakko, jolla voit täyttää sen.",
|
||||||
"refill_external": "Täytä ulkoisella lompakolla",
|
"refill_external": "Täytä ulkoisella lompakolla",
|
||||||
"refill_lnd_balance": "Täytä Lightning lompakon saldoa",
|
"refill_lnd_balance": "Täytä Lightning-lompakon saldoa",
|
||||||
"sameWalletAsInvoiceError": "Et voi maksaa laskua samalla lompakolla, jolla se on luotu.",
|
"sameWalletAsInvoiceError": "Et voi maksaa laskua samalla lompakolla, jolla se on luotu.",
|
||||||
"title": "hallinnoi varoja"
|
"title": "hallinnoi varoja"
|
||||||
},
|
},
|
||||||
|
@ -107,12 +107,12 @@
|
||||||
"wasnt_paid_and_expired": "Tätä laskua ei maksettu, ja se on vanhentunut"
|
"wasnt_paid_and_expired": "Tätä laskua ei maksettu, ja se on vanhentunut"
|
||||||
},
|
},
|
||||||
"plausibledeniability": {
|
"plausibledeniability": {
|
||||||
"create_fake_storage": "Luo väärennetty tallennustila",
|
"create_fake_storage": "Luo Salattu tallennustila",
|
||||||
"create_password": "Luo salasana",
|
"create_password": "Luo salasana",
|
||||||
"create_password_explanation": "Väärennetyn tallennustilan salasanan ei tule täsmätä oikean tallennustilan salasanan kanssa",
|
"create_password_explanation": "Väärennetyn tallennustilan salasana ei tule täsmätä oikean tallennustilan salasanan kanssa",
|
||||||
"help": "Joissain tilanteissa, saatat olla pakotettu kertomaan salasanasi. Pitääksesi kolikkosi turvassa, BlueWallet voi luoda toisen salatun tallennustilan, toisella salasanalla. Paineen alla, voit kertoa tämän salasanan kolmannelle osapuolelle. Annettaessa BlueWalletiin, se avaa uuden väärennetyn tallennustilan. Se näyttää oikealta kolmannelle osapuolelle, mutta pitää oikean tallennustilasi kolikkoineen turvassa.",
|
"help": "Joissain tilanteissa, saatat olla pakotettu kertomaan salasanasi. Pitääksesi kolikkosi turvassa, BlueWallet voi luoda toisen salatun tallennustilan, toisella salasanalla. Paineen alla, voit kertoa tämän salasanan kolmannelle osapuolelle. Jos se tulee sisään BlueWallet:iin, se avaa uuden \"väärennetyn\" tallennustilan. Se näyttää oikealta kolmannelle osapuolelle, mutta pitää oikean tallennustilasi kolikkoineen turvassa.",
|
||||||
"help2": "Uusi tallennustila näyttää täysin toimivalta, ja voit säilyttää pieniä summia siellä, jotta se näyttää uskottavalta.",
|
"help2": "Uusi tallennustila näyttää täysin toimivalta, ja voit säilyttää pieniä summia siellä, jotta se näyttää uskottavalta.",
|
||||||
"password_should_not_match": "Väärennetyn tallennustilan salasanan ei tule täsmätä oikean tallennustilan salasanan kanssa",
|
"password_should_not_match": "Salasana on käytössä. Ole hyvä, ja kokeile toista salasanaa.",
|
||||||
"passwords_do_not_match": "Salasanat eivät täsmää, yritä uudelleen",
|
"passwords_do_not_match": "Salasanat eivät täsmää, yritä uudelleen",
|
||||||
"retype_password": "Salasana uudelleen",
|
"retype_password": "Salasana uudelleen",
|
||||||
"success": "Onnistui",
|
"success": "Onnistui",
|
||||||
|
@ -136,21 +136,21 @@
|
||||||
"header": "Vastaanota"
|
"header": "Vastaanota"
|
||||||
},
|
},
|
||||||
"send": {
|
"send": {
|
||||||
"broadcastButton": "LÄHETTÄÄ",
|
"broadcastButton": "LÄHETÄ",
|
||||||
"broadcastError": "virhe",
|
"broadcastError": "virhe",
|
||||||
"broadcastNone": "Syötä siirtotapahtuman tiiviste",
|
"broadcastNone": "Syötä siirtotapahtuman tiiviste",
|
||||||
"broadcastPending": "odottaa",
|
"broadcastPending": "odottaa",
|
||||||
"broadcastSuccess": "onnistunut",
|
"broadcastSuccess": "onnistui",
|
||||||
"confirm_header": "Vahvista",
|
"confirm_header": "Vahvista",
|
||||||
"confirm_sendNow": "Lähetä nyt",
|
"confirm_sendNow": "Lähetä nyt",
|
||||||
"create_amount": "Summa",
|
"create_amount": "Summa",
|
||||||
"create_broadcast": "Kuuluta",
|
"create_broadcast": "Lähetä",
|
||||||
"create_copy": "Kopioi ja lähetä myöhemmin",
|
"create_copy": "Kopioi ja lähetä myöhemmin",
|
||||||
"create_details": "Tiedot",
|
"create_details": "Tarkemmat tiedot",
|
||||||
"create_fee": "Siirtokulu",
|
"create_fee": "Siirtokulu",
|
||||||
"create_memo": "Muistio",
|
"create_memo": "Muistio",
|
||||||
"create_satoshi_per_byte": "Satoshia per tavu",
|
"create_satoshi_per_byte": "Satoshia per tavu",
|
||||||
"create_this_is_hex": "Tämä on siirron hex, allekirjoitettu ja valmis lähetettävksi verkkoon.",
|
"create_this_is_hex": "Tämä on siirtotapahtuman hex, allekirjoitettu ja valmis lähetettävksi verkkoon.",
|
||||||
"create_to": "Vastaanottaja",
|
"create_to": "Vastaanottaja",
|
||||||
"create_tx_size": "TX koko",
|
"create_tx_size": "TX koko",
|
||||||
"create_verify": "Varmenna coinb.in :ssä",
|
"create_verify": "Varmenna coinb.in :ssä",
|
||||||
|
@ -158,19 +158,19 @@
|
||||||
"details_add_rec_rem": "Poista Vastaanottaja",
|
"details_add_rec_rem": "Poista Vastaanottaja",
|
||||||
"details_address": "osoite",
|
"details_address": "osoite",
|
||||||
"details_address_field_is_not_valid": "Osoite ei kelpaa",
|
"details_address_field_is_not_valid": "Osoite ei kelpaa",
|
||||||
"details_adv_fee_bump": "Salli Siirtomaksun Nosto",
|
"details_adv_fee_bump": "Salli Siirtokulun Nosto",
|
||||||
"details_adv_full": "Käytä Koko Saldo",
|
"details_adv_full": "Käytä Koko Saldo",
|
||||||
"details_adv_full_remove": "Muut vastaanottajat poistetaan tästä siirtotapahtumasta.",
|
"details_adv_full_remove": "Muut vastaanottajat poistetaan tästä siirtotapahtumasta.",
|
||||||
"details_adv_full_sure": "Haluatko varmasti käyttää lompakon koko saldoa tähän siirtotapahtumaan?",
|
"details_adv_full_sure": "Haluatko varmasti käyttää lompakon koko saldon tähän siirtotapahtumaan?",
|
||||||
"details_adv_import": "Tuo Siirtotapahtuma",
|
"details_adv_import": "Tuo Siirtotapahtuma",
|
||||||
"details_amount_field_is_not_valid": "Määrä ei kelpaa",
|
"details_amount_field_is_not_valid": "Määrä ei kelpaa",
|
||||||
"details_create": "Luo Lasku",
|
"details_create": "Luo Lasku",
|
||||||
"details_error_decode": "Virhe: Bitcoin-osoitetta ei voi muuntaa",
|
"details_error_decode": "Virhe: Bitcoin-osoitetta ei voida dekoodata",
|
||||||
"details_fee_field_is_not_valid": "Siirtomaksu ei kelpaa",
|
"details_fee_field_is_not_valid": "Siirtokulukenttä ei ole pätevä",
|
||||||
"details_next": "Seuraava",
|
"details_next": "Seuraava",
|
||||||
"details_no_maximum": "Valittu lompakko ei tue automaattista enimmäis-saldolaskelmaa. Haluatko varmasti valita tämän lompakon?",
|
"details_no_maximum": "Valittu lompakko ei tue automaattista enimmäis-saldolaskelmaa. Haluatko varmasti valita tämän lompakon?",
|
||||||
"details_no_multiple": "Valittu lompakko ei tue Bitcoinin lähettämistä useille vastaanottajille. Haluatko varmasti valita tämän lompakon?",
|
"details_no_multiple": "Valittu lompakko ei tue Bitcoinin lähettämistä useille vastaanottajille. Haluatko varmasti valita tämän lompakon?",
|
||||||
"details_no_signed_tx": "Valittu tiedosto ei sisällä allekirjoitettua siirtotapahtumaa, joka voidaan tuoda.",
|
"details_no_signed_tx": "Valittu tiedosto ei sisällä tuotavaa siirtotapahtumaa.",
|
||||||
"details_note_placeholder": "muistiinpano itselle",
|
"details_note_placeholder": "muistiinpano itselle",
|
||||||
"details_scan": "Skannaa",
|
"details_scan": "Skannaa",
|
||||||
"details_total_exceeds_balance": "Lähetettävä summa ylittää katteen",
|
"details_total_exceeds_balance": "Lähetettävä summa ylittää katteen",
|
||||||
|
@ -181,21 +181,31 @@
|
||||||
"dynamic_prev": "Edellinen",
|
"dynamic_prev": "Edellinen",
|
||||||
"dynamic_start": "Aloita",
|
"dynamic_start": "Aloita",
|
||||||
"dynamic_stop": "Lopeta",
|
"dynamic_stop": "Lopeta",
|
||||||
|
"fee_10m": "10 m",
|
||||||
|
"fee_1d": "1 p",
|
||||||
|
"fee_3h": "3 t",
|
||||||
|
"fee_custom": "Mukautettu",
|
||||||
|
"fee_fast": "Nopea",
|
||||||
|
"fee_medium": "Keskitaso",
|
||||||
|
"fee_replace_min": "Maksettavan kokonaiskulun (satoshia tavua kohti) tulisi olla korkeampi kuin {min} sat/tavu",
|
||||||
|
"fee_satbyte": "sat/tavu",
|
||||||
|
"fee_slow": "Hidas",
|
||||||
"header": "Lähetä",
|
"header": "Lähetä",
|
||||||
"input_clear": "Tyhjää",
|
"input_clear": "Tyhjää",
|
||||||
"input_done": "Valmis",
|
"input_done": "Valmis",
|
||||||
"input_paste": "Liitä",
|
"input_paste": "Liitä",
|
||||||
"input_total": "Loppusumma:",
|
"input_total": "Yhteensä:",
|
||||||
"permission_camera_message": "Tarvitsemme lupaasi käyttämään kameraasi",
|
"permission_camera_message": "Tarvitsemme lupasi kameran käyttöön",
|
||||||
"permission_camera_title": "Kameran käyttölupa",
|
"permission_camera_title": "Kameran käyttölupa",
|
||||||
|
"open_settings": "Avaa Asetukset",
|
||||||
"permission_storage_later": "Kysy Minulta Myöhemmin",
|
"permission_storage_later": "Kysy Minulta Myöhemmin",
|
||||||
"permission_storage_message": "BlueWallet tarvitsee luvan käyttää tallennustilaasi tämän siirtotapahtuman tallentamiseksi.",
|
"permission_storage_message": "BlueWallet tarvitsee luvan käyttää tallennustilaasi tämän siirtotapahtuman tallentamiseksi.",
|
||||||
"permission_storage_title": "BlueWallet-Tallennustilan Käyttöoikeus",
|
"permission_storage_title": "BlueWallet Tallennustilan Käyttöoikeus",
|
||||||
"psbt_clipboard": "Kopioi leikepöydälle",
|
"psbt_clipboard": "Kopioi Leikepöydälle",
|
||||||
"psbt_this_is_psbt": "Tämä on osittain allekirjoitettu bitcoin-tapahtuma (PSBT). Ole hyvä ja allekirjoittakaa se hardware lompakolla.",
|
"psbt_this_is_psbt": "Tämä on osittain allekirjoitettu bitcoin-siirtotapahtuma (PSBT). Ole hyvä ja allekirjoita se hardware-lompakolla.",
|
||||||
"psbt_tx_export": "Vie tiedostoon",
|
"psbt_tx_export": "Vie tiedostoon",
|
||||||
"psbt_tx_open": "Avaa allekirjoitettu siirtotapahtuma",
|
"psbt_tx_open": "Avaa Allekirjoitettu Siirtotapahtuma",
|
||||||
"psbt_tx_scan": "Skannaa allekirjoitettu siirtotapahtuma",
|
"psbt_tx_scan": "Skannaa Allekirjoitettu Siirtotapahtuma",
|
||||||
"qr_error_no_qrcode": "Valittu kuva ei sisällä QR-koodia.",
|
"qr_error_no_qrcode": "Valittu kuva ei sisällä QR-koodia.",
|
||||||
"qr_error_no_wallet": "Valittu tiedosto ei sisällä tuotavaa lompakkoa.",
|
"qr_error_no_wallet": "Valittu tiedosto ei sisällä tuotavaa lompakkoa.",
|
||||||
"success_done": "Valmis",
|
"success_done": "Valmis",
|
||||||
|
@ -203,9 +213,9 @@
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"about": "Tietoa",
|
"about": "Tietoa",
|
||||||
"about_awesome": "Rakennettu mahtavasti",
|
"about_awesome": "Mahtavasti rakennettu",
|
||||||
"about_backup": "Varmuuskopioi avaimesi aina!",
|
"about_backup": "Varmuuskopioi aina avaimesi!",
|
||||||
"about_free": "BlueWallet on ilmainen ja avoimen lähdekoodin projekti. Bitcoin-käyttäjien tekemä.",
|
"about_free": "BlueWallet on ilmainen ja avoimen lähdekoodin projekti. Bitcoin käyttäjien tekemä.",
|
||||||
"about_release_notes": "Julkaisutiedot",
|
"about_release_notes": "Julkaisutiedot",
|
||||||
"about_review": "Jätä meille arvostelu",
|
"about_review": "Jätä meille arvostelu",
|
||||||
"about_selftest": "Suorita itsetestaus",
|
"about_selftest": "Suorita itsetestaus",
|
||||||
|
@ -215,61 +225,61 @@
|
||||||
"advanced_options": "Lisäasetukset",
|
"advanced_options": "Lisäasetukset",
|
||||||
"currency": "Valuutta",
|
"currency": "Valuutta",
|
||||||
"currency_source": "Hinnat saadaan CoinDeskistä",
|
"currency_source": "Hinnat saadaan CoinDeskistä",
|
||||||
"default_desc": "Kun toiminto on poistettu käytöstä, BlueWallet avaa valitun lompakon heti käynnistettäessä.",
|
"default_desc": "Kun on pois käytöstä, BlueWallet avaa valitun lompakon heti käynnistettäessä.",
|
||||||
"default_info": "Oletus kohtaan",
|
"default_info": "Oletustiedot",
|
||||||
"default_title": "Käynnistyksessä",
|
"default_title": "Käynnistettäessä",
|
||||||
"default_wallets": "Näytä Kaikki Lompakot",
|
"default_wallets": "Näytä Kaikki Lompakot",
|
||||||
"electrum_connected": "Yhdistetty",
|
"electrum_connected": "Yhdistetty",
|
||||||
"electrum_connected_not": "Ei yhteyttä",
|
"electrum_connected_not": "Ei yhteyttä",
|
||||||
"electrum_error_connect": "Ei voi muodostaa yhteyttä toimitettuun Electrum-palvelimeen",
|
"electrum_error_connect": "Ei voida yhdistää tarjottuun Electrum-palvelimeen",
|
||||||
"electrum_host": "ylläpitäjä, esimerkiksi {example}",
|
"electrum_host": "ylläpitäjä, esimerkiksi {example}",
|
||||||
"electrum_port": "TCP-portti, yleensä {example}",
|
"electrum_port": "TCP-portti, yleensä {example}",
|
||||||
"electrum_port_ssl": "SSL-portti, yleensä {example}",
|
"electrum_port_ssl": "SSL-portti, yleensä {example}",
|
||||||
"electrum_saved": "Muutoksesi on tallennettu onnistuneesti. Uudelleenkäynnistys voi olla tarpeen, jotta muutokset tulevat voimaan.",
|
"electrum_saved": "Muutoksesi on tallennettu onnistuneesti. Uudelleenkäynnistys voi olla tarpeen, jotta muutokset tulevat voimaan.",
|
||||||
"electrum_settings": "Electrum-asetukset",
|
"electrum_settings": "Electrum-Asetukset",
|
||||||
"electrum_settings_explain": "Aseta tyhjäksi käyttääksesi oletusasetusta",
|
"electrum_settings_explain": "Jätä tyhjäksi käyttääksesi oletusasetusta",
|
||||||
"electrum_status": "Status",
|
"electrum_status": "Tila",
|
||||||
"encrypt_decrypt": "Pura tallennustilan salaus",
|
"encrypt_decrypt": "Pura tallennustilan salaus",
|
||||||
"encrypt_decrypt_q": "Haluatko varmasti purkaa tallennustilan salauksen? Tämän avulla lompakoihisi pääsee käsiksi ilman salasanaa.",
|
"encrypt_decrypt_q": "Haluatko varmasti purkaa tallennustilan salauksen? Tämä mahdollistaa lompakkoihisi pääsyn ilman salasanaa.",
|
||||||
"encrypt_del_uninstall": "Poista, jos BlueWallet poistetaan",
|
"encrypt_del_uninstall": "Poista, jos BlueWallet poistetaan",
|
||||||
"encrypt_enc_and_pass": "Salattu ja Salasanalla suojattu",
|
"encrypt_enc_and_pass": "Salattu ja Salasanalla suojattu",
|
||||||
"encrypt_title": "Tietoturva",
|
"encrypt_title": "Tietoturva",
|
||||||
"encrypt_tstorage": "tallennustila",
|
"encrypt_tstorage": "tallennustila",
|
||||||
"encrypt_use": "Käytä {type}",
|
"encrypt_use": "Käytä {type}",
|
||||||
"encrypt_use_expl": "{type} -toimintoa käytetään henkilöllisyytesi vahvistamiseen ennen siirtotapahtuman tekemistä, lompakon lukituksen avaamista, vientiä tai poistamista. {type} ei käytetä salatun tallennustilan lukituksen avaamiseen.",
|
"encrypt_use_expl": "{type} käytetään henkilöllisyytesi vahvistamiseen ennen siirtotapahtuman tekemistä, lompakon lukituksen avaamista, vientiä tai poistamista. {type} ei käytetä salatun tallennustilan lukituksen avaamiseen.",
|
||||||
"general": "Yleinen",
|
"general": "Yleinen",
|
||||||
"general_adv_mode": "Kehittynyt tila",
|
"general_adv_mode": "Kehittynyt tila",
|
||||||
"general_adv_mode_e": "Kun tämä asetus on käytössä, näet lisäasetukset, kuten erilaiset lompakkotyypit, kyvyn määrittää LNDHub-ilmentymän, johon haluat muodostaa yhteyden, ja mukautetun entropian lompakon luomisen aikana.",
|
"general_adv_mode_e": "Kun tämä asetus on käytössä, näet lisäasetukset, kuten erilaiset lompakkotyypit, kyvyn määrittää LNDHub-instanssi, johon haluat muodostaa yhteyden, ja mukautetun entropian lompakon luomisen aikana.",
|
||||||
"general_continuity": "Jatkuvuus",
|
"general_continuity": "Jatkuvuus",
|
||||||
"general_continuity_e": "Kun tämä asetus on käytössä, voit tarkastella valittuja lompakoita ja siirtotapahtumia muilla Apple iCloud -laitteilla.",
|
"general_continuity_e": "Kun tämä asetus on käytössä, voit tarkastella valittuja lompakoita ja siirtotapahtumia muilla Apple iCloud -laitteilla.",
|
||||||
|
"groundcontrol_explanation": "GroundControl on ilmainen avoimen lähdekoodin push-ilmoituspalvelin bitcoin-lompakoille. Voit asentaa oman GroundControl-palvelimen ja laittaa sen URL-osoitteen tähän, jotta et luota BlueWallet-infrastruktuuriin. Jätä tyhjäksi käyttääksesi oletusasetusta",
|
||||||
"header": "asetukset",
|
"header": "asetukset",
|
||||||
"language": "Kieli",
|
"language": "Kieli",
|
||||||
"language_restart": "Kun valitset uuden kielen, muutoksen voimaantulo edellyttää, että BlueWallet käynnistetään uudelleen.",
|
"language_restart": "Kun valitset uuden kielen, muutoksen voimaantulo edellyttää, että BlueWallet käynnistetään uudelleen.",
|
||||||
"lightning_error_lndhub_uri": "Ei kelvollinen LndHub-URI",
|
"lightning_error_lndhub_uri": "LndHub-URI ei kelpaa",
|
||||||
"lightning_saved": "Muutoksesi on tallennettu onnistuneesti",
|
"lightning_saved": "Muutoksesi on tallennettu onnistuneesti",
|
||||||
"lightning_settings": "Lightning asetukset",
|
"lightning_settings": "Lightning-Asetukset",
|
||||||
"lightning_settings_explain": "Yhdistääksesi omaan LND-solmuun, asenna LndHub ja laita sen URL tänne. Jätä tyhjäksi käyttääksesi BlueWalletin LNDHubia (lndhub.io). Muutosten tallentamisen jälkeen luodut lompakot yhdistävät annettuun LNDHubiin.",
|
"lightning_settings_explain": "Yhdistääksesi omaan LND-solmuun, asenna LndHub ja laita sen URL tänne. Jätä tyhjäksi käyttääksesi BlueWalletin LNDHubia (lndhub.io). Muutosten tallentamisen jälkeen luodut lompakot yhdistävät annettuun LNDHubiin.",
|
||||||
"network": "Verkko",
|
"network": "Verkko",
|
||||||
"network_broadcast": "Lähetä siirtotapahtuma",
|
"network_broadcast": "Lähetä siirtotapahtuma",
|
||||||
"network_electrum": "Electrum-palvelin",
|
"network_electrum": "Electrum-palvelin",
|
||||||
|
"not_a_valid_uri": "URI ei kelpaa",
|
||||||
|
"notifications": "Ilmoitukset",
|
||||||
"password": "Salasana",
|
"password": "Salasana",
|
||||||
"password_explain": "Luo salasana, jota käytät tallennustilan salauksen purkamiseen",
|
"password_explain": "Luo salasana, jota käytät tallennustilan salauksen purkamiseen",
|
||||||
"passwords_do_not_match": "Salasanat eivät täsmää",
|
"passwords_do_not_match": "Salasanat eivät täsmää",
|
||||||
"plausible_deniability": "Uskottava kiistettävyys...",
|
"plausible_deniability": "Uskottava kiistettävyys",
|
||||||
"retype_password": "Salasana uudelleen",
|
|
||||||
"notifications": "Ilmoitukset",
|
|
||||||
"save": "Tallenna",
|
|
||||||
"saved": "Tallennettu",
|
|
||||||
"not_a_valid_uri": "Ei kelvollinen URI",
|
|
||||||
"push_notifications": "Push-ilmoitukset",
|
"push_notifications": "Push-ilmoitukset",
|
||||||
"groundcontrol_explanation": "GroundControl on ilmainen avoimen lähdekoodin push-ilmoituspalvelin bitcoin-lompakoille. Voit asentaa oman GroundControl-palvelimen ja laittaa sen URL-osoitteen tähän, jotta et luota BlueWallet-infrastruktuuriin. Jätä tyhjäksi käyttääksesi oletusasetusta"
|
"retype_password": "Salasana uudelleen",
|
||||||
|
"save": "Tallenna",
|
||||||
|
"saved": "Tallennettu"
|
||||||
},
|
},
|
||||||
"transactions": {
|
"transactions": {
|
||||||
"cancel_explain": "Korvaamme tämän siirtotapahtuman sillä, joka maksaa sinulle ja jolla on korkeammat siirtokulut. Tämä peruuttaa siirtotapahtuman tehokkaasti. Tätä kutsutaan RBF - Korvattavissa korkeammalla kululla.",
|
"cancel_explain": "Korvaamme tämän siirtotapahtuman sillä, joka maksaa sinulle ja on korkeammat siirtokulut. Tämä peruuttaa siirtotapahtuman tehokkaasti. Tätä kutsutaan RBF - Replace By Fee - Korvaa korkeammilla kuluilla.",
|
||||||
"cancel_no": "Tämä siirtotapahtuma ei ole vaihdettavissa",
|
"cancel_no": "Tämä siirtotapahtuma ei ole vaihdettavissa",
|
||||||
"cancel_title": "Peruuta tämä siirtotapahtuma (RBF)",
|
"cancel_title": "Peruuta tämä siirtotapahtuma (RBF)",
|
||||||
"cpfp_create": "Luo",
|
"cpfp_create": "Luo",
|
||||||
"cpfp_exp": "Luomme toisen siirtotapahtuman, joka kuluttaa vahvistamattoman siirtotapahtuman. Kokonaiskulu on suurempi kuin alkuperäinen siirtotapahtumakulu, joten sen pitäisi olla louhittu nopeammin. Tätä kutsutaan CPFP - lapsi maksaa vanhemmalle.",
|
"cpfp_exp": "Luomme toisen siirtotapahtuman, joka kuluttaa vahvistamattoman siirtotapahtuman. Kokonaiskulu on suurempi kuin alkuperäinen siirtokulu, joten sen pitäisi olla louhittu nopeammin. Tätä kutsutaan CPFP - Child Pays For Parent - Lapsi Maksaa Vanhemmalle.",
|
||||||
"cpfp_no_bump": "Tämä siirtotapahtuma ei ole nostettavissa",
|
"cpfp_no_bump": "Tämä siirtotapahtuma ei ole nostettavissa",
|
||||||
"cpfp_title": "Nosta siirtokuluja (CPFP)",
|
"cpfp_title": "Nosta siirtokuluja (CPFP)",
|
||||||
"details_block": "Lohkon järjestysnumero",
|
"details_block": "Lohkon järjestysnumero",
|
||||||
|
@ -279,17 +289,17 @@
|
||||||
"details_outputs": "Ulostulot",
|
"details_outputs": "Ulostulot",
|
||||||
"details_received": "Vastaanotettu",
|
"details_received": "Vastaanotettu",
|
||||||
"details_show_in_block_explorer": "Näytä lohkoketjuselaimessa",
|
"details_show_in_block_explorer": "Näytä lohkoketjuselaimessa",
|
||||||
"details_title": "Siirto",
|
"details_title": "Siirtotapahtuma",
|
||||||
"details_to": "Ulostulo",
|
"details_to": "Ulostulo",
|
||||||
"details_transaction_details": "Siirtotapahtuman tiedot",
|
"details_transaction_details": "Siirtotapahtuman tiedot",
|
||||||
"enable_hw": "Tätä lompakkoa ei käytetä yhdessä hardware-lompakon kanssa. Haluatko ottaa hardware-lompakon käyttöön?",
|
"enable_hw": "Tätä lompakkoa ei ole käytetty yhdessä hardware-lompakon kanssa. Haluatko ottaa hardware-lompakon käyttöön?",
|
||||||
"list_conf": "conf",
|
"list_conf": "conf: {number}",
|
||||||
"list_title": "siirrot",
|
"list_title": "siirtotapahtumat",
|
||||||
"transactions_count": "siirtotapahtumien määrä",
|
"rbf_explain": "Korvaamme tämän siirtotapahtuman toisella jossa on korkeammat siirtokulut, joten se pitäisi olla louhittu nopeammin. Tätä kutsutaan RBF - Replace By Fee - Korvattavissa korkeammilla kuluilla.",
|
||||||
"rbf_explain": "Korvaamme tämän siirtotapahtuman toisella jossa on korkeammat siirtokulut, joten se pitäisi olla louhittu nopeammin. Tätä kutsutaan RBF - Korvattavissa korkeammalla kululla.",
|
|
||||||
"rbf_title": "Nosta siirtokuluja (RBF)",
|
"rbf_title": "Nosta siirtokuluja (RBF)",
|
||||||
"status_bump": "Nosta siirtokuluja",
|
"status_bump": "Nosta siirtokuluja",
|
||||||
"status_cancel": "Peruuta siirtotapahtuma"
|
"status_cancel": "Peruuta Siirtotapahtuma",
|
||||||
|
"transactions_count": "siirtotapahtumien määrä"
|
||||||
},
|
},
|
||||||
"wallets": {
|
"wallets": {
|
||||||
"add_bitcoin": "Bitcoin",
|
"add_bitcoin": "Bitcoin",
|
||||||
|
@ -298,8 +308,9 @@
|
||||||
"add_entropy_provide": "Hanki entropia nopanheiton kautta",
|
"add_entropy_provide": "Hanki entropia nopanheiton kautta",
|
||||||
"add_entropy_remain": "{gen} tavua luotua entropiaa. Jäljellä olevat {rem} tavut saadaan Järjestelmän satunnaislukugeneraattorilta.",
|
"add_entropy_remain": "{gen} tavua luotua entropiaa. Jäljellä olevat {rem} tavut saadaan Järjestelmän satunnaislukugeneraattorilta.",
|
||||||
"add_import_wallet": "Tuo lompakko",
|
"add_import_wallet": "Tuo lompakko",
|
||||||
"add_lightning": "Salama",
|
"import_file": "Tuo tiedosto",
|
||||||
"add_lndhub": "Yhdistä LNDHubiisi",
|
"add_lightning": "Lightning",
|
||||||
|
"add_lndhub": "Yhdistä LNDHub:iisi",
|
||||||
"add_lndhub_error": "Annettu solmun osoite ei ole kelvollinen LNDHub-solmu.",
|
"add_lndhub_error": "Annettu solmun osoite ei ole kelvollinen LNDHub-solmu.",
|
||||||
"add_lndhub_placeholder": "solmusi osoite",
|
"add_lndhub_placeholder": "solmusi osoite",
|
||||||
"add_or": "tai",
|
"add_or": "tai",
|
||||||
|
@ -312,14 +323,14 @@
|
||||||
"details_connected_to": "Yhdistetty",
|
"details_connected_to": "Yhdistetty",
|
||||||
"details_del_wb": "Lompakon saldo",
|
"details_del_wb": "Lompakon saldo",
|
||||||
"details_del_wb_err": "Annettu saldo ei vastaa tämän lompakon saldoa. Yritä uudelleen",
|
"details_del_wb_err": "Annettu saldo ei vastaa tämän lompakon saldoa. Yritä uudelleen",
|
||||||
"details_del_wb_q": "Tällä lompakolla on saldo. Ennen kuin jatkat, huomaa, että et voi palauttaa varoja ilman tämän lompakon siemenlauseketta. Syötä lompakkosi saldo {saldo} satoshia välttääksesi tämän lompakon vahingossa tapahtuvan poistamisen.",
|
"details_del_wb_q": "Tällä lompakolla on saldoa. Ennen kuin jatkat, huomaa, että et voi palauttaa varoja ilman tämän lompakon siemenlauseketta. Syötä lompakkosi saldo {balance} satoshia välttääksesi vahingossa tämän lompakon poistamisen.",
|
||||||
"details_delete": "Poista",
|
"details_delete": "Poista",
|
||||||
"details_delete_wallet": "Poista lompakko",
|
"details_delete_wallet": "Poista lompakko",
|
||||||
"details_display": "näkyy lompakkolistassa",
|
"details_display": "näkyy lompakkojen listassa",
|
||||||
"details_export_backup": "Vie / varmuuskopioi",
|
"details_export_backup": "Vie / varmuuskopioi",
|
||||||
"details_marketplace": "Kauppapaikka",
|
"details_marketplace": "Kauppapaikka",
|
||||||
"details_master_fingerprint": "Isäntä sormenjälki",
|
"details_master_fingerprint": "Pää sormenjälki",
|
||||||
"details_no_cancel": "En, peruuta",
|
"details_no_cancel": "Ei, peruuta",
|
||||||
"details_save": "Tallenna",
|
"details_save": "Tallenna",
|
||||||
"details_show_xpub": "Näytä lompakon XPUB",
|
"details_show_xpub": "Näytä lompakon XPUB",
|
||||||
"details_title": "Lompakko",
|
"details_title": "Lompakko",
|
||||||
|
@ -332,26 +343,26 @@
|
||||||
"import_error": "Tuonti epäonnistui. Varmista, että annettu tieto on oikein",
|
"import_error": "Tuonti epäonnistui. Varmista, että annettu tieto on oikein",
|
||||||
"import_explanation": "Kirjoita tähän muistisanasi, yksityinen avain, WIF tai jotain mitä sinulla on. BlueWallet tekee parhaansa arvatakseen oikean muodon ja tuo lompakkosi",
|
"import_explanation": "Kirjoita tähän muistisanasi, yksityinen avain, WIF tai jotain mitä sinulla on. BlueWallet tekee parhaansa arvatakseen oikean muodon ja tuo lompakkosi",
|
||||||
"import_imported": "Tuotu",
|
"import_imported": "Tuotu",
|
||||||
"import_scan_qr": "tai skannaa QR-koodi?",
|
"import_scan_qr": "Skannaa tai tuo tiedosto",
|
||||||
"import_success": "Onnistui",
|
"import_success": "Lompakkosi tuonti onnistui.",
|
||||||
"import_title": "tuo",
|
"import_title": "tuo",
|
||||||
"list_create_a_button": "Lisää nyt",
|
"list_create_a_button": "Lisää nyt",
|
||||||
"list_create_a_wallet": "Lisää lompakko",
|
"list_create_a_wallet": "Lisää lompakko",
|
||||||
"list_create_a_wallet1": "Se on ilmaista ja voit luoda",
|
"list_create_a_wallet1": "Se on ilmaista ja voit luoda",
|
||||||
"list_create_a_wallet2": "niin monta kuin haluat",
|
"list_create_a_wallet2": "niin monta kuin haluat",
|
||||||
"list_empty_txs1": "Siirtosi näkyvät tässä,",
|
"list_empty_txs1": "Siirtotapahtumasi näkyvät tässä,",
|
||||||
"list_empty_txs1_lightning": "Salama-lompakkoa tulisi käyttää päivittäisiin siirtotapahtumiin. Siirtokulut ovat kohtuuttoman halpoja ja nopeus on liekehtivän nopea.",
|
"list_empty_txs1_lightning": "Lightning-lompakkoa tulisi käyttää päivittäisiin siirtotapahtumiin. Siirtokulut ovat kohtuuttoman halpoja ja nopeus on liekehtivän kova.",
|
||||||
"list_empty_txs2": "Aloita lompakostasi",
|
"list_empty_txs2": "Aloita lompakostasi",
|
||||||
"list_empty_txs2_lightning": "Aloita sen käyttäminen napsauttamalla \"hallinnoi varoja\" ja lisää saldoasi.",
|
"list_empty_txs2_lightning": "Aloita sen käyttäminen napsauttamalla \"hallinnoi varoja\" ja lisää saldoasi.\n",
|
||||||
"list_header": "Lompakko edustaa salaista paria (yksityinen avain) ja osoitetta, jonka voit jakaa vastaanottaaksesi kolikoita.",
|
"list_header": "Lompakko edustaa avainparia, yhtä yksityistä ja yhtä, jonka voit jakaa vastaanottaaksesi kolikoita.",
|
||||||
"list_import_error": "Tämän lompakon tuomisessa tapahtui virhe.",
|
"list_import_error": "Tämän lompakon tuomisessa tapahtui virhe.",
|
||||||
"list_import_problem": "Tämän lompakon tuonnissa oli ongelma",
|
"list_import_problem": "Tämän lompakon tuomisessa oli ongelma",
|
||||||
"list_latest_transaction": "viimeisin siirto",
|
"list_latest_transaction": "viimeisin siirto",
|
||||||
"list_long_choose": "Valitse Kuva",
|
"list_long_choose": "Valitse Kuva",
|
||||||
"list_long_clipboard": "Kopio leikepöydältä",
|
"list_long_clipboard": "Kopioi Leikepöydältä",
|
||||||
"list_long_scan": "Skannaa QR-koodi",
|
"list_long_scan": "Skannaa QR-koodi",
|
||||||
"take_photo": "Ota Kuva",
|
"take_photo": "Ota Kuva",
|
||||||
"list_tap_here_to_buy": "Napsauta tästä ostaaksesi Bitcoinia",
|
"list_tap_here_to_buy": "Osta Bitcoinia",
|
||||||
"list_title": "lompakot",
|
"list_title": "lompakot",
|
||||||
"list_tryagain": "Yritä uudelleen",
|
"list_tryagain": "Yritä uudelleen",
|
||||||
"reorder_title": "Järjestele Lompakot",
|
"reorder_title": "Järjestele Lompakot",
|
||||||
|
|
100
loc/he.json
100
loc/he.json
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"_": {
|
"_": {
|
||||||
"bad_password": "סיסמה שגויה, נסו שוב.",
|
"bad_password": "סיסמה שגויה, אנא נסו שוב.",
|
||||||
"cancel": "ביטול",
|
"cancel": "ביטול",
|
||||||
"continue": "המשך",
|
"continue": "המשך",
|
||||||
"enter_password": "הזינו סיסמה",
|
"enter_password": "הכניסו סיסמה",
|
||||||
"never": "אף פעם",
|
"never": "אף פעם",
|
||||||
"of": "{number} של {total}",
|
"of": "{number} מתוך {total}",
|
||||||
"ok": "אוקיי",
|
"ok": "אישור",
|
||||||
"storage_is_encrypted": "האחסון שלך מוצפן, נדרשת סיסמה לפתיחה",
|
"storage_is_encrypted": "האחסון שלך מוצפן, נדרשת סיסמה לפתיחה",
|
||||||
"yes": "כן"
|
"yes": "כן"
|
||||||
},
|
},
|
||||||
|
@ -33,35 +33,35 @@
|
||||||
"are_you_sure_you_want_to_logout": "האם אתם בטוחים שאתם רוצים להתנתק מ- HodlHodl?",
|
"are_you_sure_you_want_to_logout": "האם אתם בטוחים שאתם רוצים להתנתק מ- HodlHodl?",
|
||||||
"cont_address_escrow": "פקדון",
|
"cont_address_escrow": "פקדון",
|
||||||
"cont_address_to": "עבור",
|
"cont_address_to": "עבור",
|
||||||
"cont_buying": "קונה",
|
"cont_buying": "קנייה",
|
||||||
"cont_cancel": "ביטול חוזה",
|
"cont_cancel": "ביטול חוזה",
|
||||||
"cont_cancel_q": "האם אתם בטוחים שאתם רוצים לבטל חוזה זה?",
|
"cont_cancel_q": "האם אתם בטוחים שאתם רוצים לבטל חוזה זה?",
|
||||||
"cont_cancel_y": "כן, בטל חוזה.",
|
"cont_cancel_y": "כן, בטל חוזה.",
|
||||||
"cont_chat": "פתחו שיחה עם הצד השני",
|
"cont_chat": "פתחו שיחה עם הצד השני",
|
||||||
"cont_how": "איך לשלם",
|
"cont_how": "איך לשלם",
|
||||||
"cont_no": "אין לך שום חוזה פעיל.",
|
"cont_no": "אין לך שום חוזה פעיל.",
|
||||||
"cont_paid": "סמן חוזה כשולם",
|
"cont_paid": "סימון חוזה כשולם",
|
||||||
"cont_paid_e": "עשה זאת רק אם שילמת למוכר עם אמצאי התשלום המוסכם",
|
"cont_paid_e": "עשו זאת רק אם שילמתם למוכר עם אמצעי התשלום המוסכם",
|
||||||
"cont_paid_q": "האם אתם בטוחים שאתם רוצים לסמן חוזה זה כשולם?",
|
"cont_paid_q": "האם אתם בטוחים שאתם רוצים לסמן חוזה זה כשולם?",
|
||||||
"cont_selling": "מוכר",
|
"cont_selling": "מכירה",
|
||||||
"cont_st_completed": "הכל בוצע",
|
"cont_st_completed": "הכל בוצע!",
|
||||||
"cont_st_in_progress_buyer": "המטבעות נעולים בפיקדון, אנא שלמו למוכר",
|
"cont_st_in_progress_buyer": "המטבעות נעולים בפיקדון, אנא שלמו למוכר",
|
||||||
"cont_st_paid_enought": "הביטקוין נעול בפיקדון! אנא שלמו למוכר\nבאמצעי התשלום המוסכם",
|
"cont_st_paid_enought": "הביטקוין נעול בפיקדון! אנא שלמו למוכר\nבאמצעי התשלום המוסכם",
|
||||||
"cont_st_paid_waiting": "מחכה לשחרור המטבעות מהפיקדון על-ידי המוכר ",
|
"cont_st_paid_waiting": "מחכה לשחרור המטבעות מהפיקדון על-ידי המוכר ",
|
||||||
"cont_st_waiting": "מחכה להפקדת המטבעות בפיקדון על-ידי המוכר ...",
|
"cont_st_waiting": "מחכה להפקדת המטבעות בפיקדון על-ידי המוכר ...",
|
||||||
"cont_title": "החוזים שלי",
|
"cont_title": "החוזים שלי",
|
||||||
"filter_any": "הכל",
|
"filter_any": "הכל",
|
||||||
"filter_buying": "קונה",
|
"filter_buying": "קנייה",
|
||||||
"filter_country_global": "הצעות גלובליות",
|
"filter_country_global": "הצעות גלובליות",
|
||||||
"filter_country_near": "לידי",
|
"filter_country_near": "לידי",
|
||||||
"filter_currency": "מטבע",
|
"filter_currency": "מטבע",
|
||||||
"filter_detail": "פרטים",
|
"filter_detail": "פרטים",
|
||||||
"filter_filters": "מסננים",
|
"filter_filters": "מסננים",
|
||||||
"filter_iambuying": "אני רוצה לקנות ביטקוין",
|
"filter_iambuying": "אני רוצה לקנות ביטקוין",
|
||||||
"filter_iamselling": "רוצה למכור ביטקוין",
|
"filter_iamselling": "אני רוצה למכור ביטקוין",
|
||||||
"filter_method": "אמצעי תשלום",
|
"filter_method": "אמצעי תשלום",
|
||||||
"filter_search": "חיפוש",
|
"filter_search": "חיפוש",
|
||||||
"filter_selling": "מוכר",
|
"filter_selling": "מכירה",
|
||||||
"item_minmax": "מינימום/מקסימום",
|
"item_minmax": "מינימום/מקסימום",
|
||||||
"item_nooffers": "אין הצעות. נסו לשנות \"לידי\" להצעות גלובליות!",
|
"item_nooffers": "אין הצעות. נסו לשנות \"לידי\" להצעות גלובליות!",
|
||||||
"item_rating": "{rating} החלפות",
|
"item_rating": "{rating} החלפות",
|
||||||
|
@ -113,8 +113,8 @@
|
||||||
"help": "בנסיבות מסוימות, יתכן ותאולצו לחשוף את סיסמת הארנק. כדי לשמור על המטבעות בטוחים, BlueWallet מאפשר ליצור אחסון מוצפן נוסף, עם סיסמה שונה. תחת לחץ, תוכלו לחשוף את סיסמה זו לצד שלישי. אם הסיסמה תוכנס ל- BlueWallet, אחסון 'מזויף' חדש יפתח. מצב זה יראה לגיטימי לצד השלישי, בזמן שהאחסון הראשי ישמר בסודיות עם כשהמטבעות מוגנים.",
|
"help": "בנסיבות מסוימות, יתכן ותאולצו לחשוף את סיסמת הארנק. כדי לשמור על המטבעות בטוחים, BlueWallet מאפשר ליצור אחסון מוצפן נוסף, עם סיסמה שונה. תחת לחץ, תוכלו לחשוף את סיסמה זו לצד שלישי. אם הסיסמה תוכנס ל- BlueWallet, אחסון 'מזויף' חדש יפתח. מצב זה יראה לגיטימי לצד השלישי, בזמן שהאחסון הראשי ישמר בסודיות עם כשהמטבעות מוגנים.",
|
||||||
"help2": "האחסון החדש יתפקד באופן מלא, ותוכלו לאחסן בו סכומים מינימליים כך שיראה יותר מהימן.",
|
"help2": "האחסון החדש יתפקד באופן מלא, ותוכלו לאחסן בו סכומים מינימליים כך שיראה יותר מהימן.",
|
||||||
"password_should_not_match": "הסיסמה כבר בשימוש. אנא נסו סיסמה אחרת.",
|
"password_should_not_match": "הסיסמה כבר בשימוש. אנא נסו סיסמה אחרת.",
|
||||||
"passwords_do_not_match": "סיסמה אינה תואמת, נסו שוב",
|
"passwords_do_not_match": "סיסמאות אינן תואמות, נסו שוב",
|
||||||
"retype_password": "סיסמה בשנית",
|
"retype_password": "הכניסו שוב סיסמה",
|
||||||
"success": "הצלחה",
|
"success": "הצלחה",
|
||||||
"title": "הכחשה סבירה"
|
"title": "הכחשה סבירה"
|
||||||
},
|
},
|
||||||
|
@ -129,16 +129,16 @@
|
||||||
"title": "ארנקך נוצר..."
|
"title": "ארנקך נוצר..."
|
||||||
},
|
},
|
||||||
"receive": {
|
"receive": {
|
||||||
"details_create": "צרו",
|
"details_create": "יצירה",
|
||||||
"details_label": "תיאור",
|
"details_label": "תיאור",
|
||||||
"details_setAmount": "בחר כמות",
|
"details_setAmount": "קבלה עם סכום",
|
||||||
"details_share": "שיתוף",
|
"details_share": "שיתוף",
|
||||||
"header": "קבלה"
|
"header": "קבלה"
|
||||||
},
|
},
|
||||||
"send": {
|
"send": {
|
||||||
"broadcastButton": "שדר",
|
"broadcastButton": "שדר",
|
||||||
"broadcastError": "שגיאה",
|
"broadcastError": "שגיאה",
|
||||||
"broadcastNone": "גיבוב קלט העסקה",
|
"broadcastNone": "קלט גיבוב העברה",
|
||||||
"broadcastPending": "ממתין",
|
"broadcastPending": "ממתין",
|
||||||
"broadcastSuccess": "הצלחה",
|
"broadcastSuccess": "הצלחה",
|
||||||
"confirm_header": "אישור",
|
"confirm_header": "אישור",
|
||||||
|
@ -150,7 +150,7 @@
|
||||||
"create_fee": "עמלה",
|
"create_fee": "עמלה",
|
||||||
"create_memo": "תזכיר",
|
"create_memo": "תזכיר",
|
||||||
"create_satoshi_per_byte": "סאטושי לבייט",
|
"create_satoshi_per_byte": "סאטושי לבייט",
|
||||||
"create_this_is_hex": "זוהי העסקה החתומה שלך, מוכנה לשידור לרשת.",
|
"create_this_is_hex": "זוהי ההעברה שלך, חתומה ומוכנה לשידור לרשת.",
|
||||||
"create_to": "עבור",
|
"create_to": "עבור",
|
||||||
"create_tx_size": "גודל ההעברה",
|
"create_tx_size": "גודל ההעברה",
|
||||||
"create_verify": "אמתו ב- coinb.in",
|
"create_verify": "אמתו ב- coinb.in",
|
||||||
|
@ -158,11 +158,11 @@
|
||||||
"details_add_rec_rem": "הסרת נמען",
|
"details_add_rec_rem": "הסרת נמען",
|
||||||
"details_address": "כתובת",
|
"details_address": "כתובת",
|
||||||
"details_address_field_is_not_valid": "שדה כתובת לא תקין",
|
"details_address_field_is_not_valid": "שדה כתובת לא תקין",
|
||||||
"details_adv_fee_bump": "אפשר העלאת עמלה",
|
"details_adv_fee_bump": "אפשר הקפצת עמלה",
|
||||||
"details_adv_full": "שימוש בכל היתרה",
|
"details_adv_full": "שימוש בכל היתרה",
|
||||||
"details_adv_full_remove": "שאר הנמענים ימחקו מהעברה זו.",
|
"details_adv_full_remove": "שאר הנמענים ימחקו מהעברה זו.",
|
||||||
"details_adv_full_sure": "האם אתם בטוחים שתרצו להשתמש בכל יתרת הארנק בשביל עסקה זאת?",
|
"details_adv_full_sure": "האם אתם בטוחים שתרצו להשתמש בכל יתרת הארנק בשביל העברה זאת?",
|
||||||
"details_adv_import": "יבוא עסקה",
|
"details_adv_import": "יבוא העברה",
|
||||||
"details_amount_field_is_not_valid": "שדה סכום אינו תקין",
|
"details_amount_field_is_not_valid": "שדה סכום אינו תקין",
|
||||||
"details_create": "יצירת קבלה",
|
"details_create": "יצירת קבלה",
|
||||||
"details_error_decode": "שגיאה: לא ניתן לפענח כתובת ביטקוין",
|
"details_error_decode": "שגיאה: לא ניתן לפענח כתובת ביטקוין",
|
||||||
|
@ -170,8 +170,8 @@
|
||||||
"details_next": "הבא",
|
"details_next": "הבא",
|
||||||
"details_no_maximum": "הארנק הנבחר אינו תומך בחישוב יתרה מקסימלית אוטומטי. האם לבחור בארנק זה בכל זאת?",
|
"details_no_maximum": "הארנק הנבחר אינו תומך בחישוב יתרה מקסימלית אוטומטי. האם לבחור בארנק זה בכל זאת?",
|
||||||
"details_no_multiple": "הארנק הנבחר אינו תומך בשליחת ביטקוין לנמענים מרובים. האם לבחור בארנק זה בכל זאת?",
|
"details_no_multiple": "הארנק הנבחר אינו תומך בשליחת ביטקוין לנמענים מרובים. האם לבחור בארנק זה בכל זאת?",
|
||||||
"details_no_signed_tx": "הקובץ הנבחר אינו מכיל העברה חתומה שניתן לייבא.",
|
"details_no_signed_tx": "הקובץ הנבחר אינו מכיל העברה שניתן לייבא.",
|
||||||
"details_note_placeholder": "פתק לעצמך",
|
"details_note_placeholder": "הערה לעצמך",
|
||||||
"details_scan": "סריקה",
|
"details_scan": "סריקה",
|
||||||
"details_total_exceeds_balance": "הסכום לשליחה חורג מהיתרה הזמינה.",
|
"details_total_exceeds_balance": "הסכום לשליחה חורג מהיתרה הזמינה.",
|
||||||
"details_wallet_before_tx": "לפני יצירת העברה, עליך להוסיף ארנק ביטקוין.",
|
"details_wallet_before_tx": "לפני יצירת העברה, עליך להוסיף ארנק ביטקוין.",
|
||||||
|
@ -181,16 +181,16 @@
|
||||||
"dynamic_prev": "הקודם",
|
"dynamic_prev": "הקודם",
|
||||||
"dynamic_start": "התחל",
|
"dynamic_start": "התחל",
|
||||||
"dynamic_stop": "עצור",
|
"dynamic_stop": "עצור",
|
||||||
"fee_10m": "10m",
|
"fee_10m": "10 ד'",
|
||||||
"fee_1d": "1d",
|
"fee_1d": "1 י'",
|
||||||
"fee_3h": "3h",
|
"fee_3h": "3 ש'",
|
||||||
"fee_custom": "Custom",
|
"fee_custom": "אחר",
|
||||||
"fee_fast": "Fast",
|
"fee_fast": "מהיר",
|
||||||
"fee_medium": "Medium",
|
"fee_medium": "בינוני",
|
||||||
"fee_replace_min": "The total fee rate (satoshi per byte) you want to pay should be higher than {min} sat/byte",
|
"fee_replace_min": "סך כל העמלה (סאטושי לבייט) שתרצו לשלם צריך להיות גבוה מ- {min} סאט/בייט",
|
||||||
"fee_satbyte": "in sat/byte",
|
"fee_satbyte": "בסאטושי/בייט",
|
||||||
"fee_slow": "Slow",
|
"fee_slow": "איטי",
|
||||||
"header": "שלח",
|
"header": "שליחה",
|
||||||
"input_clear": "נקה",
|
"input_clear": "נקה",
|
||||||
"input_done": "בוצע",
|
"input_done": "בוצע",
|
||||||
"input_paste": "הדבק",
|
"input_paste": "הדבק",
|
||||||
|
@ -201,11 +201,11 @@
|
||||||
"permission_storage_later": "שאל אותי מאוחר יותר",
|
"permission_storage_later": "שאל אותי מאוחר יותר",
|
||||||
"permission_storage_message": "BlueWallet צריך את הרשאתך לגשת לאחסון שלך כדי לשמור את ההעברה.",
|
"permission_storage_message": "BlueWallet צריך את הרשאתך לגשת לאחסון שלך כדי לשמור את ההעברה.",
|
||||||
"permission_storage_title": "הרשאת גישה לאחסון BlueWallet",
|
"permission_storage_title": "הרשאת גישה לאחסון BlueWallet",
|
||||||
"psbt_clipboard": "העתק ללוח",
|
"psbt_clipboard": "העתקה ללוח",
|
||||||
"psbt_this_is_psbt": "זוהי העברת ביטקוין חתומה חלקית (PSBT). אנא סיימו את תהליך החתימה בארנק החומרה שלכם.",
|
"psbt_this_is_psbt": "זוהי העברת ביטקוין חתומה חלקית (PSBT). אנא סיימו את תהליך החתימה בארנק החומרה שלכם.",
|
||||||
"psbt_tx_export": "יצא לקובץ",
|
"psbt_tx_export": "יצא לקובץ",
|
||||||
"psbt_tx_open": "פתחו עסקה חתומה",
|
"psbt_tx_open": "פתחו העברה חתומה",
|
||||||
"psbt_tx_scan": "סרקו עסקה חתומה",
|
"psbt_tx_scan": "סרקו העברה חתומה",
|
||||||
"qr_error_no_qrcode": "התמונה אינה מכילה קוד QR.",
|
"qr_error_no_qrcode": "התמונה אינה מכילה קוד QR.",
|
||||||
"qr_error_no_wallet": "הקובץ הנבחר אינו מכיל ארנק שניתן לייבא.",
|
"qr_error_no_wallet": "הקובץ הנבחר אינו מכיל ארנק שניתן לייבא.",
|
||||||
"success_done": "בוצע",
|
"success_done": "בוצע",
|
||||||
|
@ -215,7 +215,7 @@
|
||||||
"about": "אודות",
|
"about": "אודות",
|
||||||
"about_awesome": "נבנה בעזרת",
|
"about_awesome": "נבנה בעזרת",
|
||||||
"about_backup": "תמיד גבו את המפתחות שלכם!",
|
"about_backup": "תמיד גבו את המפתחות שלכם!",
|
||||||
"about_free": "BlueWallet הינו פרויקט חופשי בקוד פתוח. נוצר על ידי קהילת ביטקוין.",
|
"about_free": "פרויקט BlueWallet הינו פרויקט חופשי בקוד פתוח. נוצר על ידי קהילת ביטקוין.",
|
||||||
"about_release_notes": "הערות שחרור",
|
"about_release_notes": "הערות שחרור",
|
||||||
"about_review": "השאירו לנו ביקורת",
|
"about_review": "השאירו לנו ביקורת",
|
||||||
"about_selftest": "הרץ בדיקה עצמית",
|
"about_selftest": "הרץ בדיקה עצמית",
|
||||||
|
@ -252,7 +252,7 @@
|
||||||
"general_adv_mode_e": "כאשר מופעל, אפשרויות מתקדמות יוצגו כגון סוגי ארנק שונים, אפשרות להתחבר לצומת LNDHub לפי רצונך ואנטרופיה מותאמת בתהליך יצירת ארנק.",
|
"general_adv_mode_e": "כאשר מופעל, אפשרויות מתקדמות יוצגו כגון סוגי ארנק שונים, אפשרות להתחבר לצומת LNDHub לפי רצונך ואנטרופיה מותאמת בתהליך יצירת ארנק.",
|
||||||
"general_continuity": "המשכיות",
|
"general_continuity": "המשכיות",
|
||||||
"general_continuity_e": "כאשר מופעל, תוכלו לצפות בארנקים והעברות נבחרים, באמצעות מכשירי Apple iCloud מחוברים אחרים.",
|
"general_continuity_e": "כאשר מופעל, תוכלו לצפות בארנקים והעברות נבחרים, באמצעות מכשירי Apple iCloud מחוברים אחרים.",
|
||||||
"groundcontrol_explanation": "GroundControl הינו שרת התראות חופשי בקוד פתוח בשביל ארנקי ביטקוין. באפשרותך להתקין שרת GroundControl אישי ולהכניס את ה- URL שלו כאן, כדי לא להסתמך על התשתית של BlueWallet. השאירו ריק כדי להשתמש בברירת המחדל",
|
"groundcontrol_explanation": "שרת GroundControl הינו שרת התראות חופשי בקוד פתוח בשביל ארנקי ביטקוין. באפשרותך להתקין שרת GroundControl אישי ולהכניס את ה- URL שלו כאן, כדי לא להסתמך על התשתית של BlueWallet. השאירו ריק כדי להשתמש בברירת המחדל",
|
||||||
"header": "הגדרות",
|
"header": "הגדרות",
|
||||||
"language": "שפה",
|
"language": "שפה",
|
||||||
"language_restart": "כאשר בוחרים שפה חדשה, יתכן ותדרש הפעלה מחדש של BlueWallet כדי שהשינוי ייכנס לתוקף.",
|
"language_restart": "כאשר בוחרים שפה חדשה, יתכן ותדרש הפעלה מחדש של BlueWallet כדי שהשינוי ייכנס לתוקף.",
|
||||||
|
@ -261,7 +261,7 @@
|
||||||
"lightning_settings": "הגדרות ברק",
|
"lightning_settings": "הגדרות ברק",
|
||||||
"lightning_settings_explain": "כדי להתחבר לצומת LND אישי, אנא התקינו LndHub והכניסו את כתובת ה- URL שלו כאן בהגדרות. השאירו ריק כדי להשתמש ב- LNDHub של BlueWallet (lndhub.io). ארנקים שנוצרו אחרי שמירת השינויים יתחברו ל- LNDHub שהוגדר.",
|
"lightning_settings_explain": "כדי להתחבר לצומת LND אישי, אנא התקינו LndHub והכניסו את כתובת ה- URL שלו כאן בהגדרות. השאירו ריק כדי להשתמש ב- LNDHub של BlueWallet (lndhub.io). ארנקים שנוצרו אחרי שמירת השינויים יתחברו ל- LNDHub שהוגדר.",
|
||||||
"network": "רשת",
|
"network": "רשת",
|
||||||
"network_broadcast": "שידור עסקה",
|
"network_broadcast": "שידור העברה",
|
||||||
"network_electrum": "שרת אלקטרום",
|
"network_electrum": "שרת אלקטרום",
|
||||||
"not_a_valid_uri": "URI לא תקני",
|
"not_a_valid_uri": "URI לא תקני",
|
||||||
"notifications": "התראות",
|
"notifications": "התראות",
|
||||||
|
@ -270,16 +270,16 @@
|
||||||
"passwords_do_not_match": "סיסמאות לא תואמות",
|
"passwords_do_not_match": "סיסמאות לא תואמות",
|
||||||
"plausible_deniability": "הכחשה סבירה",
|
"plausible_deniability": "הכחשה סבירה",
|
||||||
"push_notifications": "התראות",
|
"push_notifications": "התראות",
|
||||||
"retype_password": "סיסמה בשנית",
|
"retype_password": "הכניסו שוב סיסמה",
|
||||||
"save": "שמירה",
|
"save": "שמירה",
|
||||||
"saved": "נשמר"
|
"saved": "נשמר"
|
||||||
},
|
},
|
||||||
"transactions": {
|
"transactions": {
|
||||||
"cancel_explain": "אנו נחליף את ההעברה הזאת באחת עם עמלה גבוהה יותר. פעולה זאת למעשה מבטלת את העברה. פעולה זאת נקראת RBF - Replace By Fee.",
|
"cancel_explain": "אנו נחליף את ההעברה הזאת באחת עם עמלה גבוהה יותר. פעולה זאת למעשה מבטלת את העברה. פעולה זאת נקראת RBF - Replace By Fee.",
|
||||||
"cancel_no": "העברה זאת אינה ניתנת להחלפה",
|
"cancel_no": "העברה זאת אינה ניתנת להחלפה",
|
||||||
"cancel_title": "בטל עסקה זאת (RBF)",
|
"cancel_title": "בטל העברה זאת (RBF)",
|
||||||
"cpfp_create": "צור",
|
"cpfp_create": "צור",
|
||||||
"cpfp_exp": "אנו ניצור העברה נוספת שתשתמש בעודף שנשאר מההעברה הקודמת שבוצעה. סך כל העמלה יהיה גבוה יותר מעמלת העסקה המקורית, כך שמהירות קבלת האישור אמורה לעלות. פעולה זאת נקראת CPFP - Child Pays For Parent.",
|
"cpfp_exp": "אנו ניצור העברה נוספת שתשתמש בעודף שנשאר מההעברה הקודמת שבוצעה. סך כל העמלה יהיה גבוה יותר מעמלת ההעברה המקורית, כך שמהירות קבלת האישור אמורה לעלות. פעולה זאת נקראת CPFP - Child Pays For Parent.",
|
||||||
"cpfp_no_bump": "עמלת העברה זו אינה ניתנת להעלאה",
|
"cpfp_no_bump": "עמלת העברה זו אינה ניתנת להעלאה",
|
||||||
"cpfp_title": "הקפץ עמלה (CPFP)",
|
"cpfp_title": "הקפץ עמלה (CPFP)",
|
||||||
"details_block": "גובה הבלוק",
|
"details_block": "גובה הבלוק",
|
||||||
|
@ -289,23 +289,23 @@
|
||||||
"details_outputs": "פלטים",
|
"details_outputs": "פלטים",
|
||||||
"details_received": "התקבל",
|
"details_received": "התקבל",
|
||||||
"details_show_in_block_explorer": "צפייה בסייר בלוקים",
|
"details_show_in_block_explorer": "צפייה בסייר בלוקים",
|
||||||
"details_title": "עסקה",
|
"details_title": "העברה",
|
||||||
"details_to": "פלט",
|
"details_to": "פלט",
|
||||||
"details_transaction_details": "פרטי עסקה",
|
"details_transaction_details": "פרטי העברה",
|
||||||
"enable_hw": "ארנק זה אינו נמצא בשימוש בצירוף ארנק חומרה. האם תרצו לאפשר שימוש בארנק חומרה?",
|
"enable_hw": "ארנק זה אינו נמצא בשימוש בצירוף ארנק חומרה. האם תרצו לאפשר שימוש בארנק חומרה?",
|
||||||
"list_conf": "אישורים",
|
"list_conf": "אישורים",
|
||||||
"list_title": "תנועות",
|
"list_title": "תנועות",
|
||||||
"rbf_explain": "אנו נחליף את העברה זו בהעברה עם עמלה גבוהה יותר, כך שמהירות קבלת האישור אמורה לעלות. פעולה זאת נקראת CPFP - Child Pays For Parent.",
|
"rbf_explain": "אנו נחליף את העברה זו בהעברה עם עמלה גבוהה יותר, כך שמהירות קבלת האישור אמורה לעלות. פעולה זאת נקראת CPFP - Child Pays For Parent.",
|
||||||
"rbf_title": "העלאת עמלה (RBF)",
|
"rbf_title": "העלאת עמלה (RBF)",
|
||||||
"status_bump": "העלאת עמלה",
|
"status_bump": "העלאת עמלה",
|
||||||
"status_cancel": "ביטול עסקה",
|
"status_cancel": "ביטול העברה",
|
||||||
"transactions_count": "מספר תנועות"
|
"transactions_count": "מספר תנועות"
|
||||||
},
|
},
|
||||||
"wallets": {
|
"wallets": {
|
||||||
"add_bitcoin": "ביטקוין",
|
"add_bitcoin": "ביטקוין",
|
||||||
"add_create": "יצירה",
|
"add_create": "יצירה",
|
||||||
"add_entropy_generated": "{gen} בייטים של אנתרופיה",
|
"add_entropy_generated": "{gen} בייטים של אנתרופיה",
|
||||||
"add_entropy_provide": "ספקו אנטרופיה בעזרת קוביות ",
|
"add_entropy_provide": "ספקו אנטרופיה על ידי הטלת קוביות ",
|
||||||
"add_entropy_remain": "{gen} בייטים של אנתרופיה. שאר {rem} בייטים יתקבלו ממחולל מספרים רנדומליים של המערכת.",
|
"add_entropy_remain": "{gen} בייטים של אנתרופיה. שאר {rem} בייטים יתקבלו ממחולל מספרים רנדומליים של המערכת.",
|
||||||
"add_import_wallet": "יבוא ארנק",
|
"add_import_wallet": "יבוא ארנק",
|
||||||
"import_file": "יבוא קובץ",
|
"import_file": "יבוא קובץ",
|
||||||
|
@ -341,7 +341,7 @@
|
||||||
"export_title": "יצוא ארנק",
|
"export_title": "יצוא ארנק",
|
||||||
"import_do_import": "יבוא",
|
"import_do_import": "יבוא",
|
||||||
"import_error": "היבוא כשל. אנא וודאו שהמידע שסופק תקין.",
|
"import_error": "היבוא כשל. אנא וודאו שהמידע שסופק תקין.",
|
||||||
"import_explanation": "כתבו כאן את מילות הגיבוי, המפתח הפרטי, WIF או כל דבר אחר שברשותך. BlueWallet ישתדל לנחש את הפורמט הנכון וייבא את ארנק.",
|
"import_explanation": "כתבו כאן את מילות הגיבוי, המפתח הפרטי, WIF או כל דבר אחר שברשותכם. BlueWallet ישתדל לנחש את הפורמט הנכון וייבא את ארנק.",
|
||||||
"import_imported": "יובא",
|
"import_imported": "יובא",
|
||||||
"import_scan_qr": "סריקה או יבוא קובץ",
|
"import_scan_qr": "סריקה או יבוא קובץ",
|
||||||
"import_success": "ארנקך יובא בהצלחה.",
|
"import_success": "ארנקך יובא בהצלחה.",
|
||||||
|
@ -352,14 +352,14 @@
|
||||||
"list_create_a_wallet2": "כמה שתרצו",
|
"list_create_a_wallet2": "כמה שתרצו",
|
||||||
"list_empty_txs1": "התנועות שלך יופיעו פה",
|
"list_empty_txs1": "התנועות שלך יופיעו פה",
|
||||||
"list_empty_txs1_lightning": "ארנק הברק משמש לתשלומים יומיומיים. העברות זולות בצורה לא הגיונית המתבצעות במהירות הבזק.",
|
"list_empty_txs1_lightning": "ארנק הברק משמש לתשלומים יומיומיים. העברות זולות בצורה לא הגיונית המתבצעות במהירות הבזק.",
|
||||||
"list_empty_txs2": "התחילו שימוש בארנק",
|
"list_empty_txs2": "עם תחילת השימוש בארנק",
|
||||||
"list_empty_txs2_lightning": "\nלהתחלת שימוש לחצו על \"ניהול כספים\" ומלאו את היתרה",
|
"list_empty_txs2_lightning": "\nלהתחלת שימוש לחצו על \"ניהול כספים\" ומלאו את היתרה",
|
||||||
"list_header": "ארנק מייצר צמד מפתחות, מפתח פרטי וכתובת שאותה ניתן לשתף כדי לקבל מטבעות.",
|
"list_header": "ארנק מייצר צמד מפתחות, מפתח פרטי וכתובת שאותה ניתן לשתף כדי לקבל מטבעות.",
|
||||||
"list_import_error": "התרחשה שגיאה בניסיון לייבא ארנק זה.",
|
"list_import_error": "התרחשה שגיאה בניסיון לייבא ארנק זה.",
|
||||||
"list_import_problem": "לא ניתן לייבא ארנק הזה",
|
"list_import_problem": "לא ניתן לייבא ארנק הזה",
|
||||||
"list_latest_transaction": "עסקה אחרונה",
|
"list_latest_transaction": "העברה אחרונה",
|
||||||
"list_long_choose": "בחר תמונה",
|
"list_long_choose": "בחר תמונה",
|
||||||
"list_long_clipboard": "העתק מלוח",
|
"list_long_clipboard": "העתקה מלוח",
|
||||||
"list_long_scan": "סריקת קוד QR",
|
"list_long_scan": "סריקת קוד QR",
|
||||||
"take_photo": "צילום תמונה",
|
"take_photo": "צילום תמונה",
|
||||||
"list_tap_here_to_buy": "קנו ביטקוין",
|
"list_tap_here_to_buy": "קנו ביטקוין",
|
||||||
|
|
|
@ -170,7 +170,7 @@
|
||||||
"details_next": "Következő",
|
"details_next": "Következő",
|
||||||
"details_no_maximum": "A kiválasztott tárca nem támogatja az automatikus teljes egyenleg számítást. Biztosan ezt a tárcát választod?",
|
"details_no_maximum": "A kiválasztott tárca nem támogatja az automatikus teljes egyenleg számítást. Biztosan ezt a tárcát választod?",
|
||||||
"details_no_multiple": "A kiválasztott tárca nem támogatja a Bitcoin küldést több kedvezményezettnek. Biztosan ezt a tárcát választod?",
|
"details_no_multiple": "A kiválasztott tárca nem támogatja a Bitcoin küldést több kedvezményezettnek. Biztosan ezt a tárcát választod?",
|
||||||
"details_no_signed_tx": "A kiválasztott fájl nem tartalmaz importálható aláírt tranzakciót.",
|
"details_no_signed_tx": "A kiválasztott fájl nem tartalmaz importálható tranzakciót.",
|
||||||
"details_note_placeholder": "saját megjegyzés",
|
"details_note_placeholder": "saját megjegyzés",
|
||||||
"details_scan": "Szkennelés",
|
"details_scan": "Szkennelés",
|
||||||
"details_total_exceeds_balance": "A megadott összeg nagyobb, mint a tárca elérhető egyenlege",
|
"details_total_exceeds_balance": "A megadott összeg nagyobb, mint a tárca elérhető egyenlege",
|
||||||
|
@ -184,20 +184,20 @@
|
||||||
"fee_10m": "10m",
|
"fee_10m": "10m",
|
||||||
"fee_1d": "1d",
|
"fee_1d": "1d",
|
||||||
"fee_3h": "3h",
|
"fee_3h": "3h",
|
||||||
"fee_custom": "Custom",
|
"fee_custom": "beállított",
|
||||||
"fee_fast": "Fast",
|
"fee_fast": "Gyors",
|
||||||
"fee_medium": "Medium",
|
"fee_medium": "Közepes",
|
||||||
"fee_replace_min": "The total fee rate (satoshi per byte) you want to pay should be higher than {min} sat/byte",
|
"fee_replace_min": "A teljes tranzakciós díj (satoshi / byte) amit fizetsz, magasabb lesz, mint a minimum díj.",
|
||||||
"fee_satbyte": "in sat/byte",
|
"fee_satbyte": "satoshi/byte",
|
||||||
"fee_slow": "Slow",
|
"fee_slow": "Lassú",
|
||||||
"header": "Küldés",
|
"header": "Küldés",
|
||||||
"input_clear": "Törlés",
|
"input_clear": "Törlés",
|
||||||
"input_done": "Kész",
|
"input_done": "Kész",
|
||||||
"input_paste": "beillesztés",
|
"input_paste": "beillesztés",
|
||||||
"input_total": "Összesen:",
|
"input_total": "Összesen:",
|
||||||
"open_settings": "Beállítások megnyitása",
|
|
||||||
"permission_camera_message": "Kamera használat engedélyezése",
|
"permission_camera_message": "Kamera használat engedélyezése",
|
||||||
"permission_camera_title": "Kamera használatának engedélyezése",
|
"permission_camera_title": "Kamera használatának engedélyezése",
|
||||||
|
"open_settings": "Beállítások megnyitása",
|
||||||
"permission_storage_later": "Később",
|
"permission_storage_later": "Később",
|
||||||
"permission_storage_message": "A tranzakció elmentéséhez engedélyezned kell a BlueWallet hozzáférését a háttértárhoz.",
|
"permission_storage_message": "A tranzakció elmentéséhez engedélyezned kell a BlueWallet hozzáférését a háttértárhoz.",
|
||||||
"permission_storage_title": "BlueWallet Tárhely Hozzáférés Engedélyezés",
|
"permission_storage_title": "BlueWallet Tárhely Hozzáférés Engedélyezés",
|
||||||
|
@ -252,6 +252,7 @@
|
||||||
"general_adv_mode_e": "Ha engedélyezve van, további opciók is elérhetőek, mint különböző tárca típusok, Lightning LNDHub beállítások és entrópia beállítások tárca készítésénél. ",
|
"general_adv_mode_e": "Ha engedélyezve van, további opciók is elérhetőek, mint különböző tárca típusok, Lightning LNDHub beállítások és entrópia beállítások tárca készítésénél. ",
|
||||||
"general_continuity": "Folytonosság",
|
"general_continuity": "Folytonosság",
|
||||||
"general_continuity_e": "Ha engedélyezve van, láthatod a kiválasztott tárcákat és tranzakciókat más, csatlakoztatott Apple iCloud eszközökön.",
|
"general_continuity_e": "Ha engedélyezve van, láthatod a kiválasztott tárcákat és tranzakciókat más, csatlakoztatott Apple iCloud eszközökön.",
|
||||||
|
"groundcontrol_explanation": "A GroundControl egy ingyenes, nyílt forráskodú, push üzenetküldő szerver Bitcoin tárcákhoz. Telepítheted a saját GroundControl szerveredet webcímed megadásával, ami függetlet lesz a BlueWallet infrastuktúrától. Hagyd üresen alapértelmezésben.",
|
||||||
"header": "beállítások",
|
"header": "beállítások",
|
||||||
"language": "Nyelv",
|
"language": "Nyelv",
|
||||||
"language_restart": "Új nyelv kiválasztásánal, szügség lehet a BlueWallet újraindítására. ",
|
"language_restart": "Új nyelv kiválasztásánal, szügség lehet a BlueWallet újraindítására. ",
|
||||||
|
@ -307,6 +308,7 @@
|
||||||
"add_entropy_provide": "Entrópia megadása véletlenszerűen ",
|
"add_entropy_provide": "Entrópia megadása véletlenszerűen ",
|
||||||
"add_entropy_remain": "{gen} byte generálva entrópiával. A megmaradt {rem} byte a rendszer véletlenszám generátorával készül.",
|
"add_entropy_remain": "{gen} byte generálva entrópiával. A megmaradt {rem} byte a rendszer véletlenszám generátorával készül.",
|
||||||
"add_import_wallet": "Tárca importálása",
|
"add_import_wallet": "Tárca importálása",
|
||||||
|
"import_file": "fájl importálása",
|
||||||
"add_lightning": "Lightning",
|
"add_lightning": "Lightning",
|
||||||
"add_lndhub": "Kapcsolódj az LNDHub-hoz",
|
"add_lndhub": "Kapcsolódj az LNDHub-hoz",
|
||||||
"add_lndhub_error": "A megadott node cím hibás LBDHub node.",
|
"add_lndhub_error": "A megadott node cím hibás LBDHub node.",
|
||||||
|
@ -340,7 +342,6 @@
|
||||||
"import_do_import": "Importálás",
|
"import_do_import": "Importálás",
|
||||||
"import_error": "Importálás sikertelen. Ellenőrizd, hogy helyes adatokat adtál-e meg.",
|
"import_error": "Importálás sikertelen. Ellenőrizd, hogy helyes adatokat adtál-e meg.",
|
||||||
"import_explanation": "Írd be a kulcsszavaidat, a titkos kulcsodat, WIF-et, vagy bármi mást. A BlueWallet megpróbálja kitalálni a helyes formátumot, és importálja a tárcádat",
|
"import_explanation": "Írd be a kulcsszavaidat, a titkos kulcsodat, WIF-et, vagy bármi mást. A BlueWallet megpróbálja kitalálni a helyes formátumot, és importálja a tárcádat",
|
||||||
"import_file": "fájl importálása",
|
|
||||||
"import_imported": "Importálva",
|
"import_imported": "Importálva",
|
||||||
"import_scan_qr": "vagy QR-kód szkennelése?",
|
"import_scan_qr": "vagy QR-kód szkennelése?",
|
||||||
"import_success": "Sikeres importálás!",
|
"import_success": "Sikeres importálás!",
|
||||||
|
@ -360,6 +361,7 @@
|
||||||
"list_long_choose": "Válassz fényképet",
|
"list_long_choose": "Válassz fényképet",
|
||||||
"list_long_clipboard": "Másolás vágólapról",
|
"list_long_clipboard": "Másolás vágólapról",
|
||||||
"list_long_scan": "QR kód szkennelése",
|
"list_long_scan": "QR kód szkennelése",
|
||||||
|
"take_photo": "Fénykép készítése",
|
||||||
"list_tap_here_to_buy": "Bitcoin vásárláshoz kattints ide",
|
"list_tap_here_to_buy": "Bitcoin vásárláshoz kattints ide",
|
||||||
"list_title": "tárcák",
|
"list_title": "tárcák",
|
||||||
"list_tryagain": "Próbáld újra",
|
"list_tryagain": "Próbáld újra",
|
||||||
|
@ -367,7 +369,6 @@
|
||||||
"select_no_bitcoin": "Jelenleg nincs elérhető Bitcoin tárca.",
|
"select_no_bitcoin": "Jelenleg nincs elérhető Bitcoin tárca.",
|
||||||
"select_no_bitcoin_exp": "A Lightning tárca feltöltéséhez Bitcoin tárcára van szükség. Készíts vagy importálj egy Bitcoin tárcát.",
|
"select_no_bitcoin_exp": "A Lightning tárca feltöltéséhez Bitcoin tárcára van szükség. Készíts vagy importálj egy Bitcoin tárcát.",
|
||||||
"select_wallet": "Válassz tárcát",
|
"select_wallet": "Válassz tárcát",
|
||||||
"take_photo": "Fénykép készítése",
|
|
||||||
"xpub_copiedToClipboard": "Vágólapra másolva",
|
"xpub_copiedToClipboard": "Vágólapra másolva",
|
||||||
"xpub_title": "a tárca XPUB kulcsa"
|
"xpub_title": "a tárca XPUB kulcsa"
|
||||||
}
|
}
|
||||||
|
|
|
@ -170,13 +170,13 @@
|
||||||
"details_next": "Next",
|
"details_next": "Next",
|
||||||
"details_no_maximum": "The selected wallet does not support automatic maximum balance calculation. Are you sure to want to select this wallet?",
|
"details_no_maximum": "The selected wallet does not support automatic maximum balance calculation. Are you sure to want to select this wallet?",
|
||||||
"details_no_multiple": "The selected wallet does not support sending Bitcoin to multiple recipients. Are you sure to want to select this wallet?",
|
"details_no_multiple": "The selected wallet does not support sending Bitcoin to multiple recipients. Are you sure to want to select this wallet?",
|
||||||
"details_no_signed_tx": "The selected file does not contain a signed transaction that can be imported.",
|
"details_no_signed_tx": "The selected file does not contain a transaction that can be imported.",
|
||||||
"details_note_placeholder": "catatan pribadi",
|
"details_note_placeholder": "catatan pribadi",
|
||||||
"details_scan": "Pindai",
|
"details_scan": "Pindai",
|
||||||
"details_total_exceeds_balance": "Jumlah yang dikirim melebihi saldo.",
|
"details_total_exceeds_balance": "Jumlah yang dikirim melebihi saldo.",
|
||||||
"details_wallet_before_tx": "Before creating a transaction, you must first add a Bitcoin wallet.",
|
"details_wallet_before_tx": "Before creating a transaction, you must first add a Bitcoin wallet.",
|
||||||
"details_wallet_selection": "Wallet Selection",
|
"details_wallet_selection": "Wallet Selection",
|
||||||
"dynamic_init": "Initialing",
|
"dynamic_init": "Initializing",
|
||||||
"dynamic_next": "Next",
|
"dynamic_next": "Next",
|
||||||
"dynamic_prev": "Previous",
|
"dynamic_prev": "Previous",
|
||||||
"dynamic_start": "Start",
|
"dynamic_start": "Start",
|
||||||
|
|
262
loc/it.json
262
loc/it.json
|
@ -5,17 +5,107 @@
|
||||||
"continue": "Continua",
|
"continue": "Continua",
|
||||||
"enter_password": "Inserisci password",
|
"enter_password": "Inserisci password",
|
||||||
"never": "mai",
|
"never": "mai",
|
||||||
|
"of": "{number} su {total}",
|
||||||
"ok": "OK",
|
"ok": "OK",
|
||||||
"storage_is_encrypted": "Il tuo archivio è criptato. È necessaria una password per decriptarlo"
|
"storage_is_encrypted": "Il tuo archivio è criptato. È necessaria una password per decriptarlo",
|
||||||
|
"yes": "Sì"
|
||||||
|
},
|
||||||
|
"azteco": {
|
||||||
|
"codeIs": "Your voucher code is",
|
||||||
|
"errorBeforeRefeem": "Before redeeming you must first add a Bitcoin wallet.",
|
||||||
|
"errorSomething": "Something went wrong. Is this voucher still valid?",
|
||||||
|
"redeem": "Redeem to wallet",
|
||||||
|
"redeemButton": "Redeem",
|
||||||
|
"success": "Fatto",
|
||||||
|
"title": "Redeem Azte.co voucher"
|
||||||
|
},
|
||||||
|
"entropy": {
|
||||||
|
"save": "Salva",
|
||||||
|
"title": "Entropy",
|
||||||
|
"undo": "Annulla"
|
||||||
|
},
|
||||||
|
"errors": {
|
||||||
|
"broadcast": "Broadcast failed",
|
||||||
|
"error": "Errore",
|
||||||
|
"network": "Errore di rete"
|
||||||
|
},
|
||||||
|
"hodl": {
|
||||||
|
"are_you_sure_you_want_to_logout": "Are you sure you want to logout from HodlHodl?",
|
||||||
|
"cont_address_escrow": "Escrow",
|
||||||
|
"cont_address_to": "A",
|
||||||
|
"cont_buying": "buying",
|
||||||
|
"cont_cancel": "Cancella contratto",
|
||||||
|
"cont_cancel_q": "Sei sicuro di voler cancellare questo contratto?",
|
||||||
|
"cont_cancel_y": "Sì, cancella il contratto",
|
||||||
|
"cont_chat": "Open chat with counterparty",
|
||||||
|
"cont_how": "Come pagare",
|
||||||
|
"cont_no": "You don't have any contracts in progress",
|
||||||
|
"cont_paid": "Mark contract as Paid",
|
||||||
|
"cont_paid_e": "Do this only if you sent funds to the seller via agreed payment method",
|
||||||
|
"cont_paid_q": "Are you sure you want to mark this contract as paid?",
|
||||||
|
"cont_selling": "selling",
|
||||||
|
"cont_st_completed": "All done!",
|
||||||
|
"cont_st_in_progress_buyer": "Coins are in escrow, please pay seller",
|
||||||
|
"cont_st_paid_enought": "Bitcoins are in escrow! Please pay seller\nvia agreed payment method",
|
||||||
|
"cont_st_paid_waiting": "Waiting for seller to release coins from escrow",
|
||||||
|
"cont_st_waiting": "Waiting for seller to deposit bitcoins to escrow...",
|
||||||
|
"cont_title": "I miei contratti",
|
||||||
|
"filter_any": "Any",
|
||||||
|
"filter_buying": "Buying",
|
||||||
|
"filter_country_global": "Global offers",
|
||||||
|
"filter_country_near": "Vicino a me",
|
||||||
|
"filter_currency": "Valuta",
|
||||||
|
"filter_detail": "Dettaglio",
|
||||||
|
"filter_filters": "Filtri",
|
||||||
|
"filter_iambuying": "Sto comprando bitcoin",
|
||||||
|
"filter_iamselling": "Sto vendendo bitcoin",
|
||||||
|
"filter_method": "Metodo di pagamento",
|
||||||
|
"filter_search": "Cerca",
|
||||||
|
"filter_selling": "Selling",
|
||||||
|
"item_minmax": "Min/Max",
|
||||||
|
"item_nooffers": "No offers. Try to change \"Near me\" to Global offers!",
|
||||||
|
"item_rating": "{rating} trades",
|
||||||
|
"item_rating_no": "Nessuna recensione",
|
||||||
|
"login": "Login",
|
||||||
|
"mycont": "I miei contratti",
|
||||||
|
"offer_accept": "Accept offer",
|
||||||
|
"offer_account_finish": "Looks like you didn't finish setting up account on HodlHodl, would you like to finish setup now?",
|
||||||
|
"offer_choosemethod": "Scegli il metodo di pagamento",
|
||||||
|
"offer_confirmations": "Conferme",
|
||||||
|
"offer_minmax": "min / max",
|
||||||
|
"offer_minutes": "min",
|
||||||
|
"offer_promt_fiat": "How much {currency} do you want to buy?",
|
||||||
|
"offer_promt_fiat_e": "For example 100",
|
||||||
|
"offer_window": "window",
|
||||||
|
"p2p": "A p2p exchange"
|
||||||
},
|
},
|
||||||
"lnd": {
|
"lnd": {
|
||||||
|
"errorInvoiceExpired": "Invoice expired",
|
||||||
|
"exchange": "Exchange",
|
||||||
"expired": "Scaduto",
|
"expired": "Scaduto",
|
||||||
|
"expiredLow": "expired",
|
||||||
|
"expiresIn": "Expires: {time}",
|
||||||
|
"payButton": "Pay",
|
||||||
"placeholder": "Fattura",
|
"placeholder": "Fattura",
|
||||||
|
"potentialFee": "Commissioni potenziali: {fee}",
|
||||||
"refill": "Ricarica",
|
"refill": "Ricarica",
|
||||||
|
"refill_card": "Ricarica con una carta",
|
||||||
|
"refill_create": "In order to proceed, please create a Bitcoin wallet to refill with.",
|
||||||
|
"refill_external": "Refill with External Wallet",
|
||||||
"refill_lnd_balance": "Ricarica saldo del portafoglio Lightning",
|
"refill_lnd_balance": "Ricarica saldo del portafoglio Lightning",
|
||||||
"sameWalletAsInvoiceError": "Non puoi pagare una fattura con lo stesso portafoglio utilizzato per crearla.",
|
"sameWalletAsInvoiceError": "Non puoi pagare una fattura con lo stesso portafoglio utilizzato per crearla.",
|
||||||
"title": "Gestisci fondi"
|
"title": "Gestisci fondi"
|
||||||
},
|
},
|
||||||
|
"lndViewInvoice": {
|
||||||
|
"additional_info": "Additional Information",
|
||||||
|
"for": "For:",
|
||||||
|
"has_been_paid": "This invoice has been paid for",
|
||||||
|
"open_direct_channel": "Open direct channel with this node:",
|
||||||
|
"please_pay": "Please pay",
|
||||||
|
"preimage": "Preimage",
|
||||||
|
"sats": "sats",
|
||||||
|
"wasnt_paid_and_expired": "This invoice was not paid for and has expired"
|
||||||
|
},
|
||||||
"plausibledeniability": {
|
"plausibledeniability": {
|
||||||
"create_fake_storage": "Crea archivio falso criptato",
|
"create_fake_storage": "Crea archivio falso criptato",
|
||||||
"create_password": "Crea una password",
|
"create_password": "Crea una password",
|
||||||
|
@ -28,6 +118,16 @@
|
||||||
"success": "Fatto",
|
"success": "Fatto",
|
||||||
"title": "Negazione Plausibile"
|
"title": "Negazione Plausibile"
|
||||||
},
|
},
|
||||||
|
"pleasebackup": {
|
||||||
|
"ask": "Have you saved your wallet's backup phrase? This backup phrase is required to access your funds in case you lose this device. Without the backup phrase, your funds will be permanently lost.",
|
||||||
|
"ask_no": "No, I have not",
|
||||||
|
"ask_yes": "Yes, I have",
|
||||||
|
"ok": "OK, I wrote this down!",
|
||||||
|
"ok_lnd": "OK, I have saved it.",
|
||||||
|
"text": "Please take a moment to write down this mnemonic phrase on a piece of paper. It's your backup you can use to restore the wallet on other device.",
|
||||||
|
"text_lnd": "Please take a moment to save this LNDHub authentication. It's your backup you can use to restore the wallet on other device.",
|
||||||
|
"title": "Your wallet is created..."
|
||||||
|
},
|
||||||
"receive": {
|
"receive": {
|
||||||
"details_create": "Crea",
|
"details_create": "Crea",
|
||||||
"details_label": "Descrizione",
|
"details_label": "Descrizione",
|
||||||
|
@ -36,65 +136,207 @@
|
||||||
"header": "Ricevi"
|
"header": "Ricevi"
|
||||||
},
|
},
|
||||||
"send": {
|
"send": {
|
||||||
|
"broadcastButton": "BROADCAST",
|
||||||
|
"broadcastError": "error",
|
||||||
|
"broadcastNone": "Input transaction hash",
|
||||||
|
"broadcastPending": "pending",
|
||||||
|
"broadcastSuccess": "success",
|
||||||
"confirm_header": "Conferma",
|
"confirm_header": "Conferma",
|
||||||
"confirm_sendNow": "Invia ora",
|
"confirm_sendNow": "Invia ora",
|
||||||
"create_amount": "Importo",
|
"create_amount": "Importo",
|
||||||
"create_broadcast": "Trasmetti",
|
"create_broadcast": "Trasmetti",
|
||||||
|
"create_copy": "Copy and broadcast later",
|
||||||
"create_details": "Dettagli",
|
"create_details": "Dettagli",
|
||||||
"create_fee": "Commissione",
|
"create_fee": "Commissione",
|
||||||
|
"create_memo": "Memo",
|
||||||
|
"create_satoshi_per_byte": "Satoshi per byte",
|
||||||
"create_this_is_hex": "Questo è l'hex della transazione, firmato e pronto per essere trasmesso sulla rete.",
|
"create_this_is_hex": "Questo è l'hex della transazione, firmato e pronto per essere trasmesso sulla rete.",
|
||||||
"create_to": "A",
|
"create_to": "A",
|
||||||
"create_tx_size": "Grandezza TX",
|
"create_tx_size": "Grandezza TX",
|
||||||
|
"create_verify": "Verify on coinb.in",
|
||||||
|
"details_add_rec_add": "Add Recipient",
|
||||||
|
"details_add_rec_rem": "Remove Recipient",
|
||||||
"details_address": "Indirizzo",
|
"details_address": "Indirizzo",
|
||||||
"details_address_field_is_not_valid": "Indirizzo non valido",
|
"details_address_field_is_not_valid": "Indirizzo non valido",
|
||||||
|
"details_adv_fee_bump": "Allow Fee Bump",
|
||||||
|
"details_adv_full": "Use Full Balance",
|
||||||
|
"details_adv_full_remove": "Your other recipients will be removed from this transaction.",
|
||||||
|
"details_adv_full_sure": "Are you sure you want to use your wallet's full balance for this transaction?",
|
||||||
|
"details_adv_import": "Import Transaction",
|
||||||
"details_amount_field_is_not_valid": "Importo non valido",
|
"details_amount_field_is_not_valid": "Importo non valido",
|
||||||
"details_create": "Crea",
|
"details_create": "Crea",
|
||||||
|
"details_error_decode": "Error: Unable to decode Bitcoin address",
|
||||||
"details_fee_field_is_not_valid": "Commissione non valida",
|
"details_fee_field_is_not_valid": "Commissione non valida",
|
||||||
|
"details_next": "Next",
|
||||||
|
"details_no_maximum": "The selected wallet does not support automatic maximum balance calculation. Are you sure to want to select this wallet?",
|
||||||
|
"details_no_multiple": "The selected wallet does not support sending Bitcoin to multiple recipients. Are you sure to want to select this wallet?",
|
||||||
|
"details_no_signed_tx": "The selected file does not contain a transaction that can be imported.",
|
||||||
"details_note_placeholder": "Nota",
|
"details_note_placeholder": "Nota",
|
||||||
"details_scan": "Scansiona",
|
"details_scan": "Scansiona",
|
||||||
"details_total_exceeds_balance": "L'importo da inviare eccede i fondi disponibili.",
|
"details_total_exceeds_balance": "L'importo da inviare eccede i fondi disponibili.",
|
||||||
|
"details_wallet_before_tx": "Before creating a transaction, you must first add a Bitcoin wallet.",
|
||||||
|
"details_wallet_selection": "Wallet Selection",
|
||||||
|
"dynamic_init": "Initializing",
|
||||||
|
"dynamic_next": "Next",
|
||||||
|
"dynamic_prev": "Previous",
|
||||||
|
"dynamic_start": "Start",
|
||||||
|
"dynamic_stop": "Stop",
|
||||||
|
"fee_10m": "10m",
|
||||||
|
"fee_1d": "1d",
|
||||||
|
"fee_3h": "3h",
|
||||||
|
"fee_custom": "Custom",
|
||||||
|
"fee_fast": "Fast",
|
||||||
|
"fee_medium": "Medium",
|
||||||
|
"fee_replace_min": "The total fee rate (satoshi per byte) you want to pay should be higher than {min} sat/byte",
|
||||||
|
"fee_satbyte": "in sat/byte",
|
||||||
|
"fee_slow": "Slow",
|
||||||
"header": "Invia",
|
"header": "Invia",
|
||||||
"success_done": "Fatto"
|
"input_clear": "Clear",
|
||||||
|
"input_done": "Done",
|
||||||
|
"input_paste": "Paste",
|
||||||
|
"input_total": "Total:",
|
||||||
|
"permission_camera_message": "We need your permission to use your camera",
|
||||||
|
"permission_camera_title": "Permission to use camera",
|
||||||
|
"open_settings": "Open Settings",
|
||||||
|
"permission_storage_later": "Ask Me Later",
|
||||||
|
"permission_storage_message": "BlueWallet needs your permission to access your storage to save this transaction.",
|
||||||
|
"permission_storage_title": "BlueWallet Storage Access Permission",
|
||||||
|
"psbt_clipboard": "Copy to Clipboard",
|
||||||
|
"psbt_this_is_psbt": "This is a partially signed bitcoin transaction (PSBT). Please finish signing it with your hardware wallet.",
|
||||||
|
"psbt_tx_export": "Export to file",
|
||||||
|
"psbt_tx_open": "Open Signed Transaction",
|
||||||
|
"psbt_tx_scan": "Scan Signed Transaction",
|
||||||
|
"qr_error_no_qrcode": "The selected image does not contain a QR Code.",
|
||||||
|
"qr_error_no_wallet": "The selected file does not contain a wallet that can be imported.",
|
||||||
|
"success_done": "Fatto",
|
||||||
|
"txSaved": "The transaction file ({filePath}) has been saved in your Downloads folder ."
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"about": "Informazioni",
|
"about": "Informazioni",
|
||||||
|
"about_awesome": "Built with the awesome",
|
||||||
|
"about_backup": "Always backup your keys!",
|
||||||
|
"about_free": "BlueWallet is a free and open source project. Crafted by Bitcoin users.",
|
||||||
|
"about_release_notes": "Release notes",
|
||||||
|
"about_review": "Leave us a review",
|
||||||
|
"about_selftest": "Run self test",
|
||||||
|
"about_sm_github": "GitHub",
|
||||||
|
"about_sm_telegram": "Telegram chat",
|
||||||
|
"about_sm_twitter": "Follow us on Twitter",
|
||||||
|
"advanced_options": "Advanced Options",
|
||||||
"currency": "Valuta",
|
"currency": "Valuta",
|
||||||
|
"currency_source": "Prices are obtained from CoinDesk",
|
||||||
|
"default_desc": "When disabled, BlueWallet will immediately open the selected wallet at launch.",
|
||||||
|
"default_info": "Default info",
|
||||||
|
"default_title": "On Launch",
|
||||||
|
"default_wallets": "View All Wallets",
|
||||||
|
"electrum_connected": "Connected",
|
||||||
|
"electrum_connected_not": "Not Connected",
|
||||||
|
"electrum_error_connect": "Can't connect to provided Electrum server",
|
||||||
|
"electrum_host": "host, for example {example}",
|
||||||
|
"electrum_port": "TCP port, usually {example}",
|
||||||
|
"electrum_port_ssl": "SSL port, usually {example}",
|
||||||
|
"electrum_saved": "Your changes have been saved successfully. Restart may be required for changes to take effect.",
|
||||||
|
"electrum_settings": "Electrum Settings",
|
||||||
|
"electrum_settings_explain": "Set to blank to use default",
|
||||||
|
"electrum_status": "Status",
|
||||||
|
"encrypt_decrypt": "Decrypt Storage",
|
||||||
|
"encrypt_decrypt_q": "Are you sure you want to decrypt your storage? This will allow your wallets to be accessed without a password.",
|
||||||
|
"encrypt_del_uninstall": "Delete if BlueWallet is uninstalled",
|
||||||
|
"encrypt_enc_and_pass": "Encrypted and Password protected",
|
||||||
|
"encrypt_title": "Security",
|
||||||
|
"encrypt_tstorage": "storage",
|
||||||
|
"encrypt_use": "Use {type}",
|
||||||
|
"encrypt_use_expl": "{type} will be used to confirm your identity prior to making a transaction, unlocking, exporting or deleting a wallet. {type} will not be used to unlock an encrypted storage.",
|
||||||
|
"general": "General",
|
||||||
"general_adv_mode": "Enable advanced mode",
|
"general_adv_mode": "Enable advanced mode",
|
||||||
|
"general_adv_mode_e": "When enabled, you will see advanced options such as different wallet types, the ability to specify the LNDHub instance you wish to connect to and custom entropy during wallet creation.",
|
||||||
|
"general_continuity": "Continuity",
|
||||||
|
"general_continuity_e": "When enabled, you will be able to view selected wallets, and transactions, using your other Apple iCloud connected devices.",
|
||||||
|
"groundcontrol_explanation": "GroundControl is a free opensource push notifications server for bitcoin wallets. You can install your own GroundControl server and put its URL here to not rely on BlueWallet's infrastructure. Leave blank to use default",
|
||||||
"header": "Impostazioni",
|
"header": "Impostazioni",
|
||||||
"language": "Lingua",
|
"language": "Lingua",
|
||||||
|
"language_restart": "When selecting a new language, restarting BlueWallet may be required for the change to take effect.",
|
||||||
|
"lightning_error_lndhub_uri": "Not a valid LndHub URI",
|
||||||
|
"lightning_saved": "Your changes have been saved successfully",
|
||||||
"lightning_settings": "Impostazioni Lightning",
|
"lightning_settings": "Impostazioni Lightning",
|
||||||
"lightning_settings_explain": "Per connetterti al tuo nodo LND personale installa LndHub e inserisci il suo URL qui nelle impostazioni. Lascialo vuoto per utilizzare il nodo LndHub di default (lndhub.io)",
|
"lightning_settings_explain": "Per connetterti al tuo nodo LND personale installa LndHub e inserisci il suo URL qui nelle impostazioni. Lascialo vuoto per utilizzare il nodo LndHub di default (lndhub.io)",
|
||||||
|
"network": "Network",
|
||||||
|
"network_broadcast": "Broadcast transaction",
|
||||||
|
"network_electrum": "Electrum server",
|
||||||
|
"not_a_valid_uri": "Not a valid URI",
|
||||||
|
"notifications": "Notifications",
|
||||||
|
"password": "Password",
|
||||||
"password_explain": "Crea la password che userai per decriptare l'archivio",
|
"password_explain": "Crea la password che userai per decriptare l'archivio",
|
||||||
"passwords_do_not_match": "Le password non corrispondono",
|
"passwords_do_not_match": "Le password non corrispondono",
|
||||||
"plausible_deniability": "Negazione plausibile...",
|
"plausible_deniability": "Negazione plausibile...",
|
||||||
|
"push_notifications": "Push notifications",
|
||||||
"retype_password": "Reinserisci password",
|
"retype_password": "Reinserisci password",
|
||||||
"save": "Salva"
|
"save": "Salva",
|
||||||
|
"saved": "Saved"
|
||||||
},
|
},
|
||||||
"transactions": {
|
"transactions": {
|
||||||
|
"cancel_explain": "We will replace this transaction with the one that pays you and has higher fees. This effectively cancels transaction. This is called RBF - Replace By Fee.",
|
||||||
|
"cancel_no": "This transaction is not replaceable",
|
||||||
|
"cancel_title": "Cancel this transaction (RBF)",
|
||||||
|
"cpfp_create": "Create",
|
||||||
|
"cpfp_exp": "We will create another transaction that spends your unconfirmed transaction. The total fee will be higher than the original transaction fee, so it should be mined faster. This is called CPFP - Child Pays For Parent.",
|
||||||
|
"cpfp_no_bump": "This transaction is not bumpable",
|
||||||
|
"cpfp_title": "Bump fee (CPFP)",
|
||||||
|
"details_block": "Block Height",
|
||||||
"details_copy": "Copia",
|
"details_copy": "Copia",
|
||||||
"details_from": "Da",
|
"details_from": "Da",
|
||||||
|
"details_inputs": "Inputs",
|
||||||
|
"details_outputs": "Outputs",
|
||||||
|
"details_received": "Received",
|
||||||
"details_show_in_block_explorer": "Mostra sul block explorer",
|
"details_show_in_block_explorer": "Mostra sul block explorer",
|
||||||
"details_title": "Transazione",
|
"details_title": "Transazione",
|
||||||
"details_to": "A",
|
"details_to": "A",
|
||||||
"details_transaction_details": "Dettagli transazione",
|
"details_transaction_details": "Dettagli transazione",
|
||||||
"list_title": "Transazioni"
|
"enable_hw": "This wallet is not being used in conjunction with a hardwarde wallet. Would you like to enable hardware wallet use?",
|
||||||
|
"list_conf": "conf",
|
||||||
|
"list_title": "Transazioni",
|
||||||
|
"rbf_explain": "We will replace this transaction with the one with a higher fee, so it should be mined faster. This is called RBF - Replace By Fee.",
|
||||||
|
"rbf_title": "Bump fee (RBF)",
|
||||||
|
"status_bump": "Bump Fee",
|
||||||
|
"status_cancel": "Cancel Transaction",
|
||||||
|
"transactions_count": "transactions count"
|
||||||
},
|
},
|
||||||
"wallets": {
|
"wallets": {
|
||||||
|
"add_bitcoin": "Bitcoin",
|
||||||
"add_create": "Crea",
|
"add_create": "Crea",
|
||||||
|
"add_entropy_generated": "{gen} bytes of generated entropy",
|
||||||
|
"add_entropy_provide": "Provide entropy via dice rolls",
|
||||||
|
"add_entropy_remain": "{gen} bytes of generated entropy. Remaining {rem} bytes will be obtained from the System random number generator.",
|
||||||
"add_import_wallet": "Importa Portafoglio",
|
"add_import_wallet": "Importa Portafoglio",
|
||||||
|
"import_file": "Import File",
|
||||||
|
"add_lightning": "Lightning",
|
||||||
|
"add_lndhub": "Connect to your LNDHub",
|
||||||
|
"add_lndhub_error": "The provided node address is not valid LNDHub node.",
|
||||||
|
"add_lndhub_placeholder": "your node address",
|
||||||
"add_or": "o",
|
"add_or": "o",
|
||||||
"add_title": "Aggiungi Portafoglio",
|
"add_title": "Aggiungi Portafoglio",
|
||||||
"add_wallet_name": "Nome Portafoglio",
|
"add_wallet_name": "Nome Portafoglio",
|
||||||
"add_wallet_type": "Tipo",
|
"add_wallet_type": "Tipo",
|
||||||
"details_address": "Indirizzo",
|
"details_address": "Indirizzo",
|
||||||
|
"details_advanced": "Advanced",
|
||||||
"details_are_you_sure": "Sei sicuro?",
|
"details_are_you_sure": "Sei sicuro?",
|
||||||
|
"details_connected_to": "Connected to",
|
||||||
|
"details_del_wb": "Wallet Balance",
|
||||||
|
"details_del_wb_err": "The provided balance amount does not match this wallet's balance. Please, try again",
|
||||||
|
"details_del_wb_q": "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 {balance} satoshis.",
|
||||||
"details_delete": "Elimina",
|
"details_delete": "Elimina",
|
||||||
|
"details_delete_wallet": "Delete wallet",
|
||||||
|
"details_display": "display in wallets list",
|
||||||
"details_export_backup": "Esporta / Backup",
|
"details_export_backup": "Esporta / Backup",
|
||||||
|
"details_marketplace": "Marketplace",
|
||||||
|
"details_master_fingerprint": "Master fingerprint",
|
||||||
"details_no_cancel": "No, annulla",
|
"details_no_cancel": "No, annulla",
|
||||||
"details_save": "Salva",
|
"details_save": "Salva",
|
||||||
"details_show_xpub": "Mostra XPUB del portafoglio",
|
"details_show_xpub": "Mostra XPUB del portafoglio",
|
||||||
"details_title": "Portafoglio",
|
"details_title": "Portafoglio",
|
||||||
"details_type": "Tipo",
|
"details_type": "Tipo",
|
||||||
|
"details_use_with_hardware_wallet": "Use with hardware wallet",
|
||||||
|
"details_wallet_updated": "Wallet updated",
|
||||||
"details_yes_delete": "Si, elimina",
|
"details_yes_delete": "Si, elimina",
|
||||||
"export_title": "Esporta portafoglio",
|
"export_title": "Esporta portafoglio",
|
||||||
"import_do_import": "Importa",
|
"import_do_import": "Importa",
|
||||||
|
@ -109,11 +351,23 @@
|
||||||
"list_create_a_wallet1": "È gratuito e puoi crearne",
|
"list_create_a_wallet1": "È gratuito e puoi crearne",
|
||||||
"list_create_a_wallet2": "quanti ne vuoi",
|
"list_create_a_wallet2": "quanti ne vuoi",
|
||||||
"list_empty_txs1": "Le tue transazioni appariranno qui,",
|
"list_empty_txs1": "Le tue transazioni appariranno qui,",
|
||||||
|
"list_empty_txs1_lightning": "Lightning wallet should be used for your daily transactions. Fees are unfairly cheap and speed is blazing fast.",
|
||||||
"list_empty_txs2": "Nessuna transazione",
|
"list_empty_txs2": "Nessuna transazione",
|
||||||
|
"list_empty_txs2_lightning": "\nTo start using it tap on \"manage funds\" and topup your balance.",
|
||||||
|
"list_header": "A wallet represents a pair of keys, one private and one you can share to receive coins.",
|
||||||
|
"list_import_error": "An error was encountered when attempting to import this wallet.",
|
||||||
|
"list_import_problem": "There was a problem importing this wallet",
|
||||||
"list_latest_transaction": "Transazioni recenti",
|
"list_latest_transaction": "Transazioni recenti",
|
||||||
|
"list_long_choose": "Choose Photo",
|
||||||
|
"list_long_clipboard": "Copy from Clipboard",
|
||||||
|
"list_long_scan": "Scan QR Code",
|
||||||
|
"take_photo": "Take Photo",
|
||||||
"list_tap_here_to_buy": "Clicca qui per comprare Bitcoin",
|
"list_tap_here_to_buy": "Clicca qui per comprare Bitcoin",
|
||||||
"list_title": "Portafogli",
|
"list_title": "Portafogli",
|
||||||
|
"list_tryagain": "Try Again",
|
||||||
"reorder_title": "Riordina Portafogli",
|
"reorder_title": "Riordina Portafogli",
|
||||||
|
"select_no_bitcoin": "There are currently no Bitcoin wallets available.",
|
||||||
|
"select_no_bitcoin_exp": "A Bitcoin wallet is required to refill Lightning wallets. Please, create or import one.",
|
||||||
"select_wallet": "Seleziona Portafoglio",
|
"select_wallet": "Seleziona Portafoglio",
|
||||||
"xpub_copiedToClipboard": "Copiata negli appunti.",
|
"xpub_copiedToClipboard": "Copiata negli appunti.",
|
||||||
"xpub_title": "XPUB del Portafoglio"
|
"xpub_title": "XPUB del Portafoglio"
|
||||||
|
|
172
loc/jp_jp.json
172
loc/jp_jp.json
|
@ -32,7 +32,7 @@
|
||||||
"hodl": {
|
"hodl": {
|
||||||
"are_you_sure_you_want_to_logout": "本当にHodlHodlからログアウトしますか?",
|
"are_you_sure_you_want_to_logout": "本当にHodlHodlからログアウトしますか?",
|
||||||
"cont_address_escrow": "エスクロー",
|
"cont_address_escrow": "エスクロー",
|
||||||
"cont_address_to": "To",
|
"cont_address_to": "宛先",
|
||||||
"cont_buying": "買う",
|
"cont_buying": "買う",
|
||||||
"cont_cancel": "コントラクトをキャンセル",
|
"cont_cancel": "コントラクトをキャンセル",
|
||||||
"cont_cancel_q": "本当にこのコントラクトをキャンセルしますか?",
|
"cont_cancel_q": "本当にこのコントラクトをキャンセルしますか?",
|
||||||
|
@ -50,8 +50,8 @@
|
||||||
"cont_st_paid_waiting": "販売者がエスクローからコインをリリースするのを待っています。",
|
"cont_st_paid_waiting": "販売者がエスクローからコインをリリースするのを待っています。",
|
||||||
"cont_st_waiting": "販売者がエスクローにビットコインを預けるのを待っています。",
|
"cont_st_waiting": "販売者がエスクローにビットコインを預けるのを待っています。",
|
||||||
"cont_title": "マイコントラクト",
|
"cont_title": "マイコントラクト",
|
||||||
"filter_any": "Any",
|
"filter_any": "すべて",
|
||||||
"filter_buying": "Buying",
|
"filter_buying": "買う",
|
||||||
"filter_country_global": "グローバル・オファー",
|
"filter_country_global": "グローバル・オファー",
|
||||||
"filter_country_near": "近くで探す",
|
"filter_country_near": "近くで探す",
|
||||||
"filter_currency": "通貨",
|
"filter_currency": "通貨",
|
||||||
|
@ -74,7 +74,7 @@
|
||||||
"offer_confirmations": "承認",
|
"offer_confirmations": "承認",
|
||||||
"offer_minmax": "最小 / 最大",
|
"offer_minmax": "最小 / 最大",
|
||||||
"offer_minutes": "分",
|
"offer_minutes": "分",
|
||||||
"offer_promt_fiat": "How much {currency} do you want to buy?",
|
"offer_promt_fiat": "いくら {currency} を購入しますか?",
|
||||||
"offer_promt_fiat_e": "例 100",
|
"offer_promt_fiat_e": "例 100",
|
||||||
"offer_window": "ウィンドウ",
|
"offer_window": "ウィンドウ",
|
||||||
"p2p": "P2P エクスチェンジ"
|
"p2p": "P2P エクスチェンジ"
|
||||||
|
@ -89,9 +89,9 @@
|
||||||
"placeholder": "入金依頼",
|
"placeholder": "入金依頼",
|
||||||
"potentialFee": "手数料推計: {fee}",
|
"potentialFee": "手数料推計: {fee}",
|
||||||
"refill": "送金",
|
"refill": "送金",
|
||||||
"refill_card": "Refill with bank card",
|
"refill_card": "銀行カードで補充する",
|
||||||
"refill_create": "In order to proceed, please create a Bitcoin wallet to refill with.",
|
"refill_create": "先に進むためには、ビットコインウォレットを作成して補充してください。",
|
||||||
"refill_external": "Refill with External Wallet",
|
"refill_external": "外部ウォレットで補充",
|
||||||
"refill_lnd_balance": "Lightning ウォレットへ送金",
|
"refill_lnd_balance": "Lightning ウォレットへ送金",
|
||||||
"sameWalletAsInvoiceError": "以前作成したウォレットと同じウォレットへの支払いはできません。",
|
"sameWalletAsInvoiceError": "以前作成したウォレットと同じウォレットへの支払いはできません。",
|
||||||
"title": "資金の管理"
|
"title": "資金の管理"
|
||||||
|
@ -125,7 +125,7 @@
|
||||||
"ok": "すべてのニモニックを書きとめました",
|
"ok": "すべてのニモニックを書きとめました",
|
||||||
"ok_lnd": "はい、書きとめました",
|
"ok_lnd": "はい、書きとめました",
|
||||||
"text": "すべてのニモニックを別紙に書きとめてください。他のデバイスへウォレットをリストアする際にニモニックが必要になります。デスクトップ用ウォレットの Electrum wallet (https://electrum.org/) へニモニックを使用してウォレットをリストアすることが可能です。",
|
"text": "すべてのニモニックを別紙に書きとめてください。他のデバイスへウォレットをリストアする際にニモニックが必要になります。デスクトップ用ウォレットの Electrum wallet (https://electrum.org/) へニモニックを使用してウォレットをリストアすることが可能です。",
|
||||||
"text_lnd": "Please take a moment to save this LNDHub authentication. It's your backup you can use to restore the wallet on other device.",
|
"text_lnd": "このLNDHub認証を保存しておいてください。これはあなたのバックアップであり、他のデバイス上でウォレットを復元するために使用できます。",
|
||||||
"title": "ウォレットを作成しています..."
|
"title": "ウォレットを作成しています..."
|
||||||
},
|
},
|
||||||
"receive": {
|
"receive": {
|
||||||
|
@ -136,9 +136,9 @@
|
||||||
"header": "入金"
|
"header": "入金"
|
||||||
},
|
},
|
||||||
"send": {
|
"send": {
|
||||||
"broadcastButton": "BROADCAST",
|
"broadcastButton": "ブロードキャスト",
|
||||||
"broadcastError": "エラー",
|
"broadcastError": "エラー",
|
||||||
"broadcastNone": "Input transaction hash",
|
"broadcastNone": "インプットトランザクションハッシュ",
|
||||||
"broadcastPending": "試行中",
|
"broadcastPending": "試行中",
|
||||||
"broadcastSuccess": "成功",
|
"broadcastSuccess": "成功",
|
||||||
"confirm_header": "確認",
|
"confirm_header": "確認",
|
||||||
|
@ -158,38 +158,38 @@
|
||||||
"details_add_rec_rem": "宛先を削除",
|
"details_add_rec_rem": "宛先を削除",
|
||||||
"details_address": "アドレス",
|
"details_address": "アドレス",
|
||||||
"details_address_field_is_not_valid": "アドレス欄が正しくありません",
|
"details_address_field_is_not_valid": "アドレス欄が正しくありません",
|
||||||
"details_adv_fee_bump": "Allow Fee Bump",
|
"details_adv_fee_bump": "費用のバンプ(増加)を許可",
|
||||||
"details_adv_full": "Use Full Balance",
|
"details_adv_full": "全残高を使う",
|
||||||
"details_adv_full_remove": "Your other recipients will be removed from this transaction.",
|
"details_adv_full_remove": "BlueWalletがアンインストールされたら消去",
|
||||||
"details_adv_full_sure": "Are you sure you want to use your wallet's full balance for this transaction?",
|
"details_adv_full_sure": "本当にこのウォレットの全残高をこのトランザクションに利用しますか?",
|
||||||
"details_adv_import": "Import Transaction",
|
"details_adv_import": "トランザクションをインポート",
|
||||||
"details_amount_field_is_not_valid": "金額欄が正しくありません",
|
"details_amount_field_is_not_valid": "金額欄が正しくありません",
|
||||||
"details_create": "作成",
|
"details_create": "作成",
|
||||||
"details_error_decode": "Error: Unable to decode Bitcoin address",
|
"details_error_decode": "エラー:ビットコインアドレスを復号できません",
|
||||||
"details_fee_field_is_not_valid": "手数料欄が正しくありません",
|
"details_fee_field_is_not_valid": "手数料欄が正しくありません",
|
||||||
"details_next": "次",
|
"details_next": "次",
|
||||||
"details_no_maximum": "選択したウォレットは、最大残高の自動計算に対応していません。このウォレットを選択してもよろしいですか?",
|
"details_no_maximum": "選択したウォレットは、最大残高の自動計算に対応していません。このウォレットを選択してもよろしいですか?",
|
||||||
"details_no_multiple": "選択したウォレットは、複数の受信者へのビットコインの送信をサポートしていません。このウォレットを選択してもよろしいですか?",
|
"details_no_multiple": "選択したウォレットは、複数の受信者へのビットコインの送信をサポートしていません。このウォレットを選択してもよろしいですか?",
|
||||||
"details_no_signed_tx": "The selected file does not contain a signed transaction that can be imported.",
|
"details_no_signed_tx": "選択したファイルには、インポート可能なトランザクションが含まれていません。",
|
||||||
"details_note_placeholder": "ラベル",
|
"details_note_placeholder": "ラベル",
|
||||||
"details_scan": "読取り",
|
"details_scan": "読取り",
|
||||||
"details_total_exceeds_balance": "送金額が利用可能残額を超えています。",
|
"details_total_exceeds_balance": "送金額が利用可能残額を超えています。",
|
||||||
"details_wallet_before_tx": "Before creating a transaction, you must first add a Bitcoin wallet.",
|
"details_wallet_before_tx": "トランザクションを作成する前に、まずはビットコインウォレットを追加する必要があります。",
|
||||||
"details_wallet_selection": "ウォレット選択",
|
"details_wallet_selection": "ウォレット選択",
|
||||||
"dynamic_init": "インストール中",
|
"dynamic_init": "インストール中",
|
||||||
"dynamic_next": "次",
|
"dynamic_next": "次",
|
||||||
"dynamic_prev": "前",
|
"dynamic_prev": "前",
|
||||||
"dynamic_start": "スタート",
|
"dynamic_start": "スタート",
|
||||||
"dynamic_stop": "ストップ",
|
"dynamic_stop": "ストップ",
|
||||||
"fee_10m": "10m",
|
"fee_10m": "10分",
|
||||||
"fee_1d": "1d",
|
"fee_1d": "1日",
|
||||||
"fee_3h": "3h",
|
"fee_3h": "3時間",
|
||||||
"fee_custom": "Custom",
|
"fee_custom": "カスタム",
|
||||||
"fee_fast": "Fast",
|
"fee_fast": "高速",
|
||||||
"fee_medium": "Medium",
|
"fee_medium": "中速",
|
||||||
"fee_replace_min": "The total fee rate (satoshi per byte) you want to pay should be higher than {min} sat/byte",
|
"fee_replace_min": "支払いたい手数料レート(1バイトあたりのsatoshi)は、{min} sat/byteよりも高い必要があります。",
|
||||||
"fee_satbyte": "in sat/byte",
|
"fee_satbyte": "sat/バイト",
|
||||||
"fee_slow": "Slow",
|
"fee_slow": "低速",
|
||||||
"header": "送金",
|
"header": "送金",
|
||||||
"input_clear": "クリア",
|
"input_clear": "クリア",
|
||||||
"input_done": "完了",
|
"input_done": "完了",
|
||||||
|
@ -197,71 +197,71 @@
|
||||||
"input_total": "合計:",
|
"input_total": "合計:",
|
||||||
"permission_camera_message": "カメラを使用するのに許可が必要です",
|
"permission_camera_message": "カメラを使用するのに許可が必要です",
|
||||||
"permission_camera_title": "カメラの使用許可",
|
"permission_camera_title": "カメラの使用許可",
|
||||||
"open_settings": "Open Settings",
|
"open_settings": "設定を開く",
|
||||||
"permission_storage_later": "Ask Me Later",
|
"permission_storage_later": "後で聞く",
|
||||||
"permission_storage_message": "BlueWallet needs your permission to access your storage to save this transaction.",
|
"permission_storage_message": "BlueWalletはこのトランザクションを保存するためにストレージへのアクセス許可を必要としています。",
|
||||||
"permission_storage_title": "BlueWallet Storage Access Permission",
|
"permission_storage_title": "BlueWalletストレージアクセス許可",
|
||||||
"psbt_clipboard": "クリップボードにコピー",
|
"psbt_clipboard": "クリップボードにコピー",
|
||||||
"psbt_this_is_psbt": "これは部分的に署名されたビットコイントランザクション(PSBT)です。ハードウェアウォレットで署名を完了させてください。",
|
"psbt_this_is_psbt": "これは部分的に署名されたビットコイントランザクション(PSBT)です。ハードウェアウォレットで署名を完了させてください。",
|
||||||
"psbt_tx_export": "ファイルにエクスポート",
|
"psbt_tx_export": "ファイルにエクスポート",
|
||||||
"psbt_tx_open": "署名トランザクションを開く",
|
"psbt_tx_open": "署名トランザクションを開く",
|
||||||
"psbt_tx_scan": "署名トランザクションをスキャン",
|
"psbt_tx_scan": "署名トランザクションをスキャン",
|
||||||
"qr_error_no_qrcode": "選択された画像はQRコードを含んでいません。",
|
"qr_error_no_qrcode": "選択された画像はQRコードを含んでいません。",
|
||||||
"qr_error_no_wallet": "The selected file does not contain a wallet that can be imported.",
|
"qr_error_no_wallet": "選択されたファイルにはインポートできるウォレットが含まれていません。",
|
||||||
"success_done": "完了",
|
"success_done": "完了",
|
||||||
"txSaved": "The transaction file ({filePath}) has been saved in your Downloads folder ."
|
"txSaved": "トランザクションファイル ({filePath}) はダウンロードフォルダに保存されました。"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"about": "BlueWallet について",
|
"about": "BlueWallet について",
|
||||||
"about_awesome": "Built with the awesome",
|
"about_awesome": "Built with the awesome",
|
||||||
"about_backup": "Always backup your keys!",
|
"about_backup": "常に秘密鍵はバックアップしましょう!",
|
||||||
"about_free": "BlueWallet is a free and open source project. Crafted by Bitcoin users.",
|
"about_free": "BlueWalletはフリーでオープンソースのプロジェクトです。ビットコインユーザーによって作られています。",
|
||||||
"about_release_notes": "Release notes",
|
"about_release_notes": "リリースノート",
|
||||||
"about_review": "レビューを書く",
|
"about_review": "レビューを書く",
|
||||||
"about_selftest": "Run self test",
|
"about_selftest": "セルフテストを実行",
|
||||||
"about_sm_github": "GitHub",
|
"about_sm_github": "GitHub",
|
||||||
"about_sm_telegram": "テレグラムチャット",
|
"about_sm_telegram": "テレグラムチャット",
|
||||||
"about_sm_twitter": "Twitterでフォロー",
|
"about_sm_twitter": "Twitterでフォロー",
|
||||||
"advanced_options": "上級設定",
|
"advanced_options": "上級設定",
|
||||||
"currency": "通貨",
|
"currency": "通貨",
|
||||||
"currency_source": "CoinDeskから取得された価格",
|
"currency_source": "CoinDeskから取得された価格",
|
||||||
"default_desc": "When disabled, BlueWallet will immediately open the selected wallet at launch.",
|
"default_desc": "無効にすれば、BlueWalletは起動時に選択したウォレットをすぐに開きます。",
|
||||||
"default_info": "Default info",
|
"default_info": "デフォルト情報",
|
||||||
"default_title": "On Launch",
|
"default_title": "起動時",
|
||||||
"default_wallets": "View All Wallets",
|
"default_wallets": "すべてのウォレットを確認",
|
||||||
"electrum_connected": "Connected",
|
"electrum_connected": "接続済",
|
||||||
"electrum_connected_not": "Not Connected",
|
"electrum_connected_not": "未接続",
|
||||||
"electrum_error_connect": "Can't connect to provided Electrum server",
|
"electrum_error_connect": "指定されたElectrumサーバーに接続できません",
|
||||||
"electrum_host": "host, for example {example}",
|
"electrum_host": "ホスト 例 {example}",
|
||||||
"electrum_port": "TCP port, usually {example}",
|
"electrum_port": "TCP ポート 通常 {example}",
|
||||||
"electrum_port_ssl": "SSL port, usually {example}",
|
"electrum_port_ssl": "SSL ポート 通常 {example}",
|
||||||
"electrum_saved": "Your changes have been saved successfully. Restart may be required for changes to take effect.",
|
"electrum_saved": "変更は正常に保存されました。変更の適用には、リスタートが必要な場合があります。",
|
||||||
"electrum_settings": "Electrum Settings",
|
"electrum_settings": "Electrum 設定",
|
||||||
"electrum_settings_explain": "Set to blank to use default",
|
"electrum_settings_explain": "ブランクに設定してデフォルトを使用",
|
||||||
"electrum_status": "ステータス",
|
"electrum_status": "ステータス",
|
||||||
"encrypt_decrypt": "ストレージ復号化",
|
"encrypt_decrypt": "ストレージ復号化",
|
||||||
"encrypt_decrypt_q": "Are you sure you want to decrypt your storage? This will allow your wallets to be accessed without a password.",
|
"encrypt_decrypt_q": "本当にストレージを復号化しますか?これによりウォレットがパスワードなしでアクセス可能になります。",
|
||||||
"encrypt_del_uninstall": "Delete if BlueWallet is uninstalled",
|
"encrypt_del_uninstall": "BlueWalletをアンインストールしたら削除",
|
||||||
"encrypt_enc_and_pass": "Encrypted and Password protected",
|
"encrypt_enc_and_pass": "暗号化されパスワードで保護されています",
|
||||||
"encrypt_title": "セキュリティ",
|
"encrypt_title": "セキュリティ",
|
||||||
"encrypt_tstorage": "ストレージ",
|
"encrypt_tstorage": "ストレージ",
|
||||||
"encrypt_use": "{type} を使う",
|
"encrypt_use": "{type} を使う",
|
||||||
"encrypt_use_expl": "{type} は、トランザクションの実行、ウォレットのロック解除、エクスポート、または削除を行う前の本人確認に使用されます。{type} は暗号化されたストレージのロック解除には使用されません。",
|
"encrypt_use_expl": "{type} は、トランザクションの実行、ウォレットのロック解除、エクスポート、または削除を行う前の本人確認に使用されます。{type} は暗号化されたストレージのロック解除には使用されません。",
|
||||||
"general": "一般情報",
|
"general": "一般情報",
|
||||||
"general_adv_mode": "Enable advanced mode",
|
"general_adv_mode": "Enable advanced mode",
|
||||||
"general_adv_mode_e": "When enabled, you will see advanced options such as different wallet types, the ability to specify the LNDHub instance you wish to connect to and custom entropy during wallet creation.",
|
"general_adv_mode_e": "この機能を有効にすると、異なるウォレットタイプ、接続先の LNDHub インスタンスの指定、ウォレット作成時のカスタムエントロピーなどの高度なオプションが表示されます。",
|
||||||
"general_continuity": "Continuity",
|
"general_continuity": "継続性",
|
||||||
"general_continuity_e": "When enabled, you will be able to view selected wallets, and transactions, using your other Apple iCloud connected devices.",
|
"general_continuity_e": "この機能を有効にすると、Apple iCloudに接続している他のデバイスを使用して、選択したウォレットやトランザクションを表示できるようになります。",
|
||||||
"groundcontrol_explanation": "GroundControl is a free opensource push notifications server for bitcoin wallets. You can install your own GroundControl server and put its URL here to not rely on BlueWallet's infrastructure. Leave blank to use default",
|
"groundcontrol_explanation": "GroundControlはビットコインウォレットのための無料のオープンソースのプッシュ通知サーバーです。独自のGroundControlサーバーをインストールし、BlueWalletのインフラに依存しないようにURLをここに入力することができます。デフォルトを使用するには空白のままにしてください。",
|
||||||
"header": "設定",
|
"header": "設定",
|
||||||
"language": "言語",
|
"language": "言語",
|
||||||
"language_restart": "When selecting a new language, restarting BlueWallet may be required for the change to take effect.",
|
"language_restart": "新しい言語を選択した場合、変更を有効にするには BlueWallet の再起動が必要な場合があります。",
|
||||||
"lightning_error_lndhub_uri": "Not a valid LndHub URI",
|
"lightning_error_lndhub_uri": "有効なLndHub URIではありません",
|
||||||
"lightning_saved": "Your changes have been saved successfully",
|
"lightning_saved": "変更は正常に保存されました",
|
||||||
"lightning_settings": "Lightning 設定",
|
"lightning_settings": "Lightning 設定",
|
||||||
"lightning_settings_explain": "他の LND ノードへ接続するには LndHub をインストール後、URL を入力してください。既定の設定を使用するには空欄にしますndHub\n (lndhub.io)",
|
"lightning_settings_explain": "他の LND ノードへ接続するには LndHub をインストール後、URL を入力してください。既定の設定を使用するには空欄にしますndHub\n (lndhub.io)",
|
||||||
"network": "Network",
|
"network": "ネットワーク",
|
||||||
"network_broadcast": "Broadcast transaction",
|
"network_broadcast": "ブロードキャストトランザクション",
|
||||||
"network_electrum": "Electrum サーバー",
|
"network_electrum": "Electrum サーバー",
|
||||||
"not_a_valid_uri": "有効なURIではありません",
|
"not_a_valid_uri": "有効なURIではありません",
|
||||||
"notifications": "通知",
|
"notifications": "通知",
|
||||||
|
@ -275,11 +275,11 @@
|
||||||
"saved": "保存済"
|
"saved": "保存済"
|
||||||
},
|
},
|
||||||
"transactions": {
|
"transactions": {
|
||||||
"cancel_explain": "We will replace this transaction with the one that pays you and has higher fees. This effectively cancels transaction. This is called RBF - Replace By Fee.",
|
"cancel_explain": "このトランザクションを、最初の支払い時より高い手数料を持つものに置き換えます。これは事実上、最初のトランザクションをキャンセルします。これはReplace By Fee - RBFと呼ばれています。",
|
||||||
"cancel_no": "このトランザクションは交換可能ではありません",
|
"cancel_no": "このトランザクションは交換可能ではありません",
|
||||||
"cancel_title": "このトランザクションをキャンセル (RBF)",
|
"cancel_title": "このトランザクションをキャンセル (RBF)",
|
||||||
"cpfp_create": "作成",
|
"cpfp_create": "作成",
|
||||||
"cpfp_exp": "We will create another transaction that spends your unconfirmed transaction. The total fee will be higher than the original transaction fee, so it should be mined faster. This is called CPFP - Child Pays For Parent.",
|
"cpfp_exp": "あなたの未承認トランザクションを消費する別のトランザクションを作成します。元のトランザクションの手数料よりも合計金額が高くなるため、より早くマイニングされます。これはCPFP - Child Pays For Parentと呼ばれています。",
|
||||||
"cpfp_no_bump": "このトランザクションはバンプ可能ではありません",
|
"cpfp_no_bump": "このトランザクションはバンプ可能ではありません",
|
||||||
"cpfp_title": "バンプ費用 (CPFP)",
|
"cpfp_title": "バンプ費用 (CPFP)",
|
||||||
"details_block": "ブロック高",
|
"details_block": "ブロック高",
|
||||||
|
@ -292,44 +292,44 @@
|
||||||
"details_title": "取引",
|
"details_title": "取引",
|
||||||
"details_to": "送り先",
|
"details_to": "送り先",
|
||||||
"details_transaction_details": "取引詳細",
|
"details_transaction_details": "取引詳細",
|
||||||
"enable_hw": "This wallet is not being used in conjunction with a hardwarde wallet. Would you like to enable hardware wallet use?",
|
"enable_hw": "このウォレットはハードウォレットとの併用はされていません。ハードウェアウォレットの使用を有効にしますか?",
|
||||||
"list_conf": "確認",
|
"list_conf": "コンファメーション: {number}",
|
||||||
"list_title": "取引",
|
"list_title": "取引",
|
||||||
"rbf_explain": "We will replace this transaction with the one with a higher fee, so it should be mined faster. This is called RBF - Replace By Fee.",
|
"rbf_explain": "このトランザクションを手数料の高いものに置き換えるので、マイニングが早くなるはずです。これをRBF - Replace By Feeといいます。",
|
||||||
"rbf_title": "Bump fee (RBF)",
|
"rbf_title": "手数料をバンプ (RBF)",
|
||||||
"status_bump": "Bump Fee",
|
"status_bump": "手数料をバンプ",
|
||||||
"status_cancel": "Cancel Transaction",
|
"status_cancel": "トランザクションをキャンセル",
|
||||||
"transactions_count": "transactions count"
|
"transactions_count": "トランザクションカウント"
|
||||||
},
|
},
|
||||||
"wallets": {
|
"wallets": {
|
||||||
"add_bitcoin": "ビットコイン",
|
"add_bitcoin": "ビットコイン",
|
||||||
"add_create": "作成",
|
"add_create": "作成",
|
||||||
"add_entropy_generated": "{gen} bytes of generated entropy",
|
"add_entropy_generated": "生成されたエントロピーの {gen} バイト",
|
||||||
"add_entropy_provide": "サイコロを振ってエントロピーを提供",
|
"add_entropy_provide": "サイコロを振ってエントロピーを提供",
|
||||||
"add_entropy_remain": "{gen} bytes of generated entropy. Remaining {rem} bytes will be obtained from the System random number generator.",
|
"add_entropy_remain": "生成されたエントロピーの{gen}バイト。残りの{rem}バイトはシステム乱数発生器から取得されます。",
|
||||||
"add_import_wallet": "ウォレットをインポート",
|
"add_import_wallet": "ウォレットをインポート",
|
||||||
"import_file": "Import File",
|
"import_file": "インポートファイル",
|
||||||
"add_lightning": "Lightning",
|
"add_lightning": "ライトニング",
|
||||||
"add_lndhub": "Connect to your LNDHub",
|
"add_lndhub": "あなたのLNDHubに接続",
|
||||||
"add_lndhub_error": "The provided node address is not valid LNDHub node.",
|
"add_lndhub_error": "指定されたノードアドレスは有効なLNDHubノードではありません。",
|
||||||
"add_lndhub_placeholder": "your node address",
|
"add_lndhub_placeholder": "あなたのノードアドレス",
|
||||||
"add_or": "又は",
|
"add_or": "又は",
|
||||||
"add_title": "ウォレットの追加",
|
"add_title": "ウォレットの追加",
|
||||||
"add_wallet_name": "ウォレット名",
|
"add_wallet_name": "ウォレット名",
|
||||||
"add_wallet_type": "タイプ",
|
"add_wallet_type": "タイプ",
|
||||||
"details_address": "アドレス",
|
"details_address": "アドレス",
|
||||||
"details_advanced": "Advanced",
|
"details_advanced": "上級設定",
|
||||||
"details_are_you_sure": "実行しますか?",
|
"details_are_you_sure": "実行しますか?",
|
||||||
"details_connected_to": "接続",
|
"details_connected_to": "接続",
|
||||||
"details_del_wb": "ウォレット残高",
|
"details_del_wb": "ウォレット残高",
|
||||||
"details_del_wb_err": "The provided balance amount does not match this wallet's balance. Please, try again",
|
"details_del_wb_err": "提供された残高は、このウォレットの残高と一致しません。もう一度お試しください。",
|
||||||
"details_del_wb_q": "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 {balance} satoshis.",
|
"details_del_wb_q": "このウォレットには残高があります。先に進む前に、このウォレットのシードフレーズがないと資金を回収できない点に注意してください。誤ってこのウォレットを削除しないようにするために、このウォレットの残高 {balance} satoshisを入力してください。",
|
||||||
"details_delete": "削除",
|
"details_delete": "削除",
|
||||||
"details_delete_wallet": "ウォレット削除",
|
"details_delete_wallet": "ウォレット削除",
|
||||||
"details_display": "display in wallets list",
|
"details_display": "ウォレットリストで表示",
|
||||||
"details_export_backup": "エクスポート / バックアップ",
|
"details_export_backup": "エクスポート / バックアップ",
|
||||||
"details_marketplace": "Marketplace",
|
"details_marketplace": "マーケットプレイス",
|
||||||
"details_master_fingerprint": "Master fingerprint",
|
"details_master_fingerprint": "マスタフィンガープリント",
|
||||||
"details_no_cancel": "いいえ、中止します",
|
"details_no_cancel": "いいえ、中止します",
|
||||||
"details_save": "保存",
|
"details_save": "保存",
|
||||||
"details_show_xpub": "ウォレット XPUB の表示",
|
"details_show_xpub": "ウォレット XPUB の表示",
|
||||||
|
@ -354,8 +354,8 @@
|
||||||
"list_empty_txs1_lightning": "Lightning ウォレットを日常の取引にご利用ください。手数料は安く、送金はあっという間に完了します。",
|
"list_empty_txs1_lightning": "Lightning ウォレットを日常の取引にご利用ください。手数料は安く、送金はあっという間に完了します。",
|
||||||
"list_empty_txs2": "現在は何もありません",
|
"list_empty_txs2": "現在は何もありません",
|
||||||
"list_empty_txs2_lightning": "\n利用を開始するには\"資金の管理\"をタップしてウォレットへ送金してください。",
|
"list_empty_txs2_lightning": "\n利用を開始するには\"資金の管理\"をタップしてウォレットへ送金してください。",
|
||||||
"list_header": "A wallet represents a pair of keys, one private and one you can share to receive coins.",
|
"list_header": "ウォレットが表示する鍵のペアは、ひとつが秘密鍵、もうひとつはコインを受け取るために他人と共有することができる鍵です。",
|
||||||
"list_import_error": "An error was encountered when attempting to import this wallet.",
|
"list_import_error": "ウォレットのインポート時にエラーが起こりました。",
|
||||||
"list_import_problem": "このウォレットのインポートに問題が生じました",
|
"list_import_problem": "このウォレットのインポートに問題が生じました",
|
||||||
"list_latest_transaction": "最新の取引",
|
"list_latest_transaction": "最新の取引",
|
||||||
"list_long_choose": "写真選択",
|
"list_long_choose": "写真選択",
|
||||||
|
|
|
@ -170,13 +170,13 @@
|
||||||
"details_next": "Volgende",
|
"details_next": "Volgende",
|
||||||
"details_no_maximum": "The selected wallet does not support automatic maximum balance calculation. Are you sure to want to select this wallet?",
|
"details_no_maximum": "The selected wallet does not support automatic maximum balance calculation. Are you sure to want to select this wallet?",
|
||||||
"details_no_multiple": "The selected wallet does not support sending Bitcoin to multiple recipients. Are you sure to want to select this wallet?",
|
"details_no_multiple": "The selected wallet does not support sending Bitcoin to multiple recipients. Are you sure to want to select this wallet?",
|
||||||
"details_no_signed_tx": "The selected file does not contain a signed transaction that can be imported.",
|
"details_no_signed_tx": "The selected file does not contain a transaction that can be imported.",
|
||||||
"details_note_placeholder": "notitie voor mezelf",
|
"details_note_placeholder": "notitie voor mezelf",
|
||||||
"details_scan": "Scan",
|
"details_scan": "Scan",
|
||||||
"details_total_exceeds_balance": "Het verzendingsbedrag overschrijdt het beschikbare saldo.",
|
"details_total_exceeds_balance": "Het verzendingsbedrag overschrijdt het beschikbare saldo.",
|
||||||
"details_wallet_before_tx": "Before creating a transaction, you must first add a Bitcoin wallet.",
|
"details_wallet_before_tx": "Before creating a transaction, you must first add a Bitcoin wallet.",
|
||||||
"details_wallet_selection": "Wallet Selection",
|
"details_wallet_selection": "Wallet Selection",
|
||||||
"dynamic_init": "Initialing",
|
"dynamic_init": "Initializing",
|
||||||
"dynamic_next": "Volgende",
|
"dynamic_next": "Volgende",
|
||||||
"dynamic_prev": "Vorige",
|
"dynamic_prev": "Vorige",
|
||||||
"dynamic_start": "Start",
|
"dynamic_start": "Start",
|
||||||
|
|
|
@ -170,7 +170,7 @@
|
||||||
"details_next": "Próximo",
|
"details_next": "Próximo",
|
||||||
"details_no_maximum": "A carteira selecionada não suporta cálculo automático de saldo máximo. Tem certeza que deseja selecionar esta carteira?",
|
"details_no_maximum": "A carteira selecionada não suporta cálculo automático de saldo máximo. Tem certeza que deseja selecionar esta carteira?",
|
||||||
"details_no_multiple": "A carteira selecionada não suporta o envio de Bitcoins para vários destinatários. Tem certeza que deseja selecionar esta carteira?",
|
"details_no_multiple": "A carteira selecionada não suporta o envio de Bitcoins para vários destinatários. Tem certeza que deseja selecionar esta carteira?",
|
||||||
"details_no_signed_tx": "O arquivo selecionado não contém uma transação assinada que possa ser importada.",
|
"details_no_signed_tx": "O arquivo selecionado não contém uma transação que possa ser importada.",
|
||||||
"details_note_placeholder": "Nota pessoal",
|
"details_note_placeholder": "Nota pessoal",
|
||||||
"details_scan": "Ler",
|
"details_scan": "Ler",
|
||||||
"details_total_exceeds_balance": "Valor total excede o saldo disponível",
|
"details_total_exceeds_balance": "Valor total excede o saldo disponível",
|
||||||
|
@ -181,6 +181,15 @@
|
||||||
"dynamic_prev": "Anterior",
|
"dynamic_prev": "Anterior",
|
||||||
"dynamic_start": "Começar",
|
"dynamic_start": "Começar",
|
||||||
"dynamic_stop": "Parar",
|
"dynamic_stop": "Parar",
|
||||||
|
"fee_10m": "10m",
|
||||||
|
"fee_1d": "1d",
|
||||||
|
"fee_3h": "3h",
|
||||||
|
"fee_custom": "Inserir",
|
||||||
|
"fee_fast": "Veloz",
|
||||||
|
"fee_medium": "Médio",
|
||||||
|
"fee_replace_min": "A taxa total (satoshi por byte) que você deseja pagar deve ser superior a {min} sat/byte",
|
||||||
|
"fee_satbyte": "em sat/byte",
|
||||||
|
"fee_slow": "Lento",
|
||||||
"header": "Enviar",
|
"header": "Enviar",
|
||||||
"input_clear": "Limpar",
|
"input_clear": "Limpar",
|
||||||
"input_done": "Feito",
|
"input_done": "Feito",
|
||||||
|
@ -217,7 +226,7 @@
|
||||||
"currency": "Moeda",
|
"currency": "Moeda",
|
||||||
"currency_source": "Os preços são obtidos no CoinDesk",
|
"currency_source": "Os preços são obtidos no CoinDesk",
|
||||||
"default_desc": "Quando desativado, o BlueWallet abrirá imediatamente a carteira selecionada no lançamento.",
|
"default_desc": "Quando desativado, o BlueWallet abrirá imediatamente a carteira selecionada no lançamento.",
|
||||||
"default_info": "Padrão em",
|
"default_info": "Carteira padrão",
|
||||||
"default_title": "No lançamento",
|
"default_title": "No lançamento",
|
||||||
"default_wallets": "Ver todas as carteiras",
|
"default_wallets": "Ver todas as carteiras",
|
||||||
"electrum_connected": "Conectado",
|
"electrum_connected": "Conectado",
|
||||||
|
@ -243,6 +252,7 @@
|
||||||
"general_adv_mode_e": "Quando ativado, você verá opções avançadas, como diferentes tipos de carteira, a capacidade de especificar a instância do LNDHub à qual deseja se conectar e a entropia personalizada durante a criação da carteira.",
|
"general_adv_mode_e": "Quando ativado, você verá opções avançadas, como diferentes tipos de carteira, a capacidade de especificar a instância do LNDHub à qual deseja se conectar e a entropia personalizada durante a criação da carteira.",
|
||||||
"general_continuity": "Continuidade",
|
"general_continuity": "Continuidade",
|
||||||
"general_continuity_e": "Quando ativado, você poderá visualizar carteiras selecionadas e transações, usando seus outros dispositivos conectados ao Apple iCloud.",
|
"general_continuity_e": "Quando ativado, você poderá visualizar carteiras selecionadas e transações, usando seus outros dispositivos conectados ao Apple iCloud.",
|
||||||
|
"groundcontrol_explanation": "GroundControl é um servidor de notificações push de código aberto gratuito para carteiras bitcoin. Você pode instalar seu próprio servidor GroundControl e colocar sua URL aqui para não depender da infraestrutura da BlueWallet. Deixe em branco para usar o padrão",
|
||||||
"header": "definições",
|
"header": "definições",
|
||||||
"language": "Idioma",
|
"language": "Idioma",
|
||||||
"language_restart": "Ao selecionar um novo idioma, pode ser necessário reiniciar a BlueWallet para que a alteração tenha efeito.",
|
"language_restart": "Ao selecionar um novo idioma, pode ser necessário reiniciar a BlueWallet para que a alteração tenha efeito.",
|
||||||
|
@ -253,17 +263,16 @@
|
||||||
"network": "Rede",
|
"network": "Rede",
|
||||||
"network_broadcast": "Divulgar transação",
|
"network_broadcast": "Divulgar transação",
|
||||||
"network_electrum": "Servidor Electrum",
|
"network_electrum": "Servidor Electrum",
|
||||||
|
"not_a_valid_uri": "Não é um URI válido",
|
||||||
|
"notifications": "Notificações",
|
||||||
"password": "Senha",
|
"password": "Senha",
|
||||||
"password_explain": "Definir a senha para descriptografar os arquivos",
|
"password_explain": "Definir a senha para descriptografar os arquivos",
|
||||||
"passwords_do_not_match": "Senhas não conferem",
|
"passwords_do_not_match": "Senhas não conferem",
|
||||||
"plausible_deniability": "Negação plausível...",
|
"plausible_deniability": "Negação plausível...",
|
||||||
"retype_password": "Inserir senha novamente",
|
|
||||||
"notifications": "Notificações",
|
|
||||||
"save": "Salvar",
|
|
||||||
"saved": "Salvo",
|
|
||||||
"not_a_valid_uri": "Não é um URI válido",
|
|
||||||
"push_notifications": "Notificações via push",
|
"push_notifications": "Notificações via push",
|
||||||
"groundcontrol_explanation": "GroundControl é um servidor de notificações push de código aberto gratuito para carteiras bitcoin. Você pode instalar seu próprio servidor GroundControl e colocar sua URL aqui para não depender da infraestrutura da BlueWallet. Deixe em branco para usar o padrão"
|
"retype_password": "Inserir senha novamente",
|
||||||
|
"save": "Salvar",
|
||||||
|
"saved": "Salvo"
|
||||||
},
|
},
|
||||||
"transactions": {
|
"transactions": {
|
||||||
"cancel_explain": "Substituiremos esta transação por aquela que lhe paga e tem taxas mais altas. Isso efetivamente cancela a transação. Isso é chamado de RBF - Substituir por Taxa.",
|
"cancel_explain": "Substituiremos esta transação por aquela que lhe paga e tem taxas mais altas. Isso efetivamente cancela a transação. Isso é chamado de RBF - Substituir por Taxa.",
|
||||||
|
@ -284,13 +293,13 @@
|
||||||
"details_to": "Para",
|
"details_to": "Para",
|
||||||
"details_transaction_details": "Detalhes",
|
"details_transaction_details": "Detalhes",
|
||||||
"enable_hw": "Esta carteira não está sendo usada em conjunto com uma carteira de hardware. Gostaria de habilitar o uso de carteira de hardware?",
|
"enable_hw": "Esta carteira não está sendo usada em conjunto com uma carteira de hardware. Gostaria de habilitar o uso de carteira de hardware?",
|
||||||
"list_conf": "conf",
|
"list_conf": "conf: {number}",
|
||||||
"list_title": "Transações",
|
"list_title": "Transações",
|
||||||
"transactions_count": "contagem de transações",
|
|
||||||
"rbf_explain": "Substituiremos essa transação por outra com uma taxa mais alta, portanto, ela deve ser confirmada mais rapidamente. Isso é chamado de RBF - Substituir por Taxa.",
|
"rbf_explain": "Substituiremos essa transação por outra com uma taxa mais alta, portanto, ela deve ser confirmada mais rapidamente. Isso é chamado de RBF - Substituir por Taxa.",
|
||||||
"rbf_title": "Aumento de taxa (RBF)",
|
"rbf_title": "Aumento de taxa (RBF)",
|
||||||
"status_bump": "Aumento de taxa",
|
"status_bump": "Aumento de taxa",
|
||||||
"status_cancel": "Cancelar Transação"
|
"status_cancel": "Cancelar Transação",
|
||||||
|
"transactions_count": "contagem de transações"
|
||||||
},
|
},
|
||||||
"wallets": {
|
"wallets": {
|
||||||
"add_bitcoin": "Bitcoin",
|
"add_bitcoin": "Bitcoin",
|
||||||
|
@ -334,7 +343,7 @@
|
||||||
"import_error": "Erro. Por favor, confira se o formato que você passou é válido.",
|
"import_error": "Erro. Por favor, confira se o formato que você passou é válido.",
|
||||||
"import_explanation": "Escreva aqui sua frase mnemônica, chave privada, WIF, ou o que você tiver. Faremos nosso melhor para adivinhar o formato e importat sua carteira",
|
"import_explanation": "Escreva aqui sua frase mnemônica, chave privada, WIF, ou o que você tiver. Faremos nosso melhor para adivinhar o formato e importat sua carteira",
|
||||||
"import_imported": "Importada",
|
"import_imported": "Importada",
|
||||||
"import_scan_qr": "ou ler um código QR?",
|
"import_scan_qr": "Ler um código QR ou arquivo",
|
||||||
"import_success": "Sucesso",
|
"import_success": "Sucesso",
|
||||||
"import_title": "importar",
|
"import_title": "importar",
|
||||||
"list_create_a_button": "Criar agora",
|
"list_create_a_button": "Criar agora",
|
||||||
|
@ -353,7 +362,7 @@
|
||||||
"list_long_clipboard": "Copiar da área de transferência",
|
"list_long_clipboard": "Copiar da área de transferência",
|
||||||
"list_long_scan": "Ler QR Code",
|
"list_long_scan": "Ler QR Code",
|
||||||
"take_photo": "Registrar Foto",
|
"take_photo": "Registrar Foto",
|
||||||
"list_tap_here_to_buy": "Toque aqui para comprar Bitcoin",
|
"list_tap_here_to_buy": "Comprar Bitcoin",
|
||||||
"list_title": "carteiras",
|
"list_title": "carteiras",
|
||||||
"list_tryagain": "Tente Novamente",
|
"list_tryagain": "Tente Novamente",
|
||||||
"reorder_title": "Reordenar carteiras",
|
"reorder_title": "Reordenar carteiras",
|
||||||
|
|
|
@ -170,7 +170,7 @@
|
||||||
"details_next": "Próximo",
|
"details_next": "Próximo",
|
||||||
"details_no_maximum": "A carteira selecionada não suporta cálculo automático de saldo máximo. Tem a certeza que deseja selecionar esta carteira?",
|
"details_no_maximum": "A carteira selecionada não suporta cálculo automático de saldo máximo. Tem a certeza que deseja selecionar esta carteira?",
|
||||||
"details_no_multiple": "A carteira selecionada não suporta o envio de Bitcoins para vários destinatários. Tem a certeza que deseja selecionar esta carteira?",
|
"details_no_multiple": "A carteira selecionada não suporta o envio de Bitcoins para vários destinatários. Tem a certeza que deseja selecionar esta carteira?",
|
||||||
"details_no_signed_tx": "O ficheiro seleccionado não contém uma transacção assinada que possa ser importada.",
|
"details_no_signed_tx": "O ficheiro seleccionado não contém uma transacção que possa ser importada.",
|
||||||
"details_note_placeholder": "Nota pessoal",
|
"details_note_placeholder": "Nota pessoal",
|
||||||
"details_scan": "Scan",
|
"details_scan": "Scan",
|
||||||
"details_total_exceeds_balance": "O valor total excede o saldo disponível.",
|
"details_total_exceeds_balance": "O valor total excede o saldo disponível.",
|
||||||
|
@ -181,6 +181,15 @@
|
||||||
"dynamic_prev": "Anterior",
|
"dynamic_prev": "Anterior",
|
||||||
"dynamic_start": "Começar",
|
"dynamic_start": "Começar",
|
||||||
"dynamic_stop": "Parar",
|
"dynamic_stop": "Parar",
|
||||||
|
"fee_10m": "10m",
|
||||||
|
"fee_1d": "1d",
|
||||||
|
"fee_3h": "3h",
|
||||||
|
"fee_custom": "Escolher",
|
||||||
|
"fee_fast": "Rápido",
|
||||||
|
"fee_medium": "Médio",
|
||||||
|
"fee_replace_min": "A taxa total (satoshi/byte) que deseja pagar deve ser superior a {min} sat/byte",
|
||||||
|
"fee_satbyte": "em sat/byte",
|
||||||
|
"fee_slow": "Lento",
|
||||||
"header": "Enviar",
|
"header": "Enviar",
|
||||||
"input_clear": "Limpar",
|
"input_clear": "Limpar",
|
||||||
"input_done": "Feito",
|
"input_done": "Feito",
|
||||||
|
@ -217,7 +226,7 @@
|
||||||
"currency": "Moeda",
|
"currency": "Moeda",
|
||||||
"currency_source": "Os preços são obtidos no CoinDesk",
|
"currency_source": "Os preços são obtidos no CoinDesk",
|
||||||
"default_desc": "Quando desactivado, a BlueWallet abrirá imediatamente a carteira seleccionada no lançamento.",
|
"default_desc": "Quando desactivado, a BlueWallet abrirá imediatamente a carteira seleccionada no lançamento.",
|
||||||
"default_info": "Padrão em",
|
"default_info": "Carteira padrão",
|
||||||
"default_title": "A abrir",
|
"default_title": "A abrir",
|
||||||
"default_wallets": "Ver todas as carteiras",
|
"default_wallets": "Ver todas as carteiras",
|
||||||
"electrum_connected": "Conectado",
|
"electrum_connected": "Conectado",
|
||||||
|
@ -243,6 +252,7 @@
|
||||||
"general_adv_mode_e": "Quando activado, verá opções avançadas, como diferentes tipos de carteira, a capacidade de especificar a instância do LNDHub à qual se deseja conectar e a entropia personalizada durante a criação da carteira.",
|
"general_adv_mode_e": "Quando activado, verá opções avançadas, como diferentes tipos de carteira, a capacidade de especificar a instância do LNDHub à qual se deseja conectar e a entropia personalizada durante a criação da carteira.",
|
||||||
"general_continuity": "Continuidade",
|
"general_continuity": "Continuidade",
|
||||||
"general_continuity_e": "Quando activado, poderá visualizar carteiras seleccionadas e transacções, usando os seus outros dispositivos conectados ao Apple iCloud.",
|
"general_continuity_e": "Quando activado, poderá visualizar carteiras seleccionadas e transacções, usando os seus outros dispositivos conectados ao Apple iCloud.",
|
||||||
|
"groundcontrol_explanation": "GroundControl é um servidor de notificações push de código aberto gratuito para carteiras bitcoin. Pode instalar seu próprio servidor GroundControl e colocar sua URL aqui para não depender da infraestrutura da BlueWallet. Deixe em branco para usar o padrão",
|
||||||
"header": "definições",
|
"header": "definições",
|
||||||
"language": "Idioma",
|
"language": "Idioma",
|
||||||
"language_restart": "Ao selecionar um novo idioma, pode ser necessário reiniciar a BlueWallet para que a alteração tenha efeito.",
|
"language_restart": "Ao selecionar um novo idioma, pode ser necessário reiniciar a BlueWallet para que a alteração tenha efeito.",
|
||||||
|
@ -253,17 +263,16 @@
|
||||||
"network": "Rede",
|
"network": "Rede",
|
||||||
"network_broadcast": "Transmitir transacção",
|
"network_broadcast": "Transmitir transacção",
|
||||||
"network_electrum": "Electrum server",
|
"network_electrum": "Electrum server",
|
||||||
|
"not_a_valid_uri": "Não é um URI válido",
|
||||||
|
"notifications": "Notificações",
|
||||||
"password": "Password",
|
"password": "Password",
|
||||||
"password_explain": "Definir a password para desencriptar o armazenamento",
|
"password_explain": "Definir a password para desencriptar o armazenamento",
|
||||||
"passwords_do_not_match": "Passwords não coincidem",
|
"passwords_do_not_match": "Passwords não coincidem",
|
||||||
"plausible_deniability": "Negação plausível...",
|
"plausible_deniability": "Negação plausível...",
|
||||||
"retype_password": "Inserir password novamente",
|
|
||||||
"notifications": "Notificações",
|
|
||||||
"save": "Guardar",
|
|
||||||
"saved": "Guardado",
|
|
||||||
"not_a_valid_uri": "Não é um URI válido",
|
|
||||||
"push_notifications": "Notificações via push",
|
"push_notifications": "Notificações via push",
|
||||||
"groundcontrol_explanation": "GroundControl é um servidor de notificações push de código aberto gratuito para carteiras bitcoin. Pode instalar seu próprio servidor GroundControl e colocar sua URL aqui para não depender da infraestrutura da BlueWallet. Deixe em branco para usar o padrão"
|
"retype_password": "Inserir password novamente",
|
||||||
|
"save": "Guardar",
|
||||||
|
"saved": "Guardado"
|
||||||
},
|
},
|
||||||
"transactions": {
|
"transactions": {
|
||||||
"cancel_explain": "Substituiremos esta transacção por aquela que lhe paga e tem taxas mais altas. Isso efectivamente cancela a transacção. Isto é chamado de RBF - Substituir por Taxa.",
|
"cancel_explain": "Substituiremos esta transacção por aquela que lhe paga e tem taxas mais altas. Isso efectivamente cancela a transacção. Isto é chamado de RBF - Substituir por Taxa.",
|
||||||
|
@ -286,11 +295,11 @@
|
||||||
"enable_hw": "Esta carteira não está a ser usada em conjunto com uma carteira de hardware. Gostaria de habilitar o uso de carteira de hardware?",
|
"enable_hw": "Esta carteira não está a ser usada em conjunto com uma carteira de hardware. Gostaria de habilitar o uso de carteira de hardware?",
|
||||||
"list_conf": "conf",
|
"list_conf": "conf",
|
||||||
"list_title": "transacções",
|
"list_title": "transacções",
|
||||||
"transactions_count": "contagem de transações",
|
|
||||||
"rbf_explain": "Substituiremos esta transacção por outra com uma taxa mais alta, portanto, ela deve ser confirmada mais rapidamente. Isto é chamado de RBF - Substituir por Taxa.",
|
"rbf_explain": "Substituiremos esta transacção por outra com uma taxa mais alta, portanto, ela deve ser confirmada mais rapidamente. Isto é chamado de RBF - Substituir por Taxa.",
|
||||||
"rbf_title": "Aumento de taxa (RBF)",
|
"rbf_title": "Aumento de taxa (RBF)",
|
||||||
"status_bump": "Aumento de taxa",
|
"status_bump": "Aumento de taxa",
|
||||||
"status_cancel": "Cancelar transacção"
|
"status_cancel": "Cancelar transacção",
|
||||||
|
"transactions_count": "contagem de transações"
|
||||||
},
|
},
|
||||||
"wallets": {
|
"wallets": {
|
||||||
"add_bitcoin": "Bitcoin",
|
"add_bitcoin": "Bitcoin",
|
||||||
|
|
35
loc/ru.json
35
loc/ru.json
|
@ -170,9 +170,9 @@
|
||||||
"details_next": "Дальше",
|
"details_next": "Дальше",
|
||||||
"details_no_maximum": "Этот кошелёк не поддерживает отправку всех средств. Ты уверен что хочешь выбрать его?",
|
"details_no_maximum": "Этот кошелёк не поддерживает отправку всех средств. Ты уверен что хочешь выбрать его?",
|
||||||
"details_no_multiple": "Этот кошелёк не поддерживает отправку нескольким получателям. Ты уверен что хочешь выбрать его?",
|
"details_no_multiple": "Этот кошелёк не поддерживает отправку нескольким получателям. Ты уверен что хочешь выбрать его?",
|
||||||
"details_no_signed_tx": "В файле нет подписаных транзакций которые можно импортировать",
|
"details_no_signed_tx": "В файле нет транзакций которые можно импортировать.",
|
||||||
"details_note_placeholder": "примечание платежа",
|
"details_note_placeholder": "примечание платежа",
|
||||||
"details_scan": "Отсканировать QR",
|
"details_scan": "Скан",
|
||||||
"details_total_exceeds_balance": "Общая сумма превышает баланс.",
|
"details_total_exceeds_balance": "Общая сумма превышает баланс.",
|
||||||
"details_wallet_before_tx": "Перед созданием транзакции нужно сделать Bitcoin кошелёк.",
|
"details_wallet_before_tx": "Перед созданием транзакции нужно сделать Bitcoin кошелёк.",
|
||||||
"details_wallet_selection": "Выбор Кошелька",
|
"details_wallet_selection": "Выбор Кошелька",
|
||||||
|
@ -181,6 +181,15 @@
|
||||||
"dynamic_prev": "Предыдущий",
|
"dynamic_prev": "Предыдущий",
|
||||||
"dynamic_start": "Старт",
|
"dynamic_start": "Старт",
|
||||||
"dynamic_stop": "Стоп",
|
"dynamic_stop": "Стоп",
|
||||||
|
"fee_10m": "10м",
|
||||||
|
"fee_1d": "1д",
|
||||||
|
"fee_3h": "3ч",
|
||||||
|
"fee_custom": "Другое",
|
||||||
|
"fee_fast": "Быстро",
|
||||||
|
"fee_medium": "Средне",
|
||||||
|
"fee_replace_min": "Комиссия (сатоши за байт) которую нужно заплатить должна быть выше {min} сатоши/байт",
|
||||||
|
"fee_satbyte": "сатоши/байт",
|
||||||
|
"fee_slow": "Медленно",
|
||||||
"header": "Отправить",
|
"header": "Отправить",
|
||||||
"input_clear": "Очистить",
|
"input_clear": "Очистить",
|
||||||
"input_done": "Готово",
|
"input_done": "Готово",
|
||||||
|
@ -188,6 +197,7 @@
|
||||||
"input_total": "Всего:",
|
"input_total": "Всего:",
|
||||||
"permission_camera_message": "Нужно ваше разрешение на использование камеры",
|
"permission_camera_message": "Нужно ваше разрешение на использование камеры",
|
||||||
"permission_camera_title": "Разрешите пользоваться камерой",
|
"permission_camera_title": "Разрешите пользоваться камерой",
|
||||||
|
"open_settings": "Открыть настройки",
|
||||||
"permission_storage_later": "Спроси меня позже",
|
"permission_storage_later": "Спроси меня позже",
|
||||||
"permission_storage_message": "BlueWallet нужно ваше разрешение для доступа к хранилищу файлов, чтобы сохранить эту транзакцию.",
|
"permission_storage_message": "BlueWallet нужно ваше разрешение для доступа к хранилищу файлов, чтобы сохранить эту транзакцию.",
|
||||||
"permission_storage_title": "Разрешите сохранять файлы",
|
"permission_storage_title": "Разрешите сохранять файлы",
|
||||||
|
@ -242,6 +252,7 @@
|
||||||
"general_adv_mode_e": "При включении вы увидите расширенные параметры, такие как разные типы кошельков, возможность указать экземпляр LNDHub, к которому вы хотите подключиться, и пользовательскую энтропию при создании кошелька.",
|
"general_adv_mode_e": "При включении вы увидите расширенные параметры, такие как разные типы кошельков, возможность указать экземпляр LNDHub, к которому вы хотите подключиться, и пользовательскую энтропию при создании кошелька.",
|
||||||
"general_continuity": "Непрерывность",
|
"general_continuity": "Непрерывность",
|
||||||
"general_continuity_e": "Когда эта функция включена, вы сможете просматривать выбранные кошельки и транзакции, используя другие устройства, подключенные к Apple iCloud.",
|
"general_continuity_e": "Когда эта функция включена, вы сможете просматривать выбранные кошельки и транзакции, используя другие устройства, подключенные к Apple iCloud.",
|
||||||
|
"groundcontrol_explanation": "GroundControl - это бесплатный сервер push-уведомлений с открытым исходным кодом для биткойн-кошельков. Вы можете установить свой собственный сервер GroundControl и указать здесь его URL, чтобы не полагаться на инфраструктуру BlueWallet. Оставьте пустым, чтобы использовать по умолчанию",
|
||||||
"header": "Настройки",
|
"header": "Настройки",
|
||||||
"language": "Язык",
|
"language": "Язык",
|
||||||
"language_restart": "При выборе нового языка может потребоваться перезапуск BlueWallet, чтобы изменения вступили в силу.",
|
"language_restart": "При выборе нового языка может потребоваться перезапуск BlueWallet, чтобы изменения вступили в силу.",
|
||||||
|
@ -252,17 +263,16 @@
|
||||||
"network": "Сеть",
|
"network": "Сеть",
|
||||||
"network_broadcast": "Отправить транзакцию",
|
"network_broadcast": "Отправить транзакцию",
|
||||||
"network_electrum": "Electrum сервер",
|
"network_electrum": "Electrum сервер",
|
||||||
|
"not_a_valid_uri": "Неверный URI",
|
||||||
|
"notifications": "Уведомления",
|
||||||
"password": "Пароль",
|
"password": "Пароль",
|
||||||
"password_explain": "Придумай пароль для расшифровки хранилища",
|
"password_explain": "Придумай пароль для расшифровки хранилища",
|
||||||
"passwords_do_not_match": "Пароли не совпадают",
|
"passwords_do_not_match": "Пароли не совпадают",
|
||||||
"plausible_deniability": "Правдоподобная имитация...",
|
"plausible_deniability": "Правдоподобная имитация...",
|
||||||
"retype_password": "Набери пароль повторно",
|
|
||||||
"notifications": "Уведомления",
|
|
||||||
"save": "Сохранить",
|
|
||||||
"saved": "Сохранено",
|
|
||||||
"not_a_valid_uri": "Неверный URI",
|
|
||||||
"push_notifications": "Push-уведомления",
|
"push_notifications": "Push-уведомления",
|
||||||
"groundcontrol_explanation": "GroundControl - это бесплатный сервер push-уведомлений с открытым исходным кодом для биткойн-кошельков. Вы можете установить свой собственный сервер GroundControl и указать здесь его URL, чтобы не полагаться на инфраструктуру BlueWallet. Оставьте пустым, чтобы использовать по умолчанию"
|
"retype_password": "Набери пароль повторно",
|
||||||
|
"save": "Сохранить",
|
||||||
|
"saved": "Сохранено"
|
||||||
},
|
},
|
||||||
"transactions": {
|
"transactions": {
|
||||||
"cancel_explain": "Мы заменим эту транзакцию другой, которая платит вам и имеет более высокую комиссию. Это отменит предыдущую транзакцию. Это называется RBF - Replace By Fee.",
|
"cancel_explain": "Мы заменим эту транзакцию другой, которая платит вам и имеет более высокую комиссию. Это отменит предыдущую транзакцию. Это называется RBF - Replace By Fee.",
|
||||||
|
@ -283,13 +293,13 @@
|
||||||
"details_to": "Кому",
|
"details_to": "Кому",
|
||||||
"details_transaction_details": "Детали транзакции",
|
"details_transaction_details": "Детали транзакции",
|
||||||
"enable_hw": "Кошелек не используется вместе с аппаратным. Вы хотите включить поддержку аппаратного кошелека?",
|
"enable_hw": "Кошелек не используется вместе с аппаратным. Вы хотите включить поддержку аппаратного кошелека?",
|
||||||
"list_conf": "подтв.",
|
"list_conf": "{number} подтв.",
|
||||||
"list_title": "Мои транзакции",
|
"list_title": "Мои транзакции",
|
||||||
"transactions_count": "кол-во транзакций",
|
|
||||||
"rbf_explain": "Мы заменим эту транзакцию другой с более высокой комиссией, поэтому будет обработана быстрее. Это называется RBF - Replace By Fee.",
|
"rbf_explain": "Мы заменим эту транзакцию другой с более высокой комиссией, поэтому будет обработана быстрее. Это называется RBF - Replace By Fee.",
|
||||||
"rbf_title": "Повысить комиссию (RBF)",
|
"rbf_title": "Повысить комиссию (RBF)",
|
||||||
"status_bump": "Повысить комиссию",
|
"status_bump": "Повысить комиссию",
|
||||||
"status_cancel": "Отменить транзакцию"
|
"status_cancel": "Отменить транзакцию",
|
||||||
|
"transactions_count": "кол-во транзакций"
|
||||||
},
|
},
|
||||||
"wallets": {
|
"wallets": {
|
||||||
"add_bitcoin": "Bitcoin",
|
"add_bitcoin": "Bitcoin",
|
||||||
|
@ -298,6 +308,7 @@
|
||||||
"add_entropy_provide": "Сгенерировать энторию с помощью игральных костей",
|
"add_entropy_provide": "Сгенерировать энторию с помощью игральных костей",
|
||||||
"add_entropy_remain": "{gen} байтов сгенерированной энтории. Оставшиеся {rem} байт будут получены из системного генератора случайных чисел.",
|
"add_entropy_remain": "{gen} байтов сгенерированной энтории. Оставшиеся {rem} байт будут получены из системного генератора случайных чисел.",
|
||||||
"add_import_wallet": "Импортировать кошелек",
|
"add_import_wallet": "Импортировать кошелек",
|
||||||
|
"import_file": "Импортировать файл",
|
||||||
"add_lightning": "Lightning",
|
"add_lightning": "Lightning",
|
||||||
"add_lndhub": "Подключиться к своему LNDHub",
|
"add_lndhub": "Подключиться к своему LNDHub",
|
||||||
"add_lndhub_error": "Не верный адрес LNDHub.",
|
"add_lndhub_error": "Не верный адрес LNDHub.",
|
||||||
|
@ -343,7 +354,7 @@
|
||||||
"list_empty_txs1_lightning": "Lightning кошелек отлично подходит для ежедневных транзакций. Комиссия несправедливо мала, а скорость невероятно высока.",
|
"list_empty_txs1_lightning": "Lightning кошелек отлично подходит для ежедневных транзакций. Комиссия несправедливо мала, а скорость невероятно высока.",
|
||||||
"list_empty_txs2": "Пока транзакций нет ",
|
"list_empty_txs2": "Пока транзакций нет ",
|
||||||
"list_empty_txs2_lightning": "\nДля начала использования нажми \"Мои средства\" и пополни баланс.",
|
"list_empty_txs2_lightning": "\nДля начала использования нажми \"Мои средства\" и пополни баланс.",
|
||||||
"list_header": "Кошелек - это секретный (приватный) ключ и соответствующий ему адрес на который можно получать Bitcoin",
|
"list_header": "Кошелек - пара ключей, один приватный, другой - публичный используется, чтобы получить Bitcoin.",
|
||||||
"list_import_error": "При импорте этого кошелька возникла ошибка.",
|
"list_import_error": "При импорте этого кошелька возникла ошибка.",
|
||||||
"list_import_problem": "Ошибка при импорте кошелька",
|
"list_import_problem": "Ошибка при импорте кошелька",
|
||||||
"list_latest_transaction": "Последняя транзакция",
|
"list_latest_transaction": "Последняя транзакция",
|
||||||
|
|
|
@ -170,13 +170,13 @@
|
||||||
"details_next": "Ďalej",
|
"details_next": "Ďalej",
|
||||||
"details_no_maximum": "The selected wallet does not support automatic maximum balance calculation. Are you sure to want to select this wallet?",
|
"details_no_maximum": "The selected wallet does not support automatic maximum balance calculation. Are you sure to want to select this wallet?",
|
||||||
"details_no_multiple": "The selected wallet does not support sending Bitcoin to multiple recipients. Are you sure to want to select this wallet?",
|
"details_no_multiple": "The selected wallet does not support sending Bitcoin to multiple recipients. Are you sure to want to select this wallet?",
|
||||||
"details_no_signed_tx": "The selected file does not contain a signed transaction that can be imported.",
|
"details_no_signed_tx": "The selected file does not contain a transaction that can be imported.",
|
||||||
"details_note_placeholder": "poznámka pre seba",
|
"details_note_placeholder": "poznámka pre seba",
|
||||||
"details_scan": "Skenovať",
|
"details_scan": "Skenovať",
|
||||||
"details_total_exceeds_balance": "Čiastka, ktorú chcete poslať, presahuje dostupný zostatok.",
|
"details_total_exceeds_balance": "Čiastka, ktorú chcete poslať, presahuje dostupný zostatok.",
|
||||||
"details_wallet_before_tx": "Pred vytvorením transakcie potrebujete najprv pridať Bitcoinovú peňaženku.",
|
"details_wallet_before_tx": "Pred vytvorením transakcie potrebujete najprv pridať Bitcoinovú peňaženku.",
|
||||||
"details_wallet_selection": "Výber peňaženky",
|
"details_wallet_selection": "Výber peňaženky",
|
||||||
"dynamic_init": "Initialing",
|
"dynamic_init": "Initializing",
|
||||||
"dynamic_next": "Next",
|
"dynamic_next": "Next",
|
||||||
"dynamic_prev": "Previous",
|
"dynamic_prev": "Previous",
|
||||||
"dynamic_start": "Start",
|
"dynamic_start": "Start",
|
||||||
|
|
|
@ -8,7 +8,9 @@
|
||||||
"of": "{number} od {total}",
|
"of": "{number} od {total}",
|
||||||
"ok": "OK",
|
"ok": "OK",
|
||||||
"storage_is_encrypted": "Shramba je šifrirana. Za dešifriranje je potrebno geslo",
|
"storage_is_encrypted": "Shramba je šifrirana. Za dešifriranje je potrebno geslo",
|
||||||
"yes": "Da"
|
"yes": "Da",
|
||||||
|
"invalid_animated_qr_code_fragment" : "Neveljaven del animirane QR kode, prosimo poskusite ponovno",
|
||||||
|
"file_saved": "Datoteka ({filePath}) je bila shranjena v mapo Prenosi."
|
||||||
},
|
},
|
||||||
"azteco": {
|
"azteco": {
|
||||||
"codeIs": "Koda vašega bona je",
|
"codeIs": "Koda vašega bona je",
|
||||||
|
@ -170,7 +172,7 @@
|
||||||
"details_next": "Naprej",
|
"details_next": "Naprej",
|
||||||
"details_no_maximum": "Izbrana denarnica ne podpira samodejnega izračuna največjega stanja. Ali ste prepričani, da želite izbrati to denarnico?",
|
"details_no_maximum": "Izbrana denarnica ne podpira samodejnega izračuna največjega stanja. Ali ste prepričani, da želite izbrati to denarnico?",
|
||||||
"details_no_multiple": "Izbrana denarnica ne podpira pošiljanja več prejemnikom. Ali ste prepričani, da želite izbrati to denarnico?",
|
"details_no_multiple": "Izbrana denarnica ne podpira pošiljanja več prejemnikom. Ali ste prepričani, da želite izbrati to denarnico?",
|
||||||
"details_no_signed_tx": "Izbrana datoteka ne vsebuje podpisane transakcije, ki jo je mogoče uvoziti.",
|
"details_no_signed_tx": "Izbrana datoteka ne vsebuje transakcije, ki jo je mogoče uvoziti.",
|
||||||
"details_note_placeholder": "lastna opomba",
|
"details_note_placeholder": "lastna opomba",
|
||||||
"details_scan": "Skeniraj",
|
"details_scan": "Skeniraj",
|
||||||
"details_total_exceeds_balance": "Znesek presega razpoložljivo stanje.",
|
"details_total_exceeds_balance": "Znesek presega razpoložljivo stanje.",
|
||||||
|
@ -209,7 +211,8 @@
|
||||||
"qr_error_no_qrcode": "Izbrana slika ne vsebuje QR kode.",
|
"qr_error_no_qrcode": "Izbrana slika ne vsebuje QR kode.",
|
||||||
"qr_error_no_wallet": "Izbrana datoteka ne vsebuje denarnice, ki jo je mogoče uvoziti.",
|
"qr_error_no_wallet": "Izbrana datoteka ne vsebuje denarnice, ki jo je mogoče uvoziti.",
|
||||||
"success_done": "Končano",
|
"success_done": "Končano",
|
||||||
"txSaved": "Transakcijska datoteka ({filePath}) je bila shranjena v mapo Prenosi."
|
"txSaved": "Transakcijska datoteka ({filePath}) je bila shranjena v mapo Prenosi.",
|
||||||
|
"problem_with_psbt": "Težava s PSBT"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"about": "O aplikaciji",
|
"about": "O aplikaciji",
|
||||||
|
@ -293,7 +296,7 @@
|
||||||
"details_to": "Izhod",
|
"details_to": "Izhod",
|
||||||
"details_transaction_details": "Podrobnosti transakcije",
|
"details_transaction_details": "Podrobnosti transakcije",
|
||||||
"enable_hw": "Ta denarnica se ne uporablja skupaj s strojno denarnico. Ali želite omogočiti uporabo strojne denarnice?",
|
"enable_hw": "Ta denarnica se ne uporablja skupaj s strojno denarnico. Ali želite omogočiti uporabo strojne denarnice?",
|
||||||
"list_conf": "potrd",
|
"list_conf": "potrd: {number}",
|
||||||
"list_title": "transakcije",
|
"list_title": "transakcije",
|
||||||
"rbf_explain": "To transakcijo bomo nadomestili z novo, ki plača višjo omrežnino, zato bi morala biti potrditev hitrejša. To se imenuje RBF - Replace By Fee.",
|
"rbf_explain": "To transakcijo bomo nadomestili z novo, ki plača višjo omrežnino, zato bi morala biti potrditev hitrejša. To se imenuje RBF - Replace By Fee.",
|
||||||
"rbf_title": "Povečaj omrežnino (RBF)",
|
"rbf_title": "Povečaj omrežnino (RBF)",
|
||||||
|
@ -371,5 +374,17 @@
|
||||||
"select_wallet": "Izberite Denarnico",
|
"select_wallet": "Izberite Denarnico",
|
||||||
"xpub_copiedToClipboard": "Kopirano v odložišče.",
|
"xpub_copiedToClipboard": "Kopirano v odložišče.",
|
||||||
"xpub_title": "XPUB denarnice"
|
"xpub_title": "XPUB denarnice"
|
||||||
|
},
|
||||||
|
"multisig": {
|
||||||
|
"provide_signature": "Vnesite podpis",
|
||||||
|
"vault_key": "Ključ trezorja {number}",
|
||||||
|
"fee": "Omrežnina: {number}",
|
||||||
|
"fee_btc": "{number} BTC",
|
||||||
|
"confirm": "Potrditev",
|
||||||
|
"header": "Pošlji",
|
||||||
|
"share": "Deli",
|
||||||
|
"how_many_signatures_can_bluewallet_make": "koliko podpisov lahko naredi bluewallet",
|
||||||
|
"scan_or_import_file": "Skenirajte ali uvozite datoteko",
|
||||||
|
"export_coordination_setup": "izvoz koordinacijskih nastavitev"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -170,13 +170,13 @@
|
||||||
"details_next": "Nästa",
|
"details_next": "Nästa",
|
||||||
"details_no_maximum": "The selected wallet does not support automatic maximum balance calculation. Are you sure to want to select this wallet?",
|
"details_no_maximum": "The selected wallet does not support automatic maximum balance calculation. Are you sure to want to select this wallet?",
|
||||||
"details_no_multiple": "The selected wallet does not support sending Bitcoin to multiple recipients. Are you sure to want to select this wallet?",
|
"details_no_multiple": "The selected wallet does not support sending Bitcoin to multiple recipients. Are you sure to want to select this wallet?",
|
||||||
"details_no_signed_tx": "The selected file does not contain a signed transaction that can be imported.",
|
"details_no_signed_tx": "The selected file does not contain a transaction that can be imported.",
|
||||||
"details_note_placeholder": "egen notering",
|
"details_note_placeholder": "egen notering",
|
||||||
"details_scan": "Skanna",
|
"details_scan": "Skanna",
|
||||||
"details_total_exceeds_balance": "Beloppet överstiger plånbokens tillgängliga belopp",
|
"details_total_exceeds_balance": "Beloppet överstiger plånbokens tillgängliga belopp",
|
||||||
"details_wallet_before_tx": "Before creating a transaction, you must first add a Bitcoin wallet.",
|
"details_wallet_before_tx": "Before creating a transaction, you must first add a Bitcoin wallet.",
|
||||||
"details_wallet_selection": "Wallet Selection",
|
"details_wallet_selection": "Wallet Selection",
|
||||||
"dynamic_init": "Initialing",
|
"dynamic_init": "Initializing",
|
||||||
"dynamic_next": "Nästa",
|
"dynamic_next": "Nästa",
|
||||||
"dynamic_prev": "Föregående",
|
"dynamic_prev": "Föregående",
|
||||||
"dynamic_start": "Starta",
|
"dynamic_start": "Starta",
|
||||||
|
|
|
@ -170,7 +170,7 @@
|
||||||
"details_next": "ถัดไป",
|
"details_next": "ถัดไป",
|
||||||
"details_no_maximum": "The selected wallet does not support automatic maximum balance calculation. Are you sure to want to select this wallet?",
|
"details_no_maximum": "The selected wallet does not support automatic maximum balance calculation. Are you sure to want to select this wallet?",
|
||||||
"details_no_multiple": "The selected wallet does not support sending Bitcoin to multiple recipients. Are you sure to want to select this wallet?",
|
"details_no_multiple": "The selected wallet does not support sending Bitcoin to multiple recipients. Are you sure to want to select this wallet?",
|
||||||
"details_no_signed_tx": "The selected file does not contain a signed transaction that can be imported.",
|
"details_no_signed_tx": "The selected file does not contain a transaction that can be imported.",
|
||||||
"details_note_placeholder": "หมายเหตุถึงตัวท่านเอง",
|
"details_note_placeholder": "หมายเหตุถึงตัวท่านเอง",
|
||||||
"details_scan": "สแกน",
|
"details_scan": "สแกน",
|
||||||
"details_total_exceeds_balance": "จำนวนเงินที่จะส่งเกินเงินที่มี.",
|
"details_total_exceeds_balance": "จำนวนเงินที่จะส่งเกินเงินที่มี.",
|
||||||
|
|
|
@ -170,13 +170,13 @@
|
||||||
"details_next": "Next",
|
"details_next": "Next",
|
||||||
"details_no_maximum": "The selected wallet does not support automatic maximum balance calculation. Are you sure to want to select this wallet?",
|
"details_no_maximum": "The selected wallet does not support automatic maximum balance calculation. Are you sure to want to select this wallet?",
|
||||||
"details_no_multiple": "The selected wallet does not support sending Bitcoin to multiple recipients. Are you sure to want to select this wallet?",
|
"details_no_multiple": "The selected wallet does not support sending Bitcoin to multiple recipients. Are you sure to want to select this wallet?",
|
||||||
"details_no_signed_tx": "The selected file does not contain a signed transaction that can be imported.",
|
"details_no_signed_tx": "The selected file does not contain a transaction that can be imported.",
|
||||||
"details_note_placeholder": "kendime not",
|
"details_note_placeholder": "kendime not",
|
||||||
"details_scan": "Tara",
|
"details_scan": "Tara",
|
||||||
"details_total_exceeds_balance": "Gönderme miktarı mevcut bakiyeyi aşıyor.",
|
"details_total_exceeds_balance": "Gönderme miktarı mevcut bakiyeyi aşıyor.",
|
||||||
"details_wallet_before_tx": "Before creating a transaction, you must first add a Bitcoin wallet.",
|
"details_wallet_before_tx": "Before creating a transaction, you must first add a Bitcoin wallet.",
|
||||||
"details_wallet_selection": "Wallet Selection",
|
"details_wallet_selection": "Wallet Selection",
|
||||||
"dynamic_init": "Initialing",
|
"dynamic_init": "Initializing",
|
||||||
"dynamic_next": "Next",
|
"dynamic_next": "Next",
|
||||||
"dynamic_prev": "Previous",
|
"dynamic_prev": "Previous",
|
||||||
"dynamic_start": "Start",
|
"dynamic_start": "Start",
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
},
|
},
|
||||||
"azteco": {
|
"azteco": {
|
||||||
"codeIs": "Your voucher code is",
|
"codeIs": "Your voucher code is",
|
||||||
"errorBeforeRefeem": "Before redeeming you must first add a Bitcoin wallet.",
|
"errorBeforeRefeem": "你需先添加一个Bitcoin钱包",
|
||||||
"errorSomething": "Something went wrong. Is this voucher still valid?",
|
"errorSomething": "Something went wrong. Is this voucher still valid?",
|
||||||
"redeem": "Redeem to wallet",
|
"redeem": "Redeem to wallet",
|
||||||
"redeemButton": "Redeem",
|
"redeemButton": "Redeem",
|
||||||
|
@ -30,10 +30,10 @@
|
||||||
"network": "网络错误"
|
"network": "网络错误"
|
||||||
},
|
},
|
||||||
"hodl": {
|
"hodl": {
|
||||||
"are_you_sure_you_want_to_logout": "Are you sure you want to logout from HodlHodl?",
|
"are_you_sure_you_want_to_logout": "您确定要注销HodlHodl吗?",
|
||||||
"cont_address_escrow": "Escrow",
|
"cont_address_escrow": "Escrow",
|
||||||
"cont_address_to": "To",
|
"cont_address_to": "To",
|
||||||
"cont_buying": "buying",
|
"cont_buying": "购买",
|
||||||
"cont_cancel": "Cancel contract",
|
"cont_cancel": "Cancel contract",
|
||||||
"cont_cancel_q": "Are you sure you want to cancel this contract?",
|
"cont_cancel_q": "Are you sure you want to cancel this contract?",
|
||||||
"cont_cancel_y": "Yes, cancel contract",
|
"cont_cancel_y": "Yes, cancel contract",
|
||||||
|
@ -170,13 +170,13 @@
|
||||||
"details_next": "Next",
|
"details_next": "Next",
|
||||||
"details_no_maximum": "The selected wallet does not support automatic maximum balance calculation. Are you sure to want to select this wallet?",
|
"details_no_maximum": "The selected wallet does not support automatic maximum balance calculation. Are you sure to want to select this wallet?",
|
||||||
"details_no_multiple": "The selected wallet does not support sending Bitcoin to multiple recipients. Are you sure to want to select this wallet?",
|
"details_no_multiple": "The selected wallet does not support sending Bitcoin to multiple recipients. Are you sure to want to select this wallet?",
|
||||||
"details_no_signed_tx": "The selected file does not contain a signed transaction that can be imported.",
|
"details_no_signed_tx": "The selected file does not contain a transaction that can be imported.",
|
||||||
"details_note_placeholder": "消息",
|
"details_note_placeholder": "消息",
|
||||||
"details_scan": "扫描",
|
"details_scan": "扫描",
|
||||||
"details_total_exceeds_balance": "余额不足",
|
"details_total_exceeds_balance": "余额不足",
|
||||||
"details_wallet_before_tx": "Before creating a transaction, you must first add a Bitcoin wallet.",
|
"details_wallet_before_tx": "Before creating a transaction, you must first add a Bitcoin wallet.",
|
||||||
"details_wallet_selection": "Wallet Selection",
|
"details_wallet_selection": "Wallet Selection",
|
||||||
"dynamic_init": "Initialing",
|
"dynamic_init": "Initializing",
|
||||||
"dynamic_next": "Next",
|
"dynamic_next": "Next",
|
||||||
"dynamic_prev": "Previous",
|
"dynamic_prev": "Previous",
|
||||||
"dynamic_start": "Start",
|
"dynamic_start": "Start",
|
||||||
|
|
|
@ -27,7 +27,7 @@ export const FiatUnit = Object.freeze({
|
||||||
SGD: { endPointKey: 'SGD', symbol: 'S$', locale: 'zh-SG' },
|
SGD: { endPointKey: 'SGD', symbol: 'S$', locale: 'zh-SG' },
|
||||||
SEK: { endPointKey: 'SEK', symbol: 'kr', locale: 'sv-SE' },
|
SEK: { endPointKey: 'SEK', symbol: 'kr', locale: 'sv-SE' },
|
||||||
THB: { endPointKey: 'THB', symbol: '฿', locale: 'th-TH' },
|
THB: { endPointKey: 'THB', symbol: '฿', locale: 'th-TH' },
|
||||||
TWD: { endPointKey: 'TWD', symbol: '$', locale: 'zh-Hant-TW' },
|
TWD: { endPointKey: 'TWD', symbol: 'NT$', locale: 'zh-Hant-TW' },
|
||||||
UAH: { endPointKey: 'UAH', symbol: '₴', locale: 'uk-UA' },
|
UAH: { endPointKey: 'UAH', symbol: '₴', locale: 'uk-UA' },
|
||||||
VEF: { endPointKey: 'VEF', symbol: 'Bs.', locale: 'es-VE' },
|
VEF: { endPointKey: 'VEF', symbol: 'Bs.', locale: 'es-VE' },
|
||||||
ZAR: { endPointKey: 'ZAR', symbol: 'R', locale: 'en-ZA' },
|
ZAR: { endPointKey: 'ZAR', symbol: 'R', locale: 'en-ZA' },
|
||||||
|
|
|
@ -20,7 +20,7 @@ export class NetworkTransactionFee {
|
||||||
export default class NetworkTransactionFees {
|
export default class NetworkTransactionFees {
|
||||||
static recommendedFees() {
|
static recommendedFees() {
|
||||||
// eslint-disable-next-line no-async-promise-executor
|
// eslint-disable-next-line no-async-promise-executor
|
||||||
return new Promise(async (resolve, reject) => {
|
return new Promise(async resolve => {
|
||||||
try {
|
try {
|
||||||
const response = await BlueElectrum.estimateFees();
|
const response = await BlueElectrum.estimateFees();
|
||||||
if (typeof response === 'object') {
|
if (typeof response === 'object') {
|
||||||
|
@ -28,12 +28,12 @@ export default class NetworkTransactionFees {
|
||||||
resolve(networkFee);
|
resolve(networkFee);
|
||||||
} else {
|
} else {
|
||||||
const networkFee = new NetworkTransactionFee(1, 1, 1);
|
const networkFee = new NetworkTransactionFee(1, 1, 1);
|
||||||
reject(networkFee);
|
resolve(networkFee);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn(err);
|
console.warn(err);
|
||||||
const networkFee = new NetworkTransactionFee(1, 1, 1);
|
const networkFee = new NetworkTransactionFee(1, 1, 1);
|
||||||
reject(networkFee);
|
resolve(networkFee);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
12141
package-lock.json
generated
12141
package-lock.json
generated
File diff suppressed because it is too large
Load diff
69
package.json
69
package.json
|
@ -1,25 +1,26 @@
|
||||||
{
|
{
|
||||||
"name": "bluewallet",
|
"name": "bluewallet",
|
||||||
"version": "5.6.0",
|
"version": "5.6.2",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.9.6",
|
"@babel/core": "^7.10.4",
|
||||||
"@babel/runtime": "^7.9.6",
|
"@babel/runtime": "^7.10.4",
|
||||||
"@react-native-community/eslint-config": "^1.1.0",
|
"@jest/reporters": "^26.4.1",
|
||||||
|
"@react-native-community/eslint-config": "^2.0.0",
|
||||||
"babel-cli": "^6.26.0",
|
"babel-cli": "^6.26.0",
|
||||||
"babel-eslint": "^10.1.0",
|
"babel-eslint": "^10.1.0",
|
||||||
"babel-jest": "^26.0.1",
|
"babel-jest": "^26.1.0",
|
||||||
"babel-preset-flow": "^6.23.0",
|
"babel-preset-flow": "^6.23.0",
|
||||||
"eslint": "^6.8.0",
|
"eslint": "^7.5.0",
|
||||||
"eslint-plugin-babel": "^5.3.0",
|
"eslint-plugin-babel": "^5.3.1",
|
||||||
"eslint-plugin-import": "^2.20.2",
|
"eslint-plugin-import": "^2.22.0",
|
||||||
"eslint-plugin-node": "^11.1.0",
|
"eslint-plugin-node": "^11.1.0",
|
||||||
"eslint-plugin-promise": "^4.2.1",
|
"eslint-plugin-promise": "^4.2.1",
|
||||||
"eslint-plugin-react": "^7.20.0",
|
"eslint-plugin-react": "^7.20.3",
|
||||||
"flow-bin": "^0.125.1",
|
"flow-bin": "^0.134.0",
|
||||||
"jest": "^24.9.0",
|
"jest": "^26.1.0",
|
||||||
"jetifier": "^1.6.3",
|
"jetifier": "^1.6.3",
|
||||||
"react-test-renderer": "16.11.0"
|
"react-test-renderer": "16.13.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.16.0",
|
"node": ">=10.16.0",
|
||||||
|
@ -42,7 +43,7 @@
|
||||||
"e2e:debug": "(test -f android/app/build/outputs/apk/debug/app-debug.apk && test -f android/app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk) || npm run e2e:debug-build; npm run e2e:debug-test",
|
"e2e:debug": "(test -f android/app/build/outputs/apk/debug/app-debug.apk && test -f android/app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk) || npm run e2e:debug-build; npm run e2e:debug-test",
|
||||||
"e2e:release-build": "npx detox build -c android.emu.release",
|
"e2e:release-build": "npx detox build -c android.emu.release",
|
||||||
"e2e:release-test": "detox test -c android.emu.release --record-videos all --take-screenshots all --headless",
|
"e2e:release-test": "detox test -c android.emu.release --record-videos all --take-screenshots all --headless",
|
||||||
"lint": "eslint *.js screen/**/*.js blue_modules/*.js class/**/*.js models/ loc/ tests/**/*.js",
|
"lint": "eslint *.js screen/**/*.js blue_modules/*.js class/**/*.js models/ loc/ tests/**/*.js components/**/*.js",
|
||||||
"lint:fix": "npm run lint -- --fix",
|
"lint:fix": "npm run lint -- --fix",
|
||||||
"lint:quickfix": "git status --porcelain | grep -v '\\.json' | grep '\\.js' --color=never | awk '{print $2}' | xargs eslint --fix; exit 0",
|
"lint:quickfix": "git status --porcelain | grep -v '\\.json' | grep '\\.js' --color=never | awk '{print $2}' | xargs eslint --fix; exit 0",
|
||||||
"unit": "jest tests/unit/*"
|
"unit": "jest tests/unit/*"
|
||||||
|
@ -55,24 +56,27 @@
|
||||||
"setupFiles": [
|
"setupFiles": [
|
||||||
"./tests/setup.js"
|
"./tests/setup.js"
|
||||||
],
|
],
|
||||||
|
"watchPathIgnorePatterns": [
|
||||||
|
"<rootDir>/node_modules"
|
||||||
|
],
|
||||||
"setupFilesAfterEnv": [
|
"setupFilesAfterEnv": [
|
||||||
"./tests/setupAfterEnv.js"
|
"./tests/setupAfterEnv.js"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/preset-env": "7.11.0",
|
"@babel/preset-env": "7.11.5",
|
||||||
"@react-native-community/async-storage": "1.12.0",
|
"@react-native-community/async-storage": "1.12.1",
|
||||||
"@react-native-community/blur": "3.6.0",
|
"@react-native-community/blur": "3.6.0",
|
||||||
"@react-native-community/clipboard": "1.2.3",
|
"@react-native-community/clipboard": "1.4.0",
|
||||||
"@react-native-community/geolocation": "2.0.2",
|
"@react-native-community/geolocation": "2.0.2",
|
||||||
"@react-native-community/masked-view": "0.1.10",
|
"@react-native-community/masked-view": "0.1.10",
|
||||||
"@react-native-community/push-notification-ios": "1.5.0",
|
"@react-native-community/push-notification-ios": "1.5.0",
|
||||||
"@react-native-community/slider": "3.0.3",
|
"@react-native-community/slider": "3.0.3",
|
||||||
"@react-navigation/drawer": "5.9.0",
|
"@react-navigation/drawer": "5.9.2",
|
||||||
"@react-navigation/native": "5.7.3",
|
"@react-navigation/native": "5.7.5",
|
||||||
"@react-navigation/stack": "5.9.0",
|
"@react-navigation/stack": "5.9.2",
|
||||||
"@remobile/react-native-qrcode-local-image": "git+https://github.com/BlueWallet/react-native-qrcode-local-image.git",
|
"@remobile/react-native-qrcode-local-image": "git+https://github.com/BlueWallet/react-native-qrcode-local-image.git",
|
||||||
"@sentry/react-native": "1.8.0",
|
"@sentry/react-native": "1.8.2",
|
||||||
"amplitude-js": "5.11.0",
|
"amplitude-js": "5.11.0",
|
||||||
"assert": "1.5.0",
|
"assert": "1.5.0",
|
||||||
"bc-bech32": "file:blue_modules/bc-bech32",
|
"bc-bech32": "file:blue_modules/bc-bech32",
|
||||||
|
@ -82,16 +86,16 @@
|
||||||
"bip21": "2.0.3",
|
"bip21": "2.0.3",
|
||||||
"bip32": "2.0.5",
|
"bip32": "2.0.5",
|
||||||
"bip39": "2.6.0",
|
"bip39": "2.6.0",
|
||||||
"bitcoinjs-lib": "5.1.10",
|
"bitcoinjs-lib": "5.2.0",
|
||||||
"bolt11": "1.2.7",
|
"bolt11": "1.2.7",
|
||||||
"buffer": "5.6.0",
|
"buffer": "5.6.0",
|
||||||
"buffer-reverse": "1.0.1",
|
"buffer-reverse": "1.0.1",
|
||||||
"coinselect": "3.1.12",
|
"coinselect": "3.1.12",
|
||||||
"crypto-js": "3.1.9-1",
|
"crypto-js": "3.1.9-1",
|
||||||
"dayjs": "1.8.33",
|
"dayjs": "1.8.35",
|
||||||
"detox": "17.5.6",
|
"detox": "17.5.6",
|
||||||
"ecurve": "1.0.6",
|
"ecurve": "1.0.6",
|
||||||
"electrum-client": "git+https://github.com/BlueWallet/rn-electrum-client.git#99c75385f99e82b87fbfed2abfb2cccd322fe3e9",
|
"electrum-client": "git+https://github.com/BlueWallet/rn-electrum-client.git#f9a827e724a5a2e578fdfdb483f83793af55b030",
|
||||||
"electrum-mnemonic": "2.0.0",
|
"electrum-mnemonic": "2.0.0",
|
||||||
"eslint-config-prettier": "6.11.0",
|
"eslint-config-prettier": "6.11.0",
|
||||||
"eslint-config-standard": "14.1.1",
|
"eslint-config-standard": "14.1.1",
|
||||||
|
@ -104,29 +108,30 @@
|
||||||
"lottie-react-native": "3.5.0",
|
"lottie-react-native": "3.5.0",
|
||||||
"metro-react-native-babel-preset": "0.63.0",
|
"metro-react-native-babel-preset": "0.63.0",
|
||||||
"path-browserify": "1.0.1",
|
"path-browserify": "1.0.1",
|
||||||
|
"payjoin-client": "git+https://github.com/bitcoinjs/payjoin-client.git#31d2118a4c0d00192d975f3a6da2a96238f8f7a5",
|
||||||
"pbkdf2": "3.1.1",
|
"pbkdf2": "3.1.1",
|
||||||
"prettier": "2.1.1",
|
"prettier": "2.1.1",
|
||||||
"process": "0.11.10",
|
"process": "0.11.10",
|
||||||
"prop-types": "15.7.2",
|
"prop-types": "15.7.2",
|
||||||
"react": "16.11.0",
|
"react": "16.13.1",
|
||||||
"react-localization": "1.0.15",
|
"react-localization": "1.0.15",
|
||||||
"react-native": "0.62.2",
|
"react-native": "0.63.3",
|
||||||
"react-native-blue-crypto": "git+https://github.com/Overtorment/react-native-blue-crypto.git",
|
"react-native-blue-crypto": "git+https://github.com/Overtorment/react-native-blue-crypto.git",
|
||||||
"react-native-camera": "3.39.1",
|
"react-native-camera": "3.39.1",
|
||||||
"react-native-default-preference": "1.4.3",
|
"react-native-default-preference": "1.4.3",
|
||||||
"react-native-device-info": "6.0.3",
|
"react-native-device-info": "6.2.0",
|
||||||
"react-native-document-picker": "git+https://github.com/BlueWallet/react-native-document-picker.git#3684d4fcc2bc0b47c32be39024e4796004c3e428",
|
"react-native-document-picker": "git+https://github.com/BlueWallet/react-native-document-picker.git#3684d4fcc2bc0b47c32be39024e4796004c3e428",
|
||||||
"react-native-elements": "2.2.1",
|
"react-native-elements": "2.3.1",
|
||||||
"react-native-fingerprint-scanner": "git+https://github.com/BlueWallet/react-native-fingerprint-scanner.git#ce644673681716335d786727bab998f7e632ab5e",
|
"react-native-fingerprint-scanner": "git+https://github.com/BlueWallet/react-native-fingerprint-scanner.git#ce644673681716335d786727bab998f7e632ab5e",
|
||||||
"react-native-fs": "2.16.6",
|
"react-native-fs": "2.16.6",
|
||||||
"react-native-gesture-handler": "1.7.0",
|
"react-native-gesture-handler": "1.8.0",
|
||||||
"react-native-handoff": "git+https://github.com/marcosrdz/react-native-handoff.git",
|
"react-native-handoff": "git+https://github.com/marcosrdz/react-native-handoff.git",
|
||||||
"react-native-haptic-feedback": "1.10.0",
|
"react-native-haptic-feedback": "1.10.0",
|
||||||
"react-native-image-picker": "2.3.3",
|
"react-native-image-picker": "2.3.4",
|
||||||
"react-native-inappbrowser-reborn": "git+https://github.com/BlueWallet/react-native-inappbrowser.git#v3.4.0",
|
"react-native-inappbrowser-reborn": "git+https://github.com/BlueWallet/react-native-inappbrowser.git#v3.4.0",
|
||||||
"react-native-level-fs": "3.0.1",
|
"react-native-level-fs": "3.0.1",
|
||||||
"react-native-linear-gradient": "2.5.6",
|
"react-native-linear-gradient": "2.5.6",
|
||||||
"react-native-localize": " 1.4.0",
|
"react-native-localize": "1.4.2",
|
||||||
"react-native-modal": "11.5.6",
|
"react-native-modal": "11.5.6",
|
||||||
"react-native-obscure": "1.2.1",
|
"react-native-obscure": "1.2.1",
|
||||||
"react-native-passcode-auth": "git+https://github.com/BlueWallet/react-native-passcode-auth.git#a2ff977ba92b36f8d0a5567f59c05cc608e8bd12",
|
"react-native-passcode-auth": "git+https://github.com/BlueWallet/react-native-passcode-auth.git#a2ff977ba92b36f8d0a5567f59c05cc608e8bd12",
|
||||||
|
@ -142,7 +147,7 @@
|
||||||
"react-native-safe-area-context": "3.1.8",
|
"react-native-safe-area-context": "3.1.8",
|
||||||
"react-native-screens": "2.11.0",
|
"react-native-screens": "2.11.0",
|
||||||
"react-native-secure-key-store": "git+https://github.com/BlueWallet/react-native-secure-key-store.git#4ba25dedb3d5ae15c22fd0ea0555116055630966",
|
"react-native-secure-key-store": "git+https://github.com/BlueWallet/react-native-secure-key-store.git#4ba25dedb3d5ae15c22fd0ea0555116055630966",
|
||||||
"react-native-share": "3.7.0",
|
"react-native-share": "4.0.2",
|
||||||
"react-native-snap-carousel": "3.9.1",
|
"react-native-snap-carousel": "3.9.1",
|
||||||
"react-native-sortable-list": "0.0.24",
|
"react-native-sortable-list": "0.0.24",
|
||||||
"react-native-svg": "12.1.0",
|
"react-native-svg": "12.1.0",
|
||||||
|
@ -150,7 +155,7 @@
|
||||||
"react-native-tooltip": "git+https://github.com/BlueWallet/react-native-tooltip.git#d369e7ece09e4dec73873f1cfeac83e9d35294a6",
|
"react-native-tooltip": "git+https://github.com/BlueWallet/react-native-tooltip.git#d369e7ece09e4dec73873f1cfeac83e9d35294a6",
|
||||||
"react-native-vector-icons": "6.6.0",
|
"react-native-vector-icons": "6.6.0",
|
||||||
"react-native-watch-connectivity": "1.0.2",
|
"react-native-watch-connectivity": "1.0.2",
|
||||||
"react-native-webview": "10.8.3",
|
"react-native-webview": "10.9.2",
|
||||||
"react-test-render": "1.1.2",
|
"react-test-render": "1.1.2",
|
||||||
"readable-stream": "3.6.0",
|
"readable-stream": "3.6.0",
|
||||||
"realm": "6.1.0",
|
"realm": "6.1.0",
|
||||||
|
|
|
@ -125,9 +125,8 @@ const ReceiveDetails = () => {
|
||||||
backgroundColor: BlueCurrentTheme.colors.elevated,
|
backgroundColor: BlueCurrentTheme.colors.elevated,
|
||||||
},
|
},
|
||||||
share: {
|
share: {
|
||||||
alignItems: 'center',
|
|
||||||
alignContent: 'flex-end',
|
|
||||||
marginBottom: 24,
|
marginBottom: 24,
|
||||||
|
marginHorizontal: 16,
|
||||||
},
|
},
|
||||||
modalButton: {
|
modalButton: {
|
||||||
backgroundColor: BlueCurrentTheme.colors.modalButton,
|
backgroundColor: BlueCurrentTheme.colors.modalButton,
|
||||||
|
@ -193,7 +192,7 @@ const ReceiveDetails = () => {
|
||||||
if (!address) {
|
if (!address) {
|
||||||
// either sleep expired or getAddressAsync threw an exception
|
// either sleep expired or getAddressAsync threw an exception
|
||||||
console.warn('either sleep expired or getAddressAsync threw an exception');
|
console.warn('either sleep expired or getAddressAsync threw an exception');
|
||||||
address = wallet._getExternalAddressByIndex(wallet.next_free_address_index);
|
address = wallet._getExternalAddressByIndex(wallet.getNextFreeAddressIndex());
|
||||||
} else {
|
} else {
|
||||||
BlueApp.saveToDisk(); // caching whatever getAddressAsync() generated internally
|
BlueApp.saveToDisk(); // caching whatever getAddressAsync() generated internally
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
/* global alert */
|
/* global alert */
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Image, View, TouchableOpacity, StatusBar, Platform, StyleSheet, Linking, Alert } from 'react-native';
|
import { Image, View, TouchableOpacity, StatusBar, Platform, StyleSheet, Linking, Alert, TextInput } from 'react-native';
|
||||||
import { RNCamera } from 'react-native-camera';
|
import { RNCamera } from 'react-native-camera';
|
||||||
import { Icon } from 'react-native-elements';
|
import { Icon } from 'react-native-elements';
|
||||||
import ImagePicker from 'react-native-image-picker';
|
import ImagePicker from 'react-native-image-picker';
|
||||||
import { useNavigation, useRoute, useIsFocused, useTheme } from '@react-navigation/native';
|
import { useNavigation, useRoute, useIsFocused, useTheme } from '@react-navigation/native';
|
||||||
import DocumentPicker from 'react-native-document-picker';
|
|
||||||
import RNFS from 'react-native-fs';
|
|
||||||
import loc from '../../loc';
|
import loc from '../../loc';
|
||||||
import { BlueLoadingHook, BlueTextHooks, BlueButtonHook, BlueSpacing40 } from '../../BlueComponents';
|
import { BlueLoadingHook, BlueTextHooks, BlueButtonHook, BlueSpacing40 } from '../../BlueComponents';
|
||||||
import { getSystemName } from 'react-native-device-info';
|
import { getSystemName } from 'react-native-device-info';
|
||||||
const prompt = require('../../blue_modules/prompt');
|
import { BlueCurrentTheme } from '../../components/themes';
|
||||||
|
import { decodeUR, extractSingleWorkload } from 'bc-ur';
|
||||||
const LocalQRCode = require('@remobile/react-native-qrcode-local-image');
|
const LocalQRCode = require('@remobile/react-native-qrcode-local-image');
|
||||||
const createHash = require('create-hash');
|
const createHash = require('create-hash');
|
||||||
const isDesktop = getSystemName() === 'Mac OS X';
|
const isDesktop = getSystemName() === 'Mac OS X';
|
||||||
|
const fs = require('../../blue_modules/fs');
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
root: {
|
root: {
|
||||||
|
@ -65,12 +65,21 @@ const styles = StyleSheet.create({
|
||||||
backdoorButton: {
|
backdoorButton: {
|
||||||
width: 40,
|
width: 40,
|
||||||
height: 40,
|
height: 40,
|
||||||
backgroundColor: 'rgba(0,0,0,0)',
|
backgroundColor: 'rgba(0,0,0,0.1)',
|
||||||
justifyContent: 'center',
|
|
||||||
borderRadius: 0,
|
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
left: 0,
|
},
|
||||||
bottom: 0,
|
backdoorInputWrapper: { position: 'absolute', left: '5%', top: '0%', width: '90%', height: '70%', backgroundColor: 'white' },
|
||||||
|
backdoorInput: {
|
||||||
|
height: '50%',
|
||||||
|
marginTop: 5,
|
||||||
|
marginHorizontal: 20,
|
||||||
|
borderColor: BlueCurrentTheme.colors.formBorder,
|
||||||
|
borderBottomColor: BlueCurrentTheme.colors.formBorder,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderRadius: 4,
|
||||||
|
backgroundColor: BlueCurrentTheme.colors.inputBackgroundColor,
|
||||||
|
color: BlueCurrentTheme.colors.foregroundColor,
|
||||||
|
textAlignVertical: 'top',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -85,6 +94,9 @@ const ScanQRCode = () => {
|
||||||
const isFocused = useIsFocused();
|
const isFocused = useIsFocused();
|
||||||
const [cameraStatus, setCameraStatus] = useState(RNCamera.Constants.CameraStatus.PENDING_AUTHORIZATION);
|
const [cameraStatus, setCameraStatus] = useState(RNCamera.Constants.CameraStatus.PENDING_AUTHORIZATION);
|
||||||
const [backdoorPressed, setBackdoorPressed] = useState(0);
|
const [backdoorPressed, setBackdoorPressed] = useState(0);
|
||||||
|
const [backdoorText, setBackdoorText] = useState('');
|
||||||
|
const [backdoorVisible, setBackdoorVisible] = useState(false);
|
||||||
|
const [animatedQRCodeData, setAnimatedQRCodeData] = useState({});
|
||||||
const stylesHook = StyleSheet.create({
|
const stylesHook = StyleSheet.create({
|
||||||
openSettingsContainer: {
|
openSettingsContainer: {
|
||||||
backgroundColor: colors.brandingColor,
|
backgroundColor: colors.brandingColor,
|
||||||
|
@ -94,6 +106,34 @@ const ScanQRCode = () => {
|
||||||
return createHash('sha256').update(s).digest().toString('hex');
|
return createHash('sha256').update(s).digest().toString('hex');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const _onReadUniformResource = ur => {
|
||||||
|
try {
|
||||||
|
const [index, total] = extractSingleWorkload(ur);
|
||||||
|
animatedQRCodeData[index + 'of' + total] = ur;
|
||||||
|
if (Object.values(animatedQRCodeData).length === total) {
|
||||||
|
const payload = decodeUR(Object.values(animatedQRCodeData));
|
||||||
|
// lets look inside that data
|
||||||
|
let data = false;
|
||||||
|
if (Buffer.from(payload, 'hex').toString().startsWith('psbt')) {
|
||||||
|
// its a psbt, and whoever requested it expects it encoded in base64
|
||||||
|
data = Buffer.from(payload, 'hex').toString('base64');
|
||||||
|
} else {
|
||||||
|
// its something else. probably plain text is expected
|
||||||
|
data = Buffer.from(payload, 'hex').toString();
|
||||||
|
}
|
||||||
|
if (launchedBy) {
|
||||||
|
navigation.navigate(launchedBy);
|
||||||
|
}
|
||||||
|
onBarScanned({ data });
|
||||||
|
} else {
|
||||||
|
setAnimatedQRCodeData(animatedQRCodeData);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(error);
|
||||||
|
alert(loc._.invalid_animated_qr_code_fragment);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const onBarCodeRead = ret => {
|
const onBarCodeRead = ret => {
|
||||||
const h = HashIt(ret.data);
|
const h = HashIt(ret.data);
|
||||||
if (scannedCache[h]) {
|
if (scannedCache[h]) {
|
||||||
|
@ -102,6 +142,10 @@ const ScanQRCode = () => {
|
||||||
}
|
}
|
||||||
scannedCache[h] = +new Date();
|
scannedCache[h] = +new Date();
|
||||||
|
|
||||||
|
if (ret.data.toUpperCase().startsWith('UR')) {
|
||||||
|
return _onReadUniformResource(ret.data);
|
||||||
|
}
|
||||||
|
|
||||||
if (!isLoading) {
|
if (!isLoading) {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
try {
|
try {
|
||||||
|
@ -121,26 +165,10 @@ const ScanQRCode = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const showFilePicker = async () => {
|
const showFilePicker = async () => {
|
||||||
try {
|
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
const res = await DocumentPicker.pick();
|
const { data } = await fs.showFilePickerAndReadFile();
|
||||||
const file = await RNFS.readFile(res.uri);
|
if (data) onBarCodeRead({ data });
|
||||||
const fileParsed = JSON.parse(file);
|
|
||||||
if (fileParsed.keystore.xpub) {
|
|
||||||
let masterFingerprint;
|
|
||||||
if (fileParsed.keystore.ckcc_xfp) {
|
|
||||||
masterFingerprint = Number(fileParsed.keystore.ckcc_xfp);
|
|
||||||
}
|
|
||||||
onBarCodeRead({ data: fileParsed.keystore.xpub, additionalProperties: { masterFingerprint, label: fileParsed.keystore.label } });
|
|
||||||
} else {
|
|
||||||
throw new Error();
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
if (!DocumentPicker.isCancel(err)) {
|
|
||||||
alert(loc.send.qr_error_no_wallet);
|
|
||||||
}
|
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const showImagePicker = () => {
|
const showImagePicker = () => {
|
||||||
|
@ -218,6 +246,45 @@ const ScanQRCode = () => {
|
||||||
<TouchableOpacity style={styles.imagePickerTouch} onPress={showImagePicker}>
|
<TouchableOpacity style={styles.imagePickerTouch} onPress={showImagePicker}>
|
||||||
<Icon name="image" type="font-awesome" color="#ffffff" />
|
<Icon name="image" type="font-awesome" color="#ffffff" />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
{showFileImportButton && (
|
||||||
|
<TouchableOpacity style={styles.filePickerTouch} onPress={showFilePicker}>
|
||||||
|
<Icon name="file-import" type="material-community" color="#ffffff" />
|
||||||
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
|
{backdoorVisible && (
|
||||||
|
<View style={styles.backdoorInputWrapper}>
|
||||||
|
<BlueTextHooks>Provide QR code contents manually:</BlueTextHooks>
|
||||||
|
<TextInput
|
||||||
|
testID="scanQrBackdoorInput"
|
||||||
|
multiline
|
||||||
|
underlineColorAndroid="transparent"
|
||||||
|
style={styles.backdoorInput}
|
||||||
|
autoCorrect={false}
|
||||||
|
autoCapitalize="none"
|
||||||
|
spellCheck={false}
|
||||||
|
selectTextOnFocus={false}
|
||||||
|
keyboardType={Platform.OS === 'android' ? 'visible-password' : 'default'}
|
||||||
|
value={backdoorText}
|
||||||
|
onChangeText={setBackdoorText}
|
||||||
|
/>
|
||||||
|
<BlueButtonHook
|
||||||
|
title="OK"
|
||||||
|
testID="scanQrBackdoorOkButton"
|
||||||
|
onPress={() => {
|
||||||
|
setBackdoorVisible(false);
|
||||||
|
let data;
|
||||||
|
try {
|
||||||
|
data = JSON.parse(backdoorText);
|
||||||
|
// this might be a json string (for convenience - in case there are "\n" in there)
|
||||||
|
} catch (_) {
|
||||||
|
data = backdoorText;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data) onBarCodeRead({ data });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
testID="ScanQrBackdoorButton"
|
testID="ScanQrBackdoorButton"
|
||||||
style={styles.backdoorButton}
|
style={styles.backdoorButton}
|
||||||
|
@ -227,23 +294,10 @@ const ScanQRCode = () => {
|
||||||
// this allows to mock and test QR scanning in e2e tests
|
// this allows to mock and test QR scanning in e2e tests
|
||||||
setBackdoorPressed(backdoorPressed + 1);
|
setBackdoorPressed(backdoorPressed + 1);
|
||||||
if (backdoorPressed < 10) return;
|
if (backdoorPressed < 10) return;
|
||||||
let data, userInput;
|
setBackdoorPressed(0);
|
||||||
try {
|
setBackdoorVisible(true);
|
||||||
userInput = await prompt('Provide QR code contents manually:', '', false, 'plain-text');
|
|
||||||
data = JSON.parse(userInput);
|
|
||||||
// this might be a json string (for convenience - in case there are "\n" in there)
|
|
||||||
} catch (_) {
|
|
||||||
data = userInput;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data) onBarCodeRead({ data });
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{showFileImportButton && (
|
|
||||||
<TouchableOpacity style={styles.filePickerTouch} onPress={showFilePicker}>
|
|
||||||
<Icon name="file-import" type="material-community" color="#ffffff" />
|
|
||||||
</TouchableOpacity>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
/* global alert */
|
/* global alert */
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { ActivityIndicator, FlatList, TouchableOpacity, StyleSheet, View } from 'react-native';
|
import { ActivityIndicator, FlatList, TouchableOpacity, StyleSheet, Switch, View } from 'react-native';
|
||||||
import { Text } from 'react-native-elements';
|
import { Text } from 'react-native-elements';
|
||||||
|
import { PayjoinClient } from 'payjoin-client';
|
||||||
|
import PayjoinTransaction from '../../class/payjoin-transaction';
|
||||||
import { BlueButton, BlueText, SafeBlueArea, BlueCard, BlueSpacing40, BlueNavigationStyle } from '../../BlueComponents';
|
import { BlueButton, BlueText, SafeBlueArea, BlueCard, BlueSpacing40, BlueNavigationStyle } from '../../BlueComponents';
|
||||||
import { BitcoinUnit } from '../../models/bitcoinUnits';
|
import { BitcoinUnit } from '../../models/bitcoinUnits';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
@ -32,7 +34,10 @@ export default class Confirm extends Component {
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
fee: props.route.params.fee,
|
isPayjoinEnabled: false,
|
||||||
|
payjoinUrl: props.route.params.fromWallet.allowPayJoin() ? props.route.params?.payjoinUrl : false,
|
||||||
|
psbt: props.route.params?.psbt,
|
||||||
|
fee: props.route.params?.fee,
|
||||||
feeSatoshi: new Bignumber(props.route.params.fee).multipliedBy(100000000).toNumber(),
|
feeSatoshi: new Bignumber(props.route.params.fee).multipliedBy(100000000).toNumber(),
|
||||||
memo: props.route.params.memo,
|
memo: props.route.params.memo,
|
||||||
recipients: props.route.params.recipients,
|
recipients: props.route.params.recipients,
|
||||||
|
@ -50,25 +55,29 @@ export default class Confirm extends Component {
|
||||||
this.isBiometricUseCapableAndEnabled = await Biometric.isBiometricUseCapableAndEnabled();
|
this.isBiometricUseCapableAndEnabled = await Biometric.isBiometricUseCapableAndEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
broadcast() {
|
send() {
|
||||||
this.setState({ isLoading: true }, async () => {
|
this.setState({ isLoading: true }, async () => {
|
||||||
try {
|
try {
|
||||||
// await BlueElectrum.ping();
|
const txids2watch = [];
|
||||||
await BlueElectrum.waitTillConnected();
|
if (!this.state.isPayjoinEnabled) {
|
||||||
|
await this.broadcast(this.state.tx);
|
||||||
if (this.isBiometricUseCapableAndEnabled) {
|
|
||||||
if (!(await Biometric.unlockWithBiometrics())) {
|
|
||||||
this.setState({ isLoading: false });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await this.state.fromWallet.broadcastTx(this.state.tx);
|
|
||||||
if (!result) {
|
|
||||||
throw new Error(loc.errors.broadcast);
|
|
||||||
} else {
|
} else {
|
||||||
|
const wallet = new PayjoinTransaction(this.state.psbt, txHex => this.broadcast(txHex), this.state.fromWallet);
|
||||||
|
const payjoinClient = new PayjoinClient({
|
||||||
|
wallet,
|
||||||
|
payjoinUrl: this.state.payjoinUrl,
|
||||||
|
});
|
||||||
|
await payjoinClient.run();
|
||||||
|
const payjoinPsbt = wallet.getPayjoinPsbt();
|
||||||
|
if (payjoinPsbt) {
|
||||||
|
const tx = payjoinPsbt.extractTransaction();
|
||||||
|
txids2watch.push(tx.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const txid = bitcoin.Transaction.fromHex(this.state.tx).getId();
|
const txid = bitcoin.Transaction.fromHex(this.state.tx).getId();
|
||||||
notifications.majorTomToGroundControl([], [], [txid]);
|
txids2watch.push(txid);
|
||||||
|
notifications.majorTomToGroundControl([], [], txids2watch);
|
||||||
EV(EV.enum.REMOTE_TRANSACTIONS_COUNT_CHANGED); // someone should fetch txs
|
EV(EV.enum.REMOTE_TRANSACTIONS_COUNT_CHANGED); // someone should fetch txs
|
||||||
let amount = 0;
|
let amount = 0;
|
||||||
const recipients = this.state.recipients;
|
const recipients = this.state.recipients;
|
||||||
|
@ -101,8 +110,8 @@ export default class Confirm extends Component {
|
||||||
amount,
|
amount,
|
||||||
dismissModal: () => this.props.navigation.dangerouslyGetParent().pop(),
|
dismissModal: () => this.props.navigation.dangerouslyGetParent().pop(),
|
||||||
});
|
});
|
||||||
|
|
||||||
this.setState({ isLoading: false });
|
this.setState({ isLoading: false });
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
ReactNativeHapticFeedback.trigger('notificationError', {
|
ReactNativeHapticFeedback.trigger('notificationError', {
|
||||||
ignoreAndroidSystemSettings: false,
|
ignoreAndroidSystemSettings: false,
|
||||||
|
@ -113,6 +122,24 @@ export default class Confirm extends Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async broadcast(tx) {
|
||||||
|
await BlueElectrum.ping();
|
||||||
|
await BlueElectrum.waitTillConnected();
|
||||||
|
|
||||||
|
if (this.isBiometricUseCapableAndEnabled) {
|
||||||
|
if (!(await Biometric.unlockWithBiometrics())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await this.state.fromWallet.broadcastTx(tx);
|
||||||
|
if (!result) {
|
||||||
|
throw new Error(loc.errors.broadcast);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
_renderItem = ({ index, item }) => {
|
_renderItem = ({ index, item }) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -168,11 +195,17 @@ export default class Confirm extends Component {
|
||||||
{currency.satoshiToLocalCurrency(this.state.feeSatoshi)})
|
{currency.satoshiToLocalCurrency(this.state.feeSatoshi)})
|
||||||
</Text>
|
</Text>
|
||||||
<BlueSpacing40 />
|
<BlueSpacing40 />
|
||||||
{this.state.isLoading ? (
|
{!!this.state.payjoinUrl && (
|
||||||
<ActivityIndicator />
|
<View style={styles.payjoinWrapper}>
|
||||||
) : (
|
<Text style={styles.payjoinText}>Payjoin</Text>
|
||||||
<BlueButton onPress={() => this.broadcast()} title={loc.send.confirm_sendNow} />
|
<Switch
|
||||||
|
testID="PayjoinSwitch"
|
||||||
|
value={this.state.isPayjoinEnabled}
|
||||||
|
onValueChange={isPayjoinEnabled => this.setState({ isPayjoinEnabled })}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
)}
|
)}
|
||||||
|
{this.state.isLoading ? <ActivityIndicator /> : <BlueButton onPress={() => this.send()} title={loc.send.confirm_sendNow} />}
|
||||||
|
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
testID="TransactionDetailsButton"
|
testID="TransactionDetailsButton"
|
||||||
|
@ -287,11 +320,20 @@ const styles = StyleSheet.create({
|
||||||
fontWeight: '500',
|
fontWeight: '500',
|
||||||
alignSelf: 'center',
|
alignSelf: 'center',
|
||||||
},
|
},
|
||||||
|
payjoinWrapper: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
marginHorizontal: 20,
|
||||||
|
marginBottom: 10,
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
payjoinText: { color: '#81868e', fontSize: 14 },
|
||||||
});
|
});
|
||||||
|
|
||||||
Confirm.propTypes = {
|
Confirm.propTypes = {
|
||||||
navigation: PropTypes.shape({
|
navigation: PropTypes.shape({
|
||||||
goBack: PropTypes.func,
|
goBack: PropTypes.func,
|
||||||
|
dismiss: PropTypes.func,
|
||||||
navigate: PropTypes.func,
|
navigate: PropTypes.func,
|
||||||
dangerouslyGetParent: PropTypes.func,
|
dangerouslyGetParent: PropTypes.func,
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -253,8 +253,8 @@ SendCreate.navigationOptions = ({ navigation, route }) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...BlueNavigationStyle,
|
...BlueNavigationStyle(),
|
||||||
title: loc.send.create.details,
|
title: loc.send.create_details,
|
||||||
headerRight,
|
headerRight,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -38,15 +38,17 @@ import * as bitcoin from 'bitcoinjs-lib';
|
||||||
|
|
||||||
import NetworkTransactionFees, { NetworkTransactionFee } from '../../models/networkTransactionFees';
|
import NetworkTransactionFees, { NetworkTransactionFee } from '../../models/networkTransactionFees';
|
||||||
import { BitcoinUnit, Chain } from '../../models/bitcoinUnits';
|
import { BitcoinUnit, Chain } from '../../models/bitcoinUnits';
|
||||||
import { AppStorage, HDSegwitBech32Wallet, LightningCustodianWallet, WatchOnlyWallet } from '../../class';
|
import { AppStorage, HDSegwitBech32Wallet, LightningCustodianWallet, MultisigHDWallet, WatchOnlyWallet } from '../../class';
|
||||||
import { BitcoinTransaction } from '../../models/bitcoinTransactionInfo';
|
import { BitcoinTransaction } from '../../models/bitcoinTransactionInfo';
|
||||||
import DocumentPicker from 'react-native-document-picker';
|
import DocumentPicker from 'react-native-document-picker';
|
||||||
import DeeplinkSchemaMatch from '../../class/deeplink-schema-match';
|
import DeeplinkSchemaMatch from '../../class/deeplink-schema-match';
|
||||||
import loc from '../../loc';
|
import loc from '../../loc';
|
||||||
import { BlueCurrentTheme } from '../../components/themes';
|
import { BlueCurrentTheme } from '../../components/themes';
|
||||||
|
import { AbstractHDElectrumWallet } from '../../class/wallets/abstract-hd-electrum-wallet';
|
||||||
const currency = require('../../blue_modules/currency');
|
const currency = require('../../blue_modules/currency');
|
||||||
const BlueApp: AppStorage = require('../../BlueApp');
|
const BlueApp: AppStorage = require('../../BlueApp');
|
||||||
const prompt = require('../../blue_modules/prompt');
|
const prompt = require('../../blue_modules/prompt');
|
||||||
|
const fs = require('../../blue_modules/fs');
|
||||||
|
|
||||||
const btcAddressRx = /^[a-zA-Z0-9]{26,35}$/;
|
const btcAddressRx = /^[a-zA-Z0-9]{26,35}$/;
|
||||||
|
|
||||||
|
@ -131,7 +133,6 @@ const styles = StyleSheet.create({
|
||||||
createButton: {
|
createButton: {
|
||||||
marginHorizontal: 56,
|
marginHorizontal: 56,
|
||||||
marginVertical: 16,
|
marginVertical: 16,
|
||||||
alignItems: 'center',
|
|
||||||
minHeight: 44,
|
minHeight: 44,
|
||||||
},
|
},
|
||||||
select: {
|
select: {
|
||||||
|
@ -309,12 +310,14 @@ export default class SendDetails extends Component {
|
||||||
units[this.state.recipientsScrollIndex] = BitcoinUnit.BTC; // also resetting current unit to BTC
|
units[this.state.recipientsScrollIndex] = BitcoinUnit.BTC; // also resetting current unit to BTC
|
||||||
recipients[[this.state.recipientsScrollIndex]].address = address;
|
recipients[[this.state.recipientsScrollIndex]].address = address;
|
||||||
recipients[[this.state.recipientsScrollIndex]].amount = options.amount;
|
recipients[[this.state.recipientsScrollIndex]].amount = options.amount;
|
||||||
|
recipients[[this.state.recipientsScrollIndex]].amountSats = new BigNumber(options.amount).multipliedBy(100000000).toNumber();
|
||||||
this.setState({
|
this.setState({
|
||||||
addresses: recipients,
|
addresses: recipients,
|
||||||
memo: options.label || options.message,
|
memo: options.label || options.message,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
amountUnit: BitcoinUnit.BTC,
|
amountUnit: BitcoinUnit.BTC,
|
||||||
units,
|
units,
|
||||||
|
payjoinUrl: options.pj || '',
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.setState({ isLoading: false });
|
this.setState({ isLoading: false });
|
||||||
|
@ -332,10 +335,10 @@ export default class SendDetails extends Component {
|
||||||
if (this.props.route.params.uri) {
|
if (this.props.route.params.uri) {
|
||||||
const uri = this.props.route.params.uri;
|
const uri = this.props.route.params.uri;
|
||||||
try {
|
try {
|
||||||
const { address, amount, memo } = this.decodeBitcoinUri(uri);
|
const { address, amount, memo, payjoinUrl } = DeeplinkSchemaMatch.decodeBitcoinUri(uri);
|
||||||
addresses.push(new BitcoinTransaction(address, amount, currency.btcToSatoshi(amount)));
|
addresses.push(new BitcoinTransaction(address, amount, currency.btcToSatoshi(amount)));
|
||||||
initialMemo = memo;
|
initialMemo = memo;
|
||||||
this.setState({ addresses, memo: initialMemo, isLoading: false, amountUnit: BitcoinUnit.BTC });
|
this.setState({ addresses, memo: initialMemo, isLoading: false, amountUnit: BitcoinUnit.BTC, payjoinUrl });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
alert(loc.send.details_error_decode);
|
alert(loc.send.details_error_decode);
|
||||||
|
@ -359,10 +362,10 @@ export default class SendDetails extends Component {
|
||||||
}
|
}
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
|
|
||||||
await this.reCalcTx();
|
this.reCalcTx();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const recommendedFees = await NetworkTransactionFees.recommendedFees();
|
const recommendedFees = await Promise.race([NetworkTransactionFees.recommendedFees(), BlueApp.sleep(2000)]);
|
||||||
if (recommendedFees && 'fastestFee' in recommendedFees) {
|
if (recommendedFees && 'fastestFee' in recommendedFees) {
|
||||||
await AsyncStorage.setItem(NetworkTransactionFee.StorageKey, JSON.stringify(recommendedFees));
|
await AsyncStorage.setItem(NetworkTransactionFee.StorageKey, JSON.stringify(recommendedFees));
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -370,21 +373,28 @@ export default class SendDetails extends Component {
|
||||||
networkTransactionFees: recommendedFees,
|
networkTransactionFees: recommendedFees,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (_) {}
|
} catch (_) {} // either sleep expired or recommendedFees threw an exception
|
||||||
|
|
||||||
if (this.props.route.params.uri) {
|
if (this.props.route.params.uri) {
|
||||||
try {
|
try {
|
||||||
const { address, amount, memo } = this.decodeBitcoinUri(this.props.route.params.uri);
|
const { address, amount, memo, payjoinUrl } = DeeplinkSchemaMatch.decodeBitcoinUri(this.props.route.params.uri);
|
||||||
this.setState({ address, amount, memo });
|
this.setState({ address, amount, memo, isLoading: false, payjoinUrl });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
this.setState({ isLoading: false });
|
||||||
alert(loc.send.details_error_decode);
|
alert(loc.send.details_error_decode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.state.fromWallet.fetchUtxo();
|
try {
|
||||||
|
await Promise.race([this.state.fromWallet.fetchUtxo(), BlueApp.sleep(6000)]);
|
||||||
|
} catch (_) {
|
||||||
|
// either sleep expired or fetchUtxo threw an exception
|
||||||
|
}
|
||||||
|
|
||||||
this.setState({ isLoading: false });
|
this.setState({ isLoading: false });
|
||||||
await this.reCalcTx();
|
|
||||||
|
this.reCalcTx();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
|
@ -400,27 +410,6 @@ export default class SendDetails extends Component {
|
||||||
this.setState({ renderWalletSelectionButtonHidden: false, isAmountToolbarVisibleForAndroid: false });
|
this.setState({ renderWalletSelectionButtonHidden: false, isAmountToolbarVisibleForAndroid: false });
|
||||||
};
|
};
|
||||||
|
|
||||||
decodeBitcoinUri(uri) {
|
|
||||||
let amount = '';
|
|
||||||
let parsedBitcoinUri = null;
|
|
||||||
let address = uri || '';
|
|
||||||
let memo = '';
|
|
||||||
try {
|
|
||||||
parsedBitcoinUri = DeeplinkSchemaMatch.bip21decode(uri);
|
|
||||||
address = 'address' in parsedBitcoinUri ? parsedBitcoinUri.address : address;
|
|
||||||
if ('options' in parsedBitcoinUri) {
|
|
||||||
if ('amount' in parsedBitcoinUri.options) {
|
|
||||||
amount = parsedBitcoinUri.options.amount.toString();
|
|
||||||
amount = parsedBitcoinUri.options.amount;
|
|
||||||
}
|
|
||||||
if ('label' in parsedBitcoinUri.options) {
|
|
||||||
memo = parsedBitcoinUri.options.label || memo;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (_) {}
|
|
||||||
return { address, amount, memo };
|
|
||||||
}
|
|
||||||
|
|
||||||
async createTransaction() {
|
async createTransaction() {
|
||||||
Keyboard.dismiss();
|
Keyboard.dismiss();
|
||||||
this.setState({ isLoading: true });
|
this.setState({ isLoading: true });
|
||||||
|
@ -484,13 +473,63 @@ export default class SendDetails extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getChangeAddressFast() {
|
||||||
|
if (this.state.changeAddress) return this.state.changeAddress; // cache
|
||||||
|
|
||||||
|
/** @type {AbstractHDElectrumWallet|WatchOnlyWallet} */
|
||||||
|
const wallet = this.state.fromWallet;
|
||||||
|
let changeAddress;
|
||||||
|
if (WatchOnlyWallet.type === wallet.type && !wallet.isHd()) {
|
||||||
|
// plain watchonly - just get the address
|
||||||
|
changeAddress = wallet.getAddress();
|
||||||
|
} else if (WatchOnlyWallet.type === wallet.type || wallet instanceof AbstractHDElectrumWallet) {
|
||||||
|
changeAddress = wallet._getInternalAddressByIndex(wallet.getNextFreeChangeAddressIndex());
|
||||||
|
} else {
|
||||||
|
// legacy wallets
|
||||||
|
changeAddress = wallet.getAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
return changeAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getChangeAddressAsync() {
|
||||||
|
if (this.state.changeAddress) return this.state.changeAddress; // cache
|
||||||
|
|
||||||
|
/** @type {AbstractHDElectrumWallet|WatchOnlyWallet} */
|
||||||
|
const wallet = this.state.fromWallet;
|
||||||
|
let changeAddress;
|
||||||
|
if (WatchOnlyWallet.type === wallet.type && !wallet.isHd()) {
|
||||||
|
// plain watchonly - just get the address
|
||||||
|
changeAddress = wallet.getAddress();
|
||||||
|
} else {
|
||||||
|
// otherwise, lets call widely-used getChangeAddressAsync()
|
||||||
|
try {
|
||||||
|
changeAddress = await Promise.race([BlueApp.sleep(2000), wallet.getChangeAddressAsync()]);
|
||||||
|
} catch (_) {}
|
||||||
|
|
||||||
|
if (!changeAddress) {
|
||||||
|
// either sleep expired or getChangeAddressAsync threw an exception
|
||||||
|
if (wallet instanceof AbstractHDElectrumWallet) {
|
||||||
|
changeAddress = wallet._getInternalAddressByIndex(wallet.getNextFreeChangeAddressIndex());
|
||||||
|
} else {
|
||||||
|
// legacy wallets
|
||||||
|
changeAddress = wallet.getAddress();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changeAddress) this.setState({ changeAddress }); // cache
|
||||||
|
|
||||||
|
return changeAddress;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recalculating fee options by creating skeleton of future tx.
|
* Recalculating fee options by creating skeleton of future tx.
|
||||||
*/
|
*/
|
||||||
reCalcTx = async (all = false) => {
|
reCalcTx = (all = false) => {
|
||||||
const wallet = this.state.fromWallet;
|
const wallet = this.state.fromWallet;
|
||||||
const fees = this.state.networkTransactionFees;
|
const fees = this.state.networkTransactionFees;
|
||||||
const changeAddress = await wallet.getChangeAddressAsync();
|
const changeAddress = this.getChangeAddressFast();
|
||||||
const requestedSatPerByte = Number(this.state.fee);
|
const requestedSatPerByte = Number(this.state.fee);
|
||||||
const feePrecalc = { ...this.state.feePrecalc };
|
const feePrecalc = { ...this.state.feePrecalc };
|
||||||
|
|
||||||
|
@ -564,7 +603,7 @@ export default class SendDetails extends Component {
|
||||||
async createPsbtTransaction() {
|
async createPsbtTransaction() {
|
||||||
/** @type {HDSegwitBech32Wallet} */
|
/** @type {HDSegwitBech32Wallet} */
|
||||||
const wallet = this.state.fromWallet;
|
const wallet = this.state.fromWallet;
|
||||||
const changeAddress = await wallet.getChangeAddressAsync();
|
const changeAddress = await this.getChangeAddressAsync();
|
||||||
const requestedSatPerByte = Number(this.state.fee);
|
const requestedSatPerByte = Number(this.state.fee);
|
||||||
console.log({ requestedSatPerByte, utxo: wallet.getUtxo() });
|
console.log({ requestedSatPerByte, utxo: wallet.getUtxo() });
|
||||||
|
|
||||||
|
@ -606,6 +645,16 @@ export default class SendDetails extends Component {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (wallet.type === MultisigHDWallet.type) {
|
||||||
|
this.props.navigation.navigate('PsbtMultisig', {
|
||||||
|
memo: this.state.memo,
|
||||||
|
psbtBase64: psbt.toBase64(),
|
||||||
|
walletId: wallet.getID(),
|
||||||
|
});
|
||||||
|
this.setState({ isLoading: false });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
BlueApp.tx_metadata = BlueApp.tx_metadata || {};
|
BlueApp.tx_metadata = BlueApp.tx_metadata || {};
|
||||||
BlueApp.tx_metadata[tx.getId()] = {
|
BlueApp.tx_metadata[tx.getId()] = {
|
||||||
txhex: tx.toHex(),
|
txhex: tx.toHex(),
|
||||||
|
@ -619,6 +668,8 @@ export default class SendDetails extends Component {
|
||||||
tx: tx.toHex(),
|
tx: tx.toHex(),
|
||||||
recipients: targets,
|
recipients: targets,
|
||||||
satoshiPerByte: requestedSatPerByte,
|
satoshiPerByte: requestedSatPerByte,
|
||||||
|
payjoinUrl: this.state.payjoinUrl,
|
||||||
|
psbt,
|
||||||
});
|
});
|
||||||
this.setState({ isLoading: false });
|
this.setState({ isLoading: false });
|
||||||
}
|
}
|
||||||
|
@ -772,32 +823,54 @@ export default class SendDetails extends Component {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* watch-only wallets with enabled HW wallet support have different flow. we have to show PSBT to user as QR code
|
||||||
|
* so he can scan it and sign it. then we have to scan it back from user (via camera and QR code), and ask
|
||||||
|
* user whether he wants to broadcast it.
|
||||||
|
* alternatively, user can export psbt file, sign it externally and then import it
|
||||||
|
*
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
importTransaction = async () => {
|
importTransaction = async () => {
|
||||||
|
if (this.state.fromWallet.type !== WatchOnlyWallet.type) {
|
||||||
|
alert('Error: importing transaction in non-watchonly wallet (this should never happen)');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await DocumentPicker.pick({
|
const res = await DocumentPicker.pick({
|
||||||
type: Platform.OS === 'ios' ? ['io.bluewallet.psbt', 'io.bluewallet.psbt.txn'] : [DocumentPicker.types.allFiles],
|
type:
|
||||||
|
Platform.OS === 'ios'
|
||||||
|
? ['io.bluewallet.psbt', 'io.bluewallet.psbt.txn', DocumentPicker.types.plainText, 'public.json']
|
||||||
|
: [DocumentPicker.types.allFiles],
|
||||||
});
|
});
|
||||||
if (DeeplinkSchemaMatch.isPossiblyPSBTFile(res.uri)) {
|
|
||||||
|
if (DeeplinkSchemaMatch.isPossiblySignedPSBTFile(res.uri)) {
|
||||||
|
// we assume that transaction is already signed, so all we have to do is get txhex and pass it to next screen
|
||||||
|
// so user can broadcast:
|
||||||
const file = await RNFS.readFile(res.uri, 'ascii');
|
const file = await RNFS.readFile(res.uri, 'ascii');
|
||||||
const bufferDecoded = Buffer.from(file, 'ascii').toString('base64');
|
const psbt = bitcoin.Psbt.fromBase64(file);
|
||||||
if (bufferDecoded) {
|
const txhex = psbt.extractTransaction().toHex();
|
||||||
if (this.state.fromWallet.type === WatchOnlyWallet.type) {
|
|
||||||
// watch-only wallets with enabled HW wallet support have different flow. we have to show PSBT to user as QR code
|
|
||||||
// so he can scan it and sign it. then we have to scan it back from user (via camera and QR code), and ask
|
|
||||||
// user whether he wants to broadcast it.
|
|
||||||
// alternatively, user can export psbt file, sign it externally and then import it
|
|
||||||
this.props.navigation.navigate('PsbtWithHardwareWallet', {
|
this.props.navigation.navigate('PsbtWithHardwareWallet', {
|
||||||
memo: this.state.memo,
|
memo: this.state.memo,
|
||||||
fromWallet: this.state.fromWallet,
|
fromWallet: this.state.fromWallet,
|
||||||
psbt: file,
|
txhex,
|
||||||
});
|
});
|
||||||
this.setState({ isLoading: false });
|
this.setState({ isLoading: false, isAdvancedTransactionOptionsVisible: false });
|
||||||
return;
|
} else if (DeeplinkSchemaMatch.isPossiblyPSBTFile(res.uri)) {
|
||||||
}
|
// looks like transaction is UNsigned, so we construct PSBT object and pass to next screen
|
||||||
} else {
|
// so user can do smth with it:
|
||||||
throw new Error();
|
const file = await RNFS.readFile(res.uri, 'ascii');
|
||||||
}
|
const psbt = bitcoin.Psbt.fromBase64(file);
|
||||||
|
this.props.navigation.navigate('PsbtWithHardwareWallet', {
|
||||||
|
memo: this.state.memo,
|
||||||
|
fromWallet: this.state.fromWallet,
|
||||||
|
psbt,
|
||||||
|
});
|
||||||
|
this.setState({ isLoading: false, isAdvancedTransactionOptionsVisible: false });
|
||||||
} else if (DeeplinkSchemaMatch.isTXNFile(res.uri)) {
|
} else if (DeeplinkSchemaMatch.isTXNFile(res.uri)) {
|
||||||
|
// plain text file with txhex ready to broadcast
|
||||||
const file = await RNFS.readFile(res.uri, 'ascii');
|
const file = await RNFS.readFile(res.uri, 'ascii');
|
||||||
this.props.navigation.navigate('PsbtWithHardwareWallet', {
|
this.props.navigation.navigate('PsbtWithHardwareWallet', {
|
||||||
memo: this.state.memo,
|
memo: this.state.memo,
|
||||||
|
@ -805,7 +878,8 @@ export default class SendDetails extends Component {
|
||||||
txhex: file,
|
txhex: file,
|
||||||
});
|
});
|
||||||
this.setState({ isLoading: false, isAdvancedTransactionOptionsVisible: false });
|
this.setState({ isLoading: false, isAdvancedTransactionOptionsVisible: false });
|
||||||
return;
|
} else {
|
||||||
|
alert('Unrecognized file format');
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!DocumentPicker.isCancel(err)) {
|
if (!DocumentPicker.isCancel(err)) {
|
||||||
|
@ -814,6 +888,112 @@ export default class SendDetails extends Component {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
askCosignThisTransaction = async () => {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
Alert.alert(
|
||||||
|
loc.multisig.cosign_this_transaction,
|
||||||
|
'',
|
||||||
|
[
|
||||||
|
{
|
||||||
|
text: loc._.no,
|
||||||
|
style: 'cancel',
|
||||||
|
onPress: () => resolve(false),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: loc._.yes,
|
||||||
|
onPress: () => resolve(true),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
{ cancelable: false },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
_importTransactionMultisig = async base64arg => {
|
||||||
|
try {
|
||||||
|
/** @type MultisigHDWallet */
|
||||||
|
const fromWallet = this.state.fromWallet;
|
||||||
|
const base64 = base64arg || (await fs.openSignedTransaction());
|
||||||
|
if (!base64) return;
|
||||||
|
const psbt = bitcoin.Psbt.fromBase64(base64); // if it doesnt throw - all good, its valid
|
||||||
|
|
||||||
|
if (fromWallet.howManySignaturesCanWeMake() > 0 && (await this.askCosignThisTransaction())) {
|
||||||
|
fromWallet.cosignPsbt(psbt);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.props.navigation.navigate('PsbtMultisig', {
|
||||||
|
memo: this.state.memo,
|
||||||
|
psbtBase64: psbt.toBase64(),
|
||||||
|
walletId: fromWallet.getID(),
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
alert(loc.send.problem_with_psbt + ': ' + error.message);
|
||||||
|
}
|
||||||
|
this.setState({ isLoading: false, isAdvancedTransactionOptionsVisible: false });
|
||||||
|
};
|
||||||
|
|
||||||
|
importTransactionMultisig = async () => {
|
||||||
|
return this._importTransactionMultisig();
|
||||||
|
};
|
||||||
|
|
||||||
|
onBarScanned = ret => {
|
||||||
|
this.props.navigation.dangerouslyGetParent().pop();
|
||||||
|
if (!ret.data) ret = { data: ret };
|
||||||
|
if (ret.data.toUpperCase().startsWith('UR')) {
|
||||||
|
alert('BC-UR not decoded. This should never happen');
|
||||||
|
} else if (ret.data.indexOf('+') === -1 && ret.data.indexOf('=') === -1 && ret.data.indexOf('=') === -1) {
|
||||||
|
// this looks like NOT base64, so maybe its transaction's hex
|
||||||
|
// we dont support it in this flow
|
||||||
|
} else {
|
||||||
|
// psbt base64?
|
||||||
|
return this._importTransactionMultisig(ret.data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
importTransactionMultisigScanQr = async () => {
|
||||||
|
this.setState({ isAdvancedTransactionOptionsVisible: false });
|
||||||
|
this.props.navigation.navigate('ScanQRCodeRoot', {
|
||||||
|
screen: 'ScanQRCode',
|
||||||
|
params: {
|
||||||
|
onBarScanned: this.onBarScanned,
|
||||||
|
showFileImportButton: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
handleAddRecipient = () => {
|
||||||
|
const { addresses } = this.state;
|
||||||
|
addresses.push(new BitcoinTransaction());
|
||||||
|
this.setState(
|
||||||
|
{
|
||||||
|
addresses,
|
||||||
|
isAdvancedTransactionOptionsVisible: false,
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
this.scrollView.scrollToEnd();
|
||||||
|
if (this.state.addresses.length > 1) this.scrollView.flashScrollIndicators();
|
||||||
|
// after adding recipient it automatically scrolls to the last one
|
||||||
|
this.setState({ recipientsScrollIndex: this.state.addresses.length - 1 });
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
handleRemoveRecipient = () => {
|
||||||
|
const { addresses } = this.state;
|
||||||
|
addresses.splice(this.state.recipientsScrollIndex, 1);
|
||||||
|
this.setState(
|
||||||
|
{
|
||||||
|
addresses,
|
||||||
|
isAdvancedTransactionOptionsVisible: false,
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
if (this.state.addresses.length > 1) this.scrollView.flashScrollIndicators();
|
||||||
|
// after deletion it automatically scrolls to the last one
|
||||||
|
this.setState({ recipientsScrollIndex: this.state.addresses.length - 1 });
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
renderAdvancedTransactionOptionsModal = () => {
|
renderAdvancedTransactionOptionsModal = () => {
|
||||||
const isSendMaxUsed = this.state.addresses.some(element => element.amount === BitcoinUnit.MAX);
|
const isSendMaxUsed = this.state.addresses.some(element => element.amount === BitcoinUnit.MAX);
|
||||||
return (
|
return (
|
||||||
|
@ -856,6 +1036,22 @@ export default class SendDetails extends Component {
|
||||||
onPress={this.importTransaction}
|
onPress={this.importTransaction}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{this.state.fromWallet.type === MultisigHDWallet.type && (
|
||||||
|
<BlueListItem
|
||||||
|
title={loc.send.details_adv_import}
|
||||||
|
hideChevron
|
||||||
|
component={TouchableOpacity}
|
||||||
|
onPress={this.importTransactionMultisig}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{this.state.fromWallet.type === MultisigHDWallet.type && this.state.fromWallet.howManySignaturesCanWeMake() > 0 && (
|
||||||
|
<BlueListItem
|
||||||
|
title={loc.multisig.co_sign_transaction}
|
||||||
|
hideChevron
|
||||||
|
component={TouchableOpacity}
|
||||||
|
onPress={this.importTransactionMultisigScanQr}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{this.state.fromWallet.allowBatchSend() && (
|
{this.state.fromWallet.allowBatchSend() && (
|
||||||
<>
|
<>
|
||||||
<BlueListItem
|
<BlueListItem
|
||||||
|
@ -863,43 +1059,14 @@ export default class SendDetails extends Component {
|
||||||
title={loc.send.details_add_rec_add}
|
title={loc.send.details_add_rec_add}
|
||||||
hideChevron
|
hideChevron
|
||||||
component={TouchableOpacity}
|
component={TouchableOpacity}
|
||||||
onPress={() => {
|
onPress={this.handleAddRecipient}
|
||||||
const addresses = this.state.addresses;
|
|
||||||
addresses.push(new BitcoinTransaction());
|
|
||||||
this.setState(
|
|
||||||
{
|
|
||||||
addresses,
|
|
||||||
isAdvancedTransactionOptionsVisible: false,
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
this.scrollView.scrollToEnd();
|
|
||||||
if (this.state.addresses.length > 1) this.scrollView.flashScrollIndicators();
|
|
||||||
// after adding recipient it automatically scrolls to the last one
|
|
||||||
this.setState({ recipientsScrollIndex: this.state.addresses.length - 1 });
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<BlueListItem
|
<BlueListItem
|
||||||
title={loc.send.details_add_rec_rem}
|
title={loc.send.details_add_rec_rem}
|
||||||
hideChevron
|
hideChevron
|
||||||
disabled={this.state.addresses.length < 2}
|
disabled={this.state.addresses.length < 2}
|
||||||
component={TouchableOpacity}
|
component={TouchableOpacity}
|
||||||
onPress={() => {
|
onPress={this.handleRemoveRecipient}
|
||||||
const addresses = this.state.addresses;
|
|
||||||
addresses.splice(this.state.recipientsScrollIndex, 1);
|
|
||||||
this.setState(
|
|
||||||
{
|
|
||||||
addresses,
|
|
||||||
isAdvancedTransactionOptionsVisible: false,
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
if (this.state.addresses.length > 1) this.scrollView.flashScrollIndicators();
|
|
||||||
// after deletion it automatically scrolls to the last one
|
|
||||||
this.setState({ recipientsScrollIndex: this.state.addresses.length - 1 });
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
@ -1033,7 +1200,7 @@ export default class SendDetails extends Component {
|
||||||
onChangeText={async text => {
|
onChangeText={async text => {
|
||||||
text = text.trim();
|
text = text.trim();
|
||||||
const transactions = this.state.addresses;
|
const transactions = this.state.addresses;
|
||||||
const { address, amount, memo } = this.decodeBitcoinUri(text);
|
const { address, amount, memo, payjoinUrl } = DeeplinkSchemaMatch.decodeBitcoinUri(text);
|
||||||
item.address = address || text;
|
item.address = address || text;
|
||||||
item.amount = amount || item.amount;
|
item.amount = amount || item.amount;
|
||||||
transactions[index] = item;
|
transactions[index] = item;
|
||||||
|
@ -1041,6 +1208,7 @@ export default class SendDetails extends Component {
|
||||||
addresses: transactions,
|
addresses: transactions,
|
||||||
memo: memo || this.state.memo,
|
memo: memo || this.state.memo,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
|
payjoinUrl,
|
||||||
});
|
});
|
||||||
this.reCalcTx();
|
this.reCalcTx();
|
||||||
}}
|
}}
|
||||||
|
@ -1185,6 +1353,7 @@ SendDetails.propTypes = {
|
||||||
goBack: PropTypes.func,
|
goBack: PropTypes.func,
|
||||||
navigate: PropTypes.func,
|
navigate: PropTypes.func,
|
||||||
setParams: PropTypes.func,
|
setParams: PropTypes.func,
|
||||||
|
dangerouslyGetParent: PropTypes.func,
|
||||||
}),
|
}),
|
||||||
route: PropTypes.shape({
|
route: PropTypes.shape({
|
||||||
name: PropTypes.string,
|
name: PropTypes.string,
|
||||||
|
|
491
screen/send/psbtMultisig.js
Normal file
491
screen/send/psbtMultisig.js
Normal file
|
@ -0,0 +1,491 @@
|
||||||
|
/* global alert */
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import { FlatList, Platform, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
||||||
|
import { BlueButton, BlueButtonLink, BlueCard, BlueNavigationStyle, BlueSpacing20, BlueText, SafeBlueArea } from '../../BlueComponents';
|
||||||
|
import { DynamicQRCode } from '../../components/DynamicQRCode';
|
||||||
|
import { SquareButton } from '../../components/SquareButton';
|
||||||
|
import { getSystemName } from 'react-native-device-info';
|
||||||
|
import loc from '../../loc';
|
||||||
|
import { Icon } from 'react-native-elements';
|
||||||
|
import ImagePicker from 'react-native-image-picker';
|
||||||
|
import ScanQRCode from './ScanQRCode';
|
||||||
|
import { useNavigation, useRoute, useTheme } from '@react-navigation/native';
|
||||||
|
import { BitcoinUnit } from '../../models/bitcoinUnits';
|
||||||
|
|
||||||
|
const BlueApp = require('../../BlueApp');
|
||||||
|
const bitcoin = require('bitcoinjs-lib');
|
||||||
|
const currency = require('../../blue_modules/currency');
|
||||||
|
const fs = require('../../blue_modules/fs');
|
||||||
|
const LocalQRCode = require('@remobile/react-native-qrcode-local-image');
|
||||||
|
const isDesktop = getSystemName() === 'Mac OS X';
|
||||||
|
const BigNumber = require('bignumber.js');
|
||||||
|
|
||||||
|
const shortenAddress = addr => {
|
||||||
|
return addr.substr(0, Math.floor(addr.length / 2) - 1) + '\n' + addr.substr(Math.floor(addr.length / 2) - 1, addr.length);
|
||||||
|
};
|
||||||
|
|
||||||
|
const PsbtMultisig = () => {
|
||||||
|
const navigation = useNavigation();
|
||||||
|
const route = useRoute();
|
||||||
|
const { colors } = useTheme();
|
||||||
|
const [flatListHeight, setFlatListHeight] = useState(0);
|
||||||
|
|
||||||
|
const walletId = route.params.walletId;
|
||||||
|
const psbtBase64 = route.params.psbtBase64;
|
||||||
|
const memo = route.params.memo;
|
||||||
|
|
||||||
|
const [psbt, setPsbt] = useState(bitcoin.Psbt.fromBase64(psbtBase64));
|
||||||
|
const [isModalVisible, setIsModalVisible] = useState(false);
|
||||||
|
const stylesHook = StyleSheet.create({
|
||||||
|
root: {
|
||||||
|
backgroundColor: colors.elevated,
|
||||||
|
},
|
||||||
|
textBtc: {
|
||||||
|
color: colors.buttonAlternativeTextColor,
|
||||||
|
},
|
||||||
|
textDestinationFirstFour: {
|
||||||
|
color: colors.buttonAlternativeTextColor,
|
||||||
|
},
|
||||||
|
textBtcUnitValue: {
|
||||||
|
color: colors.buttonAlternativeTextColor,
|
||||||
|
},
|
||||||
|
textDestination: {
|
||||||
|
color: colors.foregroundColor,
|
||||||
|
},
|
||||||
|
modalContentShort: {
|
||||||
|
backgroundColor: colors.elevated,
|
||||||
|
},
|
||||||
|
textFiat: {
|
||||||
|
color: colors.alternativeTextColor,
|
||||||
|
},
|
||||||
|
provideSignatureButton: {
|
||||||
|
backgroundColor: colors.buttonDisabledBackgroundColor,
|
||||||
|
},
|
||||||
|
exportButton: {
|
||||||
|
backgroundColor: colors.buttonDisabledBackgroundColor,
|
||||||
|
},
|
||||||
|
provideSignatureButtonText: {
|
||||||
|
color: colors.buttonTextColor,
|
||||||
|
},
|
||||||
|
vaultKeyCircle: {
|
||||||
|
backgroundColor: colors.buttonDisabledBackgroundColor,
|
||||||
|
},
|
||||||
|
vaultKeyText: {
|
||||||
|
color: colors.alternativeTextColor,
|
||||||
|
},
|
||||||
|
feeFiatText: {
|
||||||
|
color: colors.alternativeTextColor,
|
||||||
|
},
|
||||||
|
vaultKeyCircleSuccess: {
|
||||||
|
backgroundColor: colors.msSuccessBG,
|
||||||
|
},
|
||||||
|
vaultKeyTextSigned: {
|
||||||
|
color: colors.msSuccessBG,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
/** @type MultisigHDWallet */
|
||||||
|
const wallet = BlueApp.getWallets().find(w => w.getID() === walletId);
|
||||||
|
let destination = [];
|
||||||
|
let totalSat = 0;
|
||||||
|
const targets = [];
|
||||||
|
for (const output of psbt.txOutputs) {
|
||||||
|
if (output.address && !wallet.weOwnAddress(output.address)) {
|
||||||
|
totalSat += output.value;
|
||||||
|
destination.push(output.address);
|
||||||
|
targets.push({ address: output.address, value: output.value });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
destination = shortenAddress(destination.join(', '));
|
||||||
|
const totalBtc = new BigNumber(totalSat).dividedBy(100000000).toNumber();
|
||||||
|
const totalFiat = currency.satoshiToLocalCurrency(totalSat);
|
||||||
|
const fileName = `${Date.now()}.psbt`;
|
||||||
|
|
||||||
|
const howManySignaturesWeHave = () => {
|
||||||
|
return wallet.calculateHowManySignaturesWeHaveFromPsbt(psbt);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getFee = () => {
|
||||||
|
return wallet.calculateFeeFromPsbt(psbt);
|
||||||
|
};
|
||||||
|
|
||||||
|
const _renderItem = el => {
|
||||||
|
if (el.index >= howManySignaturesWeHave()) return _renderItemUnsigned(el);
|
||||||
|
else return _renderItemSigned(el);
|
||||||
|
};
|
||||||
|
|
||||||
|
const _renderItemUnsigned = el => {
|
||||||
|
const renderProvideSignature = el.index === howManySignaturesWeHave();
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
<View style={styles.itemUnsignedWrapper}>
|
||||||
|
<View style={[styles.vaultKeyCircle, stylesHook.vaultKeyCircle]}>
|
||||||
|
<Text style={[styles.vaultKeyText, stylesHook.vaultKeyText]}>{el.index + 1}</Text>
|
||||||
|
</View>
|
||||||
|
<View style={styles.vaultKeyTextWrapper}>
|
||||||
|
<Text style={[styles.vaultKeyText, stylesHook.vaultKeyText]}>
|
||||||
|
{loc.formatString(loc.multisig.vault_key, { number: el.index + 1 })}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{renderProvideSignature && (
|
||||||
|
<View>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={[styles.provideSignatureButton, stylesHook.provideSignatureButton]}
|
||||||
|
onPress={() => {
|
||||||
|
setIsModalVisible(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text style={[styles.provideSignatureButtonText, stylesHook.provideSignatureButtonText]}>
|
||||||
|
{loc.multisig.provide_signature}
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const _renderItemSigned = el => {
|
||||||
|
return (
|
||||||
|
<View style={styles.flexDirectionRow}>
|
||||||
|
<View style={[styles.vaultKeyCircleSuccess, stylesHook.vaultKeyCircleSuccess]}>
|
||||||
|
<Icon size={24} name="check" type="ionicons" color={colors.msSuccessCheck} />
|
||||||
|
</View>
|
||||||
|
<View style={styles.vaultKeyTextSignedWrapper}>
|
||||||
|
<Text style={[styles.vaultKeyTextSigned, stylesHook.vaultKeyTextSigned]}>
|
||||||
|
{loc.formatString(loc.multisig.vault_key, { number: el.index + 1 })}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const _combinePSBT = receivedPSBTBase64 => {
|
||||||
|
const receivedPSBT = bitcoin.Psbt.fromBase64(receivedPSBTBase64);
|
||||||
|
try {
|
||||||
|
const newPsbt = psbt.combine(receivedPSBT);
|
||||||
|
navigation.dangerouslyGetParent().pop();
|
||||||
|
setPsbt(newPsbt);
|
||||||
|
setIsModalVisible(false);
|
||||||
|
} catch (error) {
|
||||||
|
alert(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onBarScanned = ret => {
|
||||||
|
if (!ret.data) ret = { data: ret };
|
||||||
|
if (ret.data.toUpperCase().startsWith('UR')) {
|
||||||
|
alert('BC-UR not decoded. This should never happen');
|
||||||
|
} else if (ret.data.indexOf('+') === -1 && ret.data.indexOf('=') === -1 && ret.data.indexOf('=') === -1) {
|
||||||
|
// this looks like NOT base64, so maybe its transaction's hex
|
||||||
|
// we dont support it in this flow
|
||||||
|
} else {
|
||||||
|
// psbt base64?
|
||||||
|
_combinePSBT(ret.data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onConfirm = () => {
|
||||||
|
try {
|
||||||
|
psbt.finalizeAllInputs();
|
||||||
|
} catch (_) {} // ignore if it is already finalized
|
||||||
|
|
||||||
|
try {
|
||||||
|
const tx = psbt.extractTransaction().toHex();
|
||||||
|
const satoshiPerByte = Math.round(getFee() / (tx.length / 2));
|
||||||
|
navigation.navigate('Confirm', {
|
||||||
|
fee: new BigNumber(getFee()).dividedBy(100000000).toNumber(),
|
||||||
|
memo: memo,
|
||||||
|
fromWallet: wallet,
|
||||||
|
tx,
|
||||||
|
recipients: targets,
|
||||||
|
satoshiPerByte,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
alert(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const openScanner = () => {
|
||||||
|
if (isDesktop) {
|
||||||
|
ImagePicker.launchCamera(
|
||||||
|
{
|
||||||
|
title: null,
|
||||||
|
mediaType: 'photo',
|
||||||
|
takePhotoButtonTitle: null,
|
||||||
|
},
|
||||||
|
response => {
|
||||||
|
if (response.uri) {
|
||||||
|
const uri = Platform.OS === 'ios' ? response.uri.toString().replace('file://', '') : response.path.toString();
|
||||||
|
LocalQRCode.decode(uri, (error, result) => {
|
||||||
|
if (!error) {
|
||||||
|
onBarScanned(result);
|
||||||
|
} else {
|
||||||
|
alert(loc.send.qr_error_no_qrcode);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (response.error) {
|
||||||
|
ScanQRCode.presentCameraNotAuthorizedAlert(response.error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
navigation.navigate('ScanQRCodeRoot', {
|
||||||
|
screen: 'ScanQRCode',
|
||||||
|
params: {
|
||||||
|
onBarScanned: onBarScanned,
|
||||||
|
showFileImportButton: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const exportPSBT = async () => {
|
||||||
|
await fs.writeFileAndExport(fileName, psbt.toBase64());
|
||||||
|
};
|
||||||
|
|
||||||
|
const isConfirmEnabled = () => {
|
||||||
|
return howManySignaturesWeHave() >= wallet.getM();
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderDynamicQrCode = () => {
|
||||||
|
return (
|
||||||
|
<SafeBlueArea style={[styles.root, stylesHook.root]}>
|
||||||
|
<ScrollView centerContent contentContainerStyle={styles.scrollViewContent}>
|
||||||
|
<View style={[styles.modalContentShort, stylesHook.modalContentShort]}>
|
||||||
|
<DynamicQRCode value={psbt.toHex()} capacity={666} />
|
||||||
|
<BlueSpacing20 />
|
||||||
|
<SquareButton
|
||||||
|
style={[styles.exportButton, stylesHook.exportButton]}
|
||||||
|
onPress={openScanner}
|
||||||
|
title={loc.multisig.scan_or_import_file}
|
||||||
|
/>
|
||||||
|
<BlueSpacing20 />
|
||||||
|
<SquareButton style={[styles.exportButton, stylesHook.exportButton]} onPress={exportPSBT} title={loc.multisig.share} />
|
||||||
|
<BlueSpacing20 />
|
||||||
|
<BlueButtonLink title={loc._.cancel} onPress={() => setIsModalVisible(false)} />
|
||||||
|
</View>
|
||||||
|
</ScrollView>
|
||||||
|
</SafeBlueArea>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const destinationAddress = () => {
|
||||||
|
// eslint-disable-next-line prefer-const
|
||||||
|
let destinationAddressView = [];
|
||||||
|
const destinations = Object.entries(destination.split(','));
|
||||||
|
for (const [index, address] of destinations) {
|
||||||
|
if (index > 1) {
|
||||||
|
destinationAddressView.push(
|
||||||
|
<View style={styles.destionationTextContainer} key={`end-${index}`}>
|
||||||
|
<Text numberOfLines={0} style={[styles.textDestinationFirstFour, stylesHook.textFiat]}>
|
||||||
|
and {destinations.length - 2} more...
|
||||||
|
</Text>
|
||||||
|
</View>,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
const currentAddress = address;
|
||||||
|
const firstFour = currentAddress.substring(0, 5);
|
||||||
|
const lastFour = currentAddress.substring(currentAddress.length - 5, currentAddress.length);
|
||||||
|
const middle = currentAddress.split(firstFour)[1].split(lastFour)[0];
|
||||||
|
destinationAddressView.push(
|
||||||
|
<View style={styles.destionationTextContainer} key={`${currentAddress}-${index}`}>
|
||||||
|
<Text numberOfLines={2} style={[styles.textDestinationFirstFour, stylesHook.textBtc]}>
|
||||||
|
{firstFour}
|
||||||
|
<Text> </Text>
|
||||||
|
<Text style={[styles.textDestination, stylesHook.textFiat]}>{middle}</Text>
|
||||||
|
<Text> </Text>
|
||||||
|
<Text style={[styles.textDestinationFirstFour, stylesHook.textBtc]}>{lastFour}</Text>
|
||||||
|
</Text>
|
||||||
|
</View>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return destinationAddressView;
|
||||||
|
};
|
||||||
|
|
||||||
|
const header = (
|
||||||
|
<View style={stylesHook.root}>
|
||||||
|
<View style={styles.containerText}>
|
||||||
|
<BlueText style={[styles.textBtc, stylesHook.textBtc]}>{totalBtc}</BlueText>
|
||||||
|
<View style={styles.textBtcUnit}>
|
||||||
|
<BlueText style={[styles.textBtcUnitValue, stylesHook.textBtcUnitValue]}> {BitcoinUnit.BTC}</BlueText>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View style={styles.containerText}>
|
||||||
|
<BlueText style={[styles.textFiat, stylesHook.textFiat]}>{totalFiat}</BlueText>
|
||||||
|
</View>
|
||||||
|
<View>{destinationAddress()}</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
const footer = (
|
||||||
|
<View style={styles.bottomWrapper}>
|
||||||
|
<View style={styles.bottomFeesWrapper}>
|
||||||
|
<BlueText style={[styles.feeFiatText, stylesHook.feeFiatText]}>
|
||||||
|
{loc.formatString(loc.multisig.fee, { number: currency.satoshiToLocalCurrency(getFee()) })} -{' '}
|
||||||
|
</BlueText>
|
||||||
|
<BlueText>{loc.formatString(loc.multisig.fee_btc, { number: currency.satoshiToBTC(getFee()) })}</BlueText>
|
||||||
|
</View>
|
||||||
|
<BlueButton disabled={!isConfirmEnabled()} title={loc.multisig.confirm} onPress={onConfirm} />
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isModalVisible) return renderDynamicQrCode();
|
||||||
|
|
||||||
|
const onLayout = e => {
|
||||||
|
setFlatListHeight(e.nativeEvent.layout.height);
|
||||||
|
};
|
||||||
|
|
||||||
|
const data = new Array(wallet.getM());
|
||||||
|
return (
|
||||||
|
<SafeBlueArea style={[styles.root, stylesHook.root]}>
|
||||||
|
<View style={styles.container}>
|
||||||
|
<View style={styles.mstopcontainer}>
|
||||||
|
<View style={styles.mscontainer}>
|
||||||
|
<View style={[styles.msleft, { height: flatListHeight - 200 }]} />
|
||||||
|
</View>
|
||||||
|
<View style={styles.msright}>
|
||||||
|
<BlueCard>
|
||||||
|
<FlatList
|
||||||
|
data={data}
|
||||||
|
onLayout={onLayout}
|
||||||
|
renderItem={_renderItem}
|
||||||
|
keyExtractor={(_item, index) => `${index}`}
|
||||||
|
ListHeaderComponent={header}
|
||||||
|
scrollEnabled={false}
|
||||||
|
/>
|
||||||
|
</BlueCard>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
{footer}
|
||||||
|
</View>
|
||||||
|
</SafeBlueArea>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
root: {
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
mstopcontainer: {
|
||||||
|
flex: 1,
|
||||||
|
flexDirection: 'row',
|
||||||
|
},
|
||||||
|
mscontainer: {
|
||||||
|
flex: 10,
|
||||||
|
},
|
||||||
|
msleft: {
|
||||||
|
width: 1,
|
||||||
|
borderStyle: 'dashed',
|
||||||
|
borderWidth: 0.8,
|
||||||
|
borderColor: '#c4c4c4',
|
||||||
|
marginLeft: 40,
|
||||||
|
marginTop: 185,
|
||||||
|
},
|
||||||
|
msright: {
|
||||||
|
flex: 90,
|
||||||
|
marginLeft: '-11%',
|
||||||
|
},
|
||||||
|
scrollViewContent: {
|
||||||
|
flexGrow: 1,
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
},
|
||||||
|
container: {
|
||||||
|
flexDirection: 'column',
|
||||||
|
paddingTop: 24,
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
containerText: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
destionationTextContainer: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
marginBottom: 4,
|
||||||
|
paddingHorizontal: 60,
|
||||||
|
fontSize: 14,
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
textFiat: {
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: '500',
|
||||||
|
marginBottom: 30,
|
||||||
|
},
|
||||||
|
textBtc: {
|
||||||
|
fontWeight: 'bold',
|
||||||
|
fontSize: 30,
|
||||||
|
},
|
||||||
|
textDestinationFirstFour: {
|
||||||
|
fontSize: 14,
|
||||||
|
},
|
||||||
|
textDestination: {
|
||||||
|
paddingTop: 10,
|
||||||
|
paddingBottom: 40,
|
||||||
|
fontSize: 14,
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
},
|
||||||
|
bottomModal: {
|
||||||
|
justifyContent: 'flex-end',
|
||||||
|
margin: 0,
|
||||||
|
},
|
||||||
|
modalContentShort: {
|
||||||
|
marginLeft: 20,
|
||||||
|
marginRight: 20,
|
||||||
|
},
|
||||||
|
copyToClipboard: {
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
exportButton: {
|
||||||
|
height: 48,
|
||||||
|
borderRadius: 8,
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: 'center',
|
||||||
|
paddingHorizontal: 16,
|
||||||
|
},
|
||||||
|
provideSignatureButton: {
|
||||||
|
marginTop: 24,
|
||||||
|
marginLeft: 40,
|
||||||
|
height: 48,
|
||||||
|
borderRadius: 8,
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: 'center',
|
||||||
|
paddingHorizontal: 16,
|
||||||
|
marginBottom: 8,
|
||||||
|
},
|
||||||
|
provideSignatureButtonText: { fontWeight: '600', fontSize: 15 },
|
||||||
|
vaultKeyText: { fontSize: 18, fontWeight: 'bold' },
|
||||||
|
vaultKeyTextWrapper: { justifyContent: 'center', alignItems: 'center', paddingLeft: 16 },
|
||||||
|
vaultKeyCircle: {
|
||||||
|
width: 42,
|
||||||
|
height: 42,
|
||||||
|
borderRadius: 25,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
vaultKeyCircleSuccess: {
|
||||||
|
width: 42,
|
||||||
|
height: 42,
|
||||||
|
borderRadius: 25,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
itemUnsignedWrapper: { flexDirection: 'row', paddingTop: 16 },
|
||||||
|
textDestinationSpacingRight: { marginRight: 4 },
|
||||||
|
textDestinationSpacingLeft: { marginLeft: 4 },
|
||||||
|
vaultKeyTextSigned: { fontSize: 18, fontWeight: 'bold' },
|
||||||
|
vaultKeyTextSignedWrapper: { justifyContent: 'center', alignItems: 'center', paddingLeft: 16 },
|
||||||
|
flexDirectionRow: { flexDirection: 'row', paddingVertical: 12 },
|
||||||
|
textBtcUnit: { justifyContent: 'flex-end', bottom: 8 },
|
||||||
|
bottomFeesWrapper: { flexDirection: 'row', paddingBottom: 20 },
|
||||||
|
bottomWrapper: { justifyContent: 'center', alignItems: 'center', paddingVertical: 20 },
|
||||||
|
});
|
||||||
|
|
||||||
|
PsbtMultisig.navigationOptions = () => ({
|
||||||
|
...BlueNavigationStyle(null, false),
|
||||||
|
title: loc.multisig.header,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default PsbtMultisig;
|
|
@ -33,7 +33,6 @@ import { getSystemName } from 'react-native-device-info';
|
||||||
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||||
import RNFS from 'react-native-fs';
|
import RNFS from 'react-native-fs';
|
||||||
import DocumentPicker from 'react-native-document-picker';
|
import DocumentPicker from 'react-native-document-picker';
|
||||||
import { decodeUR, extractSingleWorkload } from 'bc-ur/dist';
|
|
||||||
import loc from '../../loc';
|
import loc from '../../loc';
|
||||||
import { BlueCurrentTheme } from '../../components/themes';
|
import { BlueCurrentTheme } from '../../components/themes';
|
||||||
import ScanQRCode from './ScanQRCode';
|
import ScanQRCode from './ScanQRCode';
|
||||||
|
@ -68,6 +67,7 @@ const styles = StyleSheet.create({
|
||||||
rootPadding: {
|
rootPadding: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
paddingTop: 20,
|
paddingTop: 20,
|
||||||
|
backgroundColor: BlueCurrentTheme.colors.elevated,
|
||||||
},
|
},
|
||||||
closeCamera: {
|
closeCamera: {
|
||||||
width: 40,
|
width: 40,
|
||||||
|
@ -89,6 +89,7 @@ const styles = StyleSheet.create({
|
||||||
hexWrap: {
|
hexWrap: {
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
|
backgroundColor: BlueCurrentTheme.colors.elevated,
|
||||||
},
|
},
|
||||||
hexLabel: {
|
hexLabel: {
|
||||||
color: BlueCurrentTheme.colors.foregroundColor,
|
color: BlueCurrentTheme.colors.foregroundColor,
|
||||||
|
@ -124,44 +125,6 @@ const styles = StyleSheet.create({
|
||||||
export default class PsbtWithHardwareWallet extends Component {
|
export default class PsbtWithHardwareWallet extends Component {
|
||||||
cameraRef = null;
|
cameraRef = null;
|
||||||
|
|
||||||
_onReadUniformResource = ur => {
|
|
||||||
try {
|
|
||||||
const [index, total] = extractSingleWorkload(ur);
|
|
||||||
const { animatedQRCodeData } = this.state;
|
|
||||||
if (animatedQRCodeData.length > 0) {
|
|
||||||
const currentTotal = animatedQRCodeData[0].total;
|
|
||||||
if (total !== currentTotal) {
|
|
||||||
alert('invalid animated QRCode');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!animatedQRCodeData.find(i => i.index === index)) {
|
|
||||||
this.setState(
|
|
||||||
state => ({
|
|
||||||
animatedQRCodeData: [
|
|
||||||
...state.animatedQRCodeData,
|
|
||||||
{
|
|
||||||
index,
|
|
||||||
total,
|
|
||||||
data: ur,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
() => {
|
|
||||||
if (this.state.animatedQRCodeData.length === total) {
|
|
||||||
const payload = decodeUR(this.state.animatedQRCodeData.map(i => i.data));
|
|
||||||
const psbtB64 = Buffer.from(payload, 'hex').toString('base64');
|
|
||||||
const Tx = this._combinePSBT(psbtB64);
|
|
||||||
this.setState({ txhex: Tx.toHex() });
|
|
||||||
this.props.navigation.dangerouslyGetParent().pop();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (Err) {
|
|
||||||
alert('invalid animated QRCode fragment, please try again');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
_combinePSBT = receivedPSBT => {
|
_combinePSBT = receivedPSBT => {
|
||||||
return this.state.fromWallet.combinePsbt(this.state.psbt, receivedPSBT);
|
return this.state.fromWallet.combinePsbt(this.state.psbt, receivedPSBT);
|
||||||
};
|
};
|
||||||
|
@ -169,11 +132,11 @@ export default class PsbtWithHardwareWallet extends Component {
|
||||||
onBarScanned = ret => {
|
onBarScanned = ret => {
|
||||||
if (ret && !ret.data) ret = { data: ret };
|
if (ret && !ret.data) ret = { data: ret };
|
||||||
if (ret.data.toUpperCase().startsWith('UR')) {
|
if (ret.data.toUpperCase().startsWith('UR')) {
|
||||||
return this._onReadUniformResource(ret.data);
|
alert('BC-UR not decoded. This should never happen');
|
||||||
}
|
}
|
||||||
if (ret.data.indexOf('+') === -1 && ret.data.indexOf('=') === -1 && ret.data.indexOf('=') === -1) {
|
if (ret.data.indexOf('+') === -1 && ret.data.indexOf('=') === -1 && ret.data.indexOf('=') === -1) {
|
||||||
// this looks like NOT base64, so maybe its transaction's hex
|
// this looks like NOT base64, so maybe its transaction's hex
|
||||||
this.setState({ txhex: ret.data }, () => this.props.navigation.dangerouslyGetParent().pop());
|
this.setState({ txhex: ret.data });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
@ -201,11 +164,20 @@ export default class PsbtWithHardwareWallet extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
static getDerivedStateFromProps(nextProps, prevState) {
|
static getDerivedStateFromProps(nextProps, prevState) {
|
||||||
|
if (!prevState.psbt && !nextProps.route.params.txhex) {
|
||||||
|
alert('There is no transaction signing in progress');
|
||||||
|
return {
|
||||||
|
...prevState,
|
||||||
|
isLoading: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const deepLinkPSBT = nextProps.route.params.deepLinkPSBT;
|
const deepLinkPSBT = nextProps.route.params.deepLinkPSBT;
|
||||||
const txhex = nextProps.route.params.txhex;
|
const txhex = nextProps.route.params.txhex;
|
||||||
if (deepLinkPSBT) {
|
if (deepLinkPSBT) {
|
||||||
|
const psbt = bitcoin.Psbt.fromBase64(deepLinkPSBT);
|
||||||
try {
|
try {
|
||||||
const Tx = prevState.fromWallet.combinePsbt(prevState.psbt, deepLinkPSBT);
|
const Tx = prevState.fromWallet.combinePsbt(prevState.psbt, psbt);
|
||||||
return {
|
return {
|
||||||
...prevState,
|
...prevState,
|
||||||
txhex: Tx.toHex(),
|
txhex: Tx.toHex(),
|
||||||
|
|
|
@ -1,19 +1,104 @@
|
||||||
import React, { Component } from 'react';
|
import React, { useEffect, useRef } from 'react';
|
||||||
import LottieView from 'lottie-react-native';
|
import LottieView from 'lottie-react-native';
|
||||||
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||||
import { View, StyleSheet } from 'react-native';
|
import { View, StyleSheet } from 'react-native';
|
||||||
import { Text } from 'react-native-elements';
|
import { Text } from 'react-native-elements';
|
||||||
import { BlueButton, SafeBlueArea, BlueCard } from '../../BlueComponents';
|
import { BlueButton, SafeBlueArea, BlueCard } from '../../BlueComponents';
|
||||||
import { BitcoinUnit } from '../../models/bitcoinUnits';
|
import { BitcoinUnit } from '../../models/bitcoinUnits';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import loc from '../../loc';
|
import loc from '../../loc';
|
||||||
import { BlueCurrentTheme } from '../../components/themes';
|
import { useNavigation, useRoute, useTheme } from '@react-navigation/native';
|
||||||
|
|
||||||
|
const Success = () => {
|
||||||
|
const { colors } = useTheme();
|
||||||
|
const { dangerouslyGetParent } = useNavigation();
|
||||||
|
const { amount, fee = 0, amountUnit = BitcoinUnit.BTC, invoiceDescription = '' } = useRoute().params;
|
||||||
|
const animationRef = useRef();
|
||||||
|
const stylesHook = StyleSheet.create({
|
||||||
|
root: {
|
||||||
|
backgroundColor: colors.elevated,
|
||||||
|
},
|
||||||
|
amountValue: {
|
||||||
|
color: colors.alternativeTextColor2,
|
||||||
|
},
|
||||||
|
amountUnit: {
|
||||||
|
color: colors.alternativeTextColor2,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
useEffect(() => {
|
||||||
|
console.log('send/success - useEffect');
|
||||||
|
ReactNativeHapticFeedback.trigger('notificationSuccess', { ignoreAndroidSystemSettings: false });
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
const pop = () => {
|
||||||
|
dangerouslyGetParent().pop();
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
animationRef.current.reset();
|
||||||
|
animationRef.current.resume();
|
||||||
|
}, [colors]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SafeBlueArea style={[styles.root, stylesHook.root]}>
|
||||||
|
<BlueCard style={styles.amout}>
|
||||||
|
{amount > 0 && (
|
||||||
|
<View style={styles.view}>
|
||||||
|
<Text style={[styles.amountValue, stylesHook.amountValue]}>{amount}</Text>
|
||||||
|
<Text style={[styles.amountUnit, stylesHook.amountUnit]}>{' ' + amountUnit}</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
{fee > 0 && (
|
||||||
|
<Text style={styles.feeText}>
|
||||||
|
{loc.send.create_fee}: {fee} {BitcoinUnit.BTC}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
{fee <= 0 && (
|
||||||
|
<Text numberOfLines={0} style={styles.feeText}>
|
||||||
|
{invoiceDescription}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</BlueCard>
|
||||||
|
<View style={styles.ready}>
|
||||||
|
<LottieView
|
||||||
|
style={styles.lottie}
|
||||||
|
source={require('../../img/bluenice.json')}
|
||||||
|
autoPlay
|
||||||
|
ref={animationRef}
|
||||||
|
loop={false}
|
||||||
|
colorFilters={[
|
||||||
|
{
|
||||||
|
keypath: 'spark',
|
||||||
|
color: colors.success,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
keypath: 'circle',
|
||||||
|
color: colors.success,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
keypath: 'Oval',
|
||||||
|
color: colors.successCheck,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<BlueCard>
|
||||||
|
<BlueButton onPress={pop} title={loc.send.success_done} />
|
||||||
|
</BlueCard>
|
||||||
|
</SafeBlueArea>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Success.navigationOptions = {
|
||||||
|
headerShown: false,
|
||||||
|
gesturesEnabled: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Success;
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
root: {
|
root: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
paddingTop: 19,
|
paddingTop: 19,
|
||||||
backgroundColor: BlueCurrentTheme.colors.elevated,
|
|
||||||
},
|
},
|
||||||
amout: {
|
amout: {
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
@ -26,12 +111,10 @@ const styles = StyleSheet.create({
|
||||||
paddingBottom: 16,
|
paddingBottom: 16,
|
||||||
},
|
},
|
||||||
amountValue: {
|
amountValue: {
|
||||||
color: BlueCurrentTheme.colors.alternativeTextColor2,
|
|
||||||
fontSize: 36,
|
fontSize: 36,
|
||||||
fontWeight: '600',
|
fontWeight: '600',
|
||||||
},
|
},
|
||||||
amountUnit: {
|
amountUnit: {
|
||||||
color: BlueCurrentTheme.colors.alternativeTextColor2,
|
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
marginHorizontal: 4,
|
marginHorizontal: 4,
|
||||||
paddingBottom: 6,
|
paddingBottom: 6,
|
||||||
|
@ -61,92 +144,3 @@ const styles = StyleSheet.create({
|
||||||
height: 400,
|
height: 400,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default class Success extends Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
console.log('send/success constructor');
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
amount: props.route.params.amount,
|
|
||||||
fee: props.route.params.fee || 0,
|
|
||||||
amountUnit: props.route.params.amountUnit || BitcoinUnit.BTC,
|
|
||||||
invoiceDescription: props.route.params.invoiceDescription || '',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async componentDidMount() {
|
|
||||||
console.log('send/success - componentDidMount');
|
|
||||||
ReactNativeHapticFeedback.trigger('notificationSuccess', { ignoreAndroidSystemSettings: false });
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<SafeBlueArea style={styles.root}>
|
|
||||||
<BlueCard style={styles.amout}>
|
|
||||||
<View style={styles.view}>
|
|
||||||
<Text style={styles.amountValue}>{this.state.amount}</Text>
|
|
||||||
<Text style={styles.amountUnit}>{' ' + this.state.amountUnit}</Text>
|
|
||||||
</View>
|
|
||||||
{this.state.fee > 0 && (
|
|
||||||
<Text style={styles.feeText}>
|
|
||||||
{loc.send.create_fee}: {this.state.fee} {BitcoinUnit.BTC}
|
|
||||||
</Text>
|
|
||||||
)}
|
|
||||||
{this.state.fee <= 0 && (
|
|
||||||
<Text numberOfLines={0} style={styles.feeText}>
|
|
||||||
{this.state.invoiceDescription}
|
|
||||||
</Text>
|
|
||||||
)}
|
|
||||||
</BlueCard>
|
|
||||||
<View style={styles.ready}>
|
|
||||||
<LottieView
|
|
||||||
style={styles.lottie}
|
|
||||||
source={require('../../img/bluenice.json')}
|
|
||||||
autoPlay
|
|
||||||
loop={false}
|
|
||||||
colorFilters={[
|
|
||||||
{
|
|
||||||
keypath: 'spark',
|
|
||||||
color: BlueCurrentTheme.colors.success,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
keypath: 'circle',
|
|
||||||
color: BlueCurrentTheme.colors.success,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
keypath: 'Oval',
|
|
||||||
color: BlueCurrentTheme.colors.successCheck,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
<BlueCard>
|
|
||||||
<BlueButton onPress={() => this.props.navigation.dangerouslyGetParent().pop()} title={loc.send.success_done} />
|
|
||||||
</BlueCard>
|
|
||||||
</SafeBlueArea>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Success.propTypes = {
|
|
||||||
navigation: PropTypes.shape({
|
|
||||||
goBack: PropTypes.func,
|
|
||||||
navigate: PropTypes.func,
|
|
||||||
dangerouslyGetParent: PropTypes.func,
|
|
||||||
state: PropTypes.shape({
|
|
||||||
params: PropTypes.shape({
|
|
||||||
amount: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
||||||
fee: PropTypes.number,
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
route: PropTypes.shape({
|
|
||||||
params: PropTypes.object,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
|
|
||||||
Success.navigationOptions = {
|
|
||||||
headerShown: false,
|
|
||||||
gesturesEnabled: false,
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { ScrollView, Platform, TouchableWithoutFeedback, TouchableOpacity, StyleSheet } from 'react-native';
|
import { ScrollView, Platform, TouchableWithoutFeedback, TouchableOpacity, StyleSheet } from 'react-native';
|
||||||
import { BlueLoading, BlueTextHooks, BlueSpacing20, BlueListItemHooks, BlueNavigationStyle, BlueCard } from '../../BlueComponents';
|
import { BlueLoading, BlueTextHooks, BlueSpacing20, BlueListItem, BlueNavigationStyle, BlueCard } from '../../BlueComponents';
|
||||||
import { AppStorage } from '../../class';
|
import { AppStorage } from '../../class';
|
||||||
import { useNavigation, useTheme } from '@react-navigation/native';
|
import { useNavigation, useTheme } from '@react-navigation/native';
|
||||||
import HandoffSettings from '../../class/handoff';
|
import HandoffSettings from '../../class/handoff';
|
||||||
|
@ -58,17 +58,12 @@ const GeneralSettings = () => {
|
||||||
<ScrollView style={stylesWithThemeHook.scroll}>
|
<ScrollView style={stylesWithThemeHook.scroll}>
|
||||||
{BlueApp.getWallets().length > 1 && (
|
{BlueApp.getWallets().length > 1 && (
|
||||||
<>
|
<>
|
||||||
<BlueListItemHooks
|
<BlueListItem component={TouchableOpacity} onPress={() => navigate('DefaultView')} title={loc.settings.default_title} chevron />
|
||||||
component={TouchableOpacity}
|
|
||||||
onPress={() => navigate('DefaultView')}
|
|
||||||
title={loc.settings.default_title}
|
|
||||||
chevron
|
|
||||||
/>
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{Platform.OS === 'ios' ? (
|
{Platform.OS === 'ios' ? (
|
||||||
<>
|
<>
|
||||||
<BlueListItemHooks
|
<BlueListItem
|
||||||
hideChevron
|
hideChevron
|
||||||
title={loc.settings.general_continuity}
|
title={loc.settings.general_continuity}
|
||||||
Component={TouchableWithoutFeedback}
|
Component={TouchableWithoutFeedback}
|
||||||
|
@ -80,7 +75,7 @@ const GeneralSettings = () => {
|
||||||
<BlueSpacing20 />
|
<BlueSpacing20 />
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
<BlueListItemHooks
|
<BlueListItem
|
||||||
Component={TouchableWithoutFeedback}
|
Component={TouchableWithoutFeedback}
|
||||||
title={loc.settings.general_adv_mode}
|
title={loc.settings.general_adv_mode}
|
||||||
switch={{ onValueChange: onAdvancedModeSwitch, value: isAdancedModeEnabled }}
|
switch={{ onValueChange: onAdvancedModeSwitch, value: isAdancedModeEnabled }}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ScrollView, StyleSheet } from 'react-native';
|
import { ScrollView, StyleSheet } from 'react-native';
|
||||||
import { SafeBlueArea, BlueListItemHooks, BlueNavigationStyle } from '../../BlueComponents';
|
import { SafeBlueArea, BlueListItem, BlueNavigationStyle } from '../../BlueComponents';
|
||||||
import { useNavigation, useTheme } from '@react-navigation/native';
|
import { useNavigation, useTheme } from '@react-navigation/native';
|
||||||
import loc from '../../loc';
|
import loc from '../../loc';
|
||||||
|
|
||||||
|
@ -29,9 +29,9 @@ const NetworkSettings = () => {
|
||||||
return (
|
return (
|
||||||
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={styles.root}>
|
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={styles.root}>
|
||||||
<ScrollView>
|
<ScrollView>
|
||||||
<BlueListItemHooks title={loc.settings.network_electrum} onPress={navigateToElectrumSettings} chevron />
|
<BlueListItem title={loc.settings.network_electrum} onPress={navigateToElectrumSettings} chevron />
|
||||||
<BlueListItemHooks title={loc.settings.lightning_settings} onPress={navigateToLightningSettings} chevron />
|
<BlueListItem title={loc.settings.lightning_settings} onPress={navigateToLightningSettings} chevron />
|
||||||
<BlueListItemHooks title={loc.settings.network_broadcast} onPress={navigateToBroadcast} chevron />
|
<BlueListItem title={loc.settings.network_broadcast} onPress={navigateToBroadcast} chevron />
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</SafeBlueArea>
|
</SafeBlueArea>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { useEffect, useState } from 'react';
|
import React from 'react';
|
||||||
import { ScrollView, Linking, Dimensions, Image, View, Text, StyleSheet } from 'react-native';
|
import { ScrollView, Linking, Image, View, Text, StyleSheet, useWindowDimensions } from 'react-native';
|
||||||
import { useNavigation, useTheme } from '@react-navigation/native';
|
import { useNavigation, useTheme } from '@react-navigation/native';
|
||||||
import {
|
import {
|
||||||
BlueTextCentered,
|
BlueTextCentered,
|
||||||
|
@ -7,20 +7,17 @@ import {
|
||||||
BlueButton,
|
BlueButton,
|
||||||
SafeBlueArea,
|
SafeBlueArea,
|
||||||
BlueCard,
|
BlueCard,
|
||||||
BlueListItemHooks,
|
BlueListItem,
|
||||||
BlueNavigationStyle,
|
BlueNavigationStyle,
|
||||||
BlueLoadingHook,
|
|
||||||
} from '../../BlueComponents';
|
} from '../../BlueComponents';
|
||||||
import { getApplicationName, getVersion, getBundleId, getBuildNumber } from 'react-native-device-info';
|
import { getApplicationName, getVersion, getBundleId, getBuildNumber } from 'react-native-device-info';
|
||||||
import Rate, { AndroidMarket } from 'react-native-rate';
|
import Rate, { AndroidMarket } from 'react-native-rate';
|
||||||
import loc from '../../loc';
|
import loc from '../../loc';
|
||||||
|
|
||||||
const { width, height } = Dimensions.get('window');
|
|
||||||
|
|
||||||
const About = () => {
|
const About = () => {
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
|
||||||
const { navigate } = useNavigation();
|
const { navigate } = useNavigation();
|
||||||
const { colors } = useTheme();
|
const { colors } = useTheme();
|
||||||
|
const { width, height } = useWindowDimensions();
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
root: {
|
root: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
|
@ -58,10 +55,6 @@ const About = () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setIsLoading(false);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleOnReleaseNotesPress = () => {
|
const handleOnReleaseNotesPress = () => {
|
||||||
navigate('ReleaseNotes');
|
navigate('ReleaseNotes');
|
||||||
};
|
};
|
||||||
|
@ -102,9 +95,7 @@ const About = () => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return isLoading ? (
|
return (
|
||||||
<BlueLoadingHook />
|
|
||||||
) : (
|
|
||||||
<SafeBlueArea style={styles.root}>
|
<SafeBlueArea style={styles.root}>
|
||||||
<ScrollView testID="AboutScrollView">
|
<ScrollView testID="AboutScrollView">
|
||||||
<BlueCard>
|
<BlueCard>
|
||||||
|
@ -115,7 +106,7 @@ const About = () => {
|
||||||
<BlueButton onPress={handleOnRatePress} title={loc.settings.about_review + ' ⭐🙏'} />
|
<BlueButton onPress={handleOnRatePress} title={loc.settings.about_review + ' ⭐🙏'} />
|
||||||
</View>
|
</View>
|
||||||
</BlueCard>
|
</BlueCard>
|
||||||
<BlueListItemHooks
|
<BlueListItem
|
||||||
leftIcon={{
|
leftIcon={{
|
||||||
name: 'twitter',
|
name: 'twitter',
|
||||||
type: 'font-awesome',
|
type: 'font-awesome',
|
||||||
|
@ -124,7 +115,7 @@ const About = () => {
|
||||||
onPress={handleOnTwitterPress}
|
onPress={handleOnTwitterPress}
|
||||||
title={loc.settings.about_sm_twitter}
|
title={loc.settings.about_sm_twitter}
|
||||||
/>
|
/>
|
||||||
<BlueListItemHooks
|
<BlueListItem
|
||||||
leftIcon={{
|
leftIcon={{
|
||||||
name: 'telegram',
|
name: 'telegram',
|
||||||
type: 'font-awesome',
|
type: 'font-awesome',
|
||||||
|
@ -133,7 +124,7 @@ const About = () => {
|
||||||
onPress={handleOnTelegramPress}
|
onPress={handleOnTelegramPress}
|
||||||
title={loc.settings.about_sm_telegram}
|
title={loc.settings.about_sm_telegram}
|
||||||
/>
|
/>
|
||||||
<BlueListItemHooks
|
<BlueListItem
|
||||||
leftIcon={{
|
leftIcon={{
|
||||||
name: 'github',
|
name: 'github',
|
||||||
type: 'font-awesome',
|
type: 'font-awesome',
|
||||||
|
@ -154,7 +145,7 @@ const About = () => {
|
||||||
<BlueTextCentered>Electrum server</BlueTextCentered>
|
<BlueTextCentered>Electrum server</BlueTextCentered>
|
||||||
</View>
|
</View>
|
||||||
</BlueCard>
|
</BlueCard>
|
||||||
<BlueListItemHooks
|
<BlueListItem
|
||||||
leftIcon={{
|
leftIcon={{
|
||||||
name: 'book',
|
name: 'book',
|
||||||
type: 'font-awesome',
|
type: 'font-awesome',
|
||||||
|
@ -164,7 +155,7 @@ const About = () => {
|
||||||
onPress={handleOnReleaseNotesPress}
|
onPress={handleOnReleaseNotesPress}
|
||||||
title={loc.settings.about_release_notes}
|
title={loc.settings.about_release_notes}
|
||||||
/>
|
/>
|
||||||
<BlueListItemHooks
|
<BlueListItem
|
||||||
leftIcon={{
|
leftIcon={{
|
||||||
name: 'law',
|
name: 'law',
|
||||||
type: 'octicon',
|
type: 'octicon',
|
||||||
|
@ -174,7 +165,7 @@ const About = () => {
|
||||||
onPress={handleOnLicensingPress}
|
onPress={handleOnLicensingPress}
|
||||||
title="MIT License"
|
title="MIT License"
|
||||||
/>
|
/>
|
||||||
<BlueListItemHooks
|
<BlueListItem
|
||||||
leftIcon={{
|
leftIcon={{
|
||||||
name: 'flask',
|
name: 'flask',
|
||||||
type: 'font-awesome',
|
type: 'font-awesome',
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { FlatList, TouchableOpacity, ActivityIndicator, View, StyleSheet } from 'react-native';
|
import { FlatList, TouchableOpacity, ActivityIndicator, View, StyleSheet } from 'react-native';
|
||||||
import { SafeBlueArea, BlueListItemHooks, BlueTextHooks, BlueCard, BlueNavigationStyle } from '../../BlueComponents';
|
import { SafeBlueArea, BlueListItem, BlueTextHooks, BlueCard, BlueNavigationStyle } from '../../BlueComponents';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Icon } from 'react-native-elements';
|
|
||||||
import { FiatUnit } from '../../models/fiatUnit';
|
import { FiatUnit } from '../../models/fiatUnit';
|
||||||
import loc from '../../loc';
|
import loc from '../../loc';
|
||||||
import { useTheme } from '@react-navigation/native';
|
import { useTheme } from '@react-navigation/native';
|
||||||
|
@ -52,11 +51,11 @@ const Currency = () => {
|
||||||
extraData={data}
|
extraData={data}
|
||||||
renderItem={({ item }) => {
|
renderItem={({ item }) => {
|
||||||
return (
|
return (
|
||||||
<BlueListItemHooks
|
<BlueListItem
|
||||||
disabled={isSavingNewPreferredCurrency}
|
disabled={isSavingNewPreferredCurrency}
|
||||||
title={`${item.endPointKey} (${item.symbol})`}
|
title={`${item.endPointKey} (${item.symbol})`}
|
||||||
{...(selectedCurrency.endPointKey === item.endPointKey
|
{...(selectedCurrency.endPointKey === item.endPointKey
|
||||||
? { rightIcon: <Icon name="check" type="octaicon" color="#0070FF" /> }
|
? { rightIcon: { name: 'check', type: 'octaicon', color: '#0070FF' } }
|
||||||
: { hideChevron: true })}
|
: { hideChevron: true })}
|
||||||
Component={TouchableOpacity}
|
Component={TouchableOpacity}
|
||||||
onPress={async () => {
|
onPress={async () => {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { View, TouchableWithoutFeedback, StyleSheet } from 'react-native';
|
import { View, TouchableWithoutFeedback, StyleSheet } from 'react-native';
|
||||||
import { useNavigation } from '@react-navigation/native';
|
import { useNavigation } from '@react-navigation/native';
|
||||||
import { SafeBlueArea, BlueCard, BlueNavigationStyle, BlueListItemHooks, BlueTextHooks } from '../../BlueComponents';
|
import { SafeBlueArea, BlueCard, BlueNavigationStyle, BlueListItem, BlueTextHooks } from '../../BlueComponents';
|
||||||
import OnAppLaunch from '../../class/on-app-launch';
|
import OnAppLaunch from '../../class/on-app-launch';
|
||||||
import loc from '../../loc';
|
import loc from '../../loc';
|
||||||
const BlueApp = require('../../BlueApp');
|
const BlueApp = require('../../BlueApp');
|
||||||
|
@ -57,7 +57,7 @@ const DefaultView = () => {
|
||||||
return (
|
return (
|
||||||
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={styles.flex}>
|
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={styles.flex}>
|
||||||
<View>
|
<View>
|
||||||
<BlueListItemHooks
|
<BlueListItem
|
||||||
title={loc.settings.default_wallets}
|
title={loc.settings.default_wallets}
|
||||||
Component={TouchableWithoutFeedback}
|
Component={TouchableWithoutFeedback}
|
||||||
switch={{
|
switch={{
|
||||||
|
@ -70,7 +70,7 @@ const DefaultView = () => {
|
||||||
<BlueTextHooks>{loc.settings.default_desc}</BlueTextHooks>
|
<BlueTextHooks>{loc.settings.default_desc}</BlueTextHooks>
|
||||||
</BlueCard>
|
</BlueCard>
|
||||||
{!viewAllWalletsEnabled && (
|
{!viewAllWalletsEnabled && (
|
||||||
<BlueListItemHooks title={loc.settings.default_info} onPress={selectWallet} rightTitle={defaultWalletLabel} chevron />
|
<BlueListItem title={loc.settings.default_info} onPress={selectWallet} rightTitle={defaultWalletLabel} chevron />
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
</SafeBlueArea>
|
</SafeBlueArea>
|
||||||
|
|
|
@ -8,7 +8,7 @@ import {
|
||||||
SafeBlueArea,
|
SafeBlueArea,
|
||||||
BlueSpacing20,
|
BlueSpacing20,
|
||||||
BlueCard,
|
BlueCard,
|
||||||
BlueListItemHooks,
|
BlueListItem,
|
||||||
BlueHeaderDefaultSubHooks,
|
BlueHeaderDefaultSubHooks,
|
||||||
BlueTextHooks,
|
BlueTextHooks,
|
||||||
BlueNavigationStyle,
|
BlueNavigationStyle,
|
||||||
|
@ -150,7 +150,7 @@ const EncryptStorage = () => {
|
||||||
{biometrics.isDeviceBiometricCapable && (
|
{biometrics.isDeviceBiometricCapable && (
|
||||||
<>
|
<>
|
||||||
<BlueHeaderDefaultSubHooks leftText="biometrics" rightComponent={null} />
|
<BlueHeaderDefaultSubHooks leftText="biometrics" rightComponent={null} />
|
||||||
<BlueListItemHooks
|
<BlueListItem
|
||||||
title={loc.formatString(loc.settings.encrypt_use, { type: biometrics.biometricsType })}
|
title={loc.formatString(loc.settings.encrypt_use, { type: biometrics.biometricsType })}
|
||||||
Component={TouchableWithoutFeedback}
|
Component={TouchableWithoutFeedback}
|
||||||
switch={{ value: biometrics.isBiometricsEnabled, onValueChange: onUseBiometricSwitch }}
|
switch={{ value: biometrics.isBiometricsEnabled, onValueChange: onUseBiometricSwitch }}
|
||||||
|
@ -162,7 +162,7 @@ const EncryptStorage = () => {
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<BlueHeaderDefaultSubHooks leftText={loc.settings.encrypt_tstorage} rightComponent={null} />
|
<BlueHeaderDefaultSubHooks leftText={loc.settings.encrypt_tstorage} rightComponent={null} />
|
||||||
<BlueListItemHooks
|
<BlueListItem
|
||||||
testID="EncyptedAndPasswordProtected"
|
testID="EncyptedAndPasswordProtected"
|
||||||
hideChevron
|
hideChevron
|
||||||
title={loc.settings.encrypt_enc_and_pass}
|
title={loc.settings.encrypt_enc_and_pass}
|
||||||
|
@ -170,7 +170,7 @@ const EncryptStorage = () => {
|
||||||
switch={{ onValueChange: onEncryptStorageSwitch, value: storageIsEncrypted }}
|
switch={{ onValueChange: onEncryptStorageSwitch, value: storageIsEncrypted }}
|
||||||
/>
|
/>
|
||||||
{Platform.OS === 'ios' && (
|
{Platform.OS === 'ios' && (
|
||||||
<BlueListItemHooks
|
<BlueListItem
|
||||||
hideChevron
|
hideChevron
|
||||||
title={loc.settings.encrypt_del_uninstall}
|
title={loc.settings.encrypt_del_uninstall}
|
||||||
Component={TouchableWithoutFeedback}
|
Component={TouchableWithoutFeedback}
|
||||||
|
@ -181,7 +181,7 @@ const EncryptStorage = () => {
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{storageIsEncrypted && (
|
{storageIsEncrypted && (
|
||||||
<BlueListItemHooks
|
<BlueListItem
|
||||||
onPress={navigateToPlausibleDeniability}
|
onPress={navigateToPlausibleDeniability}
|
||||||
title={loc.settings.plausible_deniability}
|
title={loc.settings.plausible_deniability}
|
||||||
chevron
|
chevron
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import React, { useState, useEffect, useCallback } from 'react';
|
import React, { useState, useEffect, useCallback } from 'react';
|
||||||
import { FlatList, StyleSheet } from 'react-native';
|
import { FlatList, StyleSheet } from 'react-native';
|
||||||
import { SafeBlueArea, BlueListItemHooks, BlueCard, BlueLoadingHook, BlueNavigationStyle, BlueTextHooks } from '../../BlueComponents';
|
import { SafeBlueArea, BlueListItem, BlueCard, BlueLoadingHook, BlueNavigationStyle, BlueTextHooks } from '../../BlueComponents';
|
||||||
import { Icon } from 'react-native-elements';
|
|
||||||
import { AvailableLanguages } from '../../loc/languages';
|
import { AvailableLanguages } from '../../loc/languages';
|
||||||
import loc from '../../loc';
|
import loc from '../../loc';
|
||||||
|
|
||||||
|
@ -22,7 +21,7 @@ const Language = () => {
|
||||||
const renderItem = useCallback(
|
const renderItem = useCallback(
|
||||||
({ item }) => {
|
({ item }) => {
|
||||||
return (
|
return (
|
||||||
<BlueListItemHooks
|
<BlueListItem
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
console.log('setLanguage', item.value);
|
console.log('setLanguage', item.value);
|
||||||
loc.saveLanguage(item.value);
|
loc.saveLanguage(item.value);
|
||||||
|
@ -31,7 +30,7 @@ const Language = () => {
|
||||||
title={item.label}
|
title={item.label}
|
||||||
{...(language === item.value
|
{...(language === item.value
|
||||||
? {
|
? {
|
||||||
rightIcon: <Icon name="check" type="octaicon" color="#0070FF" />,
|
rightIcon: { name: 'check', type: 'octaicon', color: '#0070FF' },
|
||||||
}
|
}
|
||||||
: { hideChevron: true })}
|
: { hideChevron: true })}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -5,7 +5,7 @@ import {
|
||||||
BlueLoading,
|
BlueLoading,
|
||||||
BlueTextHooks,
|
BlueTextHooks,
|
||||||
BlueSpacing20,
|
BlueSpacing20,
|
||||||
BlueListItemHooks,
|
BlueListItem,
|
||||||
BlueNavigationStyle,
|
BlueNavigationStyle,
|
||||||
BlueCard,
|
BlueCard,
|
||||||
BlueButton,
|
BlueButton,
|
||||||
|
@ -101,7 +101,7 @@ const NotificationSettings = () => {
|
||||||
<BlueLoading />
|
<BlueLoading />
|
||||||
) : (
|
) : (
|
||||||
<ScrollView style={stylesWithThemeHook.scroll}>
|
<ScrollView style={stylesWithThemeHook.scroll}>
|
||||||
<BlueListItemHooks
|
<BlueListItem
|
||||||
Component={TouchableWithoutFeedback}
|
Component={TouchableWithoutFeedback}
|
||||||
title={loc.settings.push_notifications}
|
title={loc.settings.push_notifications}
|
||||||
switch={{ onValueChange: onNotificationsSwitch, value: isNotificationsEnabled }}
|
switch={{ onValueChange: onNotificationsSwitch, value: isNotificationsEnabled }}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ScrollView, TouchableOpacity, StyleSheet, StatusBar } from 'react-native';
|
import { ScrollView, TouchableOpacity, StyleSheet, StatusBar } from 'react-native';
|
||||||
import { BlueListItemHooks, BlueHeaderDefaultSubHooks } from '../../BlueComponents';
|
import { BlueListItem, BlueHeaderDefaultSubHooks } from '../../BlueComponents';
|
||||||
import { useNavigation } from '@react-navigation/native';
|
import { useNavigation } from '@react-navigation/native';
|
||||||
import loc from '../../loc';
|
import loc from '../../loc';
|
||||||
|
|
||||||
|
@ -17,24 +17,24 @@ const Settings = () => {
|
||||||
<ScrollView style={styles.root}>
|
<ScrollView style={styles.root}>
|
||||||
<StatusBar barStyle="default" />
|
<StatusBar barStyle="default" />
|
||||||
<BlueHeaderDefaultSubHooks leftText={loc.settings.header} rightComponent={null} />
|
<BlueHeaderDefaultSubHooks leftText={loc.settings.header} rightComponent={null} />
|
||||||
<BlueListItemHooks title={loc.settings.general} component={TouchableOpacity} onPress={() => navigate('GeneralSettings')} chevron />
|
<BlueListItem title={loc.settings.general} component={TouchableOpacity} onPress={() => navigate('GeneralSettings')} chevron />
|
||||||
<BlueListItemHooks title={loc.settings.currency} component={TouchableOpacity} onPress={() => navigate('Currency')} chevron />
|
<BlueListItem title={loc.settings.currency} component={TouchableOpacity} onPress={() => navigate('Currency')} chevron />
|
||||||
<BlueListItemHooks title={loc.settings.language} component={TouchableOpacity} onPress={() => navigate('Language')} chevron />
|
<BlueListItem title={loc.settings.language} component={TouchableOpacity} onPress={() => navigate('Language')} chevron />
|
||||||
<BlueListItemHooks
|
<BlueListItem
|
||||||
title={loc.settings.encrypt_title}
|
title={loc.settings.encrypt_title}
|
||||||
onPress={() => navigate('EncryptStorage')}
|
onPress={() => navigate('EncryptStorage')}
|
||||||
component={TouchableOpacity}
|
component={TouchableOpacity}
|
||||||
testID="SecurityButton"
|
testID="SecurityButton"
|
||||||
chevron
|
chevron
|
||||||
/>
|
/>
|
||||||
<BlueListItemHooks title={loc.settings.network} component={TouchableOpacity} onPress={() => navigate('NetworkSettings')} chevron />
|
<BlueListItem title={loc.settings.network} component={TouchableOpacity} onPress={() => navigate('NetworkSettings')} chevron />
|
||||||
<BlueListItemHooks
|
<BlueListItem
|
||||||
title={loc.settings.notifications}
|
title={loc.settings.notifications}
|
||||||
component={TouchableOpacity}
|
component={TouchableOpacity}
|
||||||
onPress={() => navigate('NotificationSettings')}
|
onPress={() => navigate('NotificationSettings')}
|
||||||
chevron
|
chevron
|
||||||
/>
|
/>
|
||||||
<BlueListItemHooks
|
<BlueListItem
|
||||||
title={loc.settings.about}
|
title={loc.settings.about}
|
||||||
component={TouchableOpacity}
|
component={TouchableOpacity}
|
||||||
onPress={() => navigate('About')}
|
onPress={() => navigate('About')}
|
||||||
|
|
|
@ -215,7 +215,12 @@ export default class TransactionsStatus extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
const tx = new HDSegwitBech32Transaction(null, this.state.tx.hash, this.state.wallet);
|
const tx = new HDSegwitBech32Transaction(null, this.state.tx.hash, this.state.wallet);
|
||||||
if ((await tx.isOurTransaction()) && (await tx.getRemoteConfirmationsNum()) === 0 && (await tx.isSequenceReplaceable())) {
|
if (
|
||||||
|
(await tx.isOurTransaction()) &&
|
||||||
|
(await tx.getRemoteConfirmationsNum()) === 0 &&
|
||||||
|
(await tx.isSequenceReplaceable()) &&
|
||||||
|
(await tx.canBumpTx())
|
||||||
|
) {
|
||||||
return this.setState({ isRBFBumpFeePossible: buttonStatus.possible });
|
return this.setState({ isRBFBumpFeePossible: buttonStatus.possible });
|
||||||
} else {
|
} else {
|
||||||
return this.setState({ isRBFBumpFeePossible: buttonStatus.notPossible });
|
return this.setState({ isRBFBumpFeePossible: buttonStatus.notPossible });
|
||||||
|
|
|
@ -16,7 +16,7 @@ import AsyncStorage from '@react-native-community/async-storage';
|
||||||
import {
|
import {
|
||||||
BlueTextCenteredHooks,
|
BlueTextCenteredHooks,
|
||||||
BlueTextHooks,
|
BlueTextHooks,
|
||||||
BlueListItemHooks,
|
BlueListItem,
|
||||||
LightningButton,
|
LightningButton,
|
||||||
BitcoinButton,
|
BitcoinButton,
|
||||||
BlueFormLabel,
|
BlueFormLabel,
|
||||||
|
@ -27,7 +27,6 @@ import {
|
||||||
} from '../../BlueComponents';
|
} from '../../BlueComponents';
|
||||||
import { HDSegwitBech32Wallet, SegwitP2SHWallet, HDSegwitP2SHWallet, LightningCustodianWallet, AppStorage } from '../../class';
|
import { HDSegwitBech32Wallet, SegwitP2SHWallet, HDSegwitP2SHWallet, LightningCustodianWallet, AppStorage } from '../../class';
|
||||||
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||||
import { Icon } from 'react-native-elements';
|
|
||||||
import { useTheme, useNavigation } from '@react-navigation/native';
|
import { useTheme, useNavigation } from '@react-navigation/native';
|
||||||
import { Chain } from '../../models/bitcoinUnits';
|
import { Chain } from '../../models/bitcoinUnits';
|
||||||
import loc from '../../loc';
|
import loc from '../../loc';
|
||||||
|
@ -93,7 +92,6 @@ const styles = StyleSheet.create({
|
||||||
borderRadius: 4,
|
borderRadius: 4,
|
||||||
},
|
},
|
||||||
createButton: {
|
createButton: {
|
||||||
alignItems: 'center',
|
|
||||||
flex: 1,
|
flex: 1,
|
||||||
marginTop: 32,
|
marginTop: 32,
|
||||||
},
|
},
|
||||||
|
@ -312,36 +310,36 @@ const WalletsAdd = () => {
|
||||||
<View>
|
<View>
|
||||||
<BlueSpacing20 />
|
<BlueSpacing20 />
|
||||||
<Text style={[styles.advancedText, stylesHook.advancedText]}>{loc.settings.advanced_options}</Text>
|
<Text style={[styles.advancedText, stylesHook.advancedText]}>{loc.settings.advanced_options}</Text>
|
||||||
<BlueListItemHooks
|
<BlueListItem
|
||||||
containerStyle={[styles.noPadding, stylesHook.noPadding]}
|
containerStyle={[styles.noPadding, stylesHook.noPadding]}
|
||||||
bottomDivider={false}
|
bottomDivider={false}
|
||||||
onPress={() => setSelectedIndex(0)}
|
onPress={() => setSelectedIndex(0)}
|
||||||
title={HDSegwitBech32Wallet.typeReadable}
|
title={HDSegwitBech32Wallet.typeReadable}
|
||||||
{...(selectedIndex === 0
|
{...(selectedIndex === 0
|
||||||
? {
|
? {
|
||||||
rightIcon: <Icon name="check" type="octaicon" color="#0070FF" />,
|
rightIcon: { name: 'check', type: 'octaicon', color: '#0070FF' },
|
||||||
}
|
}
|
||||||
: { hideChevron: true })}
|
: { hideChevron: true })}
|
||||||
/>
|
/>
|
||||||
<BlueListItemHooks
|
<BlueListItem
|
||||||
containerStyle={[styles.noPadding, stylesHook.noPadding]}
|
containerStyle={[styles.noPadding, stylesHook.noPadding]}
|
||||||
bottomDivider={false}
|
bottomDivider={false}
|
||||||
onPress={() => setSelectedIndex(1)}
|
onPress={() => setSelectedIndex(1)}
|
||||||
title={SegwitP2SHWallet.typeReadable}
|
title={SegwitP2SHWallet.typeReadable}
|
||||||
{...(selectedIndex === 1
|
{...(selectedIndex === 1
|
||||||
? {
|
? {
|
||||||
rightIcon: <Icon name="check" type="octaicon" color="#0070FF" />,
|
rightIcon: { name: 'check', type: 'octaicon', color: '#0070FF' },
|
||||||
}
|
}
|
||||||
: { hideChevron: true })}
|
: { hideChevron: true })}
|
||||||
/>
|
/>
|
||||||
<BlueListItemHooks
|
<BlueListItem
|
||||||
containerStyle={[styles.noPadding, stylesHook.noPadding]}
|
containerStyle={[styles.noPadding, stylesHook.noPadding]}
|
||||||
bottomDivider={false}
|
bottomDivider={false}
|
||||||
onPress={() => setSelectedIndex(2)}
|
onPress={() => setSelectedIndex(2)}
|
||||||
title={HDSegwitP2SHWallet.typeReadable}
|
title={HDSegwitP2SHWallet.typeReadable}
|
||||||
{...(selectedIndex === 2
|
{...(selectedIndex === 2
|
||||||
? {
|
? {
|
||||||
rightIcon: <Icon name="check" type="octaicon" color="#0070FF" />,
|
rightIcon: { name: 'check', type: 'octaicon', color: '#0070FF' },
|
||||||
}
|
}
|
||||||
: { hideChevron: true })}
|
: { hideChevron: true })}
|
||||||
/>
|
/>
|
||||||
|
@ -351,7 +349,7 @@ const WalletsAdd = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<BlueSpacing20 />
|
<BlueSpacing20 />
|
||||||
<Text style={styles.advancedText}>{loc.settings.advanced_options}</Text>
|
<Text style={[styles.advancedText, stylesHook.advancedText]}>{loc.settings.advanced_options}</Text>
|
||||||
<BlueSpacing20 />
|
<BlueSpacing20 />
|
||||||
<BlueTextHooks>Connect to your LNDHub</BlueTextHooks>
|
<BlueTextHooks>Connect to your LNDHub</BlueTextHooks>
|
||||||
<View style={[styles.lndUri, stylesHook.lndUri]}>
|
<View style={[styles.lndUri, stylesHook.lndUri]}>
|
||||||
|
|
|
@ -14,6 +14,7 @@ import {
|
||||||
Linking,
|
Linking,
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
StatusBar,
|
StatusBar,
|
||||||
|
PermissionsAndroid,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { SecondButton, SafeBlueArea, BlueCard, BlueSpacing20, BlueNavigationStyle, BlueText, BlueLoadingHook } from '../../BlueComponents';
|
import { SecondButton, SafeBlueArea, BlueCard, BlueSpacing20, BlueNavigationStyle, BlueText, BlueLoadingHook } from '../../BlueComponents';
|
||||||
import { LightningCustodianWallet } from '../../class/wallets/lightning-custodian-wallet';
|
import { LightningCustodianWallet } from '../../class/wallets/lightning-custodian-wallet';
|
||||||
|
@ -22,14 +23,18 @@ import { HDLegacyP2PKHWallet } from '../../class/wallets/hd-legacy-p2pkh-wallet'
|
||||||
import { HDSegwitP2SHWallet } from '../../class/wallets/hd-segwit-p2sh-wallet';
|
import { HDSegwitP2SHWallet } from '../../class/wallets/hd-segwit-p2sh-wallet';
|
||||||
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||||
import Biometric from '../../class/biometrics';
|
import Biometric from '../../class/biometrics';
|
||||||
import { HDSegwitBech32Wallet, SegwitP2SHWallet, LegacyWallet, SegwitBech32Wallet, WatchOnlyWallet } from '../../class';
|
import { HDSegwitBech32Wallet, SegwitP2SHWallet, LegacyWallet, SegwitBech32Wallet, WatchOnlyWallet, MultisigHDWallet } from '../../class';
|
||||||
import { ScrollView } from 'react-native-gesture-handler';
|
import { ScrollView } from 'react-native-gesture-handler';
|
||||||
import loc from '../../loc';
|
import loc from '../../loc';
|
||||||
import { useTheme, useRoute, useNavigation } from '@react-navigation/native';
|
import { useTheme, useRoute, useNavigation } from '@react-navigation/native';
|
||||||
|
import RNFS from 'react-native-fs';
|
||||||
|
import Share from 'react-native-share';
|
||||||
|
import { getSystemName } from 'react-native-device-info';
|
||||||
const EV = require('../../blue_modules/events');
|
const EV = require('../../blue_modules/events');
|
||||||
const prompt = require('../../blue_modules/prompt');
|
const prompt = require('../../blue_modules/prompt');
|
||||||
const BlueApp = require('../../BlueApp');
|
const BlueApp = require('../../BlueApp');
|
||||||
const notifications = require('../../blue_modules/notifications');
|
const notifications = require('../../blue_modules/notifications');
|
||||||
|
const isDesktop = getSystemName() === 'Mac OS X';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
root: {
|
root: {
|
||||||
|
@ -94,6 +99,7 @@ const styles = StyleSheet.create({
|
||||||
const WalletDetails = () => {
|
const WalletDetails = () => {
|
||||||
const { wallet } = useRoute().params;
|
const { wallet } = useRoute().params;
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
const [backdoorPressed, setBackdoorPressed] = useState(0);
|
||||||
const [walletName, setWalletName] = useState(wallet.getLabel());
|
const [walletName, setWalletName] = useState(wallet.getLabel());
|
||||||
const [useWithHardwareWallet, setUseWithHardwareWallet] = useState(wallet.useWithHardwareWalletEnabled());
|
const [useWithHardwareWallet, setUseWithHardwareWallet] = useState(wallet.useWithHardwareWalletEnabled());
|
||||||
const [hideTransactionsInWalletsList, setHideTransactionsInWalletsList] = useState(!wallet.getHideTransactionsInWalletsList());
|
const [hideTransactionsInWalletsList, setHideTransactionsInWalletsList] = useState(!wallet.getHideTransactionsInWalletsList());
|
||||||
|
@ -148,6 +154,7 @@ const WalletDetails = () => {
|
||||||
|
|
||||||
const presentWalletHasBalanceAlert = useCallback(async () => {
|
const presentWalletHasBalanceAlert = useCallback(async () => {
|
||||||
ReactNativeHapticFeedback.trigger('notificationWarning', { ignoreAndroidSystemSettings: false });
|
ReactNativeHapticFeedback.trigger('notificationWarning', { ignoreAndroidSystemSettings: false });
|
||||||
|
try {
|
||||||
const walletBalanceConfirmation = await prompt(
|
const walletBalanceConfirmation = await prompt(
|
||||||
loc.wallets.details_del_wb,
|
loc.wallets.details_del_wb,
|
||||||
loc.formatString(loc.wallets.details_del_wb_q, { balance: wallet.getBalance() }),
|
loc.formatString(loc.wallets.details_del_wb_q, { balance: wallet.getBalance() }),
|
||||||
|
@ -169,6 +176,7 @@ const WalletDetails = () => {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
alert(loc.wallets.details_del_wb_err);
|
alert(loc.wallets.details_del_wb_err);
|
||||||
}
|
}
|
||||||
|
} catch (_) {}
|
||||||
}, [popToTop, setParams, wallet]);
|
}, [popToTop, setParams, wallet]);
|
||||||
|
|
||||||
const navigateToWalletExport = () => {
|
const navigateToWalletExport = () => {
|
||||||
|
@ -176,6 +184,11 @@ const WalletDetails = () => {
|
||||||
wallet,
|
wallet,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
const navigateToMultisigCoordinationSetup = () => {
|
||||||
|
navigate('ExportMultisigCoordinationSetup', {
|
||||||
|
walletId: wallet.getID(),
|
||||||
|
});
|
||||||
|
};
|
||||||
const navigateToXPub = () =>
|
const navigateToXPub = () =>
|
||||||
navigate('WalletXpub', {
|
navigate('WalletXpub', {
|
||||||
secret: wallet.getSecret(),
|
secret: wallet.getSecret(),
|
||||||
|
@ -204,6 +217,65 @@ const WalletDetails = () => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const exportInternals = async () => {
|
||||||
|
if (backdoorPressed < 10) return setBackdoorPressed(backdoorPressed + 1);
|
||||||
|
setBackdoorPressed(0);
|
||||||
|
if (wallet.type !== HDSegwitBech32Wallet.type) return;
|
||||||
|
const fileName = 'wallet-externals.json';
|
||||||
|
const contents = JSON.stringify(
|
||||||
|
{
|
||||||
|
_balances_by_external_index: wallet._balances_by_external_index,
|
||||||
|
_balances_by_internal_index: wallet._balances_by_internal_index,
|
||||||
|
_txs_by_external_index: wallet._txs_by_external_index,
|
||||||
|
_txs_by_internal_index: wallet._txs_by_internal_index,
|
||||||
|
_utxo: wallet._utxo,
|
||||||
|
next_free_address_index: wallet.next_free_address_index,
|
||||||
|
next_free_change_address_index: wallet.next_free_change_address_index,
|
||||||
|
internal_addresses_cache: wallet.internal_addresses_cache,
|
||||||
|
external_addresses_cache: wallet.external_addresses_cache,
|
||||||
|
_xpub: wallet._xpub,
|
||||||
|
gap_limit: wallet.gap_limit,
|
||||||
|
label: wallet.label,
|
||||||
|
_lastTxFetch: wallet._lastTxFetch,
|
||||||
|
_lastBalanceFetch: wallet._lastBalanceFetch,
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2,
|
||||||
|
);
|
||||||
|
if (Platform.OS === 'ios') {
|
||||||
|
const filePath = RNFS.TemporaryDirectoryPath + `/${fileName}`;
|
||||||
|
await RNFS.writeFile(filePath, contents);
|
||||||
|
Share.open({
|
||||||
|
url: 'file://' + filePath,
|
||||||
|
saveToFiles: isDesktop,
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.log(error);
|
||||||
|
alert(error.message);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
RNFS.unlink(filePath);
|
||||||
|
});
|
||||||
|
} else if (Platform.OS === 'android') {
|
||||||
|
const granted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE, {
|
||||||
|
title: loc.send.permission_storage_title,
|
||||||
|
message: loc.send.permission_storage_message,
|
||||||
|
buttonNeutral: loc.send.permission_storage_later,
|
||||||
|
buttonNegative: loc._.cancel,
|
||||||
|
buttonPositive: loc._.ok,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
|
||||||
|
console.log('Storage Permission: Granted');
|
||||||
|
const filePath = RNFS.DownloadDirectoryPath + `/${fileName}`;
|
||||||
|
await RNFS.writeFile(filePath, contents);
|
||||||
|
alert(loc.formatString(loc.send.txSaved, { filePath: fileName }));
|
||||||
|
} else {
|
||||||
|
console.log('Storage Permission: Denied');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const navigateToBroadcast = () => {
|
const navigateToBroadcast = () => {
|
||||||
navigate('Broadcast');
|
navigate('Broadcast');
|
||||||
};
|
};
|
||||||
|
@ -295,6 +367,35 @@ const WalletDetails = () => {
|
||||||
<BlueSpacing20 />
|
<BlueSpacing20 />
|
||||||
<Text style={[styles.textLabel1, stylesHook.textLabel1]}>{loc.wallets.details_type.toLowerCase()}</Text>
|
<Text style={[styles.textLabel1, stylesHook.textLabel1]}>{loc.wallets.details_type.toLowerCase()}</Text>
|
||||||
<Text style={[styles.textValue, stylesHook.textValue]}>{wallet.typeReadable}</Text>
|
<Text style={[styles.textValue, stylesHook.textValue]}>{wallet.typeReadable}</Text>
|
||||||
|
|
||||||
|
{wallet.type === MultisigHDWallet.type && (
|
||||||
|
<>
|
||||||
|
<Text style={[styles.textLabel2, stylesHook.textLabel2]}>multisig</Text>
|
||||||
|
<BlueText>
|
||||||
|
{wallet.getM()} of {wallet.getN()}{' '}
|
||||||
|
{wallet.isNativeSegwit()
|
||||||
|
? 'native segwit (p2wsh)'
|
||||||
|
: wallet.isWrappedSegwit()
|
||||||
|
? 'wrapped segwit (p2sh-p2wsh)'
|
||||||
|
: 'legacy (p2sh)'}
|
||||||
|
</BlueText>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{wallet.type === MultisigHDWallet.type && wallet.howManySignaturesCanWeMake() > 0 && (
|
||||||
|
<>
|
||||||
|
<Text style={[styles.textLabel2, stylesHook.textLabel2]}>{loc.multisig.how_many_signatures_can_bluewallet_make}</Text>
|
||||||
|
<BlueText>{wallet.howManySignaturesCanWeMake()}</BlueText>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{wallet.type === MultisigHDWallet.type && !!wallet.getDerivationPath() && (
|
||||||
|
<>
|
||||||
|
<Text style={[styles.textLabel2, stylesHook.textLabel2]}>derivation path</Text>
|
||||||
|
<BlueText>{wallet.getDerivationPath()}</BlueText>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
{wallet.type === LightningCustodianWallet.type && (
|
{wallet.type === LightningCustodianWallet.type && (
|
||||||
<>
|
<>
|
||||||
<Text style={[styles.textLabel1, stylesHook.textLabel1]}>{loc.wallets.details_connected_to.toLowerCase()}</Text>
|
<Text style={[styles.textLabel1, stylesHook.textLabel1]}>{loc.wallets.details_connected_to.toLowerCase()}</Text>
|
||||||
|
@ -302,7 +403,9 @@ const WalletDetails = () => {
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<>
|
<>
|
||||||
<Text style={[styles.textLabel2, stylesHook.textLabel2]}>{loc.transactions.list_title.toLowerCase()}</Text>
|
<Text onPress={exportInternals} style={[styles.textLabel2, stylesHook.textLabel2]}>
|
||||||
|
{loc.transactions.list_title.toLowerCase()}
|
||||||
|
</Text>
|
||||||
<View style={styles.hardware}>
|
<View style={styles.hardware}>
|
||||||
<BlueText>{loc.wallets.details_display}</BlueText>
|
<BlueText>{loc.wallets.details_display}</BlueText>
|
||||||
<Switch value={hideTransactionsInWalletsList} onValueChange={setHideTransactionsInWalletsList} />
|
<Switch value={hideTransactionsInWalletsList} onValueChange={setHideTransactionsInWalletsList} />
|
||||||
|
@ -333,6 +436,15 @@ const WalletDetails = () => {
|
||||||
|
|
||||||
<BlueSpacing20 />
|
<BlueSpacing20 />
|
||||||
|
|
||||||
|
{wallet.type === MultisigHDWallet.type && (
|
||||||
|
<>
|
||||||
|
<SecondButton
|
||||||
|
onPress={navigateToMultisigCoordinationSetup}
|
||||||
|
title={loc.multisig.export_coordination_setup.replace(/^\w/, c => c.toUpperCase())}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
{(wallet.type === HDLegacyBreadwalletWallet.type ||
|
{(wallet.type === HDLegacyBreadwalletWallet.type ||
|
||||||
wallet.type === HDLegacyP2PKHWallet.type ||
|
wallet.type === HDLegacyP2PKHWallet.type ||
|
||||||
wallet.type === HDSegwitBech32Wallet.type ||
|
wallet.type === HDSegwitBech32Wallet.type ||
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, { useRef, useState, useEffect } from 'react';
|
import React, { useRef } from 'react';
|
||||||
import { StatusBar, View, TouchableOpacity, InteractionManager, StyleSheet, Alert, useWindowDimensions } from 'react-native';
|
import { StatusBar, View, TouchableOpacity, StyleSheet, Alert, useWindowDimensions } from 'react-native';
|
||||||
import { DrawerContentScrollView } from '@react-navigation/drawer';
|
import { DrawerContentScrollView } from '@react-navigation/drawer';
|
||||||
import { WalletsCarousel, BlueNavigationStyle, BlueHeaderDefaultMainHooks } from '../../BlueComponents';
|
import { WalletsCarousel, BlueNavigationStyle, BlueHeaderDefaultMain } from '../../BlueComponents';
|
||||||
import { Icon } from 'react-native-elements';
|
import { Icon } from 'react-native-elements';
|
||||||
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
@ -14,11 +14,11 @@ import { useTheme, useRoute } from '@react-navigation/native';
|
||||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||||
const EV = require('../../blue_modules/events');
|
const EV = require('../../blue_modules/events');
|
||||||
const BlueApp: AppStorage = require('../../BlueApp');
|
const BlueApp: AppStorage = require('../../BlueApp');
|
||||||
const BlueElectrum = require('../../blue_modules/BlueElectrum');
|
|
||||||
|
|
||||||
const DrawerList = props => {
|
const DrawerList = props => {
|
||||||
|
console.log('drawerList rendering...');
|
||||||
const walletsCarousel = useRef();
|
const walletsCarousel = useRef();
|
||||||
const [wallets, setWallets] = useState(BlueApp.getWallets().concat(false));
|
const wallets = useRoute().params?.wallets || BlueApp.getWallets() || [];
|
||||||
const height = useWindowDimensions().height;
|
const height = useWindowDimensions().height;
|
||||||
const { colors } = useTheme();
|
const { colors } = useTheme();
|
||||||
const { selectedWallet } = useRoute().params || '';
|
const { selectedWallet } = useRoute().params || '';
|
||||||
|
@ -27,83 +27,6 @@ const DrawerList = props => {
|
||||||
backgroundColor: colors.brandingColor,
|
backgroundColor: colors.brandingColor,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
let lastSnappedTo = 0;
|
|
||||||
|
|
||||||
const refreshTransactions = () => {
|
|
||||||
InteractionManager.runAfterInteractions(async () => {
|
|
||||||
let noErr = true;
|
|
||||||
try {
|
|
||||||
// await BlueElectrum.ping();
|
|
||||||
await BlueElectrum.waitTillConnected();
|
|
||||||
const balanceStart = +new Date();
|
|
||||||
await BlueApp.fetchWalletBalances(lastSnappedTo || 0);
|
|
||||||
const balanceEnd = +new Date();
|
|
||||||
console.log('fetch balance took', (balanceEnd - balanceStart) / 1000, 'sec');
|
|
||||||
const start = +new Date();
|
|
||||||
await BlueApp.fetchWalletTransactions(lastSnappedTo || 0);
|
|
||||||
const end = +new Date();
|
|
||||||
console.log('fetch tx took', (end - start) / 1000, 'sec');
|
|
||||||
} catch (err) {
|
|
||||||
noErr = false;
|
|
||||||
console.warn(err);
|
|
||||||
}
|
|
||||||
if (noErr) await BlueApp.saveToDisk(); // caching
|
|
||||||
|
|
||||||
redrawScreen();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
EV(EV.enum.TRANSACTIONS_COUNT_CHANGED);
|
|
||||||
console.log('drawerList wallets changed');
|
|
||||||
}, [wallets]);
|
|
||||||
|
|
||||||
const redrawScreen = (scrollToEnd = false) => {
|
|
||||||
console.log('drawerList redrawScreen()');
|
|
||||||
|
|
||||||
const newWallets = BlueApp.getWallets().concat(false);
|
|
||||||
if (scrollToEnd) {
|
|
||||||
scrollToEnd = newWallets.length > wallets.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
setWallets(newWallets);
|
|
||||||
if (scrollToEnd) {
|
|
||||||
// eslint-disable-next-line no-unused-expressions
|
|
||||||
walletsCarousel.current?.snapToItem(wallets.length - 2);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// here, when we receive REMOTE_TRANSACTIONS_COUNT_CHANGED we fetch TXs and balance for current wallet.
|
|
||||||
// placing event subscription here so it gets exclusively re-subscribed more often. otherwise we would
|
|
||||||
// have to unsubscribe on unmount and resubscribe again on mount.
|
|
||||||
EV(EV.enum.REMOTE_TRANSACTIONS_COUNT_CHANGED, refreshTransactions, true);
|
|
||||||
|
|
||||||
EV(EV.enum.WALLETS_COUNT_CHANGED, () => redrawScreen(true));
|
|
||||||
|
|
||||||
console.log('drawerList useEffect');
|
|
||||||
// the idea is that upon wallet launch we will refresh
|
|
||||||
// all balances and all transactions here:
|
|
||||||
redrawScreen();
|
|
||||||
InteractionManager.runAfterInteractions(async () => {
|
|
||||||
try {
|
|
||||||
await BlueElectrum.waitTillConnected();
|
|
||||||
const balanceStart = +new Date();
|
|
||||||
await BlueApp.fetchWalletBalances();
|
|
||||||
const balanceEnd = +new Date();
|
|
||||||
console.log('fetch all wallet balances took', (balanceEnd - balanceStart) / 1000, 'sec');
|
|
||||||
const start = +new Date();
|
|
||||||
await BlueApp.fetchWalletTransactions();
|
|
||||||
const end = +new Date();
|
|
||||||
console.log('fetch all wallet txs took', (end - start) / 1000, 'sec');
|
|
||||||
redrawScreen();
|
|
||||||
await BlueApp.saveToDisk();
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleClick = index => {
|
const handleClick = index => {
|
||||||
console.log('click', index);
|
console.log('click', index);
|
||||||
|
@ -156,82 +79,13 @@ const DrawerList = props => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onSnapToItem = index => {
|
|
||||||
console.log('onSnapToItem', index);
|
|
||||||
lastSnappedTo = index;
|
|
||||||
if (index < BlueApp.getWallets().length) {
|
|
||||||
// not the last
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wallets[index].type === PlaceholderWallet.type) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// now, lets try to fetch balance and txs for this wallet in case it has changed
|
|
||||||
lazyRefreshWallet(index);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decides whether wallet with such index shoud be refreshed,
|
|
||||||
* refreshes if yes and redraws the screen
|
|
||||||
* @param index {Integer} Index of the wallet.
|
|
||||||
* @return {Promise.<void>}
|
|
||||||
*/
|
|
||||||
const lazyRefreshWallet = async index => {
|
|
||||||
/** @type {Array.<AbstractWallet>} wallets */
|
|
||||||
const wallets = BlueApp.getWallets();
|
|
||||||
if (!wallets[index]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const oldBalance = wallets[index].getBalance();
|
|
||||||
let noErr = true;
|
|
||||||
let didRefresh = false;
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (wallets[index] && wallets[index].type !== PlaceholderWallet.type && wallets[index].timeToRefreshBalance()) {
|
|
||||||
console.log('snapped to, and now its time to refresh wallet #', index);
|
|
||||||
await wallets[index].fetchBalance();
|
|
||||||
if (oldBalance !== wallets[index].getBalance() || wallets[index].getUnconfirmedBalance() !== 0) {
|
|
||||||
console.log('balance changed, thus txs too');
|
|
||||||
// balance changed, thus txs too
|
|
||||||
await wallets[index].fetchTransactions();
|
|
||||||
redrawScreen();
|
|
||||||
didRefresh = true;
|
|
||||||
} else if (wallets[index].timeToRefreshTransaction()) {
|
|
||||||
console.log(wallets[index].getLabel(), 'thinks its time to refresh TXs');
|
|
||||||
await wallets[index].fetchTransactions();
|
|
||||||
if (wallets[index].fetchPendingTransactions) {
|
|
||||||
await wallets[index].fetchPendingTransactions();
|
|
||||||
}
|
|
||||||
if (wallets[index].fetchUserInvoices) {
|
|
||||||
await wallets[index].fetchUserInvoices();
|
|
||||||
await wallets[index].fetchBalance(); // chances are, paid ln invoice was processed during `fetchUserInvoices()` call and altered user's balance, so its worth fetching balance again
|
|
||||||
}
|
|
||||||
redrawScreen();
|
|
||||||
didRefresh = true;
|
|
||||||
} else {
|
|
||||||
console.log('balance not changed');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Err) {
|
|
||||||
noErr = false;
|
|
||||||
console.warn(Err);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (noErr && didRefresh) {
|
|
||||||
await BlueApp.saveToDisk(); // caching
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderWalletsCarousel = () => {
|
const renderWalletsCarousel = () => {
|
||||||
return (
|
return (
|
||||||
<WalletsCarousel
|
<WalletsCarousel
|
||||||
removeClippedSubviews={false}
|
removeClippedSubviews={false}
|
||||||
data={wallets}
|
data={wallets.concat(false)}
|
||||||
onPress={handleClick}
|
onPress={handleClick}
|
||||||
handleLongPress={handleLongPress}
|
handleLongPress={handleLongPress}
|
||||||
onSnapToItem={onSnapToItem}
|
|
||||||
ref={walletsCarousel}
|
ref={walletsCarousel}
|
||||||
testID="WalletsList"
|
testID="WalletsList"
|
||||||
vertical
|
vertical
|
||||||
|
@ -244,19 +98,16 @@ const DrawerList = props => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onNewWalletPress = () => {
|
||||||
|
return !BlueApp.getWallets().some(wallet => wallet.type === PlaceholderWallet.type) ? props.navigation.navigate('AddWalletRoot') : null;
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DrawerContentScrollView {...props} scrollEnabled={false}>
|
<DrawerContentScrollView {...props} scrollEnabled={false}>
|
||||||
<View styles={[styles.root, stylesHook.root]}>
|
<View styles={[styles.root, stylesHook.root]}>
|
||||||
<StatusBar barStyle="default" />
|
<StatusBar barStyle="default" />
|
||||||
<SafeAreaView style={styles.root}>
|
<SafeAreaView style={styles.root}>
|
||||||
<BlueHeaderDefaultMainHooks
|
<BlueHeaderDefaultMain leftText={loc.wallets.list_title} onNewWalletPress={onNewWalletPress} isDrawerList />
|
||||||
leftText={loc.wallets.list_title}
|
|
||||||
onNewWalletPress={
|
|
||||||
!BlueApp.getWallets().some(wallet => wallet.type === PlaceholderWallet.type)
|
|
||||||
? () => props.navigation.navigate('AddWalletRoot')
|
|
||||||
: null
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
{renderWalletsCarousel()}
|
{renderWalletsCarousel()}
|
||||||
</View>
|
</View>
|
||||||
|
|
128
screen/wallets/exportMultisigCoordinationSetup.js
Normal file
128
screen/wallets/exportMultisigCoordinationSetup.js
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
import React, { useCallback, useState } from 'react';
|
||||||
|
import { ActivityIndicator, InteractionManager, ScrollView, StatusBar, StyleSheet, useWindowDimensions, View } from 'react-native';
|
||||||
|
import QRCode from 'react-native-qrcode-svg';
|
||||||
|
import { BlueNavigationStyle, BlueSpacing20, BlueText, SafeBlueArea } from '../../BlueComponents';
|
||||||
|
import Privacy from '../../Privacy';
|
||||||
|
import Biometric from '../../class/biometrics';
|
||||||
|
import loc from '../../loc';
|
||||||
|
import { encodeUR } from '../../blue_modules/bc-ur/dist';
|
||||||
|
import { useFocusEffect, useNavigation, useRoute, useTheme } from '@react-navigation/native';
|
||||||
|
import { SquareButton } from '../../components/SquareButton';
|
||||||
|
|
||||||
|
const BlueApp = require('../../BlueApp');
|
||||||
|
const fs = require('../../blue_modules/fs');
|
||||||
|
|
||||||
|
const ExportMultisigCoordinationSetup = () => {
|
||||||
|
const walletId = useRoute().params.walletId;
|
||||||
|
const wallet = BlueApp.getWallets().find(w => w.getID() === walletId);
|
||||||
|
const qrCodeContents = encodeUR(Buffer.from(wallet.getXpub(), 'ascii').toString('hex'), 77777)[0];
|
||||||
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
const { goBack } = useNavigation();
|
||||||
|
const { colors } = useTheme();
|
||||||
|
const { width, height } = useWindowDimensions();
|
||||||
|
const stylesHook = {
|
||||||
|
...styles,
|
||||||
|
loading: {
|
||||||
|
...styles.loading,
|
||||||
|
backgroundColor: colors.elevated,
|
||||||
|
},
|
||||||
|
root: {
|
||||||
|
...styles.root,
|
||||||
|
backgroundColor: colors.elevated,
|
||||||
|
},
|
||||||
|
type: { ...styles.type, color: colors.foregroundColor },
|
||||||
|
secret: { ...styles.secret, color: colors.foregroundColor },
|
||||||
|
};
|
||||||
|
|
||||||
|
const exportTxtFile = async () => {
|
||||||
|
await fs.writeFileAndExport(wallet.getLabel() + '.txt', wallet.getXpub());
|
||||||
|
};
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
Privacy.enableBlur();
|
||||||
|
const task = InteractionManager.runAfterInteractions(async () => {
|
||||||
|
if (wallet) {
|
||||||
|
const isBiometricsEnabled = await Biometric.isBiometricUseCapableAndEnabled();
|
||||||
|
|
||||||
|
if (isBiometricsEnabled) {
|
||||||
|
if (!(await Biometric.unlockWithBiometrics())) {
|
||||||
|
return goBack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return () => {
|
||||||
|
task.cancel();
|
||||||
|
Privacy.disableBlur();
|
||||||
|
};
|
||||||
|
}, [goBack, wallet]),
|
||||||
|
);
|
||||||
|
|
||||||
|
return isLoading ? (
|
||||||
|
<View style={stylesHook.loading}>
|
||||||
|
<ActivityIndicator />
|
||||||
|
</View>
|
||||||
|
) : (
|
||||||
|
<SafeBlueArea style={stylesHook.root}>
|
||||||
|
<StatusBar barStyle="light-content" />
|
||||||
|
<ScrollView contentContainerStyle={styles.scrollViewContent}>
|
||||||
|
<View>
|
||||||
|
<BlueText style={stylesHook.type}>{wallet.getLabel()}</BlueText>
|
||||||
|
</View>
|
||||||
|
<BlueSpacing20 />
|
||||||
|
<View style={styles.activeQrcode}>
|
||||||
|
<QRCode
|
||||||
|
value={qrCodeContents}
|
||||||
|
size={height > width ? width - 40 : width / 2}
|
||||||
|
logoSize={70}
|
||||||
|
color="#000000"
|
||||||
|
logoBackgroundColor={colors.brandingColor}
|
||||||
|
backgroundColor="#FFFFFF"
|
||||||
|
ecl="H"
|
||||||
|
/>
|
||||||
|
<BlueSpacing20 />
|
||||||
|
<SquareButton backgroundColor="#EEF0F4" onPress={exportTxtFile} title={loc.multisig.share} />
|
||||||
|
</View>
|
||||||
|
<BlueSpacing20 />
|
||||||
|
<BlueText style={stylesHook.secret}>{wallet.getXpub()}</BlueText>
|
||||||
|
</ScrollView>
|
||||||
|
</SafeBlueArea>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
loading: {
|
||||||
|
flex: 1,
|
||||||
|
paddingTop: 20,
|
||||||
|
},
|
||||||
|
root: {
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
scrollViewContent: {
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
flexGrow: 1,
|
||||||
|
},
|
||||||
|
activeQrcode: { borderWidth: 6, borderRadius: 8, borderColor: '#FFFFFF' },
|
||||||
|
type: {
|
||||||
|
fontSize: 17,
|
||||||
|
fontWeight: '700',
|
||||||
|
},
|
||||||
|
secret: {
|
||||||
|
alignItems: 'center',
|
||||||
|
paddingHorizontal: 16,
|
||||||
|
fontSize: 16,
|
||||||
|
lineHeight: 24,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
ExportMultisigCoordinationSetup.navigationOptions = ({ navigation }) => ({
|
||||||
|
...BlueNavigationStyle(navigation, true),
|
||||||
|
title: loc.multisig.export_coordination_setup,
|
||||||
|
headerLeft: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default ExportMultisigCoordinationSetup;
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue