From be1da157e71cbd54e6c139e6ee170d4492a6ac44 Mon Sep 17 00:00:00 2001 From: DanZ Date: Mon, 19 Dec 2022 12:36:56 +0200 Subject: [PATCH] v1.11 (#328) --- package.json | 35 +- public/index.html | 1 - src/App.js | 2 + src/assets/svg/wallets/coinbase-wallet.svg | 9 + src/components/Containers/Header/Header.js | 11 +- src/components/Features/Account/Account.js | 22 +- .../Features/ToastManager/ToastManager.js | 14 +- .../UI/Modal/LoginModal/LoginModal.js | 108 ++--- .../UI/MultiChoiceItem/MultiChoiceItem.js | 8 +- .../MultiChoiceItem.module.scss | 4 + .../UI/MultiChoiceMenu/MultiChoiceMenu.js | 2 +- .../StarknetWalletButton.js | 2 +- src/components/UI/Tab/Tab.js | 5 +- src/components/UI/Tab/Tab.module.scss | 15 + src/components/UI/Tabs/Tabs.js | 2 +- .../UI/WalletButton/WalletButton.js | 17 +- .../UI/WalletButton/WalletButton.module.scss | 4 + src/config/constants.js | 11 +- src/config/sources.js | 8 - src/config/terms.js | 22 +- src/config/translations.js | 5 +- src/config/wallets.js | 14 +- src/hooks/index.js | 5 +- src/hooks/useAccountChange.js | 9 +- src/hooks/useAutoConnect.js | 47 +++ src/hooks/useCoinbaseWallet.js | 15 + src/hooks/useContract.js | 11 +- src/hooks/useEthereumWallet.js | 26 +- src/hooks/useGetStarknet.js | 27 ++ src/hooks/useMetaMask.js | 43 ++ src/hooks/useStarknetWallet.js | 87 ++-- src/hooks/useTokenContractAPI.js | 22 +- src/hooks/useTransfer.js | 3 - src/hooks/useTransferToL1.js | 27 +- src/hooks/useTransferToL2.js | 10 +- src/hooks/useWalletHandlers.js | 90 ++++ src/hooks/useWeb3.js | 9 + src/index.js | 7 +- .../BlockHashProvider/BlockHashProvider.js | 24 +- src/providers/ModalProvider/modal-hooks.js | 17 +- .../TokensProvider/TokensProvider.js | 16 +- .../TokensProvider/tokens-reducer.js | 6 +- .../TransfersLogProvider.js | 34 +- .../transfers-log-reducer.js | 7 +- .../WalletsProvider/WalletsProvider.js | 27 +- .../WalletsProvider/wallets-hooks.js | 54 +-- src/routes/Bridge/Bridge.js | 8 +- src/routes/Terms/Terms.module.scss | 2 - yarn.lock | 395 ++++++++++++------ 49 files changed, 938 insertions(+), 411 deletions(-) create mode 100644 src/assets/svg/wallets/coinbase-wallet.svg create mode 100644 src/hooks/useAutoConnect.js create mode 100644 src/hooks/useCoinbaseWallet.js create mode 100644 src/hooks/useGetStarknet.js create mode 100644 src/hooks/useMetaMask.js create mode 100644 src/hooks/useWalletHandlers.js create mode 100644 src/hooks/useWeb3.js diff --git a/package.json b/package.json index 0ebf345c..5da00c84 100644 --- a/package.json +++ b/package.json @@ -1,24 +1,24 @@ { "name": "starkgate-frontend", - "version": "1.10.0", + "version": "1.11.0", "homepage": "./", "scripts": { - "clean": "rm -rf ./dist", "prebuild": "yarn run clean", "build": "react-scripts build", - "build:mainnet": "GENERATE_SOURCEMAP=false env-cmd -f .env.mainnet yarn run build", "build:goerli": "GENERATE_SOURCEMAP=false env-cmd -f .env.goerli yarn run build", - "start": "ESLINT_NO_DEV_ERRORS=true react-scripts start", - "test": "react-scripts test --env=jsdom", + "build:mainnet": "GENERATE_SOURCEMAP=false env-cmd -f .env.mainnet yarn run build", + "clean": "rm -rf ./dist", "eject": "react-scripts eject", - "lint:scripts": "eslint **/*.{js,jsx}", - "lint:styles": "stylelint **/*.{css,scss}", - "lint:prettier": "prettier --check **/*.{js,jsx,css,scss}", - "lint": "yarn run -s lint:scripts & yarn run -s lint:styles & yarn run -s lint:prettier", + "format": "yarn run -s format:scripts & yarn run -s format:styles & yarn run -s format:prettier", + "format:prettier": "prettier --write **/*.{js,jsx,css,scss,yaml,yml,md}", "format:scripts": "eslint --fix **/*.{js,jsx}", "format:styles": "stylelint --fix **/*.{css,scss}", - "format:prettier": "prettier --write **/*.{js,jsx,css,scss,yaml,yml,md}", - "format": "yarn run -s format:scripts & yarn run -s format:styles & yarn run -s format:prettier" + "lint": "yarn run -s lint:scripts & yarn run -s lint:styles & yarn run -s lint:prettier", + "lint:prettier": "prettier --check **/*.{js,jsx,css,scss}", + "lint:scripts": "eslint **/*.{js,jsx}", + "lint:styles": "stylelint **/*.{css,scss}", + "start": "ESLINT_NO_DEV_ERRORS=true react-scripts start", + "test": "react-scripts test --env=jsdom" }, "husky": { "hooks": { @@ -55,22 +55,25 @@ "@emotion/react": "^11.8.2", "@emotion/styled": "^11.8.1", "@headlessui/react": "^1.5.0", + "@metamask/onboarding": "^1.0.1", "@mui/material": "^5.5.3", "@mui/styles": "^5.5.0", "@splitbee/web": "^0.3.0", - "@starkware-industries/commons-js-enums": "1.1.0", - "@starkware-industries/commons-js-hooks": "1.0.0", - "@starkware-industries/commons-js-libs": "1.2.1", - "@starkware-industries/commons-js-utils": "1.1.0", + "@starkware-industries/commons-js-enums": "^1.2.0", + "@starkware-industries/commons-js-hooks": "^1.0.0", + "@starkware-industries/commons-js-utils": "^1.2.0", + "get-starknet": "1.5.0", "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": "4.9.0", "use-breakpoint": "^3.0.1", "use-deep-compare-effect": "^1.8.1", - "use-wallet": "^0.13.6" + "use-wallet": "^0.13.6", + "web3": "^1.8.1" }, "devDependencies": { "@babel/cli": "^7.16.0", diff --git a/public/index.html b/public/index.html index 336dab9e..9b8b6dc6 100644 --- a/public/index.html +++ b/public/index.html @@ -1,7 +1,6 @@ - StarkGate Alpha diff --git a/src/App.js b/src/App.js index d1a0bd5f..e9b0d957 100644 --- a/src/App.js +++ b/src/App.js @@ -8,6 +8,7 @@ import {Footer, Header} from './components/Containers'; import {StyledBackground} from './components/UI'; import {SideButton} from './components/UI/SideButton/SideButton'; import {useConstants, useDiscordTabTracking} from './hooks'; +import {useAutoConnect} from './hooks/useAutoConnect'; import {useApp} from './providers/AppProvider'; import {Bridge, Faq, ProtectedRoute, Terms} from './routes'; @@ -15,6 +16,7 @@ export const App = () => { const [trackDiscordClick] = useDiscordTabTracking(); const {DISCORD_LINK_URL} = useConstants(); const {isAcceptTerms, isScrollActive} = useApp(); + useAutoConnect(); const onDiscordClick = () => { trackDiscordClick(); diff --git a/src/assets/svg/wallets/coinbase-wallet.svg b/src/assets/svg/wallets/coinbase-wallet.svg new file mode 100644 index 00000000..f95efc12 --- /dev/null +++ b/src/assets/svg/wallets/coinbase-wallet.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/components/Containers/Header/Header.js b/src/components/Containers/Header/Header.js index aac4739b..a4397bf5 100644 --- a/src/components/Containers/Header/Header.js +++ b/src/components/Containers/Header/Header.js @@ -1,8 +1,10 @@ -import {toClasses} from '@starkware-industries/commons-js-utils'; +import {openInNewTab, toClasses} from '@starkware-industries/commons-js-utils'; import React from 'react'; import {useLocation} from 'react-router-dom'; import {ReactComponent as StarkGateLogo} from '../../../assets/img/starkgate.svg'; +import {ReactComponent as LinkIcon} from '../../../assets/svg/icons/link.svg'; +import {STARKNET_ECOSYSTEM_URL} from '../../../config/constants'; import {useTabsTranslation} from '../../../hooks'; import {useApp} from '../../../providers/AppProvider'; import {useMenu} from '../../../providers/MenuProvider'; @@ -15,11 +17,16 @@ export const Header = () => { const {showSourceMenu} = useMenu(); const {navigateToRoute, isAcceptTerms} = useApp(); const {pathname} = useLocation(); - const {termsTxt, faqTxt} = useTabsTranslation(); + const {discoverAppsTxt, termsTxt, faqTxt} = useTabsTranslation(); const {selectDefaultSource} = useSource(); const [, swapToL1] = useIsL1(); const tabs = [ + { + text: discoverAppsTxt, + icon: , + onClick: () => openInNewTab(STARKNET_ECOSYSTEM_URL) + }, { text: termsTxt, isActive: pathname === '/terms', diff --git a/src/components/Features/Account/Account.js b/src/components/Features/Account/Account.js index e53dd89c..046f4250 100644 --- a/src/components/Features/Account/Account.js +++ b/src/components/Features/Account/Account.js @@ -1,18 +1,19 @@ -import {addAddressPadding} from '@starkware-industries/commons-js-libs/starknet'; +import {useLogger} from '@starkware-industries/commons-js-hooks'; import {evaluate, findIndexById} from '@starkware-industries/commons-js-utils'; import PropTypes from 'prop-types'; -import React from 'react'; +import React, {useEffect} from 'react'; +import {addAddressPadding} from 'starknet'; import { useAccountTracking, + useAccountTranslation, useCompleteTransferToL1, - useEnvs, - useAccountTranslation + useEnvs } from '../../../hooks'; import {useMenu} from '../../../providers/MenuProvider'; import {useTransfer} from '../../../providers/TransferProvider'; import {useAccountTransfersLog} from '../../../providers/TransfersLogProvider'; -import {useAccountHash, useWallets} from '../../../providers/WalletsProvider'; +import {useWallets} from '../../../providers/WalletsProvider'; import { AccountAddress, BackButton, @@ -26,6 +27,7 @@ import {TransferLog} from '../TransferLog/TransferLog'; import styles from './Account.module.scss'; export const Account = ({transferId}) => { + const logger = useLogger('Account'); const {titleTxt, btnTxt} = useAccountTranslation(); const [ trackTxLinkClick, @@ -40,10 +42,15 @@ export const Account = ({transferId}) => { const {isL1, isL2, fromNetwork} = useTransfer(); const transfers = useAccountTransfersLog(account); const completeTransferToL1 = useCompleteTransferToL1(); - const accountHash = useAccountHash(); + + useEffect(() => { + if (!account) { + showSourceMenu(); + } + }, [account]); const renderTransfers = () => { - return accountHash && transfers.length + return transfers.length ? transfers.map((transfer, index) => ( { }; const handleLogout = () => { + logger.log(`logout ${fromNetwork} wallet`); showSourceMenu(); resetWallet(); }; diff --git a/src/components/Features/ToastManager/ToastManager.js b/src/components/Features/ToastManager/ToastManager.js index fbb97e0f..d161a0c9 100644 --- a/src/components/Features/ToastManager/ToastManager.js +++ b/src/components/Features/ToastManager/ToastManager.js @@ -4,7 +4,7 @@ import { isRejected, NetworkType } from '@starkware-industries/commons-js-enums'; -import {usePrevious} from '@starkware-industries/commons-js-hooks'; +import {usePrevious, useDidMountEffect} from '@starkware-industries/commons-js-hooks'; import {getCookie, getFullTime, setCookie} from '@starkware-industries/commons-js-utils'; import PropTypes from 'prop-types'; import React, {useEffect} from 'react'; @@ -41,10 +41,8 @@ export const ToastManager = () => { const {breakpoint} = useBreakpoint(Breakpoint); const accountHash = useAccountHash(); - useEffect(() => { - if (!accountHash) { - clearToasts(); - } + useDidMountEffect(() => { + clearToasts(); }, [accountHash]); useEffect(() => { @@ -58,11 +56,7 @@ export const ToastManager = () => { if (accountHash) { renderToasts(); } - }, [transfers, accountHash]); - - useEffect(() => { - return () => clearToasts(); - }, []); + }, [transfers]); const renderToasts = () => { transfers.forEach(transfer => { diff --git a/src/components/UI/Modal/LoginModal/LoginModal.js b/src/components/UI/Modal/LoginModal/LoginModal.js index 71094f6b..80ebce48 100644 --- a/src/components/UI/Modal/LoginModal/LoginModal.js +++ b/src/components/UI/Modal/LoginModal/LoginModal.js @@ -1,109 +1,69 @@ -import { - ChainInfo, - LoginErrorType, - NetworkType, - WalletErrorType, - WalletStatus -} from '@starkware-industries/commons-js-enums'; -import {useDidMountEffect} from '@starkware-industries/commons-js-hooks'; +import {NetworkType, WalletStatus} from '@starkware-industries/commons-js-enums'; +import {useDidMountEffect, useLogger} from '@starkware-industries/commons-js-hooks'; import {evaluate} from '@starkware-industries/commons-js-utils'; import PropTypes from 'prop-types'; import React, {useEffect, useState} from 'react'; -import { - useEnvs, - useLoginTracking, - useLoginTranslation, - useWalletHandlerProvider -} from '../../../../hooks'; +import {useLoginTracking, useLoginTranslation, useWalletHandlers} from '../../../../hooks'; import {useLogin} from '../../../../providers/AppProvider'; import {useHideModal} from '../../../../providers/ModalProvider'; -import {useLoginWallet, useWalletsStatus} from '../../../../providers/WalletsProvider'; +import {useWalletsStatus} from '../../../../providers/WalletsProvider'; import {MultiChoiceMenu} from '../../index'; -const AUTO_CONNECT_TIMEOUT_DURATION = 100; - const LoginModal = ({networkName}) => { - const {titleTxt, unsupportedChainIdTxt} = useLoginTranslation(); - const [trackWalletClick, trackLoginError] = useLoginTracking(); - const {AUTO_CONNECT, SUPPORTED_L1_CHAIN_ID} = useEnvs(); + const logger = useLogger('LoginModal'); const [error, setError] = useState(null); + const {titleTxt} = useLoginTranslation(); + const [trackWalletClick, trackLoginError] = useLoginTracking(); const {statusL1, statusL2} = useWalletsStatus(); const [network, setNetwork] = useState(networkName || NetworkType.L1); - const {walletError, walletStatus, connectWallet} = useLoginWallet(network); - const walletHandlers = useWalletHandlerProvider(network); + const walletHandlers = useWalletHandlers(network); const {isLoggedIn} = useLogin(); const hideModal = useHideModal(); - useEffect(() => { - isLoggedIn && hideModal(); - }, [isLoggedIn]); - useDidMountEffect(() => { - if (statusL1 !== WalletStatus.CONNECTED) { - network !== NetworkType.L1 && setNetwork(NetworkType.L1); - } else if (statusL2 !== WalletStatus.CONNECTED) { - network !== NetworkType.L2 && setNetwork(NetworkType.L2); + if (network === NetworkType.L2 && statusL2 === WalletStatus.CONNECTED) { + setNetwork(NetworkType.L1); + } + + if (network === NetworkType.L1 && statusL1 === WalletStatus.CONNECTED) { + setNetwork(NetworkType.L2); } }, [statusL1, statusL2]); useEffect(() => { - walletError ? handleWalletError(walletError) : setError(null); - }, [walletError]); + isLoggedIn && hideModal(); + }, [isLoggedIn]); + + useEffect(() => { + const walletHandler = walletHandlers.find(({error}) => !!error); + if (walletHandler) { + const {error} = walletHandler; + logger.log('set wallet error', error); + setError(error); + } else { + setError(null); + } + }, [walletHandlers]); useEffect(() => { - let timeoutId; if (error) { trackLoginError(error); - } else if (!error && AUTO_CONNECT) { - if (walletHandlers.length > 0) { - timeoutId = setTimeout( - () => onWalletConnect(walletHandlers[0]), - AUTO_CONNECT_TIMEOUT_DURATION - ); - } } - return () => clearTimeout(timeoutId); - }, [error, walletHandlers]); + }, [error]); - const onWalletConnect = walletHandler => { - const {config} = walletHandler; - const {name} = config; + const walletChoiceClick = async walletHandler => { + const {name} = walletHandler; trackWalletClick(name); - if (!walletHandler.isInstalled()) { - try { - return walletHandler.install(); - } catch (ex) { - setError(ex); - } - } else { - return connectWallet(config); - } - }; - - const handleWalletError = error => { - if (error.name === WalletErrorType.CHAIN_UNSUPPORTED_ERROR) { - setError({ - type: LoginErrorType.UNSUPPORTED_CHAIN_ID, - message: evaluate(unsupportedChainIdTxt, { - chainName: ChainInfo.L1[SUPPORTED_L1_CHAIN_ID].NAME - }) - }); - } + logger.log(`connect to ${name}`); + await walletHandler.connect(); }; const mapLoginWalletsToChoices = () => { return walletHandlers.map(walletHandler => { - const { - config: {id, description, name, logoPath} - } = walletHandler; return { - id, - description, - isLoading: walletStatus === WalletStatus.CONNECTING, - logoPath, - name, - onClick: () => onWalletConnect(walletHandler) + ...walletHandler, + onClick: () => walletChoiceClick(walletHandler) }; }); }; diff --git a/src/components/UI/MultiChoiceItem/MultiChoiceItem.js b/src/components/UI/MultiChoiceItem/MultiChoiceItem.js index fa3243b2..59df764f 100644 --- a/src/components/UI/MultiChoiceItem/MultiChoiceItem.js +++ b/src/components/UI/MultiChoiceItem/MultiChoiceItem.js @@ -20,10 +20,15 @@ export const MultiChoiceItem = ({ size = CircleLogoSize.MEDIUM, isDisabled = false, isLoading, + error, onClick }) => (
@@ -50,5 +55,6 @@ MultiChoiceItem.propTypes = { size: PropTypes.number, isDisabled: PropTypes.bool, isLoading: PropTypes.bool, + error: PropTypes.object, onClick: PropTypes.func }; diff --git a/src/components/UI/MultiChoiceItem/MultiChoiceItem.module.scss b/src/components/UI/MultiChoiceItem/MultiChoiceItem.module.scss index 58b56955..7fff327b 100644 --- a/src/components/UI/MultiChoiceItem/MultiChoiceItem.module.scss +++ b/src/components/UI/MultiChoiceItem/MultiChoiceItem.module.scss @@ -8,6 +8,10 @@ border-radius: 8px; overflow-x: auto; + &.error { + border: 1px solid $--color-jasper; + } + &:not(:last-child) { margin-bottom: 8px; } diff --git a/src/components/UI/MultiChoiceMenu/MultiChoiceMenu.js b/src/components/UI/MultiChoiceMenu/MultiChoiceMenu.js index d9bfd81a..5e11e560 100644 --- a/src/components/UI/MultiChoiceMenu/MultiChoiceMenu.js +++ b/src/components/UI/MultiChoiceMenu/MultiChoiceMenu.js @@ -18,7 +18,7 @@ export const MultiChoiceMenu = ({title, description, choices, error, footer}) => {error && ( <>
- + )}
diff --git a/src/components/UI/StarknetWalletButton/StarknetWalletButton.js b/src/components/UI/StarknetWalletButton/StarknetWalletButton.js index eff6d02b..d867a316 100644 --- a/src/components/UI/StarknetWalletButton/StarknetWalletButton.js +++ b/src/components/UI/StarknetWalletButton/StarknetWalletButton.js @@ -1,6 +1,6 @@ import {ChainInfo, NetworkType} from '@starkware-industries/commons-js-enums'; -import {addAddressPadding} from '@starkware-industries/commons-js-libs/starknet'; import React from 'react'; +import {addAddressPadding} from 'starknet'; import {useEnvs} from '../../../hooks'; import {useIsL2} from '../../../providers/TransferProvider'; diff --git a/src/components/UI/Tab/Tab.js b/src/components/UI/Tab/Tab.js index a987b69e..67c8d30c 100644 --- a/src/components/UI/Tab/Tab.js +++ b/src/components/UI/Tab/Tab.js @@ -4,10 +4,10 @@ import React from 'react'; import styles from './Tab.module.scss'; -export const Tab = ({text, isActive, onClick}) => { +export const Tab = ({text, isActive, icon, onClick}) => { return (
- {text} + {text} {icon}
); }; @@ -15,5 +15,6 @@ export const Tab = ({text, isActive, onClick}) => { Tab.propTypes = { text: PropTypes.string, isActive: PropTypes.bool, + icon: PropTypes.object, onClick: PropTypes.func }; diff --git a/src/components/UI/Tab/Tab.module.scss b/src/components/UI/Tab/Tab.module.scss index 88856297..55175ee8 100644 --- a/src/components/UI/Tab/Tab.module.scss +++ b/src/components/UI/Tab/Tab.module.scss @@ -1,6 +1,7 @@ @import '../../../index'; .tab { + display: flex; font-weight: 600; font-size: 14px; line-height: 20px; @@ -11,7 +12,21 @@ color: $--color-gainsboro-1; } + svg { + margin-left: 8px; + + path { + stroke: $--color-white; + } + } + &:hover { color: $--color-light-steel-blue; + + svg { + path { + stroke: $--color-light-steel-blue; + } + } } } diff --git a/src/components/UI/Tabs/Tabs.js b/src/components/UI/Tabs/Tabs.js index ac48c293..722a430e 100644 --- a/src/components/UI/Tabs/Tabs.js +++ b/src/components/UI/Tabs/Tabs.js @@ -8,7 +8,7 @@ export const Tabs = ({tabs}) => { return tabs.map((tab, index) => { return ( - + {index !== tabs.length - 1 && } ); diff --git a/src/components/UI/WalletButton/WalletButton.js b/src/components/UI/WalletButton/WalletButton.js index 774a7bb6..e712ecb7 100644 --- a/src/components/UI/WalletButton/WalletButton.js +++ b/src/components/UI/WalletButton/WalletButton.js @@ -1,5 +1,5 @@ import {WalletStatus} from '@starkware-industries/commons-js-enums'; -import {evaluate, shortenAddress} from '@starkware-industries/commons-js-utils'; +import {evaluate, shortenAddress, toClasses} from '@starkware-industries/commons-js-utils'; import PropTypes from 'prop-types'; import React from 'react'; @@ -17,7 +17,7 @@ export const WalletButton = ({account, chain, network, logoPath, status, onClick return status === WalletStatus.CONNECTED ? ( ) : ( - + ); }; @@ -56,16 +56,18 @@ AccountWalletButton.propTypes = { onClick: PropTypes.func }; -const ConnectWalletButton = ({onClick, network}) => { +const ConnectWalletButton = ({onClick, network, status}) => { const {colorOrangeSoda, colorFlame, colorWhite} = useColors(); - const {connectWalletBtnTxt} = useHeaderTranslation(); + const {connectWalletBtnTxt, connectingWalletBtnTxt} = useHeaderTranslation(); + const connecting = status === WalletStatus.CONNECTING; + return (