@xswap-link/sdk 0.13.1 → 0.14.1
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/CHANGELOG.md +22 -0
- package/dist/index.d.mts +10 -2
- package/dist/index.d.ts +10 -2
- package/dist/index.global.js +87 -86
- package/dist/index.js +2888 -2521
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2828 -2458
- package/dist/index.mjs.map +1 -1
- package/localTheme.ts +5 -3
- package/package.json +1 -1
- package/src/assets/icons/AdjustmentsIcon.tsx +16 -0
- package/src/assets/icons/CaretDownIcon.tsx +12 -0
- package/src/assets/icons/CaretsUpDownIcon.tsx +13 -0
- package/src/assets/icons/ChainlinkMarkIcon.tsx +17 -0
- package/src/assets/icons/ClockIcon.tsx +14 -0
- package/src/assets/icons/CloseIcon.tsx +6 -2
- package/src/assets/icons/WarningIcon.tsx +19 -0
- package/src/assets/icons/index.ts +6 -0
- package/src/components/Modal/index.tsx +14 -9
- package/src/components/PoweredBy/index.tsx +12 -5
- package/src/components/Swap/Header/Controls/index.tsx +50 -36
- package/src/components/Swap/Header/index.tsx +12 -5
- package/src/components/Swap/HistoryView/ActivityCard/index.tsx +212 -0
- package/src/components/Swap/HistoryView/index.tsx +24 -6
- package/src/components/Swap/PickerView/index.tsx +151 -0
- package/src/components/Swap/ReorderButton/ReorderButton.tsx +5 -7
- package/src/components/Swap/SettingsView/Delivery/index.tsx +27 -28
- package/src/components/Swap/SettingsView/InfiniteApproval/index.tsx +27 -24
- package/src/components/Swap/SettingsView/Slippage/index.tsx +44 -43
- package/src/components/Swap/SettingsView/index.tsx +1 -1
- package/src/components/Swap/SwapView/ConfirmationView/TxOverview/index.tsx +50 -4
- package/src/components/Swap/SwapView/FeesPanel/DeliveryInfo/index.tsx +9 -19
- package/src/components/Swap/SwapView/FeesPanel/Fee/index.tsx +4 -4
- package/src/components/Swap/SwapView/FeesPanel/Fees/index.tsx +5 -9
- package/src/components/Swap/SwapView/FeesPanel/index.tsx +21 -22
- package/src/components/Swap/SwapView/SwapButton/index.tsx +25 -6
- package/src/components/Swap/SwapView/SwapPanel/AmountPanel/Balance/index.tsx +56 -12
- package/src/components/Swap/SwapView/SwapPanel/AmountPanel/index.tsx +42 -41
- package/src/components/Swap/SwapView/SwapPanel/TokenPanel/ChainPicker/index.tsx +143 -0
- package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/NetworkFilter/index.tsx +159 -0
- package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/TokenItem/index.tsx +105 -55
- package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/index.tsx +226 -276
- package/src/components/Swap/SwapView/SwapPanel/TokenPanel/index.tsx +57 -97
- package/src/components/Swap/SwapView/SwapPanel/UsdValue/index.tsx +27 -0
- package/src/components/Swap/SwapView/SwapPanel/WalletPanel/index.tsx +60 -49
- package/src/components/Swap/SwapView/SwapPanel/index.tsx +20 -25
- package/src/components/Swap/SwapView/WalletPicker/index.tsx +6 -1
- package/src/components/Swap/SwapView/index.tsx +2 -1
- package/src/components/Swap/index.tsx +20 -4
- package/src/components/ToggleButton/index.tsx +18 -15
- package/src/components/TxConfigForm/index.tsx +5 -5
- package/src/components/TxWidgetWC/index.tsx +4 -0
- package/src/components/TxWidgetWCWrapped/index.tsx +2 -0
- package/src/context/HistoryProvider.tsx +2 -1
- package/src/context/SwapProvider.tsx +87 -62
- package/src/context/TxUIWrapper.tsx +3 -3
- package/src/hooks/useEvmContractApi.ts +28 -4
- package/src/models/TransactionHistory.ts +1 -0
- package/src/models/payloads/GetBalancesPayload.ts +1 -0
- package/src/models/payloads/ModalIntegrationPayload.ts +1 -0
- package/src/models/payloads/WidgetIntegrationPayload.ts +1 -0
- package/src/services/api.ts +37 -0
- package/src/utils/index.ts +48 -1
- package/src/utils/tokens.ts +20 -1
- package/tailwind.config.js +4 -1
- package/src/components/Swap/SwapView/SwapPanel/ChainPanel/ChainPicker/ChainItem/index.tsx +0 -97
- package/src/components/Swap/SwapView/SwapPanel/ChainPanel/ChainPicker/index.tsx +0 -103
- package/src/components/Swap/SwapView/SwapPanel/ChainPanel/index.tsx +0 -65
- package/src/components/Swap/SwapView/SwapPanel/MaxPanel/index.tsx +0 -43
- package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/QuickPickTokenItem/index.tsx +0 -37
package/localTheme.ts
CHANGED
|
@@ -50,8 +50,9 @@ const lightThemeStyles: ModalIntegrationThemeStyles = {
|
|
|
50
50
|
};
|
|
51
51
|
|
|
52
52
|
const darkThemeStyles: ModalIntegrationThemeStyles = {
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
// Lavender CTA gradient from the swap-v2 design
|
|
54
|
+
mainAccentLight: "#D3DAF2",
|
|
55
|
+
mainAccentDark: "#B8C1E9",
|
|
55
56
|
bgPrimary: "#020202",
|
|
56
57
|
bgSecondary: "#0F0F0F",
|
|
57
58
|
bgTertiary: "#ffffff",
|
|
@@ -62,7 +63,8 @@ const darkThemeStyles: ModalIntegrationThemeStyles = {
|
|
|
62
63
|
textGreen: "#4CAF50",
|
|
63
64
|
clickSecondary: "#3681C6",
|
|
64
65
|
clickTertiary: "#FFFFFF",
|
|
65
|
-
|
|
66
|
+
// Dark text sits on the light lavender primary button
|
|
67
|
+
buttonPrText: "#020202",
|
|
66
68
|
buttonPrOffBg: "#272727",
|
|
67
69
|
buttonPrOffText: "#90A4AE",
|
|
68
70
|
labelText: "#FFFFFF",
|
package/package.json
CHANGED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export const AdjustmentsIcon = ({ className }: { className?: string }) => {
|
|
2
|
+
return (
|
|
3
|
+
<svg
|
|
4
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
5
|
+
fill="none"
|
|
6
|
+
// The 20px-wide artwork is clipped to a 16px box in the design,
|
|
7
|
+
// so the slider lines run edge to edge.
|
|
8
|
+
viewBox="2 0 16 10.4"
|
|
9
|
+
stroke="currentColor"
|
|
10
|
+
strokeLinejoin="round"
|
|
11
|
+
className={className}
|
|
12
|
+
>
|
|
13
|
+
<path d="M8.4 2.1L20 2.1M5.2 2.1C5.2 1.21634 5.91635 0.5 6.8 0.5C7.68366 0.5 8.4 1.21635 8.4 2.1C8.4 2.98366 7.68366 3.7 6.8 3.7C5.91635 3.7 5.2 2.98365 5.2 2.1ZM0 2.1H5.2M14.8 8.3L20 8.3M11.6 8.3C11.6 7.41634 12.3163 6.7 13.2 6.7C14.0837 6.7 14.8 7.41635 14.8 8.3C14.8 9.18366 14.0837 9.9 13.2 9.9C12.3163 9.9 11.6 9.18366 11.6 8.3ZM6.8 8.3H11.6" />
|
|
14
|
+
</svg>
|
|
15
|
+
);
|
|
16
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const CaretsUpDownIcon = () => {
|
|
2
|
+
return (
|
|
3
|
+
<svg
|
|
4
|
+
width="10"
|
|
5
|
+
height="16"
|
|
6
|
+
viewBox="0 0 10 16"
|
|
7
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
8
|
+
>
|
|
9
|
+
<path d="M4.99987 16L0 10.9999L5 12.2499L10 10.9999L4.99987 16Z" />
|
|
10
|
+
<path d="M5.00013 0L10 5.00013L5 3.7501L0 5.00013L5.00013 0Z" />
|
|
11
|
+
</svg>
|
|
12
|
+
);
|
|
13
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const ChainlinkMarkIcon = () => {
|
|
2
|
+
return (
|
|
3
|
+
<svg
|
|
4
|
+
width="24"
|
|
5
|
+
height="24"
|
|
6
|
+
viewBox="0 0 24 24"
|
|
7
|
+
fill="none"
|
|
8
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
9
|
+
>
|
|
10
|
+
<rect width="24" height="24" rx="4" fill="white" fillOpacity="0.03" />
|
|
11
|
+
<path
|
|
12
|
+
d="M12.0667 5L6 8.5V15.5L12.0667 19L18.1333 15.5V8.5L12.0667 5ZM15.5632 14.0166L12.0678 16.0331L8.57239 14.0166V9.98344L12.0678 7.96688L15.5632 9.98344V14.0166Z"
|
|
13
|
+
fill="#0847F7"
|
|
14
|
+
/>
|
|
15
|
+
</svg>
|
|
16
|
+
);
|
|
17
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const ClockIcon = ({ className }: { className?: string }) => {
|
|
2
|
+
return (
|
|
3
|
+
<svg
|
|
4
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
5
|
+
fill="none"
|
|
6
|
+
viewBox="0 0 16 16"
|
|
7
|
+
stroke="currentColor"
|
|
8
|
+
className={className}
|
|
9
|
+
>
|
|
10
|
+
<path d="M8 0.5C12.1421 0.5 15.5 3.85786 15.5 8C15.5 12.0748 12.2503 15.3875 8.20117 15.4941L4.35352 11.6465C3.67047 10.924 2.67065 9.92446 1.84375 9.10742C1.42752 8.69616 1.05392 8.32896 0.78418 8.06543C0.671658 7.9555 0.576752 7.86393 0.504883 7.79395C0.614072 3.7471 3.92679 0.5 8 0.5Z" />
|
|
11
|
+
<path d="M8 4V8H10.0011" />
|
|
12
|
+
</svg>
|
|
13
|
+
);
|
|
14
|
+
};
|
|
@@ -6,10 +6,14 @@ export const CloseIcon: FC<{ className?: string }> = ({ className }) => {
|
|
|
6
6
|
height="18"
|
|
7
7
|
width="18"
|
|
8
8
|
xmlns="http://www.w3.org/2000/svg"
|
|
9
|
-
viewBox="0 0 10
|
|
9
|
+
viewBox="0 0 10 9"
|
|
10
|
+
fill="none"
|
|
11
|
+
stroke="currentColor"
|
|
10
12
|
className={className || ""}
|
|
11
13
|
>
|
|
12
|
-
|
|
14
|
+
{/* Hairline 1px stroke at any render size, as in the design */}
|
|
15
|
+
<path vectorEffect="non-scaling-stroke" d="M0 0.5H1L9 8.5H10" />
|
|
16
|
+
<path vectorEffect="non-scaling-stroke" d="M10 0.5H9L1 8.5H0" />
|
|
13
17
|
</svg>
|
|
14
18
|
);
|
|
15
19
|
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export const WarningIcon = ({ className }: { className?: string }) => {
|
|
2
|
+
return (
|
|
3
|
+
<svg
|
|
4
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
5
|
+
viewBox="0 0 14 14"
|
|
6
|
+
fill="none"
|
|
7
|
+
className={className}
|
|
8
|
+
>
|
|
9
|
+
<path
|
|
10
|
+
d="M11 0C12.6568 6.60986e-05 14 1.34319 14 3V11C14 12.6568 12.6568 13.9999 11 14H3C1.34315 14 1.61073e-08 12.6569 0 11V3C1.28858e-07 1.34315 1.34315 0 3 0H11Z"
|
|
11
|
+
fill="#F46F28"
|
|
12
|
+
/>
|
|
13
|
+
<path
|
|
14
|
+
d="M7.66699 9.66699V11H6.33301V9.66699H7.66699ZM8 3V4.33301L7.66699 5.66699V8.33301H6.33301V5.66699L6 4.33301V3H8Z"
|
|
15
|
+
fill="#0B0B0B"
|
|
16
|
+
/>
|
|
17
|
+
</svg>
|
|
18
|
+
);
|
|
19
|
+
};
|
|
@@ -1,12 +1,17 @@
|
|
|
1
|
+
export * from "./AdjustmentsIcon";
|
|
1
2
|
export * from "./ArrowDownIcon";
|
|
3
|
+
export * from "./ClockIcon";
|
|
2
4
|
export * from "./ArrowDownLongIcon";
|
|
3
5
|
export * from "./ArrowLeftIcon";
|
|
4
6
|
export * from "./ArrowRightIcon";
|
|
5
7
|
export * from "./ArrowsExchange";
|
|
6
8
|
export * from "./ArrowUpIcon";
|
|
7
9
|
export * from "./ArrowUpRightIcon";
|
|
10
|
+
export * from "./CaretDownIcon";
|
|
11
|
+
export * from "./CaretsUpDownIcon";
|
|
8
12
|
export * from "./CashbacklIcon";
|
|
9
13
|
export * from "./ChainlinkIcon";
|
|
14
|
+
export * from "./ChainlinkMarkIcon";
|
|
10
15
|
export * from "./CheckIcon";
|
|
11
16
|
export * from "./ChevronDownIcon";
|
|
12
17
|
export * from "./ChevronUpIcon";
|
|
@@ -37,6 +42,7 @@ export * from "./ThumbUpRoundedIcon";
|
|
|
37
42
|
export * from "./TimerIcon";
|
|
38
43
|
export * from "./TokensIcon";
|
|
39
44
|
export * from "./WaitingIcon";
|
|
45
|
+
export * from "./WarningIcon";
|
|
40
46
|
export * from "./WalletConnectIcon";
|
|
41
47
|
export * from "./XMarkIcon";
|
|
42
48
|
export * from "./XPowerIcon";
|
|
@@ -7,6 +7,11 @@ import { FC, ReactNode } from "react";
|
|
|
7
7
|
* - Modals needs to be inside all app's main contexts,
|
|
8
8
|
* - Modals' need to rerender on props change.
|
|
9
9
|
*
|
|
10
|
+
* Presentation follows the swap-v2 design:
|
|
11
|
+
* - sm and up: the content replaces the widget in place (fills the widget
|
|
12
|
+
* container with the same radius), or is centered when in overlay mode,
|
|
13
|
+
* - below sm: a bottom sheet pinned to the viewport with a drag handle.
|
|
14
|
+
*
|
|
10
15
|
* Usage:
|
|
11
16
|
* ```
|
|
12
17
|
* const [open, setOpen] = useState(false);
|
|
@@ -30,12 +35,14 @@ export const Modal: FC<{
|
|
|
30
35
|
return (
|
|
31
36
|
<div
|
|
32
37
|
className={`${
|
|
33
|
-
overlay
|
|
34
|
-
|
|
38
|
+
overlay
|
|
39
|
+
? "fixed bg-[rgba(0,0,0,0.8)]"
|
|
40
|
+
: "fixed sm:absolute bg-[rgba(2,2,2,0.8)] sm:bg-transparent"
|
|
41
|
+
} top-0 left-0 right-0 bottom-0 flex flex-col justify-end sm:justify-center items-center z-10 ${
|
|
35
42
|
className || ""
|
|
36
43
|
}`}
|
|
37
44
|
onMouseDown={(e) => {
|
|
38
|
-
if (e.target === e.currentTarget
|
|
45
|
+
if (e.target === e.currentTarget) {
|
|
39
46
|
e.stopPropagation();
|
|
40
47
|
e.nativeEvent.stopImmediatePropagation();
|
|
41
48
|
e.preventDefault();
|
|
@@ -44,13 +51,11 @@ export const Modal: FC<{
|
|
|
44
51
|
}}
|
|
45
52
|
>
|
|
46
53
|
<div
|
|
47
|
-
className=
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
maxWidth: "480px",
|
|
51
|
-
maxHeight: "100%",
|
|
52
|
-
}}
|
|
54
|
+
className={`flex flex-col w-full bg-gradient-to-r from-t_bg_secondary/50 to-t_bg_secondary/50 bg-t_bg_primary border-t sm:border border-solid border-t_text_primary border-opacity-10 rounded-t-[24px] rounded-b-none sm:rounded-[32px] p-4 pt-2.5 sm:p-8 h-[85vh] max-h-full overflow-hidden ${
|
|
55
|
+
overlay ? "sm:h-[600px] sm:max-w-[636px]" : "sm:h-full"
|
|
56
|
+
}`}
|
|
53
57
|
>
|
|
58
|
+
<div className="sm:hidden mx-auto mb-4 h-1 w-8 rounded-full bg-t_bg_tertiary bg-opacity-10 shrink-0" />
|
|
54
59
|
{children}
|
|
55
60
|
</div>
|
|
56
61
|
</div>
|
|
@@ -1,11 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ChainlinkMarkIcon } from "@src/assets/icons";
|
|
2
2
|
|
|
3
3
|
export const PoweredBy = () => {
|
|
4
4
|
return (
|
|
5
|
-
<div className="flex justify-
|
|
6
|
-
<
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
<div className="flex justify-end items-center">
|
|
6
|
+
<div className="flex items-center gap-2.5 px-3 py-2 rounded-lg bg-gradient-to-r from-t_bg_tertiary/0 to-t_bg_tertiary/5">
|
|
7
|
+
<div className="flex items-center [&_svg]:h-5 [&_svg]:w-5">
|
|
8
|
+
<ChainlinkMarkIcon />
|
|
9
|
+
</div>
|
|
10
|
+
<p className="text-xs whitespace-nowrap leading-5 tracking-[0.015em]">
|
|
11
|
+
<span className="text-t_text_primary text-opacity-70">
|
|
12
|
+
{`POWERED BY `}
|
|
13
|
+
</span>
|
|
14
|
+
<span className="text-t_text_primary">CCIP.</span>
|
|
15
|
+
</p>
|
|
9
16
|
</div>
|
|
10
17
|
</div>
|
|
11
18
|
);
|
|
@@ -1,58 +1,72 @@
|
|
|
1
|
-
import {
|
|
2
|
-
CloseIcon,
|
|
3
|
-
HistoryGradientBgIcon,
|
|
4
|
-
SettingsGradientBgIcon,
|
|
5
|
-
} from "@src/assets/icons";
|
|
1
|
+
import { AdjustmentsIcon, ClockIcon, CloseIcon } from "@src/assets/icons";
|
|
6
2
|
import { useSwapContext } from "@src/context";
|
|
3
|
+
import { FC, ReactNode } from "react";
|
|
7
4
|
|
|
8
5
|
type Props = {
|
|
9
6
|
onClose: () => void;
|
|
10
7
|
};
|
|
8
|
+
|
|
9
|
+
const HeaderIconButton: FC<{
|
|
10
|
+
onClick: () => void;
|
|
11
|
+
label: string;
|
|
12
|
+
children: ReactNode;
|
|
13
|
+
}> = ({ onClick, label, children }) => (
|
|
14
|
+
<button
|
|
15
|
+
type="button"
|
|
16
|
+
onClick={onClick}
|
|
17
|
+
aria-label={label}
|
|
18
|
+
className="flex items-center justify-center w-8 h-8 rounded-md bg-t_bg_tertiary bg-opacity-5 text-t_text_primary hover:bg-opacity-10 transition-colors"
|
|
19
|
+
>
|
|
20
|
+
{children}
|
|
21
|
+
</button>
|
|
22
|
+
);
|
|
23
|
+
|
|
11
24
|
export const Controls = ({ onClose }: Props) => {
|
|
12
25
|
const { tab, setTab, overlay } = useSwapContext();
|
|
13
26
|
|
|
14
27
|
return (
|
|
15
|
-
<div className="flex gap-
|
|
28
|
+
<div className="flex gap-2 items-center">
|
|
16
29
|
{tab === "Swap" ? (
|
|
17
30
|
<>
|
|
18
|
-
<
|
|
19
|
-
type="button"
|
|
31
|
+
<HeaderIconButton
|
|
20
32
|
onClick={() => setTab("Settings")}
|
|
21
|
-
|
|
22
|
-
className="fill-white"
|
|
33
|
+
label="Open preferences"
|
|
23
34
|
>
|
|
24
|
-
<
|
|
25
|
-
</
|
|
26
|
-
<
|
|
27
|
-
type="button"
|
|
35
|
+
<AdjustmentsIcon className="w-4 h-4 opacity-50" />
|
|
36
|
+
</HeaderIconButton>
|
|
37
|
+
<HeaderIconButton
|
|
28
38
|
onClick={() => setTab("History")}
|
|
29
|
-
|
|
30
|
-
className="fill-white"
|
|
39
|
+
label="Open activity"
|
|
31
40
|
>
|
|
32
|
-
<
|
|
33
|
-
</
|
|
41
|
+
<ClockIcon className="w-4 h-4 opacity-50" />
|
|
42
|
+
</HeaderIconButton>
|
|
34
43
|
{overlay && (
|
|
35
|
-
<
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
>
|
|
41
|
-
<CloseIcon />
|
|
42
|
-
</button>
|
|
44
|
+
<HeaderIconButton onClick={onClose} label="Close">
|
|
45
|
+
<div className="w-4 h-4 opacity-50 fill-t_text_primary flex items-center justify-center">
|
|
46
|
+
<CloseIcon />
|
|
47
|
+
</div>
|
|
48
|
+
</HeaderIconButton>
|
|
43
49
|
)}
|
|
44
50
|
</>
|
|
45
51
|
) : (
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
<>
|
|
53
|
+
{tab === "Settings" && (
|
|
54
|
+
<HeaderIconButton
|
|
55
|
+
onClick={() => setTab("Settings")}
|
|
56
|
+
label="Preferences"
|
|
57
|
+
>
|
|
58
|
+
<AdjustmentsIcon className="w-4 h-4 opacity-50" />
|
|
59
|
+
</HeaderIconButton>
|
|
60
|
+
)}
|
|
61
|
+
<HeaderIconButton
|
|
62
|
+
onClick={() => setTab("Swap")}
|
|
63
|
+
label="Back to swap"
|
|
64
|
+
>
|
|
65
|
+
<div className="w-4 h-4 opacity-50 fill-t_text_primary flex items-center justify-center">
|
|
66
|
+
<CloseIcon />
|
|
67
|
+
</div>
|
|
68
|
+
</HeaderIconButton>
|
|
69
|
+
</>
|
|
56
70
|
)}
|
|
57
71
|
</div>
|
|
58
72
|
);
|
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
import { useSwapContext } from "@src/context";
|
|
2
2
|
import { Controls } from "./Controls";
|
|
3
3
|
|
|
4
|
+
const DEFAULT_WIDGET_TITLE = "XLane.";
|
|
5
|
+
|
|
4
6
|
type Props = {
|
|
5
7
|
onClose: () => void;
|
|
6
8
|
};
|
|
7
9
|
export const Header = ({ onClose }: Props) => {
|
|
8
|
-
const { tab,
|
|
10
|
+
const { tab, integrationConfig } = useSwapContext();
|
|
11
|
+
|
|
12
|
+
const title =
|
|
13
|
+
tab === "Settings"
|
|
14
|
+
? "Preferences"
|
|
15
|
+
: tab === "History"
|
|
16
|
+
? "My Activity"
|
|
17
|
+
: integrationConfig.widgetTitle || DEFAULT_WIDGET_TITLE;
|
|
9
18
|
|
|
10
19
|
return (
|
|
11
|
-
<div className="flex justify-between
|
|
12
|
-
<div className="text-
|
|
13
|
-
{tab === "Swap" && bridgeUI ? "Bridge" : tab}
|
|
14
|
-
</div>
|
|
20
|
+
<div className="flex justify-between items-center">
|
|
21
|
+
<div className="text-2xl leading-8 tracking-[0.01em]">{title}</div>
|
|
15
22
|
<Controls onClose={onClose} />
|
|
16
23
|
</div>
|
|
17
24
|
);
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import {
|
|
2
|
+
faArrowUpRightFromSquare,
|
|
3
|
+
faChevronRight,
|
|
4
|
+
} from "@fortawesome/free-solid-svg-icons";
|
|
5
|
+
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
6
|
+
import {
|
|
7
|
+
Spinner,
|
|
8
|
+
Timer,
|
|
9
|
+
TimerDisplayFormat,
|
|
10
|
+
TokenLogo,
|
|
11
|
+
} from "@src/components";
|
|
12
|
+
import { CCIP_EXPLORER } from "@src/constants";
|
|
13
|
+
import { useSwapContext } from "@src/context";
|
|
14
|
+
import { useWallet } from "@src/context/WalletProvider";
|
|
15
|
+
import { Transaction } from "@src/models";
|
|
16
|
+
import { shortAddress, weiToHumanReadable } from "@src/utils";
|
|
17
|
+
import { format } from "date-fns";
|
|
18
|
+
import { useMemo } from "react";
|
|
19
|
+
|
|
20
|
+
interface Props {
|
|
21
|
+
transaction: Transaction;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const ActivityCard = ({ transaction }: Props) => {
|
|
25
|
+
const { supportedChains, findTokenDataByAddressAndChain, tokenPrices } =
|
|
26
|
+
useSwapContext();
|
|
27
|
+
const { evm, solana } = useWallet();
|
|
28
|
+
|
|
29
|
+
const receiverAddress =
|
|
30
|
+
transaction.receiver ||
|
|
31
|
+
(transaction.sourceChainId === "mainnet-beta"
|
|
32
|
+
? solana.address
|
|
33
|
+
: evm.address);
|
|
34
|
+
|
|
35
|
+
const srcToken = findTokenDataByAddressAndChain(
|
|
36
|
+
transaction.sourceChainId === "mainnet-beta"
|
|
37
|
+
? transaction.tokenAddress
|
|
38
|
+
: transaction.tokenAddress.toLowerCase(),
|
|
39
|
+
transaction.sourceChainId,
|
|
40
|
+
);
|
|
41
|
+
const dstToken = transaction.tokenOutAddress
|
|
42
|
+
? findTokenDataByAddressAndChain(
|
|
43
|
+
transaction.sourceChainId === "mainnet-beta"
|
|
44
|
+
? transaction.tokenOutAddress
|
|
45
|
+
: transaction.tokenOutAddress?.toLowerCase(),
|
|
46
|
+
transaction.targetChainId,
|
|
47
|
+
)
|
|
48
|
+
: undefined;
|
|
49
|
+
|
|
50
|
+
const srcChain = supportedChains.find(
|
|
51
|
+
(chain) => chain.chainId === transaction.sourceChainId,
|
|
52
|
+
);
|
|
53
|
+
const dstChain = supportedChains.find(
|
|
54
|
+
(chain) => chain.chainId === transaction.targetChainId,
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const date = format(
|
|
58
|
+
new Date(transaction.timestamp * 1000),
|
|
59
|
+
"dd.MM.yyyy · hh.mmaa",
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
const receivedToken = dstToken || srcToken;
|
|
63
|
+
|
|
64
|
+
const amountToken =
|
|
65
|
+
transaction.tokenOutAmount && dstToken ? dstToken : srcToken;
|
|
66
|
+
|
|
67
|
+
const amount = useMemo(() => {
|
|
68
|
+
if (transaction.tokenOutAmount && dstToken) {
|
|
69
|
+
return weiToHumanReadable({
|
|
70
|
+
amount: transaction.tokenOutAmount,
|
|
71
|
+
decimals: dstToken.decimals,
|
|
72
|
+
precisionFractionalPlaces: 4,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
return weiToHumanReadable({
|
|
76
|
+
amount: transaction.amountWei,
|
|
77
|
+
decimals: srcToken?.decimals || 18,
|
|
78
|
+
precisionFractionalPlaces: 4,
|
|
79
|
+
});
|
|
80
|
+
}, [transaction, srcToken, dstToken]);
|
|
81
|
+
|
|
82
|
+
const amountUsd = useMemo(() => {
|
|
83
|
+
const token = dstToken || srcToken;
|
|
84
|
+
const chainId = dstToken
|
|
85
|
+
? transaction.targetChainId
|
|
86
|
+
: transaction.sourceChainId;
|
|
87
|
+
if (!token) return null;
|
|
88
|
+
const price = tokenPrices[chainId]?.[token.address];
|
|
89
|
+
if (!price) return null;
|
|
90
|
+
return (Number(amount.replace(/,/g, "")) * Number(price)).toFixed(2);
|
|
91
|
+
}, [amount, dstToken, srcToken, tokenPrices, transaction]);
|
|
92
|
+
|
|
93
|
+
const explorerUrl = transaction.messageId
|
|
94
|
+
? `${CCIP_EXPLORER}${transaction.messageId}`
|
|
95
|
+
: `${
|
|
96
|
+
supportedChains.find(
|
|
97
|
+
(chain) => chain.chainId === transaction.sourceChainId,
|
|
98
|
+
)?.transactionExplorer
|
|
99
|
+
}/${transaction.hash}`;
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<div className="flex flex-col gap-px pb-2">
|
|
103
|
+
<div className="flex items-center justify-between gap-2 px-4 py-2 rounded-t-xl bg-t_bg_tertiary bg-opacity-5">
|
|
104
|
+
<p className="text-xs leading-[18px] tracking-[0.01em] text-t_text_primary text-opacity-50 whitespace-nowrap">
|
|
105
|
+
{date}
|
|
106
|
+
</p>
|
|
107
|
+
<div className="flex items-center gap-2">
|
|
108
|
+
{transaction.status === "IN_PROGRESS" ? (
|
|
109
|
+
<div className="flex items-center gap-1.5 px-2 py-1 rounded-md bg-t_warning_dark bg-opacity-10 text-t_text_orange">
|
|
110
|
+
<Spinner className="w-3 h-3" />
|
|
111
|
+
<p className="text-xs leading-[18px] whitespace-nowrap">
|
|
112
|
+
{`Est. Time `}
|
|
113
|
+
<Timer
|
|
114
|
+
displayFormat={TimerDisplayFormat.Minutes}
|
|
115
|
+
expiryTimestamp={
|
|
116
|
+
new Date(transaction.estimatedDeliveryTimestamp)
|
|
117
|
+
}
|
|
118
|
+
customTextOnExpire="finalizing"
|
|
119
|
+
/>
|
|
120
|
+
</p>
|
|
121
|
+
</div>
|
|
122
|
+
) : transaction.status === "DONE" ? (
|
|
123
|
+
<p className="text-xs leading-[18px] text-t_text_green px-2 py-1">
|
|
124
|
+
Done
|
|
125
|
+
</p>
|
|
126
|
+
) : transaction.status === "REVERTED" ? (
|
|
127
|
+
<p className="text-xs leading-[18px] text-t_text_red px-2 py-1">
|
|
128
|
+
Reverted
|
|
129
|
+
</p>
|
|
130
|
+
) : null}
|
|
131
|
+
<a
|
|
132
|
+
href={explorerUrl}
|
|
133
|
+
target="_blank"
|
|
134
|
+
rel="noreferrer"
|
|
135
|
+
className="flex items-center justify-center w-6 h-6 rounded-md bg-t_bg_tertiary bg-opacity-5 no-underline text-t_text_primary hover:bg-opacity-10 transition-colors"
|
|
136
|
+
aria-label="Show the transaction in the chain explorer"
|
|
137
|
+
>
|
|
138
|
+
<FontAwesomeIcon
|
|
139
|
+
icon={faArrowUpRightFromSquare}
|
|
140
|
+
className="w-3 h-3 opacity-70"
|
|
141
|
+
/>
|
|
142
|
+
</a>
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
<div className="flex items-center gap-3 px-4 py-3 rounded-b-xl bg-t_bg_tertiary bg-opacity-[0.03]">
|
|
146
|
+
<div className="flex items-center shrink-0">
|
|
147
|
+
{dstToken && (
|
|
148
|
+
<div className="flex items-end -mr-2">
|
|
149
|
+
<TokenLogo
|
|
150
|
+
token={srcToken}
|
|
151
|
+
className="min-w-[38px] w-[38px] h-[38px] -mr-2.5"
|
|
152
|
+
generatedLogoClassName="min-w-[38px] w-[38px] h-[38px] text-[15px] -mr-2.5"
|
|
153
|
+
/>
|
|
154
|
+
{srcChain && (
|
|
155
|
+
<img
|
|
156
|
+
src={srcChain.image}
|
|
157
|
+
alt={srcChain.name}
|
|
158
|
+
className="w-4 h-4 shrink-0 border border-solid border-t_bg_secondary rounded-full"
|
|
159
|
+
/>
|
|
160
|
+
)}
|
|
161
|
+
</div>
|
|
162
|
+
)}
|
|
163
|
+
<div className="flex items-end">
|
|
164
|
+
<TokenLogo
|
|
165
|
+
token={receivedToken}
|
|
166
|
+
className="min-w-[38px] w-[38px] h-[38px] -mr-2.5"
|
|
167
|
+
generatedLogoClassName="min-w-[38px] w-[38px] h-[38px] text-[15px] -mr-2.5"
|
|
168
|
+
/>
|
|
169
|
+
{dstChain && (
|
|
170
|
+
<img
|
|
171
|
+
src={dstChain.image}
|
|
172
|
+
alt={dstChain.name}
|
|
173
|
+
className="w-4 h-4 shrink-0 border border-solid border-t_bg_secondary rounded-full"
|
|
174
|
+
/>
|
|
175
|
+
)}
|
|
176
|
+
</div>
|
|
177
|
+
</div>
|
|
178
|
+
<div className="flex justify-between items-center gap-2 w-full min-w-0">
|
|
179
|
+
<div className="text-left min-w-0">
|
|
180
|
+
<div className="flex items-center gap-1.5 text-sm font-medium leading-5 tracking-[0.01em] text-t_text_primary">
|
|
181
|
+
<span>{srcToken?.symbol || "?"}</span>
|
|
182
|
+
{dstToken && (
|
|
183
|
+
<>
|
|
184
|
+
<FontAwesomeIcon
|
|
185
|
+
icon={faChevronRight}
|
|
186
|
+
className="w-2.5 h-2.5 opacity-50"
|
|
187
|
+
/>
|
|
188
|
+
<span>{dstToken.symbol}</span>
|
|
189
|
+
</>
|
|
190
|
+
)}
|
|
191
|
+
</div>
|
|
192
|
+
<div className="flex gap-2 items-center text-xs leading-[18px] tracking-[0.01em] text-t_text_primary text-opacity-50">
|
|
193
|
+
<span className="whitespace-nowrap truncate">
|
|
194
|
+
{`Receiver ${receiverAddress ? shortAddress(receiverAddress) : "-"}`}
|
|
195
|
+
</span>
|
|
196
|
+
</div>
|
|
197
|
+
</div>
|
|
198
|
+
<div className="text-right shrink-0">
|
|
199
|
+
<div className="text-sm font-medium leading-5 tracking-[0.01em] text-t_text_primary">
|
|
200
|
+
{amountToken ? `${amount} ${amountToken.symbol}` : amount}
|
|
201
|
+
</div>
|
|
202
|
+
{amountUsd && (
|
|
203
|
+
<div className="text-xs leading-[18px] tracking-[0.01em] text-t_text_primary text-opacity-50">
|
|
204
|
+
{`${amountUsd} USD`}
|
|
205
|
+
</div>
|
|
206
|
+
)}
|
|
207
|
+
</div>
|
|
208
|
+
</div>
|
|
209
|
+
</div>
|
|
210
|
+
</div>
|
|
211
|
+
);
|
|
212
|
+
};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { Spinner
|
|
1
|
+
import { Spinner } from "@src/components";
|
|
2
2
|
import { useHistory } from "@src/context";
|
|
3
3
|
import { useWallet } from "@src/context/WalletProvider";
|
|
4
4
|
import { useMemo } from "react";
|
|
5
5
|
import { Virtuoso } from "react-virtuoso";
|
|
6
|
+
import { ActivityCard } from "./ActivityCard";
|
|
6
7
|
|
|
7
8
|
export const HistoryView = () => {
|
|
8
9
|
const { history, historyLoadedOnce, solanaHistory, solanaHistoryLoadedOnce } =
|
|
@@ -27,12 +28,29 @@ export const HistoryView = () => {
|
|
|
27
28
|
historyLoadedOnce && (solanaAddress ? solanaHistoryLoadedOnce : true);
|
|
28
29
|
|
|
29
30
|
return isHistoryLoaded ? (
|
|
30
|
-
<div className="
|
|
31
|
-
|
|
31
|
+
<div className="flex flex-col items-center justify-center gap-2 h-full min-h-[320px] text-center">
|
|
32
|
+
<p className="text-sm font-medium text-t_text_primary">
|
|
33
|
+
Nothing here yet...
|
|
34
|
+
</p>
|
|
35
|
+
<p className="text-xs leading-[18px] text-t_text_primary text-opacity-50">
|
|
36
|
+
Your swaps will show up here
|
|
37
|
+
<br />
|
|
38
|
+
once you make the first one.
|
|
39
|
+
</p>
|
|
32
40
|
</div>
|
|
33
41
|
) : (
|
|
34
|
-
<div className="flex justify-center
|
|
42
|
+
<div className="flex flex-col items-center justify-center gap-4 h-full min-h-[320px] text-center">
|
|
35
43
|
<Spinner className="w-8 h-8" />
|
|
44
|
+
<div className="flex flex-col gap-1">
|
|
45
|
+
<p className="text-sm font-medium text-t_text_primary">
|
|
46
|
+
Where's it...
|
|
47
|
+
</p>
|
|
48
|
+
<p className="text-xs leading-[18px] text-t_text_primary text-opacity-50">
|
|
49
|
+
We crunch data for your view.
|
|
50
|
+
<br />
|
|
51
|
+
Please wait a moment.
|
|
52
|
+
</p>
|
|
53
|
+
</div>
|
|
36
54
|
</div>
|
|
37
55
|
);
|
|
38
56
|
}
|
|
@@ -40,9 +58,9 @@ export const HistoryView = () => {
|
|
|
40
58
|
return (
|
|
41
59
|
<Virtuoso
|
|
42
60
|
data={unifiedHistory}
|
|
43
|
-
style={{ height: "
|
|
61
|
+
style={{ height: "424px" }}
|
|
44
62
|
itemContent={(_, transaction) => {
|
|
45
|
-
return <
|
|
63
|
+
return <ActivityCard transaction={transaction} />;
|
|
46
64
|
}}
|
|
47
65
|
/>
|
|
48
66
|
);
|