diff --git a/.env.development b/.env.development index 991aa28e..ff0c3018 100644 --- a/.env.development +++ b/.env.development @@ -1,3 +1,4 @@ +REACT_APP_URL=https://devnet-goerli.starkgate.starknet.io REACT_APP_AUTO_CONNECT=false # 20 seconds REACT_APP_POLL_BLOCK_NUMBER_INTERVAL=20000 @@ -8,5 +9,6 @@ REACT_APP_ETHERSCAN_URL=https://goerli.etherscan.io REACT_APP_VOYAGER_URL=https://goerli.voyager.online REACT_APP_LOCAL_STORAGE_TRANSFERS_LOG_KEY=STARKGATE_TRANSFERS REACT_APP_LOCAL_STORAGE_ONBOARDING_TIMESTAMP_KEY=STARKGATE_ONBOARDING_TIMESTAMP +REACT_APP_LOCAL_STORAGE_ACCEPT_TERMS=STARKGATE_ACCEPT_TERMS REACT_APP_ONBOARDING_MODAL_TIMEOUT_HRS=24 REACT_APP_SUPPORTED_TOKENS=ETH,WBTC,USDC,USDT,DAI,SLF diff --git a/.env.production b/.env.production index 474b1aa9..6de75808 100644 --- a/.env.production +++ b/.env.production @@ -1,3 +1,4 @@ +REACT_APP_URL=https://starkgate.starknet.io REACT_APP_AUTO_CONNECT=false # 30 seconds REACT_APP_POLL_BLOCK_NUMBER_INTERVAL=30000 @@ -8,5 +9,6 @@ REACT_APP_ETHERSCAN_URL=https://etherscan.io REACT_APP_VOYAGER_URL=https://voyager.online REACT_APP_LOCAL_STORAGE_TRANSFERS_LOG_KEY=STARKGATE_TRANSFERS REACT_APP_LOCAL_STORAGE_ONBOARDING_TIMESTAMP_KEY=STARKGATE_ONBOARDING_TIMESTAMP +REACT_APP_LOCAL_STORAGE_ACCEPT_TERMS=STARKGATE_ACCEPT_TERMS REACT_APP_ONBOARDING_MODAL_TIMEOUT_HRS=24 REACT_APP_SUPPORTED_TOKENS=ETH diff --git a/.env.testing b/.env.testing index 719334c4..daaec135 100644 --- a/.env.testing +++ b/.env.testing @@ -1,3 +1,4 @@ +REACT_APP_URL=https://goerli.starkgate.starknet.io REACT_APP_AUTO_CONNECT=false # 20 seconds REACT_APP_POLL_BLOCK_NUMBER_INTERVAL=20000 @@ -7,4 +8,5 @@ REACT_APP_STARKNET_CONTRACT_ADDRESS=0xde29d060D45901Fb19ED6C6e959EB22d8626708e REACT_APP_ETHERSCAN_URL=https://goerli.etherscan.io REACT_APP_VOYAGER_URL=https://goerli.voyager.online REACT_APP_LOCAL_STORAGE_TRANSFERS_LOG_KEY=STARKGATE_TRANSFERS +REACT_APP_LOCAL_STORAGE_ACCEPT_TERMS=STARKGATE_ACCEPT_TERMS REACT_APP_SUPPORTED_TOKENS=ETH,WBTC,USDC,USDT,DAI,SLF diff --git a/package.json b/package.json index 301e079a..77216893 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,8 @@ "react": "^17.0.2", "react-dom": "^17.0.2", "react-hot-toast": "^2.2.0", + "react-router": "^6.3.0", + "react-router-dom": "6", "react-scripts": "4.0.3", "starknet": "^3.7.0", "use-async-memo": "^1.2.3", diff --git a/src/App.js b/src/App.js index 908ac5f2..98a56252 100644 --- a/src/App.js +++ b/src/App.js @@ -1,12 +1,39 @@ import React from 'react'; +import {Route, Routes, useLocation} from 'react-router-dom'; import styles from './App.module.scss'; -import {Footer, Header, Main} from './components/Containers'; +import {Footer, Header} from './components/Containers'; +import {StyledBackground} from './components/UI'; +import {useApp} from './providers/AppProvider'; +import {Faq, Bridge, Login, ProtectedRoute, Terms} from './routes'; -export const App = () => ( -
-
-
-
-); +export const App = () => { + const {isLoggedIn, isAcceptTerms} = useApp(); + const {pathname} = useLocation(); + const informativeRoutes = ['/terms', '/faq']; + + return ( +
+
+ + + + + + } + path="/" + /> + } path="/terms" /> + } path="/login" /> + } path="/faq" /> + + +
+ ); +}; diff --git a/src/App.module.scss b/src/App.module.scss index 5adc9a58..9ea30172 100644 --- a/src/App.module.scss +++ b/src/App.module.scss @@ -1,9 +1,4 @@ .app { - overflow: hidden; - flex-direction: column; display: flex; - background-image: url('assets/img/stars.png'); - background-size: cover; - background-repeat: no-repeat; - background-position: center center; + flex-direction: column; } diff --git a/src/analytics/track-event.js b/src/analytics/track-event.js index 7a676501..32fb9c8a 100644 --- a/src/analytics/track-event.js +++ b/src/analytics/track-event.js @@ -5,7 +5,6 @@ export const TrackEvent = { SELECT_TOKEN_MENU: 'SELECT_TOKEN_MENU', ACCOUNT_MENU: 'ACCOUNT_MENU', TRANSFER_MENU: 'TRANSFER_MENU', - FAQ_MENU: 'FAQ_MENU', /** * Transfer menu @@ -55,6 +54,16 @@ export const TrackEvent = { LOGIN_ERROR: 'LOGIN_SCREEN/login_error' }, + /** + * Terms screen + */ + TERMS_SCREEN: 'TERMS_SCREEN', + TERMS: { + ACCEPT_CLICK: 'TERMS_SCREEN/accept_click' + }, + + FAQ_SCREEN: 'FAQ_SCREEN', + /** * Tabs */ diff --git a/src/components/Containers/Footer/Footer.js b/src/components/Containers/Footer/Footer.js index 8ec7d0ba..f67e92e8 100644 --- a/src/components/Containers/Footer/Footer.js +++ b/src/components/Containers/Footer/Footer.js @@ -10,9 +10,7 @@ export const Footer = () => { return !isMobile(breakpoint) ? ( ) : null; }; diff --git a/src/components/Containers/Footer/Footer.module.scss b/src/components/Containers/Footer/Footer.module.scss index 5abb6db2..1d0139cf 100644 --- a/src/components/Containers/Footer/Footer.module.scss +++ b/src/components/Containers/Footer/Footer.module.scss @@ -7,12 +7,20 @@ $copyright-color: $--color-beta; font-size: 13px; height: #{$height}px; border-top: 1px solid transparent; + width: 100%; + display: flex; + justify-content: space-around; + align-items: center; + position: absolute; + bottom: 0; + left: 0; + right: 0; + overflow: hidden; + background: transparent; .copyright { color: $copyright-color; - display: flex; font-size: 12px; line-height: 24px; - margin: auto; } } diff --git a/src/components/Containers/Header/Header.js b/src/components/Containers/Header/Header.js index 41591538..124ffe5e 100644 --- a/src/components/Containers/Header/Header.js +++ b/src/components/Containers/Header/Header.js @@ -1,4 +1,5 @@ import React from 'react'; +import {useNavigate, useLocation} from 'react-router-dom'; import useBreakpoint from 'use-breakpoint'; import {track} from '../../../analytics'; @@ -6,40 +7,59 @@ import {ReactComponent as StarkGateLogo} from '../../../assets/img/starkgate.svg import constants from '../../../config/constants'; import {Breakpoint} from '../../../enums'; import {useColors} from '../../../hooks'; +import {useLogin} from '../../../providers/AppProvider'; import {useMenu} from '../../../providers/MenuProvider'; import {useIsL1, useIsL2} from '../../../providers/TransferProvider'; import {useL1Wallet, useL2Wallet, useWallets} from '../../../providers/WalletsProvider'; import utils from '../../../utils'; import {Tab, WalletButton} from '../../UI'; import styles from './Header.module.scss'; -import {CHAIN_TXT, TAB_DISCORD_TXT, TAB_FAQ_TXT} from './Header.strings'; +import {CHAIN_TXT, TAB_DISCORD_TXT, TAB_FAQ_TXT, TAB_TERMS_TXT} from './Header.strings'; const {DISCORD_LINK_URL} = constants; export const Header = () => { - const {chainName, isConnected} = useWallets(); - const {showAccountMenu, showTransferMenu, showFaqMenu} = useMenu(); + const navigate = useNavigate(); + const {pathname} = useLocation(); + const {chainName} = useWallets(); + const {showAccountMenu, showTransferMenu} = useMenu(); const [, swapToL1] = useIsL1(); const [, swapToL2] = useIsL2(); const {account: l1Account, isConnected: isL1AccountConnected, config: l1Config} = useL1Wallet(); const {account: l2Account, isConnected: isL2AccountConnected, config: l2Config} = useL2Wallet(); const {breakpoint} = useBreakpoint(Breakpoint); const {colorDiscord, colorWhiteOp50} = useColors(); + const {isLoggedIn} = useLogin(); + + const maybeNavigateToIndex = () => { + pathname !== '/' && navigate('/'); + }; const onL2WalletButtonClick = () => { + maybeNavigateToIndex(); swapToL2(); showAccountMenu(); }; const onL1WalletButtonClick = () => { + maybeNavigateToIndex(); swapToL1(); showAccountMenu(); }; const onLogoClick = () => { + maybeNavigateToIndex(); showTransferMenu(); }; + const onTabFaqClick = () => { + navigate('faq', {replace: true}); + }; + + const onTabTermsClick = () => { + navigate('terms', {replace: true}); + }; + const onTabDiscordClick = () => { track(TrackEvent.DISCORD_TAB_CLICK); utils.browser.openInNewTab(DISCORD_LINK_URL); @@ -51,13 +71,14 @@ export const Header = () => {
- {isConnected && ( + {isLoggedIn && (
{CHAIN_TXT(chainName)}
)}
- + + {isL1AccountConnected && ( export const TAB_DISCORD_TXT = utils.getTranslation('containers.header.tab_discord_txt'); -export const TAB_FAQ_TXT = utils.getTranslation('containers.header.tab_faq'); +export const TAB_FAQ_TXT = utils.getTranslation('containers.header.tab_faq_txt'); + +export const TAB_TERMS_TXT = utils.getTranslation('containers.header.tab_terms_txt'); diff --git a/src/components/Containers/Main/Main.js b/src/components/Containers/Main/Main.js deleted file mode 100644 index 453e2751..00000000 --- a/src/components/Containers/Main/Main.js +++ /dev/null @@ -1,34 +0,0 @@ -import React, {useEffect, useState} from 'react'; - -import {useVars, useWindowSize} from '../../../hooks'; -import {EventManagerProvider} from '../../../providers/EventManagerProvider'; -import {TokensProvider} from '../../../providers/TokensProvider'; -import {useL1Wallet, useL2Wallet} from '../../../providers/WalletsProvider'; -import {Bridge, Login} from '../../Features'; -import styles from './Main.module.scss'; - -export const Main = () => { - const windowSize = useWindowSize(); - const {mainOffset} = useVars(); - const {isConnected: isL1Connected} = useL1Wallet(); - const {isConnected: isL2Connected} = useL2Wallet(); - const [height, setHeight] = useState(null); - - useEffect(() => { - setHeight(document.body.offsetHeight - mainOffset); - }, [windowSize]); - - return ( -
- {isL1Connected && isL2Connected ? ( - - - - - - ) : ( - - )} -
- ); -}; diff --git a/src/components/Containers/Main/Main.module.scss b/src/components/Containers/Main/Main.module.scss deleted file mode 100644 index fb35a370..00000000 --- a/src/components/Containers/Main/Main.module.scss +++ /dev/null @@ -1,5 +0,0 @@ -.main { - display: flex; - flex-direction: column; - overflow-y: auto; -} diff --git a/src/components/Containers/index.js b/src/components/Containers/index.js index 90346829..c0789a3d 100644 --- a/src/components/Containers/index.js +++ b/src/components/Containers/index.js @@ -1,3 +1,2 @@ -export * from './Main/Main'; export * from './Footer/Footer'; export * from './Header/Header'; diff --git a/src/components/Features/Bridge/Bridge.module.scss b/src/components/Features/Bridge/Bridge.module.scss deleted file mode 100644 index 26649c54..00000000 --- a/src/components/Features/Bridge/Bridge.module.scss +++ /dev/null @@ -1,15 +0,0 @@ -@import '../../../index'; - -$background-color: $--color-alpha; - -.bridge { - display: flex; - flex-direction: column; - background: $background-color; - border-radius: 20px; - padding: 20px; - width: 500px; - max-height: 650px; - margin: auto; - transition: 0.3s ease-in-out; -} diff --git a/src/components/Features/Faq/Faq.js b/src/components/Features/Faq/Faq.js deleted file mode 100644 index b264c56e..00000000 --- a/src/components/Features/Faq/Faq.js +++ /dev/null @@ -1,30 +0,0 @@ -import React from 'react'; - -import {faqs} from '../../../config/faqs'; -import {useMenu} from '../../../providers/MenuProvider'; -import {BackButton, Menu, MenuTitle} from '../../UI'; -import styles from './Faq.module.scss'; -import {TITLE_TXT} from './Faq.strings'; - -export const Faq = () => { - const {showTransferMenu} = useMenu(); - - return ( - -
- - -
-
    - {faqs.map((faq, i) => ( -
  1. -

    -
    -

  2. - ))} -
-
-
-
- ); -}; diff --git a/src/components/Features/Faq/Faq.module.scss b/src/components/Features/Faq/Faq.module.scss deleted file mode 100644 index 197f3947..00000000 --- a/src/components/Features/Faq/Faq.module.scss +++ /dev/null @@ -1,84 +0,0 @@ -@import '../../../index'; - -$color: $--color-white; -$link-color: $--color-gamma; -$link-color-hover: $--color-gamma-1; -$visited-link-color: $--color-alpha-6; -$visited-link-color-hover: $--color-alpha-6-hover; -$border-color: $--color-alpha-3; -$background-answer: $--color-alpha-3; - -.faq { - color: $color; - overflow-y: auto; - text-align: left; - - a { - color: $link-color; - - &:hover { - color: $link-color-hover; - } - - &:visited { - color: $visited-link-color; - - &:hover { - color: $visited-link-color-hover; - } - } - } - - ol { - line-height: 1.6; - padding-inline-start: 20px; - margin-block-start: 0; - padding-right: 16px; - - .question { - position: relative; - margin-bottom: 40px; - - &::marker { - font-size: 16px; - font-weight: 600; - } - - h4 { - letter-spacing: 0.7px; - } - - & * { - position: relative; - z-index: 1; - } - - .answer { - &::before { - content: ''; - position: absolute; - display: block; - z-index: 0; - top: -10px; - left: -20px; - width: 462px; - height: calc(100% + 20px); - background-color: $background-answer; - border-radius: 7px; - } - } - } - } - - .container { - color: $color; - padding: 15px; - margin: 10px 0; - overflow-y: auto; - max-height: 450px; - - > ol { - margin-top: -20px; - } - } -} diff --git a/src/components/Features/Faq/Faq.strings.js b/src/components/Features/Faq/Faq.strings.js deleted file mode 100644 index c0b501df..00000000 --- a/src/components/Features/Faq/Faq.strings.js +++ /dev/null @@ -1,3 +0,0 @@ -import utils from '../../../utils'; - -export const TITLE_TXT = utils.getTranslation('menus.faq.title_txt'); diff --git a/src/components/Features/index.js b/src/components/Features/index.js index 1eaa1378..e1774175 100644 --- a/src/components/Features/index.js +++ b/src/components/Features/index.js @@ -1,7 +1,4 @@ export * from './Account/Account'; -export * from './Bridge/Bridge'; -export * from './Login/Login'; -export * from './Faq/Faq'; export * from './SelectToken/SelectToken'; export * from './Transfer/Transfer'; export * from './TransferLog/TransferLog'; diff --git a/src/components/UI/FullScreenContainer/FullScreenContainer.js b/src/components/UI/FullScreenContainer/FullScreenContainer.js new file mode 100644 index 00000000..24d1eac4 --- /dev/null +++ b/src/components/UI/FullScreenContainer/FullScreenContainer.js @@ -0,0 +1,21 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import useBreakpoint from 'use-breakpoint'; + +import {Breakpoint} from '../../../enums'; +import {toClasses} from '../../../utils/object'; +import styles from './FullScreenContainer.module.scss'; + +export const FullScreenContainer = ({children}) => { + const {breakpoint} = useBreakpoint(Breakpoint); + + return ( +
+ {children} +
+ ); +}; + +FullScreenContainer.propTypes = { + children: PropTypes.oneOfType([PropTypes.object, PropTypes.array]) +}; diff --git a/src/components/UI/FullScreenContainer/FullScreenContainer.module.scss b/src/components/UI/FullScreenContainer/FullScreenContainer.module.scss new file mode 100644 index 00000000..74f4a8c3 --- /dev/null +++ b/src/components/UI/FullScreenContainer/FullScreenContainer.module.scss @@ -0,0 +1,49 @@ +@import '../../../index'; + +$link-color: $--color-gamma; +$link-color-hover: $--color-gamma-1; +$visited-link-color: $--color-alpha-6; +$visited-link-color-hover: $--color-alpha-6-hover; +$color: $--color-white; +$shadow-color: $--color-black; + +.fullScreenContainer { + left: 0; + right: 0; + position: absolute; + padding: 0 300px; + top: 0; + bottom: #{$--footer-height}px; + color: $color; + overflow: auto; + text-shadow: 1px 1px $shadow-color; + transition: 0.3s ease-in-out; + + &.laptop { + padding: 0 200px; + } + + &.tablet { + padding: 0 100px; + } + + &.mobile { + padding: 0 20px; + } + + a { + color: $link-color; + + &:hover { + color: $link-color-hover; + } + + &:visited { + color: $visited-link-color; + + &:hover { + color: $visited-link-color-hover; + } + } + } +} diff --git a/src/components/UI/StyledBackground/StyledBackground.js b/src/components/UI/StyledBackground/StyledBackground.js new file mode 100644 index 00000000..c5d5690a --- /dev/null +++ b/src/components/UI/StyledBackground/StyledBackground.js @@ -0,0 +1,40 @@ +import PropTypes from 'prop-types'; +import React from 'react'; + +import lightAccentBackgroundPath from '../../../assets/img/light-accent.png'; +import starsBackgroundPath from '../../../assets/img/stars.png'; +import styles from './StyledBackground.module.scss'; + +export const StyledBackground = ({withStars = true, withLightAccent = true, children}) => { + const stars = `url(${starsBackgroundPath})`; + const lightAccent = `url(${lightAccentBackgroundPath})`; + + let backgroundImage = ''; + + if (withStars) { + backgroundImage += stars; + } + if (withLightAccent) { + if (withStars) { + backgroundImage += ','; + } + backgroundImage += lightAccent; + } + + return ( +
+ {children} +
+ ); +}; + +StyledBackground.propTypes = { + withStars: PropTypes.bool, + withLightAccent: PropTypes.bool, + children: PropTypes.oneOfType([PropTypes.object, PropTypes.array]) +}; diff --git a/src/components/UI/StyledBackground/StyledBackground.module.scss b/src/components/UI/StyledBackground/StyledBackground.module.scss new file mode 100644 index 00000000..9d059f82 --- /dev/null +++ b/src/components/UI/StyledBackground/StyledBackground.module.scss @@ -0,0 +1,12 @@ +@import '../../../index'; + +.styledBackground { + position: absolute; + background-size: cover; + background-repeat: no-repeat; + background-position: center center; + top: #{$--header-height}px; + bottom: 0; + left: 0; + right: 0; +} diff --git a/src/components/UI/WalletButton/WalletButton.js b/src/components/UI/WalletButton/WalletButton.js index b091b1c1..7f61a3b1 100644 --- a/src/components/UI/WalletButton/WalletButton.js +++ b/src/components/UI/WalletButton/WalletButton.js @@ -2,7 +2,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import useBreakpoint from 'use-breakpoint'; -import {isMobile, isTablet, Breakpoint} from '../../../enums'; +import {Breakpoint, isDesktop, isMobile, isMobileOrTablet} from '../../../enums'; import {useColors} from '../../../hooks'; import utils from '../../../utils'; import {toClasses} from '../../../utils/object'; @@ -17,10 +17,10 @@ export const WalletButton = ({account, logoPath, onClick}) => { const getText = () => { const address = utils.wallet.shortenAddress(account); - if (isTablet(breakpoint)) { - return address; - } else if (isMobile(breakpoint)) { + if (isMobileOrTablet(breakpoint)) { return ''; + } else if (!isDesktop(breakpoint)) { + return address; } return BTN_TXT(address); }; diff --git a/src/components/UI/WalletButton/WalletButton.module.scss b/src/components/UI/WalletButton/WalletButton.module.scss index 62e2609a..c55a3398 100644 --- a/src/components/UI/WalletButton/WalletButton.module.scss +++ b/src/components/UI/WalletButton/WalletButton.module.scss @@ -9,7 +9,8 @@ } } - &.tablet { + &.laptop, + .tablet { width: 150px; font-size: 16px; } diff --git a/src/components/UI/index.js b/src/components/UI/index.js index e7841159..37c9f647 100644 --- a/src/components/UI/index.js +++ b/src/components/UI/index.js @@ -30,5 +30,7 @@ export * from './LogoutButton/LogoutButton'; export * from './AccountAddress/AccountAddress'; export * from './RefreshIcon/RefreshIcon'; export * from './Stepper/Stepper'; +export * from './StyledBackground/StyledBackground'; +export * from './FullScreenContainer/FullScreenContainer'; export * from './Modal'; export * from './Toast'; diff --git a/src/config/envs.js b/src/config/envs.js index b866d7b2..effcb35c 100644 --- a/src/config/envs.js +++ b/src/config/envs.js @@ -2,6 +2,7 @@ import utils from '../utils'; const envs = { env: process.env.NODE_ENV, + appUrl: process.env.REACT_APP_URL, autoConnect: Boolean(process.env.REACT_APP_AUTO_CONNECT), pollBlockNumberInterval: Number(process.env.REACT_APP_POLL_BLOCK_NUMBER_INTERVAL), supportedTokens: process.env.REACT_APP_SUPPORTED_TOKENS.split(','), @@ -19,7 +20,8 @@ const envs = { localStorageTransfersLogKey: process.env.REACT_APP_LOCAL_STORAGE_TRANSFERS_LOG_KEY, localStorageOnboardingExpirationTimestampKey: process.env.REACT_APP_LOCAL_STORAGE_ONBOARDING_TIMESTAMP_KEY, - onboardingModalTimeoutHrs: process.env.REACT_APP_ONBOARDING_MODAL_TIMEOUT_HRS + onboardingModalTimeoutHrs: process.env.REACT_APP_ONBOARDING_MODAL_TIMEOUT_HRS, + localStorageAcceptTermsKey: process.env.REACT_APP_LOCAL_STORAGE_ACCEPT_TERMS }; export default envs; diff --git a/src/config/strings.js b/src/config/strings.js index b873e46a..a71d41fd 100644 --- a/src/config/strings.js +++ b/src/config/strings.js @@ -4,12 +4,23 @@ const strings = { chain_txt: '{{chainName}} testnet', wallet_btn_txt: 'Account | {{address}}', tab_discord_txt: 'Discord', - tab_faq: 'FAQ' + tab_terms_txt: 'Terms', + tab_faq_txt: 'FAQ' }, footer: { rights_txt: '© 2022 StarkWare Industries Ltd. All Rights Reserved' } }, + screens: { + faq: { + title_txt: 'FAQ' + }, + terms: { + title_txt: 'Terms of Service', + last_revised_txt: 'Last Revised: April 4, 2022', + accept_btn_txt: 'I Accept' + } + }, menus: { back_btn_txt: 'Back', login: { @@ -50,9 +61,6 @@ const strings = { balance_title_txt: 'Available balance', input_placeholder_txt: '0.00', transfer_btn_txt: 'Transfer' - }, - faq: { - title_txt: 'FAQ' } }, modals: { diff --git a/src/enums/Breakpoint.js b/src/enums/Breakpoint.js index d42b367a..27800c26 100644 --- a/src/enums/Breakpoint.js +++ b/src/enums/Breakpoint.js @@ -5,6 +5,10 @@ export const Breakpoint = { DESKTOP: 1200 }; +export const isMobileOrTablet = breakpoint => { + return isMobile(breakpoint) || isTablet(breakpoint); +}; + export const isMobile = breakpoint => { return Breakpoint[breakpoint] === Breakpoint.MOBILE; }; @@ -12,3 +16,7 @@ export const isMobile = breakpoint => { export const isTablet = breakpoint => { return Breakpoint[breakpoint] === Breakpoint.TABLET; }; + +export const isDesktop = breakpoint => { + return Breakpoint[breakpoint] === Breakpoint.DESKTOP; +}; diff --git a/src/enums/MenuType.js b/src/enums/MenuType.js index 91ab0a0c..e8413fe4 100644 --- a/src/enums/MenuType.js +++ b/src/enums/MenuType.js @@ -1,6 +1,5 @@ export const MenuType = { TRANSFER: 'TRANSFER', SELECT_TOKEN: 'SELECT_TOKEN', - ACCOUNT: 'ACCOUNT', - FAQ: 'FAQ' + ACCOUNT: 'ACCOUNT' }; diff --git a/src/index.js b/src/index.js index 3672d3a7..50233202 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,7 @@ import splitbee from '@splitbee/web'; import React from 'react'; import ReactDOM from 'react-dom'; +import {BrowserRouter} from 'react-router-dom'; import {App} from './App'; import {ModalWrapper} from './components/UI'; @@ -22,9 +23,11 @@ if (env === 'development' || utils.browser.getUrlParameter('debugApp')) { } ReactDOM.render( - - - - , + + + + + + , document.getElementById('root') ); diff --git a/src/index.scss b/src/index.scss index 3c6671c2..b99a92a2 100644 --- a/src/index.scss +++ b/src/index.scss @@ -66,20 +66,9 @@ body { width: 100%; font-family: $--primary-font; background-color: $--color-alpha-3; - background-image: url('assets/img/light-accent.png'); - background-size: cover; - background-repeat: no-repeat; - background-position: center center; transition: 0.3s; } -header, -footer { - display: flex; - align-items: center; - justify-content: space-between; -} - .center { top: 50%; left: 50%; diff --git a/src/providers/AppProvider/AppProvider.js b/src/providers/AppProvider/AppProvider.js new file mode 100644 index 00000000..00f0f67d --- /dev/null +++ b/src/providers/AppProvider/AppProvider.js @@ -0,0 +1,47 @@ +import PropTypes from 'prop-types'; +import React, {useReducer} from 'react'; + +import envs from '../../config/envs'; +import utils from '../../utils'; +import {AppContext} from './app-context'; +import {actions, initialState, reducer} from './app-reducer'; + +const {localStorageAcceptTermsKey} = envs; + +export const AppProvider = ({children}) => { + const [state, dispatch] = useReducer(reducer, initialState); + + const login = () => { + dispatch({ + type: actions.SET_IS_LOGGED_IN, + isLoggedIn: true + }); + }; + + const logout = () => { + dispatch({ + type: actions.SET_IS_LOGGED_IN, + isLoggedIn: false + }); + }; + + const acceptTerms = () => { + utils.storage.setItem(localStorageAcceptTermsKey, true); + dispatch({ + type: actions.SET_ACCEPT_TERMS + }); + }; + + const value = { + ...state, + acceptTerms, + login, + logout + }; + + return {children}; +}; + +AppProvider.propTypes = { + children: PropTypes.oneOfType([PropTypes.object, PropTypes.array]) +}; diff --git a/src/providers/AppProvider/app-context.js b/src/providers/AppProvider/app-context.js new file mode 100644 index 00000000..59c7dc1d --- /dev/null +++ b/src/providers/AppProvider/app-context.js @@ -0,0 +1,10 @@ +import {createContext} from 'react'; + +import {initialState} from './app-reducer'; + +export const AppContext = createContext({ + ...initialState, + acceptTerms: () => {}, + login: () => {}, + logout: () => {} +}); diff --git a/src/providers/AppProvider/app-hooks.js b/src/providers/AppProvider/app-hooks.js new file mode 100644 index 00000000..fe8a304b --- /dev/null +++ b/src/providers/AppProvider/app-hooks.js @@ -0,0 +1,17 @@ +import {useContext} from 'react'; + +import {AppContext} from './app-context'; + +export const useApp = () => useContext(AppContext); + +export const useTerms = () => { + const {isAcceptTerms, acceptTerms} = useApp(); + + return {isAcceptTerms, acceptTerms}; +}; + +export const useLogin = () => { + const {isLoggedIn, login, logout} = useApp(); + + return {isLoggedIn, login, logout}; +}; diff --git a/src/providers/AppProvider/app-reducer.js b/src/providers/AppProvider/app-reducer.js new file mode 100644 index 00000000..9d7f676c --- /dev/null +++ b/src/providers/AppProvider/app-reducer.js @@ -0,0 +1,35 @@ +import envs from '../../config/envs'; +import utils from '../../utils'; + +const {localStorageAcceptTermsKey} = envs; + +export const actions = { + SET_ACCEPT_TERMS: 'App/SET_ACCEPT_TERMS', + SET_IS_LOGGED_IN: 'App/SET_IS_LOGGED_IN' +}; + +export const initialState = { + isAcceptTerms: utils.storage.getItem(localStorageAcceptTermsKey), + isLoggedIn: false +}; + +export const reducer = (state, action) => { + switch (action.type) { + case actions.SET_ACCEPT_TERMS: { + return { + ...state, + isAcceptTerms: true + }; + } + + case actions.SET_IS_LOGGED_IN: { + return { + ...state, + isLoggedIn: action.isLoggedIn + }; + } + + default: + return state; + } +}; diff --git a/src/providers/AppProvider/index.js b/src/providers/AppProvider/index.js new file mode 100644 index 00000000..5b98ad77 --- /dev/null +++ b/src/providers/AppProvider/index.js @@ -0,0 +1,3 @@ +export * from './app-context'; +export * from './app-hooks'; +export * from './AppProvider'; diff --git a/src/providers/EventManagerProvider/EventManagerProvider.js b/src/providers/EventManagerProvider/EventManagerProvider.js index e323a192..8e410ff1 100644 --- a/src/providers/EventManagerProvider/EventManagerProvider.js +++ b/src/providers/EventManagerProvider/EventManagerProvider.js @@ -15,7 +15,7 @@ const filters = {}; export const EventManagerProvider = ({children}) => { const logger = useLogger(EventManagerProvider.displayName); const getTokenBridgeContract = useL1TokenBridgeContract(); - const {account: l1Account, chainId: l1ChainId, getBlockNumber} = useL1Wallet(); + const {account: l1Account, chainId: l1ChainId} = useL1Wallet(); const {account: l2Account, chainId: l2ChainId} = useL2Wallet(); const l1Tokens = useL1Tokens(); const l2Tokens = useL2Tokens(); diff --git a/src/providers/MenuProvider/menu-hooks.js b/src/providers/MenuProvider/menu-hooks.js index 53a23232..4b82cce8 100644 --- a/src/providers/MenuProvider/menu-hooks.js +++ b/src/providers/MenuProvider/menu-hooks.js @@ -9,7 +9,6 @@ export const useMenu = () => { return { menu, menuProps, - showFaqMenu: useShowMenu(MenuType.FAQ), showAccountMenu: useShowMenu(MenuType.ACCOUNT), showTransferMenu: useShowMenu(MenuType.TRANSFER), showSelectTokenMenu: useShowMenu(MenuType.SELECT_TOKEN), diff --git a/src/providers/index.js b/src/providers/index.js index 2afc5b45..27cbc64e 100644 --- a/src/providers/index.js +++ b/src/providers/index.js @@ -1,3 +1,4 @@ +import {AppProvider} from './AppProvider'; import {BlockHashProvider} from './BlockHashProvider'; import {MenuProvider} from './MenuProvider'; import {ModalProvider} from './ModalProvider'; @@ -8,6 +9,7 @@ import {WalletsProvider} from './WalletsProvider'; import {combineProviders} from './combine-providers'; export const Providers = combineProviders([ + AppProvider, MenuProvider, TransferProvider, ModalProvider, diff --git a/src/components/Features/Bridge/Bridge.js b/src/routes/Bridge/Bridge.js similarity index 63% rename from src/components/Features/Bridge/Bridge.js rename to src/routes/Bridge/Bridge.js index f2206f25..878bd284 100644 --- a/src/components/Features/Bridge/Bridge.js +++ b/src/routes/Bridge/Bridge.js @@ -1,13 +1,15 @@ import React, {useEffect} from 'react'; -import {Account, Faq, SelectToken, ToastProvider, Transfer} from '..'; -import {setUser, track, TrackEvent} from '../../../analytics'; -import envs from '../../../config/envs'; -import {MenuType} from '../../../enums'; -import {useMenu} from '../../../providers/MenuProvider'; -import {useOnboardingModal} from '../../../providers/ModalProvider'; -import {useL1Wallet, useL2Wallet} from '../../../providers/WalletsProvider'; -import utils from '../../../utils'; +import {setUser, track, TrackEvent} from '../../analytics'; +import {Account, SelectToken, ToastProvider, Transfer} from '../../components/Features'; +import envs from '../../config/envs'; +import {MenuType} from '../../enums'; +import {EventManagerProvider} from '../../providers/EventManagerProvider'; +import {useMenu} from '../../providers/MenuProvider'; +import {useOnboardingModal} from '../../providers/ModalProvider'; +import {TokensProvider} from '../../providers/TokensProvider'; +import {useL1Wallet, useL2Wallet} from '../../providers/WalletsProvider'; +import utils from '../../utils'; import styles from './Bridge.module.scss'; const {localStorageOnboardingExpirationTimestampKey, onboardingModalTimeoutHrs} = envs; @@ -46,8 +48,6 @@ export const Bridge = () => { return ; case MenuType.ACCOUNT: return ; - case MenuType.FAQ: - return ; case MenuType.TRANSFER: default: return ; @@ -56,8 +56,14 @@ export const Bridge = () => { return (
- - {renderMenu()} + + +
+ + {renderMenu()} +
+
+
); }; diff --git a/src/routes/Bridge/Bridge.module.scss b/src/routes/Bridge/Bridge.module.scss new file mode 100644 index 00000000..172f0921 --- /dev/null +++ b/src/routes/Bridge/Bridge.module.scss @@ -0,0 +1,26 @@ +@import '../../index'; + +$background-color: $--color-alpha; + +.bridge { + display: flex; + flex-direction: column; + overflow: auto; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + + .bridgeMenu { + display: flex; + flex-direction: column; + background: $background-color; + border-radius: 20px; + padding: 20px; + width: 500px; + max-height: 650px; + margin: auto; + transition: 0.3s ease-in-out; + } +} diff --git a/src/routes/Faq/Faq.js b/src/routes/Faq/Faq.js new file mode 100644 index 00000000..04d32f34 --- /dev/null +++ b/src/routes/Faq/Faq.js @@ -0,0 +1,29 @@ +import React, {useEffect} from 'react'; + +import {track, TrackEvent} from '../../analytics'; +import {FullScreenContainer} from '../../components/UI'; +import {faqs} from '../../config/faqs'; +import styles from './Faq.module.scss'; +import {TITLE_TXT} from './Faq.strings'; + +export const Faq = () => { + useEffect(() => { + track(TrackEvent.FAQ_SCREEN); + }, []); + + return ( + +
+

{TITLE_TXT}

+
    + {faqs.map((faq, i) => ( +
  1. +

    +
    +

  2. + ))} +
+
+
+ ); +}; diff --git a/src/routes/Faq/Faq.module.scss b/src/routes/Faq/Faq.module.scss new file mode 100644 index 00000000..6f0929d0 --- /dev/null +++ b/src/routes/Faq/Faq.module.scss @@ -0,0 +1,32 @@ +@import '../../index'; + +.faq { + ol { + line-height: 1.6; + padding-inline-start: 20px; + margin-block-start: 0; + padding-right: 16px; + + .question { + position: relative; + + &:not(:last-child) { + margin-bottom: 60px; + } + + &::marker { + font-size: 24px; + font-weight: 600; + } + + h2 { + letter-spacing: 0.7px; + } + + & * { + position: relative; + z-index: 1; + } + } + } +} diff --git a/src/routes/Faq/Faq.strings.js b/src/routes/Faq/Faq.strings.js new file mode 100644 index 00000000..112efadd --- /dev/null +++ b/src/routes/Faq/Faq.strings.js @@ -0,0 +1,5 @@ +import utils from '../../utils'; + +const {title_txt} = utils.getTranslation('screens.faq'); + +export const TITLE_TXT = title_txt; diff --git a/src/components/Features/Login/Login.constants.js b/src/routes/Login/Login.constants.js similarity index 100% rename from src/components/Features/Login/Login.constants.js rename to src/routes/Login/Login.constants.js diff --git a/src/components/Features/Login/Login.js b/src/routes/Login/Login.js similarity index 71% rename from src/components/Features/Login/Login.js rename to src/routes/Login/Login.js index 04c3adbe..5d48f769 100644 --- a/src/components/Features/Login/Login.js +++ b/src/routes/Login/Login.js @@ -1,12 +1,14 @@ import React, {useEffect, useRef, useState} from 'react'; +import {useNavigate} from 'react-router-dom'; -import {track, TrackEvent} from '../../../analytics'; -import {ChainInfo, NetworkType, WalletStatus, WalletType} from '../../../enums'; -import {useConfig, useWalletHandlerProvider} from '../../../hooks'; -import {useHideModal, useProgressModal} from '../../../providers/ModalProvider'; -import {useL1Wallet, useL2Wallet, useWallets} from '../../../providers/WalletsProvider'; -import utils from '../../../utils'; -import {Menu, WalletLogin} from '../../UI'; +import {track, TrackEvent} from '../../analytics'; +import {WalletLogin} from '../../components/UI'; +import {ChainInfo, NetworkType, WalletStatus, WalletType} from '../../enums'; +import {useConfig, useWalletHandlerProvider} from '../../hooks'; +import {useLogin} from '../../providers/AppProvider'; +import {useHideModal, useProgressModal} from '../../providers/ModalProvider'; +import {useL1Wallet, useL2Wallet, useWallets} from '../../providers/WalletsProvider'; +import utils from '../../utils'; import {AUTO_CONNECT_TIMEOUT_DURATION, MODAL_TIMEOUT_DURATION} from './Login.constants'; import styles from './Login.module.scss'; import { @@ -27,8 +29,10 @@ export const Login = () => { const showProgressModal = useProgressModal(); const getWalletHandlers = useWalletHandlerProvider(); const {status, error} = useWallets(); - const {connectWallet: connectL1Wallet, isConnected} = useL1Wallet(); - const {connectWallet: connectL2Wallet} = useL2Wallet(); + const {connectWallet: connectL1Wallet, isConnected: isConnectedL1Wallet} = useL1Wallet(); + const {connectWallet: connectL2Wallet, isConnected: isConnectedL2Wallet} = useL2Wallet(); + const {login} = useLogin(); + const navigate = useNavigate(); useEffect(() => { track(TrackEvent.LOGIN_SCREEN); @@ -51,8 +55,14 @@ export const Login = () => { }, [walletType, getWalletHandlers]); useEffect(() => { - isConnected && setWalletType(WalletType.L2); - }, [isConnected]); + if (isConnectedL1Wallet && isConnectedL2Wallet) { + login(); + navigate('/'); + } + if (isConnectedL1Wallet) { + setWalletType(WalletType.L2); + } + }, [isConnectedL1Wallet, isConnectedL2Wallet]); useEffect(() => { error && handleError(error); @@ -148,21 +158,19 @@ export const Login = () => { }; return ( - -
-
-
{TITLE_TXT}
-

- {SUBTITLE_TXT(walletType === WalletType.L1 ? NetworkType.L1.name : NetworkType.L2.name)} -

-
{renderLoginWallets()}
- {errorMsg &&
{errorMsg}
} -
-
-
- {DOWNLOAD_TEXT[0]} {DOWNLOAD_TEXT[1]} -
+
+
+
{TITLE_TXT}
+

+ {SUBTITLE_TXT(walletType === WalletType.L1 ? NetworkType.L1.name : NetworkType.L2.name)} +

+
{renderLoginWallets()}
+ {errorMsg &&
{errorMsg}
} +
+
+
+ {DOWNLOAD_TEXT[0]} {DOWNLOAD_TEXT[1]}
-
+
); }; diff --git a/src/components/Features/Login/Login.module.scss b/src/routes/Login/Login.module.scss similarity index 98% rename from src/components/Features/Login/Login.module.scss rename to src/routes/Login/Login.module.scss index c1de3017..e65cd784 100644 --- a/src/components/Features/Login/Login.module.scss +++ b/src/routes/Login/Login.module.scss @@ -1,4 +1,4 @@ -@import '../../../index'; +@import '../../index'; $background-color: $--color-alpha; $text-color: $--color-white; diff --git a/src/components/Features/Login/Login.strings.js b/src/routes/Login/Login.strings.js similarity index 92% rename from src/components/Features/Login/Login.strings.js rename to src/routes/Login/Login.strings.js index dfb60394..98fe3f36 100644 --- a/src/components/Features/Login/Login.strings.js +++ b/src/routes/Login/Login.strings.js @@ -1,4 +1,4 @@ -import utils from '../../../utils'; +import utils from '../../utils'; const {title_txt, subtitle_txt, download_txt, modal_txt, unsupported_browser_txt} = utils.getTranslation('menus.login'); diff --git a/src/routes/ProtectedRoute.js b/src/routes/ProtectedRoute.js new file mode 100644 index 00000000..5563eeb8 --- /dev/null +++ b/src/routes/ProtectedRoute.js @@ -0,0 +1,16 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import {Navigate} from 'react-router-dom'; + +export const ProtectedRoute = ({isAllowed, redirectPath, children}) => { + if (!isAllowed) { + return ; + } + return children; +}; + +ProtectedRoute.propTypes = { + isAllowed: PropTypes.bool, + redirectPath: PropTypes.string, + children: PropTypes.oneOfType([PropTypes.object, PropTypes.array]) +}; diff --git a/src/routes/Terms/Terms.js b/src/routes/Terms/Terms.js new file mode 100644 index 00000000..6e3fceab --- /dev/null +++ b/src/routes/Terms/Terms.js @@ -0,0 +1,558 @@ +import React, {useEffect, useRef, useState} from 'react'; +import {useNavigate} from 'react-router-dom'; + +import {track, TrackEvent} from '../../analytics'; +import {Button, FullScreenContainer} from '../../components/UI'; +import {useColors, useConfig} from '../../hooks'; +import {useTerms} from '../../providers/AppProvider'; +import {useL1Wallet, useL2Wallet} from '../../providers/WalletsProvider'; +import styles from './Terms.module.scss'; +import {ACCEPT_BTN_TXT, LAST_REVISED_TXT, TITLE_TXT} from './Terms.strings'; + +const STARKWARE_SITE_URL = 'https://starkware.co/'; +const STARKNET_DOCS_URL = 'https://starknet.io/documentation/'; +const STARKGATE_MAIL_ADDRESS = 'starkgate@starknet.io'; +const STARKGATE_CONTRACTS_URL = + 'https://github.com/starkware-libs/starkgate-contracts/tree/main/src/starkware/starknet/apps/starkgate'; + +export const Terms = () => { + const navigate = useNavigate(); + const termsRef = useRef(); + const acceptButtonRef = useRef(); + const {appUrl} = useConfig(); + const {isAcceptTerms, acceptTerms} = useTerms(); + const {colorGamma1, colorWhite} = useColors(); + const {account: l1account} = useL1Wallet(); + const {account: l2account} = useL2Wallet(); + const [marginBottom, setMarginBottom] = useState(); + const [acceptButtonEnable, setAcceptButtonEnable] = useState(false); + + useEffect(() => { + track(TrackEvent.TERMS_SCREEN); + setMarginBottom(acceptButtonRef?.current?.clientHeight); + }, []); + + const onScroll = () => { + if (termsRef.current) { + const {scrollTop, scrollHeight, clientHeight} = termsRef.current; + if (scrollTop + clientHeight === scrollHeight) { + setAcceptButtonEnable(true); + } + } + }; + + const accept = () => { + track(TrackEvent.TERMS.ACCEPT_CLICK, {l1account, l2account}); + acceptTerms(); + navigate('/'); + }; + + return ( + +
+

{TITLE_TXT}

+

{LAST_REVISED_TXT}

+
+

+ + StarkWare Industries Ltd. + {' '} + ("StarkWare", "we", "our") welcomes you + (the "User(s)", or " + you") to{' '} + + {appUrl} + {' '} + (the "Site" + ), a website that provides information and hosts a user interface (the " + Interface + ") to a pair of smart contracts on the Ethereum blockchain and on the StarkNet + network that facilitates your ability to conduct transactions with your ETH and ERC-20 + tokens that reside on Layer 1 in a gas-efficient manner, via the StarkNet Alpha network + and its STARK-based computational compression capabilities (the "Bridge + "). Each User may use the Site, Interface and/or Bridge1 in accordance + with, and subject to, the terms and conditions hereunder. +

+
    +
  1. +

    Acceptance of the Terms

    +

    + By entering, connecting to, accessing or using the Site, Interface and/or the + Bridge, you acknowledge that you have read and understood the following Terms of + Service (collectively, the "Terms"), and the terms of our{' '} + Privacy Policy available at https://starknet.io/privacy-policy/ and you agree + to be bound by them and to comply with all applicable laws and regulations regarding + your use of the Site, Interface and the Bridge, and you acknowledge that these Terms + constitute a binding and enforceable legal contract between StarkWare and you.{' '} + + IF YOU DO NOT AGREE TO THESE TERMS, PLEASE DO NOT ENTER, CONNECT TO, ACCESS OR USE + THE SITE, INTERFACE AND/OR BRIDGE IN ANY MANNER. + +

    +

    + + THE BRIDGE OPERATES IN PART ON STARKNET, A PERMISSIONLESS DECENTRALIZED VALIDITY + -ROLLUP THAT OPERATES AS AN L2 NETWORK OVER ETHEREUM. WHEN YOU USE THE BRIDGE, YOU + ARE ALSO USING STARKNET TO SCALE YOUR ETHEREUM TRANSACTIONS. BY USING THE BRIDGE, + YOU ACKNOWLEDGE AND AGREE THAT YOU HAVE READ, AND AGREE TO, THE STARKNET TERMS OF + USE LOCATED AT https://starknet.io/terms/, WHICH WILL APPLY TO ANY USE BY YOU OF + THE BRIDGE AND/OR OF STARKNET. + +

    +

    + The Site, Interface and Bridge are available only to individuals who (a) are at + least eighteen (18) years old; and (b) possess the legal capacity to enter into + these Terms (on behalf of themselves and their organization) and to form a binding + agreement under any applicable law. You hereby represent that you possess the legal + authority to enter into these Terms on your and (if applicable) your organization’s + behalf and to form a binding agreement under any applicable law, to use the Site, + Interface and Bridge in accordance with these Terms, and to fully perform your + obligations hereunder. +

    +

    + For the avoidance of doubt, if you are acting on behalf of an organization, any act + or omission performed by you in connection with the Site, Interface and/or Bridge + shall obligate your organization. +

    +

    + + Please be advised that these Terms contain provisions, including an Agreement to + Arbitrate, that govern how claims that you may have or assert against StarkWare + are resolved, which will require the parties to submit claims they may have + against one another to binding and final arbitration. Under the Agreement to + Arbitrate, the parties will (1) only be permitted to pursue claims against each + other on an individual basis, not as a plaintiff or class member in any class or + representative action or proceeding and (2) only be permitted to seek relief + (including monetary, injunctive, and declaratory relief) on an individual basis. + +

    +
  2. +
  3. +

    The Site, Interface and Bridge - Explanation and Certain Risk Factors

    +

    + + The Site and Interface do not offer the Bridge, which is a pair of + autonomously functioning smart contracts on the Ethereum Blockchain and StarkNet, + and which may be accessed independently without use of the Site or the Interface. + 2 + +

    +

    + Use of the Bridge and any activity thereon is subject to any additional terms and + conditions that may be included in the Bridge, including those located at the{' '} + + following link + + . +

    +

    + + YOUR USE OF THE BRIDGE IS ENTIRELY AT YOUR OWN RISK. + +

    +

    + On the Site, you will have the ability via the Interface to access the Bridge. More + information about the Bridge is available{' '} + + here + + . +

    +

    + Decentralized Protocol: The Bridge is a pair of decentralized smart + contracts, one running on the Ethereum blockchain and the other on StarkNet. The + Interface provides an Interface to access the Bridge in a user-friendly manner, + [however due to its decentralized nature StarkWare does not control the Bridge1]. + The Bridge is non-custodial. StarkWare does not have any ability to control or + modify how you conduct transactions on Ethereum or StarkNet. By using the Bridge, + you acknowledge and recognize that use of the Bridge is at your own risk and will be + subject to the rules encoded within the Bridge, including as to when and on what + terms and at which speed your transactions are conducted on Ethereum and/or + StarkNet, neither of which StarkWare can influence or fully control. +

    +

    + Risk Disclosures Relating to Blockchain. By utilizing the Bridge, you + represent that you understand the inherent risks associated with cryptocurrency + systems; and warrant that you have an understanding of the usage and intricacies of + cryptographic tokens, digital assets, blockchains and other distributed and + decentralized systems, and smart contracts. In particular, you are aware and + recognize that any transactions conducted on a blockchain cannot be undone or + reversed. +

    +

    + Information. The site may contain information regarding the Bridge or + particular instances of the Bridge. Such information is provided for informational + purposes only, without any representation or warranty. StarkWare does not assume any + responsibility or liability for the accuracy or inaccuracy of any such information. + Before acting upon or making any decisions on the basis of such information, you are + responsible to independently verify such information. +

    +

    + Fees. StarkWare reserves the right to charge fees for use of the bridge , and + in such case shall display to you any applicable fees prior to you incurring the + fee. There are fees and costs associated with using the Bridge, a description of + which can be found{' '} + + here + + . +

    +

    + We are not responsible for any loss: The Bridge, StarkNet and other + blockchain networks are decentralized systems that are still under active + development, and therefore: (a) may contain bugs, errors and defects, (b) may + function improperly or be subject to periods of downtime and unavailability, (c) may + result in total or partial loss or corruption of cryptocurrencies with respect to + which they are used and/or data, (d) may be modified at any time, including through + the release of subsequent versions, all with or without notice to you, or (e) may + have security vulnerabilities and be subject to hacking. StarkWare will not be + liable or responsible for any losses or damages to you, including without limitation + any loss of funds on Ethereum and/or StarkNet with which you conduct your + transactions using the Bridge, as a result of any of the foregoing. +

    +

    + + WITHOUT LIMITING THE GENERALITY OF ANY OTHER PROVISION OF THE TERMS, YOU + ACKNOWLEDGE AND AGREE THAT YOU ASSUME FULL RESPONSIBILITY FOR YOUR USE OF THE + SITE, THE INTERFACE, AND THE BRIDGE, ALL OF WHICH WILL BE AT YOUR OWN RISK. + RECOGNIZING SUCH, YOU UNDERSTAND AND AGREE THAT, TO THE FULLEST EXTENT PERMITTED + BY APPLICABLE LAW, NEITHER STARKWARE NOR ITS SHAREHOLDERS, OFFICERS OR DIRECTORS + WILL BE LIABLE TO YOU FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + CONSEQUENTIAL, PUNITIVE, EXEMPLARY OR OTHER DAMAGES OF ANY KIND, INCLUDING WITHOUT + LIMITATION DAMAGES FOR LOSS OF PROFITS, GOODWILL, USE, DATA, CRYPTOCURRENCIES OR + OTHER TANGIBLE OR INTANGIBLE LOSSES OR ANY OTHER DAMAGES BASED ON CONTRACT, TORT, + STRICT LIABILITY OR ANY OTHER THEORY (EVEN IF STARKWARE HAD BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGES), RESULTING FROM THE SITE, THE INTERFACE OR THE + BRIDGE. + +

    +

    + + SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF CERTAIN WARRANTIES OR THE + LIMITATION OR EXCLUSION OF LIABILITY FOR DIRECT, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES. ACCORDINGLY, IN SUCH JURISDICTIONS THE FOREGOING WILL BE + REINTERPRETED SO AS TO BE EFFECTIVE TO GREATEST EXTENT POSSIBLE UNDER APPLICABLE + LAW. + +

    +
  4. +
  5. +

    Use Restrictions; Compliance

    +

    + There are certain conducts which are strictly prohibited when using the Site and + Interface. Please read the following restrictions carefully. Failure to comply with + any of the provisions set forth herein may result (at StarkWare’s sole discretion) + in the termination of your use of the Site and/or Interface and may also expose you + to civil and/or criminal liability. +

    +

    + The Interface and/or Bridge may not be available or appropriate for use in certain + jurisdictions. By accessing or using the Interface and/or Bridge, you agree that you + are solely and entirely responsible for compliance with all laws and regulations + that may apply to you. You may not use the Interface and/or Bridge if you are a + citizen, resident, or member of any jurisdiction or group that is subject to + economic sanctions, or if your use of the Interface and/or Bridge would be illegal + or otherwise violate any applicable law. You may not use the Interface in connection + with or which would involve proceeds of any unlawful activity. +

    +
  6. +
  7. +

    Minors

    +

    + The Site, Interface and Bridge are intended for Users over the age of eighteen (18). +

    +
  8. +
  9. +

    Contacting us via the Site

    +

    + In order to contact us, please email us at:{' '} + {STARKGATE_MAIL_ADDRESS} +

    +
  10. +
  11. +

    Links to Third Party Sites

    +

    + Certain links provided herein may permit our Users to leave this Site and enter non- + StarkWare sites or platform. Those linked sites and platform are provided solely as + a convenience to you. These linked sites and platform are not under the control of + StarkWare and it is not responsible for the availability of such external sites or + platform, and does not endorse and is not responsible or liable for any content + including but not limited to content advertising, products or other information on + or available from such linked sites and platform or any link contained in linked + sites or service. In addition, StarkWare is not responsible or liable for such + linked sites and platform’ privacy practices and/or any other practices. Your access + to, use of and reliance upon any such sites, platform and content and your dealings + with such third parties are at your sole risk and expense. You further acknowledge + and agree that StarkWare shall not be responsible or liable, directly or indirectly, + for any damage or loss caused or alleged to be caused, by or in connection with use + of or reliance on any platform, content, products or other materials available on or + through such linked sites or resource. +

    +
  12. +
  13. +

    Availability

    +

    + The Site’s, the Interface’s and the Bridge’s availability and functionality depends + on various factors, such as communication networks and public blockchain networks. + StarkWare does not warrant or guarantee that the Site and/or Interface and/or Bridge + will operate and/or be available at all times without disruption or interruption, or + that it will be immune from unauthorized access or error-free. +

    +
  14. +
  15. +

    Changes to The Site

    +

    + StarkWare reserves the right to modify, correct, amend, enhance, improve, make any + other changes to, or discontinue, temporarily or permanently this Site and/or the + Interface and/or Bridge1 (or any part thereof) without notice, at any time. In + addition, you hereby acknowledge that the content provided under this Site may be + changed, extended in terms of content and form or removed at any time without any + notice to you. You agree that StarkWare shall not be liable to you or to any third + party for any modification, suspension, or discontinuance of this Site or the + Interface or the Bridge. You hereby agree that StarkWare is not responsible for any + errors or malfunctions that may occur in connection with the performance of such + changes. +

    +
  16. +
  17. +

    Disclaimers and No Warranties

    +

    + TO THE FULLEST EXTENT LEGALLY PERMISSIBLE, THE SITE AND THE INTERFACE ARE PROVIDED + ON AN "AS IS", "WITH ALL FAULTS" AND " + AS AVAILABLE" BASIS, AND STARKWARE, INCLUDING ITS VENDORS, OFFICERS, + SHAREHOLDERS, SUB-CONTRACTORS, DIRECTORS, EMPLOYEES, AFFILIATES, SUBSIDIARIES, + LICENSORS, AGENTS AND SUPPLIERS (COLLECTIVELY, " + STARKWARE’S REPRESENTATIVES + "), DISCLAIM ALL WARRANTIES OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, + INCLUDING BUT NOT LIMITED TO WARRANTIES OF TITLE OR NON-INFRINGEMENT OR IMPLIED + WARRANTIES OF USE, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE AND THOSE + ARISING FROM A COURSE OF DEALING OR USAGE OF TRADE. YOU MAY HAVE ADDITIONAL CONSUMER + RIGHTS UNDER YOUR LOCAL LAWS THAT THIS AGREEMENT CANNOT CHANGE. +

    +

    + ADDITIONAL DISCLAIMERS AND WARRANTIES REGARDING THE BRIDGE MAY APPLY PURSUANT TO + ADDITIONAL TERMS AND CONDITIONS INCLUDED IN THE BRIDGE. +

    +

    + WE DO NOT WARRANT (I) THAT THE USE AND OPERATION OF THE SITE, THE INTERFACE + AND/OR THE BRIDGE IS OR WILL BE SECURE, TIMELY, ACCURATE, COMPLETE, UNINTERRUPTED, + WITHOUT ERRORS, OR FREE OF VIRUSES, DEFECTS, WORMS, OTHER HARMFUL COMPONENTS OR + OTHER PROGRAM LIMITATIONS, (II) THAT WE WILL CORRECT ANY ERRORS OR DEFECTS IN THE + SITE OR THE INTERFACE, (III) AND/OR MAKE ANY REPRESENTATION REGARDING THE USE, + INABILITY TO USE OR OPERATE, OR THE RESULTS OF THE USE OF THE SITE AND/OR INTERFACE + AND/OR BRIDGE (INCLUDING THAT THE RESULTS OF USING THE SITE AND/OR INTERFACE AND/OR + BRIDGE WILL MEET YOUR REQUIREMENTS). STARKWARE AND STARKWARE’S REPRESENTATIVES + DISCLAIM ALL WARRANTIES AND CONDITIONS WITH REGARD TO THE USE OF THE SITE AND THE + INTERFACE, INCLUDING BUT NOT LIMITED TO THE AVAILABILITY, RELIABILITY OR THE QUALITY + OF THE SITE AND THE INTERFACE, AND ARE NOT AND SHALL NOT BE RESPONSIBLE FOR ANY + ERROR, FAULT OR MISTAKE RELATED TO ANY CONTENT AND/OR INFORMATION DISPLAYED WITHIN + THE SITE OR THE INTERFACE. +

    +

    + WE ARE NOT RESPONSIBLE AND HAVE NO LIABILITY FOR ANY ITEM OR SERVICE PROVIDED BY ANY + PERSON OR ENTITY OTHER THAN STARKWARE. +

    +

    + WE ARE NOT RESPONSIBLE FOR ANY CONSEQUENCES TO YOU OR OTHERS THAT MAY RESULT FROM + TECHNICAL PROBLEMS (INCLUDING WITHOUT LIMITATION IN CONNECTION WITH THE INTERNET + SUCH AS SLOW CONNECTIONS, TRAFFIC CONGESTION, OVERLOAD OF SERVERS, DELAYS OR + INTERRUPTIONS) OR ANY TELECOMMUNICATIONS OR INTERNET PROVIDERS OR ANY BLOCKCHAIN + NETWORK. +

    +

    + YOU AGREE THAT USE OF THE SITE, THE INTERFACE AND/OR THE BRIDGE IS ENTIRELY AT YOUR + OWN RISK. +

    +

    + INASMUCH AS SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSIONS OR LIMITATIONS AS SET + FORTH HEREIN, THE FULL EXTENT OF THE ABOVE EXCLUSIONS AND LIMITATIONS MAY NOT APPLY. +

    +
  18. +
  19. +

    Limitation of Liability

    +

    + TO THE MAXIMUM EXTENT LEGALLY PERMISSIBLE, IN NO EVENT SHALL STARKWARE, INCLUDING + STARKWARE’S REPRESENTATIVES BE LIABLE FOR ANY DAMAGES WHATSOEVER, INCLUDING, BUT NOT + LIMITED TO, DIRECT, INDIRECT, SPECIAL, PUNITIVE, EXEMPLARY, INCIDENTAL OR + CONSEQUENTIAL DAMAGES OF ANY KIND, UNDER ANY LEGAL THEORY (INCLUDING, WITHOUT + LIMITATION, CONTRACT, NEGLIGENCE, TORT OR STRICT LIABILITY), INCLUDING, WITHOUT + LIMITATION, LOSS OF GOODWILL, PROFITS OR DATA AND BUSINESS INTERRUPTION, ARISING + HEREUNDER, RESULTING FROM OR ARISING OUT OF THE SITE, THE INTERFACE AND/OR THE + STARKWARE, AND/OR THE FAILURE OF THE SITE, THE INTERFACE AND/OR THE STARKWARE TO + PERFORM AS REPRESENTED OR EXPECTED, OR FROM ANY CONTENT, OR FROM THE PERFORMANCE OR + FAILURE OF STARKWARE TO PERFORM UNDER THESE TERMS, ANY OTHER ACT OR OMISSION OF + STARKWARE OR STARKWARE ‘S REPRESENTATIVES BY ANY OTHER CAUSE WHATSOEVER; OR BASED + UPON BREACH OF WARRANTY, GUARANTEE OR CONDITION, BREACH OF CONTRACT, NEGLIGENCE, + STRICT LIABILITY, TORT, OR ANY OTHER LEGAL THEORY, REGARDLESS OF WHETHER STARKWARE + OR STARKWARE’S REPRESENTATIVES HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. +

    +

    + IN ANY CASE, WITHOUT LIMITING THE GENERALITY OF THE FOREGOING AND TO THE MAXIMUM + EXTENT LEGALLY PERMISSIBLE, STARKWARE AND STARKWARE’S REPRESENTATIVES’ TOTAL + AGGREGATE LIABILITY FOR ALL DAMAGES OR LOSSES WHATSOEVER ARISING HEREUNDER OR IN + CONNECTION WITH YOUR USE OR INABILITY TO USE THE SITE, THE INTERFACE AND/OR THE + BRIDGE SHALL BE LIMITED TO THE AMOUNT ACTUALLY PAID BY YOU, IF ANY, TO STARKWARE FOR + USE OF THE SITE, INTERFACE OR BRIDGE IN RESPECT OF THE PARTICULAR TRANSACTION IN + CONNECTION WITH WHICH THE LOSS OCCURRED, OR $US 10.00, WHICHEVER IS GREATER. YOU + WILL NOT, AND WAIVE ANY RIGHT TO, SEEK TO RECOVER ANY OTHER DAMAGES, INCLUDING + CONSEQUENTIAL, LOST PROFITS, LOSS OF FUNDS, SPECIAL, INDIRECT OR INCIDENTAL DAMAGES + FROM STARKWARE AND FROM STARKWARE’S REPRESENTATIVES. +

    +

    + INASMUCH AS SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSIONS OR LIMITATIONS AS SET + FORTH HEREIN, THE FULL EXTENT OF THE ABOVE EXCLUSIONS AND LIMITATIONS MAY NOT APPLY. +

    +
  20. +
  21. +

    Release of Claims

    +

    + The source code of the software underlying the Bridge is publicly accessible. Before + using the Interface and the Bridge, it is your responsibility to familiarize + yourself with the functionality and methods of operation of the Bridge. You + expressly agree that you assume all risks in connection with your access and use of + the Interface and your interaction with the Bridge. You further expressly waive and + release us from any and all liability, claims, causes of action, or damages arising + from or in any way relating to your use of the Interface and your interaction with + the Bridge. +

    +
  22. +
  23. +

    Amendments to the Terms

    +

    + StarkWare may, at its sole discretion, change the Terms from time to time, including + any other policies incorporated thereto, so please re-visit this page frequently. In + case of any material change, we will make reasonable efforts to post a clear notice + on the Site regarding such change. Such material changes will take effect seven (7) + days after such notice was provided on our Site. Otherwise, all other changes to + these Terms are effective as of the stated "Last Revised" date and your + continued use of the Site or the Interface or the Bridge on or after the Last + Revised date will constitute acceptance of, and agreement to be bound by, those + changes. In the event that the Terms should be amended to comply with any legal + requirements or security concerns, the amendments may take effect immediately, or as + required by the law and without any prior notice. +

    +
  24. +
  25. +

    + Termination of these Terms and the Termination of the Site’s and Interface’s and + Bridge’s Operation +

    +

    + At any time, StarkWare may without notice discontinue your use of the Site or the + Interface or the Bridge, at its sole discretion, in addition to any other remedies + that may be available to StarkWare under any applicable law. +

    +

    + Additionally, StarkWare may at any time, at its sole discretion, cease the operation + of the Site or the Interface or the Bridge or any part thereof, temporarily or + permanently, delete any information or content from the Site or correct, modify, + amend, enhance, improve and make any other changes thereto or discontinue displaying + or providing any information, Content or features therein without giving any prior + notice. You agree and acknowledge that StarkWare does not assume any responsibility + with respect to, or in connection with the termination of the Site’s or the + Interface’s or the Bridge’s operation and loss of any data. The provisions of these + Terms that, by their nature and content, must survive the termination of these Terms + in order to achieve the fundamental purposes of these Terms shall so survive. + Without limiting the generality of the forgoing, the Intellectual Property, + Disclaimer and Warranties, Limitation of Liability, Indemnification and General + sections will survive the termination of the Terms. +

    +
  26. +
  27. +

    + Governing Law; Jurisdiction; Agreement to Arbitrate; No Class Action; Waiver of + Right to Jury Trial +

    +

    + Any claim relating to the Site, the Interface, the Bridge or the use thereof will be + governed by and interpreted in accordance with the laws of Singapore, without + reference to its conflict-of-laws principles and the United Nations Convention + Relating to a Uniform Law on the International Sale of Goods may not be applied. +

    +

    + All disputes arising out of or in connection with the present contract shall be + finally settled under the Rules of Arbitration of the International Chamber of + Commerce by one or more arbitrators appointed in accordance with the said Rules. The + arbitration will be conducted in the English language and held by teleconference or, + if teleconference is not possible, in Singapore (the " + Agreement to Arbitrate + "). +

    +

    + You must bring any and all claims or disputes against us in your individual capacity + and not as a plaintiff in or member of any purported class action, collective + action, private attorney general action, or other representative proceeding. This + provision applies to class arbitration. +

    +

    You and we both agree to waive the right to demand a trial by jury.

    +

    + Notwithstanding the foregoing, StarkWare may seek injunctive relief in any court of + competent jurisdiction. +

    +
  28. +
  29. +

    General

    +

    + (a) These Terms constitute the entire terms and conditions between you and StarkWare + relating to the subject matter herein and supersedes any and all prior or + contemporaneous written or oral agreements or understandings between you and + StarkWare, (b) these Terms do not, and shall not be construed to create any + relationship, partnership, joint venture, employer-employee, agency, or + franchisor-franchisee relationship between the parties hereto, (c) no waiver by + either party of any breach or default hereunder will be deemed to be a waiver of any + preceding or subsequent breach or default, (d) any heading, caption or section title + contained herein is inserted only as a matter of convenience, and in no way defines + or explains any section or provision hereof, (e){' '} + + YOU ACKNOWLEDGE AND AGREE THAT ANY CAUSE OF ACTION THAT YOU MAY HAVE ARISING OUT + OF OR RELATED TO THE SITE MUST COMMENCE WITHIN ONE (1) YEAR AFTER THE CAUSE OF + ACTION ACCRUES. OTHERWISE, SUCH CAUSE OF ACTION IS PERMANENTLY BARRED + + , (f) if any provision hereof is adjudged by any court of competent jurisdiction to + be unenforceable, that provision shall be limited or eliminated to the minimum + extent necessary so that these Terms shall otherwise remain in full force and effect + while most nearly adhering to the intent expressed herein, (g) you may not assign or + transfer these Terms (including all rights and obligations hereunder) without our + prior written consent and any attempt to do so in violation of the foregoing shall + be void. We may assign or transfer these Terms without restriction or notification, + (h) no amendment hereof will be binding unless in writing and signed by StarkWare, + and (i) the parties agree that all correspondence relating to these Terms shall be + written and in the English language. +

    +
  30. +
+
+
+ {!isAcceptTerms && ( +
+
+ )} +
+ ); +}; diff --git a/src/routes/Terms/Terms.module.scss b/src/routes/Terms/Terms.module.scss new file mode 100644 index 00000000..85d26c84 --- /dev/null +++ b/src/routes/Terms/Terms.module.scss @@ -0,0 +1,32 @@ +@import '../../index'; + +.terms { + padding-left: inherit; + padding-right: inherit; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + overflow: auto; + line-height: 1.7; + + b { + letter-spacing: 0.5px; + } + + li::marker { + font-size: 24px; + font-weight: bold; + } +} + +.acceptButton { + padding-left: inherit; + padding-right: inherit; + text-align: center; + position: absolute; + bottom: 0; + left: 0; + right: 0; +} diff --git a/src/routes/Terms/Terms.strings.js b/src/routes/Terms/Terms.strings.js new file mode 100644 index 00000000..e85d26d4 --- /dev/null +++ b/src/routes/Terms/Terms.strings.js @@ -0,0 +1,7 @@ +import utils from '../../utils'; + +const {title_txt, accept_btn_txt, last_revised_txt} = utils.getTranslation('screens.terms'); + +export const TITLE_TXT = title_txt; +export const ACCEPT_BTN_TXT = accept_btn_txt; +export const LAST_REVISED_TXT = last_revised_txt; diff --git a/src/routes/index.js b/src/routes/index.js new file mode 100644 index 00000000..7b773c35 --- /dev/null +++ b/src/routes/index.js @@ -0,0 +1,5 @@ +export * from './Bridge/Bridge'; +export * from './Terms/Terms'; +export * from './Login/Login'; +export * from './Faq/Faq'; +export * from './ProtectedRoute'; diff --git a/src/styles/colors.module.scss b/src/styles/colors.module.scss index 681b6e07..ff7be163 100644 --- a/src/styles/colors.module.scss +++ b/src/styles/colors.module.scss @@ -37,4 +37,5 @@ $--color-discord: #5865f2; colorWhiteOp10: $--color-white-op-10; colorWhiteOp20: $--color-white-op-20; colorWhiteOp50: $--color-white-op-50; + colorGamma1: $--color-gamma-1; } diff --git a/yarn.lock b/yarn.lock index cf97b6c3..795a051e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1635,6 +1635,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.7.6": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.9.tgz#d19fbf802d01a8cb6cf053a64e472d42c434ba72" + integrity sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.10.4", "@babel/template@^7.12.13", "@babel/template@^7.16.0", "@babel/template@^7.3.3": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.0.tgz#d16a35ebf4cd74e202083356fab21dd89363ddd6" @@ -10914,6 +10921,13 @@ hex-color-regex@^1.1.0: resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== +history@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/history/-/history-5.3.0.tgz#1548abaa245ba47992f063a0783db91ef201c73b" + integrity sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ== + dependencies: + "@babel/runtime" "^7.7.6" + hmac-drbg@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" @@ -17243,6 +17257,21 @@ react-refresh@^0.8.3: resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.8.3.tgz#721d4657672d400c5e3c75d063c4a85fb2d5d68f" integrity sha512-X8jZHc7nCMjaCqoU+V2I0cOhNW+QMBwSUkeXnTi8IPe6zaRWfn60ZzvFDZqWPfmSJfjub7dDW1SP0jaHWLu/hg== +react-router-dom@6: + version "6.3.0" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.3.0.tgz#a0216da813454e521905b5fa55e0e5176123f43d" + integrity sha512-uaJj7LKytRxZNQV8+RbzJWnJ8K2nPsOOEuX7aQstlMZKQT0164C+X2w6bnkqU3sjtLvpd5ojrezAyfZ1+0sStw== + dependencies: + history "^5.2.0" + react-router "6.3.0" + +react-router@6.3.0, react-router@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.3.0.tgz#3970cc64b4cb4eae0c1ea5203a80334fdd175557" + integrity sha512-7Wh1DzVQ+tlFjkeo+ujvjSqSJmkt1+8JO+T5xklPlgrh70y7ogx75ODRW0ThWhY7S+6yEDks8TYrtQe/aoboBQ== + dependencies: + history "^5.2.0" + react-scripts@4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/react-scripts/-/react-scripts-4.0.3.tgz#b1cafed7c3fa603e7628ba0f187787964cb5d345"