WA-232 fetching balance of activated custom tokens

This commit is contained in:
apanizo 2018-07-18 12:14:38 +02:00
parent 95caa29f69
commit da6345b1cc
7 changed files with 73 additions and 30 deletions

View File

@ -6,6 +6,7 @@ import FirstPage, { TOKEN_ADDRESS_PARAM } from '~/routes/tokens/component/AddTok
import SecondPage, { TOKEN_SYMBOL_PARAM, TOKEN_DECIMALS_PARAM, TOKEN_LOGO_URL_PARAM, TOKEN_NAME_PARAM } from '~/routes/tokens/component/AddToken/SecondPage' import SecondPage, { TOKEN_SYMBOL_PARAM, TOKEN_DECIMALS_PARAM, TOKEN_LOGO_URL_PARAM, TOKEN_NAME_PARAM } from '~/routes/tokens/component/AddToken/SecondPage'
import { makeToken, type Token } from '~/routes/tokens/store/model/token' import { makeToken, type Token } from '~/routes/tokens/store/model/token'
import addTokenAction from '~/routes/tokens/store/actions/addTokens' import addTokenAction from '~/routes/tokens/store/actions/addTokens'
import enableTokenAction from '~/routes/tokens/store/actions/enabletoken'
import Review from './Review' import Review from './Review'
export const getSteps = () => [ export const getSteps = () => [
@ -15,7 +16,8 @@ export const getSteps = () => [
type Props = { type Props = {
tokens: string[], tokens: string[],
safeAddress: string, safeAddress: string,
addToken: typeof addTokenAction addToken: typeof addTokenAction,
enableToken: typeof enableTokenAction,
} }
type State = { type State = {
@ -24,7 +26,7 @@ type State = {
export const ADD_TOKEN_RESET_BUTTON_TEXT = 'RESET' export const ADD_TOKEN_RESET_BUTTON_TEXT = 'RESET'
export const addTokenFnc = async (values: Object, addToken, safeAddress: string) => { export const addTokenFnc = async (values: Object, addToken, enableToken, safeAddress: string) => {
const address = values[TOKEN_ADDRESS_PARAM] const address = values[TOKEN_ADDRESS_PARAM]
const name = values[TOKEN_NAME_PARAM] const name = values[TOKEN_NAME_PARAM]
const symbol = values[TOKEN_SYMBOL_PARAM] const symbol = values[TOKEN_SYMBOL_PARAM]
@ -41,6 +43,7 @@ export const addTokenFnc = async (values: Object, addToken, safeAddress: string)
removable: true, removable: true,
}) })
await enableToken(safeAddress, token)
return addToken(safeAddress, token) return addToken(safeAddress, token)
} }
@ -50,9 +53,9 @@ class AddToken extends React.Component<Props, State> {
} }
onAddToken = async (values: Object) => { onAddToken = async (values: Object) => {
const { addToken, safeAddress } = this.props const { addToken, enableToken, safeAddress } = this.props
return addTokenFnc(values, addToken, safeAddress) return addTokenFnc(values, addToken, enableToken, safeAddress)
} }
onReset = () => { onReset = () => {

View File

@ -38,10 +38,17 @@ class TokenLayout extends React.PureComponent<TokenProps, State> {
} }
onAddToken = () => { onAddToken = () => {
const { addresses, safeAddress, addToken } = this.props const {
addresses, safeAddress, addToken, enableToken,
} = this.props
this.setState({ this.setState({
component: <AddToken addToken={addToken} tokens={addresses.toArray()} safeAddress={safeAddress} />, component: <AddToken
addToken={addToken}
enableToken={enableToken}
tokens={addresses.toArray()}
safeAddress={safeAddress}
/>,
}) })
} }

View File

@ -8,7 +8,7 @@ import { getWeb3 } from '~/wallets/getWeb3'
import { type GlobalState } from '~/store/index' import { type GlobalState } from '~/store/index'
import { makeToken, type Token, type TokenProps } from '~/routes/tokens/store/model/token' import { makeToken, type Token, type TokenProps } from '~/routes/tokens/store/model/token'
import { ensureOnce } from '~/utils/singleton' import { ensureOnce } from '~/utils/singleton'
import { getTokens } from '~/utils/localStorage/tokens' import { getActiveTokenAddresses, getTokens } from '~/utils/localStorage/tokens'
import { getSafeEthToken } from '~/utils/tokens' import { getSafeEthToken } from '~/utils/tokens'
import { enhancedFetch } from '~/utils/fetch' import { enhancedFetch } from '~/utils/fetch'
import addTokens from './addTokens' import addTokens from './addTokens'
@ -48,9 +48,9 @@ export const fetchTokensData = async () => {
export const fetchTokens = (safeAddress: string) => export const fetchTokens = (safeAddress: string) =>
async (dispatch: ReduxDispatch<GlobalState>) => { async (dispatch: ReduxDispatch<GlobalState>) => {
const tokens: List<string> = getTokens(safeAddress) const tokens: List<string> = getActiveTokenAddresses(safeAddress)
const ethBalance = await getSafeEthToken(safeAddress) const ethBalance = await getSafeEthToken(safeAddress)
const customTokens = getTokens(safeAddress)
const json = await exports.fetchTokensData() const json = await exports.fetchTokensData()
try { try {
@ -61,10 +61,18 @@ export const fetchTokens = (safeAddress: string) =>
return makeToken({ ...item, status, funds }) return makeToken({ ...item, status, funds })
})) }))
const customTokenRecords = await Promise.all(customTokens.map(async (item: TokenProps) => {
const status = tokens.includes(item.address)
const funds = status ? await calculateBalanceOf(item.address, safeAddress, item.decimals) : '0'
return makeToken({ ...item, status, funds })
}))
const balances: Map<string, Token> = Map().withMutations((map) => { const balances: Map<string, Token> = Map().withMutations((map) => {
balancesRecords.forEach(record => map.set(record.get('address'), record)) balancesRecords.forEach(record => map.set(record.get('address'), record))
customTokenRecords.forEach(record => map.set(record.get('address'), record))
map.set('ETH', ethBalance) map.set(ethBalance.get('address'), ethBalance)
}) })
return dispatch(addTokens(safeAddress, balances)) return dispatch(addTokens(safeAddress, balances))

View File

@ -6,7 +6,7 @@ import addTokens, { ADD_TOKENS } from '~/routes/tokens/store/actions/addTokens'
import { type Token } from '~/routes/tokens/store/model/token' import { type Token } from '~/routes/tokens/store/model/token'
import disableToken, { DISABLE_TOKEN } from '~/routes/tokens/store/actions/disableToken' import disableToken, { DISABLE_TOKEN } from '~/routes/tokens/store/actions/disableToken'
import enableToken, { ENABLE_TOKEN } from '~/routes/tokens/store/actions/enableToken' import enableToken, { ENABLE_TOKEN } from '~/routes/tokens/store/actions/enableToken'
import { setTokens, getTokens } from '~/utils/localStorage/tokens' import { setActiveTokenAddresses, getActiveTokenAddresses } from '~/utils/localStorage/tokens'
import { ensureOnce } from '~/utils/singleton' import { ensureOnce } from '~/utils/singleton'
import { calculateActiveErc20TokensFrom } from '~/utils/tokens' import { calculateActiveErc20TokensFrom } from '~/utils/tokens'
@ -14,7 +14,7 @@ export const TOKEN_REDUCER_ID = 'tokens'
export type State = Map<string, Map<string, Token>> export type State = Map<string, Map<string, Token>>
const setTokensOnce = ensureOnce(setTokens) const setTokensOnce = ensureOnce(setActiveTokenAddresses)
export default handleActions({ export default handleActions({
[ADD_TOKENS]: (state: State, action: ActionType<typeof addTokens>): State => { [ADD_TOKENS]: (state: State, action: ActionType<typeof addTokens>): State => {
@ -33,26 +33,26 @@ export default handleActions({
}, },
[ADD_TOKEN]: (state: State, action: ActionType<typeof addToken>): State => { [ADD_TOKEN]: (state: State, action: ActionType<typeof addToken>): State => {
const { safeAddress, token } = action.payload const { safeAddress, token } = action.payload
const activeTokens = getTokens(safeAddress) const activeTokens = getActiveTokenAddresses(safeAddress)
activeTokens.push(token.get('address')) activeTokens.push(token.get('address'))
setTokens(activeTokens) setActiveTokenAddresses(activeTokens)
return state.setIn([safeAddress, token.get('address')], token) return state.setIn([safeAddress, token.get('address')], token)
}, },
[DISABLE_TOKEN]: (state: State, action: ActionType<typeof disableToken>): State => { [DISABLE_TOKEN]: (state: State, action: ActionType<typeof disableToken>): State => {
const { address, safeAddress, symbol } = action.payload const { address, safeAddress, symbol } = action.payload
const activeTokens = getTokens(safeAddress) const activeTokens = getActiveTokenAddresses(safeAddress)
const index = activeTokens.indexOf(address) const index = activeTokens.indexOf(address)
setTokens(safeAddress, activeTokens.delete(index)) setActiveTokenAddresses(safeAddress, activeTokens.delete(index))
return state.setIn([safeAddress, symbol, 'status'], false) return state.setIn([safeAddress, symbol, 'status'], false)
}, },
[ENABLE_TOKEN]: (state: State, action: ActionType<typeof enableToken>): State => { [ENABLE_TOKEN]: (state: State, action: ActionType<typeof enableToken>): State => {
const { address, safeAddress, symbol } = action.payload const { address, safeAddress, symbol } = action.payload
const activeTokens = getTokens(safeAddress) const activeTokens = getActiveTokenAddresses(safeAddress)
setTokens(safeAddress, activeTokens.push(address)) setActiveTokenAddresses(safeAddress, activeTokens.push(address))
return state.setIn([safeAddress, symbol, 'status'], true) return state.setIn([safeAddress, symbol, 'status'], true)
}, },

View File

@ -13,7 +13,7 @@ import { travelToTokens } from '~/test/builder/safe.dom.utils'
import { sleep } from '~/utils/timer' import { sleep } from '~/utils/timer'
import { buildMathPropsFrom } from '~/test/utils/buildReactRouterProps' import { buildMathPropsFrom } from '~/test/utils/buildReactRouterProps'
import { tokenListSelector, activeTokensSelector } from '~/routes/tokens/store/selectors' import { tokenListSelector, activeTokensSelector } from '~/routes/tokens/store/selectors'
import { getTokens } from '~/utils/localStorage/tokens' import { getActiveTokenAddresses } from '~/utils/localStorage/tokens'
import { enableFirstToken, testToken } from '~/test/builder/tokens.dom.utils' import { enableFirstToken, testToken } from '~/test/builder/tokens.dom.utils'
import * as fetchTokensModule from '~/routes/tokens/store/actions/fetchTokens' import * as fetchTokensModule from '~/routes/tokens/store/actions/fetchTokens'
import * as enhancedFetchModule from '~/utils/fetch' import * as enhancedFetchModule from '~/utils/fetch'
@ -108,19 +108,19 @@ describe('DOM > Feature > Enable and disable default tokens', () => {
it('localStorage always returns a list', async () => { it('localStorage always returns a list', async () => {
const store = aNewStore() const store = aNewStore()
const safeAddress = await aMinedSafe(store) const safeAddress = await aMinedSafe(store)
let tokens: List<string> = getTokens(safeAddress) let tokens: List<string> = getActiveTokenAddresses(safeAddress)
expect(tokens).toEqual(List([])) expect(tokens).toEqual(List([]))
await store.dispatch(fetchTokensModule.fetchTokens(safeAddress)) await store.dispatch(fetchTokensModule.fetchTokens(safeAddress))
tokens = getTokens(safeAddress) tokens = getActiveTokenAddresses(safeAddress)
expect(tokens.count()).toBe(0) expect(tokens.count()).toBe(0)
await enableFirstToken(store, safeAddress) await enableFirstToken(store, safeAddress)
tokens = getTokens(safeAddress) tokens = getActiveTokenAddresses(safeAddress)
expect(tokens.count()).toBe(1) expect(tokens.count()).toBe(1)
await store.dispatch(fetchTokensModule.fetchTokens(safeAddress)) await store.dispatch(fetchTokensModule.fetchTokens(safeAddress))
tokens = getTokens(safeAddress) tokens = getActiveTokenAddresses(safeAddress)
expect(tokens.count()).toBe(1) expect(tokens.count()).toBe(1)
}) })
}) })

View File

@ -5,7 +5,6 @@ import { type Owner } from '~/routes/safe/store/model/owner'
export const SAFES_KEY = 'SAFES' export const SAFES_KEY = 'SAFES'
export const TX_KEY = 'TX' export const TX_KEY = 'TX'
export const OWNERS_KEY = 'OWNERS' export const OWNERS_KEY = 'OWNERS'
export const TOKENS_KEY = 'TOKENS'
export const load = (key: string) => { export const load = (key: string) => {
try { try {

View File

@ -1,13 +1,18 @@
// @flow // @flow
import { List } from 'immutable' import { List } from 'immutable'
import { load, TOKENS_KEY } from '~/utils/localStorage' import { load } from '~/utils/localStorage'
import { type Token, type TokenProps } from '~/routes/tokens/store/model/token'
export const ACTIVE_TOKENS_KEY = 'ACTIVE_TOKENS'
export const TOKENS_KEY = 'TOKENS'
const getActiveTokensKey = (safeAddress: string) => `${ACTIVE_TOKENS_KEY}-${safeAddress}`
const getTokensKey = (safeAddress: string) => `${TOKENS_KEY}-${safeAddress}` const getTokensKey = (safeAddress: string) => `${TOKENS_KEY}-${safeAddress}`
export const setTokens = (safeAddress: string, tokens: List<string>) => { export const setActiveTokenAddresses = (safeAddress: string, tokens: List<string>) => {
try { try {
const serializedState = JSON.stringify(tokens) const serializedState = JSON.stringify(tokens)
const key = getTokensKey(safeAddress) const key = getActiveTokensKey(safeAddress)
localStorage.setItem(key, serializedState) localStorage.setItem(key, serializedState)
} catch (err) { } catch (err) {
// eslint-disable-next-line // eslint-disable-next-line
@ -15,14 +20,35 @@ export const setTokens = (safeAddress: string, tokens: List<string>) => {
} }
} }
export const getTokens = (safeAddress: string): List<string> => { export const getActiveTokenAddresses = (safeAddress: string): List<string> => {
const key = getTokensKey(safeAddress) const key = getActiveTokensKey(safeAddress)
const data = load(key) const data = load(key)
return data ? List(data) : List() return data ? List(data) : List()
} }
export const storedTokensBefore = (safeAddress: string) => { export const storedTokensBefore = (safeAddress: string) => {
const key = getTokensKey(safeAddress) const key = getActiveTokensKey(safeAddress)
return localStorage.getItem(key) === null return localStorage.getItem(key) === null
} }
export const getTokens: List<TokenProps> = (safeAddress: string) => {
const key = getTokensKey(safeAddress)
const data = load(key)
return data ? List(data) : List()
}
export const setToken = (safeAddress: string, token: Token) => {
const data: List<Token> = getTokens(safeAddress)
data.push(token)
try {
const serializedState = JSON.stringify(data)
const key = getTokensKey(safeAddress)
localStorage.setItem(key, serializedState)
} catch (err) {
// eslint-disable-next-line
console.log('Error adding token in localstorage')
}
}