Merge branch 'development' into fix/readme

This commit is contained in:
Mikhail Mikheev 2021-04-20 11:49:07 +03:00 committed by GitHub
commit dc166e5941
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 73 additions and 226 deletions

View File

@ -1,5 +1,5 @@
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 { AbiItem } from 'web3-utils'
@ -87,9 +87,8 @@ export const buildSafe = async (
currentVersion: currentVersion ?? '',
needsUpdate,
featuresEnabled,
balances: localSafe?.balances || Map(),
balances: [],
latestIncomingTxBlock: 0,
activeTokens: Set(),
modules,
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 { BalanceRecord } from 'src/logic/tokens/store/actions/fetchSafeTokens'
@ -33,8 +33,7 @@ export type SafeRecordProps = {
owners: List<SafeOwner>
modules?: ModulePair[] | null
spendingLimits?: SpendingLimit[] | null
activeTokens: Set<string>
balances: Map<string, BalanceRecord>
balances: BalanceRecord[]
nonce: number
latestIncomingTxBlock: number
recurringUser?: boolean
@ -53,8 +52,7 @@ const makeSafe = Record<SafeRecordProps>({
owners: List([]),
modules: [],
spendingLimits: [],
activeTokens: Set(),
balances: Map(),
balances: [],
nonce: 0,
loadedViaUrl: false,
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 { 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 addresses = storedSafe.owners.map((owner) => checksumAddress(owner.address))
const owners = buildOwnersFrom(Array.from(names), Array.from(addresses))
const activeTokens = Set(storedSafe.activeTokens)
const balances = Map(storedSafe.balances)
return {
...storedSafe,
owners,
balances,
activeTokens,
latestIncomingTxBlock: 0,
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 { createSelector } from 'reselect'
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 makeSafe, { SafeRecord, SafeRecordProps } from '../models/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]
@ -65,14 +66,14 @@ export const safeSelector = createSelector(
},
)
export const safeActiveTokensSelector = createSelector(
export const safeBalancesSelector = createSelector(
safeSelector,
(safe): Set<string> => {
(safe): Array<BalanceRecord> => {
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 safeBalancesSelector = createSelector(safeSelector, safeFieldSelector('balances'))
export const safeNeedsUpdateSelector = createSelector(safeSelector, safeFieldSelector('needsUpdate'))
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) => {
return currentSafe?.totalFiatBalance
})

View File

@ -1,5 +1,5 @@
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'
const getMockedOldSafe = ({
@ -7,7 +7,6 @@ const getMockedOldSafe = ({
needsUpdate,
balances,
recurringUser,
activeTokens,
owners,
featuresEnabled,
currentVersion,
@ -38,13 +37,10 @@ const getMockedOldSafe = ({
owners: owners || List([owner1, owner2]),
modules: modules || [],
spendingLimits: spendingLimits || [],
activeTokens: activeTokens || Set([mockedActiveTokenAddress1, mockedActiveTokenAddress2]),
balances:
balances ||
Map({
[mockedActiveTokenAddress1]: { tokenBalance: '100' },
[mockedActiveTokenAddress2]: { tokenBalance: '10' },
}),
balances: balances || [
{ tokenAddress: mockedActiveTokenAddress1, tokenBalance: '100' },
{ tokenAddress: mockedActiveTokenAddress2, tokenBalance: '10' },
],
nonce: nonce || 2,
latestIncomingTxBlock: latestIncomingTxBlock || 1,
recurringUser: recurringUser || false,
@ -177,34 +173,15 @@ describe('shouldSafeStoreBeUpdated', () => {
// Then
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`, () => {
// given
const mockedActiveTokenAddress1 = '0x36591cd3DA96b21Ac9ca54cFaf80fe45107294F1'
const mockedActiveTokenAddress2 = '0x92aF97cbF10742dD2527ffaBA70e34C03CFFC2c1'
const oldBalances = Map({
[mockedActiveTokenAddress1]: { tokenBalance: '100' },
[mockedActiveTokenAddress2]: { tokenBalance: '100' },
})
const newBalances = Map({
[mockedActiveTokenAddress1]: { tokenBalance: '100' },
})
const oldBalances = [
{ tokenAddress: mockedActiveTokenAddress1, tokenBalance: '100' },
{ tokenAddress: mockedActiveTokenAddress2, tokenBalance: '100' },
]
const newBalances = [{ tokenAddress: mockedActiveTokenAddress1, tokenBalance: '100' }]
const oldSafe = getMockedOldSafe({ balances: oldBalances })
const newSafeProps: Partial<SafeRecordProps> = {
balances: newBalances,

View File

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

View File

@ -67,10 +67,22 @@ export const staticAppsList: Array<StaticAppInfo> = [
},
// dHedge
{
url: `${process.env.REACT_APP_IPFS_GATEWAY}/QmaiemnumMaaK9wE1pbMfm3YSBUpcFNgDh3Bf6VZCZq57Q`,
url: `https://app.dhedge.org/`,
disabled: false,
networks: [ETHEREUM_NETWORK.MAINNET],
},
// ENS
{
url: `https://app.ens.domains/`,
disabled: false,
networks: [ETHEREUM_NETWORK.MAINNET, ETHEREUM_NETWORK.RINKEBY],
},
// Gnosis Starter
{
url: `${process.env.REACT_APP_IPFS_GATEWAY}/QmdCwUutYH8GXXNgTShB4cKJ8YJq4PqZ55QxMznKc9DbeS`,
disabled: false,
networks: [ETHEREUM_NETWORK.MAINNET, ETHEREUM_NETWORK.RINKEBY],
},
// Idle
{
url: `${process.env.REACT_APP_IPFS_GATEWAY}/QmTvrLwJtyjG8QFHgvqdPhcV5DBMQ7oZceSU4uvPw9vQaj`,

View File

@ -77,7 +77,7 @@ const Coins = (props: Props): React.ReactElement => {
const columns = generateColumns()
const autoColumns = columns.filter((c) => !c.custom)
const selectedCurrency = useSelector(currentCurrencySelector)
const activeTokens = useSelector(extendedSafeTokensSelector)
const safeTokens = useSelector(extendedSafeTokensSelector)
const granted = useSelector(grantedSelector)
const { trackEvent } = useAnalytics()
@ -85,22 +85,14 @@ const Coins = (props: Props): React.ReactElement => {
trackEvent({ category: SAFE_NAVIGATION_EVENT, action: 'Coins' })
}, [trackEvent])
const filteredData: List<BalanceData> = useMemo(() => getBalanceData(activeTokens, selectedCurrency), [
activeTokens,
const filteredData: List<BalanceData> = useMemo(() => getBalanceData(safeTokens, selectedCurrency), [
safeTokens,
selectedCurrency,
])
return (
<TableContainer>
<Table
columns={columns}
data={filteredData}
defaultFixed
defaultOrderBy={BALANCE_TABLE_ASSET_ID}
defaultRowsPerPage={100}
label="Balances"
size={filteredData.size}
>
<Table columns={columns} data={filteredData} defaultRowsPerPage={100} label="Balances" size={filteredData.size}>
{(sortedData) =>
sortedData.map((row, index) => (
<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 { getNetworkInfo } from 'src/config'
import { FIXED } from 'src/components/Table/sorting'
import { formatAmountInUsFormat } from 'src/logic/tokens/utils/formatAmount'
import { TableColumn } from 'src/components/Table/types.d'
import { Token } from 'src/logic/tokens/store/model/token'
@ -20,14 +18,12 @@ export interface BalanceData {
assetOrder: string
balance: string
balanceOrder: number
fixed: boolean
value: string
valueOrder: number
}
export const getBalanceData = (activeTokens: List<Token>, currencySelected?: string): List<BalanceData> => {
const { nativeCoin } = getNetworkInfo()
return activeTokens.map((token) => {
export const getBalanceData = (safeTokens: List<Token>, currencySelected?: string): List<BalanceData> => {
return safeTokens.map((token) => {
const { tokenBalance, fiatBalance } = token.balance
return {
@ -40,7 +36,6 @@ export const getBalanceData = (activeTokens: List<Token>, currencySelected?: str
assetOrder: token.name,
[BALANCE_TABLE_BALANCE_ID]: `${formatAmountInUsFormat(tokenBalance?.toString() || '0')} ${token.symbol}`,
balanceOrder: Number(tokenBalance),
[FIXED]: token.symbol === nativeCoin.symbol,
[BALANCE_TABLE_VALUE_ID]: getTokenPriceInCurrency(fiatBalance || '0', currencySelected),
valueOrder: Number(tokenBalance),
}

View File

@ -1,29 +0,0 @@
import Badge from '@material-ui/core/Badge'
import React from 'react'
import { useSelector } from 'react-redux'
import { SettingsIcon } from 'src/routes/safe/components/assets/SettingsIcon'
import { grantedSelector } from 'src/routes/safe/container/selector'
import { safeNeedsUpdateSelector } from 'src/logic/safe/store/selectors'
const SettingsTab = () => {
const needsUpdate = useSelector(safeNeedsUpdateSelector)
const granted = useSelector(grantedSelector)
return (
<>
<SettingsIcon />
<Badge
badgeContent=""
color="error"
invisible={!needsUpdate || !granted}
style={{ paddingRight: '10px' }}
variant="dot"
>
Settings
</Badge>
</>
)
}
export default SettingsTab

View File

@ -1,21 +0,0 @@
import { secondary } from 'src/theme/variables'
import { createStyles } from '@material-ui/core'
export const styles = createStyles({
tabWrapper: {
display: 'flex',
flexDirection: 'row',
'& svg': {
display: 'block',
marginRight: '5px',
},
'& .fill': {
fill: 'rgba(0, 0, 0, 0.54)',
},
},
tabWrapperSelected: {
'& .fill': {
fill: secondary,
},
},
})

View File

@ -1,25 +0,0 @@
// TODO: remove this file. It's no longer used
import { screenSm, sm } from 'src/theme/variables'
import { createStyles } from '@material-ui/core'
export const styles = createStyles({
receiveModal: {
height: 'auto',
maxWidth: 'calc(100% - 30px)',
minHeight: '544px',
overflow: 'hidden',
},
receive: {
borderRadius: '4px',
marginLeft: sm,
width: '50%',
'& > span': {
fontSize: '14px',
},
[`@media (min-width: ${screenSm}px)`]: {
minWidth: '95px',
width: 'auto',
},
},
})

View File

@ -1,26 +0,0 @@
import * as React from 'react'
import Bold from 'src/components/layout/Bold'
import Button from 'src/components/layout/Button'
import Col from 'src/components/layout/Col'
import Link from 'src/components/layout/Link'
import Paragraph from 'src/components/layout/Paragraph/index'
import Row from 'src/components/layout/Row'
import { SAFELIST_ADDRESS } from 'src/routes/routes'
const NoRights = () => (
<Row>
<Col center="xs" margin="md" sm={10} smOffset={2} start="sm" xs={12}>
<Paragraph size="lg">
<Bold>Not possible to add Safe, check its address and ownership</Bold>
</Paragraph>
</Col>
<Col center="xs" margin="md" sm={10} smOffset={2} start="sm" xs={12}>
<Button color="primary" component={Link} to={SAFELIST_ADDRESS} variant="contained">
Safe List
</Button>
</Col>
</Row>
)
export default NoRights

View File

@ -1,4 +1,4 @@
import { List, Map } from 'immutable'
import { List } from 'immutable'
import { createSelector } from 'reselect'
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 { 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'
export const grantedSelector = createSelector(
@ -25,28 +25,30 @@ const safeEthAsTokenSelector = createSelector(safeSelector, (safe?: SafeRecord):
})
export const extendedSafeTokensSelector = createSelector(
safeActiveTokensSelector,
safeBalancesSelector,
tokensSelector,
safeEthAsTokenSelector,
(safeTokens, balances, tokensList, ethAsToken): List<Token> => {
const extendedTokens = Map<string, Token>().withMutations((map) => {
safeTokens.forEach((tokenAddress) => {
const baseToken = tokensList.get(tokenAddress)
const tokenBalance = balances?.get(tokenAddress)
(safeBalances, tokensList, ethAsToken): List<Token> => {
const extendedTokens: Array<Token> = []
if (baseToken) {
const updatedBaseToken = baseToken.set('balance', tokenBalance || { tokenBalance: '0', fiatBalance: '0' })
if (sameAddress(tokenAddress, ethAsToken?.address)) {
map.set(tokenAddress, updatedBaseToken.set('logoUri', ethAsToken?.logoUri || baseToken.logoUri))
} else {
map.set(tokenAddress, updatedBaseToken)
}
}
})
safeBalances.forEach((safeBalance) => {
const tokenAddress = safeBalance.tokenAddress
if (!tokenAddress) {
return
}
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)
},
)