From 04b41511d78922ac71d5e46322f74f6ae0d8af28 Mon Sep 17 00:00:00 2001 From: Lucas Santos Date: Mon, 30 Sep 2024 11:04:06 -0300 Subject: [PATCH 1/2] fix: return faucet flow and use migration controller to add new state --- source/assets/images/faucet-error.svg | 4 + source/assets/images/faucet-loading.svg | 4 + source/assets/images/faucet-success.svg | 5 + source/assets/images/faucetmodal.png | Bin 0 -> 5053 bytes source/assets/locales/en.json | 15 ++ source/assets/locales/es.json | 15 ++ source/components/Modal/FaucetAccessModal.tsx | 38 +++++ .../Modal/FaucetFirstAccessModal.tsx | 55 ++++++++ source/components/Modal/index.tsx | 2 + source/components/index.tsx | 1 + source/constants/trustedApps.json | 1 + source/pages/Faucet/Faucet.tsx | 121 ++++++++++++++++ .../Faucet/components/FaucetApiFeedback.tsx | 46 ++++++ .../Faucet/components/FaucetCardAccount.tsx | 33 +++++ .../Faucet/components/FaucetFeedback.tsx | 22 +++ source/pages/Faucet/components/index.tsx | 3 + source/pages/Faucet/hooks/index.ts | 1 + .../Faucet/hooks/useFaucetComponentStates.ts | 133 ++++++++++++++++++ source/pages/Faucet/index.ts | 1 + source/pages/Home/Home.tsx | 31 +++- source/routers/index.tsx | 6 + .../Background/controllers/MainController.ts | 11 ++ .../controllers/faucetController.ts | 27 ---- source/scripts/Background/migration/v3_0_1.ts | 3 +- source/state/vault/index.ts | 20 +++ source/state/vault/types.ts | 1 + source/types/faucet.ts | 33 +++++ source/utils/constants.ts | 67 +++++++++ source/utils/faucet.ts | 29 ++++ 29 files changed, 698 insertions(+), 30 deletions(-) create mode 100644 source/assets/images/faucet-error.svg create mode 100644 source/assets/images/faucet-loading.svg create mode 100644 source/assets/images/faucet-success.svg create mode 100644 source/assets/images/faucetmodal.png create mode 100644 source/components/Modal/FaucetAccessModal.tsx create mode 100644 source/components/Modal/FaucetFirstAccessModal.tsx create mode 100644 source/pages/Faucet/Faucet.tsx create mode 100644 source/pages/Faucet/components/FaucetApiFeedback.tsx create mode 100644 source/pages/Faucet/components/FaucetCardAccount.tsx create mode 100644 source/pages/Faucet/components/FaucetFeedback.tsx create mode 100644 source/pages/Faucet/components/index.tsx create mode 100644 source/pages/Faucet/hooks/index.ts create mode 100644 source/pages/Faucet/hooks/useFaucetComponentStates.ts create mode 100644 source/pages/Faucet/index.ts delete mode 100644 source/scripts/Background/controllers/faucetController.ts create mode 100644 source/types/faucet.ts create mode 100644 source/utils/faucet.ts diff --git a/source/assets/images/faucet-error.svg b/source/assets/images/faucet-error.svg new file mode 100644 index 000000000..ffe9d6b33 --- /dev/null +++ b/source/assets/images/faucet-error.svg @@ -0,0 +1,4 @@ + + + + diff --git a/source/assets/images/faucet-loading.svg b/source/assets/images/faucet-loading.svg new file mode 100644 index 000000000..179d6689d --- /dev/null +++ b/source/assets/images/faucet-loading.svg @@ -0,0 +1,4 @@ + + + + diff --git a/source/assets/images/faucet-success.svg b/source/assets/images/faucet-success.svg new file mode 100644 index 000000000..5090ae89f --- /dev/null +++ b/source/assets/images/faucet-success.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/source/assets/images/faucetmodal.png b/source/assets/images/faucetmodal.png new file mode 100644 index 0000000000000000000000000000000000000000..7f15444cf36838209f8b3258522a0267e5579ab4 GIT binary patch literal 5053 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 T*PYoi00000NkvXXu0mjfB^{}2 literal 0 HcmV?d00001 diff --git a/source/assets/locales/en.json b/source/assets/locales/en.json index b503d6869..e3face600 100644 --- a/source/assets/locales/en.json +++ b/source/assets/locales/en.json @@ -520,5 +520,20 @@ "walletSeedPhrasePage": { "keepSeedPhrase": "Keep your seed phrase secret!", "anyoneWithThisInfo": "Anyone with this information is able to steal your funds." + }, + "faucet": { + "grabTextOne":"Grab ${{token}} with our faucet!", + "grabTextTwo":"Grab ${{token}} with our faucet on the {{rpcName}}!", + "pleaseWait":"Please wait while we work our magic...", + "doNotClose":"Do not close the wallet.", + "ERROR":"ERROR!", + "CONGRATULATIONS":"CONGRATULATIONS!", + "someHasJust":"Some {{tokenSymbol}} has just been sent to your {{networkName}} wallet.", + "transactionHash":"Transaction hash", + "requestNow": "Request Now", + "Close": "Close", + "tryAgain": "Try again", + "withOurFaucet": "Grab {{token}} with our faucet to begin experiencing the {{networkName}} network!", + "youCanGet": "You can get {{quantity}} {{token}} per wallet address every 24h." } } \ No newline at end of file diff --git a/source/assets/locales/es.json b/source/assets/locales/es.json index ce03e0da3..740acb6b2 100644 --- a/source/assets/locales/es.json +++ b/source/assets/locales/es.json @@ -520,5 +520,20 @@ "walletSeedPhrasePage": { "keepSeedPhrase": "¡Mantén en secreto tu seed frase!", "anyoneWithThisInfo": "Cualquier persona con esta información puede robar tus fondos." + }, + "faucet": { + "grabTextOne": "¡Obtén ${{token}} con nuestra llave!", + "grabTextTwo": "¡Obtén ${{token}} con nuestra llave en el {{rpcName}}!", + "pleaseWait": "Por favor, espere mientras hacemos nuestra magia...", + "doNotClose": "No cierres la billetera.", + "ERROR": "¡ERROR!", + "CONGRATULATIONS": "¡FELICITACIONES!", + "someHasJust": "Algo de {{tokenSymbol}} acaba de ser enviado a tu billetera de {{networkName}}.", + "transactionHash": "Hash de transacción", + "requestNow": "Solicitar ahora", + "Close": "Cerrar", + "tryAgain": "Intentar de nuevo", + "withOurFaucet": "¡Obtén {{token}} con nuestra llave para comenzar a experimentar la red {{networkName}}!", + "youCanGet": "Puedes obtener {{quantity}} {{token}} por dirección de billetera cada 24 horas." } } \ No newline at end of file diff --git a/source/components/Modal/FaucetAccessModal.tsx b/source/components/Modal/FaucetAccessModal.tsx new file mode 100644 index 000000000..eea833f70 --- /dev/null +++ b/source/components/Modal/FaucetAccessModal.tsx @@ -0,0 +1,38 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { useSelector } from 'react-redux'; + +import rolluxLogo from 'assets/images/rolluxChain.png'; +import sysLogo from 'assets/images/sysChain.svg'; +import { useUtils } from 'hooks/useUtils'; +import { RootState } from 'state/store'; +import { faucetNetworkData } from 'utils/constants'; + +export const FaucetAccessModal = () => { + const { navigate } = useUtils(); + const { t } = useTranslation(); + + const { + activeNetwork: { chainId }, + } = useSelector((state: RootState) => state.vault); + + const currentNetworkData = faucetNetworkData?.[chainId]; + + return ( +
navigate('/faucet')} + className="cursor-pointer z-[49] py-2 justify-center absolute left-[4.3%] top-[8rem] w-[364px] flex items-center rounded-b-[8px] bg-brand-blue400 opacity-100 hover:opacity-55" + > +
+ + +
+

+ {t('faucet.grabTextTwo', { + token: currentNetworkData?.token, + rpcName: currentNetworkData?.network, + })} +

+
+ ); +}; diff --git a/source/components/Modal/FaucetFirstAccessModal.tsx b/source/components/Modal/FaucetFirstAccessModal.tsx new file mode 100644 index 000000000..025155cb5 --- /dev/null +++ b/source/components/Modal/FaucetFirstAccessModal.tsx @@ -0,0 +1,55 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { useSelector } from 'react-redux'; + +import { Icon } from '..'; +import image from 'assets/images/faucetmodal.png'; +import rolluxLogo from 'assets/images/rolluxChain.png'; +import sysLogo from 'assets/images/sysChain.svg'; +import { RootState } from 'state/store'; +import { faucetNetworkData } from 'utils/constants'; + +type FaucetFirstAccessModalProps = { + handleOnClose: () => void; +}; + +export const FaucetFirstAccessModal = ({ + handleOnClose, +}: FaucetFirstAccessModalProps) => { + const { t } = useTranslation(); + + const { + activeNetwork: { chainId }, + } = useSelector((state: RootState) => state.vault); + + const currentNetworkData = faucetNetworkData?.[chainId]; + + return ( +
+
+
+ + +
+
+

+ {t('faucet.grabTextOne', { + token: currentNetworkData?.token, + })} +

+
+
+ + + + +
+ ); +}; diff --git a/source/components/Modal/index.tsx b/source/components/Modal/index.tsx index cb89ee178..d1d3c73b0 100755 --- a/source/components/Modal/index.tsx +++ b/source/components/Modal/index.tsx @@ -1 +1,3 @@ export * from './Modal'; +export * from './FaucetAccessModal'; +export * from './FaucetFirstAccessModal'; diff --git a/source/components/index.tsx b/source/components/index.tsx index dff4c37df..04aaa384f 100644 --- a/source/components/index.tsx +++ b/source/components/index.tsx @@ -15,3 +15,4 @@ export * from './Loading'; export * from './FiatComponent'; export * from './Fee'; export * from './KeepAliveContainer'; +export * from './Modal'; diff --git a/source/constants/trustedApps.json b/source/constants/trustedApps.json index e563816b1..8de89f181 100644 --- a/source/constants/trustedApps.json +++ b/source/constants/trustedApps.json @@ -82,6 +82,7 @@ "metabase.one", "cryptokitties.co", "bridge.syscoin.org", + "faucet.syscoin.org", "luxy.io", "app.pegasys.finance", "beta.pegasys.finance", diff --git a/source/pages/Faucet/Faucet.tsx b/source/pages/Faucet/Faucet.tsx new file mode 100644 index 000000000..12a2ad98c --- /dev/null +++ b/source/pages/Faucet/Faucet.tsx @@ -0,0 +1,121 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { useSelector } from 'react-redux'; + +import { FaucetStatusResponse } from '../../types/faucet'; +import errorIcon from 'assets/images/faucet-error.svg'; +import loadingIcon from 'assets/images/faucet-loading.svg'; +import successIcon from 'assets/images/faucet-success.svg'; +import { NeutralButton } from 'components/Button'; +import { Layout } from 'components/Layout'; +import { RootState } from 'state/store'; +import { faucetNetworkData } from 'utils/constants'; +import { ellipsis } from 'utils/format'; + +import { + FaucetApiFeedback, + FaucetCardAccount, + FaucetFeedback, +} from './components'; +import { useFaucetComponentStates } from './hooks'; + +export const Faucet: React.FC = () => { + const { t } = useTranslation(); + + const { + account, + status, + handleFaucetButton, + faucetButtonLabel, + isLoading, + faucetRequestDetails, + errorMessage, + txHash, + } = useFaucetComponentStates(); + + const { + activeNetwork: { chainId }, + } = useSelector((state: RootState) => state.vault); + + const currentFaucetNetwork = faucetNetworkData?.[chainId]; + + const renderFaucetContent = () => { + switch (status) { + case FaucetStatusResponse.REQUEST: + return ( + !isLoading && ( + <> + + + + + ) + ); + case FaucetStatusResponse.SUCCESS: + return ( + !isLoading && ( + <> + + + + ) + ); + case FaucetStatusResponse.ERROR: + return ( + !isLoading && ( + <> + + + ) + ); + default: + return null; + } + }; + + return ( + + {isLoading && ( + + )} + {renderFaucetContent()} + {!isLoading && ( +
+ + {faucetButtonLabel} + +
+ )} +
+ ); +}; diff --git a/source/pages/Faucet/components/FaucetApiFeedback.tsx b/source/pages/Faucet/components/FaucetApiFeedback.tsx new file mode 100644 index 000000000..b171bdd44 --- /dev/null +++ b/source/pages/Faucet/components/FaucetApiFeedback.tsx @@ -0,0 +1,46 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; + +import { StatusModal } from 'components/Modal/StatusModal'; +import { useUtils } from 'hooks/useUtils'; + +type FaucetApiFeedbackProps = { + apiResponse: string; + apiTitle: string; + status?: string; +}; +export const FaucetApiFeedback: React.FC = ({ + apiTitle, + apiResponse, + status, +}) => { + const { useCopyClipboard } = useUtils(); + const { t } = useTranslation(); + + const [copied, copyText] = useCopyClipboard(); + + const handleCopyToClipboard = () => copyText(apiResponse); + + return ( + <> + +
+

{apiTitle}

+

(status ? handleCopyToClipboard() : null)} + className="text-white text-sm underline overflow-hidden" + style={{ + cursor: status ? 'pointer' : 'default', + }} + > + {apiResponse} +

+
+ + ); +}; diff --git a/source/pages/Faucet/components/FaucetCardAccount.tsx b/source/pages/Faucet/components/FaucetCardAccount.tsx new file mode 100644 index 000000000..05ae0b9d1 --- /dev/null +++ b/source/pages/Faucet/components/FaucetCardAccount.tsx @@ -0,0 +1,33 @@ +import { toSvg } from 'jdenticon'; +import React, { useEffect } from 'react'; + +type FaucetCardAccountProps = { + accountAddress: string; + accountName: string; + accountXpub: string; +}; +export const FaucetCardAccount: React.FC = ({ + accountName, + accountAddress, + accountXpub, +}) => { + useEffect(() => { + const placeholder = document.querySelector('.add-identicon'); + if (!placeholder) return; + + placeholder.innerHTML = toSvg(accountXpub, 50, { + backColor: '#07152B', + padding: 1, + }); + }, [accountXpub]); + + return ( +
+
+
+

{accountName}

+

{accountAddress}

+
+
+ ); +}; diff --git a/source/pages/Faucet/components/FaucetFeedback.tsx b/source/pages/Faucet/components/FaucetFeedback.tsx new file mode 100644 index 000000000..21cb884fb --- /dev/null +++ b/source/pages/Faucet/components/FaucetFeedback.tsx @@ -0,0 +1,22 @@ +import React from 'react'; + +type FaucetFeedbackProps = { + icon: string; + textFeedbackDesc: string; + textFeedbackTitle: string; +}; +export const FaucetFeedback: React.FC = ({ + icon, + textFeedbackDesc, + textFeedbackTitle, +}) => ( +
+ +

+ {textFeedbackTitle} +

+

+ {textFeedbackDesc} +

+
+); diff --git a/source/pages/Faucet/components/index.tsx b/source/pages/Faucet/components/index.tsx new file mode 100644 index 000000000..c9d37f4cc --- /dev/null +++ b/source/pages/Faucet/components/index.tsx @@ -0,0 +1,3 @@ +export * from './FaucetCardAccount'; +export * from './FaucetFeedback'; +export * from './FaucetApiFeedback'; diff --git a/source/pages/Faucet/hooks/index.ts b/source/pages/Faucet/hooks/index.ts new file mode 100644 index 000000000..d7df07a16 --- /dev/null +++ b/source/pages/Faucet/hooks/index.ts @@ -0,0 +1 @@ +export * from './useFaucetComponentStates'; diff --git a/source/pages/Faucet/hooks/useFaucetComponentStates.ts b/source/pages/Faucet/hooks/useFaucetComponentStates.ts new file mode 100644 index 000000000..bae4e747e --- /dev/null +++ b/source/pages/Faucet/hooks/useFaucetComponentStates.ts @@ -0,0 +1,133 @@ +import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useSelector } from 'react-redux'; + +import { + FaucetChainIds, + FaucetStatusResponse, + faucetTxDetailsProps, +} from '../../../types/faucet'; +import { useUtils } from 'hooks/useUtils'; +import { RootState } from 'state/store'; +import { + faucetTxRolluxInfo, + faucetTxRolluxTestnetInfo, + faucetTxSyscoinNEVMInfo, + faucetTxSyscoinNEVMTestnetInfo, +} from 'utils/constants'; +import { claimFaucet } from 'utils/faucet'; + +export const useFaucetComponentStates = () => { + const { t } = useTranslation(); + const { navigate } = useUtils(); + + const { + accounts, + activeAccount, + activeNetwork: { chainId }, + } = useSelector((state: RootState) => state.vault); + + const [status, setStatus] = useState( + FaucetStatusResponse.REQUEST + ); + const [isLoading, setIsLoading] = useState(false); + const [txHash, setTxHash] = useState(''); + const [errorMessage, setErrorMessage] = useState(''); + const [faucetTxDetailsInfo, setFaucetTxDetailsInfo] = + useState(null); + + const account = { + xpub: accounts[activeAccount.type]?.[activeAccount.id]?.xpub, + label: accounts[activeAccount.type]?.[activeAccount.id]?.label, + address: accounts[activeAccount.type]?.[activeAccount.id]?.address, + }; + + const faucetRequestDetails = useMemo( + () => ({ + icon: faucetTxDetailsInfo?.icon, + tokenSymbol: faucetTxDetailsInfo?.token, + networkName: faucetTxDetailsInfo?.networkName, + grabText: t('faucet.withOurFaucet', { + token: faucetTxDetailsInfo?.token, + networkName: faucetTxDetailsInfo?.networkName, + }), + tokenQuantity: t('faucet.youCanGet', { + quantity: faucetTxDetailsInfo?.quantity, + token: faucetTxDetailsInfo?.token, + }), + smartContract: faucetTxDetailsInfo?.smartContract, + }), + [faucetTxDetailsInfo] + ); + + const handleRequestFaucet = useCallback(async () => { + setIsLoading(true); + try { + const data = await claimFaucet(chainId, account.address); + if (data?.data?.status) { + setTxHash(data.data.hash); + setStatus(FaucetStatusResponse.SUCCESS); + } else { + throw new Error( + data?.data?.message || data?.message || 'Unknown error' + ); + } + } catch (error: any) { + setStatus(FaucetStatusResponse.ERROR); + setErrorMessage(error.message || 'An error occurred'); + } finally { + setIsLoading(false); + } + }, [chainId, account.address]); + + const handleFaucetButton = useCallback(() => { + if ( + status === FaucetStatusResponse.REQUEST || + status === FaucetStatusResponse.ERROR + ) { + handleRequestFaucet(); + } else if (status === FaucetStatusResponse.SUCCESS) { + navigate('/home'); + } + }, [status, handleRequestFaucet, navigate]); + + const faucetButtonLabel = useMemo(() => { + switch (status) { + case FaucetStatusResponse.REQUEST: + return t('faucet.requestNow'); + case FaucetStatusResponse.SUCCESS: + return t('faucet.Close'); + case FaucetStatusResponse.ERROR: + return t('faucet.tryAgain'); + default: + return ''; + } + }, [status, t]); + + useEffect(() => { + switch (chainId) { + case FaucetChainIds.RolluxMainnet: + setFaucetTxDetailsInfo(faucetTxRolluxInfo); + break; + case FaucetChainIds.RolluxTestnet: + setFaucetTxDetailsInfo(faucetTxRolluxTestnetInfo); + break; + case FaucetChainIds.NevmMainnet: + setFaucetTxDetailsInfo(faucetTxSyscoinNEVMInfo); + break; + default: + setFaucetTxDetailsInfo(faucetTxSyscoinNEVMTestnetInfo); + } + }, [chainId]); + + return { + account, + status, + handleFaucetButton, + faucetButtonLabel, + isLoading, + faucetRequestDetails, + errorMessage, + txHash, + }; +}; diff --git a/source/pages/Faucet/index.ts b/source/pages/Faucet/index.ts new file mode 100644 index 000000000..758894849 --- /dev/null +++ b/source/pages/Faucet/index.ts @@ -0,0 +1 @@ +export * from './Faucet'; diff --git a/source/pages/Home/Home.tsx b/source/pages/Home/Home.tsx index c287136a9..e045d3d3b 100755 --- a/source/pages/Home/Home.tsx +++ b/source/pages/Home/Home.tsx @@ -1,10 +1,12 @@ -import React, { useEffect, useMemo, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; import { useLocation } from 'react-router-dom'; import { CustomJsonRpcProvider } from '@pollum-io/sysweb3-keyring'; +import { FaucetChainIds } from '../../types/faucet'; +import { FaucetAccessModal, FaucetFirstAccessModal } from 'components/index'; import { Header, Icon, Button, Loading } from 'components/index'; import { StatusModal } from 'components/Modal/StatusModal'; import { WalletProviderDefaultModal } from 'components/Modal/WalletProviderDafault'; @@ -43,6 +45,7 @@ export const Home = () => { isBitcoinBased, lastLogin, isLoadingBalances, + shouldShowFaucetModal: isOpenFaucetModal, } = useSelector((rootState: RootState) => rootState.vault); //* States @@ -51,7 +54,7 @@ export const Home = () => { const [showModalHardWallet, setShowModalHardWallet] = useState(true); //* Constants - const { url } = activeNetwork; + const { url, chainId } = activeNetwork; let isInCooldown: boolean; @@ -124,6 +127,18 @@ export const Home = () => { return formatBalanceDecimals(fiatPriceValue, true); }, [fiatPriceValue, isTestnet, moreThanMillion]); + const handleOnCloseFaucetModal = useCallback(() => { + controllerEmitter( + ['wallet', 'setFaucetModalState'], + [{ chainId: activeNetwork.chainId, state: false }] + ); + }, [activeNetwork]); + + const shouldShowFaucetFirstModal = !!isOpenFaucetModal?.[chainId]; + + const isFaucetAvailable = + !isBitcoinBased && Object.values(FaucetChainIds).includes(chainId); + return (
{accounts[activeAccount.type][activeAccount.id] && @@ -134,6 +149,18 @@ export const Home = () => {
+ {isFaucetAvailable && ( + <> + {shouldShowFaucetFirstModal ? ( + + ) : ( + + )} + + )} +
diff --git a/source/routers/index.tsx b/source/routers/index.tsx index a321fa7e6..1afabcbc1 100644 --- a/source/routers/index.tsx +++ b/source/routers/index.tsx @@ -41,6 +41,7 @@ import { WarningModal } from 'components/Modal'; import { useUtils } from 'hooks/index'; import { useController } from 'hooks/useController'; import { ChainErrorPage } from 'pages/Chain'; +import { Faucet } from 'pages/Faucet'; import { SwitchNetwork } from 'pages/SwitchNetwork'; import { inactivityTime, @@ -216,6 +217,11 @@ export const Router = () => { element={} />} /> + } />} + /> + } />} diff --git a/source/scripts/Background/controllers/MainController.ts b/source/scripts/Background/controllers/MainController.ts index c99547482..35c3460e1 100644 --- a/source/scripts/Background/controllers/MainController.ts +++ b/source/scripts/Background/controllers/MainController.ts @@ -53,6 +53,7 @@ import { setTransactionStatusToAccelerated, setUpdatedNftsToState, setOpenDAppErrorModal, + setFaucetModalState as setShouldShowFaucetModal, } from 'state/vault'; import { IOmmitedAccount, @@ -1549,6 +1550,16 @@ class MainController extends KeyringManager { }), ]); } + + public async setFaucetModalState({ + chainId, + isOpen, + }: { + chainId: number; + isOpen: boolean; + }) { + store.dispatch(setShouldShowFaucetModal({ chainId, isOpen })); + } } export default MainController; diff --git a/source/scripts/Background/controllers/faucetController.ts b/source/scripts/Background/controllers/faucetController.ts deleted file mode 100644 index a6eaa1342..000000000 --- a/source/scripts/Background/controllers/faucetController.ts +++ /dev/null @@ -1,27 +0,0 @@ -import axios from 'axios'; - -const claimFaucet = async (chainId: number, walletAddress: string) => { - let chainName: string; - - if (chainId === 57) { - chainName = `nevm-mainnet`; - } else if (chainId === 5700) { - chainName = `nevm-testnet`; - } else if (chainId === 57000) { - chainName = `rollux-testnet`; - } else if (chainId === 570) { - chainName = `rollux-mainnet`; - } else { - chainName = ``; - } - try { - const { data } = await axios.get( - `https://chains.tools/api/faucet/claim?networkKey=${chainName}&walletAddress=${walletAddress}` - ); - return data; - } catch (err) { - return err; - } -}; - -export { claimFaucet }; diff --git a/source/scripts/Background/migration/v3_0_1.ts b/source/scripts/Background/migration/v3_0_1.ts index 278f2a1d0..885855dd6 100644 --- a/source/scripts/Background/migration/v3_0_1.ts +++ b/source/scripts/Background/migration/v3_0_1.ts @@ -1,6 +1,7 @@ /* eslint-disable camelcase */ import { saveState } from 'state/paliStorage'; +import { initialState as initialVaultState } from 'state/vault'; import { IVaultState } from 'state/vault/types'; type V3_0_1 = { @@ -13,7 +14,7 @@ const MigrateRunner = async (oldState: any) => { ...oldState, vault: { ...oldState.vault, - // add new properties here + shouldShowFaucetModal: initialVaultState.shouldShowFaucetModal, }, }; await saveState(newState); diff --git a/source/state/vault/index.ts b/source/state/vault/index.ts index d2f9a383b..4a20183c0 100644 --- a/source/state/vault/index.ts +++ b/source/state/vault/index.ts @@ -83,6 +83,12 @@ export const initialState: IVaultState = { isPolling: false, currentBlock: undefined, coinsList: [], + shouldShowFaucetModal: { + 57: true, + 570: true, + 5700: true, + 57000: true, + }, }; export const getHasEncryptedVault = createAsyncThunk( @@ -750,6 +756,18 @@ const VaultState = createSlice({ } as IEvmTransactionResponse; }, + setFaucetModalState: ( + state: IVaultState, + action: PayloadAction<{ chainId: number; isOpen: boolean }> + ) => { + const { chainId, isOpen } = action.payload; + if (state.isBitcoinBased) { + return; + } + + state.shouldShowFaucetModal[chainId] = isOpen; + }, + setTransactionStatusToAccelerated( state: IVaultState, action: PayloadAction<{ @@ -786,6 +804,7 @@ const VaultState = createSlice({ ] = removedTx; }, }, + extraReducers: (builder) => { builder.addCase(getHasEncryptedVault.fulfilled, (state, action) => { state.hasEncryptedVault = action.payload; @@ -807,6 +826,7 @@ export const { setActiveNetwork, setIsNetworkChanging, setIsDappAskingToChangeNetwork, + setFaucetModalState, setIsLoadingBalances, setIsLoadingAssets, setIsLoadingTxs, diff --git a/source/state/vault/types.ts b/source/state/vault/types.ts index 51ae6c93b..cea175f79 100644 --- a/source/state/vault/types.ts +++ b/source/state/vault/types.ts @@ -43,6 +43,7 @@ export interface IVaultState { isTimerEnabled: boolean; lastLogin: number; networks: INetworksVault; + shouldShowFaucetModal: { [k: number]: boolean }; timer: number; } diff --git a/source/types/faucet.ts b/source/types/faucet.ts new file mode 100644 index 000000000..204acafb2 --- /dev/null +++ b/source/types/faucet.ts @@ -0,0 +1,33 @@ +/* eslint-disable no-shadow */ +export type faucetTxDetailsProps = { + icon: string; + networkName: string; + quantity: number; + smartContract: string; + token: string; +}; + +export enum FaucetChainIds { + RolluxMainnet = 570, + RolluxTestnet = 57000, + NevmTestnet = 5700, + NevmMainnet = 57, +} + +export enum FaucetChainSymbols { + SYS = 'SYS', + TSYS = 'TSYS', +} + +export enum FaucetChainNames { + ROLLUX = 'Rollux', + ROLLUX_TESTNET = 'Rollux Testnet', + SYSCOIN_NEVM = 'Syscoin NEVM', + SYSCOIN_NEVM_TESTNET = 'Syscoin NEVM Testnet', +} + +export enum FaucetStatusResponse { + ERROR = 'error', + REQUEST = 'request', + SUCCESS = 'success', +} diff --git a/source/utils/constants.ts b/source/utils/constants.ts index 5c1715f8c..48053438f 100644 --- a/source/utils/constants.ts +++ b/source/utils/constants.ts @@ -1,5 +1,13 @@ import { INetwork, INetworkType } from '@pollum-io/sysweb3-network'; +import { + FaucetChainIds, + FaucetChainNames, + FaucetChainSymbols, +} from '../types/faucet'; +import rolluxChain from 'assets/images/rolluxChain.png'; +import sysChain from 'assets/images/sysChain.svg'; + export const INITIAL_FEE = { baseFee: 0, gasPrice: 0, @@ -62,3 +70,62 @@ export const SYSCOIN_MAINNET_DEFAULT_NETWORK = { network: SYSCOIN_MAINNET_NETWORK_57, isEdit: true, }; + +interface IFaucetNetworkData { + [key: string]: { + network: string; + token: string; + }; +} + +// for faucet +export const faucetNetworkData: IFaucetNetworkData = { + [FaucetChainIds.NevmMainnet]: { + token: FaucetChainSymbols.SYS, + network: FaucetChainNames.SYSCOIN_NEVM, + }, + [FaucetChainIds.NevmTestnet]: { + token: FaucetChainSymbols.TSYS, + network: FaucetChainNames.SYSCOIN_NEVM_TESTNET, + }, + [FaucetChainIds.RolluxMainnet]: { + token: FaucetChainSymbols.SYS, + network: FaucetChainNames.ROLLUX, + }, + [FaucetChainIds.RolluxTestnet]: { + token: FaucetChainSymbols.TSYS, + network: FaucetChainNames.ROLLUX_TESTNET, + }, +}; + +export const faucetTxRolluxInfo = { + icon: rolluxChain, + token: '$SYS', + networkName: 'Rollux', + quantity: 0.001, + smartContract: '0x35EE5876Db071b527dC62FD3EE3c32e4304d8C23', +}; + +export const faucetTxRolluxTestnetInfo = { + icon: rolluxChain, + token: '$TSYS', + networkName: 'Rollux Testnet', + quantity: 1, + smartContract: '0x35EE5876Db071b527dC62FD3EE3c32e4304d8C23', +}; + +export const faucetTxSyscoinNEVMInfo = { + icon: sysChain, + token: '$SYS', + networkName: 'Syscoin NEVM', + quantity: 0.01, + smartContract: '0x35EE5876Db071b527dC62FD3EE3c32e4304d8C23', +}; + +export const faucetTxSyscoinNEVMTestnetInfo = { + icon: sysChain, + token: '$TSYS', + networkName: 'Syscoin NEVM Testnet', + quantity: 1, + smartContract: '0x35EE5876Db071b527dC62FD3EE3c32e4304d8C23', +}; diff --git a/source/utils/faucet.ts b/source/utils/faucet.ts new file mode 100644 index 000000000..cf7988f11 --- /dev/null +++ b/source/utils/faucet.ts @@ -0,0 +1,29 @@ +import axios from 'axios'; + +const getChainName = (chainId: number): string => { + switch (chainId) { + case 57: + return 'nevm-mainnet'; + case 5700: + return 'nevm-testnet'; + case 57000: + return 'rollux-testnet'; + case 570: + return 'rollux-mainnet'; + default: + return ''; + } +}; + +export const claimFaucet = async (chainId: number, walletAddress: string) => { + const chainName = getChainName(chainId); + + try { + const { data } = await axios.get( + `https://chains.tools/api/faucet/claim?networkKey=${chainName}&walletAddress=${walletAddress}` + ); + return data; + } catch (err) { + return err; + } +}; From c111282114e9c139e580d76eb0dba292a9eed8a1 Mon Sep 17 00:00:00 2001 From: Lucas Santos Date: Mon, 30 Sep 2024 11:22:13 -0300 Subject: [PATCH 2/2] chore: add description and update pali version --- package.json | 4 ++-- source/config/consts.js | 2 +- source/scripts/Background/controllers/MigrationController.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index e2057fa87..737d5429d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "paliwallet", - "version": "3.0.0", + "version": "3.0.1", "description": "A Non-Custodial Crypto Wallet", "private": true, "repository": { @@ -184,4 +184,4 @@ }, "homepage": "https://github.com/syscoin/pali_wallet#readme", "author": "pollum labs" -} +} \ No newline at end of file diff --git a/source/config/consts.js b/source/config/consts.js index 08234a87c..559ba35cd 100644 --- a/source/config/consts.js +++ b/source/config/consts.js @@ -74,7 +74,7 @@ const MV2_OPTIONS = { const MV3_OPTIONS = { manifest_version: 3, name: 'Pali Wallet', - version: '3.0.0', + version: '3.0.1', icons: { 16: 'assets/icons/favicon-16.png', 32: 'assets/icons/favicon-32.png', diff --git a/source/scripts/Background/controllers/MigrationController.ts b/source/scripts/Background/controllers/MigrationController.ts index 856502516..f91557747 100644 --- a/source/scripts/Background/controllers/MigrationController.ts +++ b/source/scripts/Background/controllers/MigrationController.ts @@ -9,7 +9,7 @@ const MigrationController = async () => { /** * version < 3.0.1 - * Description: Example of migration from version 3.0.0 to 3.0.1 + * Description: add faucet feature */ if (currentPaliVersion === '3.0.1') { await v3_0_1(state);