diff --git a/packages/synapse-interface/components/_Transaction/_Transaction.tsx b/packages/synapse-interface/components/_Transaction/_Transaction.tsx index 3c3dbef934..1c0369c833 100644 --- a/packages/synapse-interface/components/_Transaction/_Transaction.tsx +++ b/packages/synapse-interface/components/_Transaction/_Transaction.tsx @@ -38,7 +38,6 @@ const TimeRemaining = ({ } interface _TransactionProps { - synapseSDK: any connectedAddress: string originValue: number originChain: Chain @@ -48,15 +47,14 @@ interface _TransactionProps { originTxHash: string bridgeModuleName: string estimatedTime: number // in seconds - kappa?: string timestamp: number currentTime: number - isComplete: boolean + kappa?: string + isStoredComplete: boolean } /** TODO: Update naming after refactoring existing Activity / Transaction flow */ export const _Transaction = ({ - synapseSDK, connectedAddress, originValue, originChain, @@ -66,13 +64,13 @@ export const _Transaction = ({ originTxHash, bridgeModuleName, estimatedTime, - kappa, timestamp, currentTime, - isComplete, + kappa, + isStoredComplete, }: _TransactionProps) => { const dispatch = useAppDispatch() - const transactions = use_TransactionsState() + const { transactions } = use_TransactionsState() const [originTxExplorerLink, originExplorerName] = getTxBlockExplorerLink( originChain.id, @@ -103,34 +101,41 @@ export const _Transaction = ({ }, [estimatedTime, currentTime, timestamp]) const [isTxComplete, _kappa] = useBridgeTxStatus({ - synapseSDK, originChainId: originChain.id, destinationChainId: destinationChain.id, originTxHash, bridgeModuleName, - kappa, - checkStatus: isEstimatedTimeReached, + kappa: kappa, + checkStatus: !isStoredComplete || isEstimatedTimeReached, currentTime: currentTime, }) + /** Check if store already marked tx as complete, otherwise check hook status */ + const isTxCompleted = isStoredComplete ?? isTxComplete + /** Update tx kappa when available */ useEffect(() => { if (_kappa && originTxHash) { - dispatch(updateTransactionKappa({ originTxHash, kappa: _kappa })) + dispatch( + updateTransactionKappa({ originTxHash, kappa: _kappa as string }) + ) } }, [_kappa, dispatch]) /** Update tx for completion */ /** Check that we have not already marked tx as complete */ useEffect(() => { - const txKappa = kappa ?? _kappa + const txKappa = _kappa if (isTxComplete && originTxHash && txKappa) { - if (transactions[originTxHash].isComplete === false) { - dispatch(completeTransaction({ originTxHash, kappa: txKappa })) + const txn = transactions.find((tx) => tx.originTxHash === originTxHash) + if (!txn.isComplete) { + dispatch( + completeTransaction({ originTxHash, kappa: txKappa as string }) + ) } } - }, [isTxComplete, dispatch, transactions]) + }, [isTxComplete, dispatch, transactions, _kappa]) const handleClearTransaction = useCallback(() => { dispatch(removeTransaction({ originTxHash })) @@ -155,22 +160,33 @@ export const _Transaction = ({ /> - +
+ +
+ {new Date(timestamp * 1000).toLocaleString('en-US', { + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + hour12: true, + })} + {/*
{typeof _kappa === 'string' && _kappa?.substring(0, 15)}
*/} +
+
{/* TODO: Update visual format */}
- {isComplete ? ( + {isTxCompleted ? ( ) : ( )}
@@ -192,7 +208,7 @@ export const _Transaction = ({ text="Contact Support" link="https://discord.gg/synapseprotocol" /> - {isComplete && ( + {isTxComplete && ( { - const { synapseSDK } = useSynapseContext() - const transactions = use_TransactionsState() + const { transactions } = use_TransactionsState() - const transactionsArray: _TransactionDetails[] = Object.values(transactions) - - const hasTransactions: boolean = transactionsArray.length > 0 + const hasTransactions: boolean = checkTransactionsExist(transactions) const [currentTime, setCurrentTime] = useState( getTimeMinutesFromNow(0) @@ -35,11 +33,12 @@ export const _Transactions = ({ }, []) if (hasTransactions) { + const sortedTransactions = _.orderBy(transactions, ['timestamp'], ['desc']) return ( -
- {transactionsArray.map((tx: _TransactionDetails) => ( +
+ {sortedTransactions.slice(0, 5).map((tx: _TransactionDetails) => ( <_Transaction - synapseSDK={synapseSDK} + key={tx.timestamp} connectedAddress={connectedAddress} originValue={Number(tx.originValue)} originChain={tx.originChain} @@ -52,7 +51,7 @@ export const _Transactions = ({ kappa={tx?.kappa} timestamp={tx.timestamp} currentTime={currentTime} - isComplete={tx.isComplete} + isStoredComplete={tx.isComplete} /> ))}
diff --git a/packages/synapse-interface/components/_Transaction/helpers/useBridgeTxStatus.tsx b/packages/synapse-interface/components/_Transaction/helpers/useBridgeTxStatus.tsx index d3c5901e58..423e6db51b 100644 --- a/packages/synapse-interface/components/_Transaction/helpers/useBridgeTxStatus.tsx +++ b/packages/synapse-interface/components/_Transaction/helpers/useBridgeTxStatus.tsx @@ -2,7 +2,6 @@ import { useState, useEffect } from 'react' import { useSynapseContext } from '@/utils/providers/SynapseProvider' interface UseBridgeTxStatusProps { - synapseSDK: any originChainId: number destinationChainId: number originTxHash: string @@ -20,10 +19,10 @@ export const useBridgeTxStatus = ({ kappa, checkStatus = false, currentTime, -}: UseBridgeTxStatusProps) => { +}: UseBridgeTxStatusProps): [boolean, string] => { const { synapseSDK } = useSynapseContext() const [isComplete, setIsComplete] = useState(false) - const [fetchedKappa, setFetchedKappa] = useState(null) + const [fetchedKappa, setFetchedKappa] = useState(kappa ?? null) const getKappa = async (): Promise => { if (!synapseSDK) return null @@ -54,6 +53,7 @@ export const useBridgeTxStatus = ({ bridgeModuleName, kappa ) + return status } catch (error) { console.error('Error in getBridgeTxStatus:', error) @@ -65,28 +65,26 @@ export const useBridgeTxStatus = ({ if (!checkStatus) return if (isComplete) return ;(async () => { - let _kappa - - if (!kappa) { - console.log('fetching kappa') - _kappa = await getKappa() + if (fetchedKappa === null) { + let _kappa = await getKappa() setFetchedKappa(_kappa) - } else { - _kappa = kappa } - console.log('fetching tx status') - const txStatus = await getBridgeTxStatus( - destinationChainId, - bridgeModuleName, - _kappa - ) + if (fetchedKappa) { + const txStatus = await getBridgeTxStatus( + destinationChainId, + bridgeModuleName, + fetchedKappa + ) - if (txStatus !== null) { - setIsComplete(txStatus) + if (txStatus !== null && txStatus === true && fetchedKappa !== null) { + setIsComplete(true) + } else { + setIsComplete(false) + } } })() - }, [currentTime, checkStatus]) + }, [currentTime, checkStatus, fetchedKappa]) return [isComplete, fetchedKappa] } diff --git a/packages/synapse-interface/slices/_transactions/reducer.ts b/packages/synapse-interface/slices/_transactions/reducer.ts index d789bbf2f2..00d1bd9acc 100644 --- a/packages/synapse-interface/slices/_transactions/reducer.ts +++ b/packages/synapse-interface/slices/_transactions/reducer.ts @@ -1,6 +1,7 @@ -import { createSlice } from '@reduxjs/toolkit' +import { PayloadAction, createSlice } from '@reduxjs/toolkit' import { Chain, Token } from '@/utils/types' +import StateManagedBridge from '@/pages/state-managed-bridge' /** TODO: Rename entire slice once done refactoring prior Activity flow */ export interface _TransactionDetails { @@ -18,75 +19,58 @@ export interface _TransactionDetails { } export interface _TransactionsState { - [transactionHash: string]: _TransactionDetails + transactions: any[] } -export const initialState: _TransactionsState = {} +export const initialState: _TransactionsState = { + transactions: [], +} export const transactionsSlice = createSlice({ - name: 'transactions', + name: '_transactions', initialState, reducers: { - addTransaction: ( - transactions: _TransactionsState, - { - payload: { - originTxHash, - originValue, - bridgeModuleName, - originChain, - originToken, - destinationChain, - destinationToken, - estimatedTime, - timestamp, - }, - } - ) => { - if (!originTxHash) return - - transactions[originTxHash] = { - originTxHash, - originValue, - bridgeModuleName, - originChain, - originToken, - destinationChain, - destinationToken, - estimatedTime, - timestamp, - kappa: null, - isComplete: false, - } + addTransaction: (state, action: PayloadAction) => { + state.transactions.push(action.payload) }, removeTransaction: ( - transactions: _TransactionsState, - { payload: { originTxHash } } + state, + action: PayloadAction<{ originTxHash: string }> ) => { - if (transactions[originTxHash]) { - delete transactions[originTxHash] - } + const { originTxHash } = action.payload + state.transactions = state.transactions.filter( + (tx) => tx.originTxHash !== originTxHash + ) }, updateTransactionKappa: ( - transactions: _TransactionsState, - { payload: { originTxHash, kappa } } + state, + action: PayloadAction<{ originTxHash: string; kappa: string }> ) => { - const tx = transactions[originTxHash] - if (!tx) return + const { originTxHash, kappa } = action.payload + + const txIndex = state.transactions.findIndex( + (tx) => tx.originTxHash === originTxHash + ) - tx.kappa = kappa + if (txIndex !== -1) { + state.transactions[txIndex].kappa = kappa + } }, completeTransaction: ( - transactions: _TransactionsState, - { payload: { originTxHash } } + state, + action: PayloadAction<{ originTxHash: string; kappa: string }> ) => { - const tx = transactions[originTxHash] - if (!tx) return + const { originTxHash } = action.payload - tx.isComplete = true + const txIndex = state.transactions.findIndex( + (tx) => tx.originTxHash === originTxHash + ) + if (txIndex !== -1) { + state.transactions[txIndex].isComplete = true + } }, - clearTransactions: (transactions: _TransactionsState) => { - transactions = {} // eslint-disable-line + clearTransactions: (state) => { + state.transactions = [] }, }, }) diff --git a/packages/synapse-interface/slices/_transactions/updater.tsx b/packages/synapse-interface/slices/_transactions/updater.tsx index e618bd4a90..cb9d828884 100644 --- a/packages/synapse-interface/slices/_transactions/updater.tsx +++ b/packages/synapse-interface/slices/_transactions/updater.tsx @@ -4,18 +4,38 @@ import { use_TransactionsState } from './hooks' import { addTransaction } from './reducer' import { useTransactionsState } from '../transactions/hooks' import { checkTransactionsExist } from '@/utils/checkTransactionsExist' -import { PendingBridgeTransaction } from '../transactions/actions' +import { + PendingBridgeTransaction, + removePendingBridgeTransaction, +} from '../transactions/actions' +import _ from 'lodash' export default function Updater() { const dispatch = useAppDispatch() const { pendingBridgeTransactions } = useTransactionsState() - const transactions = use_TransactionsState() + const { transactions } = use_TransactionsState() /** Add transaction if not in _transactions store */ useEffect(() => { if (checkTransactionsExist(pendingBridgeTransactions)) { pendingBridgeTransactions.forEach((tx: PendingBridgeTransaction) => { - if (!transactions[tx.transactionHash]) { + /** Check Transaction has been confirmed */ + const txnConfirmed = + !_.isNull(tx.transactionHash) && !_.isUndefined(tx.transactionHash) + + /** Check Transaction is already stored */ + const txnExists = + transactions && + transactions.some( + (storedTx) => tx.transactionHash == storedTx.originTxHash + ) + + /** Remove pendingBridgeTransaction once stored in transactions */ + if (txnExists) { + dispatch(removePendingBridgeTransaction(tx.id)) + } + + if (txnConfirmed && !txnExists) { dispatch( addTransaction({ originTxHash: tx.transactionHash, @@ -32,7 +52,7 @@ export default function Updater() { } }) } - }, [pendingBridgeTransactions]) + }, [pendingBridgeTransactions, transactions]) return null }