thirdweb 5.106.1 → 5.107.1-nightly-d53f8a25b4978a9f92f494f50955765817e31bad-20250920000329

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.
Files changed (146) hide show
  1. package/dist/cjs/bridge/Token.js +4 -1
  2. package/dist/cjs/bridge/Token.js.map +1 -1
  3. package/dist/cjs/exports/x402.js +8 -0
  4. package/dist/cjs/exports/x402.js.map +1 -0
  5. package/dist/cjs/react/core/design-system/index.js +1 -0
  6. package/dist/cjs/react/core/design-system/index.js.map +1 -1
  7. package/dist/cjs/react/web/ui/Bridge/BuyWidget.js +1 -1
  8. package/dist/cjs/react/web/ui/Bridge/CheckoutWidget.js +1 -1
  9. package/dist/cjs/react/web/ui/Bridge/TransactionWidget.js +1 -1
  10. package/dist/cjs/react/web/ui/Bridge/swap-widget/SearchInput.js +1 -1
  11. package/dist/cjs/react/web/ui/Bridge/swap-widget/SearchInput.js.map +1 -1
  12. package/dist/cjs/react/web/ui/Bridge/swap-widget/SwapWidget.js +44 -28
  13. package/dist/cjs/react/web/ui/Bridge/swap-widget/SwapWidget.js.map +1 -1
  14. package/dist/cjs/react/web/ui/Bridge/swap-widget/select-chain.js +9 -7
  15. package/dist/cjs/react/web/ui/Bridge/swap-widget/select-chain.js.map +1 -1
  16. package/dist/cjs/react/web/ui/Bridge/swap-widget/select-token-ui.js +82 -46
  17. package/dist/cjs/react/web/ui/Bridge/swap-widget/select-token-ui.js.map +1 -1
  18. package/dist/cjs/react/web/ui/Bridge/swap-widget/swap-ui.js +215 -80
  19. package/dist/cjs/react/web/ui/Bridge/swap-widget/swap-ui.js.map +1 -1
  20. package/dist/cjs/react/web/ui/Bridge/swap-widget/utils.js +6 -0
  21. package/dist/cjs/react/web/ui/Bridge/swap-widget/utils.js.map +1 -1
  22. package/dist/cjs/react/web/ui/ConnectWallet/icons/ArrowUpDownIcon.js +1 -1
  23. package/dist/cjs/react/web/ui/ConnectWallet/icons/ArrowUpDownIcon.js.map +1 -1
  24. package/dist/cjs/react/web/ui/components/buttons.js +5 -2
  25. package/dist/cjs/react/web/ui/components/buttons.js.map +1 -1
  26. package/dist/cjs/react/web/ui/components/formElements.js +4 -1
  27. package/dist/cjs/react/web/ui/components/formElements.js.map +1 -1
  28. package/dist/cjs/react/web/ui/hooks/useisMobile.js +19 -0
  29. package/dist/cjs/react/web/ui/hooks/useisMobile.js.map +1 -0
  30. package/dist/cjs/stories/Bridge/Swap/SelectChain.stories.js +16 -6
  31. package/dist/cjs/stories/Bridge/Swap/SelectChain.stories.js.map +1 -1
  32. package/dist/cjs/stories/Bridge/Swap/SwapWidget.stories.js +6 -11
  33. package/dist/cjs/stories/Bridge/Swap/SwapWidget.stories.js.map +1 -1
  34. package/dist/cjs/version.js +1 -1
  35. package/dist/cjs/version.js.map +1 -1
  36. package/dist/cjs/x402/facilitator.js +76 -0
  37. package/dist/cjs/x402/facilitator.js.map +1 -0
  38. package/dist/cjs/x402/fetchWithPayment.js +121 -0
  39. package/dist/cjs/x402/fetchWithPayment.js.map +1 -0
  40. package/dist/esm/bridge/Token.js +4 -1
  41. package/dist/esm/bridge/Token.js.map +1 -1
  42. package/dist/esm/exports/x402.js +3 -0
  43. package/dist/esm/exports/x402.js.map +1 -0
  44. package/dist/esm/react/core/design-system/index.js +1 -0
  45. package/dist/esm/react/core/design-system/index.js.map +1 -1
  46. package/dist/esm/react/web/ui/Bridge/BuyWidget.js +1 -1
  47. package/dist/esm/react/web/ui/Bridge/CheckoutWidget.js +1 -1
  48. package/dist/esm/react/web/ui/Bridge/TransactionWidget.js +1 -1
  49. package/dist/esm/react/web/ui/Bridge/swap-widget/SearchInput.js +1 -1
  50. package/dist/esm/react/web/ui/Bridge/swap-widget/SearchInput.js.map +1 -1
  51. package/dist/esm/react/web/ui/Bridge/swap-widget/SwapWidget.js +44 -28
  52. package/dist/esm/react/web/ui/Bridge/swap-widget/SwapWidget.js.map +1 -1
  53. package/dist/esm/react/web/ui/Bridge/swap-widget/select-chain.js +11 -9
  54. package/dist/esm/react/web/ui/Bridge/swap-widget/select-chain.js.map +1 -1
  55. package/dist/esm/react/web/ui/Bridge/swap-widget/select-token-ui.js +83 -47
  56. package/dist/esm/react/web/ui/Bridge/swap-widget/select-token-ui.js.map +1 -1
  57. package/dist/esm/react/web/ui/Bridge/swap-widget/swap-ui.js +218 -83
  58. package/dist/esm/react/web/ui/Bridge/swap-widget/swap-ui.js.map +1 -1
  59. package/dist/esm/react/web/ui/Bridge/swap-widget/utils.js +5 -0
  60. package/dist/esm/react/web/ui/Bridge/swap-widget/utils.js.map +1 -1
  61. package/dist/esm/react/web/ui/ConnectWallet/icons/ArrowUpDownIcon.js +2 -2
  62. package/dist/esm/react/web/ui/ConnectWallet/icons/ArrowUpDownIcon.js.map +1 -1
  63. package/dist/esm/react/web/ui/components/buttons.js +5 -2
  64. package/dist/esm/react/web/ui/components/buttons.js.map +1 -1
  65. package/dist/esm/react/web/ui/components/formElements.js +5 -2
  66. package/dist/esm/react/web/ui/components/formElements.js.map +1 -1
  67. package/dist/esm/react/web/ui/hooks/useisMobile.js +16 -0
  68. package/dist/esm/react/web/ui/hooks/useisMobile.js.map +1 -0
  69. package/dist/esm/stories/Bridge/Swap/SelectChain.stories.js +12 -4
  70. package/dist/esm/stories/Bridge/Swap/SelectChain.stories.js.map +1 -1
  71. package/dist/esm/stories/Bridge/Swap/SwapWidget.stories.js +7 -12
  72. package/dist/esm/stories/Bridge/Swap/SwapWidget.stories.js.map +1 -1
  73. package/dist/esm/version.js +1 -1
  74. package/dist/esm/version.js.map +1 -1
  75. package/dist/esm/x402/facilitator.js +73 -0
  76. package/dist/esm/x402/facilitator.js.map +1 -0
  77. package/dist/esm/x402/fetchWithPayment.js +118 -0
  78. package/dist/esm/x402/fetchWithPayment.js.map +1 -0
  79. package/dist/types/bridge/Token.d.ts +2 -0
  80. package/dist/types/bridge/Token.d.ts.map +1 -1
  81. package/dist/types/bridge/types/Token.d.ts +2 -0
  82. package/dist/types/bridge/types/Token.d.ts.map +1 -1
  83. package/dist/types/exports/x402.d.ts +3 -0
  84. package/dist/types/exports/x402.d.ts.map +1 -0
  85. package/dist/types/react/core/design-system/index.d.ts +1 -0
  86. package/dist/types/react/core/design-system/index.d.ts.map +1 -1
  87. package/dist/types/react/web/ui/Bridge/BuyWidget.d.ts +1 -1
  88. package/dist/types/react/web/ui/Bridge/CheckoutWidget.d.ts +1 -1
  89. package/dist/types/react/web/ui/Bridge/TransactionWidget.d.ts +1 -1
  90. package/dist/types/react/web/ui/Bridge/swap-widget/SearchInput.d.ts.map +1 -1
  91. package/dist/types/react/web/ui/Bridge/swap-widget/SwapWidget.d.ts +5 -0
  92. package/dist/types/react/web/ui/Bridge/swap-widget/SwapWidget.d.ts.map +1 -1
  93. package/dist/types/react/web/ui/Bridge/swap-widget/select-chain.d.ts +1 -0
  94. package/dist/types/react/web/ui/Bridge/swap-widget/select-chain.d.ts.map +1 -1
  95. package/dist/types/react/web/ui/Bridge/swap-widget/select-token-ui.d.ts.map +1 -1
  96. package/dist/types/react/web/ui/Bridge/swap-widget/swap-ui.d.ts +2 -1
  97. package/dist/types/react/web/ui/Bridge/swap-widget/swap-ui.d.ts.map +1 -1
  98. package/dist/types/react/web/ui/Bridge/swap-widget/utils.d.ts +1 -0
  99. package/dist/types/react/web/ui/Bridge/swap-widget/utils.d.ts.map +1 -1
  100. package/dist/types/react/web/ui/components/basic.d.ts +1 -1
  101. package/dist/types/react/web/ui/components/basic.d.ts.map +1 -1
  102. package/dist/types/react/web/ui/components/buttons.d.ts +1 -0
  103. package/dist/types/react/web/ui/components/buttons.d.ts.map +1 -1
  104. package/dist/types/react/web/ui/components/formElements.d.ts.map +1 -1
  105. package/dist/types/react/web/ui/hooks/useisMobile.d.ts +2 -0
  106. package/dist/types/react/web/ui/hooks/useisMobile.d.ts.map +1 -0
  107. package/dist/types/stories/Bridge/Swap/SelectChain.stories.d.ts +4 -2
  108. package/dist/types/stories/Bridge/Swap/SelectChain.stories.d.ts.map +1 -1
  109. package/dist/types/stories/Bridge/Swap/SwapWidget.stories.d.ts.map +1 -1
  110. package/dist/types/version.d.ts +1 -1
  111. package/dist/types/version.d.ts.map +1 -1
  112. package/dist/types/x402/facilitator.d.ts +48 -0
  113. package/dist/types/x402/facilitator.d.ts.map +1 -0
  114. package/dist/types/x402/fetchWithPayment.d.ts +43 -0
  115. package/dist/types/x402/fetchWithPayment.d.ts.map +1 -0
  116. package/package.json +10 -1
  117. package/src/bridge/Token.ts +6 -0
  118. package/src/bridge/types/Token.ts +2 -0
  119. package/src/exports/x402.ts +5 -0
  120. package/src/react/core/design-system/index.ts +1 -0
  121. package/src/react/web/ui/Bridge/BuyWidget.tsx +1 -1
  122. package/src/react/web/ui/Bridge/CheckoutWidget.tsx +1 -1
  123. package/src/react/web/ui/Bridge/TransactionWidget.tsx +1 -1
  124. package/src/react/web/ui/Bridge/swap-widget/SearchInput.tsx +1 -0
  125. package/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsx +62 -45
  126. package/src/react/web/ui/Bridge/swap-widget/select-chain.tsx +25 -12
  127. package/src/react/web/ui/Bridge/swap-widget/select-token-ui.tsx +302 -197
  128. package/src/react/web/ui/Bridge/swap-widget/swap-ui.tsx +496 -233
  129. package/src/react/web/ui/Bridge/swap-widget/utils.ts +6 -0
  130. package/src/react/web/ui/ConnectWallet/icons/ArrowUpDownIcon.tsx +10 -10
  131. package/src/react/web/ui/components/basic.tsx +1 -1
  132. package/src/react/web/ui/components/buttons.tsx +6 -2
  133. package/src/react/web/ui/components/formElements.tsx +5 -1
  134. package/src/react/web/ui/hooks/useisMobile.ts +21 -0
  135. package/src/stories/Bridge/Swap/SelectChain.stories.tsx +40 -2
  136. package/src/stories/Bridge/Swap/SwapWidget.stories.tsx +18 -13
  137. package/src/version.ts +1 -1
  138. package/src/x402/facilitator.ts +87 -0
  139. package/src/x402/fetchWithPayment.ts +173 -0
  140. package/dist/cjs/react/web/ui/Bridge/swap-widget/common.js +0 -17
  141. package/dist/cjs/react/web/ui/Bridge/swap-widget/common.js.map +0 -1
  142. package/dist/esm/react/web/ui/Bridge/swap-widget/common.js +0 -14
  143. package/dist/esm/react/web/ui/Bridge/swap-widget/common.js.map +0 -1
  144. package/dist/types/react/web/ui/Bridge/swap-widget/common.d.ts +0 -9
  145. package/dist/types/react/web/ui/Bridge/swap-widget/common.d.ts.map +0 -1
  146. package/src/react/web/ui/Bridge/swap-widget/common.tsx +0 -35
@@ -1,9 +1,9 @@
1
- import { DiscIcon } from "@radix-ui/react-icons";
2
1
  import { useMemo, useState } from "react";
3
2
  import type { Token } from "../../../../../bridge/index.js";
4
3
  import type { BridgeChain } from "../../../../../bridge/types/Chain.js";
5
4
  import type { ThirdwebClient } from "../../../../../client/client.js";
6
5
  import { toTokens } from "../../../../../utils/units.js";
6
+ import { useCustomTheme } from "../../../../core/design-system/CustomThemeProvider.js";
7
7
  import {
8
8
  fontSize,
9
9
  iconSize,
@@ -12,15 +12,15 @@ import {
12
12
  } from "../../../../core/design-system/index.js";
13
13
  import { CoinsIcon } from "../../ConnectWallet/icons/CoinsIcon.js";
14
14
  import { WalletDotIcon } from "../../ConnectWallet/icons/WalletDotIcon.js";
15
- import { formatTokenAmount } from "../../ConnectWallet/screens/formatTokenBalance.js";
16
- import { Container, Line, ModalHeader } from "../../components/basic.js";
15
+ import { Container, noScrollBar } from "../../components/basic.js";
17
16
  import { Button } from "../../components/buttons.js";
18
17
  import { Img } from "../../components/Img.js";
19
18
  import { Skeleton } from "../../components/Skeleton.js";
20
19
  import { Spacer } from "../../components/Spacer.js";
21
20
  import { Spinner } from "../../components/Spinner.js";
22
21
  import { Text } from "../../components/text.js";
23
- import { DecimalRenderer } from "./common.js";
22
+ import { StyledDiv } from "../../design-system/elements.js";
23
+ import { useIsMobile } from "../../hooks/useisMobile.js";
24
24
  import { SearchInput } from "./SearchInput.js";
25
25
  import { SelectChainButton } from "./SelectChainButton.js";
26
26
  import { SelectBridgeChain } from "./select-chain.js";
@@ -31,6 +31,7 @@ import {
31
31
  useTokenBalances,
32
32
  useTokens,
33
33
  } from "./use-tokens.js";
34
+ import { tokenAmountFormatter } from "./utils.js";
34
35
 
35
36
  /**
36
37
  * @internal
@@ -138,6 +139,7 @@ function SelectTokenUI(
138
139
  showMore: (() => void) | undefined;
139
140
  },
140
141
  ) {
142
+ const isMobile = useIsMobile();
141
143
  const [screen, setScreen] = useState<"select-chain" | "select-token">(
142
144
  "select-token",
143
145
  );
@@ -180,184 +182,70 @@ function SelectTokenUI(
180
182
  });
181
183
  }, [otherTokens]);
182
184
 
183
- const noTokensFound =
184
- !props.isFetching &&
185
- sortedOtherTokens.length === 0 &&
186
- props.ownedTokens.length === 0;
187
-
188
- if (screen === "select-token") {
185
+ if (!isMobile) {
189
186
  return (
190
- <Container>
191
- <Container px="md" py="md+">
192
- <ModalHeader onBack={props.onBack} title="Select Token" />
193
- </Container>
194
- <Line />
195
-
196
- {!props.selectedChain && (
197
- <div
198
- style={{
199
- display: "flex",
200
- alignItems: "center",
201
- justifyContent: "center",
202
- height: "400px",
187
+ <Container
188
+ style={{
189
+ display: "grid",
190
+ gridTemplateColumns: "300px 1fr",
191
+ height: "100%",
192
+ }}
193
+ >
194
+ <LeftContainer>
195
+ <SelectBridgeChain
196
+ onBack={() => setScreen("select-token")}
197
+ client={props.client}
198
+ isMobile={false}
199
+ onSelectChain={(chain) => {
200
+ props.setSelectedChain(chain);
201
+ setScreen("select-token");
203
202
  }}
204
- >
205
- <Spinner color="secondaryText" size="xl" />
206
- </div>
207
- )}
208
-
209
- {props.selectedChain && (
210
- <>
211
- <Container p="md">
212
- <SelectChainButton
213
- onClick={() => setScreen("select-chain")}
214
- selectedChain={props.selectedChain}
215
- client={props.client}
216
- />
217
- </Container>
218
-
219
- {/* search */}
220
- <Container px="md">
221
- <SearchInput
222
- value={props.search}
223
- onChange={props.setSearch}
224
- placeholder="Search by Token or Address"
225
- />
226
- </Container>
227
-
228
- <Spacer y="sm" />
229
- <Container px="md">
230
- <Container
231
- flex="column"
232
- style={{
233
- height: "400px",
234
- overflowY: "auto",
235
- scrollbarWidth: "none",
236
- paddingBottom: spacing.md,
237
- }}
238
- >
239
- {props.isFetching &&
240
- new Array(20).fill(0).map((_, i) => (
241
- // biome-ignore lint/suspicious/noArrayIndexKey: ok
242
- <TokenButtonSkeleton key={i} />
243
- ))}
244
-
245
- {!props.isFetching && sortedOwnedTokens.length > 0 && (
246
- <Container
247
- px="xs"
248
- py="xs"
249
- flex="row"
250
- gap="xs"
251
- center="y"
252
- color="secondaryText"
253
- >
254
- <WalletDotIcon size={iconSize.sm} />
255
- <Text
256
- size="sm"
257
- color="secondaryText"
258
- weight={500}
259
- style={{
260
- overflow: "unset",
261
- }}
262
- >
263
- Your Tokens
264
- </Text>
265
- </Container>
266
- )}
267
-
268
- {!props.isFetching &&
269
- sortedOwnedTokens.map((token) => (
270
- <TokenButton
271
- key={token.token_address}
272
- token={token}
273
- client={props.client}
274
- onSelect={props.setSelectedToken}
275
- isSelected={
276
- !!props.selectedToken &&
277
- props.selectedToken.tokenAddress.toLowerCase() ===
278
- token.token_address.toLowerCase() &&
279
- token.chain_id === props.selectedToken.chainId
280
- }
281
- />
282
- ))}
283
-
284
- {!props.isFetching && sortedOwnedTokens.length > 0 && (
285
- <Container
286
- px="xs"
287
- py="xs"
288
- flex="row"
289
- gap="xs"
290
- center="y"
291
- color="secondaryText"
292
- style={{
293
- marginTop: spacing.sm,
294
- }}
295
- >
296
- <CoinsIcon size={iconSize.sm} />
297
- <Text
298
- size="sm"
299
- color="secondaryText"
300
- weight={500}
301
- style={{
302
- overflow: "unset",
303
- }}
304
- >
305
- Other Tokens
306
- </Text>
307
- </Container>
308
- )}
309
-
310
- {!props.isFetching &&
311
- sortedOtherTokens.map((token) => (
312
- <TokenButton
313
- key={token.address}
314
- token={token}
315
- client={props.client}
316
- onSelect={props.setSelectedToken}
317
- isSelected={
318
- props.selectedToken?.tokenAddress.toLowerCase() ===
319
- token.address.toLowerCase()
320
- }
321
- />
322
- ))}
323
-
324
- {props.showMore && (
325
- <Button
326
- variant="secondary"
327
- fullWidth
328
- onClick={() => {
329
- props.showMore?.();
330
- }}
331
- >
332
- Load More
333
- </Button>
334
- )}
335
-
336
- {noTokensFound && (
337
- <div
338
- style={{
339
- flex: 1,
340
- display: "flex",
341
- alignItems: "center",
342
- justifyContent: "center",
343
- }}
344
- >
345
- <Text size="md" color="secondaryText">
346
- No Tokens Found
347
- </Text>
348
- </div>
349
- )}
350
- </Container>
351
- </Container>
352
- </>
353
- )}
203
+ selectedChain={props.selectedChain}
204
+ />
205
+ </LeftContainer>
206
+ <Container flex="column" relative scrollY>
207
+ <TokenSelectionScreen
208
+ onSelectToken={props.setSelectedToken}
209
+ isMobile={false}
210
+ selectedToken={props.selectedToken}
211
+ isFetching={props.isFetching}
212
+ ownedTokens={props.ownedTokens}
213
+ otherTokens={sortedOtherTokens}
214
+ showMore={props.showMore}
215
+ selectedChain={props.selectedChain}
216
+ onSelectChain={() => setScreen("select-chain")}
217
+ client={props.client}
218
+ search={props.search}
219
+ setSearch={props.setSearch}
220
+ />
221
+ </Container>
354
222
  </Container>
355
223
  );
356
224
  }
357
225
 
226
+ if (screen === "select-token") {
227
+ return (
228
+ <TokenSelectionScreen
229
+ onSelectToken={props.setSelectedToken}
230
+ selectedToken={props.selectedToken}
231
+ isFetching={props.isFetching}
232
+ ownedTokens={props.ownedTokens}
233
+ otherTokens={sortedOtherTokens}
234
+ showMore={props.showMore}
235
+ selectedChain={props.selectedChain}
236
+ isMobile={true}
237
+ onSelectChain={() => setScreen("select-chain")}
238
+ client={props.client}
239
+ search={props.search}
240
+ setSearch={props.setSearch}
241
+ />
242
+ );
243
+ }
244
+
358
245
  if (screen === "select-chain") {
359
246
  return (
360
247
  <SelectBridgeChain
248
+ isMobile={true}
361
249
  onBack={() => setScreen("select-token")}
362
250
  client={props.client}
363
251
  onSelectChain={(chain) => {
@@ -379,7 +267,7 @@ function TokenButtonSkeleton() {
379
267
  display: "flex",
380
268
  alignItems: "center",
381
269
  gap: spacing.sm,
382
- padding: `${spacing.sm} ${spacing.sm}`,
270
+ padding: `${spacing.xs} ${spacing.xs}`,
383
271
  height: "70px",
384
272
  }}
385
273
  >
@@ -398,6 +286,7 @@ function TokenButton(props: {
398
286
  onSelect: (tokenWithPrices: TokenSelection) => void;
399
287
  isSelected: boolean;
400
288
  }) {
289
+ const theme = useCustomTheme();
401
290
  const tokenBalanceInUnits =
402
291
  "balance" in props.token
403
292
  ? toTokens(BigInt(props.token.balance), props.token.decimals)
@@ -416,7 +305,7 @@ function TokenButton(props: {
416
305
  fontWeight: 500,
417
306
  fontSize: fontSize.md,
418
307
  border: "1px solid transparent",
419
- padding: `${spacing.sm} ${spacing.xs}`,
308
+ padding: `${spacing.xs} ${spacing.xs}`,
420
309
  textAlign: "left",
421
310
  lineHeight: "1.5",
422
311
  borderRadius: radius.lg,
@@ -451,7 +340,14 @@ function TokenButton(props: {
451
340
  }}
452
341
  fallback={
453
342
  <Container color="secondaryText">
454
- <DiscIcon width={iconSize.lg} height={iconSize.lg} />
343
+ <Container
344
+ style={{
345
+ background: `linear-gradient(45deg, white, ${theme.colors.accentText})`,
346
+ borderRadius: radius.full,
347
+ width: `${iconSize.lg}px`,
348
+ height: `${iconSize.lg}px`,
349
+ }}
350
+ />
455
351
  </Container>
456
352
  }
457
353
  />
@@ -473,18 +369,15 @@ function TokenButton(props: {
473
369
  <Text size="md" color="primaryText" weight={500}>
474
370
  {props.token.symbol}
475
371
  </Text>
372
+
476
373
  {"balance" in props.token && (
477
- <DecimalRenderer
478
- integerSize="md"
479
- fractionSize="sm"
480
- value={formatTokenAmount(
481
- BigInt(props.token.balance),
482
- props.token.decimals,
483
- 3,
374
+ <Text size="md" color="primaryText">
375
+ {tokenAmountFormatter.format(
376
+ Number(
377
+ toTokens(BigInt(props.token.balance), props.token.decimals),
378
+ ),
484
379
  )}
485
- color="primaryText"
486
- weight={500}
487
- />
380
+ </Text>
488
381
  )}
489
382
  </Container>
490
383
  <Container
@@ -495,9 +388,8 @@ function TokenButton(props: {
495
388
  }}
496
389
  >
497
390
  <Text
498
- size="sm"
391
+ size="xs"
499
392
  color="secondaryText"
500
- weight={400}
501
393
  style={{
502
394
  overflow: "hidden",
503
395
  textOverflow: "ellipsis",
@@ -509,16 +401,9 @@ function TokenButton(props: {
509
401
  </Text>
510
402
  {usdValue && (
511
403
  <Container flex="row">
512
- <Text size="sm" color="secondaryText" weight={400}>
513
- $
404
+ <Text size="xs" color="secondaryText" weight={400}>
405
+ ${usdValue.toFixed(2)}
514
406
  </Text>
515
- <DecimalRenderer
516
- value={usdValue.toFixed(2)}
517
- color="secondaryText"
518
- weight={500}
519
- integerSize="sm"
520
- fractionSize="xs"
521
- />
522
407
  </Container>
523
408
  )}
524
409
  </Container>
@@ -526,3 +411,223 @@ function TokenButton(props: {
526
411
  </Button>
527
412
  );
528
413
  }
414
+
415
+ function TokenSelectionScreen(props: {
416
+ selectedChain: BridgeChain | undefined;
417
+ isMobile: boolean;
418
+ onSelectChain: () => void;
419
+ client: ThirdwebClient;
420
+ search: string;
421
+ setSearch: (search: string) => void;
422
+ isFetching: boolean;
423
+ ownedTokens: TokenBalance[];
424
+ otherTokens: Token[];
425
+ showMore: (() => void) | undefined;
426
+ selectedToken: TokenSelection | undefined;
427
+ onSelectToken: (token: TokenSelection) => void;
428
+ }) {
429
+ const noTokensFound =
430
+ !props.isFetching &&
431
+ props.otherTokens.length === 0 &&
432
+ props.ownedTokens.length === 0;
433
+
434
+ return (
435
+ <Container fullHeight flex="column">
436
+ <Container px="md" pt="md+">
437
+ <Text size="lg" weight={600} color="primaryText" trackingTight>
438
+ Select Token
439
+ </Text>
440
+ <Spacer y="3xs" />
441
+ <Text
442
+ size="xs"
443
+ color="secondaryText"
444
+ multiline
445
+ style={{
446
+ textWrap: "pretty",
447
+ maxWidth: "70%",
448
+ }}
449
+ >
450
+ Select a token from the list or search for a token by symbol or
451
+ address
452
+ </Text>
453
+ </Container>
454
+
455
+ {!props.selectedChain && (
456
+ <div
457
+ style={{
458
+ display: "flex",
459
+ alignItems: "center",
460
+ justifyContent: "center",
461
+ minHeight: "400px",
462
+ }}
463
+ >
464
+ <Spinner color="secondaryText" size="xl" />
465
+ </div>
466
+ )}
467
+
468
+ {props.selectedChain && (
469
+ <>
470
+ {props.isMobile ? (
471
+ <Container p="md">
472
+ <SelectChainButton
473
+ onClick={props.onSelectChain}
474
+ selectedChain={props.selectedChain}
475
+ client={props.client}
476
+ />
477
+ </Container>
478
+ ) : (
479
+ <Spacer y="md" />
480
+ )}
481
+
482
+ {/* search */}
483
+ <Container px="md">
484
+ <SearchInput
485
+ value={props.search}
486
+ onChange={props.setSearch}
487
+ placeholder="Search by token or address"
488
+ />
489
+ </Container>
490
+
491
+ <Spacer y="xs" />
492
+
493
+ <Container
494
+ pb="md"
495
+ px="md"
496
+ expand
497
+ gap="xxs"
498
+ flex="column"
499
+ style={{
500
+ minHeight: "400px",
501
+ maxHeight: props.isMobile ? "450px" : "none",
502
+ overflowY: "auto",
503
+ scrollbarWidth: "none",
504
+ paddingBottom: spacing.md,
505
+ }}
506
+ >
507
+ {props.isFetching &&
508
+ new Array(20).fill(0).map((_, i) => (
509
+ // biome-ignore lint/suspicious/noArrayIndexKey: ok
510
+ <TokenButtonSkeleton key={i} />
511
+ ))}
512
+
513
+ {!props.isFetching && props.ownedTokens.length > 0 && (
514
+ <Container
515
+ px="xs"
516
+ py="xs"
517
+ flex="row"
518
+ gap="xs"
519
+ center="y"
520
+ color="secondaryText"
521
+ >
522
+ <WalletDotIcon size={iconSize.xs} />
523
+ <Text
524
+ size="sm"
525
+ color="secondaryText"
526
+ style={{
527
+ overflow: "unset",
528
+ }}
529
+ >
530
+ Your Tokens
531
+ </Text>
532
+ </Container>
533
+ )}
534
+
535
+ {!props.isFetching &&
536
+ props.ownedTokens.map((token) => (
537
+ <TokenButton
538
+ key={token.token_address}
539
+ token={token}
540
+ client={props.client}
541
+ onSelect={props.onSelectToken}
542
+ isSelected={
543
+ !!props.selectedToken &&
544
+ props.selectedToken.tokenAddress.toLowerCase() ===
545
+ token.token_address.toLowerCase() &&
546
+ token.chain_id === props.selectedToken.chainId
547
+ }
548
+ />
549
+ ))}
550
+
551
+ {!props.isFetching && props.ownedTokens.length > 0 && (
552
+ <Container
553
+ px="xs"
554
+ py="xs"
555
+ flex="row"
556
+ gap="xs"
557
+ center="y"
558
+ color="secondaryText"
559
+ style={{
560
+ marginTop: spacing.sm,
561
+ }}
562
+ >
563
+ <CoinsIcon size={iconSize.xs} />
564
+ <Text
565
+ size="sm"
566
+ color="secondaryText"
567
+ style={{
568
+ overflow: "unset",
569
+ }}
570
+ >
571
+ Other Tokens
572
+ </Text>
573
+ </Container>
574
+ )}
575
+
576
+ {!props.isFetching &&
577
+ props.otherTokens.map((token) => (
578
+ <TokenButton
579
+ key={token.address}
580
+ token={token}
581
+ client={props.client}
582
+ onSelect={props.onSelectToken}
583
+ isSelected={
584
+ props.selectedToken?.tokenAddress.toLowerCase() ===
585
+ token.address.toLowerCase()
586
+ }
587
+ />
588
+ ))}
589
+
590
+ {props.showMore && (
591
+ <Button
592
+ variant="secondary"
593
+ fullWidth
594
+ onClick={() => {
595
+ props.showMore?.();
596
+ }}
597
+ >
598
+ Load More
599
+ </Button>
600
+ )}
601
+
602
+ {noTokensFound && (
603
+ <div
604
+ style={{
605
+ flex: 1,
606
+ display: "flex",
607
+ alignItems: "center",
608
+ justifyContent: "center",
609
+ }}
610
+ >
611
+ <Text size="md" color="secondaryText">
612
+ No Tokens Found
613
+ </Text>
614
+ </div>
615
+ )}
616
+ </Container>
617
+ </>
618
+ )}
619
+ </Container>
620
+ );
621
+ }
622
+
623
+ const LeftContainer = /* @__PURE__ */ StyledDiv((_) => {
624
+ const theme = useCustomTheme();
625
+ return {
626
+ display: "flex",
627
+ flexDirection: "column",
628
+ overflowY: "auto",
629
+ ...noScrollBar,
630
+ borderRight: `1px solid ${theme.colors.separatorLine}`,
631
+ position: "relative",
632
+ };
633
+ });