diff --git a/src/components/Providers.tsx b/src/components/Providers.tsx index fd9aa524..e2d0d44d 100644 --- a/src/components/Providers.tsx +++ b/src/components/Providers.tsx @@ -1,15 +1,16 @@ import { Analytics } from '@vercel/analytics/react'; -import { AppContext, AppContextType, ColorModeContext, SnackbarIconType } from 'contexts'; -import { CssBaseline, ThemeProvider } from 'soroswap-ui'; +import { AppContext, AppContextType, ColorModeContext, SnackbarIconType, ProtocolsStatus } from 'contexts'; import { Provider } from 'react-redux'; -import { theme } from 'soroswap-ui'; -import { useMemo, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import InkathonProvider from 'inkathon/InkathonProvider'; import MainLayout from './Layout/MainLayout'; import MySorobanReactProvider from 'soroban/MySorobanReactProvider'; import store from 'state'; import { SorobanContextType } from '@soroban-react/core'; import { SoroswapThemeProvider } from 'soroswap-ui'; +import { Protocols } from 'soroswap-router-sdk'; +import { PlatformType } from 'state/routing/types'; + export default function Providers({ children, sorobanReactProviderProps, @@ -21,6 +22,19 @@ export default function Providers({ const [maxHops, setMaxHops] = useState(2); + //Defines the default protocols to be used in the app + const defaultProtocols = [ + Protocols.SOROSWAP + ] + const [protocols, setProtocols] = useState(defaultProtocols); + + //Defines the default platforms to be used in the app + const defaultProtocolsStatus: ProtocolsStatus[] = [ + { key: Protocols.SOROSWAP, value: true }, + { key: Protocols.PHOENIX, value: false }, + { key: PlatformType.STELLAR_CLASSIC, value: true }, + ] + const [protocolsStatus, setProtocolsStatus] = useState(defaultProtocolsStatus); const [openSnackbar, setOpenSnackbar] = useState(false); const [snackbarMessage, setSnackbarMessage] = useState(''); const [snackbarTitle, setSnackbarTitle] = useState('Swapped'); @@ -54,6 +68,10 @@ export default function Providers({ Settings: { maxHops, setMaxHops, + protocols, + setProtocols, + protocolsStatus, + setProtocolsStatus, }, }; diff --git a/src/components/Settings/ProtocolsSettings/index.tsx b/src/components/Settings/ProtocolsSettings/index.tsx new file mode 100644 index 00000000..69af46ab --- /dev/null +++ b/src/components/Settings/ProtocolsSettings/index.tsx @@ -0,0 +1,132 @@ +import Expand from 'components/Expand'; +import QuestionHelper from 'components/QuestionHelper'; +import Row, { RowBetween } from 'components/Row'; +import { BodySmall } from 'components/Text'; +import { AppContext } from 'contexts'; +import React, { useContext, useEffect, useState } from 'react' +import { Box, styled, Switch, SwitchProps, Typography, useTheme } from 'soroswap-ui'; + + +export const CustomSwitch = styled((props: SwitchProps) => ( + +))(({ theme }) => ({ + width: 42, + height: 26, + padding: 0, + alignContent: 'center', + alignItems: 'center', + '& .MuiSwitch-switchBase': { + padding: 0, + margin: 3, + '&.Mui-checked': { + transform: 'translateX(16px)', + color: '#8866DD', + '& .MuiSwitch-thumb:before': { + backgroundColor: '#8866DD', + borderRadius: 32, + }, + '& + .MuiSwitch-track': { + backgroundColor: theme.palette.background.paper, + opacity: 1, + border: 0, + }, + }, + }, + '& .MuiSwitch-thumb': { + backgroundColor: 'rgba(136, 102, 221, 0.25)', + width: 20, + height: 20, + '&:before': { + content: "''", + position: 'absolute', + width: '100%', + height: '100%', + left: 0, + top: 0, + backgroundRepeat: 'no-repeat', + backgroundPosition: 'center', + }, + }, + '& .MuiSwitch-track': { + borderRadius: 32, + backgroundColor: theme.palette.background.paper, + opacity: 1, + }, +})); + +const Option = styled(Row, { + shouldForwardProp: (prop) => prop !== 'isActive', +}) <{ isActive: boolean }>` + width: auto; + cursor: pointer; + padding: 6px 12px; + text-align: center; + gap: 4px; + border-radius: 12px; + background: ${({ isActive, theme }) => + isActive ? theme.palette.customBackground.module : 'transparent'}; + pointer-events: ${({ isActive }) => isActive && 'none'}; +`; + +const firstLetterUppercase = (string: string) => { + return string.charAt(0).toUpperCase() + string.slice(1); +} +const ProtocolsSettings = () => { + const theme = useTheme(); + const [isOpen, setIsOpen] = useState(false); + const { Settings } = useContext(AppContext); + const { protocolsStatus, setProtocolsStatus } = Settings; + + const switchProtocolValue = (key: string) => { + const newProtocolsStatus = protocolsStatus.map((protocol) => { + if (protocol.key === key) { + return { + key: protocol.key, + value: !protocol.value, + }; + } + return protocol; + }); + setProtocolsStatus(newProtocolsStatus); + } + + return ( + + setIsOpen(!isOpen)} + header={ + + Protocols + + The protocols Soroswap.finance will use to calculate the most efficient path for your transaction. + + } + /> + + } + button={<>} + > + + + {protocolsStatus.map((option, index) => { + return ( + + {firstLetterUppercase(option.key)} + { switchProtocolValue(option.key) }} color="secondary" /> + + ) + })} + { } + + + + + + ) +} + +export default ProtocolsSettings diff --git a/src/components/Settings/index.tsx b/src/components/Settings/index.tsx index b620036a..c6665f5e 100644 --- a/src/components/Settings/index.tsx +++ b/src/components/Settings/index.tsx @@ -8,6 +8,7 @@ import { useRef, useState } from 'react'; import MaxSlippageSettings from './MaxSlippageSettings'; import MenuButton from './MenuButton'; import MaxHopsSettings from './MaxHopsSettings'; +import ProtocolsSettings from './ProtocolsSettings'; const Menu = styled('div')` position: relative; @@ -74,6 +75,7 @@ export default function SettingsTab({ + diff --git a/src/contexts/index.ts b/src/contexts/index.ts index f0f8a446..0b6ef333 100644 --- a/src/contexts/index.ts +++ b/src/contexts/index.ts @@ -1,4 +1,6 @@ import React from 'react'; +import { Protocols } from 'soroswap-router-sdk'; +import { PlatformType } from 'state/routing/types'; type ConnectWalletModalType = { isConnectWalletModalOpen: boolean; @@ -13,6 +15,11 @@ export enum SnackbarIconType { ERROR, } +export interface ProtocolsStatus { + key: Protocols | PlatformType; + value: boolean; +} + export type SnackbarContextType = { openSnackbar: boolean; snackbarMessage: string; @@ -27,6 +34,10 @@ export type SnackbarContextType = { export type Settings = { maxHops: number; setMaxHops: React.Dispatch>; + protocols: Protocols[]; + setProtocols: React.Dispatch>; + protocolsStatus: ProtocolsStatus[]; + setProtocolsStatus: React.Dispatch>; }; export type AppContextType = { @@ -57,5 +68,9 @@ export const AppContext = React.createContext({ Settings: { maxHops: 2, setMaxHops: () => {}, + protocols: [], + setProtocols: () => {}, + protocolsStatus: [], + setProtocolsStatus: () => {}, }, }); diff --git a/src/functions/generateRoute.ts b/src/functions/generateRoute.ts index 3004cd49..df991a24 100644 --- a/src/functions/generateRoute.ts +++ b/src/functions/generateRoute.ts @@ -20,6 +20,7 @@ import { getBestPath, getHorizonBestPath } from 'helpers/horizon/getHorizonPath' import { PlatformType } from 'state/routing/types'; import { CurrencyAmount as AmountAsset } from 'interfaces'; import { DexDistribution } from 'helpers/aggregator'; +import { fn } from 'cypress/types/jquery'; export interface BuildTradeRoute { amountCurrency: AmountAsset | CurrencyAmount; @@ -57,30 +58,44 @@ export const useRouterSDK = () => { const { isEnabled: isAggregator } = useAggregator(); const { Settings } = useContext(AppContext); - const { maxHops } = Settings; + const { maxHops, protocols, protocolsStatus } = Settings; const network = sorobanContext.activeChain?.networkPassphrase as Networks; + const getPairsFns = useMemo(() => { + console.log('Router protocols changed!') + const routerProtocols = [] + if(!shouldUseBackend) return undefined +// here you should add your new supported aggregator protocols + for(let protocol of protocolsStatus){ + if(protocol.key === Protocols.SOROSWAP && protocol.value === true){ + routerProtocols.push({protocol: Protocols.SOROSWAP, fn: async () => fetchAllSoroswapPairs(network)}); + } + if(protocol.key === Protocols.PHOENIX && protocol.value === true){ + routerProtocols.push({protocol: Protocols.PHOENIX, fn: async () => fetchAllPhoenixPairs(network)}); + } + } + console.log('New router protocols:', routerProtocols) + return routerProtocols; + }, [network, protocolsStatus]); + + const getProtocols = useMemo(() => { + const newProtocols = []; + for(let protocol of protocolsStatus){ + if(protocol.key != PlatformType.STELLAR_CLASSIC && protocol.value === true){ + newProtocols.push(protocol.key); + } + } + return newProtocols as Protocols[]; + },[protocolsStatus]); const router = useMemo(() => { - const protocols = [Protocols.SOROSWAP]; // if (isAggregator) protocols.push(Protocols.PHOENIX); return new Router({ - getPairsFns: shouldUseBackend - ? [ - { - protocol: Protocols.SOROSWAP, - fn: async () => fetchAllSoroswapPairs(network), - }, - { - protocol: Protocols.PHOENIX, - fn: async () => fetchAllPhoenixPairs(network), - }, - ] - : undefined, + getPairsFns: getPairsFns, pairsCacheInSeconds: 60, - protocols: protocols, + protocols: getProtocols, network, maxHops, }); @@ -119,8 +134,11 @@ export const useRouterSDK = () => { tradeType, }; - const horizonPath = (await getHorizonBestPath(horizonProps, sorobanContext)) as BuildTradeRoute; - + let horizonPath; + if(protocolsStatus.find((protocol) => protocol.key === PlatformType.STELLAR_CLASSIC && protocol.value === true)){ + horizonPath = (await getHorizonBestPath(horizonProps, sorobanContext)) as BuildTradeRoute; + } + let sorobanPath: BuildTradeRoute; if (isAggregator) { sorobanPath = (await router @@ -146,17 +164,17 @@ export const useRouterSDK = () => { })) as BuildTradeRoute; } const bestPath = getBestPath(horizonPath, sorobanPath, tradeType); - // .then((res) => { - // if (!res) return; - // const response = { - // ...res, - // platform: PlatformType.ROUTER, - // }; - // return response; - // }); return bestPath; }; return { generateRoute, resetRouterSdkCache, maxHops }; }; +// .then((res) => { +// if (!res) return; +// const response = { +// ...res, +// platform: PlatformType.ROUTER, +// }; +// return response; +// });