Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix account balance discrepancies #5959

Merged
merged 41 commits into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
415e800
fix balance discrepency between bx and app and app and change wallet …
walmat Jul 26, 2024
2f82761
Update src/helpers/buildWalletSections.tsx
walmat Jul 26, 2024
4ff91ef
fix missing import from commit
walmat Jul 26, 2024
d010309
also fix send sheet balance discrepancy
walmat Jul 26, 2024
50df200
fix swap user assets not refetching on pending txn resolution and PTR
walmat Jul 31, 2024
cf6a796
.
walmat Jul 31, 2024
2605a21
update en_US key
walmat Aug 3, 2024
eb8bbe2
rmeove unused hook and bake it into the user assets store
walmat Aug 5, 2024
bc4c6e8
good lord
walmat Aug 5, 2024
148a1eb
separate invalidateQueries because they don't update when grouped for…
walmat Aug 5, 2024
d5a2a67
more instances
walmat Aug 5, 2024
1b4013f
cleanup
walmat Aug 5, 2024
a151992
Merge branch 'develop' into @matthew/APP-1689
walmat Aug 5, 2024
207e811
remove stuff
walmat Aug 7, 2024
1277e6d
cleanup useWatchPendingTxs
walmat Aug 8, 2024
9efc708
code suggestions
walmat Aug 8, 2024
2c018d8
break up useWallets
benisgold Aug 8, 2024
647d1b0
rm latestBackup from useWallets
benisgold Aug 8, 2024
c05f854
useMemo -> useEffect
benisgold Aug 8, 2024
4f05b85
logging
benisgold Aug 8, 2024
bb5cd77
cleanup
benisgold Aug 8, 2024
8061f7a
remove unused withPositions params
benisgold Aug 8, 2024
2aa197b
type
benisgold Aug 8, 2024
f9bf7ab
fix PROFILE_STICKY_HEADER loading value
benisgold Aug 8, 2024
423f6df
Merge branch 'develop' of https://github.com/rainbow-me/rainbow into …
benisgold Aug 8, 2024
5e81f25
fix
benisgold Aug 8, 2024
fc10958
optional chaining
benisgold Aug 9, 2024
b0eb61b
fixes to buildWalletSections + related code
benisgold Aug 9, 2024
943117b
fix accountInfo isLoadingAccountBalance
benisgold Aug 9, 2024
7a11e75
clean up useWalletBalances
benisgold Aug 9, 2024
38e9eb5
stuff is still broken but committing walletsWithBalancesAndNames fix
benisgold Aug 9, 2024
335bb0d
refactor useWalletsWithBalancesAndNames and fix consumers
benisgold Aug 9, 2024
04069ba
fix wallet screen balance loading state
benisgold Aug 9, 2024
4c6855d
Merge branch 'develop' of https://github.com/rainbow-me/rainbow into …
benisgold Aug 9, 2024
4240c4e
console.log
benisgold Aug 13, 2024
cbf63df
switch amount types from string | number -> string
benisgold Aug 13, 2024
e1349b0
align stale/cache times, remove keepPreviousData
benisgold Aug 13, 2024
6acff71
remove spammy isDamaged logging
benisgold Aug 13, 2024
5e06f01
add loading balance copy + remove incorrectly used no balance copy
benisgold Aug 13, 2024
33c8024
Merge branch 'develop' of https://github.com/rainbow-me/rainbow into …
benisgold Aug 13, 2024
3ada8f0
fix i18n mistake
benisgold Aug 14, 2024
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
32 changes: 14 additions & 18 deletions src/__swaps__/screens/Swap/providers/swap-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -264,24 +264,20 @@ export const SwapProvider = ({ children }: SwapProviderProps) => {
}
}

queryClient.invalidateQueries([
// old user assets invalidation (will cause a re-fetch)
{
queryKey: userAssetsQueryKey({
address: parameters.quote.from,
currency: nativeCurrency,
connectedToHardhat,
}),
},
// new swaps user assets invalidations
{
queryKey: swapsUserAssetsQueryKey({
address: parameters.quote.from as Address,
currency: nativeCurrency,
testnetMode: !!connectedToHardhat,
}),
},
]);
queryClient.invalidateQueries(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

separated all of the instances of invalidateQueries out because Arrays weren't working for whatever reason.

userAssetsQueryKey({
address: parameters.quote.from,
currency: nativeCurrency,
connectedToHardhat,
})
);
queryClient.invalidateQueries(
swapsUserAssetsQueryKey({
address: parameters.quote.from as Address,
currency: nativeCurrency,
testnetMode: !!connectedToHardhat,
})
);

clearCustomGasSettings(chainId);
NotificationManager?.postNotification('rapCompleted');
Expand Down
4 changes: 2 additions & 2 deletions src/components/DappBrowser/control-panel/ControlPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ export const ControlPanel = () => {
wallet.type === WalletTypes.readOnly
? i18n.t(i18n.l.wallet.change_wallet.watching)
: account.balance
? convertAmountToNativeDisplay(account.balance, nativeCurrency)
? account.balance
jinchung marked this conversation as resolved.
Show resolved Hide resolved
walmat marked this conversation as resolved.
Show resolved Hide resolved
: i18n.t(i18n.l.wallet.change_wallet.no_balance),
uniqueId: account.address,
color: colors.avatarBackgrounds[account.color],
Expand Down Expand Up @@ -472,7 +472,7 @@ const HomePanel = ({
},
screen: Routes.MAIN_EXCHANGE_SCREEN,
});
}, [runWalletChecksBeforeSwapOrBridge, selectedWallet?.uniqueId]);
}, [navigate, runWalletChecksBeforeSwapOrBridge, selectedWallet?.uniqueId, swapsV2Enabled]);

const handleOnPressBridge = useCallback(async () => {
const valid = await runWalletChecksBeforeSwapOrBridge();
Expand Down
12 changes: 4 additions & 8 deletions src/components/change-wallet/AddressRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@ import { EditWalletContextMenuActions } from '@/screens/ChangeWalletSheet';
import { toChecksumAddress } from '@/handlers/web3';
import { IS_IOS, IS_ANDROID } from '@/env';
import { ContextMenu } from '../context-menu';
import { convertAmountToNativeDisplay } from '@/helpers/utilities';
import { useSelector } from 'react-redux';
import { AppState } from '@/redux/store';
import { useForegroundColor } from '@/design-system';

const maxAccountLabelWidth = deviceUtils.dimensions.width - 88;
Expand Down Expand Up @@ -121,7 +118,6 @@ interface AddressRowProps {
}

export default function AddressRow({ contextMenuActions, data, editMode, onPress }: AddressRowProps) {
const nativeCurrency = useSelector((state: AppState) => state.settings.nativeCurrency);
const notificationsEnabled = useExperimentalFlag(NOTIFICATIONS);

const { address, balance, color: accountColor, ens, image: accountImage, isSelected, isReadOnly, isLedger, label, walletId } = data;
walmat marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -130,13 +126,13 @@ export default function AddressRow({ contextMenuActions, data, editMode, onPress

const labelQuaternary = useForegroundColor('labelQuaternary');

const cleanedUpBalance = useMemo(() => {
const balanceOrNoBalance = useMemo(() => {
walmat marked this conversation as resolved.
Show resolved Hide resolved
if (balance) {
return convertAmountToNativeDisplay(balance, nativeCurrency);
return balance;
} else {
return lang.t('wallet.change_wallet.no_balance');
}
}, [balance, nativeCurrency]);
}, [balance]);

const cleanedUpLabel = useMemo(() => removeFirstEmojiFromString(label), [label]);

Expand Down Expand Up @@ -254,7 +250,7 @@ export default function AddressRow({ contextMenuActions, data, editMode, onPress
<StyledTruncatedText color={colors.dark} testID={`change-wallet-address-row-label-${walletName}`}>
{walletName}
</StyledTruncatedText>
<StyledBottomRowText color={labelQuaternary}>{cleanedUpBalance}</StyledBottomRowText>
<StyledBottomRowText color={labelQuaternary}>{balanceOrNoBalance}</StyledBottomRowText>
</ColumnWithMargins>
</Row>
<Column style={sx.rightContent}>
Expand Down
8 changes: 3 additions & 5 deletions src/components/contacts/ContactRow.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,11 @@ import useExperimentalFlag, { PROFILES } from '@/config/experimentalHooks';
import { fetchReverseRecord } from '@/handlers/ens';
import { ENS_DOMAIN } from '@/helpers/ens';
import { isENSAddressFormat, isValidDomainFormat } from '@/helpers/validators';
import { useAccountSettings, useContacts, useDimensions, useENSAvatar } from '@/hooks';
import { useContacts, useDimensions, useENSAvatar } from '@/hooks';
import styled from '@/styled-thing';
import { margin } from '@/styles';
import { addressHashedColorIndex, addressHashedEmoji } from '@/utils/profileUtils';
import * as i18n from '@/languages';
import { convertAmountToNativeDisplay } from '@/helpers/utilities';

const ContactAddress = styled(TruncatedAddress).attrs(({ theme: { colors }, lite }) => ({
align: 'left',
Expand Down Expand Up @@ -58,17 +57,16 @@ const ContactRow = ({ address, color, nickname, symmetricalMargins, ...props },
const profilesEnabled = useExperimentalFlag(PROFILES);
const { width: deviceWidth } = useDimensions();
const { onAddOrUpdateContacts } = useContacts();
const { nativeCurrency } = useAccountSettings();
const { colors } = useTheme();
const { accountType, balance, ens, image, label, network, onPress, showcaseItem, testID } = props;

const cleanedUpBalance = useMemo(() => {
if (balance) {
return convertAmountToNativeDisplay(balance, nativeCurrency);
return balance;
walmat marked this conversation as resolved.
Show resolved Hide resolved
} else {
return i18n.t(i18n.l.wallet.change_wallet.no_balance);
}
}, [balance, nativeCurrency]);
}, [balance]);

// show avatar for contact rows that are accounts, not contacts
const avatar = accountType !== 'contacts' ? returnStringFirstEmoji(label) || profileUtils.addressHashedEmoji(address) : null;
Expand Down
26 changes: 9 additions & 17 deletions src/helpers/buildWalletSections.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { createSelector } from 'reselect';
import { buildBriefCoinsList, buildBriefUniqueTokenList } from './assets';
import { add, convertAmountToNativeDisplay } from './utilities';
import { NativeCurrencyKey, ParsedAddressAsset } from '@/entities';
import { queryClient } from '@/react-query';
import { positionsQueryKey } from '@/resources/defi/PositionsQuery';
Expand Down Expand Up @@ -39,6 +38,7 @@ const EMPTY_WALLET_CONTENT = [
const ONLY_NFTS_CONTENT = [{ type: 'ETH_CARD', uid: 'eth-card' }];

const sortedAssetsSelector = (state: any) => state.sortedAssets;
const accountBalanceSelector = (state: any) => state.accountBalance;
const hiddenCoinsSelector = (state: any) => state.hiddenCoins;
const isCoinListEditedSelector = (state: any) => state.isCoinListEdited;
const isLoadingUserAssetsSelector = (state: any) => state.isLoadingUserAssets;
Expand Down Expand Up @@ -105,23 +105,14 @@ const withPositionsSection = (isLoadingUserAssets: boolean) => {
const withBriefBalanceSection = (
sortedAssets: ParsedAddressAsset[],
isLoadingUserAssets: boolean,
accountBalance: number | undefined,
benisgold marked this conversation as resolved.
Show resolved Hide resolved
nativeCurrency: NativeCurrencyKey,
isCoinListEdited: boolean,
pinnedCoins: any,
hiddenCoins: any,
collectibles: any,
nftSort: string
collectibles: any
) => {
const { briefAssets, totalBalancesValue } = buildBriefCoinsList(sortedAssets, nativeCurrency, isCoinListEdited, pinnedCoins, hiddenCoins);

const { accountAddress: address } = store.getState().settings;
const positionsObj: RainbowPositions | undefined = queryClient.getQueryData(positionsQueryKey({ address, currency: nativeCurrency }));

const positionsTotal = positionsObj?.totals?.total?.amount || '0';
walmat marked this conversation as resolved.
Show resolved Hide resolved

const totalBalanceWithPositionsValue = add(totalBalancesValue, positionsTotal);

const totalValue = convertAmountToNativeDisplay(totalBalanceWithPositionsValue, nativeCurrency);
const { briefAssets } = buildBriefCoinsList(sortedAssets, nativeCurrency, isCoinListEdited, pinnedCoins, hiddenCoins);

const hasTokens = briefAssets?.length;
const hasNFTs = collectibles?.length;
Expand All @@ -133,7 +124,7 @@ const withBriefBalanceSection = (
{
type: 'PROFILE_STICKY_HEADER',
uid: 'assets-profile-header-compact',
value: totalValue,
value: accountBalance,
isLoadingUserAssets,
benisgold marked this conversation as resolved.
Show resolved Hide resolved
},
{
Expand Down Expand Up @@ -162,7 +153,7 @@ const withBriefBalanceSection = (
{
type: 'PROFILE_BALANCE_ROW',
uid: 'profile-balance',
value: totalValue,
value: accountBalance,
},
{
type: 'PROFILE_BALANCE_ROW_SPACE_AFTER',
Expand All @@ -172,13 +163,13 @@ const withBriefBalanceSection = (
{
type: 'PROFILE_ACTION_BUTTONS_ROW',
uid: 'profile-action-buttons',
value: totalValue,
value: accountBalance,
},
hasTokens
? {
type: 'PROFILE_ACTION_BUTTONS_ROW_SPACE_AFTER',
uid: 'profile-action-buttons-space-after',
value: totalValue,
value: accountBalance,
}
: { type: 'BIG_EMPTY_WALLET_SPACER', uid: 'big-empty-wallet-spacer-1' },
];
Expand Down Expand Up @@ -226,6 +217,7 @@ const briefBalanceSectionSelector = createSelector(
[
sortedAssetsSelector,
isLoadingUserAssetsSelector,
accountBalanceSelector,
nativeCurrencySelector,
isCoinListEditedSelector,
pinnedCoinsSelector,
Expand Down
36 changes: 19 additions & 17 deletions src/hooks/useRefreshAccountData.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { captureException } from '@sentry/react-native';
import delay from 'delay';
import { useCallback, useState } from 'react';
import { useCallback, useMemo, useState } from 'react';
import { useDispatch } from 'react-redux';
import { getIsHardhatConnected } from '@/handlers/web3';
import { walletConnectLoadState } from '../redux/walletconnect';
Expand All @@ -10,34 +10,36 @@ import { PROFILES, useExperimentalFlag } from '@/config';
import logger from '@/utils/logger';
import { queryClient } from '@/react-query';
import { userAssetsQueryKey } from '@/resources/assets/UserAssetsQuery';
import { userAssetsQueryKey as swapsUserAssetsQueryKey } from '@/__swaps__/screens/Swap/resources/assets/userAssets';
import { nftsQueryKey } from '@/resources/nfts';
import { positionsQueryKey } from '@/resources/defi/PositionsQuery';
import { Address } from 'viem';
import { addysSummaryQueryKey } from '@/resources/summary/summary';
import useWallets from './useWallets';

export default function useRefreshAccountData() {
const dispatch = useDispatch();
const { accountAddress, nativeCurrency } = useAccountSettings();
const [isRefreshing, setIsRefreshing] = useState(false);
const profilesEnabled = useExperimentalFlag(PROFILES);

const { wallets } = useWallets();

const allAddresses = useMemo(
() => Object.values(wallets || {}).flatMap(wallet => wallet.addresses.map(account => account.address as Address)),
[wallets]
);

const fetchAccountData = useCallback(async () => {
const connectedToHardhat = getIsHardhatConnected();

queryClient.invalidateQueries({
queryKey: nftsQueryKey({ address: accountAddress }),
});
queryClient.invalidateQueries({
queryKey: positionsQueryKey({
address: accountAddress,
currency: nativeCurrency,
}),
});
queryClient.invalidateQueries({
queryKey: userAssetsQueryKey({
address: accountAddress,
currency: nativeCurrency,
connectedToHardhat,
}),
});
queryClient.invalidateQueries(nftsQueryKey({ address: accountAddress }));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here. Arrays were just not working..

queryClient.invalidateQueries(positionsQueryKey({ address: accountAddress as Address, currency: nativeCurrency }));
queryClient.invalidateQueries(addysSummaryQueryKey({ addresses: allAddresses, currency: nativeCurrency }));
queryClient.invalidateQueries(userAssetsQueryKey({ address: accountAddress, currency: nativeCurrency, connectedToHardhat }));
queryClient.invalidateQueries(
swapsUserAssetsQueryKey({ address: accountAddress as Address, currency: nativeCurrency, testnetMode: !!connectedToHardhat })
);

try {
const getWalletNames = dispatch(fetchWalletNames());
Expand Down
77 changes: 73 additions & 4 deletions src/hooks/useWalletBalances.ts
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bulk of the changes to this file are to squash positions data in to the balance data when the bool is true (by default it is).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important to note that we fetch positions for addresses independently, so this doesn't scale as well as I'd like but should work for the time being.

Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,88 @@ import { useMemo } from 'react';
import { Address } from 'viem';
import useAccountSettings from './useAccountSettings';
import { useAddysSummary } from '@/resources/summary/summary';
import { useQueries } from '@tanstack/react-query';
import { fetchPositions, positionsQueryKey } from '@/resources/defi/PositionsQuery';
import { RainbowPositions } from '@/resources/defi/types';
import { add, convertAmountToNativeDisplay } from '@/helpers/utilities';
import { queryClient } from '@/react-query';

const useWalletBalances = (wallets: AllRainbowWallets) => {
type WalletBalanceWithPositions = {
assetBalanceAmount: number;
assetBalanceDisplay: string;
positionsBalanceAmount: string | number;
positionsBalanceDisplay: string;
totalBalanceAmount: string | number;
benisgold marked this conversation as resolved.
Show resolved Hide resolved
totalBalanceDisplay: string;
};

type WalletBalanceWithoutPositions = Omit<WalletBalanceWithPositions, 'positionsBalanceAmount' | 'positionsBalanceDisplay'>;

type WalletBalanceResult<T extends boolean> = {
balances: Record<Address, T extends true ? WalletBalanceWithPositions : WalletBalanceWithoutPositions>;
isLoading: boolean;
};

const useWalletBalances = <T extends boolean = true>(wallets: AllRainbowWallets, withPositions: T = true as T): WalletBalanceResult<T> => {
const { nativeCurrency } = useAccountSettings();

const walletAddresses: Address[] = useMemo(
const allAddresses = useMemo(
() => Object.values(wallets).flatMap(wallet => wallet.addresses.map(account => account.address as Address)),
[wallets]
);

const { data, isLoading } = useAddysSummary({ addresses: walletAddresses, currency: nativeCurrency });
const { data: summaryData, isLoading: isSummaryLoading } = useAddysSummary({
addresses: allAddresses,
currency: nativeCurrency,
});

const positionQueries = useQueries({
queries: allAddresses.map(address => ({
queryKey: positionsQueryKey({ address, currency: nativeCurrency }),
queryFn: () => fetchPositions({ address, currency: nativeCurrency }),
enabled: !!address && (withPositions as boolean),
})),
});

const balancesWithPositions = useMemo(() => {
const result: Record<Address, WalletBalanceWithPositions | WalletBalanceWithoutPositions> = {};

for (const address of allAddresses) {
const lowerCaseAddress = address.toLowerCase() as Address;
const assetBalance = summaryData?.data?.addresses?.[lowerCaseAddress]?.summary?.asset_value || 0;

if (!withPositions) {
result[lowerCaseAddress] = {
assetBalanceAmount: assetBalance,
assetBalanceDisplay: convertAmountToNativeDisplay(assetBalance, nativeCurrency),
totalBalanceAmount: assetBalance,
totalBalanceDisplay: convertAmountToNativeDisplay(assetBalance, nativeCurrency),
};
} else {
const positionData = queryClient.getQueryData<RainbowPositions | undefined>(
positionsQueryKey({ address, currency: nativeCurrency })
);
const positionsBalance = positionData?.totals?.total?.amount || '0';
const totalAccountBalance = add(assetBalance, positionsBalance);

result[lowerCaseAddress] = {
assetBalanceAmount: assetBalance,
assetBalanceDisplay: convertAmountToNativeDisplay(assetBalance, nativeCurrency),
positionsBalanceAmount: positionsBalance,
positionsBalanceDisplay: convertAmountToNativeDisplay(positionsBalance, nativeCurrency),
totalBalanceAmount: totalAccountBalance,
totalBalanceDisplay: convertAmountToNativeDisplay(totalAccountBalance, nativeCurrency),
};
}
}

return result;
}, [allAddresses, summaryData, withPositions, nativeCurrency]);

const isLoading = isSummaryLoading || positionQueries.some(query => query.isLoading);

return {
balances: data?.data?.addresses,
balances: balancesWithPositions as WalletBalanceResult<T>['balances'],
isLoading,
};
};
Expand Down
Loading