diff --git a/src/react/App.tsx b/src/react/App.tsx
index 0a497fa..5d028ef 100644
--- a/src/react/App.tsx
+++ b/src/react/App.tsx
@@ -1,27 +1,34 @@
import { CssBaseline, ThemeProvider, StyledEngineProvider } from "@mui/material";
import { FC, ReactElement } from "react";
import { HashRouter, Route, Switch } from "react-router-dom";
+
+import BTECContextWrapper from "./BTECContext";
import { OnlineDetector } from "./components/OnlineDetector";
import VersionFooter from "./components/VersionFooter";
+import { paths } from "./constants";
import GlobalContextWrapper from "./GlobalContext";
-import CreateMnemonic from "./pages/CreateMnemonic";
-import Home from "./pages/Home";
-import theme from "./theme";
-import ConfigureValidatorKeys from "./pages/ConfigureValidatorKeys";
-import MnemonicImport from "./pages/MnemonicImport";
-import ConfigureWithdrawalAddress from "./pages/ConfigureWithdrawalAddress";
-import { BTECImportPath, ConfigureBTECPath, ConfigureCreatePath, ConfigureExistingPath, CreateCredentialsPath, CreateKeysCreatePath, CreateKeysExistingPath, CreatePath, ExistingImportPath, FinishCreatePath, FinishCredentialsPath, FinishExistingPath } from "./constants";
-import CreateValidatorKeys from "./pages/CreateValidatorKeys";
import KeyCreationContextWrapper from "./KeyCreationContext";
-import BTECContextWrapper from "./BTECContext";
-import FinishKeyGeneration from "./pages/FinishKeyGeneration";
+import ConfigureValidatorKeys from "./pages/ConfigureValidatorKeys";
+import ConfigureWithdrawalAddress from "./pages/ConfigureWithdrawalAddress";
import CreateCredentialsChange from "./pages/CreateCredentialsChange";
+import CreateMnemonic from "./pages/CreateMnemonic";
+import CreateValidatorKeys from "./pages/CreateValidatorKeys";
import FinishCredentialsGeneration from "./pages/FinishCredentialsGeneration";
+import FinishKeyGeneration from "./pages/FinishKeyGeneration";
+import Home from "./pages/Home";
+import MnemonicImport from "./pages/MnemonicImport";
+import theme from "./theme";
/**
- * The React app top level including theme and routing.
+ * Routing for the application. Broken into four sections:
+ * - Primary home page
+ * - Routes for creating a mnemonic and validator keys
+ * - Routes for using an existing mnemonic to create validator keys
+ * - Routes for generating the credentials change
*
- * @returns the react element containing the app
+ * Each of the three flows is wrapped in a React Context that will store
+ * the inputs of the user to be accessible across each page. This prevents
+ * prop drilling
*/
const App: FC = (): ReactElement => {
return (
@@ -40,10 +47,10 @@ const App: FC = (): ReactElement => {
{/* Create Mnemonic & Keys Flow */}
- } />
- } />
- } />
- } />
+ } />
+ } />
+ } />
+ } />
@@ -51,10 +58,10 @@ const App: FC = (): ReactElement => {
{/* Import Mnemonic & Generate Keys Flow */}
- } />
- } />
- } />
- } />
+ } />
+ } />
+ } />
+ } />
@@ -62,10 +69,10 @@ const App: FC = (): ReactElement => {
{/* Update Withdrawal Credentials Flow */}
- } />
- } />
- } />
- } />
+ } />
+ } />
+ } />
+ } />
diff --git a/src/react/BTECContext.tsx b/src/react/BTECContext.tsx
index aa7d60a..9b407dd 100644
--- a/src/react/BTECContext.tsx
+++ b/src/react/BTECContext.tsx
@@ -30,6 +30,9 @@ export const BTECContext = createContext({
setWithdrawalAddress: () => {},
});
+/**
+ * Context for making the withdrawal credentials change
+ */
const BTECContextWrapper = ({ children }: { children: React.ReactNode}) => {
const [btecIndices, setBTECIndices] = useState("");
const [btecCredentials, setBTECCredentials] = useState("");
diff --git a/src/react/KeyCreationContext.tsx b/src/react/KeyCreationContext.tsx
index 21f798a..a1fff9c 100644
--- a/src/react/KeyCreationContext.tsx
+++ b/src/react/KeyCreationContext.tsx
@@ -30,6 +30,9 @@ export const KeyCreationContext = createContext({
setWithdrawalAddress: () => {},
});
+/**
+ * Context for generating a validator key for both using an existing mnemonic or a new one
+ */
const KeyCreationContextWrapper = ({ children }: { children: React.ReactNode}) => {
const [folderLocation, setFolderLocation] = useState("");
const [index, setIndex] = useState(0);
diff --git a/src/react/components/FolderSelector.tsx b/src/react/components/FolderSelector.tsx
index 15c9107..06dbf7f 100644
--- a/src/react/components/FolderSelector.tsx
+++ b/src/react/components/FolderSelector.tsx
@@ -1,16 +1,26 @@
import { Button, Typography } from "@mui/material";
import { OpenDialogOptions, OpenDialogReturnValue } from 'electron';
import { useState } from "react";
+
import { errors } from "../constants";
interface FolderSelectorParams {
onFolderSelect: (folder: string) => void;
}
+/**
+ * Component to select a folder and will use onFolderSelect param to provide
+ * selected folder to parent
+ * @param onFolderSelect callback to provide selected folder
+ */
const FolderSelector = ({ onFolderSelect }: FolderSelectorParams) => {
const [displayFolderPicker, setDisplayFolderPicker] = useState(false);
const [errorMessage, setErrorMessage] = useState("");
+ /**
+ * Called upon selecting a folder. Will verify the folder exists and is writable
+ * @param folderPath the path of the folder to verify
+ */
const verifyFolder = (folderPath: string) => {
window.bashUtils.doesDirectoryExist(folderPath)
.then((exists) => {
@@ -30,6 +40,9 @@ const FolderSelector = ({ onFolderSelect }: FolderSelectorParams) => {
});
};
+ /**
+ * Will open a dialog to allow the user to select a folder
+ */
const chooseFolder = () => {
const options: OpenDialogOptions = {
properties: ['openDirectory']
diff --git a/src/react/components/Loader.tsx b/src/react/components/Loader.tsx
index 160f0e3..d75c1f0 100644
--- a/src/react/components/Loader.tsx
+++ b/src/react/components/Loader.tsx
@@ -2,14 +2,16 @@ interface LoaderParams {
message: string;
}
-const Loader = ({ message }: LoaderParams) => {
- return (
-
-
{message}
+/**
+ * Simple loading spinner component with dynamic message
+ * @param message message to display to the user
+ */
+const Loader = ({ message }: LoaderParams) => (
+
+
{message}
-
-
- )
-};
+
+
+);
export default Loader;
diff --git a/src/react/components/OnlineDetector.tsx b/src/react/components/OnlineDetector.tsx
index c8a13aa..7f05c94 100644
--- a/src/react/components/OnlineDetector.tsx
+++ b/src/react/components/OnlineDetector.tsx
@@ -1,25 +1,14 @@
-import {
- Box,
- Button,
- Dialog,
- DialogActions,
- DialogContent,
- DialogTitle,
- Grid,
- Typography,
- styled,
-} from "@mui/material";
+import { Button, Typography } from "@mui/material";
import PermScanWifiIcon from "@mui/icons-material/PermScanWifi";
import React from "react";
+
import OnlineWarningModal from "../modals/OnlineWarningModal";
/**
* This will add an event listener to detect the users internet connectivity.
* If active, a pulsing warning icon with text will appear on the screen that
- * when clicked will show a dialog warning the user of the danger of internet
+ * when clicked will show a modal to warn the user of the danger of internet
* connectivity.
- *
- * @returns the warning and dialog component to render if necessary
*/
export const OnlineDetector = () => {
const [open, setOpen] = React.useState(false);
diff --git a/src/react/components/VerifyMnemonic.tsx b/src/react/components/VerifyMnemonic.tsx
index 4ae1316..4d4ac77 100644
--- a/src/react/components/VerifyMnemonic.tsx
+++ b/src/react/components/VerifyMnemonic.tsx
@@ -1,8 +1,10 @@
import { Grid, TextField } from "@mui/material";
-import { Dispatch, ReactNode, SetStateAction, useContext, useEffect, useMemo, useRef, useState } from "react";
-import { GlobalContext } from "../GlobalContext";
-import { Network } from "../types";
+import { Dispatch, SetStateAction, useContext, useRef, useState } from "react";
+
import { errors } from "../constants";
+import { GlobalContext } from "../GlobalContext";
+import { KeyCreationContext } from "../KeyCreationContext";
+import { Network } from "../types";
interface VerifyMnemonicParams {
hasError: boolean;
@@ -11,13 +13,22 @@ interface VerifyMnemonicParams {
onVerifyMnemonic: () => void;
}
+/**
+ * Creates a grid of inputs if on mainnet or a single textbox for ease of testing.
+ * The user fills the form with the mnemonic and the input will be provided to the
+ * onVerifyMnemonic for futher action
+ *
+ * mnemonicToVerify is provided as a param instead of existing only in this component
+ * so the parent can fill the value if the user goes back in navigation
+ */
const VerifyMnemonic = ({
hasError = false,
mnemonicToVerify,
setMnemonicToVerify,
onVerifyMnemonic,
}: VerifyMnemonicParams) => {
- const { mnemonic, network } = useContext(GlobalContext);
+ const { mnemonic } = useContext(KeyCreationContext);
+ const { network } = useContext(GlobalContext);
const [mnemonicToVerifyArray, setMnemonicToVerifyArray] = useState(
mnemonicToVerify ? mnemonicToVerify.split(' ') : Array(24).fill(''));
const inputRefs = useRef([]);
@@ -41,8 +52,11 @@ const VerifyMnemonic = ({
}
};
+ /**
+ * Will focus on a new input depending on the pressed key
+ * @param index the current input index
+ */
const handleKeysForWord = (index: number) => (e: React.KeyboardEvent) => {
- // Navigate phrase confirmation with spacebar or arrowkeys
let nextFocus = index;
const currentTextFieldValue = mnemonicToVerifyArray[index];
diff --git a/src/react/components/VersionFooter.tsx b/src/react/components/VersionFooter.tsx
index 8e14020..42fa49f 100644
--- a/src/react/components/VersionFooter.tsx
+++ b/src/react/components/VersionFooter.tsx
@@ -1,32 +1,15 @@
import { Typography } from "@mui/material";
-import styled from "styled-components";
declare var VERSION: string;
declare var COMMITHASH: string;
-const SoftText = styled(Typography)`
- color: gray;
- text-align: center;
- font-size: 10px;
-`;
-
-const Container = styled.div`
- position: fixed;
- bottom: 35;
- width: 100%;
-`;
-
/**
- * This component is a footer used to display the version and commit hash.
- *
- * @returns the footer component containing the version and commit hash
+ * Footer to display the version and commit hash
*/
-const VersionFooter = () => {
- return(
-
- Version: {VERSION} - Commit Hash: {COMMITHASH}
-
- )
-}
+const VersionFooter = () => (
+
+);
-export default VersionFooter;
\ No newline at end of file
+export default VersionFooter;
diff --git a/src/react/components/WizardWrapper.tsx b/src/react/components/WizardWrapper.tsx
index bd82519..5da2725 100644
--- a/src/react/components/WizardWrapper.tsx
+++ b/src/react/components/WizardWrapper.tsx
@@ -1,7 +1,8 @@
import { ReactNode, useContext } from "react";
-import { GlobalContext } from "../GlobalContext";
import { Step, StepLabel, Stepper, Typography } from "@mui/material";
+
import { stepLabels } from "../constants";
+import { GlobalContext } from "../GlobalContext";
import { StepKey } from "../types";
interface WizardWrapperParams {
@@ -12,6 +13,15 @@ interface WizardWrapperParams {
title: string;
}
+/**
+ * Wrapper of a page to display the network, title, stepper, and action bar buttons.
+ *
+ * @param actionBarItems A list of buttons to display
+ * @param activeTimelineIndex The index of the timelineItems array that is active
+ * @param children The inner content of the page
+ * @param timelineItems A list of steps to display
+ * @param title The title to appear at the top of the page
+ */
const WizardWrapper = ({
actionBarItems,
activeTimelineIndex,
diff --git a/src/react/constants.ts b/src/react/constants.ts
index b747113..8248913 100644
--- a/src/react/constants.ts
+++ b/src/react/constants.ts
@@ -53,17 +53,19 @@ export const CreateMnemonicFlow = [StepKey.MnemonicGeneration, StepKey.KeyConfig
export const ExistingMnemonicFlow = [StepKey.MnemonicImport, StepKey.KeyConfiguration, StepKey.KeyGeneration, StepKey.Finish];
export const BTECFlow = [StepKey.MnemonicImport, StepKey.BTECConfiguration, StepKey.BTECGeneration, StepKey.FinishBTEC];
-export const CreatePath = "/create";
-export const ConfigureCreatePath = "/configure-create";
-export const CreateKeysCreatePath = "/create-keys";
-export const FinishCreatePath ="/finish-create";
+export const paths = {
+ CREATE_MNEMONIC: "/create",
+ CONFIGURE_CREATE: "/configure-create",
+ CREATE_KEYS_CREATE: "/create-keys",
+ FINISH_CREATE:"/finish-create",
-export const ExistingImportPath = "/import-existing";
-export const ConfigureExistingPath = "/configure-existing";
-export const CreateKeysExistingPath = "/create-existing-keys";
-export const FinishExistingPath = "/finish-existing";
+ EXISTING_IMPORT: "/import-existing",
+ CONFIGURE_EXISTING: "/configure-existing",
+ CREATE_KEYS_EXISTING: "/create-existing-keys",
+ FINISH_EXISTING: "/finish-existing",
-export const BTECImportPath = "/import-btec";
-export const ConfigureBTECPath = "/configure-btec";
-export const CreateCredentialsPath = "/create-btec";
-export const FinishCredentialsPath = "/finish-btec";
+ BTEC_IMPORT: "/import-btec",
+ CONFIGURE_BTEC: "/configure-btec",
+ CREATE_CREDENTIALS: "/create-btec",
+ FINISH_CREDENTIALS: "/finish-btec",
+};
diff --git a/src/react/globalContext.tsx b/src/react/globalContext.tsx
index 1d79ad7..942345e 100644
--- a/src/react/globalContext.tsx
+++ b/src/react/globalContext.tsx
@@ -1,26 +1,25 @@
import { Dispatch, SetStateAction, createContext, useState } from "react";
+
import { Network } from "./types";
interface GlobalContextType {
- mnemonic: string;
- setMnemonic: Dispatch>;
network: Network;
setNetwork: Dispatch>;
}
export const GlobalContext = createContext({
- mnemonic: "",
- setMnemonic: () => {},
network: Network.MAINNET,
setNetwork: () => {},
});
+/**
+ * Global context for the network which is used across the application
+ */
const GlobalContextWrapper = ({ children }: { children: React.ReactNode}) => {
- const [mnemonic, setMnemonic] = useState("");
const [network, setNetwork] = useState(Network.MAINNET);
return (
-
+
{children}
);
diff --git a/src/react/helpers.ts b/src/react/helpers.ts
index 2c417d0..9e9640a 100644
--- a/src/react/helpers.ts
+++ b/src/react/helpers.ts
@@ -1,3 +1,8 @@
+/**
+ * Helper function to clean the mnemonic of invalid characters and extraneous spacing
+ * @param mnemonic the mnemonic to clean
+ * @returns the cleaned mnemonic
+ */
export const cleanMnemonic = (mnemonic: String): string => {
const punctuationRemoved = mnemonic.replace(/[.,\/#!$%\^&\*;:{}=\-_`~()]/g, " ");
const singleSpace = punctuationRemoved.replace(/\s\s+/g, " ");
diff --git a/src/react/index.tsx b/src/react/index.tsx
index 1a3e066..33bfa16 100644
--- a/src/react/index.tsx
+++ b/src/react/index.tsx
@@ -1,6 +1,7 @@
import * as ReactDOM from "react-dom";
-import App from "./App";
import 'typeface-roboto';
+
+import App from "./App";
import './index.css';
// We find our app DOM element as before
diff --git a/src/react/modals/NetworkPickerModal.tsx b/src/react/modals/NetworkPickerModal.tsx
index 6e5e107..131156a 100644
--- a/src/react/modals/NetworkPickerModal.tsx
+++ b/src/react/modals/NetworkPickerModal.tsx
@@ -7,9 +7,10 @@ import {
RadioGroup,
Typography,
} from "@mui/material";
-import { Network } from "../types";
import { useContext, useEffect, useState } from "react";
+
import { GlobalContext } from "../GlobalContext";
+import { Network } from "../types";
import WagyuModal from "./WagyuModal";
interface NetworkPickerModalParams {
@@ -17,6 +18,9 @@ interface NetworkPickerModalParams {
showModal: boolean;
}
+/**
+ * Modal to allow the user to pick the Ethereum Network
+ */
const NetworkPickerModal = ({onClose, showModal}: NetworkPickerModalParams) => {
const { network, setNetwork } = useContext(GlobalContext);
const [formNetwork, setFormNetwork] = useState(Network.MAINNET);
diff --git a/src/react/modals/OnlineWarningModal.tsx b/src/react/modals/OnlineWarningModal.tsx
index 1140341..d37bd96 100644
--- a/src/react/modals/OnlineWarningModal.tsx
+++ b/src/react/modals/OnlineWarningModal.tsx
@@ -1,4 +1,5 @@
import { Button, Typography } from "@mui/material";
+
import WagyuModal from "./WagyuModal";
interface OlineWarningModalParams {
@@ -7,49 +8,50 @@ interface OlineWarningModalParams {
open: boolean;
}
-const OnlineWarningModal = ({ onClose, onHideWarning, open }: OlineWarningModalParams) => {
- return (
-
-
-
Internet Connection Detected
-
-
- Being connected to the internet while using this tool drastically increases the risk of exposing your Secret Recovery Phrase.
-
-
- You can avoid this risk by having a live OS such as Tails installed on a USB drive and run on a computer with network capabilities disabled.
-
+/**
+ * Modal to display to the user to explain the risks of using this tool with network connectivity
+ */
+const OnlineWarningModal = ({ onClose, onHideWarning, open }: OlineWarningModalParams) => (
+
+
+
Internet Connection Detected
+
+
+ Being connected to the internet while using this tool drastically increases the risk of exposing your Secret Recovery Phrase.
+
+
+ You can avoid this risk by having a live OS such as Tails installed on a USB drive and run on a computer with network capabilities disabled.
+
-
- You can visit https://tails.net/install/ for instructions on how to download, install, and run Tails on a USB device.
-
-
- If you have any questions you can get help at https://dsc.gg/ethstaker
-
-
-
-
-
-
+
+ You can visit https://tails.net/install/ for instructions on how to download, install, and run Tails on a USB device.
+
+
+ If you have any questions you can get help at https://dsc.gg/ethstaker
+
How would you like to use your existing secret recovery phrase?
+/**
+ * Modal for the user to pick which action they would like to take when reusing a mnemonic
+ *
+ * Options are: Generate existing keys or Generate BLS change
+ */
+const ReuseMnemonicActionModal = ({ onClose, onSubmit, showModal}: ReuseMnemonicActionModalParams) => (
+
+
+
How would you like to use your existing secret recovery phrase?