diff --git a/packages/suite/src/actions/suite/storageActions.ts b/packages/suite/src/actions/suite/storageActions.ts index e954599c0d00..43e314088598 100644 --- a/packages/suite/src/actions/suite/storageActions.ts +++ b/packages/suite/src/actions/suite/storageActions.ts @@ -4,7 +4,7 @@ import { cloneObject } from '@trezor/utils'; import { Discovery, FormDraftKeyPrefix } from '@suite-common/wallet-types'; import { notificationsActions } from '@suite-common/toast-notifications'; -import { getFormDraftKey } from '@suite-common/wallet-utils'; +import { selectHistoricRatesByTransactions, getFormDraftKey } from '@suite-common/wallet-utils'; import { FormDraftPrefixKeyValues } from '@suite-common/wallet-constants'; import { selectDevices, deviceActions } from '@suite-common/wallet-core'; @@ -16,7 +16,7 @@ import { } from 'src/utils/suite/storage'; import type { AppState, Dispatch, GetState, TrezorDevice } from 'src/types/suite'; import type { Account, Network } from 'src/types/wallet'; -import type { FormState } from '@suite-common/wallet-types'; +import type { FormState, RatesByTimestamps } from '@suite-common/wallet-types'; import type { Trade } from 'src/types/wallet/coinmarketCommonTypes'; import type { PreloadStoreAction } from 'src/support/suite/preloadStore'; import { GraphData } from 'src/types/wallet/graph'; @@ -29,7 +29,6 @@ import { MetadataState } from '@suite-common/metadata-types'; export type StorageAction = NonNullable; export type StorageLoadAction = Extract; -// send form drafts start export const saveDraft = async (formState: FormState, accountKey: string) => { if (!(await db.isAccessible())) return; @@ -100,8 +99,6 @@ export const saveCoinjoinDebugSettings = () => async (_dispatch: Dispatch, getSt db.addItem('coinjoinDebugSettings', debug || {}, 'debug', true); }; -// send form drafts end - export const saveFormDraft = async (key: string, draft: FieldValues) => { if (!(await db.isAccessible())) return; @@ -164,6 +161,12 @@ const removeAccountGraph = async (account: Account) => { ]); }; +export const removeAccountHistoricRates = async (accountKey: string) => { + if (!(await db.isAccessible())) return; + + return db.removeItemByPK('historicRates', accountKey); +}; + export const removeAccountWithDependencies = (getState: GetState) => (account: Account) => Promise.all([ ...FormDraftPrefixKeyValues.map(prefix => removeAccountFormDraft(prefix, account.key)), @@ -172,6 +175,7 @@ export const removeAccountWithDependencies = (getState: GetState) => (account: A removeAccountGraph(account), removeCoinjoinAccount(account.key, getState()), removeAccount(account), + removeAccountHistoricRates(account.key), ]); export const forgetDevice = (device: TrezorDevice) => async (_: Dispatch, getState: GetState) => { @@ -214,6 +218,18 @@ export const saveGraph = async (graphData: GraphData[]) => { return db.addItems('graph', graphData, true); }; +export const saveAccountHistoricRates = + (accountKey: string, historicRates: RatesByTimestamps) => + async (_dispatch: Dispatch, getState: GetState) => { + if (!(await db.isAccessible())) return Promise.resolve(); + const allTxs = getState().wallet.transactions.transactions; + const accTxs = allTxs[accountKey] || []; + + const accHistoricRates = selectHistoricRatesByTransactions(historicRates, accTxs); + + return db.addItem('historicRates', accHistoricRates, accountKey, true); + }; + export const saveAccountTransactions = (account: Account) => async (_dispatch: Dispatch, getState: GetState) => { if (!(await db.isAccessible())) return Promise.resolve(); @@ -244,6 +260,7 @@ export const rememberDevice = const discovery = wallet.discovery .filter(d => d.deviceState === device.state) .map(serializeDiscovery); + const historicRates = wallet.fiat.historic; const accountPromises = accounts.reduce( (promises, account) => @@ -252,6 +269,7 @@ export const rememberDevice = dispatch(saveAccountTransactions(account)), dispatch(saveAccountDraft(account)), dispatch(saveCoinjoinAccount(account.key)), + dispatch(saveAccountHistoricRates(account.key, historicRates)), ], FormDraftPrefixKeyValues.map(prefix => dispatch(saveAccountFormDraft(prefix, account.key)), diff --git a/packages/suite/src/middlewares/wallet/storageMiddleware.ts b/packages/suite/src/middlewares/wallet/storageMiddleware.ts index d02f6dcfa778..552a7d83424e 100644 --- a/packages/suite/src/middlewares/wallet/storageMiddleware.ts +++ b/packages/suite/src/middlewares/wallet/storageMiddleware.ts @@ -13,6 +13,7 @@ import { selectAccountByKey, deviceActions, selectDeviceByState, + selectHistoricFiatRates, } from '@suite-common/wallet-core'; import { isDeviceRemembered } from '@suite-common/suite-utils'; import { messageSystemActions } from '@suite-common/message-system'; @@ -74,7 +75,10 @@ const storageMiddleware = (api: MiddlewareAPI) => { } if (transactionsActions.resetTransaction.match(action)) { - storageActions.removeAccountTransactions(action.payload.account); + const { account } = action.payload; + + storageActions.removeAccountTransactions(account); + storageActions.removeAccountHistoricRates(account.key); } if ( @@ -85,10 +89,17 @@ const storageMiddleware = (api: MiddlewareAPI) => { ) { const { account } = action.payload; const device = findAccountDevice(account, selectDevices(api.getState())); + const historicRates = selectHistoricFiatRates(api.getState()); // update only transactions for remembered device if (isDeviceRemembered(device)) { + storageActions.removeAccountHistoricRates(account.key); storageActions.removeAccountTransactions(account); api.dispatch(storageActions.saveAccountTransactions(account)); + if (historicRates) { + api.dispatch( + storageActions.saveAccountHistoricRates(account.key, historicRates), + ); + } } } diff --git a/packages/suite/src/storage/definitions.ts b/packages/suite/src/storage/definitions.ts index da578d75bbc2..a5d534bba22a 100644 --- a/packages/suite/src/storage/definitions.ts +++ b/packages/suite/src/storage/definitions.ts @@ -2,7 +2,7 @@ import type { DBSchema } from 'idb'; import { FieldValues } from 'react-hook-form'; import type { SuiteState } from 'src/reducers/suite/suiteReducer'; -import type { FormState } from '@suite-common/wallet-types'; +import type { FormState, RatesByTimestamps } from '@suite-common/wallet-types'; import type { AcquiredDevice } from 'src/types/suite'; import type { MetadataState } from 'src/types/suite/metadata'; import type { Trade } from 'src/types/wallet/coinmarketCommonTypes'; @@ -45,6 +45,10 @@ export interface SuiteDBSchema extends DBSchema { evmSettings: SuiteState['evmSettings']; }; }; + historicRates: { + key: string; + value: RatesByTimestamps; + }; walletSettings: { key: string; value: WalletSettings; diff --git a/packages/suite/src/storage/index.ts b/packages/suite/src/storage/index.ts index 4e3cad556727..17a3635647a1 100644 --- a/packages/suite/src/storage/index.ts +++ b/packages/suite/src/storage/index.ts @@ -4,7 +4,7 @@ import { migrate } from './migrations'; import type { SuiteDBSchema } from './definitions'; -const VERSION = 44; // don't forget to add migration and CHANGELOG when changing versions! +const VERSION = 45; // don't forget to add migration and CHANGELOG when changing versions! /** * If the object stores don't already exist then creates them. diff --git a/packages/suite/src/storage/migrations/index.ts b/packages/suite/src/storage/migrations/index.ts index 50304ceecf82..ab3dfd779711 100644 --- a/packages/suite/src/storage/migrations/index.ts +++ b/packages/suite/src/storage/migrations/index.ts @@ -802,6 +802,9 @@ export const migrate: OnUpgradeFunc = async ( } if (oldVersion < 45) { + // object store for settings + db.createObjectStore('historicRates'); + await updateAll(transaction, 'txs', tx => { // @ts-expect-error delete tx.tx.rates; diff --git a/packages/suite/src/support/extraDependencies.ts b/packages/suite/src/support/extraDependencies.ts index 52590736ca8c..81d859c601de 100644 --- a/packages/suite/src/support/extraDependencies.ts +++ b/packages/suite/src/support/extraDependencies.ts @@ -2,7 +2,7 @@ import { saveAs } from 'file-saver'; import { PayloadAction } from '@reduxjs/toolkit'; import { resolveStaticPath } from '@suite-common/suite-utils'; -import { getAccountKey } from '@suite-common/wallet-utils'; +import { getAccountKey, buildHistoricRatesFromStorage } from '@suite-common/wallet-utils'; import { DeviceRootState, selectIsPendingTransportEvent, @@ -11,6 +11,7 @@ import { DiscoveryRootState, selectDiscoveryByDeviceState, deviceActions, + FiatRatesState, } from '@suite-common/wallet-core'; import { NetworkSymbol } from '@suite-common/wallet-config'; import { ExtraDependencies } from '@suite-common/redux-utils'; @@ -116,6 +117,13 @@ export const extraDependencies: ExtraDependencies = { state.transactions[k][item.order] = item.tx; }); }, + storageLoadHistoricRates: (state: FiatRatesState, { payload }: StorageLoadAction) => { + if (payload.historicRates) { + const fiatRates = payload.historicRates.map(rate => rate.value); + const historicRates = buildHistoricRatesFromStorage(fiatRates); + state.historic = historicRates; + } + }, storageLoadAccounts: (_, { payload }: StorageLoadAction) => payload.accounts.map(acc => acc.backendType === 'coinjoin' ? fixLoadedCoinjoinAccount(acc) : acc, diff --git a/packages/suite/src/support/suite/preloadStore.ts b/packages/suite/src/support/suite/preloadStore.ts index 69bbe5da07f3..50aa4296b7da 100644 --- a/packages/suite/src/support/suite/preloadStore.ts +++ b/packages/suite/src/support/suite/preloadStore.ts @@ -29,6 +29,7 @@ export const preloadStore = async () => { const discovery = await db.getItemsExtended('discovery'); const walletSettings = await db.getItemByPK('walletSettings', 'wallet'); const coinmarketTrades = await db.getItemsExtended('coinmarketTrades'); + const historicRates = await db.getItemsWithKeys('historicRates'); const graph = await db.getItemsExtended('graph'); const analytics = await db.getItemByPK('analytics', 'suite'); const metadata = await db.getItemByPK('metadata', 'state'); @@ -52,6 +53,7 @@ export const preloadStore = async () => { txs, graph, coinmarketTrades, + historicRates, sendFormDrafts, formDrafts, analytics, diff --git a/suite-common/redux-utils/src/extraDependenciesType.ts b/suite-common/redux-utils/src/extraDependenciesType.ts index 2f95624ff6b5..a9739c2c35bc 100644 --- a/suite-common/redux-utils/src/extraDependenciesType.ts +++ b/suite-common/redux-utils/src/extraDependenciesType.ts @@ -113,6 +113,7 @@ export type ExtraDependencies = { storageLoadBlockchain: StorageLoadReducer; storageLoadAccounts: StorageLoadReducer; storageLoadTransactions: StorageLoadTransactionsReducer; + storageLoadHistoricRates: StorageLoadReducer; storageLoadFirmware: StorageLoadReducer; storageLoadDiscovery: StorageLoadReducer; addButtonRequestFirmware: AddButtonRequestReducer; diff --git a/suite-common/test-utils/src/extraDependenciesMock.ts b/suite-common/test-utils/src/extraDependenciesMock.ts index 2bc54e9d392c..cfd9b1782238 100644 --- a/suite-common/test-utils/src/extraDependenciesMock.ts +++ b/suite-common/test-utils/src/extraDependenciesMock.ts @@ -117,6 +117,7 @@ export const extraDependenciesMock: ExtraDependencies = { storageLoadBlockchain: mockReducer('storageLoadBlockchain'), storageLoadAccounts: mockReducer('storageLoadAccounts'), storageLoadTransactions: mockReducer('storageLoadTransactions'), + storageLoadHistoricRates: mockReducer('storageLoadHistoricRates'), storageLoadFirmware: mockReducer('storageLoadFirmware'), storageLoadDiscovery: mockReducer('storageLoadDiscovery'), addButtonRequestFirmware: mockReducer('addButtonRequestFirmware'), diff --git a/suite-common/wallet-core/src/fiat-rates/fiatRatesReducer.ts b/suite-common/wallet-core/src/fiat-rates/fiatRatesReducer.ts index ab3f390dc212..3ec00b68f0eb 100644 --- a/suite-common/wallet-core/src/fiat-rates/fiatRatesReducer.ts +++ b/suite-common/wallet-core/src/fiat-rates/fiatRatesReducer.ts @@ -13,7 +13,7 @@ export const fiatRatesInitialState: FiatRatesState = { export const prepareFiatRatesReducer = createReducerWithExtraDeps( fiatRatesInitialState, - builder => { + (builder, extra) => { builder .addCase(updateFiatRatesThunk.pending, (state, action) => { const { ticker, localCurrency, rateType } = action.meta.arg; diff --git a/suite-common/wallet-utils/src/transactionUtils.ts b/suite-common/wallet-utils/src/transactionUtils.ts index 3aa3a4aae95a..8cea7f890ddb 100644 --- a/suite-common/wallet-utils/src/transactionUtils.ts +++ b/suite-common/wallet-utils/src/transactionUtils.ts @@ -166,7 +166,7 @@ export const sumTransactions = (transactions: WalletAccountTransaction[]) => { } // count in only if Inputs/Outputs includes my account (EVM does not need to) - if (tx.targets.length && tx.type === 'sent') { + if (tx.type === 'sent') { totalAmount = totalAmount.minus(amount); } @@ -200,7 +200,10 @@ export const sumTransactionsFiat = ( ) => { let totalAmount = new BigNumber(0); transactions.forEach(tx => { - const amount = formatNetworkAmount(tx.amount, tx.symbol); + const isWithToken = tx.tokens[0]?.contract; + const amount = isWithToken + ? formatAmount(tx.tokens[0]?.amount, tx.tokens[0]?.decimals) + : formatNetworkAmount(tx.amount, tx.symbol); const fee = formatNetworkAmount(tx.fee, tx.symbol); const fiatRateKey = getFiatRateKey(tx.symbol, fiatCurrency);