mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2024-11-19 01:40:12 +01:00
REF: prettier 140 chars
WIP: HD wallets TST REF
This commit is contained in:
parent
b369a1b917
commit
7619183285
@ -9,7 +9,7 @@
|
||||
'warn',
|
||||
{
|
||||
singleQuote: true,
|
||||
printWidth: 120,
|
||||
printWidth: 140,
|
||||
trailingComma: 'all'
|
||||
}
|
||||
]
|
||||
|
30
App.test.js
30
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');
|
||||
@ -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]));
|
||||
|
@ -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={{
|
||||
|
@ -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() {
|
||||
|
@ -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!'));
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -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>}
|
||||
*/
|
||||
@ -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;
|
||||
|
@ -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';
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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',
|
||||
|
@ -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',
|
||||
|
@ -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',
|
||||
|
@ -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',
|
||||
|
@ -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"
|
||||
/>
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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');
|
||||
|
@ -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');
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
{(() => {
|
||||
|
@ -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>
|
||||
|
@ -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');
|
||||
|
@ -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');
|
||||
|
@ -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', {
|
||||
|
@ -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,
|
||||
});
|
||||
}}
|
||||
>
|
||||
|
Loading…
Reference in New Issue
Block a user