diff --git a/packages/widget-v2/package.json b/packages/widget-v2/package.json
index cfbd31211..14a923098 100644
--- a/packages/widget-v2/package.json
+++ b/packages/widget-v2/package.json
@@ -56,6 +56,7 @@
"lodash.debounce": "^4.0.8",
"pluralize": "^8.0.0",
"rc-virtual-list": "^3.14.5",
+ "react-error-boundary": "^4.0.13",
"react-shadow-scope": "^1.0.5",
"zod": "^3.23.8"
}
diff --git a/packages/widget-v2/src/components/AssetChainInput.tsx b/packages/widget-v2/src/components/AssetChainInput.tsx
index c553c625b..7dad04173 100644
--- a/packages/widget-v2/src/components/AssetChainInput.tsx
+++ b/packages/widget-v2/src/components/AssetChainInput.tsx
@@ -6,7 +6,7 @@ import { useTheme } from 'styled-components';
import { CogIcon } from '@/icons/CogIcon';
import { Button, GhostButton } from '@/components/Button';
import { useAtom } from 'jotai';
-import { skipAssets } from '@/state/skip';
+import { skipAssets } from '@/state/skipClient';
import { useUsdValue } from '@/utils/useUsdValue';
import { formatUSD } from '@/utils/intl';
diff --git a/packages/widget-v2/src/modals/ManualAddressModal/ManualAddressModal.tsx b/packages/widget-v2/src/modals/ManualAddressModal/ManualAddressModal.tsx
index 5d69fb2a1..743fa32d2 100644
--- a/packages/widget-v2/src/modals/ManualAddressModal/ManualAddressModal.tsx
+++ b/packages/widget-v2/src/modals/ManualAddressModal/ManualAddressModal.tsx
@@ -12,9 +12,9 @@ import {
import { WALLET_LIST } from '@/modals/WalletSelectorModal/WalletSelectorFlow';
import { Button } from '@/components/Button';
import { SmallText, Text } from '@/components/Typography';
-import { destinationAssetAtom, destinationWalletAtom } from '@/state/swap';
+import { destinationAssetAtom, destinationWalletAtom } from '@/state/swapPage';
import { useAtom } from 'jotai';
-import { getChain } from '@/state/skip';
+import { getChain } from '@/state/skipClient';
export const ManualAddressModal = NiceModal.create((modalProps: ModalProps) => {
const { theme } = modalProps;
diff --git a/packages/widget-v2/src/modals/TokenAndChainSelectorModal/TokenAndChainSelectorModal.tsx b/packages/widget-v2/src/modals/TokenAndChainSelectorModal/TokenAndChainSelectorModal.tsx
index e8d104692..94e2e1f48 100644
--- a/packages/widget-v2/src/modals/TokenAndChainSelectorModal/TokenAndChainSelectorModal.tsx
+++ b/packages/widget-v2/src/modals/TokenAndChainSelectorModal/TokenAndChainSelectorModal.tsx
@@ -3,7 +3,7 @@ import { Modal, ModalProps } from '@/components/Modal';
import { Column } from '@/components/Layout';
import { styled } from 'styled-components';
import { useAtom } from 'jotai';
-import { ChainWithAsset, ClientAsset, skipAssets } from '@/state/skip';
+import { ChainWithAsset, ClientAsset, skipAssets } from '@/state/skipClient';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { VirtualList } from '@/components/VirtualList';
import {
diff --git a/packages/widget-v2/src/modals/TokenAndChainSelectorModal/TokenAndChainSelectorModalRowItem.tsx b/packages/widget-v2/src/modals/TokenAndChainSelectorModal/TokenAndChainSelectorModalRowItem.tsx
index 2dbce565f..56c678933 100644
--- a/packages/widget-v2/src/modals/TokenAndChainSelectorModal/TokenAndChainSelectorModalRowItem.tsx
+++ b/packages/widget-v2/src/modals/TokenAndChainSelectorModal/TokenAndChainSelectorModalRowItem.tsx
@@ -1,7 +1,7 @@
import { Row } from '@/components/Layout';
import { ModalRowItem } from '@/components/ModalRowItem';
import { SmallText, Text } from '@/components/Typography';
-import { ChainWithAsset, ClientAsset } from '@/state/skip';
+import { ChainWithAsset, ClientAsset } from '@/state/skipClient';
import { CircleSkeletonElement, SkeletonElement } from '@/components/Skeleton';
import { styled } from 'styled-components';
import { Chain } from '@chain-registry/types';
diff --git a/packages/widget-v2/src/modals/TokenAndChainSelectorModal/TokenAndChainSelectorModalSearchInput.tsx b/packages/widget-v2/src/modals/TokenAndChainSelectorModal/TokenAndChainSelectorModalSearchInput.tsx
index b9bb35633..52b13e58a 100644
--- a/packages/widget-v2/src/modals/TokenAndChainSelectorModal/TokenAndChainSelectorModalSearchInput.tsx
+++ b/packages/widget-v2/src/modals/TokenAndChainSelectorModal/TokenAndChainSelectorModalSearchInput.tsx
@@ -5,7 +5,7 @@ import { SkipLogoIcon } from '@/icons/SkipLogoIcon';
import { SmallText } from '@/components/Typography';
import { SearchIcon } from '@/icons/SearchIcon';
import { StyledAssetLabel } from '@/components/AssetChainInput';
-import { ClientAsset } from '@/state/skip';
+import { ClientAsset } from '@/state/skipClient';
import { LeftArrowIcon } from '@/icons/ArrowIcon';
import { useModal } from '@ebay/nice-modal-react';
import { Button } from '@/components/Button';
diff --git a/packages/widget-v2/src/modals/TransactionHistoryModal/TransactionHistoryModalItem.tsx b/packages/widget-v2/src/modals/TransactionHistoryModal/TransactionHistoryModalItem.tsx
index 507903cc7..0b00404ca 100644
--- a/packages/widget-v2/src/modals/TransactionHistoryModal/TransactionHistoryModalItem.tsx
+++ b/packages/widget-v2/src/modals/TransactionHistoryModal/TransactionHistoryModalItem.tsx
@@ -1,7 +1,7 @@
import { RouteResponse } from '@skip-go/client';
import { SmallText } from '@/components/Typography';
import { useAtom } from 'jotai';
-import { skipAssets, getChain, ClientAsset } from '@/state/skip';
+import { skipAssets, getChain, ClientAsset } from '@/state/skipClient';
import { Column, Row } from '@/components/Layout';
import styled, { useTheme } from 'styled-components';
import { getFormattedAssetAmount } from '@/utils/crypto';
diff --git a/packages/widget-v2/src/pages/ErrorPage/ErrorPage.tsx b/packages/widget-v2/src/pages/ErrorPage/ErrorPage.tsx
new file mode 100644
index 000000000..a0e672a40
--- /dev/null
+++ b/packages/widget-v2/src/pages/ErrorPage/ErrorPage.tsx
@@ -0,0 +1,13 @@
+import { errorAtom } from '@/state/errorPage';
+import { useResetAtom } from 'jotai/utils';
+
+export const ErrorPage = () => {
+ const resetError = useResetAtom(errorAtom);
+
+ return (
+
+ error page
+
+
+ );
+};
diff --git a/packages/widget-v2/src/pages/SwapExecutionPage/SwapExecutionPage.tsx b/packages/widget-v2/src/pages/SwapExecutionPage/SwapExecutionPage.tsx
index 322852e32..2dba7da34 100644
--- a/packages/widget-v2/src/pages/SwapExecutionPage/SwapExecutionPage.tsx
+++ b/packages/widget-v2/src/pages/SwapExecutionPage/SwapExecutionPage.tsx
@@ -8,7 +8,7 @@ import { useModal } from '@ebay/nice-modal-react';
import { ManualAddressModal } from '@/modals/ManualAddressModal/ManualAddressModal';
import styled, { useTheme } from 'styled-components';
import { useAtom } from 'jotai';
-import { destinationWalletAtom } from '@/state/swap';
+import { destinationWalletAtom } from '@/state/swapPage';
import { SwapExecutionPageRouteSimple } from './SwapExecutionPageRouteSimple';
import { SwapExecutionPageRouteDetailed } from './SwapExecutionPageRouteDetailed';
@@ -17,6 +17,7 @@ import { Operation, txState } from './SwapExecutionPageRouteDetailedRow';
import { SmallText } from '@/components/Typography';
import { SignatureIcon } from '@/icons/SignatureIcon';
import pluralize from 'pluralize';
+import operations from './operations.json';
enum SwapExecutionState {
destinationAddressUnset,
@@ -25,14 +26,10 @@ enum SwapExecutionState {
confirmed,
}
-export type SwapExecutionPageProps = {
- operations: Operation[];
-};
-
const SIGNATURES_REQUIRED = 2;
const TX_DELAY_MS = 5_000; // 5 seconds
-export const SwapExecutionPage = ({ operations }: SwapExecutionPageProps) => {
+export const SwapExecutionPage = () => {
const theme = useTheme();
const [destinationWallet] = useAtom(destinationWalletAtom);
@@ -157,7 +154,10 @@ export const SwapExecutionPage = ({ operations }: SwapExecutionPageProps) => {
onClick: () => setSimpleRoute(!simpleRoute),
}}
/>
-
+
{renderMainButton}
& {
+ amount?: string;
+};
+
+export const sourceAssetAtom = atom();
+
+export const destinationAssetAtom = atom();
+
+export const connectedWalletAtom = atom();
+
+export const destinationWalletAtom = atom();
diff --git a/packages/widget-v2/src/state/errorPage.ts b/packages/widget-v2/src/state/errorPage.ts
new file mode 100644
index 000000000..ddb34fedc
--- /dev/null
+++ b/packages/widget-v2/src/state/errorPage.ts
@@ -0,0 +1,7 @@
+import { atomWithReset } from 'jotai/utils';
+
+export const errorAtom = atomWithReset<
+ ExpectedErrorDetails | Error | undefined
+>(undefined);
+
+type ExpectedErrorDetails = {};
diff --git a/packages/widget-v2/src/state/router.ts b/packages/widget-v2/src/state/router.ts
new file mode 100644
index 000000000..635be0787
--- /dev/null
+++ b/packages/widget-v2/src/state/router.ts
@@ -0,0 +1,8 @@
+import { atom } from 'jotai';
+
+export enum Routes {
+ SwapPage,
+ SwapExecutionPage,
+}
+
+export const currentPageAtom = atom(Routes.SwapPage);
diff --git a/packages/widget-v2/src/state/skip.ts b/packages/widget-v2/src/state/skipClient.ts
similarity index 100%
rename from packages/widget-v2/src/state/skip.ts
rename to packages/widget-v2/src/state/skipClient.ts
diff --git a/packages/widget-v2/src/state/swap.ts b/packages/widget-v2/src/state/swapPage.ts
similarity index 89%
rename from packages/widget-v2/src/state/swap.ts
rename to packages/widget-v2/src/state/swapPage.ts
index 7331e976c..ebd689647 100644
--- a/packages/widget-v2/src/state/swap.ts
+++ b/packages/widget-v2/src/state/swapPage.ts
@@ -1,5 +1,5 @@
import { atom } from 'jotai';
-import { ClientAsset } from './skip';
+import { ClientAsset } from './skipClient';
import { Wallet } from '@/components/RenderWalletList';
export type AssetAtom = Partial & {
diff --git a/packages/widget-v2/src/stories/ManualAddressModals.stories.tsx b/packages/widget-v2/src/stories/ManualAddressModals.stories.tsx
index 1168e0c33..7a4e02d5c 100644
--- a/packages/widget-v2/src/stories/ManualAddressModals.stories.tsx
+++ b/packages/widget-v2/src/stories/ManualAddressModals.stories.tsx
@@ -4,8 +4,8 @@ import { Row } from '@/components/Layout';
import { defaultTheme, lightTheme } from '@/widget/theme';
import { ManualAddressModal } from '@/modals/ManualAddressModal/ManualAddressModal';
import { useEffect, useState } from 'react';
-import { skipAssets } from '@/state/skip';
-import { destinationAssetAtom } from '@/state/swap';
+import { skipAssets } from '@/state/skipClient';
+import { destinationAssetAtom } from '@/state/swapPage';
import { useAtom } from 'jotai';
const meta = {
diff --git a/packages/widget-v2/src/stories/SwapExecutionFlow.stories.tsx b/packages/widget-v2/src/stories/SwapExecutionPage.stories.tsx
similarity index 82%
rename from packages/widget-v2/src/stories/SwapExecutionFlow.stories.tsx
rename to packages/widget-v2/src/stories/SwapExecutionPage.stories.tsx
index 230a3c11b..9146e19d5 100644
--- a/packages/widget-v2/src/stories/SwapExecutionFlow.stories.tsx
+++ b/packages/widget-v2/src/stories/SwapExecutionPage.stories.tsx
@@ -2,12 +2,11 @@ import type { Meta, StoryObj } from '@storybook/react';
import { renderLightAndDarkTheme } from './renderLightAndDarkTheme';
import { SwapExecutionPage } from '@/pages/SwapExecutionPage/SwapExecutionPage';
import NiceModal from '@ebay/nice-modal-react';
-import { destinationAssetAtom } from '@/state/swap';
+import { destinationAssetAtom } from '@/state/swapPage';
import { useAtom } from 'jotai';
import operations from '@/pages/SwapExecutionPage/operations.json';
-import { skipAssets } from '@/state/skip';
+import { skipAssets } from '@/state/skipClient';
import { useEffect, useState } from 'react';
-import { Operation } from '@/pages/SwapExecutionPage/SwapExecutionPageRouteDetailedRow';
const meta = {
title: 'Pages/SwapExecutionPage',
@@ -16,8 +15,8 @@ const meta = {
const [_destinationAsset, setDestinationAsset] =
useAtom(destinationAssetAtom);
const [shouldRender, setShouldRender] = useState(false);
- const firstOperation = props.operations[0];
- const lastOperation = props.operations[props.operations.length - 1];
+ const firstOperation = operations[0];
+ const lastOperation = operations[operations.length - 1];
const [{ data: assets }] = useAtom(skipAssets);
@@ -57,7 +56,5 @@ type Story = StoryObj;
export default meta;
export const SwapExecutionPageExample: Story = {
- args: {
- operations: operations as Operation[],
- },
+ args: {},
};
diff --git a/packages/widget-v2/src/utils/useUsdValue.ts b/packages/widget-v2/src/utils/useUsdValue.ts
index f080fe4d8..cc036b169 100644
--- a/packages/widget-v2/src/utils/useUsdValue.ts
+++ b/packages/widget-v2/src/utils/useUsdValue.ts
@@ -1,7 +1,7 @@
import { useQuery } from '@tanstack/react-query';
import { useMemo } from 'react';
import { z } from 'zod';
-import { getAssets } from '@/state/skip';
+import { getAssets } from '@/state/skipClient';
interface Args {
coingeckoID: string;
diff --git a/packages/widget-v2/src/widget/Router.tsx b/packages/widget-v2/src/widget/Router.tsx
new file mode 100644
index 000000000..65c918d28
--- /dev/null
+++ b/packages/widget-v2/src/widget/Router.tsx
@@ -0,0 +1,31 @@
+import { ErrorPage } from '@/pages/ErrorPage/ErrorPage';
+import { SwapExecutionPage } from '@/pages/SwapExecutionPage/SwapExecutionPage';
+import { SwapPage } from '@/pages/SwapPage/SwapPage';
+import { errorAtom } from '@/state/errorPage';
+import { Routes, currentPageAtom } from '@/state/router';
+import { useAtom } from 'jotai';
+import { ErrorBoundary } from 'react-error-boundary';
+
+export const Router = () => {
+ const [currentPage] = useAtom(currentPageAtom);
+ const [error, setError] = useAtom(errorAtom);
+
+ if (error) {
+ return ;
+ }
+
+ switch (currentPage) {
+ case Routes.SwapPage:
+ return (
+ setError(error)}>
+
+
+ );
+ case Routes.SwapExecutionPage:
+ return (
+ setError(error)}>
+
+
+ );
+ }
+};
diff --git a/packages/widget-v2/src/widget/Widget.tsx b/packages/widget-v2/src/widget/Widget.tsx
index 7f828b53a..db7b8fc5b 100644
--- a/packages/widget-v2/src/widget/Widget.tsx
+++ b/packages/widget-v2/src/widget/Widget.tsx
@@ -1,10 +1,10 @@
import { ShadowDomAndProviders } from './ShadowDomAndProviders';
-import { SwapPage } from '@/pages/SwapPage/SwapPage';
import NiceModal, { useModal } from '@ebay/nice-modal-react';
import { styled } from 'styled-components';
import { Modal } from '@/components/Modal';
import { cloneElement, ReactElement } from 'react';
import { PartialTheme } from './theme';
+import { Router } from './Router';
export type SwapWidgetProps = {
theme?: PartialTheme;
@@ -15,7 +15,7 @@ export const SwapWidget = (props: SwapWidgetProps) => {
-
+
@@ -26,7 +26,7 @@ const SwapWidgetWithoutNiceModalProvider = (props: SwapWidgetProps) => {
return (
-
+
);
diff --git a/packages/widget-v2/tsconfig.app.json b/packages/widget-v2/tsconfig.app.json
index 50f728991..2b65b1b31 100644
--- a/packages/widget-v2/tsconfig.app.json
+++ b/packages/widget-v2/tsconfig.app.json
@@ -26,7 +26,7 @@
/* Aliases */
"baseUrl": ".",
"paths": {
- "@*": ["src/*"]
+ "@/*": ["src/*"]
}
},
"include": ["src"]
diff --git a/yarn.lock b/yarn.lock
index 25d554dba..6f8d0f155 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -24160,6 +24160,17 @@ __metadata:
languageName: node
linkType: hard
+"react-error-boundary@npm:^4.0.13":
+ version: 4.0.13
+ resolution: "react-error-boundary@npm:4.0.13"
+ dependencies:
+ "@babel/runtime": ^7.12.5
+ peerDependencies:
+ react: ">=16.13.1"
+ checksum: 50398d080015d51d22c6f94c56f4ea336d10232d72345b36ee6f15b6b643666d20b072829b02f091a80e5af68fe67f68a62ef0d2b649dbd759ead929304449af
+ languageName: node
+ linkType: hard
+
"react-hot-toast@npm:^2.4.1":
version: 2.4.1
resolution: "react-hot-toast@npm:2.4.1"
@@ -28495,6 +28506,7 @@ __metadata:
lodash.debounce: ^4.0.8
pluralize: ^8.0.0
rc-virtual-list: ^3.14.5
+ react-error-boundary: ^4.0.13
react-shadow-scope: ^1.0.5
storybook: ^8.2.6
styled-components: ^6.0.0