Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: feat/944 - Extension: Custom BIP44 derivation paths and Version Info #1155

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
423 changes: 0 additions & 423 deletions apps/extension/src/App/Accounts/AddAccount.tsx

This file was deleted.

1 change: 0 additions & 1 deletion apps/extension/src/App/Accounts/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
export { default as AddAccount } from "./AddAccount";
export * from "./DeleteAccount";
export * from "./RenameAccount";
export * from "./ViewAccount";
Expand Down
17 changes: 17 additions & 0 deletions apps/extension/src/App/Common/AppHeaderNavigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { useVaultContext } from "context";
import { FaDiscord, FaXTwitter } from "react-icons/fa6";
import { GoAlert, GoQuestion } from "react-icons/go";
import { useNavigate } from "react-router-dom";
import sdkPackage from "../../../../../packages/sdk/package.json";
import extensionPackage from "../../../package.json";

type AppHeaderNavigationProps = {
open: boolean;
Expand Down Expand Up @@ -97,6 +99,21 @@ export const AppHeaderNavigation = ({
Lock Wallet
</li>
</Stack>
<div className="relative h-full">
<Stack
as="ul"
gap={1}
direction="vertical"
className="absolute bottom-4"
>
<li>
Keychain Version: <strong>{extensionPackage.version}</strong>
</li>
<li>
SDK Version: <strong>{sdkPackage.version}</strong>
</li>
</Stack>
</div>
<footer className="flex items-center justify-between text-yellow-950 text-2xl">
<Stack
as="ul"
Expand Down
45 changes: 45 additions & 0 deletions apps/extension/src/Setup/Common/AdvancedOptions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Bip44Path } from "@namada/types";
import React, { useState } from "react";
import { AdvancedOptionsMenu } from "./AdvancedOptionsMenu";
import Bip39PassphraseForm from "./Bip39PassphraseForm";
import Bip44Form from "./Bip44Form";

type Props = {
path: Bip44Path;
passphrase: string;
setPath: (path: Bip44Path) => void;
setPassphrase: (passphrase: string) => void;
};

export enum Option {
Menu = "menu",
Passphrase = "passphrase",
Path = "path",
}

export const AdvancedOptions: React.FC<Props> = ({
path,
passphrase,
setPath,
setPassphrase,
}) => {
const [view, setView] = useState<Option>(Option.Menu);

return (
<div className="bg-black">
{view === Option.Menu && (
<AdvancedOptionsMenu
onPathClick={() => setView(Option.Path)}
onPassphraseClick={() => setView(Option.Passphrase)}
/>
)}
{view === Option.Path && <Bip44Form path={path} setPath={setPath} />}
{view === Option.Passphrase && (
<Bip39PassphraseForm
passphrase={passphrase}
setPassphrase={setPassphrase}
/>
)}
</div>
);
};
18 changes: 18 additions & 0 deletions apps/extension/src/Setup/Common/AdvancedOptionsMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Stack } from "@namada/components";
import React from "react";

type Props = {
onPathClick: () => void;
onPassphraseClick: () => void;
};
export const AdvancedOptionsMenu: React.FC<Props> = ({
onPathClick,
onPassphraseClick,
}) => {
return (
<Stack as="ul" gap={2} direction="vertical" className="[&_li]:text-white">
<li onClick={onPathClick}>Set Custom Derivation Path</li>
<li onClick={onPassphraseClick}>Import with BIP39 Passphrase</li>
</Stack>
);
};
42 changes: 42 additions & 0 deletions apps/extension/src/Setup/Common/Bip39PassphraseForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React from "react";

import { Alert, Input, Stack } from "@namada/components";

type Props = {
passphrase: string;
setPassphrase: (passphrase: string) => void;
};

const Bip39PassphraseForm: React.FC<Props> = ({
passphrase,
setPassphrase,
}) => {
return (
<div className="flex flex-col w-full px-3">
<div className="mb-2 [&_input]:w-[92%]">
<div className="my-3">
<Alert type={"warning"} title={"Please note"} className="mb-3">
<Stack gap={6}>
<p className="text-[13px] leading-[1.25] text-white">
This import option is only users who have created a Namada
account using the Namada protocol CLI v.0.17.0 or older, and
used a BIP39 passphrase. Do not input your Namada extension
password
</p>
<Input
data-testid="setup-import-keys-passphrase-input"
label="Enter your passphrase"
placeholder="Optional passphrase for your seed phrase."
hideIcon={true}
onChange={(e) => setPassphrase(e.target.value)}
value={passphrase}
/>
</Stack>
</Alert>
</div>
</div>
</div>
);
};

export default Bip39PassphraseForm;
81 changes: 81 additions & 0 deletions apps/extension/src/Setup/Common/Bip44Form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import React, { useEffect, useState } from "react";

import { chains } from "@namada/chains";
import { Input } from "@namada/components";
import { Bip44Path } from "@namada/types";

type Props = {
path: Bip44Path;
setPath: (path: Bip44Path) => void;
};

const Bip44Form: React.FC<Props> = ({ path, setPath }) => {
const [customPath, setCustomPath] = useState({
...path,
index: 1,
});
const { coinType } = chains.namada.bip44;

const handleNumericChange = (
e: React.ChangeEvent<HTMLInputElement>,
key: keyof Bip44Path
): void => {
const result = e.target.value.replace(/\D/g, "") || "0";
setCustomPath({
...customPath,
[key]: parseInt(result),
});
};

useEffect(() => {
setPath(customPath);
}, [customPath]);

const handleFocus = (e: React.ChangeEvent<HTMLInputElement>): void =>
e.target.select();

const parentDerivationPath = `m/44'/${coinType}'/`;

return (
<div className="flex flex-col w-full px-3">
<div className="mb-2 [&_input]:w-[92%]">
<div className="my-3">
<label className="text-base font-medium text-neutral-300">
<div className="flex w-full justify-start items-center">
<span className="h-px px-1 text-xs text-neutral-300">
{parentDerivationPath}
</span>
<Input
type="number"
min="0"
value={customPath.account}
onChange={(e) => handleNumericChange(e, "account")}
onFocus={handleFocus}
/>
<i>&apos;/</i>
<Input
type="number"
min="0"
max="1"
value={customPath.change}
onChange={(e) => handleNumericChange(e, "change")}
onFocus={handleFocus}
/>
<i>&apos;/</i>
<Input
type="number"
min="0"
value={customPath.index}
onChange={(e) => handleNumericChange(e, "index")}
onFocus={handleFocus}
/>
<i>&apos;</i>
</div>
</label>
</div>
</div>
</div>
);
};

export default Bip44Form;
2 changes: 2 additions & 0 deletions apps/extension/src/Setup/Common/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export * from "./AccountAlias";
export * from "./Bip39PassphraseForm";
export * from "./Bip44Form";
export * from "./Completion";
export * from "./ContainerHeader";
export * from "./LedgerStep";
Expand Down
78 changes: 24 additions & 54 deletions apps/extension/src/Setup/ImportAccount/SeedPhraseImport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ import {
RadioGroup,
Stack,
} from "@namada/components";
import { Bip44Path } from "@namada/types";
import { assertNever } from "@namada/utils";
import { SeedPhraseList } from "Setup/Common";
import { AccountSecret, ValidateMnemonicMsg } from "background/keyring";
import { useRequester } from "hooks/useRequester";
import { GoX } from "react-icons/go";
import { Ports } from "router";
import { SeedPhraseList } from "Setup/Common";
import { AdvancedOptions } from "Setup/Common/AdvancedOptions";
import { fillArray, filterPrivateKeyPrefix, validatePrivateKey } from "utils";

type Props = {
Expand All @@ -32,9 +33,15 @@ enum MnemonicTypes {
export const SeedPhraseImport: React.FC<Props> = ({ onConfirm }) => {
const requester = useRequester();
const [privateKey, setPrivateKey] = useState("");
const [showAdvanced, setShowAdvanced] = useState(false);
const [passphrase, setPassphrase] = useState("");
const [path, setPath] = useState<Bip44Path>({
account: 0,
change: 0,
index: 0,
});
const [bip39Passphrase, setBip39Passphrase] = useState("");
const [invalidWordIndex, setInvalidWordIndex] = useState<number>();
const [showPassphrase, setShowPassphrase] = useState(false);
const [mnemonicType, setMnemonicType] = useState<MnemonicTypes>(
MnemonicTypes.TwelveWords
);
Expand Down Expand Up @@ -68,7 +75,7 @@ export const SeedPhraseImport: React.FC<Props> = ({ onConfirm }) => {
const isSubmitButtonDisabled =
mnemonicType === MnemonicTypes.PrivateKey ?
privateKey === "" || privateKeyError !== ""
: mnemonics.slice(0, mnemonicType).some((mnemonic) => !mnemonic);
: mnemonics.slice(0, mnemonicType).some((mnemonic) => !mnemonic);

const onPaste = useCallback(
(index: number, e: React.ClipboardEvent<HTMLInputElement>) => {
Expand Down Expand Up @@ -146,31 +153,13 @@ export const SeedPhraseImport: React.FC<Props> = ({ onConfirm }) => {
setInvalidWordIndex(invalidWordIndex);
typeof invalidWordIndex === "number" ?
setMnemonicError(`Word #${invalidWordIndex + 1} is invalid!`)
: setMnemonicError(error);
: setMnemonicError(error);
} else {
setMnemonicError(error);
}
}
}
}, [mnemonics, mnemonicType, privateKey, passphrase, showPassphrase]);

const onPassphraseChange = useCallback(
(value: string) => {
setPassphrase(value);
},
[passphrase]
);

const onShowPassphraseChange = useCallback(
(e: React.MouseEvent) => {
e.preventDefault();
if (!showPassphrase) {
setPassphrase("");
}
setShowPassphrase(!showPassphrase);
},
[showPassphrase]
);
}, [mnemonics, mnemonicType, privateKey, passphrase, showAdvanced]);

return (
<>
Expand Down Expand Up @@ -215,7 +204,7 @@ export const SeedPhraseImport: React.FC<Props> = ({ onConfirm }) => {
]}
onChange={(value: string) => {
if (Number(value) === MnemonicTypes.PrivateKey) {
setShowPassphrase(false);
setShowAdvanced(false);
setPassphrase("");
}
setMnemonicType(Number(value));
Expand Down Expand Up @@ -246,41 +235,22 @@ export const SeedPhraseImport: React.FC<Props> = ({ onConfirm }) => {
)}
</Stack>
<Stack direction="vertical" gap={4}>
{showPassphrase && (
<div className="relative">
<div onClick={onShowPassphraseChange}>
<i className="block text-lg absolute top-5 right-4 text-white cursor-pointer">
<GoX />
</i>
</div>
<Alert type={"warning"} title={"Please note"} className="mb-3">
<Stack gap={6}>
<p className="text-[13px] leading-[1.25] text-white">
This import option is only users who have created a Namada
account using the Namada protocol CLI v.0.17.0 or older, and
used a BIP39 passphrase. Do not input your Namada extension
password
</p>
<Input
data-testid="setup-import-keys-passphrase-input"
label="Enter your passphrase"
placeholder="Optional passphrase for your seed phrase."
hideIcon={true}
onChange={(e) => onPassphraseChange(e.target.value)}
value={passphrase}
/>
</Stack>
</Alert>
</div>
{showAdvanced && (
<AdvancedOptions
path={path}
passphrase={bip39Passphrase}
setPath={setPath}
setPassphrase={setBip39Passphrase}
/>
)}
{!showPassphrase && mnemonicType !== MnemonicTypes.PrivateKey && (
{mnemonicType !== MnemonicTypes.PrivateKey && !showAdvanced && (
<LinkButton
data-testid="setup-import-keys-use-passphrase-button"
className="text-xs !text-neutral-400"
onClick={onShowPassphraseChange}
onClick={() => setShowAdvanced(true)}
type="button" // makes enter key ignore this and submit form
>
Import with BIP39 Passphrase
Advanced
</LinkButton>
)}
<ActionButton
Expand Down
2 changes: 2 additions & 0 deletions apps/namadillo/src/App/Settings/SettingsMain.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { routes } from "App/routes";
import { version as sdkVersion } from "../../../../../packages/sdk/package.json";
import { version } from "../../../package.json";
import { SettingsPanelMenuItem } from "./SettingsPanelMenuItem";

Expand All @@ -14,6 +15,7 @@ export const SettingsMain = (): JSX.Element => {
</ul>
<div className="text-xs">
<div>Namadillo Version: {version}</div>
<div>SDK Version: {sdkVersion}</div>
</div>
</div>
);
Expand Down
3 changes: 0 additions & 3 deletions packages/crypto/lib/src/crypto/zip32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ use masp_primitives::{
use wasm_bindgen::prelude::*;
use zeroize::{Zeroize, ZeroizeOnDrop};

#[wasm_bindgen]
pub struct ExtSpendingKey(pub(crate) ExtendedSpendingKey);

#[wasm_bindgen]
#[derive(Zeroize)]
pub struct DerivationResult {
Expand Down
Loading