diff --git a/App.js b/App.js index bef711a..ac5fec3 100644 --- a/App.js +++ b/App.js @@ -33,17 +33,16 @@ import XButton from "./src/components/XButton"; import Camera from "./src/components/Camera"; import SelectCoin from "./src/components/SelectCoin"; import SendTransaction from "./src/components/SendTransaction"; +import SweepPrivateKey from "./src/components/SweepPrivateKey"; import Settings from "./src/components/Settings"; import Biometrics from "./src/components/Biometrics"; import PinPad from "./src/components/PinPad"; import Loading from "./src/components/Loading"; +//import ElectrumTesting from "./src/components/ElectrumTesting"; import * as electrum from "./src/utils/electrum"; import nodejs from "nodejs-mobile-react-native"; import bitcoinUnits from "bitcoin-units"; -const { - networks -} = require("./src/utils/networks"); const { Constants: { @@ -55,7 +54,6 @@ const { getDifferenceBetweenDates, isOnline, getNetworkType, - getAddress, pauseExecution, capitalize, getInfoFromAddressPath, @@ -63,59 +61,69 @@ const { validatePrivateKey } = require("./src/utils/helpers"); const { width } = Dimensions.get("window"); -const bitcoin = require("rn-bitcoinjs-lib"); const bip39 = require("bip39"); const moment = require("moment"); export default class App extends PureComponent { - + state = { upperContentFlex: new Animated.Value(1), lowerContentFlex: new Animated.Value(0), - + displayCameraRow: false, cameraRowOpacity: new Animated.Value(0), - + displayReceiveTransaction: false, receiveTransactionOpacity: new Animated.Value(0), - + displayTransactionDetail: false, transactionDetailOpacity: new Animated.Value(0), - + displaySelectCoin: false, selectCoinOpacity: new Animated.Value(0), - + displayLoading: false, loadingOpacity: new Animated.Value(0), - + displayTextInput: false, textInputOpacity: new Animated.Value(0), - + + displaySweepPrivateKey: false, + sweepPrivateKeyOpacity: new Animated.Value(0), + displayPriceHeader: false, priceHeaderOpacity: new Animated.Value(0), - + displayXButton: false, xButtonOpacity: new Animated.Value(0), - + displayCamera: false, cameraOpacity: new Animated.Value(0), - + displaySettings: false, settingsOpacity: new Animated.Value(0), - + displayBiometrics: false, biometricsOpacity: new Animated.Value(0), displayBiometricAuthenticationRetry: false, - + displayPin: false, pinOpacity: new Animated.Value(0), - + displayTransactionList: true, transactionListOpacity: new Animated.Value(0), - + appState: AppState.currentState, appHasLoaded: false, - + + /* + I do wonder how long it will take for someone to find and sweep this private key... + Addr: bc1qcgt450ctz7c0zpgzq0wmua3je7mmpzzed9wyfg + Priv: L323HBXNkhn4ogPvmMZBa5fFVE7BjL6f5osyXYxmVsUjNoBvYAHG + */ + //Only used to pass as a prop to SweepPrivateKey when sweeping a private key. + privateKey: "", + address: "", amount: 0, optionSelected: "", @@ -125,7 +133,7 @@ export default class App extends PureComponent { loadingTransactions: true, loadingAnimationName: "cloudBook" }; - + setExchangeRate = async ({selectedCrypto = "bitcoin"} = {}) => { //const start = this.props.transaction.feeTimestamp; //const end = new Date(); @@ -142,7 +150,7 @@ export default class App extends PureComponent { } //} }; - + onCoinPress = async ({ coin = "bitcoin", wallet = "wallet0", initialLoadingMessage = "" } = {}) => { try { const sameCoin = this.props.wallet.selectedCrypto === coin; @@ -151,12 +159,12 @@ export default class App extends PureComponent { this.resetView(); return; } - + this.updateSelectCoin({display: false, duration: 200}); - + const network = getNetworkType(coin); await this.props.updateWallet({ selectedCrypto: coin, network, selectedWallet: wallet }); - + if (this.props.wallet[wallet].addresses[coin].length > 0) { //This condition occurs when the user selects a coin that already has generated addresses from the "SelectCoin" view. this.updateLoading({ display: false }); @@ -175,7 +183,7 @@ export default class App extends PureComponent { }); return; } - + this.setState({ loadingTransactions: true }); InteractionManager.runAfterInteractions(() => { this.refreshWallet({reconnectToElectrum: !sameCoin}); @@ -185,15 +193,15 @@ export default class App extends PureComponent { this.resetView(); } }; - + launchDefaultFuncs = async ({ displayLoading = true, resetView = true } = {}) => { this.updateBiometrics({display: false}); this.updatePin({display: false}); if (displayLoading) this.updateLoading({display: true}); - + //Push user to the default view while the rest of the wallet data loads. //this.resetView(); - + try { const onBack = () => { if (this.state.displayPin || this.state.displayBiometrics || this.state.appHasLoaded === false) return true; @@ -214,10 +222,10 @@ export default class App extends PureComponent { } catch (e) { console.log(e); } - + this.startDate = new Date(); clearInterval(this._refreshWallet); - + this.setState({ loadingMessage: "Fetching Network Status", loadingProgress: 0.35, appHasLoaded: true }); /* Clear or Reset any pending transactions to start from a clean slate. @@ -226,29 +234,30 @@ export default class App extends PureComponent { */ const isConnected = await isOnline(); await Promise.all([this.props.resetTransaction(), this.props.updateUser({ isOnline: isConnected })]); - + //If online, connect to an electrum server. if (isConnected) { this.refreshWallet(); - await pauseExecution() + await pauseExecution(); } else { //Device is offline. Ensure any loading animations are disabled. await pauseExecution(); this.setState({ loadingTransactions: false }); } - + //Push user to the default view while the rest of the wallet data loads. if (resetView) this.resetView(); }; - + refreshWallet = async ({ ignoreLoading = false, reconnectToElectrum = true } = {}) => { //This helps to prevent the app from disconnecting and stalling when attempting to connect to an electrum server after some time. - await nodejs.start("main.js"); - + //await nodejs.start("main.js"); + try { //Enable the loading state this.setState({ loadingTransactions: true }); - + const { selectedWallet, selectedCrypto } = this.props.wallet; + //Check if the user is online const isConnected = await isOnline(); if (!isConnected) { @@ -258,26 +267,18 @@ export default class App extends PureComponent { alert("Your device is currently offline. Please check your network connection and try again."); return; } + //Save isConnected state to isOnline. if (this.props.user.isOnline === false) this.props.updateUser({ isOnline: isConnected }); - - const { selectedWallet, selectedCrypto } = this.props.wallet; - - await this.setExchangeRate({ selectedCrypto }); //Set the exchange rate for the selected currency - + + this.setExchangeRate({ selectedCrypto }); //Set the exchange rate for the selected currency //Update status of the user-facing loading message and progress bar if (!ignoreLoading) this.setState({ loadingMessage: "Connecting to Electrum Server...", loadingProgress: 0.4 }); - + if (reconnectToElectrum) { - //Disconnect from the currently connected Electrum server. Not entirely sure if this is necessary, but it seems to prevent the app from stalling in certain scenarios. - await electrum.stop(); - //Spin up electrum, connect to a peer & start Electrum's keep-alive function; //Returns: { customPeers: [], data: { host: "" port: 443 }, error: false, method: "connectToPeer" } - const electrumStartResponse = await electrum.start({ - coin: selectedCrypto, - customPeers: this.props.settings.customPeers[selectedCrypto] - }); + const electrumStartResponse = await this.restartElectrum({ coin: selectedCrypto }); if (electrumStartResponse.error === false) { //Set the current electrum peer. this.props.updateSettings({currentPeer: electrumStartResponse.data}); @@ -290,32 +291,30 @@ export default class App extends PureComponent { } //Remove any pre-existing instance of this._refreshWallet clearInterval(this._refreshWallet); - - if (!__DEV__) { - //Set an interval to run this.refreshWallet approximately every 2 minutes. - this._refreshWallet = setInterval(async () => { - const currentTime = new Date(); - const seconds = (currentTime.getTime() - this.startDate.getTime()) / 1000; - let end = moment(); - let difference = 0; - try { - end = this.props.wallet[selectedWallet].lastUsedAddress[selectedCrypto]; - } catch (e) { - } - try { - difference = getDifferenceBetweenDates({start: moment(), end}); - } catch (e) { - } - if (seconds > 10 && difference >= 1.8) { - await this.refreshWallet(); - } - }, 60 * 2000); - } + + //Set an interval to run this.refreshWallet approximately every 2 minutes. + this._refreshWallet = setInterval(async () => { + const currentTime = new Date(); + const seconds = (currentTime.getTime() - this.startDate.getTime()) / 1000; + let end = moment(); + let difference = 0; + try { + end = this.props.wallet[selectedWallet].lastUsedAddress[selectedCrypto]; + } catch (e) { + } + try { + difference = getDifferenceBetweenDates({start: moment(), end}); + } catch (e) { + } + if (seconds > 10 && difference >= 1.8) { + await this.refreshWallet(); + } + }, 60 * 2000); } - + //Update status of the user-facing loading message and progress bar if (ignoreLoading === false) this.setState({ loadingMessage: "Getting Current Block Height & Exchange Rate...", loadingProgress: 0.5 }); - + //Gather existing addresses, changeAddresses and their respective indexes for use later on let addresses = []; try { @@ -325,10 +324,10 @@ export default class App extends PureComponent { try { changeAddresses = this.props.wallet[selectedWallet].changeAddresses[selectedCrypto]; } catch (e) {} - + let addressIndex = this.props.wallet[selectedWallet].addressIndex[selectedCrypto]; let changeAddressIndex = this.props.wallet[selectedWallet].changeAddressIndex[selectedCrypto]; - + /* //Rescan Addresses if user is waiting for any pending transactions await Promise.all(this.props.wallet[selectedWallet].addresses[selectedCrypto].map((add, i) => { @@ -339,40 +338,41 @@ export default class App extends PureComponent { if (add.block <= 0 && i < changeAddressIndex) changeAddressIndex = i; })); */ - + //Gather existing utxo's for use later on let utxos = []; try { utxos = this.props.wallet[selectedWallet].utxos[selectedCrypto] || []; } catch (e) { } - + //Specify the threshold at which the app will continue searching empty addresses before giving up. - const indexThreshold = addresses.length < 20 ? addresses.length-1 : 20; - + //const indexThreshold = addresses.length < 20 ? addresses.length-1 : 20; + + //Get & Set Current Block Height await this.props.updateBlockHeight({ selectedCrypto }); const currentBlockHeight = this.props.wallet.blockHeight[selectedCrypto]; - + //Update status of the user-facing loading message and progress bar if (ignoreLoading === false) this.setState({ loadingMessage: "Generating Addresses,\nUpdating Transactions.\nThis may take a while...", loadingProgress: 0.65 }); - + //This function loads up the user's transaction history for the transaction list, gathers the wallet's next available addresses/changeAddresses and creates more as needed //TODO: This function is way too large/multipurpose and needs to be broken up for easier use and testing. - await this.props.getNextAvailableAddress({ addresses, changeAddresses, addressIndex, changeAddressIndex, indexThreshold: 1, currentBlockHeight, selectedCrypto, selectedWallet, wallet: selectedWallet, customPeers: this.props.settings.customPeers[selectedCrypto] }); + await this.props.getNextAvailableAddress({ addresses, changeAddresses, addressIndex, changeAddressIndex, indexThreshold: 1, currentBlockHeight, selectedCrypto, selectedWallet, wallet: selectedWallet, customPeers: this.props.settings.customPeers[selectedCrypto] }); + //Update status of the user-facing loading message and progress bar if (ignoreLoading === false) this.setState({ loadingMessage: "Updating UTXO's", loadingProgress: 0.8 }); - + //Fetch any new utxos. //Re-gather all known addresses and changeAddresses in case more were created from the getNextAvailableAddress function. addresses = this.props.wallet[selectedWallet].addresses[selectedCrypto]; changeAddresses = this.props.wallet[selectedWallet].changeAddresses[selectedCrypto]; - + //Scan all addresses & changeAddresses for UTXO's and save them. //Note: The app uses the saved UTXO response to verify/update the wallet's balance. const resetUtxosResponse = await this.props.resetUtxos({ addresses, changeAddresses, currentUtxos: utxos, selectedCrypto, selectedWallet, wallet: selectedWallet, currentBlockHeight }); - //Iterate over the new utxos and rescan the transactions if a utxo with a new hash appears let needsToRescanTransactions = false; addressIndex = this.props.wallet[selectedWallet].addressIndex[selectedCrypto]; @@ -384,8 +384,8 @@ export default class App extends PureComponent { if (newUtxo.tx_hash === transaction.hash) { noHashMatches = false; } - } catch (e) { console.log(e) } - + } catch (e) {} + })); //If the transactions need to be rescanned set the index. Use the lowest index based on the results. if (noHashMatches) { @@ -398,10 +398,10 @@ export default class App extends PureComponent { } else { if (Number(pathInfo.addressIndex) < addressIndex) addressIndex = pathInfo.addressIndex; } - } catch (e) { console.log(e) } + } catch (e) {} } })); - + //Check if any transactions have <1 confirmations. If so, rescan them by the lowest index. let transactionsThatNeedRescanning = []; await Promise.all(this.props.wallet[selectedWallet].transactions[selectedCrypto].map((transaction) => { @@ -410,31 +410,30 @@ export default class App extends PureComponent { transactionsThatNeedRescanning.push(transaction.address); } })); - + //Push all addresses and changeAddresses into the same array. const allAddresses = this.props.wallet[selectedWallet].addresses[selectedCrypto].concat(this.props.wallet[selectedWallet].changeAddresses[selectedCrypto]); - + //Get lowest index to rescan addresses & changeAddresses with. await Promise.all( transactionsThatNeedRescanning.map(async (transactionAddress) => { try { //Filter for the transaction address const filteredTransactionAddress = allAddresses.filter((address) => address.address === transactionAddress); - //Extract the addresses path (Ex: m/49'/1'/0'/1/6) const path = filteredTransactionAddress[0].path; const pathInfo = await getInfoFromAddressPath(path); - + //Check the path's index and save the lowest value. if (pathInfo.isChangeAddress) { if (Number(pathInfo.addressIndex) < changeAddressIndex) changeAddressIndex = pathInfo.addressIndex; } else { if (Number(pathInfo.addressIndex) < addressIndex) addressIndex = pathInfo.addressIndex; } - } catch (e) { console.log(e); } + } catch (e) {} }) ); - + /* let transactionPathsThatNeedRescanning = []; await Promise.all(this.props.wallet[selectedWallet].transactions[selectedCrypto].map((transaction) => { @@ -454,16 +453,16 @@ export default class App extends PureComponent { }) ); */ - + //Begin Rescan of transactions if necessary based on the saved path indexes. let getNextAvailableAddressResponse = { error: false, data: [] }; if (needsToRescanTransactions) { getNextAvailableAddressResponse = await this.props.getNextAvailableAddress({ addresses, changeAddresses, addressIndex, changeAddressIndex, indexThreshold: 1, currentBlockHeight, selectedCrypto, selectedWallet, wallet: selectedWallet, customPeers: this.props.settings.customPeers[selectedCrypto] }); } - + //Update status of the user-facing loading message and progress bar if (ignoreLoading === false) this.setState({ loadingMessage: "Updating Balance", loadingProgress: 1 }); - + //If there was no issue fetching the UTXO sets or the next available addresses, update the balance using the newly acquired UTXO's. if (resetUtxosResponse.error === false && getNextAvailableAddressResponse.error === false) { try { @@ -473,7 +472,7 @@ export default class App extends PureComponent { console.log(e); } } - + //Cease the loading state. this.setState({ loadingTransactions: false }); } catch (e) { @@ -481,14 +480,14 @@ export default class App extends PureComponent { this.setState({ loadingTransactions: false }); } }; - + authenticateUserWithBiometrics = () => { const optionalConfigObject = { unifiedErrors: false // use unified error messages (default false) }; const authenticate = () => { TouchID.authenticate("To open Bitbip", optionalConfigObject) - .then(success => { + .then(() => { //Hide the retry button on the Biometric Authentication view. this.setState({ displayBiometricAuthenticationRetry: false }); //Forward the user to the Pin view if they've enabled it. Otherwise, forward them to the app via launchDefaultFuncs. @@ -499,7 +498,7 @@ export default class App extends PureComponent { } this.launchDefaultFuncs(); }) - .catch(error => { + .catch(() => { //Display the retry button on the Biometric Authentication view in case they hit cancel or encountered some other error during the authentication process. this.setState({ displayBiometricAuthenticationRetry: true }); }); @@ -514,16 +513,18 @@ export default class App extends PureComponent { authenticate(); //TouchID is supported. } }) - .catch(e => {}); + .catch(() => {}); }; - + createWallet = async (walletName = "wallet0", ignoreAddressCheck = false) => { try { + const { selectedWallet, selectedCrypto } = this.props.wallet; + this.updateBiometrics({display: false}); this.updatePin({display: false}); await this.updateLoading({display: true}); await this.props.updateWallet({ selectedCrypto: "bitcoin" }); - + //Figure out what type of security/authentication is allowed for settings. let biometricsIsSupported = false; let biometricTypeSupported = ""; @@ -541,8 +542,10 @@ export default class App extends PureComponent { } this.props.updateSettings({ biometricsIsSupported, biometricTypeSupported }); }) - .catch(e => { this.props.updateSettings({ biometricsIsSupported, biometricTypeSupported }) }); - + .catch(() => { + this.props.updateSettings({ biometricsIsSupported, biometricTypeSupported }); + }); + //Create Wallet if first timer this.setState({loadingMessage: "Creating Wallet...", loadingProgress: 0.1}); await this.props.createWallet({addressAmount: 2, changeAddressAmount: 2, wallet: walletName, generateAllAddresses: true}); @@ -550,11 +553,7 @@ export default class App extends PureComponent { const wallets = this.props.wallet.wallets.concat(walletName); //Set the selectedWallet accordingly and update the wallets array. await this.props.updateWallet({ selectedWallet: walletName, wallets }); - const { selectedWallet, selectedCrypto } = this.props.wallet; this.setState({loadingMessage: "Fetching Current Block Height...", loadingProgress: 0.15}); - //Get Current Block Height - await this.props.updateBlockHeight({ selectedCrypto }); - const currentBlockHeight = this.props.wallet.blockHeight[selectedCrypto]; let addresses = []; try { addresses = this.props.wallet[selectedWallet].addresses[selectedCrypto]; @@ -566,6 +565,14 @@ export default class App extends PureComponent { } catch (e) { } if (ignoreAddressCheck === false) { + //Spin up the nodejs thread and connect to electrum. + //await nodejs.start("main.js"); + //await electrum.stop({ coin: selectedCrypto }); + await this.restartElectrum({ coin: selectedCrypto }); + //Get Current Block Height + await this.props.updateBlockHeight({ selectedCrypto }); + const currentBlockHeight = this.props.wallet.blockHeight[selectedCrypto]; + this.setState({loadingMessage: "Syncing...", loadingProgress: 0.15}); //Load up the user's transaction history and next available addresses await this.props.getNextAvailableAddress({ @@ -577,23 +584,18 @@ export default class App extends PureComponent { currentBlockHeight, wallet: selectedWallet }); - this.setState({loadingMessage: "Finished Creating Wallet", loadingProgress: 0.3}); + this.setState({loadingMessage: "Finished Creating Wallet", loadingProgress: 0.3, loadingAnimationName: "cloudBook"}); } this.launchDefaultFuncs({ displayLoading: false }); } catch (e) { console.log(e); } }; - - async componentWillMount() { - //Spin up the nodejs thread - await nodejs.start("main.js"); - } - + _handleAppStateChange = async (nextAppState) => { //Foreground -> Background if (this.state.appState.match(/active/) && nextAppState.match(/inactive|background/) && !this.state.displayCamera) { - electrum.stop(); + electrum.stop({ coin: this.props.wallet.selectedCrypto }); //Clear/Remove Wallet Refresh Timer clearInterval(this._refreshWallet); this.setState({appHasLoaded: false}); @@ -611,7 +613,7 @@ export default class App extends PureComponent { this.updateReceiveTransaction({display: false}); this.updateFlex({upperContentFlex: 1, lowerContentFlex: 0}); //this.updateLoading({ display: true }); - + try { //Determine if user is a first timer. Create a new wallet if so. if (this.props.wallet.wallet0.addresses.bitcoin.length === 0) { @@ -619,7 +621,7 @@ export default class App extends PureComponent { return; } } catch (e) {} - + try { //Check if Biometrics is Enabled if (this.props.settings.biometrics) { @@ -627,7 +629,7 @@ export default class App extends PureComponent { return; } } catch (e) {} - + try { //Check if Pin is Enabled if (this.props.settings.pin) { @@ -640,9 +642,10 @@ export default class App extends PureComponent { /* const { selectedCrypto } = this.props.wallet; await nodejs.start("main.js"); - await electrum.stop(); + await electrum.stop({ coin: selectedCrypto }); await electrum.start({ coin: selectedCrypto, + peers: this.props.settings.peers[selectedCrypto], customPeers: this.props.settings.customPeers[selectedCrypto] }); */ @@ -652,45 +655,49 @@ export default class App extends PureComponent { } this.setState({appState: nextAppState}); }; - + async componentDidMount() { //This gets called after redux-persist rehydrates - InteractionManager.runAfterInteractions(() => { + //Spin up the nodejs thread + await nodejs.start("main.js"); + + InteractionManager.runAfterInteractions(async () => { try { //Determine if the user has any existing wallets. Create a new wallet if so. if (this.props.wallet.wallets.length === 0) { this.createWallet("wallet0", true); return; } - + //Check if Biometrics is Enabled if (this.props.settings.biometrics) { this.onBiometricsPress(); return; } - + //Check if Pin is Enabled if (this.props.settings.pin) { this.onPinPress(); return; } - + //Resume normal operations this.launchDefaultFuncs(); } catch (e) { console.log(e); } }); + } - - componentWillUpdate() { + + componentDidUpdate() { if (Platform.OS === "ios") LayoutAnimation.easeInEaseOut(); } - + componentWillUnmount() { try { //Stop Electrum Process - electrum.stop(); + electrum.stop({ coin: this.props.wallet.selectedCrypto }); //Remove Back Button Listener BackHandler.removeEventListener("hardwareBackPress", this.resetView); //Start the listener that detects if the app is in the background or foreground @@ -701,7 +708,7 @@ export default class App extends PureComponent { console.log(e); } } - + //Handles The "upper" & "lower" Flex Animation updateFlex = ({upperContentFlex = 1, lowerContentFlex = 1, duration = 250} = {}) => { return new Promise(async (resolve) => { @@ -731,7 +738,7 @@ export default class App extends PureComponent { } }); }; - + //Handles The "QRCode" Opacity Animation updateReceiveTransaction = async ({ display = true, duration = 400 } = {}) => { return new Promise(async (resolve) => { @@ -754,7 +761,7 @@ export default class App extends PureComponent { } }); }; - + //Handles The "TransactionDetail" Opacity Animation updateTransactionDetail = async ({ display = true, duration = 400 } = {}) => { return new Promise(async (resolve) => { @@ -776,7 +783,7 @@ export default class App extends PureComponent { } }); }; - + //Handles The "SelectCoin" Opacity Animation updateSelectCoin = async ({ display = true, duration = 400 } = {}) => { return new Promise(async (resolve) => { @@ -798,7 +805,7 @@ export default class App extends PureComponent { } }); }; - + //Handles The "Loading" Opacity Animation updateLoading = async ({ display = true, duration = 600 } = {}) => { return new Promise(async (resolve) => { @@ -820,7 +827,7 @@ export default class App extends PureComponent { } }); }; - + //Handles The "TransactionList" View Opacity Animation updateTransactionList = async ({ display = true, duration = 0 } = {}) => { return new Promise(async (resolve) => { @@ -843,7 +850,7 @@ export default class App extends PureComponent { } }); }; - + //Handles The "Camera" View Opacity Animation updateCamera = async ({ display = true, duration = 400 } = {}) => { return new Promise(async (resolve) => { @@ -865,7 +872,7 @@ export default class App extends PureComponent { } }); }; - + //Handles The "Settings" View Opacity Animation updateSettings = async ({ display = true, duration = 400 } = {}) => { return new Promise(async (resolve) => { @@ -887,7 +894,7 @@ export default class App extends PureComponent { } }); }; - + //Handles The "Biometrics" View Opacity Animation updateBiometrics = async ({ display = true, duration = 400 } = {}) => { return new Promise(async (resolve) => { @@ -910,7 +917,7 @@ export default class App extends PureComponent { } }); }; - + //Handles The "PinPad" View Opacity Animation updatePin = async ({ display = true, duration = 400 } = {}) => { return new Promise(async (resolve) => { @@ -932,7 +939,7 @@ export default class App extends PureComponent { } }); }; - + //Handles The "Header" Opacity Animation updatePriceHeader = async ({ display = true, duration = 400 } = {}) => { return new Promise(async (resolve) => { @@ -955,7 +962,7 @@ export default class App extends PureComponent { } }); }; - + //Handles The TextInput Opacity Animation updateTextInput = async ({ display = true, duration = 400 } = {}) => { return new Promise(async (resolve) => { @@ -978,7 +985,29 @@ export default class App extends PureComponent { } }); }; - + + updateSweepPrivateKey = async ({ display = true, duration = 400 } = {}) => { + return new Promise(async (resolve) => { + try { + if (display) this.setState({ displaySweepPrivateKey: display }); + Animated.timing( + this.state.sweepPrivateKeyOpacity, + { + toValue: display ? 1 : 0, + duration + } + ).start(async () => { + //Perform any other action after the update has been completed. + if (!display) this.setState({ displaySweepPrivateKey: display }); + resolve({error: false}); + }); + } catch (e) { + console.log(e); + resolve({ error: true, data: e }); + } + }); + }; + //Handles The "Send", "Camera" and "Receive" Row Opacity Animation updateCameraRow = async ({ display = true, duration = 400 } = {}) => { return new Promise(async (resolve) => { @@ -1001,7 +1030,7 @@ export default class App extends PureComponent { } }); }; - + //Handles The "X" Button Opacity Animation updateXButton = async ({ display = true, duration = 400 } = {}) => { return new Promise(async (resolve) => { @@ -1024,7 +1053,7 @@ export default class App extends PureComponent { } }); }; - + //Handles the series of animations necessary when the user taps "Send" onSendPress = async () => { try { @@ -1043,7 +1072,32 @@ export default class App extends PureComponent { console.log(e); } }; - + + //Handles the series of animations necessary when the user intends to sweep a transaction + onSweep = async (key = "") => { + try { + if (!key) { + alert("No private key detected."); + return; + } + await this.setState({ privateKey: key }); + //Open Send State + this.updateCameraRow({display: false}); + this.updateXButton({display: true}); + this.updateCamera({ display: false }); + this.updatePriceHeader({display: false, duration: 250}); + this.updateTransactionList({ display: false, duration: 200 }); + this.updateTextInput({display: false, duration: 200}); + this.updateFlex({upperContentFlex: 1, lowerContentFlex: 0}); + this.updateSweepPrivateKey({display: true, duration: Platform.OS === "ios" ? 800 : 300}); + InteractionManager.runAfterInteractions(() => { + this.setState({optionSelected: "send"}); + }); + } catch (e) { + console.log(e); + } + }; + //Handles the series of animations necessary when the user taps "Receive" onReceivePress = async () => { if (this.state.optionSelected !== "receive") { @@ -1068,7 +1122,7 @@ export default class App extends PureComponent { this.updatePriceHeader({display: true, duration: 350}); } }; - + //Handles the series of animations necessary when the user taps a specific transaction from the TransactionList. onTransactionPress = async (transaction = "") => { try { @@ -1079,13 +1133,13 @@ export default class App extends PureComponent { this.updateTransactionList({display: false, duration: 400}); this.updateFlex({upperContentFlex: 0, lowerContentFlex: 1, duration: 400}); this.updateTransactionDetail({display: true}); - + const {selectedWallet, selectedCrypto} = this.props.wallet; transaction = await this.props.wallet[selectedWallet].transactions[selectedCrypto].filter((tx) => tx.hash === transaction); this.props.updateWallet({selectedTransaction: transaction[0]}); } catch (e) {} }; - + //Handles the series of animations necessary when the user taps the selected crypto symbol onSelectCoinPress = async () => { //This prevents any possibility of the user tapping into the view without prior authorization. @@ -1112,7 +1166,7 @@ export default class App extends PureComponent { this.updatePriceHeader({display: true, duration: 350}); } }; - + //Handles the series of animations necessary when the user taps the Camera icon. onCameraPress = async () => { try { @@ -1129,7 +1183,7 @@ export default class App extends PureComponent { console.log(e); } }; - + //Handles the series of animations necessary when the user taps the Settings icon. onSettingsPress = async () => { try { @@ -1147,7 +1201,7 @@ export default class App extends PureComponent { console.log(e); } }; - + //Handles the series of animations necessary to transition the view to handle Biometrics. onBiometricsPress = async () => { try { @@ -1168,7 +1222,7 @@ export default class App extends PureComponent { console.log(e); } }; - + //Handles the series of animations necessary to transition the view to handle Pin. onPinPress = async () => { try { @@ -1190,7 +1244,7 @@ export default class App extends PureComponent { console.log(e); } }; - + //Handles the series of animations necessary to revert the view back to it's original layout. resetView = async () => { this.updateTransactionList({ display: true }); @@ -1198,6 +1252,7 @@ export default class App extends PureComponent { if (this.state.displayCamera) this.updateCamera({ display: false }); if (this.state.displayXButton) this.updateXButton({display: false, duration: 100}); if (this.state.displayTextInput) this.updateTextInput({display: false, duration: 200}); + if (this.state.displaySweepPrivateKey) this.updateSweepPrivateKey({display: false, duration: 200}); if (this.state.displayLoading) this.updateLoading({display: false, duration: 200}); if (this.state.displayReceiveTransaction) this.updateReceiveTransaction({display: false, duration: 200}); if (this.state.displaySettings) this.updateSettings({display: false, duration: 200}); @@ -1207,10 +1262,10 @@ export default class App extends PureComponent { this.updateFlex({ duration: 400 }); InteractionManager.runAfterInteractions(() => { this.props.updateWallet({ selectedTransaction: "" }); - this.setState({optionSelected: "", transactionsAreExpanded: false, loadingAnimationName: "loader"}); + this.setState({optionSelected: "", transactionsAreExpanded: false, loadingAnimationName: "loader", privateKey: ""}); }); }; - + //Handles the series of animations necessary for the user to expand the transaction list. expandTransactions = () => { this.setState({ transactionsAreExpanded: true }); @@ -1222,268 +1277,62 @@ export default class App extends PureComponent { //this.updateReceiveTransaction({display: false, duration: 200}); //this.updateSelectCoin({display: false, duration: 200}); }; - + //Handles any action that requires the Keyboard to be dismissed. dismissKeyboard = async () => { Keyboard.dismiss(); }; - - //Handles any action that requires a private key to be swept. - //This function will auto-sweep the funds of any private key into the user's currently selected wallet. - //TODO: Add ability for user to specify the fee when sweeping. Disable this method until a custom fee can be applied. - sweepPrivateKey = async ({ privateKey = "", network = "bitcoin" } = {}) => { - return new Promise(async (resolve) => { + + restartElectrum = ({ coin = "" } = {}) => { + return new Promise(async(resolve) => { try { - //Switch to the specified network in order to sweep the coins - if (network !== this.props.wallet.selectedCrypto) { - await this.props.updateWallet({selectedCrypto: network}); - //Disconnect from the current electurm server. - await electrum.stop(); - await electrum.start({coin: network, customPeers: this.props.settings.customPeers[network]}); - } - - //Get addresses from the private key - const keyPair = bitcoin.ECPair.fromWIF(privateKey, networks[network]); - const bech32Address = await getAddress(keyPair, networks[network], "bech32"); //Bech32 - const p2shAddress = await getAddress(keyPair, networks[network], "p2sh"); //(3) Address - const p2pkhAddress = await getAddress(keyPair, networks[network], "p2pkh");//(1) Address - - //Get the balance for each address. - const bech32BalanceResult = await Promise.all([ - electrum.getAddressScriptHashBalance({address: bech32Address, id: 6, network: networks[network]}), //Bech32 format demands we use the scriptHash variant of the getAddressBalance function - electrum.getAddressScriptHashMempool({address: bech32Address, id: 5, network: networks[network]}) - ]); - const p2shBalanceResult = await Promise.all([ - electrum.getAddressBalance({address: p2shAddress, id: 1}), - electrum.getMempool({address: p2shAddress, id: 3}), - ]); - const p2pkhBalanceResult = await Promise.all([ - electrum.getAddressBalance({address: p2pkhAddress, id: 2}), - electrum.getMempool({address: p2pkhAddress, id: 4}) - ]); - - let balance = 0; - let bech32Balance = 0; - let p2shBalance = 0; - let p2pkhBalance = 0; - const selectedWallet = this.props.wallet.selectedWallet; - const selectedCrypto = this.props.wallet.selectedCrypto; - const changeAddressIndex = this.props.wallet[selectedWallet].changeAddressIndex[selectedCrypto]; - const changeAddress = this.props.wallet[selectedWallet].changeAddresses[selectedCrypto][changeAddressIndex].address; - //Add up and store all balances from each address - await Promise.all( - bech32BalanceResult.map((balanceResult) => { - if (balanceResult.error === false && !!balanceResult.data && balanceResult.data.constructor === Object) { - try { - let unconfirmed, confirmed = 0; - try { - unconfirmed = balanceResult.data.unconfirmed; - confirmed = balanceResult.data.confirmed; - } catch (e) { - } - balance = Number(unconfirmed) + Number(confirmed) + Number(balance); - bech32Balance = Number(unconfirmed) + Number(confirmed) + Number(bech32Balance); - } catch (e) { - } - } - }), - p2shBalanceResult.map((balanceResult) => { - if (balanceResult.error === false && !!balanceResult.data && balanceResult.data.constructor === Object) { - try { - let unconfirmed, confirmed = 0; - try { - unconfirmed = balanceResult.data.unconfirmed; - confirmed = balanceResult.data.confirmed; - } catch (e) { - } - balance = Number(unconfirmed) + Number(confirmed) + Number(balance); - p2shBalance = Number(unconfirmed) + Number(confirmed) + Number(p2shBalance); - } catch (e) { - } - } - }), - p2pkhBalanceResult.map((balanceResult) => { - if (balanceResult.error === false && !!balanceResult.data && balanceResult.data.constructor === Object) { - try { - let unconfirmed, confirmed = 0; - try { - unconfirmed = balanceResult.data.unconfirmed; - confirmed = balanceResult.data.confirmed; - } catch (e) { - } - balance = Number(unconfirmed) + Number(confirmed) + Number(balance); - p2pkhBalance = Number(unconfirmed) + Number(confirmed) + Number(p2pkhBalance); - } catch (e) { - } - } - }) - ); - - console.log("Logging Address Balances..."); - console.log(`${bech32Address}: ${bech32Balance}`); - console.log(`${p2shAddress}: ${p2shBalance}`); - console.log(`${p2pkhAddress}: ${p2pkhBalance}`); - console.log(`Total Balance: ${balance}`); - - //Setup transaction builder for the given network - let txb = new bitcoin.TransactionBuilder(networks[network]); - - //Set the target to the current wallet's next available changeAddress. - let targets = [{address: changeAddress, value: balance}]; - - //Fetch the utxos for each address - let bech32Utxos, p2shUtxos, p2pkhUtxos = []; - try { - if (bech32Balance) { - const utxoResponse = await electrum.listUnspentAddressScriptHash({ - id: Math.random(), - address: bech32Address, - network: networks[selectedCrypto] - }); - if (utxoResponse.error === false) bech32Utxos = utxoResponse.data; - } - } catch (e) { - bech32Utxos = [] - } - try { - if (p2shBalance) { - const utxoResponse = await electrum.listUnspentAddressScriptHash({ - id: Math.random(), - address: p2shAddress, - network: networks[selectedCrypto] - }); - if (utxoResponse.error === false) p2shUtxos = utxoResponse.data; - } - } catch (e) { - p2shUtxos = [] - } - try { - if (p2pkhBalance) { - const utxoResponse = await electrum.listUnspentAddressScriptHash({ - id: Math.random(), - address: p2pkhAddress, - network: networks[selectedCrypto] - }); - if (utxoResponse.error === false) p2pkhUtxos = utxoResponse.data; - } - } catch (e) { - p2pkhUtxos = [] - } - - //Start adding the inputs for each address if any utxo's are available - try { - //Add Inputs - await Promise.all( - bech32Utxos.map((utxo) => { - const p2wpkh = bitcoin.payments.p2wpkh({ - pubkey: keyPair.publicKey, - network: networks[network] - }); - txb.addInput(utxo.tx_hash, utxo.tx_pos, null, p2wpkh.output); - }) - ); - } catch (e) {} + if (!coin) resolve({ error: true, data: {} }); + + //This helps to prevent the app from disconnecting and stalling when attempting to connect to an electrum server after some time. + //await nodejs.start("main.js"); + + //Disconnect from the currently connected Electrum server. Not entirely sure if this is necessary, but it seems to prevent the app from stalling in certain scenarios. try { - //Add Inputs - await Promise.all( - p2shUtxos.map((utxo) => { - txb.addInput(utxo.tx_hash, utxo.tx_pos) - }) - ); + //await electrum.stop({ coin }); } catch (e) {} + try { - //Add Inputs - await Promise.all( - p2pkhUtxos.map((utxo) => { - txb.addInput(utxo.tx_hash, utxo.tx_pos) - }) - ); - } catch (e) {} - - //Add our next available changeAddress for the given wallet as an output minus whatever the user decides the fee to be. - await Promise.all( - targets.map((target) => { - try { - txb.addOutput(target.address, target.value - 500); - } catch (e) { + let hasPeers = false; + let hasCustomPeers = false; + try {hasPeers = Array.isArray(this.props.settings.peers[coin]) && this.props.settings.peers[coin].length;} catch (e) {} + try {hasCustomPeers = Array.isArray(this.props.settings.customPeers[coin]) && this.props.settings.customPeers[coin].length;} catch (e) {} + + if (!hasPeers && !hasCustomPeers) { + //Attempt to retrieve a list of peers from the default servers. + const startResponse = await electrum.start({ + coin, + peers: [], + customPeers: [] + + }); + if (startResponse.error === true) { + resolve(startResponse); + return; } - }) - ); - - //Loop through and sign - try { - if (bech32Utxos.length > 0) { - await Promise.all( - bech32Utxos.map((utxo, i) => { - try { - txb.sign(i, keyPair, null, null, utxo.value); - } catch (e) { - } - }) - ); + const peers = await electrum.getPeers({coin}); + await this.props.updatePeersList({peerList: peers.data, coin}); } } catch (e) {} - try { - if (p2shUtxos.length > 0) { - let utxoLength = 0; - try { - utxoLength = bech32Utxos.length; - } catch (e) {} - await Promise.all( - p2shUtxos.map((utxo, i) => { - try { - const p2wpkh = bitcoin.payments.p2wpkh({ - pubkey: keyPair.publicKey, - network: networks[network] - }); - const p2sh = bitcoin.payments.p2sh({redeem: p2wpkh, network: networks[network]}); - txb.sign(utxoLength+i, keyPair, p2sh.redeem.output, null, utxo.value); - } catch (e) { - console.log(e); - console.log(i); - } - }) - ); - } - } catch (e) { - console.log(e); + + //Connect to an electrum server + const result = await electrum.start({ coin, peers: this.props.settings.peers[coin], customPeers: this.props.settings.customPeers[coin] }); + if (result.error === true) { + resolve(result); + return; } - try { - if (p2pkhUtxos.length > 0) { - let utxoLength = 0; - try { - utxoLength = bech32Utxos.length; - } catch (e) {} - try { - utxoLength = utxoLength + p2shUtxos.length; - } catch (e) {} - await Promise.all( - p2pkhUtxos.map((utxo, i) => { - try { - txb.sign(utxoLength+i, keyPair); - } catch (e) { - console.log(e); - } - }) - ); - } - } catch (e) {} - - //Create the raw transaction hex - const rawTx = txb.build().toHex(); - console.log(rawTx); - - //Reset the user's view - this.resetView(); - - resolve({error: false, data: rawTx}); - //alert(`Private Key Detected with a balance of ${balance}:\n${privateKey}\nApologies, but private key sweep functionality has not been implemented yet. I'm getting there, I promise! :-)`); + await pauseExecution(); + resolve(result); } catch (e) { console.log(e); } - }) + }); }; - + //Handles any BarCodeRead action. onBarCodeRead = async ({ data }) => { try { @@ -1492,24 +1341,18 @@ export default class App extends PureComponent { await this.updateCamera({ display: false }); this.createNewWallet({ mnemonic: data }); return; - //const result = await importWallet({ updateWallet: this.props.updateWallet, wallets: this.props.wallet.wallets, createWallet: this.createNewWallet, mnemonic: data }); } - + //Determine if we need to sweep a private key const validatePrivateKeyResults = await validatePrivateKey(data); if (validatePrivateKeyResults.isPrivateKey === true) { - + //Remove Camera View await this.updateCamera({ display: false }); - - alert("Private Key Detected. Unfortunately, Bitbip is not able to sweep this key into your wallet just yet, but we're getting there!"); - //Auto sweep the data. - //TODO: Add ability for user to specify the fee when sweeping. Disable this method until a custom fee can be applied. - //this.sweepPrivateKey({ privateKey: data, network: validatePrivateKeyResults.network }); - + this.onSweep(data); return; } - + //Check if this is a BitId Request //TODO: Complete this BitId function. if (data.substr(0, 5).toLowerCase() === "bitid") { @@ -1519,7 +1362,7 @@ export default class App extends PureComponent { //Reveal Sign Message View //await this.updateSignMessage({ display: true }); } - + const qrCodeData = await parsePaymentRequest(data); //Throw error if unable to interpret the qrcode data. if (qrCodeData.error) { @@ -1528,17 +1371,17 @@ export default class App extends PureComponent { return; } InteractionManager.runAfterInteractions(async () => { - + //Switch to proper Electrum Server if the qrcode coin data doesn't match our currently selected coin. if (qrCodeData.data.coin !== this.props.wallet.selectedCrypto) { const coin = qrCodeData.data.coin; await this.props.updateWallet({selectedCrypto: coin}); //Disconnect from the current electurm server. - await electrum.stop(); + //await electrum.stop({ coin }); //Connect to the relevant electrum server as per the qrcode data. - await electrum.start({coin, customPeers: this.props.settings.customPeers[coin]}); + await this.restartElectrum({ coin }); } - + //Pass the transaction data forward for use. this.props.updateTransaction(qrCodeData.data); //Trigger the onSendPress animation to expose the transaction view. @@ -1548,7 +1391,7 @@ export default class App extends PureComponent { //console.log(e); } }; - + //Returns the fiat balance based on the most recent exchange rate of the selected crypto. getFiatBalance = () => { try { @@ -1562,7 +1405,7 @@ export default class App extends PureComponent { return 0; } }; - + //Returns the confirmed balance of the selected crypto. getCryptoBalance = () => { let confirmedBalance = 0; @@ -1572,7 +1415,7 @@ export default class App extends PureComponent { } catch (e) {} return confirmedBalance; }; - + //Returns the next available empty address of the selected crypto. getQrCodeAddress = () => { try { @@ -1583,7 +1426,7 @@ export default class App extends PureComponent { return ""; } }; - + //Returns all transactions for the selected crypto. getTransactions = () => { try { @@ -1598,15 +1441,15 @@ export default class App extends PureComponent { return []; } }; - + onPinFailure = async () => { try { await this.createWallet("wallet0", true); } catch (e) { - + } }; - + createNewWallet = async ({ mnemonic = "" }) => { try { //Get highest wallet number @@ -1623,35 +1466,34 @@ export default class App extends PureComponent { const wallets = this.props.wallet.wallets.concat(walletName); //Set Loading Message await this.setState({loadingMessage: `Creating ${walletName.split('wallet').join('Wallet ')} & Generating Addresses`, loadingProgress: 0.5}); - + //Close Receive State this.updateSelectCoin({display: false}); this.updateSettings({display: false}); this.updateXButton({display: false, duration: 100}); await this.updateLoading({ display: true }); - + //Set the selectedWallet accordingly and update the wallets array. await this.props.updateWallet({ selectedWallet: walletName, wallets }); - + await this.props.createWallet({ wallet: walletName, mnemonic, generateAllAddresses: mnemonic === "" }); - + const { selectedCrypto } = this.props.wallet; - await electrum.stop(); - await electrum.start({ coin: selectedCrypto, customPeers: this.props.settings.customPeers[selectedCrypto] }); + await this.restartElectrum({ coin: selectedCrypto }); //Get Current Block Height this.props.updateBlockHeight({ selectedCrypto }); - + //There's no need to check address/transaction history for new, random mnemonics. if (mnemonic !== "") { await this.refreshWallet({ignoreLoading: false}); } - + this.resetView(); } catch (e) { console.log(e); } }; - + render() { //return ; return ( @@ -1659,16 +1501,16 @@ export default class App extends PureComponent { - + - + {this.state.displayPriceHeader && - + } - + {this.state.displayBiometrics && } - + {this.state.displayPin && } - + {this.state.displayLoading && } - + {this.state.displayCamera && } - + {this.state.displaySettings && } - + {this.props.wallet.selectedWallet.split('wallet').join('Wallet ')} @@ -1717,25 +1559,32 @@ export default class App extends PureComponent { onSelectCoinPress={this.onSelectCoinPress} /> - + {this.state.displayReceiveTransaction && } - + {this.state.displayTextInput && - + - + } - + + {this.state.displaySweepPrivateKey && + + + + + } + {this.state.displayCameraRow && } - + {this.state.displaySelectCoin && } - + - + - + - + {this.state.displayTransactionList && - + {!this.state.loadingTransactions && - + } {this.state.loadingTransactions && this.state.displayTransactionList && @@ -1770,32 +1619,33 @@ export default class App extends PureComponent { Transactions - + {this.state.transactionsAreExpanded && - } + } {!this.state.transactionsAreExpanded && - } + } } - + {this.state.displayTransactionDetail && this.props.blacklistTransaction({ transaction: this.props.wallet.selectedTransaction.hash, wallet: this.props.wallet.selectedWallet, selectedCrypto: this.props.wallet.selectedCrypto })} transaction={this.props.wallet.selectedTransaction} selectedCrypto={this.props.wallet.selectedCrypto} cryptoUnit={this.props.settings.cryptoUnit} exchangeRate={this.props.wallet.exchangeRate[this.props.wallet.selectedCrypto]} currentBlockHeight={this.props.wallet.blockHeight[this.props.wallet.selectedCrypto]} /> } - + - + - + {this.state.displayXButton && + onPress={this.resetView} + /> } ); @@ -1940,7 +1790,7 @@ const walletActions = require("./src/actions/wallet"); const settingsActions = require("./src/actions/settings"); const transactionActions = require("./src/actions/transaction"); -const mapStateToProps = ({...state}, props) => ({ +const mapStateToProps = ({...state}) => ({ ...state }); diff --git a/ProjectData.json b/ProjectData.json index 8eac46b..3740b9a 100644 --- a/ProjectData.json +++ b/ProjectData.json @@ -16,6 +16,7 @@ "SEND_TRANSACTION_SUCCESS": "SEND_TRANSACTION_SUCCESS", "SEND_TRANSACTION": "SEND_TRANSACTION", "UPDATE_SETTINGS": "UPDATE_SETTINGS", + "UPDATE_PEERS_LIST": "UPDATE_PEERS_LIST", "LOGOUT": "LOGOUT", "DELETE_WALLET": "DELETE_WALLET", "WIPE_DEVICE": "WIPE_DEVICE", diff --git a/README.md b/README.md index 9ad301a..94af70f 100644 --- a/README.md +++ b/README.md @@ -45,9 +45,8 @@ Bitbip's Alpha is available for download here: * Add support for UTXO blacklisting * This will allow users to blacklist any utxo that they do not wish to include in their list of available utxo's when sending transactions. Blacklisting a utxo will also exclude it's amount from their total balance. * Allow users to manually select which public Electrum servers to randomly use if not connecting to their own node - * Add support for p2sh & legacy addresses in settings * Add support for additional currencies in the settings - * Support individual private key sweeping functionality + * Support individual private key sweeping functionality - *Complete (0.1.1)* * UI Improvements/Redesign: * Settings View * SelectCoin View diff --git a/android/app/build.gradle b/android/app/build.gradle index 0becc88..48e09bb 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -106,8 +106,8 @@ android { applicationId "com.kisswallet" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 11 - versionName "0.1" + versionCode 12 + versionName "0.1.1" multiDexEnabled true } splits { @@ -119,7 +119,7 @@ android { } } - /* + signingConfigs { release { storeFile file(MYAPP_RELEASE_STORE_FILE) @@ -128,7 +128,7 @@ android { keyPassword new String(System.console().readPassword("\n\$ Enter keys password: ")) } } - */ + buildTypes { debug { @@ -138,7 +138,7 @@ android { manifestPlaceholders = [excludeSystemAlertWindowPermission: "true"] minifyEnabled enableProguardInReleaseBuilds proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" - //signingConfig signingConfigs.release + signingConfig signingConfigs.release } } // applicationVariants are e.g. debug, release diff --git a/android/app/src/main/java/com/bitbip/MainApplication.java b/android/app/src/main/java/com/bitbip/MainApplication.java index 05fb859..7e939af 100644 --- a/android/app/src/main/java/com/bitbip/MainApplication.java +++ b/android/app/src/main/java/com/bitbip/MainApplication.java @@ -46,7 +46,7 @@ protected List getPackages() { new SvgPackage(), new LinearGradientPackage(), new RandomBytesPackage(), - new KeychainPackage() + new KeychainPackage() ); } diff --git a/android/build.gradle b/android/build.gradle index 0600aef..3993afb 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -13,7 +13,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.3.0' + classpath 'com.android.tools.build:gradle:3.3.2' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/babel.config.js b/babel.config.js index b66a04a..b62027c 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,3 +1,3 @@ module.exports = { presets: ["module:metro-react-native-babel-preset"] -} +}; diff --git a/ios/bitbip/Info.plist b/ios/bitbip/Info.plist index 4ef155f..a8321b5 100644 --- a/ios/bitbip/Info.plist +++ b/ios/bitbip/Info.plist @@ -17,11 +17,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 0.1 + 0.1.1 CFBundleSignature ???? CFBundleVersion - 15 + 1 LSRequiresIPhoneOS NSAppTransportSecurity diff --git a/metro.config.js b/metro.config.js index 4c05576..dc77eca 100644 --- a/metro.config.js +++ b/metro.config.js @@ -8,14 +8,14 @@ const blacklist = require('metro-config/src/defaults/blacklist'); module.exports = { transformer: { - getTransformOptions: async () => ({ - transform: { - experimentalImportSupport: false, - inlineRequires: false, - }, - }), + getTransformOptions: async () => ({ + transform: { + experimentalImportSupport: false, + inlineRequires: false, + }, + }), }, - resolver:{ + resolver: { blacklistRE: blacklist([ /nodejs-assets\/.*/, /android\/.*/, diff --git a/nodejs-assets/nodejs-project/api.js b/nodejs-assets/nodejs-project/api.js new file mode 100644 index 0000000..82952cf --- /dev/null +++ b/nodejs-assets/nodejs-project/api.js @@ -0,0 +1,38 @@ +class API { + constructor() { + this.coin = "bitcoin"; + this.mainClient = { + bitcoin: false, + litecoin: false, + bitcoinTestnet: false, + litecoinTestnet: false + }; + this.peer = { + bitcoin: {}, + litecoin: {}, + bitcoinTestnet: {}, + litecoinTestnet: {} + }; + this.peers = { + bitcoin: [], + litecoin: [], + bitcoinTestnet: [], + litecoinTestnet: [] + }; + } + + updateCoin(coin) { + this.coin = coin; + }; + + updateMainClient(mainClient) { + this.mainClient = mainClient; + } + + updatePeer(peer) { + this.peer = peer; + } + +} + +module.exports = new API(); \ No newline at end of file diff --git a/nodejs-assets/nodejs-project/electrumUtils.js b/nodejs-assets/nodejs-project/electrumUtils.js index 7933600..9bbafea 100644 --- a/nodejs-assets/nodejs-project/electrumUtils.js +++ b/nodejs-assets/nodejs-project/electrumUtils.js @@ -1,72 +1,136 @@ const rn_bridge = require("rn-bridge"); const ElectrumClient = require("electrum-client"); +const api = require("./api"); -this.coin = "bitcoin"; -this.mainClient = false; -this.peer = { +const disconnectFromPeer = async ({ id, coin }) => { + const failure = (data = {}) => { + rn_bridge.channel.send(JSON.stringify({ error: true, id, method: "disconnectFromPeer", data })); + }; + try { + if (coin != api.coin) return; + if (api.mainClient[api.coin] === false) { + //No peer to disconnect from... + rn_bridge.channel.send(JSON.stringify({ error: false, data: { message: "No peer to disconnect from.", coin }, id, method: "disconnectFromPeer" })); + return; + } + + //Attempt to disconnect from peer... + //await api.mainClient[api.coin].close(); + api.mainClient[api.coin] = false; + api.coin = ""; + rn_bridge.channel.send(JSON.stringify({error: false, id, method: "disconnectFromPeer", coin})); + } catch (e) { + failure(e); + } }; -const connectToPeer = async ({ id, customPeers = [], coin = "bitcoin" } = {}) => { - try { - this.coin = coin; +const connectToPeer = async ({ id, peers = [], customPeers = [], coin = "bitcoin" } = {}) => { + try { + api.coin = coin; + let customPeersLength = 0; + try { + customPeersLength = customPeers.length + } catch (e) {} //Attempt to connect to specified peer - if (customPeers.length > 0) { + if (customPeersLength > 0) { const {port = "", host = "", protocol = "ssl"} = customPeers[0]; - this.mainClient = new ElectrumClient(port, host, protocol); - const connectionResponse = await this.mainClient.connect(); - if (!connectionResponse.error) this.peer = { port: connectionResponse.data.port, host: connectionResponse.data.host, protocol: "ssl" }; - rn_bridge.channel.send(JSON.stringify({ id, error: connectionResponse.error, method: "connectToPeer", data: connectionResponse.data, customPeers })); + api.mainClient[coin] = new ElectrumClient(port, host, protocol); + const connectionResponse = await api.mainClient[coin].connect(); + if (!connectionResponse.error) api.peer[coin] = { port: connectionResponse.data.port, host: connectionResponse.data.host, protocol: "ssl" }; + rn_bridge.channel.send(JSON.stringify({ id, error: connectionResponse.error, method: "connectToPeer", data: connectionResponse.data, customPeers, coin })); } else { + /* + //If previously connected to a peer, return the previous peer data. + if (api.mainClient[api.coin] !== false) { + rn_bridge.channel.send(JSON.stringify({ id, error: false, method: "connectToPeer", data: api.peer[api.coin], customPeers, coin })); + return; + } + */ //Attempt to connect to random peer if none specified - const connectionResponse = await connectToRandomPeer(); - rn_bridge.channel.send(JSON.stringify({ id, error: connectionResponse.error, method: "connectToPeer", data: connectionResponse.data, customPeers })); + const connectionResponse = await connectToRandomPeer(coin, peers); + if (connectionResponse.coin !== api.coin) return; + rn_bridge.channel.send(JSON.stringify({ id, error: connectionResponse.error, method: "connectToPeer", data: connectionResponse.data, customPeers, coin })); } } catch (e) { - //return connectToPeer({ id, customPeers }); - rn_bridge.channel.send(JSON.stringify({ id, error: true, method: "connectToPeer", data: e, customPeers })); + rn_bridge.channel.send(JSON.stringify({ id, error: true, method: "connectToPeer", data: e, customPeers, coin })); } }; -const connectToRandomPeer = async () => { +const connectToRandomPeer = async (coin, peers = []) => { //Peers can be found in /node_modules/electrum-host-parse/fixtures/peers.json. //Additional Peers can be located here in servers.json & servers_testnet.json for reference: https://github.com/spesmilo/electrum/tree/master/electrum - let peers = require("electrum-host-parse").getDefaultPeers(this.coin).filter(v => v.ssl); - const initialPeerLength = peers.length; - //Attempt to connect to a random peer. If unable to connect initially iterate through available peers at random. + + let hasPeers = false; + try { hasPeers = (Array.isArray(peers) && peers.length) || (Array.isArray(api.peers[coin]) && api.peers[coin].length) } catch (e) {} + if (hasPeers) { + if (Array.isArray(peers) && peers.length) { + //Update peer list + api.peers[coin] = peers; + } else { + //Set the saved peer list + peers = api.peers[coin]; + } + } else { + //Use the default peer list for a connection if no other peers were passed down and no saved peer list is present. + peers = require("electrum-host-parse").getDefaultPeers(coin).filter(v => v.ssl); + } + const initialPeerLength = peers.length; //Acquire length of our default peers. + //Attempt to connect to a random default peer. Continue to iterate through default peers at random if unable to connect. for (let i = 0; i <= initialPeerLength; i++) { try { const randomIndex = peers.length * Math.random() | 0; const peer = peers[randomIndex]; - this.mainClient = new ElectrumClient(peer.ssl, peer.host, "ssl"); - const connectionResponse = await this.mainClient.connect(); + if (hasPeers) { + api.mainClient[coin] = new ElectrumClient(peer.port, peer.host, peer.protocol); + } else { + api.mainClient[coin] = new ElectrumClient(peer.ssl, peer.host, "ssl"); + } + const connectionResponse = await api.mainClient[coin].connect(); if (connectionResponse.error === false) { - this.peer = { port: connectionResponse.data.port, host: connectionResponse.data.host, protocol: "ssl" }; - return { - error: connectionResponse.error, + + //Ensure the server is responsive beyond a successful connection response + let versionResponse = []; + try { + //versionResponse = await api.mainClient[coin].server_version(v1, v2); + //const blockHeaderResponse = await api.mainClient[coin].blockchainHeaders_subscribe(); + //connectionResponse.data = Object.assign(blockHeaderResponse, connectionResponse.data); + } catch (e) {} + + if (connectionResponse.data) { + api.peer[coin] = { + port: connectionResponse.data.port, + host: connectionResponse.data.host, + protocol: "ssl" + }; + return { + error: connectionResponse.error, + method: "connectToRandomPeer", + data: connectionResponse.data, + coin + }; + } else { + if (peers.length === 1) return { + error: true, + method: "connectToRandomPeer", + data: connectionResponse.data, + coin + }; + peers.splice(randomIndex, 1); + } + } else { + if (peers.length === 1) return { + error: true, method: "connectToRandomPeer", - data: connectionResponse.data + data: connectionResponse.data, + coin }; - } else { - if (peers.length === 1) return { error: true, method: "connectToRandomPeer", data: connectionResponse.data}; peers.splice(randomIndex, 1); } - } catch (e) {} - } - return { error: true, method: "connectToRandomPeer", data: "Unable to connect to any peer."}; - /* - const getRandomPeer = () => peers[peers.length * Math.random() | 0]; - const peer = getRandomPeer(); - console.log("begin connection:", JSON.stringify(peer)); - this.mainClient = new ElectrumClient(peer.ssl, peer.host, "ssl"); - try { - return await this.mainClient.connect(); - } catch (e) { - console.log("bad connection:", JSON.stringify(peer)); - console.log("trying again"); - return connectToRandomPeer(this.coin) + } catch (e) { + } } - */ + return { error: true, method: "connectToRandomPeer", data: "Unable to connect to any peer." }; }; /* @@ -74,25 +138,16 @@ This is usually the first client’s message, plus it’s sent every minute as a Client sends its own version and version of the protocol it supports. Server responds with its supported version of the protocol (higher number at server-side is usually compatible). */ -const getVersion = async ({ id = "1", v1 = "3.2.3", v2 = "1.2", coin = "" }) => { +const getVersion = async ({ id = "1", v1 = "3.2.3", v2 = "1.4", coin = "" } = {}) => { /* //Peers can be found in /node_modules/electrum-host-parse/fixtures/peers.json. - //Other useful peers: BitcoinSegwitTestnet, Litecoin, LitecoinTestnet //Additional Peers can be located here for reference: https://github.com/spesmilo/electrum/blob/afa1a4d22a31d23d088c6670e1588eed32f7114d/lib/network.py#L57 - const peers = require("electrum-host-parse").getDefaultPeers("BitcoinSegwit").filter(v => v.ssl); - const getRandomPeer = () => peers[peers.length * Math.random() | 0]; - const peer = getRandomPeer(); - console.log("begin connection:", JSON.stringify(peer)); - this.mainClient = new ElectrumClient(peer.ssl, peer.host, "ssl"); */ - let peerData = ""; - if (this.mainClient === false) peerData = await connectToRandomPeer(coin); - if (coin && coin !== this.coin) peerData = await connectToRandomPeer(coin); - //peerData = await connectToRandomPeer("BitcoinSegwitTestnet"); - //rn_bridge.channel.send(JSON.stringify({ id, error: false, method: "getVersion", data: "", peerData, coin })); + if (api.mainClient[coin] === false) peerData = await connectToRandomPeer(coin, api.peers[coin]); + if (coin !== api.coin) peerData = await connectToRandomPeer(coin, api.peers[api.coin]); try { - const response = await this.mainClient.server_version(); + const response = await api.mainClient[coin].server_version(v1, v2); rn_bridge.channel.send(JSON.stringify({ id, error: false, method: "getVersion", data: response, peerData, coin })); } catch (e) { console.log("bad connection:", JSON.stringify(e)); @@ -101,10 +156,11 @@ const getVersion = async ({ id = "1", v1 = "3.2.3", v2 = "1.2", coin = "" }) => } }; -const getBanner = async ({ id= "" }) => { +const getBanner = async ({ id = "", coin = "" } = {}) => { try { - if (this.mainClient === false) await connectToRandomPeer(this.coin); - const response = await this.mainClient.server_banner(); + if (coin != api.coin) return; + if (api.mainClient[api.coin] === false) await connectToRandomPeer(api.coin, api.peers[api.coin]); + const response = await api.mainClient[api.coin].server_banner(); rn_bridge.channel.send(JSON.stringify({ id, error: false, method: "getBanner", data: response})); } catch (e) { console.log(e); @@ -112,10 +168,21 @@ const getBanner = async ({ id= "" }) => { } }; -const getDonationAddress = async ({ id = "" }) => { +const pingServer = async ({ id = Math.random() } = {}) => { + try { + if (api.mainClient[api.coin] === false) await connectToRandomPeer(api.coin, api.peers[api.coin]); + const response = await api.mainClient[api.coin].server_ping(); + rn_bridge.channel.send(JSON.stringify({ id, error: false, method: "pingServer", data: response})); + } catch (e) { + console.log(e); + rn_bridge.channel.send(JSON.stringify({ id, error: true, method: "pingServer", data: e })); + } +}; + +const getDonationAddress = async ({ id = "", coin = "" } = {}) => { try { - if (this.mainClient === false) await connectToRandomPeer(this.coin); - const response = await this.mainClient.serverDonation_address(); + if (api.mainClient[api.coin] === false) await connectToRandomPeer(api.coin, api.peers[api.coin]); + const response = await api.mainClient[api.coin].serverDonation_address(); rn_bridge.channel.send(JSON.stringify({ id, error: false, method: "getDonationAddress", data: response})); } catch (e) { console.log(e); @@ -129,46 +196,30 @@ Servers are connected to an IRC channel (#electrum at freenode.net) where they c Each server announces its version, history pruning limit of every address (“p100”, “p10000” etc.–the number means how many transactions the server may keep for every single address) and supported protocols (“t” = tcp@50001, “h” = http@8081, “s” = tcp/tls@50002, “g” = https@8082; non-standard port would be announced this way: “t3300” for tcp on port 3300). Note: At the time of writing there isn’t a true subscription implementation of this method, but servers only send one-time response. They don’t send notifications yet. */ -const getPeers = async ({ id = "", method = "getPeers" }) => { +const getPeers = async ({ id = "", method = "getPeers", coin = "" } = {}) => { try { - if (this.mainClient === false) await connectToRandomPeer(this.coin); - const response = await this.mainClient.serverPeers_subscribe(); - rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response})); + if (coin != api.coin) return; + if (api.mainClient[api.coin] === false) await connectToRandomPeer(api.coin, api.peers[api.coin]); + const response = await api.mainClient[api.coin].serverPeers_subscribe(); + rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response, coin})); } catch (e) { console.log(e); - rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: null })); + rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: null, coin })); } }; -const getAvailablePeers = async ({ id = "", method = "getAvailablePeers" }) => { +const getAvailablePeers = async ({ id = "", method = "getAvailablePeers", coin = "" } = {}) => { try { + if (coin != api.coin) return; //Peers can be found in /node_modules/electrum-host-parse/fixtures/peers.json. //Other useful peers: BitcoinSegwitTestnet, Litecoin, LitecoinTestnet //Additional Peers can be located here for reference: //(electrum/lib/network.py) https://github.com/spesmilo/electrum/blob/afa1a4d22a31d23d088c6670e1588eed32f7114d/lib/network.py#L57 - const peers = require("electrum-host-parse").getDefaultPeers(this.coin).filter(v => v.ssl); + const peers = require("electrum-host-parse").getDefaultPeers(api.coin).filter(v => v.ssl); rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: peers})); } catch (e) { console.log(e); - rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e })); - } -}; - -const disconnectFromPeer = async ({ id = "", method = "disconnectFromPeer" }) => { - try { - if (this.mainClient === false) { - //No peer to disconnect from... - rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: { message: "No peer to disconnect from." }})); - } else { - //Attempt to disconnect from peer... - await this.mainClient.close(); - this.mainClient = false; - this.coin = "bitcoin"; - rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: "Successfully disconnected from peer."})); - } - } catch (e) { - console.log(e); - rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "Unable to disconnect from peer.", method, data: e })); + rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e, coin })); } }; @@ -176,28 +227,30 @@ const disconnectFromPeer = async ({ id = "", method = "disconnectFromPeer" }) => A request to send to the client notifications about new blocks height. Responds with the current block height. */ -const getNewBlockHeightSubscribe = async ({ id = "", method = "getNewBlockHeightSubscribe" }) => { +const getNewBlockHeightSubscribe = async ({ id = "", method = "getNewBlockHeightSubscribe", coin = "" } = {}) => { try { - if (this.mainClient === false) await connectToRandomPeer(this.coin); - const response = await this.mainClient.blockchainNumblocks_subscribe(); - rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response})); + if (coin != api.coin) return; + if (api.mainClient[api.coin] === false) await connectToRandomPeer(api.coin, api.peers[api.coin]); + const response = await api.mainClient[api.coin].blockchainNumblocks_subscribe(); + rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response, coin})); } catch (e) { console.log(e); - rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e })); + rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e, coin })); } }; /* A request to send to the client notifications about new blocks in form of parsed blockheaders. */ -const getNewBlockHeadersSubscribe = async ({ id = "", method = "getNewBlockHeadersSubscribe" } = {}) => { +const getNewBlockHeadersSubscribe = async ({ id = "", method = "getNewBlockHeadersSubscribe", coin = "" } = {}) => { try { - if (this.mainClient === false) await connectToRandomPeer(this.coin); - const response = await this.mainClient.blockchainHeaders_subscribe(); - rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response})); + if (coin != api.coin) return; + if (api.mainClient[api.coin] === false) await connectToRandomPeer(coin, api.peers[api.coin]); + const response = await api.mainClient[coin].blockchainHeaders_subscribe(); + rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response, coin})); } catch (e) { console.log(e); - rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: null })); + rn_bridge.channel.send(JSON.stringify({ id, error: true, method, data: null, coin })); } }; @@ -206,217 +259,233 @@ A request to send to the client notifications when status (i.e., transaction his Status is a hash of the transaction history. If there isn’t any transaction for the address yet, the status is null. */ -const getHashOfAddressChangesSubscribe = async ({ address = "", id = "", method = "getHashOfAddressChangesSubscribe" }) => { +const getHashOfAddressChangesSubscribe = async ({ address = "", id = "", method = "getHashOfAddressChangesSubscribe", coin = "" }) => { try { - if (this.mainClient === false) await connectToRandomPeer(this.coin); - const response = await this.mainClient.blockchainAddress_subscribe(address); - rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response})); + if (coin != api.coin) return; + if (api.mainClient[api.coin] === false) await connectToRandomPeer(api.coin, api.peers[api.coin]); + const response = await api.mainClient[api.coin].blockchainAddress_subscribe(address); + rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response, coin})); } catch (e) { console.log(e); - rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: null })); + rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: null, coin })); } }; /* For a given address a list of transactions and their heights (and fees in newer versions) is returned. */ -const getAddressHistory = async ({ address = "", id = "", method = "getAddressHistory" }) => { +const getAddressHistory = async ({ address = "", id = "", method = "getAddressHistory", coin = {} } = {}) => { try { - if (this.mainClient === false) await connectToRandomPeer(this.coin); - const response = await this.mainClient.blockchainAddress_getHistory(address); - rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response})); + if (coin != api.coin) return; + if (api.mainClient[api.coin] === false) await connectToRandomPeer(api.coin, api.peers[api.coin]); + const response = await api.mainClient[api.coin].blockchainAddress_gethistory(address); + rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response, coin})); } catch (e) { console.log(e); - rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e })); + rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e, coin })); } }; /* For a given scriptHash, a list of transactions and their heights (and fees in newer versions) is returned. */ -const getAddressScriptHashHistory = async ({ scriptHash = "", id = "", method = "getAddressScriptHashHistory" }) => { +const getAddressScriptHashHistory = async ({ scriptHash = "", id = "", method = "getAddressScriptHashHistory", coin = "" } = {}) => { try { - if (this.mainClient === false) await connectToRandomPeer(this.coin); - - const response = await this.mainClient.blockchainScripthash_getHistory(scriptHash); - rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response, coin: this.coin })); + if (coin != api.coin) return; + if (api.mainClient[api.coin] === false) await connectToRandomPeer(api.coin, api.peers[api.coin]); + const response = await api.mainClient[api.coin].blockchainScripthash_getHistory(scriptHash); + rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response, coin })); } catch (e) { console.log(e); - rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e })); + rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e, coin })); } }; /* For a given scriptHash, a list of transactions and their heights (and fees in newer versions) is returned. */ -const getAddressScriptHashesHistory = async ({ scriptHashes = [], id = "", method = "getAddressScriptHashesHistory" }) => { +const getAddressScriptHashesHistory = async ({ scriptHashes = [], id = "", method = "getAddressScriptHashesHistory", coin = "" } = {}) => { try { - if (this.mainClient === false) await connectToRandomPeer(this.coin); - const response = await this.mainClient.blockchainScripthashes_getHistory(scriptHashes); - rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response, coin: this.coin })); + if (coin != api.coin) return; + if (api.mainClient[api.coin] === false) await connectToRandomPeer(api.coin, api.peers[api.coin]); + const response = await api.mainClient[api.coin].blockchainScripthashes_getHistory(scriptHashes); + rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response, coin })); } catch (e) { console.log(e); - rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e })); + rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: e, method, data: [], coin })); } }; /* For a given scriptHash, a list of transactions, fees and their heights (and fees in newer versions) is returned. */ -const getAddressScriptHashMempool = async ({ scriptHash = "", id = "", method = "getAddressScriptHashMempool" }) => { +const getAddressScriptHashMempool = async ({ scriptHash = "", id = "", method = "getAddressScriptHashMempool", coin = {} }) => { try { - if (this.mainClient === false) await connectToRandomPeer(this.coin); - - const response = await this.mainClient.blockchainScripthash_getMempool(scriptHash); - rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response })); + if (coin != api.coin) return; + if (api.mainClient[api.coin] === false) await connectToRandomPeer(api.coin, api.peers[api.coin]); + const response = await api.mainClient[api.coin].blockchainScripthash_getMempool(scriptHash); + rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response, coin })); } catch (e) { console.log(e); - rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e })); + rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: e, method, data: [], coin })); } }; -const getAddressScriptHashesMempool = async ({ scriptHashes = [], id = "", method = "getAddressScriptHashesMempool" }) => { +const getAddressScriptHashesMempool = async ({ scriptHashes = [], id = "", method = "getAddressScriptHashesMempool", coin = "" } = {}) => { try { - if (this.mainClient === false) await connectToRandomPeer(this.coin); - - const response = await this.mainClient.blockchainScripthashes_getMempool(scriptHashes); - rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response })); + if (coin != api.coin) return; + if (api.mainClient[api.coin] === false) await connectToRandomPeer(api.coin, api.peers[api.coin]); + + const response = await api.mainClient[api.coin].blockchainScripthashes_getMempool(scriptHashes); + rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response, coin })); } catch (e) { console.log(e); - rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e })); + rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e, coin })); } }; -const getMempool = async ({ address = "", id = "", method = "getMempool" }) => { +const getMempool = async ({ address = "", id = "", method = "getMempool", coin = "" } = {}) => { try { - if (this.mainClient === false) await connectToRandomPeer(this.coin); - const response = await this.mainClient.blockchainAddress_getMempool(address); - rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response})); + if (coin != api.coin) return; + if (api.mainClient[api.coin] === false) await connectToRandomPeer(api.coin, api.peers[api.coin]); + const response = await api.mainClient[api.coin].blockchainAddress_getMempool(address); + rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response, coin })); } catch (e) { console.log(e); - rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e })); + rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e, coin })); } }; -const getAddressBalance = async ({ address = "", id = "", method = "getAddressBalance" }) => { +const getAddressBalance = async ({ address = "", id = "", method = "getAddressBalance", coin = "" } = {}) => { try { - if (this.mainClient === false) await connectToRandomPeer(this.coin); - const response = await this.mainClient.blockchainAddress_getBalance(address); - rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response})); + if (coin != api.coin) return; + if (api.mainClient[api.coin] === false) await connectToRandomPeer(api.coin, api.peers[api.coin]); + const response = await api.mainClient[api.coin].blockchainAddress_getBalance(address); + rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response, coin })); } catch (e) { console.log(e); - rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e })); + rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e, coin })); } }; -const getAddressScriptHashBalance = async ({ scriptHash = "", id = "", method = "getAddressScriptHashBalance" }) => { +const getAddressScriptHashBalance = async ({ scriptHash = "", id = "", method = "getAddressScriptHashBalance", coin = "" } = {}) => { try { - if (this.mainClient === false) await connectToRandomPeer(this.coin); - const response = await this.mainClient.blockchainScripthash_getBalance(scriptHash); - rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response, scriptHash})); + if (coin != api.coin) return; + if (api.mainClient[api.coin] === false) await connectToRandomPeer(api.coin, api.peers[api.coin]); + const response = await api.mainClient[api.coin].blockchainScripthash_getBalance(scriptHash); + rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response, scriptHash, coin })); } catch (e) { console.log(e); - rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e })); + rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e, coin })); } }; -const getAddressScriptHashesBalance = async ({ scriptHashes = [], id = "", method = "getAddressScriptHashesBalance" } = {}) => { +const getAddressScriptHashesBalance = async ({ scriptHashes = [], id = "", method = "getAddressScriptHashesBalance", coin = "" } = {}) => { try { - if (this.mainClient === false) await connectToRandomPeer(this.coin); - const response = await this.mainClient.blockchainScripthashes_getBalance(scriptHashes); - rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response })); + if (coin != api.coin) return; + if (api.mainClient[api.coin] === false) await connectToRandomPeer(api.coin, api.peers[api.coin]); + const response = await api.mainClient[api.coin].blockchainScripthashes_getBalance(scriptHashes); + rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response, coin })); } catch (e) { console.log(e); - rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e })); + rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e, coin })); } }; -const getAddressProof = async ({ address = "", id = "", method = "getAddressProof" }) => { +const getAddressProof = async ({ address = "", id = "", method = "getAddressProof", coin = "" } = {}) => { try { - if (this.mainClient === false) await connectToRandomPeer(this.coin); - const response = await this.mainClient.blockchainAddress_getProof(address); - rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response})); + if (coin != api.coin) return; + if (api.mainClient[api.coin] === false) await connectToRandomPeer(api.coin, api.peers[api.coin]); + const response = await api.mainClient[api.coin].blockchainAddress_getProof(address); + rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response, coin })); } catch (e) { console.log(e); - rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e })); + rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e, coin })); } }; -const listUnspentAddress = async ({ address = "", id = "", method = "listUnspentAddress" }) => { +const listUnspentAddress = async ({ address = "", id = "", method = "listUnspentAddress", coin = "" } = {}) => { try { - if (this.mainClient === false) await connectToRandomPeer(this.coin); - const response = await this.mainClient.blockchainAddress_listunspent(address); - rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response})); + if (coin != api.coin) return; + if (api.mainClient[api.coin] === false) await connectToRandomPeer(api.coin, api.peers[api.coin]); + const response = await api.mainClient[api.coin].blockchainAddress_listunspent(address); + rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response, coin })); } catch (e) { console.log(e); - rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e })); + rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e, coin })); } }; -const listUnspentAddressScriptHash = async ({ scriptHash = "", id = "", method = "listUnspentAddressScriptHash" }) => { +const listUnspentAddressScriptHash = async ({ scriptHash = "", id = "", method = "listUnspentAddressScriptHash", coin = "" } = {}) => { try { - if (this.mainClient === false) await connectToRandomPeer(this.coin); - const response = await this.mainClient.blockchainScripthash_listunspent(scriptHash); - rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response })); + if (coin != api.coin) return; + if (api.mainClient[api.coin] === false) await connectToRandomPeer(api.coin, api.peers[api.coin]); + const response = await api.mainClient[api.coin].blockchainScripthash_listunspent(scriptHash); + rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response, coin })); } catch (e) { console.log(e); - rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e })); + rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e, coin })); } }; -const listUnspentAddressScriptHashes = async ({ scriptHashes = [], id = "", method = "listUnspentAddressScriptHashes" } = {}) => { +const listUnspentAddressScriptHashes = async ({ scriptHashes = [], id = "", method = "listUnspentAddressScriptHashes", coin = "" } = {}) => { try { - if (this.mainClient === false) await connectToRandomPeer(this.coin); - const response = await this.mainClient.blockchainScripthashes_listunspent(scriptHashes); - rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response })); + if (coin != api.coin) return; + if (api.mainClient[api.coin] === false) await connectToRandomPeer(api.coin, api.peers[api.coin]); + const response = await api.mainClient[api.coin].blockchainScripthashes_listunspent(scriptHashes); + rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response, coin })); } catch (e) { console.log(e); - rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e })); + rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e, coin })); } }; -const getAddressUtxo = async ({ txHash = "", index = "", id = "", method = "getAddressUtxo" }) => { +const getAddressUtxo = async ({ txHash = "", index = "", id = "", method = "getAddressUtxo", coin = "" } = {}) => { try { - if (this.mainClient === false) await connectToRandomPeer(this.coin); - const response = await this.mainClient.blockchainUtxo_getAddress(txHash, index); - rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response})); + if (coin != api.coin) return; + if (api.mainClient[api.coin] === false) await connectToRandomPeer(api.coin, api.peers[api.coin]); + const response = await api.mainClient[api.coin].blockchainUtxo_getAddress(txHash, index); + rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response, coin })); } catch (e) { console.log(e); - rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e })); + rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e, coin })); } }; -const getBlockHeader = async ({ height = "", id = "", method = "getBlockHeader" } = {}) => { +const getBlockHeader = async ({ height = "", id = "", method = "getBlockHeader", coin = "" } = {}) => { try { - if (this.mainClient === false) await connectToRandomPeer(this.coin); - const response = await this.mainClient.blockchainBlock_getHeader(height); - rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response})); + if (coin != api.coin) return; + if (api.mainClient[api.coin] === false) await connectToRandomPeer(api.coin, api.peers[api.coin]); + const response = await api.mainClient[api.coin].blockchainBlock_getHeader(height); + rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response, coin })); } catch (e) { console.log(e); - rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e })); + rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e, coin })); } }; //Same as getBlockHeader, but used only on bitcoinTestnet for the moment. getBlockHeader wont work for bitcoinTestnet. -const getHeader = async ({ height = "", id = "", method = "getHeader" } = {}) => { +const getHeader = async ({ height = "", id = "", method = "getHeader", coin = "" } = {}) => { try { - if (this.mainClient === false) await connectToRandomPeer(this.coin); - const response = await this.mainClient.blockchainBlock_getBlockHeader(height); - rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response})); + if (coin != api.coin) return; + if (api.mainClient[api.coin] === false) await connectToRandomPeer(api.coin, api.peers[api.coin]); + const response = await api.mainClient[api.coin].blockchainBlock_getBlockHeader(height); + rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response, coin })); } catch (e) { console.log(e); - rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e })); + rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e, coin })); } }; -const getBlockChunk = async ({ index = "", id = "", method = "getBlockChunk" }) => { +const getBlockChunk = async ({ index = "", id = "", method = "getBlockChunk", coin = "" } = {}) => { try { - if (this.mainClient === false) await connectToRandomPeer(this.coin); - const response = await this.mainClient.blockchainBlock_getChunk(index); - rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response})); + if (coin != api.coin) return; + if (api.mainClient[api.coin] === false) await connectToRandomPeer(api.coin, api.peers[api.coin]); + const response = await api.mainClient[api.coin].blockchainBlock_getChunk(index); + rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response, coin })); } catch (e) { console.log(e); - rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e })); + rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e, coin })); } }; @@ -424,25 +493,26 @@ const getBlockChunk = async ({ index = "", id = "", method = "getBlockChunk" }) Submits raw transaction (serialized, hex-encoded) to the network. Returns transaction id, or an error if the transaction is invalid for any reason. */ -const broadcastTransaction = async ({ rawTx = "", id = "", method = "broadcastTransaction" }) => { +const broadcastTransaction = async ({ rawTx = "", id = "", method = "broadcastTransaction", coin = "" } = {}) => { try { - if (this.mainClient === false) await connectToRandomPeer(this.coin); - const response = await this.mainClient.blockchainTransaction_broadcast(rawTx); - rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response})); + if (api.mainClient[coin] === false) await connectToRandomPeer(api.coin, api.peers[api.coin]); + const response = await api.mainClient[coin].blockchainTransaction_broadcast(rawTx); + rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response, coin })); } catch (e) { console.log(e); - rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e })); + rn_bridge.channel.send(JSON.stringify({ id, error: true, method, data: e, coin })); } }; -const getTransactionMerkle = async ({ txHash = "", height = "", id = "", method = "getTransactionMerkle" }) => { +const getTransactionMerkle = async ({ txHash = "", height = "", id = "", method = "getTransactionMerkle", coin = "" } = {}) => { try { - if (this.mainClient === false) await connectToRandomPeer(this.coin); - const response = await this.mainClient.blockchainTransaction_getMerkle(txHash, height); - rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response})); + if (coin != api.coin) return; + if (api.mainClient[api.coin] === false) await connectToRandomPeer(api.coin, api.peers[api.coin]); + const response = await api.mainClient[api.coin].blockchainTransaction_getMerkle(txHash, height); + rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response, coin })); } catch (e) { console.log(e); - rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e })); + rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e, coin })); } }; @@ -451,15 +521,16 @@ Method for obtaining raw transaction (hex-encoded) for given txid. If the transaction doesn’t exist, an error is returned. */ -const getTransactionHex = async ({ txId = "", id = "", method = "getTransactionHex" }) => { +const getTransactionHex = async ({ txId = "", id = "", method = "getTransactionHex", coin = "" } = {}) => { try { - if (this.mainClient === false) await connectToRandomPeer(this.coin); - const response = await this.mainClient.blockchainTransaction_get(txId); + if (coin != api.coin) return; + if (api.mainClient[api.coin] === false) await connectToRandomPeer(api.coin, api.peers[api.coin]); + const response = await api.mainClient[api.coin].blockchainTransaction_get(txId); //const decodedTx = await TxDecoder(response, bitcoin.networks.bitcoin); - rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response })); + rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response, coin })); } catch (e) { console.log(e); - rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e })); + rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e, coin })); } }; @@ -469,14 +540,34 @@ Reference: https://github.com/kyuupichan/electrumx/blob/master/docs/protocol-met If the transaction doesn’t exist, an error is returned. */ -const getTransaction = async ({ txHash = "", id = "", method = "getTransaction" } = {}) => { +const getTransaction = async ({ txHash = "", id = "", method = "getTransaction", coin = "" } = {}) => { + const failure = () => { + try { + rn_bridge.channel.send(JSON.stringify({ id, error: true, method, data: {}, coin })); + } catch (e) { + console.log(e); + } + }; + try { + if (coin != api.coin) failure(); + if (api.mainClient[api.coin] === false) await connectToRandomPeer(api.coin, api.peers[api.coin]); + const response = await api.mainClient[api.coin].blockchainTransaction_get(txHash, true); + rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response, coin })); + } catch (e) { + console.log(e); + rn_bridge.channel.send(JSON.stringify({ id, error: true, method, data: e, coin })); + } +}; + +const getTransactions = async ({ id = "", txHashes = [], coin = "", method = "getTransactions" } = {}) => { try { - if (this.mainClient === false) await connectToRandomPeer(this.coin); - const response = await this.mainClient.blockchainTransaction_get(txHash, true); - rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response })); + if (coin != api.coin) return; + if (api.mainClient[api.coin] === false) await connectToRandomPeer(api.coin, api.peers[api.coin]); + const response = await api.mainClient[api.coin].blockchainTransactions_get(txHashes, true); + rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response, coin })); } catch (e) { console.log(e); - rn_bridge.channel.send(JSON.stringify({ id, error: true, method, data: e })); + rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "Error", errorMsg: e, method, data: [], coin })); } }; @@ -485,14 +576,15 @@ Estimates the transaction fee per kilobyte that needs to be paid for a transacti If the node doesn’t have enough information to make an estimate, the value -1 will be returned. Parameter: How many blocks the transaction may wait before being included. */ -const getFeeEstimate = async ({ blocksWillingToWait = 4, id = "", method = "getFeeEstimate" }) => { +const getFeeEstimate = async ({ blocksWillingToWait = 4, id = "", method = "getFeeEstimate", coin = "" } = {}) => { try { - if (this.mainClient === false) await connectToRandomPeer(this.coin); - const response = await this.mainClient.blockchainEstimatefee(blocksWillingToWait); - rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response, coin: this.coin})); + //if (coin != api.coin) return; + if (api.mainClient[coin] === false) await connectToRandomPeer(api.coin, api.peers[coin]); + const response = await api.mainClient[api.coin].blockchainEstimatefee(blocksWillingToWait); + rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: response, coin})); } catch (e) { console.log(e); - rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e, coin: this.coin })); + rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e, coin })); } }; @@ -501,49 +593,7 @@ const getAddress = (keyPair, network) => { //return bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey, network }).address; }; -const createAddresses = async ({ id = "", method = "createAddresses", root = "", network, networkValue, addressAmount = 0, addressIndex = 0, changeAddressAmount = 0, changeAddressIndex = 0 } = {}) => { - - /* - try { - - //const seed = bip39.mnemonicToSeed(mnemonic); - //const root = bip32.fromSeed(seed, network); - - let addresses = []; - let changeAddresses = []; - - //Generate Addresses - if (Number(addressAmount !== 0)) { - for (let i = addressIndex; i < addressAmount + addressIndex; i++) { - const addressPath = `m/49'/${networkValue}'/0'/0/${i}`; - const addressKeypair = root.derivePath(addressPath); - const address = await getAddress(addressKeypair, network); - //addresses.push({address}); - addresses.push({address, path: addressPath}); - } - } - - //Generate Change Addresses - if (Number(changeAddressAmount) !== 0) { - for (let i = changeAddressIndex; i < changeAddressAmount + changeAddressIndex; i++) { - const changeAddressPath = `m/49'/${networkValue}'/0'/1/${i}`; - const changeAddressKeypair = root.derivePath(changeAddressPath); - const address = getAddress(changeAddressKeypair, network); - changeAddresses.push({address, path: changeAddressPath}); - } - } - - - rn_bridge.channel.send(JSON.stringify({ id, error: false, method, data: { addresses, changeAddresses }})); - } catch (e) { - console.log(e); - rn_bridge.channel.send(JSON.stringify({ id, error: true, errorTitle: "", errorMsg: "", method, data: e, network, networkValue, addressAmount, changeAddressAmount, addressIndex, changeAddressIndex })); - } - */ -}; - module.exports = { - createAddresses, getAddressScriptHashHistory, getAddressScriptHashesHistory, getAddressScriptHashBalance, @@ -554,6 +604,7 @@ module.exports = { listUnspentAddressScriptHashes, getVersion, getBanner, + pingServer, getDonationAddress, getPeers, getAvailablePeers, @@ -576,5 +627,6 @@ module.exports = { getTransactionMerkle, getTransactionHex, getTransaction, + getTransactions, getFeeEstimate }; \ No newline at end of file diff --git a/nodejs-assets/nodejs-project/main.js b/nodejs-assets/nodejs-project/main.js index 6bed8bb..9ac0fe6 100644 --- a/nodejs-assets/nodejs-project/main.js +++ b/nodejs-assets/nodejs-project/main.js @@ -3,6 +3,7 @@ const rn_bridge = require("rn-bridge"); const { getVersion, getBanner, + pingServer, getDonationAddress, getPeers, getAvailablePeers, @@ -29,11 +30,11 @@ const { getTransactionMerkle, getTransactionHex, getTransaction, + getTransactions, getFeeEstimate, listUnspentAddressScriptHash, listUnspentAddressScriptHashes, - connectToPeer, - createAddresses + connectToPeer } = require("./electrumUtils"); rn_bridge.channel.on("message", (msg) => { @@ -42,157 +43,177 @@ rn_bridge.channel.on("message", (msg) => { let blocksWillingToWait = 4; let txId = ""; let txHash = ""; + let txHashes = []; let height = ""; let index = ""; let rawTx = ""; let coin = ""; - let network = ""; - let networkValue = ""; - let addressAmount = 0; - let addressIndex = 0; - let changeAddressAmount = 0; - let changeAddressIndex = 0; - let root = ""; + let peers = []; let customPeers = []; let scriptHash = ""; let scriptHashes = []; - + try { msg = JSON.parse(msg); } catch (e) { console.log(e); } - try { id = msg.id } catch (e) {} - + try { id = msg.id; } catch (e) {} + switch(msg.method) { case "connectToPeer": - try { customPeers = msg.customPeers || [] } catch (e) {} - try { coin = msg.coin } catch (e) {} - connectToPeer({ id, customPeers, coin }); - break; - case "createAddresses": - try { network = msg.network } catch (e) {} - try { networkValue = msg.networkValue } catch (e) {} - try { root = msg.root } catch (e) {} - try { addressAmount = msg.addressAmount } catch (e) {} - try { addressIndex = msg.addressIndex } catch (e) {} - try { changeAddressAmount = msg.changeAddressAmount } catch (e) {} - try { changeAddressIndex = msg.changeAddressIndex } catch (e) {} - createAddresses({ id, network, networkValue, addressAmount, addressIndex, changeAddressAmount, changeAddressIndex, root }); + try { peers = msg.peers || []; } catch (e) {} + try { customPeers = msg.customPeers || []; } catch (e) {} + try { coin = msg.coin; } catch (e) {} + connectToPeer({ id, peers, customPeers, coin }); + break; + case "disconnectFromPeer": + try { coin = msg.coin; } catch (e) {} + disconnectFromPeer({ coin, id }); break; case "getAddressBalance": - try { address = msg.address } catch (e) {} - getAddressBalance({ address, id }); + try { address = msg.address; } catch (e) {} + try { coin = msg.coin; } catch (e) {} + getAddressBalance({ address, id, coin }); break; case "getAddressScriptHashBalance": - try { scriptHash = msg.scriptHash } catch (e) {} - getAddressScriptHashBalance({ scriptHash, id }); + try { scriptHash = msg.scriptHash; } catch (e) {} + try { coin = msg.coin; } catch (e) {} + getAddressScriptHashBalance({ scriptHash, coin, id }); break; case "getAddressScriptHashesBalance": - try { scriptHashes = msg.scriptHashes } catch (e) {} - getAddressScriptHashesBalance({ scriptHashes, id }); + try { scriptHashes = msg.scriptHashes; } catch (e) {} + try { coin = msg.coin; } catch (e) {} + getAddressScriptHashesBalance({ scriptHashes, coin, id }); break; case "getAddressScriptHashHistory": - try { scriptHash = msg.scriptHash } catch (e) {} - getAddressScriptHashHistory({ scriptHash, id }); + try { scriptHash = msg.scriptHash; } catch (e) {} + try { coin = msg.coin; } catch (e) {} + getAddressScriptHashHistory({ scriptHash, coin, id }); break; case "getAddressScriptHashesHistory": - try { scriptHashes = msg.scriptHashes } catch (e) {} - getAddressScriptHashesHistory({ scriptHashes, id }); + try { scriptHashes = msg.scriptHashes; } catch (e) {} + try { coin = msg.coin; } catch (e) {} + getAddressScriptHashesHistory({ scriptHashes, coin, id }); break; case "getAddressScriptHashMempool": - try { scriptHash = msg.scriptHash } catch (e) {} - getAddressScriptHashMempool({ scriptHash, id }); + try { scriptHash = msg.scriptHash; } catch (e) {} + try { coin = msg.coin; } catch (e) {} + getAddressScriptHashMempool({ scriptHash, coin, id }); break; case "getAddressScriptHashesMempool": - try { scriptHashes = msg.scriptHashes } catch (e) {} - getAddressScriptHashesMempool({ scriptHashes, id }); + try { scriptHashes = msg.scriptHashes; } catch (e) {} + try { coin = msg.coin; } catch (e) {} + getAddressScriptHashesMempool({ scriptHashes, coin, id }); break; case "listUnspentAddressScriptHash": - try { scriptHash = msg.scriptHash } catch (e) {} - listUnspentAddressScriptHash({ scriptHash, id }); + try { scriptHash = msg.scriptHash; } catch (e) {} + try { coin = msg.coin; } catch (e) {} + listUnspentAddressScriptHash({ scriptHash, coin, id }); break; case "listUnspentAddressScriptHashes": - try { scriptHashes = msg.scriptHashes } catch (e) {} - listUnspentAddressScriptHashes({ scriptHashes, id }); + try { scriptHashes = msg.scriptHashes; } catch (e) {} + try { coin = msg.coin; } catch (e) {} + listUnspentAddressScriptHashes({ scriptHashes, coin, id }); break; case "getMempool": - try { address = msg.address } catch (e) {} - getMempool({ address, id }); + try { address = msg.address; } catch (e) {} + try { coin = msg.coin; } catch (e) {} + getMempool({ address, coin, id }); break; case "listUnspentAddress": - try { address = msg.address } catch (e) {} - listUnspentAddress({ address, id }); + try { address = msg.address; } catch (e) {} + try { coin = msg.coin; } catch (e) {} + listUnspentAddress({ address, coin, id }); break; case "getFeeEstimate": - try { blocksWillingToWait = msg.blocksWillingToWait } catch (e) {} - getFeeEstimate({ blocksWillingToWait, id }); + try { blocksWillingToWait = msg.blocksWillingToWait; } catch (e) {} + try { coin = msg.coin; } catch (e) {} + getFeeEstimate({ blocksWillingToWait, coin, id }); break; case "getAddressHistory": - try { address = msg.address } catch (e) {} - getAddressHistory({ address, id }); + try { address = msg.address; } catch (e) {} + try { coin = msg.coin; } catch (e) {} + getAddressHistory({ address, coin, id }); break; case "getTransactionHex": - try { txId = msg.txId } catch (e) {} - getTransactionHex({ txId, id }); + try { txId = msg.txId; } catch (e) {} + try { coin = msg.coin; } catch (e) {} + getTransactionHex({ txId, coin, id }); break; case "getDonationAddress": getDonationAddress({ id }); break; - case "disconnectFromPeer": - disconnectFromPeer({ id }); - break; case "getAvailablePeers": - getAvailablePeers({ id }); + try { coin = msg.coin; } catch (e) {} + getAvailablePeers({ coin, id }); break; case "getPeers": - getPeers({ id }); + try { coin = msg.coin; } catch (e) {} + getPeers({ coin, id }); break; case "getNewBlockHeightSubscribe": - getNewBlockHeightSubscribe({ id }); + try { coin = msg.coin; } catch (e) {} + getNewBlockHeightSubscribe({ coin, id }); break; case "getNewBlockHeadersSubscribe": - getNewBlockHeadersSubscribe({ id }); + try { coin = msg.coin; } catch (e) {} + getNewBlockHeadersSubscribe({ coin, id }); break; case "getTransactionMerkle": - try { txHash = msg.txHash } catch (e) {} - try { height = msg.height } catch (e) {} - getTransactionMerkle({ id, txHash, height }); + try { txHash = msg.txHash; } catch (e) {} + try { height = msg.height; } catch (e) {} + try { coin = msg.coin; } catch (e) {} + getTransactionMerkle({ id, txHash, height, coin }); break; case "getTransaction": - try { txHash = msg.txHash } catch (e) {} - getTransaction({ id, txHash }); + try { txHash = msg.txHash; } catch (e) {} + try { coin = msg.coin; } catch (e) {} + getTransaction({ id, txHash, coin }); + break; + case "getTransactions": + try { txHashes = msg.txHashes; } catch (e) {} + try { coin = msg.coin; } catch (e) {} + getTransactions({ id, txHashes, coin }); break; case "getAddressUtxo": - try { txHash = msg.txHash } catch (e) {} - try { index = msg.index } catch (e) {} - getAddressUtxo({ id, txHash, index }); + try { txHash = msg.txHash; } catch (e) {} + try { index = msg.index; } catch (e) {} + try { coin = msg.coin; } catch (e) {} + getAddressUtxo({ id, txHash, index, coin }); break; case "broadcastTransaction": - try { rawTx = msg.rawTx } catch (e) {} - broadcastTransaction({ id, rawTx }); + try { rawTx = msg.rawTx; } catch (e) {} + try { coin = msg.coin; } catch (e) {} + broadcastTransaction({ id, rawTx, coin }); break; case "getBlockChunk": - try { index = msg.index } catch (e) {} - getBlockChunk({ id, index }); + try { index = msg.index; } catch (e) {} + try { coin = msg.coin; } catch (e) {} + getBlockChunk({ id, index, coin }); break; case "getBlockHeader": - try { height = msg.height } catch (e) {} - getBlockHeader({ id, height }); + try { height = msg.height; } catch (e) {} + try { coin = msg.coin; } catch (e) {} + getBlockHeader({ id, height, coin }); break; case "getHeader": - try { height = msg.height } catch (e) {} + try { height = msg.height; } catch (e) {} getHeader({ id, height }); break; case "getBanner": getBanner({ id }); break; + //pingServer + case "pingServer": + pingServer({ id }); + break; case "getAddressProof": - try { address = msg.address } catch (e) {} + try { address = msg.address; } catch (e) {} getAddressProof({ id, address }); break; case "getVersion": - try { coin = msg.coin } catch (e) {} + try { coin = msg.coin; } catch (e) {} getVersion({ id, coin }); break; default: diff --git a/package.json b/package.json index 8f84024..4f77472 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bitbip", - "version": "0.1.0", + "version": "0.1.1", "private": true, "scripts": { "start": "node node_modules/react-native/local-cli/cli.js start", @@ -21,15 +21,15 @@ "buffer-reverse": "^1.0.1", "coinselect": "^3.1.11", "crypto": "^1.0.1", - "events": "^3.0.0", + "events": "^1.1.1", "lottie-react-native": "^2.6.1", "moment": "^2.24.0", - "nodejs-mobile-react-native": "^0.4.0", + "nodejs-mobile-react-native": "^0.4.1", "prop-types": "^15.7.2", "query-string": "^6.4.2", - "react": "^16.8.6", + "react": "16.8.3", "react-native": "^0.59.5", - "react-native-camera": "^2.5.0", + "react-native-camera": "^1.12.0", "react-native-crypto": "^2.1.2", "react-native-keychain": "^3.1.1", "react-native-linear-gradient": "^2.5.4", @@ -43,14 +43,14 @@ "react-native-typography": "^1.4.0", "react-native-vector-icons": "^6.4.2", "react-redux": "^7.0.2", - "readable-stream": "^3.3.0", + "readable-stream": "^1.0.33", "redux": "^4.0.1", "redux-logger": "^3.0.6", "redux-persist": "^5.10.0", "redux-thunk": "^2.3.0", "rn-bitcoinjs-lib": "^4.0.3-1", "stream-browserify": "^1.0.0", - "vm-browserify": "^1.1.0" + "vm-browserify": "0.0.4" }, "devDependencies": { "@babel/core": "^7.4.3", diff --git a/shim.js b/shim.js index ec5285a..8a4fd89 100644 --- a/shim.js +++ b/shim.js @@ -1,18 +1,18 @@ -if (typeof __dirname === 'undefined') global.__dirname = '/' -if (typeof __filename === 'undefined') global.__filename = '' +if (typeof __dirname === 'undefined') global.__dirname = '/'; +if (typeof __filename === 'undefined') global.__filename = ''; if (typeof process === 'undefined') { - global.process = require('process') + global.process = require('process'); } else { - const bProcess = require('process') + const bProcess = require('process'); for (var p in bProcess) { if (!(p in process)) { - process[p] = bProcess[p] + process[p] = bProcess[p]; } } } -process.browser = false -if (typeof Buffer === 'undefined') global.Buffer = require('buffer').Buffer +process.browser = false; +if (typeof Buffer === 'undefined') global.Buffer = require('buffer').Buffer; if (typeof Buffer.prototype.reverse === 'undefined') { var bufferReverse = require('buffer-reverse'); @@ -23,12 +23,12 @@ if (typeof Buffer.prototype.reverse === 'undefined') { } // global.location = global.location || { port: 80 } -const isDev = typeof __DEV__ === 'boolean' && __DEV__ -process.env['NODE_ENV'] = isDev ? 'development' : 'production' +const isDev = typeof __DEV__ === 'boolean' && __DEV__; +process.env['NODE_ENV'] = isDev ? 'development' : 'production'; if (typeof localStorage !== 'undefined') { - localStorage.debug = isDev ? '*' : '' + localStorage.debug = isDev ? '*' : ''; } // If using the crypto shim, uncomment the following line to ensure // crypto is loaded first, so it can populate global.crypto -require('crypto') +require('crypto'); diff --git a/src/actions/settings.js b/src/actions/settings.js index 71169a2..721441e 100644 --- a/src/actions/settings.js +++ b/src/actions/settings.js @@ -12,6 +12,38 @@ export const updateSettings = (payload) => ({ payload }); +export const updatePeersList = ({ peerList = [], coin = "bitcoin"} = {}) => (dispatch: any) => { + return new Promise(async (resolve) => { + const failure = (errorTitle = "", errorMsg = "") => { + resolve({ error: true, errorTitle, errorMsg }); + return; + }; + try { + let peers = []; + await Promise.all(peerList.map((peer) => { + try { + const host = peer[1]; + const port = Number(peer[2][1].replace(/\D/g,'')); + const protocol = "ssl"; + peers.push({ host, port, protocol }); + } catch (e) {} + })); + dispatch({ + type: actions.UPDATE_PEERS_LIST, + payload: { + coin, + peers + } + }); + resolve({ error: false, data: "" }); + } catch (e) { + console.log(e); + failure(e); + } + failure(); + }); +}; + export const wipeDevice = () => (dispatch: any) => { return new Promise(async (resolve) => { const failure = (errorTitle = "", errorMsg = "") => { diff --git a/src/actions/transaction.js b/src/actions/transaction.js index 7ca2ca4..45f7ab4 100644 --- a/src/actions/transaction.js +++ b/src/actions/transaction.js @@ -7,7 +7,8 @@ const { walletHelpers } = require("../utils/walletApi"); const moment = require("moment"); -let coinSelect = require("coinselect"); +//const coinSelect = require("coinselect"); +//import * as electrum from "../utils/electrum"; export const updateTransaction = (payload) => ({ type: actions.UPDATE_TRANSACTION, @@ -35,7 +36,7 @@ export const getRecommendedFee = () => (dispatch: any) => { console.log(e); failure(); } - + dispatch({ type: actions.UPDATE_TRANSACTION, payload: { recommendedFee, feeTimestamp: moment().format() } diff --git a/src/actions/wallet.js b/src/actions/wallet.js index 48641ee..c5f0880 100644 --- a/src/actions/wallet.js +++ b/src/actions/wallet.js @@ -29,7 +29,7 @@ const updateWallet = (payload) => ({ payload }); -const getExchangeRate = ({ selectedCoin = "bitcoin" } = {}) => (dispatch: any) => { +const getExchangeRate = ({ selectedCoin = "bitcoin" } = {}) => () => { return new Promise(async (resolve) => { const failure = (errorTitle = "", errorMsg = "") => { @@ -76,12 +76,12 @@ const deleteWallet = ({ wallet } = {}) => async (dispatch: any) => { }); }; -const importWallet = ({ wallets = [], mnemonic = "" } = {}) => async (dispatch: any) => { +const importWallet = ({ wallets = [], mnemonic = "" } = {}) => async () => { return new Promise(async (resolve) => { const failure = (data) => { resolve({error: true, data}); }; - + try { //Get highest wallet number let highestNumber = 0; @@ -94,9 +94,9 @@ const importWallet = ({ wallets = [], mnemonic = "" } = {}) => async (dispatch: ); //Add wallet name to wallets array; const walletName = `wallet${highestNumber+1}`; - + const response = await createWallet({ wallet: walletName, mnemonic}); - + if (response.error === false) { resolve({error: false, data: response.data}); } else { @@ -118,7 +118,7 @@ const createWallet = ({ wallet = "wallet0", selectedCrypto = "bitcoin", addressA if (mnemonic === "") { mnemonic = bip39.generateMnemonic(256); } - + if (bip39.validateMnemonic(mnemonic)) { await setKeychainValue({ key: wallet, value: mnemonic }); } else { @@ -127,12 +127,12 @@ const createWallet = ({ wallet = "wallet0", selectedCrypto = "bitcoin", addressA failure("Invalid Mnemonic"); return; } - + const coins = availableCoins; - + let allAddresses = {}; await Promise.all(coins.map(async (coin) => { allAddresses[coin] = { addresses: [], changeAddresses: [] };})); - + //Generate receiving and change addresses. if (generateAllAddresses) { await Promise.all( @@ -155,7 +155,7 @@ const createWallet = ({ wallet = "wallet0", selectedCrypto = "bitcoin", addressA allAddresses[selectedCrypto].changeAddresses = generatedAddresses.data.changeAddresses; } } - + let addresses = {}, changeAddresses = {}; await Promise.all( coins.map(coin => { @@ -168,7 +168,7 @@ const createWallet = ({ wallet = "wallet0", selectedCrypto = "bitcoin", addressA } }) ); - + const payload = { [wallet]: { ...defaultWalletShape, @@ -176,12 +176,12 @@ const createWallet = ({ wallet = "wallet0", selectedCrypto = "bitcoin", addressA changeAddresses } }; - + await dispatch({ type: actions.UPDATE_WALLET, payload }); - + resolve({ error: false, data: allAddresses }); } catch (e) { console.log(e); @@ -190,7 +190,7 @@ const createWallet = ({ wallet = "wallet0", selectedCrypto = "bitcoin", addressA }); }; -const resetUtxos = ({wallet = "wallet0", addresses = [], changeAddresses = [], currentUtxos = [], currentBlockHeight = 0, selectedCrypto = "bitcoin"} = {}) => async (dispatch: any) => { +const resetUtxos = ({wallet = "wallet0", addresses = [], changeAddresses = [], currentBlockHeight = 0, selectedCrypto = "bitcoin"} = {}) => async (dispatch: any) => { return new Promise(async (resolve) => { const failure = (data) => { resolve({error: true, data}); @@ -198,6 +198,7 @@ const resetUtxos = ({wallet = "wallet0", addresses = [], changeAddresses = [], c try { /* //Add existing utxos to addresses + let currentUtxos = []; await Promise.all( currentUtxos.map(async (utxo) => { let match = false; @@ -209,8 +210,8 @@ const resetUtxos = ({wallet = "wallet0", addresses = [], changeAddresses = [], c } )); */ - - + + //Returns { error: false, data: { utxos, balance } } const utxoResult = await walletHelpers.utxos[selectedCrypto].default({ addresses, changeAddresses, currentBlockHeight }); if (utxoResult.error === true) { @@ -224,7 +225,7 @@ const resetUtxos = ({wallet = "wallet0", addresses = [], changeAddresses = [], c failure(); return; } - + if (utxoResult.error === false) { dispatch({ type: actions.RESET_UTXOS, @@ -261,7 +262,7 @@ const addTransaction = ({ wallet = "wallet0", transaction = {}, selectedCrypto = transaction }, }); - resolve({ error: false, data: transaction }) + resolve({ error: false, data: transaction }); } catch (e) { console.log(e); failure(e); @@ -269,7 +270,7 @@ const addTransaction = ({ wallet = "wallet0", transaction = {}, selectedCrypto = }); }; -const updateBalance = ({ wallet = "wallet0", utxos = [], selectedCrypto = "bitcoin", confirmations = 0 } = {}) => async (dispatch: any) => { +const updateBalance = ({ wallet = "wallet0", utxos = [], selectedCrypto = "bitcoin" } = {}) => async (dispatch: any) => { return new Promise(async (resolve) => { const failure = (data) => { resolve({ error: true, data }); @@ -280,12 +281,12 @@ const updateBalance = ({ wallet = "wallet0", utxos = [], selectedCrypto = "bitco failure(); return; } - + let confirmedBalance = 0; let unconfirmedBalance = 0; await Promise.all(utxos.map(async (utxo) => { try { - confirmedBalance += utxo.value + confirmedBalance += utxo.value; } catch (e) {} })); /* @@ -303,7 +304,7 @@ const updateBalance = ({ wallet = "wallet0", utxos = [], selectedCrypto = "bitco timestamp: moment() }, }); - resolve({ error: false, data: confirmedBalance }) + resolve({ error: false, data: confirmedBalance }); } catch (e) { console.log(e); failure(e); @@ -315,7 +316,6 @@ const updateBlockHeight = ({ selectedCrypto = "bitcoin" } = {}) => async (dispat return new Promise(async (resolve) => { const failure = (data) => { resolve({ error: true, data }); - return; }; try { const response = await walletHelpers.getBlockHeight[selectedCrypto].default(); @@ -331,7 +331,6 @@ const updateBlockHeight = ({ selectedCrypto = "bitcoin" } = {}) => async (dispat }, }); resolve({ error: false, data: response }); - return; } failure("Unable to fetch block height."); } catch (e) { @@ -356,7 +355,7 @@ const addAddresses = ({ wallet = "wallet0", selectedCrypto = "bitcoin", addressA if (addresses.error) { addresses = { data: { addresses: [], changeAddresses: [] } }; } - + dispatch({ type: actions.ADD_ADDRESSES, payload: { @@ -370,6 +369,7 @@ const addAddresses = ({ wallet = "wallet0", selectedCrypto = "bitcoin", addressA resolve({ error: false, data: addresses.data }); } catch (e) { console.log(e); + failure(e); } }); }; @@ -380,7 +380,7 @@ const blacklistTransaction = ({ transaction = "", wallet = "wallet0", selectedCr resolve({error: true, data}); }; try { - + dispatch({ type: actions.BLACKLIST_TRANSACTION, payload: { @@ -392,36 +392,37 @@ const blacklistTransaction = ({ transaction = "", wallet = "wallet0", selectedCr resolve({ error: false, data: transaction, wallet, selectedCrypto }); } catch (e) { console.log(e); + failure(e); } }); }; -const initialImportSync = ({ wallet = "wallet0", selectedCrypto = "bitcoin", indexThreshold = 1, currentBlockHeight = 0 }) => async (dispatch: any) => { +const initialImportSync = ({ wallet = "wallet0", selectedCrypto = "bitcoin", currentBlockHeight = 0 }) => async (dispatch: any) => { return new Promise(async (resolve) => { const failure = (data) => { resolve({ error: true, data }); }; - + const isConnected = await isOnline(); if (isConnected === false) { failure("Offline"); return; } - + try { //The threshold dictates how many empty addresses the function should search for before resolving - const defaultIndexThreshold = 1; + //const defaultIndexThreshold = 1; //Add all address transactions to transactions array let transactions = []; let addressIndexes = []; let changeAddressIndexes = []; - + //Create Addresses //Generate receiving and change addresses. const newAddresses = await generateAddresses({ addressAmount: 50, changeAddressAmount: 50, addressIndex: 0, selectedCrypto, wallet }); const addresses = newAddresses.data.addresses; const changeAddresses = newAddresses.data.changeAddresses; - + await Promise.all( addresses.map(async (addr) => { try { @@ -458,8 +459,8 @@ const initialImportSync = ({ wallet = "wallet0", selectedCrypto = "bitcoin", ind } }) ); - - + + //Filter transactions by timestamp. /* transactions.sort((obj1, obj2) => { @@ -468,7 +469,7 @@ const initialImportSync = ({ wallet = "wallet0", selectedCrypto = "bitcoin", ind return obj2Value - obj1Value || obj2.block - obj1.block || obj1.amount - obj2.amount; }); */ - + const payload = { wallet, selectedCrypto, @@ -479,12 +480,12 @@ const initialImportSync = ({ wallet = "wallet0", selectedCrypto = "bitcoin", ind changeAddresses, timestamp: moment() }; - + dispatch({ type: actions.UPDATE_NEXT_AVAILABLE_ADDRESS, payload }); - resolve({error: false, data: payload}) + resolve({error: false, data: payload}); } catch (e) { console.log(e); failure(e); @@ -497,35 +498,35 @@ const getNextAvailableAddress = ({ wallet = "wallet0", addresses = [], changeAdd const failure = (data) => { resolve({ error: true, data }); }; - + const isConnected = await isOnline(); if (isConnected === false) { failure("Offline"); return; } - + try { //Create Addresses if none exist if (!addresses.length) { //Generate receiving and change addresses. const newAddresses = await generateAddresses({ addressAmount: 5, changeAddressAmount: 0, addressIndex: 0, selectedCrypto, wallet }); - addresses = newAddresses.data.addresses; + if (!newAddresses.error) addresses = newAddresses.data.addresses; } //Create Change Addresses if none exist if (!changeAddresses.length) { //Generate receiving and change addresses. const newAddresses = await generateAddresses({ addressAmount: 0, changeAddressAmount: 5, addressIndex: 0, selectedCrypto, wallet }); - changeAddresses = newAddresses.data.changeAddresses; + if (!newAddresses.error) changeAddresses = newAddresses.data.changeAddresses; } - + let allAddresses = addresses.slice(addressIndex, addresses.length); let allChangeAddresses = changeAddresses.slice(changeAddressIndex, changeAddresses.length); allAddresses = allAddresses.concat(allChangeAddresses); - + let allTransactions = []; let foundLastUsedAddress = false; let foundLastUsedChangeAddress = false; - + for (let i = 0; i < 10; i++) { const transactions = await getAllTransactions({ allAddresses, @@ -534,17 +535,17 @@ const getNextAvailableAddress = ({ wallet = "wallet0", addresses = [], changeAdd selectedCrypto, currentBlockHeight }); - + if (transactions.error === false && transactions.data.length) allTransactions = allTransactions.concat(transactions.data); - + addressIndex = transactions.lastUsedAddress !== null ? transactions.lastUsedAddress + 1 : addressIndex; foundLastUsedAddress = transactions.lastUsedAddress === null || transactions.lastUsedAddress < addresses.length - 1; - + changeAddressIndex = transactions.lastUsedChangeAddress !== null ? transactions.lastUsedChangeAddress + 1 : changeAddressIndex; foundLastUsedChangeAddress = transactions.lastUsedChangeAddress === null || transactions.lastUsedChangeAddress < changeAddresses.length - 1; - + allAddresses = []; - + //Create Addresses if none exist if (foundLastUsedAddress === false) { i = 0; @@ -553,7 +554,7 @@ const getNextAvailableAddress = ({ wallet = "wallet0", addresses = [], changeAdd allAddresses = allAddresses.concat(newAddresses.data.addresses); addresses = addresses.concat(newAddresses.data.addresses); } - + //Create Change Addresses if none exist if (foundLastUsedChangeAddress === false) { i = 0; @@ -562,10 +563,13 @@ const getNextAvailableAddress = ({ wallet = "wallet0", addresses = [], changeAdd allAddresses = allAddresses.concat(newChangeAddresses.data.changeAddresses); changeAddresses = changeAddresses.concat(newChangeAddresses.data.changeAddresses); } - - if (foundLastUsedAddress && foundLastUsedChangeAddress) break; + + if (foundLastUsedAddress && foundLastUsedChangeAddress) { + i = 10; + break; + } } - + if (allTransactions.length) { const payload = { wallet, @@ -576,7 +580,7 @@ const getNextAvailableAddress = ({ wallet = "wallet0", addresses = [], changeAdd addresses, changeAddresses }; - + dispatch({ type: actions.UPDATE_NEXT_AVAILABLE_ADDRESS, payload @@ -589,7 +593,7 @@ const getNextAvailableAddress = ({ wallet = "wallet0", addresses = [], changeAdd console.log(e); failure(e); } - + }); }; diff --git a/src/components/Biometrics.js b/src/components/Biometrics.js index 99f0bb7..eee682d 100644 --- a/src/components/Biometrics.js +++ b/src/components/Biometrics.js @@ -15,13 +15,12 @@ const { colors } } = require("../../ProjectData.json"); -import TouchID from "react-native-touch-id"; retryAuthentication = (retryAuthentication = null) => { if (retryAuthentication === null) return; return ( Retry - ) + ); }; getIcon = (biometricTypeSupported = "", retryAuthentication = null) => { @@ -29,24 +28,24 @@ getIcon = (biometricTypeSupported = "", retryAuthentication = null) => { if (biometricTypeSupported === "FaceID") { return ( - + FaceID Enabled {this.retryAuthentication(retryAuthentication)} - ) + ); } if (biometricTypeSupported === "TouchID") { return ( - + TouchID Enabled {this.retryAuthentication(retryAuthentication)} - ) + ); } return( @@ -56,7 +55,7 @@ getIcon = (biometricTypeSupported = "", retryAuthentication = null) => { {this.retryAuthentication(retryAuthentication)} - ) + ); } catch (e) { return( @@ -65,7 +64,7 @@ getIcon = (biometricTypeSupported = "", retryAuthentication = null) => { It appears that your device does not support Biometric security. - ) + ); } }; @@ -76,7 +75,7 @@ class Biometrics extends PureComponent { {getIcon(biometricTypeSupported, retryAuthentication)} - ) + ); } } diff --git a/src/components/Button.js b/src/components/Button.js index 8d24eba..66bdfb2 100644 --- a/src/components/Button.js +++ b/src/components/Button.js @@ -22,7 +22,7 @@ class Button extends PureComponent { return this.props.disabled || this.props.loading; }; render() { - const { title, text, text2, disabled, activeOpacity, loading, style, titleStyle, textStyle } = this.props; + const { title, text, text2, activeOpacity, loading, style, titleStyle, textStyle } = this.props; return ( {loading && @@ -38,7 +38,7 @@ class Button extends PureComponent { {text2} } - ) + ); } } @@ -87,7 +87,7 @@ const styles = StyleSheet.create({ textAlign: "center" }, text: { - ...systemWeights.regular, + ...systemWeights.light, color: colors.white, fontSize: 18, textAlign: "center" diff --git a/src/components/Camera.js b/src/components/Camera.js index 07d0fde..9e441c2 100644 --- a/src/components/Camera.js +++ b/src/components/Camera.js @@ -22,7 +22,7 @@ class Camera extends PureComponent { const notAuthorizedView = ( - + It appears I do not have permission to access your camera. To utilize this feature in the future you will need to enable camera permissions for this app from your phones settings. @@ -35,7 +35,7 @@ class Camera extends PureComponent { ref={ref => { this.camera = ref; }} - style = {styles.container} + style={styles.container} onBarCodeRead={this.props.onBarCodeRead} onMountError={() => { alert("There was an error encountered when loading the camera. Please ensure the app has permission to use this feature in your phone settings."); @@ -48,10 +48,10 @@ class Camera extends PureComponent { permissionDialogMessage={'We need your permission to use your camera phone'} /> - + - ) + ); } } diff --git a/src/components/CameraRow.js b/src/components/CameraRow.js index 62dd98f..6741d8c 100644 --- a/src/components/CameraRow.js +++ b/src/components/CameraRow.js @@ -27,14 +27,14 @@ class CameraRow extends PureComponent { - + Receive - ) + ); } } diff --git a/src/components/ElectrumOptions.js b/src/components/ElectrumOptions.js index e384d73..c21396b 100644 --- a/src/components/ElectrumOptions.js +++ b/src/components/ElectrumOptions.js @@ -65,7 +65,7 @@ class ElectrumInput extends PureComponent { autoCapitalize="none" selectionColor={colors.lightPurple} keyboardType="decimal-pad" - onChangeText={(port) => {if (!isNaN(port) || port === "") this.props.onChangeText({coin: this.props.coin, value: { port, host: this.props.host } })} } + onChangeText={(port) => {if (!isNaN(port) || port === "") this.props.onChangeText({coin: this.props.coin, value: { port, host: this.props.host } });}} value={this.props.port} multiline={false} placeholder={this.props.portPlaceholder} @@ -88,7 +88,7 @@ class ElectrumInput extends PureComponent { /> - ) + ); } } @@ -117,8 +117,8 @@ class ElectrumOptions extends PureComponent { const coin = availableCoins[i]; let host = ""; let port = ""; - try { host = props.settings.customPeers[coin][0].host } catch (e) {} - try { port = props.settings.customPeers[coin][0].port } catch (e) {} + try { host = props.settings.customPeers[coin][0].host; } catch (e) {} + try { port = props.settings.customPeers[coin][0].port; } catch (e) {} coins[coin] = { host, port }; } this.state = { @@ -153,12 +153,12 @@ class ElectrumOptions extends PureComponent { return; } - await electrum.stop(); + await electrum.stop({ coin }); const result = await electrum.start({ coin, customPeers: [{ host, port }]}); if (result.error === false) { - alert(`Success!!\nSuccessfully connect to:\n${host}:${port}`) + alert(`Success!!\nSuccessfully connect to:\n${host}:${port}`); } else { - alert(`Failure\nUnable to connect to:\n${host}:${port}`) + alert(`Failure\nUnable to connect to:\n${host}:${port}`); } await this.setState({ loading: "" }); } catch (e) { @@ -196,13 +196,13 @@ class ElectrumOptions extends PureComponent { } //Attempt to connect to the customPeer before saving. - await electrum.stop(); + await electrum.stop({ coin }); const result = await electrum.start({ coin, customPeers: [{ host, port }]}); if (result.error === false) { const currentPeers = this.props.settings.customPeers; await this.props.updateSettings({ customPeers: {...currentPeers, [coin]: [{ host, port }] } }); } else { - alert(`Failure\nUnable to connect to:\n${host}:${port}`) + alert(`Failure\nUnable to connect to:\n${host}:${port}`); } this.setState({ saving: "" }); @@ -240,7 +240,7 @@ class ElectrumOptions extends PureComponent { customPeers={this.props.settings.customPeers} portPlaceholder="50002" /> - ) + ); }} /> @@ -249,7 +249,7 @@ class ElectrumOptions extends PureComponent { - + ); @@ -330,7 +330,7 @@ const walletActions = require("../actions/wallet"); const transactionActions = require("../actions/transaction"); const settingsActions = require("../actions/settings"); -const mapStateToProps = ({...state}, props) => ({ +const mapStateToProps = ({...state}) => ({ ...state }); diff --git a/src/components/ElectrumTesting.js b/src/components/ElectrumTesting.js new file mode 100644 index 0000000..f666151 --- /dev/null +++ b/src/components/ElectrumTesting.js @@ -0,0 +1,476 @@ +/** + * @format + * @flow + */ + +import React, {PureComponent} from "react"; +import { + StyleSheet, + Text, + TouchableOpacity, + View, + ScrollView, + ActivityIndicator +} from "react-native"; + +const { + getVersion, + getPeers, + getAvailablePeers, + getNewBlockHeadersSubscribe, + getAddressScriptHashHistory, + getAddressScriptHashMempool, + listUnspentAddressScriptHash, + getBlockHeader, + broadcastTransaction, + getFeeEstimate, + getAddressScriptHashesBalance, + getAddressScriptHashesHistory, + getAddressScriptHashesMempool, + listUnspentAddressScriptHashes, + pingServer +} = require("../utils/electrum"); +import * as electrum from "../utils/electrum"; +const { + Constants: { + colors + } +} = require("../../ProjectData.json"); + +type Props = {}; + +class ElectrumTesting extends PureComponent { + + constructor(props) { + super(props); + this.state = { + output: "", + selectedCrypto: "", + defaultError: { error: true, errorTitle: "Unable to connect to electrum node", errorMsg: "" }, + loading: { + getAddressBalance: false, + getAddressScriptHashBalance: false, + getAddressScriptHashesBalance: false, + getAddressScriptHashMempool: false, + getAddressScriptHashesMempool: false, + listUnspentAddressScriptHash: false, + listUnspentAddressScriptHashes: false, + getMempool: false, + listUnspentAddress: false, + getFeeEstimate: false, + getAddressHistory: false, + getAddressScriptHashHistory: false, + getAddressScriptHashesHistory: false, + getTransactionHex: false, + getDonationAddress: false, + disconnectFromPeer: false, + getAvailablePeers: false, + getPeers: false, + getNewBlockHeightSubscribe: false, + getNewBlockHeadersSubscribe: false, + getTransactionMerkle: false, + getAddressUtxo: false, + broadcastTransaction: false, + getBlockChunk: false, + getBlockHeader: false, + getBanner: false, + pingServer: false, + getAddressProof: false, + getVersion: false + } + }; + } + + async componentDidMount(): void { + //Spin up the nodejs thread + //await Promise.all(nodejs.start("main.js")); + this.onCryptoButtonPress("bitcoin"); + } + + cryptoButton = ({ label = "", key = "" }) => { + const backgroundColor = this.state.selectedCrypto === key ? colors.white : colors.darkPurple; + const borderRadius = this.state.selectedCrypto === key ? 20 : 0; + const textColor = this.state.selectedCrypto === key ? colors.darkPurple : colors.white; + return ( + this.onCryptoButtonPress(key)} + style={[styles.cryptoButton, { backgroundColor, borderRadius }]} + > + + {label} + + + ); + }; + + onCryptoButtonPress = async (key = "") => { + try { + if (this.state.selectedCrypto === key) return; + let output = this.state.output; + output = `${output}\n\nConnecting to ${key}...`; + + const loadingKeys = Object.keys(this.state.loading); + + //Reset loading state when switching between coins. + let loading = {}; + await Promise.all(loadingKeys.map((key) => loading = {...loading, [key]: false} )); + + await this.setState({ selectedCrypto: key, output, loading }); + try { + let hasPeers = false; + let hasCustomPeers = false; + try {hasPeers = Array.isArray(this.props.settings.peers[key]) && this.props.settings.peers[key].length;} catch (e) {} + try {hasCustomPeers = Array.isArray(this.props.settings.customPeers[key]) && this.props.settings.customPeers[key].length;} catch (e) {} + + if (!hasPeers && !hasCustomPeers) { + //Attempt to retrieve a list of peers from the default servers. + const startResponse = await electrum.start({ + coin: key, + peers: [], + customPeers: [] + + }); + if (startResponse.error === false) { + const peers = await electrum.getPeers({coin: key}); + await this.props.updatePeersList({peerList: peers.data, coin: key}); + } + } + } catch (e) {} + + //await electrum.stop({ coin: key }); + let result = await electrum.start({ + coin: key, + peers: this.props.settings.peers[key], + customPeers: this.props.settings.customPeers[key] + + }); + + result = JSON.stringify(result); + output = `${output}\n${result}`; + await this.setState({ output }); + } catch (e) { + console.log(e); + } + }; + + clearButton = () => { + if (this.state.output) { + return ( + this.setState({ output: "" })} + style={styles.clearButton} + > + + Clear + + + ); + } + }; + + getAddresses = (type = "array") => { + const addresses = { + bitcoin: ["bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq", "bc1q6f6l84dd07g2478ggvwc8h0cyszz9m4j3kjzjz"], + bitcoinTestnet: ["tb1qlffxly3zuc8prf8l4v54z8ddsequz77j3s8q8l"], + litecoin: ["ltc1qmz65daz7dxqueuwznd3qrrjnudzs3xkgf7kwql", "ltc1qvn4euzcmjn6lqw56seru32rvjh5y03jgde4pgn", "ltc1qh8uyuuk560qgalmshh3fttp8m4n9cjpjyu5dpt"], + litecoinTestnet: ["tltc1qlffxly3zuc8prf8l4v54z8ddsequz77jgc97hk", "tltc1q6q3u7tureyx0c5m9y4exfqynl626sppu56u7w3"] + }; + if (type === "array") { + return this.props.wallet[this.props.wallet.selectedWallet].addresses[this.state.selectedCrypto] || addresses[this.state.selectedCrypto]; + } else if (type === "single") { + return this.props.wallet[this.props.wallet.selectedWallet].addresses[this.state.selectedCrypto][0].address || addresses[this.state.selectedCrypto][0]; + } else { + return addresses["bitcoin"]; + } + }; + + MethodButton ({ method = "", label = "", onPress = () => null, afterOnPress = () => null } = {}) { + return ( + { + let output = this.state.output; + output = `${output}\n\nRunning ${method} on ${this.state.selectedCrypto}...`; + await this.setState({ loading: { ...this.state.loading, [method]: true }}); + let result = { error: true, errorTitle: "Unable to connect to electrum node", errorMsg: "" }; + try { result = await onPress(); } catch (e) {} + result = JSON.stringify(result); + output = `${output}\n${result}`; + await this.setState({ loading: { ...this.state.loading, [method]: false }, output}); + afterOnPress(); + }} + style={styles.button} + > + + + {label} + + + ); + } + + + render() { + return ( + + + {this.clearButton()} + + + this.scrollView = ref} + onContentSizeChange={() => { + this.scrollView.scrollToEnd({animated: true}); + }} + contentContainerStyle={{ alignItems: "flex-start", justifyContent: "flex-end", marginHorizontal: 10, paddingBottom: 10 }} + > + {this.state.output} + + + + + {this.cryptoButton({ label: "BTC", key: "bitcoin" })} + {this.cryptoButton({ label: "BTCt", key: "bitcoinTestnet" })} + {this.cryptoButton({ label: "LTC", key: "litecoin" })} + {this.cryptoButton({ label: "LTCt", key: "litecoinTestnet" })} + + + + + + + {/* + Get Address Script Hashes Balance + */} + {this.MethodButton({ + method: "getAddressScriptHashesBalance", + label: "1 Get Address Script Hashes Balance", + onPress: () => getAddressScriptHashesBalance({ addresses: this.getAddresses(), coin: this.state.selectedCrypto }) + })} + + {/* + Get Address Script Hash History + */} + {this.MethodButton({ + method: "getAddressScriptHashHistory", + label: "2 Get Address Script Hash History", + onPress: () => getAddressScriptHashHistory({ address: this.getAddresses("single"), coin: this.state.selectedCrypto }) + })} + + {/* + Get Address Script Hashes History + */} + {this.MethodButton({ + method: "getAddressScriptHashesHistory", + label: "3 Get Address Script Hashes History", + onPress: () => getAddressScriptHashesHistory({ addresses: this.getAddresses("array"), coin: this.state.selectedCrypto }) + })} + + {/* + Get Address Script Hash Mempool + */} + {this.MethodButton({ + method: "getAddressScriptHashMempool", + label: "4 Get Address Script Hash Mempool", + onPress: () => getAddressScriptHashMempool({ address: this.getAddresses("single"), coin: this.state.selectedCrypto }) + })} + + {/* + Get Address Script Hashes Mempool + */} + {this.MethodButton({ + method: "getAddressScriptHashesMempool", + label: "5 Get Address Script Hashes Mempool", + onPress: () => getAddressScriptHashesMempool({ addresses: this.getAddresses("array"), coin: this.state.selectedCrypto }) + })} + + {/* + Get UTXO's for Address Script Hash + */} + {this.MethodButton({ + method: "listUnspentAddressScriptHash", + label: "6 Get UTXO's for Address Script Hash", + onPress: () => listUnspentAddressScriptHash({ address: this.getAddresses("single"), coin: this.state.selectedCrypto }) + })} + + {/* + Get UTXO's for Address Script Hashes + */} + {this.MethodButton({ + method: "listUnspentAddressScriptHashes", + label: "7 Get UTXO's for Address Script Hashes", + onPress: () => listUnspentAddressScriptHashes({ addresses: this.getAddresses("array"), coin: this.state.selectedCrypto }) + })} + + {/* + Get Fee Estimate + */} + {this.MethodButton({ + method: "getFeeEstimate", + label: "8 Get Fee Estimate", + onPress: () => getFeeEstimate({ blocksWillingToWait: 8, coin: this.state.selectedCrypto }) + })} + + {/* + Get Version + */} + {this.MethodButton({ + method: "getVersion", + label: "9 Get Version", + onPress: () => getVersion({ coin: this.state.selectedCrypto }) + })} + + {/* + Get Available Peers + */} + {this.MethodButton({ + method: "getAvailablePeers", + label: "10 Get Available Peers", + onPress: () => getAvailablePeers({ coin: this.state.selectedCrypto }) + })} + + {/* + Get Peers + */} + {this.MethodButton({ + method: "getPeers", + label: "11 Get Peers", + onPress: () => getPeers({ coin: this.state.selectedCrypto }) + })} + + {/* + Disconnect From Peer + */} + {this.MethodButton({ + method: "disconnectFromPeer", + label: "12 Disconnect From Peer", + onPress: async () => await electrum.stop({ coin: this.state.selectedCrypto }), + afterOnPress: () => this.setState({ selectedCrypto: "" }) + })} + + {/* + Subscribe: Get New Block Headers + */} + {this.MethodButton({ + method: "getNewBlockHeadersSubscribe", + label: "13 Subscribe: Get New Block Headers", + onPress: async () => await getNewBlockHeadersSubscribe({ coin: this.state.selectedCrypto }) + })} + + {/* + Get A Specific Block Header + */} + {this.MethodButton({ + method: "getBlockHeader", + label: "14 Get A Specific Block Header", + onPress: async () => await getBlockHeader({ height: 1, coin: this.state.selectedCrypto }) + })} + + {/* + Broadcast Transaction + */} + {this.MethodButton({ + method: "broadcastTransaction", + label: "15 Broadcast Transaction", + onPress: async () => await broadcastTransaction({ rawTx: "", coin: this.state.selectedCrypto }) + })} + + {/* + Ping Server + */} + {this.MethodButton({ + method: "pingServer", + label: "16 Ping Server", + onPress: async () => await pingServer() + })} + + + + + ); + } +} + +const styles = StyleSheet.create({ + upperContent: { + flex: 0.5 + }, + lowerContent: { + flex: 0.5, + backgroundColor: colors.white + }, + methodContainer: { + flex: 0.6, + justifyContent: "center", + alignItems: "center", + backgroundColor: colors.white, + paddingBottom: 20 + }, + cryptoButton: { + width: "25%", + paddingHorizontal: 10, + paddingVertical: 8 + }, + cryptoButtonText: { + textAlign: "center", + fontSize: 14, + fontWeight: "bold" + }, + clearButton: { + position: "absolute", + zIndex: 100, + top: 5, + right: 10, + color: colors.white, + borderColor: colors.white, + borderWidth: 0.5, + borderRadius: 20, + padding: 5 + }, + activityIndicator: { + position: "absolute", + alignItems: "center", + justifyContent: "center" + }, + text: { + textAlign: "center", + fontSize: 14, + color: colors.darkPurple, + fontWeight: "bold" + }, + button: { + alignItems: "center", + justifyContent: "center", + borderWidth: 0.5, + borderColor: colors.darkPurple, + borderRadius: 5, + paddingHorizontal: 15, + paddingVertical: 5, + marginBottom: 15 + }, +}); + +const connect = require("react-redux").connect; +const bindActionCreators = require("redux").bindActionCreators; +const userActions = require("../actions/user"); +const walletActions = require("../actions/wallet"); +const transactionActions = require("../actions/transaction"); +const settingsActions = require("../actions/settings"); + +const mapStateToProps = ({...state}) => ({ + ...state +}); + +const mapDispatchToProps = (dispatch) => { + const actions = { + ...userActions, + ...walletActions, + ...transactionActions, + ...settingsActions + }; + return bindActionCreators({ + ...actions + }, dispatch); +}; + +module.exports = connect(mapStateToProps, mapDispatchToProps)(ElectrumTesting); \ No newline at end of file diff --git a/src/components/Fade.js b/src/components/Fade.js index c5ce57b..cbde767 100644 --- a/src/components/Fade.js +++ b/src/components/Fade.js @@ -11,20 +11,6 @@ const { } = require("../../ProjectData.json"); class Fade extends PureComponent { - getFade() { - try { - let arr = new Array(this.props.size).fill(null); - const increment = 1/this.props.size; - arr.map((item, i) => { - return ( - - ); - }); - } catch (e) { - console.log(e); - } - }; - render() { let arr = new Array(this.props.size).fill(null); const increment = 1/this.props.size; diff --git a/src/components/Header.js b/src/components/Header.js index a2457dd..3c11c42 100644 --- a/src/components/Header.js +++ b/src/components/Header.js @@ -76,7 +76,7 @@ class Header extends PureComponent { Currently Offline } - ) + ); } } diff --git a/src/components/ImportPhrase.js b/src/components/ImportPhrase.js index b38eccd..772285f 100644 --- a/src/components/ImportPhrase.js +++ b/src/components/ImportPhrase.js @@ -108,18 +108,18 @@ class ImportPhrase extends PureComponent { - this.updateMnemonic(mnemonic)} - value={this.state.mnemonic} - multiline={true} - /> + this.updateMnemonic(mnemonic)} + value={this.state.mnemonic} + multiline={true} + /> this.updateCamera({ display: true })} style={styles.cameraIcon}> - + @@ -137,7 +137,7 @@ class ImportPhrase extends PureComponent { {!this.state.displayCamera && - + } ); @@ -214,7 +214,7 @@ const walletActions = require("../actions/wallet"); const transactionActions = require("../actions/transaction"); const settingsActions = require("../actions/settings"); -const mapStateToProps = ({...state}, props) => ({ +const mapStateToProps = ({...state}) => ({ ...state }); diff --git a/src/components/Loading.js b/src/components/Loading.js index 53f3c7a..c47821d 100644 --- a/src/components/Loading.js +++ b/src/components/Loading.js @@ -28,25 +28,18 @@ const getAnimation = (name = "book") => { switch (name) { case "book": return require(`../assets/lottie/loading_book.json`); - break; case "loader": return require(`../assets/lottie/snap_loader_white.json`); - break; case "bitcoinMoon": return require(`../assets/lottie/bitcoin_to_the_moon.json`); - break; case "rocket": return require(`../assets/lottie/bms-rocket.json`); - break; case "cloudBook": return require(`../assets/lottie/downloading_book.json`); - break; case "threeCircleLoader": return require(`../assets/lottie/strategy_shape`); - break; default: return getCoinImage(name); //Assume the requested image is a coin - break; } } catch (e) { return require(`../assets/lottie/snap_loader_white.json`); @@ -64,7 +57,7 @@ class Loading extends PureComponent { style={{width: 100, height: 100, marginBottom: 40}} source={getAnimation(animationName)} /> - ) + ); } return ( - ) + ); } render() { @@ -100,7 +93,7 @@ class Loading extends PureComponent { - ) + ); } } diff --git a/src/components/MnemonicCarousel.js b/src/components/MnemonicCarousel.js index 65e6e5a..66bb359 100644 --- a/src/components/MnemonicCarousel.js +++ b/src/components/MnemonicCarousel.js @@ -125,7 +125,7 @@ const walletActions = require("../actions/wallet"); const transactionActions = require("../actions/transaction"); const settingsActions = require("../actions/settings"); -const mapStateToProps = ({...state}, props) => ({ +const mapStateToProps = ({...state}) => ({ ...state }); diff --git a/src/components/MnemonicSliderEntry.js b/src/components/MnemonicSliderEntry.js index 823c9f2..f32e998 100644 --- a/src/components/MnemonicSliderEntry.js +++ b/src/components/MnemonicSliderEntry.js @@ -97,7 +97,7 @@ const walletActions = require("../actions/wallet"); const transactionActions = require("../actions/transaction"); const settingsActions = require("../actions/settings"); -const mapStateToProps = ({...state}, props) => ({ +const mapStateToProps = ({...state}) => ({ ...state }); diff --git a/src/components/PinPad.js b/src/components/PinPad.js index 1cd0f50..5dd6dc7 100644 --- a/src/components/PinPad.js +++ b/src/components/PinPad.js @@ -47,7 +47,7 @@ class Pin extends PureComponent { pinSetup, pinSetupStep: 1, invalidPin: false - } + }; } async componentDidMount() { @@ -110,7 +110,7 @@ class Pin extends PureComponent { this.props.onSuccess(); } } catch (e) { - console.log(e) + console.log(e); } }; @@ -170,7 +170,7 @@ class Pin extends PureComponent { getDots = () => { try { if (this.state.value.length > 4) { - return ` ● ● ● ● +${this.state.value.length - 4}` + return ` ● ● ● ● +${this.state.value.length - 4}`; } else { const marks = this.state.value.replace(/./g, ' ● '); const dots = makeDots(4-this.state.value.length); @@ -196,7 +196,7 @@ class Pin extends PureComponent { Please Enter Your Pin - ) + ); } else { return ( @@ -204,19 +204,19 @@ class Pin extends PureComponent { Please Re-Enter Your Pin - ) + ); } } return ( - + Enter pin: {`Attempts Remaining: ${this.props.settings.pinAttemptsRemaining}`} - ) + ); } catch (e) {} }; @@ -224,49 +224,49 @@ class Pin extends PureComponent { return ( - + {this.getHeaderText()} - - {this.getDots()} + + {this.getDots()} - + {this.renderButton(this.state.digits[0])} {this.renderButton(this.state.digits[1])} {this.renderButton(this.state.digits[2])} - + {this.renderButton(this.state.digits[3])} {this.renderButton(this.state.digits[4])} {this.renderButton(this.state.digits[5])} - + {this.renderButton(this.state.digits[6])} {this.renderButton(this.state.digits[7])} {this.renderButton(this.state.digits[8])} - + this.handleClear()} activeOpacity={ACTIVE_OPACITY} style={[styles.buttonContainer, { borderWidth: 0 }]}> C {this.renderButton(this.state.digits[9])} - + - + Submit - ) + ); } } @@ -286,7 +286,7 @@ const connect = require("react-redux").connect; const bindActionCreators = require("redux").bindActionCreators; const settingsActions = require("../actions/settings"); -const mapStateToProps = ({...state}, props) => ({ +const mapStateToProps = ({...state}) => ({ ...state }); diff --git a/src/components/ReceiveTransaction.js b/src/components/ReceiveTransaction.js index a297d76..1d1817b 100644 --- a/src/components/ReceiveTransaction.js +++ b/src/components/ReceiveTransaction.js @@ -21,9 +21,8 @@ const { const { capitalize } = require("../utils/helpers"); -const queryString = require('query-string'); -formatUri = ({ coin = "bitcoin", address = "", amount = "", label = "" } = {}) => { +formatUri = ({ coin = "bitcoin", address = "" } = {}) => { try { //return `${coin}:${address}?amount=${amount.toString()}&label=${label}`; return `${coin}:${address}`; @@ -39,7 +38,7 @@ onSharePress = (address = "", selectedCoin = "Bitcoin") => { }, { // Android only: dialogTitle: "My Bitcoin Address." - }) + }); } catch (e) { console.log(e); } @@ -69,12 +68,12 @@ class ReceiveTransaction extends PureComponent { toValue: 0, duration } - ).start() + ).start(); }, duration/4); }); } catch (e) { console.log(e); - alert("Unable to copy address. Please try again or check your phone's permissions.") + alert("Unable to copy address. Please try again or check your phone's permissions."); } }; @@ -88,7 +87,7 @@ class ReceiveTransaction extends PureComponent { render() { - if (!this.props.address || !this.props.amount) return ; + if (!this.props.address || !this.props.amount) return ; return ( @@ -113,11 +112,11 @@ class ReceiveTransaction extends PureComponent {