-
+ )}
+
+ {hasSynced === false && (
+
+
+
Fund your Zap wallet
+
Might as well fund your wallet while you're waiting to sync.
+
+ {address && address.length ? (
+
+ ) : (
+
+ )}
)}
@@ -93,8 +113,8 @@ class Syncing extends Component {
Syncing.propTypes = {
fetchBlockHeight: PropTypes.func.isRequired,
- newAddress: PropTypes.func.isRequired,
address: PropTypes.string.isRequired,
+ hasSynced: PropTypes.bool,
syncPercentage: PropTypes.number,
blockHeight: PropTypes.number,
lndBlockHeight: PropTypes.number
diff --git a/app/components/Wallet/ReceiveModal.js b/app/components/Wallet/ReceiveModal.js
index 323a8a5f..83874282 100644
--- a/app/components/Wallet/ReceiveModal.js
+++ b/app/components/Wallet/ReceiveModal.js
@@ -117,7 +117,7 @@ ReceiveModal.propTypes = {
}).isRequired,
isOpen: PropTypes.bool.isRequired,
pubkey: PropTypes.string,
- address: PropTypes.string.isRequired,
+ address: PropTypes.string,
alias: PropTypes.string,
closeReceiveModal: PropTypes.func.isRequired
}
diff --git a/app/containers/Root.js b/app/containers/Root.js
index cf41b704..96b94632 100644
--- a/app/containers/Root.js
+++ b/app/containers/Root.js
@@ -32,7 +32,7 @@ import {
setReEnterSeedIndexes
} from '../reducers/onboarding'
import { fetchBlockHeight, lndSelectors } from '../reducers/lnd'
-import { newAddress } from '../reducers/address'
+import { walletAddress } from '../reducers/address'
import Routes from '../routes'
const mapDispatchToProps = {
@@ -54,7 +54,7 @@ const mapDispatchToProps = {
unlockWallet,
setSignupCreate,
setSignupImport,
- newAddress,
+ walletAddress,
updateReEnterSeedInput,
updateRecoverSeedInput,
setReEnterSeedIndexes,
@@ -66,6 +66,7 @@ const mapStateToProps = state => ({
lnd: state.lnd,
onboarding: state.onboarding,
address: state.address,
+ info: state.info,
syncPercentage: lndSelectors.syncPercentage(state),
passwordIsValid: onboardingSelectors.passwordIsValid(state),
@@ -84,7 +85,7 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
fetchBlockHeight: dispatchProps.fetchBlockHeight,
blockHeight: stateProps.lnd.blockHeight,
lndBlockHeight: stateProps.lnd.lndBlockHeight,
- newAddress: dispatchProps.newAddress,
+ hasSynced: stateProps.info.hasSynced,
syncPercentage: stateProps.syncPercentage,
address: stateProps.address.address
}
diff --git a/app/lnd/methods/index.js b/app/lnd/methods/index.js
index 4e081765..df66368d 100644
--- a/app/lnd/methods/index.js
+++ b/app/lnd/methods/index.js
@@ -60,7 +60,7 @@ export default function(lnd, log, event, msg, data) {
// Data looks like { address: '' }
walletController
.newAddress(lnd, data.type)
- .then(({ address }) => event.sender.send('receiveAddress', address))
+ .then(({ address }) => event.sender.send('receiveAddress', { type: data.type, address }))
.catch(error => log.error('newaddress:', error))
break
case 'setAlias':
diff --git a/app/reducers/address.js b/app/reducers/address.js
index 29af7b97..209a708f 100644
--- a/app/reducers/address.js
+++ b/app/reducers/address.js
@@ -1,4 +1,6 @@
import { ipcRenderer } from 'electron'
+import Store from 'electron-store'
+
// ------------------------------------
// Constants
// ------------------------------------
@@ -36,15 +38,46 @@ export function closeWalletModal() {
}
}
+// Get our existing address if there is one, otherwise generate a new one.
+export const walletAddress = type => (dispatch, getState) => {
+ let address
+
+ // Wallet addresses are keyed under the node pubKey in our store.
+ const state = getState()
+ const pubKey = state.info.data.identity_pubkey
+ if (pubKey) {
+ const store = new Store({ name: 'wallet' })
+ address = store.get(`${pubKey}.${type}`, null)
+ }
+
+ // If we have an address already, use that. Otherwise, generate a new address.
+ if (address) {
+ dispatch({ type: RECEIVE_ADDRESS, address })
+ } else {
+ dispatch(newAddress(type))
+ }
+}
+
// Send IPC event for getinfo
-export const newAddress = type => async dispatch => {
+export const newAddress = type => dispatch => {
dispatch(getAddress())
ipcRenderer.send('lnd', { msg: 'newaddress', data: { type: addressTypes[type] } })
}
// Receive IPC event for info
-export const receiveAddress = (event, address) => dispatch =>
- dispatch({ type: RECEIVE_ADDRESS, address })
+export const receiveAddress = (event, data) => (dispatch, getState) => {
+ const state = getState()
+ const pubKey = state.info.data.identity_pubkey
+
+ // If we know the node's public key, store the address for reuse.
+ if (pubKey) {
+ const type = Object.keys(addressTypes).find(key => addressTypes[key] === data.type)
+ const store = new Store({ name: 'wallet' })
+ store.set(`${pubKey}.${type}`, data.address)
+ }
+
+ dispatch({ type: RECEIVE_ADDRESS, address: data.address })
+}
// ------------------------------------
// Action Handlers
diff --git a/app/reducers/info.js b/app/reducers/info.js
index 49dba465..a7304242 100644
--- a/app/reducers/info.js
+++ b/app/reducers/info.js
@@ -1,6 +1,8 @@
+import Store from 'electron-store'
import bitcoin from 'bitcoinjs-lib'
import { ipcRenderer } from 'electron'
+import { walletAddress } from './address'
// ------------------------------------
// Constants
@@ -8,6 +10,7 @@ import { ipcRenderer } from 'electron'
export const GET_INFO = 'GET_INFO'
export const RECEIVE_INFO = 'RECEIVE_INFO'
export const SET_WALLET_CURRENCY_FILTERS = 'SET_WALLET_CURRENCY_FILTERS'
+export const SET_HAS_SYNCED = 'SET_HAS_SYNCED'
// ------------------------------------
// Actions
@@ -25,6 +28,13 @@ export function setWalletCurrencyFilters(showWalletCurrencyFilters) {
}
}
+export const setHasSynced = hasSynced => {
+ return {
+ type: SET_HAS_SYNCED,
+ hasSynced
+ }
+}
+
// Send IPC event for getinfo
export const fetchInfo = () => async dispatch => {
dispatch(getInfo())
@@ -32,8 +42,20 @@ export const fetchInfo = () => async dispatch => {
}
// Receive IPC event for info
-export const receiveInfo = (event, data) => dispatch => {
+export const receiveInfo = (event, data) => (dispatch, getState) => {
dispatch({ type: RECEIVE_INFO, data })
+
+ // Now that we have the node info, get the current wallet address.
+ dispatch(walletAddress('np2wkh'))
+
+ // Determine the node's current sync state.
+ const state = getState()
+ if (typeof state.info.hasSynced === 'undefined') {
+ const store = new Store({ name: 'wallet' })
+ const hasSynced = store.get(`${data.identity_pubkey}.hasSynced`, false)
+ store.set(`${data.identity_pubkey}.hasSynced`, hasSynced)
+ dispatch(setHasSynced(hasSynced))
+ }
}
const networks = {
@@ -57,6 +79,10 @@ const networks = {
// Action Handlers
// ------------------------------------
const ACTION_HANDLERS = {
+ [SET_HAS_SYNCED]: (state, { hasSynced }) => ({
+ ...state,
+ hasSynced
+ }),
[GET_INFO]: state => ({ ...state, infoLoading: true }),
[RECEIVE_INFO]: (state, { data }) => ({
...state,
@@ -75,6 +101,7 @@ const ACTION_HANDLERS = {
// ------------------------------------
const initialState = {
infoLoading: false,
+ hasSynced: undefined,
network: {},
data: {},
showWalletCurrencyFilters: false
diff --git a/app/reducers/lnd.js b/app/reducers/lnd.js
index 2bb63f3a..9b2263d4 100644
--- a/app/reducers/lnd.js
+++ b/app/reducers/lnd.js
@@ -1,7 +1,8 @@
+import Store from 'electron-store'
import { createSelector } from 'reselect'
import { fetchTicker } from './ticker'
import { fetchBalance } from './balance'
-import { fetchInfo } from './info'
+import { fetchInfo, setHasSynced } from './info'
import { requestBlockHeight } from '../api'
import { showNotification } from '../notifications'
// ------------------------------------
@@ -25,14 +26,23 @@ export const GRPC_CONNECTED = 'GRPC_CONNECTED'
export const lndSyncing = () => dispatch => dispatch({ type: START_SYNCING })
// Receive IPC event for LND stoping sync
-export const lndSynced = () => dispatch => {
+export const lndSynced = () => (dispatch, getState) => {
+ // Persist the fact that the wallet has been synced at least once.
+ const state = getState()
+ const pubKey = state.info.data.identity_pubkey
+ if (pubKey) {
+ const store = new Store({ name: 'wallet' })
+ store.set(`${pubKey}.hasSynced`, true)
+ }
+
+ dispatch({ type: STOP_SYNCING })
+ dispatch(setHasSynced(true))
+
// Fetch data now that we know LND is synced
dispatch(fetchTicker())
dispatch(fetchBalance())
dispatch(fetchInfo())
- dispatch({ type: STOP_SYNCING })
-
// HTML 5 desktop notification for the new transaction
const notifTitle = 'Lightning Node Synced'
const notifBody = "Visa who? You're your own payment processor now!"
diff --git a/app/reducers/transaction.js b/app/reducers/transaction.js
index 42798639..69dfb4c3 100644
--- a/app/reducers/transaction.js
+++ b/app/reducers/transaction.js
@@ -58,9 +58,21 @@ export const fetchTransactions = () => dispatch => {
}
// Receive IPC event for payments
-export const receiveTransactions = (event, { transactions }) => dispatch =>
+export const receiveTransactions = (event, { transactions }) => (dispatch, getState) => {
dispatch({ type: RECEIVE_TRANSACTIONS, transactions })
+ // If our current wallet address has been used, generate a new one.
+ const state = getState()
+ const currentAddress = state.address.address
+ let usedAddresses = []
+ transactions.forEach(transaction => {
+ usedAddresses = usedAddresses.concat(transaction.dest_addresses)
+ })
+ if (usedAddresses.includes(currentAddress)) {
+ dispatch(newAddress('np2wkh'))
+ }
+}
+
export const sendCoins = ({ value, addr, currency }) => dispatch => {
// backend needs amount in satoshis no matter what currency we are using
const amount = btc.convert(currency, 'sats', value)
diff --git a/app/routes/activity/containers/ActivityContainer.js b/app/routes/activity/containers/ActivityContainer.js
index 1ed441e2..9a30bfd9 100644
--- a/app/routes/activity/containers/ActivityContainer.js
+++ b/app/routes/activity/containers/ActivityContainer.js
@@ -13,7 +13,7 @@ import {
updateSearchActive,
updateSearchText
} from 'reducers/activity'
-import { newAddress, openWalletModal } from 'reducers/address'
+import { walletAddress, openWalletModal } from 'reducers/address'
import { setFormType } from 'reducers/form'
import { payFormSelectors } from 'reducers/payform'
@@ -33,7 +33,7 @@ const mapDispatchToProps = {
hideActivityModal,
changeFilter,
toggleFilterPulldown,
- newAddress,
+ walletAddress,
openWalletModal,
fetchBalance,
updateSearchActive,
@@ -93,7 +93,7 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => ({
setCurrency: dispatchProps.setCurrency,
setWalletCurrencyFilters: dispatchProps.setWalletCurrencyFilters,
- newAddress: dispatchProps.newAddress,
+ walletAddress: dispatchProps.walletAddress,
openReceiveModal: dispatchProps.openWalletModal,
openPayForm: () => dispatchProps.setFormType('PAY_FORM'),
openRequestForm: () => dispatchProps.setFormType('REQUEST_FORM')
diff --git a/app/routes/app/components/App.js b/app/routes/app/components/App.js
index b48d0e43..53354203 100644
--- a/app/routes/app/components/App.js
+++ b/app/routes/app/components/App.js
@@ -21,7 +21,6 @@ class App extends Component {
const {
fetchTicker,
fetchInfo,
- newAddress,
fetchChannels,
fetchSuggestedNodes,
fetchBalance,
@@ -32,8 +31,6 @@ class App extends Component {
fetchTicker()
// fetch node info
fetchInfo()
- // fetch new address for wallet
- newAddress('np2wkh')
// fetch nodes channels
fetchChannels()
// fetch suggested nodes list from zap.jackmallers.com/suggested-peers
@@ -108,7 +105,6 @@ App.propTypes = {
receiveModalProps: PropTypes.object,
channelFormProps: PropTypes.object,
- newAddress: PropTypes.func.isRequired,
fetchInfo: PropTypes.func.isRequired,
fetchTicker: PropTypes.func.isRequired,
clearError: PropTypes.func.isRequired,
diff --git a/app/routes/app/containers/AppContainer.js b/app/routes/app/containers/AppContainer.js
index 98c5ee67..aa2b1016 100644
--- a/app/routes/app/containers/AppContainer.js
+++ b/app/routes/app/containers/AppContainer.js
@@ -5,7 +5,7 @@ import { btc } from 'utils'
import { fetchTicker, setCurrency, tickerSelectors } from 'reducers/ticker'
-import { newAddress, closeWalletModal } from 'reducers/address'
+import { closeWalletModal } from 'reducers/address'
import { fetchInfo } from 'reducers/info'
@@ -79,7 +79,6 @@ const mapDispatchToProps = {
fetchTicker,
setCurrency,
- newAddress,
closeWalletModal,
fetchInfo,
@@ -396,7 +395,6 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
pubkey: stateProps.info.data.identity_pubkey,
address: stateProps.address.address,
alias: stateProps.info.data.alias,
- newAddress: dispatchProps.newAddress,
closeReceiveModal: dispatchProps.closeWalletModal
}
diff --git a/test/reducers/__snapshots__/info.spec.js.snap b/test/reducers/__snapshots__/info.spec.js.snap
index e73a2362..ceb3f788 100644
--- a/test/reducers/__snapshots__/info.spec.js.snap
+++ b/test/reducers/__snapshots__/info.spec.js.snap
@@ -3,6 +3,7 @@
exports[`reducers infoReducer should correctly getInfo 1`] = `
Object {
"data": Object {},
+ "hasSynced": undefined,
"infoLoading": true,
"network": Object {},
"showWalletCurrencyFilters": false,
@@ -12,6 +13,7 @@ Object {
exports[`reducers infoReducer should correctly receiveInfo 1`] = `
Object {
"data": "foo",
+ "hasSynced": undefined,
"infoLoading": false,
"network": Object {
"bitcoinJsNetwork": Object {
@@ -37,6 +39,7 @@ Object {
exports[`reducers infoReducer should handle initial state 1`] = `
Object {
"data": Object {},
+ "hasSynced": undefined,
"infoLoading": false,
"network": Object {},
"showWalletCurrencyFilters": false,