Skip to content

Commit

Permalink
Merge pull request #11875 from brave/asset-management-hook
Browse files Browse the repository at this point in the history
chore(wallet): Created Asset Management Hook
  • Loading branch information
Douglashdaniel authored Jan 19, 2022
2 parents ce61501 + 7def6aa commit 0362599
Show file tree
Hide file tree
Showing 12 changed files with 340 additions and 99 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,4 @@ export const cancelTransaction = createAction<BraveWallet.TransactionInfo>('canc
export const speedupTransaction = createAction<BraveWallet.TransactionInfo>('speedupTransaction')
export const defaultCurrenciesUpdated = createAction<DefaultCurrencies>('defaultCurrenciesUpdated')
export const expandWalletNetworks = createAction('expandWalletNetworks')
export const refreshBalancesAndPriceHistory = createAction('refreshBalancesAndPriceHistory')
7 changes: 4 additions & 3 deletions components/brave_wallet_ui/common/async/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,19 +244,20 @@ handler.on(WalletActions.addUserAsset.getType(), async (store: Store, payload: A
const braveWalletService = getAPIProxy().braveWalletService
const result = await braveWalletService.addUserAsset(payload.token, payload.chainId)
store.dispatch(WalletActions.addUserAssetError(!result.success))
await refreshBalancesPricesAndHistory(store)
})

handler.on(WalletActions.removeUserAsset.getType(), async (store: Store, payload: RemoveUserAssetPayloadType) => {
const braveWalletService = getAPIProxy().braveWalletService
await braveWalletService.removeUserAsset(payload.token, payload.chainId)
await refreshBalancesPricesAndHistory(store)
})

handler.on(WalletActions.setUserAssetVisible.getType(), async (store: Store, payload: SetUserAssetVisiblePayloadType) => {
const braveWalletService = getAPIProxy().braveWalletService
await braveWalletService.setUserAssetVisible(payload.token, payload.chainId, payload.isVisible)
await refreshBalancesPricesAndHistory(store)
})

handler.on(WalletActions.refreshBalancesAndPriceHistory.getType(), async (store: Store) => {
refreshBalancesPricesAndHistory(store)
})

handler.on(WalletActions.selectPortfolioTimeline.getType(), async (store: Store, payload: BraveWallet.AssetPriceTimeframe) => {
Expand Down
3 changes: 2 additions & 1 deletion components/brave_wallet_ui/common/constants/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ export const mockERC20Token: BraveWallet.BlockchainToken = {
isErc721: false,
decimals: 18,
visible: true,
tokenId: ''
tokenId: '',
coingeckoId: ''
}

export const mockAccount: WalletAccountType = {
Expand Down
113 changes: 113 additions & 0 deletions components/brave_wallet_ui/common/hooks/assets-management.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { renderHook, act } from '@testing-library/react-hooks'
import * as WalletActions from '../actions/wallet_actions'
import {
mockNetwork
} from '../constants/mocks'
import useAssetManagement from './assets-management'
import { AccountAssetOptions } from '../../options/asset-options'

const mockUserVisibleTokensInfo = [
AccountAssetOptions[1],
AccountAssetOptions[2]
]

const mockCustomToken = {
contractAddress: 'customTokenContractAddress',
name: 'Custom Token',
symbol: 'CT',
logo: '',
isErc20: true,
isErc721: false,
decimals: 18,
visible: true,
tokenId: '',
coingeckoId: ''
}

describe('useAssetManagement hook', () => {
let addedAssetSpy: jest.SpyInstance
let removedAssetSpy: jest.SpyInstance
let refreshedBalancesPricesSpy: jest.SpyInstance
let setUserAssetVisibleSpy: jest.SpyInstance

addedAssetSpy = jest.spyOn(WalletActions, 'addUserAsset')
removedAssetSpy = jest.spyOn(WalletActions, 'removeUserAsset')
refreshedBalancesPricesSpy = jest.spyOn(WalletActions, 'refreshBalancesAndPriceHistory')
setUserAssetVisibleSpy = jest.spyOn(WalletActions, 'setUserAssetVisible')

it('Should add an asset', () => {
const { result } = renderHook(() => useAssetManagement(
WalletActions.addUserAsset,
WalletActions.setUserAssetVisible,
WalletActions.removeUserAsset,
WalletActions.refreshBalancesAndPriceHistory,
mockNetwork,
AccountAssetOptions,
mockUserVisibleTokensInfo
))
act(() => result.current.onUpdateVisibleAssets([AccountAssetOptions[1], AccountAssetOptions[2], AccountAssetOptions[3]]))
expect(addedAssetSpy).toBeCalledWith({ chainId: mockNetwork.chainId, token: { ...AccountAssetOptions[3], logo: '' } })
expect(refreshedBalancesPricesSpy).toBeCalledTimes(1)
})

it('Should remove an asset', () => {
const { result } = renderHook(() => useAssetManagement(
WalletActions.addUserAsset,
WalletActions.setUserAssetVisible,
WalletActions.removeUserAsset,
WalletActions.refreshBalancesAndPriceHistory,
mockNetwork,
AccountAssetOptions,
mockUserVisibleTokensInfo
))
act(() => result.current.onUpdateVisibleAssets([AccountAssetOptions[1]]))
expect(removedAssetSpy).toBeCalledWith({ chainId: mockNetwork.chainId, token: AccountAssetOptions[2] })
expect(refreshedBalancesPricesSpy).toBeCalledTimes(1)
})

it('Should remove and add an asset', () => {
const { result } = renderHook(() => useAssetManagement(
WalletActions.addUserAsset,
WalletActions.setUserAssetVisible,
WalletActions.removeUserAsset,
WalletActions.refreshBalancesAndPriceHistory,
mockNetwork,
AccountAssetOptions,
mockUserVisibleTokensInfo
))
act(() => result.current.onUpdateVisibleAssets([AccountAssetOptions[1], AccountAssetOptions[3]]))
expect(addedAssetSpy).toBeCalledWith({ chainId: mockNetwork.chainId, token: { ...AccountAssetOptions[3], logo: '' } })
expect(removedAssetSpy).toBeCalledWith({ chainId: mockNetwork.chainId, token: AccountAssetOptions[2] })
expect(refreshedBalancesPricesSpy).toBeCalledTimes(1)
})

it('Should set custom tokens visibility to false', () => {
const { result } = renderHook(() => useAssetManagement(
WalletActions.addUserAsset,
WalletActions.setUserAssetVisible,
WalletActions.removeUserAsset,
WalletActions.refreshBalancesAndPriceHistory,
mockNetwork,
AccountAssetOptions,
[...mockUserVisibleTokensInfo, mockCustomToken]
))
act(() => result.current.onUpdateVisibleAssets([...mockUserVisibleTokensInfo, { ...mockCustomToken, visible: false }]))
expect(setUserAssetVisibleSpy).toBeCalledWith({ chainId: mockNetwork.chainId, token: { ...mockCustomToken, visible: false }, isVisible: false })
expect(refreshedBalancesPricesSpy).toBeCalledTimes(1)
})

it('Should set custom tokens visibility to true', () => {
const { result } = renderHook(() => useAssetManagement(
WalletActions.addUserAsset,
WalletActions.setUserAssetVisible,
WalletActions.removeUserAsset,
WalletActions.refreshBalancesAndPriceHistory,
mockNetwork,
AccountAssetOptions,
[...mockUserVisibleTokensInfo, { ...mockCustomToken, visible: false }]
))
act(() => result.current.onUpdateVisibleAssets([...mockUserVisibleTokensInfo, mockCustomToken]))
expect(setUserAssetVisibleSpy).toBeCalledWith({ chainId: mockNetwork.chainId, token: mockCustomToken, isVisible: true })
expect(refreshedBalancesPricesSpy).toBeCalledTimes(1)
})
})
88 changes: 88 additions & 0 deletions components/brave_wallet_ui/common/hooks/assets-management.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Copyright (c) 2021 The Brave Authors. All rights reserved.
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// you can obtain one at http://mozilla.org/MPL/2.0/.

import * as React from 'react'
import { SimpleActionCreator, EmptyActionCreator } from 'redux-act'
import {
BraveWallet
} from '../../constants/types'

import {
AddUserAssetPayloadType,
SetUserAssetVisiblePayloadType,
RemoveUserAssetPayloadType
} from '../constants/action_types'

import { stripERC20TokenImageURL } from '../../utils/string-utils'

export default function useAssetManagement (
addUserAsset: SimpleActionCreator<AddUserAssetPayloadType>,
setUserAssetVisible: SimpleActionCreator<SetUserAssetVisiblePayloadType>,
removeUserAsset: SimpleActionCreator<RemoveUserAssetPayloadType>,
refreshBalancesPricesAndHistory: EmptyActionCreator,
selectedNetwork: BraveWallet.EthereumChain,
fullTokenList: BraveWallet.BlockchainToken[],
userVisibleTokensInfo: BraveWallet.BlockchainToken[]
) {
const onAddUserAsset = (token: BraveWallet.BlockchainToken) => {
addUserAsset({
token: {
...token,
logo: stripERC20TokenImageURL(token.logo) || ''
},
chainId: selectedNetwork.chainId
})
}

const onAddCustomAsset = (token: BraveWallet.BlockchainToken) => {
onAddUserAsset(token)
refreshBalancesPricesAndHistory()
}

const findVisibleTokenInfo = (token: BraveWallet.BlockchainToken) => {
return userVisibleTokensInfo.find((t) => t.contractAddress.toLowerCase() === token.contractAddress.toLowerCase())
}

const onUpdateVisibleAssets = React.useCallback((updatedTokensList: BraveWallet.BlockchainToken[]) => {
// Gets a list of all added tokens and adds them to the userVisibleTokensInfo list
updatedTokensList.filter((firstItem) =>
!userVisibleTokensInfo.some((secondItem) =>
firstItem.contractAddress.toLowerCase() === secondItem.contractAddress.toLowerCase()))
.forEach((token) => onAddUserAsset(token))

// Gets a list of all removed tokens and removes them from the userVisibleTokensInfo list
userVisibleTokensInfo.filter((firstItem) =>
!updatedTokensList.some((secondItem) =>
firstItem.contractAddress.toLowerCase() === secondItem.contractAddress.toLowerCase()))
.forEach((token) => removeUserAsset({ token, chainId: selectedNetwork.chainId }))

// Gets a list of custom tokens returned from updatedTokensList payload
// then compares customTokens against userVisibleTokensInfo list and updates the custom tokens visibility if it has changed
updatedTokensList.filter((firstItem) =>
!fullTokenList.some((secondItem) =>
firstItem.contractAddress.toLowerCase() === secondItem.contractAddress.toLowerCase()))
.forEach((token) => {
const foundToken = findVisibleTokenInfo(token)
// Since a networks native token (example 'ETH') can be removed from the the userVisibleTokensInfo list,
// when it is re-added we handle it as a custom token since it is not part of the token registry.
// This check here will add it only if it's value 'visible' is returned true
if (token.contractAddress.toLowerCase() === '' && !foundToken?.visible && token.visible) {
onAddUserAsset(token)
}
// Updates token visibility exluding a networks native token
if (foundToken?.visible !== token.visible && token.contractAddress.toLowerCase() !== '') {
setUserAssetVisible({ token, chainId: selectedNetwork.chainId, isVisible: token.visible })
}
})

// Refreshes Balances, Prices and Price History when done.
refreshBalancesPricesAndHistory()
}, [userVisibleTokensInfo, selectedNetwork])

return {
onUpdateVisibleAssets,
onAddCustomAsset
}
}
4 changes: 3 additions & 1 deletion components/brave_wallet_ui/common/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import usePricing from './pricing'
import usePreset from './select-preset'
import useTokenInfo from './token'
import useExplorer from './explorer'
import useAssetManagement from './assets-management'

export {
useAssets,
Expand All @@ -25,5 +26,6 @@ export {
useSend,
usePreset,
useTokenInfo,
useExplorer
useExplorer,
useAssetManagement
}
Loading

0 comments on commit 0362599

Please sign in to comment.