diff --git a/src/components/CookiesBanner/index.jsx b/src/components/CookiesBanner/index.jsx
index f857685b..b8dcd88e 100644
--- a/src/components/CookiesBanner/index.jsx
+++ b/src/components/CookiesBanner/index.jsx
@@ -30,7 +30,7 @@ const useStyles = makeStyles({
padding: '27px 15px',
position: 'fixed',
width: '100%',
- zIndex: '5',
+ zIndex: '15',
},
content: {
maxWidth: '100%',
diff --git a/src/components/Root/index.js b/src/components/Root/index.js
index 8dac4d82..399c541f 100644
--- a/src/components/Root/index.js
+++ b/src/components/Root/index.js
@@ -4,7 +4,7 @@ import 'babel-polyfill'
import { theme as styledTheme } from '@gnosis.pm/safe-react-components'
import { MuiThemeProvider } from '@material-ui/core/styles'
import { ConnectedRouter } from 'connected-react-router'
-import React, { Suspense } from 'react'
+import React from 'react'
import { hot } from 'react-hot-loader/root'
import { Provider } from 'react-redux'
import { ThemeProvider } from 'styled-components'
@@ -15,6 +15,7 @@ import PageFrame from '../layout/PageFrame'
import AppRoutes from '~/routes'
import { history, store } from '~/store'
import theme from '~/theme/mui'
+import { wrapInSuspense } from '~/utils/wrapInSuspense'
import './index.scss'
import './OnboardCustom.scss'
@@ -24,11 +25,7 @@ const Root = () => (
-
- }>
-
-
-
+ {wrapInSuspense(, )}
diff --git a/src/logic/addressBook/store/selectors/index.js b/src/logic/addressBook/store/selectors/index.js
index edef7837..36c73f6b 100644
--- a/src/logic/addressBook/store/selectors/index.js
+++ b/src/logic/addressBook/store/selectors/index.js
@@ -1,7 +1,6 @@
/* eslint-disable import/named */
// @flow
import { List, Map } from 'immutable'
-import { useSelector } from 'react-redux'
import { Selector, createSelector } from 'reselect'
import type { AddressBook } from '~/logic/addressBook/model/addressBook'
@@ -35,15 +34,3 @@ export const getAddressBookListSelector: Selector {
- if (!userAddress) {
- return null
- }
- const addressBook = useSelector(getAddressBook)
- const result = addressBook.filter((addressBookItem) => addressBookItem.address === userAddress)
- if (result.size > 0) {
- return result.get(0).name
- }
- return null
-}
diff --git a/src/logic/addressBook/utils/index.js b/src/logic/addressBook/utils/index.js
index 52d98617..cfd3b864 100644
--- a/src/logic/addressBook/utils/index.js
+++ b/src/logic/addressBook/utils/index.js
@@ -38,7 +38,7 @@ export const getNameFromAddressBook = (userAddress: string): string | null => {
return null
}
const addressBook = useSelector(getAddressBook)
- return getNameFromAdbk(addressBook, userAddress)
+ return addressBook ? getNameFromAdbk(addressBook, userAddress) : null
}
export const getOwnersWithNameFromAddressBook = (addressBook: AddressBook, ownerList: List) => {
diff --git a/src/logic/collectibles/store/actions/fetchCollectibles.js b/src/logic/collectibles/store/actions/fetchCollectibles.js
index 0ed61a56..7e326bec 100644
--- a/src/logic/collectibles/store/actions/fetchCollectibles.js
+++ b/src/logic/collectibles/store/actions/fetchCollectibles.js
@@ -1,4 +1,5 @@
// @flow
+import { batch } from 'react-redux'
import type { Dispatch } from 'redux'
import { getNetwork } from '~/config'
@@ -13,8 +14,10 @@ const fetchCollectibles = () => async (dispatch: Dispatch, getState
const source = getConfiguredSource()
const collectibles = await source.fetchAllUserCollectiblesByCategoryAsync(safeAddress, network)
- dispatch(addNftAssets(collectibles.nftAssets))
- dispatch(addNftTokens(collectibles.nftTokens))
+ batch(() => {
+ dispatch(addNftAssets(collectibles.nftAssets))
+ dispatch(addNftTokens(collectibles.nftTokens))
+ })
}
export default fetchCollectibles
diff --git a/src/logic/contracts/generateBatchRequests.js b/src/logic/contracts/generateBatchRequests.js
new file mode 100644
index 00000000..6a0d31b8
--- /dev/null
+++ b/src/logic/contracts/generateBatchRequests.js
@@ -0,0 +1,58 @@
+// @flow
+import { getWeb3 } from '~/logic/wallets/getWeb3'
+
+/**
+ * Generates a batch request for grouping RPC calls
+ * @param {object} args
+ * @param {object} args.abi - contract ABI
+ * @param {string} args.address - contract address
+ * @param {object|undefined} args.batch - not required. If set, batch must be initialized outside (web3.BatchRequest)
+ * @param {object|undefined} args.context - not required. Can be any object, to be added to the batch response
+ * @param {array<{ args: [any], method: string, type: 'eth'|undefined } | string>} args.methods - methods to be called
+ * @returns {Promise<[*]>}
+ */
+const generateBatchRequests = ({ abi, address, batch, context, methods }) => {
+ const web3 = getWeb3()
+ const contractInstance = new web3.eth.Contract(abi, address)
+ const localBatch = batch ? null : new web3.BatchRequest()
+
+ const values = methods.map((methodObject) => {
+ let method, type, args = []
+
+ if (typeof methodObject === 'string') {
+ method = methodObject
+ } else {
+ ;({ method, type, args = [] } = methodObject)
+ }
+
+ return new Promise((resolve) => {
+ const resolver = (error, result) => {
+ if (error) {
+ resolve(null)
+ } else {
+ resolve(result)
+ }
+ }
+
+ try {
+ let request
+ if (type !== undefined) {
+ request = web3[type][method].request(...args, resolver)
+ } else {
+ request = contractInstance.methods[method](...args).call.request(resolver)
+ }
+ batch ? batch.add(request) : localBatch.add(request)
+ } catch (e) {
+ resolve(null)
+ }
+ })
+ })
+
+ localBatch && localBatch.execute()
+
+ const returnValues = context ? [context, ...values] : values
+
+ return Promise.all(returnValues)
+}
+
+export default generateBatchRequests
diff --git a/src/logic/contracts/methodIds.js b/src/logic/contracts/methodIds.js
index a946cf38..1f8d6eab 100644
--- a/src/logic/contracts/methodIds.js
+++ b/src/logic/contracts/methodIds.js
@@ -53,8 +53,8 @@ const METHOD_TO_ID = {
'0x694e80c3': SAFE_METHODS_NAMES.CHANGE_THRESHOLD,
}
-export const decodeParamsFromSafeMethod = async (data: string) => {
- const web3 = await getWeb3()
+export const decodeParamsFromSafeMethod = (data: string) => {
+ const web3 = getWeb3()
const [methodId, params] = [data.slice(0, 10), data.slice(10)]
switch (methodId) {
diff --git a/src/logic/contracts/safeContracts.js b/src/logic/contracts/safeContracts.js
index c82c94fe..7e53953c 100644
--- a/src/logic/contracts/safeContracts.js
+++ b/src/logic/contracts/safeContracts.js
@@ -3,7 +3,7 @@ import contract from 'truffle-contract'
import ProxyFactorySol from '@gnosis.pm/safe-contracts/build/contracts/GnosisSafeProxyFactory.json'
import GnosisSafeSol from '@gnosis.pm/safe-contracts/build/contracts/GnosisSafe.json'
import SafeProxy from '@gnosis.pm/safe-contracts/build/contracts/GnosisSafeProxy.json'
-import { ensureOnce } from '~/utils/singleton'
+import { ensureOnce, ensureOnceAsync } from '~/utils/singleton'
import { simpleMemoize } from '~/components/forms/validator'
import { getWeb3, getNetworkIdFrom } from '~/logic/wallets/getWeb3'
import { calculateGasOf, calculateGasPrice } from '~/logic/wallets/ethTransactions'
@@ -95,13 +95,12 @@ export const estimateGasForDeployingSafe = async (
return gas * parseInt(gasPrice, 10)
}
-export const getGnosisSafeInstanceAt = async (safeAddress: string) => {
+export const getGnosisSafeInstanceAt = simpleMemoize(async (safeAddress: string) => {
const web3 = getWeb3()
const GnosisSafe = await getGnosisSafeContract(web3)
const gnosisSafe = await GnosisSafe.at(safeAddress)
-
return gnosisSafe
-}
+})
const cleanByteCodeMetadata = (bytecode: string): string => {
const metaData = 'a165'
diff --git a/src/logic/currencyValues/api/fetchTokenCurrenciesBalances.js b/src/logic/currencyValues/api/fetchTokenCurrenciesBalances.js
index b3c15e0b..7fa21013 100644
--- a/src/logic/currencyValues/api/fetchTokenCurrenciesBalances.js
+++ b/src/logic/currencyValues/api/fetchTokenCurrenciesBalances.js
@@ -10,7 +10,11 @@ const fetchTokenCurrenciesBalances = (safeAddress: string) => {
const apiUrl = getTxServiceHost()
const url = `${apiUrl}safes/${safeAddress}/balances/usd/`
- return axios.get(url)
+ return axios.get(url, {
+ params: {
+ limit: 3000,
+ },
+ })
}
export default fetchTokenCurrenciesBalances
diff --git a/src/logic/currencyValues/store/actions/fetchCurrencySelectedValue.js b/src/logic/currencyValues/store/actions/fetchCurrencySelectedValue.js
index 9dd73823..c4129f9a 100644
--- a/src/logic/currencyValues/store/actions/fetchCurrencySelectedValue.js
+++ b/src/logic/currencyValues/store/actions/fetchCurrencySelectedValue.js
@@ -1,37 +1,21 @@
// @flow
-import { List } from 'immutable'
import { Dispatch as ReduxDispatch } from 'redux'
import fetchCurrenciesRates from '~/logic/currencyValues/api/fetchCurrenciesRates'
-import { setCurrencyBalances } from '~/logic/currencyValues/store/actions/setCurrencyBalances'
+import { setCurrencyRate } from '~/logic/currencyValues/store/actions/setCurrencyRate'
import { AVAILABLE_CURRENCIES } from '~/logic/currencyValues/store/model/currencyValues'
-import { currencyValuesListSelector } from '~/logic/currencyValues/store/selectors'
import type { GlobalState } from '~/store'
// eslint-disable-next-line max-len
-const fetchCurrencySelectedValue = (currencyValueSelected: AVAILABLE_CURRENCIES) => async (
+const fetchCurrencySelectedValue = (currencyValueSelected: $Keys) => async (
dispatch: ReduxDispatch,
- getState: Function,
) => {
- const state = getState()
- const currencyBalancesList = currencyValuesListSelector(state)
- const selectedCurrencyRateInBaseCurrency = await fetchCurrenciesRates(AVAILABLE_CURRENCIES.USD, currencyValueSelected)
-
- const newList = []
- for (const currencyValue of currencyBalancesList) {
- const { balanceInBaseCurrency } = currencyValue
-
- const balanceInSelectedCurrency = balanceInBaseCurrency * selectedCurrencyRateInBaseCurrency
-
- const updatedValue = currencyValue.merge({
- currencyName: currencyValueSelected,
- balanceInSelectedCurrency,
- })
-
- newList.push(updatedValue)
+ if (AVAILABLE_CURRENCIES.USD === currencyValueSelected) {
+ return dispatch(setCurrencyRate('1'))
}
- dispatch(setCurrencyBalances(List(newList)))
+ const selectedCurrencyRateInBaseCurrency = await fetchCurrenciesRates(AVAILABLE_CURRENCIES.USD, currencyValueSelected)
+ dispatch(setCurrencyRate(selectedCurrencyRateInBaseCurrency))
}
export default fetchCurrencySelectedValue
diff --git a/src/logic/currencyValues/store/actions/fetchCurrencyValues.js b/src/logic/currencyValues/store/actions/fetchCurrencyValues.js
index 8ad14f95..d71b18c0 100644
--- a/src/logic/currencyValues/store/actions/fetchCurrencyValues.js
+++ b/src/logic/currencyValues/store/actions/fetchCurrencyValues.js
@@ -1,43 +1,32 @@
// @flow
-import { List } from 'immutable'
+import { batch } from 'react-redux'
import type { Dispatch as ReduxDispatch } from 'redux'
-import fetchTokenCurrenciesBalances from '~/logic/currencyValues/api/fetchTokenCurrenciesBalances'
import fetchCurrencySelectedValue from '~/logic/currencyValues/store/actions/fetchCurrencySelectedValue'
import { CURRENCY_SELECTED_KEY } from '~/logic/currencyValues/store/actions/saveCurrencySelected'
-import { setCurrencyBalances } from '~/logic/currencyValues/store/actions/setCurrencyBalances'
+import { setCurrencyRate } from '~/logic/currencyValues/store/actions/setCurrencyRate'
import { setCurrencySelected } from '~/logic/currencyValues/store/actions/setCurrencySelected'
-import { AVAILABLE_CURRENCIES, makeBalanceCurrency } from '~/logic/currencyValues/store/model/currencyValues'
+import { AVAILABLE_CURRENCIES } from '~/logic/currencyValues/store/model/currencyValues'
import type { GlobalState } from '~/store'
import { loadFromStorage } from '~/utils/storage'
-export const fetchCurrencyValues = (safeAddress: string) => async (dispatch: ReduxDispatch) => {
+export const fetchCurrencyValues = () => async (dispatch: ReduxDispatch) => {
try {
- const tokensFetched = await fetchTokenCurrenciesBalances(safeAddress)
-
- // eslint-disable-next-line max-len
- const currencyList = List(
- tokensFetched.data
- .filter((currencyBalance) => currencyBalance.balanceUsd)
- .map((currencyBalance) => {
- const { balanceUsd, tokenAddress } = currencyBalance
- return makeBalanceCurrency({
- currencyName: balanceUsd ? AVAILABLE_CURRENCIES.USD : null,
- tokenAddress,
- balanceInBaseCurrency: balanceUsd,
- balanceInSelectedCurrency: balanceUsd,
- })
- }),
- )
-
- dispatch(setCurrencyBalances(currencyList))
const currencyStored = await loadFromStorage(CURRENCY_SELECTED_KEY)
+
if (!currencyStored) {
- return dispatch(setCurrencySelected(AVAILABLE_CURRENCIES.USD))
+ return batch(() => {
+ dispatch(setCurrencySelected(AVAILABLE_CURRENCIES.USD))
+ dispatch(setCurrencyRate(1))
+ })
}
+
const { currencyValueSelected } = currencyStored
- dispatch(fetchCurrencySelectedValue(currencyValueSelected))
- dispatch(setCurrencySelected(currencyValueSelected))
+
+ batch(() => {
+ dispatch(setCurrencySelected(currencyValueSelected))
+ dispatch(fetchCurrencySelectedValue(currencyValueSelected))
+ })
} catch (err) {
console.error('Error fetching tokens price list', err)
}
diff --git a/src/logic/currencyValues/store/actions/setCurrencyRate.js b/src/logic/currencyValues/store/actions/setCurrencyRate.js
new file mode 100644
index 00000000..c0cee64c
--- /dev/null
+++ b/src/logic/currencyValues/store/actions/setCurrencyRate.js
@@ -0,0 +1,12 @@
+// @flow
+import { createAction } from 'redux-actions'
+
+import type { CurrencyValuesProps } from '~/logic/currencyValues/store/model/currencyValues'
+
+export const SET_CURRENCY_RATE = 'SET_CURRENCY_RATE'
+
+// eslint-disable-next-line max-len
+export const setCurrencyRate = createAction(
+ SET_CURRENCY_RATE,
+ (currencyRate: string): CurrencyValuesProps => ({ currencyRate }),
+)
diff --git a/src/logic/currencyValues/store/actions/setCurrencySelected.js b/src/logic/currencyValues/store/actions/setCurrencySelected.js
index f8426f34..be0f1c2b 100644
--- a/src/logic/currencyValues/store/actions/setCurrencySelected.js
+++ b/src/logic/currencyValues/store/actions/setCurrencySelected.js
@@ -9,5 +9,5 @@ export const SET_CURRENT_CURRENCY = 'SET_CURRENT_CURRENCY'
// eslint-disable-next-line max-len
export const setCurrencySelected = createAction(
SET_CURRENT_CURRENCY,
- (currencyValueSelected: AVAILABLE_CURRENCIES): CurrencyValuesProps => ({ currencyValueSelected }),
+ (currencyValueSelected: $Keys): CurrencyValuesProps => ({ currencyValueSelected }),
)
diff --git a/src/logic/currencyValues/store/model/currencyValues.js b/src/logic/currencyValues/store/model/currencyValues.js
index a2d07581..f9d813a9 100644
--- a/src/logic/currencyValues/store/model/currencyValues.js
+++ b/src/logic/currencyValues/store/model/currencyValues.js
@@ -39,7 +39,7 @@ export const AVAILABLE_CURRENCIES = {
}
export type BalanceCurrencyType = {
- currencyName: AVAILABLE_CURRENCIES,
+ currencyName: $Keys,
tokenAddress: string,
balanceInBaseCurrency: string,
balanceInSelectedCurrency: string,
@@ -53,7 +53,8 @@ export const makeBalanceCurrency = Record({
})
export type CurrencyValuesProps = {
- currencyValueSelected: AVAILABLE_CURRENCIES,
+ currencyValueSelected: $Keys,
+ currencyRate: string,
currencyValuesList: BalanceCurrencyType[],
}
diff --git a/src/logic/currencyValues/store/reducer/currencyValues.js b/src/logic/currencyValues/store/reducer/currencyValues.js
index 8d24aff6..2d690fcd 100644
--- a/src/logic/currencyValues/store/reducer/currencyValues.js
+++ b/src/logic/currencyValues/store/reducer/currencyValues.js
@@ -2,8 +2,8 @@
import { Map } from 'immutable'
import { type ActionType, handleActions } from 'redux-actions'
-import { SET_CURRENCY_BALANCES } from '../actions/setCurrencyBalances'
-
+import { SET_CURRENCY_BALANCES } from '~/logic/currencyValues/store/actions/setCurrencyBalances'
+import { SET_CURRENCY_RATE } from '~/logic/currencyValues/store/actions/setCurrencyRate'
import { SET_CURRENT_CURRENCY } from '~/logic/currencyValues/store/actions/setCurrencySelected'
import type { State } from '~/logic/tokens/store/reducer/tokens'
@@ -11,19 +11,20 @@ export const CURRENCY_VALUES_KEY = 'currencyValues'
export default handleActions(
{
+ [SET_CURRENCY_RATE]: (state: State, action: ActionType): State => {
+ const { currencyRate } = action.payload
+
+ return state.set('currencyRate', currencyRate)
+ },
[SET_CURRENCY_BALANCES]: (state: State, action: ActionType): State => {
const { currencyBalances } = action.payload
- const newState = state.set('currencyBalances', currencyBalances)
-
- return newState
+ return state.set('currencyBalances', currencyBalances)
},
[SET_CURRENT_CURRENCY]: (state: State, action: ActionType): State => {
const { currencyValueSelected } = action.payload
- const newState = state.set('currencyValueSelected', currencyValueSelected)
-
- return newState
+ return state.set('currencyValueSelected', currencyValueSelected)
},
},
Map(),
diff --git a/src/logic/currencyValues/store/selectors/index.js b/src/logic/currencyValues/store/selectors/index.js
index 35d4c3f1..429bfef5 100644
--- a/src/logic/currencyValues/store/selectors/index.js
+++ b/src/logic/currencyValues/store/selectors/index.js
@@ -7,4 +7,7 @@ import { type GlobalState } from '~/store'
export const currencyValuesListSelector = (state: GlobalState) =>
state[CURRENCY_VALUES_KEY].get('currencyBalances') ? state[CURRENCY_VALUES_KEY].get('currencyBalances') : List([])
+
export const currentCurrencySelector = (state: GlobalState) => state[CURRENCY_VALUES_KEY].get('currencyValueSelected')
+
+export const currencyRateSelector = (state: GlobalState) => state[CURRENCY_VALUES_KEY].get('currencyRate')
diff --git a/src/logic/tokens/store/actions/activateAssetsByBalance.js b/src/logic/tokens/store/actions/activateAssetsByBalance.js
index 3cffa977..61811460 100644
--- a/src/logic/tokens/store/actions/activateAssetsByBalance.js
+++ b/src/logic/tokens/store/actions/activateAssetsByBalance.js
@@ -17,9 +17,14 @@ const activateAssetsByBalance = (safeAddress: string) => async (
getState: GetState,
) => {
try {
- await dispatch(fetchCollectibles())
const state = getState()
const safes = safesMapSelector(state)
+
+ if (safes.size === 0) {
+ return
+ }
+
+ await dispatch(fetchCollectibles())
const availableAssets = nftAssetsSelector(state)
const alreadyActiveAssets = safeActiveAssetsSelectorBySafe(safeAddress, safes)
const blacklistedAssets = safeBlacklistedAssetsSelectorBySafe(safeAddress, safes)
diff --git a/src/logic/tokens/store/actions/activateTokensByBalance.js b/src/logic/tokens/store/actions/activateTokensByBalance.js
deleted file mode 100644
index da377195..00000000
--- a/src/logic/tokens/store/actions/activateTokensByBalance.js
+++ /dev/null
@@ -1,61 +0,0 @@
-// @flow
-import { Set } from 'immutable'
-import type { Dispatch as ReduxDispatch } from 'redux'
-
-import fetchTokenBalanceList from '~/logic/tokens/api/fetchTokenBalanceList'
-import updateActiveTokens from '~/routes/safe/store/actions/updateActiveTokens'
-import updateSafe from '~/routes/safe/store/actions/updateSafe'
-import {
- safeActiveTokensSelectorBySafe,
- safeBlacklistedTokensSelectorBySafe,
- safesMapSelector,
-} from '~/routes/safe/store/selectors'
-import { type GetState, type GlobalState } from '~/store'
-
-const activateTokensByBalance = (safeAddress: string) => async (
- dispatch: ReduxDispatch,
- getState: GetState,
-) => {
- try {
- const result = await fetchTokenBalanceList(safeAddress)
- const safes = safesMapSelector(getState())
- const alreadyActiveTokens = safeActiveTokensSelectorBySafe(safeAddress, safes)
- const blacklistedTokens = safeBlacklistedTokensSelectorBySafe(safeAddress, safes)
-
- // addresses: potentially active tokens by balance
- // balances: tokens' balance returned by the backend
- const { addresses, balances } = result.data.reduce(
- (acc, { balance, tokenAddress }) => ({
- addresses: [...acc.addresses, tokenAddress],
- balances: [[tokenAddress, balance]],
- }),
- {
- addresses: [],
- balances: [],
- },
- )
-
- // update balance list for the safe
- dispatch(
- updateSafe({
- address: safeAddress,
- balances: Set(balances),
- }),
- )
-
- // active tokens by balance, excluding those already blacklisted and the `null` address
- const activeByBalance = addresses.filter((address) => address !== null && !blacklistedTokens.includes(address))
-
- // need to persist those already active tokens, despite its balances
- const activeTokens = alreadyActiveTokens.toSet().union(activeByBalance)
-
- // update list of active tokens
- dispatch(updateActiveTokens(safeAddress, activeTokens))
- } catch (err) {
- console.error('Error fetching active token list', err)
- }
-
- return null
-}
-
-export default activateTokensByBalance
diff --git a/src/logic/tokens/store/actions/fetchSafeTokens.js b/src/logic/tokens/store/actions/fetchSafeTokens.js
new file mode 100644
index 00000000..276a9ae2
--- /dev/null
+++ b/src/logic/tokens/store/actions/fetchSafeTokens.js
@@ -0,0 +1,100 @@
+// @flow
+import { BigNumber } from 'bignumber.js'
+import { List, Map } from 'immutable'
+import { batch } from 'react-redux'
+import type { Dispatch as ReduxDispatch } from 'redux'
+
+import fetchTokenCurrenciesBalances from '~/logic/currencyValues/api/fetchTokenCurrenciesBalances'
+import { setCurrencyBalances } from '~/logic/currencyValues/store/actions/setCurrencyBalances'
+import { AVAILABLE_CURRENCIES, makeBalanceCurrency } from '~/logic/currencyValues/store/model/currencyValues'
+import { CURRENCY_VALUES_KEY } from '~/logic/currencyValues/store/reducer/currencyValues'
+import addTokens from '~/logic/tokens/store/actions/saveTokens'
+import { makeToken } from '~/logic/tokens/store/model/token'
+import { TOKEN_REDUCER_ID } from '~/logic/tokens/store/reducer/tokens'
+import updateSafe from '~/routes/safe/store/actions/updateSafe'
+import { SAFE_REDUCER_ID } from '~/routes/safe/store/reducer/safe'
+import { type GetState, type GlobalState } from '~/store'
+
+const humanReadableBalance = (balance, decimals) => BigNumber(balance).times(`1e-${decimals}`).toFixed()
+const noFunc = () => {}
+const updateSafeValue = (address) => (valueToUpdate) => updateSafe({ address, ...valueToUpdate })
+
+const fetchSafeTokens = (safeAddress: string) => async (dispatch: ReduxDispatch, getState: GetState) => {
+ try {
+ const state = getState()
+ const safe = state[SAFE_REDUCER_ID].getIn([SAFE_REDUCER_ID, safeAddress])
+ const currentTokens = state[TOKEN_REDUCER_ID]
+
+ if (!safe) {
+ return
+ }
+
+ const result = await fetchTokenCurrenciesBalances(safeAddress)
+ const currentEthBalance = safe.get('ethBalance')
+ const safeBalances = safe.get('balances')
+ const alreadyActiveTokens = safe.get('activeTokens')
+ const blacklistedTokens = safe.get('blacklistedTokens')
+ const currencyValues = state[CURRENCY_VALUES_KEY]
+ const storedCurrencyBalances = currencyValues.get('currencyBalances')
+
+ const { balances, currencyList, ethBalance, tokens } = result.data.reduce(
+ (acc, { balance, balanceUsd, token, tokenAddress }) => {
+ if (tokenAddress === null) {
+ acc.ethBalance = humanReadableBalance(balance, 18)
+ } else {
+ acc.balances = acc.balances.merge({ [tokenAddress]: humanReadableBalance(balance, token.decimals) })
+
+ if (currentTokens && !currentTokens.get(tokenAddress)) {
+ acc.tokens = acc.tokens.push(makeToken({ address: tokenAddress, ...token }))
+ }
+ }
+
+ acc.currencyList = acc.currencyList.push(
+ makeBalanceCurrency({
+ currencyName: balanceUsd ? AVAILABLE_CURRENCIES.USD : null,
+ tokenAddress,
+ balanceInBaseCurrency: balanceUsd,
+ balanceInSelectedCurrency: balanceUsd,
+ }),
+ )
+
+ return acc
+ },
+ {
+ balances: Map(),
+ currencyList: List(),
+ ethBalance: '0',
+ tokens: List(),
+ },
+ )
+
+ // need to persist those already active tokens, despite its balances
+ const activeTokens = alreadyActiveTokens.toSet().union(
+ // active tokens by balance, excluding those already blacklisted and the `null` address
+ balances.keySeq().toSet().subtract(blacklistedTokens),
+ )
+
+ const update = updateSafeValue(safeAddress)
+ const updateActiveTokens = activeTokens.equals(alreadyActiveTokens) ? noFunc : update({ activeTokens })
+ const updateBalances = balances.equals(safeBalances) ? noFunc : update({ balances })
+ const updateEthBalance = ethBalance === currentEthBalance ? noFunc : update({ ethBalance })
+
+ const updateCurrencies = currencyList.equals(storedCurrencyBalances) ? noFunc : setCurrencyBalances(currencyList)
+
+ const updateTokens = tokens.size === 0 ? noFunc : addTokens(tokens)
+
+ batch(() => {
+ dispatch(updateActiveTokens)
+ dispatch(updateBalances)
+ dispatch(updateEthBalance)
+ dispatch(updateCurrencies)
+ dispatch(updateTokens)
+ })
+ } catch (err) {
+ console.error('Error fetching active token list', err)
+ }
+
+ return null
+}
+
+export default fetchSafeTokens
diff --git a/src/logic/tokens/store/actions/fetchTokens.js b/src/logic/tokens/store/actions/fetchTokens.js
index fdff81a6..1971bb0f 100644
--- a/src/logic/tokens/store/actions/fetchTokens.js
+++ b/src/logic/tokens/store/actions/fetchTokens.js
@@ -38,7 +38,10 @@ const createERC721TokenContract = async () => {
return erc721Token
}
-const OnlyBalanceToken = {
+// For the `batchRequest` of balances, we're just using the `balanceOf` method call.
+// So having a simple ABI only with `balanceOf` prevents errors
+// when instantiating non-standard ERC-20 Tokens.
+export const OnlyBalanceToken = {
contractName: 'OnlyBalanceToken',
abi: [
{
@@ -82,23 +85,12 @@ const OnlyBalanceToken = {
],
}
-// For the `batchRequest` of balances, we're just using the `balanceOf` method call.
-// So having a simple ABI only with `balanceOf` prevents errors
-// when instantiating non-standard ERC-20 Tokens.
-const createOnlyBalanceToken = () => {
- const web3 = getWeb3()
- const contract = new web3.eth.Contract(OnlyBalanceToken.abi)
- return contract
-}
-
export const getHumanFriendlyToken = ensureOnce(createHumanFriendlyTokenContract)
export const getStandardTokenContract = ensureOnce(createStandardTokenContract)
export const getERC721TokenContract = ensureOnce(createERC721TokenContract)
-export const getOnlyBalanceToken = ensureOnce(createOnlyBalanceToken)
-
export const containsMethodByHash = async (contractAddress: string, methodHash: string) => {
const web3 = getWeb3()
const byteCode = await web3.eth.getCode(contractAddress)
diff --git a/src/logic/tokens/utils/alternativeAbi.js b/src/logic/tokens/utils/alternativeAbi.js
index c6a8abfb..074ff62c 100644
--- a/src/logic/tokens/utils/alternativeAbi.js
+++ b/src/logic/tokens/utils/alternativeAbi.js
@@ -23,7 +23,7 @@ export const ALTERNATIVE_TOKEN_ABI = [
outputs: [
{
name: '',
- type: 'bytes32',
+ type: 'string',
},
],
payable: false,
diff --git a/src/routes/index.js b/src/routes/index.js
index 46e78d99..2e274dcf 100644
--- a/src/routes/index.js
+++ b/src/routes/index.js
@@ -1,30 +1,32 @@
// @flow
import React, { useEffect, useState } from 'react'
-import { connect } from 'react-redux'
+import { useSelector } from 'react-redux'
import { Redirect, Route, Switch, withRouter } from 'react-router-dom'
import { LOAD_ADDRESS, OPEN_ADDRESS, SAFELIST_ADDRESS, SAFE_PARAM_ADDRESS, WELCOME_ADDRESS } from './routes'
-import Welcome from './welcome/container'
import Loader from '~/components/Loader'
import { defaultSafeSelector } from '~/routes/safe/store/selectors'
-import { withTracker } from '~/utils/googleAnalytics'
+import { useAnalytics } from '~/utils/googleAnalytics'
-const Safe = React.lazy(() => import('./safe/container'))
+const Welcome = React.lazy(() => import('./welcome/container'))
const Open = React.lazy(() => import('./open/container/Open'))
+const Safe = React.lazy(() => import('./safe/container'))
+
const Load = React.lazy(() => import('./load/container/Load'))
const SAFE_ADDRESS = `${SAFELIST_ADDRESS}/:${SAFE_PARAM_ADDRESS}`
type RoutesProps = {
- defaultSafe?: string,
location: Object,
}
-const Routes = ({ defaultSafe, location }: RoutesProps) => {
+const Routes = ({ location }: RoutesProps) => {
const [isInitialLoad, setInitialLoad] = useState(true)
+ const defaultSafe = useSelector(defaultSafeSelector)
+ const { trackPage } = useAnalytics()
useEffect(() => {
if (location.pathname !== '/') {
@@ -32,6 +34,11 @@ const Routes = ({ defaultSafe, location }: RoutesProps) => {
}
}, [])
+ useEffect(() => {
+ const page = location.pathname + location.search
+ trackPage(page)
+ }, [location.pathname, trackPage])
+
return (
{
return
}
- setInitialLoad(false)
if (defaultSafe) {
return
}
@@ -54,17 +60,13 @@ const Routes = ({ defaultSafe, location }: RoutesProps) => {
return
}}
/>
-
-
-
-
+
+
+
+
)
}
-// $FlowFixMe
-export default connect