From 21c2bd4a86e7e7f3a9aead1d3e38f9a553ed3807 Mon Sep 17 00:00:00 2001 From: Jean Ribeiro Date: Tue, 15 Oct 2024 12:17:34 -0300 Subject: [PATCH] feat: store indexed data in persisted transactions (#2869) * feat: poll noves indexer data * feat: store indexed data in persisted transactions * feat: adds latest block fetched logic and store for noves --- .../shared/src/lib/auxiliary/noves/index.ts | 1 + .../src/lib/core/network/constants/index.ts | 1 - .../fetchAndPersistTransactionsForAccounts.ts | 35 +++++++++++-- .../src/lib/core/transactions/stores/index.ts | 1 + ...-persisted-for-noves-transactions.store.ts | 51 +++++++++++++++++++ .../transactions/stores/transactions.store.ts | 35 +++++++++++++ .../types/persisted-transaction.type.ts | 34 +++---------- .../src/lib/core/utils/types/types.type.ts | 4 ++ 8 files changed, 130 insertions(+), 32 deletions(-) create mode 100644 packages/shared/src/lib/core/transactions/stores/latest-block-persisted-for-noves-transactions.store.ts diff --git a/packages/shared/src/lib/auxiliary/noves/index.ts b/packages/shared/src/lib/auxiliary/noves/index.ts index 5c1de0df9f..4e203a63ad 100644 --- a/packages/shared/src/lib/auxiliary/noves/index.ts +++ b/packages/shared/src/lib/auxiliary/noves/index.ts @@ -1,3 +1,4 @@ export * from './apis' +export * from './constants' export * from './interfaces' export * from './types' diff --git a/packages/shared/src/lib/core/network/constants/index.ts b/packages/shared/src/lib/core/network/constants/index.ts index 49e3df975e..e17f9cf031 100644 --- a/packages/shared/src/lib/core/network/constants/index.ts +++ b/packages/shared/src/lib/core/network/constants/index.ts @@ -23,4 +23,3 @@ export * from './network-status-poll-interval.constant' export * from './noves-translate-api-url.constant' export * from './seconds-per-milestone.constant' export * from './supported-network-id.constant' -export * from '../../../auxiliary/noves/constants/supported-network-id-to-noves-chain.constant' diff --git a/packages/shared/src/lib/core/transactions/actions/fetchAndPersistTransactionsForAccounts.ts b/packages/shared/src/lib/core/transactions/actions/fetchAndPersistTransactionsForAccounts.ts index 11b2334ef2..8f5cf3ed2a 100644 --- a/packages/shared/src/lib/core/transactions/actions/fetchAndPersistTransactionsForAccounts.ts +++ b/packages/shared/src/lib/core/transactions/actions/fetchAndPersistTransactionsForAccounts.ts @@ -3,17 +3,20 @@ import { IAccountState, getAddressFromAccountForNetwork } from '@core/account' import { addBlockscoutTokenTransferToPersistedTransactions, addBlockscoutTransactionToPersistedTransactions, + addNovesTransactionToPersistedTransactions, + getLatestBlockForPersistedNovesTransactionsForAccountNetwork, getPersistedTransactionsForChain, isBlockscoutTokenTransferPersisted, isBlockscoutTransactionPersisted, + updateLatestBlockForPersistedNovesTransactionsForAccountNetwork, } from '../stores' import { BlockscoutApi } from '@auxiliary/blockscout/api' -import { EvmNetworkId, IEvmNetwork, SUPPORTED_NETWORK_ID_TO_NOVES_CHAIN, getEvmNetworks } from '@core/network' +import { EvmNetworkId, IEvmNetwork, getEvmNetworks } from '@core/network' import { BlockscoutTokenTransfer } from '@auxiliary/blockscout/types' import { generateEvmActivityFromPersistedTransaction } from '@core/activity/utils' import { EvmActivity, addAccountActivities, allAccountActivities } from '@core/activity' import { get } from 'svelte/store' -import { NovesApi, NovesTxResponse } from '@auxiliary/noves' +import { SUPPORTED_NETWORK_ID_TO_NOVES_CHAIN, NovesApi, NovesTxResponse } from '@auxiliary/noves' export async function fetchAndPersistTransactionsForAccounts( profileId: string, @@ -38,12 +41,11 @@ export async function fetchAndPersistTransactionsForNetwork( try { const [blockscoutTransactionsPromise, novesTransactionsPromise] = await Promise.allSettled([ fetchBlockscoutTransactionsForAccount(profileId, account, network), - fetchNovesTransactionsForAccount(account, network), + fetchNovesTransactionsForAccount(profileId, account, network), ]) const blockscoutTransactions = blockscoutTransactionsPromise.status === 'fulfilled' && blockscoutTransactionsPromise.value - // eslint-disable-next-line @typescript-eslint/no-unused-vars const novesTransactions = novesTransactionsPromise.status === 'fulfilled' && novesTransactionsPromise.value blockscoutTransactions && @@ -54,6 +56,20 @@ export async function fetchAndPersistTransactionsForNetwork( blockscoutTransactions ) + if (novesTransactions && novesTransactions.length > 0) { + addNovesTransactionToPersistedTransactions(profileId, account.index, network.id, novesTransactions) + + // Transactions are in reverse-chronological order + // So first transaction will be the latest one + const latestBlock = novesTransactions[0].rawTransactionData.blockNumber + updateLatestBlockForPersistedNovesTransactionsForAccountNetwork( + profileId, + account.index, + network.id, + latestBlock + ) + } + const blockscoutTokenTransfers = await fetchBlockscoutTokenTransfersForAccount(profileId, account, network) blockscoutTokenTransfers && addBlockscoutTokenTransferToPersistedTransactions( @@ -130,6 +146,7 @@ async function fetchBlockscoutTransactionsForAccount( } async function fetchNovesTransactionsForAccount( + profileId: string, account: IAccountState, network: IEvmNetwork ): Promise { @@ -139,8 +156,16 @@ async function fetchNovesTransactionsForAccount( return [] } + const latestBlock = getLatestBlockForPersistedNovesTransactionsForAccountNetwork( + profileId, + account.index, + network.id + ) + const novesApi = new NovesApi() - const transactions = await novesApi.translate.getTransactionsFromAddress(address, novesChain) + const transactions = await novesApi.translate.getTransactionsFromAddress(address, novesChain, { + startBlock: latestBlock, + }) return transactions } diff --git a/packages/shared/src/lib/core/transactions/stores/index.ts b/packages/shared/src/lib/core/transactions/stores/index.ts index 07282a2660..4a2b74f22b 100644 --- a/packages/shared/src/lib/core/transactions/stores/index.ts +++ b/packages/shared/src/lib/core/transactions/stores/index.ts @@ -1 +1,2 @@ +export * from './latest-block-persisted-for-noves-transactions.store' export * from './transactions.store' diff --git a/packages/shared/src/lib/core/transactions/stores/latest-block-persisted-for-noves-transactions.store.ts b/packages/shared/src/lib/core/transactions/stores/latest-block-persisted-for-noves-transactions.store.ts new file mode 100644 index 0000000000..296cf6f912 --- /dev/null +++ b/packages/shared/src/lib/core/transactions/stores/latest-block-persisted-for-noves-transactions.store.ts @@ -0,0 +1,51 @@ +import { NetworkId } from '@core/network' +import { get, writable } from 'svelte/store' + +type LatestBlockPersistedForAccountNetwork = { + [profileId: string]: { + [accountId: number]: { + [networkId in NetworkId]?: { + latestBlock?: number + } + } + } +} + +export const latestBlockPersistedForNovesTransactions = writable({}) + +export function getLatestBlockForPersistedNovesTransactionsForAccountNetwork( + profileId: string, + accountId: number, + networkId: NetworkId +): number | undefined { + return get(latestBlockPersistedForNovesTransactions)[profileId]?.[accountId]?.[networkId]?.latestBlock +} + +export function updateLatestBlockForPersistedNovesTransactionsForAccountNetwork( + profileId: string, + accountId: number, + networkId: NetworkId, + latestBlock: number +): void { + latestBlockPersistedForNovesTransactions.update((state) => { + if (!state[profileId]) { + state[profileId] = {} + } + + if (!state[profileId][accountId]) { + state[profileId][accountId] = {} + } + + if (!state[profileId][accountId][networkId]) { + state[profileId][accountId][networkId] = {} + } + + const obj = state[profileId][accountId][networkId] ?? {} + + obj.latestBlock = latestBlock + + state[profileId][accountId][networkId] = obj + + return state + }) +} diff --git a/packages/shared/src/lib/core/transactions/stores/transactions.store.ts b/packages/shared/src/lib/core/transactions/stores/transactions.store.ts index 9fd733b697..64139ade96 100644 --- a/packages/shared/src/lib/core/transactions/stores/transactions.store.ts +++ b/packages/shared/src/lib/core/transactions/stores/transactions.store.ts @@ -5,6 +5,7 @@ import { persistent } from '@core/utils/store' import { get } from 'svelte/store' import { LocalEvmTransaction, PersistedTransaction } from '../types/' import { BlockscoutTokenTransfer } from '@auxiliary/blockscout/types' +import { NovesTxResponse } from '@auxiliary/noves' type PersistedTransactions = { [profileId: string]: { @@ -147,6 +148,40 @@ export function addBlockscoutTransactionToPersistedTransactions( }) } +export function addNovesTransactionToPersistedTransactions( + profileId: string, + accountIndex: number, + networkId: NetworkId, + newTransactions: NovesTxResponse[] +): void { + persistedTransactions.update((state) => { + if (!state[profileId]) { + state[profileId] = {} + } + if (!state[profileId][accountIndex]) { + state[profileId][accountIndex] = { + [networkId]: {}, + } + } + if (!state[profileId][accountIndex][networkId]) { + state[profileId][accountIndex][networkId] = {} + } + + const _transactions = state[profileId][accountIndex][networkId] ?? {} + for (const transaction of newTransactions) { + const existingTransaction = _transactions?.[transaction.rawTransactionData.transactionHash.toLowerCase()] + const updatedTransaction: PersistedTransaction = { + ...existingTransaction, + noves: transaction, + } + _transactions[transaction.rawTransactionData.transactionHash.toLowerCase()] = updatedTransaction + } + state[profileId][accountIndex][networkId] = _transactions + + return state + }) +} + export function addBlockscoutTokenTransferToPersistedTransactions( profileId: string, accountIndex: number, diff --git a/packages/shared/src/lib/core/transactions/types/persisted-transaction.type.ts b/packages/shared/src/lib/core/transactions/types/persisted-transaction.type.ts index c77c05fc73..aa0153636a 100644 --- a/packages/shared/src/lib/core/transactions/types/persisted-transaction.type.ts +++ b/packages/shared/src/lib/core/transactions/types/persisted-transaction.type.ts @@ -1,30 +1,12 @@ import { IBlockscoutTransaction } from '@auxiliary/blockscout/interfaces' import { LocalEvmTransaction } from './local-evm-transaction.interface' import { BlockscoutTokenTransfer } from '@auxiliary/blockscout/types' +import { NovesTxResponse } from '@auxiliary/noves' +import { AtLeastOne } from '@core/utils/types' -export type PersistedTransaction = - | { - blockscout: IBlockscoutTransaction - tokenTransfer: BlockscoutTokenTransfer - local: LocalEvmTransaction - } - | { - blockscout?: IBlockscoutTransaction - tokenTransfer: BlockscoutTokenTransfer - local: LocalEvmTransaction - } - | { - blockscout: IBlockscoutTransaction - tokenTransfer: BlockscoutTokenTransfer - local?: LocalEvmTransaction - } - | { - blockscout?: IBlockscoutTransaction - tokenTransfer: BlockscoutTokenTransfer - local?: LocalEvmTransaction - } - | { - blockscout?: IBlockscoutTransaction - tokenTransfer?: BlockscoutTokenTransfer - local: LocalEvmTransaction - } +export type PersistedTransaction = AtLeastOne<{ + blockscout: IBlockscoutTransaction + noves: NovesTxResponse + tokenTransfer: BlockscoutTokenTransfer + local: LocalEvmTransaction +}> diff --git a/packages/shared/src/lib/core/utils/types/types.type.ts b/packages/shared/src/lib/core/utils/types/types.type.ts index 6f87c89811..b0bea60d7a 100644 --- a/packages/shared/src/lib/core/utils/types/types.type.ts +++ b/packages/shared/src/lib/core/utils/types/types.type.ts @@ -1 +1,5 @@ export type PartiallyOptional = Omit & Partial> + +export type AtLeastOne = Keys extends keyof T + ? Required> & Partial> + : never