Remove fixed sort for balances (#2164)

* remove safe activeTokens

* buildSafe: take balances from store or initialize them as empty array

Co-authored-by: Fernando <fernando.greco@gmail.com>
Co-authored-by: Daniel Sanchez <daniel.sanchez@gnosis.pm>
This commit is contained in:
nicolas 2021-04-19 06:37:57 -03:00 committed by GitHub
parent f53cc84825
commit fe17101abf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 60 additions and 124 deletions

View File

@ -1,5 +1,5 @@
import GnosisSafeSol from '@gnosis.pm/safe-contracts/build/contracts/GnosisSafe.json' import GnosisSafeSol from '@gnosis.pm/safe-contracts/build/contracts/GnosisSafe.json'
import { List, Set, Map } from 'immutable' import { List } from 'immutable'
import { Action, Dispatch } from 'redux' import { Action, Dispatch } from 'redux'
import { AbiItem } from 'web3-utils' import { AbiItem } from 'web3-utils'
@ -87,9 +87,8 @@ export const buildSafe = async (
currentVersion: currentVersion ?? '', currentVersion: currentVersion ?? '',
needsUpdate, needsUpdate,
featuresEnabled, featuresEnabled,
balances: localSafe?.balances || Map(), balances: [],
latestIncomingTxBlock: 0, latestIncomingTxBlock: 0,
activeTokens: Set(),
modules, modules,
spendingLimits, spendingLimits,
} }

View File

@ -1,4 +1,4 @@
import { List, Map, Record, RecordOf, Set } from 'immutable' import { List, Record, RecordOf } from 'immutable'
import { FEATURES } from 'src/config/networks/network.d' import { FEATURES } from 'src/config/networks/network.d'
import { BalanceRecord } from 'src/logic/tokens/store/actions/fetchSafeTokens' import { BalanceRecord } from 'src/logic/tokens/store/actions/fetchSafeTokens'
@ -33,8 +33,7 @@ export type SafeRecordProps = {
owners: List<SafeOwner> owners: List<SafeOwner>
modules?: ModulePair[] | null modules?: ModulePair[] | null
spendingLimits?: SpendingLimit[] | null spendingLimits?: SpendingLimit[] | null
activeTokens: Set<string> balances: BalanceRecord[]
balances: Map<string, BalanceRecord>
nonce: number nonce: number
latestIncomingTxBlock: number latestIncomingTxBlock: number
recurringUser?: boolean recurringUser?: boolean
@ -53,8 +52,7 @@ const makeSafe = Record<SafeRecordProps>({
owners: List([]), owners: List([]),
modules: [], modules: [],
spendingLimits: [], spendingLimits: [],
activeTokens: Set(), balances: [],
balances: Map(),
nonce: 0, nonce: 0,
loadedViaUrl: false, loadedViaUrl: false,
latestIncomingTxBlock: 0, latestIncomingTxBlock: 0,

View File

@ -1,4 +1,4 @@
import { Map, Set, List } from 'immutable' import { Map, List } from 'immutable'
import { Action, handleActions } from 'redux-actions' import { Action, handleActions } from 'redux-actions'
import { ADD_SAFE_OWNER } from 'src/logic/safe/store/actions/addSafeOwner' import { ADD_SAFE_OWNER } from 'src/logic/safe/store/actions/addSafeOwner'
@ -25,14 +25,10 @@ export const buildSafe = (storedSafe: SafeRecordProps): SafeRecordProps => {
const names = storedSafe.owners.map((owner) => owner.name) const names = storedSafe.owners.map((owner) => owner.name)
const addresses = storedSafe.owners.map((owner) => checksumAddress(owner.address)) const addresses = storedSafe.owners.map((owner) => checksumAddress(owner.address))
const owners = buildOwnersFrom(Array.from(names), Array.from(addresses)) const owners = buildOwnersFrom(Array.from(names), Array.from(addresses))
const activeTokens = Set(storedSafe.activeTokens)
const balances = Map(storedSafe.balances)
return { return {
...storedSafe, ...storedSafe,
owners, owners,
balances,
activeTokens,
latestIncomingTxBlock: 0, latestIncomingTxBlock: 0,
modules: null, modules: null,
} }

View File

@ -1,4 +1,4 @@
import { List, Set } from 'immutable' import { List } from 'immutable'
import { matchPath, RouteComponentProps } from 'react-router-dom' import { matchPath, RouteComponentProps } from 'react-router-dom'
import { createSelector } from 'reselect' import { createSelector } from 'reselect'
import { SAFELIST_ADDRESS, SAFE_PARAM_ADDRESS } from 'src/routes/routes' import { SAFELIST_ADDRESS, SAFE_PARAM_ADDRESS } from 'src/routes/routes'
@ -9,6 +9,7 @@ import { AppReduxState } from 'src/store'
import { checksumAddress } from 'src/utils/checksumAddress' import { checksumAddress } from 'src/utils/checksumAddress'
import makeSafe, { SafeRecord, SafeRecordProps } from '../models/safe' import makeSafe, { SafeRecord, SafeRecordProps } from '../models/safe'
import { SafesMap } from 'src/routes/safe/store/reducer/types/safe' import { SafesMap } from 'src/routes/safe/store/reducer/types/safe'
import { BalanceRecord } from 'src/logic/tokens/store/actions/fetchSafeTokens'
const safesStateSelector = (state: AppReduxState) => state[SAFE_REDUCER_ID] const safesStateSelector = (state: AppReduxState) => state[SAFE_REDUCER_ID]
@ -65,14 +66,14 @@ export const safeSelector = createSelector(
}, },
) )
export const safeActiveTokensSelector = createSelector( export const safeBalancesSelector = createSelector(
safeSelector, safeSelector,
(safe): Set<string> => { (safe): Array<BalanceRecord> => {
if (!safe) { if (!safe) {
return Set() return []
} }
return safe.activeTokens return safe.balances
}, },
) )
@ -86,8 +87,6 @@ export const safeNameSelector = createSelector(safeSelector, safeFieldSelector('
export const safeEthBalanceSelector = createSelector(safeSelector, safeFieldSelector('ethBalance')) export const safeEthBalanceSelector = createSelector(safeSelector, safeFieldSelector('ethBalance'))
export const safeBalancesSelector = createSelector(safeSelector, safeFieldSelector('balances'))
export const safeNeedsUpdateSelector = createSelector(safeSelector, safeFieldSelector('needsUpdate')) export const safeNeedsUpdateSelector = createSelector(safeSelector, safeFieldSelector('needsUpdate'))
export const safeCurrentVersionSelector = createSelector(safeSelector, safeFieldSelector('currentVersion')) export const safeCurrentVersionSelector = createSelector(safeSelector, safeFieldSelector('currentVersion'))
@ -117,18 +116,6 @@ export const safeOwnersAddressesListSelector = createSelector(
}, },
) )
export const getActiveTokensAddressesForAllSafes = createSelector(safesListSelector, (safes) => {
const addresses = Set().withMutations((set) => {
safes.forEach((safe) => {
safe.activeTokens.forEach((tokenAddress) => {
set.add(tokenAddress)
})
})
})
return addresses
})
export const safeTotalFiatBalanceSelector = createSelector(safeSelector, (currentSafe) => { export const safeTotalFiatBalanceSelector = createSelector(safeSelector, (currentSafe) => {
return currentSafe?.totalFiatBalance return currentSafe?.totalFiatBalance
}) })

View File

@ -1,5 +1,5 @@
import { SafeRecordProps } from 'src/logic/safe/store/models/safe' import { SafeRecordProps } from 'src/logic/safe/store/models/safe'
import { List, Set, Map } from 'immutable' import { List } from 'immutable'
import { shouldSafeStoreBeUpdated } from 'src/logic/safe/utils/shouldSafeStoreBeUpdated' import { shouldSafeStoreBeUpdated } from 'src/logic/safe/utils/shouldSafeStoreBeUpdated'
const getMockedOldSafe = ({ const getMockedOldSafe = ({
@ -7,7 +7,6 @@ const getMockedOldSafe = ({
needsUpdate, needsUpdate,
balances, balances,
recurringUser, recurringUser,
activeTokens,
owners, owners,
featuresEnabled, featuresEnabled,
currentVersion, currentVersion,
@ -38,13 +37,10 @@ const getMockedOldSafe = ({
owners: owners || List([owner1, owner2]), owners: owners || List([owner1, owner2]),
modules: modules || [], modules: modules || [],
spendingLimits: spendingLimits || [], spendingLimits: spendingLimits || [],
activeTokens: activeTokens || Set([mockedActiveTokenAddress1, mockedActiveTokenAddress2]), balances: balances || [
balances: { tokenAddress: mockedActiveTokenAddress1, tokenBalance: '100' },
balances || { tokenAddress: mockedActiveTokenAddress2, tokenBalance: '10' },
Map({ ],
[mockedActiveTokenAddress1]: { tokenBalance: '100' },
[mockedActiveTokenAddress2]: { tokenBalance: '10' },
}),
nonce: nonce || 2, nonce: nonce || 2,
latestIncomingTxBlock: latestIncomingTxBlock || 1, latestIncomingTxBlock: latestIncomingTxBlock || 1,
recurringUser: recurringUser || false, recurringUser: recurringUser || false,
@ -177,34 +173,15 @@ describe('shouldSafeStoreBeUpdated', () => {
// Then // Then
expect(expectedResult).toEqual(true) expect(expectedResult).toEqual(true)
}) })
it(`Given an old activeTokens list and a new activeTokens list for the safe, should return true`, () => {
// given
const mockedActiveTokenAddress1 = '0x36591cd3DA96b21Ac9ca54cFaf80fe45107294F1'
const mockedActiveTokenAddress2 = '0x92aF97cbF10742dD2527ffaBA70e34C03CFFC2c1'
const oldActiveTokens = Set([mockedActiveTokenAddress1, mockedActiveTokenAddress2])
const newActiveTokens = Set([mockedActiveTokenAddress1])
const oldSafe = getMockedOldSafe({ activeTokens: oldActiveTokens })
const newSafeProps: Partial<SafeRecordProps> = {
activeTokens: newActiveTokens,
}
// When
const expectedResult = shouldSafeStoreBeUpdated(newSafeProps, oldSafe)
// Then
expect(expectedResult).toEqual(true)
})
it(`Given an old balances list and a new balances list for the safe, should return true`, () => { it(`Given an old balances list and a new balances list for the safe, should return true`, () => {
// given // given
const mockedActiveTokenAddress1 = '0x36591cd3DA96b21Ac9ca54cFaf80fe45107294F1' const mockedActiveTokenAddress1 = '0x36591cd3DA96b21Ac9ca54cFaf80fe45107294F1'
const mockedActiveTokenAddress2 = '0x92aF97cbF10742dD2527ffaBA70e34C03CFFC2c1' const mockedActiveTokenAddress2 = '0x92aF97cbF10742dD2527ffaBA70e34C03CFFC2c1'
const oldBalances = Map({ const oldBalances = [
[mockedActiveTokenAddress1]: { tokenBalance: '100' }, { tokenAddress: mockedActiveTokenAddress1, tokenBalance: '100' },
[mockedActiveTokenAddress2]: { tokenBalance: '100' }, { tokenAddress: mockedActiveTokenAddress2, tokenBalance: '100' },
}) ]
const newBalances = Map({ const newBalances = [{ tokenAddress: mockedActiveTokenAddress1, tokenBalance: '100' }]
[mockedActiveTokenAddress1]: { tokenBalance: '100' },
})
const oldSafe = getMockedOldSafe({ balances: oldBalances }) const oldSafe = getMockedOldSafe({ balances: oldBalances })
const newSafeProps: Partial<SafeRecordProps> = { const newSafeProps: Partial<SafeRecordProps> = {
balances: newBalances, balances: newBalances,

View File

@ -1,50 +1,46 @@
import { backOff } from 'exponential-backoff' import { backOff } from 'exponential-backoff'
import { List, Map } from 'immutable' import { List } from 'immutable'
import { Dispatch } from 'redux' import { Dispatch } from 'redux'
import { fetchTokenCurrenciesBalances, TokenBalance } from 'src/logic/safe/api/fetchTokenCurrenciesBalances' import { fetchTokenCurrenciesBalances, TokenBalance } from 'src/logic/safe/api/fetchTokenCurrenciesBalances'
import { addTokens } from 'src/logic/tokens/store/actions/addTokens' import { addTokens } from 'src/logic/tokens/store/actions/addTokens'
import { makeToken, Token } from 'src/logic/tokens/store/model/token' import { makeToken, Token } from 'src/logic/tokens/store/model/token'
import { TokenState } from 'src/logic/tokens/store/reducer/tokens'
import updateSafe from 'src/logic/safe/store/actions/updateSafe' import updateSafe from 'src/logic/safe/store/actions/updateSafe'
import { AppReduxState } from 'src/store' import { AppReduxState } from 'src/store'
import { humanReadableValue } from 'src/logic/tokens/utils/humanReadableValue' import { humanReadableValue } from 'src/logic/tokens/utils/humanReadableValue'
import { safeActiveTokensSelector, safeSelector } from 'src/logic/safe/store/selectors' import { safeSelector } from 'src/logic/safe/store/selectors'
import { tokensSelector } from 'src/logic/tokens/store/selectors'
import BigNumber from 'bignumber.js' import BigNumber from 'bignumber.js'
import { currentCurrencySelector } from 'src/logic/currencyValues/store/selectors' import { currentCurrencySelector } from 'src/logic/currencyValues/store/selectors'
import { ZERO_ADDRESS, sameAddress } from 'src/logic/wallets/ethAddresses' import { ZERO_ADDRESS, sameAddress } from 'src/logic/wallets/ethAddresses'
export type BalanceRecord = { export type BalanceRecord = {
tokenAddress?: string
tokenBalance: string tokenBalance: string
fiatBalance?: string fiatBalance?: string
} }
interface ExtractedData { interface ExtractedData {
balances: Map<string, BalanceRecord> balances: Array<BalanceRecord>
ethBalance: string ethBalance: string
tokens: List<Token> tokens: List<Token>
} }
const extractDataFromResult = (currentTokens: TokenState) => ( const extractDataFromResult = (
acc: ExtractedData, acc: ExtractedData,
{ balance, fiatBalance, tokenInfo }: TokenBalance, { balance, fiatBalance, tokenInfo }: TokenBalance,
): ExtractedData => { ): ExtractedData => {
const { address, decimals } = tokenInfo const { address, decimals } = tokenInfo
acc.balances = acc.balances.merge({ acc.balances.push({
[address]: { tokenAddress: address,
fiatBalance, fiatBalance,
tokenBalance: humanReadableValue(balance, Number(decimals)), tokenBalance: humanReadableValue(balance, Number(decimals)),
},
}) })
// Extract network token balance from backend balances // Extract network token balance from backend balances
if (sameAddress(address, ZERO_ADDRESS)) { if (sameAddress(address, ZERO_ADDRESS)) {
acc.ethBalance = humanReadableValue(balance, Number(decimals)) acc.ethBalance = humanReadableValue(balance, Number(decimals))
} } else {
if (currentTokens && !currentTokens.get(address)) {
acc.tokens = acc.tokens.push(makeToken({ ...tokenInfo })) acc.tokens = acc.tokens.push(makeToken({ ...tokenInfo }))
} }
@ -58,7 +54,6 @@ export const fetchSafeTokens = (safeAddress: string, currencySelected?: string)
try { try {
const state = getState() const state = getState()
const safe = safeSelector(state) const safe = safeSelector(state)
const currentTokens = tokensSelector(state)
if (!safe) { if (!safe) {
return return
@ -68,24 +63,19 @@ export const fetchSafeTokens = (safeAddress: string, currencySelected?: string)
const tokenCurrenciesBalances = await backOff(() => const tokenCurrenciesBalances = await backOff(() =>
fetchTokenCurrenciesBalances({ safeAddress, selectedCurrency: currencySelected ?? selectedCurrency }), fetchTokenCurrenciesBalances({ safeAddress, selectedCurrency: currencySelected ?? selectedCurrency }),
) )
const alreadyActiveTokens = safeActiveTokensSelector(state)
const { balances, ethBalance, tokens } = tokenCurrenciesBalances.items.reduce<ExtractedData>( const { balances, ethBalance, tokens } = tokenCurrenciesBalances.items.reduce<ExtractedData>(
extractDataFromResult(currentTokens), extractDataFromResult,
{ {
balances: Map(), balances: [],
ethBalance: '0', ethBalance: '0',
tokens: List(), tokens: List(),
}, },
) )
// need to persist those already active tokens, despite its balances
const activeTokens = alreadyActiveTokens.union(balances.keySeq().toSet())
dispatch( dispatch(
updateSafe({ updateSafe({
address: safeAddress, address: safeAddress,
activeTokens,
balances, balances,
ethBalance, ethBalance,
totalFiatBalance: new BigNumber(tokenCurrenciesBalances.fiatTotal).toFixed(2), totalFiatBalance: new BigNumber(tokenCurrenciesBalances.fiatTotal).toFixed(2),

View File

@ -77,7 +77,7 @@ const Coins = (props: Props): React.ReactElement => {
const columns = generateColumns() const columns = generateColumns()
const autoColumns = columns.filter((c) => !c.custom) const autoColumns = columns.filter((c) => !c.custom)
const selectedCurrency = useSelector(currentCurrencySelector) const selectedCurrency = useSelector(currentCurrencySelector)
const activeTokens = useSelector(extendedSafeTokensSelector) const safeTokens = useSelector(extendedSafeTokensSelector)
const granted = useSelector(grantedSelector) const granted = useSelector(grantedSelector)
const { trackEvent } = useAnalytics() const { trackEvent } = useAnalytics()
@ -85,22 +85,14 @@ const Coins = (props: Props): React.ReactElement => {
trackEvent({ category: SAFE_NAVIGATION_EVENT, action: 'Coins' }) trackEvent({ category: SAFE_NAVIGATION_EVENT, action: 'Coins' })
}, [trackEvent]) }, [trackEvent])
const filteredData: List<BalanceData> = useMemo(() => getBalanceData(activeTokens, selectedCurrency), [ const filteredData: List<BalanceData> = useMemo(() => getBalanceData(safeTokens, selectedCurrency), [
activeTokens, safeTokens,
selectedCurrency, selectedCurrency,
]) ])
return ( return (
<TableContainer> <TableContainer>
<Table <Table columns={columns} data={filteredData} defaultRowsPerPage={100} label="Balances" size={filteredData.size}>
columns={columns}
data={filteredData}
defaultFixed
defaultOrderBy={BALANCE_TABLE_ASSET_ID}
defaultRowsPerPage={100}
label="Balances"
size={filteredData.size}
>
{(sortedData) => {(sortedData) =>
sortedData.map((row, index) => ( sortedData.map((row, index) => (
<TableRow className={classes.hide} data-testid={BALANCE_ROW_TEST_ID} key={index} tabIndex={-1}> <TableRow className={classes.hide} data-testid={BALANCE_ROW_TEST_ID} key={index} tabIndex={-1}>

View File

@ -1,6 +1,4 @@
import { List } from 'immutable' import { List } from 'immutable'
import { getNetworkInfo } from 'src/config'
import { FIXED } from 'src/components/Table/sorting'
import { formatAmountInUsFormat } from 'src/logic/tokens/utils/formatAmount' import { formatAmountInUsFormat } from 'src/logic/tokens/utils/formatAmount'
import { TableColumn } from 'src/components/Table/types.d' import { TableColumn } from 'src/components/Table/types.d'
import { Token } from 'src/logic/tokens/store/model/token' import { Token } from 'src/logic/tokens/store/model/token'
@ -20,14 +18,12 @@ export interface BalanceData {
assetOrder: string assetOrder: string
balance: string balance: string
balanceOrder: number balanceOrder: number
fixed: boolean
value: string value: string
valueOrder: number valueOrder: number
} }
export const getBalanceData = (activeTokens: List<Token>, currencySelected?: string): List<BalanceData> => { export const getBalanceData = (safeTokens: List<Token>, currencySelected?: string): List<BalanceData> => {
const { nativeCoin } = getNetworkInfo() return safeTokens.map((token) => {
return activeTokens.map((token) => {
const { tokenBalance, fiatBalance } = token.balance const { tokenBalance, fiatBalance } = token.balance
return { return {
@ -40,7 +36,6 @@ export const getBalanceData = (activeTokens: List<Token>, currencySelected?: str
assetOrder: token.name, assetOrder: token.name,
[BALANCE_TABLE_BALANCE_ID]: `${formatAmountInUsFormat(tokenBalance?.toString() || '0')} ${token.symbol}`, [BALANCE_TABLE_BALANCE_ID]: `${formatAmountInUsFormat(tokenBalance?.toString() || '0')} ${token.symbol}`,
balanceOrder: Number(tokenBalance), balanceOrder: Number(tokenBalance),
[FIXED]: token.symbol === nativeCoin.symbol,
[BALANCE_TABLE_VALUE_ID]: getTokenPriceInCurrency(fiatBalance || '0', currencySelected), [BALANCE_TABLE_VALUE_ID]: getTokenPriceInCurrency(fiatBalance || '0', currencySelected),
valueOrder: Number(tokenBalance), valueOrder: Number(tokenBalance),
} }

View File

@ -1,4 +1,4 @@
import { List, Map } from 'immutable' import { List } from 'immutable'
import { createSelector } from 'reselect' import { createSelector } from 'reselect'
import { Token } from 'src/logic/tokens/store/model/token' import { Token } from 'src/logic/tokens/store/model/token'
@ -7,7 +7,7 @@ import { getEthAsToken } from 'src/logic/tokens/utils/tokenHelpers'
import { isUserAnOwner, sameAddress } from 'src/logic/wallets/ethAddresses' import { isUserAnOwner, sameAddress } from 'src/logic/wallets/ethAddresses'
import { userAccountSelector } from 'src/logic/wallets/store/selectors' import { userAccountSelector } from 'src/logic/wallets/store/selectors'
import { safeActiveTokensSelector, safeBalancesSelector, safeSelector } from 'src/logic/safe/store/selectors' import { safeBalancesSelector, safeSelector } from 'src/logic/safe/store/selectors'
import { SafeRecord } from 'src/logic/safe/store/models/safe' import { SafeRecord } from 'src/logic/safe/store/models/safe'
export const grantedSelector = createSelector( export const grantedSelector = createSelector(
@ -25,28 +25,30 @@ const safeEthAsTokenSelector = createSelector(safeSelector, (safe?: SafeRecord):
}) })
export const extendedSafeTokensSelector = createSelector( export const extendedSafeTokensSelector = createSelector(
safeActiveTokensSelector,
safeBalancesSelector, safeBalancesSelector,
tokensSelector, tokensSelector,
safeEthAsTokenSelector, safeEthAsTokenSelector,
(safeTokens, balances, tokensList, ethAsToken): List<Token> => { (safeBalances, tokensList, ethAsToken): List<Token> => {
const extendedTokens = Map<string, Token>().withMutations((map) => { const extendedTokens: Array<Token> = []
safeTokens.forEach((tokenAddress) => {
const baseToken = tokensList.get(tokenAddress)
const tokenBalance = balances?.get(tokenAddress)
if (baseToken) { safeBalances.forEach((safeBalance) => {
const updatedBaseToken = baseToken.set('balance', tokenBalance || { tokenBalance: '0', fiatBalance: '0' }) const tokenAddress = safeBalance.tokenAddress
if (sameAddress(tokenAddress, ethAsToken?.address)) {
map.set(tokenAddress, updatedBaseToken.set('logoUri', ethAsToken?.logoUri || baseToken.logoUri)) if (!tokenAddress) {
} else { return
map.set(tokenAddress, updatedBaseToken) }
}
} const baseToken = sameAddress(tokenAddress, ethAsToken?.address) ? ethAsToken : tokensList.get(tokenAddress)
})
if (!baseToken) {
return
}
const token = baseToken.set('balance', safeBalance)
extendedTokens.push(token)
}) })
return extendedTokens.toList() return List(extendedTokens)
}, },
) )