mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2024-11-19 18:00:17 +01:00
Merge branch 'master' of github.com:Overtorment/BlueWallet
This commit is contained in:
commit
46a5030ee6
@ -9,6 +9,7 @@
|
||||
'warn',
|
||||
{
|
||||
singleQuote: true,
|
||||
printWidth: 140,
|
||||
trailingComma: 'all'
|
||||
}
|
||||
]
|
||||
|
10
App.js
10
App.js
@ -23,14 +23,8 @@ const appjson = require('./app.json');
|
||||
|
||||
const CustomDrawerContentComponent = props => (
|
||||
<ScrollView>
|
||||
<SafeAreaView
|
||||
style={styles.container}
|
||||
forceInset={{ top: 'always', horizontal: 'never' }}
|
||||
>
|
||||
<Text
|
||||
onPress={() => props.navigation.navigate('About')}
|
||||
style={styles.heading}
|
||||
>
|
||||
<SafeAreaView style={styles.container} forceInset={{ top: 'always', horizontal: 'never' }}>
|
||||
<Text onPress={() => props.navigation.navigate('About')} style={styles.heading}>
|
||||
{' '}
|
||||
{pkg.name} v{pkg.version} (build {appjson.expo.ios.buildNumber})
|
||||
</Text>
|
||||
|
58
App.test.js
58
App.test.js
@ -10,6 +10,7 @@ let assert = require('assert');
|
||||
jest.mock('react-native-qrcode', () => 'Video');
|
||||
const AsyncStorage = new MockStorage();
|
||||
jest.setMock('AsyncStorage', AsyncStorage);
|
||||
jest.useFakeTimers();
|
||||
jest.mock('Picker', () => {
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
const React = require('React');
|
||||
@ -164,9 +165,7 @@ it('Appstorage - encryptStorage & load encrypted storage works', async () => {
|
||||
assert.equal(Storage2.wallets[1].getLabel(), 'testlabel2');
|
||||
|
||||
// next, adding new `fake` storage which should be unlocked with `fake` password
|
||||
let createFakeStorageResult = await Storage2.createFakeStorage(
|
||||
'fakePassword',
|
||||
);
|
||||
let createFakeStorageResult = await Storage2.createFakeStorage('fakePassword');
|
||||
assert.ok(createFakeStorageResult);
|
||||
assert.equal(Storage2.wallets.length, 0);
|
||||
assert.equal(Storage2.cachedPassword, 'fakePassword');
|
||||
@ -190,46 +189,6 @@ it('Appstorage - encryptStorage & load encrypted storage works', async () => {
|
||||
assert.equal(Storage3.wallets[0].getLabel(), 'fakewallet');
|
||||
});
|
||||
|
||||
it('bip38 decodes', async () => {
|
||||
const bip38 = require('./bip38');
|
||||
const wif = require('wif');
|
||||
|
||||
let encryptedKey =
|
||||
'6PRVWUbkzq2VVjRuv58jpwVjTeN46MeNmzUHqUjQptBJUHGcBakduhrUNc';
|
||||
let decryptedKey = await bip38.decrypt(
|
||||
encryptedKey,
|
||||
'TestingOneTwoThree',
|
||||
() => {},
|
||||
{ N: 1, r: 8, p: 8 }, // using non-default parameters to speed it up (not-bip38 compliant)
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
wif.encode(0x80, decryptedKey.privateKey, decryptedKey.compressed),
|
||||
'5KN7MzqK5wt2TP1fQCYyHBtDrXdJuXbUzm4A9rKAteGu3Qi5CVR',
|
||||
);
|
||||
});
|
||||
|
||||
it('bip38 decodes slow', async () => {
|
||||
if (process.env.USER === 'burn') {
|
||||
// run only on circleCI
|
||||
return;
|
||||
}
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 60000;
|
||||
const bip38 = require('bip38');
|
||||
const wif = require('wif');
|
||||
|
||||
let encryptedKey =
|
||||
'6PnU5voARjBBykwSddwCdcn6Eu9EcsK24Gs5zWxbJbPZYW7eiYQP8XgKbN';
|
||||
let decryptedKey = await bip38.decrypt(encryptedKey, 'qwerty', status =>
|
||||
process.stdout.write(parseInt(status.percent) + '%\r'),
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
wif.encode(0x80, decryptedKey.privateKey, decryptedKey.compressed),
|
||||
'KxqRtpd9vFju297ACPKHrGkgXuberTveZPXbRDiQ3MXZycSQYtjc',
|
||||
);
|
||||
});
|
||||
|
||||
it('Wallet can fetch UTXO', async () => {
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 30000;
|
||||
let w = new SegwitP2SHWallet();
|
||||
@ -251,11 +210,20 @@ it('Wallet can fetch balance', async () => {
|
||||
assert.ok(w._lastBalanceFetch > 0);
|
||||
});
|
||||
|
||||
it.skip('Wallet can fetch TXs', async () => {
|
||||
let w = new LegacyWallet();
|
||||
w._address = '12eQ9m4sgAwTSQoNXkRABKhCXCsjm2jdVG';
|
||||
await w.fetchTransactions();
|
||||
console.log('txs num:', w.getTransactions().length);
|
||||
assert.equal(w.getTransactions().length, 2);
|
||||
});
|
||||
|
||||
describe('currency', () => {
|
||||
it.only('fetches exchange rate and saves to AsyncStorage', async () => {
|
||||
it('fetches exchange rate and saves to AsyncStorage', async () => {
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 15000;
|
||||
AsyncStorage.storageCache = {}; // cleanup from other tests
|
||||
let currency = require('./currency');
|
||||
await currency.startUpdater(true);
|
||||
await currency.startUpdater();
|
||||
let cur = AsyncStorage.storageCache[AppStorage.CURRENCY];
|
||||
cur = JSON.parse(cur);
|
||||
assert.ok(Number.isInteger(cur[currency.STRUCT.LAST_UPDATED]));
|
||||
|
32
App2.test.js
Normal file
32
App2.test.js
Normal file
@ -0,0 +1,32 @@
|
||||
/* global it, jasmine */
|
||||
let assert = require('assert');
|
||||
|
||||
it('bip38 decodes', async () => {
|
||||
const bip38 = require('./bip38');
|
||||
const wif = require('wif');
|
||||
|
||||
let encryptedKey = '6PRVWUbkzq2VVjRuv58jpwVjTeN46MeNmzUHqUjQptBJUHGcBakduhrUNc';
|
||||
let decryptedKey = await bip38.decrypt(
|
||||
encryptedKey,
|
||||
'TestingOneTwoThree',
|
||||
() => {},
|
||||
{ N: 1, r: 8, p: 8 }, // using non-default parameters to speed it up (not-bip38 compliant)
|
||||
);
|
||||
|
||||
assert.equal(wif.encode(0x80, decryptedKey.privateKey, decryptedKey.compressed), '5KN7MzqK5wt2TP1fQCYyHBtDrXdJuXbUzm4A9rKAteGu3Qi5CVR');
|
||||
});
|
||||
|
||||
it('bip38 decodes slow', async () => {
|
||||
if (process.env.USER === 'burn' || process.env.USER === 'igor') {
|
||||
// run only on circleCI
|
||||
return;
|
||||
}
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 60000;
|
||||
const bip38 = require('bip38');
|
||||
const wif = require('wif');
|
||||
|
||||
let encryptedKey = '6PnU5voARjBBykwSddwCdcn6Eu9EcsK24Gs5zWxbJbPZYW7eiYQP8XgKbN';
|
||||
let decryptedKey = await bip38.decrypt(encryptedKey, 'qwerty', status => process.stdout.write(parseInt(status.percent) + '%\r'));
|
||||
|
||||
assert.equal(wif.encode(0x80, decryptedKey.privateKey, decryptedKey.compressed), 'KxqRtpd9vFju297ACPKHrGkgXuberTveZPXbRDiQ3MXZycSQYtjc');
|
||||
});
|
10
BlueApp.js
10
BlueApp.js
@ -15,10 +15,7 @@ async function startAndDecrypt(retry) {
|
||||
let password = false;
|
||||
if (await BlueApp.storageIsEncrypted()) {
|
||||
do {
|
||||
password = await prompt(
|
||||
(retry && loc._.bad_password) || loc._.enter_password,
|
||||
loc._.storage_is_encrypted,
|
||||
);
|
||||
password = await prompt((retry && loc._.bad_password) || loc._.enter_password, loc._.storage_is_encrypted);
|
||||
} while (!password);
|
||||
}
|
||||
let success = await BlueApp.loadFromDisk(password);
|
||||
@ -35,10 +32,7 @@ async function startAndDecrypt(retry) {
|
||||
console.log('time to refresh wallet #0');
|
||||
let oldBalance = wallets[0].getBalance();
|
||||
await wallets[0].fetchBalance();
|
||||
if (
|
||||
oldBalance !== wallets[0].getBalance() ||
|
||||
wallets[0].getUnconfirmedBalance() !== 0
|
||||
) {
|
||||
if (oldBalance !== wallets[0].getBalance() || wallets[0].getUnconfirmedBalance() !== 0) {
|
||||
// balance changed, thus txs too
|
||||
await wallets[0].fetchTransactions();
|
||||
hadToRefresh = true;
|
||||
|
@ -3,25 +3,8 @@ import React, { Component } from 'react';
|
||||
import { SafeAreaView } from 'react-navigation';
|
||||
import Ionicons from 'react-native-vector-icons/Ionicons';
|
||||
import { LinearGradient } from 'expo';
|
||||
import {
|
||||
Icon,
|
||||
Button,
|
||||
FormLabel,
|
||||
FormInput,
|
||||
Card,
|
||||
Text,
|
||||
Header,
|
||||
List,
|
||||
ListItem,
|
||||
} from 'react-native-elements';
|
||||
import {
|
||||
TouchableOpacity,
|
||||
ActivityIndicator,
|
||||
View,
|
||||
StyleSheet,
|
||||
Dimensions,
|
||||
Image,
|
||||
} from 'react-native';
|
||||
import { Icon, Button, FormLabel, FormInput, Card, Text, Header, List, ListItem } from 'react-native-elements';
|
||||
import { TouchableOpacity, ActivityIndicator, View, StyleSheet, Dimensions, Image } from 'react-native';
|
||||
import Carousel from 'react-native-snap-carousel';
|
||||
let loc = require('./loc/');
|
||||
/** @type {AppStorage} */
|
||||
@ -91,12 +74,7 @@ export class BlueCard extends Component {
|
||||
|
||||
export class BlueText extends Component {
|
||||
render() {
|
||||
return (
|
||||
<Text
|
||||
{...this.props}
|
||||
style={{ color: BlueApp.settings.foregroundColor }}
|
||||
/>
|
||||
);
|
||||
return <Text {...this.props} style={{ color: BlueApp.settings.foregroundColor }} />;
|
||||
}
|
||||
}
|
||||
|
||||
@ -123,12 +101,7 @@ export class BlueListItem extends Component {
|
||||
|
||||
export class BlueFormLabel extends Component {
|
||||
render() {
|
||||
return (
|
||||
<FormLabel
|
||||
{...this.props}
|
||||
labelStyle={{ color: BlueApp.settings.foregroundColor }}
|
||||
/>
|
||||
);
|
||||
return <FormLabel {...this.props} labelStyle={{ color: BlueApp.settings.foregroundColor }} />;
|
||||
}
|
||||
}
|
||||
|
||||
@ -215,12 +188,7 @@ export class BlueHeaderDefaultSub extends Component {
|
||||
>
|
||||
<View style={stylesBlueIcon.box}>
|
||||
<View style={stylesBlueIcon.ballTransparrent}>
|
||||
<Icon
|
||||
name="times"
|
||||
size={16}
|
||||
type="font-awesome"
|
||||
color={BlueApp.settings.foregroundColor}
|
||||
/>
|
||||
<Icon name="times" size={16} type="font-awesome" color={BlueApp.settings.foregroundColor} />
|
||||
</View>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
@ -264,12 +232,7 @@ export class BlueHeaderDefaultMain extends Component {
|
||||
>
|
||||
<View style={stylesBlueIcon.box}>
|
||||
<View style={stylesBlueIcon.ballTransparrent}>
|
||||
<Icon
|
||||
name="kebab-horizontal"
|
||||
size={22}
|
||||
type="octicon"
|
||||
color={BlueApp.settings.foregroundColor}
|
||||
/>
|
||||
<Icon name="kebab-horizontal" size={22} type="octicon" color={BlueApp.settings.foregroundColor} />
|
||||
</View>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
@ -281,23 +244,13 @@ export class BlueHeaderDefaultMain extends Component {
|
||||
|
||||
export class BlueSpacing extends Component {
|
||||
render() {
|
||||
return (
|
||||
<View
|
||||
{...this.props}
|
||||
style={{ height: 60, backgroundColor: BlueApp.settings.brandingColor }}
|
||||
/>
|
||||
);
|
||||
return <View {...this.props} style={{ height: 60, backgroundColor: BlueApp.settings.brandingColor }} />;
|
||||
}
|
||||
}
|
||||
|
||||
export class BlueSpacing40 extends Component {
|
||||
render() {
|
||||
return (
|
||||
<View
|
||||
{...this.props}
|
||||
style={{ height: 50, backgroundColor: BlueApp.settings.brandingColor }}
|
||||
/>
|
||||
);
|
||||
return <View {...this.props} style={{ height: 50, backgroundColor: BlueApp.settings.brandingColor }} />;
|
||||
}
|
||||
}
|
||||
|
||||
@ -463,14 +416,7 @@ export class BlueTransactionIncommingIcon extends Component {
|
||||
<View {...this.props} style={stylesBlueIcon.container}>
|
||||
<View style={stylesBlueIcon.boxIncomming}>
|
||||
<View style={stylesBlueIcon.ballIncomming}>
|
||||
<Icon
|
||||
{...this.props}
|
||||
name="arrow-down"
|
||||
size={16}
|
||||
type="font-awesome"
|
||||
color="#37c0a1"
|
||||
iconStyle={{ left: 0, top: 8 }}
|
||||
/>
|
||||
<Icon {...this.props} name="arrow-down" size={16} type="font-awesome" color="#37c0a1" iconStyle={{ left: 0, top: 8 }} />
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
@ -505,14 +451,7 @@ export class BlueTransactionOutgoingIcon extends Component {
|
||||
<View {...this.props} style={stylesBlueIcon.container}>
|
||||
<View style={stylesBlueIcon.boxIncomming}>
|
||||
<View style={stylesBlueIcon.ballOutgoing}>
|
||||
<Icon
|
||||
{...this.props}
|
||||
name="arrow-down"
|
||||
size={16}
|
||||
type="font-awesome"
|
||||
color="#d0021b"
|
||||
iconStyle={{ left: 0, top: 8 }}
|
||||
/>
|
||||
<Icon {...this.props} name="arrow-down" size={16} type="font-awesome" color="#d0021b" iconStyle={{ left: 0, top: 8 }} />
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
@ -556,14 +495,7 @@ export class BlueReceiveButtonIcon extends Component {
|
||||
transform: [{ rotate: '-45deg' }],
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
{...this.props}
|
||||
name="arrow-down"
|
||||
size={16}
|
||||
type="font-awesome"
|
||||
color="#2f5fb3"
|
||||
iconStyle={{ left: 0, top: 15 }}
|
||||
/>
|
||||
<Icon {...this.props} name="arrow-down" size={16} type="font-awesome" color="#2f5fb3" iconStyle={{ left: 0, top: 15 }} />
|
||||
</View>
|
||||
<Text
|
||||
style={{
|
||||
@ -620,14 +552,7 @@ export class BlueSendButtonIcon extends Component {
|
||||
transform: [{ rotate: '225deg' }],
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
{...this.props}
|
||||
name="arrow-down"
|
||||
size={16}
|
||||
type="font-awesome"
|
||||
color="#2f5fb3"
|
||||
iconStyle={{ left: 0, top: 0 }}
|
||||
/>
|
||||
<Icon {...this.props} name="arrow-down" size={16} type="font-awesome" color="#2f5fb3" iconStyle={{ left: 0, top: 0 }} />
|
||||
</View>
|
||||
<Text
|
||||
style={{
|
||||
@ -773,10 +698,7 @@ export class WalletsCarousel extends Component {
|
||||
}
|
||||
}}
|
||||
>
|
||||
<LinearGradient
|
||||
colors={['#65ceef', '#68bbe1']}
|
||||
style={{ padding: 15, borderRadius: 10, height: 145 }}
|
||||
>
|
||||
<LinearGradient colors={['#65ceef', '#68bbe1']} style={{ padding: 15, borderRadius: 10, height: 145 }}>
|
||||
<Image
|
||||
source={require('./img/btc-shape.png')}
|
||||
style={{
|
||||
|
@ -4,22 +4,20 @@ import {
|
||||
SegwitBech32Wallet,
|
||||
HDSegwitP2SHWallet,
|
||||
HDLegacyBreadwalletWallet,
|
||||
HDLegacyP2PKHWallet,
|
||||
LegacyWallet,
|
||||
} from './class';
|
||||
let assert = require('assert');
|
||||
|
||||
it('can convert witness to address', () => {
|
||||
let address = SegwitP2SHWallet.witnessToAddress(
|
||||
'035c618df829af694cb99e664ce1b34f80ad2c3b49bcd0d9c0b1836c66b2d25fd8',
|
||||
);
|
||||
let address = SegwitP2SHWallet.witnessToAddress('035c618df829af694cb99e664ce1b34f80ad2c3b49bcd0d9c0b1836c66b2d25fd8');
|
||||
assert.equal(address, '34ZVGb3gT8xMLT6fpqC6dNVqJtJmvdjbD7');
|
||||
|
||||
address = SegwitBech32Wallet.witnessToAddress(
|
||||
'035c618df829af694cb99e664ce1b34f80ad2c3b49bcd0d9c0b1836c66b2d25fd8',
|
||||
);
|
||||
address = SegwitBech32Wallet.witnessToAddress('035c618df829af694cb99e664ce1b34f80ad2c3b49bcd0d9c0b1836c66b2d25fd8');
|
||||
assert.equal(address, 'bc1quhnve8q4tk3unhmjts7ymxv8cd6w9xv8wy29uv');
|
||||
});
|
||||
|
||||
it('can create a BIP49', function() {
|
||||
it('can create a Segwit HD (BIP49)', async function() {
|
||||
let bip39 = require('bip39');
|
||||
let bitcoin = require('bitcoinjs-lib');
|
||||
let mnemonic =
|
||||
@ -35,10 +33,7 @@ it('can create a BIP49', function() {
|
||||
let scriptSig = bitcoin.script.witnessPubKeyHash.output.encode(keyhash);
|
||||
let addressBytes = bitcoin.crypto.hash160(scriptSig);
|
||||
let outputScript = bitcoin.script.scriptHash.output.encode(addressBytes);
|
||||
let address = bitcoin.address.fromOutputScript(
|
||||
outputScript,
|
||||
bitcoin.networks.bitcoin,
|
||||
);
|
||||
let address = bitcoin.address.fromOutputScript(outputScript, bitcoin.networks.bitcoin);
|
||||
|
||||
assert.equal(address, '3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK');
|
||||
|
||||
@ -54,24 +49,65 @@ it('can create a BIP49', function() {
|
||||
assert.equal(true, hd.validateMnemonic());
|
||||
|
||||
assert.equal(child.keyPair.toWIF(), hd._getExternalWIFByIndex(0));
|
||||
assert.equal(
|
||||
'ypub6WhHmKBmHNjcrUVNCa3sXduH9yxutMipDcwiKW31vWjcMbfhQHjXdyx4rqXbEtVgzdbhFJ5mZJWmfWwnP4Vjzx97admTUYKQt6b9D7jjSCp',
|
||||
hd.getXpub(),
|
||||
);
|
||||
|
||||
// checking that internal pointer and async address getter return the same address
|
||||
let freeAddress = await hd.getAddressAsync();
|
||||
assert.equal(hd._getExternalAddressByIndex(hd.next_free_address_index), freeAddress);
|
||||
});
|
||||
|
||||
it.only('can create a Legacy HD (BIP44)', async function() {
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 30 * 1000;
|
||||
let bip39 = require('bip39');
|
||||
let bitcoin = require('bitcoinjs-lib');
|
||||
let mnemonic = 'high relief amount witness try remember adult destroy puppy fox giant peace';
|
||||
assert.ok(bip39.validateMnemonic(mnemonic));
|
||||
let seed = bip39.mnemonicToSeed(mnemonic);
|
||||
let root = bitcoin.HDNode.fromSeedBuffer(seed);
|
||||
|
||||
let path = "m/44'/0'/0'/0/0";
|
||||
let child = root.derivePath(path);
|
||||
|
||||
let w = new LegacyWallet();
|
||||
w.setSecret(child.keyPair.toWIF());
|
||||
assert.equal('12eQ9m4sgAwTSQoNXkRABKhCXCsjm2jdVG', w.getAddress());
|
||||
|
||||
// testing our class
|
||||
let hd = new HDLegacyP2PKHWallet();
|
||||
hd.setSecret(mnemonic);
|
||||
assert.equal(hd.validateMnemonic(), true);
|
||||
assert.equal(hd._getExternalAddressByIndex(0), '12eQ9m4sgAwTSQoNXkRABKhCXCsjm2jdVG');
|
||||
assert.equal(hd._getInternalAddressByIndex(0), '1KZjqYHm7a1DjhjcdcjfQvYfF2h6PqatjX');
|
||||
assert.equal(
|
||||
hd.getXpub(),
|
||||
'xpub6CQdfC3v9gU86eaSn7AhUFcBVxiGhdtYxdC5Cw2vLmFkfth2KXCMmYcPpvZviA89X6DXDs4PJDk5QVL2G2xaVjv7SM4roWHr1gR4xB3Z7Ps',
|
||||
);
|
||||
|
||||
assert.equal(hd._getExternalWIFByIndex(0), 'L1hqNoJ26YuCdujMBJfWBNfgf4Jo7AcKFvcNcKLoMtoJDdDtRq7Q');
|
||||
assert.equal(hd._getInternalWIFByIndex(0), 'Kx3QkrfemEEV49Mj5oWfb4bsWymboPdstta7eN3kAzop9apxYEFP');
|
||||
await hd.fetchBalance();
|
||||
assert.equal(hd.balance, 0);
|
||||
await hd.fetchTransactions();
|
||||
assert.equal(hd.transactions.length, 4);
|
||||
assert.equal(hd.next_free_address_index, 1);
|
||||
assert.equal(hd.next_free_change_address_index, 1);
|
||||
|
||||
// TODO: rewrite fetchTransactions on Frisbee and uncomment:
|
||||
// let freeAddress = await hd.getAddressAsync();
|
||||
// assert.equal(hd._getExternalAddressByIndex(hd.next_free_address_index), freeAddress);
|
||||
});
|
||||
|
||||
it('HD breadwallet works', async function() {
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 300 * 1000;
|
||||
let hdBread = new HDLegacyBreadwalletWallet();
|
||||
hdBread.setSecret(
|
||||
'high relief amount witness try remember adult destroy puppy fox giant peace',
|
||||
);
|
||||
hdBread.setSecret('high relief amount witness try remember adult destroy puppy fox giant peace');
|
||||
|
||||
assert.equal(hdBread.validateMnemonic(), true);
|
||||
assert.equal(
|
||||
hdBread._getExternalAddressByIndex(0),
|
||||
'1ARGkNMdsBE36fJhddSwf8PqBXG3s4d2KU',
|
||||
);
|
||||
assert.equal(
|
||||
hdBread._getInternalAddressByIndex(0),
|
||||
'1JLvA5D7RpWgChb4A5sFcLNrfxYbyZdw3V',
|
||||
);
|
||||
assert.equal(hdBread._getExternalAddressByIndex(0), '1ARGkNMdsBE36fJhddSwf8PqBXG3s4d2KU');
|
||||
assert.equal(hdBread._getInternalAddressByIndex(0), '1JLvA5D7RpWgChb4A5sFcLNrfxYbyZdw3V');
|
||||
|
||||
assert.equal(
|
||||
hdBread.getXpub(),
|
||||
|
@ -14,17 +14,13 @@ export default class MockStorage {
|
||||
|
||||
getItem = jest.fn(key => {
|
||||
return new Promise(resolve => {
|
||||
return this.storageCache.hasOwnProperty(key)
|
||||
? resolve(this.storageCache[key])
|
||||
: resolve(null);
|
||||
return this.storageCache.hasOwnProperty(key) ? resolve(this.storageCache[key]) : resolve(null);
|
||||
});
|
||||
});
|
||||
|
||||
removeItem = jest.fn(key => {
|
||||
return new Promise((resolve, reject) => {
|
||||
return this.storageCache.hasOwnProperty(key)
|
||||
? resolve(delete this.storageCache[key])
|
||||
: reject(new Error('No such key!'));
|
||||
return this.storageCache.hasOwnProperty(key) ? resolve(delete this.storageCache[key]) : reject(new Error('No such key!'));
|
||||
});
|
||||
});
|
||||
|
||||
@ -33,8 +29,6 @@ export default class MockStorage {
|
||||
});
|
||||
|
||||
getAllKeys = jest.fn(key => {
|
||||
return new Promise((resolve, reject) =>
|
||||
resolve(Object.keys(this.storageCache)),
|
||||
);
|
||||
return new Promise((resolve, reject) => resolve(Object.keys(this.storageCache)));
|
||||
});
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
# BlueWallet - Bitcoin Wallet
|
||||
|
||||
[![CircleCI](https://circleci.com/gh/Overtorment/BlueWallet.svg?style=svg)](https://circleci.com/gh/Overtorment/BlueWallet)
|
||||
[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier)
|
||||
|
||||
Thin Bitcoin Wallet.
|
||||
Built with React Native, Expo and BlockCypher API.
|
||||
|
@ -197,9 +197,7 @@ export class AppStorage {
|
||||
} else {
|
||||
// decrypted ok, this is our bucket
|
||||
// we serialize our object's data, encrypt it, and add it to buckets
|
||||
newData.push(
|
||||
encryption.encrypt(JSON.stringify(data), this.cachedPassword),
|
||||
);
|
||||
newData.push(encryption.encrypt(JSON.stringify(data), this.cachedPassword));
|
||||
await AsyncStorage.setItem(AppStorage.FLAG_ENCRYPTED, '1');
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ const bip39 = require('bip39');
|
||||
|
||||
/**
|
||||
* HD Wallet (BIP39).
|
||||
* In particular, BIP49 (P2SH Segwit) https://github.com/bitcoin/bips/blob/master/bip-0049.mediawiki
|
||||
* In particular, Breadwallet-compatible (Legacy addresses)
|
||||
*/
|
||||
export class HDLegacyBreadwalletWallet extends HDSegwitP2SHWallet {
|
||||
constructor() {
|
||||
@ -16,10 +16,6 @@ export class HDLegacyBreadwalletWallet extends HDSegwitP2SHWallet {
|
||||
return 'HD Legacy Breadwallet-compatible (P2PKH)';
|
||||
}
|
||||
|
||||
getAddress() {
|
||||
// TODO: derive from hierarchy, return next free address
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://github.com/bitcoinjs/bitcoinjs-lib/issues/584
|
||||
* @see https://github.com/bitcoinjs/bitcoinjs-lib/issues/914
|
||||
@ -59,4 +55,28 @@ export class HDLegacyBreadwalletWallet extends HDSegwitP2SHWallet {
|
||||
|
||||
return child.getAddress();
|
||||
}
|
||||
|
||||
_getExternalWIFByIndex(index) {
|
||||
index = index * 1; // cast to int
|
||||
let mnemonic = this.secret;
|
||||
let seed = bip39.mnemonicToSeed(mnemonic);
|
||||
let root = bitcoin.HDNode.fromSeedBuffer(seed);
|
||||
let path = "m/0'/1/" + index;
|
||||
let child = root.derivePath(path);
|
||||
return child.keyPair.toWIF();
|
||||
}
|
||||
|
||||
_getInternalWIFByIndex(index) {
|
||||
index = index * 1; // cast to int
|
||||
let mnemonic = this.secret;
|
||||
let seed = bip39.mnemonicToSeed(mnemonic);
|
||||
let root = bitcoin.HDNode.fromSeedBuffer(seed);
|
||||
let path = "m/0'/1/" + index;
|
||||
let child = root.derivePath(path);
|
||||
return child.keyPair.toWIF();
|
||||
}
|
||||
|
||||
getAddressAsync() {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
104
class/hd-legacy-p2pkh-wallet.js
Normal file
104
class/hd-legacy-p2pkh-wallet.js
Normal file
@ -0,0 +1,104 @@
|
||||
import { LegacyWallet, HDSegwitP2SHWallet } from './';
|
||||
const bitcoin = require('bitcoinjs-lib');
|
||||
const bip39 = require('bip39');
|
||||
|
||||
/**
|
||||
* HD Wallet (BIP39).
|
||||
* In particular, BIP44 (P2PKH legacy addressess)
|
||||
* @see https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
|
||||
*/
|
||||
export class HDLegacyP2PKHWallet extends HDSegwitP2SHWallet {
|
||||
constructor() {
|
||||
super();
|
||||
this.type = 'HDlegacyP2PKH';
|
||||
}
|
||||
|
||||
getTypeReadable() {
|
||||
return 'HD Legacy (BIP44 P2PKH)';
|
||||
}
|
||||
|
||||
getXpub() {
|
||||
let mnemonic = this.secret;
|
||||
let seed = bip39.mnemonicToSeed(mnemonic);
|
||||
let root = bitcoin.HDNode.fromSeedBuffer(seed);
|
||||
|
||||
let path = "m/44'/0'/0'";
|
||||
let child = root.derivePath(path).neutered();
|
||||
return child.toBase58();
|
||||
}
|
||||
|
||||
_getExternalWIFByIndex(index) {
|
||||
index = index * 1; // cast to int
|
||||
let mnemonic = this.secret;
|
||||
let seed = bip39.mnemonicToSeed(mnemonic);
|
||||
let root = bitcoin.HDNode.fromSeedBuffer(seed);
|
||||
let path = "m/44'/0'/0'/0/" + index;
|
||||
let child = root.derivePath(path);
|
||||
return child.keyPair.toWIF();
|
||||
}
|
||||
|
||||
_getInternalWIFByIndex(index) {
|
||||
index = index * 1; // cast to int
|
||||
let mnemonic = this.secret;
|
||||
let seed = bip39.mnemonicToSeed(mnemonic);
|
||||
let root = bitcoin.HDNode.fromSeedBuffer(seed);
|
||||
let path = "m/44'/0'/0'/1/" + index;
|
||||
let child = root.derivePath(path);
|
||||
return child.keyPair.toWIF();
|
||||
}
|
||||
|
||||
_getExternalAddressByIndex(index) {
|
||||
index = index * 1; // cast to int
|
||||
let mnemonic = this.secret;
|
||||
let seed = bip39.mnemonicToSeed(mnemonic);
|
||||
let root = bitcoin.HDNode.fromSeedBuffer(seed);
|
||||
let path = "m/44'/0'/0'/0/" + index;
|
||||
let child = root.derivePath(path);
|
||||
|
||||
let w = new LegacyWallet();
|
||||
w.setSecret(child.keyPair.toWIF());
|
||||
return w.getAddress();
|
||||
}
|
||||
|
||||
_getInternalAddressByIndex(index) {
|
||||
index = index * 1; // cast to int
|
||||
let mnemonic = this.secret;
|
||||
let seed = bip39.mnemonicToSeed(mnemonic);
|
||||
let root = bitcoin.HDNode.fromSeedBuffer(seed);
|
||||
|
||||
let path = "m/44'/0'/0'/1/" + index;
|
||||
let child = root.derivePath(path);
|
||||
|
||||
let w = new LegacyWallet();
|
||||
w.setSecret(child.keyPair.toWIF());
|
||||
return w.getAddress();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
async getAddressAsync() {
|
||||
// looking for free external address
|
||||
let freeAddress = '';
|
||||
let c;
|
||||
for (c = -1; c < 5; c++) {
|
||||
let Legacy = new LegacyWallet();
|
||||
Legacy.setSecret(this._getExternalWIFByIndex(this.next_free_address_index + c));
|
||||
await Legacy.fetchTransactions();
|
||||
if (Legacy.transactions.length === 0) {
|
||||
// found free address
|
||||
freeAddress = Legacy.getAddress();
|
||||
this.next_free_address_index += c + 1; // now points to the one _after_
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!freeAddress) {
|
||||
// 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
|
||||
this.next_free_address_index += c + 1; // now points to the one _after_
|
||||
}
|
||||
|
||||
return freeAddress;
|
||||
}
|
||||
}
|
@ -3,10 +3,12 @@ import { SegwitP2SHWallet } from './segwit-p2sh-wallet';
|
||||
import Frisbee from 'frisbee';
|
||||
const bitcoin = require('bitcoinjs-lib');
|
||||
const bip39 = require('bip39');
|
||||
const BigNumber = require('bignumber.js');
|
||||
|
||||
/**
|
||||
* HD Wallet (BIP39).
|
||||
* In particular, BIP49 (P2SH Segwit) https://github.com/bitcoin/bips/blob/master/bip-0049.mediawiki
|
||||
* In particular, BIP49 (P2SH Segwit)
|
||||
* @see https://github.com/bitcoin/bips/blob/master/bip-0049.mediawiki
|
||||
*/
|
||||
export class HDSegwitP2SHWallet extends LegacyWallet {
|
||||
constructor() {
|
||||
@ -18,18 +20,22 @@ export class HDSegwitP2SHWallet extends LegacyWallet {
|
||||
this.external_addresses_cache = {}; // index => address
|
||||
}
|
||||
|
||||
allowSend() {
|
||||
return false; // TODO send from HD
|
||||
}
|
||||
|
||||
validateMnemonic() {
|
||||
return bip39.validateMnemonic(this.secret);
|
||||
}
|
||||
|
||||
getTypeReadable() {
|
||||
return 'HD SegWit (P2SH)';
|
||||
return 'HD SegWit (BIP49 P2SH)';
|
||||
}
|
||||
|
||||
/**
|
||||
* Derives from hierarchy, returns next free address
|
||||
* (the one that has no transactions). Looks for several,
|
||||
* gve ups if none found, and returns the used one
|
||||
* gives up if none found, and returns the used one
|
||||
*
|
||||
* @return {Promise.<string>}
|
||||
*/
|
||||
@ -39,9 +45,7 @@ export class HDSegwitP2SHWallet extends LegacyWallet {
|
||||
let c;
|
||||
for (c = -1; c < 5; c++) {
|
||||
let Segwit = new SegwitP2SHWallet();
|
||||
Segwit.setSecret(
|
||||
this._getExternalWIFByIndex(this.next_free_address_index + c),
|
||||
);
|
||||
Segwit.setSecret(this._getExternalWIFByIndex(this.next_free_address_index + c));
|
||||
await Segwit.fetchTransactions();
|
||||
if (Segwit.transactions.length === 0) {
|
||||
// found free address
|
||||
@ -53,9 +57,7 @@ export class HDSegwitP2SHWallet extends LegacyWallet {
|
||||
|
||||
if (!freeAddress) {
|
||||
// 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_
|
||||
}
|
||||
|
||||
@ -94,12 +96,7 @@ export class HDSegwitP2SHWallet extends LegacyWallet {
|
||||
let scriptSig = bitcoin.script.witnessPubKeyHash.output.encode(keyhash);
|
||||
let addressBytes = bitcoin.crypto.hash160(scriptSig);
|
||||
let outputScript = bitcoin.script.scriptHash.output.encode(addressBytes);
|
||||
let address = bitcoin.address.fromOutputScript(
|
||||
outputScript,
|
||||
bitcoin.networks.bitcoin,
|
||||
);
|
||||
|
||||
return address;
|
||||
return bitcoin.address.fromOutputScript(outputScript, bitcoin.networks.bitcoin);
|
||||
}
|
||||
|
||||
_getInternalAddressByIndex(index) {
|
||||
@ -115,10 +112,17 @@ export class HDSegwitP2SHWallet extends LegacyWallet {
|
||||
let scriptSig = bitcoin.script.witnessPubKeyHash.output.encode(keyhash);
|
||||
let addressBytes = bitcoin.crypto.hash160(scriptSig);
|
||||
let outputScript = bitcoin.script.scriptHash.output.encode(addressBytes);
|
||||
return bitcoin.address.fromOutputScript(
|
||||
outputScript,
|
||||
bitcoin.networks.bitcoin,
|
||||
);
|
||||
return bitcoin.address.fromOutputScript(outputScript, bitcoin.networks.bitcoin);
|
||||
}
|
||||
|
||||
getXpub() {
|
||||
let mnemonic = this.secret;
|
||||
let seed = bip39.mnemonicToSeed(mnemonic);
|
||||
let root = bitcoin.HDNode.fromSeedBuffer(seed);
|
||||
|
||||
let path = "m/49'/0'/0'";
|
||||
let child = root.derivePath(path).neutered();
|
||||
return child.toBase58();
|
||||
}
|
||||
|
||||
async fetchBalance() {
|
||||
@ -135,15 +139,21 @@ export class HDSegwitP2SHWallet extends LegacyWallet {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Async function to fetch all transactions. Use getter to get actual txs.
|
||||
* Also, sets internals:
|
||||
* `this.internal_addresses_cache`
|
||||
* `this.external_addresses_cache`
|
||||
*
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async fetchTransactions() {
|
||||
const api = new Frisbee({ baseURI: 'https://blockchain.info' });
|
||||
this.transactions = [];
|
||||
let offset = 0;
|
||||
|
||||
while (1) {
|
||||
let response = await api.get(
|
||||
'/multiaddr?active=' + this.getXpub() + '&n=100&offset=' + offset,
|
||||
);
|
||||
let response = await api.get('/multiaddr?active=' + this.getXpub() + '&n=100&offset=' + offset);
|
||||
|
||||
if (response && response.body) {
|
||||
if (response.body.txs && response.body.txs.length === 0) {
|
||||
@ -165,18 +175,12 @@ export class HDSegwitP2SHWallet extends LegacyWallet {
|
||||
let path = input.prev_out.xpub.path.split('/');
|
||||
if (path[path.length - 2] === '1') {
|
||||
// change address
|
||||
this.next_free_change_address_index = Math.max(
|
||||
path[path.length - 1] * 1 + 1,
|
||||
this.next_free_change_address_index,
|
||||
);
|
||||
this.next_free_change_address_index = Math.max(path[path.length - 1] * 1 + 1, this.next_free_change_address_index);
|
||||
// setting to point to last maximum known change address + 1
|
||||
}
|
||||
if (path[path.length - 2] === '0') {
|
||||
// main (aka external) address
|
||||
this.next_free_address_index = Math.max(
|
||||
path[path.length - 1] * 1 + 1,
|
||||
this.next_free_address_index,
|
||||
);
|
||||
this.next_free_address_index = Math.max(path[path.length - 1] * 1 + 1, this.next_free_address_index);
|
||||
// setting to point to last maximum known main address + 1
|
||||
}
|
||||
// done with cache
|
||||
@ -193,25 +197,19 @@ export class HDSegwitP2SHWallet extends LegacyWallet {
|
||||
let path = output.xpub.path.split('/');
|
||||
if (path[path.length - 2] === '1') {
|
||||
// change address
|
||||
this.next_free_change_address_index = Math.max(
|
||||
path[path.length - 1] * 1 + 1,
|
||||
this.next_free_change_address_index,
|
||||
);
|
||||
this.next_free_change_address_index = Math.max(path[path.length - 1] * 1 + 1, this.next_free_change_address_index);
|
||||
// setting to point to last maximum known change address + 1
|
||||
}
|
||||
if (path[path.length - 2] === '0') {
|
||||
// main (aka external) address
|
||||
this.next_free_address_index = Math.max(
|
||||
path[path.length - 1] * 1 + 1,
|
||||
this.next_free_address_index,
|
||||
);
|
||||
this.next_free_address_index = Math.max(path[path.length - 1] * 1 + 1, this.next_free_address_index);
|
||||
// setting to point to last maximum known main address + 1
|
||||
}
|
||||
// done with cache
|
||||
}
|
||||
}
|
||||
|
||||
tx.value = value / 100000000;
|
||||
tx.value = new BigNumber(value).div(100000000).toString() * 1;
|
||||
|
||||
this.transactions.push(tx);
|
||||
}
|
||||
@ -219,7 +217,7 @@ export class HDSegwitP2SHWallet extends LegacyWallet {
|
||||
break; // error ?
|
||||
}
|
||||
} else {
|
||||
throw new Error('Could not fetch balance from API'); // breaks here
|
||||
throw new Error('Could not fetch transactions from API'); // breaks here
|
||||
}
|
||||
|
||||
offset += 100;
|
||||
|
@ -6,3 +6,4 @@ export * from './segwit-bech-wallet';
|
||||
export * from './segwit-p2sh-wallet';
|
||||
export * from './hd-segwit-p2sh-wallet';
|
||||
export * from './hd-legacy-breadwallet-wallet';
|
||||
export * from './hd-legacy-p2pkh-wallet';
|
||||
|
@ -83,25 +83,17 @@ export class LegacyWallet extends AbstractWallet {
|
||||
});
|
||||
|
||||
let response = await api.get(
|
||||
this.getAddress() +
|
||||
'/balance' +
|
||||
((useBlockcypherTokens &&
|
||||
'&token=' + this.getRandomBlockcypherToken()) ||
|
||||
''),
|
||||
this.getAddress() + '/balance' + ((useBlockcypherTokens && '&token=' + this.getRandomBlockcypherToken()) || ''),
|
||||
);
|
||||
let json = response.body;
|
||||
if (
|
||||
typeof json === 'undefined' ||
|
||||
typeof json.final_balance === 'undefined'
|
||||
) {
|
||||
if (typeof json === 'undefined' || typeof json.final_balance === 'undefined') {
|
||||
throw new Error('Could not fetch UTXO from API' + response.err);
|
||||
}
|
||||
|
||||
this.balance = new BigNumber(json.final_balance);
|
||||
this.balance = this.balance.div(100000000).toString() * 1;
|
||||
this.unconfirmed_balance = new BigNumber(json.unconfirmed_balance);
|
||||
this.unconfirmed_balance =
|
||||
this.unconfirmed_balance.div(100000000).toString() * 1;
|
||||
this.unconfirmed_balance = this.unconfirmed_balance.div(100000000).toString() * 1;
|
||||
this._lastBalanceFetch = +new Date();
|
||||
} catch (err) {
|
||||
console.warn(err);
|
||||
@ -129,15 +121,10 @@ export class LegacyWallet extends AbstractWallet {
|
||||
this.getAddress() +
|
||||
'?limit=2000&after=' +
|
||||
maxHeight +
|
||||
((useBlockcypherTokens &&
|
||||
'&token=' + this.getRandomBlockcypherToken()) ||
|
||||
''),
|
||||
((useBlockcypherTokens && '&token=' + this.getRandomBlockcypherToken()) || ''),
|
||||
);
|
||||
json = response.body;
|
||||
if (
|
||||
typeof json === 'undefined' ||
|
||||
typeof json.final_balance === 'undefined'
|
||||
) {
|
||||
if (typeof json === 'undefined' || typeof json.final_balance === 'undefined') {
|
||||
throw new Error('Could not fetch UTXO from API' + response.err);
|
||||
}
|
||||
json.txrefs = json.txrefs || []; // case when source address is empty (or maxheight too high, no txs)
|
||||
@ -169,19 +156,10 @@ export class LegacyWallet extends AbstractWallet {
|
||||
let url;
|
||||
if (useBlockcypherTokens) {
|
||||
response = await fetch(
|
||||
(url =
|
||||
'https://api.blockcypher.com/v1/btc/main/addrs/' +
|
||||
this.getAddress() +
|
||||
'/full?token=' +
|
||||
this.getRandomBlockcypherToken()),
|
||||
(url = 'https://api.blockcypher.com/v1/btc/main/addrs/' + this.getAddress() + '/full?token=' + this.getRandomBlockcypherToken()),
|
||||
);
|
||||
} else {
|
||||
response = await fetch(
|
||||
(url =
|
||||
'https://api.blockcypher.com/v1/btc/main/addrs/' +
|
||||
this.getAddress() +
|
||||
'/full'),
|
||||
);
|
||||
response = await fetch((url = 'https://api.blockcypher.com/v1/btc/main/addrs/' + this.getAddress() + '/full'));
|
||||
}
|
||||
console.log(url);
|
||||
let json = await response.json();
|
||||
@ -369,25 +347,9 @@ export class LegacyWallet extends AbstractWallet {
|
||||
u.amount = u.amount.div(100000000);
|
||||
u.amount = u.amount.toString(10);
|
||||
}
|
||||
console.log(
|
||||
'creating legacy tx ',
|
||||
amount,
|
||||
' with fee ',
|
||||
fee,
|
||||
'secret=',
|
||||
this.getSecret(),
|
||||
'from address',
|
||||
this.getAddress(),
|
||||
);
|
||||
console.log('creating legacy tx ', amount, ' with fee ', fee, 'secret=', this.getSecret(), 'from address', this.getAddress());
|
||||
let amountPlusFee = parseFloat(new BigNumber(amount).add(fee).toString(10));
|
||||
return signer.createTransaction(
|
||||
utxos,
|
||||
toAddress,
|
||||
amountPlusFee,
|
||||
fee,
|
||||
this.getSecret(),
|
||||
this.getAddress(),
|
||||
);
|
||||
return signer.createTransaction(utxos, toAddress, amountPlusFee, fee, this.getSecret(), this.getAddress());
|
||||
}
|
||||
|
||||
getLatestTransactionTime() {
|
||||
|
@ -17,9 +17,7 @@ export class SegwitBech32Wallet extends LegacyWallet {
|
||||
try {
|
||||
let keyPair = bitcoin.ECPair.fromWIF(this.secret);
|
||||
let pubKey = keyPair.getPublicKeyBuffer();
|
||||
let scriptPubKey = bitcoin.script.witnessPubKeyHash.output.encode(
|
||||
bitcoin.crypto.hash160(pubKey),
|
||||
);
|
||||
let scriptPubKey = bitcoin.script.witnessPubKeyHash.output.encode(bitcoin.crypto.hash160(pubKey));
|
||||
address = bitcoin.address.fromOutputScript(scriptPubKey);
|
||||
} catch (err) {
|
||||
return false;
|
||||
@ -32,12 +30,7 @@ export class SegwitBech32Wallet extends LegacyWallet {
|
||||
static witnessToAddress(witness) {
|
||||
const pubKey = Buffer.from(witness, 'hex');
|
||||
const pubKeyHash = bitcoin.crypto.hash160(pubKey);
|
||||
const scriptPubKey = bitcoin.script.witnessPubKeyHash.output.encode(
|
||||
pubKeyHash,
|
||||
);
|
||||
return bitcoin.address.fromOutputScript(
|
||||
scriptPubKey,
|
||||
bitcoin.networks.bitcoin,
|
||||
);
|
||||
const scriptPubKey = bitcoin.script.witnessPubKeyHash.output.encode(pubKeyHash);
|
||||
return bitcoin.address.fromOutputScript(scriptPubKey, bitcoin.networks.bitcoin);
|
||||
}
|
||||
}
|
||||
|
@ -16,17 +16,10 @@ export class SegwitP2SHWallet extends LegacyWallet {
|
||||
static witnessToAddress(witness) {
|
||||
const pubKey = Buffer.from(witness, 'hex');
|
||||
const pubKeyHash = bitcoin.crypto.hash160(pubKey);
|
||||
const redeemScript = bitcoin.script.witnessPubKeyHash.output.encode(
|
||||
pubKeyHash,
|
||||
);
|
||||
const redeemScript = bitcoin.script.witnessPubKeyHash.output.encode(pubKeyHash);
|
||||
const redeemScriptHash = bitcoin.crypto.hash160(redeemScript);
|
||||
const scriptPubkey = bitcoin.script.scriptHash.output.encode(
|
||||
redeemScriptHash,
|
||||
);
|
||||
return bitcoin.address.fromOutputScript(
|
||||
scriptPubkey,
|
||||
bitcoin.networks.bitcoin,
|
||||
);
|
||||
const scriptPubkey = bitcoin.script.scriptHash.output.encode(redeemScriptHash);
|
||||
return bitcoin.address.fromOutputScript(scriptPubkey, bitcoin.networks.bitcoin);
|
||||
}
|
||||
|
||||
getAddress() {
|
||||
@ -35,12 +28,8 @@ export class SegwitP2SHWallet extends LegacyWallet {
|
||||
try {
|
||||
let keyPair = bitcoin.ECPair.fromWIF(this.secret);
|
||||
let pubKey = keyPair.getPublicKeyBuffer();
|
||||
let witnessScript = bitcoin.script.witnessPubKeyHash.output.encode(
|
||||
bitcoin.crypto.hash160(pubKey),
|
||||
);
|
||||
let scriptPubKey = bitcoin.script.scriptHash.output.encode(
|
||||
bitcoin.crypto.hash160(witnessScript),
|
||||
);
|
||||
let witnessScript = bitcoin.script.witnessPubKeyHash.output.encode(bitcoin.crypto.hash160(pubKey));
|
||||
let scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(witnessScript));
|
||||
address = bitcoin.address.fromOutputScript(scriptPubKey);
|
||||
} catch (err) {
|
||||
return false;
|
||||
@ -76,26 +65,9 @@ export class SegwitP2SHWallet extends LegacyWallet {
|
||||
u.amount = u.amount.div(100000000);
|
||||
u.amount = u.amount.toString(10);
|
||||
}
|
||||
console.log(
|
||||
'creating tx ',
|
||||
amount,
|
||||
' with fee ',
|
||||
fee,
|
||||
'secret=',
|
||||
this.getSecret(),
|
||||
'from address',
|
||||
this.getAddress(),
|
||||
);
|
||||
console.log('creating tx ', amount, ' with fee ', fee, 'secret=', this.getSecret(), 'from address', this.getAddress());
|
||||
let amountPlusFee = parseFloat(new BigNumber(amount).add(fee).toString(10));
|
||||
// to compensate that module substracts fee from amount
|
||||
return signer.createSegwitTransaction(
|
||||
utxos,
|
||||
address,
|
||||
amountPlusFee,
|
||||
fee,
|
||||
this.getSecret(),
|
||||
this.getAddress(),
|
||||
sequence,
|
||||
);
|
||||
return signer.createSegwitTransaction(utxos, address, amountPlusFee, fee, this.getSecret(), this.getAddress(), sequence);
|
||||
}
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ async function updateExchangeRate() {
|
||||
console.log('updated currency exchange:', lang);
|
||||
}
|
||||
|
||||
async function startUpdater(doNotSetInterval) {
|
||||
async function startUpdater() {
|
||||
lang = await AsyncStorage.getItem(AppStorage.CURRENCY);
|
||||
try {
|
||||
lang = JSON.parse(lang);
|
||||
@ -52,9 +52,7 @@ async function startUpdater(doNotSetInterval) {
|
||||
lang[STRUCT.LAST_UPDATED] = lang[STRUCT.LAST_UPDATED] || 0;
|
||||
lang[STRUCT.BTC_USD] = lang[STRUCT.BTC_USD] || 6500;
|
||||
|
||||
if (!doNotSetInterval) {
|
||||
setInterval(() => updateExchangeRate(), 2 * 60 * 100);
|
||||
}
|
||||
return updateExchangeRate();
|
||||
}
|
||||
|
||||
|
10
events.js
10
events.js
@ -1,11 +1,6 @@
|
||||
function EV(eventName, arg) {
|
||||
if (Object.values(EV.enum).indexOf(eventName) === -1) {
|
||||
return console.warn(
|
||||
'Unregistered event',
|
||||
eventName,
|
||||
'registered events:',
|
||||
EV.enum,
|
||||
);
|
||||
return console.warn('Unregistered event', eventName, 'registered events:', EV.enum);
|
||||
}
|
||||
EV.callbacks = EV.callbacks || {}; // static variable
|
||||
EV.callbacks[eventName] = EV.callbacks[eventName] || [];
|
||||
@ -27,8 +22,7 @@ function EV(eventName, arg) {
|
||||
EV.enum = {
|
||||
WALLETS_COUNT_CHANGED: 'WALLETS_COUNT_CHANGED',
|
||||
TRANSACTIONS_COUNT_CHANGED: 'TRANSACTIONS_COUNT_CHANGED',
|
||||
CREATE_TRANSACTION_NEW_DESTINATION_ADDRESS:
|
||||
'CREATE_TRANSACTION_NEW_DESTINATION_ADDRESS',
|
||||
CREATE_TRANSACTION_NEW_DESTINATION_ADDRESS: 'CREATE_TRANSACTION_NEW_DESTINATION_ADDRESS',
|
||||
RECEIVE_ADDRESS_CHANGED: 'RECEIVE_ADDRESS_CHANGED',
|
||||
};
|
||||
|
||||
|
20
loc/en.js
20
loc/en.js
@ -1,7 +1,6 @@
|
||||
module.exports = {
|
||||
_: {
|
||||
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',
|
||||
enter_password: 'Enter password',
|
||||
bad_password: 'Bad pasword, try again',
|
||||
months_ago: 'months ago',
|
||||
@ -14,9 +13,7 @@ module.exports = {
|
||||
tabBarLabel: 'Wallets',
|
||||
app_name: 'Blue Wallet',
|
||||
title: 'wallets',
|
||||
header:
|
||||
'A wallet represents a pair of a secret (private key) and an address' +
|
||||
'you can share to receive coins.',
|
||||
header: 'A wallet represents a pair of a secret (private key) and an address' + 'you can share to receive coins.',
|
||||
add: 'Add Wallet',
|
||||
create_a_wallet: 'Create a wallet',
|
||||
create_a_wallet1: "It's free and you can create",
|
||||
@ -98,8 +95,7 @@ module.exports = {
|
||||
title: 'Create Transaction',
|
||||
error: 'Error creating transaction. Invalid address or send amount?',
|
||||
go_back: 'Go Back',
|
||||
this_is_hex:
|
||||
'This is transaction hex, signed and ready to be broadcast to the network. Continue?',
|
||||
this_is_hex: 'This is transaction hex, signed and ready to be broadcast to the network. Continue?',
|
||||
to: 'To',
|
||||
amount: 'Amount',
|
||||
fee: 'Fee',
|
||||
@ -141,16 +137,12 @@ module.exports = {
|
||||
"BlueWallet, it will unlock new 'fake' storage. This will seem " +
|
||||
'legit to a 3rd party, but will secretly keep your main storage ' +
|
||||
'with coins safe.',
|
||||
help2:
|
||||
'New storage will be fully functional, and you can store some ' +
|
||||
'minimum amounts there so it looks more believable.',
|
||||
help2: 'New storage will be fully functional, and you can store some ' + 'minimum amounts there so it looks more believable.',
|
||||
create_fake_storage: 'Create fake encrypted storage',
|
||||
go_back: 'Go Back',
|
||||
create_password: 'Create a password',
|
||||
create_password_explanation:
|
||||
'Password for fake storage should not match password for your main storage',
|
||||
password_should_not_match:
|
||||
'Password for fake storage should not match password for your main storage',
|
||||
create_password_explanation: 'Password for fake storage should not match password for your main storage',
|
||||
password_should_not_match: 'Password for fake storage should not match password for your main storage',
|
||||
retype_password: 'Retype password',
|
||||
passwords_do_not_match: 'Passwords do not match, try again',
|
||||
success: 'Success',
|
||||
|
16
loc/es.js
16
loc/es.js
@ -4,9 +4,7 @@ module.exports = {
|
||||
tabBarLabel: 'Monederos',
|
||||
app_name: 'Blue Wallet',
|
||||
title: 'Mi Monederos de Bitcoin',
|
||||
header:
|
||||
'Un Monedero esta representado con secreto (clave privada) y una dirección' +
|
||||
'que puedes compartir para recibir monedas.',
|
||||
header: 'Un Monedero esta representado con secreto (clave privada) y una dirección' + 'que puedes compartir para recibir monedas.',
|
||||
add: 'Añadir Monedero',
|
||||
},
|
||||
add: {
|
||||
@ -50,8 +48,7 @@ module.exports = {
|
||||
list: {
|
||||
tabBarLabel: 'Transacciónes',
|
||||
title: 'Mi Transacciónes',
|
||||
description:
|
||||
'Una lista de las transacciones entrantes o salientes de sus monederos',
|
||||
description: 'Una lista de las transacciones entrantes o salientes de sus monederos',
|
||||
conf: 'conf',
|
||||
},
|
||||
details: {
|
||||
@ -81,11 +78,9 @@ module.exports = {
|
||||
},
|
||||
create: {
|
||||
title: 'Crear una Transaccion',
|
||||
error:
|
||||
'Error al crear una transacción. ¿Dirección o cantidad estan invalidas?',
|
||||
error: 'Error al crear una transacción. ¿Dirección o cantidad estan invalidas?',
|
||||
go_back: 'Regresar',
|
||||
this_is_hex:
|
||||
'Este es representacion hex de transacción, firmado y listo para ser transmitido a la red. ¿Continuar?',
|
||||
this_is_hex: 'Este es representacion hex de transacción, firmado y listo para ser transmitido a la red. ¿Continuar?',
|
||||
to: 'A',
|
||||
amount: 'Cantidad',
|
||||
fee: 'Tasa',
|
||||
@ -110,8 +105,7 @@ module.exports = {
|
||||
plausible_deniability: 'Negación plausible...',
|
||||
storage_not_encrypted: 'Almacenamiento: no esta encriptado',
|
||||
password: 'Contraseña',
|
||||
password_explain:
|
||||
'Crea la contraseña que usarás para descifrar el almacenamiento',
|
||||
password_explain: 'Crea la contraseña que usarás para descifrar el almacenamiento',
|
||||
retype_password: 'Ingresa la contraseña de nuevo',
|
||||
passwords_do_not_match: 'Contraseñas deben ser iguales',
|
||||
encrypt_storage: 'Cifrar almacenamiento',
|
||||
|
@ -21,13 +21,7 @@ let strings;
|
||||
locale = locale.split('-');
|
||||
locale = locale[0];
|
||||
console.log('current locale:', locale);
|
||||
if (
|
||||
locale === 'en' ||
|
||||
locale === 'ru' ||
|
||||
locale === 'ua' ||
|
||||
locale === 'es' ||
|
||||
locale === 'pt'
|
||||
) {
|
||||
if (locale === 'en' || locale === 'ru' || locale === 'ua' || locale === 'es' || locale === 'pt') {
|
||||
strings.setLanguage(locale);
|
||||
} else {
|
||||
strings.setLanguage('en');
|
||||
|
23
loc/pt_BR.js
23
loc/pt_BR.js
@ -1,7 +1,6 @@
|
||||
module.exports = {
|
||||
_: {
|
||||
storage_is_encrypted:
|
||||
'O armazenamento está encriptado. Uma password é necessaria para desencriptar',
|
||||
storage_is_encrypted: 'O armazenamento está encriptado. Uma password é necessaria para desencriptar',
|
||||
enter_password: 'Inserir password',
|
||||
bad_password: 'pasword errada, tentar novamente',
|
||||
},
|
||||
@ -10,9 +9,7 @@ module.exports = {
|
||||
tabBarLabel: 'Wallets',
|
||||
app_name: 'Blue Wallet',
|
||||
title: 'Minhas Bitcoin Wallets',
|
||||
header:
|
||||
'Uma wallet representa um par entre um segredo (chave privada) e um endereço' +
|
||||
'que pode partilhar para receber Bitcoin.',
|
||||
header: 'Uma wallet representa um par entre um segredo (chave privada) e um endereço' + 'que pode partilhar para receber Bitcoin.',
|
||||
add: 'Adicionar Wallet',
|
||||
},
|
||||
add: {
|
||||
@ -56,8 +53,7 @@ module.exports = {
|
||||
list: {
|
||||
tabBarLabel: 'Transações',
|
||||
title: 'Minhas Transações',
|
||||
description:
|
||||
'Uma lista de transações feitas ou recebidas nas suas wallets',
|
||||
description: 'Uma lista de transações feitas ou recebidas nas suas wallets',
|
||||
conf: 'conf',
|
||||
},
|
||||
details: {
|
||||
@ -89,8 +85,7 @@ module.exports = {
|
||||
title: 'Criar Transacção',
|
||||
error: 'Erro ao criar transação. Endereço inválido ou quantia?',
|
||||
go_back: 'Voltar',
|
||||
this_is_hex:
|
||||
'Este é o hex da transação, assinado e pronto para ser difundido para a network. Continuar?',
|
||||
this_is_hex: 'Este é o hex da transação, assinado e pronto para ser difundido para a network. Continuar?',
|
||||
to: 'Para',
|
||||
amount: 'Quantia',
|
||||
fee: 'Taxa',
|
||||
@ -132,16 +127,12 @@ module.exports = {
|
||||
'BlueWallet, esta vai abrir um armazenamento "falso". Que vai parecer ' +
|
||||
'legítimo a um terceiro, mas que secretamente vai manter o seu armazenamento principal ' +
|
||||
'com as moedas em segurança.',
|
||||
help2:
|
||||
'Este novo armazenamento é completamente funcional, e pode guardar ' +
|
||||
'um valor minímo para parecer mais real.',
|
||||
help2: 'Este novo armazenamento é completamente funcional, e pode guardar ' + 'um valor minímo para parecer mais real.',
|
||||
create_fake_storage: 'Criar armazenamento encriptado FALSO',
|
||||
go_back: 'Voltar',
|
||||
create_password: 'Criar password',
|
||||
create_password_explanation:
|
||||
'Password para armazenamento FALSO não deve coincidir com o principal',
|
||||
password_should_not_match:
|
||||
'Password para armazenamento FALSO não deve coincidir com o principal',
|
||||
create_password_explanation: 'Password para armazenamento FALSO não deve coincidir com o principal',
|
||||
password_should_not_match: 'Password para armazenamento FALSO não deve coincidir com o principal',
|
||||
retype_password: 'Inserir password novamente',
|
||||
passwords_do_not_match: 'Passwords não coincidem, tente novamente',
|
||||
success: 'Sucesso',
|
||||
|
23
loc/pt_PT.js
23
loc/pt_PT.js
@ -1,7 +1,6 @@
|
||||
module.exports = {
|
||||
_: {
|
||||
storage_is_encrypted:
|
||||
'O armazenamento está encriptado. Uma password é necessaria para desencriptar',
|
||||
storage_is_encrypted: 'O armazenamento está encriptado. Uma password é necessaria para desencriptar',
|
||||
enter_password: 'Inserir password',
|
||||
bad_password: 'pasword errada, tente novamente',
|
||||
},
|
||||
@ -10,9 +9,7 @@ module.exports = {
|
||||
tabBarLabel: 'Wallets',
|
||||
app_name: 'Blue Wallet',
|
||||
title: 'Minhas Bitcoin Wallets',
|
||||
header:
|
||||
'Uma wallet representa um par entre um segredo (chave privada) e um endereço' +
|
||||
'que pode partilhar para receber Bitcoin.',
|
||||
header: 'Uma wallet representa um par entre um segredo (chave privada) e um endereço' + 'que pode partilhar para receber Bitcoin.',
|
||||
add: 'Adicionar Wallet',
|
||||
},
|
||||
add: {
|
||||
@ -56,8 +53,7 @@ module.exports = {
|
||||
list: {
|
||||
tabBarLabel: 'Transacções',
|
||||
title: 'Minhas Transacções',
|
||||
description:
|
||||
'Uma lista de transações feitas ou recebidas nas suas wallets',
|
||||
description: 'Uma lista de transações feitas ou recebidas nas suas wallets',
|
||||
conf: 'conf',
|
||||
},
|
||||
details: {
|
||||
@ -89,8 +85,7 @@ module.exports = {
|
||||
title: 'Criar Transacção',
|
||||
error: 'Erro ao criar transacção. Endereço inválido ou quantia?',
|
||||
go_back: 'Voltar',
|
||||
this_is_hex:
|
||||
'Este é o hex da transacção, assinado e pronto para ser difundido para a network. Continuar?',
|
||||
this_is_hex: 'Este é o hex da transacção, assinado e pronto para ser difundido para a network. Continuar?',
|
||||
to: 'Para',
|
||||
amount: 'Quantia',
|
||||
fee: 'Taxa',
|
||||
@ -132,16 +127,12 @@ module.exports = {
|
||||
'BlueWallet, esta vai abrir um armazenamento "falso". Que vai parecer ' +
|
||||
'legítimo a um terceiro, mas que secretamente vai manter o seu armazenamento principal ' +
|
||||
'com as moedas em segurança.',
|
||||
help2:
|
||||
'Este novo armazenamento é completamente funcional, e pode guardar ' +
|
||||
'um valor minímo para parecer mais real.',
|
||||
help2: 'Este novo armazenamento é completamente funcional, e pode guardar ' + 'um valor minímo para parecer mais real.',
|
||||
create_fake_storage: 'Criar armazenamento encriptado FALSO',
|
||||
go_back: 'Voltar',
|
||||
create_password: 'Criar password',
|
||||
create_password_explanation:
|
||||
'Password para armazenamento FALSO não deve coincidir com o principal',
|
||||
password_should_not_match:
|
||||
'Password para armazenamento FALSO não deve coincidir com o principal',
|
||||
create_password_explanation: 'Password para armazenamento FALSO não deve coincidir com o principal',
|
||||
password_should_not_match: 'Password para armazenamento FALSO não deve coincidir com o principal',
|
||||
retype_password: 'Inserir password novamente',
|
||||
passwords_do_not_match: 'Passwords não coincidem, tente novamente',
|
||||
success: 'Sucesso',
|
||||
|
18
loc/ru.js
18
loc/ru.js
@ -1,7 +1,6 @@
|
||||
module.exports = {
|
||||
_: {
|
||||
storage_is_encrypted:
|
||||
'Ваше хранилище зашифровано. Введите пароль для расшифровки',
|
||||
storage_is_encrypted: 'Ваше хранилище зашифровано. Введите пароль для расшифровки',
|
||||
enter_password: 'Введите пароль',
|
||||
bad_password: 'Неверный пароль, попробуйте еще раз',
|
||||
months_ago: 'месяцев назад',
|
||||
@ -14,8 +13,7 @@ module.exports = {
|
||||
tabBarLabel: 'Кошельки',
|
||||
app_name: 'BlueWallet',
|
||||
title: 'Мои Кошельки',
|
||||
header:
|
||||
'Кошелек это секретный (приватный) ключ, и соответствующий ему адрес на который можно получать биткоины',
|
||||
header: 'Кошелек это секретный (приватный) ключ, и соответствующий ему адрес на который можно получать биткоины',
|
||||
add: 'Добавить Кошелек',
|
||||
create_a_wallet: 'Создать кошелек',
|
||||
create_a_wallet1: 'Это бесплатно и вы можете создать',
|
||||
@ -95,11 +93,9 @@ module.exports = {
|
||||
},
|
||||
create: {
|
||||
title: 'Создать Транзакцию',
|
||||
error:
|
||||
'Ошибка при создании транзакции. Неправильный адрес назначения или недостаточно средств?',
|
||||
error: 'Ошибка при создании транзакции. Неправильный адрес назначения или недостаточно средств?',
|
||||
go_back: 'Назад',
|
||||
this_is_hex:
|
||||
'Это данные транзакции. Транзакция подписана и готова быть транслирована в сеть. Продолжить?',
|
||||
this_is_hex: 'Это данные транзакции. Транзакция подписана и готова быть транслирована в сеть. Продолжить?',
|
||||
to: 'Куда',
|
||||
amount: 'Сколько',
|
||||
fee: 'Комиссия',
|
||||
@ -147,10 +143,8 @@ module.exports = {
|
||||
create_fake_storage: 'Создать фальшивое хранилище',
|
||||
go_back: 'Назад',
|
||||
create_password: 'Придумайте пароль',
|
||||
create_password_explanation:
|
||||
'Пароль для фальшивого хранилища не должен быть таким же как основной пароль',
|
||||
password_should_not_match:
|
||||
'Пароль для фальшивого хранилища не должен быть таким же как основной пароль',
|
||||
create_password_explanation: 'Пароль для фальшивого хранилища не должен быть таким же как основной пароль',
|
||||
password_should_not_match: 'Пароль для фальшивого хранилища не должен быть таким же как основной пароль',
|
||||
retype_password: 'Наберите пароль повторно',
|
||||
passwords_do_not_match: 'Пароли не совпадают, попробуйте еще раз',
|
||||
success: 'Операция успешна',
|
||||
|
18
loc/ua.js
18
loc/ua.js
@ -1,7 +1,6 @@
|
||||
module.exports = {
|
||||
_: {
|
||||
storage_is_encrypted:
|
||||
'Ваше сховище зашифроване. Введіть пароль для розшифровки',
|
||||
storage_is_encrypted: 'Ваше сховище зашифроване. Введіть пароль для розшифровки',
|
||||
enter_password: 'Введіть пароль',
|
||||
bad_password: 'Невірний пароль, спробуйте ще раз',
|
||||
},
|
||||
@ -10,8 +9,7 @@ module.exports = {
|
||||
tabBarLabel: 'Гаманці',
|
||||
app_name: 'BlueWallet',
|
||||
title: 'Мої Біткоїн Гаманці',
|
||||
header:
|
||||
'Гаманець це секретний (приватний) ключ, і відповідна йому адреса на яку можна отримувати біткоїни',
|
||||
header: 'Гаманець це секретний (приватний) ключ, і відповідна йому адреса на яку можна отримувати біткоїни',
|
||||
add: 'Додати Гаманець',
|
||||
},
|
||||
add: {
|
||||
@ -85,11 +83,9 @@ module.exports = {
|
||||
},
|
||||
create: {
|
||||
title: 'Створити Транзакцію',
|
||||
error:
|
||||
'Помилка при створенні транзакції. Невiрна адреса призначення або недостатньо коштiв?',
|
||||
error: 'Помилка при створенні транзакції. Невiрна адреса призначення або недостатньо коштiв?',
|
||||
go_back: 'Назад',
|
||||
this_is_hex:
|
||||
'Це дані транзакції. Транзакція підписана і готова бути трансльована в мережу. Продовжити?',
|
||||
this_is_hex: 'Це дані транзакції. Транзакція підписана і готова бути трансльована в мережу. Продовжити?',
|
||||
to: 'Куди',
|
||||
amount: 'Скільки',
|
||||
fee: 'Комісія',
|
||||
@ -137,10 +133,8 @@ module.exports = {
|
||||
create_fake_storage: 'Створити фальшиве сховище',
|
||||
go_back: 'Назад',
|
||||
create_password: 'Придумайте пароль',
|
||||
create_password_explanation:
|
||||
'Пароль для фальшивого сховіща не має буті таким же як основній пароль',
|
||||
password_should_not_match:
|
||||
'Пароль для фальшивого сховища не має бути таким же як основний пароль',
|
||||
create_password_explanation: 'Пароль для фальшивого сховіща не має буті таким же як основній пароль',
|
||||
password_should_not_match: 'Пароль для фальшивого сховища не має бути таким же як основний пароль',
|
||||
retype_password: 'Наберіть пароль ще раз',
|
||||
passwords_do_not_match: 'Паролі не збігаються, спробуйте ще раз',
|
||||
success: 'Операція успішна',
|
||||
|
@ -9,15 +9,7 @@
|
||||
let bitcoinjs = require('bitcoinjs-lib');
|
||||
const toSatoshi = num => parseInt((num * 100000000).toFixed(0));
|
||||
|
||||
exports.createSegwitTransaction = function(
|
||||
utxos,
|
||||
toAddress,
|
||||
amount,
|
||||
fixedFee,
|
||||
WIF,
|
||||
changeAddress,
|
||||
sequence,
|
||||
) {
|
||||
exports.createSegwitTransaction = function(utxos, toAddress, amount, fixedFee, WIF, changeAddress, sequence) {
|
||||
changeAddress = changeAddress || exports.WIF2segwitAddress(WIF);
|
||||
if (sequence === undefined) {
|
||||
sequence = bitcoinjs.Transaction.DEFAULT_SEQUENCE;
|
||||
@ -27,9 +19,7 @@ exports.createSegwitTransaction = function(
|
||||
let keyPair = bitcoinjs.ECPair.fromWIF(WIF);
|
||||
let pubKey = keyPair.getPublicKeyBuffer();
|
||||
let pubKeyHash = bitcoinjs.crypto.hash160(pubKey);
|
||||
let redeemScript = bitcoinjs.script.witnessPubKeyHash.output.encode(
|
||||
pubKeyHash,
|
||||
);
|
||||
let redeemScript = bitcoinjs.script.witnessPubKeyHash.output.encode(pubKeyHash);
|
||||
|
||||
let txb = new bitcoinjs.TransactionBuilder();
|
||||
let unspentAmount = 0;
|
||||
@ -45,33 +35,18 @@ exports.createSegwitTransaction = function(
|
||||
txb.addOutput(toAddress, amountToOutput);
|
||||
if (amountToOutput + feeInSatoshis < unspentAmount) {
|
||||
// sending less than we have, so the rest should go back
|
||||
txb.addOutput(
|
||||
changeAddress,
|
||||
unspentAmount - amountToOutput - feeInSatoshis,
|
||||
);
|
||||
txb.addOutput(changeAddress, unspentAmount - amountToOutput - feeInSatoshis);
|
||||
}
|
||||
|
||||
for (let c = 0; c < utxos.length; c++) {
|
||||
txb.sign(
|
||||
c,
|
||||
keyPair,
|
||||
redeemScript,
|
||||
null,
|
||||
parseInt((utxos[c].amount * 100000000).toFixed(0)),
|
||||
);
|
||||
txb.sign(c, keyPair, redeemScript, null, parseInt((utxos[c].amount * 100000000).toFixed(0)));
|
||||
}
|
||||
|
||||
let tx = txb.build();
|
||||
return tx.toHex();
|
||||
};
|
||||
|
||||
exports.createRBFSegwitTransaction = function(
|
||||
txhex,
|
||||
addressReplaceMap,
|
||||
feeDelta,
|
||||
WIF,
|
||||
utxodata,
|
||||
) {
|
||||
exports.createRBFSegwitTransaction = function(txhex, addressReplaceMap, feeDelta, WIF, utxodata) {
|
||||
if (feeDelta < 0) {
|
||||
throw Error('replace-by-fee requires increased fee, not decreased');
|
||||
}
|
||||
@ -89,11 +64,7 @@ exports.createRBFSegwitTransaction = function(
|
||||
// creating TX
|
||||
let txb = new bitcoinjs.TransactionBuilder();
|
||||
for (let unspent of tx.ins) {
|
||||
txb.addInput(
|
||||
unspent.hash.reverse().toString('hex'),
|
||||
unspent.index,
|
||||
highestSequence + 1,
|
||||
);
|
||||
txb.addInput(unspent.hash.reverse().toString('hex'), unspent.index, highestSequence + 1);
|
||||
}
|
||||
|
||||
for (let o of tx.outs) {
|
||||
@ -113,9 +84,7 @@ exports.createRBFSegwitTransaction = function(
|
||||
let keyPair = bitcoinjs.ECPair.fromWIF(WIF);
|
||||
let pubKey = keyPair.getPublicKeyBuffer();
|
||||
let pubKeyHash = bitcoinjs.crypto.hash160(pubKey);
|
||||
let redeemScript = bitcoinjs.script.witnessPubKeyHash.output.encode(
|
||||
pubKeyHash,
|
||||
);
|
||||
let redeemScript = bitcoinjs.script.witnessPubKeyHash.output.encode(pubKeyHash);
|
||||
for (let c = 0; c < tx.ins.length; c++) {
|
||||
let txid = tx.ins[c].hash.reverse().toString('hex');
|
||||
let index = tx.ins[c].index;
|
||||
@ -131,12 +100,8 @@ exports.generateNewSegwitAddress = function() {
|
||||
let keyPair = bitcoinjs.ECPair.makeRandom();
|
||||
let pubKey = keyPair.getPublicKeyBuffer();
|
||||
|
||||
let witnessScript = bitcoinjs.script.witnessPubKeyHash.output.encode(
|
||||
bitcoinjs.crypto.hash160(pubKey),
|
||||
);
|
||||
let scriptPubKey = bitcoinjs.script.scriptHash.output.encode(
|
||||
bitcoinjs.crypto.hash160(witnessScript),
|
||||
);
|
||||
let witnessScript = bitcoinjs.script.witnessPubKeyHash.output.encode(bitcoinjs.crypto.hash160(pubKey));
|
||||
let scriptPubKey = bitcoinjs.script.scriptHash.output.encode(bitcoinjs.crypto.hash160(witnessScript));
|
||||
let address = bitcoinjs.address.fromOutputScript(scriptPubKey);
|
||||
|
||||
return {
|
||||
@ -163,23 +128,12 @@ exports.URI = function(paymentInfo) {
|
||||
exports.WIF2segwitAddress = function(WIF) {
|
||||
let keyPair = bitcoinjs.ECPair.fromWIF(WIF);
|
||||
let pubKey = keyPair.getPublicKeyBuffer();
|
||||
let witnessScript = bitcoinjs.script.witnessPubKeyHash.output.encode(
|
||||
bitcoinjs.crypto.hash160(pubKey),
|
||||
);
|
||||
let scriptPubKey = bitcoinjs.script.scriptHash.output.encode(
|
||||
bitcoinjs.crypto.hash160(witnessScript),
|
||||
);
|
||||
let witnessScript = bitcoinjs.script.witnessPubKeyHash.output.encode(bitcoinjs.crypto.hash160(pubKey));
|
||||
let scriptPubKey = bitcoinjs.script.scriptHash.output.encode(bitcoinjs.crypto.hash160(witnessScript));
|
||||
return bitcoinjs.address.fromOutputScript(scriptPubKey);
|
||||
};
|
||||
|
||||
exports.createTransaction = function(
|
||||
utxos,
|
||||
toAddress,
|
||||
_amount,
|
||||
_fixedFee,
|
||||
WIF,
|
||||
fromAddress,
|
||||
) {
|
||||
exports.createTransaction = function(utxos, toAddress, _amount, _fixedFee, WIF, fromAddress) {
|
||||
let fixedFee = toSatoshi(_fixedFee);
|
||||
let amountToOutput = toSatoshi(_amount - _fixedFee);
|
||||
let pk = bitcoinjs.ECPair.fromWIF(WIF); // eslint-disable-line new-cap
|
||||
|
@ -1,14 +1,6 @@
|
||||
import React, { Component } from 'react';
|
||||
import { ScrollView, Linking, Dimensions } from 'react-native';
|
||||
import {
|
||||
BlueLoading,
|
||||
BlueSpacing20,
|
||||
BlueButton,
|
||||
SafeBlueArea,
|
||||
BlueCard,
|
||||
BlueText,
|
||||
BlueHeaderDefaultSub,
|
||||
} from '../BlueComponents';
|
||||
import { BlueLoading, BlueSpacing20, BlueButton, SafeBlueArea, BlueCard, BlueText, BlueHeaderDefaultSub } from '../BlueComponents';
|
||||
import PropTypes from 'prop-types';
|
||||
/** @type {AppStorage} */
|
||||
let BlueApp = require('../BlueApp');
|
||||
@ -39,16 +31,11 @@ export default class About extends Component {
|
||||
|
||||
return (
|
||||
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={{ flex: 1 }}>
|
||||
<BlueHeaderDefaultSub
|
||||
leftText={'about'}
|
||||
onClose={() => this.props.navigation.goBack()}
|
||||
/>
|
||||
<BlueHeaderDefaultSub leftText={'about'} onClose={() => this.props.navigation.goBack()} />
|
||||
|
||||
<BlueCard>
|
||||
<ScrollView maxHeight={height - 150}>
|
||||
<BlueText h4>
|
||||
BlueWallet is free and opensource Bitcoin wallet. Licensed MIT.
|
||||
</BlueText>
|
||||
<BlueText h4>BlueWallet is free and opensource Bitcoin wallet. Licensed MIT.</BlueText>
|
||||
|
||||
<BlueButton
|
||||
icon={{
|
||||
@ -81,9 +68,7 @@ export default class About extends Component {
|
||||
color: BlueApp.settings.buttonTextColor,
|
||||
}}
|
||||
onPress={() => {
|
||||
Linking.openURL(
|
||||
'https://itunes.apple.com/us/app/bluewallet-bitcoin-wallet/id1376878040?l=ru&ls=1&mt=8',
|
||||
);
|
||||
Linking.openURL('https://itunes.apple.com/us/app/bluewallet-bitcoin-wallet/id1376878040?l=ru&ls=1&mt=8');
|
||||
}}
|
||||
title="Leave us a review on Appstore"
|
||||
/>
|
||||
|
@ -2,14 +2,7 @@
|
||||
import React, { Component } from 'react';
|
||||
import { ScrollView } from 'react-native';
|
||||
import Ionicons from 'react-native-vector-icons/Ionicons';
|
||||
import {
|
||||
BlueLoading,
|
||||
BlueButton,
|
||||
SafeBlueArea,
|
||||
BlueCard,
|
||||
BlueText,
|
||||
BlueHeader,
|
||||
} from '../BlueComponents';
|
||||
import { BlueLoading, BlueButton, SafeBlueArea, BlueCard, BlueText, BlueHeader } from '../BlueComponents';
|
||||
import PropTypes from 'prop-types';
|
||||
/** @type {AppStorage} */
|
||||
let BlueApp = require('../BlueApp');
|
||||
@ -21,11 +14,7 @@ export default class PlausibleDeniability extends Component {
|
||||
static navigationOptions = {
|
||||
tabBarLabel: loc.plausibledeniability.title,
|
||||
tabBarIcon: ({ tintColor, focused }) => (
|
||||
<Ionicons
|
||||
name={focused ? 'ios-settings' : 'ios-settings-outline'}
|
||||
size={26}
|
||||
style={{ color: tintColor }}
|
||||
/>
|
||||
<Ionicons name={focused ? 'ios-settings' : 'ios-settings-outline'} size={26} style={{ color: tintColor }} />
|
||||
),
|
||||
};
|
||||
|
||||
@ -73,14 +62,9 @@ export default class PlausibleDeniability extends Component {
|
||||
}}
|
||||
title={loc.plausibledeniability.create_fake_storage}
|
||||
onPress={async () => {
|
||||
let p1 = await prompt(
|
||||
loc.plausibledeniability.create_password,
|
||||
loc.plausibledeniability.create_password_explanation,
|
||||
);
|
||||
let p1 = await prompt(loc.plausibledeniability.create_password, loc.plausibledeniability.create_password_explanation);
|
||||
if (p1 === BlueApp.cachedPassword) {
|
||||
return alert(
|
||||
loc.plausibledeniability.password_should_not_match,
|
||||
);
|
||||
return alert(loc.plausibledeniability.password_should_not_match);
|
||||
}
|
||||
|
||||
if (!p1) {
|
||||
|
@ -70,10 +70,7 @@ export default class ReceiveDetails extends Component {
|
||||
return <BlueSpacing />;
|
||||
}
|
||||
})()}
|
||||
<BlueHeaderDefaultSub
|
||||
leftText={loc.receive.list.header}
|
||||
onClose={() => this.props.navigation.goBack()}
|
||||
/>
|
||||
<BlueHeaderDefaultSub leftText={loc.receive.list.header} onClose={() => this.props.navigation.goBack()} />
|
||||
|
||||
<View
|
||||
style={{
|
||||
|
@ -1,13 +1,7 @@
|
||||
import React, { Component } from 'react';
|
||||
import { ListView, Dimensions } from 'react-native';
|
||||
import Ionicons from 'react-native-vector-icons/Ionicons';
|
||||
import {
|
||||
BlueLoading,
|
||||
SafeBlueArea,
|
||||
BlueCard,
|
||||
BlueListItem,
|
||||
BlueHeader,
|
||||
} from '../../BlueComponents';
|
||||
import { BlueLoading, SafeBlueArea, BlueCard, BlueListItem, BlueHeader } from '../../BlueComponents';
|
||||
import PropTypes from 'prop-types';
|
||||
/** @type {AppStorage} */
|
||||
let BlueApp = require('../../BlueApp');
|
||||
@ -20,11 +14,7 @@ export default class ReceiveList extends Component {
|
||||
static navigationOptions = {
|
||||
tabBarLabel: loc.receive.list.tabBarLabel,
|
||||
tabBarIcon: ({ tintColor, focused }) => (
|
||||
<Ionicons
|
||||
name={focused ? 'ios-cash' : 'ios-cash-outline'}
|
||||
size={26}
|
||||
style={{ color: tintColor }}
|
||||
/>
|
||||
<Ionicons name={focused ? 'ios-cash' : 'ios-cash-outline'} size={26} style={{ color: tintColor }} />
|
||||
),
|
||||
};
|
||||
|
||||
|
@ -1,15 +1,7 @@
|
||||
import React, { Component } from 'react';
|
||||
import { ScrollView, View } from 'react-native';
|
||||
import Ionicons from 'react-native-vector-icons/Ionicons';
|
||||
import {
|
||||
BlueLoading,
|
||||
BlueSpacing20,
|
||||
SafeBlueArea,
|
||||
BlueCard,
|
||||
BlueText,
|
||||
BlueButton,
|
||||
BlueHeader,
|
||||
} from '../BlueComponents';
|
||||
import { BlueLoading, BlueSpacing20, SafeBlueArea, BlueCard, BlueText, BlueButton, BlueHeader } from '../BlueComponents';
|
||||
import PropTypes from 'prop-types';
|
||||
import { SegwitP2SHWallet, LegacyWallet } from '../class';
|
||||
let BlueApp = require('../BlueApp');
|
||||
@ -20,11 +12,7 @@ export default class Selftest extends Component {
|
||||
static navigationOptions = {
|
||||
tabBarLabel: 'Self test',
|
||||
tabBarIcon: ({ tintColor, focused }) => (
|
||||
<Ionicons
|
||||
name={focused ? 'ios-settings' : 'ios-settings-outline'}
|
||||
size={26}
|
||||
style={{ color: tintColor }}
|
||||
/>
|
||||
<Ionicons name={focused ? 'ios-settings' : 'ios-settings-outline'} size={26} style={{ color: tintColor }} />
|
||||
),
|
||||
};
|
||||
|
||||
@ -65,8 +53,7 @@ export default class Selftest extends Component {
|
||||
// utxos as received from blockcypher
|
||||
let utxos = [
|
||||
{
|
||||
tx_hash:
|
||||
'2f445cf016fa2772db7d473bff97515355b4e6148e1c980ce351d47cf54c517f',
|
||||
tx_hash: '2f445cf016fa2772db7d473bff97515355b4e6148e1c980ce351d47cf54c517f',
|
||||
block_height: 523186,
|
||||
tx_input_n: -1,
|
||||
tx_output_n: 1,
|
||||
@ -101,12 +88,10 @@ export default class Selftest extends Component {
|
||||
double_spend: false,
|
||||
ref_balance: 300000,
|
||||
spent: false,
|
||||
tx_hash:
|
||||
'dc3605040a03724bc584ed43bc22a559f5d32a1b0708ca05b20b9018fdd523ef',
|
||||
tx_hash: 'dc3605040a03724bc584ed43bc22a559f5d32a1b0708ca05b20b9018fdd523ef',
|
||||
tx_input_n: -1,
|
||||
tx_output_n: 0,
|
||||
txid:
|
||||
'dc3605040a03724bc584ed43bc22a559f5d32a1b0708ca05b20b9018fdd523ef',
|
||||
txid: 'dc3605040a03724bc584ed43bc22a559f5d32a1b0708ca05b20b9018fdd523ef',
|
||||
value: 200000,
|
||||
vout: 0,
|
||||
},
|
||||
@ -118,12 +103,10 @@ export default class Selftest extends Component {
|
||||
double_spend: false,
|
||||
ref_balance: 100000,
|
||||
spent: false,
|
||||
tx_hash:
|
||||
'c473c104febfe6621804976d1082a1468c1198d0339e35f30a8ba1515d9eb017',
|
||||
tx_hash: 'c473c104febfe6621804976d1082a1468c1198d0339e35f30a8ba1515d9eb017',
|
||||
tx_input_n: -1,
|
||||
tx_output_n: 0,
|
||||
txid:
|
||||
'c473c104febfe6621804976d1082a1468c1198d0339e35f30a8ba1515d9eb017',
|
||||
txid: 'c473c104febfe6621804976d1082a1468c1198d0339e35f30a8ba1515d9eb017',
|
||||
value: 100000,
|
||||
vout: 0,
|
||||
},
|
||||
@ -160,8 +143,7 @@ export default class Selftest extends Component {
|
||||
// utxos as received from blockcypher
|
||||
let utxo = [
|
||||
{
|
||||
tx_hash:
|
||||
'0f5eea78fb19e72b55bd119252ff29fc16c503d0e956a9c1b5b2ab0e95e0c323',
|
||||
tx_hash: '0f5eea78fb19e72b55bd119252ff29fc16c503d0e956a9c1b5b2ab0e95e0c323',
|
||||
block_height: 514991,
|
||||
tx_input_n: -1,
|
||||
tx_output_n: 2,
|
||||
@ -174,20 +156,12 @@ export default class Selftest extends Component {
|
||||
},
|
||||
];
|
||||
|
||||
let tx = l.createTx(
|
||||
utxo,
|
||||
0.001,
|
||||
0.0001,
|
||||
'1QHf8Gp3wfmFiSdEX4FtrssCGR68diN1cj',
|
||||
);
|
||||
let tx = l.createTx(utxo, 0.001, 0.0001, '1QHf8Gp3wfmFiSdEX4FtrssCGR68diN1cj');
|
||||
let bitcoin = require('bitcoinjs-lib');
|
||||
let txDecoded = bitcoin.Transaction.fromHex(tx);
|
||||
let txid = txDecoded.getId();
|
||||
|
||||
if (
|
||||
txid !==
|
||||
'110f51d28d585e922adbf701cba802e549b8fe3a53fa5d62426ab42549c9b6de'
|
||||
) {
|
||||
if (txid !== '110f51d28d585e922adbf701cba802e549b8fe3a53fa5d62426ab42549c9b6de') {
|
||||
errorMessage += 'created txid doesnt match; ';
|
||||
isOk = false;
|
||||
}
|
||||
@ -234,10 +208,7 @@ export default class Selftest extends Component {
|
||||
let scriptSig = bitcoin.script.witnessPubKeyHash.output.encode(keyhash);
|
||||
let addressBytes = bitcoin.crypto.hash160(scriptSig);
|
||||
let outputScript = bitcoin.script.scriptHash.output.encode(addressBytes);
|
||||
let address = bitcoin.address.fromOutputScript(
|
||||
outputScript,
|
||||
bitcoin.networks.bitcoin,
|
||||
);
|
||||
let address = bitcoin.address.fromOutputScript(outputScript, bitcoin.networks.bitcoin);
|
||||
|
||||
if (address !== '3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK') {
|
||||
errorMessage += 'bip49 is not ok; ';
|
||||
|
@ -1,15 +1,7 @@
|
||||
import React, { Component } from 'react';
|
||||
import { TextInput } from 'react-native';
|
||||
import { Text, FormValidationMessage } from 'react-native-elements';
|
||||
import {
|
||||
BlueLoading,
|
||||
BlueSpacing20,
|
||||
BlueButton,
|
||||
SafeBlueArea,
|
||||
BlueCard,
|
||||
BlueText,
|
||||
BlueSpacing,
|
||||
} from '../../BlueComponents';
|
||||
import { BlueLoading, BlueSpacing20, BlueButton, SafeBlueArea, BlueCard, BlueText, BlueSpacing } from '../../BlueComponents';
|
||||
import PropTypes from 'prop-types';
|
||||
let BigNumber = require('bignumber.js');
|
||||
let BlueApp = require('../../BlueApp');
|
||||
@ -56,13 +48,7 @@ export default class SendCreate extends Component {
|
||||
utxo = this.state.fromWallet.utxo;
|
||||
let startTime = Date.now();
|
||||
|
||||
tx = this.state.fromWallet.createTx(
|
||||
utxo,
|
||||
this.state.amount,
|
||||
this.state.fee,
|
||||
this.state.address,
|
||||
this.state.memo,
|
||||
);
|
||||
tx = this.state.fromWallet.createTx(utxo, this.state.amount, this.state.fee, this.state.address, this.state.memo);
|
||||
let endTime = Date.now();
|
||||
console.log('create tx ', (endTime - startTime) / 1000, 'sec');
|
||||
|
||||
@ -113,8 +99,7 @@ export default class SendCreate extends Component {
|
||||
} else {
|
||||
this.setState({ broadcastErrorMessage: '' });
|
||||
this.setState({
|
||||
broadcastSuccessMessage:
|
||||
'Success! TXID: ' + JSON.stringify(result.result),
|
||||
broadcastSuccessMessage: 'Success! TXID: ' + JSON.stringify(result.result),
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -124,19 +109,11 @@ export default class SendCreate extends Component {
|
||||
return (
|
||||
<SafeBlueArea style={{ flex: 1, paddingTop: 20 }}>
|
||||
<BlueSpacing />
|
||||
<BlueCard
|
||||
title={loc.send.create.title}
|
||||
style={{ alignItems: 'center', flex: 1 }}
|
||||
>
|
||||
<BlueCard title={loc.send.create.title} style={{ alignItems: 'center', flex: 1 }}>
|
||||
<BlueText>{loc.send.create.error}</BlueText>
|
||||
<FormValidationMessage>
|
||||
{this.state.errorMessage}
|
||||
</FormValidationMessage>
|
||||
<FormValidationMessage>{this.state.errorMessage}</FormValidationMessage>
|
||||
</BlueCard>
|
||||
<BlueButton
|
||||
onPress={() => this.props.navigation.goBack()}
|
||||
title={loc.send.create.go_back}
|
||||
/>
|
||||
<BlueButton onPress={() => this.props.navigation.goBack()} title={loc.send.create.go_back} />
|
||||
</SafeBlueArea>
|
||||
);
|
||||
}
|
||||
@ -148,10 +125,7 @@ export default class SendCreate extends Component {
|
||||
return (
|
||||
<SafeBlueArea style={{ flex: 1, paddingTop: 20 }}>
|
||||
<BlueSpacing />
|
||||
<BlueCard
|
||||
title={loc.send.create.title}
|
||||
style={{ alignItems: 'center', flex: 1 }}
|
||||
>
|
||||
<BlueCard title={loc.send.create.title} style={{ alignItems: 'center', flex: 1 }}>
|
||||
<BlueText>{loc.send.create.this_is_hex}</BlueText>
|
||||
|
||||
<TextInput
|
||||
@ -182,8 +156,7 @@ export default class SendCreate extends Component {
|
||||
{loc.send.create.tx_size}: {this.state.size} Bytes
|
||||
</BlueText>
|
||||
<BlueText>
|
||||
{loc.send.create.satoshi_per_byte}: {this.state.satoshiPerByte}{' '}
|
||||
Sat/B
|
||||
{loc.send.create.satoshi_per_byte}: {this.state.satoshiPerByte} Sat/B
|
||||
</BlueText>
|
||||
<BlueText>
|
||||
{loc.send.create.memo}: {this.state.memo}
|
||||
@ -210,12 +183,8 @@ export default class SendCreate extends Component {
|
||||
title={loc.send.create.go_back}
|
||||
/>
|
||||
|
||||
<FormValidationMessage>
|
||||
{this.state.broadcastErrorMessage}
|
||||
</FormValidationMessage>
|
||||
<Text style={{ padding: 0, color: '#0f0' }}>
|
||||
{this.state.broadcastSuccessMessage}
|
||||
</Text>
|
||||
<FormValidationMessage>{this.state.broadcastErrorMessage}</FormValidationMessage>
|
||||
<Text style={{ padding: 0, color: '#0f0' }}>{this.state.broadcastSuccessMessage}</Text>
|
||||
</SafeBlueArea>
|
||||
);
|
||||
}
|
||||
|
@ -38,11 +38,9 @@ export default class SendDetails extends Component {
|
||||
super(props);
|
||||
let startTime = Date.now();
|
||||
let address;
|
||||
if (props.navigation.state.params)
|
||||
address = props.navigation.state.params.address;
|
||||
if (props.navigation.state.params) address = props.navigation.state.params.address;
|
||||
let fromAddress;
|
||||
if (props.navigation.state.params)
|
||||
fromAddress = props.navigation.state.params.fromAddress;
|
||||
if (props.navigation.state.params) fromAddress = props.navigation.state.params.fromAddress;
|
||||
let fromWallet = {};
|
||||
|
||||
let startTime2 = Date.now();
|
||||
@ -164,9 +162,7 @@ export default class SendDetails extends Component {
|
||||
if (!this.state.fromWallet.getAddress) {
|
||||
return (
|
||||
<View style={{ flex: 1, paddingTop: 20 }}>
|
||||
<Text>
|
||||
System error: Source wallet not found (this should never happen)
|
||||
</Text>
|
||||
<Text>System error: Source wallet not found (this should never happen)</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@ -180,10 +176,7 @@ export default class SendDetails extends Component {
|
||||
return <BlueSpacing />;
|
||||
}
|
||||
})()}
|
||||
<BlueCard
|
||||
title={loc.send.details.title}
|
||||
style={{ alignItems: 'center', flex: 1 }}
|
||||
>
|
||||
<BlueCard title={loc.send.details.title} style={{ alignItems: 'center', flex: 1 }}>
|
||||
<BlueFormInputAddress
|
||||
style={{ width: 250 }}
|
||||
onChangeText={text => this.setState({ address: text })}
|
||||
@ -192,18 +185,14 @@ export default class SendDetails extends Component {
|
||||
/>
|
||||
|
||||
<BlueFormInput
|
||||
onChangeText={text =>
|
||||
this.setState({ amount: text.replace(',', '.') })
|
||||
}
|
||||
onChangeText={text => this.setState({ amount: text.replace(',', '.') })}
|
||||
keyboardType={'numeric'}
|
||||
placeholder={loc.send.details.amount_placeholder}
|
||||
value={this.state.amount + ''}
|
||||
/>
|
||||
|
||||
<BlueFormInput
|
||||
onChangeText={text =>
|
||||
this.setState({ fee: text.replace(',', '.') })
|
||||
}
|
||||
onChangeText={text => this.setState({ fee: text.replace(',', '.') })}
|
||||
keyboardType={'numeric'}
|
||||
placeholder={loc.send.details.fee_placeholder}
|
||||
value={this.state.fee + ''}
|
||||
@ -218,12 +207,7 @@ export default class SendDetails extends Component {
|
||||
<BlueSpacing20 />
|
||||
<BlueText>
|
||||
{loc.send.details.remaining_balance}:{' '}
|
||||
{this.recalculateAvailableBalance(
|
||||
this.state.fromWallet.getBalance(),
|
||||
this.state.amount,
|
||||
this.state.fee,
|
||||
)}{' '}
|
||||
BTC
|
||||
{this.recalculateAvailableBalance(this.state.fromWallet.getBalance(), this.state.amount, this.state.fee)} BTC
|
||||
</BlueText>
|
||||
</BlueCard>
|
||||
|
||||
@ -231,10 +215,7 @@ export default class SendDetails extends Component {
|
||||
|
||||
<View style={{ flex: 1, flexDirection: 'row' }}>
|
||||
<View style={{ flex: 0.33 }}>
|
||||
<BlueButton
|
||||
onPress={() => this.props.navigation.goBack()}
|
||||
title={loc.send.details.cancel}
|
||||
/>
|
||||
<BlueButton onPress={() => this.props.navigation.goBack()} title={loc.send.details.cancel} />
|
||||
</View>
|
||||
<View style={{ flex: 0.33 }}>
|
||||
<BlueButton
|
||||
@ -249,10 +230,7 @@ export default class SendDetails extends Component {
|
||||
/>
|
||||
</View>
|
||||
<View style={{ flex: 0.33 }}>
|
||||
<BlueButton
|
||||
onPress={() => this.createTransaction()}
|
||||
title={loc.send.details.create}
|
||||
/>
|
||||
<BlueButton onPress={() => this.createTransaction()} title={loc.send.details.create} />
|
||||
</View>
|
||||
</View>
|
||||
</SafeBlueArea>
|
||||
|
@ -1,12 +1,7 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Dimensions, ActivityIndicator, View, ListView } from 'react-native';
|
||||
import Ionicons from 'react-native-vector-icons/Ionicons';
|
||||
import {
|
||||
SafeBlueArea,
|
||||
BlueCard,
|
||||
BlueListItem,
|
||||
BlueHeader,
|
||||
} from '../../BlueComponents';
|
||||
import { SafeBlueArea, BlueCard, BlueListItem, BlueHeader } from '../../BlueComponents';
|
||||
import PropTypes from 'prop-types';
|
||||
let EV = require('../../events');
|
||||
/** @type {AppStorage} */
|
||||
@ -19,11 +14,7 @@ export default class SendList extends Component {
|
||||
static navigationOptions = {
|
||||
tabBarLabel: loc.send.list.tabBarLabel,
|
||||
tabBarIcon: ({ tintColor, focused }) => (
|
||||
<Ionicons
|
||||
name={focused ? 'md-paper-plane' : 'md-paper-plane'}
|
||||
size={26}
|
||||
style={{ color: tintColor }}
|
||||
/>
|
||||
<Ionicons name={focused ? 'md-paper-plane' : 'md-paper-plane'} size={26} style={{ color: tintColor }} />
|
||||
),
|
||||
};
|
||||
|
||||
|
@ -1,12 +1,6 @@
|
||||
/* global alert */
|
||||
import React from 'react';
|
||||
import {
|
||||
Text,
|
||||
ActivityIndicator,
|
||||
Button,
|
||||
View,
|
||||
TouchableOpacity,
|
||||
} from 'react-native';
|
||||
import { Text, ActivityIndicator, Button, View, TouchableOpacity } from 'react-native';
|
||||
import { Camera, Permissions } from 'expo';
|
||||
import PropTypes from 'prop-types';
|
||||
let EV = require('../../events');
|
||||
@ -61,11 +55,7 @@ export default class CameraExample extends React.Component {
|
||||
} else {
|
||||
return (
|
||||
<View style={{ flex: 1 }}>
|
||||
<Camera
|
||||
style={{ flex: 1 }}
|
||||
type={this.state.type}
|
||||
onBarCodeRead={ret => this.onBarCodeRead(ret)}
|
||||
>
|
||||
<Camera style={{ flex: 1 }} type={this.state.type} onBarCodeRead={ret => this.onBarCodeRead(ret)}>
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
@ -81,18 +71,11 @@ export default class CameraExample extends React.Component {
|
||||
}}
|
||||
onPress={() => {
|
||||
this.setState({
|
||||
type:
|
||||
this.state.type === Camera.Constants.Type.back
|
||||
? Camera.Constants.Type.front
|
||||
: Camera.Constants.Type.back,
|
||||
type: this.state.type === Camera.Constants.Type.back ? Camera.Constants.Type.front : Camera.Constants.Type.back,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
style={{ fontSize: 18, marginBottom: 10 }}
|
||||
title="Go back"
|
||||
onPress={() => this.props.navigation.goBack()}
|
||||
/>
|
||||
<Button style={{ fontSize: 18, marginBottom: 10 }} title="Go back" onPress={() => this.props.navigation.goBack()} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</Camera>
|
||||
|
@ -2,14 +2,7 @@
|
||||
import React, { Component } from 'react';
|
||||
import { ScrollView, View, Picker } from 'react-native';
|
||||
import { FormValidationMessage } from 'react-native-elements';
|
||||
import {
|
||||
BlueLoading,
|
||||
BlueButton,
|
||||
SafeBlueArea,
|
||||
BlueCard,
|
||||
BlueText,
|
||||
BlueHeaderDefaultSub,
|
||||
} from '../BlueComponents';
|
||||
import { BlueLoading, BlueButton, SafeBlueArea, BlueCard, BlueText, BlueHeaderDefaultSub } from '../BlueComponents';
|
||||
import PropTypes from 'prop-types';
|
||||
/** @type {AppStorage} */
|
||||
let BlueApp = require('../BlueApp');
|
||||
@ -43,10 +36,7 @@ export default class Settings extends Component {
|
||||
|
||||
return (
|
||||
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={{ flex: 1 }}>
|
||||
<BlueHeaderDefaultSub
|
||||
leftText={loc.settings.header}
|
||||
onClose={() => this.props.navigation.goBack()}
|
||||
/>
|
||||
<BlueHeaderDefaultSub leftText={loc.settings.header} onClose={() => this.props.navigation.goBack()} />
|
||||
|
||||
<BlueCard>
|
||||
<ScrollView maxHeight={450}>
|
||||
@ -56,9 +46,7 @@ export default class Settings extends Component {
|
||||
<View>
|
||||
<BlueText>{loc.settings.storage_encrypted}</BlueText>
|
||||
<BlueButton
|
||||
onPress={() =>
|
||||
this.props.navigation.navigate('PlausibleDeniability')
|
||||
}
|
||||
onPress={() => this.props.navigation.navigate('PlausibleDeniability')}
|
||||
title={loc.settings.plausible_deniability}
|
||||
/>
|
||||
</View>
|
||||
@ -66,9 +54,7 @@ export default class Settings extends Component {
|
||||
} else {
|
||||
return (
|
||||
<View>
|
||||
<FormValidationMessage>
|
||||
{loc.settings.storage_not_encrypted}
|
||||
</FormValidationMessage>
|
||||
<FormValidationMessage>{loc.settings.storage_not_encrypted}</FormValidationMessage>
|
||||
<BlueButton
|
||||
icon={{
|
||||
name: 'shield',
|
||||
@ -77,18 +63,12 @@ export default class Settings extends Component {
|
||||
}}
|
||||
onPress={async () => {
|
||||
this.setState({ isLoading: true });
|
||||
let p1 = await prompt(
|
||||
loc.settings.password,
|
||||
loc.settings.password_explain,
|
||||
);
|
||||
let p1 = await prompt(loc.settings.password, loc.settings.password_explain);
|
||||
if (!p1) {
|
||||
this.setState({ isLoading: false });
|
||||
return;
|
||||
}
|
||||
let p2 = await prompt(
|
||||
loc.settings.password,
|
||||
loc.settings.retype_password,
|
||||
);
|
||||
let p2 = await prompt(loc.settings.password, loc.settings.retype_password);
|
||||
if (p1 === p2) {
|
||||
await BlueApp.encryptStorage(p1);
|
||||
this.setState({
|
||||
@ -107,10 +87,7 @@ export default class Settings extends Component {
|
||||
}
|
||||
})()}
|
||||
|
||||
<BlueButton
|
||||
onPress={() => this.props.navigation.navigate('About')}
|
||||
title={loc.settings.about}
|
||||
/>
|
||||
<BlueButton onPress={() => this.props.navigation.navigate('About')} title={loc.settings.about} />
|
||||
</ScrollView>
|
||||
|
||||
<Picker
|
||||
@ -122,31 +99,11 @@ export default class Settings extends Component {
|
||||
return this.setState({ language: itemValue });
|
||||
}}
|
||||
>
|
||||
<Picker.Item
|
||||
color={BlueApp.settings.foregroundColor}
|
||||
label="English"
|
||||
value="en"
|
||||
/>
|
||||
<Picker.Item
|
||||
color={BlueApp.settings.foregroundColor}
|
||||
label="Русский"
|
||||
value="ru"
|
||||
/>
|
||||
<Picker.Item
|
||||
color={BlueApp.settings.foregroundColor}
|
||||
label="Українська"
|
||||
value="ua"
|
||||
/>
|
||||
<Picker.Item
|
||||
color={BlueApp.settings.foregroundColor}
|
||||
label="Spanish"
|
||||
value="es"
|
||||
/>
|
||||
<Picker.Item
|
||||
color={BlueApp.settings.foregroundColor}
|
||||
label="Portuguese"
|
||||
value="pt"
|
||||
/>
|
||||
<Picker.Item color={BlueApp.settings.foregroundColor} label="English" value="en" />
|
||||
<Picker.Item color={BlueApp.settings.foregroundColor} label="Русский" value="ru" />
|
||||
<Picker.Item color={BlueApp.settings.foregroundColor} label="Українська" value="ua" />
|
||||
<Picker.Item color={BlueApp.settings.foregroundColor} label="Spanish" value="es" />
|
||||
<Picker.Item color={BlueApp.settings.foregroundColor} label="Portuguese" value="pt" />
|
||||
</Picker>
|
||||
</BlueCard>
|
||||
</SafeBlueArea>
|
||||
|
@ -2,15 +2,7 @@
|
||||
import React, { Component } from 'react';
|
||||
import { TextInput } from 'react-native';
|
||||
import { Text, FormValidationMessage } from 'react-native-elements';
|
||||
import {
|
||||
BlueLoading,
|
||||
BlueSpacing20,
|
||||
BlueButton,
|
||||
SafeBlueArea,
|
||||
BlueCard,
|
||||
BlueText,
|
||||
BlueSpacing,
|
||||
} from '../../BlueComponents';
|
||||
import { BlueLoading, BlueSpacing20, BlueButton, SafeBlueArea, BlueCard, BlueText, BlueSpacing } from '../../BlueComponents';
|
||||
import PropTypes from 'prop-types';
|
||||
let BigNumber = require('bignumber.js');
|
||||
let bitcoinjs = require('bitcoinjs-lib');
|
||||
@ -30,8 +22,7 @@ export default class SendCreate extends Component {
|
||||
this.state = {
|
||||
isLoading: true,
|
||||
feeDelta: props.navigation.state.params.feeDelta,
|
||||
newDestinationAddress:
|
||||
props.navigation.state.params.newDestinationAddress,
|
||||
newDestinationAddress: props.navigation.state.params.newDestinationAddress,
|
||||
txid: props.navigation.state.params.txid,
|
||||
sourceTx: props.navigation.state.params.sourceTx,
|
||||
fromWallet: props.navigation.state.params.sourceWallet,
|
||||
@ -90,9 +81,7 @@ export default class SendCreate extends Component {
|
||||
transferAmount = transferAmount.div(100000000).toString(10);
|
||||
}
|
||||
}
|
||||
let oldFee = new BigNumber(
|
||||
totalInputAmountSatoshi - totalOutputAmountSatoshi,
|
||||
);
|
||||
let oldFee = new BigNumber(totalInputAmountSatoshi - totalOutputAmountSatoshi);
|
||||
oldFee = parseFloat(oldFee.div(100000000).toString(10));
|
||||
|
||||
console.log('changeAddress = ', changeAddress);
|
||||
@ -113,14 +102,7 @@ export default class SendCreate extends Component {
|
||||
// more responsive
|
||||
let tx;
|
||||
try {
|
||||
tx = this.state.fromWallet.createTx(
|
||||
utxo,
|
||||
transferAmount,
|
||||
newFee,
|
||||
this.state.newDestinationAddress,
|
||||
false,
|
||||
lastSequence,
|
||||
);
|
||||
tx = this.state.fromWallet.createTx(utxo, transferAmount, newFee, this.state.newDestinationAddress, false, lastSequence);
|
||||
BlueApp.tx_metadata[this.state.txid] = txMetadata || {};
|
||||
BlueApp.tx_metadata[this.state.txid]['last_sequence'] = lastSequence;
|
||||
|
||||
@ -132,10 +114,7 @@ export default class SendCreate extends Component {
|
||||
BlueApp.tx_metadata[txid]['txhex'] = tx;
|
||||
//
|
||||
BlueApp.saveToDisk();
|
||||
console.log(
|
||||
'BlueApp.txMetadata[this.state.txid]',
|
||||
BlueApp.tx_metadata[this.state.txid],
|
||||
);
|
||||
console.log('BlueApp.txMetadata[this.state.txid]', BlueApp.tx_metadata[this.state.txid]);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
return this.setState({
|
||||
@ -173,8 +152,7 @@ export default class SendCreate extends Component {
|
||||
} else {
|
||||
this.setState({ broadcastErrorMessage: '' });
|
||||
this.setState({
|
||||
broadcastSuccessMessage:
|
||||
'Success! TXID: ' + JSON.stringify(result.result || result.txid),
|
||||
broadcastSuccessMessage: 'Success! TXID: ' + JSON.stringify(result.result || result.txid),
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -184,21 +162,11 @@ export default class SendCreate extends Component {
|
||||
return (
|
||||
<SafeBlueArea style={{ flex: 1, paddingTop: 20 }}>
|
||||
<BlueSpacing />
|
||||
<BlueCard
|
||||
title={'Replace Transaction'}
|
||||
style={{ alignItems: 'center', flex: 1 }}
|
||||
>
|
||||
<BlueText>
|
||||
Error creating transaction. Invalid address or send amount?
|
||||
</BlueText>
|
||||
<FormValidationMessage>
|
||||
{this.state.errorMessage}
|
||||
</FormValidationMessage>
|
||||
<BlueCard title={'Replace Transaction'} style={{ alignItems: 'center', flex: 1 }}>
|
||||
<BlueText>Error creating transaction. Invalid address or send amount?</BlueText>
|
||||
<FormValidationMessage>{this.state.errorMessage}</FormValidationMessage>
|
||||
</BlueCard>
|
||||
<BlueButton
|
||||
onPress={() => this.props.navigation.goBack()}
|
||||
title="Go back"
|
||||
/>
|
||||
<BlueButton onPress={() => this.props.navigation.goBack()} title="Go back" />
|
||||
</SafeBlueArea>
|
||||
);
|
||||
}
|
||||
@ -218,10 +186,7 @@ export default class SendCreate extends Component {
|
||||
|
||||
<BlueText h4>This transaction is not replaceable</BlueText>
|
||||
|
||||
<BlueButton
|
||||
onPress={() => this.props.navigation.goBack()}
|
||||
title="Back"
|
||||
/>
|
||||
<BlueButton onPress={() => this.props.navigation.goBack()} title="Back" />
|
||||
</SafeBlueArea>
|
||||
);
|
||||
}
|
||||
@ -229,14 +194,8 @@ export default class SendCreate extends Component {
|
||||
return (
|
||||
<SafeBlueArea style={{ flex: 1, paddingTop: 20 }}>
|
||||
<BlueSpacing />
|
||||
<BlueCard
|
||||
title={'Replace Transaction'}
|
||||
style={{ alignItems: 'center', flex: 1 }}
|
||||
>
|
||||
<BlueText>
|
||||
This is transaction hex, signed and ready to be broadcast to the
|
||||
network. Continue?
|
||||
</BlueText>
|
||||
<BlueCard title={'Replace Transaction'} style={{ alignItems: 'center', flex: 1 }}>
|
||||
<BlueText>This is transaction hex, signed and ready to be broadcast to the network. Continue?</BlueText>
|
||||
|
||||
<TextInput
|
||||
style={{
|
||||
@ -253,33 +212,19 @@ export default class SendCreate extends Component {
|
||||
|
||||
<BlueSpacing20 />
|
||||
|
||||
<BlueText style={{ paddingTop: 20 }}>
|
||||
To: {this.state.newDestinationAddress}
|
||||
</BlueText>
|
||||
<BlueText style={{ paddingTop: 20 }}>To: {this.state.newDestinationAddress}</BlueText>
|
||||
<BlueText>Amount: {this.state.amount} BTC</BlueText>
|
||||
<BlueText>Fee: {this.state.fee} BTC</BlueText>
|
||||
<BlueText>TX size: {this.state.size} Bytes</BlueText>
|
||||
<BlueText>satoshiPerByte: {this.state.satoshiPerByte} Sat/B</BlueText>
|
||||
</BlueCard>
|
||||
|
||||
<BlueButton
|
||||
icon={{ name: 'megaphone', type: 'octicon' }}
|
||||
onPress={() => this.broadcast()}
|
||||
title="Broadcast"
|
||||
/>
|
||||
<BlueButton icon={{ name: 'megaphone', type: 'octicon' }} onPress={() => this.broadcast()} title="Broadcast" />
|
||||
|
||||
<BlueButton
|
||||
icon={{ name: 'arrow-left', type: 'octicon' }}
|
||||
onPress={() => this.props.navigation.goBack()}
|
||||
title="Go back"
|
||||
/>
|
||||
<BlueButton icon={{ name: 'arrow-left', type: 'octicon' }} onPress={() => this.props.navigation.goBack()} title="Go back" />
|
||||
|
||||
<FormValidationMessage>
|
||||
{this.state.broadcastErrorMessage}
|
||||
</FormValidationMessage>
|
||||
<Text style={{ padding: 20, color: '#080' }}>
|
||||
{this.state.broadcastSuccessMessage}
|
||||
</Text>
|
||||
<FormValidationMessage>{this.state.broadcastErrorMessage}</FormValidationMessage>
|
||||
<Text style={{ padding: 20, color: '#080' }}>{this.state.broadcastSuccessMessage}</Text>
|
||||
</SafeBlueArea>
|
||||
);
|
||||
}
|
||||
|
@ -1,14 +1,6 @@
|
||||
import React, { Component } from 'react';
|
||||
import { ActivityIndicator, View } from 'react-native';
|
||||
import {
|
||||
BlueSpacing20,
|
||||
BlueButton,
|
||||
SafeBlueArea,
|
||||
BlueCard,
|
||||
BlueText,
|
||||
BlueFormInput,
|
||||
BlueSpacing,
|
||||
} from '../../BlueComponents';
|
||||
import { BlueSpacing20, BlueButton, SafeBlueArea, BlueCard, BlueText, BlueFormInput, BlueSpacing } from '../../BlueComponents';
|
||||
import PropTypes from 'prop-types';
|
||||
/** @type {AppStorage} */
|
||||
let BlueApp = require('../../BlueApp');
|
||||
@ -21,8 +13,7 @@ export default class RBF extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
let txid;
|
||||
if (props.navigation.state.params)
|
||||
txid = props.navigation.state.params.txid;
|
||||
if (props.navigation.state.params) txid = props.navigation.state.params.txid;
|
||||
|
||||
let sourceWallet;
|
||||
let sourceTx;
|
||||
@ -108,10 +99,7 @@ export default class RBF extends Component {
|
||||
|
||||
<BlueText h4>This transaction is not replaceable</BlueText>
|
||||
|
||||
<BlueButton
|
||||
onPress={() => this.props.navigation.goBack()}
|
||||
title="Back"
|
||||
/>
|
||||
<BlueButton onPress={() => this.props.navigation.goBack()} title="Back" />
|
||||
</SafeBlueArea>
|
||||
);
|
||||
}
|
||||
@ -119,13 +107,8 @@ export default class RBF extends Component {
|
||||
if (!this.state.sourceWallet.getAddress) {
|
||||
return (
|
||||
<SafeBlueArea style={{ flex: 1, paddingTop: 20 }}>
|
||||
<BlueText>
|
||||
System error: Source wallet not found (this should never happen)
|
||||
</BlueText>
|
||||
<BlueButton
|
||||
onPress={() => this.props.navigation.goBack()}
|
||||
title="Back"
|
||||
/>
|
||||
<BlueText>System error: Source wallet not found (this should never happen)</BlueText>
|
||||
<BlueButton onPress={() => this.props.navigation.goBack()} title="Back" />
|
||||
</SafeBlueArea>
|
||||
);
|
||||
}
|
||||
@ -133,14 +116,8 @@ export default class RBF extends Component {
|
||||
return (
|
||||
<SafeBlueArea style={{ flex: 1, paddingTop: 20 }}>
|
||||
<BlueSpacing />
|
||||
<BlueCard
|
||||
title={'Replace By Fee'}
|
||||
style={{ alignItems: 'center', flex: 1 }}
|
||||
>
|
||||
<BlueText>
|
||||
RBF allows you to increase fee on already sent but not confirmed
|
||||
transaction, thus speeding up mining
|
||||
</BlueText>
|
||||
<BlueCard title={'Replace By Fee'} style={{ alignItems: 'center', flex: 1 }}>
|
||||
<BlueText>RBF allows you to increase fee on already sent but not confirmed transaction, thus speeding up mining</BlueText>
|
||||
<BlueSpacing20 />
|
||||
|
||||
<BlueText>
|
||||
@ -149,9 +126,7 @@ export default class RBF extends Component {
|
||||
<BlueSpacing20 />
|
||||
|
||||
<BlueFormInput
|
||||
onChangeText={text =>
|
||||
this.setState({ newDestinationAddress: text })
|
||||
}
|
||||
onChangeText={text => this.setState({ newDestinationAddress: text })}
|
||||
placeholder={'receiver address here'}
|
||||
value={this.state.newDestinationAddress}
|
||||
/>
|
||||
@ -166,17 +141,11 @@ export default class RBF extends Component {
|
||||
|
||||
<View style={{ flex: 1, flexDirection: 'row', paddingTop: 20 }}>
|
||||
<View style={{ flex: 0.33 }}>
|
||||
<BlueButton
|
||||
onPress={() => this.props.navigation.goBack()}
|
||||
title="Cancel"
|
||||
/>
|
||||
<BlueButton onPress={() => this.props.navigation.goBack()} title="Cancel" />
|
||||
</View>
|
||||
<View style={{ flex: 0.33 }} />
|
||||
<View style={{ flex: 0.33 }}>
|
||||
<BlueButton
|
||||
onPress={() => this.createTransaction()}
|
||||
title="Create"
|
||||
/>
|
||||
<BlueButton onPress={() => this.createTransaction()} title="Create" />
|
||||
</View>
|
||||
</View>
|
||||
</SafeBlueArea>
|
||||
|
@ -77,10 +77,7 @@ export default class TransactionsDetails extends Component {
|
||||
return <BlueSpacing />;
|
||||
}
|
||||
})()}
|
||||
<BlueHeaderDefaultSub
|
||||
leftText={loc.transactions.details.title}
|
||||
onClose={() => this.props.navigation.goBack()}
|
||||
/>
|
||||
<BlueHeaderDefaultSub leftText={loc.transactions.details.title} onClose={() => this.props.navigation.goBack()} />
|
||||
|
||||
<BlueCard>
|
||||
{(() => {
|
||||
@ -88,9 +85,7 @@ export default class TransactionsDetails extends Component {
|
||||
if (BlueApp.tx_metadata[this.state.tx.hash]['memo']) {
|
||||
return (
|
||||
<View>
|
||||
<BlueText h4>
|
||||
{BlueApp.tx_metadata[this.state.tx.hash]['memo']}
|
||||
</BlueText>
|
||||
<BlueText h4>{BlueApp.tx_metadata[this.state.tx.hash]['memo']}</BlueText>
|
||||
<BlueSpacing20 />
|
||||
</View>
|
||||
);
|
||||
@ -99,14 +94,10 @@ export default class TransactionsDetails extends Component {
|
||||
})()}
|
||||
|
||||
<BlueText h4>{loc.transactions.details.from}:</BlueText>
|
||||
<BlueText style={{ marginBottom: 10 }}>
|
||||
{this.state.from.join(', ')}
|
||||
</BlueText>
|
||||
<BlueText style={{ marginBottom: 10 }}>{this.state.from.join(', ')}</BlueText>
|
||||
|
||||
<BlueText h4>{loc.transactions.details.to}:</BlueText>
|
||||
<BlueText style={{ marginBottom: 10 }}>
|
||||
{this.state.to.join(', ')}
|
||||
</BlueText>
|
||||
<BlueText style={{ marginBottom: 10 }}>{this.state.to.join(', ')}</BlueText>
|
||||
|
||||
<BlueText>Txid: {this.state.tx.hash}</BlueText>
|
||||
<BlueText>received: {this.state.tx.received}</BlueText>
|
||||
|
@ -2,14 +2,7 @@ import React, { Component } from 'react';
|
||||
import { ListView, Dimensions } from 'react-native';
|
||||
import Ionicons from 'react-native-vector-icons/Ionicons';
|
||||
import { Header, Icon } from 'react-native-elements';
|
||||
import {
|
||||
BlueLoading,
|
||||
BlueList,
|
||||
SafeBlueArea,
|
||||
BlueCard,
|
||||
BlueText,
|
||||
BlueListItem,
|
||||
} from '../../BlueComponents';
|
||||
import { BlueLoading, BlueList, SafeBlueArea, BlueCard, BlueText, BlueListItem } from '../../BlueComponents';
|
||||
import PropTypes from 'prop-types';
|
||||
let loc = require('../../loc');
|
||||
let EV = require('../../events');
|
||||
@ -23,11 +16,7 @@ export default class TransactionsList extends Component {
|
||||
static navigationOptions = {
|
||||
tabBarLabel: loc.transactions.list.tabBarLabel,
|
||||
tabBarIcon: ({ tintColor, focused }) => (
|
||||
<Ionicons
|
||||
name={focused ? 'ios-list-box' : 'ios-list-box-outline'}
|
||||
size={26}
|
||||
style={{ color: tintColor }}
|
||||
/>
|
||||
<Ionicons name={focused ? 'ios-list-box' : 'ios-list-box-outline'} size={26} style={{ color: tintColor }} />
|
||||
),
|
||||
};
|
||||
|
||||
@ -114,18 +103,10 @@ export default class TransactionsList extends Component {
|
||||
text: this.state.final_balance + ' BTC',
|
||||
style: { color: BlueApp.settings.foregroundColor, fontSize: 25 },
|
||||
}}
|
||||
rightComponent={
|
||||
<Icon
|
||||
name="refresh"
|
||||
color={BlueApp.settings.foregroundColor}
|
||||
onPress={() => this.refresh()}
|
||||
/>
|
||||
}
|
||||
rightComponent={<Icon name="refresh" color={BlueApp.settings.foregroundColor} onPress={() => this.refresh()} />}
|
||||
/>
|
||||
<BlueCard title={loc.transactions.list.title}>
|
||||
<BlueText style={{ marginBottom: 10 }}>
|
||||
{loc.transactions.list.description}
|
||||
</BlueText>
|
||||
<BlueText style={{ marginBottom: 10 }}>{loc.transactions.list.description}</BlueText>
|
||||
|
||||
<BlueList>
|
||||
<ListView
|
||||
@ -138,25 +119,14 @@ export default class TransactionsList extends Component {
|
||||
avatar={
|
||||
<Icon
|
||||
color={(() => {
|
||||
return (
|
||||
(rowData.confirmations &&
|
||||
((rowData.value < 0 && '#900') || '#080')) ||
|
||||
'#ebebeb'
|
||||
);
|
||||
return (rowData.confirmations && ((rowData.value < 0 && '#900') || '#080')) || '#ebebeb';
|
||||
})()}
|
||||
name={(() => {
|
||||
return (
|
||||
(rowData.value < 0 && 'call-made') ||
|
||||
'call-received'
|
||||
);
|
||||
return (rowData.value < 0 && 'call-made') || 'call-received';
|
||||
})()}
|
||||
/>
|
||||
}
|
||||
title={
|
||||
rowData.value / 100000000 +
|
||||
' BTC' +
|
||||
this.txMemo(rowData.hash)
|
||||
}
|
||||
title={rowData.value / 100000000 + ' BTC' + this.txMemo(rowData.hash)}
|
||||
subtitle={
|
||||
rowData.received
|
||||
.replace(['T'], ' ')
|
||||
|
@ -1,15 +1,7 @@
|
||||
import { SegwitP2SHWallet } from '../../class';
|
||||
import React, { Component } from 'react';
|
||||
import { ActivityIndicator, Dimensions, View } from 'react-native';
|
||||
import {
|
||||
BlueSpacing,
|
||||
BlueButton,
|
||||
SafeBlueArea,
|
||||
BlueCard,
|
||||
BlueText,
|
||||
BlueHeaderDefaultSub,
|
||||
BlueSpacing40,
|
||||
} from '../../BlueComponents';
|
||||
import { BlueSpacing, BlueButton, SafeBlueArea, BlueCard, BlueText, BlueHeaderDefaultSub, BlueSpacing40 } from '../../BlueComponents';
|
||||
import PropTypes from 'prop-types';
|
||||
let EV = require('../../events');
|
||||
let A = require('../../analytics');
|
||||
@ -53,10 +45,7 @@ export default class WalletsAdd extends Component {
|
||||
}
|
||||
|
||||
return (
|
||||
<SafeBlueArea
|
||||
forceInset={{ horizontal: 'always' }}
|
||||
style={{ flex: 1, paddingTop: 40 }}
|
||||
>
|
||||
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={{ flex: 1, paddingTop: 40 }}>
|
||||
{(() => {
|
||||
if (isIpad) {
|
||||
return <BlueSpacing40 />;
|
||||
@ -64,10 +53,7 @@ export default class WalletsAdd extends Component {
|
||||
return <BlueSpacing />;
|
||||
}
|
||||
})()}
|
||||
<BlueHeaderDefaultSub
|
||||
leftText={loc.wallets.add.title}
|
||||
onClose={() => this.props.navigation.goBack()}
|
||||
/>
|
||||
<BlueHeaderDefaultSub leftText={loc.wallets.add.title} onClose={() => this.props.navigation.goBack()} />
|
||||
|
||||
<BlueCard>
|
||||
<BlueText>{loc.wallets.add.description}</BlueText>
|
||||
|
@ -79,23 +79,14 @@ export default class WalletDetails extends Component {
|
||||
<SafeBlueArea style={{ flex: 1 }}>
|
||||
<BlueSpacingVariable />
|
||||
|
||||
<BlueHeaderDefaultSub
|
||||
leftText={loc.wallets.details.title}
|
||||
onClose={() => this.props.navigation.goBack()}
|
||||
/>
|
||||
<BlueHeaderDefaultSub leftText={loc.wallets.details.title} onClose={() => this.props.navigation.goBack()} />
|
||||
|
||||
<BlueCard style={{ alignItems: 'center', flex: 1 }}>
|
||||
<BlueFormLabel>{loc.wallets.details.address}:</BlueFormLabel>
|
||||
<BlueFormInputAddress
|
||||
value={this.state.wallet.getAddress()}
|
||||
editable
|
||||
/>
|
||||
<BlueFormInputAddress value={this.state.wallet.getAddress()} editable />
|
||||
|
||||
<BlueFormLabel>{loc.wallets.details.type}:</BlueFormLabel>
|
||||
<BlueFormInput
|
||||
value={this.state.wallet.getTypeReadable()}
|
||||
editable={false}
|
||||
/>
|
||||
<BlueFormInput value={this.state.wallet.getTypeReadable()} editable={false} />
|
||||
|
||||
<BlueFormLabel>{loc.wallets.details.label}:</BlueFormLabel>
|
||||
<BlueFormInput
|
||||
|
@ -1,14 +1,7 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Dimensions, ActivityIndicator, View } from 'react-native';
|
||||
import QRCode from 'react-native-qrcode';
|
||||
import {
|
||||
BlueSpacing,
|
||||
BlueSpacing40,
|
||||
SafeBlueArea,
|
||||
BlueCard,
|
||||
BlueText,
|
||||
BlueHeaderDefaultSub,
|
||||
} from '../../BlueComponents';
|
||||
import { BlueSpacing, BlueSpacing40, SafeBlueArea, BlueCard, BlueText, BlueHeaderDefaultSub } from '../../BlueComponents';
|
||||
import PropTypes from 'prop-types';
|
||||
/** @type {AppStorage} */
|
||||
let BlueApp = require('../../BlueApp');
|
||||
@ -80,10 +73,7 @@ export default class WalletExport extends Component {
|
||||
return <BlueSpacing />;
|
||||
}
|
||||
})()}
|
||||
<BlueHeaderDefaultSub
|
||||
leftText={loc.wallets.export.title}
|
||||
onClose={() => this.props.navigation.goBack()}
|
||||
/>
|
||||
<BlueHeaderDefaultSub leftText={loc.wallets.export.title} onClose={() => this.props.navigation.goBack()} />
|
||||
|
||||
<BlueCard style={{ alignItems: 'center', flex: 1 }}>
|
||||
<BlueText>{this.state.wallet.getAddress()}</BlueText>
|
||||
@ -93,9 +83,7 @@ export default class WalletExport extends Component {
|
||||
bgColor={BlueApp.settings.foregroundColor}
|
||||
fgColor={BlueApp.settings.brandingColor}
|
||||
/>
|
||||
<BlueText>
|
||||
{this.state.wallet.getSecret()} [Wallet Import Format]
|
||||
</BlueText>
|
||||
<BlueText>{this.state.wallet.getSecret()} [Wallet Import Format]</BlueText>
|
||||
</BlueCard>
|
||||
</SafeBlueArea>
|
||||
);
|
||||
|
@ -94,9 +94,7 @@ export default class WalletsList extends Component {
|
||||
showReceiveButton: showReceive,
|
||||
showSendButton: showSend,
|
||||
showRereshButton: (BlueApp.getWallets().length > 0 && true) || false,
|
||||
dataSource: ds.cloneWithRows(
|
||||
BlueApp.getTransactions(this.lastSnappedTo || 0),
|
||||
),
|
||||
dataSource: ds.cloneWithRows(BlueApp.getTransactions(this.lastSnappedTo || 0)),
|
||||
});
|
||||
}, 1);
|
||||
}
|
||||
@ -179,10 +177,7 @@ export default class WalletsList extends Component {
|
||||
if (wallets && wallets[index] && wallets[index].timeToRefresh()) {
|
||||
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
|
||||
) {
|
||||
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();
|
||||
@ -211,10 +206,7 @@ export default class WalletsList extends Component {
|
||||
|
||||
return (
|
||||
<SafeBlueArea>
|
||||
<BlueHeaderDefaultMain
|
||||
leftText={loc.wallets.list.title}
|
||||
onClose={() => navigate('Settings')}
|
||||
/>
|
||||
<BlueHeaderDefaultMain leftText={loc.wallets.list.title} onClose={() => navigate('Settings')} />
|
||||
|
||||
<WalletsCarousel
|
||||
data={BlueApp.getWallets().concat(false)}
|
||||
@ -246,11 +238,7 @@ export default class WalletsList extends Component {
|
||||
</Text>
|
||||
{(() => {
|
||||
if (this.state.showRereshButton) {
|
||||
return (
|
||||
<BlueRefreshIcon
|
||||
onPress={() => this.refreshTransactions()}
|
||||
/>
|
||||
);
|
||||
return <BlueRefreshIcon onPress={() => this.refreshTransactions()} />;
|
||||
}
|
||||
})()}
|
||||
</View>
|
||||
@ -263,10 +251,7 @@ export default class WalletsList extends Component {
|
||||
}}
|
||||
>
|
||||
{(() => {
|
||||
if (
|
||||
BlueApp.getTransactions(this.lastSnappedTo || 0)
|
||||
.length === 0
|
||||
) {
|
||||
if (BlueApp.getTransactions(this.lastSnappedTo || 0).length === 0) {
|
||||
return (
|
||||
<View>
|
||||
<Text
|
||||
@ -325,16 +310,10 @@ export default class WalletsList extends Component {
|
||||
);
|
||||
}
|
||||
})()}
|
||||
title={loc.transactionTimeToReadable(
|
||||
rowData.received,
|
||||
)}
|
||||
title={loc.transactionTimeToReadable(rowData.received)}
|
||||
subtitle={
|
||||
(rowData.confirmations < 200
|
||||
? loc.transactions.list.conf +
|
||||
': ' +
|
||||
rowData.confirmations +
|
||||
' '
|
||||
: '') + this.txMemo(rowData.hash)
|
||||
(rowData.confirmations < 200 ? loc.transactions.list.conf + ': ' + rowData.confirmations + ' ' : '') +
|
||||
this.txMemo(rowData.hash)
|
||||
}
|
||||
onPress={() => {
|
||||
navigate('TransactionDetails', {
|
||||
@ -355,10 +334,7 @@ export default class WalletsList extends Component {
|
||||
top: -7,
|
||||
fontWeight: '600',
|
||||
fontSize: 16,
|
||||
color:
|
||||
rowData.value / 100000000 < 0
|
||||
? BlueApp.settings.foregroundColor
|
||||
: '#37c0a1',
|
||||
color: rowData.value / 100000000 < 0 ? BlueApp.settings.foregroundColor : '#37c0a1',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
@ -1,12 +1,6 @@
|
||||
/* global alert */
|
||||
import React from 'react';
|
||||
import {
|
||||
Text,
|
||||
ActivityIndicator,
|
||||
Button,
|
||||
View,
|
||||
TouchableOpacity,
|
||||
} from 'react-native';
|
||||
import { Text, ActivityIndicator, Button, View, TouchableOpacity } from 'react-native';
|
||||
import { BlueText, SafeBlueArea, BlueButton } from '../../BlueComponents';
|
||||
import { Camera, Permissions } from 'expo';
|
||||
import { SegwitP2SHWallet, LegacyWallet } from '../../class';
|
||||
@ -46,31 +40,18 @@ export default class ScanQrWif extends React.Component {
|
||||
message: loc.wallets.scanQrWif.decoding,
|
||||
});
|
||||
shold_stop_bip38 = undefined; // eslint-disable-line
|
||||
let password = await prompt(
|
||||
loc.wallets.scanQrWif.input_password,
|
||||
loc.wallets.scanQrWif.password_explain,
|
||||
);
|
||||
let password = await prompt(loc.wallets.scanQrWif.input_password, loc.wallets.scanQrWif.password_explain);
|
||||
if (!password) {
|
||||
return;
|
||||
}
|
||||
let that = this;
|
||||
try {
|
||||
let decryptedKey = await bip38.decrypt(ret.data, password, function(
|
||||
status,
|
||||
) {
|
||||
let decryptedKey = await bip38.decrypt(ret.data, password, function(status) {
|
||||
that.setState({
|
||||
message:
|
||||
loc.wallets.scanQrWif.decoding +
|
||||
'... ' +
|
||||
status.percent.toString().substr(0, 4) +
|
||||
' %',
|
||||
message: loc.wallets.scanQrWif.decoding + '... ' + status.percent.toString().substr(0, 4) + ' %',
|
||||
});
|
||||
});
|
||||
ret.data = wif.encode(
|
||||
0x80,
|
||||
decryptedKey.privateKey,
|
||||
decryptedKey.compressed,
|
||||
);
|
||||
ret.data = wif.encode(0x80, decryptedKey.privateKey, decryptedKey.compressed);
|
||||
} catch (e) {
|
||||
console.log(e.message);
|
||||
this.setState({ message: false });
|
||||
@ -93,10 +74,7 @@ export default class ScanQrWif extends React.Component {
|
||||
let newLegacyWallet = new LegacyWallet();
|
||||
newLegacyWallet.setSecret(ret.data);
|
||||
|
||||
if (
|
||||
newWallet.getAddress() === false ||
|
||||
newLegacyWallet.getAddress() === false
|
||||
) {
|
||||
if (newWallet.getAddress() === false || newLegacyWallet.getAddress() === false) {
|
||||
alert(loc.wallets.scanQrWif.bad_wif);
|
||||
return;
|
||||
}
|
||||
@ -108,23 +86,13 @@ export default class ScanQrWif extends React.Component {
|
||||
if (newLegacyWallet.getBalance()) {
|
||||
newLegacyWallet.setLabel(loc.wallets.scanQrWif.imported_legacy);
|
||||
BlueApp.wallets.push(newLegacyWallet);
|
||||
alert(
|
||||
loc.wallets.scanQrWif.imported_wif +
|
||||
ret.data +
|
||||
loc.wallets.scanQrWif.with_address +
|
||||
newLegacyWallet.getAddress(),
|
||||
);
|
||||
alert(loc.wallets.scanQrWif.imported_wif + ret.data + loc.wallets.scanQrWif.with_address + newLegacyWallet.getAddress());
|
||||
} else {
|
||||
await newWallet.fetchBalance();
|
||||
await newWallet.fetchTransactions();
|
||||
newWallet.setLabel(loc.wallets.scanQrWif.imported_segwit);
|
||||
BlueApp.wallets.push(newWallet);
|
||||
alert(
|
||||
loc.wallets.scanQrWif.imported_wif +
|
||||
ret.data +
|
||||
loc.wallets.scanQrWif.with_address +
|
||||
newWallet.getAddress(),
|
||||
);
|
||||
alert(loc.wallets.scanQrWif.imported_wif + ret.data + loc.wallets.scanQrWif.with_address + newWallet.getAddress());
|
||||
}
|
||||
await BlueApp.saveToDisk();
|
||||
this.props.navigation.popToTop();
|
||||
@ -185,11 +153,7 @@ export default class ScanQrWif extends React.Component {
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Camera
|
||||
style={{ flex: 1 }}
|
||||
type={this.state.type}
|
||||
onBarCodeRead={ret => this.onBarCodeRead(ret)}
|
||||
>
|
||||
<Camera style={{ flex: 1 }} type={this.state.type} onBarCodeRead={ret => this.onBarCodeRead(ret)}>
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
@ -205,10 +169,7 @@ export default class ScanQrWif extends React.Component {
|
||||
}}
|
||||
onPress={() => {
|
||||
this.setState({
|
||||
type:
|
||||
this.state.type === Camera.Constants.Type.back
|
||||
? Camera.Constants.Type.front
|
||||
: Camera.Constants.Type.back,
|
||||
type: this.state.type === Camera.Constants.Type.back ? Camera.Constants.Type.front : Camera.Constants.Type.back,
|
||||
});
|
||||
}}
|
||||
>
|
||||
|
Loading…
Reference in New Issue
Block a user