diff --git a/apps/extension/package.json b/apps/extension/package.json index a9b975be3e..fad22ce1d2 100644 --- a/apps/extension/package.json +++ b/apps/extension/package.json @@ -33,11 +33,11 @@ "@namada/storage": "0.1.0", "@namada/types": "0.1.0", "@namada/utils": "0.1.0", + "@namada/ledger-namada": "0.0.1", "@cosmjs/encoding": "^0.29.0", "@ledgerhq/hw-transport": "^6.28.3", "@ledgerhq/hw-transport-webhid": "^6.27.14", "@ledgerhq/hw-transport-webusb": "^6.27.14", - "@zondax/ledger-namada": "^0.0.2", "bignumber.js": "^9.1.1", "@dao-xyz/borsh": "^5.1.5", "buffer": "^6.0.3", diff --git a/apps/extension/src/App/Accounts/AccountListing.tsx b/apps/extension/src/App/Accounts/AccountListing.tsx index a061517e10..5704d0d475 100644 --- a/apps/extension/src/App/Accounts/AccountListing.tsx +++ b/apps/extension/src/App/Accounts/AccountListing.tsx @@ -29,6 +29,24 @@ const textToClipboard = (content: string): void => { navigator.clipboard.writeText(content); }; +const isChild = (type: AccountType, path: Bip44Path): boolean => { + // All PrivateKey accounts are child accounts + if (type === AccountType.PrivateKey) { + return true; + } + + if (type === AccountType.Ledger) { + // If this is a Ledger account, a child account is any account + // with a path that isn't the default path (/0'/0/0). This is for display + // purposes only. If the sum of the path components is greater than + // zero, it is a child. + const { account, change, index = 0 } = path; + return account + change + index > 0; + } + + return false; +}; + const formatDerivationPath = ( isChildAccount: boolean, { account, change, index = 0 }: Bip44Path, @@ -36,14 +54,14 @@ const formatDerivationPath = ( ): string => isChildAccount ? `/${account}'/${ - type === AccountType.PrivateKey ? `${change}/` : "" + type !== AccountType.Mnemonic ? `${change}/` : "" }${index}` : ""; const AccountListing = ({ account, parentAlias }: Props): JSX.Element => { const { address, alias, path, type } = account; const navigate = useNavigate(); - const isChildAccount = type !== AccountType.Mnemonic; + const isChildAccount = isChild(type, path); return ( diff --git a/apps/extension/src/App/App.tsx b/apps/extension/src/App/App.tsx index e669253b88..eaed3f07ae 100644 --- a/apps/extension/src/App/App.tsx +++ b/apps/extension/src/App/App.tsx @@ -78,11 +78,13 @@ export const App: React.FC = () => { setStatus(Status.Pending); try { - const parentId = await requester.sendMessage( + const parent = await requester.sendMessage( Ports.Background, new GetActiveAccountMsg() ); - const parentAccount = accounts.find((account) => account.id === parentId); + const parentAccount = accounts.find( + (account) => account.id === parent?.id + ); setParentAccount(parentAccount); } catch (e) { console.error(e); @@ -115,7 +117,7 @@ export const App: React.FC = () => { }, onFail: () => { setError("An error occurred connecting to extension"); - setStatus(Status.Failed); + setStatus(Status.Completed); }, }, { tries: 10, ms: 100 }, diff --git a/apps/extension/src/App/Settings/ExtraSettings/ExtraSettings.tsx b/apps/extension/src/App/Settings/ExtraSettings/ExtraSettings.tsx index 04ded376b0..765a4ce129 100644 --- a/apps/extension/src/App/Settings/ExtraSettings/ExtraSettings.tsx +++ b/apps/extension/src/App/Settings/ExtraSettings/ExtraSettings.tsx @@ -4,10 +4,7 @@ import { ExtensionRequester } from "extension"; import { Mode, ExtraSetting } from "./types"; import { ResetPassword } from "./ResetPassword"; import { DeleteAccount } from "./DeleteAccount"; -import { - ExtraSettingsContainer, - CloseLink -} from "./ExtraSettings.components"; +import { ExtraSettingsContainer, CloseLink } from "./ExtraSettings.components"; /** * Container for additional settings forms such as the reset password form. @@ -17,37 +14,27 @@ const ExtraSettings: React.FC<{ requester: ExtensionRequester; onClose: () => void; onDeleteAccount: (id: string) => void; -}> = ({ - extraSetting, - requester, - onClose, - onDeleteAccount, -}) => { +}> = ({ extraSetting, requester, onClose, onDeleteAccount }) => { return ( - {extraSetting && - - Close - } - - { - extraSetting === null ? "" : - - extraSetting.mode === Mode.ResetPassword ? - : - - extraSetting.mode === Mode.DeleteAccount ? - : + {extraSetting && Close} + {extraSetting === null ? ( + "" + ) : extraSetting.mode === Mode.ResetPassword ? ( + + ) : extraSetting.mode === Mode.DeleteAccount ? ( + + ) : ( assertNever(extraSetting.mode) - } + )} ); }; diff --git a/apps/extension/src/App/Settings/ExtraSettings/ResetPassword/ResetPassword.tsx b/apps/extension/src/App/Settings/ExtraSettings/ResetPassword/ResetPassword.tsx index 1134aabc46..192fae1036 100644 --- a/apps/extension/src/App/Settings/ExtraSettings/ResetPassword/ResetPassword.tsx +++ b/apps/extension/src/App/Settings/ExtraSettings/ResetPassword/ResetPassword.tsx @@ -23,12 +23,12 @@ enum Status { Unsubmitted, Pending, Complete, - Failed -}; + Failed, +} export type Props = { - accountId: string, - requester: ExtensionRequester + accountId: string; + requester: ExtensionRequester; }; const ResetPassword: React.FC = ({ accountId, requester }) => { @@ -42,8 +42,8 @@ const ResetPassword: React.FC = ({ accountId, requester }) => { const match = newPassword === confirmNewPassword; const { feedback } = zxcvbn(newPassword); - const hasFeedback = feedback.warning !== "" || - feedback.suggestions.length > 0; + const hasFeedback = + feedback.warning !== "" || feedback.suggestions.length > 0; const shouldDisableSubmit = status === Status.Pending || @@ -52,7 +52,7 @@ const ResetPassword: React.FC = ({ accountId, requester }) => { !match || (process.env.NODE_ENV !== "development" && hasFeedback); - const handleSubmit = async () => { + const handleSubmit = async (): Promise => { setStatus(Status.Pending); const result = await requester.sendMessage( @@ -87,7 +87,7 @@ const ResetPassword: React.FC = ({ accountId, requester }) => { setCurrentPassword(e.target.value)} + onChange={(e) => setCurrentPassword(e.target.value)} /> @@ -96,16 +96,16 @@ const ResetPassword: React.FC = ({ accountId, requester }) => { setNewPassword(e.target.value)} + onChange={(e) => setNewPassword(e.target.value)} /> {feedback.warning} - {feedback.suggestions.map((suggestion, i) => + {feedback.suggestions.map((suggestion, i) => ( {suggestion} - )} + ))} @@ -113,35 +113,27 @@ const ResetPassword: React.FC = ({ accountId, requester }) => { setConfirmNewPassword(e.target.value)} + onChange={(e) => setConfirmNewPassword(e.target.value)} /> - {!match && ( - Passwords do not match - )} + {!match && Passwords do not match} - {errorMessage && ( - {errorMessage} - )} + {errorMessage && {errorMessage}} )} - {status === Status.Complete && ( -

Complete!

- )} + {status === Status.Complete &&

Complete!

} ); -} +}; export default ResetPassword; diff --git a/apps/extension/src/App/Settings/ExtraSettings/types.ts b/apps/extension/src/App/Settings/ExtraSettings/types.ts index a41b62d343..f7254a1263 100644 --- a/apps/extension/src/App/Settings/ExtraSettings/types.ts +++ b/apps/extension/src/App/Settings/ExtraSettings/types.ts @@ -2,9 +2,9 @@ export enum Mode { ResetPassword = "Reset password", DeleteAccount = "Delete account", -}; +} export type ExtraSetting = { mode: Mode; accountId: string; -} +}; diff --git a/apps/extension/src/App/Settings/Settings.tsx b/apps/extension/src/App/Settings/Settings.tsx index 3e8d9bb438..00d2bed12a 100644 --- a/apps/extension/src/App/Settings/Settings.tsx +++ b/apps/extension/src/App/Settings/Settings.tsx @@ -12,6 +12,7 @@ import { LockKeyRingMsg, SetActiveAccountMsg, QueryParentAccountsMsg, + ParentAccount, } from "background/keyring"; import { SettingsContainer, @@ -29,10 +30,6 @@ import { } from "../Accounts/Accounts.components"; import { TopLevelRoute } from "../types"; import { Status } from "../App"; -import { - ResetPassword, - Props as ResetPasswordProps -} from "./ExtraSettings/ResetPassword" import { ExtraSettings } from "./ExtraSettings"; import { Mode, ExtraSetting } from "./ExtraSettings/types"; @@ -43,11 +40,7 @@ const Settings: React.FC<{ activeAccountId: string; requester: ExtensionRequester; onSelectAccount: (account: DerivedAccount) => void; -}> = ({ - activeAccountId, - requester, - onSelectAccount, -}) => { +}> = ({ activeAccountId, requester, onSelectAccount }) => { const [extraSetting, setExtraSetting] = useState(null); const [status, setStatus] = useState(Status.Pending); const [error, setError] = useState(""); @@ -75,19 +68,21 @@ const Settings: React.FC<{ fetchParentAccounts(); }, []); - const handleSelectAccount = async (account: DerivedAccount): Promise => { - const { id } = account; + const handleSelectAccount = async ( + account: DerivedAccount + ): Promise => { + const { id, type } = account; try { await requester.sendMessage( Ports.Background, - new SetActiveAccountMsg(id) + new SetActiveAccountMsg(id, type as ParentAccount) ); // Lock current wallet keyring: await requester.sendMessage(Ports.Background, new LockKeyRingMsg()); // Fetch accounts for selected parent account - await onSelectAccount(account); + onSelectAccount(account); } catch (e) { console.error(e); setError(`An error occurred while setting active account: ${e}`); @@ -95,7 +90,9 @@ const Settings: React.FC<{ } }; - const handleDeleteAccount = async (deletedAccountId: string): Promise => { + const handleDeleteAccount = async ( + deletedAccountId: string + ): Promise => { await fetchParentAccounts(); }; @@ -122,25 +119,26 @@ const Settings: React.FC<{ - {status === Status.Failed && (

Error communicating with extension background!

)}

{error ? error : "Select account:"}

- {parentAccounts.map((account, i) => + {parentAccounts.map((account, i) => ( handleSelectAccount(account)} - onSelectMode={(mode) => setExtraSetting({ - mode, - accountId: account.id - })} + onSelectMode={(mode) => + setExtraSetting({ + mode, + accountId: account.id, + }) + } /> - )} + ))} @@ -168,7 +166,6 @@ const Settings: React.FC<{ onClose={() => setExtraSetting(null)} onDeleteAccount={handleDeleteAccount} /> -
@@ -183,37 +180,28 @@ const AccountListItem: React.FC<{ activeAccountId: string; onSelectAccount: () => void; onSelectMode: (mode: Mode) => void; -}> = ({ - account, - activeAccountId, - onSelectAccount, - onSelectMode, -}) => { +}> = ({ account, activeAccountId, onSelectAccount, onSelectMode }) => { const [expanded, setExpanded] = useState(false); return ( <> - + {account.alias} {activeAccountId === account.id && (selected)} - setExpanded(!expanded)} - /> + setExpanded(!expanded)} /> - {expanded && + {expanded && ( { setExpanded(false); onSelectMode(mode); }} /> - } + )} ); }; @@ -223,24 +211,16 @@ const AccountListItem: React.FC<{ */ const ModeSelect: React.FC<{ onSelectMode: (mode: Mode) => void; -}> = ({ - onSelectMode, -}) => { - const modes = [ - Mode.ResetPassword, - Mode.DeleteAccount, - ]; +}> = ({ onSelectMode }) => { + const modes = [Mode.ResetPassword, Mode.DeleteAccount]; return ( - {modes.map((mode, i) => - onSelectMode(mode)} - > + {modes.map((mode, i) => ( + onSelectMode(mode)}> {mode} - )} + ))} ); }; diff --git a/apps/extension/src/Approvals/Approvals.components.tsx b/apps/extension/src/Approvals/Approvals.components.tsx index 84940937ab..89c38be9a6 100644 --- a/apps/extension/src/Approvals/Approvals.components.tsx +++ b/apps/extension/src/Approvals/Approvals.components.tsx @@ -1,4 +1,4 @@ -import styled, { createGlobalStyle } from "styled-components"; +import styled, { createGlobalStyle, css } from "styled-components"; export const GlobalStyles = createGlobalStyle` html, body { @@ -93,3 +93,45 @@ export const ButtonContainer = styled.div` display: flex; flex-direction: row; `; + +export const Spinner = css` + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + border-top-color: ${(props) => props.theme.colors.primary.main}; + border-radius: 50%; + animation: button-loading-spinner 1s ease infinite; + + @keyframes button-loading-spinner { + from { + transform: rotate(0turn); + } + + to { + transform: rotate(1turn); + } + } + } +`; + +export const InfoHeader = styled.div` + display: flex; + align-items: center; + gap: 10px; +`; + +export const InfoLoader = styled.div` + position: relative; + width: 20px; + height: 20px; + + &::after { + width: 16px; + height: 16px; + border: 2px solid transparent; + ${Spinner} + } +`; diff --git a/apps/extension/src/Approvals/Approvals.tsx b/apps/extension/src/Approvals/Approvals.tsx index d689ad1f31..c39210bded 100644 --- a/apps/extension/src/Approvals/Approvals.tsx +++ b/apps/extension/src/Approvals/Approvals.tsx @@ -14,6 +14,8 @@ import { import { ApproveTransfer, ConfirmTransfer } from "./ApproveTransfer"; import { ApproveConnection } from "./ApproveConnection"; import { TopLevelRoute } from "Approvals/types"; +import { ConfirmLedgerTransfer } from "./ApproveTransfer/ConfirmLedgerTransfer"; +import { ApproveBond, ConfirmBond, ConfirmLedgerBond } from "./ApproveBond"; export enum Status { Completed, @@ -25,6 +27,7 @@ export const Approvals: React.FC = () => { const theme = getTheme("dark"); const [msgId, setMsgId] = useState(""); const [address, setAddress] = useState(""); + const [publicKey, setPublicKey] = useState(""); return ( @@ -36,15 +39,44 @@ export const Approvals: React.FC = () => { } /> } /> + } + /> + + } + /> + } + /> + + } + /> + } diff --git a/apps/extension/src/Approvals/ApproveBond/ApproveBond.tsx b/apps/extension/src/Approvals/ApproveBond/ApproveBond.tsx new file mode 100644 index 0000000000..d7dd0c9e8c --- /dev/null +++ b/apps/extension/src/Approvals/ApproveBond/ApproveBond.tsx @@ -0,0 +1,94 @@ +import { useNavigate } from "react-router-dom"; +import { useCallback, useEffect } from "react"; + +import { Button, ButtonVariant } from "@namada/components"; +import { shortenAddress } from "@namada/utils"; +import { AccountType, Tokens } from "@namada/types"; + +import { useQuery } from "hooks"; +import { Address } from "App/Accounts/AccountListing.components"; +import { + ApprovalContainer, + ButtonContainer, +} from "Approvals/Approvals.components"; +import { TopLevelRoute } from "Approvals/types"; +import { Ports } from "router"; +import { RejectTxMsg } from "background/approvals"; +import { useRequester } from "hooks/useRequester"; +import { closeCurrentTab } from "utils"; + +type Props = { + setAddress: (address: string) => void; + setMsgId: (msgId: string) => void; + setPublicKey: (publicKey: string) => void; +}; + +export const ApproveBond: React.FC = ({ + setAddress, + setMsgId, + setPublicKey, +}) => { + const navigate = useNavigate(); + const requester = useRequester(); + + const query = useQuery(); + const type = query.get("type") || ""; + const id = query.get("id") || ""; + const amount = query.get("amount") || ""; + const source = query.get("source") || ""; + const tokenAddress = query.get("token") || ""; + const tokenType = + Object.values(Tokens).find((token) => token.address === tokenAddress) + ?.symbol || ""; + const publicKey = query.get("publicKey") || ""; + + useEffect(() => { + if (source) { + setAddress(source); + } + if (publicKey) { + setPublicKey(publicKey); + } + }, [source, publicKey]); + + const handleApproveClick = (): void => { + setMsgId(id); + if (type === AccountType.Ledger) { + return navigate(`${TopLevelRoute.ConfirmLedgerBond}`); + } + navigate(TopLevelRoute.ConfirmBond); + }; + + const handleReject = useCallback(async (): Promise => { + try { + // TODO: use executeUntil here! + await requester.sendMessage(Ports.Background, new RejectTxMsg(id)); + + // Close tab + await closeCurrentTab(); + } catch (e) { + console.warn(e); + } + return; + }, [id]); + + return ( + +

Approve this Bond?

+

Source: 

+
{shortenAddress(source)}
+

+ Amount: {amount} {tokenType} +

+ + + + + +
+ ); +}; diff --git a/apps/extension/src/Approvals/ApproveBond/ConfirmBond.tsx b/apps/extension/src/Approvals/ApproveBond/ConfirmBond.tsx new file mode 100644 index 0000000000..88b44713e3 --- /dev/null +++ b/apps/extension/src/Approvals/ApproveBond/ConfirmBond.tsx @@ -0,0 +1,109 @@ +import { useEffect, useState } from "react"; +import { useNavigate } from "react-router-dom"; + +import { + Button, + ButtonVariant, + Input, + InputVariants, +} from "@namada/components"; +import { shortenAddress } from "@namada/utils"; + +import { Status } from "Approvals/Approvals"; +import { + ApprovalContainer, + ButtonContainer, + InfoHeader, + InfoLoader, +} from "Approvals/Approvals.components"; +import { Ports } from "router"; +import { useRequester } from "hooks/useRequester"; +import { SubmitApprovedBondMsg } from "background/approvals"; +import { Address } from "App/Accounts/AccountListing.components"; +import { closeCurrentTab } from "utils"; + +type Props = { + msgId: string; + address: string; +}; + +export const ConfirmBond: React.FC = ({ msgId, address }) => { + const navigate = useNavigate(); + const requester = useRequester(); + const [password, setPassword] = useState(""); + const [error, setError] = useState(); + const [status, setStatus] = useState(); + const [statusInfo, setStatusInfo] = useState(""); + + const handleApproveBond = async (): Promise => { + setStatus(Status.Pending); + try { + // TODO: use executeUntil here! + setStatusInfo("Decrypting keys and submitting transfer..."); + await requester.sendMessage( + Ports.Background, + new SubmitApprovedBondMsg(msgId, address, password) + ); + setStatus(Status.Completed); + } catch (e) { + setError("Unable to authenticate Tx!"); + setStatus(Status.Failed); + } + setStatusInfo(""); + setStatus(Status.Completed); + return; + }; + + useEffect(() => { + (async () => { + if (status === Status.Completed) { + await closeCurrentTab(); + } + })(); + }, [status]); + + return ( + + {status === Status.Pending && ( + + +

{statusInfo}

+
+ )} + {status === Status.Failed && ( +

+ {error} +
+ Try again +

+ )} + {status !== (Status.Pending || Status.Completed) && ( + <> +
+ Decrypt keys for
{shortenAddress(address)}
+
+ setPassword(e.target.value)} + /> + + + + + + )} +
+ ); +}; diff --git a/apps/extension/src/Approvals/ApproveBond/ConfirmLedgerBond.tsx b/apps/extension/src/Approvals/ApproveBond/ConfirmLedgerBond.tsx new file mode 100644 index 0000000000..d1581f7147 --- /dev/null +++ b/apps/extension/src/Approvals/ApproveBond/ConfirmLedgerBond.tsx @@ -0,0 +1,196 @@ +import { useCallback, useState } from "react"; +import { toBase64 } from "@cosmjs/encoding"; +import BigNumber from "bignumber.js"; + +import { LedgerError } from "@namada/ledger-namada"; +import { Button, ButtonVariant } from "@namada/components"; +import { defaultChainId as chainId } from "@namada/chains"; + +import { Ledger } from "background/ledger"; +import { + GetBondBytesMsg, + GetRevealPKBytesMsg, + SubmitSignedBondMsg, + SubmitSignedRevealPKMsg, +} from "background/ledger/messages"; +import { Ports } from "router"; +import { closeCurrentTab } from "utils"; +import { useRequester } from "hooks/useRequester"; +import { Status } from "Approvals/Approvals"; +import { + ApprovalContainer, + ButtonContainer, +} from "Approvals/Approvals.components"; +import { InfoHeader, InfoLoader } from "Approvals/Approvals.components"; +import { + Message, + RevealPKProps, + SubmitRevealPKMsgValue, + Tokens, +} from "@namada/types"; +import { QueryPublicKeyMsg } from "background/keyring"; + +type Props = { + address: string; + msgId: string; + publicKey: string; +}; + +export const ConfirmLedgerBond: React.FC = ({ + address, + msgId, + publicKey, +}) => { + const requester = useRequester(); + const [error, setError] = useState(); + const [status, setStatus] = useState(); + const [statusInfo, setStatusInfo] = useState(""); + + const revealPk = async (publicKey: string): Promise => { + const revealPKArgs: RevealPKProps = { + tx: { + token: Tokens.NAM.address || "", + feeAmount: new BigNumber(0), + gasLimit: new BigNumber(0), + chainId, + publicKey, + }, + publicKey, + }; + + const msgValue = new SubmitRevealPKMsgValue(revealPKArgs); + const msg = new Message(); + const encoded = msg.encode(msgValue); + + // Open Ledger transport + const ledger = await Ledger.init(); + + try { + const { bytes, path } = await requester.sendMessage( + Ports.Background, + new GetRevealPKBytesMsg(toBase64(encoded)) + ); + + // Sign with Ledgeg + const signatures = await ledger.sign(bytes, path); + + // Submit signatures for tx + setStatusInfo("Submitting reveal pk tx..."); + await requester.sendMessage( + Ports.Background, + new SubmitSignedRevealPKMsg( + toBase64(encoded), + toBase64(bytes), + signatures + ) + ); + } catch (e) { + console.warn("An error occured: ", e); + throw new Error(`${e}`); + } finally { + await ledger.closeTransport(); + } + }; + + const queryPublicKey = async ( + address: string + ): Promise => { + return await requester.sendMessage( + Ports.Background, + new QueryPublicKeyMsg(address) + ); + }; + + const submitBond = async (): Promise => { + setStatus(Status.Pending); + setStatusInfo("Querying for public key on chain..."); + + const pk = await queryPublicKey(address); + + if (!pk) { + setStatusInfo( + "Public key not found! Review and approve reveal pk on your Ledger" + ); + await revealPk(publicKey); + } + + // Open ledger transport + const ledger = await Ledger.init(); + + try { + // Constuct tx bytes from SDK + const { bytes, path } = await requester.sendMessage( + Ports.Background, + new GetBondBytesMsg(msgId) + ); + + setStatusInfo("Review and approve bond transaction on your Ledger"); + // Sign with Ledger + const signatures = await ledger.sign(bytes, path); + const { errorMessage, returnCode } = signatures; + + if (returnCode !== LedgerError.NoErrors) { + console.warn("Bond sign errors encountered, exiting: ", { + returnCode, + errorMessage, + }); + setError(errorMessage); + return setStatus(Status.Failed); + } + + // Submit signatures for tx + setStatusInfo("Submitting bond transaction..."); + await requester.sendMessage( + Ports.Background, + new SubmitSignedBondMsg(msgId, toBase64(bytes), signatures) + ); + setStatus(Status.Completed); + } catch (e) { + console.warn(e); + setStatus(Status.Failed); + await ledger.closeTransport(); + } + }; + + const handleCloseTab = useCallback(async (): Promise => { + await closeCurrentTab(); + }, []); + + return ( + + {status === Status.Failed && ( +

+ {error} +
+ Try again +

+ )} + {status === Status.Pending && ( + + + {statusInfo} + + )} + {status !== Status.Pending && status !== Status.Completed && ( + <> +

Make sure your Ledger is unlocked, and click "Submit"

+ + + + + )} + {status === Status.Completed && ( + <> +

Success! You may close this window.

+ + + + + )} +
+ ); +}; diff --git a/apps/extension/src/Approvals/ApproveBond/index.ts b/apps/extension/src/Approvals/ApproveBond/index.ts new file mode 100644 index 0000000000..e25c4c1552 --- /dev/null +++ b/apps/extension/src/Approvals/ApproveBond/index.ts @@ -0,0 +1,3 @@ +export * from "./ApproveBond"; +export * from "./ConfirmBond"; +export * from "./ConfirmLedgerBond"; diff --git a/apps/extension/src/Approvals/ApproveConnection/ApproveConnection.components.tsx b/apps/extension/src/Approvals/ApproveConnection/ApproveConnection.components.tsx deleted file mode 100644 index 08cf720e5d..0000000000 --- a/apps/extension/src/Approvals/ApproveConnection/ApproveConnection.components.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import styled from "styled-components"; - -export const ApproveConnectionContainer = styled.div` - display: flex; - flex-direction: column; -`; diff --git a/apps/extension/src/Approvals/ApproveTransfer/ApproveTransfer.components.ts b/apps/extension/src/Approvals/ApproveTransfer/ApproveTransfer.components.ts deleted file mode 100644 index 4b17bbbe0e..0000000000 --- a/apps/extension/src/Approvals/ApproveTransfer/ApproveTransfer.components.ts +++ /dev/null @@ -1,6 +0,0 @@ -import styled from "styled-components"; - -export const ApproveTxContainer = styled.div` - display: flex; - flex-direction: column; -`; diff --git a/apps/extension/src/Approvals/ApproveTransfer/ApproveTransfer.tsx b/apps/extension/src/Approvals/ApproveTransfer/ApproveTransfer.tsx index 5d738591af..792fe5492a 100644 --- a/apps/extension/src/Approvals/ApproveTransfer/ApproveTransfer.tsx +++ b/apps/extension/src/Approvals/ApproveTransfer/ApproveTransfer.tsx @@ -3,7 +3,7 @@ import { useEffect } from "react"; import { Button, ButtonVariant } from "@namada/components"; import { shortenAddress } from "@namada/utils"; -import { Tokens } from "@namada/types"; +import { AccountType, Tokens } from "@namada/types"; import { useQuery } from "hooks"; import { Address } from "App/Accounts/AccountListing.components"; @@ -13,7 +13,7 @@ import { } from "Approvals/Approvals.components"; import { TopLevelRoute } from "Approvals/types"; import { Ports } from "router"; -import { RejectTransferMsg } from "background/approvals"; +import { RejectTxMsg } from "background/approvals"; import { useRequester } from "hooks/useRequester"; import { closeCurrentTab } from "utils"; @@ -28,6 +28,7 @@ export const ApproveTransfer: React.FC = ({ setAddress, setMsgId }) => { const query = useQuery(); // TODO: Get current parent account alias to display to user + const type = query.get("type") || ""; const id = query.get("id") || ""; const amount = query.get("amount") || ""; const source = query.get("source") || ""; @@ -45,13 +46,16 @@ export const ApproveTransfer: React.FC = ({ setAddress, setMsgId }) => { const handleApproveClick = (): void => { setMsgId(id); - navigate(TopLevelRoute.ConfirmTx); + if (type === AccountType.Ledger) { + return navigate(`${TopLevelRoute.ConfirmLedgerTransfer}`); + } + navigate(TopLevelRoute.ConfirmTransfer); }; const handleReject = async (): Promise => { try { // TODO: use executeUntil here! - await requester.sendMessage(Ports.Background, new RejectTransferMsg(id)); + await requester.sendMessage(Ports.Background, new RejectTxMsg(id)); // Close tab await closeCurrentTab(); @@ -64,7 +68,6 @@ export const ApproveTransfer: React.FC = ({ setAddress, setMsgId }) => { return (

Approve this Transaction?

-

ID: {id}

Target: 

{shortenAddress(target)}

Source: 

diff --git a/apps/extension/src/Approvals/ApproveTransfer/ConfirmLedgerTransfer.tsx b/apps/extension/src/Approvals/ApproveTransfer/ConfirmLedgerTransfer.tsx new file mode 100644 index 0000000000..9877b3f309 --- /dev/null +++ b/apps/extension/src/Approvals/ApproveTransfer/ConfirmLedgerTransfer.tsx @@ -0,0 +1,103 @@ +import { useCallback, useState } from "react"; +import { LedgerError } from "@namada/ledger-namada"; +import { toBase64 } from "@cosmjs/encoding"; + +import { Button, ButtonVariant } from "@namada/components"; + +import { Ledger } from "background/ledger"; +import { + GetTransferBytesMsg, + SubmitSignedTransferMsg, +} from "background/ledger/messages"; +import { Ports } from "router"; +import { closeCurrentTab } from "utils"; +import { useRequester } from "hooks/useRequester"; +import { Status } from "Approvals/Approvals"; +import { + ApprovalContainer, + ButtonContainer, +} from "Approvals/Approvals.components"; + +type Props = { + msgId: string; +}; + +export const ConfirmLedgerTransfer: React.FC = ({ msgId }) => { + const requester = useRequester(); + const [error, setError] = useState(); + const [status, setStatus] = useState(); + + const submitTransfer = async (): Promise => { + setStatus(Status.Pending); + const ledger = await Ledger.init(); + + try { + // Constuct tx bytes from SDK + const { bytes, path } = await requester.sendMessage( + Ports.Background, + new GetTransferBytesMsg(msgId) + ); + + // Sign with Ledger + const signatures = await ledger.sign(bytes, path); + const { errorMessage, returnCode } = signatures; + + // Close transport so that it may be re-opened on a subsequent attempt (due to error) + await ledger.closeTransport(); + + if (returnCode !== LedgerError.NoErrors) { + setError(errorMessage); + return setStatus(Status.Failed); + } + + // Submit signatures for tx + await requester.sendMessage( + Ports.Background, + new SubmitSignedTransferMsg(msgId, toBase64(bytes), signatures) + ); + setStatus(Status.Completed); + } catch (e) { + console.warn(e); + const ledgerErrors = await ledger.queryErrors(); + setError(ledgerErrors); + setStatus(Status.Failed); + } + }; + + const handleCloseTab = useCallback(async (): Promise => { + await closeCurrentTab(); + }, []); + + return ( + + {status === Status.Failed && ( +

+ {error} +
+ Try again +

+ )} + {status === Status.Pending &&

Submitting transfer...

} + {status !== Status.Pending && status !== Status.Completed && ( + <> +

Make sure your Ledger is unlocked, and click "Submit"

+ + + + + )} + {status === Status.Completed && ( + <> +

Success! You may close this window.

+ + + + + )} +
+ ); +}; diff --git a/apps/extension/src/Approvals/ApproveTransfer/ConfirmTransfer.components.ts b/apps/extension/src/Approvals/ApproveTransfer/ConfirmTransfer.components.ts deleted file mode 100644 index 9e5a44b80f..0000000000 --- a/apps/extension/src/Approvals/ApproveTransfer/ConfirmTransfer.components.ts +++ /dev/null @@ -1,43 +0,0 @@ -import styled, { css } from "styled-components"; - -export const Spinner = css` - content: ""; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - border-top-color: ${(props) => props.theme.colors.primary.main}; - border-radius: 50%; - animation: button-loading-spinner 1s ease infinite; - - @keyframes button-loading-spinner { - from { - transform: rotate(0turn); - } - - to { - transform: rotate(1turn); - } - } - } -`; - -export const InfoHeader = styled.div` - display: flex; - align-items: center; - gap: 10px; -`; - -export const InfoLoader = styled.div` - position: relative; - width: 20px; - height: 20px; - - &::after { - width: 16px; - height: 16px; - border: 2px solid transparent; - ${Spinner} - } -`; diff --git a/apps/extension/src/Approvals/ApproveTransfer/ConfirmTransfer.tsx b/apps/extension/src/Approvals/ApproveTransfer/ConfirmTransfer.tsx index 7cc31f8365..ca9c92de1d 100644 --- a/apps/extension/src/Approvals/ApproveTransfer/ConfirmTransfer.tsx +++ b/apps/extension/src/Approvals/ApproveTransfer/ConfirmTransfer.tsx @@ -8,6 +8,8 @@ import { Status } from "Approvals/Approvals"; import { ApprovalContainer, ButtonContainer, + InfoHeader, + InfoLoader, } from "Approvals/Approvals.components"; import { Ports } from "router"; import { useRequester } from "hooks/useRequester"; @@ -15,7 +17,6 @@ import { SubmitApprovedTransferMsg } from "background/approvals"; import { Address } from "App/Accounts/AccountListing.components"; import { closeCurrentTab } from "utils"; import { FetchAndStoreMaspParamsMsg, HasMaspParamsMsg } from "provider"; -import { InfoHeader, InfoLoader } from "./ConfirmTransfer.components"; type Props = { msgId: string; @@ -30,7 +31,7 @@ export const ConfirmTransfer: React.FC = ({ msgId, address }) => { const [status, setStatus] = useState(); const [statusInfo, setStatusInfo] = useState(""); - const handleApprove = async (): Promise => { + const handleApproveTransfer = async (): Promise => { setStatus(Status.Pending); const hasMaspParams = await requester.sendMessage( Ports.Background, @@ -56,6 +57,7 @@ export const ConfirmTransfer: React.FC = ({ msgId, address }) => { Ports.Background, new SubmitApprovedTransferMsg(msgId, address, password) ); + setStatus(Status.Completed); } catch (e) { setError("Unable to authenticate Tx!"); setStatus(Status.Failed); @@ -100,7 +102,7 @@ export const ConfirmTransfer: React.FC = ({ msgId, address }) => { /> - - - - )} + + setAlias(e.target.value)} + variant={InputVariants.Text} + /> + + + + + ); }; diff --git a/apps/extension/src/Setup/Ledger/LedgerConfirmation.tsx b/apps/extension/src/Setup/Ledger/LedgerConfirmation.tsx new file mode 100644 index 0000000000..d631a28407 --- /dev/null +++ b/apps/extension/src/Setup/Ledger/LedgerConfirmation.tsx @@ -0,0 +1,89 @@ +import React, { useCallback, useContext, useState } from "react"; +import { useNavigate } from "react-router-dom"; +import { ThemeContext } from "styled-components"; + +import { + Button, + ButtonVariant, + Icon, + IconName, + IconSize, +} from "@namada/components"; +import { shortenAddress } from "@namada/utils"; +import { useSanitizedParams } from "@namada/hooks"; + +import { useRequester } from "hooks/useRequester"; +import { LedgerError } from "./Ledger.components"; +import { + TopSection, + TopSectionHeaderContainer, + TopSectionButtonContainer, + ButtonsContainer, + SubViewContainer, + UpperContentContainer, + Header1, + BodyText, +} from "Setup/Setup.components"; +import { Ports } from "router"; +import { AddLedgerAccountMsg } from "background/ledger/messages"; +import { closeCurrentTab } from "utils"; + +const LedgerConfirmation: React.FC = () => { + const navigate = useNavigate(); + const requester = useRequester(); + const themeContext = useContext(ThemeContext); + const { alias = "", address = "", publicKey = "" } = useSanitizedParams(); + const [error, setError] = useState(); + + const handleSubmitClick = useCallback(async (): Promise => { + try { + await requester.sendMessage( + Ports.Background, + new AddLedgerAccountMsg(alias, address, publicKey, { + account: 0, + change: 0, + index: 0, + }) + ); + closeCurrentTab(); + } catch (e) { + console.warn(e); + setError(`${e}`); + } + }, []); + + return ( + + + + navigate(-1)} style={{ cursor: "pointer" }}> + + + + + + + + Confirm Ledger Connection + + + {error && {error}} + + Connection successful for "{alias}"! + + Address: {address && shortenAddress(address)} + Add this address to your wallet? + + + + + ); +}; + +export default LedgerConfirmation; diff --git a/apps/extension/src/Setup/Setup.tsx b/apps/extension/src/Setup/Setup.tsx index 7b3554a088..23120a6117 100644 --- a/apps/extension/src/Setup/Setup.tsx +++ b/apps/extension/src/Setup/Setup.tsx @@ -28,6 +28,7 @@ import { ImportAccount } from "./ImportAccount"; import { useRequester } from "hooks/useRequester"; import { SeedPhraseImport } from "./ImportAccount/Steps"; import { Completion, Password } from "./Common"; +import LedgerConfirmation from "./Ledger/LedgerConfirmation"; type AnimatedTransitionProps = { elementKey: string; @@ -259,7 +260,17 @@ export const Setup: React.FC = () => { path={`/${TopLevelRoute.Ledger}`} element={ - + + + } + /> + + } /> diff --git a/apps/extension/src/Setup/types.ts b/apps/extension/src/Setup/types.ts index fc06a99d2e..ec902b93c1 100644 --- a/apps/extension/src/Setup/types.ts +++ b/apps/extension/src/Setup/types.ts @@ -3,6 +3,7 @@ export enum TopLevelRoute { AccountCreation = "account-creation", ImportAccount = "restore-account", Ledger = "ledger", + LedgerConfirmation = "ledger-confirmation", } export enum AccountCreationRoute { diff --git a/apps/extension/src/background/approvals/handler.ts b/apps/extension/src/background/approvals/handler.ts index b8fb368eac..653d92b815 100644 --- a/apps/extension/src/background/approvals/handler.ts +++ b/apps/extension/src/background/approvals/handler.ts @@ -1,46 +1,101 @@ import { Handler, Env, Message, InternalHandler } from "router"; import { ApprovalsService } from "./service"; -import { ApproveTransferMsg } from "provider"; -import { RejectTransferMsg, SubmitApprovedTransferMsg } from "./messages"; +import { ApproveBondMsg, ApproveUnbondMsg, ApproveTransferMsg } from "provider"; +import { + RejectTxMsg, + SubmitApprovedTransferMsg, + SubmitApprovedBondMsg, + SubmitApprovedUnbondMsg, +} from "./messages"; export const getHandler: (service: ApprovalsService) => Handler = (service) => { return (env: Env, msg: Message) => { switch (msg.constructor) { case ApproveTransferMsg: - return handleApproveTxMsg(service)(env, msg as ApproveTransferMsg); - case RejectTransferMsg: - return handleRejectTxMsg(service)(env, msg as RejectTransferMsg); + return handleApproveTransferMsg(service)( + env, + msg as ApproveTransferMsg + ); + case ApproveBondMsg: + return handleApproveBondMsg(service)(env, msg as ApproveBondMsg); + case ApproveUnbondMsg: + return handleApproveUnbondMsg(service)(env, msg as ApproveUnbondMsg); + case RejectTxMsg: + return handleRejectTxMsg(service)(env, msg as RejectTxMsg); case SubmitApprovedTransferMsg: - return handleSubmitApprovedTxMsg(service)( + return handleSubmitApprovedTransferMsg(service)( env, msg as SubmitApprovedTransferMsg ); + case SubmitApprovedBondMsg: + return handleSubmitApprovedBondMsg(service)( + env, + msg as SubmitApprovedBondMsg + ); + case SubmitApprovedUnbondMsg: + return handleSubmitApprovedUnbondMsg(service)( + env, + msg as SubmitApprovedUnbondMsg + ); + default: throw new Error("Unknown msg type"); } }; }; -const handleApproveTxMsg: ( +const handleApproveTransferMsg: ( service: ApprovalsService ) => InternalHandler = (service) => { - return async (_, { txMsg }) => { - return await service.approveTransfer(txMsg); + return async (_, { txMsg, accountType }) => { + return await service.approveTransfer(txMsg, accountType); }; }; const handleRejectTxMsg: ( service: ApprovalsService -) => InternalHandler = (service) => { +) => InternalHandler = (service) => { return async (_, { msgId }) => { - return await service.rejectTransfer(msgId); + return await service.rejectTx(msgId); }; }; -const handleSubmitApprovedTxMsg: ( +const handleSubmitApprovedTransferMsg: ( service: ApprovalsService ) => InternalHandler = (service) => { return async (_, { msgId, password }) => { return await service.submitTransfer(msgId, password); }; }; + +const handleApproveBondMsg: ( + service: ApprovalsService +) => InternalHandler = (service) => { + return async (_, { txMsg, accountType, publicKey }) => { + return await service.approveBond(txMsg, accountType, publicKey); + }; +}; + +const handleApproveUnbondMsg: ( + service: ApprovalsService +) => InternalHandler = (service) => { + return async (_, { txMsg, accountType }) => { + return await service.approveUnbond(txMsg, accountType); + }; +}; + +const handleSubmitApprovedBondMsg: ( + service: ApprovalsService +) => InternalHandler = (service) => { + return async (_, { msgId, password }) => { + return await service.submitBond(msgId, password); + }; +}; + +const handleSubmitApprovedUnbondMsg: ( + service: ApprovalsService +) => InternalHandler = (service) => { + return async (_, { msgId, password }) => { + return await service.submitUnbond(msgId, password); + }; +}; diff --git a/apps/extension/src/background/approvals/init.ts b/apps/extension/src/background/approvals/init.ts index e0d4a060fe..60bc02c6a5 100644 --- a/apps/extension/src/background/approvals/init.ts +++ b/apps/extension/src/background/approvals/init.ts @@ -1,14 +1,22 @@ import { Router } from "router"; -import { ApproveTransferMsg } from "provider"; -import { RejectTransferMsg, SubmitApprovedTransferMsg } from "./messages"; +import { ApproveBondMsg, ApproveTransferMsg } from "provider"; +import { + RejectTxMsg, + SubmitApprovedBondMsg, + SubmitApprovedUnbondMsg, + SubmitApprovedTransferMsg, +} from "./messages"; import { ROUTE } from "./constants"; import { ApprovalsService } from "./service"; import { getHandler } from "./handler"; export function init(router: Router, service: ApprovalsService): void { + router.registerMessage(ApproveBondMsg); router.registerMessage(ApproveTransferMsg); - router.registerMessage(RejectTransferMsg); + router.registerMessage(RejectTxMsg); + router.registerMessage(SubmitApprovedBondMsg); + router.registerMessage(SubmitApprovedUnbondMsg); router.registerMessage(SubmitApprovedTransferMsg); router.addHandler(ROUTE, getHandler(service)); diff --git a/apps/extension/src/background/approvals/messages.ts b/apps/extension/src/background/approvals/messages.ts index a1ee99967e..43549e9b40 100644 --- a/apps/extension/src/background/approvals/messages.ts +++ b/apps/extension/src/background/approvals/messages.ts @@ -2,13 +2,15 @@ import { Message } from "router"; import { ROUTE } from "./constants"; enum MessageType { - RejectTransfer = "reject-transfer", + RejectTx = "reject-tx", SubmitApprovedTransfer = "submit-approved-transfer", + SubmitApprovedBond = "submit-approved-bond", + SubmitApprovedUnbond = "submit-approved-unbond", } -export class RejectTransferMsg extends Message { +export class RejectTxMsg extends Message { public static type(): MessageType { - return MessageType.RejectTransfer; + return MessageType.RejectTx; } constructor(public readonly msgId: string) { @@ -27,7 +29,7 @@ export class RejectTransferMsg extends Message { } type(): string { - return RejectTransferMsg.type(); + return RejectTxMsg.type(); } } @@ -52,8 +54,11 @@ export class SubmitApprovedTransferMsg extends Message { throw new Error("address must not be empty!"); } if (!this.password) { - throw new Error("Password is required to submitTx!"); + throw new Error( + "Password is required to submitTx for this type of account!" + ); } + return; } @@ -65,3 +70,75 @@ export class SubmitApprovedTransferMsg extends Message { return SubmitApprovedTransferMsg.type(); } } + +export class SubmitApprovedBondMsg extends Message { + public static type(): MessageType { + return MessageType.SubmitApprovedBond; + } + + constructor( + public readonly msgId: string, + public readonly address: string, + public readonly password: string + ) { + super(); + } + + validate(): void { + if (!this.msgId) { + throw new Error("msgId must not be empty!"); + } + if (!this.address) { + throw new Error("address must not be empty!"); + } + if (!this.password) { + throw new Error("Password is required to submit bond tx!"); + } + + return; + } + + route(): string { + return ROUTE; + } + + type(): string { + return SubmitApprovedBondMsg.type(); + } +} + +export class SubmitApprovedUnbondMsg extends Message { + public static type(): MessageType { + return MessageType.SubmitApprovedUnbond; + } + + constructor( + public readonly msgId: string, + public readonly address: string, + public readonly password: string + ) { + super(); + } + + validate(): void { + if (!this.msgId) { + throw new Error("msgId must not be empty!"); + } + if (!this.address) { + throw new Error("address must not be empty!"); + } + if (!this.password) { + throw new Error("Password is required to submit unbond tx!"); + } + + return; + } + + route(): string { + return ROUTE; + } + + type(): string { + return SubmitApprovedUnbondMsg.type(); + } +} diff --git a/apps/extension/src/background/approvals/service.ts b/apps/extension/src/background/approvals/service.ts index 1a65d95d67..43d1050ca1 100644 --- a/apps/extension/src/background/approvals/service.ts +++ b/apps/extension/src/background/approvals/service.ts @@ -2,74 +2,171 @@ import browser from "webextension-polyfill"; import { fromBase64 } from "@cosmjs/encoding"; import { v4 as uuid } from "uuid"; import BigNumber from "bignumber.js"; +import { deserialize } from "@dao-xyz/borsh"; -import { TransferMsgValue } from "@namada/types"; +import { + AccountType, + SubmitBondMsgValue, + TransferMsgValue, +} from "@namada/types"; import { KVStore } from "@namada/storage"; -import { ExtensionRequester } from "extension"; -import { KeyRingService } from "background/keyring"; -import { TabStore } from "background/keyring"; -import { deserialize } from "@dao-xyz/borsh"; +import { KeyRingService, TabStore } from "background/keyring"; +import { LedgerService } from "background/ledger"; +import { paramsToUrl } from "@namada/utils"; export class ApprovalsService { constructor( protected readonly txStore: KVStore, protected readonly connectedTabsStore: KVStore, protected readonly keyRingService: KeyRingService, - protected readonly chainId: string, - protected readonly requester: ExtensionRequester - ) {} + protected readonly ledgerService: LedgerService + ) { } // Deserialize transfer details and prompt user - async approveTransfer(txMsg: string): Promise { + async approveTransfer(txMsg: string, type?: AccountType): Promise { const txMsgBuffer = Buffer.from(fromBase64(txMsg)); const id = uuid(); - this.txStore.set(id, txMsg); + await this.txStore.set(id, txMsg); // Decode tx details and launch approval screen const txDetails = deserialize(txMsgBuffer, TransferMsgValue); const { source, target, token, amount: amountBN } = txDetails; const amount = new BigNumber(amountBN.toString()); - const url = `${browser.runtime.getURL( + const baseUrl = `${browser.runtime.getURL( "approvals.html" - )}#/tx?id=${id}&source=${source}&target=${target}&token=${token}&amount=${amount}`; + )}#/approve-transfer`; - browser.windows.create({ - url, - width: 415, - height: 510, - type: "popup", + const url = paramsToUrl(baseUrl, { + id, + source, + target, + token, + amount: amount.toString(), + type: type as string, + }); + + this._launchApprovalWindow(url); + } + + // Deserialize bond details and prompt user + async approveBond( + txMsg: string, + type: AccountType, + publicKey?: string + ): Promise { + const txMsgBuffer = Buffer.from(fromBase64(txMsg)); + const id = uuid(); + await this.txStore.set(id, txMsg); + + // Decode tx details and launch approval screen + const txDetails = deserialize(txMsgBuffer, SubmitBondMsgValue); + + const { source, nativeToken: token, amount: amountBN } = txDetails; + const amount = new BigNumber(amountBN.toString()); + const baseUrl = `${browser.runtime.getURL("approvals.html")}#/approve-bond`; + + const url = paramsToUrl(baseUrl, { + id, + source, + token, + amount: amount.toString(), + publicKey: publicKey || "", + type: type as string, + }); + + this._launchApprovalWindow(url); + } + + // Deserialize bond details and prompt user + // TODO: Finish implementing! + async approveUnbond(txMsg: string, type?: AccountType): Promise { + const txMsgBuffer = Buffer.from(fromBase64(txMsg)); + const id = uuid(); + await this.txStore.set(id, txMsg); + + // Decode tx details and launch approval screen + const txDetails = deserialize(txMsgBuffer, SubmitBondMsgValue); + + const { source, nativeToken, amount: amountBN } = txDetails; + const amount = new BigNumber(amountBN.toString()); + // TODO: This query should include perhaps a "type" indicating whether it's a bond or unbond tx: + const baseUrl = `${browser.runtime.getURL("approvals.html")}#/approve-bond`; + + const url = paramsToUrl(baseUrl, { + id, + source, + token: nativeToken, + amount: amount.toString(), + type: type as string, }); - return; + this._launchApprovalWindow(url); } // Remove pending transaction from storage - async rejectTransfer(msgId: string): Promise { + async rejectTx(msgId: string): Promise { await this._clearPendingTx(msgId); } - // Authenticate keyring, submit approved transaction from storage + // Authenticate keyring and submit approved transfer transaction from storage async submitTransfer(msgId: string, password: string): Promise { - // TODO: Use executeUntil here: - try { - await this.keyRingService.unlock(password); - } catch (e) { - throw new Error(`${e}`); - } + await this.keyRingService.unlock(password); + // Fetch pending transfer tx const tx = await this.txStore.get(msgId); if (tx) { await this.keyRingService.submitTransfer(tx, msgId); - // Clean up storage + return await this._clearPendingTx(msgId); } throw new Error("Pending transfer not found!"); } + // Authenticate keyring and submit approved bond transaction from storage + async submitBond(msgId: string, password: string): Promise { + await this.keyRingService.unlock(password); + + // Fetch pending bond tx + const tx = await this.txStore.get(msgId); + + if (tx) { + await this.keyRingService.submitBond(tx, msgId); + await this.ledgerService.broadcastUpdateStaking(); + + return await this._clearPendingTx(msgId); + } + + throw new Error("Pending bond not found!"); + } + + async submitUnbond(msgId: string, password: string): Promise { + await this.keyRingService.unlock(password); + + // Fetch pending bond tx + const tx = await this.txStore.get(msgId); + + if (tx) { + await this.keyRingService.submitUnbond(tx, msgId); + + return await this._clearPendingTx(msgId); + } + + throw new Error("Pending bond not found!"); + } + private async _clearPendingTx(msgId: string): Promise { return await this.txStore.set(msgId, null); } + + private _launchApprovalWindow = (url: string): void => { + browser.windows.create({ + url, + width: 415, + height: 510, + type: "popup", + }); + }; } diff --git a/apps/extension/src/background/index.ts b/apps/extension/src/background/index.ts index 6f3c2ce0de..440ba4ebcc 100644 --- a/apps/extension/src/background/index.ts +++ b/apps/extension/src/background/index.ts @@ -26,7 +26,9 @@ import { SDK_KEY, PARENT_ACCOUNT_ID_KEY, UtilityStore, + ActiveAccountStore, } from "./keyring"; +import { LedgerService, init as initLedger } from "./ledger"; const store = new IndexedDBKVStore(KVPrefix.IndexedDB); @@ -72,10 +74,12 @@ const { REACT_APP_NAMADA_URL = DEFAULT_URL } = process.env; const sdkData: Record | undefined = await sdkStore.get( SDK_KEY ); - const activeAccount = await utilityStore.get(PARENT_ACCOUNT_ID_KEY); + const activeAccount = await utilityStore.get( + PARENT_ACCOUNT_ID_KEY + ); if (sdkData && activeAccount) { - const data = new TextEncoder().encode(sdkData[activeAccount]); + const data = new TextEncoder().encode(sdkData[activeAccount.id]); sdk.decode(data); } @@ -92,18 +96,27 @@ const { REACT_APP_NAMADA_URL = DEFAULT_URL } = process.env; cryptoMemory, requester ); + const ledgerService = new LedgerService( + keyRingService, + store, + connectedTabsStore, + txStore, + defaultChainId, + sdk, + requester + ); const approvalsService = new ApprovalsService( txStore, connectedTabsStore, keyRingService, - defaultChainId, - requester + ledgerService ); // Initialize messages and handlers initApprovals(router, approvalsService); initChains(router, chainsService); initKeyRing(router, keyRingService); + initLedger(router, ledgerService); router.listen(Ports.Background); })(); diff --git a/apps/extension/src/background/keyring/handler.ts b/apps/extension/src/background/keyring/handler.ts index 3705a68d1b..6394934175 100644 --- a/apps/extension/src/background/keyring/handler.ts +++ b/apps/extension/src/background/keyring/handler.ts @@ -3,6 +3,7 @@ import { KeyRingService } from "./service"; import { CheckIsLockedMsg, CheckPasswordMsg, + QueryPublicKeyMsg, CloseOffscreenDocumentMsg, ResetPasswordMsg, DeriveAccountMsg, @@ -23,8 +24,6 @@ import { EncodeInitAccountMsg, QueryAccountsMsg, QueryBalancesMsg, - SubmitBondMsg, - SubmitUnbondMsg, SubmitIbcTransferMsg, FetchAndStoreMaspParamsMsg, HasMaspParamsMsg, @@ -46,6 +45,8 @@ export const getHandler: (service: KeyRingService) => Handler = (service) => { return handleUnlockKeyRingMsg(service)(env, msg as UnlockKeyRingMsg); case CheckPasswordMsg: return handleCheckPasswordMsg(service)(env, msg as CheckPasswordMsg); + case QueryPublicKeyMsg: + return handleQueryPublicKey(service)(env, msg as QueryPublicKeyMsg); case ResetPasswordMsg: return handleResetPasswordMsg(service)(env, msg as ResetPasswordMsg); case GenerateMnemonicMsg: @@ -83,10 +84,6 @@ export const getHandler: (service: KeyRingService) => Handler = (service) => { env, msg as QueryParentAccountsMsg ); - case SubmitBondMsg: - return handleSubmitBondMsg(service)(env, msg as SubmitBondMsg); - case SubmitUnbondMsg: - return handleSubmitUnbondMsg(service)(env, msg as SubmitUnbondMsg); case SubmitIbcTransferMsg: return handleSubmitIbcTransferMsg(service)( env, @@ -162,6 +159,15 @@ const handleCheckPasswordMsg: ( }; }; +const handleQueryPublicKey: ( + service: KeyRingService +) => InternalHandler = (service) => { + return async (_, msg) => { + const { address } = msg; + return await service.queryPublicKey(address); + }; +}; + const handleResetPasswordMsg: ( service: KeyRingService ) => InternalHandler = (service) => { @@ -243,24 +249,6 @@ const handleQueryParentAccountsMsg: ( }; }; -const handleSubmitBondMsg: ( - service: KeyRingService -) => InternalHandler = (service) => { - return async (_, msg) => { - const { txMsg } = msg; - return await service.submitBond(txMsg); - }; -}; - -const handleSubmitUnbondMsg: ( - service: KeyRingService -) => InternalHandler = (service) => { - return async (_, msg) => { - const { txMsg } = msg; - return await service.submitUnbond(txMsg); - }; -}; - const handleSubmitIbcTransferMsg: ( service: KeyRingService ) => InternalHandler = (service) => { @@ -283,8 +271,8 @@ const handleSetActiveAccountMsg: ( service: KeyRingService ) => InternalHandler = (service) => { return async (_, msg) => { - const { accountId } = msg; - return await service.setActiveAccountId(accountId); + const { accountId, accountType } = msg; + return await service.setActiveAccount(accountId, accountType); }; }; @@ -292,7 +280,7 @@ const handleGetActiveAccountMsg: ( service: KeyRingService ) => InternalHandler = (service) => { return async (_, _msg) => { - return await service.getActiveAccountId(); + return await service.getActiveAccount(); }; }; diff --git a/apps/extension/src/background/keyring/init.ts b/apps/extension/src/background/keyring/init.ts index ac2dfb430d..101294a17a 100644 --- a/apps/extension/src/background/keyring/init.ts +++ b/apps/extension/src/background/keyring/init.ts @@ -5,6 +5,7 @@ import { LockKeyRingMsg, UnlockKeyRingMsg, CheckPasswordMsg, + QueryPublicKeyMsg, ResetPasswordMsg, GenerateMnemonicMsg, SaveMnemonicMsg, @@ -23,8 +24,6 @@ import { QueryAccountsMsg, QueryBalancesMsg, EncodeRevealPkMsg, - SubmitBondMsg, - SubmitUnbondMsg, SubmitIbcTransferMsg, FetchAndStoreMaspParamsMsg, HasMaspParamsMsg, @@ -36,6 +35,7 @@ import { KeyRingService } from "./service"; export function init(router: Router, service: KeyRingService): void { router.registerMessage(CheckIsLockedMsg); router.registerMessage(CheckPasswordMsg); + router.registerMessage(QueryPublicKeyMsg); router.registerMessage(CloseOffscreenDocumentMsg); router.registerMessage(ConnectInterfaceMsg); router.registerMessage(DeriveAccountMsg); @@ -51,9 +51,7 @@ export function init(router: Router, service: KeyRingService): void { router.registerMessage(SaveMnemonicMsg); router.registerMessage(ScanAccountsMsg); router.registerMessage(SetActiveAccountMsg); - router.registerMessage(SubmitBondMsg); router.registerMessage(SubmitIbcTransferMsg); - router.registerMessage(SubmitUnbondMsg); router.registerMessage(TransferCompletedEvent); router.registerMessage(UnlockKeyRingMsg); router.registerMessage(DeleteAccountMsg); diff --git a/apps/extension/src/background/keyring/keyring.ts b/apps/extension/src/background/keyring/keyring.ts index 51db0b80b5..79591d1ac9 100644 --- a/apps/extension/src/background/keyring/keyring.ts +++ b/apps/extension/src/background/keyring/keyring.ts @@ -1,6 +1,8 @@ import BigNumber from "bignumber.js"; -import { v5 as uuid, v4 as uuidV4 } from "uuid"; +import { v4 as uuidV4 } from "uuid"; +import { deserialize } from "@dao-xyz/borsh"; +import { chains } from "@namada/chains"; import { HDWallet, Mnemonic, @@ -24,9 +26,8 @@ import { DerivedAccount, TransferMsgValue, } from "@namada/types"; -import { chains } from "@namada/chains"; -import { Crypto } from "./crypto"; import { + ActiveAccountStore, KeyRingStatus, KeyStore, ResetPasswordError, @@ -38,27 +39,14 @@ import { readVecStringPointer, readStringPointer, } from "@namada/crypto/src/utils"; -import { Result } from "@namada/utils"; -import { deserialize } from "@dao-xyz/borsh"; +import { makeBip44Path, Result } from "@namada/utils"; + +import { Crypto } from "./crypto"; +import { getAccountValuesFromStore, generateId } from "utils"; // Generated UUID namespace for uuid v5 const UUID_NAMESPACE = "9bfceade-37fe-11ed-acc0-a3da3461b38c"; -// Construct unique uuid, passing in an arbitray number of arguments. -// This could be a unique parameter of the object receiving the id, -// or an index based on the number of existing objects in a hierarchy. -const getId = (name: string, ...args: (number | string)[]): string => { - // Ensure a unique UUID - let uuidName = name; - - // Concatenate any number of args onto the name parameter - args.forEach((arg) => { - uuidName = `${uuidName}::${arg}`; - }); - - return uuid(uuidName, UUID_NAMESPACE); -}; - export const KEYSTORE_KEY = "key-store"; export const SDK_KEY = "sdk-store"; export const PARENT_ACCOUNT_ID_KEY = "parent-account-id"; @@ -116,17 +104,19 @@ export class KeyRing { } } - public async getActiveAccountId(): Promise { + public async getActiveAccount(): Promise { return await this.utilityStore.get(PARENT_ACCOUNT_ID_KEY); } - public async setActiveAccountId(parentId: string): Promise { - await this.utilityStore.set(PARENT_ACCOUNT_ID_KEY, parentId); - + public async setActiveAccount( + id: string, + type: AccountType.Mnemonic | AccountType.Ledger + ): Promise { + await this.utilityStore.set(PARENT_ACCOUNT_ID_KEY, { id, type }); // To sync sdk wallet with DB const sdkData = await this.sdkStore.get(SDK_KEY); if (sdkData) { - this.sdk.decode(new TextEncoder().encode(sdkData[parentId])); + this.sdk.decode(new TextEncoder().encode(sdkData[id])); } } @@ -135,7 +125,7 @@ export class KeyRing { accountId?: string ): Promise { // default to active account if no account provided - const idToCheck = accountId ?? (await this.getActiveAccountId()); + const idToCheck = accountId ?? (await this.getActiveAccount())?.id; if (!idToCheck) { throw new Error("No account to check password against"); } @@ -191,7 +181,7 @@ export class KeyRing { } // change password held locally if active account password changed - if (accountId === (await this.getActiveAccountId())) { + if (accountId === (await this.getActiveAccount())?.id) { this._password = newPassword; } } catch (error) { @@ -238,19 +228,24 @@ export class KeyRing { const mnemonic = Mnemonic.from_phrase(phrase); const seed = mnemonic.to_seed(); const { coinType } = chains[this.chainId].bip44; - const path = `m/44'/${coinType}'/0'/0`; - const bip44 = new HDWallet(seed); - const account = bip44.derive(path); + const path = { account: 0, change: 0 }; + const bip44Path = makeBip44Path(coinType, path); + const hdWallet = new HDWallet(seed); + const account = hdWallet.derive(bip44Path); const stringPointer = account.private().to_hex(); const sk = readStringPointer(stringPointer, this._cryptoMemory); const address = new Address(sk).implicit(); const { chainId } = this; // Generate unique ID for new parent account: - const id = getId(phrase, (await this._keyStore.get()).length); + const id = generateId( + UUID_NAMESPACE, + phrase, + (await this._keyStore.get()).length + ); mnemonic.free(); - bip44.free(); + hdWallet.free(); account.free(); stringPointer.free(); @@ -261,10 +256,7 @@ export class KeyRing { owner: address, chainId, password, - path: { - account: 0, - change: 0, - }, + path, text: phrase, type: AccountType.Mnemonic, }); @@ -275,7 +267,7 @@ export class KeyRing { // to prevent adding top level secret key to existing keys this.sdk.clear_storage(); await this.addSecretKey(sk, password, alias, id); - await this.setActiveAccountId(id); + await this.setActiveAccount(id, AccountType.Mnemonic); this._password = password; return true; @@ -305,26 +297,26 @@ export class KeyRing { path: Bip44Path, parentId: string ): DerivedAccountInfo { - const { account, change, index = 0 } = path; - const root = "m/44'"; const { coinType } = chains[this.chainId].bip44; - const derivationPath = [ - root, - `${coinType}'`, - `${account}`, - change, - index, - ].join("/"); - const bip44 = new HDWallet(seed); - const derivedAccount = bip44.derive(derivationPath); - + const derivationPath = makeBip44Path(coinType, path); + const hdWallet = new HDWallet(seed); + const derivedAccount = hdWallet.derive(derivationPath); const privateKey = derivedAccount.private(); const hex = privateKey.to_hex(); const text = readStringPointer(hex, this.cryptoMemory); const address = new Address(text).implicit(); - const id = getId("account", parentId, account, change, index); - bip44.free(); + const { account, change, index = 0 } = path; + const id = generateId( + UUID_NAMESPACE, + "account", + parentId, + account, + change, + index + ); + + hdWallet.free(); derivedAccount.free(); privateKey.free(); hex.free(); @@ -343,7 +335,7 @@ export class KeyRing { parentId: string ): DerivedAccountInfo { const { index = 0 } = path; - const id = getId("shielded-account", parentId, index); + const id = generateId("shielded-account", parentId, index); const zip32 = new ShieldedHDWallet(seed); const account = zip32.derive_to_serialized_keys(index); @@ -475,9 +467,10 @@ export class KeyRing { parentId: string; seed: VecU8Pointer; }> { + const activeAccount = await this.getActiveAccount(); const storedMnemonic = await this._keyStore.getRecord( "id", - await this.getActiveAccountId() + activeAccount?.id ); if (!storedMnemonic) { @@ -575,28 +568,18 @@ export class KeyRing { /** * Query accounts from storage (active parent account + associated derived child accounts) */ - public async queryAccounts(): Promise { - const activeAccountId = await this.getActiveAccountId(); + public async queryAccounts( + activeAccountId: string + ): Promise { const parentAccount = await this._keyStore.getRecord("id", activeAccountId); + const derivedAccounts = (await this._keyStore.getRecords("parentId", activeAccountId)) || []; if (parentAccount) { const accounts = [parentAccount, ...derivedAccounts]; - // Return only non-encrypted data - - return accounts.map( - ({ address, alias, chainId, path, parentId, id, type }) => ({ - address, - alias, - chainId, - id, - parentId, - path, - type, - }) - ); + return getAccountValuesFromStore(accounts); } return []; } @@ -605,22 +588,10 @@ export class KeyRing { * Query all top-level parent accounts (mnemonic accounts) */ public async queryParentAccounts(): Promise { - const accounts = await this._keyStore.getRecords( - "type", - AccountType.Mnemonic - ); + const accounts = + (await this._keyStore.getRecords("type", AccountType.Mnemonic)) || []; // Return only non-encrypted data - return (accounts || []).map( - ({ address, alias, chainId, path, parentId, id, type }) => ({ - address, - alias, - chainId, - id, - parentId, - path, - type, - }) - ); + return getAccountValuesFromStore(accounts); } async encodeInitAccount( @@ -738,7 +709,7 @@ export class KeyRing { } // remove password held locally if active account deleted - if (accountId === (await this.getActiveAccountId())) { + if (accountId === (await this.getActiveAccount())?.id) { this._password = undefined; } @@ -753,15 +724,10 @@ export class KeyRing { } async queryBalances( - address: string + owner: string ): Promise<{ token: string; amount: string }[]> { - const account = await this._keyStore.getRecord("address", address); - if (!account) { - throw new Error(`Account not found.`); - } - try { - return (await this.query.query_balance(account.owner)).map( + return (await this.query.query_balance(owner)).map( ([token, amount]: [string, string]) => { return { token, @@ -782,6 +748,10 @@ export class KeyRing { activeAccountId: string ): Promise { this.sdk.add_key(secretKey, password, alias); + await this.initSdkStore(activeAccountId); + } + + public async initSdkStore(activeAccountId: string): Promise { const store = (await this.sdkStore.get(SDK_KEY)) || {}; this.sdkStore.set(SDK_KEY, { diff --git a/apps/extension/src/background/keyring/messages.ts b/apps/extension/src/background/keyring/messages.ts index 0e8758a3f1..f773f196ed 100644 --- a/apps/extension/src/background/keyring/messages.ts +++ b/apps/extension/src/background/keyring/messages.ts @@ -2,12 +2,18 @@ import { PhraseSize } from "@namada/crypto"; import { AccountType, Bip44Path, DerivedAccount } from "@namada/types"; import { Message } from "router"; import { ROUTE } from "./constants"; -import { KeyRingStatus, ResetPasswordError, DeleteAccountError } from "./types"; +import { + KeyRingStatus, + ResetPasswordError, + DeleteAccountError, + ParentAccount, +} from "./types"; import { Result } from "@namada/utils"; enum MessageType { CheckIsLocked = "check-is-locked", CheckPassword = "check-password", + QueryPublicKey = "query-public-key", CloseOffscreenDocument = "close-offscreen-document", DeriveAccount = "derive-account", DeriveShieldedAccount = "derive-shielded-account", @@ -117,6 +123,30 @@ export class CheckPasswordMsg extends Message { } } +export class QueryPublicKeyMsg extends Message { + public static type(): MessageType { + return MessageType.QueryPublicKey; + } + + constructor(public readonly address: string) { + super(); + } + + validate(): void { + if (!this.address) { + throw new Error("address not set"); + } + } + + route(): string { + return ROUTE; + } + + type(): string { + return QueryPublicKeyMsg.type(); + } +} + export class ResetPasswordMsg extends Message< Result > { @@ -237,7 +267,7 @@ export class ScanAccountsMsg extends Message { } // eslint-disable-next-line @typescript-eslint/no-empty-function - validate(): void {} + validate(): void { } route(): string { return ROUTE; @@ -294,7 +324,10 @@ export class SetActiveAccountMsg extends Message { return MessageType.SetActiveAccount; } - constructor(public readonly accountId: string) { + constructor( + public readonly accountId: string, + public readonly accountType: ParentAccount + ) { super(); } @@ -302,6 +335,10 @@ export class SetActiveAccountMsg extends Message { if (!this.accountId) { throw new Error("Account ID is not set!"); } + + if (!this.accountType) { + throw new Error("Account Type is required!"); + } } route(): string { @@ -313,7 +350,9 @@ export class SetActiveAccountMsg extends Message { } } -export class GetActiveAccountMsg extends Message { +export class GetActiveAccountMsg extends Message< + { id: string; type: ParentAccount } | undefined +> { public static type(): MessageType { return MessageType.GetActiveAccount; } diff --git a/apps/extension/src/background/keyring/service.ts b/apps/extension/src/background/keyring/service.ts index 1bf68f281b..18a92d44b4 100644 --- a/apps/extension/src/background/keyring/service.ts +++ b/apps/extension/src/background/keyring/service.ts @@ -1,19 +1,22 @@ import { fromBase64, toBase64 } from "@cosmjs/encoding"; import { PhraseSize } from "@namada/crypto"; -import { KVStore } from "@namada/storage"; +import { KVStore, Store } from "@namada/storage"; import { AccountType, Bip44Path, DerivedAccount } from "@namada/types"; import { Query, Sdk } from "@namada/shared"; import { Result } from "@namada/utils"; -import { KeyRing } from "./keyring"; +import { KeyRing, KEYSTORE_KEY } from "./keyring"; import { + AccountStore, KeyRingStatus, KeyStore, TabStore, ResetPasswordError, DeleteAccountError, UtilityStore, + ParentAccount, + ActiveAccountStore, } from "./types"; import { syncTabs, updateTabStorage } from "./utils"; import { ExtensionRequester, getNamadaRouterId } from "extension"; @@ -31,9 +34,13 @@ import { SUBMIT_TRANSFER_MSG_TYPE, } from "background/offscreen"; import { init as initSubmitTransferWebWorker } from "background/web-workers"; +import { LEDGERSTORE_KEY } from "background/ledger"; +import { getAccountValuesFromStore } from "utils"; export class KeyRingService { private _keyRing: KeyRing; + private _keyRingStore: Store; + private _ledgerStore: Store; constructor( protected readonly kvStore: KVStore, @@ -57,6 +64,8 @@ export class KeyRingService { query, cryptoMemory ); + this._ledgerStore = new Store(LEDGERSTORE_KEY, kvStore); + this._keyRingStore = new Store(KEYSTORE_KEY, kvStore); } lock(): { status: KeyRingStatus } { @@ -146,14 +155,40 @@ export class KeyRingService { } async queryAccounts(): Promise { - return await this._keyRing.queryAccounts(); + const { id, type } = (await this.getActiveAccount()) || {}; + + if (type !== AccountType.Ledger && id) { + // Query KeyRing accounts + return await this._keyRing.queryAccounts(id); + } + + // Query Ledger accounts + const parent = await this._ledgerStore.getRecord("id", id); + + if (parent) { + const accounts = [ + parent, + ...((await this._ledgerStore.getRecords("parentId", id)) || []), + ]; + + return getAccountValuesFromStore(accounts); + } + + throw new Error(`No accounts found for ${id} ${type}`); } async queryParentAccounts(): Promise { - return await this._keyRing.queryParentAccounts(); + const ledgerAccounts = + (await this._ledgerStore.getRecords("parentId", undefined)) || []; + + return [ + ...(await this._keyRing.queryParentAccounts()), + ...getAccountValuesFromStore(ledgerAccounts), + ]; } - async submitBond(txMsg: string): Promise { + async submitBond(txMsg: string, msgId: string): Promise { + console.log(`TODO: Broadcast notification for ${msgId}`); try { await this._keyRing.submitBond(fromBase64(txMsg)); } catch (e) { @@ -162,7 +197,8 @@ export class KeyRingService { } } - async submitUnbond(txMsg: string): Promise { + async submitUnbond(txMsg: string, msgId: string): Promise { + console.log(`TODO: Broadcast notification for ${msgId}`); try { await this._keyRing.submitUnbond(fromBase64(txMsg)); } catch (e) { @@ -283,14 +319,13 @@ export class KeyRingService { return toBase64(tx_data); } - async setActiveAccountId(accountId: string): Promise { - await this._keyRing.setActiveAccountId(accountId); - + async setActiveAccount(id: string, type: ParentAccount): Promise { + await this._keyRing.setActiveAccount(id, type); await this.broadcastAccountsChanged(); } - async getActiveAccountId(): Promise { - return await this._keyRing.getActiveAccountId(); + async getActiveAccount(): Promise { + return await this._keyRing.getActiveAccount(); } async handleTransferCompleted( @@ -369,7 +404,7 @@ export class KeyRingService { return Sdk.has_masp_params(); } - private async broadcastUpdateBalance(): Promise { + async broadcastUpdateBalance(): Promise { const tabs = await syncTabs( this.connectedTabsStore, this.requester, @@ -391,8 +426,24 @@ export class KeyRingService { } async queryBalances( - owner: string + address: string ): Promise<{ token: string; amount: string }[]> { - return this._keyRing.queryBalances(owner); + // Validate account + const account = + (await this._keyRingStore.getRecord("address", address)) || + (await this._ledgerStore.getRecord("address", address)); + + if (!account) { + throw new Error("Account not found!"); + } + return this._keyRing.queryBalances(account.owner); + } + + async initSdkStore(activeAccountId: string): Promise { + return await this._keyRing.initSdkStore(activeAccountId); + } + + async queryPublicKey(address: string): Promise { + return await this.query.query_public_key(address); } } diff --git a/apps/extension/src/background/keyring/types.ts b/apps/extension/src/background/keyring/types.ts index 318a55e6ce..f93466e2c8 100644 --- a/apps/extension/src/background/keyring/types.ts +++ b/apps/extension/src/background/keyring/types.ts @@ -1,3 +1,4 @@ +import { StoredRecord } from "@namada/storage"; import { AccountType, Bip44Path, DerivedAccount } from "@namada/types"; export enum KdfType { @@ -34,19 +35,22 @@ export type CryptoRecord = { }; }; -export interface KeyStore { - id: string; +export interface AccountStore extends StoredRecord { alias: string; address: string; owner: string; chainId: string; + publicKey?: string; + path: Bip44Path; + parentId?: string; + type: AccountType; +} + +export interface KeyStore extends AccountStore { crypto: CryptoRecord; meta?: { [key: string]: string; }; - path: Bip44Path; - parentId?: string; - type: AccountType; } export type AccountState = DerivedAccount & { @@ -67,13 +71,20 @@ export type TabStore = { timestamp: number; }; -export type UtilityStore = string | { [id: string]: CryptoRecord }; - export enum ResetPasswordError { BadPassword, KeyStoreError, } +export type ParentAccount = AccountType.Mnemonic | AccountType.Ledger; + +export type ActiveAccountStore = { + id: string; + type: ParentAccount; +}; + +export type UtilityStore = ActiveAccountStore | { [id: string]: CryptoRecord }; + export enum DeleteAccountError { BadPassword, KeyStoreError, diff --git a/apps/extension/src/background/ledger/constants.ts b/apps/extension/src/background/ledger/constants.ts new file mode 100644 index 0000000000..57854a916f --- /dev/null +++ b/apps/extension/src/background/ledger/constants.ts @@ -0,0 +1 @@ +export const ROUTE = "ledger-route"; diff --git a/apps/extension/src/background/ledger/handler.ts b/apps/extension/src/background/ledger/handler.ts new file mode 100644 index 0000000000..75d1535220 --- /dev/null +++ b/apps/extension/src/background/ledger/handler.ts @@ -0,0 +1,117 @@ +import { Handler, Env, Message, InternalHandler } from "router"; +import { LedgerService } from "./service"; +import { + AddLedgerAccountMsg, + GetBondBytesMsg, + GetRevealPKBytesMsg, + GetTransferBytesMsg, + SubmitSignedTransferMsg, + SubmitSignedBondMsg, + SubmitSignedRevealPKMsg, +} from "./messages"; + +export const getHandler: (service: LedgerService) => Handler = (service) => { + return (env: Env, msg: Message) => { + switch (msg.constructor) { + case AddLedgerAccountMsg: + return handleAddLedgerAccountMsg(service)( + env, + msg as AddLedgerAccountMsg + ); + case GetRevealPKBytesMsg: + return handleGetRevealPKBytesMsg(service)( + env, + msg as GetRevealPKBytesMsg + ); + + case GetTransferBytesMsg: + return handleGetTransferBytesMsg(service)( + env, + msg as GetTransferBytesMsg + ); + case SubmitSignedRevealPKMsg: + return handleSubmitSignedRevealPKMsg(service)( + env, + msg as SubmitSignedRevealPKMsg + ); + case SubmitSignedTransferMsg: + return handleSubmitSignedTransferMsg(service)( + env, + msg as SubmitSignedTransferMsg + ); + case GetBondBytesMsg: + return handleGetBondBytesMsg(service)(env, msg as GetTransferBytesMsg); + case SubmitSignedBondMsg: + return handleSubmitSignedBondMsg(service)( + env, + msg as SubmitSignedBondMsg + ); + + default: + throw new Error("Unknown msg type"); + } + }; +}; + +const handleAddLedgerAccountMsg: ( + service: LedgerService +) => InternalHandler = (service) => { + return async (_, msg) => { + const { alias, address, publicKey, bip44Path } = msg; + return await service.addAccount(alias, address, publicKey, bip44Path); + }; +}; + +const handleGetTransferBytesMsg: ( + service: LedgerService +) => InternalHandler = (service) => { + return async (_, msg) => { + const { msgId } = msg; + return await service.getTransferBytes(msgId); + }; +}; + +const handleSubmitSignedTransferMsg: ( + service: LedgerService +) => InternalHandler = (service) => { + return async (_, msg) => { + const { bytes, msgId, signatures } = msg; + return await service.submitTransfer(msgId, bytes, signatures); + }; +}; + +const handleGetBondBytesMsg: ( + service: LedgerService +) => InternalHandler = (service) => { + return async (_, msg) => { + const { msgId } = msg; + return await service.getBondBytes(msgId); + }; +}; + +const handleSubmitSignedBondMsg: ( + service: LedgerService +) => InternalHandler = (service) => { + return async (_, msg) => { + const { bytes, msgId, signatures } = msg; + return await service.submitBond(msgId, bytes, signatures); + }; +}; + +const handleGetRevealPKBytesMsg: ( + service: LedgerService +) => InternalHandler = (service) => { + return async (_, msg) => { + const { txMsg } = msg; + return await service.getRevealPKBytes(txMsg); + }; +}; + +const handleSubmitSignedRevealPKMsg: ( + service: LedgerService +) => InternalHandler = (service) => { + return async (_, msg) => { + const { txMsg, bytes, signatures } = msg; + return await service.submitRevealPk(txMsg, bytes, signatures); + }; +}; diff --git a/apps/extension/src/background/ledger/index.ts b/apps/extension/src/background/ledger/index.ts index d1cd2cc47a..f2a87c7999 100644 --- a/apps/extension/src/background/ledger/index.ts +++ b/apps/extension/src/background/ledger/index.ts @@ -1 +1,4 @@ +export * from "./constants"; +export * from "./init"; export * from "./ledger"; +export * from "./service"; diff --git a/apps/extension/src/background/ledger/init.ts b/apps/extension/src/background/ledger/init.ts new file mode 100644 index 0000000000..12eaba6f22 --- /dev/null +++ b/apps/extension/src/background/ledger/init.ts @@ -0,0 +1,27 @@ +import { Router } from "router"; +import { ROUTE } from "./constants"; +import { + AddLedgerAccountMsg, + GetBondBytesMsg, + GetRevealPKBytesMsg, + GetTransferBytesMsg, + SubmitSignedBondMsg, + SubmitSignedRevealPKMsg, + SubmitSignedUnbondMsg, + SubmitSignedTransferMsg, +} from "./messages"; +import { getHandler } from "./handler"; +import { LedgerService } from "./service"; + +export function init(router: Router, service: LedgerService): void { + router.registerMessage(AddLedgerAccountMsg); + router.registerMessage(GetBondBytesMsg); + router.registerMessage(GetRevealPKBytesMsg); + router.registerMessage(GetTransferBytesMsg); + router.registerMessage(SubmitSignedBondMsg); + router.registerMessage(SubmitSignedRevealPKMsg); + router.registerMessage(SubmitSignedUnbondMsg); + router.registerMessage(SubmitSignedTransferMsg); + + router.addHandler(ROUTE, getHandler(service)); +} diff --git a/apps/extension/src/background/ledger/ledger.ts b/apps/extension/src/background/ledger/ledger.ts index f8a9b74329..ce78477aaf 100644 --- a/apps/extension/src/background/ledger/ledger.ts +++ b/apps/extension/src/background/ledger/ledger.ts @@ -1,17 +1,19 @@ +import TransportUSB from "@ledgerhq/hw-transport-webusb"; +import TransportHID from "@ledgerhq/hw-transport-webhid"; +import Transport from "@ledgerhq/hw-transport"; + import { NamadaApp, ResponseAppInfo, ResponseSign, ResponseVersion, -} from "@zondax/ledger-namada"; -import TransportUSB from "@ledgerhq/hw-transport-webusb"; -import TransportHID from "@ledgerhq/hw-transport-webhid"; -import Transport from "@ledgerhq/hw-transport"; - + LedgerError, +} from "@namada/ledger-namada"; import { defaultChainId, chains } from "@namada/chains"; +import { makeBip44Path } from "@namada/utils"; const namadaChain = chains[defaultChainId]; -const bip44CoinType = namadaChain.bip44.coinType; +const { coinType } = namadaChain.bip44; export const initLedgerUSBTransport = async (): Promise => { return await TransportHID.create(); @@ -21,13 +23,14 @@ export const initLedgerHIDTransport = async (): Promise => { return await TransportUSB.create(); }; -export const DEFAULT_LEDGER_BIP44_PATH = `m/44'/${bip44CoinType}'/0'/0/0`; +export const DEFAULT_LEDGER_BIP44_PATH = makeBip44Path(coinType, { + account: 0, + change: 0, + index: 0, +}); export class Ledger { - public version: ResponseVersion | undefined; - public info: ResponseAppInfo | undefined; - - constructor(public readonly namadaApp: NamadaApp | undefined = undefined) {} + constructor(public readonly namadaApp: NamadaApp | undefined = undefined) { } /** * Returns an initialized Ledger class instance with initialized Transport @@ -38,79 +41,92 @@ export class Ledger { const namadaApp = new NamadaApp(initializedTransport); const ledger = new Ledger(namadaApp); - ledger.version = await namadaApp.getVersion(); - ledger.info = await namadaApp.getAppInfo(); - return ledger; } /** * Return status and version info of initialized NamadaApp */ - public status(): { - appName: string; - appVersion: string; - errorMessage: string; - returnCode: number; - deviceLocked: boolean; - major: number; - minor: number; - patch: number; - targetId: string; - testMode: boolean; - } { - if (!this.version || !this.info) { + public async status(): Promise<{ + version: ResponseVersion; + info: ResponseAppInfo; + }> { + if (!this.namadaApp) { throw new Error("NamadaApp is not initialized!"); } - - const { appName, appVersion, errorMessage, returnCode } = this.info; - const { - major, - minor, - patch, - targetId, - deviceLocked = false, - testMode, - } = this.version; + const version = await this.namadaApp.getVersion(); + const info = await this.namadaApp.getAppInfo(); return { - appName, - appVersion, - errorMessage, - returnCode, - deviceLocked, - major, - minor, - patch, - targetId, - testMode, + version, + info, }; } /** - * Get public key associated with optional path, otherwise, use default path + * Get address and public key associated with optional path, otherwise, use default path */ - public async getPublicKey(bip44Path?: string): Promise { + public async getAddressAndPublicKey( + bip44Path?: string + ): Promise<{ address: string; publicKey: string }> { if (!this.namadaApp) { throw new Error("NamadaApp is not initialized!"); } const path = bip44Path || DEFAULT_LEDGER_BIP44_PATH; - const { publicKey } = await this.namadaApp.getAddressAndPubKey(path); + const { address, publicKey } = await this.namadaApp.getAddressAndPubKey( + path + ); - return publicKey.toString("hex"); + return { + // Return address as bech32-encoded string + address: address.toString(), + // Return public key as hex-encoded string + publicKey: publicKey.toString("hex"), + }; } /** * Sign tx bytes with the key associated with provided (or default) path + * + * @async + * @param {Uint8Array} tx - tx data blob to sign + * @param {string} bip44Path (optional) - Bip44 path for signing account + * + * @returns {ResponseSign} */ - public async sign(tx: ArrayBuffer, bip44Path: string): Promise { + public async sign(tx: Uint8Array, bip44Path?: string): Promise { if (!this.namadaApp) { throw new Error("NamadaApp is not initialized!"); } - + const buffer = Buffer.from(tx); const path = bip44Path || DEFAULT_LEDGER_BIP44_PATH; - return await this.namadaApp.sign(path, Buffer.from(tx)); + return await this.namadaApp.sign(path, buffer); + } + + /** + * Query status to determine if device has thrown an error + */ + public async queryErrors(): Promise { + const { + info: { returnCode, errorMessage }, + } = await this.status(); + + if (returnCode !== LedgerError.NoErrors) { + return errorMessage; + } + return ""; + } + + /** + * Close the initialized transport, which may be needed if Ledger needs to be reinitialized due to error state + */ + public async closeTransport(): Promise { + if (!this.namadaApp) { + throw new Error("NamadaApp is not initialized!"); + } + + return await this.namadaApp.transport.close(); } } diff --git a/apps/extension/src/background/ledger/messages.ts b/apps/extension/src/background/ledger/messages.ts new file mode 100644 index 0000000000..7b485755b2 --- /dev/null +++ b/apps/extension/src/background/ledger/messages.ts @@ -0,0 +1,293 @@ +import { ResponseSign } from "@namada/ledger-namada"; +import { Bip44Path } from "@namada/types"; + +import { Message } from "router"; +import { ROUTE } from "./constants"; + +enum MessageType { + AddLedgerAccount = "add-ledger-account", + + // Reveal PK + GetRevealPKBytes = "get-reveal-pk-bytes", + SubmitSignedRevealPK = "submit-signed-reveal-pk", + + // Transfers + GetTransferBytes = "get-transfer-bytes", + SubmitSignedTransfer = "submit-signed-transfer", + + // Bonds + GetBondBytes = "get-bond-bytes", + SubmitSignedBond = "submit-signed-bond", + SubmitSignedUnbond = "submit-signed-unbond", +} + +export class AddLedgerAccountMsg extends Message { + public static type(): MessageType { + return MessageType.AddLedgerAccount; + } + + constructor( + public readonly alias: string, + public readonly address: string, + public readonly publicKey: string, + public readonly bip44Path: Bip44Path + ) { + super(); + } + + validate(): void { + if (!this.alias) { + throw new Error("Alias must not be empty!"); + } + + if (!this.address) { + throw new Error("Address was not provided!"); + } + + if (!this.publicKey) { + throw new Error("Public key was not provided!"); + } + + if (!this.bip44Path) { + throw new Error("BIP44 Path was not provided!"); + } + } + + route(): string { + return ROUTE; + } + + type(): string { + return AddLedgerAccountMsg.type(); + } +} + +export class GetRevealPKBytesMsg extends Message<{ + bytes: Uint8Array; + path: string; +}> { + public static type(): MessageType { + return MessageType.GetRevealPKBytes; + } + + constructor(public readonly txMsg: string) { + super(); + } + + validate(): void { + if (!this.txMsg) { + throw new Error("txMsg was not provided!"); + } + } + + route(): string { + return ROUTE; + } + + type(): string { + return GetRevealPKBytesMsg.type(); + } +} + +export class GetTransferBytesMsg extends Message<{ + bytes: Uint8Array; + path: string; +}> { + public static type(): MessageType { + return MessageType.GetTransferBytes; + } + + constructor(public readonly msgId: string) { + super(); + } + + validate(): void { + if (!this.msgId) { + throw new Error("Transfer Tx msgId was not provided!"); + } + } + + route(): string { + return ROUTE; + } + + type(): string { + return GetTransferBytesMsg.type(); + } +} + +export class GetBondBytesMsg extends Message<{ + bytes: Uint8Array; + path: string; +}> { + public static type(): MessageType { + return MessageType.GetBondBytes; + } + + constructor(public readonly msgId: string) { + super(); + } + + validate(): void { + if (!this.msgId) { + throw new Error("Bond Tx msgId was not provided!"); + } + } + + route(): string { + return ROUTE; + } + + type(): string { + return GetBondBytesMsg.type(); + } +} + +export class SubmitSignedRevealPKMsg extends Message { + public static type(): MessageType { + return MessageType.SubmitSignedRevealPK; + } + + constructor( + public readonly txMsg: string, + public readonly bytes: string, + public readonly signatures: ResponseSign + ) { + super(); + } + + validate(): void { + if (!this.txMsg) { + throw new Error("txMsg was not provided!"); + } + + if (!this.bytes) { + throw new Error("bytes were not provided!"); + } + + if (!this.signatures) { + throw new Error("No signatures were provided!"); + } + } + + route(): string { + return ROUTE; + } + + type(): string { + return SubmitSignedRevealPKMsg.type(); + } +} + +export class SubmitSignedTransferMsg extends Message { + public static type(): MessageType { + return MessageType.SubmitSignedTransfer; + } + + constructor( + public readonly msgId: string, + public readonly bytes: string, + public readonly signatures: ResponseSign + ) { + super(); + } + + validate(): void { + if (!this.msgId) { + throw new Error("msgId was not provided!"); + } + + if (!this.bytes) { + throw new Error("bytes were not provided!"); + } + + if (!this.signatures) { + throw new Error("No signatures were provided!"); + } + } + + route(): string { + return ROUTE; + } + + type(): string { + return SubmitSignedTransferMsg.type(); + } +} + +export class SubmitSignedBondMsg extends Message { + public static type(): MessageType { + return MessageType.SubmitSignedBond; + } + + constructor( + public readonly msgId: string, + public readonly bytes: string, + public readonly signatures: ResponseSign + ) { + super(); + } + + validate(): void { + if (!this.msgId) { + throw new Error("msgId was not provided!"); + } + + if (!this.bytes) { + throw new Error("bytes were not provided!"); + } + + if (!this.signatures) { + throw new Error("No signatures were provided!"); + } + } + + route(): string { + return ROUTE; + } + + type(): string { + return SubmitSignedBondMsg.type(); + } +} + +export class SubmitSignedUnbondMsg extends Message { + public static type(): MessageType { + return MessageType.SubmitSignedUnbond; + } + + constructor( + public readonly msgId: string, + public readonly bytes: string, + public readonly signatures: ResponseSign, + public readonly publicKey: string + ) { + super(); + } + + validate(): void { + if (!this.msgId) { + throw new Error("msgId was not provided!"); + } + + if (!this.bytes) { + throw new Error("bytes were not provided!"); + } + + if (!this.signatures) { + throw new Error("No signatures were provided!"); + } + + if (!this.publicKey) { + throw new Error("No publicKey provided!"); + } + } + + route(): string { + return ROUTE; + } + + type(): string { + return SubmitSignedUnbondMsg.type(); + } +} diff --git a/apps/extension/src/background/ledger/service.ts b/apps/extension/src/background/ledger/service.ts new file mode 100644 index 0000000000..5471108126 --- /dev/null +++ b/apps/extension/src/background/ledger/service.ts @@ -0,0 +1,331 @@ +import { fromBase64 } from "@cosmjs/encoding"; +import { deserialize } from "@dao-xyz/borsh"; + +import { + AccountType, + Bip44Path, + SubmitBondMsgValue, + SubmitRevealPKMsgValue, + TransferMsgValue, +} from "@namada/types"; +import { ResponseSign } from "@namada/ledger-namada"; +import { Sdk } from "@namada/shared"; +import { IStore, KVStore, Store } from "@namada/storage"; +import { chains } from "@namada/chains"; +import { makeBip44Path } from "@namada/utils"; + +import { + AccountStore, + KeyRingService, + TabStore, + syncTabs, +} from "background/keyring"; +import { generateId } from "utils"; +import { ExtensionRequester } from "extension"; +import { Ports } from "router"; +import { UpdatedStakingEventMsg } from "content/events"; + +export const LEDGERSTORE_KEY = "ledger-store"; +const UUID_NAMESPACE = "be9fdaee-ffa2-11ed-8ef1-325096b39f47"; + +export class LedgerService { + private _ledgerStore: IStore; + + constructor( + protected readonly keyring: KeyRingService, + protected readonly kvStore: KVStore, + protected readonly connectedTabsStore: KVStore, + protected readonly txStore: KVStore, + protected readonly chainId: string, + protected readonly sdk: Sdk, + protected readonly requester: ExtensionRequester + ) { + this._ledgerStore = new Store(LEDGERSTORE_KEY, kvStore); + } + + async getRevealPKBytes( + txMsg: string + ): Promise<{ bytes: Uint8Array; path: string }> { + const { coinType } = chains[this.chainId].bip44; + + try { + // Deserialize txMsg to retrieve source + const { publicKey } = deserialize( + Buffer.from(fromBase64(txMsg)), + SubmitRevealPKMsgValue + ); + + // Query account from Ledger storage to determine path for signer + const account = await this._ledgerStore.getRecord("publicKey", publicKey); + + if (!account) { + throw new Error(`Ledger account not found for ${publicKey}`); + } + + const bytes = await this.sdk.build_reveal_pk(fromBase64(txMsg)); + const path = makeBip44Path(coinType, account.path); + + return { bytes, path }; + } catch (e) { + console.warn(e); + throw new Error(`${e}`); + } + } + + async submitRevealPk( + txMsg: string, + bytes: string, + signatures: ResponseSign + ): Promise { + const { + wrapperSignature: { + raw: { data: wrapperSig }, + }, + rawSignature: { + raw: { data: rawSig }, + }, + // TODO: We need the correct type updated in ResponseSign + } = signatures as any; // eslint-disable-line + + if (!wrapperSig) { + throw new Error("No wrapper signature was produced!"); + } + + if (!rawSig) { + throw new Error("No raw signature was produced!"); + } + + try { + await this.sdk.submit_signed_reveal_pk( + fromBase64(txMsg), + fromBase64(bytes), + new Uint8Array(wrapperSig), + new Uint8Array(rawSig) + ); + } catch (e) { + console.warn(e); + } + } + + async getTransferBytes( + msgId: string + ): Promise<{ bytes: Uint8Array; path: string }> { + const txMsg = await this.txStore.get(msgId); + const { coinType } = chains[this.chainId].bip44; + + if (!txMsg) { + throw new Error(`Transaction ${msgId} not found!`); + } + + try { + // Deserialize txMsg to retrieve source + const { source } = deserialize( + Buffer.from(fromBase64(txMsg)), + TransferMsgValue + ); + + // Query account from Ledger storage to determine path for signer + const account = await this._ledgerStore.getRecord("address", source); + + if (!account) { + throw new Error(`Ledger account not found for ${source}`); + } + + const bytes = await this.sdk.build_transfer(fromBase64(txMsg)); + const path = makeBip44Path(coinType, account.path); + + return { bytes, path }; + } catch (e) { + console.warn(e); + throw new Error(`${e}`); + } + } + + async submitTransfer( + msgId: string, + bytes: string, + signatures: ResponseSign + ): Promise { + const txMsg = await this.txStore.get(msgId); + + if (!txMsg) { + throw new Error(`Transaction ${msgId} not found!`); + } + + const { + wrapperSignature: { + raw: { data: wrapperSig }, + }, + rawSignature: { + raw: { data: rawSig }, + }, + // TODO: We need the correct type updated in ResponseSign + } = signatures as any; // eslint-disable-line + + if (!wrapperSig) { + throw new Error("No wrapper signature was produced!"); + } + + if (!rawSig) { + throw new Error("No raw signature was produced!"); + } + + try { + await this.sdk.submit_signed_transfer( + fromBase64(txMsg), + fromBase64(bytes), + new Uint8Array(wrapperSig), + new Uint8Array(rawSig) + ); + + // Clear pending tx if successful + await this.txStore.set(msgId, null); + } catch (e) { + console.warn(e); + } + } + + async getBondBytes( + msgId: string + ): Promise<{ bytes: Uint8Array; path: string }> { + const txMsg = await this.txStore.get(msgId); + + if (!txMsg) { + console.warn(`txMsg not found for msgId: ${msgId}`); + throw new Error(`Transfer Transaction ${msgId} not found!`); + } + + const { coinType } = chains[this.chainId].bip44; + + try { + // Deserialize txMsg to retrieve source + const { source } = deserialize( + Buffer.from(fromBase64(txMsg)), + SubmitBondMsgValue + ); + + // Query account from Ledger storage to determine path for signer + const account = await this._ledgerStore.getRecord("address", source); + + if (!account) { + throw new Error(`Ledger account not found for ${source}`); + } + + const bytes = await this.sdk.build_bond(fromBase64(txMsg)); + const path = makeBip44Path(coinType, account.path); + + return { bytes, path }; + } catch (e) { + console.warn(e); + throw new Error(`${e}`); + } + } + + /* Submit a bond with provided signatures */ + async submitBond( + msgId: string, + bytes: string, + signatures: ResponseSign + ): Promise { + const txMsg = await this.txStore.get(msgId); + + if (!txMsg) { + throw new Error(`Bond Transaction ${msgId} not found!`); + } + + const { + wrapperSignature: { + raw: { data: wrapperSig }, + }, + rawSignature: { + raw: { data: rawSig }, + }, + // TODO: We need the correct type updated in ResponseSign + } = signatures as any; // eslint-disable-line + + if (!wrapperSig) { + throw new Error("No wrapper signature was produced!"); + } + + if (!rawSig) { + throw new Error("No raw signature was produced!"); + } + + try { + await this.sdk.submit_signed_bond( + fromBase64(txMsg), + fromBase64(bytes), + new Uint8Array(wrapperSig), + new Uint8Array(rawSig) + ); + + await this.broadcastUpdateStaking(); + + // Clear pending tx if successful + await this.txStore.set(msgId, null); + } catch (e) { + console.warn(e); + } + + await this.keyring.broadcastUpdateBalance(); + } + + /** + * Append a new address record for use with Ledger + */ + async addAccount( + alias: string, + address: string, + publicKey: string, + bip44Path: Bip44Path + ): Promise { + // Check if account exists in storage, return if so: + const record = await this._ledgerStore.getRecord("address", address); + if (record) { + return; + } + + // Generate a UUID v5 unique id from alias & path + const id = generateId(UUID_NAMESPACE, alias, address); + + const account = { + id, + alias, + address, + publicKey, + owner: address, + chainId: this.chainId, + path: bip44Path, + type: AccountType.Ledger, + }; + await this._ledgerStore.append(account); + + // Prepare SDK store + this.sdk.clear_storage(); + await this.keyring.initSdkStore(id); + + // Set active account ID + await this.keyring.setActiveAccount(id, AccountType.Ledger); + } + + async broadcastUpdateStaking(): Promise { + const tabs = await syncTabs( + this.connectedTabsStore, + this.requester, + this.chainId + ); + try { + tabs?.forEach(({ tabId }: TabStore) => { + this.requester.sendMessageToTab( + tabId, + Ports.WebBrowser, + new UpdatedStakingEventMsg(this.chainId) + ); + }); + } catch (e) { + console.warn(e); + } + + return; + } +} diff --git a/apps/extension/src/background/web-workers/submit-transfer-web-worker.ts b/apps/extension/src/background/web-workers/submit-transfer-web-worker.ts index eeeb9a09c4..8975e095bd 100644 --- a/apps/extension/src/background/web-workers/submit-transfer-web-worker.ts +++ b/apps/extension/src/background/web-workers/submit-transfer-web-worker.ts @@ -10,6 +10,7 @@ import { TRANSFER_FAILED_MSG, TRANSFER_SUCCESSFUL_MSG, } from "./types"; +import { ActiveAccountStore } from "background/keyring"; (async function init() { await initShared(); @@ -22,10 +23,12 @@ import { const sdkData: Record | undefined = await sdkStore.get( "sdk-store" ); - const activeAccount = await utilityStore.get("parent-account-id"); + const activeAccount = await utilityStore.get( + "parent-account-id" + ); if (sdkData && activeAccount) { - const data = new TextEncoder().encode(sdkData[activeAccount]); + const data = new TextEncoder().encode(sdkData[activeAccount.id]); sdk.decode(data); } diff --git a/apps/extension/src/content/events.ts b/apps/extension/src/content/events.ts index afcbb94863..6fe26c4ae3 100644 --- a/apps/extension/src/content/events.ts +++ b/apps/extension/src/content/events.ts @@ -102,11 +102,36 @@ export class UpdatedBalancesEventMsg extends Message { } } +export class UpdatedStakingEventMsg extends Message { + public static type(): Events { + return Events.UpdatedStaking; + } + + constructor(public readonly chainId: string) { + super(); + } + + validate(): void { + if (!this.chainId) { + throw new Error("chainId must not be empty"); + } + } + + route(): string { + return Routes.InteractionForeground; + } + + type(): string { + return UpdatedBalancesEventMsg.type(); + } +} + export function initEvents(router: Router): void { router.registerMessage(AccountChangedEventMsg); router.registerMessage(TransferStartedEvent); router.registerMessage(TransferCompletedEvent); router.registerMessage(UpdatedBalancesEventMsg); + router.registerMessage(UpdatedStakingEventMsg); router.addHandler(Routes.InteractionForeground, (_, msg) => { const clonedMsg = @@ -135,6 +160,9 @@ export function initEvents(router: Router): void { case UpdatedBalancesEventMsg: window.dispatchEvent(new CustomEvent(Events.UpdatedBalances)); break; + case UpdatedStakingEventMsg: + window.dispatchEvent(new CustomEvent(Events.UpdatedStaking)); + break; default: throw new Error("Unknown msg type"); } diff --git a/apps/extension/src/provider/InjectedNamada.ts b/apps/extension/src/provider/InjectedNamada.ts index dc4acca6de..9536c5b75e 100644 --- a/apps/extension/src/provider/InjectedNamada.ts +++ b/apps/extension/src/provider/InjectedNamada.ts @@ -1,14 +1,15 @@ import { - Namada as INamada, + AccountType, Chain, DerivedAccount, + Namada as INamada, Signer as ISigner, } from "@namada/types"; import { InjectedProxy } from "./InjectedProxy"; import { Signer } from "./Signer"; export class InjectedNamada implements INamada { - constructor(private readonly _version: string) {} + constructor(private readonly _version: string) { } public async connect(chainId: string): Promise { return await InjectedProxy.requestMethod("connect", chainId); @@ -49,8 +50,20 @@ export class InjectedNamada implements INamada { return new Signer(chainId, this); } - public async submitBond(txMsg: string): Promise { - return await InjectedProxy.requestMethod("submitBond", txMsg); + public async submitBond(props: { + txMsg: string; + type: AccountType; + publicKey?: string; + }): Promise { + const { txMsg, type, publicKey } = props; + return await InjectedProxy.requestMethod< + { txMsg: string; type: AccountType; publicKey?: string }, + void + >("submitBond", { + txMsg, + type, + publicKey, + }); } public async submitUnbond(txMsg: string): Promise { @@ -60,11 +73,18 @@ export class InjectedNamada implements INamada { ); } - public async submitTransfer(txMsg: string): Promise { - return await InjectedProxy.requestMethod( - "submitTransfer", - txMsg - ); + public async submitTransfer(props: { + txMsg: string; + type: AccountType; + }): Promise { + const { txMsg, type } = props; + return await InjectedProxy.requestMethod< + { txMsg: string; type: AccountType }, + void + >("submitTransfer", { + txMsg, + type, + }); } public async submitIbcTransfer(txMsg: string): Promise { diff --git a/apps/extension/src/provider/Namada.test.ts b/apps/extension/src/provider/Namada.test.ts index a2a90bd329..cc035f321e 100644 --- a/apps/extension/src/provider/Namada.test.ts +++ b/apps/extension/src/provider/Namada.test.ts @@ -4,6 +4,7 @@ import BigNumber from "bignumber.js"; import { AccountMsgValue, + AccountType, IbcTransferMsgValue, IbcTransferProps, Message, @@ -16,7 +17,7 @@ import { import { KVKeys } from "router"; import { init, KVStoreMock } from "test/init"; import { chains as defaultChains } from "@namada/chains"; -import { chain, keyStore, password, ACTIVE_ACCOUNT_ID } from "./data.mock"; +import { chain, keyStore, password, ACTIVE_ACCOUNT } from "./data.mock"; import { KeyRing, KeyRingService, @@ -68,9 +69,9 @@ describe("Namada", () => { it("should return all accounts", async () => { iDBStore.set(KEYSTORE_KEY, keyStore); - utilityStore.set(PARENT_ACCOUNT_ID_KEY, ACTIVE_ACCOUNT_ID); + utilityStore.set(PARENT_ACCOUNT_ID_KEY, ACTIVE_ACCOUNT); const storedKeyStore = keyStore.map( - ({ crypto: _crypto, owner: _owner, ...account }) => account + ({ crypto: _crypto, ...account }) => account ); const storedAccounts = await namada.accounts(chain.chainId); @@ -108,7 +109,10 @@ describe("Namada", () => { const serializedTransfer = transferMessage.encode(transferMsgValue); jest.spyOn(keyRingService, "submitTransfer"); - namada.submitTransfer(toBase64(serializedTransfer)); + namada.submitTransfer({ + txMsg: toBase64(serializedTransfer), + type: AccountType.PrivateKey, + }); expect(keyRingService.submitTransfer).toBeCalled(); }); diff --git a/apps/extension/src/provider/Namada.ts b/apps/extension/src/provider/Namada.ts index 7551af508f..13b9211d6a 100644 --- a/apps/extension/src/provider/Namada.ts +++ b/apps/extension/src/provider/Namada.ts @@ -1,8 +1,15 @@ -import { Namada as INamada, Chain, DerivedAccount } from "@namada/types"; +import { + AccountType, + Namada as INamada, + Chain, + DerivedAccount, +} from "@namada/types"; import { Ports, MessageRequester } from "router"; import { ApproveTransferMsg, + ApproveBondMsg, + ApproveUnbondMsg, ConnectInterfaceMsg, GetChainMsg, GetChainsMsg, @@ -12,8 +19,6 @@ import { FetchAndStoreMaspParamsMsg, HasMaspParamsMsg, QueryBalancesMsg, - SubmitBondMsg, - SubmitUnbondMsg, SubmitIbcTransferMsg, } from "./messages"; @@ -21,7 +26,7 @@ export class Namada implements INamada { constructor( private readonly _version: string, protected readonly requester?: MessageRequester - ) {} + ) { } public async connect(chainId: string): Promise { return await this.requester?.sendMessage( @@ -83,24 +88,33 @@ export class Namada implements INamada { ); } - public async submitBond(txMsg: string): Promise { + public async submitBond(props: { + txMsg: string; + type: AccountType; + publicKey?: string; + }): Promise { + const { txMsg, type, publicKey } = props; return await this.requester?.sendMessage( Ports.Background, - new SubmitBondMsg(txMsg) + new ApproveBondMsg(txMsg, type, publicKey) ); } public async submitUnbond(txMsg: string): Promise { return await this.requester?.sendMessage( Ports.Background, - new SubmitUnbondMsg(txMsg) + new ApproveUnbondMsg(txMsg) ); } - public async submitTransfer(txMsg: string): Promise { + public async submitTransfer(props: { + txMsg: string; + type?: AccountType; + }): Promise { + const { txMsg, type } = props; return await this.requester?.sendMessage( Ports.Background, - new ApproveTransferMsg(txMsg) + new ApproveTransferMsg(txMsg, type) ); } diff --git a/apps/extension/src/provider/Signer.ts b/apps/extension/src/provider/Signer.ts index 42d2a45367..9dbd48d277 100644 --- a/apps/extension/src/provider/Signer.ts +++ b/apps/extension/src/provider/Signer.ts @@ -20,14 +20,16 @@ export class Signer implements ISigner { constructor( protected readonly chainId: string, private readonly _namada: Namada - ) {} + ) { } public async accounts(): Promise { return (await this._namada.accounts(this.chainId))?.map( - ({ alias, address, chainId, type }) => ({ + ({ alias, address, chainId, type, publicKey }) => ({ alias, address, chainId, + type, + publicKey, isShielded: type === AccountType.ShieldedKeys, }) ); @@ -36,13 +38,20 @@ export class Signer implements ISigner { /** * Submit bond transaction */ - public async submitBond(args: SubmitBondProps): Promise { + public async submitBond( + args: SubmitBondProps, + type: AccountType, + publicKey?: string + ): Promise { const msgValue = new SubmitBondMsgValue(args); - const msg = new Message(); const encoded = msg.encode(msgValue); - return await this._namada.submitBond(toBase64(encoded)); + return await this._namada.submitBond({ + txMsg: toBase64(encoded), + type, + publicKey, + }); } /** @@ -60,12 +69,18 @@ export class Signer implements ISigner { /** * Submit a transfer */ - public async submitTransfer(args: TransferProps): Promise { + public async submitTransfer( + args: TransferProps, + type: AccountType + ): Promise { const transferMsgValue = new TransferMsgValue(args); const transferMessage = new Message(); const serializedTransfer = transferMessage.encode(transferMsgValue); - return await this._namada.submitTransfer(toBase64(serializedTransfer)); + return await this._namada.submitTransfer({ + txMsg: toBase64(serializedTransfer), + type, + }); } /** diff --git a/apps/extension/src/provider/data.mock.ts b/apps/extension/src/provider/data.mock.ts index 3461151385..adf92f5e1e 100644 --- a/apps/extension/src/provider/data.mock.ts +++ b/apps/extension/src/provider/data.mock.ts @@ -1,7 +1,10 @@ import { AccountType, BridgeType, Chain, Extensions } from "@namada/types"; -import { KdfType, KeyStore } from "background/keyring"; +import { ActiveAccountStore, KdfType, KeyStore } from "background/keyring"; -export const ACTIVE_ACCOUNT_ID = "324bfe0e-cb19-5f1a-9630-9daaaecadabe"; +export const ACTIVE_ACCOUNT: ActiveAccountStore = { + id: "324bfe0e-cb19-5f1a-9630-9daaaecadabe", + type: AccountType.Mnemonic, +}; export const chain: Chain = { alias: "Namada Testnet", @@ -30,7 +33,8 @@ export const keyStore: KeyStore[] = [ owner: "atest1d9khqw36gvurxv6yxcmyvv6pgdzyxwp3g9z5gv6px5cyxs348yenjd6ygse5y32xxapy2dph26clnv", chainId: "namada-75a7e12.69483d59a9fb174", - id: ACTIVE_ACCOUNT_ID, + id: ACTIVE_ACCOUNT.id, + parentId: undefined, path: { account: 0, change: 0, @@ -60,7 +64,8 @@ export const keyStore: KeyStore[] = [ }, }, }, - type: AccountType.Mnemonic, + publicKey: undefined, + type: ACTIVE_ACCOUNT.type, }, { alias: "Derived Account", @@ -70,7 +75,7 @@ export const keyStore: KeyStore[] = [ "atest1d9khqw36geprgd2yxgcyy3fnxymnzwf4xpprgdzxgccryvp489qn2wzyxcmrgd2xxdznvv2p8akmfs", chainId: "namada-75a7e12.69483d59a9fb174", id: "123e4567-e89b-12d3-a456-426614174000", - parentId: ACTIVE_ACCOUNT_ID, + parentId: ACTIVE_ACCOUNT.id, path: { account: 0, change: 0, @@ -101,6 +106,7 @@ export const keyStore: KeyStore[] = [ }, }, }, + publicKey: undefined, type: AccountType.PrivateKey, }, ]; diff --git a/apps/extension/src/provider/messages.ts b/apps/extension/src/provider/messages.ts index 85e53869b6..556f706df0 100644 --- a/apps/extension/src/provider/messages.ts +++ b/apps/extension/src/provider/messages.ts @@ -1,4 +1,4 @@ -import { Chain, DerivedAccount } from "@namada/types"; +import { AccountType, Chain, DerivedAccount } from "@namada/types"; import { Message } from "router"; /** @@ -16,16 +16,17 @@ enum Route { enum MessageType { ConnectInterface = "connect-interface", QueryAccounts = "query-accounts", - ApproveTransfer = "approve-tx", + ApproveTransfer = "approve-transfer", + ApproveBond = "approve-bond", + ApproveUnbond = "approve-unbond", QueryBalances = "query-balances", SubmitIbcTransfer = "submit-ibc-transfer", + SubmitLedgerTransfer = "submit-ledger-transfer", EncodeInitAccount = "encode-init-account", EncodeRevealPublicKey = "encode-reveal-public-key", GetChain = "get-chain", GetChains = "get-chains", SuggestChain = "suggest-chain", - SubmitBond = "submit-bond", - SubmitUnbond = "submit-unbond", FetchAndStoreMaspParams = "fetch-and-store-masp-params", HasMaspParams = "has-masp-params", } @@ -258,62 +259,75 @@ export class EncodeRevealPkMsg extends Message { } } -export class SubmitBondMsg extends Message { +export class ApproveTransferMsg extends Message { public static type(): MessageType { - return MessageType.SubmitBond; + return MessageType.ApproveTransfer; } - constructor(public readonly txMsg: string) { + constructor( + public readonly txMsg: string, + public readonly accountType?: AccountType + ) { super(); } validate(): void { if (!this.txMsg) { - throw new Error("An encoded txMsg is required!"); + throw new Error("txMsg was not provided!"); } return; } route(): string { - return Route.KeyRing; + return Route.Approvals; } type(): string { - return SubmitBondMsg.type(); + return ApproveTransferMsg.type(); } } -export class SubmitUnbondMsg extends Message { +export class ApproveBondMsg extends Message { public static type(): MessageType { - return MessageType.SubmitUnbond; + return MessageType.ApproveBond; } - constructor(public readonly txMsg: string) { + constructor( + public readonly txMsg: string, + public readonly accountType: AccountType, + public readonly publicKey?: string + ) { super(); } validate(): void { if (!this.txMsg) { - throw new Error("An encoded txMsg is required!"); + throw new Error("txMsg was not provided!"); + } + if (!this.accountType) { + throw new Error("accountType was not provided!"); } return; } route(): string { - return Route.KeyRing; + return Route.Approvals; } type(): string { - return SubmitUnbondMsg.type(); + return ApproveBondMsg.type(); } } -export class ApproveTransferMsg extends Message { +export class ApproveUnbondMsg extends Message { public static type(): MessageType { - return MessageType.ApproveTransfer; + return MessageType.ApproveUnbond; } - constructor(public readonly txMsg: string) { + constructor( + public readonly txMsg: string, + public readonly accountType?: AccountType + ) { super(); } @@ -329,7 +343,7 @@ export class ApproveTransferMsg extends Message { } type(): string { - return ApproveTransferMsg.type(); + return ApproveUnbondMsg.type(); } } diff --git a/apps/extension/src/test/init.ts b/apps/extension/src/test/init.ts index acfc57a26f..c8c71493b1 100644 --- a/apps/extension/src/test/init.ts +++ b/apps/extension/src/test/init.ts @@ -1,20 +1,23 @@ +import { chains } from "@namada/chains"; +import { Query, Sdk } from "@namada/shared"; import { KVStore } from "@namada/storage"; +import { Chain } from "@namada/types"; import { ExtensionRouter, ExtensionMessengerMock, ExtensionRequester, getNamadaRouterId, -} from "../extension"; -import { Ports, KVPrefix } from "../router"; -import { chains } from "@namada/chains"; -import { ChainsService, init as initChains } from "../background/chains"; +} from "extension"; +import { Ports, KVPrefix } from "router"; +import { ChainsService, init as initChains } from "background/chains"; import { KeyRingService, init as initKeyRing, KeyStore, TabStore, UtilityStore, + AccountStore, } from "../background/keyring"; import { @@ -23,8 +26,7 @@ import { } from "../background/approvals"; import { Namada } from "provider"; -import { Chain } from "@namada/types"; -import { Query, Sdk } from "@namada/shared"; +import { LedgerService } from "background/ledger"; // __wasm is not exported in crypto.d.ts so need to use require instead of import /* eslint-disable @typescript-eslint/no-var-requires */ @@ -34,7 +36,7 @@ const chainId = "namada-75a7e12.69483d59a9fb174"; export class KVStoreMock implements KVStore { private storage: { [key: string]: T | null } = {}; - constructor(readonly _prefix: string) {} + constructor(readonly _prefix: string) { } get(key: string): Promise { return new Promise((resolve) => { @@ -102,12 +104,22 @@ export const init = async (): Promise<{ cryptoMemory, requester ); + + const ledgerService = new LedgerService( + keyRingService, + iDBStore as KVStore, + connectedTabsStore, + txStore, + chainId, + sdk, + requester + ); + const approvalsService = new ApprovalsService( txStore, connectedTabsStore, keyRingService, - chainId, - requester + ledgerService ); // Initialize messages and handlers diff --git a/apps/extension/src/utils/index.ts b/apps/extension/src/utils/index.ts index 4f9c874c4a..e907fd4c4e 100644 --- a/apps/extension/src/utils/index.ts +++ b/apps/extension/src/utils/index.ts @@ -1,12 +1,50 @@ import browser from "webextension-polyfill"; +import { v5 as uuid } from "uuid"; + +import { DerivedAccount } from "@namada/types"; +import { pick } from "@namada/utils"; +import { AccountStore } from "background/keyring"; /** - * Extension-specific utilities + * Query the current extension tab and close it */ - export const closeCurrentTab = async (): Promise => { const tab = await browser.tabs.getCurrent(); if (tab.id) { browser.tabs.remove(tab.id); } }; + +/** + * Return all unencrypted values from key store + */ +export const getAccountValuesFromStore = ( + accounts: AccountStore[] +): DerivedAccount[] => { + return accounts.map((account) => + pick( + account, + "address", + "alias", + "chainId", + "id", + "owner", + "parentId", + "publicKey", + "path", + "type" + ) + ); +}; + +/** + * Construct unique uuid (v5), passing in an arbitray number of arguments. + * This could be a unique parameter of the object receiving the id, + * or an index based on the number of existing objects in a hierarchy. + */ +export const generateId = ( + namespace: string, + ...args: (number | string)[] +): string => { + return uuid(args.join(":"), namespace); +}; diff --git a/apps/namada-interface/src/App/Staking/NewBondingPosition/NewBondingPosition.tsx b/apps/namada-interface/src/App/Staking/NewBondingPosition/NewBondingPosition.tsx index 87d8a71389..cb352b9b28 100644 --- a/apps/namada-interface/src/App/Staking/NewBondingPosition/NewBondingPosition.tsx +++ b/apps/namada-interface/src/App/Staking/NewBondingPosition/NewBondingPosition.tsx @@ -68,8 +68,11 @@ export const NewBondingPosition = (props: Props): JSX.Element => { const currentBondingPosition = currentBondingPositions.find( (pos) => pos.owner === currentAccount?.details.address ); - const stakedAmount: BigNumber = new BigNumber(currentBondingPosition?.stakedAmount || "0"); - const currentNAMBalance: BigNumber = currentAccount.balance["NAM"] || new BigNumber(0); + const stakedAmount: BigNumber = new BigNumber( + currentBondingPosition?.stakedAmount || "0" + ); + const currentNAMBalance: BigNumber = + currentAccount.balance["NAM"] || new BigNumber(0); const handleAddressChange = ( e: React.ChangeEvent diff --git a/apps/namada-interface/src/services/extensionEvents/handlers/namada.ts b/apps/namada-interface/src/services/extensionEvents/handlers/namada.ts index d577e9f413..c624490aac 100644 --- a/apps/namada-interface/src/services/extensionEvents/handlers/namada.ts +++ b/apps/namada-interface/src/services/extensionEvents/handlers/namada.ts @@ -6,6 +6,7 @@ import { Namada } from "@namada/integrations"; import { addAccounts, fetchBalances } from "slices/accounts"; import { actions as notificationsActions } from "slices/notifications"; import { getToast, Toasts } from "slices/transfers"; +import { fetchValidators } from "slices/StakingAndGovernance/actions"; export const NamadaAccountChangedHandler = (dispatch: Dispatch, integration: Namada) => @@ -26,6 +27,11 @@ export const NamadaUpdatedBalancesHandler = dispatch(fetchBalances()); }; +export const NamadaUpdatedStakingHandler = + (dispatch: Dispatch) => async () => { + dispatch(fetchValidators()); + }; + export const NamadaTransferStartedHandler = (dispatch: Dispatch) => async (event: CustomEventInit) => { const { msgId } = event.detail; diff --git a/apps/namada-interface/src/services/extensionEvents/provider.tsx b/apps/namada-interface/src/services/extensionEvents/provider.tsx index 77dac031bf..54b762d8fc 100644 --- a/apps/namada-interface/src/services/extensionEvents/provider.tsx +++ b/apps/namada-interface/src/services/extensionEvents/provider.tsx @@ -15,6 +15,7 @@ import { NamadaTransferCompletedHandler, NamadaTransferStartedHandler, NamadaUpdatedBalancesHandler, + NamadaUpdatedStakingHandler, KeplrAccountChangedHandler, MetamaskAccountChangedHandler, } from "./handlers"; @@ -33,8 +34,10 @@ export const ExtensionEventsProvider: React.FC = (props): JSX.Element => { namadaIntegration as Namada ); const namadaTransferStartedHandler = NamadaTransferStartedHandler(dispatch); - const namadaTransferCompletedHandler = NamadaTransferCompletedHandler(dispatch); + const namadaTransferCompletedHandler = + NamadaTransferCompletedHandler(dispatch); const namadaUpdatedBalancesHandler = NamadaUpdatedBalancesHandler(dispatch); + const namadaUpdatedStakingHandler = NamadaUpdatedStakingHandler(dispatch); // Keplr handlers const keplrAccountChangedHandler = KeplrAccountChangedHandler( @@ -51,8 +54,12 @@ export const ExtensionEventsProvider: React.FC = (props): JSX.Element => { // Register handlers: useEventListenerOnce(Events.AccountChanged, namadaAccountChangedHandler); useEventListenerOnce(Events.TransferStarted, namadaTransferStartedHandler); - useEventListenerOnce(Events.TransferCompleted, namadaTransferCompletedHandler); + useEventListenerOnce( + Events.TransferCompleted, + namadaTransferCompletedHandler + ); useEventListenerOnce(Events.UpdatedBalances, namadaUpdatedBalancesHandler); + useEventListenerOnce(Events.UpdatedStaking, namadaUpdatedStakingHandler); useEventListenerOnce(KeplrEvents.AccountChanged, keplrAccountChangedHandler); useEventListenerOnce( MetamaskEvents.AccountChanged, diff --git a/apps/namada-interface/src/slices/StakingAndGovernance/actions.ts b/apps/namada-interface/src/slices/StakingAndGovernance/actions.ts index 12fb09c418..32835a6f58 100644 --- a/apps/namada-interface/src/slices/StakingAndGovernance/actions.ts +++ b/apps/namada-interface/src/slices/StakingAndGovernance/actions.ts @@ -44,9 +44,9 @@ const toMyValidators = ( index == -1 ? (arr: MyValidators[]) => arr : (arr: MyValidators[], idx: number) => [ - ...arr.slice(0, idx), - ...arr.slice(idx + 1), - ]; + ...arr.slice(0, idx), + ...arr.slice(idx + 1), + ]; const stakedAmount = new BigNumber(stake) .plus(new BigNumber(v?.stakedAmount || 0)) @@ -159,20 +159,30 @@ export const postNewBonding = createAsyncThunk< { state: RootState } >(POST_NEW_STAKING, async (change, thunkApi) => { const { chainId } = thunkApi.getState().settings; + const { derived } = thunkApi.getState().accounts; const integration = getIntegration(chainId); const signer = integration.signer() as Signer; - await signer.submitBond({ - source: change.owner, - validator: change.validatorId, - amount: new BigNumber(change.amount), - nativeToken: Tokens.NAM.address || "", - tx: { - token: Tokens.NAM.address || "", - feeAmount: new BigNumber(0), - gasLimit: new BigNumber(0), - chainId, + const { owner, validatorId, amount } = change; + const account = derived[chainId][owner]; + const { type, publicKey } = account.details; + + await signer.submitBond( + { + source: owner, + validator: validatorId, + amount: new BigNumber(amount), + nativeToken: Tokens.NAM.address || "", + tx: { + token: Tokens.NAM.address || "", + feeAmount: new BigNumber(0), + gasLimit: new BigNumber(0), + chainId, + publicKey, + }, }, - }); + type, + publicKey + ); }); // we post an unstake transaction diff --git a/apps/namada-interface/src/slices/accounts.ts b/apps/namada-interface/src/slices/accounts.ts index bbc32b4b74..21e8c3dff8 100644 --- a/apps/namada-interface/src/slices/accounts.ts +++ b/apps/namada-interface/src/slices/accounts.ts @@ -80,7 +80,8 @@ const accountsSlice = createSlice({ state.derived[accounts[0].chainId] = {}; accounts.forEach((account) => { - const { address, alias, isShielded, chainId } = account; + const { address, alias, isShielded, chainId, type, publicKey } = + account; const currencySymbol = chains[chainId].currency.symbol; if (!state.derived[chainId]) { state.derived[chainId] = {}; @@ -91,6 +92,8 @@ const accountsSlice = createSlice({ address, alias, chainId, + type, + publicKey, isShielded, }, balance: { diff --git a/apps/namada-interface/src/slices/transfers.ts b/apps/namada-interface/src/slices/transfers.ts index 5a311eb87d..f932540524 100644 --- a/apps/namada-interface/src/slices/transfers.ts +++ b/apps/namada-interface/src/slices/transfers.ts @@ -155,19 +155,23 @@ export const submitTransferTransaction = createAsyncThunk< const integration = getIntegration(chainId); const signer = integration.signer() as Signer; - await signer.submitTransfer({ - tx: { + await signer.submitTransfer( + { + tx: { + token: Tokens.NAM.address || "", + feeAmount: new BigNumber(0), + gasLimit: new BigNumber(0), + chainId, + publicKey: txTransferArgs.account.publicKey, + }, + source: txTransferArgs.account.address, + target: txTransferArgs.target, token: Tokens.NAM.address || "", - feeAmount: new BigNumber(0), - gasLimit: new BigNumber(0), - chainId, + amount: txTransferArgs.amount, + nativeToken: Tokens.NAM.address || "", }, - source: txTransferArgs.account.address, - target: txTransferArgs.target, - token: Tokens.NAM.address || "", - amount: txTransferArgs.amount, - nativeToken: Tokens.NAM.address || "", - }); + txTransferArgs.account.type + ); } ); diff --git a/apps/namada-interface/src/store/mocks.ts b/apps/namada-interface/src/store/mocks.ts index 73dca8884d..a7b8acc4bb 100644 --- a/apps/namada-interface/src/store/mocks.ts +++ b/apps/namada-interface/src/store/mocks.ts @@ -1,5 +1,6 @@ import BigNumber from "bignumber.js"; +import { AccountType } from "@namada/types"; import { RootState } from "./store"; import { TransferType } from "slices/transfers"; import { StakingOrUnstakingState } from "slices/StakingAndGovernance"; @@ -9,20 +10,21 @@ export const mockAppState: RootState = { derived: { "namada-masp-1.5.32ccad5356012a7": { atest1v4ehgw36xqcyz3zrxsenzd3kxsunsvzzxymyywpkg4zrjv2pxepyyd3cgse5gwzxgsm5x3zrkf2pwp: - { - details: { - chainId: "namada-masp-1.5.32ccad5356012a7", - alias: "Namada", - address: - "atest1v4ehgw36xqcyz3zrxsenzd3kxsunsvzzxymyywpkg4zrjv2pxepyyd3cgse5gwzxgsm5x3zrkf2pwp", - isShielded: false, - }, - balance: { - NAM: new BigNumber(1000), - ATOM: new BigNumber(1000), - ETH: new BigNumber(1000), - }, + { + details: { + chainId: "namada-masp-1.5.32ccad5356012a7", + alias: "Namada", + address: + "atest1v4ehgw36xqcyz3zrxsenzd3kxsunsvzzxymyywpkg4zrjv2pxepyyd3cgse5gwzxgsm5x3zrkf2pwp", + isShielded: false, + type: AccountType.PrivateKey, }, + balance: { + NAM: new BigNumber(1000), + ATOM: new BigNumber(1000), + ETH: new BigNumber(1000), + }, + }, }, "namada-test.1e670ba91369ec891fc": { "39UL18": { @@ -31,6 +33,7 @@ export const mockAppState: RootState = { alias: "Namada", address: "atest1v4ehgw36xqcyz3zrxsenzd3kxsunsvzzxymyywpkg4zrjv2pxepyyd3cgse5gwzxgsm5x3zrkf2pwp", + type: AccountType.PrivateKey, isShielded: false, }, balance: { @@ -46,6 +49,7 @@ export const mockAppState: RootState = { chainId: "namada-test.89060614ce340f4baae", alias: "Namada", address: "L1qDtV8TRwYLSHdMDW518hgRw9nWnRjFTenkcBYNJruyYoLjaj8F", + type: AccountType.PrivateKey, isShielded: false, }, diff --git a/packages/integrations/src/Keplr.test.ts b/packages/integrations/src/Keplr.test.ts index c325179464..cfc0760a2f 100644 --- a/packages/integrations/src/Keplr.test.ts +++ b/packages/integrations/src/Keplr.test.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { mock } from "jest-mock-extended"; -import { Chain } from "@namada/types"; +import { AccountType, Chain } from "@namada/types"; import { Key, Keplr as IKeplr } from "@keplr-wallet/types"; import Keplr from "./Keplr"; @@ -131,6 +131,7 @@ describe("Keplr class", () => { alias: `${a}...${a}`, chainId: mockChain.chainId, address: a, + type: AccountType.PrivateKey, isShielded: false, })) ); diff --git a/packages/integrations/src/Keplr.ts b/packages/integrations/src/Keplr.ts index 53f9d76383..c2f5d740ba 100644 --- a/packages/integrations/src/Keplr.ts +++ b/packages/integrations/src/Keplr.ts @@ -12,7 +12,13 @@ import { import { Coin } from "@cosmjs/launchpad"; import Long from "long"; -import { Account, Chain, CosmosTokens, TokenBalance } from "@namada/types"; +import { + Account, + AccountType, + Chain, + CosmosTokens, + TokenBalance, +} from "@namada/types"; import { shortenAddress } from "@namada/utils"; import { BridgeProps, Integration } from "./types/Integration"; @@ -35,7 +41,7 @@ class Keplr implements Integration { * override keplr instance for testing * @param chain */ - constructor(public readonly chain: Chain) {} + constructor(public readonly chain: Chain) { } private init(): void { if (!this._keplr) { @@ -116,6 +122,7 @@ class Keplr implements Integration { alias: shortenAddress(account.address, 16), chainId: this.chain.chainId, address: account.address, + type: AccountType.PrivateKey, isShielded: false, }) ); diff --git a/packages/integrations/src/Metamask.ts b/packages/integrations/src/Metamask.ts index 08a0e4c368..24b31ca159 100644 --- a/packages/integrations/src/Metamask.ts +++ b/packages/integrations/src/Metamask.ts @@ -1,7 +1,7 @@ import { type MetaMaskInpageProvider } from "@metamask/providers"; import MetaMaskSDK from "@metamask/sdk"; -import { Account, Chain, TokenBalance } from "@namada/types"; +import { Account, AccountType, Chain, TokenBalance } from "@namada/types"; import { shortenAddress } from "@namada/utils"; import { BridgeProps, Integration } from "./types/Integration"; @@ -15,7 +15,7 @@ type MetamaskWindow = Window & class Metamask implements Integration { private _ethereum: MetaMaskInpageProvider | undefined; - constructor(public readonly chain: Chain) {} + constructor(public readonly chain: Chain) { } private init(): void { if ((window).ethereum) { @@ -48,6 +48,7 @@ class Metamask implements Integration { address, alias: shortenAddress(address, 16), chainId: this.chain.chainId, + type: AccountType.PrivateKey, isShielded: false, })); diff --git a/packages/ledger-namada/LICENSE b/packages/ledger-namada/LICENSE new file mode 100644 index 0000000000..9259c4d1e9 --- /dev/null +++ b/packages/ledger-namada/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018 - 2022 Zondax AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/ledger-namada/README.md b/packages/ledger-namada/README.md new file mode 100644 index 0000000000..4171ca0725 --- /dev/null +++ b/packages/ledger-namada/README.md @@ -0,0 +1,12 @@ +# @zondax/ledger-namada + +[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) +[![npm version](https://badge.fury.io/js/%40zondax%2Fledger-namada.svg)](https://badge.fury.io/js/%40zondax%2Fledger-namada) + +This package provides a basic client library to communicate with the Namada App running in a Ledger Nano S/X + +We recommend using the npmjs package in order to receive updates/fixes. + +## Notes + +Use `yarn install` to avoid issues. diff --git a/packages/ledger-namada/dist/common.d.ts b/packages/ledger-namada/dist/common.d.ts new file mode 100644 index 0000000000..8c9bc01661 --- /dev/null +++ b/packages/ledger-namada/dist/common.d.ts @@ -0,0 +1,90 @@ +/// +/** ****************************************************************************** + * (c) 2018 - 2023 Zondax AG + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************* */ +export declare const CHUNK_SIZE = 250; +export declare const PAYLOAD_TYPE: { + INIT: number; + ADD: number; + LAST: number; +}; +export declare const P1_VALUES: { + ONLY_RETRIEVE: number; + SHOW_ADDRESS_IN_DEVICE: number; +}; +export declare const P2_VALUES: { + DEFAULT: number; +}; +export declare const SIGN_VALUES_P2: { + DEFAULT: number; +}; +export declare const ERROR_CODE: { + NoError: number; +}; +export declare const enum SignatureType { + WrapperSignature = 0, + RawSignature = 1 +} +export declare enum LedgerError { + U2FUnknown = 1, + U2FBadRequest = 2, + U2FConfigurationUnsupported = 3, + U2FDeviceIneligible = 4, + U2FTimeout = 5, + Timeout = 14, + NoErrors = 36864, + DeviceIsBusy = 36865, + ErrorDerivingKeys = 26626, + ExecutionError = 25600, + WrongLength = 26368, + EmptyBuffer = 27010, + OutputBufferTooSmall = 27011, + DataIsInvalid = 27012, + ConditionsNotSatisfied = 27013, + TransactionRejected = 27014, + BadKeyHandle = 27264, + InvalidP1P2 = 27392, + InstructionNotSupported = 27904, + AppDoesNotSeemToBeOpen = 28161, + UnknownError = 28416, + SignVerifyError = 28417 +} +export declare const ERROR_DESCRIPTION: { + 1: string; + 2: string; + 3: string; + 4: string; + 5: string; + 14: string; + 36864: string; + 36865: string; + 26626: string; + 25600: string; + 26368: string; + 27010: string; + 27011: string; + 27012: string; + 27013: string; + 27014: string; + 27264: string; + 27392: string; + 27904: string; + 28161: string; + 28416: string; + 28417: string; +}; +export declare function errorCodeToString(statusCode: LedgerError): string; +export declare function processErrorResponse(response: any): any; +export declare function serializePath(path: string): Buffer; diff --git a/packages/ledger-namada/dist/common.js b/packages/ledger-namada/dist/common.js new file mode 100644 index 0000000000..1e9a7a5eca --- /dev/null +++ b/packages/ledger-namada/dist/common.js @@ -0,0 +1,158 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.serializePath = exports.processErrorResponse = exports.errorCodeToString = exports.ERROR_DESCRIPTION = exports.LedgerError = exports.ERROR_CODE = exports.SIGN_VALUES_P2 = exports.P2_VALUES = exports.P1_VALUES = exports.PAYLOAD_TYPE = exports.CHUNK_SIZE = void 0; +/** ****************************************************************************** + * (c) 2018 - 2023 Zondax AG + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************* */ +exports.CHUNK_SIZE = 250; +exports.PAYLOAD_TYPE = { + INIT: 0x00, + ADD: 0x01, + LAST: 0x02, +}; +exports.P1_VALUES = { + ONLY_RETRIEVE: 0x00, + SHOW_ADDRESS_IN_DEVICE: 0x01, +}; +exports.P2_VALUES = { + DEFAULT: 0x00, +}; +// noinspection JSUnusedGlobalSymbols +exports.SIGN_VALUES_P2 = { + DEFAULT: 0x00, +}; +exports.ERROR_CODE = { + NoError: 0x9000, +}; +var LedgerError; +(function (LedgerError) { + LedgerError[LedgerError["U2FUnknown"] = 1] = "U2FUnknown"; + LedgerError[LedgerError["U2FBadRequest"] = 2] = "U2FBadRequest"; + LedgerError[LedgerError["U2FConfigurationUnsupported"] = 3] = "U2FConfigurationUnsupported"; + LedgerError[LedgerError["U2FDeviceIneligible"] = 4] = "U2FDeviceIneligible"; + LedgerError[LedgerError["U2FTimeout"] = 5] = "U2FTimeout"; + LedgerError[LedgerError["Timeout"] = 14] = "Timeout"; + LedgerError[LedgerError["NoErrors"] = 36864] = "NoErrors"; + LedgerError[LedgerError["DeviceIsBusy"] = 36865] = "DeviceIsBusy"; + LedgerError[LedgerError["ErrorDerivingKeys"] = 26626] = "ErrorDerivingKeys"; + LedgerError[LedgerError["ExecutionError"] = 25600] = "ExecutionError"; + LedgerError[LedgerError["WrongLength"] = 26368] = "WrongLength"; + LedgerError[LedgerError["EmptyBuffer"] = 27010] = "EmptyBuffer"; + LedgerError[LedgerError["OutputBufferTooSmall"] = 27011] = "OutputBufferTooSmall"; + LedgerError[LedgerError["DataIsInvalid"] = 27012] = "DataIsInvalid"; + LedgerError[LedgerError["ConditionsNotSatisfied"] = 27013] = "ConditionsNotSatisfied"; + LedgerError[LedgerError["TransactionRejected"] = 27014] = "TransactionRejected"; + LedgerError[LedgerError["BadKeyHandle"] = 27264] = "BadKeyHandle"; + LedgerError[LedgerError["InvalidP1P2"] = 27392] = "InvalidP1P2"; + LedgerError[LedgerError["InstructionNotSupported"] = 27904] = "InstructionNotSupported"; + LedgerError[LedgerError["AppDoesNotSeemToBeOpen"] = 28161] = "AppDoesNotSeemToBeOpen"; + LedgerError[LedgerError["UnknownError"] = 28416] = "UnknownError"; + LedgerError[LedgerError["SignVerifyError"] = 28417] = "SignVerifyError"; +})(LedgerError || (exports.LedgerError = LedgerError = {})); +exports.ERROR_DESCRIPTION = { + [LedgerError.U2FUnknown]: 'U2F: Unknown', + [LedgerError.U2FBadRequest]: 'U2F: Bad request', + [LedgerError.U2FConfigurationUnsupported]: 'U2F: Configuration unsupported', + [LedgerError.U2FDeviceIneligible]: 'U2F: Device Ineligible', + [LedgerError.U2FTimeout]: 'U2F: Timeout', + [LedgerError.Timeout]: 'Timeout', + [LedgerError.NoErrors]: 'No errors', + [LedgerError.DeviceIsBusy]: 'Device is busy', + [LedgerError.ErrorDerivingKeys]: 'Error deriving keys', + [LedgerError.ExecutionError]: 'Execution Error', + [LedgerError.WrongLength]: 'Wrong Length', + [LedgerError.EmptyBuffer]: 'Empty Buffer', + [LedgerError.OutputBufferTooSmall]: 'Output buffer too small', + [LedgerError.DataIsInvalid]: 'Data is invalid', + [LedgerError.ConditionsNotSatisfied]: 'Conditions not satisfied', + [LedgerError.TransactionRejected]: 'Transaction rejected', + [LedgerError.BadKeyHandle]: 'Bad key handle', + [LedgerError.InvalidP1P2]: 'Invalid P1/P2', + [LedgerError.InstructionNotSupported]: 'Instruction not supported', + [LedgerError.AppDoesNotSeemToBeOpen]: 'App does not seem to be open', + [LedgerError.UnknownError]: 'Unknown error', + [LedgerError.SignVerifyError]: 'Sign/verify error', +}; +function errorCodeToString(statusCode) { + if (statusCode in exports.ERROR_DESCRIPTION) + return exports.ERROR_DESCRIPTION[statusCode]; + return `Unknown Status Code: ${statusCode}`; +} +exports.errorCodeToString = errorCodeToString; +function isDict(v) { + return typeof v === 'object' && v !== null && !(v instanceof Array) && !(v instanceof Date); +} +function processErrorResponse(response) { + if (response) { + if (isDict(response)) { + if (Object.prototype.hasOwnProperty.call(response, 'statusCode')) { + return { + returnCode: response.statusCode, + errorMessage: errorCodeToString(response.statusCode), + }; + } + if (Object.prototype.hasOwnProperty.call(response, 'returnCode') && Object.prototype.hasOwnProperty.call(response, 'errorMessage')) { + return response; + } + } + return { + returnCode: 0xffff, + errorMessage: response.toString(), + }; + } + return { + returnCode: 0xffff, + errorMessage: response.toString(), + }; +} +exports.processErrorResponse = processErrorResponse; +const HARDENED = 0x80000000; +const DEFAULT_DER_PATH_LEN = 6; +const IDENTITY_DER_PATH_LEN = 4; // m/888'/0'/ +function serializePath(path) { + if (!path.startsWith('m')) { + throw new Error(`Path should start with "m" (e.g "m/44'/5757'/5'/0/3")`); + } + const pathArray = path.split('/'); + let allocSize = 0; + if (pathArray.length === DEFAULT_DER_PATH_LEN || pathArray.length === IDENTITY_DER_PATH_LEN) { + allocSize = (pathArray.length - 1) * 4 + 1; + } + else { + throw new Error(`Invalid path. (e.g "m/44'/134'/0/0/0"`); + } + const buf = Buffer.alloc(allocSize); + buf.writeUInt8(pathArray.length - 1, 0); + for (let i = 1; i < pathArray.length; i += 1) { + let value = 0; + let child = pathArray[i]; + if (child.endsWith("'")) { + value += HARDENED; + child = child.slice(0, -1); + } + const childNumber = Number(child); + if (Number.isNaN(childNumber)) { + throw new Error(`Invalid path : ${child} is not a number. (e.g "m/44'/461'/5'/0/3")`); + } + if (childNumber >= HARDENED) { + throw new Error('Incorrect child value (bigger or equal to 0x80000000)'); + } + value += childNumber; + buf.writeUInt32LE(value, 4 * (i - 1) + 1); + } + return buf; +} +exports.serializePath = serializePath; +//# sourceMappingURL=common.js.map \ No newline at end of file diff --git a/packages/ledger-namada/dist/common.js.map b/packages/ledger-namada/dist/common.js.map new file mode 100644 index 0000000000..236b499b04 --- /dev/null +++ b/packages/ledger-namada/dist/common.js.map @@ -0,0 +1 @@ +{"version":3,"file":"common.js","sourceRoot":"","sources":["../src/common.ts"],"names":[],"mappings":"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","sourcesContent":["/** ******************************************************************************\n * (c) 2018 - 2023 Zondax AG\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n ******************************************************************************* */\nexport const CHUNK_SIZE = 250\n\nexport const PAYLOAD_TYPE = {\n INIT: 0x00,\n ADD: 0x01,\n LAST: 0x02,\n}\n\nexport const P1_VALUES = {\n ONLY_RETRIEVE: 0x00,\n SHOW_ADDRESS_IN_DEVICE: 0x01,\n}\n\nexport const P2_VALUES = {\n DEFAULT: 0x00,\n}\n\n// noinspection JSUnusedGlobalSymbols\nexport const SIGN_VALUES_P2 = {\n DEFAULT: 0x00,\n}\n\nexport const ERROR_CODE = {\n NoError: 0x9000,\n}\n\nexport const enum SignatureType {\n WrapperSignature = 0,\n RawSignature = 1,\n}\n\nexport enum LedgerError {\n U2FUnknown = 1,\n U2FBadRequest = 2,\n U2FConfigurationUnsupported = 3,\n U2FDeviceIneligible = 4,\n U2FTimeout = 5,\n Timeout = 14,\n NoErrors = 0x9000,\n DeviceIsBusy = 0x9001,\n ErrorDerivingKeys = 0x6802,\n ExecutionError = 0x6400,\n WrongLength = 0x6700,\n EmptyBuffer = 0x6982,\n OutputBufferTooSmall = 0x6983,\n DataIsInvalid = 0x6984,\n ConditionsNotSatisfied = 0x6985,\n TransactionRejected = 0x6986,\n BadKeyHandle = 0x6a80,\n InvalidP1P2 = 0x6b00,\n InstructionNotSupported = 0x6d00,\n AppDoesNotSeemToBeOpen = 0x6e01,\n UnknownError = 0x6f00,\n SignVerifyError = 0x6f01,\n}\n\nexport const ERROR_DESCRIPTION = {\n [LedgerError.U2FUnknown]: 'U2F: Unknown',\n [LedgerError.U2FBadRequest]: 'U2F: Bad request',\n [LedgerError.U2FConfigurationUnsupported]: 'U2F: Configuration unsupported',\n [LedgerError.U2FDeviceIneligible]: 'U2F: Device Ineligible',\n [LedgerError.U2FTimeout]: 'U2F: Timeout',\n [LedgerError.Timeout]: 'Timeout',\n [LedgerError.NoErrors]: 'No errors',\n [LedgerError.DeviceIsBusy]: 'Device is busy',\n [LedgerError.ErrorDerivingKeys]: 'Error deriving keys',\n [LedgerError.ExecutionError]: 'Execution Error',\n [LedgerError.WrongLength]: 'Wrong Length',\n [LedgerError.EmptyBuffer]: 'Empty Buffer',\n [LedgerError.OutputBufferTooSmall]: 'Output buffer too small',\n [LedgerError.DataIsInvalid]: 'Data is invalid',\n [LedgerError.ConditionsNotSatisfied]: 'Conditions not satisfied',\n [LedgerError.TransactionRejected]: 'Transaction rejected',\n [LedgerError.BadKeyHandle]: 'Bad key handle',\n [LedgerError.InvalidP1P2]: 'Invalid P1/P2',\n [LedgerError.InstructionNotSupported]: 'Instruction not supported',\n [LedgerError.AppDoesNotSeemToBeOpen]: 'App does not seem to be open',\n [LedgerError.UnknownError]: 'Unknown error',\n [LedgerError.SignVerifyError]: 'Sign/verify error',\n}\n\nexport function errorCodeToString(statusCode: LedgerError) {\n if (statusCode in ERROR_DESCRIPTION) return ERROR_DESCRIPTION[statusCode]\n return `Unknown Status Code: ${statusCode}`\n}\n\nfunction isDict(v: any) {\n return typeof v === 'object' && v !== null && !(v instanceof Array) && !(v instanceof Date)\n}\n\nexport function processErrorResponse(response: any) {\n if (response) {\n if (isDict(response)) {\n if (Object.prototype.hasOwnProperty.call(response, 'statusCode')) {\n return {\n returnCode: response.statusCode,\n errorMessage: errorCodeToString(response.statusCode),\n }\n }\n\n if (Object.prototype.hasOwnProperty.call(response, 'returnCode') && Object.prototype.hasOwnProperty.call(response, 'errorMessage')) {\n return response\n }\n }\n return {\n returnCode: 0xffff,\n errorMessage: response.toString(),\n }\n }\n\n return {\n returnCode: 0xffff,\n errorMessage: response.toString(),\n }\n}\n\nconst HARDENED = 0x80000000\nconst DEFAULT_DER_PATH_LEN = 6\nconst IDENTITY_DER_PATH_LEN = 4 // m/888'/0'/\n\nexport function serializePath(path: string) {\n if (!path.startsWith('m')) {\n throw new Error(`Path should start with \"m\" (e.g \"m/44'/5757'/5'/0/3\")`)\n }\n\n const pathArray = path.split('/')\n\n let allocSize = 0\n\n if (pathArray.length === DEFAULT_DER_PATH_LEN || pathArray.length === IDENTITY_DER_PATH_LEN) {\n allocSize = (pathArray.length - 1) * 4 + 1\n } else {\n throw new Error(`Invalid path. (e.g \"m/44'/134'/0/0/0\"`)\n }\n\n const buf = Buffer.alloc(allocSize)\n buf.writeUInt8(pathArray.length - 1, 0)\n\n for (let i = 1; i < pathArray.length; i += 1) {\n let value = 0\n let child = pathArray[i]\n if (child.endsWith(\"'\")) {\n value += HARDENED\n child = child.slice(0, -1)\n }\n\n const childNumber = Number(child)\n\n if (Number.isNaN(childNumber)) {\n throw new Error(`Invalid path : ${child} is not a number. (e.g \"m/44'/461'/5'/0/3\")`)\n }\n\n if (childNumber >= HARDENED) {\n throw new Error('Incorrect child value (bigger or equal to 0x80000000)')\n }\n\n value += childNumber\n\n buf.writeUInt32LE(value, 4 * (i - 1) + 1)\n }\n\n return buf\n}\n"]} \ No newline at end of file diff --git a/packages/ledger-namada/dist/config.d.ts b/packages/ledger-namada/dist/config.d.ts new file mode 100644 index 0000000000..5f81c2aa7b --- /dev/null +++ b/packages/ledger-namada/dist/config.d.ts @@ -0,0 +1,30 @@ +/** ****************************************************************************** + * (c) 2018 - 2022 Zondax AG + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************* */ +export declare const CLA = 87; +export declare const INS: { + GET_VERSION: number; + GET_PUBLIC_KEY: number; + SIGN: number; + GET_MASP_ADDRESS: number; + GET_IVK: number; + GET_OVK: number; + GET_NF: number; + GET_SIGNATURE: number; +}; +export declare const SALT_LEN = 8; +export declare const HASH_LEN = 32; +export declare const PK_LEN_25519 = 32; +export declare const ED25519_SIGNATURE_LEN = 64; diff --git a/packages/ledger-namada/dist/config.js b/packages/ledger-namada/dist/config.js new file mode 100644 index 0000000000..3f52e80b84 --- /dev/null +++ b/packages/ledger-namada/dist/config.js @@ -0,0 +1,34 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ED25519_SIGNATURE_LEN = exports.PK_LEN_25519 = exports.HASH_LEN = exports.SALT_LEN = exports.INS = exports.CLA = void 0; +/** ****************************************************************************** + * (c) 2018 - 2022 Zondax AG + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************* */ +exports.CLA = 0x57; +exports.INS = { + GET_VERSION: 0x00, + GET_PUBLIC_KEY: 0x01, + SIGN: 0x02, + GET_MASP_ADDRESS: 0x03, + GET_IVK: 0x04, + GET_OVK: 0x05, + GET_NF: 0x06, + GET_SIGNATURE: 0x0a, +}; +exports.SALT_LEN = 8; +exports.HASH_LEN = 32; +exports.PK_LEN_25519 = 32; +exports.ED25519_SIGNATURE_LEN = 64; +//# sourceMappingURL=config.js.map \ No newline at end of file diff --git a/packages/ledger-namada/dist/config.js.map b/packages/ledger-namada/dist/config.js.map new file mode 100644 index 0000000000..f97b947a42 --- /dev/null +++ b/packages/ledger-namada/dist/config.js.map @@ -0,0 +1 @@ +{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;;;mFAcmF;AACtE,QAAA,GAAG,GAAG,IAAI,CAAA;AACV,QAAA,GAAG,GAAG;IACjB,WAAW,EAAE,IAAI;IACjB,cAAc,EAAE,IAAI;IACpB,IAAI,EAAE,IAAI;IAEV,gBAAgB,EAAE,IAAI;IACtB,OAAO,EAAE,IAAI;IACb,OAAO,EAAE,IAAI;IACb,MAAM,EAAE,IAAI;IAEZ,aAAa,EAAE,IAAI;CACpB,CAAA;AACY,QAAA,QAAQ,GAAG,CAAC,CAAA;AACZ,QAAA,QAAQ,GAAG,EAAE,CAAA;AACb,QAAA,YAAY,GAAG,EAAE,CAAA;AACjB,QAAA,qBAAqB,GAAG,EAAE,CAAA","sourcesContent":["/** ******************************************************************************\n * (c) 2018 - 2022 Zondax AG\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n ******************************************************************************* */\nexport const CLA = 0x57\nexport const INS = {\n GET_VERSION: 0x00,\n GET_PUBLIC_KEY: 0x01,\n SIGN: 0x02,\n\n GET_MASP_ADDRESS: 0x03,\n GET_IVK: 0x04,\n GET_OVK: 0x05,\n GET_NF: 0x06,\n\n GET_SIGNATURE: 0x0a,\n}\nexport const SALT_LEN = 8\nexport const HASH_LEN = 32\nexport const PK_LEN_25519 = 32\nexport const ED25519_SIGNATURE_LEN = 64\n"]} \ No newline at end of file diff --git a/packages/ledger-namada/dist/index.d.ts b/packages/ledger-namada/dist/index.d.ts new file mode 100644 index 0000000000..59072a32be --- /dev/null +++ b/packages/ledger-namada/dist/index.d.ts @@ -0,0 +1,16 @@ +/** ****************************************************************************** + * (c) 2018 - 2022 Zondax AG + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************* */ +export * from './namadaApp'; diff --git a/packages/ledger-namada/dist/index.js b/packages/ledger-namada/dist/index.js new file mode 100644 index 0000000000..a4a6c9409c --- /dev/null +++ b/packages/ledger-namada/dist/index.js @@ -0,0 +1,33 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +/** ****************************************************************************** + * (c) 2018 - 2022 Zondax AG + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************* */ +__exportStar(require("./namadaApp"), exports); +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/packages/ledger-namada/dist/index.js.map b/packages/ledger-namada/dist/index.js.map new file mode 100644 index 0000000000..d79968f77f --- /dev/null +++ b/packages/ledger-namada/dist/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA;;;;;;;;;;;;;;mFAcmF;AACnF,8CAA2B","sourcesContent":["/** ******************************************************************************\n * (c) 2018 - 2022 Zondax AG\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n ******************************************************************************* */\nexport * from './namadaApp'\n"]} \ No newline at end of file diff --git a/packages/ledger-namada/dist/namadaApp.d.ts b/packages/ledger-namada/dist/namadaApp.d.ts new file mode 100644 index 0000000000..43de16128a --- /dev/null +++ b/packages/ledger-namada/dist/namadaApp.d.ts @@ -0,0 +1,34 @@ +/// +/** ****************************************************************************** + * (c) 2018 - 2022 Zondax AG + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************* */ +import Transport from '@ledgerhq/hw-transport'; +import { ResponseAddress, ResponseAppInfo, ResponseBase, ResponseSign, ResponseVersion } from './types'; +import { LedgerError, SignatureType } from './common'; +export { LedgerError }; +export * from './types'; +export declare class NamadaApp { + transport: Transport; + constructor(transport: Transport); + prepareChunks(serializedPath: Buffer, message: Buffer): Promise; + getVersion(): Promise; + getAppInfo(): Promise; + getAddressAndPubKey(path: string): Promise; + showAddressAndPubKey(path: string): Promise; + signSendChunk(chunkIdx: number, chunkNum: number, chunk: Buffer, ins: number): Promise; + getSignature(signatureType: SignatureType): Promise; + _sign(path: string, message: Buffer): Promise; + sign(path: string, message: Buffer): Promise; +} diff --git a/packages/ledger-namada/dist/namadaApp.js b/packages/ledger-namada/dist/namadaApp.js new file mode 100644 index 0000000000..cb384cecd0 --- /dev/null +++ b/packages/ledger-namada/dist/namadaApp.js @@ -0,0 +1,194 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.NamadaApp = exports.LedgerError = void 0; +const types_1 = require("./types"); +const common_1 = require("./common"); +Object.defineProperty(exports, "LedgerError", { enumerable: true, get: function () { return common_1.LedgerError; } }); +const config_1 = require("./config"); +const processResponses_1 = require("./processResponses"); +__exportStar(require("./types"), exports); +class NamadaApp { + constructor(transport) { + if (!transport) { + throw new Error('Transport has not been defined'); + } + this.transport = transport; + } + async prepareChunks(serializedPath, message) { + const chunks = []; + chunks.push(serializedPath); + for (let i = 0; i < message.length; i += common_1.CHUNK_SIZE) { + let end = i + common_1.CHUNK_SIZE; + if (i > message.length) { + end = message.length; + } + chunks.push(message.subarray(i, end)); + } + return chunks; + } + async getVersion() { + return this.transport.send(config_1.CLA, config_1.INS.GET_VERSION, 0, 0).then((response) => { + const errorCodeData = response.slice(-2); + const returnCode = errorCodeData[0] * 256 + errorCodeData[1]; + let targetId = 0; + if (response.length >= 9) { + /* eslint-disable no-bitwise */ + targetId = (response[5] << 24) + (response[6] << 16) + (response[7] << 8) + (response[8] << 0); + /* eslint-enable no-bitwise */ + } + return { + returnCode: returnCode, + errorMessage: (0, common_1.errorCodeToString)(returnCode), + // /// + testMode: response[0] !== 0, + major: response[1], + minor: response[2], + patch: response[3], + deviceLocked: response[4] === 1, + targetId: targetId.toString(16), + }; + }, common_1.processErrorResponse); + } + async getAppInfo() { + return this.transport.send(0xb0, 0x01, 0, 0).then(response => { + const errorCodeData = response.slice(-2); + const returnCode = errorCodeData[0] * 256 + errorCodeData[1]; + const result = {}; + let appName = 'err'; + let appVersion = 'err'; + let flagLen = 0; + let flagsValue = 0; + if (response[0] !== 1) { + // Ledger responds with format ID 1. There is no spec for any format != 1 + result.errorMessage = 'response format ID not recognized'; + result.returnCode = common_1.LedgerError.DeviceIsBusy; + } + else { + const appNameLen = response[1]; + appName = response.slice(2, 2 + appNameLen).toString('ascii'); + let idx = 2 + appNameLen; + const appVersionLen = response[idx]; + idx += 1; + appVersion = response.slice(idx, idx + appVersionLen).toString('ascii'); + idx += appVersionLen; + const appFlagsLen = response[idx]; + idx += 1; + flagLen = appFlagsLen; + flagsValue = response[idx]; + } + return { + returnCode, + errorMessage: (0, common_1.errorCodeToString)(returnCode), + // + appName, + appVersion, + flagLen, + flagsValue, + flagRecovery: (flagsValue & 1) !== 0, + // eslint-disable-next-line no-bitwise + flagSignedMcuCode: (flagsValue & 2) !== 0, + // eslint-disable-next-line no-bitwise + flagOnboarded: (flagsValue & 4) !== 0, + // eslint-disable-next-line no-bitwise + flagPINValidated: (flagsValue & 128) !== 0, + }; + }, common_1.processErrorResponse); + } + async getAddressAndPubKey(path) { + const serializedPath = (0, common_1.serializePath)(path); + return this.transport + .send(config_1.CLA, config_1.INS.GET_PUBLIC_KEY, common_1.P1_VALUES.ONLY_RETRIEVE, 0, serializedPath, [common_1.LedgerError.NoErrors]) + .then(processResponses_1.processGetAddrResponse, common_1.processErrorResponse); + } + async showAddressAndPubKey(path) { + const serializedPath = (0, common_1.serializePath)(path); + return this.transport + .send(config_1.CLA, config_1.INS.GET_PUBLIC_KEY, common_1.P1_VALUES.SHOW_ADDRESS_IN_DEVICE, 0, serializedPath, [common_1.LedgerError.NoErrors]) + .then(processResponses_1.processGetAddrResponse, common_1.processErrorResponse); + } + async signSendChunk(chunkIdx, chunkNum, chunk, ins) { + let payloadType = common_1.PAYLOAD_TYPE.ADD; + const p2 = 0; + if (chunkIdx === 1) { + payloadType = common_1.PAYLOAD_TYPE.INIT; + } + if (chunkIdx === chunkNum) { + payloadType = common_1.PAYLOAD_TYPE.LAST; + } + return this.transport + .send(config_1.CLA, ins, payloadType, p2, chunk, [ + common_1.LedgerError.NoErrors, + common_1.LedgerError.DataIsInvalid, + common_1.LedgerError.BadKeyHandle, + common_1.LedgerError.SignVerifyError, + ]) + .then((response) => { + const errorCodeData = response.subarray(-2); + const returnCode = errorCodeData[0] * 256 + errorCodeData[1]; + let errorMessage = (0, common_1.errorCodeToString)(returnCode); + if (returnCode === common_1.LedgerError.BadKeyHandle || + returnCode === common_1.LedgerError.DataIsInvalid || + returnCode === common_1.LedgerError.SignVerifyError) { + errorMessage = `${errorMessage} : ${response.subarray(0, response.length - 2).toString('ascii')}`; + } + return { + returnCode: returnCode, + errorMessage: errorMessage, + }; + }, common_1.processErrorResponse); + } + async getSignature(signatureType) { + return this.transport + .send(config_1.CLA, config_1.INS.GET_SIGNATURE, common_1.P1_VALUES.ONLY_RETRIEVE, signatureType, Buffer.from([]), [common_1.LedgerError.NoErrors]) + .then(processResponses_1.processGetSignatureResponse, common_1.processErrorResponse); + } + async _sign(path, message) { + const serializedPath = (0, common_1.serializePath)(path); + return this.prepareChunks(serializedPath, message).then(chunks => { + return this.signSendChunk(1, chunks.length, chunks[0], config_1.INS.SIGN).then(async (response) => { + let result = { + returnCode: response.returnCode, + errorMessage: response.errorMessage, + }; + for (let i = 1; i < chunks.length; i++) { + result = await this.signSendChunk(1 + i, chunks.length, chunks[i], config_1.INS.SIGN); + if (result.returnCode !== common_1.LedgerError.NoErrors) { + break; + } + } + return result; + }, common_1.processErrorResponse); + }, common_1.processErrorResponse); + } + async sign(path, message) { + const signCommand = await this._sign(path, message); + const result = { + returnCode: signCommand.returnCode, + errorMessage: signCommand.errorMessage, + wrapperSignature: new types_1.Signature(), + rawSignature: new types_1.Signature(), + }; + if (signCommand.returnCode !== common_1.LedgerError.NoErrors) { + return result; + } + result.wrapperSignature = new types_1.Signature(await this.getSignature(0 /* SignatureType.WrapperSignature */)); + result.rawSignature = new types_1.Signature(await this.getSignature(1 /* SignatureType.RawSignature */)); + return result; + } +} +exports.NamadaApp = NamadaApp; +//# sourceMappingURL=namadaApp.js.map \ No newline at end of file diff --git a/packages/ledger-namada/dist/namadaApp.js.map b/packages/ledger-namada/dist/namadaApp.js.map new file mode 100644 index 0000000000..6b89fc8780 --- /dev/null +++ b/packages/ledger-namada/dist/namadaApp.js.map @@ -0,0 +1 @@ +{"version":3,"file":"namadaApp.js","sourceRoot":"","sources":["../src/namadaApp.ts"],"names":[],"mappings":"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","sourcesContent":["/** ******************************************************************************\n * (c) 2018 - 2022 Zondax AG\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n ******************************************************************************* */\nimport Transport from '@ledgerhq/hw-transport'\nimport { ResponseAddress, ResponseAppInfo, ResponseBase, ResponseSign, ResponseVersion, Signature } from './types'\n\nimport {\n CHUNK_SIZE,\n errorCodeToString,\n LedgerError,\n P1_VALUES,\n PAYLOAD_TYPE,\n processErrorResponse,\n serializePath,\n SignatureType,\n} from './common'\n\nimport { CLA, INS } from './config'\nimport { processGetAddrResponse, processGetSignatureResponse } from './processResponses'\n\nexport { LedgerError }\nexport * from './types'\n\nexport class NamadaApp {\n transport: Transport\n\n constructor(transport: Transport) {\n if (!transport) {\n throw new Error('Transport has not been defined')\n }\n\n this.transport = transport\n }\n\n async prepareChunks(serializedPath: Buffer, message: Buffer) {\n const chunks = []\n\n chunks.push(serializedPath)\n for (let i = 0; i < message.length; i += CHUNK_SIZE) {\n let end = i + CHUNK_SIZE\n if (i > message.length) {\n end = message.length\n }\n chunks.push(message.subarray(i, end))\n }\n\n return chunks\n }\n\n async getVersion(): Promise {\n return this.transport.send(CLA, INS.GET_VERSION, 0, 0).then((response: any) => {\n const errorCodeData = response.slice(-2)\n const returnCode = errorCodeData[0] * 256 + errorCodeData[1]\n\n let targetId = 0\n if (response.length >= 9) {\n /* eslint-disable no-bitwise */\n targetId = (response[5] << 24) + (response[6] << 16) + (response[7] << 8) + (response[8] << 0)\n /* eslint-enable no-bitwise */\n }\n\n return {\n returnCode: returnCode,\n errorMessage: errorCodeToString(returnCode),\n // ///\n testMode: response[0] !== 0,\n major: response[1],\n minor: response[2],\n patch: response[3],\n deviceLocked: response[4] === 1,\n targetId: targetId.toString(16),\n }\n }, processErrorResponse)\n }\n\n async getAppInfo(): Promise {\n return this.transport.send(0xb0, 0x01, 0, 0).then(response => {\n const errorCodeData = response.slice(-2)\n const returnCode = errorCodeData[0] * 256 + errorCodeData[1]\n\n const result: { errorMessage?: string; returnCode?: LedgerError } = {}\n\n let appName = 'err'\n let appVersion = 'err'\n let flagLen = 0\n let flagsValue = 0\n\n if (response[0] !== 1) {\n // Ledger responds with format ID 1. There is no spec for any format != 1\n result.errorMessage = 'response format ID not recognized'\n result.returnCode = LedgerError.DeviceIsBusy\n } else {\n const appNameLen = response[1]\n appName = response.slice(2, 2 + appNameLen).toString('ascii')\n let idx = 2 + appNameLen\n const appVersionLen = response[idx]\n idx += 1\n appVersion = response.slice(idx, idx + appVersionLen).toString('ascii')\n idx += appVersionLen\n const appFlagsLen = response[idx]\n idx += 1\n flagLen = appFlagsLen\n flagsValue = response[idx]\n }\n\n return {\n returnCode,\n errorMessage: errorCodeToString(returnCode),\n //\n appName,\n appVersion,\n flagLen,\n flagsValue,\n flagRecovery: (flagsValue & 1) !== 0,\n // eslint-disable-next-line no-bitwise\n flagSignedMcuCode: (flagsValue & 2) !== 0,\n // eslint-disable-next-line no-bitwise\n flagOnboarded: (flagsValue & 4) !== 0,\n // eslint-disable-next-line no-bitwise\n flagPINValidated: (flagsValue & 128) !== 0,\n }\n }, processErrorResponse)\n }\n\n async getAddressAndPubKey(path: string): Promise {\n const serializedPath = serializePath(path)\n return this.transport\n .send(CLA, INS.GET_PUBLIC_KEY, P1_VALUES.ONLY_RETRIEVE, 0, serializedPath, [LedgerError.NoErrors])\n .then(processGetAddrResponse, processErrorResponse)\n }\n\n async showAddressAndPubKey(path: string): Promise {\n const serializedPath = serializePath(path)\n return this.transport\n .send(CLA, INS.GET_PUBLIC_KEY, P1_VALUES.SHOW_ADDRESS_IN_DEVICE, 0, serializedPath, [LedgerError.NoErrors])\n .then(processGetAddrResponse, processErrorResponse)\n }\n\n async signSendChunk(chunkIdx: number, chunkNum: number, chunk: Buffer, ins: number): Promise {\n let payloadType = PAYLOAD_TYPE.ADD\n const p2 = 0\n if (chunkIdx === 1) {\n payloadType = PAYLOAD_TYPE.INIT\n }\n if (chunkIdx === chunkNum) {\n payloadType = PAYLOAD_TYPE.LAST\n }\n\n return this.transport\n .send(CLA, ins, payloadType, p2, chunk, [\n LedgerError.NoErrors,\n LedgerError.DataIsInvalid,\n LedgerError.BadKeyHandle,\n LedgerError.SignVerifyError,\n ])\n .then((response: Buffer) => {\n const errorCodeData = response.subarray(-2)\n const returnCode = errorCodeData[0] * 256 + errorCodeData[1]\n let errorMessage = errorCodeToString(returnCode)\n\n if (\n returnCode === LedgerError.BadKeyHandle ||\n returnCode === LedgerError.DataIsInvalid ||\n returnCode === LedgerError.SignVerifyError\n ) {\n errorMessage = `${errorMessage} : ${response.subarray(0, response.length - 2).toString('ascii')}`\n }\n\n return {\n returnCode: returnCode,\n errorMessage: errorMessage,\n } as ResponseSign\n }, processErrorResponse)\n }\n\n async getSignature(signatureType: SignatureType) {\n return this.transport\n .send(CLA, INS.GET_SIGNATURE, P1_VALUES.ONLY_RETRIEVE, signatureType, Buffer.from([]), [LedgerError.NoErrors])\n .then(processGetSignatureResponse, processErrorResponse)\n }\n\n async _sign(path: string, message: Buffer) {\n const serializedPath = serializePath(path)\n\n return this.prepareChunks(serializedPath, message).then(chunks => {\n return this.signSendChunk(1, chunks.length, chunks[0], INS.SIGN).then(async response => {\n let result = {\n returnCode: response.returnCode,\n errorMessage: response.errorMessage,\n }\n\n for (let i = 1; i < chunks.length; i++) {\n result = await this.signSendChunk(1 + i, chunks.length, chunks[i], INS.SIGN)\n if (result.returnCode !== LedgerError.NoErrors) {\n break\n }\n }\n return result\n }, processErrorResponse)\n }, processErrorResponse)\n }\n\n async sign(path: string, message: Buffer) {\n const signCommand = await this._sign(path, message)\n\n const result: ResponseSign = {\n returnCode: signCommand.returnCode,\n errorMessage: signCommand.errorMessage,\n wrapperSignature: new Signature(),\n rawSignature: new Signature(),\n }\n\n if (signCommand.returnCode !== LedgerError.NoErrors) {\n return result\n }\n\n result.wrapperSignature = new Signature(await this.getSignature(SignatureType.WrapperSignature))\n result.rawSignature = new Signature(await this.getSignature(SignatureType.RawSignature))\n\n return result\n }\n\n /* Not implemented yet\n async getShieldedAddressAndPubKey(path: number, div: Buffer): Promise {\n const buf = Buffer.alloc(4);\n buf.writeUInt32LE(path, 0);\n return this.transport\n .send(CLA, INS.GET_MASP_ADDRESS, P1_VALUES.ONLY_RETRIEVE, 0, Buffer.concat([buf, div]), [LedgerError.NoErrors])\n .then(processGetShieldedAddrResponse, processErrorResponse)\n }\n\n async showShieldedAddressAndPubKey(path: number, div: Buffer): Promise {\n const buf = Buffer.alloc(4);\n buf.writeUInt32LE(path, 0);\n return this.transport\n .send(CLA, INS.GET_MASP_ADDRESS, P1_VALUES.SHOW_ADDRESS_IN_DEVICE, 0, Buffer.concat([buf, div]), [LedgerError.NoErrors])\n .then(processGetShieldedAddrResponse, processErrorResponse)\n }\n\n async getIncomingViewingKey(path: number): Promise {\n const buf = Buffer.alloc(4);\n buf.writeUInt32LE(path, 0);\n return this.transport\n .send(CLA, INS.GET_IVK, P1_VALUES.SHOW_ADDRESS_IN_DEVICE, 0, buf, [LedgerError.NoErrors])\n .then(processIncomingViewingKeyResponse, processErrorResponse)\n }\n\n async getOutgoingViewingKey(path: number): Promise {\n const buf = Buffer.alloc(4);\n buf.writeUInt32LE(path, 0);\n return this.transport\n .send(CLA, INS.GET_OVK, P1_VALUES.SHOW_ADDRESS_IN_DEVICE, 0, buf, [LedgerError.NoErrors])\n .then(processOutgoingViewingKeyResponse, processErrorResponse)\n }\n\n async getNullifier(pos: Uint8Array, cm: Buffer): Promise {\n return this.transport\n .send(CLA, INS.GET_NF, P1_VALUES.ONLY_RETRIEVE, 0, Buffer.concat([pos, cm]), [LedgerError.NoErrors])\n .then(processNullifierResponse, processErrorResponse)\n }\n */\n}\n"]} \ No newline at end of file diff --git a/packages/ledger-namada/dist/processResponses.d.ts b/packages/ledger-namada/dist/processResponses.d.ts new file mode 100644 index 0000000000..e3ebe096a2 --- /dev/null +++ b/packages/ledger-namada/dist/processResponses.d.ts @@ -0,0 +1,24 @@ +/** ****************************************************************************** + * (c) 2018 - 2022 Zondax AG + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************* */ +/// +import { ISignature } from './types'; +export declare function processGetSignatureResponse(response: Buffer): ISignature; +export declare function processGetAddrResponse(response: Buffer): { + publicKey: Buffer; + address: Buffer; + returnCode: number; + errorMessage: string; +}; diff --git a/packages/ledger-namada/dist/processResponses.js b/packages/ledger-namada/dist/processResponses.js new file mode 100644 index 0000000000..32d6357dea --- /dev/null +++ b/packages/ledger-namada/dist/processResponses.js @@ -0,0 +1,133 @@ +"use strict"; +/** ****************************************************************************** + * (c) 2018 - 2022 Zondax AG + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************* */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.processGetAddrResponse = exports.processGetSignatureResponse = void 0; +const common_1 = require("./common"); +const config_1 = require("./config"); +function processGetSignatureResponse(response) { + console.log('Processing get signature response'); + let offset = 0; + const salt = Buffer.from(response.subarray(offset, offset + config_1.SALT_LEN)); + offset += config_1.SALT_LEN; + const hashesLen = response[offset] + response[offset + 1] * 0x100 + response[offset + 2] * 0x10000 + response[offset + 3] * 0x1000000; + offset += 4; + const hashes = []; + for (let i = 0; i < hashesLen; i++) { + hashes.push(Buffer.from(response.subarray(offset, offset + config_1.HASH_LEN))); + offset += config_1.HASH_LEN; + } + const pubkey = Buffer.from(response.subarray(offset, offset + config_1.PK_LEN_25519 + 1)); + offset += config_1.PK_LEN_25519 + 1; + const hasSignature = response[offset]; + offset += 1; + let signature = null; + if (hasSignature) { + signature = Buffer.from(response.subarray(offset, offset + 65)); + offset += 65; + } + const raw = Buffer.from(response.subarray(0, offset)); + return { + salt, + hashes, + pubkey, + signature, + raw, + }; +} +exports.processGetSignatureResponse = processGetSignatureResponse; +function processGetAddrResponse(response) { + console.log('Processing get address response'); + let partialResponse = response; + const errorCodeData = partialResponse.subarray(-2); + const returnCode = errorCodeData[0] * 256 + errorCodeData[1]; + //get public key len (variable) + const publicKey = Buffer.from(partialResponse.slice(0, config_1.PK_LEN_25519)); + //"advance" buffer + partialResponse = partialResponse.slice(config_1.PK_LEN_25519); + // get the implicit address corresponding to the public key + const address = Buffer.from(partialResponse.slice(0, -2)); + return { + publicKey, + address, + returnCode, + errorMessage: (0, common_1.errorCodeToString)(returnCode), + }; +} +exports.processGetAddrResponse = processGetAddrResponse; +// Not used yet +// function processGetShieldedAddrResponse(response: Buffer) { +// console.log("Processing get address response") +// let partialResponse = response +// const errorCodeData = partialResponse.slice(-2) +// const returnCode = errorCodeData[0] * 256 + errorCodeData[1] +// //get public key len (variable) +// const raw_pkd = Buffer.from(partialResponse.slice(0, 32)) +// //"advance" buffer +// partialResponse = partialResponse.slice(32) +// // get the length of the bech32m address +// const bech32m_len = partialResponse[0] +// //"advance" buffer +// partialResponse = partialResponse.slice(1) +// // get the bech32m encoding of the shielded address +// const bech32m_addr = Buffer.from(partialResponse.slice(0, bech32m_len)) +// return { +// raw_pkd, +// bech32m_len, +// bech32m_addr, +// returnCode, +// errorMessage: errorCodeToString(returnCode), +// } +// } +// function processIncomingViewingKeyResponse(response: Buffer) { +// console.log("Processing get IVK response") +// const partialResponse = response +// const errorCodeData = partialResponse.slice(-2) +// const returnCode = errorCodeData[0] * 256 + errorCodeData[1] +// //get public key len (variable) +// const raw_ivk = Buffer.from(partialResponse.slice(0, 32)) +// return { +// raw_ivk, +// returnCode, +// errorMessage: errorCodeToString(returnCode), +// } +// } +// function processNullifierResponse(response: Buffer) { +// console.log("Processing get nullifier response") +// const partialResponse = response +// const errorCodeData = partialResponse.slice(-2) +// const returnCode = errorCodeData[0] * 256 + errorCodeData[1] +// const raw_nf = Buffer.from(partialResponse.slice(0, 32)) +// return { +// raw_nf, +// returnCode, +// errorMessage: errorCodeToString(returnCode), +// } +// } +// function processOutgoingViewingKeyResponse(response: Buffer) { +// console.log("Processing get OVK response") +// const partialResponse = response +// const errorCodeData = partialResponse.slice(-2) +// const returnCode = errorCodeData[0] * 256 + errorCodeData[1] +// //get public key len (variable) +// const raw_ovk = Buffer.from(partialResponse.slice(0, 32)) +// return { +// raw_ovk, +// returnCode, +// errorMessage: errorCodeToString(returnCode), +// } +// } +//# sourceMappingURL=processResponses.js.map \ No newline at end of file diff --git a/packages/ledger-namada/dist/processResponses.js.map b/packages/ledger-namada/dist/processResponses.js.map new file mode 100644 index 0000000000..352dc18da3 --- /dev/null +++ b/packages/ledger-namada/dist/processResponses.js.map @@ -0,0 +1 @@ +{"version":3,"file":"processResponses.js","sourceRoot":"","sources":["../src/processResponses.ts"],"names":[],"mappings":"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","sourcesContent":["/** ******************************************************************************\n * (c) 2018 - 2022 Zondax AG\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n ******************************************************************************* */\n\nimport { errorCodeToString } from './common'\nimport { HASH_LEN, PK_LEN_25519, SALT_LEN } from './config'\nimport { ISignature } from './types'\n\nexport function processGetSignatureResponse(response: Buffer): ISignature {\n console.log('Processing get signature response')\n\n let offset = 0\n const salt = Buffer.from(response.subarray(offset, offset+SALT_LEN))\n offset += SALT_LEN\n const hashesLen = response[offset] + response[offset+1]*0x100 + response[offset+2]*0x10000 + response[offset+3]*0x1000000\n offset += 4\n const hashes = []\n for (let i = 0; i < hashesLen; i ++) {\n hashes.push(Buffer.from(response.subarray(offset, offset+HASH_LEN)))\n offset += HASH_LEN\n }\n const pubkey = Buffer.from(response.subarray(offset, offset + PK_LEN_25519+1))\n offset += PK_LEN_25519+1\n const hasSignature = response[offset]\n offset += 1\n let signature = null\n if(hasSignature) {\n signature = Buffer.from(response.subarray(offset, offset + 65))\n offset += 65\n }\n const raw = Buffer.from(response.subarray(0, offset))\n\n return {\n salt,\n hashes,\n pubkey,\n signature,\n raw,\n }\n}\n\nexport function processGetAddrResponse(response: Buffer) {\n console.log('Processing get address response')\n\n let partialResponse = response\n\n const errorCodeData = partialResponse.subarray(-2)\n const returnCode = errorCodeData[0] * 256 + errorCodeData[1]\n\n //get public key len (variable)\n const publicKey = Buffer.from(partialResponse.slice(0, PK_LEN_25519))\n\n //\"advance\" buffer\n partialResponse = partialResponse.slice(PK_LEN_25519)\n\n // get the implicit address corresponding to the public key\n const address = Buffer.from(partialResponse.slice(0, -2))\n\n return {\n publicKey,\n address,\n returnCode,\n errorMessage: errorCodeToString(returnCode),\n }\n}\n\n// Not used yet\n// function processGetShieldedAddrResponse(response: Buffer) {\n// console.log(\"Processing get address response\")\n\n// let partialResponse = response\n\n// const errorCodeData = partialResponse.slice(-2)\n// const returnCode = errorCodeData[0] * 256 + errorCodeData[1]\n\n// //get public key len (variable)\n// const raw_pkd = Buffer.from(partialResponse.slice(0, 32))\n\n// //\"advance\" buffer\n// partialResponse = partialResponse.slice(32)\n\n// // get the length of the bech32m address\n// const bech32m_len = partialResponse[0]\n\n// //\"advance\" buffer\n// partialResponse = partialResponse.slice(1)\n\n// // get the bech32m encoding of the shielded address\n// const bech32m_addr = Buffer.from(partialResponse.slice(0, bech32m_len))\n\n// return {\n// raw_pkd,\n// bech32m_len,\n// bech32m_addr,\n// returnCode,\n// errorMessage: errorCodeToString(returnCode),\n// }\n// }\n\n// function processIncomingViewingKeyResponse(response: Buffer) {\n// console.log(\"Processing get IVK response\")\n\n// const partialResponse = response\n\n// const errorCodeData = partialResponse.slice(-2)\n// const returnCode = errorCodeData[0] * 256 + errorCodeData[1]\n\n// //get public key len (variable)\n// const raw_ivk = Buffer.from(partialResponse.slice(0, 32))\n\n// return {\n// raw_ivk,\n// returnCode,\n// errorMessage: errorCodeToString(returnCode),\n// }\n// }\n\n// function processNullifierResponse(response: Buffer) {\n// console.log(\"Processing get nullifier response\")\n\n// const partialResponse = response\n\n// const errorCodeData = partialResponse.slice(-2)\n// const returnCode = errorCodeData[0] * 256 + errorCodeData[1]\n\n// const raw_nf = Buffer.from(partialResponse.slice(0, 32))\n\n// return {\n// raw_nf,\n// returnCode,\n// errorMessage: errorCodeToString(returnCode),\n// }\n// }\n\n// function processOutgoingViewingKeyResponse(response: Buffer) {\n// console.log(\"Processing get OVK response\")\n\n// const partialResponse = response\n\n// const errorCodeData = partialResponse.slice(-2)\n// const returnCode = errorCodeData[0] * 256 + errorCodeData[1]\n\n// //get public key len (variable)\n// const raw_ovk = Buffer.from(partialResponse.slice(0, 32))\n\n// return {\n// raw_ovk,\n// returnCode,\n// errorMessage: errorCodeToString(returnCode),\n// }\n// }\n"]} \ No newline at end of file diff --git a/packages/ledger-namada/dist/types.d.ts b/packages/ledger-namada/dist/types.d.ts new file mode 100644 index 0000000000..aab75bd60f --- /dev/null +++ b/packages/ledger-namada/dist/types.d.ts @@ -0,0 +1,68 @@ +/// +import { LedgerError } from './common'; +export interface ResponseBase { + errorMessage: string; + returnCode: LedgerError; +} +export interface ResponseAddress extends ResponseBase { + publicKey: Buffer; + address: Buffer; +} +export interface ResponseVersion extends ResponseBase { + testMode: boolean; + major: number; + minor: number; + patch: number; + deviceLocked: boolean; + targetId: string; +} +export interface ResponseAppInfo extends ResponseBase { + appName: string; + appVersion: string; + flagLen: number; + flagsValue: number; + flagRecovery: boolean; + flagSignedMcuCode: boolean; + flagOnboarded: boolean; + flagPINValidated: boolean; +} +export interface ResponseDeviceInfo extends ResponseBase { + targetId: string; + seVersion: string; + flag: string; + mcuVersion: string; +} +export interface ResponseShieldedAddress extends ResponseBase { + raw_pkd: Buffer; + bech32m_len: number; + bech32m_addr: Buffer; +} +export interface ResponseIncomingViewingKey extends ResponseBase { + raw_ivk: Buffer; +} +export interface ResponseOutgoingViewingKey extends ResponseBase { + raw_ovk: Buffer; +} +export interface ResponseNullifier extends ResponseBase { + raw_nf: Buffer; +} +export interface ISignature { + raw: Buffer; + salt: Buffer; + hashes: Buffer[]; + pubkey: Buffer; + signature: Buffer | null; +} +export declare class Signature implements ISignature { + raw: Buffer; + salt: Buffer; + hashes: Buffer[]; + pubkey: Buffer; + signature: Buffer | null; + isFilled: boolean; + constructor(signature?: ISignature); +} +export interface ResponseSign extends ResponseBase { + wrapperSignature: Signature; + rawSignature: Signature; +} diff --git a/packages/ledger-namada/dist/types.js b/packages/ledger-namada/dist/types.js new file mode 100644 index 0000000000..e5d79875b4 --- /dev/null +++ b/packages/ledger-namada/dist/types.js @@ -0,0 +1,25 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Signature = void 0; +class Signature { + constructor(signature) { + if (signature == null) { + this.raw = Buffer.from([]); + this.isFilled = false; + this.salt = Buffer.from([]); + this.hashes = []; + this.pubkey = Buffer.from([]); + this.signature = null; + } + else { + this.isFilled = true; + this.raw = signature.raw; + this.salt = signature.salt; + this.hashes = signature.hashes; + this.pubkey = signature.pubkey; + this.signature = signature.signature; + } + } +} +exports.Signature = Signature; +//# sourceMappingURL=types.js.map \ No newline at end of file diff --git a/packages/ledger-namada/dist/types.js.map b/packages/ledger-namada/dist/types.js.map new file mode 100644 index 0000000000..4e62f9cad1 --- /dev/null +++ b/packages/ledger-namada/dist/types.js.map @@ -0,0 +1 @@ +{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";;;AAgEA,MAAa,SAAS;IASpB,YAAY,SAAsB;QAChC,IAAI,SAAS,IAAI,IAAI,EAAE;YACrB,IAAI,CAAC,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;YAC1B,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAA;YACrB,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;YAC3B,IAAI,CAAC,MAAM,GAAG,EAAE,CAAA;YAChB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;YAC7B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAA;SACtB;aAAM;YACL,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAA;YACpB,IAAI,CAAC,GAAG,GAAG,SAAS,CAAC,GAAG,CAAA;YACxB,IAAI,CAAC,IAAI,GAAG,SAAS,CAAC,IAAI,CAAA;YAC1B,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC,MAAM,CAAA;YAC9B,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC,MAAM,CAAA;YAC9B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC,SAAS,CAAA;SACrC;IACH,CAAC;CACF;AA1BD,8BA0BC","sourcesContent":["import { LedgerError } from './common'\n\nexport interface ResponseBase {\n errorMessage: string\n returnCode: LedgerError\n}\n\nexport interface ResponseAddress extends ResponseBase {\n publicKey: Buffer\n address: Buffer\n}\n\nexport interface ResponseVersion extends ResponseBase {\n testMode: boolean\n major: number\n minor: number\n patch: number\n deviceLocked: boolean\n targetId: string\n}\n\nexport interface ResponseAppInfo extends ResponseBase {\n appName: string\n appVersion: string\n flagLen: number\n flagsValue: number\n flagRecovery: boolean\n flagSignedMcuCode: boolean\n flagOnboarded: boolean\n flagPINValidated: boolean\n}\n\nexport interface ResponseDeviceInfo extends ResponseBase {\n targetId: string\n seVersion: string\n flag: string\n mcuVersion: string\n}\n\nexport interface ResponseShieldedAddress extends ResponseBase {\n raw_pkd: Buffer\n bech32m_len: number\n bech32m_addr: Buffer\n}\n\nexport interface ResponseIncomingViewingKey extends ResponseBase {\n raw_ivk: Buffer\n}\n\nexport interface ResponseOutgoingViewingKey extends ResponseBase {\n raw_ovk: Buffer\n}\n\nexport interface ResponseNullifier extends ResponseBase {\n raw_nf: Buffer\n}\n\nexport interface ISignature {\n raw: Buffer\n salt: Buffer\n hashes: Buffer[]\n pubkey: Buffer\n signature: Buffer | null\n}\nexport class Signature implements ISignature {\n raw: Buffer\n salt: Buffer\n hashes: Buffer[]\n pubkey: Buffer\n signature: Buffer | null\n\n isFilled: boolean\n\n constructor(signature?: ISignature) {\n if (signature == null) {\n this.raw = Buffer.from([])\n this.isFilled = false\n this.salt = Buffer.from([])\n this.hashes = []\n this.pubkey = Buffer.from([])\n this.signature = null\n } else {\n this.isFilled = true\n this.raw = signature.raw\n this.salt = signature.salt\n this.hashes = signature.hashes\n this.pubkey = signature.pubkey\n this.signature = signature.signature\n }\n }\n}\n\nexport interface ResponseSign extends ResponseBase {\n wrapperSignature: Signature\n rawSignature: Signature\n}\n"]} \ No newline at end of file diff --git a/packages/ledger-namada/package.json b/packages/ledger-namada/package.json new file mode 100644 index 0000000000..b55ea7ac97 --- /dev/null +++ b/packages/ledger-namada/package.json @@ -0,0 +1,78 @@ +{ + "name": "@namada/ledger-namada", + "author": "Zondax AG", + "license": "Apache-2.0", + "version": "0.0.1", + "description": "Node API for the Namada App (Ledger Nano S/X/S+)", + "main": "./dist/index.js", + "private": false, + "typings": "./dist/index.d.ts", + "types": "./dist/index.d.ts", + "homepage": "https://github.com/zondax/ledger-namada", + "repository": { + "type": "git", + "url": "git+https://github.com/zondax/ledger-namada.git" + }, + "keywords": [ + "Zondax", + "Ledger", + "Javascript", + "Namada" + ], + "scripts": { + "build": "tsc", + "copy-files": "copyfiles -u 0 src/**/*.proto dist/", + "test:integration": "yarn build && jest -t 'Integration'", + "test:key-derivation": "yarn build && jest -t 'KeyDerivation'", + "supported": "ts-node src/cmd/cli.ts supported", + "linter": "eslint --max-warnings 0 .", + "linter:fix": "yarn linter --fix", + "format": "prettier -w ." + }, + "bugs": { + "url": "https://github.com/zondax/ledger-namada/issues" + }, + "dependencies": { + "@ledgerhq/hw-transport": "^6.28.2" + }, + "devDependencies": { + "@types/ledgerhq__hw-transport": "^4.21.4", + "@typescript-eslint/eslint-plugin": "^5.59.2", + "@typescript-eslint/parser": "^5.59.2", + "bip32": "^4.0.0", + "bip39": "^3.1.0", + "core-js": "^3.30.1", + "crypto-js": "4.1.1", + "eslint": "^8.40.0", + "eslint-config-prettier": "^8.8.0", + "eslint-plugin-import": "^2.27.5", + "eslint-plugin-jest": "^27.2.1", + "eslint-plugin-prettier": "^4.0.0", + "jest": "^29.5.0", + "leb128": "^0.0.5", + "prettier": "^2.8.8", + "secp256k1": "^5.0.0", + "typescript": "^5.0.4" + }, + "moduleDirectories": [ + "node_modules", + "dist" + ], + "postcss": { + "plugins": { + "autoprefixer": {} + } + }, + "browserslist": [ + "> 1%", + "last 2 versions" + ], + "files": [ + "dist/*", + "LICENSE", + "yarn.lock" + ], + "publishConfig": { + "access": "public" + } +} diff --git a/packages/shared/lib/src/query.rs b/packages/shared/lib/src/query.rs index 33bc13187a..111f4d3a01 100644 --- a/packages/shared/lib/src/query.rs +++ b/packages/shared/lib/src/query.rs @@ -1,11 +1,13 @@ use masp_primitives::{transaction::components::Amount, zip32::ExtendedFullViewingKey}; use namada::ledger::masp::ShieldedContext; use namada::ledger::queries::RPC; -use namada::ledger::rpc::get_token_balance; -use namada::types::address::Address; -use namada::types::masp::ExtendedViewingKey; -use namada::types::token::{self, TokenAddress}; -use namada::types::uint::I256; +use namada::ledger::rpc::{get_public_key, get_token_balance}; +use namada::types::{ + address::Address, + masp::ExtendedViewingKey, + token::{self, TokenAddress}, + uint::I256, +}; use std::collections::{HashMap, HashSet}; use std::str::FromStr; use wasm_bindgen::prelude::*; @@ -212,4 +214,16 @@ impl Query { to_js_result(result) } + + pub async fn query_public_key(&self, address: &str) -> Result { + let addr = Address::from_str(address).map_err(JsError::from)?; + let pk = get_public_key(&self.client, &addr).await; + + let result = match pk { + Some(v) => Some(v.to_string()), + None => None, + }; + + to_js_result(result) + } } diff --git a/packages/shared/lib/src/sdk/mod.rs b/packages/shared/lib/src/sdk/mod.rs index e1ee893674..f970e7c904 100644 --- a/packages/shared/lib/src/sdk/mod.rs +++ b/packages/shared/lib/src/sdk/mod.rs @@ -4,7 +4,7 @@ use crate::{ sdk::masp::WebShieldedUtils, utils::{set_panic_hook, to_bytes}, }; -use borsh::BorshSerialize; +use borsh::{BorshDeserialize, BorshSerialize}; use namada::{ ledger::{ args, @@ -13,15 +13,22 @@ use namada::{ wallet::{Store, Wallet}, }, proto::{Section, Signature, Tx}, - types::{key::common::PublicKey, key::common::SecretKey as SK, key::ed25519::SecretKey}, + types::key::common::PublicKey, }; -use std::str::FromStr; use wasm_bindgen::{prelude::wasm_bindgen, JsError, JsValue}; pub mod masp; mod tx; mod wallet; +// Require that a public key is present +fn validate_pk(pk: Option) -> Result { + match pk { + Some(v) => Ok(v), + None => Err(JsError::new("No public key was provided!")), + } +} + /// Represents the Sdk public API. #[wasm_bindgen] pub struct Sdk { @@ -113,7 +120,7 @@ impl Sdk { .await?; // Sign and submit reveal pk - if let Some((mut rtx, _, pk)) = reveal_pk { + if let Some((mut rtx, _, _)) = reveal_pk { // Sign the reveal public key transaction with the fee payer signing::sign_tx(&mut self.wallet, &mut rtx, &args, &pk).await?; // Submit the reveal public key transaction first @@ -149,6 +156,46 @@ impl Sdk { Ok(()) } + /// Contruct reveal pk data for external signers, returns byte array + pub async fn build_reveal_pk(&mut self, tx_msg: &[u8]) -> Result { + let args = tx::reveal_pk_tx_args(tx_msg)?; + + let reveal_pk = namada::ledger::tx::build_reveal_pk( + &self.client, + &mut self.wallet, + args::RevealPk { + tx: args.tx.clone(), + public_key: args.public_key.clone(), + }, + ) + .await?; + + let bytes = match reveal_pk { + Some(v) => v.0.try_to_vec().map_err(JsError::from)?, + None => vec![], + }; + + to_js_result(bytes) + } + + /// Submit signed reveal pk tx + pub async fn submit_signed_reveal_pk( + &mut self, + tx_msg: &[u8], + tx_bytes: &[u8], + wrapper_sig_bytes: &[u8], + raw_sig_bytes: &[u8], + ) -> Result<(), JsError> { + let reveal_pk_tx = self.sign_tx(tx_bytes, wrapper_sig_bytes, raw_sig_bytes)?; + let args = tx::reveal_pk_tx_args(tx_msg).map_err(JsError::from)?; + + namada::ledger::tx::process_tx(&self.client, &mut self.wallet, &args.tx, reveal_pk_tx) + .await + .map_err(JsError::from)?; + + Ok(()) + } + /// Contruct transfer data for external signers, returns byte array pub async fn build_transfer(&mut self, tx_msg: &[u8]) -> Result { let args = tx::transfer_tx_args(tx_msg, None, None)?; @@ -167,45 +214,63 @@ impl Sdk { to_js_result(bytes) } + /// Contruct bond data for external signers, returns byte array + pub async fn build_bond(&mut self, tx_msg: &[u8]) -> Result { + let args = tx::bond_tx_args(tx_msg, None)?; + + let bond = namada::ledger::tx::build_bond(&self.client, &mut self.wallet, args.clone()) + .await + .map_err(JsError::from)?; + + let bytes = bond.0.try_to_vec().map_err(JsError::from)?; + + to_js_result(bytes) + } + + /// Contruct unbond data for external signers, returns byte array + pub async fn build_unbond(&mut self, tx_msg: &[u8]) -> Result { + let args = tx::unbond_tx_args(tx_msg, None)?; + + let unbond = namada::ledger::tx::build_unbond(&self.client, &mut self.wallet, args.clone()) + .await + .map_err(JsError::from)?; + + let bytes = unbond.0.try_to_vec().map_err(JsError::from)?; + + to_js_result(bytes) + } + // Append signatures and return tx bytes - pub fn sign_tx( + fn sign_tx( &self, tx_bytes: &[u8], - data_key: String, - header_key: String, - ) -> Result { - let mut tx: Tx = Tx::try_from(tx_bytes).map_err(JsError::from)?; - let data_secret = SecretKey::from_str(&data_key).map_err(JsError::from)?; - let header_secret = SecretKey::from_str(&header_key).map_err(JsError::from)?; - - // Sign over the transaction data - tx.add_section(Section::Signature(Signature::new( - vec![*tx.data_sechash(), *tx.code_sechash()], - &SK::Ed25519(data_secret), - ))); + wrapper_sig_bytes: &[u8], + raw_sig_bytes: &[u8], + ) -> Result { + let mut tx: Tx = Tx::try_from_slice(tx_bytes).map_err(JsError::from)?; - tx.protocol_filter(); + let wrapper_sig = Signature::try_from_slice(wrapper_sig_bytes).map_err(JsError::from)?; + let raw_sig = Signature::try_from_slice(raw_sig_bytes).map_err(JsError::from)?; - // Then sign over the bound wrapper - tx.add_section(Section::Signature(Signature::new( - tx.sechashes(), - &SK::Ed25519(header_secret), - ))); + tx.add_section(Section::Signature(raw_sig)); + tx.protocol_filter(); + tx.add_section(Section::Signature(wrapper_sig)); - let bytes = tx.try_to_vec().map_err(|e| JsError::from(e))?; - to_js_result(Vec::from(bytes)) + Ok(tx) } /// Submit signed transfer tx pub async fn submit_signed_transfer( &mut self, - pk: String, tx_msg: &[u8], tx_bytes: &[u8], + wrapper_sig_bytes: &[u8], + raw_sig_bytes: &[u8], ) -> Result<(), JsError> { - let transfer_tx = Tx::try_from(tx_bytes).map_err(|e| JsError::from(e))?; - let args = tx::transfer_tx_args(tx_msg, None, None).map_err(|e| JsError::from(e))?; - let pk = PublicKey::from_str(&pk).map_err(JsError::from)?; + let transfer_tx = self.sign_tx(tx_bytes, wrapper_sig_bytes, raw_sig_bytes)?; + let args = tx::transfer_tx_args(tx_msg, None, None).map_err(JsError::from)?; + let verification_key = args.tx.verification_key.clone(); + let pk = validate_pk(verification_key)?; self.submit_reveal_pk(&args.tx, transfer_tx.clone(), &pk) .await?; @@ -272,6 +337,25 @@ impl Sdk { Ok(()) } + /// Submit signed bond + pub async fn submit_signed_bond( + &mut self, + tx_msg: &[u8], + tx_bytes: &[u8], + wrapper_sig_bytes: &[u8], + raw_sig_bytes: &[u8], + ) -> Result<(), JsError> { + let bond_tx = self.sign_tx(tx_bytes, wrapper_sig_bytes, raw_sig_bytes)?; + let args = tx::bond_tx_args(tx_msg, None).map_err(JsError::from)?; + + namada::ledger::tx::process_tx(&self.client, &mut self.wallet, &args.tx, bond_tx) + .await + .map_err(JsError::from)?; + + Ok(()) + } + + /// Submit unbond pub async fn submit_unbond( &mut self, tx_msg: &[u8], @@ -288,6 +372,29 @@ impl Sdk { Ok(()) } + + /// Submit signed unbond tx + pub async fn submit_signed_unbond( + &mut self, + tx_msg: &[u8], + tx_bytes: &[u8], + wrapper_sig_bytes: &[u8], + raw_sig_bytes: &[u8], + ) -> Result<(), JsError> { + let bond_tx = self.sign_tx(tx_bytes, wrapper_sig_bytes, raw_sig_bytes)?; + let args = tx::unbond_tx_args(tx_msg, None).map_err(JsError::from)?; + let verification_key = args.tx.verification_key.clone(); + let pk = validate_pk(verification_key)?; + + self.submit_reveal_pk(&args.tx, bond_tx.clone(), &pk) + .await?; + + namada::ledger::tx::process_tx(&self.client, &mut self.wallet, &args.tx, bond_tx) + .await + .map_err(JsError::from)?; + + Ok(()) + } } #[wasm_bindgen(module = "/src/sdk/mod.js")] diff --git a/packages/shared/lib/src/sdk/tx.rs b/packages/shared/lib/src/sdk/tx.rs index 935d0e91d0..e887aaf799 100644 --- a/packages/shared/lib/src/sdk/tx.rs +++ b/packages/shared/lib/src/sdk/tx.rs @@ -34,6 +34,35 @@ pub struct SubmitBondMsg { tx: TxMsg, } +#[derive(BorshSerialize, BorshDeserialize)] +pub struct SubmitRevealPKMsg { + tx: TxMsg, + public_key: String, +} + +/// Maps serialized tx_msg into RevealPk args +/// +/// # Arguments +/// +/// * `tx_msg` - Borsh serialized tx_msg. +/// +/// # Errors +/// +/// Returns JsError if the tx_msg can't be deserialized or +/// Rust structs can't be created. +pub fn reveal_pk_tx_args(tx_msg: &[u8]) -> Result { + let tx_msg = SubmitRevealPKMsg::try_from_slice(tx_msg)?; + let SubmitRevealPKMsg { tx, public_key } = tx_msg; + let public_key = PK::Ed25519(PublicKey::from_str(&public_key).map_err(JsError::from)?); + + let args = args::RevealPk { + tx: tx_msg_into_args(tx, None)?, + public_key, + }; + + Ok(args) +} + /// Maps serialized tx_msg into BondTx args. /// /// # Arguments diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index af68f75bf1..9e1f4f0eba 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -8,7 +8,7 @@ type TimeoutOpts = { error?: string; }; -const DEFAULT_TIMEOUT = 5000; +const DEFAULT_TIMEOUT = 60000; const DEFAULT_OPTS: TimeoutOpts = { timeout: DEFAULT_TIMEOUT, @@ -23,19 +23,19 @@ const promiseWithTimeout = fn: (...args: U) => Promise, opts?: TimeoutOpts ) => - (...args: U): Promise => { - const { timeout, error } = { ...DEFAULT_OPTS, ...opts }; + (...args: U): Promise => { + const { timeout, error } = { ...DEFAULT_OPTS, ...opts }; - return new Promise(async (resolve, reject) => { - const t = setTimeout(() => { - reject(error); - }, timeout); + return new Promise(async (resolve, reject) => { + const t = setTimeout(() => { + reject(error); + }, timeout); - const res = await fn(...args); - clearTimeout(t); - resolve(res); - }); - }; + const res = await fn(...args); + clearTimeout(t); + resolve(res); + }); + }; export class Query extends RustQuery { query_balance = promiseWithTimeout(super.query_balance.bind(this), { diff --git a/packages/types/src/account.ts b/packages/types/src/account.ts index 223e6cd121..23628284d3 100644 --- a/packages/types/src/account.ts +++ b/packages/types/src/account.ts @@ -12,18 +12,25 @@ export enum AccountType { PrivateKey = "private-key", // Stored, stringified spending and viewing keys ShieldedKeys = "shielded-keys", + // Ledger account + Ledger = "ledger", } export type DerivedAccount = { id: string; chainId: string; address: string; + owner?: string; + publicKey?: string; alias: string; parentId?: string; path: Bip44Path; type: AccountType; }; -export type Account = Pick & { +export type Account = Pick< + DerivedAccount, + "address" | "alias" | "chainId" | "type" | "publicKey" +> & { isShielded: boolean; }; diff --git a/packages/types/src/events.ts b/packages/types/src/events.ts index 482c6ff477..2ebc3ffae3 100644 --- a/packages/types/src/events.ts +++ b/packages/types/src/events.ts @@ -6,6 +6,7 @@ export enum Events { TransferStarted = "namada-transfer-started", TransferCompleted = "namada-transfer-completed", UpdatedBalances = "namada-updated-balances", + UpdatedStaking = "namada-updated-staking", } // Keplr extension events diff --git a/packages/types/src/namada.ts b/packages/types/src/namada.ts index db47aaf323..e36c3cbd0d 100644 --- a/packages/types/src/namada.ts +++ b/packages/types/src/namada.ts @@ -1,4 +1,4 @@ -import { DerivedAccount } from "./account"; +import { AccountType, DerivedAccount } from "./account"; import { Chain } from "./chain"; import { Signer } from "./signer"; @@ -11,9 +11,16 @@ export interface Namada { suggestChain(chainConfig: Chain): Promise; chain: (chainId: string) => Promise; chains: () => Promise; - submitBond: (txMsg: string) => Promise; + submitBond: (props: { + txMsg: string; + type: AccountType; + publicKey?: string; + }) => Promise; submitUnbond: (txMsg: string) => Promise; - submitTransfer: (txMsg: string) => Promise; + submitTransfer: (props: { + txMsg: string; + type: AccountType; + }) => Promise; submitIbcTransfer: (txMsg: string) => Promise; encodeInitAccount: (props: { txMsg: string; diff --git a/packages/types/src/signer.ts b/packages/types/src/signer.ts index 163061072f..90f5b21037 100644 --- a/packages/types/src/signer.ts +++ b/packages/types/src/signer.ts @@ -1,4 +1,4 @@ -import { Account } from "./account"; +import { Account, AccountType } from "./account"; import { IbcTransferProps, InitAccountProps, @@ -9,9 +9,13 @@ import { export interface Signer { accounts: () => Promise; - submitBond(args: SubmitBondProps): Promise; + submitBond( + args: SubmitBondProps, + type: AccountType, + publicKey?: string + ): Promise; submitUnbond(args: SubmitUnbondProps): Promise; - submitTransfer(args: TransferProps): Promise; + submitTransfer(args: TransferProps, type: AccountType): Promise; submitIbcTransfer(args: IbcTransferProps): Promise; encodeInitAccount( args: InitAccountProps, diff --git a/packages/types/src/tx/schema/index.ts b/packages/types/src/tx/schema/index.ts index 65c1b1bf35..d66a77dc67 100644 --- a/packages/types/src/tx/schema/index.ts +++ b/packages/types/src/tx/schema/index.ts @@ -2,6 +2,7 @@ export * from "./account"; export * from "./ibcTransfer"; export * from "./transfer"; export * from "./bond"; +export * from "./revealPK"; export * from "./unbond"; import { AccountMsgValue } from "./account"; @@ -9,10 +10,12 @@ import { IbcTransferMsgValue } from "./ibcTransfer"; import { TransferMsgValue } from "./transfer"; import { SubmitBondMsgValue } from "./bond"; import { SubmitUnbondMsgValue } from "./unbond"; +import { SubmitRevealPKMsgValue } from "./revealPK"; export type Schema = | AccountMsgValue | IbcTransferMsgValue | TransferMsgValue | SubmitBondMsgValue - | SubmitUnbondMsgValue; + | SubmitUnbondMsgValue + | SubmitRevealPKMsgValue; diff --git a/packages/types/src/tx/schema/revealPK.ts b/packages/types/src/tx/schema/revealPK.ts new file mode 100644 index 0000000000..902d9b7c06 --- /dev/null +++ b/packages/types/src/tx/schema/revealPK.ts @@ -0,0 +1,17 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import { field } from "@dao-xyz/borsh"; +import { TxMsgValue } from "./tx"; +import { RevealPKProps } from "../types"; + +export class SubmitRevealPKMsgValue { + @field({ type: TxMsgValue }) + tx!: InstanceType; + + @field({ type: "string" }) + publicKey!: string; + + constructor(data: RevealPKProps) { + Object.assign(this, data); + this.tx = new TxMsgValue(data.tx); + } +} diff --git a/packages/types/src/tx/types.ts b/packages/types/src/tx/types.ts index a97304db1a..1312c3d29c 100644 --- a/packages/types/src/tx/types.ts +++ b/packages/types/src/tx/types.ts @@ -23,6 +23,11 @@ export type TxProps = { publicKey?: string; }; +export type RevealPKProps = { + publicKey: string; + tx: TxProps; +}; + export type TransferProps = { tx: TxProps; source: string; diff --git a/packages/utils/src/helpers/index.ts b/packages/utils/src/helpers/index.ts index e6ea41efa3..2bb3cf8c90 100644 --- a/packages/utils/src/helpers/index.ts +++ b/packages/utils/src/helpers/index.ts @@ -1,7 +1,11 @@ import { JsonRpcRequest } from "@cosmjs/json-rpc"; import { DateTime } from "luxon"; -import { JsonCompatibleArray, JsonCompatibleDictionary } from "@namada/types"; import BigNumber from "bignumber.js"; +import { + Bip44Path, + JsonCompatibleArray, + JsonCompatibleDictionary, +} from "@namada/types"; const MICRO_FACTOR = 1000000; // 1,000,000 @@ -172,3 +176,48 @@ export const Result = { return { ok: false as const, error }; }, }; + +/** + * Return a properly formatted BIP-044 path + * + * @param {number} coinType - SLIP-044 Coin designation + * @param {Bip44Path} bip44Path - path object + * @returns {string} + */ +export const makeBip44Path = ( + coinType: number, + bip44Path: Bip44Path +): string => { + const { account, change, index } = bip44Path; + const basePath = `m/44'/${coinType}'/${account}'/${change}`; + + return typeof index === "number" ? `${basePath}/${index}` : basePath; +}; + +/** + * Pick object parameters + */ +export function pick(obj: T, ...keys: K[]): Pick { + return keys.reduce((acc, val) => { + return (acc[val] = obj[val]), acc; + }, {} as Pick); +} + +/** + * Format url with query string params using an object + * + * @param {string} url + * @param {object} params + * @returns {string} + */ +export function paramsToUrl( + url: string, + params: Record +): string { + const queryString = new URLSearchParams(params).toString(); + + if (queryString) { + return `${url}?${queryString}`; + } + return url; +} diff --git a/yarn.lock b/yarn.lock index 878344e589..edabf01f2e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,11 @@ # yarn lockfile v1 +"@aashutoshrathi/word-wrap@^1.2.3": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" + integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== + "@ampproject/remapping@^2.1.0": version "2.1.2" resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.1.2.tgz#4edca94973ded9630d20101cd8559cedb8d8bd34" @@ -2866,6 +2871,18 @@ resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-0.11.3.tgz#a759863867befa7e583400d322652a3f44820924" integrity sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw== +"@eslint-community/eslint-utils@^4.2.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.4.0": + version "4.5.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.5.1.tgz#cdd35dce4fa1a89a4fd42b1599eb35b3af408884" + integrity sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ== + "@eslint/eslintrc@^1.2.1": version "1.2.1" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.2.1.tgz#8b5e1c49f4077235516bc9ec7d41378c0f69b8c6" @@ -2941,6 +2958,26 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" +"@eslint/eslintrc@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.0.tgz#82256f164cc9e0b59669efc19d57f8092706841d" + integrity sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.6.0" + globals "^13.19.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/js@8.44.0": + version "8.44.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.44.0.tgz#961a5903c74139390478bdc808bcde3fc45ab7af" + integrity sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw== + "@humanwhocodes/config-array@^0.10.4": version "0.10.4" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.10.4.tgz#01e7366e57d2ad104feea63e72248f22015c520c" @@ -2959,6 +2996,15 @@ debug "^4.1.1" minimatch "^3.0.4" +"@humanwhocodes/config-array@^0.11.10": + version "0.11.10" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.10.tgz#5a3ffe32cc9306365fb3fd572596cd602d5e12d2" + integrity sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ== + dependencies: + "@humanwhocodes/object-schema" "^1.2.1" + debug "^4.1.1" + minimatch "^3.0.5" + "@humanwhocodes/config-array@^0.11.6": version "0.11.7" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.7.tgz#38aec044c6c828f6ed51d5d7ae3d9b9faf6dbb0f" @@ -3122,6 +3168,18 @@ jest-util "^29.4.1" slash "^3.0.0" +"@jest/console@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.6.1.tgz#b48ba7b9c34b51483e6d590f46e5837f1ab5f639" + integrity sha512-Aj772AYgwTSr5w8qnyoJ0eDYvN6bMsH3ORH1ivMotrInHLKdUz6BDlaEXHdM6kODaBIkNIyQGzsMvRdOv7VG7Q== + dependencies: + "@jest/types" "^29.6.1" + "@types/node" "*" + chalk "^4.0.0" + jest-message-util "^29.6.1" + jest-util "^29.6.1" + slash "^3.0.0" + "@jest/core@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.5.1.tgz#267ac5f704e09dc52de2922cbf3af9edcd64b626" @@ -3327,6 +3385,40 @@ slash "^3.0.0" strip-ansi "^6.0.0" +"@jest/core@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.6.1.tgz#fac0d9ddf320490c93356ba201451825231e95f6" + integrity sha512-CcowHypRSm5oYQ1obz1wfvkjZZ2qoQlrKKvlfPwh5jUXVU12TWr2qMeH8chLMuTFzHh5a1g2yaqlqDICbr+ukQ== + dependencies: + "@jest/console" "^29.6.1" + "@jest/reporters" "^29.6.1" + "@jest/test-result" "^29.6.1" + "@jest/transform" "^29.6.1" + "@jest/types" "^29.6.1" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + ci-info "^3.2.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + jest-changed-files "^29.5.0" + jest-config "^29.6.1" + jest-haste-map "^29.6.1" + jest-message-util "^29.6.1" + jest-regex-util "^29.4.3" + jest-resolve "^29.6.1" + jest-resolve-dependencies "^29.6.1" + jest-runner "^29.6.1" + jest-runtime "^29.6.1" + jest-snapshot "^29.6.1" + jest-util "^29.6.1" + jest-validate "^29.6.1" + jest-watcher "^29.6.1" + micromatch "^4.0.4" + pretty-format "^29.6.1" + slash "^3.0.0" + strip-ansi "^6.0.0" + "@jest/environment@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.5.1.tgz#d7425820511fe7158abbecc010140c3fd3be9c74" @@ -3397,6 +3489,16 @@ "@types/node" "*" jest-mock "^29.4.1" +"@jest/environment@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.6.1.tgz#ee358fff2f68168394b4a50f18c68278a21fe82f" + integrity sha512-RMMXx4ws+Gbvw3DfLSuo2cfQlK7IwGbpuEWXCqyYDcqYTI+9Ju3a5hDnXaxjNsa6uKh9PQF2v+qg+RLe63tz5A== + dependencies: + "@jest/fake-timers" "^29.6.1" + "@jest/types" "^29.6.1" + "@types/node" "*" + jest-mock "^29.6.1" + "@jest/expect-utils@^28.1.3": version "28.1.3" resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-28.1.3.tgz#58561ce5db7cd253a7edddbc051fb39dda50f525" @@ -3432,6 +3534,13 @@ dependencies: jest-get-type "^29.2.0" +"@jest/expect-utils@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.6.1.tgz#ab83b27a15cdd203fe5f68230ea22767d5c3acc5" + integrity sha512-o319vIf5pEMx0LmzSxxkYYxo4wrRLKHq9dP1yJU7FoPTB0LfAKSz8SWD6D/6U3v/O52t9cF5t+MeJiRsfk7zMw== + dependencies: + jest-get-type "^29.4.3" + "@jest/expect@^28.1.3": version "28.1.3" resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-28.1.3.tgz#9ac57e1d4491baca550f6bdbd232487177ad6a72" @@ -3472,6 +3581,14 @@ expect "^29.4.1" jest-snapshot "^29.4.1" +"@jest/expect@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.6.1.tgz#fef18265188f6a97601f1ea0a2912d81a85b4657" + integrity sha512-N5xlPrAYaRNyFgVf2s9Uyyvr795jnB6rObuPx4QFvNJz8aAjpZUDfO4bh5G/xuplMID8PrnuF1+SfSyDxhsgYg== + dependencies: + expect "^29.6.1" + jest-snapshot "^29.6.1" + "@jest/fake-timers@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.5.1.tgz#76979745ce0579c8a94a4678af7a748eda8ada74" @@ -3556,6 +3673,18 @@ jest-mock "^29.4.1" jest-util "^29.4.1" +"@jest/fake-timers@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.6.1.tgz#c773efddbc61e1d2efcccac008139f621de57c69" + integrity sha512-RdgHgbXyosCDMVYmj7lLpUwXA4c69vcNzhrt69dJJdf8azUrpRh3ckFCaTPNjsEeRi27Cig0oKDGxy5j7hOgHg== + dependencies: + "@jest/types" "^29.6.1" + "@sinonjs/fake-timers" "^10.0.2" + "@types/node" "*" + jest-message-util "^29.6.1" + jest-mock "^29.6.1" + jest-util "^29.6.1" + "@jest/globals@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.5.1.tgz#7ac06ce57ab966566c7963431cef458434601b2b" @@ -3614,6 +3743,16 @@ "@jest/types" "^29.4.1" jest-mock "^29.4.1" +"@jest/globals@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.6.1.tgz#c8a8923e05efd757308082cc22893d82b8aa138f" + integrity sha512-2VjpaGy78JY9n9370H8zGRCFbYVWwjY6RdDMhoJHa1sYfwe6XM/azGN0SjY8kk7BOZApIejQ1BFPyH7FPG0w3A== + dependencies: + "@jest/environment" "^29.6.1" + "@jest/expect" "^29.6.1" + "@jest/types" "^29.6.1" + jest-mock "^29.6.1" + "@jest/reporters@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.5.1.tgz#ceda7be96170b03c923c37987b64015812ffec04" @@ -3799,6 +3938,36 @@ strip-ansi "^6.0.0" v8-to-istanbul "^9.0.1" +"@jest/reporters@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.6.1.tgz#3325a89c9ead3cf97ad93df3a427549d16179863" + integrity sha512-9zuaI9QKr9JnoZtFQlw4GREQbxgmNYXU6QuWtmuODvk5nvPUeBYapVR/VYMyi2WSx3jXTLJTJji8rN6+Cm4+FA== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@jest/console" "^29.6.1" + "@jest/test-result" "^29.6.1" + "@jest/transform" "^29.6.1" + "@jest/types" "^29.6.1" + "@jridgewell/trace-mapping" "^0.3.18" + "@types/node" "*" + chalk "^4.0.0" + collect-v8-coverage "^1.0.0" + exit "^0.1.2" + glob "^7.1.3" + graceful-fs "^4.2.9" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-instrument "^5.1.0" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.1.3" + jest-message-util "^29.6.1" + jest-util "^29.6.1" + jest-worker "^29.6.1" + slash "^3.0.0" + string-length "^4.0.1" + strip-ansi "^6.0.0" + v8-to-istanbul "^9.0.1" + "@jest/schemas@^28.1.3": version "28.1.3" resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-28.1.3.tgz#ad8b86a66f11f33619e3d7e1dcddd7f2d40ff905" @@ -3820,6 +3989,13 @@ dependencies: "@sinclair/typebox" "^0.25.16" +"@jest/schemas@^29.6.0": + version "29.6.0" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.0.tgz#0f4cb2c8e3dca80c135507ba5635a4fd755b0040" + integrity sha512-rxLjXyJBTL4LQeJW3aKo0M/+GkCOXsO+8i9Iu7eDb6KwtP65ayoDsitrdPBtujxQ88k4wI2FNYfa6TOGwSn6cQ== + dependencies: + "@sinclair/typebox" "^0.27.8" + "@jest/source-map@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-27.5.1.tgz#6608391e465add4205eae073b55e7f279e04e8cf" @@ -3856,6 +4032,15 @@ callsites "^3.0.0" graceful-fs "^4.2.9" +"@jest/source-map@^29.6.0": + version "29.6.0" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.6.0.tgz#bd34a05b5737cb1a99d43e1957020ac8e5b9ddb1" + integrity sha512-oA+I2SHHQGxDCZpbrsCQSoMLb3Bz547JnM+jUr9qEbuw0vQlWZfpPS7CO9J7XiwKicEz9OFn/IYoLkkiUD7bzA== + dependencies: + "@jridgewell/trace-mapping" "^0.3.18" + callsites "^3.0.0" + graceful-fs "^4.2.9" + "@jest/test-result@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.5.1.tgz#56a6585fa80f7cdab72b8c5fc2e871d03832f5bb" @@ -3916,6 +4101,16 @@ "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" +"@jest/test-result@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.6.1.tgz#850e565a3f58ee8ca6ec424db00cb0f2d83c36ba" + integrity sha512-Ynr13ZRcpX6INak0TPUukU8GWRfm/vAytE3JbJNGAvINySWYdfE7dGZMbk36oVuK4CigpbhMn8eg1dixZ7ZJOw== + dependencies: + "@jest/console" "^29.6.1" + "@jest/types" "^29.6.1" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" + "@jest/test-sequencer@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz#4057e0e9cea4439e544c6353c6affe58d095745b" @@ -3976,6 +4171,16 @@ jest-haste-map "^29.4.1" slash "^3.0.0" +"@jest/test-sequencer@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.6.1.tgz#e3e582ee074dd24ea9687d7d1aaf05ee3a9b068e" + integrity sha512-oBkC36PCDf/wb6dWeQIhaviU0l5u6VCsXa119yqdUosYAt7/FbQU2M2UoziO3igj/HBDEgp57ONQ3fm0v9uyyg== + dependencies: + "@jest/test-result" "^29.6.1" + graceful-fs "^4.2.9" + jest-haste-map "^29.6.1" + slash "^3.0.0" + "@jest/transform@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.5.1.tgz#6c3501dcc00c4c08915f292a600ece5ecfe1f409" @@ -4102,6 +4307,27 @@ slash "^3.0.0" write-file-atomic "^5.0.0" +"@jest/transform@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.6.1.tgz#acb5606019a197cb99beda3c05404b851f441c92" + integrity sha512-URnTneIU3ZjRSaf906cvf6Hpox3hIeJXRnz3VDSw5/X93gR8ycdfSIEy19FlVx8NFmpN7fe3Gb1xF+NjXaQLWg== + dependencies: + "@babel/core" "^7.11.6" + "@jest/types" "^29.6.1" + "@jridgewell/trace-mapping" "^0.3.18" + babel-plugin-istanbul "^6.1.1" + chalk "^4.0.0" + convert-source-map "^2.0.0" + fast-json-stable-stringify "^2.1.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.6.1" + jest-regex-util "^29.4.3" + jest-util "^29.6.1" + micromatch "^4.0.4" + pirates "^4.0.4" + slash "^3.0.0" + write-file-atomic "^4.0.2" + "@jest/types@^24.9.0": version "24.9.0" resolved "https://registry.yarnpkg.com/@jest/types/-/types-24.9.0.tgz#63cb26cb7500d069e5a389441a7c6ab5e909fc59" @@ -4194,6 +4420,18 @@ "@types/yargs" "^17.0.8" chalk "^4.0.0" +"@jest/types@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.1.tgz#ae79080278acff0a6af5eb49d063385aaa897bf2" + integrity sha512-tPKQNMPuXgvdOn2/Lg9HNfUvjYVGolt04Hp03f5hAk878uwOLikN+JzeLY0HcVgKgFl9Hs3EIqpu3WX27XNhnw== + dependencies: + "@jest/schemas" "^29.6.0" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + "@jridgewell/gen-mapping@^0.3.2": version "0.3.2" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" @@ -4203,6 +4441,11 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" +"@jridgewell/resolve-uri@3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" + integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== + "@jridgewell/resolve-uri@^3.0.3": version "3.0.5" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz#68eb521368db76d040a6315cdb24bf2483037b9c" @@ -4213,6 +4456,11 @@ resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== +"@jridgewell/sourcemap-codec@1.4.14": + version "1.4.14" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" + integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== + "@jridgewell/sourcemap-codec@^1.4.10": version "1.4.11" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz#771a1d8d744eeb71b6adb35808e1a6c7b9b8c8ec" @@ -4242,6 +4490,14 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" +"@jridgewell/trace-mapping@^0.3.18": + version "0.3.18" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6" + integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA== + dependencies: + "@jridgewell/resolve-uri" "3.1.0" + "@jridgewell/sourcemap-codec" "1.4.14" + "@keplr-wallet/types@^0.10.19": version "0.10.19" resolved "https://registry.yarnpkg.com/@keplr-wallet/types/-/types-0.10.19.tgz#cba3b7eb0012bfde87d5873ded39c3be95719dfc" @@ -4416,6 +4672,11 @@ resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.0.tgz#085fd70f6d7d9d109671090ccae1d3bec62554a1" integrity sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg== +"@noble/hashes@^1.2.0": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.1.tgz#8831ef002114670c603c458ab8b11328406953a9" + integrity sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA== + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -4594,6 +4855,11 @@ resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.1.1.tgz#782fa5da44c4f38ae9fd38e9184b54e451936118" integrity sha512-BUyKJGdDWqvWC5GEhyOiUrGNi9iJUr4CU0O2WxJL6QJhHeeA/NVBalH+FeK0r/x/W0rPymXt5s78TDS7d6lCwg== +"@scure/base@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.1.tgz#ebb651ee52ff84f420097055f4bf46cfba403938" + integrity sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA== + "@sinclair/typebox@^0.24.1": version "0.24.28" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.28.tgz#15aa0b416f82c268b1573ab653e4413c965fe794" @@ -4604,6 +4870,11 @@ resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.21.tgz#763b05a4b472c93a8db29b2c3e359d55b29ce272" integrity sha512-gFukHN4t8K4+wVC+ECqeqwzBDeFeTzBXroBTqE6vcWrQGbEUpHO7LYdG0f4xnvYq4VOEwITSlHlp0JBAIFMS/g== +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== + "@sinonjs/commons@^1.7.0": version "1.8.3" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" @@ -5345,6 +5616,13 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= +"@types/ledgerhq__hw-transport@^4.21.4": + version "4.21.4" + resolved "https://registry.yarnpkg.com/@types/ledgerhq__hw-transport/-/ledgerhq__hw-transport-4.21.4.tgz#3a78a02d2b51d2b0dd8099412d5567d21118225c" + integrity sha512-vep+6yZnGv6owAthIY0w3f72w4dJIb4+yE5PCHveInTlZE9wukvU6Wc5Eig0OUUxcdhTazzeZx1xUaNVLqyQSg== + dependencies: + "@types/node" "*" + "@types/long@^4.0.1": version "4.0.2" resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a" @@ -5501,6 +5779,11 @@ dependencies: "@types/node" "*" +"@types/semver@^7.3.12": + version "7.5.0" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.0.tgz#591c1ce3a702c45ee15f47a42ade72c2fd78978a" + integrity sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw== + "@types/serve-index@^1.9.1": version "1.9.1" resolved "https://registry.yarnpkg.com/@types/serve-index/-/serve-index-1.9.1.tgz#1b5e85370a192c01ec6cec4735cf2917337a6278" @@ -5700,6 +5983,22 @@ semver "^7.3.5" tsutils "^3.21.0" +"@typescript-eslint/eslint-plugin@^5.59.2": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz#aeef0328d172b9e37d9bab6dbc13b87ed88977db" + integrity sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag== + dependencies: + "@eslint-community/regexpp" "^4.4.0" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/type-utils" "5.62.0" + "@typescript-eslint/utils" "5.62.0" + debug "^4.3.4" + graphemer "^1.4.0" + ignore "^5.2.0" + natural-compare-lite "^1.4.0" + semver "^7.3.7" + tsutils "^3.21.0" + "@typescript-eslint/experimental-utils@^5.0.0": version "5.15.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-5.15.0.tgz#407bbbdf1d11d24de81cfdf556b3a9f4252ba4ae" @@ -5717,6 +6016,16 @@ "@typescript-eslint/typescript-estree" "5.15.0" debug "^4.3.2" +"@typescript-eslint/parser@^5.59.2": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.62.0.tgz#1b63d082d849a2fcae8a569248fbe2ee1b8a56c7" + integrity sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA== + dependencies: + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/typescript-estree" "5.62.0" + debug "^4.3.4" + "@typescript-eslint/scope-manager@5.15.0": version "5.15.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.15.0.tgz#d97afab5e0abf4018d1289bd711be21676cdd0ee" @@ -5725,6 +6034,14 @@ "@typescript-eslint/types" "5.15.0" "@typescript-eslint/visitor-keys" "5.15.0" +"@typescript-eslint/scope-manager@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz#d9457ccc6a0b8d6b37d0eb252a23022478c5460c" + integrity sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w== + dependencies: + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" + "@typescript-eslint/type-utils@5.15.0": version "5.15.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.15.0.tgz#d2c02eb2bdf54d0a645ba3a173ceda78346cf248" @@ -5734,11 +6051,26 @@ debug "^4.3.2" tsutils "^3.21.0" +"@typescript-eslint/type-utils@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz#286f0389c41681376cdad96b309cedd17d70346a" + integrity sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew== + dependencies: + "@typescript-eslint/typescript-estree" "5.62.0" + "@typescript-eslint/utils" "5.62.0" + debug "^4.3.4" + tsutils "^3.21.0" + "@typescript-eslint/types@5.15.0": version "5.15.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.15.0.tgz#c7bdd103843b1abae97b5518219d3e2a0d79a501" integrity sha512-yEiTN4MDy23vvsIksrShjNwQl2vl6kJeG9YkVJXjXZnkJElzVK8nfPsWKYxcsGWG8GhurYXP4/KGj3aZAxbeOA== +"@typescript-eslint/types@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" + integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== + "@typescript-eslint/typescript-estree@5.15.0": version "5.15.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.15.0.tgz#81513a742a9c657587ad1ddbca88e76c6efb0aac" @@ -5752,6 +6084,19 @@ semver "^7.3.5" tsutils "^3.21.0" +"@typescript-eslint/typescript-estree@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b" + integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA== + dependencies: + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.3.7" + tsutils "^3.21.0" + "@typescript-eslint/utils@5.15.0", "@typescript-eslint/utils@^5.13.0": version "5.15.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.15.0.tgz#468510a0974d3ced8342f37e6c662778c277f136" @@ -5764,6 +6109,20 @@ eslint-scope "^5.1.1" eslint-utils "^3.0.0" +"@typescript-eslint/utils@5.62.0", "@typescript-eslint/utils@^5.10.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86" + integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@types/json-schema" "^7.0.9" + "@types/semver" "^7.3.12" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/typescript-estree" "5.62.0" + eslint-scope "^5.1.1" + semver "^7.3.7" + "@typescript-eslint/visitor-keys@5.15.0": version "5.15.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.15.0.tgz#5669739fbf516df060f978be6a6dce75855a8027" @@ -5772,6 +6131,14 @@ "@typescript-eslint/types" "5.15.0" eslint-visitor-keys "^3.0.0" +"@typescript-eslint/visitor-keys@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e" + integrity sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw== + dependencies: + "@typescript-eslint/types" "5.62.0" + eslint-visitor-keys "^3.3.0" + "@webassemblyjs/ast@1.11.1": version "1.11.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7" @@ -5935,13 +6302,6 @@ resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== -"@zondax/ledger-namada@^0.0.2": - version "0.0.2" - resolved "https://registry.yarnpkg.com/@zondax/ledger-namada/-/ledger-namada-0.0.2.tgz#17d9fc66c8eb5d1436a8fa6b030732219af3d4c0" - integrity sha512-F/rsDAsOclrIPFbGMDFIxD+1gDbGyIKZXtZ9yJD9DMYA8d7rkWzmGdHo/uxXCnzJDHzsM04BI1tvH0qDhmKuaw== - dependencies: - "@ledgerhq/hw-transport" "^6.28.2" - abab@^2.0.3, abab@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" @@ -6025,6 +6385,11 @@ acorn@^8.8.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== +acorn@^8.9.0: + version "8.10.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" + integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== + address@^1.0.1, address@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6" @@ -6181,6 +6546,14 @@ aria-query@^5.0.0: resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.0.0.tgz#210c21aaf469613ee8c9a62c7f86525e058db52c" integrity sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg== +array-buffer-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz#fabe8bc193fea865f317fe7807085ee0dee5aead" + integrity sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A== + dependencies: + call-bind "^1.0.2" + is-array-buffer "^3.0.1" + array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" @@ -6202,6 +6575,17 @@ array-includes@^3.1.3, array-includes@^3.1.4: get-intrinsic "^1.1.1" is-string "^1.0.7" +array-includes@^3.1.6: + version "3.1.6" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.6.tgz#9e9e720e194f198266ba9e18c29e6a9b0e4b225f" + integrity sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + get-intrinsic "^1.1.3" + is-string "^1.0.7" + array-union@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" @@ -6216,6 +6600,16 @@ array.prototype.flat@^1.2.5: define-properties "^1.1.3" es-abstract "^1.19.0" +array.prototype.flat@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz#ffc6576a7ca3efc2f46a143b9d1dda9b4b3cf5e2" + integrity sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + es-shim-unscopables "^1.0.0" + array.prototype.flatmap@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.2.5.tgz#908dc82d8a406930fdf38598d51e7411d18d4446" @@ -6225,6 +6619,16 @@ array.prototype.flatmap@^1.2.5: define-properties "^1.1.3" es-abstract "^1.19.0" +array.prototype.flatmap@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz#1aae7903c2100433cb8261cd4ed310aab5c4a183" + integrity sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + es-shim-unscopables "^1.0.0" + asap@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" @@ -6392,6 +6796,19 @@ babel-jest@^29.4.1: graceful-fs "^4.2.9" slash "^3.0.0" +babel-jest@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.6.1.tgz#a7141ad1ed5ec50238f3cd36127636823111233a" + integrity sha512-qu+3bdPEQC6KZSPz+4Fyjbga5OODNcp49j6GKzG1EKbkfyJBxEYGVUmVGpwCSeGouG52R4EgYMLb6p9YeEEQ4A== + dependencies: + "@jest/transform" "^29.6.1" + "@types/babel__core" "^7.1.14" + babel-plugin-istanbul "^6.1.1" + babel-preset-jest "^29.5.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + slash "^3.0.0" + babel-loader@^8.2.3: version "8.2.3" resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.2.3.tgz#8986b40f1a64cacfcb4b8429320085ef68b1342d" @@ -6486,6 +6903,16 @@ babel-plugin-jest-hoist@^29.4.0: "@types/babel__core" "^7.1.14" "@types/babel__traverse" "^7.0.6" +babel-plugin-jest-hoist@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz#a97db437936f441ec196990c9738d4b88538618a" + integrity sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w== + dependencies: + "@babel/template" "^7.3.3" + "@babel/types" "^7.3.3" + "@types/babel__core" "^7.1.14" + "@types/babel__traverse" "^7.0.6" + babel-plugin-macros@^2.0.0: version "2.8.0" resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz#0f958a7cc6556b1e65344465d99111a1e5e10138" @@ -6636,6 +7063,14 @@ babel-preset-jest@^29.4.0: babel-plugin-jest-hoist "^29.4.0" babel-preset-current-node-syntax "^1.0.0" +babel-preset-jest@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz#57bc8cc88097af7ff6a5ab59d1cd29d52a5916e2" + integrity sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg== + dependencies: + babel-plugin-jest-hoist "^29.5.0" + babel-preset-current-node-syntax "^1.0.0" + babel-preset-react-app@^10.0.1: version "10.0.1" resolved "https://registry.yarnpkg.com/babel-preset-react-app/-/babel-preset-react-app-10.0.1.tgz#ed6005a20a24f2c88521809fa9aea99903751584" @@ -6663,7 +7098,7 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -base-x@^3.0.6: +base-x@^3.0.2, base-x@^3.0.6: version "3.0.9" resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.9.tgz#6349aaabb58526332de9f60995e548a53fe21320" integrity sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ== @@ -6720,6 +7155,16 @@ binary-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== +bip32@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/bip32/-/bip32-4.0.0.tgz#7fac3c05072188d2d355a4d6596b37188f06aa2f" + integrity sha512-aOGy88DDlVUhspIXJN+dVEtclhIsfAUppD43V0j40cPTld3pv/0X/MlrZSZ6jowIaQQzFwP8M6rFU2z2mVYjDQ== + dependencies: + "@noble/hashes" "^1.2.0" + "@scure/base" "^1.1.1" + typeforce "^1.11.5" + wif "^2.0.6" + bip39@^3.0.2: version "3.0.4" resolved "https://registry.yarnpkg.com/bip39/-/bip39-3.0.4.tgz#5b11fed966840b5e1b8539f0f54ab6392969b2a0" @@ -6730,6 +7175,13 @@ bip39@^3.0.2: pbkdf2 "^3.0.9" randombytes "^2.0.1" +bip39@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/bip39/-/bip39-3.1.0.tgz#c55a418deaf48826a6ceb34ac55b3ee1577e18a3" + integrity sha512-c9kiwdk45Do5GL0vJMe7tS95VjCii65mYAH7DfWl3uW8AVzXKQVUm64i3hzVybBDMp9r7j9iNxR85+ul8MdN/A== + dependencies: + "@noble/hashes" "^1.2.0" + bluebird@^3.5.1, bluebird@^3.5.5: version "3.7.2" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" @@ -6923,6 +7375,13 @@ bs-logger@0.x: dependencies: fast-json-stable-stringify "2.x" +bs58@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" + integrity sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw== + dependencies: + base-x "^3.0.2" + bs58@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/bs58/-/bs58-5.0.0.tgz#865575b4d13c09ea2a84622df6c8cbeb54ffc279" @@ -6930,6 +7389,15 @@ bs58@^5.0.0: dependencies: base-x "^4.0.0" +bs58check@<3.0.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/bs58check/-/bs58check-2.1.2.tgz#53b018291228d82a5aa08e7d796fdafda54aebfc" + integrity sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA== + dependencies: + bs58 "^4.0.0" + create-hash "^1.1.0" + safe-buffer "^5.1.2" + bser@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" @@ -7418,6 +7886,11 @@ core-js@^3.19.2: resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.21.1.tgz#f2e0ddc1fc43da6f904706e8e955bc19d06a0d94" integrity sha512-FRq5b/VMrWlrmCzwRrpDYNxyHP9BcAZC+xHJaqTgIE5091ZV1NTmyh0sGOg5XqpnHvR0svdy0sv1gWA1zmhxig== +core-js@^3.30.1: + version "3.31.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.31.1.tgz#f2b0eea9be9da0def2c5fece71064a7e5d687653" + integrity sha512-2sKLtfq1eFST7l7v62zaqXacPc7uG8ZAya8ogijLhTtaKNcpzpB4TMoTw2Si+8GYKRwFPMMtUT0263QFWFfqyQ== + core-util-is@~1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" @@ -7551,7 +8024,7 @@ crypto-js@3.1.9-1: resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-3.1.9-1.tgz#fda19e761fc077e01ffbfdc6e9fdfc59e8806cd8" integrity sha1-/aGedh/Ad+Af+/3G6f38WeiAbNg= -crypto-js@^4.1.1: +crypto-js@4.1.1, crypto-js@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.1.1.tgz#9e485bcf03521041bd85844786b83fb7619736cf" integrity sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw== @@ -7927,6 +8400,14 @@ define-properties@^1.1.3: dependencies: object-keys "^1.0.12" +define-properties@^1.1.4, define-properties@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.0.tgz#52988570670c9eacedd8064f4a990f2405849bd5" + integrity sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA== + dependencies: + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + defined@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" @@ -8036,6 +8517,11 @@ diff-sequences@^29.3.1: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.3.1.tgz#104b5b95fe725932421a9c6e5b4bef84c3f2249e" integrity sha512-hlM3QR272NXCi4pq+N4Kok4kOp6EsgOM3ZSpJI7Da3UAs+Ttsi8MRmB6trM/lhyzUxGfOgnpkHtgqm5Q/CTcfQ== +diff-sequences@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2" + integrity sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA== + diff@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" @@ -8413,11 +8899,67 @@ es-abstract@^1.17.2, es-abstract@^1.19.0, es-abstract@^1.19.1: string.prototype.trimstart "^1.0.4" unbox-primitive "^1.0.1" +es-abstract@^1.20.4: + version "1.21.2" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.2.tgz#a56b9695322c8a185dc25975aa3b8ec31d0e7eff" + integrity sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg== + dependencies: + array-buffer-byte-length "^1.0.0" + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + es-set-tostringtag "^2.0.1" + es-to-primitive "^1.2.1" + function.prototype.name "^1.1.5" + get-intrinsic "^1.2.0" + get-symbol-description "^1.0.0" + globalthis "^1.0.3" + gopd "^1.0.1" + has "^1.0.3" + has-property-descriptors "^1.0.0" + has-proto "^1.0.1" + has-symbols "^1.0.3" + internal-slot "^1.0.5" + is-array-buffer "^3.0.2" + is-callable "^1.2.7" + is-negative-zero "^2.0.2" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + is-string "^1.0.7" + is-typed-array "^1.1.10" + is-weakref "^1.0.2" + object-inspect "^1.12.3" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.4.3" + safe-regex-test "^1.0.0" + string.prototype.trim "^1.2.7" + string.prototype.trimend "^1.0.6" + string.prototype.trimstart "^1.0.6" + typed-array-length "^1.0.4" + unbox-primitive "^1.0.2" + which-typed-array "^1.1.9" + es-module-lexer@^0.9.0: version "0.9.3" resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19" integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ== +es-set-tostringtag@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8" + integrity sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg== + dependencies: + get-intrinsic "^1.1.3" + has "^1.0.3" + has-tostringtag "^1.0.0" + +es-shim-unscopables@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" + integrity sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w== + dependencies: + has "^1.0.3" + es-to-primitive@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" @@ -8488,6 +9030,11 @@ eslint-config-prettier@^8.4.0, eslint-config-prettier@^8.5.0: resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz#5a81680ec934beca02c7b1a61cf8ca34b66feab1" integrity sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q== +eslint-config-prettier@^8.8.0: + version "8.8.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz#bfda738d412adc917fd7b038857110efe98c9348" + integrity sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA== + eslint-config-react-app@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/eslint-config-react-app/-/eslint-config-react-app-7.0.0.tgz#0fa96d5ec1dfb99c029b1554362ab3fa1c3757df" @@ -8516,6 +9063,15 @@ eslint-import-resolver-node@^0.3.6: debug "^3.2.7" resolve "^1.20.0" +eslint-import-resolver-node@^0.3.7: + version "0.3.7" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz#83b375187d412324a1963d84fa664377a23eb4d7" + integrity sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA== + dependencies: + debug "^3.2.7" + is-core-module "^2.11.0" + resolve "^1.22.1" + eslint-import-resolver-typescript@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.5.0.tgz#07661966b272d14ba97f597b51e1a588f9722f0a" @@ -8581,6 +9137,13 @@ eslint-module-utils@^2.7.3: dependencies: debug "^3.2.7" +eslint-module-utils@^2.7.4: + version "2.8.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz#e439fee65fc33f6bba630ff621efc38ec0375c49" + integrity sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw== + dependencies: + debug "^3.2.7" + eslint-plugin-flowtype@^8.0.3: version "8.0.3" resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-8.0.3.tgz#e1557e37118f24734aa3122e7536a038d34a4912" @@ -8627,6 +9190,27 @@ eslint-plugin-import@^2.26.0: resolve "^1.22.0" tsconfig-paths "^3.14.1" +eslint-plugin-import@^2.27.5: + version "2.27.5" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz#876a6d03f52608a3e5bb439c2550588e51dd6c65" + integrity sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow== + dependencies: + array-includes "^3.1.6" + array.prototype.flat "^1.3.1" + array.prototype.flatmap "^1.3.1" + debug "^3.2.7" + doctrine "^2.1.0" + eslint-import-resolver-node "^0.3.7" + eslint-module-utils "^2.7.4" + has "^1.0.3" + is-core-module "^2.11.0" + is-glob "^4.0.3" + minimatch "^3.1.2" + object.values "^1.1.6" + resolve "^1.22.1" + semver "^6.3.0" + tsconfig-paths "^3.14.1" + eslint-plugin-jest@^25.3.0: version "25.7.0" resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-25.7.0.tgz#ff4ac97520b53a96187bad9c9814e7d00de09a6a" @@ -8634,6 +9218,13 @@ eslint-plugin-jest@^25.3.0: dependencies: "@typescript-eslint/experimental-utils" "^5.0.0" +eslint-plugin-jest@^27.2.1: + version "27.2.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-27.2.2.tgz#be4ded5f91905d9ec89aa8968d39c71f3b072c0c" + integrity sha512-euzbp06F934Z7UDl5ZUaRPLAc9MKjh0rMPERrHT7UhlCEwgb25kBj37TvMgWeHZVkR5I9CayswrpoaqZU1RImw== + dependencies: + "@typescript-eslint/utils" "^5.10.0" + eslint-plugin-jsx-a11y@^6.5.1: version "6.5.1" resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.5.1.tgz#cdbf2df901040ca140b6ec14715c988889c2a6d8" @@ -8652,6 +9243,13 @@ eslint-plugin-jsx-a11y@^6.5.1: language-tags "^1.0.5" minimatch "^3.0.4" +eslint-plugin-prettier@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz#651cbb88b1dab98bfd42f017a12fa6b2d993f94b" + integrity sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ== + dependencies: + prettier-linter-helpers "^1.0.0" + eslint-plugin-react-hooks@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.3.0.tgz#318dbf312e06fab1c835a4abef00121751ac1172" @@ -8700,6 +9298,14 @@ eslint-scope@^7.1.1: esrecurse "^4.3.0" estraverse "^5.2.0" +eslint-scope@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.0.tgz#f21ebdafda02352f103634b96dd47d9f81ca117b" + integrity sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + eslint-utils@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" @@ -8717,6 +9323,11 @@ eslint-visitor-keys@^3.0.0, eslint-visitor-keys@^3.3.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== +eslint-visitor-keys@^3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz#c22c48f48942d08ca824cc526211ae400478a994" + integrity sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA== + eslint-webpack-plugin@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/eslint-webpack-plugin/-/eslint-webpack-plugin-3.1.1.tgz#83dad2395e5f572d6f4d919eedaa9cf902890fcb" @@ -8993,6 +9604,51 @@ eslint@^8.3.0, eslint@^8.9.0: text-table "^0.2.0" v8-compile-cache "^2.0.3" +eslint@^8.40.0: + version "8.44.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.44.0.tgz#51246e3889b259bbcd1d7d736a0c10add4f0e500" + integrity sha512-0wpHoUbDUHgNCyvFB5aXLiQVfK9B0at6gUvzy83k4kAsQ/u769TQDX6iKC+aO4upIHO9WSaA3QoXYQDHbNwf1A== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.4.0" + "@eslint/eslintrc" "^2.1.0" + "@eslint/js" "8.44.0" + "@humanwhocodes/config-array" "^0.11.10" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + ajv "^6.10.0" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.2.0" + eslint-visitor-keys "^3.4.1" + espree "^9.6.0" + esquery "^1.4.2" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + graphemer "^1.4.0" + ignore "^5.2.0" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + strip-ansi "^6.0.1" + strip-json-comments "^3.1.0" + text-table "^0.2.0" + espree@^9.3.1: version "9.3.1" resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.1.tgz#8793b4bc27ea4c778c19908e0719e7b8f4115bcd" @@ -9020,6 +9676,15 @@ espree@^9.4.0: acorn-jsx "^5.3.2" eslint-visitor-keys "^3.3.0" +espree@^9.6.0: + version "9.6.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.0.tgz#80869754b1c6560f32e3b6929194a3fe07c5b82f" + integrity sha512-1FH/IiruXZ84tpUlm0aCUEwMl2Ho5ilqVh0VvQXw+byAz/4SAciyHLlfmL5WYqsvD38oymdUwBss0LtK8m4s/A== + dependencies: + acorn "^8.9.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.1" + esprima@^4.0.0, esprima@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" @@ -9032,6 +9697,13 @@ esquery@^1.4.0: dependencies: estraverse "^5.1.0" +esquery@^1.4.2: + version "1.5.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" + integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== + dependencies: + estraverse "^5.1.0" + esrecurse@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" @@ -9197,6 +9869,18 @@ expect@^29.4.1: jest-message-util "^29.4.1" jest-util "^29.4.1" +expect@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.6.1.tgz#64dd1c8f75e2c0b209418f2b8d36a07921adfdf1" + integrity sha512-XEdDLonERCU1n9uR56/Stx9OqojaLAQtZf9PrCHH9Hl8YXiEIka3H4NXJ3NOIBmQJTg7+j7buh34PMHfJujc8g== + dependencies: + "@jest/expect-utils" "^29.6.1" + "@types/node" "*" + jest-get-type "^29.4.3" + jest-matcher-utils "^29.6.1" + jest-message-util "^29.6.1" + jest-util "^29.6.1" + express@^4.17.1: version "4.17.3" resolved "https://registry.yarnpkg.com/express/-/express-4.17.3.tgz#f6c7302194a4fb54271b73a1fe7a06478c8f85a1" @@ -9299,6 +9983,11 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== +fast-diff@^1.1.2: + version "1.3.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0" + integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== + fast-glob@^3.2.11, fast-glob@^3.2.9: version "3.2.11" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" @@ -9609,11 +10298,26 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function.prototype.name@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" + integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.0" + functions-have-names "^1.2.2" + functional-red-black-tree@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= +functions-have-names@^1.2.2, functions-have-names@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + futoin-hkdf@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/futoin-hkdf/-/futoin-hkdf-1.5.1.tgz#141f00427bc9950b38a42aa786b99c318b9b688d" @@ -9647,6 +10351,16 @@ get-intrinsic@^1.1.3: has "^1.0.3" has-symbols "^1.0.3" +get-intrinsic@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz#d295644fed4505fc9cde952c37ee12b477a83d82" + integrity sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-proto "^1.0.1" + has-symbols "^1.0.3" + get-own-enumerable-property-symbols@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" @@ -9753,6 +10467,13 @@ globals@^13.15.0: dependencies: type-fest "^0.20.2" +globals@^13.19.0: + version "13.20.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.20.0.tgz#ea276a1e508ffd4f1612888f9d1bad1e2717bf82" + integrity sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ== + dependencies: + type-fest "^0.20.2" + globals@^13.6.0, globals@^13.9.0: version "13.13.0" resolved "https://registry.yarnpkg.com/globals/-/globals-13.13.0.tgz#ac32261060d8070e2719dd6998406e27d2b5727b" @@ -9767,6 +10488,13 @@ globalthis@^1.0.1: dependencies: define-properties "^1.1.3" +globalthis@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" + integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== + dependencies: + define-properties "^1.1.3" + globalyzer@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/globalyzer/-/globalyzer-0.1.0.tgz#cb76da79555669a1519d5a8edf093afaa0bf1465" @@ -9817,6 +10545,11 @@ grapheme-splitter@^1.0.4: resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + gzip-size@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462" @@ -9839,6 +10572,11 @@ has-bigints@^1.0.1: resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== +has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== + has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -9849,6 +10587,18 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +has-property-descriptors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" + integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== + dependencies: + get-intrinsic "^1.1.1" + +has-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" + integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== + has-symbols@^1.0.1, has-symbols@^1.0.2, has-symbols@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" @@ -10207,6 +10957,15 @@ internal-slot@^1.0.3: has "^1.0.3" side-channel "^1.0.4" +internal-slot@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" + integrity sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ== + dependencies: + get-intrinsic "^1.2.0" + has "^1.0.3" + side-channel "^1.0.4" + interpret@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9" @@ -10240,6 +10999,15 @@ is-arguments@^1.0.4: call-bind "^1.0.2" has-tostringtag "^1.0.0" +is-array-buffer@^3.0.1, is-array-buffer@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe" + integrity sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.0" + is-typed-array "^1.1.10" + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -10272,7 +11040,7 @@ is-buffer@~1.1.1: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-callable@^1.1.3: +is-callable@^1.1.3, is-callable@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== @@ -10289,6 +11057,13 @@ is-core-module@^2.10.0, is-core-module@^2.9.0: dependencies: has "^1.0.3" +is-core-module@^2.11.0: + version "2.12.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.1.tgz#0c0b6885b6f80011c71541ce15c8d66cf5a4f9fd" + integrity sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg== + dependencies: + has "^1.0.3" + is-core-module@^2.2.0, is-core-module@^2.8.0, is-core-module@^2.8.1: version "2.8.1" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" @@ -10347,7 +11122,7 @@ is-module@^1.0.0: resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE= -is-negative-zero@^2.0.1: +is-negative-zero@^2.0.1, is-negative-zero@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== @@ -10419,6 +11194,13 @@ is-shared-array-buffer@^1.0.1: resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6" integrity sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA== +is-shared-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" + integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== + dependencies: + call-bind "^1.0.2" + is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -10443,7 +11225,7 @@ is-symbol@^1.0.2, is-symbol@^1.0.3: dependencies: has-symbols "^1.0.2" -is-typed-array@^1.1.10, is-typed-array@^1.1.3: +is-typed-array@^1.1.10, is-typed-array@^1.1.3, is-typed-array@^1.1.9: version "1.1.10" resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.10.tgz#36a5b5cb4189b575d1a3e4b08536bfb485801e3f" integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A== @@ -10459,7 +11241,7 @@ is-typedarray@^1.0.0: resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= -is-weakref@^1.0.1: +is-weakref@^1.0.1, is-weakref@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== @@ -10587,6 +11369,14 @@ jest-changed-files@^29.4.0: execa "^5.0.0" p-limit "^3.1.0" +jest-changed-files@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.5.0.tgz#e88786dca8bf2aa899ec4af7644e16d9dcf9b23e" + integrity sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag== + dependencies: + execa "^5.0.0" + p-limit "^3.1.0" + jest-circus@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.5.1.tgz#37a5a4459b7bf4406e53d637b49d22c65d125ecc" @@ -10737,6 +11527,32 @@ jest-circus@^29.4.1: slash "^3.0.0" stack-utils "^2.0.3" +jest-circus@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.6.1.tgz#861dab37e71a89907d1c0fabc54a0019738ed824" + integrity sha512-tPbYLEiBU4MYAL2XoZme/bgfUeotpDBd81lgHLCbDZZFaGmECk0b+/xejPFtmiBP87GgP/y4jplcRpbH+fgCzQ== + dependencies: + "@jest/environment" "^29.6.1" + "@jest/expect" "^29.6.1" + "@jest/test-result" "^29.6.1" + "@jest/types" "^29.6.1" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + dedent "^0.7.0" + is-generator-fn "^2.0.0" + jest-each "^29.6.1" + jest-matcher-utils "^29.6.1" + jest-message-util "^29.6.1" + jest-runtime "^29.6.1" + jest-snapshot "^29.6.1" + jest-util "^29.6.1" + p-limit "^3.1.0" + pretty-format "^29.6.1" + pure-rand "^6.0.0" + slash "^3.0.0" + stack-utils "^2.0.3" + jest-cli@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.5.1.tgz#278794a6e6458ea8029547e6c6cbf673bd30b145" @@ -10845,6 +11661,24 @@ jest-cli@^29.4.1: prompts "^2.0.1" yargs "^17.3.1" +jest-cli@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.6.1.tgz#99d9afa7449538221c71f358f0fdd3e9c6e89f72" + integrity sha512-607dSgTA4ODIN6go9w6xY3EYkyPFGicx51a69H7yfvt7lN53xNswEVLovq+E77VsTRi5fWprLH0yl4DJgE8Ing== + dependencies: + "@jest/core" "^29.6.1" + "@jest/test-result" "^29.6.1" + "@jest/types" "^29.6.1" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + import-local "^3.0.2" + jest-config "^29.6.1" + jest-util "^29.6.1" + jest-validate "^29.6.1" + prompts "^2.0.1" + yargs "^17.3.1" + jest-config@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.5.1.tgz#5c387de33dca3f99ad6357ddeccd91bf3a0e4a41" @@ -11015,6 +11849,34 @@ jest-config@^29.4.1: slash "^3.0.0" strip-json-comments "^3.1.1" +jest-config@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.6.1.tgz#d785344509065d53a238224c6cdc0ed8e2f2f0dd" + integrity sha512-XdjYV2fy2xYixUiV2Wc54t3Z4oxYPAELUzWnV6+mcbq0rh742X2p52pii5A3oeRzYjLnQxCsZmp0qpI6klE2cQ== + dependencies: + "@babel/core" "^7.11.6" + "@jest/test-sequencer" "^29.6.1" + "@jest/types" "^29.6.1" + babel-jest "^29.6.1" + chalk "^4.0.0" + ci-info "^3.2.0" + deepmerge "^4.2.2" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-circus "^29.6.1" + jest-environment-node "^29.6.1" + jest-get-type "^29.4.3" + jest-regex-util "^29.4.3" + jest-resolve "^29.6.1" + jest-runner "^29.6.1" + jest-util "^29.6.1" + jest-validate "^29.6.1" + micromatch "^4.0.4" + parse-json "^5.2.0" + pretty-format "^29.6.1" + slash "^3.0.0" + strip-json-comments "^3.1.1" + jest-diff@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.5.1.tgz#a07f5011ac9e6643cf8a95a462b7b1ecf6680def" @@ -11075,6 +11937,16 @@ jest-diff@^29.4.1: jest-get-type "^29.2.0" pretty-format "^29.4.1" +jest-diff@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.6.1.tgz#13df6db0a89ee6ad93c747c75c85c70ba941e545" + integrity sha512-FsNCvinvl8oVxpNLttNQX7FAq7vR+gMDGj90tiP7siWw1UdakWUGqrylpsYrpvj908IYckm5Y0Q7azNAozU1Kg== + dependencies: + chalk "^4.0.0" + diff-sequences "^29.4.3" + jest-get-type "^29.4.3" + pretty-format "^29.6.1" + jest-docblock@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.5.1.tgz#14092f364a42c6108d42c33c8cf30e058e25f6c0" @@ -11103,6 +11975,13 @@ jest-docblock@^29.2.0: dependencies: detect-newline "^3.0.0" +jest-docblock@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.4.3.tgz#90505aa89514a1c7dceeac1123df79e414636ea8" + integrity sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg== + dependencies: + detect-newline "^3.0.0" + jest-each@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.5.1.tgz#5bc87016f45ed9507fed6e4702a5b468a5b2c44e" @@ -11169,6 +12048,17 @@ jest-each@^29.4.1: jest-util "^29.4.1" pretty-format "^29.4.1" +jest-each@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.6.1.tgz#975058e5b8f55c6780beab8b6ab214921815c89c" + integrity sha512-n5eoj5eiTHpKQCAVcNTT7DRqeUmJ01hsAL0Q1SMiBHcBcvTKDELixQOGMCpqhbIuTcfC4kMfSnpmDqRgRJcLNQ== + dependencies: + "@jest/types" "^29.6.1" + chalk "^4.0.0" + jest-get-type "^29.4.3" + jest-util "^29.6.1" + pretty-format "^29.6.1" + jest-environment-jsdom@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz#ea9ccd1fc610209655a77898f86b2b559516a546" @@ -11268,6 +12158,18 @@ jest-environment-node@^29.4.1: jest-mock "^29.4.1" jest-util "^29.4.1" +jest-environment-node@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.6.1.tgz#08a122dece39e58bc388da815a2166c58b4abec6" + integrity sha512-ZNIfAiE+foBog24W+2caIldl4Irh8Lx1PUhg/GZ0odM1d/h2qORAsejiFc7zb+SEmYPn1yDZzEDSU5PmDkmVLQ== + dependencies: + "@jest/environment" "^29.6.1" + "@jest/fake-timers" "^29.6.1" + "@jest/types" "^29.6.1" + "@types/node" "*" + jest-mock "^29.6.1" + jest-util "^29.6.1" + jest-fetch-mock@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/jest-fetch-mock/-/jest-fetch-mock-3.0.3.tgz#31749c456ae27b8919d69824f1c2bd85fe0a1f3b" @@ -11296,6 +12198,11 @@ jest-get-type@^29.2.0: resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.2.0.tgz#726646f927ef61d583a3b3adb1ab13f3a5036408" integrity sha512-uXNJlg8hKFEnDgFsrCjznB+sTxdkuqiCL6zMgA75qEbAJjJYTs9XPrvDctrEig2GDow22T/LvHgO57iJhXB/UA== +jest-get-type@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.4.3.tgz#1ab7a5207c995161100b5187159ca82dd48b3dd5" + integrity sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg== + jest-haste-map@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.5.1.tgz#9fd8bd7e7b4fa502d9c6164c5640512b4e811e7f" @@ -11411,6 +12318,25 @@ jest-haste-map@^29.4.1: optionalDependencies: fsevents "^2.3.2" +jest-haste-map@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.6.1.tgz#62655c7a1c1b349a3206441330fb2dbdb4b63803" + integrity sha512-0m7f9PZXxOCk1gRACiVgX85knUKPKLPg4oRCjLoqIm9brTHXaorMA0JpmtmVkQiT8nmXyIVoZd/nnH1cfC33ig== + dependencies: + "@jest/types" "^29.6.1" + "@types/graceful-fs" "^4.1.3" + "@types/node" "*" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.9" + jest-regex-util "^29.4.3" + jest-util "^29.6.1" + jest-worker "^29.6.1" + micromatch "^4.0.4" + walker "^1.0.8" + optionalDependencies: + fsevents "^2.3.2" + jest-jasmine2@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz#a037b0034ef49a9f3d71c4375a796f3b230d1ac4" @@ -11482,6 +12408,14 @@ jest-leak-detector@^29.4.1: jest-get-type "^29.2.0" pretty-format "^29.4.1" +jest-leak-detector@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.6.1.tgz#66a902c81318e66e694df7d096a95466cb962f8e" + integrity sha512-OrxMNyZirpOEwkF3UHnIkAiZbtkBWiye+hhBweCHkVbCgyEy71Mwbb5zgeTNYWJBi1qgDVfPC1IwO9dVEeTLwQ== + dependencies: + jest-get-type "^29.4.3" + pretty-format "^29.6.1" + jest-matcher-utils@^27.0.0, jest-matcher-utils@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab" @@ -11542,6 +12476,16 @@ jest-matcher-utils@^29.4.1: jest-get-type "^29.2.0" pretty-format "^29.4.1" +jest-matcher-utils@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.6.1.tgz#6c60075d84655d6300c5d5128f46531848160b53" + integrity sha512-SLaztw9d2mfQQKHmJXKM0HCbl2PPVld/t9Xa6P9sgiExijviSp7TnZZpw2Fpt+OI3nwUO/slJbOfzfUMKKC5QA== + dependencies: + chalk "^4.0.0" + jest-diff "^29.6.1" + jest-get-type "^29.4.3" + pretty-format "^29.6.1" + jest-message-util@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.5.1.tgz#bdda72806da10d9ed6425e12afff38cd1458b6cf" @@ -11647,6 +12591,21 @@ jest-message-util@^29.4.1: slash "^3.0.0" stack-utils "^2.0.3" +jest-message-util@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.6.1.tgz#d0b21d87f117e1b9e165e24f245befd2ff34ff8d" + integrity sha512-KoAW2zAmNSd3Gk88uJ56qXUWbFk787QKmjjJVOjtGFmmGSZgDBrlIL4AfQw1xyMYPNVD7dNInfIbur9B2rd/wQ== + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^29.6.1" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^29.6.1" + slash "^3.0.0" + stack-utils "^2.0.3" + jest-mock-extended@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/jest-mock-extended/-/jest-mock-extended-3.0.3.tgz#570e33b099f8da87487acb4cd7d820712dcc3b52" @@ -11712,6 +12671,15 @@ jest-mock@^29.4.1: "@types/node" "*" jest-util "^29.4.1" +jest-mock@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.6.1.tgz#049ee26aea8cbf54c764af649070910607316517" + integrity sha512-brovyV9HBkjXAEdRooaTQK42n8usKoSRR3gihzUpYeV/vwqgSoNfrksO7UfSACnPmxasO/8TmHM3w9Hp3G1dgw== + dependencies: + "@jest/types" "^29.6.1" + "@types/node" "*" + jest-util "^29.6.1" + jest-pnp-resolver@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" @@ -11737,6 +12705,11 @@ jest-regex-util@^29.2.0: resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.2.0.tgz#82ef3b587e8c303357728d0322d48bbfd2971f7b" integrity sha512-6yXn0kg2JXzH30cr2NlThF+70iuO/3irbaB4mh5WyqNIvLLP+B6sFdluO1/1RJmslyh/f9osnefECflHvTbwVA== +jest-regex-util@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.4.3.tgz#a42616141e0cae052cfa32c169945d00c0aa0bb8" + integrity sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg== + jest-resolve-dependencies@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz#d811ecc8305e731cc86dd79741ee98fed06f1da8" @@ -11786,6 +12759,14 @@ jest-resolve-dependencies@^29.4.1: jest-regex-util "^29.2.0" jest-snapshot "^29.4.1" +jest-resolve-dependencies@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.6.1.tgz#b85b06670f987a62515bbf625d54a499e3d708f5" + integrity sha512-BbFvxLXtcldaFOhNMXmHRWx1nXQO5LoXiKSGQcA1LxxirYceZT6ch8KTE1bK3X31TNG/JbkI7OkS/ABexVahiw== + dependencies: + jest-regex-util "^29.4.3" + jest-snapshot "^29.6.1" + jest-resolve@^27.4.2, jest-resolve@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.5.1.tgz#a2f1c5a0796ec18fe9eb1536ac3814c23617b384" @@ -11877,6 +12858,21 @@ jest-resolve@^29.4.1: resolve.exports "^2.0.0" slash "^3.0.0" +jest-resolve@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.6.1.tgz#4c3324b993a85e300add2f8609f51b80ddea39ee" + integrity sha512-AeRkyS8g37UyJiP9w3mmI/VXU/q8l/IH52vj/cDAyScDcemRbSBhfX/NMYIGilQgSVwsjxrCHf3XJu4f+lxCMg== + dependencies: + chalk "^4.0.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.6.1" + jest-pnp-resolver "^1.2.2" + jest-util "^29.6.1" + jest-validate "^29.6.1" + resolve "^1.20.0" + resolve.exports "^2.0.0" + slash "^3.0.0" + jest-runner@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.5.1.tgz#071b27c1fa30d90540805c5645a0ec167c7b62e5" @@ -12039,6 +13035,33 @@ jest-runner@^29.4.1: p-limit "^3.1.0" source-map-support "0.5.13" +jest-runner@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.6.1.tgz#54557087e7972d345540d622ab5bfc3d8f34688c" + integrity sha512-tw0wb2Q9yhjAQ2w8rHRDxteryyIck7gIzQE4Reu3JuOBpGp96xWgF0nY8MDdejzrLCZKDcp8JlZrBN/EtkQvPQ== + dependencies: + "@jest/console" "^29.6.1" + "@jest/environment" "^29.6.1" + "@jest/test-result" "^29.6.1" + "@jest/transform" "^29.6.1" + "@jest/types" "^29.6.1" + "@types/node" "*" + chalk "^4.0.0" + emittery "^0.13.1" + graceful-fs "^4.2.9" + jest-docblock "^29.4.3" + jest-environment-node "^29.6.1" + jest-haste-map "^29.6.1" + jest-leak-detector "^29.6.1" + jest-message-util "^29.6.1" + jest-resolve "^29.6.1" + jest-runtime "^29.6.1" + jest-util "^29.6.1" + jest-watcher "^29.6.1" + jest-worker "^29.6.1" + p-limit "^3.1.0" + source-map-support "0.5.13" + jest-runtime@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.5.1.tgz#4896003d7a334f7e8e4a53ba93fb9bcd3db0a1af" @@ -12208,6 +13231,34 @@ jest-runtime@^29.4.1: slash "^3.0.0" strip-bom "^4.0.0" +jest-runtime@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.6.1.tgz#8a0fc9274ef277f3d70ba19d238e64334958a0dc" + integrity sha512-D6/AYOA+Lhs5e5il8+5pSLemjtJezUr+8zx+Sn8xlmOux3XOqx4d8l/2udBea8CRPqqrzhsKUsN/gBDE/IcaPQ== + dependencies: + "@jest/environment" "^29.6.1" + "@jest/fake-timers" "^29.6.1" + "@jest/globals" "^29.6.1" + "@jest/source-map" "^29.6.0" + "@jest/test-result" "^29.6.1" + "@jest/transform" "^29.6.1" + "@jest/types" "^29.6.1" + "@types/node" "*" + chalk "^4.0.0" + cjs-module-lexer "^1.0.0" + collect-v8-coverage "^1.0.0" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-haste-map "^29.6.1" + jest-message-util "^29.6.1" + jest-mock "^29.6.1" + jest-regex-util "^29.4.3" + jest-resolve "^29.6.1" + jest-snapshot "^29.6.1" + jest-util "^29.6.1" + slash "^3.0.0" + strip-bom "^4.0.0" + jest-serializer@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-27.5.1.tgz#81438410a30ea66fd57ff730835123dea1fb1f64" @@ -12393,6 +13444,33 @@ jest-snapshot@^29.4.1: pretty-format "^29.4.1" semver "^7.3.5" +jest-snapshot@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.6.1.tgz#0d083cb7de716d5d5cdbe80d598ed2fbafac0239" + integrity sha512-G4UQE1QQ6OaCgfY+A0uR1W2AY0tGXUPQpoUClhWHq1Xdnx1H6JOrC2nH5lqnOEqaDgbHFgIwZ7bNq24HpB180A== + dependencies: + "@babel/core" "^7.11.6" + "@babel/generator" "^7.7.2" + "@babel/plugin-syntax-jsx" "^7.7.2" + "@babel/plugin-syntax-typescript" "^7.7.2" + "@babel/types" "^7.3.3" + "@jest/expect-utils" "^29.6.1" + "@jest/transform" "^29.6.1" + "@jest/types" "^29.6.1" + "@types/prettier" "^2.1.5" + babel-preset-current-node-syntax "^1.0.0" + chalk "^4.0.0" + expect "^29.6.1" + graceful-fs "^4.2.9" + jest-diff "^29.6.1" + jest-get-type "^29.4.3" + jest-matcher-utils "^29.6.1" + jest-message-util "^29.6.1" + jest-util "^29.6.1" + natural-compare "^1.4.0" + pretty-format "^29.6.1" + semver "^7.5.3" + jest-util@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.5.1.tgz#3ba9771e8e31a0b85da48fe0b0891fb86c01c2f9" @@ -12477,6 +13555,18 @@ jest-util@^29.4.1: graceful-fs "^4.2.9" picomatch "^2.2.3" +jest-util@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.6.1.tgz#c9e29a87a6edbf1e39e6dee2b4689b8a146679cb" + integrity sha512-NRFCcjc+/uO3ijUVyNOQJluf8PtGCe/W6cix36+M3cTFgiYqFOOW5MgN4JOOcvbUhcKTYVd1CvHz/LWi8d16Mg== + dependencies: + "@jest/types" "^29.6.1" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + jest-validate@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.5.1.tgz#9197d54dc0bdb52260b8db40b46ae668e04df067" @@ -12549,6 +13639,18 @@ jest-validate@^29.4.1: leven "^3.1.0" pretty-format "^29.4.1" +jest-validate@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.6.1.tgz#765e684af6e2c86dce950aebefbbcd4546d69f7b" + integrity sha512-r3Ds69/0KCN4vx4sYAbGL1EVpZ7MSS0vLmd3gV78O+NAx3PDQQukRU5hNHPXlyqCgFY8XUk7EuTMLugh0KzahA== + dependencies: + "@jest/types" "^29.6.1" + camelcase "^6.2.0" + chalk "^4.0.0" + jest-get-type "^29.4.3" + leven "^3.1.0" + pretty-format "^29.6.1" + jest-watch-typeahead@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/jest-watch-typeahead/-/jest-watch-typeahead-1.0.0.tgz#4de2ca1eb596acb1889752afbab84b74fcd99173" @@ -12645,6 +13747,20 @@ jest-watcher@^29.4.1: jest-util "^29.4.1" string-length "^4.0.1" +jest-watcher@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.6.1.tgz#7c0c43ddd52418af134c551c92c9ea31e5ec942e" + integrity sha512-d4wpjWTS7HEZPaaj8m36QiaP856JthRZkrgcIY/7ISoUWPIillrXM23WPboZVLbiwZBt4/qn2Jke84Sla6JhFA== + dependencies: + "@jest/test-result" "^29.6.1" + "@jest/types" "^29.6.1" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + emittery "^0.13.1" + jest-util "^29.6.1" + string-length "^4.0.1" + jest-worker@^26.2.1: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" @@ -12709,6 +13825,16 @@ jest-worker@^29.4.1: merge-stream "^2.0.0" supports-color "^8.0.0" +jest-worker@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.6.1.tgz#64b015f0e985ef3a8ad049b61fe92b3db74a5319" + integrity sha512-U+Wrbca7S8ZAxAe9L6nb6g8kPdia5hj32Puu5iOqBCMTMWFHXuK6dOV2IFrpedbTV8fjMFLdWNttQTBL6u2MRA== + dependencies: + "@types/node" "*" + jest-util "^29.6.1" + merge-stream "^2.0.0" + supports-color "^8.0.0" + jest@^27.4.3: version "27.5.1" resolved "https://registry.yarnpkg.com/jest/-/jest-27.5.1.tgz#dadf33ba70a779be7a6fc33015843b51494f63fc" @@ -12768,6 +13894,16 @@ jest@^29.4.1: import-local "^3.0.2" jest-cli "^29.4.1" +jest@^29.5.0: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest/-/jest-29.6.1.tgz#74be1cb719c3abe439f2d94aeb18e6540a5b02ad" + integrity sha512-Nirw5B4nn69rVUZtemCQhwxOBhm0nsp3hmtF4rzCeWD7BkjAXRIji7xWQfnTNbz9g0aVsBX6aZK3n+23LM6uDw== + dependencies: + "@jest/core" "^29.6.1" + "@jest/types" "^29.6.1" + import-local "^3.0.2" + jest-cli "^29.6.1" + js-crypto-env@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/js-crypto-env/-/js-crypto-env-0.3.2.tgz#02195723469da14449338ca2789fd7ff6784c533" @@ -13491,6 +14627,11 @@ nanoid@^3.3.4: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== +natural-compare-lite@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4" + integrity sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g== + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -13531,6 +14672,11 @@ node-addon-api@^2.0.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== +node-addon-api@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-5.1.0.tgz#49da1ca055e109a23d537e9de43c09cca21eb762" + integrity sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA== + node-fetch@2.6.7: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" @@ -13645,6 +14791,11 @@ object-inspect@^1.11.0, object-inspect@^1.9.0: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g== +object-inspect@^1.12.3: + version "1.12.3" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" + integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== + object-is@^1.0.1: version "1.1.5" resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" @@ -13668,6 +14819,16 @@ object.assign@^4.1.0, object.assign@^4.1.2: has-symbols "^1.0.1" object-keys "^1.1.1" +object.assign@^4.1.4: + version "4.1.4" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" + integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + has-symbols "^1.0.3" + object-keys "^1.1.1" + object.entries@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.5.tgz#e1acdd17c4de2cd96d5a08487cfb9db84d881861" @@ -13712,6 +14873,15 @@ object.values@^1.1.0, object.values@^1.1.5: define-properties "^1.1.3" es-abstract "^1.19.1" +object.values@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.6.tgz#4abbaa71eba47d63589d402856f908243eea9b1d" + integrity sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + obuf@^1.0.0, obuf@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" @@ -13783,6 +14953,18 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" +optionator@^0.9.3: + version "0.9.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" + integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== + dependencies: + "@aashutoshrathi/word-wrap" "^1.2.3" + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" @@ -14602,11 +15784,23 @@ prelude-ls@~1.1.2: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + prettier@^2.5.1: version "2.6.0" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.6.0.tgz#12f8f504c4d8ddb76475f441337542fa799207d4" integrity sha512-m2FgJibYrBGGgQXNzfd0PuDGShJgRavjUoRCw1mZERIWVSXF0iLzLm+aOqTAbLnC3n6JzUhAA8uZnFVghHJ86A== +prettier@^2.8.8: + version "2.8.8" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" + integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== + pretty-bytes@^5.3.0, pretty-bytes@^5.4.1: version "5.6.0" resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" @@ -14684,6 +15878,15 @@ pretty-format@^29.4.1: ansi-styles "^5.0.0" react-is "^18.0.0" +pretty-format@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.6.1.tgz#ec838c288850b7c4f9090b867c2d4f4edbfb0f3e" + integrity sha512-7jRj+yXO0W7e4/tSJKoR7HRIHLPPjtNaUGG2xxKQnGvPNRkgWcQ0AZX6P4KBRJN4FcTBWb3sa7DVUJmocYuoog== + dependencies: + "@jest/schemas" "^29.6.0" + ansi-styles "^5.0.0" + react-is "^18.0.0" + process-nextick-args@~1.0.6: version "1.0.7" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" @@ -14809,6 +16012,11 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== +pure-rand@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.2.tgz#a9c2ddcae9b68d736a8163036f088a2781c8b306" + integrity sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ== + q@^1.1.2: version "1.5.1" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" @@ -15285,6 +16493,15 @@ regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.3.1: call-bind "^1.0.2" define-properties "^1.1.3" +regexp.prototype.flags@^1.4.3: + version "1.5.0" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz#fe7ce25e7e4cca8db37b6634c8a2c7009199b9cb" + integrity sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + functions-have-names "^1.2.3" + regexpp@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" @@ -15435,6 +16652,15 @@ resolve@^1.14.2, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.22.0: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +resolve@^1.22.1: + version "1.22.2" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f" + integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g== + dependencies: + is-core-module "^2.11.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + resolve@^2.0.0-next.3: version "2.0.0-next.3" resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.3.tgz#d41016293d4a8586a39ca5d9b5f15cbea1f55e46" @@ -15516,6 +16742,15 @@ safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== +safe-regex-test@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" + integrity sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + is-regex "^1.1.4" + "safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" @@ -15612,6 +16847,15 @@ secp256k1@^4.0.3: node-addon-api "^2.0.0" node-gyp-build "^4.2.0" +secp256k1@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-5.0.0.tgz#be6f0c8c7722e2481e9773336d351de8cddd12f7" + integrity sha512-TKWX8xvoGHrxVdqbYeZM9w+izTF4b9z3NhSaDkdn81btvuh+ivbIMGT/zQvDtTFWhRlThpoz6LEYTr7n8A5GcA== + dependencies: + elliptic "^6.5.4" + node-addon-api "^5.0.0" + node-gyp-build "^4.2.0" + secretjs@^0.17.0: version "0.17.5" resolved "https://registry.yarnpkg.com/secretjs/-/secretjs-0.17.5.tgz#5b55e46cfa2719714831fc2019e21c21959fe587" @@ -15675,6 +16919,13 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +semver@^7.3.7, semver@^7.5.3: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" + semver@^7.3.8: version "7.3.8" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" @@ -16099,6 +17350,15 @@ string.prototype.matchall@^4.0.6: regexp.prototype.flags "^1.3.1" side-channel "^1.0.4" +string.prototype.trim@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz#a68352740859f6893f14ce3ef1bb3037f7a90533" + integrity sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + string.prototype.trimend@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" @@ -16107,6 +17367,15 @@ string.prototype.trimend@^1.0.4: call-bind "^1.0.2" define-properties "^1.1.3" +string.prototype.trimend@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533" + integrity sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + string.prototype.trimstart@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" @@ -16115,6 +17384,15 @@ string.prototype.trimstart@^1.0.4: call-bind "^1.0.2" define-properties "^1.1.3" +string.prototype.trimstart@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4" + integrity sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -16749,6 +18027,15 @@ type-tagger@^1.0.0: resolved "https://registry.yarnpkg.com/type-tagger/-/type-tagger-1.0.0.tgz#dc6297e52e17097c1b92b42c16816a18f631e7f4" integrity sha512-FIPqqpmDgdaulCnRoKv1/d3U4xVBUrYn42QXWNP3XYmgfPUDuBUsgFOb9ntT0aIe0UsUP+lknpQ5d9Kn36RssA== +typed-array-length@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" + integrity sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + is-typed-array "^1.1.9" + typedarray-to-buffer@^3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" @@ -16756,11 +18043,21 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" +typeforce@^1.11.5: + version "1.18.0" + resolved "https://registry.yarnpkg.com/typeforce/-/typeforce-1.18.0.tgz#d7416a2c5845e085034d70fcc5b6cc4a90edbfdc" + integrity sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g== + typescript-plugin-styled-components@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/typescript-plugin-styled-components/-/typescript-plugin-styled-components-2.0.0.tgz#97e94187cca0f3058c6ad8fefffbca6766c56123" integrity sha512-Wu7F96dwuphgiACHfu63vTbRRg6tkPwLnpFJwdxM70Y0PLfeKLRnvs2Yo5MAySMwE120ODMKk9W4TtJgY1ZumA== +typescript@^5.0.4: + version "5.1.6" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274" + integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA== + typescript@^5.1.3: version "5.1.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.3.tgz#8d84219244a6b40b6fb2b33cc1c062f715b9e826" @@ -16790,6 +18087,16 @@ unbox-primitive@^1.0.1: has-symbols "^1.0.2" which-boxed-primitive "^1.0.2" +unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== + dependencies: + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" + which-boxed-primitive "^1.0.2" + unicode-canonical-property-names-ecmascript@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" @@ -17395,6 +18702,18 @@ which-typed-array@^1.1.2: has-tostringtag "^1.0.0" is-typed-array "^1.1.10" +which-typed-array@^1.1.9: + version "1.1.10" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.10.tgz#74baa2789991905c2076abb317103b866c64e69e" + integrity sha512-uxoA5vLUfRPdjCuJ1h5LlYdmTLbYfums398v3WLkM+i/Wltl2/XyZpQWKbN++ck5L64SR/grOHqtXCUKmlZPNA== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + is-typed-array "^1.1.10" + which@^1.2.9, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" @@ -17409,6 +18728,13 @@ which@^2.0.1: dependencies: isexe "^2.0.0" +wif@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/wif/-/wif-2.0.6.tgz#08d3f52056c66679299726fade0d432ae74b4704" + integrity sha512-HIanZn1zmduSF+BQhkE+YXIbEiH0xPr1012QbFEGB0xsKqJii0/SqJjyn8dFv6y36kOznMgMB+LGcbZTJ1xACQ== + dependencies: + bs58check "<3.0.0" + wildcard@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec" @@ -17630,7 +18956,7 @@ write-file-atomic@^3.0.0: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" -write-file-atomic@^4.0.1: +write-file-atomic@^4.0.1, write-file-atomic@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==