diff --git a/apps/browser-extension-wallet/package.json b/apps/browser-extension-wallet/package.json
index a726ff90a..4f96dccf2 100644
--- a/apps/browser-extension-wallet/package.json
+++ b/apps/browser-extension-wallet/package.json
@@ -40,14 +40,14 @@
},
"dependencies": {
"@ant-design/icons": "^4.7.0",
- "@cardano-sdk/cardano-services-client": "0.17.6",
- "@cardano-sdk/core": "0.28.2",
- "@cardano-sdk/dapp-connector": "0.12.9",
- "@cardano-sdk/input-selection": "0.12.20",
- "@cardano-sdk/tx-construction": "0.17.10",
+ "@cardano-sdk/cardano-services-client": "0.17.7",
+ "@cardano-sdk/core": "0.28.3",
+ "@cardano-sdk/dapp-connector": "0.12.10",
+ "@cardano-sdk/input-selection": "0.12.21",
+ "@cardano-sdk/tx-construction": "0.17.11",
"@cardano-sdk/util": "0.15.0",
- "@cardano-sdk/wallet": "0.34.2",
- "@cardano-sdk/web-extension": "0.24.5",
+ "@cardano-sdk/wallet": "0.34.3",
+ "@cardano-sdk/web-extension": "0.24.6",
"@emurgo/cip14-js": "~3.0.1",
"@koralabs/handles-public-api-interfaces": "^1.6.6",
"@lace/cardano": "0.1.0",
diff --git a/apps/browser-extension-wallet/src/components/DropdownMenu/DropdownMenu.tsx b/apps/browser-extension-wallet/src/components/DropdownMenu/DropdownMenu.tsx
index fd63e0e86..3edf69b2c 100644
--- a/apps/browser-extension-wallet/src/components/DropdownMenu/DropdownMenu.tsx
+++ b/apps/browser-extension-wallet/src/components/DropdownMenu/DropdownMenu.tsx
@@ -1,4 +1,4 @@
-import React, { useState } from 'react';
+import React from 'react';
import cn from 'classnames';
import { Dropdown } from 'antd';
import { Button } from '@lace/common';
@@ -14,6 +14,8 @@ import { PostHogAction } from '@providers/AnalyticsProvider/analyticsTracker';
import { ProfileDropdown } from '@lace/ui';
import { useGetHandles } from '@hooks';
import { getAssetImageUrl } from '@src/utils/get-asset-image-url';
+import { getActiveWalletSubtitle } from '@src/utils/get-wallet-subtitle';
+import { getUiWalletType } from '@src/utils/get-ui-wallet-type';
export interface DropdownMenuProps {
isPopup?: boolean;
@@ -21,8 +23,11 @@ export interface DropdownMenuProps {
export const DropdownMenu = ({ isPopup }: DropdownMenuProps): React.ReactElement => {
const analytics = useAnalyticsContext();
- const { walletInfo } = useWalletStore();
- const [open, setOpen] = useState(false);
+ const {
+ cardanoWallet,
+ walletUI: { isDropdownMenuOpen },
+ setIsDropdownMenuOpen
+ } = useWalletStore();
const [handle] = useGetHandles();
const handleImage = handle?.profilePic;
const Chevron = isPopup ? ChevronSmall : ChevronNormal;
@@ -32,34 +37,37 @@ export const DropdownMenu = ({ isPopup }: DropdownMenuProps): React.ReactElement
};
const handleDropdownState = (openDropdown: boolean) => {
- setOpen(openDropdown);
+ setIsDropdownMenuOpen(openDropdown);
if (openDropdown) {
sendAnalyticsEvent(PostHogAction.UserWalletProfileIconClick);
}
};
+ const walletName = cardanoWallet.source.wallet.metadata.name;
+
return (
}
placement="bottomRight"
+ open={isDropdownMenuOpen}
trigger={['click']}
>
{process.env.USE_MULTI_WALLET === 'true' ? (
@@ -71,7 +79,7 @@ export const DropdownMenu = ({ isPopup }: DropdownMenuProps): React.ReactElement
data-testid="header-menu-button"
>
-
+
div.ant-switch-handle {
+ .ant-switch>div.ant-switch-handle {
height: size_unit(2.5);
width: size_unit(2.5);
}
- .ant-switch > span.ant-switch-inner svg {
+ .ant-switch>span.ant-switch-inner svg {
margin-top: 1px;
}
- .ant-switch.ant-switch-checked > span.ant-switch-inner {
+ .ant-switch.ant-switch-checked>span.ant-switch-inner {
margin: 0 size_unit(35) 0 size_unit(0.5);
}
- .ant-switch.ant-switch-checked > div.ant-switch-handle {
+ .ant-switch.ant-switch-checked>div.ant-switch-handle {
left: calc(100% - 20px - 2px);
}
}
display: flex;
justify-content: space-between !important;
+
&:hover {
background: transparent !important;
}
}
}
}
+
.separator {
display: flex;
height: 1.5px;
@@ -141,12 +147,14 @@
display: flex;
align-items: center;
justify-content: center;
+
span {
color: var(--dark-mode-mid-black, var(--text-color-white, #ffffff));
font-size: var(--body);
text-transform: uppercase;
font-weight: 700;
}
+
.avatar {
font-size: size_unit(4);
cursor: pointer;
@@ -156,6 +164,7 @@
height: 26px;
width: 26px;
}
+
.userAvatarImage {
border-radius: 30px;
}
@@ -178,7 +187,8 @@
font-size: 16px;
}
-.popUpContainer, .extendedContainer {
+.popUpContainer,
+.extendedContainer {
@include scroll-bar-style;
overflow-y: scroll;
margin: size_unit(1) size_unit(1) size_unit(1) 0;
diff --git a/apps/browser-extension-wallet/src/components/MainMenu/DropdownMenuOverlay/DropdownMenuOverlay.tsx b/apps/browser-extension-wallet/src/components/MainMenu/DropdownMenuOverlay/DropdownMenuOverlay.tsx
index 8bda01d26..588b9f3f2 100644
--- a/apps/browser-extension-wallet/src/components/MainMenu/DropdownMenuOverlay/DropdownMenuOverlay.tsx
+++ b/apps/browser-extension-wallet/src/components/MainMenu/DropdownMenuOverlay/DropdownMenuOverlay.tsx
@@ -19,6 +19,8 @@ import { WalletAccounts } from './components/WalletAccounts';
import { AddSharedWalletLink } from '@components/MainMenu/DropdownMenuOverlay/components/AddSharedWalletLink';
import { useWalletStore } from '@stores';
import classNames from 'classnames';
+import { AnyBip32Wallet } from '@cardano-sdk/web-extension';
+import { Wallet } from '@lace/cardano';
interface Props extends MenuProps {
isPopup?: boolean;
@@ -35,9 +37,10 @@ export const DropdownMenuOverlay: VFC = ({
...props
}): React.ReactElement => {
const [currentSection, setCurrentSection] = useState(Sections.Main);
- const { environmentName } = useWalletStore();
+ const { environmentName, setManageAccountsWallet } = useWalletStore();
- const openWalletAccounts = () => {
+ const openWalletAccounts = (wallet: AnyBip32Wallet) => {
+ setManageAccountsWallet(wallet);
setCurrentSection(Sections.WalletAccounts);
};
diff --git a/apps/browser-extension-wallet/src/components/MainMenu/DropdownMenuOverlay/components/UserInfo.tsx b/apps/browser-extension-wallet/src/components/MainMenu/DropdownMenuOverlay/components/UserInfo.tsx
index 920b56261..4d92675bc 100644
--- a/apps/browser-extension-wallet/src/components/MainMenu/DropdownMenuOverlay/components/UserInfo.tsx
+++ b/apps/browser-extension-wallet/src/components/MainMenu/DropdownMenuOverlay/components/UserInfo.tsx
@@ -1,18 +1,21 @@
-import React from 'react';
+import React, { useCallback } from 'react';
import classnames from 'classnames';
import { useWalletStore } from '@src/stores';
import { Menu, Tooltip as AntdTooltip } from 'antd';
import { useTranslation } from 'react-i18next';
import styles from '../DropdownMenuOverlay.module.scss';
import { CopyToClipboard } from 'react-copy-to-clipboard';
-import { toast, addEllipsis } from '@lace/common';
+import { toast, addEllipsis, useObservable } from '@lace/common';
import { WalletStatusContainer } from '@components/WalletStatus';
import { UserAvatar } from './UserAvatar';
-import { useGetHandles } from '@hooks';
+import { useGetHandles, useWalletManager } from '@hooks';
import { useAnalyticsContext } from '@providers';
import { PostHogAction } from '@providers/AnalyticsProvider/analyticsTracker';
import { ProfileDropdown } from '@lace/ui';
-import { getAssetImageUrl } from '@src/utils/get-asset-image-url';
+import { AnyBip32Wallet, AnyWallet, Bip32WalletAccount, WalletType } from '@cardano-sdk/web-extension';
+import { Wallet } from '@lace/cardano';
+import { Separator } from './Separator';
+import { getUiWalletType } from '@src/utils/get-ui-wallet-type';
const ADRESS_FIRST_PART_LENGTH = 10;
const ADRESS_LAST_PART_LENGTH = 5;
@@ -27,25 +30,99 @@ const overlayInnerStyle = {
interface UserInfoProps {
avatarVisible?: boolean;
- onOpenWalletAccounts?: (walletAddress: string) => void;
+ onOpenWalletAccounts?: (wallet: AnyBip32Wallet) => void;
}
+const NO_WALLETS: AnyWallet[] = [];
+
export const UserInfo = ({ onOpenWalletAccounts, avatarVisible = true }: UserInfoProps): React.ReactElement => {
const { t } = useTranslation();
- const { walletInfo } = useWalletStore();
+ const { walletInfo, cardanoWallet, setIsDropdownMenuOpen } = useWalletStore();
+ const { activateWallet, walletRepository } = useWalletManager();
const analytics = useAnalyticsContext();
+ const wallets = useObservable(walletRepository.wallets$, NO_WALLETS);
const walletAddress = walletInfo.addresses[0].address.toString();
const shortenedWalletAddress = addEllipsis(walletAddress, ADRESS_FIRST_PART_LENGTH, ADRESS_LAST_PART_LENGTH);
- const walletName = addEllipsis(walletInfo.name.toString(), WALLET_NAME_MAX_LENGTH, 0);
+ const fullWalletName = cardanoWallet.source.wallet.metadata.name;
+ const activeWalletName = addEllipsis(fullWalletName, WALLET_NAME_MAX_LENGTH, 0);
const [handle] = useGetHandles();
const handleName = handle?.nftMetadata?.name;
- const handleImage = handle?.profilePic;
const handleOnAddressCopy = () => {
toast.notify({ duration: TOAST_DEFAULT_DURATION, text: t('general.clipboard.copiedToClipboard') });
analytics.sendEventToPostHog(PostHogAction.UserWalletProfileWalletAddressClick);
};
+ const getLastActiveAccount = useCallback(
+ (
+ wallet: AnyBip32Wallet
+ ): Bip32WalletAccount => {
+ if (wallet.accounts.length === 1) return wallet.accounts[0];
+ if (wallet.walletId === cardanoWallet?.source.wallet.walletId) {
+ const currentlyActiveAccount = wallet.accounts.find(
+ ({ accountIndex }) => accountIndex === cardanoWallet.source.account?.accountIndex
+ );
+ if (currentlyActiveAccount) return currentlyActiveAccount;
+ }
+ if (typeof wallet.metadata.lastActiveAccountIndex !== 'undefined') {
+ const lastActiveAccount = wallet.accounts.find(
+ ({ accountIndex }) => accountIndex === wallet.metadata.lastActiveAccountIndex
+ );
+ if (lastActiveAccount) return lastActiveAccount;
+ }
+ // If last active account is deleted, fall back to any (1st) account
+ return wallet.accounts[0];
+ },
+ [cardanoWallet]
+ );
+
+ const renderBip32Wallet = useCallback(
+ (wallet: AnyBip32Wallet) => {
+ const lastActiveAccount = getLastActiveAccount(wallet);
+ return (
+ onOpenWalletAccounts(wallet)}
+ onClick={async () => {
+ await activateWallet({
+ walletId: wallet.walletId,
+ accountIndex: lastActiveAccount.accountIndex
+ });
+ setIsDropdownMenuOpen(false);
+ toast.notify({
+ duration: TOAST_DEFAULT_DURATION,
+ text: t('multiWallet.activated.wallet', { walletName: wallet.metadata.name })
+ });
+ }}
+ type={getUiWalletType(wallet.type)}
+ />
+ );
+ },
+ [activateWallet, getLastActiveAccount, onOpenWalletAccounts, setIsDropdownMenuOpen, t]
+ );
+
+ const renderWallet = useCallback(
+ (wallet: AnyWallet, isLast: boolean) => (
+
+ {wallet.type !== WalletType.Script
+ ? renderBip32Wallet(wallet)
+ : (() => {
+ throw new Error('Script wallets are not implemented');
+ })()}
+ {wallet.walletId === cardanoWallet?.source.wallet.walletId ? (
+
+
+
+ ) : undefined}
+ {isLast ? undefined :
}
+
+ ),
+ [renderBip32Wallet, cardanoWallet?.source.wallet.walletId]
+ );
+
return (
-
- {process.env.USE_MULTI_WALLET === 'true' ? (
- onOpenWalletAccounts(walletAddress)}
- type={process.env.USE_SHARED_WALLET === 'true' ? 'shared' : 'cold'}
- />
- ) : (
+ {process.env.USE_MULTI_WALLET === 'true' ? (
+ {wallets.map((wallet, i) => renderWallet(wallet, i === wallets.length - 1))}
+ ) : (
+
- {avatarVisible &&
}
+ {avatarVisible &&
}
- {walletName}
+ {activeWalletName}
{handleName || shortenedWalletAddress}
@@ -93,11 +156,13 @@ export const UserInfo = ({ onOpenWalletAccounts, avatarVisible = true }: UserInf
- )}
-
-
-
-
+
+ )}
+ {process.env.USE_MULTI_WALLET === 'true' ? undefined : (
+
+
+
+ )}
);
diff --git a/apps/browser-extension-wallet/src/components/MainMenu/DropdownMenuOverlay/components/WalletAccounts.tsx b/apps/browser-extension-wallet/src/components/MainMenu/DropdownMenuOverlay/components/WalletAccounts.tsx
index 991bc0396..e41d8a590 100644
--- a/apps/browser-extension-wallet/src/components/MainMenu/DropdownMenuOverlay/components/WalletAccounts.tsx
+++ b/apps/browser-extension-wallet/src/components/MainMenu/DropdownMenuOverlay/components/WalletAccounts.tsx
@@ -1,70 +1,127 @@
/* eslint-disable react/jsx-handler-names */
-import React, { useState } from 'react';
+import React, { useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
-import { NavigationButton } from '@lace/common';
+import { NavigationButton, toast } from '@lace/common';
import styles from './WalletAccounts.module.scss';
import { ProfileDropdown } from '@lace/ui';
import { AccountData } from '@lace/ui/dist/design-system/profile-dropdown/accounts/profile-dropdown-accounts-list.component';
import { DisableAccountConfirmation, EditAccountDrawer, useAccountDataModal } from '@lace/core';
+import { useWalletStore } from '@src/stores';
+import { useWalletManager } from '@hooks';
+import { TOAST_DEFAULT_DURATION } from '@hooks/useActionExecution';
+import { WalletType } from '@cardano-sdk/web-extension';
-const exampleAccountData = [
- {
- accountNumber: 1,
- label: 'Account #1',
- isUnlocked: true
- },
- {
- accountNumber: 2,
- label: 'Account #2',
- isUnlocked: true
- },
- {
- accountNumber: 3,
- label: 'Account #3',
- isUnlocked: true
- },
- {
- accountNumber: 4,
- label: 'Account #4',
- isUnlocked: true
- },
- {
- accountNumber: 5,
- label: 'Account #5',
- isUnlocked: true
- },
- {
- accountNumber: 6,
- label: 'Account #6',
- isUnlocked: false
- },
- {
- accountNumber: 7,
- label: 'Account #7',
- isUnlocked: false
- },
- {
- accountNumber: 8,
- label: 'Account #8',
- isUnlocked: false
- },
- {
- accountNumber: 9,
- label: 'Account #9',
- isUnlocked: false
- },
- {
- accountNumber: 10,
- label: 'Account #10',
- isUnlocked: false
- }
-];
+const defaultAccountName = (accountNumber: number) => `Account #${accountNumber}`;
+
+const NUMBER_OF_ACCOUNTS_PER_WALLET = 24;
export const WalletAccounts = ({ isPopup, onBack }: { isPopup: boolean; onBack: () => void }): React.ReactElement => {
const { t } = useTranslation();
+ const accountsListLabel = useMemo(
+ () => ({
+ unlock: t('browserView.settings.wallet.accounts.unlockLabel'),
+ lock: t('browserView.settings.wallet.accounts.lockLabel')
+ }),
+ [t]
+ );
const editAccountDrawer = useAccountDataModal();
const disableAccountConfirmation = useAccountDataModal();
- const [mockAccountData, setMockAccountData] = useState(exampleAccountData);
+ const { manageAccountsWallet: wallet, cardanoWallet, setIsDropdownMenuOpen } = useWalletStore();
+ const {
+ source: {
+ wallet: { walletId: activeWalletId },
+ account: activeAccount
+ }
+ } = cardanoWallet;
+ const { walletRepository, addAccount, activateWallet } = useWalletManager();
+ const disableUnlock = useMemo(
+ () =>
+ isPopup &&
+ (wallet.type === WalletType.Ledger || wallet.type === WalletType.Trezor) && {
+ reason: t('multiWallet.popupHwAccountEnable')
+ },
+ [isPopup, t, wallet.type]
+ );
+ const accountsData = useMemo(
+ () =>
+ Array.from({ length: NUMBER_OF_ACCOUNTS_PER_WALLET }).map((_, accountNumber): AccountData => {
+ const account = wallet.accounts.find(({ accountIndex }) => accountIndex === accountNumber);
+ return {
+ isUnlocked: !!account,
+ label: account ? account.metadata.name : defaultAccountName(accountNumber),
+ accountNumber,
+ isActive: activeWalletId === wallet.walletId && activeAccount?.accountIndex === accountNumber,
+ disableUnlock
+ };
+ }),
+ [wallet, activeAccount?.accountIndex, activeWalletId, disableUnlock]
+ );
+
+ const activateAccount = useCallback(
+ async (accountIndex: number) => {
+ await activateWallet({
+ walletId: wallet.walletId,
+ accountIndex
+ });
+ setIsDropdownMenuOpen(false);
+ const accountName = accountsData.find((acc) => acc.accountNumber === accountIndex)?.label;
+ toast.notify({
+ duration: TOAST_DEFAULT_DURATION,
+ text: t('multiWallet.activated.account', { accountName })
+ });
+ },
+ [wallet.walletId, activateWallet, accountsData, setIsDropdownMenuOpen, t]
+ );
+
+ const editAccount = useCallback(
+ (accountIndex: number) => editAccountDrawer.open(accountsData.find((a) => a.accountNumber === accountIndex)),
+ [editAccountDrawer, accountsData]
+ );
+
+ const deleteAccount = useCallback(
+ async (accountIndex: number) => {
+ disableAccountConfirmation.open(accountsData.find((a) => a.accountNumber === accountIndex));
+ },
+ [disableAccountConfirmation, accountsData]
+ );
+
+ const unlockAccount = useCallback(
+ async (accountIndex: number) => {
+ const name = defaultAccountName(accountIndex);
+ await addAccount({
+ wallet,
+ accountIndex,
+ metadata: { name }
+ });
+ setIsDropdownMenuOpen(false);
+ toast.notify({
+ duration: TOAST_DEFAULT_DURATION,
+ text: t('multiWallet.activated.account', { accountName: name })
+ });
+ },
+ [wallet, addAccount, t, setIsDropdownMenuOpen]
+ );
+
+ const lockAccount = useCallback(async () => {
+ await walletRepository.removeAccount({
+ walletId: wallet.walletId,
+ accountIndex: disableAccountConfirmation.accountData.accountNumber
+ });
+
+ disableAccountConfirmation.hide();
+ }, [walletRepository, disableAccountConfirmation, wallet.walletId]);
+
+ const renameAccount = useCallback(
+ async (newAccountName: string) => {
+ await walletRepository.updateAccountMetadata({
+ walletId: wallet.walletId,
+ accountIndex: editAccountDrawer.accountData.accountNumber,
+ metadata: { name: newAccountName }
+ });
+ editAccountDrawer.hide();
+ },
+ [walletRepository, wallet.walletId, editAccountDrawer]
+ );
return (
<>
@@ -85,27 +142,17 @@ export const WalletAccounts = ({ isPopup, onBack }: { isPopup: boolean; onBack:
data-testid="user-dropdown-wallet-account-list"
>
- editAccountDrawer.open(mockAccountData.find((a) => a.accountNumber === accountNumber))
- }
- onAccountDeleteClick={(accountNumber) => {
- disableAccountConfirmation.open(mockAccountData.find((a) => a.accountNumber === accountNumber));
- }}
- accounts={mockAccountData}
+ label={accountsListLabel}
+ onAccountActivateClick={activateAccount}
+ onAccountEditClick={editAccount}
+ onAccountDeleteClick={deleteAccount}
+ onAccountUnlockClick={unlockAccount}
+ accounts={accountsData}
/>
{
- const newAccountData = [...mockAccountData];
- const modifiedAccount = newAccountData.find(
- (a) => a.accountNumber === editAccountDrawer.accountData?.accountNumber
- );
- modifiedAccount.label = newAccountName;
- setMockAccountData(newAccountData);
- editAccountDrawer.hide();
- }}
+ onSave={renameAccount}
visible={editAccountDrawer.isOpen}
hide={editAccountDrawer.hide}
name={editAccountDrawer.accountData?.label}
@@ -122,15 +169,7 @@ export const WalletAccounts = ({ isPopup, onBack }: { isPopup: boolean; onBack:
zIndex={10_000}
open={disableAccountConfirmation.isOpen}
onCancel={disableAccountConfirmation.hide}
- onConfirm={() => {
- const newAccountData = [...mockAccountData];
- const modifiedAccount = newAccountData.find(
- (a) => a.accountNumber === disableAccountConfirmation.accountData?.accountNumber
- );
- modifiedAccount.isUnlocked = false;
- setMockAccountData(newAccountData);
- disableAccountConfirmation.hide();
- }}
+ onConfirm={lockAccount}
translations={{
title: t('account.disable.title'),
description: t('account.disable.description'),
diff --git a/apps/browser-extension-wallet/src/hooks/__tests__/useWalletManager.test.tsx b/apps/browser-extension-wallet/src/hooks/__tests__/useWalletManager.test.tsx
index 6b7830d88..355d5eb08 100644
--- a/apps/browser-extension-wallet/src/hooks/__tests__/useWalletManager.test.tsx
+++ b/apps/browser-extension-wallet/src/hooks/__tests__/useWalletManager.test.tsx
@@ -15,11 +15,14 @@ const mockEmip3encrypt = jest.fn();
const mockConnectDevice = jest.fn();
const mockRestoreWalletFromKeyAgent = jest.fn();
const mockSwitchKeyAgents = jest.fn();
+const mockLedgerGetXpub = jest.fn();
+const mockTrezorGetXpub = jest.fn();
+const mockInitializeTrezorTransport = jest.fn();
const mockLedgerCreateWithDevice = jest.fn();
const mockUseAppSettingsContext = jest.fn().mockReturnValue([{}, jest.fn()]);
import React from 'react';
import { renderHook } from '@testing-library/react-hooks';
-import { LOCK_VALUE, useWalletManager } from '../useWalletManager';
+import { LOCK_VALUE, UseWalletManager, useWalletManager } from '../useWalletManager';
import {
AppSettingsProvider,
BackgroundServiceAPIProvider,
@@ -32,9 +35,11 @@ import * as localStorage from '@src/utils/local-storage';
import * as AppSettings from '@providers/AppSettings';
import * as walletApiUi from '@src/lib/wallet-api-ui';
import { of } from 'rxjs';
-import { AnyWallet, WalletType } from '@cardano-sdk/web-extension';
+import { AnyBip32Wallet, AnyWallet, WalletManagerActivateProps, WalletType } from '@cardano-sdk/web-extension';
import { Wallet } from '@lace/cardano';
+(walletApiUi as any).logger = console;
+
jest.mock('@providers/AppSettings', () => ({
...jest.requireActual('@providers/AppSettings'),
useAppSettingsContext: mockUseAppSettingsContext
@@ -60,7 +65,14 @@ jest.mock('@lace/cardano', () => {
...actual.Wallet,
Ledger: {
LedgerKeyAgent: {
- createWithDevice: mockLedgerCreateWithDevice
+ createWithDevice: mockLedgerCreateWithDevice,
+ getXpub: mockLedgerGetXpub
+ }
+ },
+ Trezor: {
+ TrezorKeyAgent: {
+ getXpub: mockTrezorGetXpub,
+ initializeTrezorTransport: mockInitializeTrezorTransport
}
},
restoreWalletFromKeyAgent: mockRestoreWalletFromKeyAgent,
@@ -96,6 +108,11 @@ const getWrapper =
);
+const render = () =>
+ renderHook(() => useWalletManager(), {
+ wrapper: getWrapper({})
+ }).result.current;
+
describe('Testing useWalletManager hook', () => {
beforeEach(() => {
jest.resetAllMocks();
@@ -363,19 +380,19 @@ describe('Testing useWalletManager hook', () => {
describe('createHardwareWallet', () => {
test('should use cardano manager to create wallet', async () => {
const walletId = 'walletId';
- mockLedgerCreateWithDevice.mockResolvedValue({
- extendedAccountPublicKey: 'pubkey'
- });
+ mockLedgerGetXpub.mockResolvedValue('pubkey');
(walletApiUi.walletRepository as any).addWallet = jest.fn().mockResolvedValue(walletId);
(walletApiUi.walletRepository as any).addAccount = jest.fn().mockResolvedValue(undefined);
(walletApiUi.walletManager as any).activate = jest.fn().mockResolvedValue(undefined);
const accountIndex = 1;
const name = 'name';
- const chainId = {
- networkId: 0,
- networkMagic: 0
- };
+ jest.spyOn(stores, 'useWalletStore').mockImplementation(() => ({
+ currentChain: {
+ networkId: 0,
+ networkMagic: 0
+ }
+ }));
const connectedDevice = 'Ledger' as any;
const deviceConnection = 'deviceConnection' as any;
@@ -390,7 +407,6 @@ describe('Testing useWalletManager hook', () => {
deviceConnection,
accountIndex,
name,
- chainId,
connectedDevice
});
expect(walletApiUi.walletRepository.addWallet).toBeCalledTimes(1);
@@ -478,6 +494,11 @@ describe('Testing useWalletManager hook', () => {
describe('deleteWallet', () => {
const walletId = 'walletId';
+ let clearBackgroundStorage: jest.Mock;
+ let clearLocalStorage: jest.Mock;
+ let resetWalletLock: jest.Mock;
+ let setCardanoWallet: jest.Mock;
+ let deleteWallet: UseWalletManager['deleteWallet'];
beforeEach(() => {
(walletApiUi.walletManager as any).activeWalletId$ = of({ walletId });
@@ -491,21 +512,17 @@ describe('Testing useWalletManager hook', () => {
setCardanoCoin: jest.fn(),
setAddressesDiscoveryCompleted: () => {}
}));
- });
-
- test('should shutdown wallet, delete data from the LS, indexed DB and background storage, reset lock and current chain ', async () => {
- const clearBackgroundStorage = jest.fn();
- const clearLocalStorage = jest.fn();
+ clearBackgroundStorage = jest.fn();
+ clearLocalStorage = jest.fn();
jest.spyOn(localStorage, 'clearLocalStorage').mockImplementation(clearLocalStorage);
- const resetWalletLock = jest.fn();
- const setCardanoWallet = jest.fn();
+ resetWalletLock = jest.fn();
+ setCardanoWallet = jest.fn();
jest.spyOn(stores, 'useWalletStore').mockImplementation(() => ({
resetWalletLock,
setCardanoWallet
}));
-
- const {
+ ({
result: {
current: { deleteWallet }
}
@@ -515,8 +532,12 @@ describe('Testing useWalletManager hook', () => {
clearBackgroundStorage
} as unknown as BackgroundServiceAPIProviderProps['value']
})
- });
- expect(deleteWallet).toBeDefined();
+ }));
+ });
+
+ test('should shutdown wallet, delete data from the LS, indexed DB and background storage, reset lock and current chain ', async () => {
+ (walletApiUi.walletRepository as any).wallets$ = of([]);
+
await deleteWallet();
expect(walletApiUi.walletManager.deactivate).toBeCalledTimes(1);
expect(walletApiUi.walletManager.destroyData).toBeCalled();
@@ -538,6 +559,24 @@ describe('Testing useWalletManager hook', () => {
expect(resetWalletLock).toBeCalledWith();
expect(setCardanoWallet).toBeCalledWith();
});
+
+ test('should activate another wallet if exists after deletion', async () => {
+ const remainingWallet = {
+ type: WalletType.InMemory,
+ walletId: 'remaining-wallet-id',
+ accounts: [{ accountIndex: 1 }]
+ };
+ (walletApiUi.walletRepository as any).wallets$ = of([remainingWallet]);
+
+ await deleteWallet();
+
+ expect(walletApiUi.walletManager.activate).toBeCalledWith(
+ expect.objectContaining({
+ walletId: remainingWallet.walletId,
+ accountIndex: remainingWallet.accounts[0].accountIndex
+ })
+ );
+ });
});
describe('switchNetwork', () => {
@@ -632,4 +671,119 @@ describe('Testing useWalletManager hook', () => {
expect(setAddressesDiscoveryCompleted).toBeCalledWith(false);
});
});
+
+ describe('addAccount', () => {
+ describe('for existing bip32 wallet', () => {
+ const extendedAccountPublicKey =
+ '12b608b67a743891656d6463f72aa6e5f0e62ba6dc47e32edfebafab1acf0fa9f3033c2daefa3cb2ac16916b08c7e7424d4e1aafae2206d23c4d002299c07128';
+ const walletTypes = [
+ {
+ type: WalletType.InMemory,
+ walletProps: {
+ encryptedSecrets: {
+ rootPrivateKeyBytes:
+ '8403cf9d8267a7169381dd476f4fda48e1926fec8942ec51892e428e152fbed4835711cccb7efcae379627f477abb46c883f6b0c221f3aea40f9d931d2e8fdc69f85f16eb91ca380fc2e1edc2543e4dd71c1866208ea6c6960bca99f974e25776067e9a242b0e4066b96bd4d89ca99db5bd77bb65573b9cbeef85222ceed6d5a4dc516213ace986f03b183365505119b9a0abdc4375bfdf2363d7433'
+ }
+ },
+ prepare: () => {
+ global.prompt = jest.fn(() => 'passphrase1');
+ mockEmip3decrypt.mockImplementationOnce(
+ jest.requireActual('@lace/cardano').Wallet.KeyManagement.emip3decrypt
+ );
+ }
+ },
+ {
+ type: WalletType.Trezor,
+ prepare: () => mockTrezorGetXpub.mockResolvedValueOnce(extendedAccountPublicKey)
+ },
+ {
+ type: WalletType.Ledger,
+ prepare: () => mockLedgerGetXpub.mockResolvedValueOnce(extendedAccountPublicKey)
+ }
+ ];
+
+ beforeEach(() => {
+ walletApiUi.walletRepository.addAccount = jest.fn().mockResolvedValueOnce(void 0);
+ });
+
+ it.each(walletTypes)(
+ 'derives extended account public key for $type wallet and adds new account into the repository',
+ async ({ type, walletProps, prepare }) => {
+ prepare();
+ const walletId = 'bip32-wallet-id';
+ const addAccountProps = {
+ wallet: { walletId, type, ...walletProps } as AnyBip32Wallet,
+ accountIndex: 0,
+ metadata: { name: 'new account' }
+ };
+
+ const { addAccount } = render();
+ await addAccount(addAccountProps);
+ expect(walletApiUi.walletRepository.addAccount).toBeCalledWith({
+ walletId,
+ accountIndex: addAccountProps.accountIndex,
+ metadata: addAccountProps.metadata,
+ extendedAccountPublicKey
+ });
+ }
+ );
+ });
+ });
+
+ describe('activateWallet', () => {
+ const walletId = 'walletId';
+ const accountIndex = 1;
+ const originalMetadata = { name: 'wallet' };
+
+ beforeEach(() => {
+ walletApiUi.walletRepository.wallets$ = of([
+ {
+ walletId,
+ metadata: originalMetadata
+ } as AnyBip32Wallet
+ ]);
+ walletApiUi.walletManager.deactivate = jest.fn().mockResolvedValueOnce(void 0);
+ walletApiUi.walletRepository.updateWalletMetadata = jest.fn().mockResolvedValueOnce(void 0);
+ walletApiUi.walletManager.activate = jest.fn().mockResolvedValueOnce(void 0);
+ walletApiUi.walletManager.deactivate = jest.fn().mockResolvedValueOnce(void 0);
+ });
+
+ it('does not re-activate an already active wallet', async () => {
+ walletApiUi.walletManager.activeWalletId$ = of({ walletId, accountIndex } as WalletManagerActivateProps<
+ any,
+ any
+ >);
+
+ const { activateWallet } = render();
+ await activateWallet({ walletId, accountIndex });
+
+ expect(walletApiUi.walletRepository.updateWalletMetadata).not.toBeCalled();
+ expect(walletApiUi.walletManager.activate).not.toBeCalled();
+ expect(walletApiUi.walletManager.deactivate).not.toBeCalled();
+ });
+
+ it('stores lastActiveAccountIndex in wallet metadata and activates wallet via WalletManager', async () => {
+ walletApiUi.walletManager.activeWalletId$ = of({ walletId: 'otherId' } as WalletManagerActivateProps);
+
+ const { activateWallet } = render();
+ await activateWallet({ walletId, accountIndex });
+
+ expect(walletApiUi.walletRepository.updateWalletMetadata).toBeCalledWith({
+ walletId,
+ metadata: {
+ ...originalMetadata,
+ lastActiveAccountIndex: accountIndex
+ }
+ });
+
+ expect(walletApiUi.walletManager.activate).toBeCalledWith({
+ walletId,
+ accountIndex,
+ chainId: expect.objectContaining({
+ networkMagic: expect.anything(),
+ networkId: expect.anything()
+ })
+ });
+ });
+ });
});
diff --git a/apps/browser-extension-wallet/src/hooks/useWalletManager.ts b/apps/browser-extension-wallet/src/hooks/useWalletManager.ts
index c71d60162..4ad18cf6a 100644
--- a/apps/browser-extension-wallet/src/hooks/useWalletManager.ts
+++ b/apps/browser-extension-wallet/src/hooks/useWalletManager.ts
@@ -19,9 +19,10 @@ import { getWalletFromStorage } from '@src/utils/get-wallet-from-storage';
import { getUserIdService } from '@providers/AnalyticsProvider/getUserIdService';
import { ENHANCED_ANALYTICS_OPT_IN_STATUS_LS_KEY } from '@providers/AnalyticsProvider/config';
import { ILocalStorage } from '@src/types';
-import { firstValueFrom } from 'rxjs';
+import { combineLatest, firstValueFrom } from 'rxjs';
import {
AddWalletProps,
+ AnyBip32Wallet,
AnyWallet,
WalletManagerActivateProps,
WalletManagerApi,
@@ -40,7 +41,7 @@ export interface CreateWallet {
name: string;
mnemonic: string[];
password: string;
- chainId: Wallet.Cardano.ChainId;
+ chainId?: Wallet.Cardano.ChainId;
}
export interface SetWallet {
@@ -53,10 +54,17 @@ export interface CreateHardwareWallet {
accountIndex?: number;
name: string;
deviceConnection: Wallet.DeviceConnection;
- chainId: Wallet.Cardano.ChainId;
connectedDevice: Wallet.HardwareWallets;
}
+type WalletManagerAddAccountProps = {
+ wallet: AnyBip32Wallet;
+ metadata: Wallet.AccountMetadata;
+ accountIndex: number;
+};
+
+type ActivateWalletProps = Omit;
+
export interface UseWalletManager {
walletManager: WalletManagerApi;
walletRepository: WalletRepositoryApi;
@@ -67,14 +75,79 @@ export interface UseWalletManager {
activeWalletProps: WalletManagerActivateProps | null
) => Promise;
createWallet: (args: CreateWallet) => Promise;
+ activateWallet: (args: Omit) => Promise;
createHardwareWallet: (args: CreateHardwareWallet) => Promise;
connectHardwareWallet: (model: Wallet.HardwareWallets) => Promise;
saveHardwareWallet: (wallet: Wallet.CardanoWallet, chainName?: Wallet.ChainName) => Promise;
- deleteWallet: (isForgotPasswordFlow?: boolean) => Promise;
+ /**
+ * @returns active wallet id after deleting the wallet; undefined if deleted the last wallet
+ */
+ deleteWallet: (isForgotPasswordFlow?: boolean) => Promise;
switchNetwork: (chainName: Wallet.ChainName) => Promise;
+ addAccount: (props: WalletManagerAddAccountProps) => Promise;
}
-const provider = { type: Wallet.WalletManagerProviderTypes.CARDANO_SERVICES_PROVIDER, options: {} };
+const clearBytes = (bytes: Uint8Array) => {
+ for (let i = 0; i < bytes.length; i++) {
+ bytes[i] = 0;
+ }
+};
+
+const getHwExtendedAccountPublicKey = async (
+ walletType: Wallet.HardwareWallets,
+ accountIndex: number,
+ deviceConnection?: Wallet.DeviceConnection
+) => {
+ switch (walletType) {
+ case WalletType.Ledger:
+ return Wallet.Ledger.LedgerKeyAgent.getXpub({
+ communicationType: Wallet.KeyManagement.CommunicationType.Web,
+ deviceConnection: typeof deviceConnection !== 'boolean' ? deviceConnection : undefined,
+ accountIndex
+ });
+ case WalletType.Trezor:
+ await Wallet.Trezor.TrezorKeyAgent.initializeTrezorTransport({
+ manifest: Wallet.manifest,
+ communicationType: Wallet.KeyManagement.CommunicationType.Web
+ });
+ return Wallet.Trezor.TrezorKeyAgent.getXpub({
+ communicationType: Wallet.KeyManagement.CommunicationType.Web,
+ accountIndex
+ });
+ }
+};
+
+const getExtendedAccountPublicKey = async (
+ wallet: AnyBip32Wallet,
+ accountIndex: number
+) => {
+ // eslint-disable-next-line sonarjs/no-small-switch
+ switch (wallet.type) {
+ case WalletType.InMemory: {
+ // eslint-disable-next-line no-alert
+ const passphrase = Buffer.from(prompt('Please enter your passphrase'));
+ const rootPrivateKeyBytes = await Wallet.KeyManagement.emip3decrypt(
+ Buffer.from(wallet.encryptedSecrets.rootPrivateKeyBytes, 'hex'),
+ passphrase
+ );
+ const rootPrivateKeyBuffer = Buffer.from(rootPrivateKeyBytes);
+ const accountPrivateKey = await Wallet.KeyManagement.util.deriveAccountPrivateKey({
+ bip32Ed25519: Wallet.bip32Ed25519,
+ accountIndex,
+ rootPrivateKey: Wallet.Crypto.Bip32PrivateKeyHex(rootPrivateKeyBuffer.toString('hex'))
+ });
+ const accountPublicKey = await Wallet.bip32Ed25519.getBip32PublicKey(accountPrivateKey);
+ clearBytes(passphrase);
+ clearBytes(rootPrivateKeyBytes);
+ clearBytes(rootPrivateKeyBuffer);
+ return accountPublicKey;
+ }
+ case WalletType.Ledger:
+ case WalletType.Trezor:
+ return getHwExtendedAccountPublicKey(wallet.type, accountIndex);
+ }
+};
+
const chainIdFromName = (chainName: Wallet.ChainName) => {
const chainId = Wallet.Cardano.ChainIds[chainName];
if (!chainId || !AVAILABLE_CHAINS.includes(chainName)) throw new Error('Chain not supported');
@@ -86,8 +159,7 @@ const defaultAccountName = (accountIndex: number) => `Account #${accountIndex}`;
const keyAgentDataToAddWalletProps = async (
data: Wallet.KeyManagement.SerializableKeyAgentData,
backgroundService: BackgroundService,
- name: string,
- lockValue: Wallet.HexBlob | undefined
+ metadata: Wallet.WalletMetadata
): Promise> => {
const accounts = [
{
@@ -102,7 +174,7 @@ const keyAgentDataToAddWalletProps = async (
if (!mnemonic) throw new Error('Inconsistent state: mnemonic not found for in-memory wallet');
return {
type: WalletType.InMemory,
- metadata: { name, lockValue },
+ metadata,
encryptedSecrets: {
rootPrivateKeyBytes: HexBlob.fromBytes(Buffer.from(data.encryptedRootPrivateKeyBytes)),
keyMaterial: HexBlob.fromBytes(Buffer.from(JSON.parse(mnemonic).data))
@@ -113,13 +185,13 @@ const keyAgentDataToAddWalletProps = async (
case Wallet.KeyManagement.KeyAgentType.Ledger:
return {
type: WalletType.Ledger,
- metadata: { name },
+ metadata,
accounts
};
case Wallet.KeyManagement.KeyAgentType.Trezor:
return {
type: WalletType.Trezor,
- metadata: { name },
+ metadata,
accounts
};
default:
@@ -135,73 +207,6 @@ const encryptMnemonic = async (mnemonic: string[], passphrase: Uint8Array) => {
return HexBlob.fromBytes(walletEncrypted);
};
-/**
- * Creates a Ledger or Trezor hardware wallet
- * and saves it in browser storage with the data to lock/unlock it
- */
-const createHardwareWallet = async ({
- accountIndex = 0,
- deviceConnection,
- name,
- chainId,
- connectedDevice
-}: CreateHardwareWallet): Promise => {
- const keyAgent =
- connectedDevice === WalletType.Ledger
- ? await Wallet.Ledger.LedgerKeyAgent.createWithDevice(
- {
- chainId,
- communicationType: Wallet.KeyManagement.CommunicationType.Web,
- accountIndex,
- deviceConnection: typeof deviceConnection === 'object' ? deviceConnection : undefined
- },
- { bip32Ed25519: Wallet.bip32Ed25519, logger }
- )
- : await Wallet.Trezor.TrezorKeyAgent.createWithDevice(
- {
- chainId,
- trezorConfig: {
- communicationType: Wallet.KeyManagement.CommunicationType.Web,
- manifest: Wallet.manifest
- },
- accountIndex
- },
- { bip32Ed25519: Wallet.bip32Ed25519, logger }
- );
-
- const addWalletProps: AddWalletProps = {
- metadata: { name },
- type: connectedDevice,
- accounts: [
- {
- extendedAccountPublicKey: keyAgent.extendedAccountPublicKey,
- accountIndex,
- metadata: { name: defaultAccountName(accountIndex) }
- }
- ]
- };
- const walletId = await walletRepository.addWallet(addWalletProps);
- await walletManager.activate({
- walletId,
- chainId: DEFAULT_CHAIN_ID,
- accountIndex,
- provider
- });
-
- return {
- name,
- signingCoordinator,
- wallet: observableWallet,
- source: {
- wallet: {
- ...addWalletProps,
- walletId
- },
- account: addWalletProps.accounts[0]
- }
- };
-};
-
/** Connects a hardware wallet device */
export const connectHardwareWallet = async (model: Wallet.HardwareWallets): Promise =>
await Wallet.connectDevice(model);
@@ -216,7 +221,9 @@ export const useWalletManager = (): UseWalletManager => {
currentChain,
setCurrentChain,
setCardanoCoin,
- setAddressesDiscoveryCompleted
+ setAddressesDiscoveryCompleted,
+ manageAccountsWallet,
+ setManageAccountsWallet
} = useWalletStore();
const [settings, updateAppSettings] = useAppSettingsContext();
const {
@@ -235,6 +242,56 @@ export const useWalletManager = (): UseWalletManager => {
return (storedChain?.chainName && chainIdFromName(storedChain.chainName)) || DEFAULT_CHAIN_ID;
}, [currentChain]);
+ /**
+ * Creates a Ledger or Trezor hardware wallet
+ * and saves it in browser storage with the data to lock/unlock it
+ */
+ const createHardwareWallet = useCallback(
+ async ({
+ accountIndex = 0,
+ deviceConnection,
+ name,
+ connectedDevice
+ }: CreateHardwareWallet): Promise => {
+ const extendedAccountPublicKey = await getHwExtendedAccountPublicKey(
+ connectedDevice,
+ accountIndex,
+ deviceConnection
+ );
+ const addWalletProps: AddWalletProps = {
+ metadata: { name, lastActiveAccountIndex: accountIndex },
+ type: connectedDevice,
+ accounts: [
+ {
+ extendedAccountPublicKey,
+ accountIndex,
+ metadata: { name: defaultAccountName(accountIndex) }
+ }
+ ]
+ };
+ const walletId = await walletRepository.addWallet(addWalletProps);
+ await walletManager.activate({
+ walletId,
+ chainId: getCurrentChainId(),
+ accountIndex
+ });
+
+ return {
+ name,
+ signingCoordinator,
+ wallet: observableWallet,
+ source: {
+ wallet: {
+ ...addWalletProps,
+ walletId
+ },
+ account: addWalletProps.accounts[0]
+ }
+ };
+ },
+ [getCurrentChainId]
+ );
+
const tryMigrateToWalletRepository = useCallback(async (): Promise<
AnyWallet[] | undefined
> => {
@@ -267,15 +324,18 @@ export const useWalletManager = (): UseWalletManager => {
// deleteFromLocalStorage('wallet');
const walletId = await walletRepository.addWallet(
- await keyAgentDataToAddWalletProps(keyAgentData, backgroundService, walletName, lockValue)
+ await keyAgentDataToAddWalletProps(keyAgentData, backgroundService, {
+ name: walletName,
+ lockValue,
+ lastActiveAccountIndex: keyAgentData.accountIndex
+ })
);
await walletManager.activate({
// when wallet is locked before migration, keyAgentData.chainId is the default one instead of last active one
chainId: getCurrentChainId(),
walletId,
- accountIndex: keyAgentData.accountIndex,
- provider
+ accountIndex: keyAgentData.accountIndex
});
return firstValueFrom(walletRepository.wallets$);
@@ -338,9 +398,30 @@ export const useWalletManager = (): UseWalletManager => {
) {
setCardanoWallet(newCardanoWallet);
}
+
+ // Synchronize the currently managed wallet UI state with service worker
+ if (manageAccountsWallet) {
+ const managedWallet = wallets.find(
+ (w): w is AnyBip32Wallet =>
+ w.walletId === manageAccountsWallet.walletId && w.type !== WalletType.Script
+ );
+ if (!deepEquals(managedWallet, manageAccountsWallet)) {
+ setManageAccountsWallet(managedWallet);
+ }
+ }
+
return newCardanoWallet;
},
- [setCardanoWallet, setCurrentChain, tryMigrateToWalletRepository, cardanoWallet, currentChain, setCardanoCoin]
+ [
+ setCardanoWallet,
+ setCurrentChain,
+ tryMigrateToWalletRepository,
+ cardanoWallet,
+ currentChain,
+ manageAccountsWallet,
+ setCardanoCoin,
+ setManageAccountsWallet
+ ]
);
/**
@@ -348,11 +429,7 @@ export const useWalletManager = (): UseWalletManager => {
*/
const saveHardwareWallet = useCallback(
async (wallet: Wallet.CardanoWallet, chainName = CHAIN): Promise => {
- updateAppSettings({
- chainName,
- // Doesn't make sense for hardware wallets
- mnemonicVerificationFrequency: ''
- });
+ updateAppSettings({ chainName });
setCardanoWallet(wallet);
setCurrentChain(chainName);
@@ -364,7 +441,12 @@ export const useWalletManager = (): UseWalletManager => {
* Creates or restores a new in-memory wallet with the cardano-js-sdk and saves it in wallet repository
*/
const createWallet = useCallback(
- async ({ mnemonic, name, password, chainId }: CreateWallet): Promise => {
+ async ({
+ mnemonic,
+ name,
+ password,
+ chainId = getCurrentChainId()
+ }: CreateWallet): Promise => {
const accountIndex = 0;
const passphrase = Buffer.from(password, 'utf8');
const keyAgent = await Wallet.KeyManagement.InMemoryKeyAgent.fromBip39MnemonicWords(
@@ -382,7 +464,7 @@ export const useWalletManager = (): UseWalletManager => {
const lockValue = HexBlob.fromBytes(await Wallet.KeyManagement.emip3encrypt(LOCK_VALUE, passphrase));
const addWalletProps: AddWalletProps = {
- metadata: { name, lockValue },
+ metadata: { name, lockValue, lastActiveAccountIndex: accountIndex },
encryptedSecrets: {
keyMaterial: await encryptMnemonic(mnemonic, passphrase),
rootPrivateKeyBytes: HexBlob.fromBytes(
@@ -405,9 +487,8 @@ export const useWalletManager = (): UseWalletManager => {
const walletId = await walletRepository.addWallet(addWalletProps);
await walletManager.activate({
walletId,
- chainId: getCurrentChainId(),
- accountIndex,
- provider
+ chainId,
+ accountIndex
});
// Needed for reset password flow
@@ -434,14 +515,54 @@ export const useWalletManager = (): UseWalletManager => {
[getCurrentChainId]
);
+ const activateWallet = useCallback(
+ async (props: ActivateWalletProps): Promise => {
+ const [wallets, activeWallet] = await firstValueFrom(
+ combineLatest([walletRepository.wallets$, walletManager.activeWalletId$])
+ );
+ if (activeWallet?.walletId === props.walletId && activeWallet?.accountIndex === props.accountIndex) {
+ logger.debug('Wallet is already active');
+ return;
+ }
+ await walletManager.deactivate();
+ await walletRepository.updateWalletMetadata({
+ walletId: props.walletId,
+ metadata: {
+ ...wallets.find(({ walletId }) => walletId === props.walletId).metadata,
+ lastActiveAccountIndex: props.accountIndex
+ }
+ });
+ await walletManager.activate({
+ ...props,
+ chainId: getCurrentChainId()
+ });
+ },
+ [getCurrentChainId]
+ );
+
/**
* Deletes Wallet from memory, all info from browser storage and destroys all stores
+ *
+ * @returns active wallet id after deleting the wallet
*/
const deleteWallet = useCallback(
- async (isForgotPasswordFlow = false): Promise => {
+ async (isForgotPasswordFlow = false): Promise => {
const activeWallet = await firstValueFrom(walletManager.activeWalletId$);
await walletManager.deactivate();
await walletRepository.removeWallet(activeWallet.walletId);
+
+ const wallets = await firstValueFrom(walletRepository.wallets$);
+ if (wallets.length > 0) {
+ const activateProps = {
+ walletId: wallets[0].walletId,
+ chainId: getCurrentChainId(),
+ accountIndex: wallets[0].type === WalletType.Script ? undefined : wallets[0].accounts[0].accountIndex
+ };
+ await walletManager.activate(activateProps);
+ return activateProps;
+ }
+
+ // deleting last wallet clears all data
if (!isForgotPasswordFlow) {
deleteFromLocalStorage('appSettings');
}
@@ -486,7 +607,15 @@ export const useWalletManager = (): UseWalletManager => {
await walletManager.destroyData(activeWallet.walletId, chainIdFromName(chainName));
}
},
- [resetWalletLock, setCardanoWallet, backgroundService, userIdService, clearAddressBook, clearNftsFolders]
+ [
+ resetWalletLock,
+ setCardanoWallet,
+ backgroundService,
+ userIdService,
+ clearAddressBook,
+ clearNftsFolders,
+ getCurrentChainId
+ ]
);
/**
@@ -548,7 +677,23 @@ export const useWalletManager = (): UseWalletManager => {
[walletLock, loadWallet]
);
+ const addAccount = useCallback(
+ async ({ wallet, accountIndex, metadata }: WalletManagerAddAccountProps): Promise => {
+ const extendedAccountPublicKey = await getExtendedAccountPublicKey(wallet, accountIndex);
+ await walletRepository.addAccount({
+ accountIndex,
+ extendedAccountPublicKey,
+ metadata,
+ walletId: wallet.walletId
+ });
+ await walletManager.activate({ chainId: getCurrentChainId(), walletId: wallet.walletId, accountIndex });
+ },
+ [getCurrentChainId]
+ );
+
return {
+ activateWallet,
+ addAccount,
lockWallet,
unlockWallet,
loadWallet,
diff --git a/apps/browser-extension-wallet/src/lib/scripts/background/wallet.ts b/apps/browser-extension-wallet/src/lib/scripts/background/wallet.ts
index 6617be828..4e74fa959 100644
--- a/apps/browser-extension-wallet/src/lib/scripts/background/wallet.ts
+++ b/apps/browser-extension-wallet/src/lib/scripts/background/wallet.ts
@@ -20,7 +20,6 @@ import {
walletManagerProperties,
walletRepositoryProperties
} from '@cardano-sdk/web-extension';
-import { config } from '@src/config';
import { Wallet } from '@lace/cardano';
import { ADA_HANDLE_POLICY_ID, HANDLE_SERVER_URLS } from '@src/features/ada-handle/config';
import { Cardano, NotImplementedError } from '@cardano-sdk/core';
@@ -54,14 +53,8 @@ const chainIdToChainName = (chainId: Cardano.ChainId): Wallet.ChainName => {
};
const walletFactory: WalletFactory = {
- create: async ({ chainId, accountIndex, provider }, wallet, { stores, witnesser }) => {
- const chainName: Wallet.ChainName =
- // provider.options are useless now, because they are bound to wallet (not to wallet per network).
- // When we need to support custom provider URLs, add argument to WalletManager.switchNetwork
- // and store it in wallet manager's storage for loading custom URL providers
- provider?.type === (Wallet.WalletManagerProviderTypes.CARDANO_SERVICES_PROVIDER as unknown as string)
- ? chainIdToChainName(chainId)
- : config().CHAIN;
+ create: async ({ chainId, accountIndex }, wallet, { stores, witnesser }) => {
+ const chainName: Wallet.ChainName = chainIdToChainName(chainId);
const providers = getProviders(chainName);
if (wallet.type === WalletType.Script || typeof accountIndex !== 'number') {
throw new NotImplementedError('Script wallet support is not implemented');
diff --git a/apps/browser-extension-wallet/src/lib/translations/en.json b/apps/browser-extension-wallet/src/lib/translations/en.json
index 2596248f3..d9eb93166 100644
--- a/apps/browser-extension-wallet/src/lib/translations/en.json
+++ b/apps/browser-extension-wallet/src/lib/translations/en.json
@@ -792,7 +792,8 @@
"accounts": {
"title": "Accounts",
"description": "Select account to activate.",
- "unlockLabel": "Enable"
+ "unlockLabel": "Enable",
+ "lockLabel": "Disable"
},
"general": {
"title": "Your keys",
@@ -1407,6 +1408,12 @@
"description": "You'll have to start over.",
"cancel": "Go back",
"confirm": "Proceed"
- }
+ },
+ "activated": {
+ "wallet": "Wallet \"{{walletName}}\" activated",
+ "account": "Account \"{{accountName}}\" activated"
+ },
+ "popupHwAccountEnable": "Hardware wallets require the expanded view to enable accounts",
+ "walletAlreadyExists": "Wallet wasn't created because it already exists"
}
}
diff --git a/apps/browser-extension-wallet/src/lib/wallet-api-ui.ts b/apps/browser-extension-wallet/src/lib/wallet-api-ui.ts
index 114f03142..9138d804e 100644
--- a/apps/browser-extension-wallet/src/lib/wallet-api-ui.ts
+++ b/apps/browser-extension-wallet/src/lib/wallet-api-ui.ts
@@ -1,5 +1,6 @@
import {
SigningCoordinator,
+ WalletConflictError,
WalletRepositoryApi,
WalletType,
consumeRemoteApi,
@@ -44,13 +45,19 @@ export const observableWallet = consumeRemoteApi(
);
export const walletRepository = consumeRemoteApi>(
- { baseChannel: repositoryChannel(process.env.WALLET_NAME), properties: walletRepositoryProperties },
+ {
+ baseChannel: repositoryChannel(process.env.WALLET_NAME),
+ properties: walletRepositoryProperties,
+ errorTypes: [WalletConflictError]
+ },
{ logger, runtime }
);
+export const keyAgentFactory = createKeyAgentFactory({ bip32Ed25519: Wallet.bip32Ed25519, logger });
+
export const signingCoordinator = new SigningCoordinator(
{ hwOptions: { manifest: Wallet.manifest, communicationType: Wallet.KeyManagement.CommunicationType.Web } },
- { keyAgentFactory: createKeyAgentFactory({ bip32Ed25519: Wallet.bip32Ed25519, logger }) }
+ { keyAgentFactory }
);
exposeSigningCoordinatorApi({ signingCoordinator }, { logger, runtime });
diff --git a/apps/browser-extension-wallet/src/routes/PopupView.tsx b/apps/browser-extension-wallet/src/routes/PopupView.tsx
index a49351263..570b47055 100644
--- a/apps/browser-extension-wallet/src/routes/PopupView.tsx
+++ b/apps/browser-extension-wallet/src/routes/PopupView.tsx
@@ -30,6 +30,7 @@ export const PopupView = (): React.ReactElement => {
walletInfo,
isWalletLocked,
walletLock,
+ walletState,
initialHdDiscoveryCompleted
} = useWalletStore();
@@ -66,7 +67,7 @@ export const PopupView = (): React.ReactElement => {
return ;
}
- if (!!cardanoWallet && walletInfo && inMemoryWallet && initialHdDiscoveryCompleted) {
+ if (!!cardanoWallet && walletInfo && walletState && inMemoryWallet && initialHdDiscoveryCompleted) {
return ;
}
diff --git a/apps/browser-extension-wallet/src/stores/slices/ui-slice.ts b/apps/browser-extension-wallet/src/stores/slices/ui-slice.ts
index 039c33693..8816d1ce9 100644
--- a/apps/browser-extension-wallet/src/stores/slices/ui-slice.ts
+++ b/apps/browser-extension-wallet/src/stores/slices/ui-slice.ts
@@ -1,6 +1,6 @@
import { Wallet } from '@lace/cardano';
import isNil from 'lodash/isNil';
-import { ILocalStorage, NetworkConnectionStates, WalletUIProps } from '@src/types';
+import { ILocalStorage, NetworkConnectionStates, WalletUI, WalletUIProps } from '@src/types';
import { AppMode, cardanoCoin, CARDANO_COIN_SYMBOL } from '@src/utils/constants';
import { GetState, SetState } from 'zustand';
import { SliceCreator, UISlice } from '../types';
@@ -13,8 +13,14 @@ const setWalletUI = (
{
network,
networkConnection,
- areBalancesVisible
- }: { network?: Wallet.Cardano.NetworkId; networkConnection?: NetworkConnectionStates; areBalancesVisible?: boolean },
+ areBalancesVisible,
+ isDropdownMenuOpen
+ }: {
+ network?: Wallet.Cardano.NetworkId;
+ networkConnection?: NetworkConnectionStates;
+ areBalancesVisible?: boolean;
+ isDropdownMenuOpen?: boolean;
+ },
{ get, set }: { get: GetState; set: SetState }
): void => {
const { walletUI } = get();
@@ -28,13 +34,20 @@ const setWalletUI = (
symbol: CARDANO_COIN_SYMBOL[network]
}
}),
+ ...(typeof isDropdownMenuOpen !== 'undefined' && { isDropdownMenuOpen }),
...(networkConnection && { networkConnection }),
...(!isNil(areBalancesVisible) && { areBalancesVisible })
}
});
};
-const getWalletUI = ({ currentNetwork, appMode }: { currentNetwork: Wallet.Cardano.NetworkId; appMode: AppMode }) => {
+const getWalletUI = ({
+ currentNetwork,
+ appMode
+}: {
+ currentNetwork: Wallet.Cardano.NetworkId;
+ appMode: AppMode;
+}): WalletUI => {
const shouldHideBalance = HIDE_BALANCE_FEATURE_ENABLED
? getValueFromLocalStorage('hideBalance')
: false;
@@ -49,7 +62,8 @@ const getWalletUI = ({ currentNetwork, appMode }: { currentNetwork: Wallet.Carda
areBalancesVisible: !shouldHideBalance,
getHiddenBalancePlaceholder: (placeholderLength = HIDE_BALANCE_PLACEHOLDER_LENGTH, placeholderChar = '*') =>
placeholderChar.repeat(placeholderLength),
- canManageBalancesVisibility: HIDE_BALANCE_FEATURE_ENABLED
+ canManageBalancesVisibility: HIDE_BALANCE_FEATURE_ENABLED,
+ isDropdownMenuOpen: false
};
};
@@ -60,6 +74,7 @@ export const uiSlice: SliceCreator = ({ get, se
});
return {
+ setIsDropdownMenuOpen: (isDropdownMenuOpen) => setWalletUI({ isDropdownMenuOpen }, { get, set }),
walletUI: getWalletUI({ currentNetwork: currentChain.networkId, appMode }),
setCardanoCoin: (chain: Wallet.Cardano.ChainId) => setWalletUI({ network: chain.networkId }, { get, set }),
setNetworkConnection: (networkConnection: NetworkConnectionStates) =>
diff --git a/apps/browser-extension-wallet/src/stores/slices/wallet-info-slice.ts b/apps/browser-extension-wallet/src/stores/slices/wallet-info-slice.ts
index ee3c8efad..dad9a491c 100644
--- a/apps/browser-extension-wallet/src/stores/slices/wallet-info-slice.ts
+++ b/apps/browser-extension-wallet/src/stores/slices/wallet-info-slice.ts
@@ -12,7 +12,9 @@ export const walletInfoSlice: SliceCreator ({
// Wallet info and storage
setWalletInfo: (walletInfo) => set({ walletInfo }),
+ setManageAccountsWallet: (manageAccountsWallet) => set({ manageAccountsWallet }),
// Loaded wallet
+ manageAccountsWallet: undefined,
inMemoryWallet: undefined,
cardanoWallet: undefined,
walletState: undefined,
diff --git a/apps/browser-extension-wallet/src/stores/types.ts b/apps/browser-extension-wallet/src/stores/types.ts
index 173afa543..c902f9f99 100644
--- a/apps/browser-extension-wallet/src/stores/types.ts
+++ b/apps/browser-extension-wallet/src/stores/types.ts
@@ -21,10 +21,9 @@ import {
import { FetchWalletActivitiesProps, FetchWalletActivitiesReturn, IBlockchainProvider } from './slices';
import { IAssetDetails } from '@src/views/browser-view/features/assets/types';
import { TokenInfo } from '@src/utils/get-assets-information';
-import { WalletManagerApi, WalletType } from '@cardano-sdk/web-extension';
+import { AnyBip32Wallet, WalletManagerApi, WalletType } from '@cardano-sdk/web-extension';
import { AddressesDiscoveryStatus } from '@lib/communication/addresses-discoverer';
-import { Reward } from '@cardano-sdk/core';
-import { EpochNo } from '@cardano-sdk/core/dist/cjs/Cardano';
+import { Cardano, Reward } from '@cardano-sdk/core';
import { StakePoolSortOptions } from '@lace/staking';
import { ObservableWalletState } from '@hooks/useWalletState';
@@ -101,6 +100,8 @@ export interface StakePoolSearchSlice {
export type EnvironmentTypes = Wallet.ChainName;
export interface WalletInfoSlice {
+ manageAccountsWallet: AnyBip32Wallet | undefined;
+ setManageAccountsWallet: (wallet: AnyBip32Wallet) => void;
walletInfo?: WalletInfo | undefined;
setWalletInfo: (info?: WalletInfo) => void;
inMemoryWallet: Wallet.ObservableWallet | undefined;
@@ -134,6 +135,7 @@ export interface LockSlice {
export interface UISlice {
walletUI: WalletUI | undefined;
+ setIsDropdownMenuOpen: (isOpen: boolean) => void;
setCardanoCoin: (chainId: Wallet.Cardano.ChainId) => void;
setNetworkConnection: (networkConnection: NetworkConnectionStates) => void;
setBalancesVisibility: (visible: boolean) => void;
@@ -149,7 +151,7 @@ export interface ActivityDetailSlice {
type: RewardsActivityType;
status: ActivityStatus.SPENDABLE;
direction?: never;
- activity: { spendableEpoch: EpochNo; spendableDate: Date; rewards: Reward[] };
+ activity: { spendableEpoch: Cardano.EpochNo; spendableDate: Date; rewards: Reward[] };
}
| {
type: TransactionActivityType;
@@ -165,7 +167,7 @@ export interface ActivityDetailSlice {
type: TransactionActivityType;
}) => void;
setRewardsActivityDetail: (params: {
- activity: { spendableEpoch: EpochNo; spendableDate: Date; rewards: Reward[] };
+ activity: { spendableEpoch: Cardano.EpochNo; spendableDate: Date; rewards: Reward[] };
}) => void;
getActivityDetail: (params: { coinPrices: PriceResult; fiatCurrency: CurrencyInfo }) => Promise;
resetActivityState: () => void;
diff --git a/apps/browser-extension-wallet/src/types/ui.ts b/apps/browser-extension-wallet/src/types/ui.ts
index 8cf068892..139260dff 100644
--- a/apps/browser-extension-wallet/src/types/ui.ts
+++ b/apps/browser-extension-wallet/src/types/ui.ts
@@ -15,6 +15,7 @@ export interface WalletUI {
cardanoCoin: Wallet.CoinId;
appMode: AppMode;
networkConnection: NetworkConnectionStates;
+ isDropdownMenuOpen: boolean;
areBalancesVisible: boolean;
canManageBalancesVisibility: boolean;
getHiddenBalancePlaceholder: (placeholderLength?: number, placeholderChar?: string) => string;
diff --git a/apps/browser-extension-wallet/src/utils/__tests__/get-token-list.test.ts b/apps/browser-extension-wallet/src/utils/__tests__/get-token-list.test.ts
index 059936ca2..76f6b14f5 100644
--- a/apps/browser-extension-wallet/src/utils/__tests__/get-token-list.test.ts
+++ b/apps/browser-extension-wallet/src/utils/__tests__/get-token-list.test.ts
@@ -5,7 +5,7 @@ import { GetTokenListParams, getTokenList } from '../get-token-list';
import { mockAsset, mockNft } from '../mocks/test-helpers';
import { defaultCurrency } from '@providers/currency/constants';
import { Wallet } from '@lace/cardano';
-import { NftMetadata } from '@cardano-sdk/core/dist/cjs/Asset';
+import { Asset } from '@cardano-sdk/core';
import { PriceResult } from '@hooks';
const testEnvironment = 'Preprod';
@@ -27,7 +27,7 @@ describe('getTokensList', () => {
Wallet.Cardano.AssetId('659f2917fb63f12b33667463ee575eeac1845bbc736b9c0bbc40ba8254534c43'),
{
...mockNft,
- nftMetadata: { name: undefined, image: undefined } as NftMetadata
+ nftMetadata: { name: undefined, image: undefined } as Asset.NftMetadata
}
]
]),
diff --git a/apps/browser-extension-wallet/src/utils/__tests__/inspectTxType.test.ts b/apps/browser-extension-wallet/src/utils/__tests__/inspectTxType.test.ts
index 977db1d61..a5995ebcd 100644
--- a/apps/browser-extension-wallet/src/utils/__tests__/inspectTxType.test.ts
+++ b/apps/browser-extension-wallet/src/utils/__tests__/inspectTxType.test.ts
@@ -5,7 +5,7 @@ import { inspectTxType, getTxDirection } from '../tx-inspection';
import { buildMockTx } from '../mocks/tx';
import { Wallet } from '@lace/cardano';
import { TxDirections } from '@src/types';
-import { StakeDelegationCertificate } from '@cardano-sdk/core/dist/cjs/Cardano';
+import { Cardano } from '@cardano-sdk/core';
import { Hash28ByteBase16 } from '@cardano-sdk/crypto';
const ADDRESS_1 = Wallet.Cardano.PaymentAddress(
'addr_test1qq585l3hyxgj3nas2v3xymd23vvartfhceme6gv98aaeg9muzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475q2g7k3g'
@@ -156,7 +156,7 @@ describe('testing tx-inspection utils', () => {
test('should not return delegation in case pool id is missing', async () => {
const delegationTX = buildMockTx({
- certificates: [{} as StakeDelegationCertificate]
+ certificates: [{} as Cardano.StakeDelegationCertificate]
});
const walletAddresses = [
{ address: ADDRESS_1, rewardAccount: REWARD_ACCOUNT }
diff --git a/apps/browser-extension-wallet/src/utils/get-ui-wallet-type.ts b/apps/browser-extension-wallet/src/utils/get-ui-wallet-type.ts
new file mode 100644
index 000000000..67eb9f046
--- /dev/null
+++ b/apps/browser-extension-wallet/src/utils/get-ui-wallet-type.ts
@@ -0,0 +1,7 @@
+import { WalletType } from '@cardano-sdk/web-extension';
+import { ProfileDropdown } from '@lace/ui';
+
+export const getUiWalletType = (walletType: WalletType): ProfileDropdown.WalletType => {
+ if (walletType === WalletType.Script) return 'shared';
+ return walletType === WalletType.InMemory ? 'hot' : 'cold';
+};
diff --git a/apps/browser-extension-wallet/src/utils/get-wallet-subtitle.ts b/apps/browser-extension-wallet/src/utils/get-wallet-subtitle.ts
new file mode 100644
index 000000000..2a624aedb
--- /dev/null
+++ b/apps/browser-extension-wallet/src/utils/get-wallet-subtitle.ts
@@ -0,0 +1,6 @@
+import { Wallet } from '@lace/cardano';
+import { Bip32WalletAccount } from '@cardano-sdk/web-extension';
+
+export const getActiveWalletSubtitle = (account?: Bip32WalletAccount): string =>
+ // LW-9574 update to appropriate/non-hardcoded subtitle for shared wallets
+ account ? account.metadata.name : 'Shared Wallet';
diff --git a/apps/browser-extension-wallet/src/utils/mocks/store.tsx b/apps/browser-extension-wallet/src/utils/mocks/store.tsx
index 519b36105..34c062e52 100644
--- a/apps/browser-extension-wallet/src/utils/mocks/store.tsx
+++ b/apps/browser-extension-wallet/src/utils/mocks/store.tsx
@@ -31,6 +31,9 @@ export const walletStoreMock = async (
// TODO: If possible use real methods/states and mock only needed ones, like inMemoryWallet [LW-5454]
return {
+ setIsDropdownMenuOpen: jest.fn(),
+ setManageAccountsWallet: jest.fn(),
+ manageAccountsWallet: undefined,
walletState: undefined,
setWalletState: jest.fn(),
fetchNetworkInfo: jest.fn(),
@@ -73,6 +76,7 @@ export const walletStoreMock = async (
setCardanoCoin: jest.fn(),
setNetworkConnection: jest.fn(),
walletUI: {
+ isDropdownMenuOpen: false,
networkConnection: NetworkConnectionStates.CONNNECTED,
cardanoCoin,
appMode: APP_MODE_BROWSER,
diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/MultiWallet.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/MultiWallet.tsx
index fb71d9ce8..73a304898 100644
--- a/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/MultiWallet.tsx
+++ b/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/MultiWallet.tsx
@@ -21,8 +21,12 @@ import { walletRoutePaths } from '@routes';
import { useWalletManager } from '@hooks';
import { Subject } from 'rxjs';
import { Wallet } from '@lace/cardano';
-import { NavigationButton } from '@lace/common';
+import { NavigationButton, toast } from '@lace/common';
import { useBackgroundPage } from '@providers/BackgroundPageProvider';
+import { Providers } from './hardware-wallet/types';
+import { WalletConflictError } from '@cardano-sdk/web-extension';
+import { TOAST_DEFAULT_DURATION } from '@hooks/useActionExecution';
+import { useTranslation } from 'react-i18next';
const { newWallet } = walletRoutePaths;
@@ -33,9 +37,35 @@ interface ConfirmationDialog {
}
export const SetupHardwareWallet = ({ shouldShowDialog$ }: ConfirmationDialog): JSX.Element => {
- const { connectHardwareWallet } = useWalletManager();
+ const { t } = useTranslation();
+ const { connectHardwareWallet, createHardwareWallet } = useWalletManager();
const disconnectHardwareWallet$ = useMemo(() => new Subject(), []);
+ const hardwareWalletProviders = useMemo(
+ (): Providers => ({
+ connectHardwareWallet,
+ disconnectHardwareWallet$,
+ shouldShowDialog$,
+ createWallet: async ({ account, connection, model, name }) => {
+ try {
+ await createHardwareWallet({
+ connectedDevice: model,
+ deviceConnection: connection,
+ name,
+ accountIndex: account
+ });
+ } catch (error) {
+ if (error instanceof WalletConflictError) {
+ toast.notify({ duration: TOAST_DEFAULT_DURATION, text: t('multiWallet.walletAlreadyExists') });
+ } else {
+ throw error;
+ }
+ }
+ }
+ }),
+ [connectHardwareWallet, createHardwareWallet, disconnectHardwareWallet$, shouldShowDialog$, t]
+ );
+
useEffect(() => {
const onHardwareWalletDisconnect = (event: HIDConnectionEvent) => {
disconnectHardwareWallet$.next(event);
@@ -49,16 +79,7 @@ export const SetupHardwareWallet = ({ shouldShowDialog$ }: ConfirmationDialog):
};
}, [disconnectHardwareWallet$]);
- return (
-
- );
+ return ;
};
export const SetupCreateWallet = (confirmationDialog: ConfirmationDialog): JSX.Element => (
diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/create-wallet/CreateWallet.test.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/create-wallet/CreateWallet.test.tsx
index 1d4440dce..dc79f4b17 100644
--- a/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/create-wallet/CreateWallet.test.tsx
+++ b/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/create-wallet/CreateWallet.test.tsx
@@ -7,6 +7,16 @@ import { Providers } from './types';
import { walletRoutePaths } from '@routes';
import { createAssetsRoute, fillMnemonic, getNextButton, mnemonicWords, setupStep } from '../tests/utils';
import { BehaviorSubject, firstValueFrom } from 'rxjs';
+import { StoreProvider } from '@src/stores';
+import { APP_MODE_BROWSER } from '@src/utils/constants';
+import { AppSettingsProvider, DatabaseProvider } from '@providers';
+import { UseWalletManager } from '@hooks/useWalletManager';
+
+jest.mock('@hooks/useWalletManager', () => ({
+ useWalletManager: jest.fn().mockReturnValue({
+ createWallet: jest.fn().mockResolvedValue(void 0) as UseWalletManager['createWallet']
+ } as UseWalletManager)
+}));
const keepWalletSecureStep = async () => {
const nextButton = getNextButton();
@@ -61,10 +71,16 @@ describe('Multi Wallet Setup/Create Wallet', () => {
providers.createWallet.mockResolvedValue(void 0);
render(
-
-
- {createAssetsRoute()}
-
+
+
+
+
+
+ {createAssetsRoute()}
+
+
+
+
);
await setupStep();
@@ -74,10 +90,16 @@ describe('Multi Wallet Setup/Create Wallet', () => {
test('should emit correct value for shouldShowDialog', async () => {
render(
-
-
- {createAssetsRoute()}
-
+
+
+
+
+
+ {createAssetsRoute()}
+
+
+
+
);
const nameInput = screen.getByTestId('wallet-name-input');
diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/create-wallet/steps/NewRecoveryPhrase.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/create-wallet/steps/NewRecoveryPhrase.tsx
index 8aea77430..9b5a6e13f 100644
--- a/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/create-wallet/steps/NewRecoveryPhrase.tsx
+++ b/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/create-wallet/steps/NewRecoveryPhrase.tsx
@@ -1,11 +1,12 @@
import { MnemonicStage, WalletSetupMnemonicStep } from '@lace/core';
-import React, { useState } from 'react';
+import React, { useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useHistory } from 'react-router';
import { wordlists } from 'bip39';
import { WarningModal } from '@src/views/browser-view/components';
import { useCreateWallet } from '../context';
import { walletRoutePaths } from '@routes';
+import { useWalletManager } from '@hooks/useWalletManager';
const noop = (): void => void 0;
@@ -20,6 +21,7 @@ export const NewRecoveryPhrase = (): JSX.Element => {
const history = useHistory();
const { t } = useTranslation();
const { generatedMnemonic, data } = useCreateWallet();
+ const { createWallet } = useWalletManager();
const [state, setState] = useState(() => ({
isResetMnemonicModalOpen: false,
resetMnemonicStage: 'writedown'
@@ -36,6 +38,19 @@ export const NewRecoveryPhrase = (): JSX.Element => {
passphraseError: t('core.walletSetupMnemonicStep.passphraseError')
};
+ const clearSecrets = useCallback(() => {
+ for (let i = 0; i < data.mnemonic.length; i++) {
+ data.mnemonic[i] = '';
+ }
+ data.password = '';
+ }, [data]);
+
+ const saveWallet = useCallback(async () => {
+ await createWallet(data);
+ clearSecrets();
+ history.push(walletRoutePaths.assets);
+ }, [data, createWallet, history, clearSecrets]);
+
return (
<>
{
onReset={(resetStage) =>
setState((s) => ({ ...s, isResetMnemonicModalOpen: true, resetMnemonicStage: resetStage }))
}
- onNext={() => history.push(walletRoutePaths.assets)}
+ onNext={saveWallet}
onStepNext={noop}
translations={walletSetupMnemonicStepTranslations}
suggestionList={wordList}
diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/hardware-wallet/steps/NameWallet.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/hardware-wallet/steps/NameWallet.tsx
index 357ffee90..50ce0c1e5 100644
--- a/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/hardware-wallet/steps/NameWallet.tsx
+++ b/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/hardware-wallet/steps/NameWallet.tsx
@@ -1,5 +1,5 @@
import { WalletSetupWalletNameStep } from '@lace/core';
-import React, { useState } from 'react';
+import React, { useCallback, useEffect, useState } from 'react';
import { useHistory } from 'react-router';
import { StartOverDialog } from '../../../wallet-setup/components/StartOverDialog';
import { useTranslation } from 'react-i18next';
@@ -14,7 +14,7 @@ interface State {
export const NameWallet = (): JSX.Element => {
const history = useHistory();
const { t } = useTranslation();
- const { createWallet, setName, resetConnection } = useHardwareWallet();
+ const { createWallet, setName, data, resetConnection } = useHardwareWallet();
const [isStartOverDialogVisible, setIsStartOverDialogVisible] = useState(false);
const [state, setState] = useState({});
@@ -26,8 +26,9 @@ export const NameWallet = (): JSX.Element => {
chooseName: t('core.walletSetupWalletNameStep.chooseName')
};
- const handleOnNext = async (name: string) => {
- setName(name);
+ // if createWallet is called in handleOnNext, then it will get name as ''
+ useEffect(() => {
+ if (!data?.name) return;
createWallet()
.then(() => {
setState({ error: undefined });
@@ -38,11 +39,19 @@ export const NameWallet = (): JSX.Element => {
error: 'common'
});
});
- };
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [data.name]);
+
+ const handleOnNext = useCallback(
+ async (name: string) => {
+ setName(name);
+ },
+ [setName]
+ );
- const onRetry = () => {
+ const onRetry = useCallback(() => {
setState({ error: undefined });
- };
+ }, [setState]);
return (
<>
diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/restore-wallet/RestoreWallet.test.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/restore-wallet/RestoreWallet.test.tsx
index ac98bbb7e..e1932f2e5 100644
--- a/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/restore-wallet/RestoreWallet.test.tsx
+++ b/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/restore-wallet/RestoreWallet.test.tsx
@@ -7,6 +7,16 @@ import { Providers } from './types';
import { walletRoutePaths } from '@routes';
import { createAssetsRoute, fillMnemonic, getNextButton, setupStep } from '../tests/utils';
import { Subject } from 'rxjs';
+import { StoreProvider } from '@src/stores';
+import { APP_MODE_BROWSER } from '@src/utils/constants';
+import { AppSettingsProvider, DatabaseProvider } from '@providers';
+import { UseWalletManager } from '@hooks/useWalletManager';
+
+jest.mock('@hooks/useWalletManager', () => ({
+ useWalletManager: jest.fn().mockReturnValue({
+ createWallet: jest.fn().mockResolvedValue(void 0) as UseWalletManager['createWallet']
+ } as UseWalletManager)
+}));
const keepWalletSecureStep = async () => {
const nextButton = getNextButton();
@@ -68,10 +78,16 @@ describe('Multi Wallet Setup/Restore Wallet', () => {
providers.createWallet.mockResolvedValue(void 0);
render(
-
-
- {createAssetsRoute()}
-
+
+
+
+
+
+ {createAssetsRoute()}
+
+
+
+
);
await setupStep();
diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/restore-wallet/steps/RestoreRecoveryPhrase.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/restore-wallet/steps/RestoreRecoveryPhrase.tsx
index fdd17b822..30c8cd4f7 100644
--- a/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/restore-wallet/steps/RestoreRecoveryPhrase.tsx
+++ b/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/restore-wallet/steps/RestoreRecoveryPhrase.tsx
@@ -1,13 +1,16 @@
import { WalletSetupMnemonicVerificationStep } from '@lace/core';
-import React from 'react';
+import React, { useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { wordlists } from 'bip39';
import { Wallet } from '@lace/cardano';
import { useHistory } from 'react-router-dom';
import { useRestoreWallet } from '../context';
import { walletRoutePaths } from '@routes';
-
-const noop = (): void => void 0;
+import noop from 'lodash/noop';
+import { useWalletManager } from '@hooks';
+import { WalletConflictError } from '@cardano-sdk/web-extension';
+import { toast } from '@lace/common';
+import { TOAST_DEFAULT_DURATION } from '@hooks/useActionExecution';
const wordList = wordlists.english;
@@ -15,6 +18,18 @@ export const RestoreRecoveryPhrase = (): JSX.Element => {
const { t } = useTranslation();
const history = useHistory();
const { data, setMnemonic } = useRestoreWallet();
+ const { createWallet } = useWalletManager();
+ const isValidMnemonic = useMemo(
+ () => Wallet.KeyManagement.util.validateMnemonic(Wallet.KeyManagement.util.joinMnemonicWords(data.mnemonic)),
+ [data.mnemonic]
+ );
+
+ const clearSecrets = useCallback(() => {
+ for (let i = 0; i < data.mnemonic.length; i++) {
+ data.mnemonic[i] = '';
+ }
+ data.password = '';
+ }, [data]);
const walletSetupMnemonicStepTranslations = {
writePassphrase: t('core.walletSetupMnemonicStep.writePassphrase'),
@@ -32,12 +47,25 @@ export const RestoreRecoveryPhrase = (): JSX.Element => {
setMnemonic(mnemonic)}
- onCancel={() => history.goBack()}
- onSubmit={() => history.push(walletRoutePaths.assets)}
+ onCancel={() => {
+ clearSecrets();
+ history.goBack();
+ }}
+ onSubmit={useCallback(async () => {
+ try {
+ await createWallet(data);
+ } catch (error) {
+ if (error instanceof WalletConflictError) {
+ toast.notify({ duration: TOAST_DEFAULT_DURATION, text: t('multiWallet.walletAlreadyExists') });
+ } else {
+ throw error;
+ }
+ }
+ clearSecrets();
+ history.push(walletRoutePaths.assets);
+ }, [data, clearSecrets, createWallet, history, t])}
onStepNext={noop}
- isSubmitEnabled={Wallet.KeyManagement.util.validateMnemonic(
- Wallet.KeyManagement.util.joinMnemonicWords(data.mnemonic)
- )}
+ isSubmitEnabled={isValidMnemonic}
translations={walletSetupMnemonicStepTranslations}
suggestionList={wordList}
/>
diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/settings/components/SettingsRemoveWallet.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/settings/components/SettingsRemoveWallet.tsx
index 06c2a3011..504f77264 100644
--- a/apps/browser-extension-wallet/src/views/browser-view/features/settings/components/SettingsRemoveWallet.tsx
+++ b/apps/browser-extension-wallet/src/views/browser-view/features/settings/components/SettingsRemoveWallet.tsx
@@ -35,7 +35,9 @@ export const SettingsRemoveWallet = ({ popupView }: { popupView?: boolean }): Re
const removeWallet = async () => {
analytics.sendEventToPostHog(PostHogAction.SettingsHoldUpRemoveWalletClick);
setDeletingWallet(true);
- await deleteWallet();
+ const nextActiveWallet = await deleteWallet();
+ setDeletingWallet(false);
+ if (nextActiveWallet) return;
if (popupView) await backgroundServices.handleOpenBrowser({ section: BrowserViewSections.HOME });
// force reload to ensure all stores are cleaned up
location.reload();
diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/staking/helpers.ts b/apps/browser-extension-wallet/src/views/browser-view/features/staking/helpers.ts
index b851df5ec..666bcd18d 100644
--- a/apps/browser-extension-wallet/src/views/browser-view/features/staking/helpers.ts
+++ b/apps/browser-extension-wallet/src/views/browser-view/features/staking/helpers.ts
@@ -1,7 +1,7 @@
/* eslint-disable no-magic-numbers */
import { Wallet } from '@lace/cardano';
-import * as HardwareLedger from '../../../../../../../node_modules/@cardano-sdk/hardware-ledger/dist/cjs';
-import * as HardwareTrezor from '../../../../../../../node_modules/@cardano-sdk/hardware-trezor/dist/cjs';
+import * as HardwareLedger from '@cardano-sdk/hardware-ledger';
+import * as HardwareTrezor from '@cardano-sdk/hardware-trezor';
import { WalletType } from '@cardano-sdk/web-extension';
export enum LedgerMultidelegationMinAppVersion {
diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow.tsx
index 558665f97..f3d0718ad 100644
--- a/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow.tsx
+++ b/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow.tsx
@@ -30,12 +30,7 @@ import { WalletType } from '@cardano-sdk/web-extension';
import { useWalletStore } from '@src/stores';
const { CHAIN } = config();
-const {
- Cardano: { ChainIds },
- AVAILABLE_WALLETS
-} = Wallet;
-const DEFAULT_CHAIN_ID = ChainIds[CHAIN];
-
+const { AVAILABLE_WALLETS } = Wallet;
export interface HardwareWalletFlowProps {
onCancel: () => void;
onAppReload: () => void;
@@ -163,7 +158,6 @@ export const HardwareWalletFlow = ({
accountIndex,
deviceConnection,
name,
- chainId: DEFAULT_CHAIN_ID,
connectedDevice
});
setWalletCreated(cardanoWallet);
diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/helpers.ts b/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/helpers.ts
index 8671f9fb9..f7f7bfc1a 100644
--- a/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/helpers.ts
+++ b/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/helpers.ts
@@ -1,7 +1,7 @@
/* eslint-disable camelcase */
import { Wallet } from '@lace/cardano';
-import * as HardwareLedger from '../../../../../../../node_modules/@cardano-sdk/hardware-ledger/dist/cjs';
-import * as HardwareTrezor from '../../../../../../../node_modules/@cardano-sdk/hardware-trezor/dist/cjs';
+import * as HardwareLedger from '@cardano-sdk/hardware-ledger';
+import * as HardwareTrezor from '@cardano-sdk/hardware-trezor';
import { PostHogProperties } from '@providers/AnalyticsProvider/analyticsTracker';
import { WalletType } from '@cardano-sdk/web-extension';
diff --git a/apps/browser-extension-wallet/src/views/browser-view/routes/index.tsx b/apps/browser-extension-wallet/src/views/browser-view/routes/index.tsx
index 56eb96a62..b02751f2f 100644
--- a/apps/browser-extension-wallet/src/views/browser-view/routes/index.tsx
+++ b/apps/browser-extension-wallet/src/views/browser-view/routes/index.tsx
@@ -106,11 +106,15 @@ export const BrowserViewRoutes = ({ routesMap = defaultRoutes }: { routesMap?: R
const location = useLocation<{ background?: Location }>();
useEffect(() => {
- if (
- page === undefined &&
- (location.pathname === routes.newWallet.root || location.pathname === routes.sharedWallet.root)
- ) {
- setBackgroundPage({ pathname: '/assets', search: '', hash: '', state: undefined });
+ const isCreatingWallet = [routes.newWallet.root, routes.sharedWallet.root].some((path) =>
+ location.pathname.startsWith(path)
+ );
+ if (page === undefined) {
+ if (isCreatingWallet) {
+ setBackgroundPage({ pathname: '/assets', search: '', hash: '', state: undefined });
+ }
+ } else if (!isCreatingWallet) {
+ setBackgroundPage();
}
}, [location, page, setBackgroundPage]);
diff --git a/apps/browser-extension-wallet/webpack.sw.prod.js b/apps/browser-extension-wallet/webpack.sw.prod.js
index 1da5ef3b3..2b0a4d842 100644
--- a/apps/browser-extension-wallet/webpack.sw.prod.js
+++ b/apps/browser-extension-wallet/webpack.sw.prod.js
@@ -13,5 +13,7 @@ module.exports = () =>
prodConfig(),
swConfig(),
// Needed for the service worker to work with a production build
- { optimization: { moduleIds: 'named', mangleExports: false } }
+ // TODO: after removing imports from dist/cjs, service worker no longer loads when built in production mode.
+ // It is likely that some optimization is triggering it, such as tree shaking.
+ { mode: 'development', optimization: { moduleIds: 'named', mangleExports: false } }
);
diff --git a/packages/cardano/package.json b/packages/cardano/package.json
index 6c052bfbf..9d37521db 100644
--- a/packages/cardano/package.json
+++ b/packages/cardano/package.json
@@ -39,15 +39,15 @@
"watch": "yarn build --watch"
},
"dependencies": {
- "@cardano-sdk/cardano-services-client": "0.17.6",
- "@cardano-sdk/core": "0.28.2",
+ "@cardano-sdk/cardano-services-client": "0.17.7",
+ "@cardano-sdk/core": "0.28.3",
"@cardano-sdk/crypto": "0.1.21",
- "@cardano-sdk/hardware-ledger": "0.8.13",
- "@cardano-sdk/hardware-trezor": "0.4.13",
- "@cardano-sdk/key-management": "0.19.7",
+ "@cardano-sdk/hardware-ledger": "0.8.14",
+ "@cardano-sdk/hardware-trezor": "0.4.14",
+ "@cardano-sdk/key-management": "0.19.8",
"@cardano-sdk/util": "0.15.0",
- "@cardano-sdk/wallet": "0.34.2",
- "@cardano-sdk/web-extension": "0.24.5",
+ "@cardano-sdk/wallet": "0.34.3",
+ "@cardano-sdk/web-extension": "0.24.6",
"@lace/common": "0.1.0",
"@stablelib/chacha20poly1305": "1.0.1",
"bignumber.js": "9.0.1",
@@ -67,7 +67,7 @@
"webextension-polyfill": "0.8.0"
},
"devDependencies": {
- "@cardano-sdk/util-dev": "0.19.13",
+ "@cardano-sdk/util-dev": "0.19.14",
"@emurgo/cardano-message-signing-browser": "1.0.1",
"rollup-plugin-polyfill-node": "^0.8.0",
"typescript": "^4.3.5"
diff --git a/packages/cardano/src/wallet/index.ts b/packages/cardano/src/wallet/index.ts
index 98bb5e308..553e884d6 100644
--- a/packages/cardano/src/wallet/index.ts
+++ b/packages/cardano/src/wallet/index.ts
@@ -50,7 +50,7 @@ export {
export * as KeyManagement from '@cardano-sdk/key-management';
export * as Ledger from '@cardano-sdk/hardware-ledger';
-export * as Trezor from '../../../../node_modules/@cardano-sdk/hardware-trezor/dist/cjs';
+export * as Trezor from '@cardano-sdk/hardware-trezor';
export { HexBlob, Percent, BigIntMath } from '@cardano-sdk/util';
export * as Crypto from '@cardano-sdk/crypto';
diff --git a/packages/cardano/src/wallet/lib/__tests__/get-inputs-value.test.ts b/packages/cardano/src/wallet/lib/__tests__/get-inputs-value.test.ts
index 51a29509f..6522f4974 100644
--- a/packages/cardano/src/wallet/lib/__tests__/get-inputs-value.test.ts
+++ b/packages/cardano/src/wallet/lib/__tests__/get-inputs-value.test.ts
@@ -10,10 +10,9 @@ import { act } from 'react-dom/test-utils';
import { Cardano, ChainHistoryProvider } from '@cardano-sdk/core';
import { of } from 'rxjs';
import { ObservableWallet } from '@cardano-sdk/wallet';
-import { HydratedTx } from '@cardano-sdk/core/dist/cjs/Cardano';
-const getMockWalletProvider = (mock: HydratedTx) => ({
- transactionsByHashes: (_hashes: string[]) => new Promise((resolve) => resolve([mock]))
+const getMockWalletProvider = (mock: Cardano.HydratedTx) => ({
+ transactionsByHashes: (_hashes: string[]) => new Promise((resolve) => resolve([mock]))
});
describe('Testing getTxInputsValueAndAddress function', () => {
diff --git a/packages/cardano/src/wallet/lib/cardano-wallet.ts b/packages/cardano/src/wallet/lib/cardano-wallet.ts
index 968d93b1c..f04e33829 100644
--- a/packages/cardano/src/wallet/lib/cardano-wallet.ts
+++ b/packages/cardano/src/wallet/lib/cardano-wallet.ts
@@ -26,6 +26,7 @@ export type KeyAgentsByChain = Record Promise> =
await HardwareLedger.LedgerKeyAgent.checkDeviceConnection(DEFAULT_COMMUNICATION_TYPE),
...(AVAILABLE_WALLETS.includes(WalletType.Trezor) && {
[WalletType.Trezor]: async () => {
- const isTrezorInitialized = await TrezorKeyAgent.initializeTrezorTransport({
+ const isTrezorInitialized = await HardwareTrezor.TrezorKeyAgent.initializeTrezorTransport({
manifest,
communicationType: DEFAULT_COMMUNICATION_TYPE
});
// initializeTrezorTransport would still succeed even when device is not connected
- await TrezorKeyAgent.checkDeviceConnection(DEFAULT_COMMUNICATION_TYPE);
+ await HardwareTrezor.TrezorKeyAgent.checkDeviceConnection(DEFAULT_COMMUNICATION_TYPE);
return isTrezorInitialized;
}
diff --git a/packages/cardano/src/wallet/test/mocks/NetworkInfoProviderStub.ts b/packages/cardano/src/wallet/test/mocks/NetworkInfoProviderStub.ts
index a4f029fa7..ae9f81065 100644
--- a/packages/cardano/src/wallet/test/mocks/NetworkInfoProviderStub.ts
+++ b/packages/cardano/src/wallet/test/mocks/NetworkInfoProviderStub.ts
@@ -2,7 +2,6 @@
/* eslint-disable max-len */
import { Cardano, NetworkInfoProvider, Seconds, EraSummary } from '@cardano-sdk/core';
import { testnetEraSummaries } from '@cardano-sdk/util-dev';
-import { ProtocolParameters } from '@cardano-sdk/core/dist/cjs/Cardano';
export const mockedNetworkInfo: {
network: {
@@ -49,7 +48,7 @@ const mockCurrentProtocolParameters = {
poolDeposit: 500_000_000,
protocolVersion: { major: 5, minor: 0 },
stakeKeyDeposit: 2_000_000
-} as ProtocolParameters;
+} as Cardano.ProtocolParameters;
const mockGenesisParameters: Cardano.CompactGenesis = {
activeSlotsCoefficient: 0.05,
diff --git a/packages/cardano/src/wallet/types.ts b/packages/cardano/src/wallet/types.ts
index 946b0e338..30db2be74 100644
--- a/packages/cardano/src/wallet/types.ts
+++ b/packages/cardano/src/wallet/types.ts
@@ -1,5 +1,5 @@
import { Cardano, Paginated } from '@cardano-sdk/core';
-import { LedgerKeyAgent } from '@cardano-sdk/hardware-ledger';
+import type { LedgerKeyAgent } from '@cardano-sdk/hardware-ledger';
import { WalletType } from '@cardano-sdk/web-extension';
export type DeviceConnection = LedgerKeyAgent['deviceConnection'] | boolean;
diff --git a/packages/core/src/ui/components/WalletSetup/WalletSetupSelectAccountsStep.tsx b/packages/core/src/ui/components/WalletSetup/WalletSetupSelectAccountsStep.tsx
index a4437e8bb..db56cc87c 100644
--- a/packages/core/src/ui/components/WalletSetup/WalletSetupSelectAccountsStep.tsx
+++ b/packages/core/src/ui/components/WalletSetup/WalletSetupSelectAccountsStep.tsx
@@ -51,7 +51,7 @@ export const WalletSetupSelectAccountsStep = ({
>
- {t('core.walletSetupSelectAccountsStep.account')} {index + 1} {index === 0 && '- Default'}
+ {t('core.walletSetupSelectAccountsStep.account')} {index} {index === 0 && '- Default'}
))}
diff --git a/packages/staking/package.json b/packages/staking/package.json
index 3050cddfc..11563bbc5 100644
--- a/packages/staking/package.json
+++ b/packages/staking/package.json
@@ -72,9 +72,9 @@
},
"devDependencies": {
"@babel/core": "^7.21.0",
- "@cardano-sdk/core": "0.28.2",
- "@cardano-sdk/input-selection": "0.12.20",
- "@cardano-sdk/tx-construction": "0.17.10",
+ "@cardano-sdk/core": "0.28.3",
+ "@cardano-sdk/input-selection": "0.12.21",
+ "@cardano-sdk/tx-construction": "0.17.11",
"@cardano-sdk/util": "0.15.0",
"@storybook/addon-actions": "^7.6.7",
"@storybook/addon-essentials": "^7.6.7",
@@ -118,11 +118,11 @@
"wait-on": "^7.0.1"
},
"peerDependencies": {
- "@cardano-sdk/input-selection": "0.12.20",
- "@cardano-sdk/tx-construction": "0.17.10",
+ "@cardano-sdk/input-selection": "0.12.21",
+ "@cardano-sdk/tx-construction": "0.17.11",
"@cardano-sdk/util": "0.15.0",
- "@cardano-sdk/wallet": "0.34.2",
- "@cardano-sdk/web-extension": "0.24.5",
+ "@cardano-sdk/wallet": "0.34.3",
+ "@cardano-sdk/web-extension": "0.24.6",
"@lace/cardano": "^0.1.0",
"@lace/common": "^0.1.0",
"@lace/core": "0.1.0",
diff --git a/packages/ui/src/design-system/control-buttons/control-button.css.ts b/packages/ui/src/design-system/control-buttons/control-button.css.ts
index 8d5a63e7c..506592f80 100644
--- a/packages/ui/src/design-system/control-buttons/control-button.css.ts
+++ b/packages/ui/src/design-system/control-buttons/control-button.css.ts
@@ -115,6 +115,7 @@ export const container = recipe({
paddingRight: vars.spacing.$24,
},
[Scheme.ExtraSmall]: {
+ minWidth: vars.spacing.$60,
height: vars.spacing.$24,
paddingTop: vars.spacing.$2,
paddingBottom: vars.spacing.$2,
@@ -176,6 +177,10 @@ export const label = recipe({
},
[Scheme.ExtraSmall]: {
color: vars.colors.$control_buttons_label_color_extra_small,
+ },
+ },
+ sizeScheme: {
+ [Scheme.ExtraSmall]: {
fontWeight: vars.fontWeights.$regular,
fontSize: vars.fontSizes.$12,
},
diff --git a/packages/ui/src/design-system/control-buttons/control-button.data.ts b/packages/ui/src/design-system/control-buttons/control-button.data.ts
index a01a97355..c46a69bf4 100644
--- a/packages/ui/src/design-system/control-buttons/control-button.data.ts
+++ b/packages/ui/src/design-system/control-buttons/control-button.data.ts
@@ -18,3 +18,13 @@ export type ControlButtonProps = Omit & {
export type ControlButtonWithLabelProps = ControlButtonProps & {
label?: string;
};
+
+export type ColorScheme =
+ | Scheme.Danger
+ | Scheme.ExtraSmall
+ | Scheme.Filled
+ | Scheme.Outlined;
+export type ControlButtonWithLabelAndColorSchemeProps =
+ ControlButtonWithLabelProps & {
+ colorScheme?: ColorScheme;
+ };
diff --git a/packages/ui/src/design-system/control-buttons/extra-small-button.component.tsx b/packages/ui/src/design-system/control-buttons/extra-small-button.component.tsx
index 8440a8a3e..144c558cb 100644
--- a/packages/ui/src/design-system/control-buttons/extra-small-button.component.tsx
+++ b/packages/ui/src/design-system/control-buttons/extra-small-button.component.tsx
@@ -7,10 +7,10 @@ import { SkeletonButton } from '../buttons';
import * as cx from './control-button.css';
import { Scheme } from './control-button.data';
-import type { ControlButtonWithLabelProps } from './control-button.data';
+import type { ControlButtonWithLabelAndColorSchemeProps } from './control-button.data';
export const ExtraSmall = (
- props: Readonly,
+ props: Readonly,
): JSX.Element => {
return (
void;
onEditClick?: (accountNumber: number) => void;
onDeleteClick?: (accountNumber: number) => void;
onUnlockClick?: (accountNumber: number) => void;
}
+const MaybeWithDisableUnlockTooltip = ({
+ disableUnlock,
+ children,
+}: Readonly<{
+ disableUnlock: Readonly;
+ children: ReactNode;
+}>): JSX.Element => {
+ if (disableUnlock) {
+ return (
+
+ {children}
+
+ );
+ }
+
+ return <>{children}>;
+};
+
+// eslint-disable-next-line react/no-multi-comp
export const AccountItem = ({
accountNumber,
+ disableUnlock,
label,
- unlockLabel,
isUnlocked,
- onEditClick,
+ isDeletable,
+ onActivateClick,
onDeleteClick,
onUnlockClick,
}: Readonly): JSX.Element => (
@@ -36,57 +62,60 @@ export const AccountItem = ({
className={cx.root}
data-testid="wallet-account-item"
>
-
-
-
-
- {label}
-
-
- m/1842`/1841/{accountNumber}
-
+ {
+ onActivateClick?.(accountNumber);
+ }}
+ >
+
+
+
+
+ {label.name}
+
+
+ m/1852'/1815'/{accountNumber}'
+
+
-
+
{isUnlocked ? (
-
- }
- size="extraSmall"
+ isDeletable ? (
+ {
- onEditClick?.(accountNumber);
+ onDeleteClick?.(accountNumber);
}}
- data-testid="wallet-account-item-edit-btn"
/>
- }
- size="extraSmall"
- data-testid="wallet-account-item-delete-btn"
+ ) : undefined
+ ) : (
+
+ {
- onDeleteClick?.(accountNumber);
+ onUnlockClick?.(accountNumber);
}}
/>
-
- ) : (
- {
- onUnlockClick?.(accountNumber);
- }}
- />
+
)}
);
diff --git a/packages/ui/src/design-system/profile-dropdown/accounts/profile-dropdown-account-item.stories.tsx b/packages/ui/src/design-system/profile-dropdown/accounts/profile-dropdown-account-item.stories.tsx
index cc6bca874..950ef918f 100644
--- a/packages/ui/src/design-system/profile-dropdown/accounts/profile-dropdown-account-item.stories.tsx
+++ b/packages/ui/src/design-system/profile-dropdown/accounts/profile-dropdown-account-item.stories.tsx
@@ -36,6 +36,7 @@ export const Overview = (): JSX.Element => (
@@ -46,6 +47,7 @@ export const Overview = (): JSX.Element => (
diff --git a/packages/ui/src/design-system/profile-dropdown/accounts/profile-dropdown-accounts-list.component.tsx b/packages/ui/src/design-system/profile-dropdown/accounts/profile-dropdown-accounts-list.component.tsx
index d4b102e80..ebf225fc4 100644
--- a/packages/ui/src/design-system/profile-dropdown/accounts/profile-dropdown-accounts-list.component.tsx
+++ b/packages/ui/src/design-system/profile-dropdown/accounts/profile-dropdown-accounts-list.component.tsx
@@ -8,11 +8,17 @@ export interface AccountData {
label: string;
accountNumber: number;
isUnlocked: boolean;
+ isActive: boolean;
+ disableUnlock?: { reason: string };
}
export interface Props {
accounts: AccountData[];
- unlockLabel: string;
+ label: {
+ lock: string;
+ unlock: string;
+ };
+ onAccountActivateClick?: (accountNumber: number) => void;
onAccountUnlockClick?: (accountNumber: number) => void;
onAccountEditClick?: (accountNumber: number) => void;
onAccountDeleteClick?: (accountNumber: number) => void;
@@ -20,23 +26,37 @@ export interface Props {
export const AccountsList = ({
accounts,
- unlockLabel,
+ label,
+ onAccountActivateClick,
onAccountUnlockClick,
onAccountEditClick,
onAccountDeleteClick,
-}: Readonly): JSX.Element => (
-
- {accounts.map(a => (
-
- ))}
-
-);
+}: Readonly): JSX.Element => {
+ const hasMultipleUnlockedAccounts =
+ accounts.filter(a => a.isUnlocked).length > 1;
+ return (
+
+ {accounts.map(a => (
+ {
+ if (!a.isActive && a.isUnlocked) {
+ onAccountActivateClick?.(accountNumber);
+ }
+ }}
+ onUnlockClick={onAccountUnlockClick}
+ onEditClick={onAccountEditClick}
+ onDeleteClick={onAccountDeleteClick}
+ />
+ ))}
+
+ );
+};
diff --git a/packages/ui/src/design-system/profile-dropdown/accounts/profile-dropdown-accounts-list.stories.tsx b/packages/ui/src/design-system/profile-dropdown/accounts/profile-dropdown-accounts-list.stories.tsx
index 9b23bad1e..17e51d6e3 100644
--- a/packages/ui/src/design-system/profile-dropdown/accounts/profile-dropdown-accounts-list.stories.tsx
+++ b/packages/ui/src/design-system/profile-dropdown/accounts/profile-dropdown-accounts-list.stories.tsx
@@ -35,26 +35,31 @@ export const Overview = (): JSX.Element => (
accountNumber: 1,
label: 'Account #1',
isUnlocked: true,
+ isActive: false,
},
{
accountNumber: 2,
label: 'Account #2',
isUnlocked: true,
+ isActive: true,
},
{
accountNumber: 3,
label: 'Account #3',
isUnlocked: false,
+ isActive: false,
},
{
accountNumber: 4,
label: 'Account with a long name',
isUnlocked: false,
+ isActive: false,
},
{
accountNumber: 10,
label: 'Account #10',
isUnlocked: false,
+ isActive: false,
},
]}
/>
diff --git a/packages/ui/src/design-system/profile-dropdown/index.ts b/packages/ui/src/design-system/profile-dropdown/index.ts
index 40545b0ab..29d10c05f 100644
--- a/packages/ui/src/design-system/profile-dropdown/index.ts
+++ b/packages/ui/src/design-system/profile-dropdown/index.ts
@@ -3,3 +3,4 @@ export { WalletOption } from './profile-dropdown-wallet-option.component';
export { WalletStatus } from './profile-dropdown-wallet-status.component';
export { AccountsList } from './accounts/profile-dropdown-accounts-list.component';
export type { AccountData } from './accounts/profile-dropdown-accounts-list.component';
+export type { WalletType } from './profile-dropdown.data';
diff --git a/packages/ui/src/design-system/profile-dropdown/profile-dropdown-wallet-option.component.tsx b/packages/ui/src/design-system/profile-dropdown/profile-dropdown-wallet-option.component.tsx
index b971fb53d..b258cff45 100644
--- a/packages/ui/src/design-system/profile-dropdown/profile-dropdown-wallet-option.component.tsx
+++ b/packages/ui/src/design-system/profile-dropdown/profile-dropdown-wallet-option.component.tsx
@@ -73,7 +73,10 @@ export const WalletOption = ({
justifyContent="center"
>
{
+ onOpenAccountsMenu?.();
+ event.stopPropagation();
+ }}
icon={}
size="extraSmall"
as="div"
diff --git a/yarn.lock b/yarn.lock
index 9b9baeb4d..0ba42e97d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7690,23 +7690,23 @@ __metadata:
languageName: node
linkType: hard
-"@cardano-sdk/cardano-services-client@npm:0.17.6":
- version: 0.17.6
- resolution: "@cardano-sdk/cardano-services-client@npm:0.17.6"
+"@cardano-sdk/cardano-services-client@npm:0.17.7":
+ version: 0.17.7
+ resolution: "@cardano-sdk/cardano-services-client@npm:0.17.7"
dependencies:
- "@cardano-sdk/core": ~0.28.2
+ "@cardano-sdk/core": ~0.28.3
"@cardano-sdk/util": ~0.15.0
axios: ^0.27.2
class-validator: ^0.14.0
json-bigint: ~1.0.0
ts-log: ^2.2.4
- checksum: 2d792f5795a992a8c6c90347a759eafa244a41a4864d4addfd2a254acccf31a87ea6092869cf2b9c9037708727f0b9cee1fba74d8d7ce5cc69bc6e5d81b526d9
+ checksum: e3001eb8ddb0f39d959980bc6a56c5dad1407072af3cfae98a46b5c623cbfb9f9fca663b0e3f1f2d6626ea6f69f19da4d87154365790421f19fb98b478ad2983
languageName: node
linkType: hard
-"@cardano-sdk/core@npm:0.28.2, @cardano-sdk/core@npm:~0.28.2":
- version: 0.28.2
- resolution: "@cardano-sdk/core@npm:0.28.2"
+"@cardano-sdk/core@npm:0.28.3, @cardano-sdk/core@npm:~0.28.3":
+ version: 0.28.3
+ resolution: "@cardano-sdk/core@npm:0.28.3"
dependencies:
"@cardano-ogmios/client": 5.6.0
"@cardano-ogmios/schema": 5.6.0
@@ -7725,7 +7725,7 @@ __metadata:
peerDependenciesMeta:
rxjs:
optional: true
- checksum: ac041d6e787a555af2b97cfb05354759c96a917912b1e2f32611a894a212eb3772780c4ca7fdbeab747f50922a560c0c23d779720b7de6147ea4f15660b80dad
+ checksum: 0084fb65e6b6154f4c6d6cd3470ef5574848d00a1e9243495c09c50109ba745b0ab2f2b99a798cab4155de805279eca2a06abdb8c9b702c64b862a9a399e9207
languageName: node
linkType: hard
@@ -7757,78 +7757,78 @@ __metadata:
languageName: node
linkType: hard
-"@cardano-sdk/dapp-connector@npm:0.12.9, @cardano-sdk/dapp-connector@npm:~0.12.9":
- version: 0.12.9
- resolution: "@cardano-sdk/dapp-connector@npm:0.12.9"
+"@cardano-sdk/dapp-connector@npm:0.12.10, @cardano-sdk/dapp-connector@npm:~0.12.10":
+ version: 0.12.10
+ resolution: "@cardano-sdk/dapp-connector@npm:0.12.10"
dependencies:
- "@cardano-sdk/core": ~0.28.2
+ "@cardano-sdk/core": ~0.28.3
"@cardano-sdk/crypto": ~0.1.21
"@cardano-sdk/util": ~0.15.0
ts-custom-error: ^3.2.0
ts-log: ^2.2.4
webextension-polyfill: ^0.8.0
- checksum: 44696681af0d0d6f3fc73cfc4ff78ca5078e95da870ec482e598b94d408e365b369148d06beaca27de223c2df049a60ef6ea6111ab4f65163a647af7e4cdb756
+ checksum: 952fef65dd422c5f8b8e5f2f879a2f4620391137b8e6926f50fb01b39736e7a5810993075eac984112c97bbfb66268c6836d83b89b198478d73e2622b394305e
languageName: node
linkType: hard
-"@cardano-sdk/hardware-ledger@npm:0.8.13, @cardano-sdk/hardware-ledger@npm:~0.8.13":
- version: 0.8.13
- resolution: "@cardano-sdk/hardware-ledger@npm:0.8.13"
+"@cardano-sdk/hardware-ledger@npm:0.8.14, @cardano-sdk/hardware-ledger@npm:~0.8.14":
+ version: 0.8.14
+ resolution: "@cardano-sdk/hardware-ledger@npm:0.8.14"
dependencies:
"@cardano-foundation/ledgerjs-hw-app-cardano": ^6.0.0
- "@cardano-sdk/core": ~0.28.2
+ "@cardano-sdk/core": ~0.28.3
"@cardano-sdk/crypto": ~0.1.21
- "@cardano-sdk/key-management": ~0.19.7
- "@cardano-sdk/tx-construction": ~0.17.10
+ "@cardano-sdk/key-management": ~0.19.8
+ "@cardano-sdk/tx-construction": ~0.17.11
"@cardano-sdk/util": ~0.15.0
"@ledgerhq/hw-transport": ^6.28.1
"@ledgerhq/hw-transport-node-hid-noevents": ^6.27.12
"@ledgerhq/hw-transport-webhid": ^6.27.12
ts-custom-error: ^3.2.0
ts-log: ^2.2.4
- checksum: 23e42bc2440b8bc547bb2dc954e6edc44340d4da66acf6a07a8ec844209351e074efde1f2c6884fe21d3e5b38a77e55a68da5bd2f575fd9f410ba4de99c6596f
+ checksum: c80a870fb58a69b35ba058441317366dd1fd4947fb5a52df233ce02eaf711cb8ca81f9bbc81bc2135b38f3a593b703220a8d097d92e50cf1c122b55956ba55fa
languageName: node
linkType: hard
-"@cardano-sdk/hardware-trezor@npm:0.4.13, @cardano-sdk/hardware-trezor@npm:~0.4.13":
- version: 0.4.13
- resolution: "@cardano-sdk/hardware-trezor@npm:0.4.13"
+"@cardano-sdk/hardware-trezor@npm:0.4.14, @cardano-sdk/hardware-trezor@npm:~0.4.14":
+ version: 0.4.14
+ resolution: "@cardano-sdk/hardware-trezor@npm:0.4.14"
dependencies:
- "@cardano-sdk/core": ~0.28.2
+ "@cardano-sdk/core": ~0.28.3
"@cardano-sdk/crypto": ~0.1.21
- "@cardano-sdk/key-management": ~0.19.7
- "@cardano-sdk/tx-construction": ~0.17.10
+ "@cardano-sdk/key-management": ~0.19.8
+ "@cardano-sdk/tx-construction": ~0.17.11
"@cardano-sdk/util": ~0.15.0
"@trezor/connect": 9.1.6
"@trezor/connect-web": 9.1.6
lodash: ^4.17.21
ts-custom-error: ^3.2.0
ts-log: ^2.2.4
- checksum: 418e678b2cfad51fda0267fabcda4611a004669e1eb2dd203b7838c9b1e7927a033e04dc53fe5b367ffcc64ff7556cdd7e5988833824e476472190011d69e987
+ checksum: e23d729fb435d4f87142055acf11ed03ee4743d6f59d00dad6d948cc1a269ac98c9baede9614f449e7bc0e99c5c0c060d60ec073bf7e29ba69ded6e44f88216b
languageName: node
linkType: hard
-"@cardano-sdk/input-selection@npm:0.12.20, @cardano-sdk/input-selection@npm:~0.12.20":
- version: 0.12.20
- resolution: "@cardano-sdk/input-selection@npm:0.12.20"
+"@cardano-sdk/input-selection@npm:0.12.21, @cardano-sdk/input-selection@npm:~0.12.21":
+ version: 0.12.21
+ resolution: "@cardano-sdk/input-selection@npm:0.12.21"
dependencies:
- "@cardano-sdk/core": ~0.28.2
- "@cardano-sdk/key-management": ~0.19.7
+ "@cardano-sdk/core": ~0.28.3
+ "@cardano-sdk/key-management": ~0.19.8
"@cardano-sdk/util": ~0.15.0
bignumber.js: ^9.1.1
lodash: ^4.17.21
ts-custom-error: ^3.2.0
- checksum: 6166083f49d3ed614fce418f7e280686d46726047dc5c33dc121cf9fd2a9d04514994103e79b16ae042434e139820fe974eb408f86d3eab6382a7b16e94c5f4e
+ checksum: 23f9708ef4f31d62cc0b0245359e576300337103cf9dcec51517a44d9160abaca56eae09942e2cddd0f0f9edc959986c97e3434e27583e19655ef95f74827708
languageName: node
linkType: hard
-"@cardano-sdk/key-management@npm:0.19.7, @cardano-sdk/key-management@npm:~0.19.7":
- version: 0.19.7
- resolution: "@cardano-sdk/key-management@npm:0.19.7"
+"@cardano-sdk/key-management@npm:0.19.8, @cardano-sdk/key-management@npm:~0.19.8":
+ version: 0.19.8
+ resolution: "@cardano-sdk/key-management@npm:0.19.8"
dependencies:
- "@cardano-sdk/core": ~0.28.2
+ "@cardano-sdk/core": ~0.28.3
"@cardano-sdk/crypto": ~0.1.21
- "@cardano-sdk/dapp-connector": ~0.12.9
+ "@cardano-sdk/dapp-connector": ~0.12.10
"@cardano-sdk/util": ~0.15.0
"@emurgo/cardano-message-signing-nodejs": ^1.0.1
bip39: ^3.0.4
@@ -7839,36 +7839,36 @@ __metadata:
rxjs: ^7.4.0
ts-custom-error: ^3.2.0
ts-log: ^2.2.4
- checksum: a52d2f369ec062f79b1243ea5762f43e2e16e6db4e5c5a554cd254814b228ba0d54cc5389d4151f70edacb2c67baa9feb57a9df5b5adf0a4289d69d28f8cca39
+ checksum: 824327cf6fe2a4a59d3c351b30d24201db339dbc04fc7fb2c1d1d0c7c8dea1f3b82ce1b49c98f4b0ab69d5906242a47296754cf5a8bc06808212fad531edca3a
languageName: node
linkType: hard
-"@cardano-sdk/tx-construction@npm:0.17.10, @cardano-sdk/tx-construction@npm:~0.17.10":
- version: 0.17.10
- resolution: "@cardano-sdk/tx-construction@npm:0.17.10"
+"@cardano-sdk/tx-construction@npm:0.17.11, @cardano-sdk/tx-construction@npm:~0.17.11":
+ version: 0.17.11
+ resolution: "@cardano-sdk/tx-construction@npm:0.17.11"
dependencies:
- "@cardano-sdk/core": ~0.28.2
+ "@cardano-sdk/core": ~0.28.3
"@cardano-sdk/crypto": ~0.1.21
- "@cardano-sdk/input-selection": ~0.12.20
- "@cardano-sdk/key-management": ~0.19.7
+ "@cardano-sdk/input-selection": ~0.12.21
+ "@cardano-sdk/key-management": ~0.19.8
"@cardano-sdk/util": ~0.15.0
- "@cardano-sdk/util-rxjs": ~0.7.4
+ "@cardano-sdk/util-rxjs": ~0.7.5
lodash: ^4.17.21
npm: ^9.3.0
rxjs: ^7.4.0
ts-custom-error: ^3.2.0
ts-log: ^2.2.4
- checksum: 2cfa467a6927bb1fdf9b04d45e37579e282606fe98819a663e17d5af668f18619ef2d9fa2a0255448e27bfb246fb1f27999a4d1ab976bd05c142d9788bde5250
+ checksum: d24fe6fa3688047a7998786d79087d80bf120449df2da1297909742e706df123f359b40360531851191656600e66b3c2eaae17fdaee44d588fd234f9634d7177
languageName: node
linkType: hard
-"@cardano-sdk/util-dev@npm:0.19.13":
- version: 0.19.13
- resolution: "@cardano-sdk/util-dev@npm:0.19.13"
+"@cardano-sdk/util-dev@npm:0.19.14":
+ version: 0.19.14
+ resolution: "@cardano-sdk/util-dev@npm:0.19.14"
dependencies:
- "@cardano-sdk/core": ~0.28.2
+ "@cardano-sdk/core": ~0.28.3
"@cardano-sdk/crypto": ~0.1.21
- "@cardano-sdk/key-management": ~0.19.7
+ "@cardano-sdk/key-management": ~0.19.8
"@cardano-sdk/util": ~0.15.0
"@types/dockerode": ^3.3.8
axios: ^0.27.2
@@ -7881,18 +7881,18 @@ __metadata:
lodash: ^4.17.21
rxjs: ^7.4.0
ts-log: ^2.2.4
- checksum: 2275631cd616a8d42dffc544f53f1e8c50687c2f1b7a8a69fdbee5e658cf5d23835bfd188bba0f171a9ebab8b6fcabc72f0fbd24a48de2ea5907f51040bb727b
+ checksum: 52dcd466306cde60adedb798c82fb910c9d2a82043c39fe912b80ed5318fa07afdb200d13954ba92c9409ff719f1ec38438e01fa5c5d5acde42fdb4330b7a08d
languageName: node
linkType: hard
-"@cardano-sdk/util-rxjs@npm:~0.7.4":
- version: 0.7.4
- resolution: "@cardano-sdk/util-rxjs@npm:0.7.4"
+"@cardano-sdk/util-rxjs@npm:~0.7.5":
+ version: 0.7.5
+ resolution: "@cardano-sdk/util-rxjs@npm:0.7.5"
dependencies:
"@cardano-sdk/util": ~0.15.0
backoff-rxjs: ^7.0.0
rxjs: ^7.4.0
- checksum: fe18e9fd9922bece68ca6aec079418bf6cba7c23f8c001b90566ef848109f88caced0e6feb534357809e8fe8f73dfc6b012f88a4353bcaa6859af20edf9bbafd
+ checksum: a965f1a83be9052ebb484a836c1a5383b3268ca92d04e39193f31904642d2a33a19d0ab58c1ed742f0c728a409e7da1a158af52571676b4154f4d5156dd5c11d
languageName: node
linkType: hard
@@ -7910,20 +7910,20 @@ __metadata:
languageName: node
linkType: hard
-"@cardano-sdk/wallet@npm:0.34.2, @cardano-sdk/wallet@npm:~0.34.2":
- version: 0.34.2
- resolution: "@cardano-sdk/wallet@npm:0.34.2"
+"@cardano-sdk/wallet@npm:0.34.3, @cardano-sdk/wallet@npm:~0.34.3":
+ version: 0.34.3
+ resolution: "@cardano-sdk/wallet@npm:0.34.3"
dependencies:
- "@cardano-sdk/core": ~0.28.2
+ "@cardano-sdk/core": ~0.28.3
"@cardano-sdk/crypto": ~0.1.21
- "@cardano-sdk/dapp-connector": ~0.12.9
- "@cardano-sdk/hardware-ledger": ~0.8.13
- "@cardano-sdk/hardware-trezor": ~0.4.13
- "@cardano-sdk/input-selection": ~0.12.20
- "@cardano-sdk/key-management": ~0.19.7
- "@cardano-sdk/tx-construction": ~0.17.10
+ "@cardano-sdk/dapp-connector": ~0.12.10
+ "@cardano-sdk/hardware-ledger": ~0.8.14
+ "@cardano-sdk/hardware-trezor": ~0.4.14
+ "@cardano-sdk/input-selection": ~0.12.21
+ "@cardano-sdk/key-management": ~0.19.8
+ "@cardano-sdk/tx-construction": ~0.17.11
"@cardano-sdk/util": ~0.15.0
- "@cardano-sdk/util-rxjs": ~0.7.4
+ "@cardano-sdk/util-rxjs": ~0.7.5
backoff-rxjs: ^7.0.0
bignumber.js: ^9.1.1
delay: ^5.0.0
@@ -7933,24 +7933,24 @@ __metadata:
rxjs: ^7.4.0
ts-custom-error: ^3.2.0
ts-log: ^2.2.3
- checksum: ff39322df7c6d37e481de0eca6f2d40d64bdc22f4123dd695aae0f796d37c1710248be55d8af34759078ad34a980900757173a0091d05bf7c9329c93cb3fe8f9
+ checksum: 71a383767c8636e7abdc6edd1668feb44b1e69397bf58f000f27b86fc94c781238f9915594df3454988f6189717fd2211baafd7986fa4e9b2f5a07f1fa75846e
languageName: node
linkType: hard
-"@cardano-sdk/web-extension@npm:0.24.5":
- version: 0.24.5
- resolution: "@cardano-sdk/web-extension@npm:0.24.5"
+"@cardano-sdk/web-extension@npm:0.24.6":
+ version: 0.24.6
+ resolution: "@cardano-sdk/web-extension@npm:0.24.6"
dependencies:
- "@cardano-sdk/core": ~0.28.2
+ "@cardano-sdk/core": ~0.28.3
"@cardano-sdk/crypto": ~0.1.21
- "@cardano-sdk/dapp-connector": ~0.12.9
- "@cardano-sdk/hardware-ledger": ~0.8.13
- "@cardano-sdk/hardware-trezor": ~0.4.13
- "@cardano-sdk/key-management": ~0.19.7
- "@cardano-sdk/tx-construction": ~0.17.10
+ "@cardano-sdk/dapp-connector": ~0.12.10
+ "@cardano-sdk/hardware-ledger": ~0.8.14
+ "@cardano-sdk/hardware-trezor": ~0.4.14
+ "@cardano-sdk/key-management": ~0.19.8
+ "@cardano-sdk/tx-construction": ~0.17.11
"@cardano-sdk/util": ~0.15.0
- "@cardano-sdk/util-rxjs": ~0.7.4
- "@cardano-sdk/wallet": ~0.34.2
+ "@cardano-sdk/util-rxjs": ~0.7.5
+ "@cardano-sdk/wallet": ~0.34.3
backoff-rxjs: ^7.0.0
lodash: ^4.17.21
rxjs: ^7.4.0
@@ -7958,7 +7958,7 @@ __metadata:
ts-log: ^2.2.3
uuid: ^8.3.2
webextension-polyfill: ^0.8.0
- checksum: 27ccd4d6fa01addd7b06329762af4aceebe647e5929f3071a5037e68837303cf1f9c5856927299ea13737c1373759a6d0c672f445be9d67c36c5e95191678327
+ checksum: 98273ccc3f0a20c46ddcc31c25ac35b94642c55867bb718b51f313a1cdf0bd52e9ef8bfbd3dd8eb13298984677bb2062e75023838d19b1548374910d42dd9394
languageName: node
linkType: hard
@@ -10592,14 +10592,14 @@ __metadata:
resolution: "@lace/browser-extension-wallet@workspace:apps/browser-extension-wallet"
dependencies:
"@ant-design/icons": ^4.7.0
- "@cardano-sdk/cardano-services-client": 0.17.6
- "@cardano-sdk/core": 0.28.2
- "@cardano-sdk/dapp-connector": 0.12.9
- "@cardano-sdk/input-selection": 0.12.20
- "@cardano-sdk/tx-construction": 0.17.10
+ "@cardano-sdk/cardano-services-client": 0.17.7
+ "@cardano-sdk/core": 0.28.3
+ "@cardano-sdk/dapp-connector": 0.12.10
+ "@cardano-sdk/input-selection": 0.12.21
+ "@cardano-sdk/tx-construction": 0.17.11
"@cardano-sdk/util": 0.15.0
- "@cardano-sdk/wallet": 0.34.2
- "@cardano-sdk/web-extension": 0.24.5
+ "@cardano-sdk/wallet": 0.34.3
+ "@cardano-sdk/web-extension": 0.24.6
"@emurgo/cardano-message-signing-asmjs": 1.0.1
"@emurgo/cip14-js": ~3.0.1
"@koralabs/handles-public-api-interfaces": ^1.6.6
@@ -10662,16 +10662,16 @@ __metadata:
version: 0.0.0-use.local
resolution: "@lace/cardano@workspace:packages/cardano"
dependencies:
- "@cardano-sdk/cardano-services-client": 0.17.6
- "@cardano-sdk/core": 0.28.2
+ "@cardano-sdk/cardano-services-client": 0.17.7
+ "@cardano-sdk/core": 0.28.3
"@cardano-sdk/crypto": 0.1.21
- "@cardano-sdk/hardware-ledger": 0.8.13
- "@cardano-sdk/hardware-trezor": 0.4.13
- "@cardano-sdk/key-management": 0.19.7
+ "@cardano-sdk/hardware-ledger": 0.8.14
+ "@cardano-sdk/hardware-trezor": 0.4.14
+ "@cardano-sdk/key-management": 0.19.8
"@cardano-sdk/util": 0.15.0
- "@cardano-sdk/util-dev": 0.19.13
- "@cardano-sdk/wallet": 0.34.2
- "@cardano-sdk/web-extension": 0.24.5
+ "@cardano-sdk/util-dev": 0.19.14
+ "@cardano-sdk/wallet": 0.34.3
+ "@cardano-sdk/web-extension": 0.24.6
"@emurgo/cardano-message-signing-browser": 1.0.1
"@lace/common": 0.1.0
"@stablelib/chacha20poly1305": 1.0.1
@@ -10831,9 +10831,9 @@ __metadata:
dependencies:
"@ant-design/icons": ^4.7.0
"@babel/core": ^7.21.0
- "@cardano-sdk/core": 0.28.2
- "@cardano-sdk/input-selection": 0.12.20
- "@cardano-sdk/tx-construction": 0.17.10
+ "@cardano-sdk/core": 0.28.3
+ "@cardano-sdk/input-selection": 0.12.21
+ "@cardano-sdk/tx-construction": 0.17.11
"@cardano-sdk/util": 0.15.0
"@lace/cardano": ^0.1.0
"@lace/common": ^0.1.0
@@ -10895,11 +10895,11 @@ __metadata:
wait-on: ^7.0.1
zustand: ^4.4.1
peerDependencies:
- "@cardano-sdk/input-selection": 0.12.20
- "@cardano-sdk/tx-construction": 0.17.10
+ "@cardano-sdk/input-selection": 0.12.21
+ "@cardano-sdk/tx-construction": 0.17.11
"@cardano-sdk/util": 0.15.0
- "@cardano-sdk/wallet": 0.34.2
- "@cardano-sdk/web-extension": 0.24.5
+ "@cardano-sdk/wallet": 0.34.3
+ "@cardano-sdk/web-extension": 0.24.6
"@lace/cardano": ^0.1.0
"@lace/common": ^0.1.0
"@lace/core": 0.1.0