@0xsquid/deposit-widget 0.0.2-beta.0 → 0.1.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.
- package/dist/types/DepositWidget.d.ts +0 -1
- package/dist/types/components/ViewTransition.d.ts +0 -1
- package/dist/types/components/shared/buttons/button.d.ts +0 -1
- package/dist/types/components/shared/icons/types.d.ts +0 -1
- package/dist/types/components/shared/icons/user-round.d.ts +0 -1
- package/dist/types/components/shared/navigation/base-navbar.d.ts +0 -1
- package/dist/types/components/shared/navigation/sub-navbar.d.ts +0 -1
- package/dist/types/components/token-badge-icon.d.ts +0 -1
- package/dist/types/components/token-list-item.d.ts +0 -1
- package/dist/types/components/view-container.d.ts +0 -1
- package/dist/types/constants.d.ts +0 -1
- package/dist/types/hooks/ui/useMainCTAButtonState.d.ts +0 -1
- package/dist/types/hooks/use-auto-select-token.d.ts +0 -1
- package/dist/types/hooks/use-deposit-route.d.ts +0 -1
- package/dist/types/hooks/use-token-selection.d.ts +0 -1
- package/dist/types/hooks/use-transaction-history.d.ts +0 -1
- package/dist/types/index.d.ts +0 -1
- package/dist/types/services/assets-service.d.ts +0 -1
- package/dist/types/services/wallet-history/format.d.ts +0 -1
- package/dist/types/services/wallet-history/format.test.d.ts +0 -1
- package/dist/types/services/wallet-history/get-main-explorer-url.d.ts +0 -1
- package/dist/types/services/wallet-history/get-wallet-history.d.ts +0 -1
- package/dist/types/services/wallet-history/types.d.ts +0 -1
- package/dist/types/services/wallet-history/validation.d.ts +0 -1
- package/dist/types/store/use-deposit-store.d.ts +0 -1
- package/dist/types/store/use-input-mode.d.ts +0 -1
- package/dist/types/store/useRouter.d.ts +0 -1
- package/dist/types/types.d.ts +0 -1
- package/dist/types/utils/format-date.d.ts +0 -1
- package/dist/types/utils/format-date.test.d.ts +0 -1
- package/dist/types/utils/transaction.d.ts +0 -1
- package/dist/types/views/connect-wallet/connect-wallet-view.d.ts +0 -1
- package/dist/types/views/connect-wallet/wallet-list-item.d.ts +0 -1
- package/dist/types/views/main/amount-input.d.ts +0 -1
- package/dist/types/views/main/connect-prompt.d.ts +0 -1
- package/dist/types/views/main/deposit-amount-input.d.ts +0 -1
- package/dist/types/views/main/deposit-form.d.ts +0 -1
- package/dist/types/views/main/main-cta-button.d.ts +0 -1
- package/dist/types/views/main/main-view.d.ts +0 -1
- package/dist/types/views/main/navbar/actions.d.ts +0 -1
- package/dist/types/views/main/navbar/icon.d.ts +0 -1
- package/dist/types/views/main/navbar/navbar.d.ts +0 -1
- package/dist/types/views/main/navbar/title.d.ts +0 -1
- package/dist/types/views/main/recipient/account.d.ts +0 -1
- package/dist/types/views/main/recipient/recipient.d.ts +0 -1
- package/dist/types/views/main/token-selector.d.ts +0 -1
- package/dist/types/views/qr-code.d.ts +0 -1
- package/dist/types/views/render-view.d.ts +0 -1
- package/dist/types/views/select-chain/chain-type-meta.d.ts +0 -1
- package/dist/types/views/select-chain/select-chain-view.d.ts +0 -1
- package/dist/types/views/select-token.d.ts +0 -1
- package/dist/types/views/transaction-history/activity-list-item.d.ts +0 -1
- package/dist/types/views/transaction-history/transaction-history-view.d.ts +0 -1
- package/dist/types/views/transaction-progress/helpers.d.ts +0 -1
- package/dist/types/views/transaction-progress/transaction-progress-view.d.ts +0 -1
- package/dist/types/views/transaction-progress/use-transaction-progress.d.ts +0 -1
- package/package.json +7 -7
- package/src/DepositWidget.tsx +158 -0
- package/src/compiled-tailwind.css +6100 -0
- package/src/components/ViewTransition.tsx +81 -0
- package/src/components/shared/buttons/button.tsx +17 -0
- package/src/components/shared/icons/types.ts +3 -0
- package/src/components/shared/icons/user-round.tsx +21 -0
- package/src/components/shared/navigation/base-navbar.tsx +15 -0
- package/src/components/shared/navigation/sub-navbar.tsx +46 -0
- package/src/components/token-badge-icon.tsx +31 -0
- package/src/components/token-list-item.tsx +84 -0
- package/src/components/view-container.tsx +16 -0
- package/src/constants.ts +1 -0
- package/src/css.d.ts +4 -0
- package/src/fonts/DMSans-Variable.woff2 +0 -0
- package/src/hooks/ui/useMainCTAButtonState.ts +143 -0
- package/src/hooks/use-auto-select-token.ts +65 -0
- package/src/hooks/use-deposit-route.ts +58 -0
- package/src/hooks/use-token-selection.ts +17 -0
- package/src/hooks/use-transaction-history.ts +198 -0
- package/src/index.ts +3 -0
- package/src/services/assets-service.ts +21 -0
- package/src/services/wallet-history/format.test.ts +63 -0
- package/src/services/wallet-history/format.ts +128 -0
- package/src/services/wallet-history/get-main-explorer-url.ts +74 -0
- package/src/services/wallet-history/get-wallet-history.ts +24 -0
- package/src/services/wallet-history/types.ts +66 -0
- package/src/services/wallet-history/validation.ts +60 -0
- package/src/store/use-deposit-store.ts +20 -0
- package/src/store/use-input-mode.ts +10 -0
- package/src/store/useRouter.ts +49 -0
- package/src/tailwind.css +16 -0
- package/src/types.ts +39 -0
- package/src/utils/format-date.test.ts +32 -0
- package/src/utils/format-date.ts +25 -0
- package/src/utils/transaction.ts +39 -0
- package/src/views/connect-wallet/connect-wallet-view.tsx +147 -0
- package/src/views/connect-wallet/wallet-list-item.tsx +69 -0
- package/src/views/main/amount-input.tsx +272 -0
- package/src/views/main/connect-prompt.tsx +47 -0
- package/src/views/main/deposit-amount-input.tsx +42 -0
- package/src/views/main/deposit-form.tsx +13 -0
- package/src/views/main/main-cta-button.tsx +14 -0
- package/src/views/main/main-view.tsx +24 -0
- package/src/views/main/navbar/actions.tsx +25 -0
- package/src/views/main/navbar/icon.tsx +11 -0
- package/src/views/main/navbar/navbar.tsx +16 -0
- package/src/views/main/navbar/title.tsx +64 -0
- package/src/views/main/recipient/account.tsx +81 -0
- package/src/views/main/recipient/recipient.tsx +64 -0
- package/src/views/main/token-selector.tsx +77 -0
- package/src/views/qr-code.tsx +14 -0
- package/src/views/render-view.tsx +28 -0
- package/src/views/select-chain/chain-type-meta.ts +37 -0
- package/src/views/select-chain/select-chain-view.tsx +97 -0
- package/src/views/select-token.tsx +227 -0
- package/src/views/transaction-history/activity-list-item.tsx +87 -0
- package/src/views/transaction-history/transaction-history-view.tsx +58 -0
- package/src/views/transaction-progress/helpers.tsx +93 -0
- package/src/views/transaction-progress/transaction-progress-view.tsx +217 -0
- package/src/views/transaction-progress/use-transaction-progress.ts +112 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import React, { useEffect, useRef, useState } from "react";
|
|
2
|
+
import type { NavigationDirection } from "../store/useRouter";
|
|
3
|
+
import { useRouter } from "../store/useRouter";
|
|
4
|
+
import { VIEW_TRANSITION_DURATION_MS } from "../constants";
|
|
5
|
+
import { cn } from "@0xsquid/ui";
|
|
6
|
+
|
|
7
|
+
interface ViewTransitionProps {
|
|
8
|
+
viewId: string;
|
|
9
|
+
children: React.ReactElement;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const ENTER_CLASS: Record<NavigationDirection, string> = {
|
|
13
|
+
forward: "tw-animate-slide-in-right",
|
|
14
|
+
backward: "tw-animate-slide-in-left",
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const EXIT_CLASS: Record<NavigationDirection, string> = {
|
|
18
|
+
forward: "tw-animate-slide-out-left",
|
|
19
|
+
backward: "tw-animate-slide-out-right",
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
interface ExitingSlot {
|
|
23
|
+
viewId: string;
|
|
24
|
+
element: React.ReactElement;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function ViewTransition({ viewId, children }: ViewTransitionProps) {
|
|
28
|
+
const direction = useRouter((s) => s.direction);
|
|
29
|
+
const prevChildrenRef = useRef(children);
|
|
30
|
+
const [exiting, setExiting] = useState<ExitingSlot | null>(null);
|
|
31
|
+
const [activeId, setActiveId] = useState(viewId);
|
|
32
|
+
|
|
33
|
+
// Detect view change during render (React "derive state from props" pattern)
|
|
34
|
+
if (viewId !== activeId) {
|
|
35
|
+
setExiting({ viewId: activeId, element: prevChildrenRef.current });
|
|
36
|
+
setActiveId(viewId);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Keep ref in sync with latest children for the active view
|
|
40
|
+
prevChildrenRef.current = children;
|
|
41
|
+
|
|
42
|
+
// Safety timeout: clear exiting slot if onAnimationEnd doesn't fire
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
if (!exiting) return;
|
|
45
|
+
const timer = setTimeout(
|
|
46
|
+
() => setExiting(null),
|
|
47
|
+
VIEW_TRANSITION_DURATION_MS + 50,
|
|
48
|
+
);
|
|
49
|
+
return () => clearTimeout(timer);
|
|
50
|
+
}, [exiting]);
|
|
51
|
+
|
|
52
|
+
const handleAnimationEnd = (e: React.AnimationEvent) => {
|
|
53
|
+
if (e.target !== e.currentTarget) return;
|
|
54
|
+
setExiting(null);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const isAnimating = exiting !== null;
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<div className="tw-relative tw-flex-1 tw-overflow-hidden tw-w-full">
|
|
61
|
+
{exiting && direction && (
|
|
62
|
+
<div
|
|
63
|
+
key={exiting.viewId}
|
|
64
|
+
className={`tw-absolute tw-inset-0 tw-flex tw-flex-col tw-will-change-transform ${EXIT_CLASS[direction]}`}
|
|
65
|
+
>
|
|
66
|
+
{exiting.element}
|
|
67
|
+
</div>
|
|
68
|
+
)}
|
|
69
|
+
<div
|
|
70
|
+
key={viewId}
|
|
71
|
+
className={cn(
|
|
72
|
+
"tw-absolute tw-inset-0 tw-flex tw-flex-col tw-will-change-transform",
|
|
73
|
+
isAnimating && direction && ENTER_CLASS[direction],
|
|
74
|
+
)}
|
|
75
|
+
onAnimationEnd={isAnimating ? handleAnimationEnd : undefined}
|
|
76
|
+
>
|
|
77
|
+
{children}
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { cn } from "@0xsquid/ui";
|
|
2
|
+
|
|
3
|
+
type ButtonProps = React.ComponentProps<"button">;
|
|
4
|
+
|
|
5
|
+
export function Button(props: ButtonProps) {
|
|
6
|
+
return (
|
|
7
|
+
<button
|
|
8
|
+
{...props}
|
|
9
|
+
className={cn(
|
|
10
|
+
"tw-flex tw-justify-center tw-items-center tw-flex-1 tw-self-stretch tw-rounded-2xl tw-text-lg tw-text-grey-900 tw-bg-grey-100 tw-bg-cta-button-light dark:tw-bg-cta-button-dark tw-shadow-cta-button-light dark:tw-shadow-cta-button-dark",
|
|
11
|
+
// disabled
|
|
12
|
+
"disabled:tw-cursor-not-allowed disabled:tw-bg-grey-800 disabled:tw-text-grey-600 disabled:dark:tw-bg-grey-800 disabled:dark:tw-text-grey-500",
|
|
13
|
+
props.className,
|
|
14
|
+
)}
|
|
15
|
+
/>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { IconProps } from "./types";
|
|
2
|
+
|
|
3
|
+
export function UserRoundIcon({ size = "1.5rem", ...props }: IconProps) {
|
|
4
|
+
return (
|
|
5
|
+
<svg
|
|
6
|
+
{...props}
|
|
7
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
8
|
+
width={size}
|
|
9
|
+
height={size}
|
|
10
|
+
viewBox="0 0 24 24"
|
|
11
|
+
fill="none"
|
|
12
|
+
stroke="currentColor"
|
|
13
|
+
strokeWidth="2"
|
|
14
|
+
strokeLinecap="round"
|
|
15
|
+
strokeLinejoin="round"
|
|
16
|
+
>
|
|
17
|
+
<circle cx="12" cy="8" r="5" />
|
|
18
|
+
<path d="M20 21a8 8 0 0 0-16 0" />
|
|
19
|
+
</svg>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { cn } from "@0xsquid/ui";
|
|
2
|
+
|
|
3
|
+
type BaseNavBarProps = React.ComponentProps<"nav">;
|
|
4
|
+
|
|
5
|
+
export function BaseNavBar({ className, ...props }: BaseNavBarProps) {
|
|
6
|
+
return (
|
|
7
|
+
<nav
|
|
8
|
+
{...props}
|
|
9
|
+
className={cn(
|
|
10
|
+
"tw-flex tw-py-3 tw-px-4 tw-items-center tw-self-stretch tw-h-16",
|
|
11
|
+
className,
|
|
12
|
+
)}
|
|
13
|
+
/>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { ArrowLeftIcon, IconButton, SearchIcon } from "@0xsquid/ui";
|
|
2
|
+
import { useRouter } from "../../../store/useRouter";
|
|
3
|
+
import { BaseNavBar } from "./base-navbar";
|
|
4
|
+
|
|
5
|
+
interface SubNavBarProps {
|
|
6
|
+
title?: string;
|
|
7
|
+
input?: React.ComponentProps<"input">;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function SubNavBar({ title, input }: SubNavBarProps) {
|
|
11
|
+
const pop = useRouter((s) => s.pop);
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<BaseNavBar className="tw-gap-2">
|
|
15
|
+
<IconButton
|
|
16
|
+
onClick={pop}
|
|
17
|
+
icon={
|
|
18
|
+
<ArrowLeftIcon
|
|
19
|
+
className="tw-text-grey-100 [&>path]:tw-stroke-[3]"
|
|
20
|
+
size="1.5rem"
|
|
21
|
+
/>
|
|
22
|
+
}
|
|
23
|
+
/>
|
|
24
|
+
|
|
25
|
+
{input ? (
|
|
26
|
+
<div className="tw-flex tw-relative tw-flex-1 tw-items-center">
|
|
27
|
+
<SearchIcon
|
|
28
|
+
size="1rem"
|
|
29
|
+
className="tw-absolute tw-left-0 tw-top-1/2 -tw-translate-y-1/2 tw-text-material-light-average tw-pl-2 tw-w-fit"
|
|
30
|
+
/>
|
|
31
|
+
|
|
32
|
+
<input
|
|
33
|
+
className="tw-flex tw-h-8 tw-text-grey-100 tw-py-1 tw-pl-7 tw-pr-2 tw-items-center tw-gap-1 tw-flex-1 tw-rounded-squid-s tw-border tw-border-material-light-thin tw-bg-grey-800 placeholder:tw-text-lg tw-placeholder-material-light-average"
|
|
34
|
+
{...input}
|
|
35
|
+
/>
|
|
36
|
+
|
|
37
|
+
<span className="tw-size-squid-xl" />
|
|
38
|
+
</div>
|
|
39
|
+
) : title ? (
|
|
40
|
+
<span className="tw-text-grey-100 tw-text-lg tw-font-squid-main tw-font-medium">
|
|
41
|
+
{title}
|
|
42
|
+
</span>
|
|
43
|
+
) : null}
|
|
44
|
+
</BaseNavBar>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Image } from "@0xsquid/ui";
|
|
2
|
+
|
|
3
|
+
interface TokenBadgeIconProps {
|
|
4
|
+
tokenSrc: string;
|
|
5
|
+
chainSrc?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function TokenBadgeIcon({ tokenSrc, chainSrc }: TokenBadgeIconProps) {
|
|
9
|
+
return (
|
|
10
|
+
<div className="tw-size-squid-xl tw-rounded-full tw-relative tw-shrink-0">
|
|
11
|
+
<Image
|
|
12
|
+
src={tokenSrc}
|
|
13
|
+
size="xlarge"
|
|
14
|
+
rounded="full"
|
|
15
|
+
shadow
|
|
16
|
+
border="inset"
|
|
17
|
+
/>
|
|
18
|
+
{chainSrc && (
|
|
19
|
+
<span className="tw-absolute -tw-bottom-1 -tw-left-1">
|
|
20
|
+
<Image
|
|
21
|
+
src={chainSrc}
|
|
22
|
+
size="small"
|
|
23
|
+
rounded="xxs"
|
|
24
|
+
style={{ width: "1rem", height: "1rem" }}
|
|
25
|
+
border="outline-sm"
|
|
26
|
+
/>
|
|
27
|
+
</span>
|
|
28
|
+
)}
|
|
29
|
+
</div>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { formatUsdAmount } from "@0xsquid/react-hooks";
|
|
2
|
+
import { cn, TextSkeleton } from "@0xsquid/ui";
|
|
3
|
+
import { TokenBadgeIcon } from "./token-badge-icon";
|
|
4
|
+
|
|
5
|
+
interface TokenListItemProps extends React.ComponentProps<"button"> {
|
|
6
|
+
icon: string;
|
|
7
|
+
chainIcon?: string;
|
|
8
|
+
symbol: string;
|
|
9
|
+
name: string;
|
|
10
|
+
balance?: string;
|
|
11
|
+
price: number;
|
|
12
|
+
usdValue?: number;
|
|
13
|
+
disabled?: boolean;
|
|
14
|
+
isLoadingBalance?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function TokenListItem({
|
|
18
|
+
icon,
|
|
19
|
+
chainIcon,
|
|
20
|
+
symbol,
|
|
21
|
+
name,
|
|
22
|
+
balance,
|
|
23
|
+
price,
|
|
24
|
+
usdValue,
|
|
25
|
+
disabled,
|
|
26
|
+
isLoadingBalance,
|
|
27
|
+
...props
|
|
28
|
+
}: TokenListItemProps) {
|
|
29
|
+
const showUsdOnHover = (usdValue ?? 0) > 0;
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<li className="tw-self-stretch">
|
|
33
|
+
<button
|
|
34
|
+
aria-disabled={disabled}
|
|
35
|
+
tabIndex={disabled ? -1 : undefined}
|
|
36
|
+
title={disabled ? "Insufficient balance" : undefined}
|
|
37
|
+
className={cn(
|
|
38
|
+
"tw-group/token-item tw-flex tw-h-14 tw-px-4 tw-items-center tw-gap-2 tw-self-stretch tw-w-full hover:tw-bg-material-light-thin",
|
|
39
|
+
disabled && "tw-opacity-40 tw-cursor-not-allowed",
|
|
40
|
+
)}
|
|
41
|
+
{...props}
|
|
42
|
+
>
|
|
43
|
+
<TokenBadgeIcon tokenSrc={icon} chainSrc={chainIcon} />
|
|
44
|
+
|
|
45
|
+
<div className="tw-flex tw-flex-col tw-justify-center tw-gap-0.5 tw-items-start tw-flex-1 tw-self-stretch tw-overflow-hidden -tw-mt-2">
|
|
46
|
+
<span className="tw-text-grey-100 tw-text-lg tw-truncate tw-max-w-full">
|
|
47
|
+
{name}
|
|
48
|
+
</span>
|
|
49
|
+
|
|
50
|
+
<div className="tw-flex tw-justify-center tw-items-center tw-gap-1 tw-leading-[0.7]">
|
|
51
|
+
{isLoadingBalance ? (
|
|
52
|
+
<TextSkeleton width="3em" className="tw-h-3" isLoading />
|
|
53
|
+
) : showUsdOnHover ? (
|
|
54
|
+
<>
|
|
55
|
+
{balance && (
|
|
56
|
+
<span className="tw-text-sm tw-text-material-light-thick tw-leading-[inherit] group-hover/token-item:tw-hidden">
|
|
57
|
+
{balance}
|
|
58
|
+
</span>
|
|
59
|
+
)}
|
|
60
|
+
<span className="tw-text-sm tw-text-material-light-thick tw-leading-[inherit] tw-hidden group-hover/token-item:tw-inline">
|
|
61
|
+
{formatUsdAmount(usdValue)}
|
|
62
|
+
</span>
|
|
63
|
+
</>
|
|
64
|
+
) : (
|
|
65
|
+
balance && (
|
|
66
|
+
<span className="tw-text-sm tw-text-material-light-thick tw-leading-[inherit]">
|
|
67
|
+
{balance}
|
|
68
|
+
</span>
|
|
69
|
+
)
|
|
70
|
+
)}
|
|
71
|
+
|
|
72
|
+
<span className="tw-text-sm tw-text-material-light-average tw-leading-[inherit]">
|
|
73
|
+
{symbol}
|
|
74
|
+
</span>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
<div className="tw-flex tw-items-start tw-justify-center tw-text-material-light-thick">
|
|
79
|
+
{formatUsdAmount(price)}
|
|
80
|
+
</div>
|
|
81
|
+
</button>
|
|
82
|
+
</li>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { cn } from "@0xsquid/ui";
|
|
2
|
+
|
|
3
|
+
export function ViewContainer({
|
|
4
|
+
className,
|
|
5
|
+
...props
|
|
6
|
+
}: React.ComponentProps<"section">) {
|
|
7
|
+
return (
|
|
8
|
+
<section
|
|
9
|
+
{...props}
|
|
10
|
+
className={cn(
|
|
11
|
+
"tw-flex tw-flex-col tw-items-center tw-flex-1 tw-self-stretch tw-border-t tw-border-grey-800 tw-py-1 tw-overflow-auto tw-h-full tw-scrollbar-hidden",
|
|
12
|
+
className,
|
|
13
|
+
)}
|
|
14
|
+
/>
|
|
15
|
+
);
|
|
16
|
+
}
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const VIEW_TRANSITION_DURATION_MS = 250;
|
package/src/css.d.ts
ADDED
|
Binary file
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useApproval,
|
|
3
|
+
useEstimate,
|
|
4
|
+
useExecuteTransaction,
|
|
5
|
+
useSwap,
|
|
6
|
+
useWallet,
|
|
7
|
+
} from "@0xsquid/react-hooks";
|
|
8
|
+
import { useRouter } from "../../store/useRouter";
|
|
9
|
+
import { useIsPaymentMode } from "../../store/use-deposit-store";
|
|
10
|
+
import { useDepositRoute } from "../use-deposit-route";
|
|
11
|
+
import { useTokenSelection } from "../use-token-selection";
|
|
12
|
+
|
|
13
|
+
interface ButtonState {
|
|
14
|
+
label: string;
|
|
15
|
+
onClick?: () => void;
|
|
16
|
+
disabled?: boolean;
|
|
17
|
+
isLoading?: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function useMainCTAButtonState(): ButtonState {
|
|
21
|
+
const push = useRouter((store) => store.push);
|
|
22
|
+
const { connectedWallets, isChainTypeConnected } = useWallet();
|
|
23
|
+
const { token, chain, balance } = useTokenSelection();
|
|
24
|
+
const { fromAmount: typedAmount, fromAmountChanged } = useSwap();
|
|
25
|
+
const isPaymentMode = useIsPaymentMode();
|
|
26
|
+
const isConnected = connectedWallets.length > 0;
|
|
27
|
+
|
|
28
|
+
const {
|
|
29
|
+
routeData,
|
|
30
|
+
quotedSourceAmount,
|
|
31
|
+
showLoading: isRouteLoading,
|
|
32
|
+
} = useDepositRoute();
|
|
33
|
+
|
|
34
|
+
const amount = isPaymentMode ? quotedSourceAmount : typedAmount;
|
|
35
|
+
const { routeApproved, approveRoute } = useApproval({
|
|
36
|
+
squidRoute: routeData,
|
|
37
|
+
});
|
|
38
|
+
const { executeSwap, isLoading: isExecuting } =
|
|
39
|
+
useExecuteTransaction(routeData);
|
|
40
|
+
const { minAmountValueWarnMsg, fromTokenPaysGasFees, isGasBalanceEnough } =
|
|
41
|
+
useEstimate(routeData);
|
|
42
|
+
|
|
43
|
+
if (!isConnected) {
|
|
44
|
+
return {
|
|
45
|
+
label: "Connect wallet",
|
|
46
|
+
onClick: () => push({ id: "connect-wallet", chainType: null }),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (chain && !isChainTypeConnected(chain.chainType)) {
|
|
51
|
+
return {
|
|
52
|
+
label: "Connect wallet",
|
|
53
|
+
onClick: () => push({ id: "connect-wallet", chainType: chain.chainType }),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!token) {
|
|
58
|
+
return {
|
|
59
|
+
label: "Select a token",
|
|
60
|
+
disabled: true,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!amount || amount === "0") {
|
|
65
|
+
return {
|
|
66
|
+
label: "Enter amount",
|
|
67
|
+
disabled: true,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (!balance || Number(amount) > Number(balance)) {
|
|
72
|
+
return {
|
|
73
|
+
label: "Insufficient balance",
|
|
74
|
+
disabled: true,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (isRouteLoading) {
|
|
79
|
+
return {
|
|
80
|
+
label: "Getting quotes...",
|
|
81
|
+
disabled: true,
|
|
82
|
+
isLoading: true,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (!routeData) {
|
|
87
|
+
return {
|
|
88
|
+
label: "No route available",
|
|
89
|
+
disabled: true,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (
|
|
94
|
+
fromTokenPaysGasFees &&
|
|
95
|
+
!isGasBalanceEnough &&
|
|
96
|
+
minAmountValueWarnMsg !== undefined &&
|
|
97
|
+
Number(minAmountValueWarnMsg) > 0
|
|
98
|
+
) {
|
|
99
|
+
return {
|
|
100
|
+
label: "Fit gas",
|
|
101
|
+
onClick: () => fromAmountChanged(minAmountValueWarnMsg),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (routeApproved.isLoading) {
|
|
106
|
+
return {
|
|
107
|
+
label: "Checking approval...",
|
|
108
|
+
disabled: true,
|
|
109
|
+
isLoading: true,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (routeApproved.data === false) {
|
|
114
|
+
if (approveRoute.isLoading) {
|
|
115
|
+
return {
|
|
116
|
+
label: "Approving...",
|
|
117
|
+
disabled: true,
|
|
118
|
+
isLoading: true,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
label: `Approve ${token.symbol}`,
|
|
124
|
+
onClick: () => approveRoute.mutate(),
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (isExecuting) {
|
|
129
|
+
return {
|
|
130
|
+
label: isPaymentMode ? "Paying..." : "Depositing...",
|
|
131
|
+
disabled: true,
|
|
132
|
+
isLoading: true,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
label: isPaymentMode ? "Pay" : "Deposit",
|
|
138
|
+
onClick: () => {
|
|
139
|
+
void executeSwap();
|
|
140
|
+
push({ id: "transaction-progress" });
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { useEffect, useRef } from "react";
|
|
2
|
+
import {
|
|
3
|
+
CHAIN_IDS,
|
|
4
|
+
chainTypeToNativeTokenAddressMap,
|
|
5
|
+
useSquidTokens,
|
|
6
|
+
useSwap,
|
|
7
|
+
useWallet,
|
|
8
|
+
} from "@0xsquid/react-hooks";
|
|
9
|
+
import { ChainType } from "@0xsquid/squid-types";
|
|
10
|
+
|
|
11
|
+
const CHAIN_TYPE_PRIORITY: ChainType[] = [
|
|
12
|
+
ChainType.EVM,
|
|
13
|
+
ChainType.SOLANA,
|
|
14
|
+
ChainType.COSMOS,
|
|
15
|
+
ChainType.SUI,
|
|
16
|
+
ChainType.BTC,
|
|
17
|
+
ChainType.XRPL,
|
|
18
|
+
ChainType.STELLAR,
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
const DEFAULT_CHAIN_ID_BY_TYPE: Record<ChainType, string> = {
|
|
22
|
+
[ChainType.EVM]: CHAIN_IDS.ETHEREUM,
|
|
23
|
+
[ChainType.COSMOS]: CHAIN_IDS.OSMOSIS,
|
|
24
|
+
[ChainType.BTC]: CHAIN_IDS.BITCOIN,
|
|
25
|
+
[ChainType.SOLANA]: CHAIN_IDS.SOLANA,
|
|
26
|
+
[ChainType.SUI]: CHAIN_IDS.SUI,
|
|
27
|
+
[ChainType.XRPL]: CHAIN_IDS.XRPL,
|
|
28
|
+
[ChainType.STELLAR]: CHAIN_IDS.STELLAR,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export function useAutoSelectToken() {
|
|
32
|
+
const { fromToken: token, onSwapChange } = useSwap();
|
|
33
|
+
const { connectedWalletsByChainType } = useWallet();
|
|
34
|
+
const { findToken, tokens } = useSquidTokens();
|
|
35
|
+
const hasAutoSelected = useRef(false);
|
|
36
|
+
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
if (token != null) {
|
|
39
|
+
hasAutoSelected.current = false;
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (hasAutoSelected.current) return;
|
|
44
|
+
if (tokens.length === 0) return;
|
|
45
|
+
|
|
46
|
+
const connectedChainType = CHAIN_TYPE_PRIORITY.find((ct) => {
|
|
47
|
+
const walletState = connectedWalletsByChainType[ct];
|
|
48
|
+
return !!walletState?.wallet && !!walletState?.address;
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
if (!connectedChainType) return;
|
|
52
|
+
|
|
53
|
+
const nativeAddress = chainTypeToNativeTokenAddressMap[connectedChainType];
|
|
54
|
+
const defaultChainId = DEFAULT_CHAIN_ID_BY_TYPE[connectedChainType];
|
|
55
|
+
const nativeToken = findToken(nativeAddress, defaultChainId);
|
|
56
|
+
|
|
57
|
+
if (nativeToken) {
|
|
58
|
+
onSwapChange({
|
|
59
|
+
fromChainId: nativeToken.chainId,
|
|
60
|
+
fromTokenAddress: nativeToken.address,
|
|
61
|
+
});
|
|
62
|
+
hasAutoSelected.current = true;
|
|
63
|
+
}
|
|
64
|
+
}, [token, connectedWalletsByChainType, tokens, findToken, onSwapChange]);
|
|
65
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import {
|
|
2
|
+
formatBNToReadable,
|
|
3
|
+
getSquidRouteErrorMessage,
|
|
4
|
+
useGetRouteWrapper,
|
|
5
|
+
useMultiChainWallet,
|
|
6
|
+
useSwap,
|
|
7
|
+
} from "@0xsquid/react-hooks";
|
|
8
|
+
import type { RouteResponse } from "@0xsquid/squid-types";
|
|
9
|
+
import { useDepositStore } from "../store/use-deposit-store";
|
|
10
|
+
import { selectCurrentView, useRouter } from "../store/useRouter";
|
|
11
|
+
|
|
12
|
+
function formatRouteError(message: string): string {
|
|
13
|
+
if (message === "Low liquidity, please reduce swap amount and try again") {
|
|
14
|
+
return "Low liquidity, select a different token or reduce the amount";
|
|
15
|
+
}
|
|
16
|
+
return message;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function useDepositRoute(): {
|
|
20
|
+
routeData: RouteResponse["route"] | undefined;
|
|
21
|
+
quotedSourceAmount: string | undefined;
|
|
22
|
+
showLoading: boolean;
|
|
23
|
+
routeErrorMessage: string | undefined;
|
|
24
|
+
} {
|
|
25
|
+
const config = useDepositStore((s) => s.config);
|
|
26
|
+
const { fromChain, fromToken, toChain, toToken, fromAmount, toAmount } =
|
|
27
|
+
useSwap();
|
|
28
|
+
const { connectedAddress } = useMultiChainWallet(fromChain);
|
|
29
|
+
const sourceAddress = connectedAddress.address;
|
|
30
|
+
const currentView = useRouter(selectCurrentView);
|
|
31
|
+
|
|
32
|
+
const quoteOnly = !sourceAddress || !config?.destinationAddress;
|
|
33
|
+
|
|
34
|
+
const enabled =
|
|
35
|
+
currentView.id === "main" &&
|
|
36
|
+
Boolean(
|
|
37
|
+
fromChain && fromToken && toChain && toToken && (fromAmount || toAmount),
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
const { routeData, showLoading, squidRouteError } = useGetRouteWrapper({
|
|
41
|
+
quoteOnly,
|
|
42
|
+
enabled,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const quotedSourceAmount =
|
|
46
|
+
routeData && fromToken
|
|
47
|
+
? formatBNToReadable(routeData.estimate.fromAmount, fromToken.decimals)
|
|
48
|
+
: undefined;
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
routeData,
|
|
52
|
+
quotedSourceAmount,
|
|
53
|
+
showLoading,
|
|
54
|
+
routeErrorMessage: squidRouteError
|
|
55
|
+
? formatRouteError(getSquidRouteErrorMessage(squidRouteError))
|
|
56
|
+
: undefined,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useMultiChainBalance,
|
|
3
|
+
useMultiChainWallet,
|
|
4
|
+
useSwap,
|
|
5
|
+
} from "@0xsquid/react-hooks";
|
|
6
|
+
|
|
7
|
+
export function useTokenSelection() {
|
|
8
|
+
const { fromToken: token, fromChain: chain } = useSwap();
|
|
9
|
+
const { connectedAddress } = useMultiChainWallet(chain);
|
|
10
|
+
const { balance } = useMultiChainBalance({
|
|
11
|
+
chain,
|
|
12
|
+
token: token ?? undefined,
|
|
13
|
+
userAddress: connectedAddress.address,
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
return { token, chain, balance };
|
|
17
|
+
}
|