thirdweb 5.96.8-nightly-4b58eca7c8860ec83481f73b117031caca4c966f-20250502000342 → 5.96.8-nightly-80bf795760052e83dd056cf5ef3ea54723d47775-20250503000343
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/insight/common.js +7 -2
- package/dist/cjs/insight/common.js.map +1 -1
- package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.js +3 -0
- package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.js.map +1 -1
- package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/swap/TokenSelectorScreen.js +25 -76
- package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/swap/TokenSelectorScreen.js.map +1 -1
- package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/swap/fetchBalancesForWallet.js +133 -0
- package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/swap/fetchBalancesForWallet.js.map +1 -0
- package/dist/cjs/version.js +1 -1
- package/dist/esm/insight/common.js +6 -2
- package/dist/esm/insight/common.js.map +1 -1
- package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.js +4 -1
- package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.js.map +1 -1
- package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/swap/TokenSelectorScreen.js +25 -76
- package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/swap/TokenSelectorScreen.js.map +1 -1
- package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/swap/fetchBalancesForWallet.js +130 -0
- package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/swap/fetchBalancesForWallet.js.map +1 -0
- package/dist/esm/version.js +1 -1
- package/dist/types/insight/common.d.ts +1 -0
- package/dist/types/insight/common.d.ts.map +1 -1
- package/dist/types/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.d.ts.map +1 -1
- package/dist/types/react/web/ui/ConnectWallet/screens/Buy/swap/TokenSelectorScreen.d.ts.map +1 -1
- package/dist/types/react/web/ui/ConnectWallet/screens/Buy/swap/fetchBalancesForWallet.d.ts +25 -0
- package/dist/types/react/web/ui/ConnectWallet/screens/Buy/swap/fetchBalancesForWallet.d.ts.map +1 -0
- package/dist/types/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/insight/common.ts +7 -2
- package/src/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.tsx +7 -1
- package/src/react/web/ui/ConnectWallet/screens/Buy/swap/TokenSelectorScreen.tsx +30 -96
- package/src/react/web/ui/ConnectWallet/screens/Buy/swap/fetchBalancesForWallet.ts +196 -0
- package/src/version.ts +1 -1
@@ -7,14 +7,8 @@ import {
|
|
7
7
|
import { useQuery } from "@tanstack/react-query";
|
8
8
|
import { trackPayEvent } from "../../../../../../../analytics/track/pay.js";
|
9
9
|
import type { Chain } from "../../../../../../../chains/types.js";
|
10
|
-
import { getCachedChain } from "../../../../../../../chains/utils.js";
|
11
10
|
import type { ThirdwebClient } from "../../../../../../../client/client.js";
|
12
|
-
import { NATIVE_TOKEN_ADDRESS } from "../../../../../../../constants/addresses.js";
|
13
11
|
import type { Wallet } from "../../../../../../../wallets/interfaces/wallet.js";
|
14
|
-
import {
|
15
|
-
type GetWalletBalanceResult,
|
16
|
-
getWalletBalance,
|
17
|
-
} from "../../../../../../../wallets/utils/getWalletBalance.js";
|
18
12
|
import type { WalletId } from "../../../../../../../wallets/wallet-types.js";
|
19
13
|
import { useCustomTheme } from "../../../../../../core/design-system/CustomThemeProvider.js";
|
20
14
|
import {
|
@@ -47,12 +41,10 @@ import { formatTokenBalance } from "../../formatTokenBalance.js";
|
|
47
41
|
import { type ERC20OrNativeToken, isNativeToken } from "../../nativeToken.js";
|
48
42
|
import { FiatValue } from "./FiatValue.js";
|
49
43
|
import { WalletRow } from "./WalletRow.js";
|
50
|
-
|
51
|
-
type TokenBalance
|
52
|
-
|
53
|
-
|
54
|
-
token: TokenInfo;
|
55
|
-
};
|
44
|
+
import {
|
45
|
+
type TokenBalance,
|
46
|
+
fetchBalancesForWallet,
|
47
|
+
} from "./fetchBalancesForWallet.js";
|
56
48
|
|
57
49
|
type WalletKey = {
|
58
50
|
id: WalletId;
|
@@ -90,93 +82,35 @@ export function TokenSelectorScreen(props: {
|
|
90
82
|
activeAccount?.address,
|
91
83
|
connectedWallets.map((w) => w.getAccount()?.address),
|
92
84
|
],
|
85
|
+
enabled: !!props.sourceSupportedTokens && !!chainInfo.data,
|
93
86
|
queryFn: async () => {
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
// inject the destination token too since it can be used as well to pay/transfer
|
107
|
-
const toToken = isNativeToken(props.toToken)
|
108
|
-
? {
|
109
|
-
address: NATIVE_TOKEN_ADDRESS,
|
110
|
-
name: chainInfo.data?.nativeCurrency.name || "",
|
111
|
-
symbol: chainInfo.data?.nativeCurrency.symbol || "",
|
112
|
-
icon: chainInfo.data?.icon?.url,
|
113
|
-
}
|
114
|
-
: props.toToken;
|
115
|
-
|
116
|
-
const tokens = {
|
117
|
-
...props.sourceSupportedTokens,
|
118
|
-
[props.toChain.id]: [
|
119
|
-
toToken,
|
120
|
-
...(props.sourceSupportedTokens?.[props.toChain.id] || []),
|
121
|
-
],
|
122
|
-
};
|
123
|
-
|
124
|
-
return Object.entries(tokens).flatMap(([chainId, tokens]) => {
|
125
|
-
return tokens.map(async (token) => {
|
126
|
-
try {
|
127
|
-
const chain = getCachedChain(Number(chainId));
|
128
|
-
const balance = await getWalletBalance({
|
129
|
-
address: account.address,
|
130
|
-
chain,
|
131
|
-
tokenAddress: isNativeToken(token) ? undefined : token.address,
|
132
|
-
client: props.client,
|
133
|
-
});
|
134
|
-
|
135
|
-
// show the token if:
|
136
|
-
// - its not the destination token and balance is greater than 0
|
137
|
-
// - its the destination token and balance is greater than the token amount AND we the account is not the default account in fund_wallet mode
|
138
|
-
const shouldInclude =
|
139
|
-
token.address === toToken.address &&
|
140
|
-
chain.id === props.toChain.id
|
141
|
-
? props.mode === "fund_wallet" &&
|
142
|
-
account.address === activeAccount?.address
|
143
|
-
? false
|
144
|
-
: Number(balance.displayValue) > Number(props.tokenAmount)
|
145
|
-
: balance.value > 0n;
|
146
|
-
|
147
|
-
if (shouldInclude) {
|
148
|
-
const existingBalances = walletBalanceMap.get(walletKey) || [];
|
149
|
-
existingBalances.push({ balance, chain, token });
|
150
|
-
existingBalances.sort((a, b) => {
|
151
|
-
if (
|
152
|
-
a.chain.id === props.toChain.id &&
|
153
|
-
a.token.address === toToken.address
|
154
|
-
)
|
155
|
-
return -1;
|
156
|
-
if (
|
157
|
-
b.chain.id === props.toChain.id &&
|
158
|
-
b.token.address === toToken.address
|
159
|
-
)
|
160
|
-
return 1;
|
161
|
-
if (a.chain.id === props.toChain.id) return -1;
|
162
|
-
if (b.chain.id === props.toChain.id) return 1;
|
163
|
-
return a.chain.id > b.chain.id ? 1 : -1;
|
164
|
-
});
|
165
|
-
}
|
166
|
-
} catch (error) {
|
167
|
-
console.error(
|
168
|
-
`Failed to fetch balance for wallet ${wallet.id} on chain ${chainId} for token ${token.symbol}:`,
|
169
|
-
error,
|
170
|
-
);
|
171
|
-
}
|
87
|
+
const entries = await Promise.all(
|
88
|
+
connectedWallets.map(async (wallet) => {
|
89
|
+
const balances = await fetchBalancesForWallet({
|
90
|
+
wallet,
|
91
|
+
accountAddress: activeAccount?.address,
|
92
|
+
sourceSupportedTokens: props.sourceSupportedTokens || [],
|
93
|
+
toChain: props.toChain,
|
94
|
+
toToken: props.toToken,
|
95
|
+
tokenAmount: props.tokenAmount,
|
96
|
+
mode: props.mode,
|
97
|
+
client: props.client,
|
172
98
|
});
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
99
|
+
return [
|
100
|
+
{
|
101
|
+
id: wallet.id,
|
102
|
+
address: wallet.getAccount()?.address || "",
|
103
|
+
} as WalletKey,
|
104
|
+
balances,
|
105
|
+
] as const;
|
106
|
+
}),
|
107
|
+
);
|
108
|
+
const map = new Map<WalletKey, TokenBalance[]>();
|
109
|
+
for (const entry of entries) {
|
110
|
+
map.set(entry[0], entry[1]);
|
111
|
+
}
|
112
|
+
return map;
|
178
113
|
},
|
179
|
-
enabled: !!props.sourceSupportedTokens && !!chainInfo.data,
|
180
114
|
});
|
181
115
|
|
182
116
|
if (
|
@@ -0,0 +1,196 @@
|
|
1
|
+
import type { Chain } from "../../../../../../../chains/types.js";
|
2
|
+
import { getCachedChain } from "../../../../../../../chains/utils.js";
|
3
|
+
import type { ThirdwebClient } from "../../../../../../../client/client.js";
|
4
|
+
import { NATIVE_TOKEN_ADDRESS } from "../../../../../../../constants/addresses.js";
|
5
|
+
import { isInsightEnabled } from "../../../../../../../insight/common.js";
|
6
|
+
import { getOwnedTokens } from "../../../../../../../insight/get-tokens.js";
|
7
|
+
import type { Wallet } from "../../../../../../../wallets/interfaces/wallet.js";
|
8
|
+
import {
|
9
|
+
type GetWalletBalanceResult,
|
10
|
+
getWalletBalance,
|
11
|
+
} from "../../../../../../../wallets/utils/getWalletBalance.js";
|
12
|
+
import type { PayUIOptions } from "../../../../../../core/hooks/connection/ConnectButtonProps.js";
|
13
|
+
import type {
|
14
|
+
SupportedTokens,
|
15
|
+
TokenInfo,
|
16
|
+
} from "../../../../../../core/utils/defaultTokens.js";
|
17
|
+
import { type ERC20OrNativeToken, isNativeToken } from "../../nativeToken.js";
|
18
|
+
|
19
|
+
const CHUNK_SIZE = 5;
|
20
|
+
|
21
|
+
function chunkChains<T>(chains: T[]): T[][] {
|
22
|
+
const chunks: T[][] = [];
|
23
|
+
for (let i = 0; i < chains.length; i += CHUNK_SIZE) {
|
24
|
+
chunks.push(chains.slice(i, i + CHUNK_SIZE));
|
25
|
+
}
|
26
|
+
return chunks;
|
27
|
+
}
|
28
|
+
|
29
|
+
type FetchBalancesParams = {
|
30
|
+
wallet: Wallet;
|
31
|
+
accountAddress: string | undefined;
|
32
|
+
sourceSupportedTokens: SupportedTokens;
|
33
|
+
toChain: Chain;
|
34
|
+
toToken: ERC20OrNativeToken;
|
35
|
+
tokenAmount: string;
|
36
|
+
mode: PayUIOptions["mode"];
|
37
|
+
client: ThirdwebClient;
|
38
|
+
};
|
39
|
+
|
40
|
+
export type TokenBalance = {
|
41
|
+
balance: GetWalletBalanceResult;
|
42
|
+
chain: Chain;
|
43
|
+
token: TokenInfo;
|
44
|
+
};
|
45
|
+
|
46
|
+
export async function fetchBalancesForWallet({
|
47
|
+
wallet,
|
48
|
+
accountAddress,
|
49
|
+
sourceSupportedTokens,
|
50
|
+
toChain,
|
51
|
+
toToken,
|
52
|
+
tokenAmount,
|
53
|
+
mode,
|
54
|
+
client,
|
55
|
+
}: FetchBalancesParams): Promise<TokenBalance[]> {
|
56
|
+
const account = wallet.getAccount();
|
57
|
+
if (!account) {
|
58
|
+
return [];
|
59
|
+
}
|
60
|
+
|
61
|
+
const balances: TokenBalance[] = [];
|
62
|
+
|
63
|
+
// 1. Resolve all unique chains in the supported token map
|
64
|
+
const uniqueChains = Object.keys(sourceSupportedTokens).map((id) =>
|
65
|
+
getCachedChain(Number(id)),
|
66
|
+
);
|
67
|
+
|
68
|
+
// 2. Check insight availability once per chain
|
69
|
+
const insightSupport = await Promise.all(
|
70
|
+
uniqueChains.map(async (c) => ({
|
71
|
+
chain: c,
|
72
|
+
enabled: await isInsightEnabled(c),
|
73
|
+
})),
|
74
|
+
);
|
75
|
+
const insightEnabledChains = insightSupport
|
76
|
+
.filter((c) => c.enabled)
|
77
|
+
.map((c) => c.chain);
|
78
|
+
|
79
|
+
// 3. ERC-20 balances for insight-enabled chains (batched 5 chains / call)
|
80
|
+
const insightChunks = chunkChains(insightEnabledChains);
|
81
|
+
await Promise.all(
|
82
|
+
insightChunks.map(async (chunk) => {
|
83
|
+
const owned = await getOwnedTokens({
|
84
|
+
ownerAddress: account.address,
|
85
|
+
chains: chunk,
|
86
|
+
client,
|
87
|
+
});
|
88
|
+
|
89
|
+
for (const b of owned) {
|
90
|
+
const matching = sourceSupportedTokens[b.chainId]?.find(
|
91
|
+
(t) => t.address.toLowerCase() === b.tokenAddress.toLowerCase(),
|
92
|
+
);
|
93
|
+
if (matching) {
|
94
|
+
balances.push({
|
95
|
+
balance: b,
|
96
|
+
chain: getCachedChain(b.chainId),
|
97
|
+
token: matching,
|
98
|
+
});
|
99
|
+
}
|
100
|
+
}
|
101
|
+
}),
|
102
|
+
);
|
103
|
+
|
104
|
+
// 4. Build a token map that also includes the destination token so it can be used to pay
|
105
|
+
const destinationToken = isNativeToken(toToken)
|
106
|
+
? {
|
107
|
+
address: NATIVE_TOKEN_ADDRESS,
|
108
|
+
name: toChain.nativeCurrency?.name || "",
|
109
|
+
symbol: toChain.nativeCurrency?.symbol || "",
|
110
|
+
icon: toChain.icon?.url,
|
111
|
+
}
|
112
|
+
: toToken;
|
113
|
+
|
114
|
+
const tokenMap: Record<number, TokenInfo[]> = {
|
115
|
+
...sourceSupportedTokens,
|
116
|
+
[toChain.id]: [
|
117
|
+
destinationToken,
|
118
|
+
...(sourceSupportedTokens[toChain.id] || []),
|
119
|
+
],
|
120
|
+
};
|
121
|
+
|
122
|
+
// 5. Fallback RPC balances (native currency & ERC-20 that we couldn't fetch from insight)
|
123
|
+
const rpcCalls: Promise<void>[] = [];
|
124
|
+
|
125
|
+
for (const [chainIdStr, tokens] of Object.entries(tokenMap)) {
|
126
|
+
const chainId = Number(chainIdStr);
|
127
|
+
const chain = getCachedChain(chainId);
|
128
|
+
|
129
|
+
for (const token of tokens) {
|
130
|
+
const isNative = isNativeToken(token);
|
131
|
+
const isAlreadyFetched = balances.some(
|
132
|
+
(b) =>
|
133
|
+
b.chain.id === chainId &&
|
134
|
+
b.token.address.toLowerCase() === token.address.toLowerCase(),
|
135
|
+
);
|
136
|
+
if (!isNative && !isAlreadyFetched) {
|
137
|
+
// ERC20 on insight-enabled chain already handled by insight call
|
138
|
+
continue;
|
139
|
+
}
|
140
|
+
rpcCalls.push(
|
141
|
+
(async () => {
|
142
|
+
try {
|
143
|
+
const balance = await getWalletBalance({
|
144
|
+
address: account.address,
|
145
|
+
chain,
|
146
|
+
tokenAddress: isNative ? undefined : token.address,
|
147
|
+
client,
|
148
|
+
});
|
149
|
+
|
150
|
+
const include =
|
151
|
+
token.address === destinationToken.address &&
|
152
|
+
chain.id === toChain.id
|
153
|
+
? mode === "fund_wallet" && account.address === accountAddress
|
154
|
+
? false
|
155
|
+
: Number(balance.displayValue) > Number(tokenAmount)
|
156
|
+
: balance.value > 0n;
|
157
|
+
|
158
|
+
if (include) {
|
159
|
+
balances.push({ balance, chain, token });
|
160
|
+
}
|
161
|
+
} catch (err) {
|
162
|
+
console.warn(
|
163
|
+
`Failed to fetch balance for ${token.symbol} on chain ${chainId}`,
|
164
|
+
err,
|
165
|
+
);
|
166
|
+
}
|
167
|
+
})(),
|
168
|
+
);
|
169
|
+
}
|
170
|
+
}
|
171
|
+
|
172
|
+
await Promise.all(rpcCalls);
|
173
|
+
|
174
|
+
// Remove duplicates (same chainId + token address)
|
175
|
+
{
|
176
|
+
const uniq: Record<string, TokenBalance> = {};
|
177
|
+
for (const b of balances) {
|
178
|
+
const k = `${b.chain.id}-${b.token.address.toLowerCase()}`;
|
179
|
+
if (!uniq[k]) {
|
180
|
+
uniq[k] = b;
|
181
|
+
}
|
182
|
+
}
|
183
|
+
balances.splice(0, balances.length, ...Object.values(uniq));
|
184
|
+
}
|
185
|
+
// 6. Sort so that the destination token always appears first, then tokens on the destination chain, then by chain id
|
186
|
+
balances.sort((a, b) => {
|
187
|
+
const destAddress = destinationToken.address;
|
188
|
+
if (a.chain.id === toChain.id && a.token.address === destAddress) return -1;
|
189
|
+
if (b.chain.id === toChain.id && b.token.address === destAddress) return 1;
|
190
|
+
if (a.chain.id === toChain.id) return -1;
|
191
|
+
if (b.chain.id === toChain.id) return 1;
|
192
|
+
return a.chain.id - b.chain.id;
|
193
|
+
});
|
194
|
+
|
195
|
+
return balances;
|
196
|
+
}
|
package/src/version.ts
CHANGED
@@ -1 +1 @@
|
|
1
|
-
export const version = "5.96.8-nightly-
|
1
|
+
export const version = "5.96.8-nightly-80bf795760052e83dd056cf5ef3ea54723d47775-20250503000343";
|