REF: prettier 140 chars

WIP: HD wallets
TST
REF
This commit is contained in:
Overtorment 2018-07-07 22:15:14 +01:00
parent b369a1b917
commit 7619183285
30 changed files with 277 additions and 248 deletions

View File

@ -9,7 +9,7 @@
'warn',
{
singleQuote: true,
printWidth: 120,
printWidth: 140,
trailingComma: 'all'
}
]

View File

@ -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');
@ -200,14 +201,11 @@ it('bip38 decodes', async () => {
{ N: 1, r: 8, p: 8 }, // using non-default parameters to speed it up (not-bip38 compliant)
);
assert.equal(
wif.encode(0x80, decryptedKey.privateKey, decryptedKey.compressed),
'5KN7MzqK5wt2TP1fQCYyHBtDrXdJuXbUzm4A9rKAteGu3Qi5CVR',
);
assert.equal(wif.encode(0x80, decryptedKey.privateKey, decryptedKey.compressed), '5KN7MzqK5wt2TP1fQCYyHBtDrXdJuXbUzm4A9rKAteGu3Qi5CVR');
});
it('bip38 decodes slow', async () => {
if (process.env.USER === 'burn') {
if (process.env.USER === 'burn' || process.env.USER === 'igor') {
// run only on circleCI
return;
}
@ -216,14 +214,9 @@ it('bip38 decodes slow', async () => {
const wif = require('wif');
let encryptedKey = '6PnU5voARjBBykwSddwCdcn6Eu9EcsK24Gs5zWxbJbPZYW7eiYQP8XgKbN';
let decryptedKey = await bip38.decrypt(encryptedKey, 'qwerty', status =>
process.stdout.write(parseInt(status.percent) + '%\r'),
);
let decryptedKey = await bip38.decrypt(encryptedKey, 'qwerty', status => process.stdout.write(parseInt(status.percent) + '%\r'));
assert.equal(
wif.encode(0x80, decryptedKey.privateKey, decryptedKey.compressed),
'KxqRtpd9vFju297ACPKHrGkgXuberTveZPXbRDiQ3MXZycSQYtjc',
);
assert.equal(wif.encode(0x80, decryptedKey.privateKey, decryptedKey.compressed), 'KxqRtpd9vFju297ACPKHrGkgXuberTveZPXbRDiQ3MXZycSQYtjc');
});
it('Wallet can fetch UTXO', async () => {
@ -247,11 +240,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]));

View File

@ -416,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>
@ -458,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>
@ -509,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={{
@ -573,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={{

View File

@ -1,5 +1,12 @@
/* global it, jasmine */
import { SegwitP2SHWallet, SegwitBech32Wallet, HDSegwitP2SHWallet, HDLegacyBreadwalletWallet } from './class';
import {
SegwitP2SHWallet,
SegwitBech32Wallet,
HDSegwitP2SHWallet,
HDLegacyBreadwalletWallet,
HDLegacyP2PKHWallet,
LegacyWallet,
} from './class';
let assert = require('assert');
it('can convert witness to address', () => {
@ -10,7 +17,7 @@ it('can convert witness to address', () => {
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 =
@ -42,6 +49,55 @@ 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() {

View File

@ -20,9 +20,7 @@ export default class MockStorage {
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!'));
});
});

View File

@ -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
}
}

View 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;
}
}

View File

@ -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>}
*/
@ -90,9 +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) {
@ -111,6 +115,16 @@ export class HDSegwitP2SHWallet extends LegacyWallet {
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() {
const api = new Frisbee({ baseURI: 'https://blockchain.info' });
@ -125,6 +139,14 @@ 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 = [];
@ -153,10 +175,7 @@ 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') {
@ -178,10 +197,7 @@ 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') {
@ -193,7 +209,7 @@ export class HDSegwitP2SHWallet extends LegacyWallet {
}
}
tx.value = value / 100000000;
tx.value = new BigNumber(value).div(100000000).toString() * 1;
this.transactions.push(tx);
}
@ -201,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;

View File

@ -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';

View File

@ -156,11 +156,7 @@ 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'));
@ -351,16 +347,7 @@ 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());
}

View File

@ -65,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);
}
}

View File

@ -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);
}
setInterval(() => updateExchangeRate(), 2 * 60 * 100);
return updateExchangeRate();
}

View File

@ -137,9 +137,7 @@ 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',

View File

@ -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: {
@ -82,8 +80,7 @@ module.exports = {
title: 'Crear una Transaccion',
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',

View File

@ -9,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: {
@ -129,8 +127,7 @@ 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',

View File

@ -9,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: {
@ -129,8 +127,7 @@ 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',

View File

@ -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');
@ -76,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"
/>

View File

@ -62,10 +62,7 @@ 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);
}

View File

@ -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');

View File

@ -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');

View File

@ -207,8 +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>

View File

@ -71,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>

View File

@ -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');
@ -110,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;
@ -236,11 +221,7 @@ export default class SendCreate extends Component {
<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>

View File

@ -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');
@ -125,9 +117,7 @@ export default class RBF extends Component {
<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>
<BlueText>RBF allows you to increase fee on already sent but not confirmed transaction, thus speeding up mining</BlueText>
<BlueSpacing20 />
<BlueText>

View File

@ -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>
{(() => {

View File

@ -103,9 +103,7 @@ 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>

View File

@ -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');

View File

@ -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');

View File

@ -293,9 +293,8 @@ export default class WalletsList extends Component {
})()}
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', {

View File

@ -86,20 +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();
@ -176,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,
});
}}
>