@rango-dev/widget-embedded 0.1.10-next.69

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 (47) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/package.json +29 -0
  3. package/public/index.html +11 -0
  4. package/readme.md +2 -0
  5. package/src/App.tsx +78 -0
  6. package/src/app.css +28 -0
  7. package/src/components/AppRouter.tsx +15 -0
  8. package/src/components/AppRoutes.tsx +32 -0
  9. package/src/components/BottomLogo.tsx +44 -0
  10. package/src/components/Footer.tsx +8 -0
  11. package/src/components/Header.tsx +25 -0
  12. package/src/components/HeaderButtons.tsx +34 -0
  13. package/src/components/Layout.tsx +55 -0
  14. package/src/components/SwitchFromAndTo.tsx +30 -0
  15. package/src/components/TokenInfo.tsx +218 -0
  16. package/src/components/UpdateUrl.tsx +85 -0
  17. package/src/constants/errors.ts +3 -0
  18. package/src/constants/navigationRoutes.ts +14 -0
  19. package/src/constants/numbers.ts +3 -0
  20. package/src/constants/searchParams.ts +7 -0
  21. package/src/globalStyles.ts +16 -0
  22. package/src/hooks/useBestRoute.ts +70 -0
  23. package/src/hooks/useConfirmSwap.ts +234 -0
  24. package/src/hooks/useTheme.ts +37 -0
  25. package/src/index.tsx +12 -0
  26. package/src/mockData/pendingSwap.ts +607 -0
  27. package/src/pages/ConfirmSwapPage.tsx +24 -0
  28. package/src/pages/ConfirmWalletsPage.tsx +51 -0
  29. package/src/pages/HistoryPage.tsx +793 -0
  30. package/src/pages/Home.tsx +176 -0
  31. package/src/pages/LiquiditySourcesPage.tsx +49 -0
  32. package/src/pages/SelectChainPage.tsx +62 -0
  33. package/src/pages/SelectTokenPage.tsx +84 -0
  34. package/src/pages/SettingsPage.tsx +60 -0
  35. package/src/pages/SwapDetailsPage.tsx +9 -0
  36. package/src/pages/WalletsPage.tsx +61 -0
  37. package/src/services/httpService.ts +3 -0
  38. package/src/store/bestRoute.ts +82 -0
  39. package/src/store/meta.ts +35 -0
  40. package/src/store/selectors.ts +19 -0
  41. package/src/store/settings.ts +72 -0
  42. package/src/store/wallets.ts +144 -0
  43. package/src/utils/common.ts +3 -0
  44. package/src/utils/numbers.ts +94 -0
  45. package/src/utils/routing.ts +108 -0
  46. package/src/utils/swap.ts +155 -0
  47. package/src/utils/wallets.ts +345 -0
@@ -0,0 +1,176 @@
1
+ import { Alert, BestRoute, Button, styled, VerticalSwapIcon } from '@rango-dev/ui';
2
+ import React, { useEffect, useState } from 'react';
3
+ import { useInRouterContext, useNavigate } from 'react-router-dom';
4
+ import { Header } from '../components/Header';
5
+ import { TokenInfo } from '../components/TokenInfo';
6
+ import { useBestRouteStore } from '../store/bestRoute';
7
+ import { BottomLogo } from '../components/BottomLogo';
8
+ import { SwithFromAndTo } from '../components/SwitchFromAndTo';
9
+ import { Footer } from '../components/Footer';
10
+ import { navigationRoutes } from '../constants/navigationRoutes';
11
+ import { useBestRoute } from '../hooks/useBestRoute';
12
+ import { useMetaStore } from '../store/meta';
13
+ import { useWalletsStore } from '../store/wallets';
14
+ import { errorMessages } from '../constants/errors';
15
+ import {
16
+ getSwapButtonTitle,
17
+ getOutputRatio,
18
+ hasLimitError,
19
+ outputRatioHasWarning,
20
+ canComputePriceImpact,
21
+ } from '../utils/swap';
22
+
23
+ const Container = styled('div', {
24
+ display: 'flex',
25
+ flexDirection: 'column',
26
+ justifyContent: 'center',
27
+ alignItems: 'center',
28
+ });
29
+
30
+ const SwitchButtonContainer = styled('div', {
31
+ display: 'flex',
32
+ justifyContent: 'center',
33
+ alignItems: 'center',
34
+ position: 'relative',
35
+ top: '11px',
36
+ });
37
+
38
+ const BestRouteContainer = styled('div', {
39
+ width: '100%',
40
+ paddingTop: '$16',
41
+ });
42
+
43
+ const Alerts = styled('div', {
44
+ paddingTop: '$16',
45
+ });
46
+
47
+ export function Home() {
48
+ const [waningMessage, setWarningMessage] = useState('');
49
+ const isRouterInContext = useInRouterContext();
50
+ const navigate = useNavigate();
51
+ const [count, setCount] = useState(0);
52
+ const fromChain = useBestRouteStore.use.fromChain();
53
+ const fromToken = useBestRouteStore.use.fromToken();
54
+ const toChain = useBestRouteStore.use.toChain();
55
+ const toToken = useBestRouteStore.use.toToken();
56
+ const setFromChain = useBestRouteStore.use.setFromChain();
57
+ const setFromToken = useBestRouteStore.use.setFromToken();
58
+ const setToChain = useBestRouteStore.use.setToChain();
59
+ const setToToken = useBestRouteStore.use.setToToken();
60
+ const setBestRoute = useBestRouteStore.use.setBestRoute();
61
+ const inputUsdValue = useBestRouteStore.use.inputUsdValue();
62
+ const inputAmount = useBestRouteStore.use.inputAmount();
63
+ const setInputAmount = useBestRouteStore.use.setInputAmount();
64
+ const outputAmount = useBestRouteStore.use.outputAmount();
65
+ const outputUsdValue = useBestRouteStore.use.outputUsdValue();
66
+ const bestRoute = useBestRouteStore.use.bestRoute();
67
+
68
+ const loadingMetaStatus = useMetaStore.use.loadingStatus();
69
+ const accounts = useWalletsStore.use.accounts();
70
+
71
+ const swithFromAndTo = () => {
72
+ setFromChain(toChain);
73
+ setFromToken(toToken);
74
+ setToChain(fromChain);
75
+ setToToken(fromToken);
76
+ setInputAmount(outputAmount?.toString() || '');
77
+ setCount((prev) => prev + 1);
78
+ };
79
+
80
+ const { data, loading: fetchingBestRoute, error: bestRouteError, retry } = useBestRoute();
81
+
82
+ const noConnectedWallet = accounts.length > 0;
83
+
84
+ const noRoutesFound = !bestRoute?.result;
85
+
86
+ const errorMessage =
87
+ loadingMetaStatus === 'failed' ? errorMessages.genericServerError : bestRouteError;
88
+
89
+ const needsToWarnEthOnPath = false;
90
+
91
+ const showBestRoute = inputAmount && (!!data || fetchingBestRoute || bestRouteError);
92
+
93
+ const outToInRatio = getOutputRatio(inputUsdValue, outputUsdValue);
94
+
95
+ const highValueLoss = outputRatioHasWarning(inputUsdValue, outToInRatio);
96
+
97
+ const priceImpactCanNotBeComputed = !canComputePriceImpact(
98
+ bestRoute,
99
+ inputAmount,
100
+ inputUsdValue,
101
+ outputUsdValue,
102
+ );
103
+
104
+ const buttonTitle = getSwapButtonTitle(
105
+ accounts,
106
+ fetchingBestRoute,
107
+ hasLimitError(bestRoute),
108
+ highValueLoss,
109
+ priceImpactCanNotBeComputed,
110
+ needsToWarnEthOnPath,
111
+ );
112
+
113
+ const buttonDisabled =
114
+ loadingMetaStatus != 'success' ||
115
+ fetchingBestRoute ||
116
+ highValueLoss ||
117
+ (noConnectedWallet && noRoutesFound) ||
118
+ hasLimitError(bestRoute);
119
+
120
+ useEffect(() => {
121
+ setBestRoute(data);
122
+ }, [data]);
123
+
124
+ return (
125
+ <Container>
126
+ <Header onClickRefresh={retry} />
127
+ <TokenInfo
128
+ type="From"
129
+ chain={fromChain}
130
+ token={fromToken}
131
+ onAmountChange={setInputAmount}
132
+ inputAmount={inputAmount}
133
+ />
134
+ <SwitchButtonContainer>
135
+ <Button variant="ghost" onClick={swithFromAndTo}>
136
+ <VerticalSwapIcon size={36} />
137
+ {isRouterInContext && <SwithFromAndTo count={count} />}
138
+ </Button>
139
+ </SwitchButtonContainer>
140
+ <TokenInfo
141
+ type="To"
142
+ chain={toChain}
143
+ token={toToken}
144
+ outputAmount={outputAmount}
145
+ outputUsdValue={outputUsdValue}
146
+ />
147
+ {showBestRoute && (
148
+ <BestRouteContainer>
149
+ <BestRoute error={bestRouteError} loading={fetchingBestRoute} data={data} />
150
+ </BestRouteContainer>
151
+ )}
152
+ {(errorMessage || waningMessage) && (
153
+ <Alerts>
154
+ {errorMessage && <Alert description={errorMessage} type="error" />}
155
+ {waningMessage && <Alert description={waningMessage} type="warning" />}
156
+ </Alerts>
157
+ )}
158
+ <Footer>
159
+ <Button
160
+ type="primary"
161
+ align="grow"
162
+ size="large"
163
+ disabled={buttonDisabled}
164
+ onClick={() => {
165
+ if (buttonTitle === 'Connect Wallet') navigate(navigationRoutes.wallets);
166
+ else {
167
+ navigate(navigationRoutes.confirmWallets);
168
+ }
169
+ }}>
170
+ {buttonTitle}
171
+ </Button>
172
+ <BottomLogo />
173
+ </Footer>
174
+ </Container>
175
+ );
176
+ }
@@ -0,0 +1,49 @@
1
+ import { LiquiditySourcesSelector } from '@rango-dev/ui';
2
+ import React from 'react';
3
+ import { useNavigate } from 'react-router-dom';
4
+ import { useMetaStore } from '../store/meta';
5
+ import { useSettingsStore } from '../store/settings';
6
+ import { removeDuplicateFrom } from '../utils/common';
7
+
8
+ export function LiquiditySourcePage() {
9
+ const { swappers } = useMetaStore.use.meta();
10
+ const toggleLiquiditySource = useSettingsStore.use.toggleLiquiditySource();
11
+ const disabledLiquiditySources = useSettingsStore.use.disabledLiquiditySources();
12
+ const toggleAllLiquiditySources = useSettingsStore.use.toggleAllLiquiditySources();
13
+
14
+ const navigate = useNavigate();
15
+
16
+ const uniqueSwappersGroups: Array<{
17
+ title: string;
18
+ logo: string;
19
+ type: 'BRIDGE' | 'AGGREGATOR' | 'DEX';
20
+ selected: boolean;
21
+ }> = [];
22
+ removeDuplicateFrom(swappers.map((s) => s.swapperGroup))
23
+ .map((swapperGroup) => {
24
+ return swappers.find((s) => s.swapperGroup === swapperGroup);
25
+ })
26
+ .find((s) => {
27
+ if (s) {
28
+ for (const type of s.types) {
29
+ uniqueSwappersGroups.push({
30
+ title: s.swapperGroup,
31
+ logo: s.logo,
32
+ type,
33
+ selected: !disabledLiquiditySources.includes(s.swapperGroup),
34
+ });
35
+ }
36
+ }
37
+ });
38
+
39
+ return (
40
+ <LiquiditySourcesSelector
41
+ toggleAll={toggleAllLiquiditySources}
42
+ list={uniqueSwappersGroups}
43
+ onChange={(liquiditySource) => toggleLiquiditySource(liquiditySource.title)}
44
+ onBack={() => {
45
+ navigate(-1);
46
+ }}
47
+ />
48
+ );
49
+ }
@@ -0,0 +1,62 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import { BlockchainSelector } from '@rango-dev/ui';
3
+ import { useBestRouteStore } from '../store/bestRoute';
4
+ import { useMetaStore } from '../store/meta';
5
+ import { useNavigate } from 'react-router-dom';
6
+ import { useWalletsStore } from '../store/wallets';
7
+ import { BlockchainMeta } from 'rango-sdk';
8
+ import { removeDuplicateFrom } from '../utils/common';
9
+
10
+ interface PropTypes {
11
+ type: 'from' | 'to';
12
+ }
13
+
14
+ export function SelectChainPage(props: PropTypes) {
15
+ const { type } = props;
16
+ const accounts = useWalletsStore.use.accounts();
17
+
18
+ const { blockchains } = useMetaStore.use.meta();
19
+ const loadingStatus = useMetaStore.use.loadingStatus();
20
+ const fromChain = useBestRouteStore.use.fromChain();
21
+ const toChain = useBestRouteStore.use.toChain();
22
+ const setFromChain = useBestRouteStore.use.setFromChain();
23
+ const setToChain = useBestRouteStore.use.setToChain();
24
+ const setFromToken = useBestRouteStore.use.setFromToken();
25
+ const setToToken = useBestRouteStore.use.setToToken();
26
+
27
+ const navigate = useNavigate();
28
+
29
+ const [list, setList] = useState(blockchains);
30
+
31
+ useEffect(() => {
32
+ let chainSortedByConnectedWallets: BlockchainMeta[] = [];
33
+ if (accounts.length !== 0) {
34
+ const connectedChains = removeDuplicateFrom(accounts.map((account) => account.chain));
35
+ chainSortedByConnectedWallets = blockchains.sort(
36
+ (blockchainA, blockchainB) =>
37
+ connectedChains.lastIndexOf(blockchainA.name) -
38
+ connectedChains.lastIndexOf(blockchainB.name),
39
+ );
40
+ } else {
41
+ chainSortedByConnectedWallets = blockchains;
42
+ }
43
+ setList(chainSortedByConnectedWallets);
44
+ }, [blockchains.length, accounts.length]);
45
+
46
+ return (
47
+ <BlockchainSelector
48
+ type={type === 'from' ? 'Source' : 'Destination'}
49
+ list={list}
50
+ selected={type === 'from' ? fromChain : toChain}
51
+ loadingStatus={loadingStatus}
52
+ onChange={(chain) => {
53
+ if (type === 'from') setFromChain(chain);
54
+ else setToChain(chain);
55
+ if (type === 'from' && fromChain?.name != chain.name) setFromToken(null);
56
+ if (type === 'to' && toChain?.name != chain.name) setToToken(null);
57
+ navigate(-1);
58
+ }}
59
+ onBack={navigate.bind(null, -1)}
60
+ />
61
+ );
62
+ }
@@ -0,0 +1,84 @@
1
+ import React from 'react';
2
+ import { TokenSelector } from '@rango-dev/ui';
3
+ import { useBestRouteStore } from '../store/bestRoute';
4
+ import { useMetaStore } from '../store/meta';
5
+ import { useNavigate } from 'react-router-dom';
6
+ import { Token } from 'rango-sdk';
7
+ import { numberToString } from '../utils/numbers';
8
+ import BigNumber from 'bignumber.js';
9
+ import { getBalanceFromWallet } from '../utils/wallets';
10
+ import { useWalletsStore } from '../store/wallets';
11
+ import { sortedTokens } from '../utils/wallets';
12
+ import { ZERO } from '../constants/numbers';
13
+
14
+ interface PropTypes {
15
+ type: 'from' | 'to';
16
+ }
17
+
18
+ export interface TokenWithBalance extends Token {
19
+ balance?: {
20
+ amount: string;
21
+ usdValue: string;
22
+ };
23
+ }
24
+
25
+ export function SelectTokenPage(props: PropTypes) {
26
+ const { type } = props;
27
+ const navigate = useNavigate();
28
+
29
+ const { tokens } = useMetaStore.use.meta();
30
+ const fromChain = useBestRouteStore.use.fromChain();
31
+ const toChain = useBestRouteStore.use.toChain();
32
+ const fromToken = useBestRouteStore.use.fromToken();
33
+ const toToken = useBestRouteStore.use.toToken();
34
+ const setFromToken = useBestRouteStore.use.setFromToken();
35
+ const setToToken = useBestRouteStore.use.setToToken();
36
+
37
+ const balance = useWalletsStore.use.balances();
38
+
39
+ const tokenWithSelectedChain = tokens.filter((token) => {
40
+ if (type === 'from') return token.blockchain === fromChain?.name;
41
+ return token.blockchain === toChain?.name;
42
+ });
43
+
44
+ const TokensWithBalance: TokenWithBalance[] = tokenWithSelectedChain.map((token) => {
45
+ const tokenAmount = numberToString(
46
+ new BigNumber(
47
+ getBalanceFromWallet(balance, token.blockchain, token.symbol, token.address)?.amount ||
48
+ ZERO,
49
+ ),
50
+ );
51
+
52
+ let tokenUsdValue = '';
53
+ if (token.usdPrice)
54
+ tokenUsdValue = numberToString(
55
+ new BigNumber(
56
+ getBalanceFromWallet(balance, token.blockchain, token.symbol, token.address)?.amount ||
57
+ ZERO,
58
+ ).multipliedBy(token.usdPrice),
59
+ );
60
+ return {
61
+ ...token,
62
+ balance: {
63
+ amount: tokenAmount !== '0' ? tokenAmount : '',
64
+ usdValue: tokenUsdValue !== '0' ? tokenUsdValue : '',
65
+ },
66
+ };
67
+ });
68
+
69
+ const sortedTokenList = sortedTokens(TokensWithBalance, type, balance);
70
+
71
+ return (
72
+ <TokenSelector
73
+ type={type === 'from' ? 'Source' : 'Destination'}
74
+ list={sortedTokenList}
75
+ selected={type === 'from' ? fromToken : toToken}
76
+ onChange={(token) => {
77
+ if (type === 'from') setFromToken(token);
78
+ else setToToken(token);
79
+ navigate(-1);
80
+ }}
81
+ onBack={navigate.bind(null, -1)}
82
+ />
83
+ );
84
+ }
@@ -0,0 +1,60 @@
1
+ import React from 'react';
2
+ import { Settings } from '@rango-dev/ui';
3
+ import { useSettingsStore } from '../store/settings';
4
+ import { useMetaStore } from '../store/meta';
5
+ import { useNavigate } from 'react-router-dom';
6
+ import { navigationRoutes } from '../constants/navigationRoutes';
7
+ import { removeDuplicateFrom } from '../utils/common';
8
+
9
+ export function SettingsPage() {
10
+ const slippage = useSettingsStore.use.slippage();
11
+ const setSlippage = useSettingsStore.use.setSlippage();
12
+ const disabledLiquiditySources = useSettingsStore.use.disabledLiquiditySources();
13
+ const customSlippage = useSettingsStore.use.customSlippage();
14
+ const setCustomSlippage = useSettingsStore.use.setCustomSlippage();
15
+ const theme = useSettingsStore.use.theme();
16
+ const setTheme = useSettingsStore.use.setTheme();
17
+ const { swappers } = useMetaStore.use.meta();
18
+ const navigate = useNavigate();
19
+
20
+ const uniqueSwappersGroups: Array<{
21
+ title: string;
22
+ logo: string;
23
+ type: 'BRIDGE' | 'AGGREGATOR' | 'DEX';
24
+ selected: boolean;
25
+ }> = [];
26
+ removeDuplicateFrom(swappers.map((s) => s.swapperGroup))
27
+ .map((swapperGroup) => {
28
+ return swappers.find((s) => s.swapperGroup === swapperGroup);
29
+ })
30
+ .find((s) => {
31
+ if (s) {
32
+ for (const type of s.types) {
33
+ uniqueSwappersGroups.push({
34
+ title: s.swapperGroup,
35
+ logo: s.logo,
36
+ type,
37
+ selected: !disabledLiquiditySources.includes(s.swapperGroup),
38
+ });
39
+ }
40
+ }
41
+ });
42
+
43
+ return (
44
+ <Settings
45
+ slippages={[1, 2, 3, 4]}
46
+ selectedSlippage={slippage}
47
+ onSlippageChange={(slippage) => setSlippage(slippage)}
48
+ liquiditySources={uniqueSwappersGroups}
49
+ selectedLiquiditySources={uniqueSwappersGroups.filter((s) => s.selected)}
50
+ onLiquiditySourcesClick={() => navigate(navigationRoutes.liquiditySources)}
51
+ onBack={navigate.bind(null, -1)}
52
+ customSlippage={customSlippage || NaN}
53
+ onCustomSlippageChange={setCustomSlippage}
54
+ minSlippage={1}
55
+ maxSlippage={10}
56
+ selectedTheme={theme}
57
+ onThemeChange={setTheme}
58
+ />
59
+ );
60
+ }
@@ -0,0 +1,9 @@
1
+ import { SwapHistory } from '@rango-dev/ui';
2
+ import React from 'react';
3
+ import { useNavigate } from 'react-router-dom';
4
+ import { pendingSwap } from '../mockData/pendingSwap';
5
+
6
+ export function SwapDetailsPage() {
7
+ const navigate = useNavigate();
8
+ return <SwapHistory onBack={navigate.bind(null, -1)} pendingSwap={pendingSwap} />;
9
+ }
@@ -0,0 +1,61 @@
1
+ import { useNavigate } from 'react-router-dom';
2
+ import { Alert, SecondaryPage, styled, Wallet } from '@rango-dev/ui';
3
+ import React, { useState } from 'react';
4
+ import { getlistWallet } from '../utils/wallets';
5
+ import { WalletType } from '@rango-dev/wallets-shared';
6
+ import { useWallets } from '@rango-dev/wallets-core';
7
+
8
+ const ListContainer = styled('div', {
9
+ display: 'grid',
10
+ gap: '.5rem',
11
+ gridTemplateColumns: ' repeat(2, minmax(0, 1fr))',
12
+ height: '450px',
13
+ alignContent: 'baseline',
14
+ });
15
+
16
+ const AlertContainer = styled('div', {
17
+ paddingBottom: '$16',
18
+ });
19
+ export function WalletsPage() {
20
+ const navigate = useNavigate();
21
+ const { state, disconnect, getWalletInfo, connect } = useWallets();
22
+ const wallets = getlistWallet(state, getWalletInfo, Object.values(WalletType));
23
+ const [walletErrorMessage, setWalletErrorMessage] = useState('');
24
+
25
+ const onSelectWallet = async (type: WalletType) => {
26
+ const wallet = state(type);
27
+ try {
28
+ if (walletErrorMessage) setWalletErrorMessage('');
29
+ if (wallet.connected) {
30
+ await disconnect(type);
31
+ } else {
32
+ await connect(type);
33
+ }
34
+ } catch (e) {
35
+ setWalletErrorMessage('Error: ' + e.message);
36
+ }
37
+ };
38
+ return (
39
+ <SecondaryPage
40
+ title="Select Wallet"
41
+ textField={false}
42
+ onBack={() => {
43
+ navigate(-1);
44
+ }}
45
+ Content={
46
+ <>
47
+ {walletErrorMessage && (
48
+ <AlertContainer>
49
+ <Alert type="error" description={walletErrorMessage} />
50
+ </AlertContainer>
51
+ )}
52
+ <ListContainer>
53
+ {wallets.map((info, index) => (
54
+ <Wallet {...info} key={index} onClick={onSelectWallet} />
55
+ ))}
56
+ </ListContainer>
57
+ </>
58
+ }
59
+ />
60
+ );
61
+ }
@@ -0,0 +1,3 @@
1
+ import { RangoClient } from 'rango-sdk';
2
+
3
+ export const httpService = new RangoClient(process.env.REACT_API_KEY as string);
@@ -0,0 +1,82 @@
1
+ import { BestRouteResponse, BlockchainMeta } from 'rango-sdk';
2
+ import { Token } from 'rango-sdk/lib';
3
+ import { create } from 'zustand';
4
+ import { immer } from 'zustand/middleware/immer';
5
+ import BigNumber from 'bignumber.js';
6
+ import { ZERO } from '../constants/numbers';
7
+ import { getBestRouteToTokenUsdPrice } from '../utils/routing';
8
+ import createSelectors from './selectors';
9
+
10
+ const getUsdValue = (token: Token | null, amount: string) =>
11
+ new BigNumber(amount || ZERO).multipliedBy(token?.usdPrice || 0);
12
+
13
+ interface RouteState {
14
+ fromChain: BlockchainMeta | null;
15
+ toChain: BlockchainMeta | null;
16
+ inputAmount: string;
17
+ inputUsdValue: BigNumber;
18
+ outputAmount: BigNumber | null;
19
+ outputUsdValue: BigNumber;
20
+ fromToken: Token | null;
21
+ toToken: Token | null;
22
+ setFromChain: (chain: BlockchainMeta | null) => void;
23
+ setToChain: (chian: BlockchainMeta | null) => void;
24
+ setFromToken: (token: Token | null) => void;
25
+ setToToken: (token: Token | null) => void;
26
+ setInputAmount: (amount: string) => void;
27
+ bestRoute: BestRouteResponse | null;
28
+ setBestRoute: (bestRoute: BestRouteResponse | null) => void;
29
+ }
30
+
31
+ export const useBestRouteStore = createSelectors(
32
+ create<RouteState>()(
33
+ immer((set) => ({
34
+ fromChain: null,
35
+ fromToken: null,
36
+ inputAmount: '',
37
+ outputAmount: null,
38
+ inputUsdValue: new BigNumber(0),
39
+ outputUsdValue: new BigNumber(0),
40
+ toChain: null,
41
+ toToken: null,
42
+ bestRoute: null,
43
+ setBestRoute: (bestRoute) =>
44
+ set((state) => {
45
+ state.bestRoute = bestRoute;
46
+ if (!!bestRoute) {
47
+ const outputAmount = !!bestRoute.result?.outputAmount
48
+ ? new BigNumber(bestRoute.result?.outputAmount)
49
+ : null;
50
+ state.outputAmount = outputAmount;
51
+ state.outputUsdValue = new BigNumber(outputAmount || ZERO).multipliedBy(
52
+ getBestRouteToTokenUsdPrice(bestRoute) || state.toToken?.usdPrice || 0,
53
+ );
54
+ }
55
+ }),
56
+ setFromChain: (chain) =>
57
+ set((state) => {
58
+ state.fromChain = chain;
59
+ }),
60
+ setFromToken: (token) =>
61
+ set((state) => {
62
+ state.fromToken = token;
63
+ if (!!state.inputAmount)
64
+ state.inputUsdValue = getUsdValue(state.fromToken!, state.inputAmount);
65
+ }),
66
+ setToChain: (chain) =>
67
+ set((state) => {
68
+ state.toChain = chain;
69
+ }),
70
+ setToToken: (token) =>
71
+ set((state) => {
72
+ state.toToken = token;
73
+ }),
74
+ setInputAmount: (amount) =>
75
+ set((state) => {
76
+ state.inputAmount = amount;
77
+ if (!!state.fromToken)
78
+ state.inputUsdValue = getUsdValue(state.fromToken, state.inputAmount);
79
+ }),
80
+ })),
81
+ ),
82
+ );
@@ -0,0 +1,35 @@
1
+ import { create } from 'zustand';
2
+ import { MetaResponse } from 'rango-sdk';
3
+ import { httpService } from '../services/httpService';
4
+ import createSelectors from './selectors';
5
+ import { removeDuplicateFrom } from '../utils/common';
6
+
7
+ export type LoadingStatus = 'loading' | 'success' | 'failed';
8
+
9
+ interface MetaState {
10
+ meta: MetaResponse;
11
+ loadingStatus: LoadingStatus;
12
+ fetchMeta: () => Promise<void>;
13
+ }
14
+
15
+ export const useMetaStore = createSelectors(
16
+ create<MetaState>()((set) => ({
17
+ meta: { blockchains: [], popularTokens: [], swappers: [], tokens: [] },
18
+ loadingStatus: 'loading',
19
+ fetchMeta: async () => {
20
+ try {
21
+ const response = await httpService.getAllMetadata();
22
+ const chainThatHasTokenInMetaResponse = removeDuplicateFrom(
23
+ response.tokens.map((t) => t.blockchain),
24
+ );
25
+ const enabledChains = response.blockchains.filter(
26
+ (chain) => chain.enabled && chainThatHasTokenInMetaResponse.includes(chain.name),
27
+ );
28
+ response.blockchains = enabledChains.sort((a, b) => a.sort - b.sort);
29
+ set({ meta: response, loadingStatus: 'success' });
30
+ } catch (error) {
31
+ set({ loadingStatus: 'failed' });
32
+ }
33
+ },
34
+ })),
35
+ );
@@ -0,0 +1,19 @@
1
+ import { StoreApi, UseBoundStore } from 'zustand';
2
+
3
+ type State = object;
4
+
5
+ type WithSelectors<S> = S extends { getState: () => infer T }
6
+ ? S & { use: { [K in keyof T]: () => T[K] } }
7
+ : never;
8
+
9
+ const createSelectors = <S extends UseBoundStore<StoreApi<State>>>(_store: S) => {
10
+ let store = _store as WithSelectors<typeof _store>;
11
+ store.use = {};
12
+ for (let k of Object.keys(store.getState())) {
13
+ (store.use as any)[k] = () => store((s) => s[k as keyof typeof s]);
14
+ }
15
+
16
+ return store;
17
+ };
18
+
19
+ export default createSelectors;