Skip to content

Commit 89f03ae

Browse files
committed
fix: refetch token balances after dust success
1 parent e173ebb commit 89f03ae

6 files changed

Lines changed: 96 additions & 8 deletions

File tree

src/components/composite/DustFlow/hooks/useDustModalFlow.tsx

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export const useDustModalFlow = ({
4646
isOpen,
4747
}: UseDustModalFlowOptions) => {
4848
const { t } = useTranslation();
49-
const { refresh: refreshPortfolio } = usePortfolioState();
49+
const { refreshForTokens } = usePortfolioState();
5050
const { nonNativeBalances, chains, nativeExtendedTokens } = useDustBalances();
5151
const fallbackNativeToken = useFallbackNativeToken(nativeExtendedTokens);
5252
const formFields = useDustFormFields({
@@ -67,6 +67,20 @@ export const useDustModalFlow = ({
6767
prevChainId: number | undefined;
6868
}>({ prevThreshold: undefined, prevChainId: undefined });
6969

70+
const completedDustSummaryRef = useRef<DustSummaryValue | null>(null);
71+
72+
const refreshCompletedDustTokens = useCallback(() => {
73+
const summary = completedDustSummaryRef.current;
74+
if (!summary) {
75+
return;
76+
}
77+
completedDustSummaryRef.current = null;
78+
void refreshForTokens(summary.address, [
79+
...summary.selectedBalances.map((b) => b.token),
80+
summary.nativeToken,
81+
]);
82+
}, [refreshForTokens]);
83+
7084
useEffect(() => {
7185
if (isOpen) {
7286
dustFieldSyncRef.current = {
@@ -239,22 +253,27 @@ export const useDustModalFlow = ({
239253
requiresConfirmation: false,
240254
executorType: supportsBatchTransactions ? 'batch' : 'single',
241255
fetchCallData,
256+
onSuccess: () => {
257+
if (dustSummary) {
258+
completedDustSummaryRef.current = dustSummary;
259+
}
260+
},
242261
});
243262

244263
const statusSheet = useDustConversionStatusSheet({
245264
transactionForm,
246265
toTokenBalance: nativeTokenBalance,
247266
onSuccess: () => {
267+
refreshCompletedDustTokens();
248268
setDustSummary(null);
249-
refreshPortfolio();
250269
widgetNav?.resetForm();
251270
},
252271
});
253272

254273
const handleModalClose = () => {
255274
onClose();
256275
transactionForm.resetForm();
257-
refreshPortfolio();
276+
refreshCompletedDustTokens();
258277
};
259278

260279
const views = useMemo(

src/providers/PortfolioProvider/PortfolioContext.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ const defaultOrchestrationState: OrchestrationState = {
9898
},
9999
refresh: () => {},
100100
refreshByAddress: () => {},
101+
refreshForTokens: () => Promise.resolve(),
101102
};
102103

103104
export const PortfolioContext = createContext<PortfolioContextValue>({

src/providers/PortfolioProvider/hooks/useBalancesData.ts

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@ import { useCallback, useMemo } from 'react';
22
import { useQueries, useQueryClient } from '@tanstack/react-query';
33
import type { Account } from '@lifi/widget-provider';
44
import { useAccount } from '@lifi/wallet-management';
5-
import type { ChainType } from '@lifi/sdk';
6-
import { compact, max } from 'lodash';
7-
import { fetchBalancesForAddress } from '../lib/fetchBalancesForAddresses';
5+
import type { ChainType, Token as LifiToken } from '@lifi/sdk';
6+
import { compact, differenceWith, max } from 'lodash';
7+
import {
8+
fetchBalancesForAddress,
9+
fetchTokenBalancesForAddress,
10+
} from '../lib/fetchBalancesForAddresses';
811
import { usePortfolioCacheStore } from '@/stores/portfolio/PortfolioCacheStore';
912
import type { TokenBalance } from '@/types/tokens';
1013

@@ -26,6 +29,7 @@ export interface UseTokensDataResult extends ResultState {
2629
refetch: () => void;
2730
cancel: () => void;
2831
refetchForAddress: (address: string) => void;
32+
refetchForTokens: (address: string, tokens: LifiToken[]) => Promise<void>;
2933
stateByAddress: Record<string, ResultState>;
3034
}
3135

@@ -141,6 +145,35 @@ export const useBalancesData = (): UseTokensDataResult => {
141145
[queries, addresses, queryClient],
142146
);
143147

148+
const refetchForTokens = useCallback(
149+
async (address: string, tokens: LifiToken[]) => {
150+
const freshBalances = await fetchTokenBalancesForAddress(address, tokens);
151+
152+
queryClient.setQueryData<TokenQueryData>(
153+
['portfolio-tokens', address],
154+
(prev) => {
155+
if (!prev) {
156+
return prev;
157+
}
158+
159+
const kept = differenceWith(
160+
prev.balances,
161+
tokens,
162+
(b, t) =>
163+
b.token.chainId === t.chainId &&
164+
b.token.address.toLowerCase() === t.address.toLowerCase(),
165+
);
166+
167+
return {
168+
...prev,
169+
balances: [...kept, ...freshBalances],
170+
};
171+
},
172+
);
173+
},
174+
[queryClient],
175+
);
176+
144177
const balancesByAddress = useMemo(() => {
145178
return queries.reduce(
146179
(acc, query, index) => {
@@ -213,5 +246,6 @@ export const useBalancesData = (): UseTokensDataResult => {
213246
refetch,
214247
cancel,
215248
refetchForAddress,
249+
refetchForTokens,
216250
};
217251
};

src/providers/PortfolioProvider/hooks/useOrchestrationState.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,16 @@ export const useOrchestrationState = (
133133
[balances.refetchForAddress],
134134
);
135135

136+
const refreshForTokens = useCallback(
137+
(
138+
address: string,
139+
tokens: Parameters<typeof balances.refetchForTokens>[1],
140+
) => {
141+
return balances.refetchForTokens(address, tokens);
142+
},
143+
[balances.refetchForTokens],
144+
);
145+
136146
return useMemo(
137147
() => ({
138148
isEmpty,
@@ -150,6 +160,7 @@ export const useOrchestrationState = (
150160
},
151161
refresh,
152162
refreshByAddress,
163+
refreshForTokens,
153164
}),
154165
[
155166
isEmpty,
@@ -165,6 +176,7 @@ export const useOrchestrationState = (
165176
pricesSource,
166177
refresh,
167178
refreshByAddress,
179+
refreshForTokens,
168180
],
169181
);
170182
};

src/providers/PortfolioProvider/lib/fetchBalancesForAddresses.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
getWalletBalances,
66
} from '@lifi/sdk';
77
import type { Token, TokenAmount } from '@lifi/sdk';
8-
import { flatMap } from 'lodash';
8+
import { flatMap, groupBy } from 'lodash';
99
import { createBatchFetcher } from '@/utils/batches/fetcher';
1010
import { sdkClient } from '@/utils/instrumentation/lifiSdkConfig';
1111
import {
@@ -22,7 +22,7 @@ const createTokensBalanceFromPlain = <
2222
return createTokenBalance(createExtendedToken(token), token.amount);
2323
};
2424

25-
const createTokenBalancesFromPlainArray = <
25+
export const createTokenBalancesFromPlainArray = <
2626
T extends Omit<TokenAmount, 'amount'> & {
2727
amount?: string | bigint | undefined;
2828
},
@@ -46,6 +46,24 @@ export interface FetchBalancesParams {
4646
onComplete?: (tokens: TokenBalance[]) => void;
4747
}
4848

49+
/** Fetch fresh balances for a specific list of tokens — bypasses getWalletBalances indexer lag */
50+
export const fetchTokenBalancesForAddress = async (
51+
address: string,
52+
tokens: Token[],
53+
): Promise<TokenBalance[]> => {
54+
const grouped = groupBy(tokens, (t) => t.chainId);
55+
56+
const { results } = createBatchFetcher<Token, TokenAmount>(
57+
grouped,
58+
async (_chainId, tokenBatch) => {
59+
const balances = await getTokenBalances(sdkClient, address, tokenBatch);
60+
return balances.filter((t) => t.amount && t.amount > BigInt(0));
61+
},
62+
);
63+
64+
return createTokenBalancesFromPlainArray(await results);
65+
};
66+
4967
/** Fetch tokens for EVM wallet (no batching needed) */
5068
export const fetchBalancesForEVMAddress = async (address: string) => {
5169
const walletBalances = await getWalletBalances(sdkClient, address);

src/providers/PortfolioProvider/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type {
44
WalletToken,
55
} from '@/types/tokens';
66
import type { App, Chain, Protocol } from '@/types/jumper-backend';
7+
import type { Token as LifiToken } from '@lifi/sdk';
78
import type { DefiPosition } from '@/utils/positions/type-guards';
89

910
/**
@@ -123,4 +124,7 @@ export interface OrchestrationState {
123124

124125
/** Refresh data source by address */
125126
refreshByAddress: (address: string) => void;
127+
128+
/** Fetch fresh balances for specific tokens and merge into cache, bypassing getWalletBalances indexer lag */
129+
refreshForTokens: (address: string, tokens: LifiToken[]) => Promise<void>;
126130
}

0 commit comments

Comments
 (0)