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
5 changes: 5 additions & 0 deletions .changeset/sour-flies-post.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"thirdweb": patch
---

Preload wallet balances on pay embed
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
import { SwapScreenContent } from "./swap/SwapScreenContent.js";
import { TokenSelectorScreen } from "./swap/TokenSelectorScreen.js";
import { TransferFlow } from "./swap/TransferFlow.js";
import { useWalletsAndBalances } from "./swap/fetchBalancesForWallet.js";
import {
type SupportedChainAndTokens,
useBuySupportedDestinations,
Expand Down Expand Up @@ -218,6 +219,15 @@
);
}, [props.supportedTokens, supportedSourcesQuery.data, payOptions]);

// preload wallets and balances
useWalletsAndBalances({
client: props.client,
sourceSupportedTokens: sourceSupportedTokens || [],
toChain: toChain,
toToken: toToken,
mode: payOptions.mode,
});

Check warning on line 229 in packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.tsx

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.tsx#L223-L229

Added lines #L223 - L229 were not covered by tests

const { fromChain, setFromChain, fromToken, setFromToken } =
useFromTokenSelectionStates({
payOptions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
ChevronRightIcon,
Cross2Icon,
} from "@radix-ui/react-icons";
import { useQuery } from "@tanstack/react-query";
import { trackPayEvent } from "../../../../../../../analytics/track/pay.js";
import type { Chain } from "../../../../../../../chains/types.js";
import type { ThirdwebClient } from "../../../../../../../client/client.js";
Expand Down Expand Up @@ -43,14 +42,9 @@
import { WalletRow } from "./WalletRow.js";
import {
type TokenBalance,
fetchBalancesForWallet,
useWalletsAndBalances,
} from "./fetchBalancesForWallet.js";

type WalletKey = {
id: WalletId;
address: string;
};

export function TokenSelectorScreen(props: {
client: ThirdwebClient;
sourceTokens: SupportedTokens | undefined;
Expand All @@ -71,46 +65,12 @@
const chainInfo = useChainMetadata(props.toChain);
const theme = useCustomTheme();

const walletsAndBalances = useQuery({
queryKey: [
"wallets-and-balances",
props.sourceSupportedTokens,
props.toChain.id,
props.toToken,
props.tokenAmount,
props.mode,
activeAccount?.address,
connectedWallets.map((w) => w.getAccount()?.address),
],
enabled: !!props.sourceSupportedTokens && !!chainInfo.data,
queryFn: async () => {
const entries = await Promise.all(
connectedWallets.map(async (wallet) => {
const balances = await fetchBalancesForWallet({
wallet,
accountAddress: activeAccount?.address,
sourceSupportedTokens: props.sourceSupportedTokens || [],
toChain: props.toChain,
toToken: props.toToken,
tokenAmount: props.tokenAmount,
mode: props.mode,
client: props.client,
});
return [
{
id: wallet.id,
address: wallet.getAccount()?.address || "",
} as WalletKey,
balances,
] as const;
}),
);
const map = new Map<WalletKey, TokenBalance[]>();
for (const entry of entries) {
map.set(entry[0], entry[1]);
}
return map;
},
const walletsAndBalances = useWalletsAndBalances({
client: props.client,
sourceSupportedTokens: props.sourceSupportedTokens || [],
toChain: props.toChain,
toToken: props.toToken,
mode: props.mode,

Check warning on line 73 in packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/TokenSelectorScreen.tsx

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/TokenSelectorScreen.tsx#L68-L73

Added lines #L68 - L73 were not covered by tests
});

if (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useQuery } from "@tanstack/react-query";
import type { Chain } from "../../../../../../../chains/types.js";
import { getCachedChain } from "../../../../../../../chains/utils.js";
import type { ThirdwebClient } from "../../../../../../../client/client.js";
Expand All @@ -9,7 +10,11 @@
type GetWalletBalanceResult,
getWalletBalance,
} from "../../../../../../../wallets/utils/getWalletBalance.js";
import type { WalletId } from "../../../../../../../wallets/wallet-types.js";
import type { PayUIOptions } from "../../../../../../core/hooks/connection/ConnectButtonProps.js";
import { useChainMetadata } from "../../../../../../core/hooks/others/useChainQuery.js";
import { useActiveAccount } from "../../../../../../core/hooks/wallets/useActiveAccount.js";
import { useConnectedWallets } from "../../../../../../core/hooks/wallets/useConnectedWallets.js";
import type {
SupportedTokens,
TokenInfo,
Expand All @@ -32,7 +37,6 @@
sourceSupportedTokens: SupportedTokens;
toChain: Chain;
toToken: ERC20OrNativeToken;
tokenAmount: string;
mode: PayUIOptions["mode"];
client: ThirdwebClient;
};
Expand All @@ -43,13 +47,70 @@
token: TokenInfo;
};

export async function fetchBalancesForWallet({
type WalletKey = {
id: WalletId;
address: string;
};

export function useWalletsAndBalances(props: {
sourceSupportedTokens: SupportedTokens;
toChain: Chain;
toToken: ERC20OrNativeToken;
mode: PayUIOptions["mode"];
client: ThirdwebClient;
}) {
const activeAccount = useActiveAccount();
const connectedWallets = useConnectedWallets();
const chainInfo = useChainMetadata(props.toChain);

Check warning on line 64 in packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/fetchBalancesForWallet.tsx

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/fetchBalancesForWallet.tsx#L61-L64

Added lines #L61 - L64 were not covered by tests

return useQuery({
queryKey: [
"wallets-and-balances",
props.sourceSupportedTokens,
props.toChain.id,
props.toToken,
props.mode,
activeAccount?.address,
connectedWallets.map((w) => w.getAccount()?.address),
],
enabled:
!!props.sourceSupportedTokens && !!chainInfo.data && !!activeAccount,
queryFn: async () => {
const entries = await Promise.all(
connectedWallets.map(async (wallet) => {
const balances = await fetchBalancesForWallet({
wallet,
accountAddress: activeAccount?.address,
sourceSupportedTokens: props.sourceSupportedTokens || [],
toChain: props.toChain,
toToken: props.toToken,
mode: props.mode,
client: props.client,
});
return [
{
id: wallet.id,
address: wallet.getAccount()?.address || "",
} as WalletKey,
balances,
] as const;
}),
);
const map = new Map<WalletKey, TokenBalance[]>();
for (const entry of entries) {
map.set(entry[0], entry[1]);
}
return map;
},
});
}

Check warning on line 106 in packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/fetchBalancesForWallet.tsx

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/fetchBalancesForWallet.tsx#L66-L106

Added lines #L66 - L106 were not covered by tests

async function fetchBalancesForWallet({

Check warning on line 108 in packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/fetchBalancesForWallet.tsx

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/fetchBalancesForWallet.tsx#L108

Added line #L108 was not covered by tests
wallet,
accountAddress,
sourceSupportedTokens,
toChain,
toToken,
tokenAmount,
mode,
client,
}: FetchBalancesParams): Promise<TokenBalance[]> {
Expand Down Expand Up @@ -133,7 +194,7 @@
b.chain.id === chainId &&
b.token.address.toLowerCase() === token.address.toLowerCase(),
);
if (!isNative && !isAlreadyFetched) {
if (isAlreadyFetched && !isNative) {

Check warning on line 197 in packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/fetchBalancesForWallet.tsx

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/fetchBalancesForWallet.tsx#L197

Added line #L197 was not covered by tests
// ERC20 on insight-enabled chain already handled by insight call
continue;
}
Expand All @@ -148,11 +209,12 @@
});

const include =
token.address === destinationToken.address &&
token.address.toLowerCase() ===
destinationToken.address.toLowerCase() &&

Check warning on line 213 in packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/fetchBalancesForWallet.tsx

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/fetchBalancesForWallet.tsx#L212-L213

Added lines #L212 - L213 were not covered by tests
chain.id === toChain.id
? mode === "fund_wallet" && account.address === accountAddress
? false
: Number(balance.displayValue) > Number(tokenAmount)
? !(
mode === "fund_wallet" && account.address === accountAddress

Check warning on line 216 in packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/fetchBalancesForWallet.tsx

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/fetchBalancesForWallet.tsx#L215-L216

Added lines #L215 - L216 were not covered by tests
)
Comment on lines +215 to +217
Copy link
Contributor

Choose a reason for hiding this comment

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

The logic for including tokens has been modified when removing the tokenAmount parameter. Previously, tokens were included if their balance exceeded the specified amount, but now they're included regardless of balance when not in fund_wallet mode or when addresses don't match. This change could result in displaying tokens with insufficient balances to users.

Consider whether this behavior change is intentional, as it might affect user experience by showing tokens that can't actually be used for the intended transaction. If this is intentional, perhaps add a comment explaining the rationale for this change.

Suggested change
? !(
mode === "fund_wallet" && account.address === accountAddress
)
? !(
(mode === "fund_wallet" && account.address === accountAddress) ||
token.balance.lte(0) // Only include tokens with positive balances
)

Spotted by Diamond

Is this helpful? React 👍 or 👎 to let us know.

Copy link
Contributor

Choose a reason for hiding this comment

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

Is this right?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, I think it's Worth the tradeoff to not have to reload the entire thing when you change the amount. That way every token has the same behavior: if you have any in your wallet, it shows

: balance.value > 0n;

if (include) {
Expand Down
Loading