From 7620032bc3508dee73880b796c547df0a2b9275e Mon Sep 17 00:00:00 2001 From: Florian Date: Sun, 24 Nov 2024 17:53:00 +0100 Subject: [PATCH] Support secp256k1 for numbers (#395) * Support secp256k1 for numbers * Extract SecretNumberInput from ImportSecretNumbers page * Rename test for ImportSecretNumbers * Fix styling issues * Make use of SecretNumberInput within SecretNumbers page * Extract number count --- .../SecretNumberInput/SecretNumberInput.tsx | 36 +++ .../atoms/SecretNumberInput/index.ts | 1 + .../SecretNumberInput}/styles.module.css | 0 .../extension/src/components/atoms/index.ts | 1 + ....test.tsx => ImportSecretNumbers.test.tsx} | 0 .../ImportSecretNumbers.tsx | 205 +++++------------- .../ImportSecretNumbers.tsx | 9 +- .../SecretNumbers/SecretNumbers.tsx | 194 ++++------------- .../SecretNumbers/styles.module.css | 5 - .../contexts/WalletContext/WalletContext.tsx | 20 +- 10 files changed, 154 insertions(+), 317 deletions(-) create mode 100644 packages/extension/src/components/atoms/SecretNumberInput/SecretNumberInput.tsx create mode 100644 packages/extension/src/components/atoms/SecretNumberInput/index.ts rename packages/extension/src/components/{pages/AddNewWallet/ImportNewWallet/ImportSecretNumbers => atoms/SecretNumberInput}/styles.module.css (100%) rename packages/extension/src/components/pages/AddNewWallet/ImportNewWallet/ImportSecretNumbers/{SecretNumbers.test.tsx => ImportSecretNumbers.test.tsx} (100%) delete mode 100644 packages/extension/src/components/pages/ImportSecretNumbers/SecretNumbers/styles.module.css diff --git a/packages/extension/src/components/atoms/SecretNumberInput/SecretNumberInput.tsx b/packages/extension/src/components/atoms/SecretNumberInput/SecretNumberInput.tsx new file mode 100644 index 000000000..94d5f4f47 --- /dev/null +++ b/packages/extension/src/components/atoms/SecretNumberInput/SecretNumberInput.tsx @@ -0,0 +1,36 @@ +import { Grid, TextField } from '@mui/material'; +import { FC, FocusEvent } from 'react'; +import styles from './styles.module.css'; +import { useNetwork } from '../../../contexts'; + +export interface SecretNumberInputProps { + id: string; + label: string; + error: string; + onBlur: (e: FocusEvent) => void; +} + +export const SecretNumberInput: FC = ({ id, label, error, onBlur }) => { + const { hasOfflineBanner } = useNetwork(); + + return ( + + + + ); +}; diff --git a/packages/extension/src/components/atoms/SecretNumberInput/index.ts b/packages/extension/src/components/atoms/SecretNumberInput/index.ts new file mode 100644 index 000000000..c1ce97c64 --- /dev/null +++ b/packages/extension/src/components/atoms/SecretNumberInput/index.ts @@ -0,0 +1 @@ +export * from './SecretNumberInput'; diff --git a/packages/extension/src/components/pages/AddNewWallet/ImportNewWallet/ImportSecretNumbers/styles.module.css b/packages/extension/src/components/atoms/SecretNumberInput/styles.module.css similarity index 100% rename from packages/extension/src/components/pages/AddNewWallet/ImportNewWallet/ImportSecretNumbers/styles.module.css rename to packages/extension/src/components/atoms/SecretNumberInput/styles.module.css diff --git a/packages/extension/src/components/atoms/index.ts b/packages/extension/src/components/atoms/index.ts index 6e15d70dd..446d7432b 100644 --- a/packages/extension/src/components/atoms/index.ts +++ b/packages/extension/src/components/atoms/index.ts @@ -4,6 +4,7 @@ export * from './Logo'; export * from './NFTImage'; export * from './NumericInput'; export * from './PrivateRoute'; +export * from './SecretNumberInput'; export * from './TileLoader'; export * from './TokenLoader'; export * from './Tokens'; diff --git a/packages/extension/src/components/pages/AddNewWallet/ImportNewWallet/ImportSecretNumbers/SecretNumbers.test.tsx b/packages/extension/src/components/pages/AddNewWallet/ImportNewWallet/ImportSecretNumbers/ImportSecretNumbers.test.tsx similarity index 100% rename from packages/extension/src/components/pages/AddNewWallet/ImportNewWallet/ImportSecretNumbers/SecretNumbers.test.tsx rename to packages/extension/src/components/pages/AddNewWallet/ImportNewWallet/ImportSecretNumbers/ImportSecretNumbers.test.tsx diff --git a/packages/extension/src/components/pages/AddNewWallet/ImportNewWallet/ImportSecretNumbers/ImportSecretNumbers.tsx b/packages/extension/src/components/pages/AddNewWallet/ImportNewWallet/ImportSecretNumbers/ImportSecretNumbers.tsx index 0075637c5..b5e2db590 100644 --- a/packages/extension/src/components/pages/AddNewWallet/ImportNewWallet/ImportSecretNumbers/ImportSecretNumbers.tsx +++ b/packages/extension/src/components/pages/AddNewWallet/ImportNewWallet/ImportSecretNumbers/ImportSecretNumbers.tsx @@ -1,14 +1,14 @@ import { FC, useCallback, useState, FocusEvent } from 'react'; -import { Grid, TextField, Typography } from '@mui/material'; +import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined'; +import { Checkbox, FormControlLabel, Grid, Tooltip, Typography } from '@mui/material'; import { useNavigate } from 'react-router-dom'; -import { ERROR_RED, LIST_WALLETS_PATH } from '../../../../../constants'; -import { useWallet } from '../../../../../contexts'; +import { ERROR_RED, LIST_WALLETS_PATH, SECONDARY_GRAY } from '../../../../../constants'; +import { useNetwork, useWallet } from '../../../../../contexts'; import { PageWithStepper } from '../../../../templates'; -import styles from './styles.module.css'; - -const schemaInput = new RegExp(/^[0-9]{6}$/); +import { ECDSA } from 'xrpl'; +import { SecretNumberInput } from '../../../../atoms'; type InputErrors = { numbersA: string; @@ -22,6 +22,8 @@ type InputErrors = { }; const DIGIT_ERROR = 'You need 6 digits'; +const NUMBERS_COUNT = 8; +const schemaInput = new RegExp(/^[0-9]{6}$/); export interface ImportSecretNumbersProps { activeStep: number; @@ -36,6 +38,8 @@ export const ImportSecretNumbers: FC = ({ }) => { const navigate = useNavigate(); const { importNumbers } = useWallet(); + const { hasOfflineBanner } = useNetwork(); + const [isSecp256k1, setSecp256k1] = useState(false); const [numbersError, setNumbersError] = useState(''); const [inputErrors, setInputErrors] = useState({ numbersA: '', @@ -71,7 +75,11 @@ export const ImportSecretNumbers: FC = ({ ); if (numbers.find((number) => !schemaInput.test(number)) === undefined) { - const isValidNumbers = importNumbers(password, numbers); + const isValidNumbers = importNumbers({ + password, + numbers, + algorithm: isSecp256k1 ? ECDSA.secp256k1 : undefined + }); if (isValidNumbers) { navigate(LIST_WALLETS_PATH); } else if (isValidNumbers === false) { @@ -80,7 +88,7 @@ export const ImportSecretNumbers: FC = ({ setNumbersError('This wallet is already imported'); } } - }, [importNumbers, inputErrors, navigate, password]); + }, [importNumbers, inputErrors, isSecp256k1, navigate, password]); return ( = ({ Please enter your secret numbers in order to load your wallet to GemWallet. - - - - - - - - - - - - - - - - - - - - - - - - + {Array.from({ length: NUMBERS_COUNT }, (_, i) => { + const id = `numbers${String.fromCharCode(65 + i)}` as keyof InputErrors; + return ( + + ); + })} {numbersError} + setSecp256k1(!isSecp256k1)} + name="setSecp256k1" + color="primary" + style={{ transform: 'scale(0.9)' }} + /> + } + label={ + + Use "secp256k1" algorithm{' '} + + + + + } + /> ); }; diff --git a/packages/extension/src/components/pages/ImportSecretNumbers/ImportSecretNumbers.tsx b/packages/extension/src/components/pages/ImportSecretNumbers/ImportSecretNumbers.tsx index 4c2594df8..a91cabb41 100644 --- a/packages/extension/src/components/pages/ImportSecretNumbers/ImportSecretNumbers.tsx +++ b/packages/extension/src/components/pages/ImportSecretNumbers/ImportSecretNumbers.tsx @@ -1,6 +1,6 @@ import { FC, useCallback, useState } from 'react'; -import { Wallet } from 'xrpl'; +import { ECDSA, Wallet } from 'xrpl'; import { numbersToSeed, WalletToSave } from '../../../utils'; import { Congratulations } from '../Congratulations'; @@ -17,12 +17,13 @@ export const ImportSecretNumbers: FC = () => { setActiveStep((prevActiveStep) => prevActiveStep - 1); }, []); - const handleSecretNumbers = useCallback((numbers: string[]) => { + const handleSecretNumbers = useCallback((numbers: string[], algorithm: ECDSA | undefined) => { const seed = numbersToSeed(numbers); - const wallet = Wallet.fromSeed(seed); + const wallet = Wallet.fromSeed(seed, { algorithm }); setWallet({ publicAddress: wallet.address, - seed + seed, + algorithm }); setActiveStep(1); }, []); diff --git a/packages/extension/src/components/pages/ImportSecretNumbers/SecretNumbers/SecretNumbers.tsx b/packages/extension/src/components/pages/ImportSecretNumbers/SecretNumbers/SecretNumbers.tsx index c3febbf6c..0dfe15d11 100644 --- a/packages/extension/src/components/pages/ImportSecretNumbers/SecretNumbers/SecretNumbers.tsx +++ b/packages/extension/src/components/pages/ImportSecretNumbers/SecretNumbers/SecretNumbers.tsx @@ -1,13 +1,13 @@ import { FC, useCallback, useState, FocusEvent } from 'react'; -import { Grid, TextField, Typography } from '@mui/material'; +import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined'; +import { Checkbox, FormControlLabel, Grid, Tooltip, Typography } from '@mui/material'; -import { ERROR_RED } from '../../../../constants'; +import { ERROR_RED, SECONDARY_GRAY } from '../../../../constants'; import { useNetwork, useWallet } from '../../../../contexts'; import { PageWithStepper } from '../../../templates'; -import styles from './styles.module.css'; - -const schemaInput = new RegExp(/^[0-9]{6}$/); +import { ECDSA } from 'xrpl'; +import { SecretNumberInput } from '../../../atoms'; type InputErrors = { numbersA: string; @@ -21,17 +21,20 @@ type InputErrors = { }; const DIGIT_ERROR = 'You need 6 digits'; +const NUMBERS_COUNT = 8; +const schemaInput = new RegExp(/^[0-9]{6}$/); export interface SecretNumbersProps { activeStep: number; steps: number; onBack: () => void; - onNext: (seed: string[]) => void; + onNext: (seed: string[], algorithm: ECDSA | undefined) => void; } export const SecretNumbers: FC = ({ activeStep, steps, onBack, onNext }) => { const { isValidNumbers } = useWallet(); const { hasOfflineBanner } = useNetwork(); + const [isSecp256k1, setSecp256k1] = useState(false); const [numbersError, setNumbersError] = useState(''); const [inputErrors, setInputErrors] = useState({ numbersA: '', @@ -68,12 +71,12 @@ export const SecretNumbers: FC = ({ activeStep, steps, onBac if (numbers.find((number) => !schemaInput.test(number)) === undefined) { if (isValidNumbers(numbers)) { - onNext(numbers); + onNext(numbers, isSecp256k1 ? ECDSA.secp256k1 : undefined); } else { setNumbersError('Your secret numbers are incorrect.'); } } - }, [inputErrors, isValidNumbers, onNext]); + }, [inputErrors, isSecp256k1, isValidNumbers, onNext]); return ( = ({ activeStep, steps, onBac Please enter your secret numbers in order to load your wallet to GemWallet. - - - - - - - - - - - - - - - - - - - - - - - - + {Array.from({ length: NUMBERS_COUNT }, (_, i) => { + const id = `numbers${String.fromCharCode(65 + i)}` as keyof InputErrors; + return ( + + ); + })} {numbersError} + setSecp256k1(!isSecp256k1)} + name="setSecp256k1" + color="primary" + style={{ transform: 'scale(0.9)' }} + /> + } + label={ + + Use "secp256k1" algorithm{' '} + + + + + } + /> ); }; diff --git a/packages/extension/src/components/pages/ImportSecretNumbers/SecretNumbers/styles.module.css b/packages/extension/src/components/pages/ImportSecretNumbers/SecretNumbers/styles.module.css deleted file mode 100644 index 0372b2406..000000000 --- a/packages/extension/src/components/pages/ImportSecretNumbers/SecretNumbers/styles.module.css +++ /dev/null @@ -1,5 +0,0 @@ -.textField p { - margin-left: 5px; - margin-right: 0px; - margin-top: 0px; -} diff --git a/packages/extension/src/contexts/WalletContext/WalletContext.tsx b/packages/extension/src/contexts/WalletContext/WalletContext.tsx index 1e25867f8..eae45eab5 100644 --- a/packages/extension/src/contexts/WalletContext/WalletContext.tsx +++ b/packages/extension/src/contexts/WalletContext/WalletContext.tsx @@ -33,6 +33,15 @@ type ImportSeedProps = { walletName?: string; algorithm?: ECDSA; }; + +type ImportNumbersProps = { + password: string; + numbers: string[]; + walletName?: string; + // Default algorithm is: ed25519 + algorithm?: ECDSA; +}; + export interface WalletContextType { signIn: (password: string, rememberSession?: boolean) => boolean; signOut: () => void; @@ -44,7 +53,12 @@ export interface WalletContextType { importMnemonic: (password: string, mnemonic: string, walletName?: string) => boolean | undefined; isValidNumbers: (numbers: string[]) => boolean; isPasswordCorrect: (password: string) => boolean; - importNumbers: (password: string, numbers: string[], walletName?: string) => boolean | undefined; + importNumbers: ({ + password, + numbers, + walletName, + algorithm + }: ImportNumbersProps) => boolean | undefined; getCurrentWallet: () => WalletLedger | undefined; getWalletByPublicAddress: (publicAddress: string) => WalletLedger | undefined; renameWallet: (name: string, publicAddress: string) => void; @@ -257,10 +271,10 @@ const WalletProvider: FC = ({ children }) => { ); const importNumbers = useCallback( - (password: string, numbers: string[], walletName?: string) => { + ({ password, numbers, walletName, algorithm }: ImportNumbersProps) => { try { const seed = numbersToSeed(numbers); - const wallet = Wallet.fromSeed(seed); + const wallet = Wallet.fromSeed(seed, { algorithm }); if (wallets.filter((w) => w.publicAddress === wallet.address).length > 0) { return undefined; }