Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ When touching transaction and position flows, validation MUST include all releva
20. **Share-based full-exit withdrawals**: when a rebalance target leaves only dust in a source market, tx builders must switch to share-based `morphoWithdraw` (full shares burn with expected-assets guard) instead of asset-amount withdraws, so "empty market" intent cannot strand residual dust due rounding.
21. **Preview stability during quote refresh**: transaction-critical risk previews (LTV text, warning banners, risk bars, submit gating hints) must not be driven by transient placeholder/intermediate quote values while quote/route resolution is loading or refetching; display the last settled preview state (or a neutral loading state) until fresh executable quote data is available.
22. **Bigint-safe input echo formatting**: transaction-critical amount inputs must never round-trip through JavaScript `Number` when syncing bigint state back to text fields; use exact bigint/string unit formatters so typed values (for example `100000`) never mutate into precision-drifted decimals.
23. **Indexer-lag transaction history bridging**: when earnings/APY depends on recent supply/withdraw history, confirmed on-chain receipts must be parsed into a short-lived local transaction cache (scoped by canonical user address + chain, deduped by tx hash + log index, TTL-bounded), merged into reads while indexers lag, and automatically removed as soon as the API returns the same tx hash to prevent double counting.

### REQUIRED: Regression Rule Capture

Expand Down
22 changes: 20 additions & 2 deletions src/hooks/useTransactionWithToast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useSendTransaction, useWaitForTransactionReceipt } from 'wagmi';
import { StyledToast, TransactionToast } from '@/components/ui/styled-toast';
import { reportHandledError } from '@/utils/sentry';
import { toUserFacingTransactionErrorMessage } from '@/utils/transaction-errors';
import { cacheUserTransactionHistoryFromReceipt } from '@/utils/user-transaction-history-cache';
import { getExplorerTxURL } from '../utils/external';
import type { SupportedNetworks } from '../utils/networks';

Expand Down Expand Up @@ -39,15 +40,17 @@ export function useTransactionWithToast({
}: UseTransactionWithToastProps) {
const { data: hash, mutate: sendTransaction, error: txError, mutateAsync: sendTransactionAsync } = useSendTransaction();
const reportedErrorKeyRef = useRef<string | null>(null);
const handledConfirmationHashRef = useRef<string | null>(null);

const {
data: receipt,
isLoading: isConfirming,
isSuccess: isConfirmed,
isError,
error: receiptError,
} = useWaitForTransactionReceipt({
hash,
chainId: chainId,
chainId,
confirmations: 0,
});

Expand Down Expand Up @@ -86,7 +89,12 @@ export function useTransactionWithToast({
}, [isConfirming, pendingText, pendingDescription, toastId, onClick, hash]);

useEffect(() => {
if (isConfirmed) {
if (isConfirmed && hash) {
if (handledConfirmationHashRef.current === hash) {
return;
}
handledConfirmationHashRef.current = hash;

toast.update(toastId, {
render: (
<TransactionToast
Expand All @@ -101,6 +109,15 @@ export function useTransactionWithToast({
onClick,
closeButton: true,
});

if (receipt && hash && chainId) {
cacheUserTransactionHistoryFromReceipt({
receipt,
txHash: hash,
chainId,
});
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

if (onSuccessRef.current) {
onSuccessRef.current();
}
Expand Down Expand Up @@ -152,6 +169,7 @@ export function useTransactionWithToast({
isError,
txError,
receiptError,
receipt,
successText,
successDescription,
errorText,
Expand Down
25 changes: 22 additions & 3 deletions src/hooks/useUserPositionsSummaryData.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useMemo } from 'react';
import { useEffect, useMemo } from 'react';
import { useQueryClient } from '@tanstack/react-query';
import { estimateBlockAtTimestamp } from '@/utils/blockEstimation';
import type { SupportedNetworks } from '@/utils/networks';
Expand All @@ -8,6 +8,7 @@ import { useBlockTimestamps } from './queries/useBlockTimestamps';
import { usePositionSnapshots } from './queries/usePositionSnapshots';
import { useUserTransactionsQuery } from './queries/useUserTransactionsQuery';
import { usePositionsWithEarnings, getPeriodTimestamp } from './usePositionsWithEarnings';
import { mergeUserTransactionsWithRecentCache, reconcileUserTransactionHistoryCache } from '@/utils/user-transaction-history-cache';
import type { EarningsPeriod } from '@/stores/usePositionsFilters';

export type { EarningsPeriod } from '@/stores/usePositionsFilters';
Expand Down Expand Up @@ -62,6 +63,24 @@ const useUserPositionsSummaryData = (user: string | undefined, period: EarningsP
enabled: !!positions && !!user,
});

const mergedTransactions = useMemo(
() =>
mergeUserTransactionsWithRecentCache({
userAddress: user,
chainIds: uniqueChainIds,
apiTransactions: txData?.items ?? [],
}),
[user, uniqueChainIds, txData?.items],
);
Comment thread
coderabbitai[bot] marked this conversation as resolved.

useEffect(() => {
reconcileUserTransactionHistoryCache({
userAddress: user,
chainIds: uniqueChainIds,
apiTransactions: txData?.items ?? [],
});
}, [user, uniqueChainIds, txData?.items]);

const {
data: allSnapshots,
isLoading: isLoadingSnapshots,
Expand All @@ -74,7 +93,7 @@ const useUserPositionsSummaryData = (user: string | undefined, period: EarningsP

const positionsWithEarnings = usePositionsWithEarnings(
positions ?? [],
txData?.items ?? [],
mergedTransactions,
allSnapshots ?? {},
actualBlockData ?? {},
endTimestamp,
Expand Down Expand Up @@ -130,7 +149,7 @@ const useUserPositionsSummaryData = (user: string | undefined, period: EarningsP
refetch,
loadingStates,
actualBlockData: actualBlockData ?? {},
transactions: txData?.items ?? [],
transactions: mergedTransactions,
snapshotsByChain: allSnapshots ?? {},
};
};
Expand Down
1 change: 1 addition & 0 deletions src/hooks/useWrapLegacyMorpho.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export function useWrapLegacyMorpho(amount: bigint, onSuccess?: () => void) {
pendingText: 'Wrapping MORPHO...',
successText: 'Successfully wrapped MORPHO tokens!',
errorText: 'Failed to wrap MORPHO tokens',
chainId: SupportedNetworks.Mainnet,
onSuccess: () => {
tracking.complete();
onSuccess?.();
Expand Down
Loading