@0xsquid/deposit-widget 0.1.0-beta.0

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 (122) hide show
  1. package/dist/cjs/index.cjs +4062 -0
  2. package/dist/cjs/index.cjs.map +1 -0
  3. package/dist/index.d.ts +52 -0
  4. package/dist/index.esm.js +4042 -0
  5. package/dist/index.esm.js.map +1 -0
  6. package/dist/types/DepositWidget.d.ts +7 -0
  7. package/dist/types/components/ViewTransition.d.ts +7 -0
  8. package/dist/types/components/shared/buttons/button.d.ts +3 -0
  9. package/dist/types/components/shared/icons/types.d.ts +3 -0
  10. package/dist/types/components/shared/icons/user-round.d.ts +2 -0
  11. package/dist/types/components/shared/navigation/base-navbar.d.ts +3 -0
  12. package/dist/types/components/shared/navigation/sub-navbar.d.ts +6 -0
  13. package/dist/types/components/token-badge-icon.d.ts +6 -0
  14. package/dist/types/components/token-list-item.d.ts +13 -0
  15. package/dist/types/components/view-container.d.ts +1 -0
  16. package/dist/types/constants.d.ts +1 -0
  17. package/dist/types/hooks/ui/useMainCTAButtonState.d.ts +8 -0
  18. package/dist/types/hooks/use-auto-select-token.d.ts +1 -0
  19. package/dist/types/hooks/use-deposit-route.d.ts +7 -0
  20. package/dist/types/hooks/use-token-selection.d.ts +5 -0
  21. package/dist/types/hooks/use-transaction-history.d.ts +6 -0
  22. package/dist/types/index.d.ts +3 -0
  23. package/dist/types/services/assets-service.d.ts +6 -0
  24. package/dist/types/services/wallet-history/format.d.ts +10 -0
  25. package/dist/types/services/wallet-history/format.test.d.ts +1 -0
  26. package/dist/types/services/wallet-history/get-main-explorer-url.d.ts +23 -0
  27. package/dist/types/services/wallet-history/get-wallet-history.d.ts +2 -0
  28. package/dist/types/services/wallet-history/types.d.ts +66 -0
  29. package/dist/types/services/wallet-history/validation.d.ts +2 -0
  30. package/dist/types/store/use-deposit-store.d.ts +9 -0
  31. package/dist/types/store/use-input-mode.d.ts +5 -0
  32. package/dist/types/store/useRouter.d.ts +12 -0
  33. package/dist/types/types.d.ts +43 -0
  34. package/dist/types/utils/format-date.d.ts +2 -0
  35. package/dist/types/utils/format-date.test.d.ts +1 -0
  36. package/dist/types/utils/transaction.d.ts +10 -0
  37. package/dist/types/views/connect-wallet/connect-wallet-view.d.ts +6 -0
  38. package/dist/types/views/connect-wallet/wallet-list-item.d.ts +9 -0
  39. package/dist/types/views/main/amount-input.d.ts +10 -0
  40. package/dist/types/views/main/connect-prompt.d.ts +1 -0
  41. package/dist/types/views/main/deposit-amount-input.d.ts +1 -0
  42. package/dist/types/views/main/deposit-form.d.ts +1 -0
  43. package/dist/types/views/main/main-cta-button.d.ts +1 -0
  44. package/dist/types/views/main/main-view.d.ts +1 -0
  45. package/dist/types/views/main/navbar/actions.d.ts +1 -0
  46. package/dist/types/views/main/navbar/icon.d.ts +1 -0
  47. package/dist/types/views/main/navbar/navbar.d.ts +1 -0
  48. package/dist/types/views/main/navbar/title.d.ts +10 -0
  49. package/dist/types/views/main/recipient/account.d.ts +14 -0
  50. package/dist/types/views/main/recipient/recipient.d.ts +1 -0
  51. package/dist/types/views/main/token-selector.d.ts +1 -0
  52. package/dist/types/views/qr-code.d.ts +1 -0
  53. package/dist/types/views/render-view.d.ts +3 -0
  54. package/dist/types/views/select-chain/chain-type-meta.d.ts +7 -0
  55. package/dist/types/views/select-chain/select-chain-view.d.ts +6 -0
  56. package/dist/types/views/select-token.d.ts +1 -0
  57. package/dist/types/views/transaction-history/activity-list-item.d.ts +4 -0
  58. package/dist/types/views/transaction-history/transaction-history-view.d.ts +1 -0
  59. package/dist/types/views/transaction-progress/helpers.d.ts +14 -0
  60. package/dist/types/views/transaction-progress/transaction-progress-view.d.ts +1 -0
  61. package/dist/types/views/transaction-progress/use-transaction-progress.d.ts +17 -0
  62. package/package.json +99 -0
  63. package/src/DepositWidget.tsx +158 -0
  64. package/src/compiled-tailwind.css +6100 -0
  65. package/src/components/ViewTransition.tsx +81 -0
  66. package/src/components/shared/buttons/button.tsx +17 -0
  67. package/src/components/shared/icons/types.ts +3 -0
  68. package/src/components/shared/icons/user-round.tsx +21 -0
  69. package/src/components/shared/navigation/base-navbar.tsx +15 -0
  70. package/src/components/shared/navigation/sub-navbar.tsx +46 -0
  71. package/src/components/token-badge-icon.tsx +31 -0
  72. package/src/components/token-list-item.tsx +84 -0
  73. package/src/components/view-container.tsx +16 -0
  74. package/src/constants.ts +1 -0
  75. package/src/css.d.ts +4 -0
  76. package/src/fonts/DMSans-Variable.woff2 +0 -0
  77. package/src/hooks/ui/useMainCTAButtonState.ts +143 -0
  78. package/src/hooks/use-auto-select-token.ts +65 -0
  79. package/src/hooks/use-deposit-route.ts +58 -0
  80. package/src/hooks/use-token-selection.ts +17 -0
  81. package/src/hooks/use-transaction-history.ts +198 -0
  82. package/src/index.ts +3 -0
  83. package/src/services/assets-service.ts +21 -0
  84. package/src/services/wallet-history/format.test.ts +63 -0
  85. package/src/services/wallet-history/format.ts +128 -0
  86. package/src/services/wallet-history/get-main-explorer-url.ts +74 -0
  87. package/src/services/wallet-history/get-wallet-history.ts +24 -0
  88. package/src/services/wallet-history/types.ts +66 -0
  89. package/src/services/wallet-history/validation.ts +60 -0
  90. package/src/store/use-deposit-store.ts +20 -0
  91. package/src/store/use-input-mode.ts +10 -0
  92. package/src/store/useRouter.ts +49 -0
  93. package/src/tailwind.css +16 -0
  94. package/src/types.ts +39 -0
  95. package/src/utils/format-date.test.ts +32 -0
  96. package/src/utils/format-date.ts +25 -0
  97. package/src/utils/transaction.ts +39 -0
  98. package/src/views/connect-wallet/connect-wallet-view.tsx +147 -0
  99. package/src/views/connect-wallet/wallet-list-item.tsx +69 -0
  100. package/src/views/main/amount-input.tsx +272 -0
  101. package/src/views/main/connect-prompt.tsx +47 -0
  102. package/src/views/main/deposit-amount-input.tsx +42 -0
  103. package/src/views/main/deposit-form.tsx +13 -0
  104. package/src/views/main/main-cta-button.tsx +14 -0
  105. package/src/views/main/main-view.tsx +24 -0
  106. package/src/views/main/navbar/actions.tsx +25 -0
  107. package/src/views/main/navbar/icon.tsx +11 -0
  108. package/src/views/main/navbar/navbar.tsx +16 -0
  109. package/src/views/main/navbar/title.tsx +64 -0
  110. package/src/views/main/recipient/account.tsx +81 -0
  111. package/src/views/main/recipient/recipient.tsx +64 -0
  112. package/src/views/main/token-selector.tsx +77 -0
  113. package/src/views/qr-code.tsx +14 -0
  114. package/src/views/render-view.tsx +28 -0
  115. package/src/views/select-chain/chain-type-meta.ts +37 -0
  116. package/src/views/select-chain/select-chain-view.tsx +97 -0
  117. package/src/views/select-token.tsx +227 -0
  118. package/src/views/transaction-history/activity-list-item.tsx +87 -0
  119. package/src/views/transaction-history/transaction-history-view.tsx +58 -0
  120. package/src/views/transaction-progress/helpers.tsx +93 -0
  121. package/src/views/transaction-progress/transaction-progress-view.tsx +217 -0
  122. package/src/views/transaction-progress/use-transaction-progress.ts +112 -0
@@ -0,0 +1,47 @@
1
+ import { useSwap } from "@0xsquid/react-hooks";
2
+ import {
3
+ useDepositStore,
4
+ useIsPaymentMode,
5
+ usePaymentAmount,
6
+ } from "../../store/use-deposit-store";
7
+ import { buildTitleLabel } from "./navbar/title";
8
+
9
+ export function ConnectPrompt() {
10
+ const isPaymentMode = useIsPaymentMode();
11
+ const logoUrl = useDepositStore((s) => s.config?.integrator.logoUrl);
12
+ const name = useDepositStore((s) => s.config?.integrator.name);
13
+ const paymentAmount = usePaymentAmount();
14
+ const { toToken } = useSwap();
15
+
16
+ const label = buildTitleLabel({
17
+ isPaymentMode,
18
+ paymentAmount,
19
+ tokenSymbol: toToken?.symbol,
20
+ tokenUsdPrice: toToken?.usdPrice,
21
+ name,
22
+ });
23
+ const purposeCopy = `To ${isPaymentMode ? "pay" : "deposit crypto"} you'll need to connect your wallet first.`;
24
+
25
+ return (
26
+ <div className="tw-flex tw-pt-3 tw-pb-8 tw-px-4 tw-flex-col tw-justify-end tw-items-center tw-gap-4 tw-flex-1 tw-self-stretch tw-relative">
27
+ <div className="tw-w-32">
28
+ {logoUrl && <img className="tw-rounded-full tw-w-full" src={logoUrl} />}
29
+ </div>
30
+
31
+ <div className="tw-flex tw-pt-2 tw-pb-4 tw-flex-col tw-justify-end tw-items-center tw-gap-4 tw-self-center">
32
+ <span className="tw-text-grey-100 tw-font-medium tw-text-2xl tw-leading-3">
33
+ {label}
34
+ </span>
35
+
36
+ <span className="tw-text-center tw-text-material-light-average tw-font-medium tw-leading-6 tw-text-lg tw-text-balance">
37
+ {purposeCopy}
38
+ </span>
39
+
40
+ {/* TODO: add link? remove? */}
41
+ <span className="tw-text-material-light-thick tw-font-medium tw-leading-3 tw-text-lg">
42
+ Learn more
43
+ </span>
44
+ </div>
45
+ </div>
46
+ );
47
+ }
@@ -0,0 +1,42 @@
1
+ import { convertTokenAmountToUSD, useSwap } from "@0xsquid/react-hooks";
2
+ import { useDepositRoute } from "../../hooks/use-deposit-route";
3
+ import { useTokenSelection } from "../../hooks/use-token-selection";
4
+ import {
5
+ useIsPaymentMode,
6
+ usePaymentAmount,
7
+ } from "../../store/use-deposit-store";
8
+ import { AmountInput } from "./amount-input";
9
+
10
+ export function DepositAmountInput() {
11
+ const { token, balance } = useTokenSelection();
12
+ const { fromAmount, fromAmountChanged, toToken } = useSwap();
13
+ const isPaymentMode = useIsPaymentMode();
14
+ const paymentAmount = usePaymentAmount();
15
+ const { quotedSourceAmount, routeErrorMessage, showLoading } =
16
+ useDepositRoute();
17
+
18
+ const displayedAmount = isPaymentMode ? quotedSourceAmount : fromAmount;
19
+
20
+ const forcedUsdDisplay =
21
+ isPaymentMode && paymentAmount && toToken?.usdPrice
22
+ ? convertTokenAmountToUSD(paymentAmount, toToken.usdPrice)
23
+ : undefined;
24
+
25
+ return (
26
+ <AmountInput
27
+ onAmountChange={fromAmountChanged}
28
+ balance={balance}
29
+ forcedAmount={displayedAmount}
30
+ forcedUsdDisplay={forcedUsdDisplay}
31
+ allowMaxButton={!isPaymentMode}
32
+ readOnly={isPaymentMode}
33
+ loading={isPaymentMode && showLoading}
34
+ errorMessage={routeErrorMessage}
35
+ token={{
36
+ decimals: token?.decimals ?? 0,
37
+ symbol: token?.symbol ?? "",
38
+ price: token?.usdPrice ?? 0,
39
+ }}
40
+ />
41
+ );
42
+ }
@@ -0,0 +1,13 @@
1
+ import { DepositAmountInput } from "./deposit-amount-input";
2
+ import { Recipient } from "./recipient/recipient";
3
+ import { TokenSelector } from "./token-selector";
4
+
5
+ export function DepositForm() {
6
+ return (
7
+ <div className="tw-flex tw-pb-3 tw-pt-1 tw-flex-col tw-items-center tw-flex-1 tw-self-stretch tw-gap-2 tw-px-1">
8
+ <TokenSelector />
9
+ <DepositAmountInput />
10
+ <Recipient />
11
+ </div>
12
+ );
13
+ }
@@ -0,0 +1,14 @@
1
+ import { Button } from "../../components/shared/buttons/button";
2
+ import { useMainCTAButtonState } from "../../hooks/ui/useMainCTAButtonState";
3
+
4
+ export function MainCTAButton() {
5
+ const { label, onClick, disabled } = useMainCTAButtonState();
6
+
7
+ return (
8
+ <footer className="tw-flex tw-h-16 tw-p-2 tw-items-start tw-shrink-0 tw-self-stretch">
9
+ <Button onClick={onClick} disabled={disabled}>
10
+ {label}
11
+ </Button>
12
+ </footer>
13
+ );
14
+ }
@@ -0,0 +1,24 @@
1
+ import { cn } from "@0xsquid/ui";
2
+ import { useWallet } from "@0xsquid/react-hooks";
3
+ import { ConnectPrompt } from "./connect-prompt";
4
+ import { DepositForm } from "./deposit-form";
5
+ import { MainCTAButton } from "./main-cta-button";
6
+ import { NavBar } from "./navbar/navbar";
7
+ import { ViewContainer } from "../../components/view-container";
8
+
9
+ export function MainView() {
10
+ const { connectedWallets } = useWallet();
11
+ const isConnected = connectedWallets.length > 0;
12
+
13
+ return (
14
+ <>
15
+ {isConnected && <NavBar />}
16
+
17
+ <ViewContainer className={cn(!isConnected && "tw-border-t-0")}>
18
+ {isConnected ? <DepositForm /> : <ConnectPrompt />}
19
+
20
+ <MainCTAButton />
21
+ </ViewContainer>
22
+ </>
23
+ );
24
+ }
@@ -0,0 +1,25 @@
1
+ import { ClockOutlineIcon, IconButton } from "@0xsquid/ui";
2
+ import { useWallet } from "@0xsquid/react-hooks";
3
+ import { useRouter } from "../../../store/useRouter";
4
+
5
+ export function Actions() {
6
+ const push = useRouter((s) => s.push);
7
+ const { connectedWallets } = useWallet();
8
+ const isConnected = connectedWallets.length > 0;
9
+
10
+ if (!isConnected) return null;
11
+
12
+ return (
13
+ <div className="tw-flex tw-justify-end tw-items-center tw-gap-1">
14
+ <IconButton
15
+ onClick={() => push({ id: "transaction-history" })}
16
+ icon={
17
+ <ClockOutlineIcon
18
+ className="tw-text-material-light-thick"
19
+ size="1.5rem"
20
+ />
21
+ }
22
+ />
23
+ </div>
24
+ );
25
+ }
@@ -0,0 +1,11 @@
1
+ import { useDepositStore } from "../../../store/use-deposit-store";
2
+
3
+ export function Icon() {
4
+ const logoUrl = useDepositStore((s) => s.config?.integrator.logoUrl);
5
+
6
+ return (
7
+ <div className="tw-flex tw-size-10 tw-justify-center tw-items-center tw-rounded-squid-m tw-shadow-icon-light dark:tw-shadow-icon-dark tw-bg-grey-900">
8
+ {logoUrl && <img className="tw-rounded-full" src={logoUrl} />}
9
+ </div>
10
+ );
11
+ }
@@ -0,0 +1,16 @@
1
+ import { Actions } from "./actions";
2
+ import { BaseNavBar } from "../../../components/shared/navigation/base-navbar";
3
+ import { Icon } from "./icon";
4
+ import { Title } from "./title";
5
+
6
+ export function NavBar() {
7
+ return (
8
+ <BaseNavBar className="tw-justify-between">
9
+ <Icon />
10
+
11
+ <Title />
12
+
13
+ <Actions />
14
+ </BaseNavBar>
15
+ );
16
+ }
@@ -0,0 +1,64 @@
1
+ import {
2
+ convertTokenAmountToUSD,
3
+ formatUsdAmount,
4
+ useSwap,
5
+ } from "@0xsquid/react-hooks";
6
+ import {
7
+ useDepositStore,
8
+ useIsPaymentMode,
9
+ usePaymentAmount,
10
+ } from "../../../store/use-deposit-store";
11
+
12
+ export function Title() {
13
+ const name = useDepositStore((s) => s.config?.integrator.name);
14
+ const isPaymentMode = useIsPaymentMode();
15
+ const paymentAmount = usePaymentAmount();
16
+ const { toToken } = useSwap();
17
+
18
+ const label = buildTitleLabel({
19
+ isPaymentMode,
20
+ paymentAmount,
21
+ tokenSymbol: toToken?.symbol,
22
+ tokenUsdPrice: toToken?.usdPrice,
23
+ name,
24
+ });
25
+
26
+ return (
27
+ <div className="tw-flex tw-py-1 tw-px-2 tw-items-center tw-gap-1 tw-flex-1 tw-self-stretch">
28
+ <span className="tw-text-grey-100 tw-text-lg tw-font-squid-main tw-font-medium">
29
+ {label}
30
+ </span>
31
+ </div>
32
+ );
33
+ }
34
+
35
+ interface TitleLabelInput {
36
+ isPaymentMode: boolean;
37
+ paymentAmount: string | null;
38
+ tokenSymbol: string | undefined;
39
+ tokenUsdPrice: number | undefined;
40
+ name: string | undefined;
41
+ }
42
+
43
+ export function buildTitleLabel({
44
+ isPaymentMode,
45
+ paymentAmount,
46
+ tokenSymbol,
47
+ tokenUsdPrice,
48
+ name,
49
+ }: TitleLabelInput): string {
50
+ if (!isPaymentMode) return name ? `Deposit to ${name}` : "Deposit";
51
+
52
+ if (paymentAmount && tokenSymbol) {
53
+ const usdValue = tokenUsdPrice
54
+ ? convertTokenAmountToUSD(paymentAmount, tokenUsdPrice)
55
+ : null;
56
+
57
+ const amount = usdValue
58
+ ? formatUsdAmount(usdValue)
59
+ : `${paymentAmount} ${tokenSymbol}`;
60
+ return name ? `Pay ${amount} to ${name}` : `Pay ${amount}`;
61
+ }
62
+
63
+ return name ? `Pay ${name}` : "Pay";
64
+ }
@@ -0,0 +1,81 @@
1
+ import { cn, PowerIcon } from "@0xsquid/ui";
2
+ import { UserRoundIcon } from "../../../components/shared/icons/user-round";
3
+
4
+ interface AccountProps {
5
+ direction: "from" | "to";
6
+ name: string;
7
+ address: string;
8
+ icon?: string;
9
+ onDisconnect?: () => void;
10
+ }
11
+
12
+ export function Account({
13
+ direction,
14
+ address,
15
+ name,
16
+ icon,
17
+ onDisconnect,
18
+ }: AccountProps) {
19
+ return (
20
+ <>
21
+ {direction === "from" && (
22
+ <Avatar icon={icon} onDisconnect={onDisconnect} />
23
+ )}
24
+
25
+ <div
26
+ className={cn(
27
+ "tw-flex tw-flex-col tw-justify-center tw-gap-2 tw-flex-1 tw-self-stretch",
28
+
29
+ direction === "from" ? "tw-items-start" : "tw-items-end",
30
+ )}
31
+ >
32
+ <span className="tw-text-grey-100 tw-text-lg tw-leading-3">{name}</span>
33
+
34
+ <span className="tw-text-material-light-thick tw-text-sm tw-leading-3 tw-whitespace-nowrap">
35
+ {address}
36
+ </span>
37
+ </div>
38
+
39
+ {direction === "to" && <Avatar icon={icon} />}
40
+ </>
41
+ );
42
+ }
43
+
44
+ interface AvatarProps {
45
+ icon?: string;
46
+ onDisconnect?: () => void;
47
+ }
48
+
49
+ export function Avatar({ icon, onDisconnect }: AvatarProps) {
50
+ const Wrapper = onDisconnect ? "button" : "div";
51
+
52
+ return (
53
+ <Wrapper
54
+ onClick={onDisconnect}
55
+ className={cn(
56
+ "tw-group/avatar tw-relative tw-flex tw-size-squid-xl tw-justify-center tw-items-center tw-aspect-square tw-rounded-full tw-shadow-icon-light dark:tw-shadow-icon-dark tw-transition-colors tw-duration-200",
57
+ icon ? "tw-overflow-hidden" : "tw-p-2 tw-bg-grey-100 tw-text-grey-900",
58
+ onDisconnect && "hover:tw-bg-grey-300",
59
+ )}
60
+ >
61
+ {icon ? (
62
+ <img src={icon} alt="" className="tw-size-full tw-object-cover" />
63
+ ) : (
64
+ <UserRoundIcon
65
+ size="1.3rem"
66
+ className={cn(
67
+ "tw-transition-all tw-duration-200",
68
+ onDisconnect &&
69
+ "group-hover/account:tw-scale-75 group-hover/account:tw-blur-sm group-hover/account:tw-opacity-0 group-focus-within/account:tw-scale-75 group-focus-within/account:tw-blur-sm group-focus-within/account:tw-opacity-0",
70
+ )}
71
+ />
72
+ )}
73
+ {onDisconnect && (
74
+ <PowerIcon
75
+ size="1.25rem"
76
+ className="tw-absolute tw-scale-75 tw-blur-sm tw-opacity-0 tw-transition-all tw-duration-200 group-hover/account:tw-scale-100 group-hover/account:tw-blur-0 group-hover/account:tw-opacity-100 group-focus-within/account:tw-scale-100 group-focus-within/account:tw-blur-0 group-focus-within/account:tw-opacity-100"
77
+ />
78
+ )}
79
+ </Wrapper>
80
+ );
81
+ }
@@ -0,0 +1,64 @@
1
+ import {
2
+ formatHash,
3
+ useMultiChainWallet,
4
+ useSquidChains,
5
+ useWallet,
6
+ } from "@0xsquid/react-hooks";
7
+ import { ChevronRightIcon } from "@0xsquid/ui";
8
+ import { useTokenSelection } from "../../../hooks/use-token-selection";
9
+ import { useDepositStore } from "../../../store/use-deposit-store";
10
+ import { Account } from "./account";
11
+
12
+ export function Recipient() {
13
+ const { chain } = useTokenSelection();
14
+ const { connectedAddress } = useMultiChainWallet(chain);
15
+ const fromAddress = connectedAddress.address;
16
+ const { disconnectWallet } = useWallet();
17
+ const config = useDepositStore((s) => s.config);
18
+ const { findChain } = useSquidChains();
19
+
20
+ const handleDisconnect = () => {
21
+ if (chain?.chainType) {
22
+ void disconnectWallet(chain.chainType);
23
+ }
24
+ };
25
+
26
+ const fromLabel = fromAddress
27
+ ? formatHash({ hash: fromAddress, chainType: chain?.chainType })
28
+ : "Not connected";
29
+
30
+ const toLabel = config
31
+ ? formatHash({
32
+ hash: config.destinationAddress,
33
+ chainType: findChain(config.destinationToken.chainId)?.chainType,
34
+ })
35
+ : "";
36
+
37
+ return (
38
+ <div className="tw-flex tw-h-squid-xl tw-items-center tw-gap-2 tw-w-full tw-px-3">
39
+ <div className="tw-group/account tw-flex tw-items-center tw-gap-2 tw-flex-1 tw-min-w-0 tw-self-stretch">
40
+ <Account
41
+ direction="from"
42
+ name="You"
43
+ address={fromLabel}
44
+ onDisconnect={fromAddress ? handleDisconnect : undefined}
45
+ />
46
+ </div>
47
+
48
+ <div className="tw-flex tw-items-center tw-text-material-light-thick -tw-space-x-4 tw-shrink-0">
49
+ <ChevronRightIcon className="tw-opacity-33" />
50
+ <ChevronRightIcon className="tw-opacity-66" />
51
+ <ChevronRightIcon className="" />
52
+ </div>
53
+
54
+ <div className="tw-flex tw-items-center tw-gap-2 tw-flex-1 tw-min-w-0 tw-self-stretch">
55
+ <Account
56
+ direction="to"
57
+ name={config?.integrator.name ?? "Recipient"}
58
+ address={toLabel}
59
+ icon={config?.integrator.logoUrl}
60
+ />
61
+ </div>
62
+ </div>
63
+ );
64
+ }
@@ -0,0 +1,77 @@
1
+ import {
2
+ formatTokenAmount,
3
+ formatUsdAmount,
4
+ getTokenImage,
5
+ } from "@0xsquid/react-hooks";
6
+ import { ChevronDownSmallIcon, PlusIcon, UserInputType } from "@0xsquid/ui";
7
+ import { TokenBadgeIcon } from "../../components/token-badge-icon";
8
+ import { useTokenSelection } from "../../hooks/use-token-selection";
9
+ import { useInputMode } from "../../store/use-input-mode";
10
+ import { useRouter } from "../../store/useRouter";
11
+
12
+ export function TokenSelector() {
13
+ const push = useRouter((s) => s.push);
14
+ const { token, chain, balance } = useTokenSelection();
15
+ const inputMode = useInputMode((s) => s.mode);
16
+
17
+ const isUsdMode = inputMode === UserInputType.USD;
18
+ const balanceNum = +balance;
19
+ const formattedBalance =
20
+ balanceNum > 0
21
+ ? isUsdMode
22
+ ? formatUsdAmount(balanceNum * (token?.usdPrice ?? 0))
23
+ : formatTokenAmount(balance, { exact: false })
24
+ : undefined;
25
+
26
+ return (
27
+ <button
28
+ onClick={() => push({ id: "select-token" })}
29
+ className="tw-flex tw-gap-2 tw-items-center tw-self-stretch hover:tw-bg-material-light-thin tw-px-3 tw-py-1.5 tw-rounded-3xl"
30
+ >
31
+ {/* Token icon */}
32
+ {token && chain ? (
33
+ <TokenBadgeIcon
34
+ tokenSrc={getTokenImage(token)}
35
+ chainSrc={chain.chainIconURI}
36
+ />
37
+ ) : (
38
+ <span className="tw-text-grey-900 tw-text-lg tw-leading-3 tw-flex tw-items-center tw-justify-center tw-size-squid-xl tw-bg-royal-500 tw-rounded-full">
39
+ <PlusIcon size="1.6rem" />
40
+ </span>
41
+ )}
42
+
43
+ <div className="tw-flex tw-flex-col tw-items-start tw-justify-center tw-gap-2 tw-self-stretch tw-flex-1">
44
+ {token ? (
45
+ <>
46
+ <span className="tw-text-grey-100 tw-text-lg tw-font-medium tw-leading-3">
47
+ {token.name}
48
+ </span>
49
+ <div className="tw-flex tw-justify-center tw-items-center tw-gap-1">
50
+ {formattedBalance && (
51
+ <span className="tw-text-sm tw-text-material-light-thick tw-leading-3">
52
+ {formattedBalance}
53
+ </span>
54
+ )}
55
+
56
+ <span className="tw-text-sm tw-text-material-light-average tw-leading-3">
57
+ {token.symbol}
58
+ </span>
59
+ </div>
60
+ </>
61
+ ) : (
62
+ <span className="tw-text-grey-100 tw-text-lg tw-font-medium tw-leading-3">
63
+ Select a token
64
+ </span>
65
+ )}
66
+ </div>
67
+
68
+ <span className="tw-size-8 tw-flex tw-items-center tw-justify-center">
69
+ <ChevronDownSmallIcon
70
+ className="tw-text-material-light-thick"
71
+ size="1.6rem"
72
+ strokeWidth="1.2"
73
+ />
74
+ </span>
75
+ </button>
76
+ );
77
+ }
@@ -0,0 +1,14 @@
1
+ import { SubNavBar } from "../components/shared/navigation/sub-navbar";
2
+
3
+ export function QrCodeView() {
4
+ return (
5
+ <>
6
+ <SubNavBar title="QR Code" />
7
+ <div className="tw-flex tw-flex-1 tw-items-center tw-justify-center tw-self-stretch">
8
+ <span className="tw-text-grey-100 tw-text-lg tw-font-medium">
9
+ QR Code
10
+ </span>
11
+ </div>
12
+ </>
13
+ );
14
+ }
@@ -0,0 +1,28 @@
1
+ import React from "react";
2
+ import type { WidgetView } from "../types";
3
+ import { ConnectWalletView } from "./connect-wallet/connect-wallet-view";
4
+ import { TransactionProgressView } from "./transaction-progress/transaction-progress-view";
5
+ import { MainView } from "./main/main-view";
6
+ import { QrCodeView } from "./qr-code";
7
+ import { SelectChainView } from "./select-chain/select-chain-view";
8
+ import { SelectTokenView } from "./select-token";
9
+ import { TransactionHistoryView } from "./transaction-history/transaction-history-view";
10
+
11
+ export function renderView(view: WidgetView): React.ReactElement {
12
+ switch (view.id) {
13
+ case "main":
14
+ return <MainView />;
15
+ case "connect-wallet":
16
+ return <ConnectWalletView chainType={view.chainType} />;
17
+ case "select-chain":
18
+ return <SelectChainView wallet={view.wallet} />;
19
+ case "qr-code":
20
+ return <QrCodeView />;
21
+ case "select-token":
22
+ return <SelectTokenView />;
23
+ case "transaction-history":
24
+ return <TransactionHistoryView />;
25
+ case "transaction-progress":
26
+ return <TransactionProgressView />;
27
+ }
28
+ }
@@ -0,0 +1,37 @@
1
+ import { ChainType } from "@0xsquid/squid-types";
2
+
3
+ interface ChainTypeMeta {
4
+ name: string;
5
+ icon: string;
6
+ }
7
+
8
+ export const CHAIN_TYPE_META: Record<ChainType, ChainTypeMeta> = {
9
+ [ChainType.EVM]: {
10
+ icon: "https://raw.githubusercontent.com/0xsquid/assets/main/images/webp128/chains/ethereum.webp",
11
+ name: "Ethereum",
12
+ },
13
+ [ChainType.COSMOS]: {
14
+ icon: "https://raw.githubusercontent.com/0xsquid/assets/main/squid-brand-assets/custom-chain-icons/cosmos.webp",
15
+ name: "Cosmos",
16
+ },
17
+ [ChainType.BTC]: {
18
+ icon: "https://raw.githubusercontent.com/0xsquid/assets/main/images/webp128/chains/bitcoin.webp",
19
+ name: "Bitcoin",
20
+ },
21
+ [ChainType.SOLANA]: {
22
+ icon: "https://raw.githubusercontent.com/0xsquid/assets/main/images/webp128/chains/solana.webp",
23
+ name: "Solana",
24
+ },
25
+ [ChainType.SUI]: {
26
+ icon: "https://raw.githubusercontent.com/0xsquid/assets/main/images/webp128/chains/sui.webp",
27
+ name: "Sui",
28
+ },
29
+ [ChainType.XRPL]: {
30
+ icon: "https://raw.githubusercontent.com/0xsquid/assets/main/images/webp128/chains/xrpl.webp",
31
+ name: "XRPL",
32
+ },
33
+ [ChainType.STELLAR]: {
34
+ icon: "https://raw.githubusercontent.com/0xsquid/assets/main/images/webp128/chains/stellar.webp",
35
+ name: "Stellar",
36
+ },
37
+ };
@@ -0,0 +1,97 @@
1
+ import { useState } from "react";
2
+ import { ConnectingWalletStatus, useWallet } from "@0xsquid/react-hooks";
3
+ import type { Wallet } from "@0xsquid/react-hooks";
4
+ import type { ChainType } from "@0xsquid/squid-types";
5
+ import { Loader } from "@0xsquid/ui";
6
+ import { SubNavBar } from "../../components/shared/navigation/sub-navbar";
7
+ import { useRouter } from "../../store/useRouter";
8
+ import { CHAIN_TYPE_META } from "./chain-type-meta";
9
+
10
+ interface SelectChainViewProps {
11
+ wallet: Wallet;
12
+ }
13
+
14
+ export function SelectChainView({ wallet }: SelectChainViewProps) {
15
+ const popToRoot = useRouter((s) => s.popToRoot);
16
+ const { connectWallet, connectingWalletState } = useWallet();
17
+ const [connectingChainType, setConnectingChainType] =
18
+ useState<ChainType | null>(null);
19
+
20
+ if (!wallet.isMultiChain) return null;
21
+ const networks = wallet.supportedNetworks;
22
+
23
+ const isConnecting =
24
+ connectingWalletState.status === ConnectingWalletStatus.CONNECTING &&
25
+ connectingWalletState.wallet?.connectorId === wallet.connectorId;
26
+
27
+ const onChainSelected = async (chainType: ChainType) => {
28
+ setConnectingChainType(chainType);
29
+ try {
30
+ const connected = await connectWallet({
31
+ wallet,
32
+ selectedChainTypes: [chainType],
33
+ });
34
+ if (connected) {
35
+ popToRoot();
36
+ }
37
+ } catch (error) {
38
+ console.error("Failed to connect wallet:", error);
39
+ setConnectingChainType(null);
40
+ }
41
+ };
42
+
43
+ return (
44
+ <>
45
+ <SubNavBar title="Select network" />
46
+
47
+ <div className="tw-flex tw-flex-col tw-items-center tw-gap-2 tw-px-4 tw-py-5 tw-border-b tw-border-grey-800">
48
+ {wallet.icon && (
49
+ <img
50
+ src={wallet.icon}
51
+ alt={wallet.name}
52
+ className="tw-size-12 tw-rounded-xl"
53
+ />
54
+ )}
55
+
56
+ <div className="tw-flex tw-flex-col tw-items-center tw-self-stretch">
57
+ <span className="tw-text-material-light-thick tw-text-base tw-text-center">
58
+ {wallet.name} supports multiple ecosystems.
59
+ </span>
60
+
61
+ <span className="tw-text-material-light-thick tw-text-base tw-text-center">
62
+ Select one to connect.
63
+ </span>
64
+ </div>
65
+ </div>
66
+
67
+ <ul className="tw-flex tw-flex-col tw-items-start tw-flex-1 tw-self-stretch tw-py-1 tw-overflow-auto">
68
+ {networks.map((network) => {
69
+ const meta = CHAIN_TYPE_META[network.chainType];
70
+ return (
71
+ <li key={network.chainType} className="tw-self-stretch">
72
+ <button
73
+ className="tw-flex tw-min-h-14 tw-h-14 tw-px-4 tw-items-center tw-gap-3 tw-self-stretch tw-w-full hover:tw-bg-material-light-thin"
74
+ disabled={isConnecting}
75
+ onClick={() => onChainSelected(network.chainType)}
76
+ >
77
+ <div className="tw-flex tw-size-squid-xl tw-justify-center tw-items-center tw-aspect-square tw-rounded-xl tw-shadow-icon-light dark:tw-shadow-icon-dark">
78
+ <img
79
+ src={meta.icon}
80
+ alt={meta.name}
81
+ className="tw-size-full tw-rounded-xl"
82
+ />
83
+ </div>
84
+ <span className="tw-text-grey-200 tw-text-lg tw-flex-1 tw-text-left">
85
+ {meta.name}
86
+ </span>
87
+ {isConnecting && connectingChainType === network.chainType && (
88
+ <Loader size="20" className="tw-text-grey-100" />
89
+ )}
90
+ </button>
91
+ </li>
92
+ );
93
+ })}
94
+ </ul>
95
+ </>
96
+ );
97
+ }