Merge branch 'experimental' of github.com:BlueWallet/BlueWallet into experimental

# Conflicts:
#	android/app/app.iml
#	android/app/build.gradle
#	android/app/src/main/AndroidManifest.xml
#	android/app/src/main/java/io/bluewallet/bluewallet/MainApplication.java
#	android/gradle/wrapper/gradle-wrapper.properties
#	android/settings.gradle
#	ios/BlueWallet.xcodeproj/project.pbxproj
#	ios/BlueWallet/Info.plist
#	package-lock.json
#	package.json
This commit is contained in:
Marcos Rodriguez Vélez 2019-01-22 17:45:41 -05:00
commit 3c603b6543
24 changed files with 897 additions and 587 deletions

View file

@ -57,7 +57,7 @@ describe('unit - LegacyWallet', function() {
let b = LegacyWallet.fromJson(key);
assert(key === JSON.stringify(b));
assert.equal(key, JSON.stringify(b));
assert.strictEqual(key, JSON.stringify(b));
});
it('can validate addresses', () => {
@ -118,8 +118,8 @@ it('Appstorage - loadFromDisk works', async () => {
let Storage2 = new AppStorage();
await Storage2.loadFromDisk();
assert.equal(Storage2.wallets.length, 1);
assert.equal(Storage2.wallets[0].getLabel(), 'testlabel');
assert.strictEqual(Storage2.wallets.length, 1);
assert.strictEqual(Storage2.wallets[0].getLabel(), 'testlabel');
let isEncrypted = await Storage2.storageIsEncrypted();
assert.ok(!isEncrypted);
@ -146,7 +146,7 @@ it('Appstorage - encryptStorage & load encrypted storage works', async () => {
assert.ok(!isEncrypted);
await Storage.encryptStorage('password');
isEncrypted = await Storage.storageIsEncrypted();
assert.equal(Storage.cachedPassword, 'password');
assert.strictEqual(Storage.cachedPassword, 'password');
assert.ok(isEncrypted);
// saved, now trying to load, using good password
@ -156,8 +156,8 @@ it('Appstorage - encryptStorage & load encrypted storage works', async () => {
assert.ok(isEncrypted);
let loadResult = await Storage2.loadFromDisk('password');
assert.ok(loadResult);
assert.equal(Storage2.wallets.length, 1);
assert.equal(Storage2.wallets[0].getLabel(), 'testlabel');
assert.strictEqual(Storage2.wallets.length, 1);
assert.strictEqual(Storage2.wallets[0].getLabel(), 'testlabel');
// now trying to load, using bad password
@ -166,7 +166,7 @@ it('Appstorage - encryptStorage & load encrypted storage works', async () => {
assert.ok(isEncrypted);
loadResult = await Storage2.loadFromDisk('passwordBAD');
assert.ok(!loadResult);
assert.equal(Storage2.wallets.length, 0);
assert.strictEqual(Storage2.wallets.length, 0);
// now, trying case with adding data after decrypt.
// saveToDisk should be handled correctly
@ -176,14 +176,14 @@ it('Appstorage - encryptStorage & load encrypted storage works', async () => {
assert.ok(isEncrypted);
loadResult = await Storage2.loadFromDisk('password');
assert.ok(loadResult);
assert.equal(Storage2.wallets.length, 1);
assert.equal(Storage2.wallets[0].getLabel(), 'testlabel');
assert.strictEqual(Storage2.wallets.length, 1);
assert.strictEqual(Storage2.wallets[0].getLabel(), 'testlabel');
w = new SegwitP2SHWallet();
w.setLabel('testlabel2');
await w.generate();
Storage2.wallets.push(w);
assert.equal(Storage2.wallets.length, 2);
assert.equal(Storage2.wallets[1].getLabel(), 'testlabel2');
assert.strictEqual(Storage2.wallets.length, 2);
assert.strictEqual(Storage2.wallets[1].getLabel(), 'testlabel2');
await Storage2.saveToDisk();
// saved to encrypted storage after load. next load should be successfull
Storage2 = new AppStorage();
@ -191,15 +191,15 @@ it('Appstorage - encryptStorage & load encrypted storage works', async () => {
assert.ok(isEncrypted);
loadResult = await Storage2.loadFromDisk('password');
assert.ok(loadResult);
assert.equal(Storage2.wallets.length, 2);
assert.equal(Storage2.wallets[0].getLabel(), 'testlabel');
assert.equal(Storage2.wallets[1].getLabel(), 'testlabel2');
assert.strictEqual(Storage2.wallets.length, 2);
assert.strictEqual(Storage2.wallets[0].getLabel(), 'testlabel');
assert.strictEqual(Storage2.wallets[1].getLabel(), 'testlabel2');
// next, adding new `fake` storage which should be unlocked with `fake` password
let createFakeStorageResult = await Storage2.createFakeStorage('fakePassword');
assert.ok(createFakeStorageResult);
assert.equal(Storage2.wallets.length, 0);
assert.equal(Storage2.cachedPassword, 'fakePassword');
assert.strictEqual(Storage2.wallets.length, 0);
assert.strictEqual(Storage2.cachedPassword, 'fakePassword');
w = new SegwitP2SHWallet();
w.setLabel('fakewallet');
await w.generate();
@ -210,14 +210,14 @@ it('Appstorage - encryptStorage & load encrypted storage works', async () => {
let Storage3 = new AppStorage();
loadResult = await Storage3.loadFromDisk('password');
assert.ok(loadResult);
assert.equal(Storage3.wallets.length, 2);
assert.equal(Storage3.wallets[0].getLabel(), 'testlabel');
assert.strictEqual(Storage3.wallets.length, 2);
assert.strictEqual(Storage3.wallets[0].getLabel(), 'testlabel');
// fake:
Storage3 = new AppStorage();
loadResult = await Storage3.loadFromDisk('fakePassword');
assert.ok(loadResult);
assert.equal(Storage3.wallets.length, 1);
assert.equal(Storage3.wallets[0].getLabel(), 'fakewallet');
assert.strictEqual(Storage3.wallets.length, 1);
assert.strictEqual(Storage3.wallets[0].getLabel(), 'fakewallet');
});
it('Wallet can fetch UTXO', async () => {
@ -245,7 +245,7 @@ it('Wallet can fetch TXs', async () => {
let w = new LegacyWallet();
w._address = '12eQ9m4sgAwTSQoNXkRABKhCXCsjm2jdVG';
await w.fetchTransactions();
assert.equal(w.getTransactions().length, 2);
assert.strictEqual(w.getTransactions().length, 2);
let tx0 = w.getTransactions()[0];
let txExpected = {
@ -296,7 +296,7 @@ it('Wallet can fetch TXs', async () => {
delete txExpected.confirmations;
delete tx0.preference; // that bs is not always the same
delete txExpected.preference;
assert.deepEqual(tx0, txExpected);
assert.deepStrictEqual(tx0, txExpected);
});
describe('currency', () => {
@ -321,7 +321,7 @@ describe('currency', () => {
await currency.setPrefferedCurrency(FiatUnit.EUR);
await currency.startUpdater();
let preferred = await currency.getPreferredCurrency();
assert.equal(preferred.endPointKey, 'EUR');
assert.strictEqual(preferred.endPointKey, 'EUR');
cur = JSON.parse(AsyncStorage.storageCache[AppStorage.EXCHANGE_RATES]);
assert.ok(cur['BTC_EUR'] > 0);
});

View file

@ -14,7 +14,10 @@ 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.strictEqual(
wif.encode(0x80, decryptedKey.privateKey, decryptedKey.compressed),
'5KN7MzqK5wt2TP1fQCYyHBtDrXdJuXbUzm4A9rKAteGu3Qi5CVR',
);
});
it('bip38 decodes slow', async () => {
@ -29,7 +32,10 @@ it('bip38 decodes slow', async () => {
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');
assert.strictEqual(
wif.encode(0x80, decryptedKey.privateKey, decryptedKey.compressed),
'KxqRtpd9vFju297ACPKHrGkgXuberTveZPXbRDiQ3MXZycSQYtjc',
);
});
describe('Watch only wallet', () => {
@ -46,16 +52,16 @@ describe('Watch only wallet', () => {
w.setSecret('167zK5iZrs1U6piDqubD3FjRqUTM2CZnb8');
await w.fetchTransactions();
assert.equal(w.getTransactions().length, 233);
assert.strictEqual(w.getTransactions().length, 233);
w = new WatchOnlyWallet();
w.setSecret('1BiJW1jyUaxcJp2JWwbPLPzB1toPNWTFJV');
await w.fetchTransactions();
assert.equal(w.getTransactions().length, 2);
assert.strictEqual(w.getTransactions().length, 2);
// fetch again and make sure no duplicates
await w.fetchTransactions();
assert.equal(w.getTransactions().length, 2);
assert.strictEqual(w.getTransactions().length, 2);
});
it('can fetch complex TXs', async () => {

View file

@ -10,12 +10,14 @@ import {
Animated,
ActivityIndicator,
View,
UIManager,
StyleSheet,
Dimensions,
Image,
SafeAreaView,
Clipboard,
Platform,
LayoutAnimation,
TextInput,
} from 'react-native';
import LinearGradient from 'react-native-linear-gradient';
@ -193,6 +195,60 @@ export const BlueCopyToClipboardButton = ({ stringToCopy }) => {
);
};
export class BlueCopyTextToClipboard extends Component {
static propTypes = {
text: PropTypes.string,
};
static defaultProps = {
text: '',
};
state = { hasTappedText: false };
constructor() {
super();
UIManager.setLayoutAnimationEnabledExperimental && UIManager.setLayoutAnimationEnabledExperimental(true);
}
copyToClipboard = () => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.spring, () => {
Clipboard.setString(this.props.text);
setTimeout(() => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.spring);
this.setState({ hasTappedText: false });
}, 1000);
});
this.setState({ hasTappedText: true });
};
render() {
return (
<View style={{ justifyContent: 'center', alignItems: 'center', paddingHorizontal: 16 }}>
<TouchableOpacity onPress={this.copyToClipboard} disabled={this.state.hasTappedText}>
<Text style={styleCopyTextToClipboard.address} numberOfLines={0}>
{this.props.text}
</Text>
{this.state.hasTappedText && (
<Text style={styleCopyTextToClipboard.address} numberOfLines={0}>
{loc.wallets.xpub.copiedToClipboard}
</Text>
)}
</TouchableOpacity>
</View>
);
}
}
const styleCopyTextToClipboard = StyleSheet.create({
address: {
marginVertical: 32,
fontSize: 15,
color: '#9aa0aa',
textAlign: 'center',
},
});
export class SafeBlueArea extends Component {
render() {
return (

View file

@ -6,13 +6,13 @@ let bitcoin = require('bitcoinjs-lib');
it('can convert witness to address', () => {
let address = SegwitP2SHWallet.witnessToAddress('035c618df829af694cb99e664ce1b34f80ad2c3b49bcd0d9c0b1836c66b2d25fd8');
assert.equal(address, '34ZVGb3gT8xMLT6fpqC6dNVqJtJmvdjbD7');
assert.strictEqual(address, '34ZVGb3gT8xMLT6fpqC6dNVqJtJmvdjbD7');
address = SegwitBech32Wallet.witnessToAddress('035c618df829af694cb99e664ce1b34f80ad2c3b49bcd0d9c0b1836c66b2d25fd8');
assert.equal(address, 'bc1quhnve8q4tk3unhmjts7ymxv8cd6w9xv8wy29uv');
assert.strictEqual(address, 'bc1quhnve8q4tk3unhmjts7ymxv8cd6w9xv8wy29uv');
address = SegwitBech32Wallet.scriptPubKeyToAddress('00144d757460da5fcaf84cc22f3847faaa1078e84f6a');
assert.equal(address, 'bc1qf46hgcx6tl90snxz9uuy0742zpuwsnm27ysdh7');
assert.strictEqual(address, 'bc1qf46hgcx6tl90snxz9uuy0742zpuwsnm27ysdh7');
});
it('can create a Segwit HD (BIP49)', async function() {
@ -21,39 +21,39 @@ it('can create a Segwit HD (BIP49)', async function() {
'honey risk juice trip orient galaxy win situate shoot anchor bounce remind horse traffic exotic since escape mimic ramp skin judge owner topple erode';
let hd = new HDSegwitP2SHWallet();
hd.setSecret(mnemonic);
assert.equal('3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK', hd._getExternalAddressByIndex(0));
assert.equal('35p5LwCAE7mH2css7onyQ1VuS1jgWtQ4U3', hd._getExternalAddressByIndex(1));
assert.equal('32yn5CdevZQLk3ckuZuA8fEKBco8mEkLei', hd._getInternalAddressByIndex(0));
assert.equal(true, hd.validateMnemonic());
assert.strictEqual('3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK', hd._getExternalAddressByIndex(0));
assert.strictEqual('35p5LwCAE7mH2css7onyQ1VuS1jgWtQ4U3', hd._getExternalAddressByIndex(1));
assert.strictEqual('32yn5CdevZQLk3ckuZuA8fEKBco8mEkLei', hd._getInternalAddressByIndex(0));
assert.strictEqual(true, hd.validateMnemonic());
await hd.fetchBalance();
assert.equal(hd.getBalance(), 0);
assert.strictEqual(hd.getBalance(), 0);
assert.ok(hd._lastTxFetch === 0);
await hd.fetchTransactions();
assert.ok(hd._lastTxFetch > 0);
assert.equal(hd.transactions.length, 4);
assert.strictEqual(hd.transactions.length, 4);
assert.equal('L4MqtwJm6hkbACLG4ho5DF8GhcXdLEbbvpJnbzA9abfD6RDpbr2m', hd._getExternalWIFByIndex(0));
assert.equal(
assert.strictEqual('L4MqtwJm6hkbACLG4ho5DF8GhcXdLEbbvpJnbzA9abfD6RDpbr2m', hd._getExternalWIFByIndex(0));
assert.strictEqual(
'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);
assert.strictEqual(hd._getExternalAddressByIndex(hd.next_free_address_index), freeAddress);
let freeChangeAddress = await hd.getChangeAddressAsync();
assert.equal(hd._getInternalAddressByIndex(hd.next_free_change_address_index), freeChangeAddress);
assert.strictEqual(hd._getInternalAddressByIndex(hd.next_free_change_address_index), freeChangeAddress);
});
it('Segwit HD (BIP49) can generate addressess only via ypub', async function() {
let ypub = 'ypub6WhHmKBmHNjcrUVNCa3sXduH9yxutMipDcwiKW31vWjcMbfhQHjXdyx4rqXbEtVgzdbhFJ5mZJWmfWwnP4Vjzx97admTUYKQt6b9D7jjSCp';
let hd = new HDSegwitP2SHWallet();
hd._xpub = ypub;
assert.equal('3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK', hd._getExternalAddressByIndex(0));
assert.equal('35p5LwCAE7mH2css7onyQ1VuS1jgWtQ4U3', hd._getExternalAddressByIndex(1));
assert.equal('32yn5CdevZQLk3ckuZuA8fEKBco8mEkLei', hd._getInternalAddressByIndex(0));
assert.strictEqual('3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK', hd._getExternalAddressByIndex(0));
assert.strictEqual('35p5LwCAE7mH2css7onyQ1VuS1jgWtQ4U3', hd._getExternalAddressByIndex(1));
assert.strictEqual('32yn5CdevZQLk3ckuZuA8fEKBco8mEkLei', hd._getInternalAddressByIndex(0));
});
it('can generate Segwit HD (BIP49)', async () => {
@ -88,56 +88,56 @@ it('HD (BIP49) can create TX', async () => {
await hd.getChangeAddressAsync(); // to refresh internal pointer to next free address
await hd.getAddressAsync(); // to refresh internal pointer to next free address
let txhex = hd.createTx(hd.utxo, 0.000014, 0.000001, '3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK');
assert.equal(
assert.strictEqual(
txhex,
'010000000001029d98d81fe2b596fd79e845fa9f38d7e0b6fb73303c40fac604d04df1fa137aee00000000171600142f18e8406c9d210f30c901b24e5feeae78784eb7ffffffff67fb86f310df24e508d40fce9511c7fde4dd4ee91305fd08a074279a70e2cd22000000001716001468dde644410cc789d91a7f36b823f38369755a1cffffffff02780500000000000017a914a3a65daca3064280ae072b9d6773c027b30abace87dc0500000000000017a914850f4dbc255654de2c12c6f6d79cf9cb756cad038702483045022100dc8390a9fd34c31259fa47f9fc182f20d991110ecfd5b58af1cf542fe8de257a022004c2d110da7b8c4127675beccc63b46fd65c706951f090fd381fa3b21d3c5c08012102edd141c5a27a726dda66be10a38b0fd3ccbb40e7c380034aaa43a1656d5f4dd60247304402207c0aef8313d55e72474247daad955979f62e56d1cbac5f2d14b8b022c6ce112602205d9aa3804f04624b12ab8a5ab0214b529c531c2f71c27c6f18aba6502a6ea0a80121030db3c49461a5e539e97bab62ab2b8f88151d1c2376493cf73ef1d02ef60637fd00000000',
);
txhex = hd.createTx(hd.utxo, 0.000005, 0.000001, '3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK');
var tx = bitcoin.Transaction.fromHex(txhex);
assert.equal(tx.ins.length, 1);
assert.equal(tx.outs.length, 2);
assert.equal(tx.outs[0].value, 500);
assert.equal(tx.outs[1].value, 400);
assert.strictEqual(tx.ins.length, 1);
assert.strictEqual(tx.outs.length, 2);
assert.strictEqual(tx.outs[0].value, 500);
assert.strictEqual(tx.outs[1].value, 400);
let chunksIn = bitcoin.script.decompile(tx.outs[0].script);
let toAddress = bitcoin.address.fromOutputScript(chunksIn);
chunksIn = bitcoin.script.decompile(tx.outs[1].script);
let changeAddress = bitcoin.address.fromOutputScript(chunksIn);
assert.equal('3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK', toAddress);
assert.equal(hd._getInternalAddressByIndex(hd.next_free_change_address_index), changeAddress);
assert.strictEqual('3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK', toAddress);
assert.strictEqual(hd._getInternalAddressByIndex(hd.next_free_change_address_index), changeAddress);
//
txhex = hd.createTx(hd.utxo, 0.000015, 0.000001, '3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK');
tx = bitcoin.Transaction.fromHex(txhex);
assert.equal(tx.ins.length, 2);
assert.equal(tx.outs.length, 2);
assert.strictEqual(tx.ins.length, 2);
assert.strictEqual(tx.outs.length, 2);
//
txhex = hd.createTx(hd.utxo, 0.00025, 0.00001, '3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK');
tx = bitcoin.Transaction.fromHex(txhex);
assert.equal(tx.ins.length, 7);
assert.equal(tx.outs.length, 1);
assert.strictEqual(tx.ins.length, 7);
assert.strictEqual(tx.outs.length, 1);
chunksIn = bitcoin.script.decompile(tx.outs[0].script);
toAddress = bitcoin.address.fromOutputScript(chunksIn);
assert.equal('3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK', toAddress);
assert.strictEqual('3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK', toAddress);
// checking that change amount is at least 3x of fee, otherwise screw the change, just add it to fee.
// theres 0.00003 on UTXOs, lets transfer (0.00003 - 100sat), soo fee is equal to change (100 sat)
// which throws @dust error if broadcasted
txhex = hd.createTx(hd.utxo, 0.000028, 0.000001, '3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK');
tx = bitcoin.Transaction.fromHex(txhex);
assert.equal(tx.ins.length, 2);
assert.equal(tx.outs.length, 1); // only 1 output, which means change is neglected
assert.equal(tx.outs[0].value, 2800);
assert.strictEqual(tx.ins.length, 2);
assert.strictEqual(tx.outs.length, 1); // only 1 output, which means change is neglected
assert.strictEqual(tx.outs[0].value, 2800);
});
it('Segwit HD (BIP49) can fetch UTXO', async function() {
let hd = new HDSegwitP2SHWallet();
hd.usedAddresses = ['1Ez69SnzzmePmZX3WpEzMKTrcBF2gpNQ55', '1BiTCHeYzJNMxBLFCMkwYXNdFEdPJP53ZV']; // hacking internals
await hd.fetchUtxo();
assert.equal(hd.utxo.length, 11);
assert.strictEqual(hd.utxo.length, 11);
assert.ok(typeof hd.utxo[0].confirmations === 'number');
assert.ok(hd.utxo[0].txid);
assert.ok(hd.utxo[0].vout);
@ -168,7 +168,7 @@ it('can work with malformed mnemonic', () => {
hd = new HDSegwitP2SHWallet();
hd.setSecret(mnemonic);
let seed2 = hd.getMnemonicToSeedHex();
assert.equal(seed1, seed2);
assert.strictEqual(seed1, seed2);
assert.ok(hd.validateMnemonic());
});
@ -182,28 +182,28 @@ it('can create a Legacy HD (BIP44)', async function() {
let mnemonic = process.env.HD_MNEMONIC_BREAD;
let hd = new HDLegacyP2PKHWallet();
hd.setSecret(mnemonic);
assert.equal(hd.validateMnemonic(), true);
assert.equal(hd._getExternalAddressByIndex(0), '12eQ9m4sgAwTSQoNXkRABKhCXCsjm2jdVG');
assert.equal(hd._getExternalAddressByIndex(1), '1QDCFcpnrZ4yrAQxmbvSgeUC9iZZ8ehcR5');
assert.equal(hd._getInternalAddressByIndex(0), '1KZjqYHm7a1DjhjcdcjfQvYfF2h6PqatjX');
assert.equal(hd._getInternalAddressByIndex(1), '13CW9WWBsWpDUvLtbFqYziWBWTYUoQb4nU');
assert.equal(
assert.strictEqual(hd.validateMnemonic(), true);
assert.strictEqual(hd._getExternalAddressByIndex(0), '12eQ9m4sgAwTSQoNXkRABKhCXCsjm2jdVG');
assert.strictEqual(hd._getExternalAddressByIndex(1), '1QDCFcpnrZ4yrAQxmbvSgeUC9iZZ8ehcR5');
assert.strictEqual(hd._getInternalAddressByIndex(0), '1KZjqYHm7a1DjhjcdcjfQvYfF2h6PqatjX');
assert.strictEqual(hd._getInternalAddressByIndex(1), '13CW9WWBsWpDUvLtbFqYziWBWTYUoQb4nU');
assert.strictEqual(
hd.getXpub(),
'xpub6CQdfC3v9gU86eaSn7AhUFcBVxiGhdtYxdC5Cw2vLmFkfth2KXCMmYcPpvZviA89X6DXDs4PJDk5QVL2G2xaVjv7SM4roWHr1gR4xB3Z7Ps',
);
assert.equal(hd._getExternalWIFByIndex(0), 'L1hqNoJ26YuCdujMBJfWBNfgf4Jo7AcKFvcNcKLoMtoJDdDtRq7Q');
assert.equal(hd._getExternalWIFByIndex(1), 'KyyH4h59iatJWwFfiYPnYkw39SP7cBwydC3xzszsBBXHpfwz9cKb');
assert.equal(hd._getInternalWIFByIndex(0), 'Kx3QkrfemEEV49Mj5oWfb4bsWymboPdstta7eN3kAzop9apxYEFP');
assert.equal(hd._getInternalWIFByIndex(1), 'Kwfg1EDjFapN9hgwafdNPEH22z3vkd4gtG785vXXjJ6uvVWAJGtr');
assert.strictEqual(hd._getExternalWIFByIndex(0), 'L1hqNoJ26YuCdujMBJfWBNfgf4Jo7AcKFvcNcKLoMtoJDdDtRq7Q');
assert.strictEqual(hd._getExternalWIFByIndex(1), 'KyyH4h59iatJWwFfiYPnYkw39SP7cBwydC3xzszsBBXHpfwz9cKb');
assert.strictEqual(hd._getInternalWIFByIndex(0), 'Kx3QkrfemEEV49Mj5oWfb4bsWymboPdstta7eN3kAzop9apxYEFP');
assert.strictEqual(hd._getInternalWIFByIndex(1), 'Kwfg1EDjFapN9hgwafdNPEH22z3vkd4gtG785vXXjJ6uvVWAJGtr');
await hd.fetchBalance();
assert.equal(hd.balance, 0);
assert.strictEqual(hd.balance, 0);
assert.ok(hd._lastTxFetch === 0);
await hd.fetchTransactions();
assert.ok(hd._lastTxFetch > 0);
assert.equal(hd.transactions.length, 4);
assert.equal(hd.next_free_address_index, 1);
assert.equal(hd.next_free_change_address_index, 1);
assert.strictEqual(hd.transactions.length, 4);
assert.strictEqual(hd.next_free_address_index, 1);
assert.strictEqual(hd.next_free_change_address_index, 1);
for (let tx of hd.getTransactions()) {
assert.ok(tx.value === 1000 || tx.value === 1377 || tx.value === -1377 || tx.value === -1000);
@ -211,17 +211,17 @@ it('can create a Legacy HD (BIP44)', async function() {
// 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);
assert.strictEqual(hd._getExternalAddressByIndex(hd.next_free_address_index), freeAddress);
});
it('Legacy HD (BIP44) can generate addressess based on xpub', async function() {
let xpub = 'xpub6CQdfC3v9gU86eaSn7AhUFcBVxiGhdtYxdC5Cw2vLmFkfth2KXCMmYcPpvZviA89X6DXDs4PJDk5QVL2G2xaVjv7SM4roWHr1gR4xB3Z7Ps';
let hd = new HDLegacyP2PKHWallet();
hd._xpub = xpub;
assert.equal(hd._getExternalAddressByIndex(0), '12eQ9m4sgAwTSQoNXkRABKhCXCsjm2jdVG');
assert.equal(hd._getInternalAddressByIndex(0), '1KZjqYHm7a1DjhjcdcjfQvYfF2h6PqatjX');
assert.equal(hd._getExternalAddressByIndex(1), '1QDCFcpnrZ4yrAQxmbvSgeUC9iZZ8ehcR5');
assert.equal(hd._getInternalAddressByIndex(1), '13CW9WWBsWpDUvLtbFqYziWBWTYUoQb4nU');
assert.strictEqual(hd._getExternalAddressByIndex(0), '12eQ9m4sgAwTSQoNXkRABKhCXCsjm2jdVG');
assert.strictEqual(hd._getInternalAddressByIndex(0), '1KZjqYHm7a1DjhjcdcjfQvYfF2h6PqatjX');
assert.strictEqual(hd._getExternalAddressByIndex(1), '1QDCFcpnrZ4yrAQxmbvSgeUC9iZZ8ehcR5');
assert.strictEqual(hd._getInternalAddressByIndex(1), '13CW9WWBsWpDUvLtbFqYziWBWTYUoQb4nU');
});
it('Legacy HD (BIP44) can create TX', async () => {
@ -239,38 +239,38 @@ it('Legacy HD (BIP44) can create TX', async () => {
await hd.getAddressAsync(); // to refresh internal pointer to next free address
let txhex = hd.createTx(hd.utxo, 0.0008, 0.000005, '3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK');
assert.equal(
assert.strictEqual(
txhex,
'01000000045fbc74110c2d6fcf4d1161a59913fbcd2b6ab3c5a9eb4d0dc0859515cbc8654f030000006b4830450221009be5dbe37db5a8409ddce3570140c95d162a07651b1e48cf39a6a741892adc53022061a25b8024d8f3cb1b94f264245de0c6e9a103ea557ddeb66245b40ec8e9384b012102ad7b2216f3a2b38d56db8a7ee5c540fd12c4bbb7013106eff78cc2ace65aa002ffffffff5fbc74110c2d6fcf4d1161a59913fbcd2b6ab3c5a9eb4d0dc0859515cbc8654f000000006a47304402207106e9fa4e2e35d351fbccc9c0fad3356d85d0cd35a9d7e9cbcefce5440da1e5022073c1905b5927447378c0f660e62900c1d4b2691730799458889fb87d86f5159101210316e84a2556f30a199541633f5dda6787710ccab26771b7084f4c9e1104f47667ffffffff5fbc74110c2d6fcf4d1161a59913fbcd2b6ab3c5a9eb4d0dc0859515cbc8654f020000006a4730440220250b15094096c4d4fe6793da8e45fa118ed057cc2759a480c115e76e23590791022079cdbdc9e630d713395602071e2837ecc1d192a36a24d8ec71bc51d5e62b203b01210316e84a2556f30a199541633f5dda6787710ccab26771b7084f4c9e1104f47667ffffffff5fbc74110c2d6fcf4d1161a59913fbcd2b6ab3c5a9eb4d0dc0859515cbc8654f010000006b483045022100879da610e6ed12c84d55f12baf3bf6222d59b5282502b3c7f4db1d22152c16900220759a1c88583cbdaf7fde21c273ad985dfdf94a2fa85e42ee41dcea2fd69136fd012102ad7b2216f3a2b38d56db8a7ee5c540fd12c4bbb7013106eff78cc2ace65aa002ffffffff02803801000000000017a914a3a65daca3064280ae072b9d6773c027b30abace872c4c0000000000001976a9146ee5e3e66dc73587a3a2d77a1a6c8554fae21b8a88ac00000000',
);
var tx = bitcoin.Transaction.fromHex(txhex);
assert.equal(tx.ins.length, 4);
assert.equal(tx.outs.length, 2);
assert.equal(tx.outs[0].value, 80000); // payee
assert.equal(tx.outs[1].value, 19500); // change
assert.strictEqual(tx.ins.length, 4);
assert.strictEqual(tx.outs.length, 2);
assert.strictEqual(tx.outs[0].value, 80000); // payee
assert.strictEqual(tx.outs[1].value, 19500); // change
let chunksIn = bitcoin.script.decompile(tx.outs[0].script);
let toAddress = bitcoin.address.fromOutputScript(chunksIn);
chunksIn = bitcoin.script.decompile(tx.outs[1].script);
let changeAddress = bitcoin.address.fromOutputScript(chunksIn);
assert.equal('3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK', toAddress);
assert.equal(hd._getInternalAddressByIndex(hd.next_free_change_address_index), changeAddress);
assert.strictEqual('3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK', toAddress);
assert.strictEqual(hd._getInternalAddressByIndex(hd.next_free_change_address_index), changeAddress);
// checking that change amount is at least 3x of fee, otherwise screw the change, just add it to fee.
// theres 0.001 on UTXOs, lets transfer (0.001 - 100sat), soo fee is equal to change (100 sat)
// which throws @dust error if broadcasted
txhex = hd.createTx(hd.utxo, 0.000998, 0.000001, '3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK');
tx = bitcoin.Transaction.fromHex(txhex);
assert.equal(tx.ins.length, 4);
assert.equal(tx.outs.length, 1); // only 1 output, which means change is neglected
assert.equal(tx.outs[0].value, 99800);
assert.strictEqual(tx.ins.length, 4);
assert.strictEqual(tx.outs.length, 1); // only 1 output, which means change is neglected
assert.strictEqual(tx.outs[0].value, 99800);
});
it('Legacy HD (BIP44) can fetch UTXO', async function() {
let hd = new HDLegacyP2PKHWallet();
hd.usedAddresses = ['1Ez69SnzzmePmZX3WpEzMKTrcBF2gpNQ55', '1BiTCHeYzJNMxBLFCMkwYXNdFEdPJP53ZV']; // hacking internals
await hd.fetchUtxo();
assert.equal(hd.utxo.length, 11);
assert.strictEqual(hd.utxo.length, 11);
assert.ok(typeof hd.utxo[0].confirmations === 'number');
assert.ok(hd.utxo[0].txid);
assert.ok(hd.utxo[0].vout);
@ -290,31 +290,31 @@ it('HD breadwallet works', async function() {
let hdBread = new HDLegacyBreadwalletWallet();
hdBread.setSecret(process.env.HD_MNEMONIC_BREAD);
assert.equal(hdBread.validateMnemonic(), true);
assert.equal(hdBread._getExternalAddressByIndex(0), '1ARGkNMdsBE36fJhddSwf8PqBXG3s4d2KU');
assert.equal(hdBread._getInternalAddressByIndex(0), '1JLvA5D7RpWgChb4A5sFcLNrfxYbyZdw3V');
assert.strictEqual(hdBread.validateMnemonic(), true);
assert.strictEqual(hdBread._getExternalAddressByIndex(0), '1ARGkNMdsBE36fJhddSwf8PqBXG3s4d2KU');
assert.strictEqual(hdBread._getInternalAddressByIndex(0), '1JLvA5D7RpWgChb4A5sFcLNrfxYbyZdw3V');
assert.equal(
assert.strictEqual(
hdBread.getXpub(),
'xpub68nLLEi3KERQY7jyznC9PQSpSjmekrEmN8324YRCXayMXaavbdEJsK4gEcX2bNf9vGzT4xRks9utZ7ot1CTHLtdyCn9udvv1NWvtY7HXroh',
);
await hdBread.fetchBalance();
assert.equal(hdBread.balance, 0);
assert.strictEqual(hdBread.balance, 0);
assert.ok(hdBread._lastTxFetch === 0);
await hdBread.fetchTransactions();
assert.ok(hdBread._lastTxFetch > 0);
assert.equal(hdBread.transactions.length, 177);
assert.strictEqual(hdBread.transactions.length, 177);
for (let tx of hdBread.getTransactions()) {
assert.ok(tx.confirmations);
}
assert.equal(hdBread.next_free_address_index, 10);
assert.equal(hdBread.next_free_change_address_index, 118);
assert.strictEqual(hdBread.next_free_address_index, 10);
assert.strictEqual(hdBread.next_free_change_address_index, 118);
// checking that internal pointer and async address getter return the same address
let freeAddress = await hdBread.getAddressAsync();
assert.equal(hdBread._getExternalAddressByIndex(hdBread.next_free_address_index), freeAddress);
assert.strictEqual(hdBread._getExternalAddressByIndex(hdBread.next_free_address_index), freeAddress);
});
it('can convert blockchain.info TX to blockcypher TX format', () => {

View file

@ -152,7 +152,7 @@ describe('LightningCustodianWallet', () => {
}
await l2.fetchTransactions();
assert.equal(l2.transactions_raw.length, txLen + 1);
assert.strictEqual(l2.transactions_raw.length, txLen + 1);
// transactions became more after paying an invoice
// now, trying to pay duplicate invoice
@ -165,7 +165,7 @@ describe('LightningCustodianWallet', () => {
}
assert.ok(caughtError);
await l2.fetchTransactions();
assert.equal(l2.transactions_raw.length, txLen + 1);
assert.strictEqual(l2.transactions_raw.length, txLen + 1);
// havent changed since last time
end = +new Date();
if ((end - start) / 1000 > 9) {
@ -191,21 +191,21 @@ describe('LightningCustodianWallet', () => {
await lNew.createAccount(true);
await lNew.authorize();
await lNew.fetchBalance();
assert.equal(lNew.balance, 0);
assert.strictEqual(lNew.balance, 0);
let invoices = await lNew.getUserInvoices();
let invoice = await lNew.addInvoice(1, 'test memo');
let invoices2 = await lNew.getUserInvoices();
assert.equal(invoices2.length, invoices.length + 1);
assert.strictEqual(invoices2.length, invoices.length + 1);
assert.ok(invoices2[0].ispaid === false);
assert.ok(invoices2[0].description);
assert.equal(invoices2[0].description, 'test memo');
assert.strictEqual(invoices2[0].description, 'test memo');
assert.ok(invoices2[0].payment_request);
assert.ok(invoices2[0].timestamp);
assert.ok(invoices2[0].expire_time);
assert.equal(invoices2[0].amt, 1);
assert.strictEqual(invoices2[0].amt, 1);
for (let inv of invoices2) {
assert.equal(inv.type, 'user_invoice');
assert.strictEqual(inv.type, 'user_invoice');
}
await lOld.fetchBalance();
@ -225,11 +225,11 @@ describe('LightningCustodianWallet', () => {
await lOld.fetchBalance();
await lNew.fetchBalance();
assert.equal(oldBalance - lOld.balance, 1);
assert.equal(lNew.balance, 1);
assert.strictEqual(oldBalance - lOld.balance, 1);
assert.strictEqual(lNew.balance, 1);
await lOld.fetchTransactions();
assert.equal(lOld.transactions_raw.length, txLen + 1, 'internal invoice should also produce record in payer`s tx list');
assert.strictEqual(lOld.transactions_raw.length, txLen + 1, 'internal invoice should also produce record in payer`s tx list');
let newTx = lOld.transactions_raw.slice().pop();
assert.ok(typeof newTx.fee !== 'undefined');
assert.ok(newTx.value);
@ -244,8 +244,8 @@ describe('LightningCustodianWallet', () => {
await lNew.payInvoice(invoice);
await lOld.fetchBalance();
await lNew.fetchBalance();
assert.equal(lOld.balance - oldBalance, 1);
assert.equal(lNew.balance, 0);
assert.strictEqual(lOld.balance - oldBalance, 1);
assert.strictEqual(lNew.balance, 0);
// now, paying same internal invoice. should fail:
@ -261,16 +261,16 @@ describe('LightningCustodianWallet', () => {
assert.ok(coughtError);
await lOld.fetchTransactions();
assert.equal(txLen, lOld.transactions_raw.length, 'tx count should not be changed');
assert.equal(invLen, (await lNew.getUserInvoices()).length, 'invoices count should not be changed');
assert.strictEqual(txLen, lOld.transactions_raw.length, 'tx count should not be changed');
assert.strictEqual(invLen, (await lNew.getUserInvoices()).length, 'invoices count should not be changed');
// testing how limiting works:
assert.equal(lNew.user_invoices_raw.length, 1);
assert.strictEqual(lNew.user_invoices_raw.length, 1);
await lNew.addInvoice(666, 'test memo 2');
invoices = await lNew.getUserInvoices(1);
assert.equal(invoices.length, 2);
assert.equal(invoices[0].amt, 1);
assert.equal(invoices[1].amt, 666);
assert.strictEqual(invoices.length, 2);
assert.strictEqual(invoices[0].amt, 1);
assert.strictEqual(invoices[1].amt, 666);
});
it('can pay free amount (tip) invoice', async function() {
@ -319,7 +319,7 @@ describe('LightningCustodianWallet', () => {
let decoded = await l2.decodeInvoice(invoice);
assert.ok(decoded.payment_hash);
assert.ok(decoded.description);
assert.equal(+decoded.num_satoshis, 0);
assert.strictEqual(+decoded.num_satoshis, 0);
await l2.checkRouteInvoice(invoice);
@ -342,10 +342,10 @@ describe('LightningCustodianWallet', () => {
}
await l2.fetchTransactions();
assert.equal(l2.transactions_raw.length, txLen + 1);
assert.strictEqual(l2.transactions_raw.length, txLen + 1);
// transactions became more after paying an invoice
await l2.fetchBalance();
assert.equal(oldBalance - l2.balance, 3);
assert.strictEqual(oldBalance - l2.balance, 3);
});
});

View file

@ -89,6 +89,8 @@
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/apk_list" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/build-info" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/builds" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/check-libraries" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/check-manifest" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/checkDebugClasspath" />
@ -96,21 +98,31 @@
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/compatible_screen_manifest" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-runtime-classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-verifier" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant-run-apk" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant_run_main_apk_resources" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant_run_merged_manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant_run_split_apk_resources" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaPrecompile" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/javac" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jniLibs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/linked_res_for_bundle" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifest-checker" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/merged_assets" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/merged_manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/module_bundle" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/prebuild" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/processed_res" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/reload-dex" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/resources" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/shader_assets" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/shaders" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/split-apk" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/split_list" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/splits-support" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />

View file

@ -102,7 +102,7 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "3.6.2"
versionName "3.6.3"
ndk {
abiFilters "armeabi-v7a", "x86"
}

View file

@ -3,6 +3,7 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.CAMERA"/>
<application
android:name=".MainApplication"

View file

@ -3,19 +3,14 @@ package io.bluewallet.bluewallet;
import android.app.Application;
import com.facebook.react.ReactApplication;
import com.reactnativecommunity.webview.RNCWebViewPackage;
import com.oblador.vectoricons.VectorIconsPackage;
import org.reactnative.camera.RNCameraPackage;
import io.sentry.RNSentryPackage;
import com.bitgo.randombytes.RandomBytesPackage;
import im.shimo.react.prompt.RNPromptPackage;
import com.BV.LinearGradient.LinearGradientPackage;
import com.mkuczera.RNReactNativeHapticFeedbackPackage;
import com.idehub.GoogleAnalyticsBridge.GoogleAnalyticsBridgePackage;
import com.swmansion.gesturehandler.react.RNGestureHandlerPackage;
import com.rnfs.RNFSPackage;
import com.learnium.RNDeviceInfo.RNDeviceInfo;
import org.reactnative.camera.RNCameraPackage;
import com.reactnativecommunity.webview.RNCWebViewPackage;
import com.oblador.vectoricons.VectorIconsPackage;
import com.horcrux.svg.SvgPackage;
import io.sentry.RNSentryPackage;
import com.bitgo.randombytes.RandomBytesPackage;
@ -28,6 +23,7 @@ import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.soloader.SoLoader;
import org.reactnative.camera.RNCameraPackage;
import com.swmansion.gesturehandler.react.RNGestureHandlerPackage;
import com.rnfs.RNFSPackage;
@ -46,18 +42,19 @@ public class MainApplication extends Application implements ReactApplication {
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new RNCWebViewPackage(),
new SvgPackage(),
new RNCameraPackage(),
new RNSentryPackage(),
new RandomBytesPackage(),
new GoogleAnalyticsBridgePackage(),
new RNFSPackage(),
new RNDeviceInfo(),
new VectorIconsPackage(),
new RNPromptPackage(),
new LinearGradientPackage(),
new RNReactNativeHapticFeedbackPackage(),
new GoogleAnalyticsBridgePackage(),
new RNCWebViewPackage(),
new RNFSPackage() ,
new VectorIconsPackage(),
new SvgPackage(),
new LinearGradientPackage(),
new RNDeviceInfo(),
new RNCameraPackage(),
new RNGestureHandlerPackage()
);
}

View file

@ -2,7 +2,7 @@
buildscript {
ext {
buildToolsVersion = "27.0.3"
buildToolsVersion = "28.0.3"
minSdkVersion = 16
compileSdkVersion = 27
targetSdkVersion = 26

View file

@ -499,6 +499,13 @@
remoteGlobalIDString = 0974579A1D2A440A000D9368;
remoteInfo = RCTWKWebView;
};
B4DA894521F6056C00A3CB6C /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 5F3DCC24027F4B1EAACBAE3C /* RNCWebView.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 134814201AA4EA6300B7C361;
remoteInfo = RNCWebView;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
@ -1032,6 +1039,14 @@
name = Products;
sourceTree = "<group>";
};
B4DA891821F6056C00A3CB6C /* Products */ = {
isa = PBXGroup;
children = (
B4DA894621F6056C00A3CB6C /* libRNCWebView.a */,
);
name = Products;
sourceTree = "<group>";
};
FC7D399B1A1043C49023DC79 /* Resources */ = {
isa = PBXGroup;
children = (
@ -1081,8 +1096,8 @@
buildPhases = (
13B07F871A680F5B00A75B9A /* Sources */,
13B07F8C1A680F5B00A75B9A /* Frameworks */,
13B07F8E1A680F5B00A75B9A /* Resources */,
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
13B07F8E1A680F5B00A75B9A /* Resources */,
B9B0C844E4364469A20FB477 /* Upload Debug Symbols to Sentry */,
);
buildRules = (
@ -1237,6 +1252,10 @@
ProductGroup = B4C5B8E121F2DF3000A845C4 /* Products */;
ProjectRef = 3853DFD4C6D44BB8B17AEDF0 /* RNCamera.xcodeproj */;
},
{
ProductGroup = B4DA891821F6056C00A3CB6C /* Products */;
ProjectRef = 5F3DCC24027F4B1EAACBAE3C /* RNCWebView.xcodeproj */;
},
{
ProductGroup = B4C5B8E521F2DF3000A845C4 /* Products */;
ProjectRef = 03D541ACCDD2451D9971158E /* RNDeviceInfo.xcodeproj */;
@ -1691,6 +1710,13 @@
remoteRef = B4C5BAB521F3708C00A845C4 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
B4DA894621F6056C00A3CB6C /* libRNCWebView.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRNCWebView.a;
remoteRef = B4DA894521F6056C00A3CB6C /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
/* End PBXReferenceProxy section */
/* Begin PBXResourcesBuildPhase section */
@ -1755,7 +1781,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "export SENTRY_PROPERTIES=sentry.properties\nexport NODE_BINARY=node\n../node_modules/@sentry/cli/bin/sentry-cli react-native xcode ../node_modules/react-native/scripts/react-native-xcode.sh";
shellScript = "export SENTRY_PROPERTIES=sentry.properties\nexport NODE_BINARY=node\n../node_modules/@sentry/cli/bin/sentry-cli react-native xcode ../node_modules/react-native/scripts/react-native-xcode.sh\n";
};
2D02E4CB1E0B4B27006451C7 /* Bundle React Native Code And Images */ = {
isa = PBXShellScriptBuildPhase;

View file

@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>3.6.2</string>
<string>3.6.3</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
@ -56,7 +56,7 @@
<key>NSCalendarsUsageDescription</key>
<string>This alert should not show up as we do not require this data</string>
<key>NSCameraUsageDescription</key>
<string>In order to quickly scan the recipient's address, we need your permission to use the camera to scan their QR Code.</string>
<string>In order to quickly scan the recipient&apos;s address, we need your permission to use the camera to scan their QR Code.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>This alert should not show up as we do not require this data</string>
<key>NSMotionUsageDescription</key>
@ -67,6 +67,8 @@
<string>This alert should not show up as we do not require this data</string>
<key>NSSpeechRecognitionUsageDescription</key>
<string>This alert should not show up as we do not require this data</string>
<key>NSMicrophoneUsageDescription</key>
<string>This alert should not show up as we do not require this data</string>
<key>UIAppFonts</key>
<array>
<string>AntDesign.ttf</string>

View file

@ -4,8 +4,8 @@ module.exports = {
enter_password: 'Unesi lozinku',
bad_password: 'Kriva lozinka, pokušaj ponovo',
never: 'nikad',
continue: 'Continue',
ok: 'OK',
continue: 'Nastavi',
ok: 'U redu',
},
wallets: {
select_wallet: 'Odaberi volet',
@ -13,7 +13,7 @@ module.exports = {
list: {
app_name: 'Blue Wallet',
title: 'Voleti',
header: 'Volet je par privatnog ključa (tajna!) i javne adrese' + 'koju slobodno možete dijeliti kada primate novce.',
header: 'Volet je par privatnog ključa (tajna!) i javne adrese ' + 'koju slobodno možete dijeliti kada primate novce.',
add: 'Dodaj volet',
create_a_wallet: 'Stvori novi volet',
create_a_wallet1: 'Ne košta ništa i možete',
@ -33,10 +33,10 @@ module.exports = {
create: 'Stvori',
label_new_segwit: 'Novi SegWit',
label_new_lightning: 'Novi Lightning',
wallet_name: 'ime voleta',
wallet_type: 'tip',
wallet_name: 'ime voleta:',
wallet_type: 'tip:',
or: 'ili',
import_wallet: 'Unesi volet',
import_wallet: 'Unesi vanjski volet',
imported: 'Unešeno',
coming_soon: 'Dolazi uskoro',
lightning: 'Lightning',
@ -53,10 +53,10 @@ module.exports = {
yes_delete: 'Da, briši',
no_cancel: 'Ne, otiaži',
delete: 'Obriši',
save: 'Pohrani',
save: 'Spremi',
delete_this_wallet: 'Obriši ovaj volet',
export_backup: 'Izvoz / bekap',
buy_bitcoin: 'Kupi Bitcoin',
buy_bitcoin: 'Kupovina Bitcoina',
show_xpub: 'Prikaži voletov XPUB',
},
export: {
@ -147,22 +147,22 @@ module.exports = {
satoshi_per_byte: 'Satoshi / byte',
memo: 'Bilješka',
broadcast: 'Objavi',
not_enough_fee: 'Trošak slanja je premal. Povećaj ga.',
not_enough_fee: 'Trošak slanja je premalen. Povećaj ga.',
},
},
receive: {
header: 'Primanje',
header: 'Primi',
details: {
title: 'Pokaži ovu adresu platitelju',
share: 'pokaži',
share: 'podijeli',
copiedToClipboard: 'Kopirano u međuspremnik.',
label: 'Opis',
create: 'Create',
create: 'Stvori',
setAmount: 'Odredi iznos za primiti',
},
},
buyBitcoin: {
header: 'Kupi Bitcoin',
header: 'Kupovina Bitcoina',
tap_your_address: 'Klikni na adresu za spremanje u međuspremnik:',
copied: 'Spremljeno u međuspremnik!',
},
@ -176,26 +176,26 @@ module.exports = {
retype_password: 'Ponovi lozinku',
passwords_do_not_match: 'Lozinke su različite',
encrypt_storage: 'Kriptiraj spremnik',
lightning_settings: 'Lightning settings',
lightning_settings: 'Lightning postavke',
lightning_settings_explain:
'To connect to your own LND node please install LndHub' +
' and put its URL here in settings. Leave blank to use default ' +
'Za spajanje na tvoj vlastiti LND čvor trebaš instalirati LndHub' +
' i upisati njegov URL ovdje. Ostavi prazno za standardni ' +
'ndHub\n (lndhub.io)',
save: 'save',
about: 'Iznos',
save: 'Spremi',
about: 'Informacije',
language: 'Jezik',
currency: 'Valuta',
},
plausibledeniability: {
title: 'Fejk volet',
help:
'U iznimnim okolnostima netko gadan (pa još ako drži oklagiju) te' +
'može neljubazno pritisnuti da mu otkriješ lozinku za ovaj volet.' +
'BlueWallet ti čuva leđa buraz. Nemaš brige. Gledaj.' +
'Stvoriti ćemo dupli volet sa drugačijom lozinkom. Haha, žišku?' +
'Pa kad se ovaj počne pjeniti a ti vidiš da je vrag odnio šalu' +
'ti mu podvali ovaj drugi volet. Eto mu ga. Nek si cucla.',
help2: 'Novi spremnik će biti posve funkcionalan, možeš pohraniti koliko' + 'misliš da je potrebno da izgleda uvjerljivo.',
'Pazi. Netko gadan te može u iznimnim okolnostima (pljačka, prijevremeni izbori, itd.) ' +
'brutalno pritisnuti da mu otkriješ lozinku za svoj volet. ' +
'BlueWallet ti čuva leđa buraz. Nemaš brige. Gledaj, ' +
'stvoriti ćemo fejk volet sa drugačijom lozinkom. Haha, žišku? ' +
'Pa kad se ovaj počne pjeniti, a ti vidiš da je vrag odnio šalu, ' +
'samo mu podvali lozinku za ovaj drugi volet. Eto mu ga. Nek si cucla. ',
help2: 'Novi spremnik će biti posve funkcionalan, možeš pohraniti koliko ' + 'misliš da je potrebno da izgleda uvjerljivo.',
create_fake_storage: 'Stvori fejk enkriptirani spremnik',
go_back: 'Povratak',
create_password: 'Unesi lozinku',
@ -212,6 +212,6 @@ module.exports = {
refill: 'Dopuni',
withdraw: 'Isprazni',
expired: 'Isteklo',
sameWalletAsInvoiceError: 'Ne možeš platiti račun s istim voletom s kojim si račun stvorio, ono.',
sameWalletAsInvoiceError: 'Buraz! Ne možeš platiti račun s istim voletom s kojim si račun stvorio, ono.',
},
};

View file

@ -1,6 +1,7 @@
export const FiatUnit = Object.freeze({
USD: { endPointKey: 'USD', symbol: '$', locale: 'en-US' },
AUD: { endPointKey: 'AUD', symbol: '$', locale: 'en-AU' },
BRL: { endPointKey: 'BRL', symbol: 'R$', locale: 'pt-BR' },
CAD: { endPointKey: 'CAD', symbol: '$', locale: 'en-CA' },
CZK: { endPointKey: 'CZK', symbol: 'Kč', locale: 'cs-CZ' },
CNY: { endPointKey: 'CNY', symbol: '¥', locale: 'zh-CN' },
@ -9,6 +10,7 @@ export const FiatUnit = Object.freeze({
HRK: { endPointKey: 'HRK', symbol: 'HRK', locale: 'hr-HR' },
INR: { endPointKey: 'INR', symbol: '₹', locale: 'hi-HN' },
JPY: { endPointKey: 'JPY', symbol: '¥', locale: 'ja-JP' },
MXN: { endPointKey: 'MXN', symbol: '$', locale: 'es-MX' },
PLN: { endPointKey: 'PLN', symbol: 'zł', locale: 'pl-PL' },
RUB: { endPointKey: 'RUB', symbol: '₽', locale: 'ru-RU' },
SGD: { endPointKey: 'SGD', symbol: 'S$', locale: 'zh-SG' },

812
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,17 +1,17 @@
{
"name": "BlueWallet",
"version": "3.6.2",
"version": "3.6.0",
"devDependencies": {
"babel-eslint": "^8.2.6",
"babel-eslint": "^10.0.1",
"babel-jest": "23.6.0",
"eslint": "^4.19.1",
"eslint-plugin-babel": "^4.1.2",
"eslint": "^5.12.1",
"eslint-plugin-babel": "^5.3.0",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-node": "^6.0.1",
"eslint-plugin-promise": "^3.8.0",
"eslint-plugin-node": "^8.0.1",
"eslint-plugin-promise": "^4.0.1",
"eslint-plugin-react": "^7.12.3",
"jest": "23.6.0",
"metro-react-native-babel-preset": "^0.49.1",
"metro-react-native-babel-preset": "^0.51.1",
"prettier-eslint-cli": "^4.7.1",
"react-test-renderer": "^16.7.0",
"rn-nodeify": "github:tradle/rn-nodeify"
@ -36,7 +36,7 @@
},
"dependencies": {
"@babel/preset-env": "^7.2.3",
"bignumber.js": "^7.0.0",
"bignumber.js": "^8.0.2",
"bip21": "^2.0.2",
"bip39": "^2.5.0",
"bitcoinjs-lib": "^3.3.2",
@ -44,30 +44,30 @@
"buffer-reverse": "^1.0.1",
"crypto-js": "^3.1.9-1",
"dayjs": "^1.8.0",
"eslint-config-prettier": "^2.10.0",
"eslint-config-prettier": "^3.6.0",
"eslint-config-standard": "^12.0.0",
"eslint-config-standard-react": "^7.0.2",
"eslint-plugin-prettier": "^2.6.2",
"eslint-plugin-prettier": "^3.0.1",
"eslint-plugin-standard": "^4.0.0",
"frisbee": "^1.6.4",
"intl": "^1.2.5",
"mocha": "^5.2.0",
"node-libs-react-native": "^1.0.1",
"path-browserify": "0.0.0",
"prettier": "^1.14.2",
"prettier": "^1.16.0",
"process": "^0.11.10",
"prop-types": "^15.6.2",
"react": "^16.7.0",
"react-localization": "^1.0.10",
"react-native": "^0.57.8",
"react-native-camera": "^1.8.0",
"react-native-camera": "^1.9.0",
"react-native-custom-qr-codes": "^2.0.0",
"react-native-device-info": "^0.25.0",
"react-native-device-info": "^0.25.1",
"react-native-elements": "^0.19.0",
"react-native-flexi-radio-button": "^0.2.2",
"react-native-fs": "^2.13.3",
"react-native-gesture-handler": "^1.0.12",
"react-native-google-analytics-bridge": "^6.1.2",
"react-native-google-analytics-bridge": "^7.0.0",
"react-native-haptic-feedback": "^1.4.2",
"react-native-level-fs": "^3.0.1",
"react-native-linear-gradient": "^2.5.3",
@ -80,8 +80,8 @@
"react-native-sentry": "^0.40.2",
"react-native-snap-carousel": "^3.7.4",
"react-native-sortable-list": "0.0.22",
"react-native-svg": "^8.0.10",
"react-native-vector-icons": "^6.0.2",
"react-native-svg": "^9.0.0",
"react-native-vector-icons": "^6.2.0",
"react-native-webview": "2.8.0",
"react-native-wkwebview-reborn": "^2.0.0",
"react-navigation": "^3.0.9",
@ -90,6 +90,7 @@
"request-promise-native": "^1.0.5",
"secure-random": "^1.1.1",
"stream-browserify": "^1.0.0",
"util": "^0.11.1",
"wif": "^2.0.1"
},
"react-native": {

View file

@ -1,7 +1,15 @@
/* global alert */
import React, { Component } from 'react';
import { Animated, StyleSheet, View, TouchableOpacity, Clipboard, Share } from 'react-native';
import { BlueLoading, SafeBlueArea, BlueButton, BlueNavigationStyle, BlueText, BlueSpacing20 } from '../../BlueComponents';
import { View, Share } from 'react-native';
import {
BlueLoading,
BlueCopyTextToClipboard,
SafeBlueArea,
BlueButton,
BlueNavigationStyle,
BlueText,
BlueSpacing20,
} from '../../BlueComponents';
import PropTypes from 'prop-types';
import { QRCode } from 'react-native-custom-qr-codes';
/** @type {AppStorage} */
@ -16,13 +24,6 @@ export default class LNDViewAdditionalInvoiceInformation extends Component {
state = { walletInfo: undefined };
copyToClipboard = () => {
this.setState({ addressText: loc.receive.details.copiedToClipboard }, () => {
Clipboard.setString(this.state.walletInfo.uris[0]);
setTimeout(() => this.setState({ addressText: this.state.walletInfo.uris[0] }), 1000);
});
};
async componentDidMount() {
const fromWallet = this.props.navigation.getParam('fromWallet');
try {
@ -56,11 +57,7 @@ export default class LNDViewAdditionalInvoiceInformation extends Component {
/>
<BlueSpacing20 />
<BlueText>Open direct channel with this node:</BlueText>
<TouchableOpacity onPress={this.copyToClipboard}>
<Animated.Text style={styles.address} numberOfLines={0}>
{this.state.addressText}
</Animated.Text>
</TouchableOpacity>
<BlueCopyTextToClipboard text={this.state.walletInfo.uris[0]} />
</View>
<View style={{ marginBottom: 24 }}>
<BlueButton
@ -83,15 +80,6 @@ export default class LNDViewAdditionalInvoiceInformation extends Component {
}
}
const styles = StyleSheet.create({
address: {
marginVertical: 32,
fontSize: 15,
color: '#9aa0aa',
textAlign: 'center',
},
});
LNDViewAdditionalInvoiceInformation.propTypes = {
navigation: PropTypes.shape({
goBack: PropTypes.function,

View file

@ -1,6 +1,14 @@
import React, { Component } from 'react';
import { Animated, StyleSheet, View, TouchableOpacity, Clipboard, Dimensions, Share, ScrollView, BackHandler } from 'react-native';
import { BlueLoading, BlueText, SafeBlueArea, BlueButton, BlueNavigationStyle, BlueSpacing20 } from '../../BlueComponents';
import { View, Dimensions, Share, ScrollView, BackHandler } from 'react-native';
import {
BlueLoading,
BlueText,
SafeBlueArea,
BlueButton,
BlueCopyTextToClipboard,
BlueNavigationStyle,
BlueSpacing20,
} from '../../BlueComponents';
import PropTypes from 'prop-types';
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
import { Icon } from 'react-native-elements';
@ -88,16 +96,10 @@ export default class LNDViewInvoice extends Component {
}
handleBackButton() {
this.props.navigation.dismiss();
return true;
}
copyToClipboard = () => {
this.setState({ addressText: loc.receive.details.copiedToClipboard }, () => {
Clipboard.setString(this.state.invoice.payment_request);
setTimeout(() => this.setState({ addressText: this.state.invoice.payment_request }), 1000);
});
};
onLayout = () => {
const { height } = Dimensions.get('window');
this.setState({ qrCodeHeight: height > width ? width - 20 : width / 2 });
@ -181,12 +183,11 @@ export default class LNDViewInvoice extends Component {
flex: 1,
alignItems: 'center',
marginTop: 8,
paddingHorizontal: 16,
justifyContent: 'space-between',
}}
onLayout={this.onLayout}
>
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center', paddingHorizontal: 16 }}>
<QRFast
value={typeof this.state.invoice === 'object' ? invoice.payment_request : invoice}
fgColor={BlueApp.settings.brandingColor}
@ -198,11 +199,7 @@ export default class LNDViewInvoice extends Component {
<BlueSpacing20 />
{invoice && invoice.amt && <BlueText>Please pay {invoice.amt} sats</BlueText>}
{invoice && invoice.description && <BlueText>For: {invoice.description}</BlueText>}
<TouchableOpacity onPress={this.copyToClipboard}>
<Animated.Text style={styles.address} numberOfLines={0}>
{this.state.addressText}
</Animated.Text>
</TouchableOpacity>
<BlueCopyTextToClipboard text={this.state.invoice.payment_request} />
<BlueButton
icon={{
@ -235,15 +232,6 @@ export default class LNDViewInvoice extends Component {
}
}
const styles = StyleSheet.create({
address: {
marginVertical: 32,
fontSize: 15,
color: '#9aa0aa',
textAlign: 'center',
},
});
LNDViewInvoice.propTypes = {
navigation: PropTypes.shape({
goBack: PropTypes.function,

View file

@ -85,6 +85,12 @@ export default class ScanLndInvoice extends React.Component {
return this.props.navigation.goBack();
}
// handling BIP21 w/BOLT11 support
let ind = data.indexOf('lightning=');
if (ind !== -1) {
data = data.substring(ind + 10).split('&')[0];
}
data = data.replace('LIGHTNING:', '').replace('lightning:', '');
console.log(data);

View file

@ -1,8 +1,16 @@
import React, { Component } from 'react';
import { Animated, StyleSheet, View, TouchableOpacity, Clipboard, Share } from 'react-native';
import { View, Share } from 'react-native';
import { QRCode } from 'react-native-custom-qr-codes';
import bip21 from 'bip21';
import { BlueLoading, SafeBlueArea, BlueButton, BlueButtonLink, BlueNavigationStyle, is } from '../../BlueComponents';
import {
BlueLoading,
SafeBlueArea,
BlueCopyTextToClipboard,
BlueButton,
BlueButtonLink,
BlueNavigationStyle,
is,
} from '../../BlueComponents';
import PropTypes from 'prop-types';
/** @type {AppStorage} */
let BlueApp = require('../../BlueApp');
@ -70,13 +78,6 @@ export default class ReceiveDetails extends Component {
}
}
copyToClipboard = () => {
this.setState({ addressText: loc.receive.details.copiedToClipboard }, () => {
Clipboard.setString(this.state.address);
setTimeout(() => this.setState({ addressText: this.state.address }), 1000);
});
};
render() {
console.log('render() receive/details, address,secret=', this.state.address, ',', this.state.secret);
if (this.state.isLoading) {
@ -94,11 +95,7 @@ export default class ReceiveDetails extends Component {
backgroundColor={BlueApp.settings.brandingColor}
logo={require('../../img/qr-code.png')}
/>
<TouchableOpacity onPress={this.copyToClipboard}>
<Animated.Text style={styles.address} numberOfLines={0}>
{this.state.addressText}
</Animated.Text>
</TouchableOpacity>
<BlueCopyTextToClipboard text={this.state.addressText} />
</View>
<View style={{ marginBottom: 24, alignItems: 'center' }}>
<BlueButtonLink
@ -129,15 +126,6 @@ export default class ReceiveDetails extends Component {
}
}
const styles = StyleSheet.create({
address: {
marginVertical: 32,
fontSize: 15,
color: '#9aa0aa',
textAlign: 'center',
},
});
ReceiveDetails.propTypes = {
navigation: PropTypes.shape({
goBack: PropTypes.function,

View file

@ -1,21 +1,9 @@
import React, { Component } from 'react';
import {
StyleSheet,
View,
Share,
TextInput,
KeyboardAvoidingView,
Clipboard,
Animated,
TouchableOpacity,
Platform,
Dimensions,
ScrollView,
} from 'react-native';
import { View, Share, TextInput, KeyboardAvoidingView, Platform, Dimensions, ScrollView } from 'react-native';
import { QRCode as QRSlow } from 'react-native-custom-qr-codes';
import QRFast from 'react-native-qrcode';
import bip21 from 'bip21';
import { SafeBlueArea, BlueButton, BlueNavigationStyle, BlueBitcoinAmount, BlueText } from '../../BlueComponents';
import { SafeBlueArea, BlueButton, BlueNavigationStyle, BlueBitcoinAmount, BlueText, BlueCopyTextToClipboard } from '../../BlueComponents';
import PropTypes from 'prop-types';
/** @type {AppStorage} */
let BlueApp = require('../../BlueApp');
@ -52,13 +40,6 @@ export default class ReceiveAmount extends Component {
};
}
copyToClipboard = () => {
this.setState({ addressText: loc.receive.details.copiedToClipboard }, () => {
Clipboard.setString(this.state.bip21);
setTimeout(() => this.setState({ addressText: this.state.address }), 1000);
});
};
determineSize = () => {
if (width > 312) {
return width - 48;
@ -133,11 +114,7 @@ export default class ReceiveAmount extends Component {
)}
</View>
<View style={{ marginBottom: 24, alignItems: 'center', justifyContent: 'space-between' }}>
<TouchableOpacity onPress={this.copyToClipboard}>
<Animated.Text style={styles.address} numberOfLines={0}>
{this.state.bip21}
</Animated.Text>
</TouchableOpacity>
<BlueCopyTextToClipboard text={this.state.bip21} />
</View>
</View>
);
@ -181,12 +158,3 @@ export default class ReceiveAmount extends Component {
);
}
}
const styles = StyleSheet.create({
address: {
marginVertical: 32,
fontSize: 15,
color: '#9aa0aa',
textAlign: 'center',
},
});

View file

@ -109,7 +109,7 @@ export default class About extends Component {
};
Rate.rate(options, success => {
if (success) {
console.warn('User Rated.');
console.log('User Rated.');
}
});
}}

View file

@ -1,6 +1,14 @@
import React, { Component } from 'react';
import { Animated, Linking, StyleSheet, View, TouchableOpacity, Clipboard } from 'react-native';
import { BlueNavigationStyle, BlueLoading, SafeBlueArea, BlueButton, BlueText, BlueSpacing40 } from '../../BlueComponents';
import { Linking, View } from 'react-native';
import {
BlueNavigationStyle,
BlueCopyTextToClipboard,
BlueLoading,
SafeBlueArea,
BlueButton,
BlueText,
BlueSpacing40,
} from '../../BlueComponents';
import PropTypes from 'prop-types';
/** @type {AppStorage} */
let BlueApp = require('../../BlueApp');
@ -58,13 +66,6 @@ export default class BuyBitcoin extends Component {
}
}
copyToClipboard = () => {
this.setState({ addressText: loc.buyBitcoin.copied }, () => {
Clipboard.setString(this.state.address);
setTimeout(() => this.setState({ addressText: this.state.address }), 1000);
});
};
render() {
console.log('render() receive/details, address,secret=', this.state.address, ',', this.state.secret);
if (this.state.isLoading) {
@ -77,11 +78,7 @@ export default class BuyBitcoin extends Component {
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center', paddingHorizontal: 16 }}>
<BlueText>{loc.buyBitcoin.tap_your_address}</BlueText>
<TouchableOpacity onPress={this.copyToClipboard}>
<Animated.Text style={styles.address} numberOfLines={0}>
{this.state.addressText}
</Animated.Text>
</TouchableOpacity>
<BlueCopyTextToClipboard text={this.state.addressText} />
<BlueButton
icon={{
@ -108,15 +105,6 @@ export default class BuyBitcoin extends Component {
}
}
const styles = StyleSheet.create({
address: {
marginVertical: 32,
fontSize: 15,
color: '#9aa0aa',
textAlign: 'center',
},
});
BuyBitcoin.propTypes = {
navigation: PropTypes.shape({
goBack: PropTypes.function,

View file

@ -1,7 +1,7 @@
import React, { Component } from 'react';
import { Dimensions, Platform, ActivityIndicator, View, Clipboard, Animated, TouchableOpacity } from 'react-native';
import { Dimensions, Platform, ActivityIndicator, View } from 'react-native';
import { QRCode as QRSlow } from 'react-native-custom-qr-codes';
import { BlueSpacing20, SafeBlueArea, BlueText, BlueNavigationStyle } from '../../BlueComponents';
import { BlueSpacing20, SafeBlueArea, BlueText, BlueNavigationStyle, BlueCopyTextToClipboard } from '../../BlueComponents';
import PropTypes from 'prop-types';
const QRFast = require('react-native-qrcode');
/** @type {AppStorage} */
@ -49,13 +49,6 @@ export default class WalletXpub extends Component {
}, 1000);
}
copyToClipboard = () => {
this.setState({ xpubText: loc.wallets.xpub.copiedToClipboard }, () => {
Clipboard.setString(this.state.xpub);
setTimeout(() => this.setState({ xpubText: this.state.xpub }), 1000);
});
};
onLayout = () => {
const { height } = Dimensions.get('window');
this.setState({ qrCodeHeight: height > width ? width - 40 : width / 2 });
@ -110,11 +103,7 @@ export default class WalletXpub extends Component {
}
})()}
<BlueSpacing20 />
<TouchableOpacity onPress={this.copyToClipboard}>
<Animated.Text style={{ paddingHorizontal: 8, textAlign: 'center' }} numberOfLines={0}>
{this.state.xpubText}
</Animated.Text>
</TouchableOpacity>
<BlueCopyTextToClipboard text={this.state.xpubText} />
</View>
</SafeBlueArea>
);