Skip to content
Closed
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
83 changes: 83 additions & 0 deletions apps/web/src/api/mutations/change-password.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { CONFIG, getAccountFullQueryOptions, Keys } from "@ecency/sdk";
import { deriveHiveKeys, detectHiveKeyDerivation } from "@ecency/wallets";
import { AuthorityType, PrivateKey } from "@hiveio/dhive";
import { useMutation, UseMutationOptions, useQuery } from "@tanstack/react-query";
import * as R from "remeda";

interface Payload {
newKeys: Record<string, PrivateKey>;
currentPassword: string;
}

type ChangePasswordOptions = Pick<
UseMutationOptions<unknown, Error, Payload>,
"onSuccess" | "onError"
>;

export function useAccountChangePassword(username: string, options?: ChangePasswordOptions) {
const { data: accountData } = useQuery(getAccountFullQueryOptions(username));

return useMutation({
mutationKey: ["accounts", "revoke-key", accountData?.name],
mutationFn: async ({ currentPassword, newKeys }: Payload) => {
if (!accountData) {
throw new Error("[SDK][Update password] – cannot update keys for anon user");
}

const currentDerivation = await detectHiveKeyDerivation(username, currentPassword, "owner");

let currentKeys: Record<string, string> = {};
let currentKey: PrivateKey;

if (currentDerivation === "bip44") {
const keys = await deriveHiveKeys(currentPassword);
currentKeys = {
owner: keys.ownerPubkey,
active: keys.activePubkey,
posting: keys.postingPubkey,
memo: keys.memoPubkey
};
currentKey = PrivateKey.fromString(keys.owner);
} else if (currentDerivation === "master-password") {
currentKeys = {
owner: PrivateKey.fromLogin(username, currentPassword, "owner").createPublic().toString(),
active: PrivateKey.fromLogin(username, currentPassword, "active")
.createPublic()
.toString(),
posting: PrivateKey.fromLogin(username, currentPassword, "posting")
.createPublic()
.toString(),
memo: PrivateKey.fromLogin(username, currentPassword, "memo").createPublic().toString()
};
currentKey = PrivateKey.fromLogin(username, currentPassword, "owner");
} else {
currentKeys = {
owner: PrivateKey.fromString(currentPassword).createPublic().toString()
};
currentKey = PrivateKey.fromString(currentPassword);
}

const prepareAuth = (keyName: keyof Keys) => {
const auth: AuthorityType = R.clone(accountData[keyName]);

auth.key_auths.push([newKeys[keyName].createPublic().toString(), 1]);
auth.key_auths.filter(([key]) => currentKeys[keyName] === key);

return auth;
};
Comment on lines +60 to +67
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical bug: filter() return value is discarded, old keys are never removed.

Array.prototype.filter() returns a new array and does not mutate the original. The current code pushes the new key but never actually removes the old key since the filter result is thrown away.

Proposed fix
       const prepareAuth = (keyName: keyof Keys) => {
         const auth: AuthorityType = R.clone(accountData[keyName]);

         auth.key_auths.push([newKeys[keyName].createPublic().toString(), 1]);
-        auth.key_auths.filter(([key]) => currentKeys[keyName] === key);
+        auth.key_auths = auth.key_auths.filter(([key]) => key !== currentKeys[keyName]);

         return auth;
       };

Note: Also corrected the filter logic—you want to exclude the old key (!==), not keep only the old key (===).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const prepareAuth = (keyName: keyof Keys) => {
const auth: AuthorityType = R.clone(accountData[keyName]);
auth.key_auths.push([newKeys[keyName].createPublic().toString(), 1]);
auth.key_auths.filter(([key]) => currentKeys[keyName] === key);
return auth;
};
const prepareAuth = (keyName: keyof Keys) => {
const auth: AuthorityType = R.clone(accountData[keyName]);
auth.key_auths.push([newKeys[keyName].createPublic().toString(), 1]);
auth.key_auths = auth.key_auths.filter(([key]) => key !== currentKeys[keyName]);
return auth;
};
🤖 Prompt for AI Agents
In @apps/web/src/api/mutations/change-password.ts around lines 60 - 67, In
prepareAuth, the code pushes the new key into auth.key_auths but mistakenly
discards the result of filter so the old key remains; change the line that calls
filter to assign its result back to auth.key_auths and invert the predicate to
exclude the old key (use !== with currentKeys[keyName]) so the old public key is
removed and only the new one is kept along with any other non-matching entries.


return CONFIG.hiveClient.broadcast.updateAccount(
{
account: accountData.name,
json_metadata: accountData.json_metadata,
owner: prepareAuth("owner"),
active: prepareAuth("active"),
posting: prepareAuth("posting"),
memo_key: accountData.memo_key
},
currentKey
);
Comment on lines +69 to +79
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

memo_key is not updated with the new key.

The mutation updates owner, active, and posting authorities with new keys, but memo_key still uses accountData.memo_key. This leaves the memo key unchanged after a password change, which may not be the intended behavior.

Proposed fix if memo_key should also be updated
         return CONFIG.hiveClient.broadcast.updateAccount(
           {
             account: accountData.name,
             json_metadata: accountData.json_metadata,
             owner: prepareAuth("owner"),
             active: prepareAuth("active"),
             posting: prepareAuth("posting"),
-            memo_key: accountData.memo_key
+            memo_key: newKeys.memo_key.createPublic().toString()
           },
           currentKey
         );
🤖 Prompt for AI Agents
In @apps/web/src/api/mutations/change-password.ts around lines 69 - 79, The
updateAccount call is leaving memo_key as accountData.memo_key so the memo key
isn't rotated; compute the new memo public key from the new password/key (the
same place you derive owner/active/posting keys) and replace
accountData.memo_key with that new memo key in the arguments to
CONFIG.hiveClient.broadcast.updateAccount; look for the prepareAuth helper and
where new keys are derived (e.g., functions that produce owner/active/posting
keys) and use that same derivation to produce the new memo_key passed to
updateAccount along with owner/active/posting and currentKey.

},
...options
});
}
1 change: 1 addition & 0 deletions apps/web/src/api/mutations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ export * from "./subscribe-to-community";
export * from "./hs-login-refresh";
export * from "./create-community";
export * from "./analytics";
export * from "./change-password";
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { useAccountChangePassword } from "@/api/mutations";
import { useClientActiveUser } from "@/api/queries";
import { error, success } from "@/features/shared";
import { Button, KeyInput, KeyInputImperativeHandle } from "@/features/ui";
import { WalletSeedPhrase } from "@/features/wallet";
import { getAccountFullQueryOptions } from "@ecency/sdk";
import { useHiveKeysQuery } from "@ecency/wallets";
import { PrivateKey } from "@hiveio/dhive";
import { useQuery } from "@tanstack/react-query";
import i18next from "i18next";
import { useCallback, useRef, useState } from "react";

export function ManageKeyChangePassword() {
const activeUser = useClientActiveUser();

const keyInputRef = useRef<KeyInputImperativeHandle>(null);

const [showSeed, setShowSeed] = useState(false);
const [currentKey, setCurrentKey] = useState<PrivateKey>();
const [rawKey, setRawKey] = useState<string>();

const { data: accountOwnerPublicKeys } = useQuery({
...getAccountFullQueryOptions(activeUser?.username!),
select: (resp) => resp.owner.key_auths as [string, number][]
});
const { data: keys } = useHiveKeysQuery(activeUser?.username!);

const { mutateAsync: changePassword } = useAccountChangePassword(activeUser?.username!);
Comment on lines +22 to +28
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Non-null assertions on potentially undefined username could cause runtime errors.

If activeUser is undefined or username is missing, these queries will be called with undefined!, which could lead to unexpected behavior or API errors. Consider adding early returns or guards.

Suggested approach
+  if (!activeUser?.username) {
+    return null; // or a loading/login prompt
+  }
+
   const { data: accountOwnerPublicKeys } = useQuery({
-    ...getAccountFullQueryOptions(activeUser?.username!),
+    ...getAccountFullQueryOptions(activeUser.username),
     select: (resp) => resp.owner.key_auths as [string, number][]
   });
-  const { data: keys } = useHiveKeysQuery(activeUser?.username!);
+  const { data: keys } = useHiveKeysQuery(activeUser.username);

-  const { mutateAsync: changePassword } = useAccountChangePassword(activeUser?.username!);
+  const { mutateAsync: changePassword } = useAccountChangePassword(activeUser.username);
🤖 Prompt for AI Agents
In
@apps/web/src/app/(dynamicPages)/profile/[username]/permissions/_components/manage-keys-change-password.tsx
around lines 22 - 28, activeUser?.username is being non-null asserted when
calling useQuery(getAccountFullQueryOptions), useHiveKeysQuery and
useAccountChangePassword which can blow up if activeUser is undefined; instead
derive a safe username const (e.g. const username = activeUser?.username) and
keep hooks called unconditionally but pass an enabled flag or guarded query
options so they do nothing until username is truthy: update
getAccountFullQueryOptions call and useQuery to include enabled: !!username (or
return early from select if username missing), call useHiveKeysQuery(username)
with an options param or internal enabled check so it waits for username, and
ensure useAccountChangePassword is invoked in a way that defers mutate creation
until username exists (via hook options or by returning a no-op mutate) rather
than using the non-null assertion on activeUser?.username!.


const handleGenerateNewPassword = useCallback(async () => {
const { privateKey, raw } = await keyInputRef.current!.handleSign();
const publicKey = privateKey.createPublic().toString();

if (accountOwnerPublicKeys?.find(([key]) => key === publicKey)) {
setCurrentKey(privateKey);
setRawKey(raw);
setShowSeed(true);
} else {
error(i18next.t("permissions.change-password.invalid-key"));
}
}, [accountOwnerPublicKeys]);

const handleChangePassword = useCallback(async () => {
if (keys && currentKey) {
await changePassword({
newKeys: {
owner: PrivateKey.fromString(keys.owner),
active: PrivateKey.fromString(keys.active),
posting: PrivateKey.fromString(keys.posting),
memo_key: PrivateKey.fromString(keys.memo)
},
Comment on lines +46 to +51
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Key name mismatch: memo_key vs memo.

The mutation's Payload interface in change-password.ts expects newKeys to be a Record<string, PrivateKey> and prepareAuth is called with "owner", "active", "posting" as keyof Keys. However, here you're passing memo_key while the mutation likely expects memo to match the authority key names.

Proposed fix
         newKeys: {
           owner: PrivateKey.fromString(keys.owner),
           active: PrivateKey.fromString(keys.active),
           posting: PrivateKey.fromString(keys.posting),
-          memo_key: PrivateKey.fromString(keys.memo)
+          memo: PrivateKey.fromString(keys.memo)
         },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
newKeys: {
owner: PrivateKey.fromString(keys.owner),
active: PrivateKey.fromString(keys.active),
posting: PrivateKey.fromString(keys.posting),
memo_key: PrivateKey.fromString(keys.memo)
},
newKeys: {
owner: PrivateKey.fromString(keys.owner),
active: PrivateKey.fromString(keys.active),
posting: PrivateKey.fromString(keys.posting),
memo: PrivateKey.fromString(keys.memo)
},
🤖 Prompt for AI Agents
In
@apps/web/src/app/(dynamicPages)/profile/[username]/permissions/_components/manage-keys-change-password.tsx
around lines 46 - 51, The newKeys object uses the wrong key name "memo_key";
change it to "memo" so it matches the Payload/Keys expected by the mutation and
prepareAuth calls—replace the property memo_key with memo (using
PrivateKey.fromString(keys.memo)) in the newKeys literal and ensure any callers
using prepareAuth("owner"/"active"/"posting"/"memo") and the Payload type in
change-password.ts align with this name.

currentPassword: rawKey!
});

setShowSeed(false);
setCurrentKey(undefined);
success(i18next.t("permissions.change-password.success"));
}
}, [keys, currentKey, activeUser, rawKey]);
Comment on lines +43 to +59
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Missing error handling for changePassword mutation.

If the mutation fails, the user receives no feedback. Consider wrapping in try/catch or using the mutation's error state.

Suggested approach
   const handleChangePassword = useCallback(async () => {
     if (keys && currentKey) {
+      try {
         await changePassword({
           newKeys: {
             owner: PrivateKey.fromString(keys.owner),
             active: PrivateKey.fromString(keys.active),
             posting: PrivateKey.fromString(keys.posting),
-            memo_key: PrivateKey.fromString(keys.memo)
+            memo: PrivateKey.fromString(keys.memo)
           },
           currentPassword: rawKey!
         });

         setShowSeed(false);
         setCurrentKey(undefined);
         success(i18next.t("permissions.change-password.success"));
+      } catch (e) {
+        error(i18next.t("permissions.change-password.error"));
+      }
     }
   }, [keys, currentKey, activeUser, rawKey]);
🤖 Prompt for AI Agents
In
@apps/web/src/app/(dynamicPages)/profile/[username]/permissions/_components/manage-keys-change-password.tsx
around lines 43 - 59, handleChangePassword currently awaits changePassword but
has no error handling, so failures give no user feedback; wrap the async call in
a try/catch inside handleChangePassword (or use the mutation's onError/state)
and on error call the UI error handler (e.g., error(i18next.t(...)) or surface
the mutation error message) while ensuring finally logic (setShowSeed(false),
setCurrentKey(undefined)) still runs only on success or is handled
appropriately; update the dependency usage around rawKey and currentKey as
needed.


return (
<div className="w-full pt-4">
{!showSeed && (
<div className="flex flex-col items-start gap-4 w-full">
<KeyInput
keyType="owner"
placeholder="Current key"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Hardcoded English string should use i18n.

The placeholder "Current key" is not internationalized.

           <KeyInput
             keyType="owner"
-            placeholder="Current key"
+            placeholder={i18next.t("permissions.change-password.current-key-placeholder")}
             className="w-full"
             ref={keyInputRef}
           />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
placeholder="Current key"
<KeyInput
keyType="owner"
placeholder={i18next.t("permissions.change-password.current-key-placeholder")}
className="w-full"
ref={keyInputRef}
/>
🤖 Prompt for AI Agents
In
@apps/web/src/app/(dynamicPages)/profile/[username]/permissions/_components/manage-keys-change-password.tsx
at line 67, Replace the hardcoded placeholder "Current key" in the
ManageKeysChangePassword component's input (the placeholder prop on the input
element in manage-keys-change-password.tsx) with a call to the project's i18n
translation helper (e.g., useTranslations()/t() or your existing i18n hook),
e.g., const t = useTranslations('profile.permissions'); then use t('currentKey')
(or the appropriate key) for the placeholder; add the new translation key to the
locale files.

className="w-full"
ref={keyInputRef}
/>
<Button onClick={() => handleGenerateNewPassword()} size="sm">
{i18next.t("permissions.change-password.generate")}
</Button>
</div>
)}
{showSeed && (
<WalletSeedPhrase
size="sm"
showTitle={false}
username={activeUser?.username!}
onValidated={() => handleChangePassword()}
/>
)}
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,43 +6,81 @@ import { Button, Modal, ModalBody, ModalHeader } from "@/features/ui";
import { useState } from "react";
import { ManageKeysAddKeys } from "./manage-keys-add-keys";
import { UilPlus } from "@tooni/iconscout-unicons-react";
import clsx from "clsx";
import { ManageKeyChangePassword } from "./manage-keys-change-password";

export function ManageKeys() {
const [tab, setTab] = useState<"password" | "key">("password");
const [showChangePassword, setShowChangePassword] = useState(false);

return (
<div className="grid grid-cols-1 md:grid-cols-2 gap-2 lg:gap-4 rounded-xl p-4 bg-white/80 dark:bg-dark-200/90 text-gray-900 dark:text-white pb-4">
<div className="md:col-span-2 flex justify-between gap-4">
<div>
<div className="text-sm md:text-lg font-bold pb-1">
{i18next.t("permissions.keys.title")}
<div className="flex gap-1 mb-4">
<div
className={clsx(
"cursor-pointer text-sm font-semibold rounded-full px-2 py-1",
tab === "password" && "bg-gray-100 dark:bg-gray-100/10 "
)}
onClick={() => setTab("password")}
>
{i18next.t("permissions.change-password.title")}
</div>
<div
className={clsx(
"cursor-pointer text-sm font-semibold rounded-full px-2 py-1",
tab === "key" && "bg-gray-100 dark:bg-gray-100/10 "
)}
onClick={() => setTab("key")}
>
{i18next.t("permissions.keys.title")}
</div>
Comment on lines +21 to +38
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Missing keyboard accessibility for tab navigation.

The tab elements are clickable divs without keyboard support. Users navigating via keyboard cannot activate these tabs.

Add keyboard accessibility
             <div
               className={clsx(
                 "cursor-pointer text-sm font-semibold rounded-full px-2 py-1",
                 tab === "password" && "bg-gray-100 dark:bg-gray-100/10 "
               )}
               onClick={() => setTab("password")}
+              role="tab"
+              tabIndex={0}
+              onKeyDown={(e) => e.key === "Enter" && setTab("password")}
+              aria-selected={tab === "password"}
             >

Apply similarly to the "key" tab button.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div
className={clsx(
"cursor-pointer text-sm font-semibold rounded-full px-2 py-1",
tab === "password" && "bg-gray-100 dark:bg-gray-100/10 "
)}
onClick={() => setTab("password")}
>
{i18next.t("permissions.change-password.title")}
</div>
<div
className={clsx(
"cursor-pointer text-sm font-semibold rounded-full px-2 py-1",
tab === "key" && "bg-gray-100 dark:bg-gray-100/10 "
)}
onClick={() => setTab("key")}
>
{i18next.t("permissions.keys.title")}
</div>
<div
className={clsx(
"cursor-pointer text-sm font-semibold rounded-full px-2 py-1",
tab === "password" && "bg-gray-100 dark:bg-gray-100/10 "
)}
onClick={() => setTab("password")}
role="tab"
tabIndex={0}
onKeyDown={(e) => e.key === "Enter" && setTab("password")}
aria-selected={tab === "password"}
>
{i18next.t("permissions.change-password.title")}
</div>
<div
className={clsx(
"cursor-pointer text-sm font-semibold rounded-full px-2 py-1",
tab === "key" && "bg-gray-100 dark:bg-gray-100/10 "
)}
onClick={() => setTab("key")}
role="tab"
tabIndex={0}
onKeyDown={(e) => e.key === "Enter" && setTab("key")}
aria-selected={tab === "key"}
>
{i18next.t("permissions.keys.title")}
</div>
🤖 Prompt for AI Agents
In
@apps/web/src/app/(dynamicPages)/profile/[username]/permissions/_components/manage-keys.tsx
around lines 21 - 38, The tab elements are non-interactive divs and lack
keyboard accessibility; update the tab UI (the elements that read tab, use
setTab("password") and setTab("key")) to be keyboard-focusable and activatable
by converting them to semantic <button> elements or adding role="button",
tabIndex={0}, and an onKeyDown handler that calls setTab when Enter or Space is
pressed; ensure the active styling logic (tab === "password"/"key") and the
i18next.t labels remain unchanged so the visual state and localization are
preserved.

</div>

<div className="text-sm opacity-75">{i18next.t("permissions.keys.hint")}</div>
<div
className="text-sm opacity-75"
dangerouslySetInnerHTML={{
__html:
tab === "password"
? i18next.t("permissions.change-password.hint")
: i18next.t("permissions.keys.hint")
}}
/>
</div>

<Button
appearance="gray"
size="sm"
className="whitespace-nowrap"
icon={<UilPlus />}
onClick={() => setShowChangePassword(true)}
>
{i18next.t("permissions.keys.add-key")}
</Button>
{tab === "key" && (
<Button
appearance="gray"
size="sm"
className="whitespace-nowrap"
icon={<UilPlus />}
onClick={() => setShowChangePassword(true)}
>
{i18next.t("permissions.keys.add-key")}
</Button>
)}
</div>

<ManageKey keyName="owner" />
<ManageKey keyName="active" />
<ManageKey keyName="posting" />
<ManageKey keyName="memo" />
{tab === "password" && <ManageKeyChangePassword />}
{tab === "key" && (
<>
<ManageKey keyName="owner" />
<ManageKey keyName="active" />
<ManageKey keyName="posting" />
<ManageKey keyName="memo" />

<Modal show={showChangePassword} onHide={() => setShowChangePassword(false)} centered={true}>
<ModalHeader closeButton={true}>{i18next.t("password-update.title")}</ModalHeader>
<ModalBody>
<ManageKeysAddKeys onSuccess={() => setShowChangePassword(false)} />
</ModalBody>
</Modal>
<Modal
show={showChangePassword}
onHide={() => setShowChangePassword(false)}
centered={true}
>
<ModalHeader closeButton={true}>{i18next.t("password-update.title")}</ModalHeader>
<ModalBody>
<ManageKeysAddKeys onSuccess={() => setShowChangePassword(false)} />
</ModalBody>
</Modal>
</>
)}
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ export function ProfilePermissions() {
return (
<div className="flex flex-col gap-4">
<ManageAuthorities />
<ManageKeys />
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="grid grid-cols-1 md:grid-cols-2 items-start gap-4">
<ManageKeys />
<AccountRecovery />
</div>
</div>
Expand Down
9 changes: 8 additions & 1 deletion apps/web/src/features/i18n/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -3084,8 +3084,15 @@
"sessions": {
"title": "Posting active sessions"
},
"change-password": {
"title": "Change password",
"hint": "Update your password to enhance the security of your account. Note that new passwords must now be based on a seed phrase, as traditional raw passwords are no longer supported.<br> In case of changing current seed phrase You will lose access to all external tokens.",
"generate": "Generate new password",
"invalid-key": "Entered key is invalid. Account doesn't contain such owner key",
"success": "Password has changed successfully!"
},
"keys": {
"title": "Keys & Passwords",
"title": "Keys",
"hint": "A Hive account can hold multiple keys simultaneously, all of which are listed below",
"add-key": "Add new keys",
"key-created": "Keys have created and assigned successfully.",
Expand Down
24 changes: 9 additions & 15 deletions apps/web/src/features/ui/input/key-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,14 @@ export interface KeyInputImperativeHandle {
}

function capitalizeFirstLetter(str) {
if (typeof str !== 'string' || str.length === 0) return '';
return str[0].toUpperCase() + str.slice(1);
if (typeof str !== "string" || str.length === 0) return "";
return str[0].toUpperCase() + str.slice(1);
}

export const KeyInput = forwardRef<
KeyInputImperativeHandle,
Props & Omit<HTMLProps<HTMLInputElement>, "ref" | "type">
>((
{ onSign, isLoading, keyType, className, ...inputProps },
ref
) => {
>(({ onSign, isLoading, keyType, className, ...inputProps }, ref) => {
const inputRef = useRef<HTMLInputElement>(null);

const { activeUser } = useActiveAccount();
Expand All @@ -65,28 +62,25 @@ export const KeyInput = forwardRef<
}

let privateKey: PrivateKey;

try {
if (cryptoUtils.isWif(key)) {
privateKey = PrivateKey.fromString(key);
} else {
const derivation = await detectHiveKeyDerivation(
activeUser.username,
key,
keyType
);

const derivation = await detectHiveKeyDerivation(activeUser.username, key, keyType);

if (derivation === "bip44") {
const keys = deriveHiveKeys(key);
const derivedKey = keyType === "active" ? keys.active : keys.owner;
privateKey = PrivateKey.fromString(derivedKey);
} else if (derivation === "master-password") {
privateKey = PrivateKey.fromLogin(activeUser.username, key, keyType);
} else {
privateKey = PrivateKey.from(key);
privateKey = PrivateKey.from(key);
}
}
} catch (err) {
}
catch (err) {
const errorMessage = err instanceof Error && err.message.includes("base58")
? i18next.t("key-or-hot.invalid-key")
: i18next.t("key-or-hot.key-error");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,16 @@ interface Payload {
keepCurrent?: boolean;
}

/**
* Only native Hive and custom passwords could be updated here
* Seed based password cannot be updated here, it will be in an account always for now
*/
type UpdatePasswordOptions = Pick<
UseMutationOptions<unknown, Error, Payload>,
"onSuccess" | "onError"
>;

/**
* @deprecated
* Only native Hive and custom passwords could be updated here
* Seed based password cannot be updated here, it will be in an account always for now
*/
export function useAccountUpdatePassword(
username: string,
options?: UpdatePasswordOptions
Expand Down