From 925a11a5b0e56f782901e0bae04f4b1f671b8243 Mon Sep 17 00:00:00 2001 From: jonah <57741810+bigboydiamonds@users.noreply.github.com> Date: Wed, 8 Nov 2023 11:02:05 -0800 Subject: [PATCH] Fe/move pending bridge transactions (#1546) * Move `pendingBridgeTransactions` actions to `transactions` * Move `pendingBridgeTransactions` reducers to `transactions` * Update `resetTransactionsState` reducer to reset `pendingBridgeTransactions` and `fallbackQueryHistoricalTransactions` * Replace all instances of `pendingBridgeTransactions` to updated * Update version tracking to reset cache * Remove bridge from `persistedReducers` * Update `pendingBridgeTransactions` instance * Clean imports * ... * Update actions naming * cf --------- Co-authored-by: Jonah Lin <57741810+linjonah@users.noreply.github.com> --- .../components/Portfolio/Activity.tsx | 9 ++- .../components/Portfolio/Portfolio.tsx | 6 +- .../Transaction/MostRecentTransaction.tsx | 6 +- .../Transaction/PendingTransaction.tsx | 2 +- .../pages/state-managed-bridge/index.tsx | 9 +-- .../slices/bridge/actions.ts | 38 ++---------- .../slices/bridge/reducer.ts | 58 ------------------ .../slices/portfolio/updater.tsx | 13 +--- .../slices/transactions/actions.ts | 32 ++++++++++ .../slices/transactions/reducer.ts | 61 +++++++++++++++++++ .../slices/transactions/updater.tsx | 26 +++----- packages/synapse-interface/store/reducer.ts | 4 +- 12 files changed, 121 insertions(+), 143 deletions(-) diff --git a/packages/synapse-interface/components/Portfolio/Activity.tsx b/packages/synapse-interface/components/Portfolio/Activity.tsx index 83596a484d..1080879a06 100644 --- a/packages/synapse-interface/components/Portfolio/Activity.tsx +++ b/packages/synapse-interface/components/Portfolio/Activity.tsx @@ -9,9 +9,7 @@ import { Chain, Token } from '@/utils/types' import { tokenAddressToToken } from '@/constants/tokens' import { TransactionsState } from '@/slices/transactions/reducer' import { PortfolioState } from '@/slices/portfolio/reducer' -import { PendingBridgeTransaction } from '@/slices/bridge/actions' -import { BridgeState } from '@/slices/bridge/reducer' -import { useBridgeState } from '@/slices/bridge/hooks' +import { PendingBridgeTransaction } from '@/slices/transactions/actions' import { Transaction, TransactionType } from './Transaction/Transaction' import { PendingTransaction } from './Transaction/PendingTransaction' import { UserExplorerLink } from './Transaction/components/TransactionExplorerLink' @@ -27,8 +25,8 @@ export const Activity = ({ visibility }: { visibility: boolean }) => { pendingAwaitingCompletionTransactions, fallbackQueryPendingTransactions, fallbackQueryHistoricalTransactions, + pendingBridgeTransactions, }: TransactionsState = useTransactionsState() - const { pendingBridgeTransactions }: BridgeState = useBridgeState() const { searchInput, searchedBalancesAndAllowances }: PortfolioState = usePortfolioState() @@ -328,7 +326,8 @@ export const Activity = ({ visibility }: { visibility: boolean }) => { export const PendingTransactionAwaitingIndexing = () => { const { address } = useAccount() - const { pendingBridgeTransactions }: BridgeState = useBridgeState() + const { pendingBridgeTransactions }: TransactionsState = + useTransactionsState() return ( <> {pendingBridgeTransactions.map( diff --git a/packages/synapse-interface/components/Portfolio/Portfolio.tsx b/packages/synapse-interface/components/Portfolio/Portfolio.tsx index 8e1ec5b412..0f41b69603 100644 --- a/packages/synapse-interface/components/Portfolio/Portfolio.tsx +++ b/packages/synapse-interface/components/Portfolio/Portfolio.tsx @@ -24,11 +24,11 @@ import { resetTransactionsState } from '@/slices/transactions/actions' import { PortfolioState } from '@/slices/portfolio/reducer' import { useBridgeState } from '@/slices/bridge/hooks' import { BridgeState } from '@/slices/bridge/reducer' -import { isValidAddress } from '@/utils/isValidAddress' -import { Activity } from './Activity' -import { ViewSearchAddressBanner } from './SearchBar' import { resetBridgeInputs } from '@/slices/bridge/actions' import { resetReduxCache } from '@/slices/application/actions' +import { isValidAddress } from '@/utils/isValidAddress' +import { ViewSearchAddressBanner } from './SearchBar' +import { Activity } from './Activity' export const Portfolio = () => { const dispatch = useAppDispatch() diff --git a/packages/synapse-interface/components/Portfolio/Transaction/MostRecentTransaction.tsx b/packages/synapse-interface/components/Portfolio/Transaction/MostRecentTransaction.tsx index e22a46b390..513bf75da5 100644 --- a/packages/synapse-interface/components/Portfolio/Transaction/MostRecentTransaction.tsx +++ b/packages/synapse-interface/components/Portfolio/Transaction/MostRecentTransaction.tsx @@ -1,13 +1,11 @@ import { useState, useEffect, useMemo } from 'react' import { useAccount, Address } from 'wagmi' import { Chain, Token } from '@/utils/types' -import { useBridgeState } from '@/slices/bridge/hooks' import { useTransactionsState } from '@/slices/transactions/hooks' import { usePortfolioState } from '@/slices/portfolio/hooks' import { PortfolioState } from '@/slices/portfolio/reducer' -import { BridgeState } from '@/slices/bridge/reducer' import { TransactionsState } from '@/slices/transactions/reducer' -import { PendingBridgeTransaction } from '@/slices/bridge/actions' +import { PendingBridgeTransaction } from '@/slices/transactions/actions' import { BridgeTransaction } from '@/slices/api/generated' import { getTimeMinutesBeforeNow } from '@/utils/time' import { TransactionType } from './Transaction' @@ -18,7 +16,6 @@ import { checkTransactionsExist } from '@/utils/checkTransactionsExist' export const MostRecentTransaction = () => { const { address } = useAccount() - const { pendingBridgeTransactions }: BridgeState = useBridgeState() const { userHistoricalTransactions, isUserHistoricalTransactionsLoading, @@ -27,6 +24,7 @@ export const MostRecentTransaction = () => { pendingAwaitingCompletionTransactions, fallbackQueryHistoricalTransactions, fallbackQueryPendingTransactions, + pendingBridgeTransactions, }: TransactionsState = useTransactionsState() const { searchInput, searchedBalancesAndAllowances }: PortfolioState = usePortfolioState() diff --git a/packages/synapse-interface/components/Portfolio/Transaction/PendingTransaction.tsx b/packages/synapse-interface/components/Portfolio/Transaction/PendingTransaction.tsx index b3e3b11bf0..fb11240b48 100644 --- a/packages/synapse-interface/components/Portfolio/Transaction/PendingTransaction.tsx +++ b/packages/synapse-interface/components/Portfolio/Transaction/PendingTransaction.tsx @@ -5,7 +5,7 @@ import { useAppDispatch } from '@/store/hooks' import { removePendingBridgeTransaction, updatePendingBridgeTransaction, -} from '@/slices/bridge/actions' +} from '@/slices/transactions/actions' import { BridgeType } from '@/slices/api/generated' import { getTimeMinutesFromNow } from '@/utils/time' import { ARBITRUM, ETH } from '@/constants/chains/master' diff --git a/packages/synapse-interface/pages/state-managed-bridge/index.tsx b/packages/synapse-interface/pages/state-managed-bridge/index.tsx index e2f69f874d..61cc43a06f 100644 --- a/packages/synapse-interface/pages/state-managed-bridge/index.tsx +++ b/packages/synapse-interface/pages/state-managed-bridge/index.tsx @@ -56,26 +56,21 @@ import { Address, zeroAddress } from 'viem' import { stringToBigInt } from '@/utils/bigint/format' import { Warning } from '@/components/Warning' import { useAppDispatch } from '@/store/hooks' -import { NetworkTokenBalancesAndAllowances } from '@/utils/actions/fetchPortfolioBalances' import { fetchAndStoreSingleTokenAllowance, - fetchAndStoreSingleTokenBalance, useFetchPortfolioBalances, } from '@/slices/portfolio/hooks' import { updatePendingBridgeTransaction, addPendingBridgeTransaction, removePendingBridgeTransaction, - PendingBridgeTransaction, -} from '@/slices/bridge/actions' +} from '@/slices/transactions/actions' import { getTimeMinutesFromNow } from '@/utils/time' -import { FetchState } from '@/slices/portfolio/actions' import { updateSingleTokenAllowance } from '@/slices/portfolio/actions' import { FromChainListOverlay } from '@/components/StateManagedBridge/FromChainListOverlay' import { ToChainListOverlay } from '@/components/StateManagedBridge/ToChainListOverlay' import { FromTokenListOverlay } from '@/components/StateManagedBridge/FromTokenListOverlay' import { ToTokenListOverlay } from '@/components/StateManagedBridge/ToTokenListOverlay' -import { checkTransactionsExist } from '@/utils/checkTransactionsExist' const StateManagedBridge = () => { const { address } = useAccount() @@ -102,9 +97,7 @@ const StateManagedBridge = () => { toChainIds, fromTokens, toTokens, - pendingBridgeTransactions, }: BridgeState = useBridgeState() - const { showSettingsSlideOver, showDestinationAddress, diff --git a/packages/synapse-interface/slices/bridge/actions.ts b/packages/synapse-interface/slices/bridge/actions.ts index e1bb6466f6..dd40742a2c 100644 --- a/packages/synapse-interface/slices/bridge/actions.ts +++ b/packages/synapse-interface/slices/bridge/actions.ts @@ -1,42 +1,12 @@ import { createAction } from '@reduxjs/toolkit' -import { Chain, Token } from '@/utils/types' - -export interface PendingBridgeTransaction { - id: number - originChain: Chain - originToken: Token - originValue: string - destinationChain: Chain - destinationToken: Token - transactionHash?: string - timestamp: number - isSubmitted: boolean - estimatedTime: number - bridgeModuleName: string -} - -export const addPendingBridgeTransaction = - createAction('bridge/addPendingBridgeTransaction') -export const updatePendingBridgeTransaction = createAction<{ - id: number - timestamp: number - transactionHash: string - isSubmitted: boolean -}>('bridge/updatePendingBridgeTransaction') -export const removePendingBridgeTransaction = createAction( - 'bridge/removePendingBridgeTransaction' -) -export const updatePendingBridgeTransactions = createAction< - PendingBridgeTransaction[] ->('bridge/updatePendingBridgeTransactions') -export const resetBridgeInputs = createAction('bridge/resetBridgeInputs') -export const resetFetchedBridgeQuotes = createAction( - 'bridge/resetFetchedBridgeQuotes' -) export const updateDebouncedFromValue = createAction( 'bridge/updateDebouncedFromValue' ) export const updateDebouncedToTokensFromValue = createAction( 'bridge/updateDebouncedToTokensFromValue' ) +export const resetBridgeInputs = createAction('bridge/resetBridgeInputs') +export const resetFetchedBridgeQuotes = createAction( + 'bridge/resetFetchedBridgeQuotes' +) diff --git a/packages/synapse-interface/slices/bridge/reducer.ts b/packages/synapse-interface/slices/bridge/reducer.ts index dd96da2145..368b72878f 100644 --- a/packages/synapse-interface/slices/bridge/reducer.ts +++ b/packages/synapse-interface/slices/bridge/reducer.ts @@ -13,12 +13,7 @@ import { getToChainIds } from '@/utils/routeMaker/getToChainIds' import { getToTokens } from '@/utils/routeMaker/getToTokens' import { findTokenByRouteSymbol } from '@/utils/findTokenByRouteSymbol' import { - PendingBridgeTransaction, - addPendingBridgeTransaction, - removePendingBridgeTransaction, resetFetchedBridgeQuotes, - updatePendingBridgeTransaction, - updatePendingBridgeTransactions, resetBridgeInputs, updateDebouncedFromValue, updateDebouncedToTokensFromValue, @@ -47,7 +42,6 @@ export interface BridgeState { isLoading: boolean deadlineMinutes: number | null destinationAddress: Address | null - pendingBridgeTransactions: PendingBridgeTransaction[] } const { @@ -85,7 +79,6 @@ export const initialState: BridgeState = { isLoading: false, deadlineMinutes: null, destinationAddress: null, - pendingBridgeTransactions: [], } export const bridgeSlice = createSlice({ @@ -481,57 +474,6 @@ export const bridgeSlice = createSlice({ state.debouncedToTokensFromValue = action.payload } ) - .addCase( - addPendingBridgeTransaction, - (state, action: PayloadAction) => { - state.pendingBridgeTransactions = [ - action.payload, - ...state.pendingBridgeTransactions, - ] - } - ) - .addCase( - updatePendingBridgeTransaction, - ( - state, - action: PayloadAction<{ - id: number - timestamp: number - transactionHash: string - isSubmitted: boolean - }> - ) => { - const { id, timestamp, transactionHash, isSubmitted } = action.payload - const transactionIndex = state.pendingBridgeTransactions.findIndex( - (transaction) => transaction.id === id - ) - - if (transactionIndex !== -1) { - state.pendingBridgeTransactions = - state.pendingBridgeTransactions.map((transaction, index) => - index === transactionIndex - ? { ...transaction, transactionHash, isSubmitted, timestamp } - : transaction - ) - } - } - ) - .addCase( - removePendingBridgeTransaction, - (state, action: PayloadAction) => { - const idTimestampToRemove = action.payload - state.pendingBridgeTransactions = - state.pendingBridgeTransactions.filter( - (transaction) => transaction.id !== idTimestampToRemove - ) - } - ) - .addCase( - updatePendingBridgeTransactions, - (state, action: PayloadAction) => { - state.pendingBridgeTransactions = action.payload - } - ) .addCase(resetBridgeInputs, (state) => { state.fromChainId = initialState.fromChainId state.fromToken = initialState.fromToken diff --git a/packages/synapse-interface/slices/portfolio/updater.tsx b/packages/synapse-interface/slices/portfolio/updater.tsx index 2beef0e4c1..f1df2c29bf 100644 --- a/packages/synapse-interface/slices/portfolio/updater.tsx +++ b/packages/synapse-interface/slices/portfolio/updater.tsx @@ -1,25 +1,18 @@ import { useEffect } from 'react' +import { useAccount } from 'wagmi' import { Address } from '@wagmi/core' -import { watchPendingTransactions, waitForTransaction } from '@wagmi/core' import { useAppDispatch } from '@/store/hooks' -import { useBridgeState } from '../bridge/hooks' import { useTransactionsState } from '../transactions/hooks' import { TransactionsState } from '../transactions/reducer' -import { BridgeState } from '../bridge/reducer' import { fetchAndStoreSingleNetworkPortfolioBalances } from './hooks' - -import { useAccount } from 'wagmi' -import { - PendingBridgeTransaction, - updatePendingBridgeTransaction, -} from '../bridge/actions' +import { PendingBridgeTransaction } from '../transactions/actions' import { BridgeTransaction } from '../api/generated' export default function Updater(): null { const dispatch = useAppDispatch() const { address } = useAccount() - const { pendingBridgeTransactions }: BridgeState = useBridgeState() const { + pendingBridgeTransactions, userHistoricalTransactions, isUserHistoricalTransactionsLoading, }: TransactionsState = useTransactionsState() diff --git a/packages/synapse-interface/slices/transactions/actions.ts b/packages/synapse-interface/slices/transactions/actions.ts index 1a078610e4..4e1e0aca90 100644 --- a/packages/synapse-interface/slices/transactions/actions.ts +++ b/packages/synapse-interface/slices/transactions/actions.ts @@ -1,6 +1,38 @@ import { createAction } from '@reduxjs/toolkit' import { BridgeTransaction } from '../api/generated' +import { Chain, Token } from '@/utils/types' + +export interface PendingBridgeTransaction { + id: number + originChain: Chain + originToken: Token + originValue: string + destinationChain: Chain + destinationToken: Token + transactionHash?: string + timestamp: number + isSubmitted: boolean + estimatedTime: number + bridgeModuleName: string +} + +export const addPendingBridgeTransaction = + createAction( + 'transactions/addPendingBridgeTransaction' + ) +export const updatePendingBridgeTransaction = createAction<{ + id: number + timestamp: number + transactionHash: string + isSubmitted: boolean +}>('transactions/updatePendingBridgeTransaction') +export const removePendingBridgeTransaction = createAction( + 'transactions/removePendingBridgeTransaction' +) +export const updatePendingBridgeTransactions = createAction< + PendingBridgeTransaction[] +>('transactions/updatePendingBridgeTransactions') export const updateUserHistoricalTransactions = createAction< BridgeTransaction[] diff --git a/packages/synapse-interface/slices/transactions/reducer.ts b/packages/synapse-interface/slices/transactions/reducer.ts index e6a3e8d72c..2515cff67c 100644 --- a/packages/synapse-interface/slices/transactions/reducer.ts +++ b/packages/synapse-interface/slices/transactions/reducer.ts @@ -1,6 +1,11 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit' import { + PendingBridgeTransaction, + addPendingBridgeTransaction, + removePendingBridgeTransaction, + updatePendingBridgeTransaction, + updatePendingBridgeTransactions, updateUserHistoricalTransactions, updateIsUserHistoricalTransactionsLoading, updateUserPendingTransactions, @@ -26,6 +31,7 @@ export interface TransactionsState { pendingAwaitingCompletionTransactions: BridgeTransaction[] fallbackQueryPendingTransactions: BridgeTransaction[] fallbackQueryHistoricalTransactions: BridgeTransaction[] + pendingBridgeTransactions: PendingBridgeTransaction[] } const initialState: TransactionsState = { @@ -37,6 +43,7 @@ const initialState: TransactionsState = { pendingAwaitingCompletionTransactions: [], fallbackQueryPendingTransactions: [], fallbackQueryHistoricalTransactions: [], + pendingBridgeTransactions: [], } export const transactionsSlice = createSlice({ @@ -45,6 +52,57 @@ export const transactionsSlice = createSlice({ reducers: {}, extraReducers: (builder) => { builder + .addCase( + addPendingBridgeTransaction, + (state, action: PayloadAction) => { + state.pendingBridgeTransactions = [ + action.payload, + ...state.pendingBridgeTransactions, + ] + } + ) + .addCase( + updatePendingBridgeTransaction, + ( + state, + action: PayloadAction<{ + id: number + timestamp: number + transactionHash: string + isSubmitted: boolean + }> + ) => { + const { id, timestamp, transactionHash, isSubmitted } = action.payload + const transactionIndex = state.pendingBridgeTransactions.findIndex( + (transaction) => transaction.id === id + ) + + if (transactionIndex !== -1) { + state.pendingBridgeTransactions = + state.pendingBridgeTransactions.map((transaction, index) => + index === transactionIndex + ? { ...transaction, transactionHash, isSubmitted, timestamp } + : transaction + ) + } + } + ) + .addCase( + removePendingBridgeTransaction, + (state, action: PayloadAction) => { + const idTimestampToRemove = action.payload + state.pendingBridgeTransactions = + state.pendingBridgeTransactions.filter( + (transaction) => transaction.id !== idTimestampToRemove + ) + } + ) + .addCase( + updatePendingBridgeTransactions, + (state, action: PayloadAction) => { + state.pendingBridgeTransactions = action.payload + } + ) .addCase( updateUserHistoricalTransactions, (state, action: PayloadAction) => { @@ -168,6 +226,7 @@ export const transactionsSlice = createSlice({ } ) .addCase(resetTransactionsState, (state) => { + state.pendingBridgeTransactions = initialState.pendingBridgeTransactions state.userHistoricalTransactions = initialState.userHistoricalTransactions state.isUserHistoricalTransactionsLoading = @@ -179,6 +238,8 @@ export const transactionsSlice = createSlice({ initialState.pendingAwaitingCompletionTransactions state.fallbackQueryPendingTransactions = initialState.fallbackQueryPendingTransactions + state.fallbackQueryHistoricalTransactions = + initialState.fallbackQueryHistoricalTransactions }) }, }) diff --git a/packages/synapse-interface/slices/transactions/updater.tsx b/packages/synapse-interface/slices/transactions/updater.tsx index 6d3c300c58..837d93c97e 100644 --- a/packages/synapse-interface/slices/transactions/updater.tsx +++ b/packages/synapse-interface/slices/transactions/updater.tsx @@ -1,4 +1,3 @@ -import useWindowFocus from 'use-window-focus' import { useEffect, useMemo } from 'react' import { useAppDispatch } from '@/store/hooks' import { useAccount, Address } from 'wagmi' @@ -16,35 +15,26 @@ import { oneDayInMinutes, } from '@/utils/time' import { + updatePendingBridgeTransactions, + removePendingBridgeTransaction, + PendingBridgeTransaction, addFallbackQueryHistoricalTransaction, removeFallbackQueryHistoricalTransaction, removeFallbackQueryPendingTransaction, resetTransactionsState, updateIsUserPendingTransactionsLoading, -} from './actions' -import { updateIsUserHistoricalTransactionsLoading, updateUserHistoricalTransactions, updateUserPendingTransactions, -} from './actions' -import { useBridgeState } from '../bridge/hooks' -import { BridgeState } from '../bridge/reducer' -import { PortfolioState } from '../portfolio/reducer' -import { usePortfolioState } from '../portfolio/hooks' -import { PortfolioTabs } from '../portfolio/actions' -import { - updatePendingBridgeTransactions, - removePendingBridgeTransaction, - PendingBridgeTransaction, -} from '../bridge/actions' -import { addSeenHistoricalTransaction, addPendingAwaitingCompletionTransaction, removePendingAwaitingCompletionTransaction, } from './actions' +import { PortfolioState } from '../portfolio/reducer' +import { usePortfolioState } from '../portfolio/hooks' +import { PortfolioTabs } from '../portfolio/actions' import { getValidAddress } from '@/utils/isValidAddress' import { checkTransactionsExist } from '@/utils/checkTransactionsExist' -import { getTimeMinutesFromNow } from '@/utils/time' const queryHistoricalTime: number = getTimeMinutesBeforeNow(oneMonthInMinutes) const queryPendingTime: number = getTimeMinutesBeforeNow(oneDayInMinutes) @@ -60,8 +50,8 @@ export default function Updater(): null { pendingAwaitingCompletionTransactions, fallbackQueryPendingTransactions, fallbackQueryHistoricalTransactions, + pendingBridgeTransactions, }: TransactionsState = useTransactionsState() - const { pendingBridgeTransactions }: BridgeState = useBridgeState() const { activeTab, searchInput, @@ -178,7 +168,7 @@ export default function Updater(): null { useEffect(() => { const matchingTransactionHashes = new Set( pendingBridgeTransactions - .filter( + ?.filter( (recentTx) => (userPendingTransactions && userPendingTransactions.some( diff --git a/packages/synapse-interface/store/reducer.ts b/packages/synapse-interface/store/reducer.ts index ffefb3ae91..6828b18c6e 100644 --- a/packages/synapse-interface/store/reducer.ts +++ b/packages/synapse-interface/store/reducer.ts @@ -19,20 +19,20 @@ import { RootActions } from '@/slices/application/actions' const persistedReducers = { application, - bridge, transactions, } export const storageKey: string = 'synapse-interface' export const persistConfig: PersistConfig = { - version: 0, // upgrade to reset cache + version: 1, // upgrade to reset cache when updated data structures throw errors key: storageKey, storage, whitelist: Object.keys(persistedReducers), } export const appReducer = combineReducers({ + bridge, portfolio, swap, bridgeDisplay,