diff --git a/package.json b/package.json index 8cfc157ff..05285f5ff 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": { 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 000000000..7f15444cf Binary files /dev/null and b/source/assets/images/faucetmodal.png differ 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/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/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/MigrationController.ts b/source/scripts/Background/controllers/MigrationController.ts index 24eec7dd2..cdeee4317 100644 --- a/source/scripts/Background/controllers/MigrationController.ts +++ b/source/scripts/Background/controllers/MigrationController.ts @@ -14,7 +14,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); 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; + } +};