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

feat: Update Keychain for new modified-zip32 #1624

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
11 changes: 10 additions & 1 deletion apps/extension/src/App/Accounts/ParentAccounts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,23 @@ export const ParentAccounts = (): JSX.Element => {
(account) => account.parentId || account.type === AccountType.Ledger
)
.map((account) => {
const outdated =
let outdated =
account.type !== AccountType.Ledger &&
typeof account.pseudoExtendedKey === "undefined";

// The only account without a parent is the ledger account
const parent =
parentAccounts.find((pa) => pa.id === account.parentId) || account;

if (parent?.type === AccountType.Mnemonic) {
if (
account.type === AccountType.ShieldedKeys &&
!account.modifiedZip32Path
) {
outdated = true;
}
}

return { ...parent, outdated };
});

Expand Down
20 changes: 14 additions & 6 deletions apps/extension/src/Setup/Common/AdvancedOptionsMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import { Alert, LinkButton, Stack } from "@namada/components";
import { Bip44Path } from "@namada/types";
import { Bip44Path, Zip32Path } from "@namada/types";
import clsx from "clsx";
import React, { useState } from "react";
import { FaChevronLeft, FaChevronRight } from "react-icons/fa";
import Bip39PassphraseForm from "./Bip39PassphraseForm";
import Bip44Form from "./Bip44Form";
import Zip32Form from "./Zip32Form";

type Props = {
passphrase: string;
path: Bip44Path;
bip44Path: Bip44Path;
zip32Path: Zip32Path;
setPassphrase: (passphrase: string) => void;
setPath: (path: Bip44Path) => void;
setBip44Path: (path: Bip44Path) => void;
setZip32Path: (path: Zip32Path) => void;
};

export enum Option {
Expand All @@ -21,9 +24,11 @@ export enum Option {

export const AdvancedOptionsMenu: React.FC<Props> = ({
passphrase,
path,
bip44Path,
zip32Path,
setPassphrase,
setPath,
setBip44Path,
setZip32Path,
}) => {
const [option, setOption] = useState(Option.Menu);
const menuText: Record<Option, string> = {
Expand Down Expand Up @@ -84,7 +89,10 @@ export const AdvancedOptionsMenu: React.FC<Props> = ({
</div>
</div>
{option === Option.Path && (
<Bip44Form path={path} setPath={setPath} />
<>
<Bip44Form path={bip44Path} setPath={setBip44Path} />
<Zip32Form path={zip32Path} setPath={setZip32Path} />
</>
)}
{option === Option.Passphrase && (
<Bip39PassphraseForm
Expand Down
86 changes: 86 additions & 0 deletions apps/extension/src/Setup/Common/Zip32Form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import React from "react";

import { chains } from "@namada/chains";
import { Alert, Input, LinkButton, Stack } from "@namada/components";
import { Zip32Path } from "@namada/types";

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

const Zip32Form: React.FC<Props> = ({ path, setPath }) => {
const { coinType } = chains.namada.bip44;

const handleNumericChange = (
e: React.ChangeEvent<HTMLInputElement>
): void => {
const result = e.target.value.replace(/\D/g, "") || "0";
setPath({
account: parseInt(result),
});
};

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

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

const handleReset = (): void => {
setPath({ account: 0 });
};

return (
<div className="flex flex-col w-full">
<div className="my-3">
<Alert
type="info"
title="Please note"
className="[&_strong]:normal-case"
>
<Stack gap={6}>
<ul className="text-white list-disc mx-6 [&_button]:text-white">
<li>
You can create multiple addresses from one recovery phrase
</li>
<li>A lost path cannot be recovered</li>
<li>
If you&apos;re unfamiliar with this feature, skip or undo this
step -{" "}
<LinkButton
className="underline bold"
role="button"
onClick={(e) => {
e.preventDefault();
handleReset();
}}
>
Reset
</LinkButton>
</li>
</ul>
<label className="text-base font-medium text-neutral-300">
ZIP32 Path
<div className="flex w-full justify-start items-center pt-2">
<span className="h-px px-1 text-xs text-neutral-300">
{parentDerivationPath}
</span>
<Input
type="number"
className="w-60"
min="0"
value={path.account}
onChange={handleNumericChange}
onFocus={handleFocus}
/>
<i>&apos;</i>
</div>
</label>
</Stack>
</Alert>
</div>
</div>
);
};

export default Zip32Form;
20 changes: 13 additions & 7 deletions apps/extension/src/Setup/ImportAccount/SeedPhraseImport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
RadioGroup,
Stack,
} from "@namada/components";
import { Bip44Path } from "@namada/types";
import { Bip44Path, Zip32Path } from "@namada/types";
import { assertNever } from "@namada/utils";
import { AccountSecret, ValidateMnemonicMsg } from "background/keyring";
import { useRequester } from "hooks/useRequester";
Expand All @@ -19,8 +19,10 @@ import { fillArray, filterPrivateKeyPrefix, validatePrivateKey } from "utils";

type Props = {
onConfirm: (accountSecret: AccountSecret) => void;
path: Bip44Path;
setPath: (path: Bip44Path) => void;
bip44Path: Bip44Path;
zip32Path: Zip32Path;
setBip44Path: (path: Bip44Path) => void;
setZip32Path: (path: Zip32Path) => void;
};

const SHORT_PHRASE_COUNT = 12;
Expand All @@ -34,8 +36,10 @@ enum SecretType {

export const SeedPhraseImport: React.FC<Props> = ({
onConfirm,
path,
setPath,
bip44Path,
zip32Path,
setBip44Path,
setZip32Path,
}) => {
const requester = useRequester();
const [privateKey, setPrivateKey] = useState("");
Expand Down Expand Up @@ -241,8 +245,10 @@ export const SeedPhraseImport: React.FC<Props> = ({
{mnemonicType !== SecretType.PrivateKey && (
<AdvancedOptions>
<AdvancedOptionsMenu
path={path}
setPath={setPath}
bip44Path={bip44Path}
zip32Path={zip32Path}
setBip44Path={setBip44Path}
setZip32Path={setZip32Path}
passphrase={bip39Passphrase}
setPassphrase={setBip39Passphrase}
/>
Expand Down
22 changes: 16 additions & 6 deletions apps/extension/src/Setup/Ledger/LedgerConnect.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { chains } from "@namada/chains";
import { ActionButton, Alert, Image, Stack } from "@namada/components";
import { Ledger as LedgerApp, makeBip44Path } from "@namada/sdk/web";
import { Bip44Path } from "@namada/types";
import { Bip44Path, Zip32Path } from "@namada/types";
import { LedgerError } from "@zondax/ledger-namada";
import { LedgerStep } from "Setup/Common";
import { AdvancedOptions } from "Setup/Common/AdvancedOptions";
Expand All @@ -11,11 +11,18 @@ import React, { useState } from "react";
import { useNavigate } from "react-router-dom";

type Props = {
path: Bip44Path;
setPath: (path: Bip44Path) => void;
bip44Path: Bip44Path;
zip32Path: Zip32Path;
setBip44Path: (path: Bip44Path) => void;
setZip32Path: (path: Zip32Path) => void;
};

export const LedgerConnect: React.FC<Props> = ({ path, setPath }) => {
export const LedgerConnect: React.FC<Props> = ({
bip44Path,
zip32Path: _zip32Path, // TODO
setBip44Path,
setZip32Path: _setZip32Path, // TODO
}) => {
const navigate = useNavigate();
const [error, setError] = useState<string>();
const [isLedgerConnecting, setIsLedgerConnecting] = useState(false);
Expand All @@ -34,8 +41,9 @@ export const LedgerConnect: React.FC<Props> = ({ path, setPath }) => {

setIsLedgerConnecting(true);
const { address, publicKey } = await ledger.showAddressAndPublicKey(
makeBip44Path(chains.namada.bip44.coinType, path)
makeBip44Path(chains.namada.bip44.coinType, bip44Path)
);
// TODO: Shielded import will be enabled in PR: https://github.com/anoma/namada-interface/pull/1575
setIsLedgerConnecting(false);
navigate(routes.ledgerImport(), {
state: {
Expand Down Expand Up @@ -99,7 +107,9 @@ export const LedgerConnect: React.FC<Props> = ({ path, setPath }) => {
)}

<AdvancedOptions>
<Bip44Form path={path} setPath={setPath} />
<Bip44Form path={bip44Path} setPath={setBip44Path} />
{/* TODO: Enable in https://github.com/anoma/namada-interface/pull/1575 */}
{/*<Zip32Form path={zip32Path} setPath={setZip32Path} />*/}
</AdvancedOptions>

<LedgerStep
Expand Down
12 changes: 8 additions & 4 deletions apps/extension/src/Setup/Ledger/LedgerImport.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ActionButton, Alert, Loading, Stack } from "@namada/components";
import { Bip44Path } from "@namada/types";
import { Bip44Path, Zip32Path } from "@namada/types";
import { AccountAlias, Password } from "Setup/Common";
import { AccountManager } from "Setup/query";
import routes from "Setup/routes";
Expand All @@ -14,11 +14,13 @@ type LedgerImportLocationState = {

type LedgerProps = {
passwordRequired: boolean;
path: Bip44Path;
bip44Path: Bip44Path;
zip32Path: Zip32Path;
};

export const LedgerImport = ({
path,
bip44Path,
zip32Path,
passwordRequired,
}: LedgerProps): JSX.Element => {
const location = useLocation();
Expand Down Expand Up @@ -60,7 +62,9 @@ export const LedgerImport = ({
alias,
address,
publicKey,
path,
bip44Path,
zip32Path,
// TODO: Enable shielded
});

navigate(routes.ledgerComplete(), {
Expand Down
35 changes: 25 additions & 10 deletions apps/extension/src/Setup/Setup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
Container,
LifecycleExecutionWrapper as Wrapper,
} from "@namada/components";
import { Bip44Path, DerivedAccount } from "@namada/types";
import { Bip44Path, DerivedAccount, Zip32Path } from "@namada/types";
import { assertNever } from "@namada/utils";
import { AccountSecret, AccountStore } from "background/keyring";
import { AnimatePresence, motion } from "framer-motion";
Expand Down Expand Up @@ -74,11 +74,14 @@ export const Setup: React.FC = () => {
const [currentStep, setCurrentStep] = useState(0);
const [totalSteps, setTotalSteps] = useState(0);
const [currentPageTitle, setCurrentPageTitle] = useState("");
const [path, setPath] = useState<Bip44Path>({
const [bip44Path, setBip44Path] = useState<Bip44Path>({
account: 0,
change: 0,
index: 0,
});
const [zip32Path, setZip32Path] = useState<Zip32Path>({
account: 0,
});

const [parentAccountStore, setParentAccountStore] = useState<AccountStore>();
const [shieldedAccount, setShieldedAccount] = useState<DerivedAccount>();
Expand Down Expand Up @@ -251,7 +254,8 @@ export const Setup: React.FC = () => {
if (accountSecret) {
void storeAccount({
...accountCreationDetails,
path,
bip44Path,
zip32Path,
accountSecret,
flow: "create",
});
Expand All @@ -273,7 +277,8 @@ export const Setup: React.FC = () => {
alias={accountCreationDetails.alias || ""}
accountSecret={selectedAccountSecret}
password={accountCreationDetails.password || ""}
path={path}
path={bip44Path}
// TODO: Display custom zip32 path across apps!
parentAccountStore={parentAccountStore}
shieldedAccount={shieldedAccount}
status={completionStatus}
Expand All @@ -298,8 +303,10 @@ export const Setup: React.FC = () => {
element={
<Wrapper onLoad={setCurrentPage("Import Existing Keys", 1)}>
<SeedPhraseImport
path={path}
setPath={setPath}
bip44Path={bip44Path}
zip32Path={zip32Path}
setBip44Path={setBip44Path}
setZip32Path={setZip32Path}
onConfirm={(accountSecret: AccountSecret) => {
setAccountSecret(accountSecret);
navigate(routes.accountImportCreate());
Expand Down Expand Up @@ -328,7 +335,8 @@ export const Setup: React.FC = () => {
if (accountSecret) {
void storeAccount({
...accountCreationDetails,
path,
bip44Path,
zip32Path,
accountSecret,
flow: "import",
});
Expand All @@ -350,7 +358,8 @@ export const Setup: React.FC = () => {
alias={accountCreationDetails.alias || ""}
accountSecret={selectedAccountSecret}
password={accountCreationDetails.password || ""}
path={path}
path={bip44Path}
// TODO: Pass zip32 path!
parentAccountStore={parentAccountStore}
shieldedAccount={shieldedAccount}
status={completionStatus}
Expand All @@ -375,7 +384,12 @@ export const Setup: React.FC = () => {
<Wrapper
onLoad={setCurrentPage("Connect Your Ledger Hardware", 1)}
>
<LedgerConnect path={path} setPath={setPath} />
<LedgerConnect
bip44Path={bip44Path}
zip32Path={zip32Path}
setBip44Path={setBip44Path}
setZip32Path={setZip32Path}
/>
</Wrapper>
}
/>
Expand All @@ -389,7 +403,8 @@ export const Setup: React.FC = () => {
)}
>
<LedgerImport
path={path}
bip44Path={bip44Path}
zip32Path={zip32Path}
passwordRequired={!passwordInitialized}
/>
</Wrapper>
Expand Down
Loading
Loading