Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,8 @@ import { getBackgroundStorage, setBackgroundStorage } from '@lib/scripts/backgro
import { useBackgroundServiceAPIContext } from '@providers';
import { WarningModal } from '@src/views/browser-view/components';
import { useTranslation } from 'react-i18next';
import { useCurrentWallet, useWalletManager, useLMP } from '@hooks';
import { useCurrentWallet, useWalletManager } from '@hooks';
import { useCurrentBlockchain } from '@src/multichain';
import { AddNewMidnightWalletLink } from './components/AddNewMidnightWalletLink';

interface Props extends MenuProps {
isPopup?: boolean;
Expand All @@ -61,10 +60,8 @@ export const DropdownMenuOverlay: VFC<Props> = ({
const { walletRepository } = useWalletManager();
const currentWallet = useCurrentWallet();
const wallets = useObservable(walletRepository.wallets$);
const { midnightWallets } = useLMP();

const sharedWalletsEnabled = posthog?.isFeatureFlagEnabled('shared-wallets');
const midnightWalletsEnabled = posthog?.isFeatureFlagEnabled('midnight-wallets');
const [currentSection, setCurrentSection] = useState<Sections>(Sections.Main);
const { environmentName, setManageAccountsWallet, walletType, isSharedWallet } = useWalletStore();
const { blockchain } = useCurrentBlockchain();
Expand Down Expand Up @@ -140,7 +137,6 @@ export const DropdownMenuOverlay: VFC<Props> = ({
!isBitcoinWallet &&
wallets?.some((w) => w.type === WalletType.Script && w.ownSigners[0].walletId === currentWallet?.walletId);
const showAddSharedWalletLink = sharedWalletsEnabled && !isSharedWallet && !hasLinkedSharedWallet;
const showAddMidnightWalletLink = midnightWalletsEnabled && midnightWallets && midnightWallets.length === 0;

const handleNamiModeChange = async (activated: boolean) => {
const mode = activated ? 'nami' : 'lace';
Expand Down Expand Up @@ -195,7 +191,6 @@ export const DropdownMenuOverlay: VFC<Props> = ({
<AddNewWalletLink isPopup={isPopup} sendAnalyticsEvent={sendAnalyticsEvent} />
)}
{!isBitcoinWallet && showAddSharedWalletLink && <AddSharedWalletLink isPopup={isPopup} />}
{showAddMidnightWalletLink && <AddNewMidnightWalletLink />}
{!isBitcoinWallet && <AddressBookLink />}
<SettingsLink />
<Separator />
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { useBackgroundServiceAPIContext } from '@providers';
import { BrowserViewSections } from '@lib/scripts/types';
import { useBackgroundPage } from '@providers/BackgroundPageProvider';
import { PostHogAction } from '@lace/common';
import { cameFromLmpStorage } from '@src/utils/lmp';

interface Props {
isPopup?: boolean;
Expand All @@ -22,6 +23,7 @@ export const AddNewWalletLink = ({ isPopup, sendAnalyticsEvent }: Props): React.

const openNewWallet = () => {
sendAnalyticsEvent(PostHogAction.UserWalletProfileAddNewWalletClick);
cameFromLmpStorage.clear();
if (isPopup) {
backgroundServices.handleOpenBrowser({ section: BrowserViewSections.NEW_WALLET });
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import { Separator } from './Separator';
import { getUiWalletType } from '@src/utils/get-ui-wallet-type';
import { isScriptWallet } from '@lace/core';
import { useCurrentBlockchain } from '@src/multichain';
import { LmpBundleWallet } from '@src/utils/lmp';

const ADRESS_FIRST_PART_LENGTH = 10;
const ADRESS_LAST_PART_LENGTH = 5;
Expand Down Expand Up @@ -196,14 +195,14 @@ export const UserInfo = ({
);

const renderLmpWallet = useCallback(
({ walletId, walletName }: LmpBundleWallet, isLast: boolean) => (
({ walletId, walletName }: Wallet.LmpBundleWallet, isLast: boolean) => (
<div key={walletId}>
<ProfileDropdown.WalletOption
style={{ textAlign: 'left' }}
key={walletId}
title={shortenWalletName(walletName, WALLET_OPTION_NAME_MAX_LENGTH)}
id={`wallet-option-${walletId}`}
onClick={switchToLMP}
onClick={() => switchToLMP()}
type={'hot'}
profile={{
customProfileComponent: (
Expand Down
42 changes: 32 additions & 10 deletions v1/apps/browser-extension-wallet/src/hooks/useLMP.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import { consumeRemoteApi } from '@cardano-sdk/web-extension';
import { logger, useObservable } from '@lace/common';
import { APP_MODE, bundleAppApiProps, lmpApiBaseChannel, LmpBundleWallet, lmpModeStorage } from '@src/utils/lmp';
import { Wallet } from '@lace/cardano';
import {
APP_MODE,
bundleAppApiProps,
lmpApiBaseChannel,
lmpModeStorage,
onboardingParamsStorage
} from '@src/utils/lmp';
import { runtime } from 'webextension-polyfill';

const lmpApi = consumeRemoteApi(
Expand All @@ -11,18 +18,33 @@ const lmpApi = consumeRemoteApi(
{ logger, runtime }
);

const switchToLMP = (): void =>
const navigateToLMP = (): void => {
if (window.location.pathname.startsWith('/popup.html')) {
chrome.tabs.create({ url: '/tab.html' });
} else {
window.location.href = '/tab.html';
}
};

const switchToLMP = async (): Promise<void> => {
await lmpModeStorage.set(APP_MODE.LMP);
navigateToLMP();
};

const startMidnightCreate = (): void =>
void (async () => {
await onboardingParamsStorage.set({ mode: 'create' });
await switchToLMP();
})();

const startMidnightRestore = (): void =>
void (async () => {
await lmpModeStorage.set(APP_MODE.LMP);
if (window.location.pathname.startsWith('/popup.html')) {
chrome.tabs.create({ url: '/tab.html' });
} else {
window.location.href = '/tab.html';
}
await onboardingParamsStorage.set({ mode: 'restore' });
await switchToLMP();
})();

// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export const useLMP = () => {
const midnightWallets = useObservable<LmpBundleWallet[] | undefined>(lmpApi.wallets$);
return { midnightWallets, switchToLMP };
const midnightWallets = useObservable<Wallet.LmpBundleWallet[] | undefined>(lmpApi.wallets$);
return { midnightWallets, switchToLMP, startMidnightCreate, startMidnightRestore };
};
14 changes: 2 additions & 12 deletions v1/apps/browser-extension-wallet/src/hooks/useWalletManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,11 +251,7 @@ const first64AsciiBytesToHex = (input: string): string => {
return resultBuffer.toString('hex');
};

const clearBytes = (bytes: Uint8Array) => {
for (let i = 0; i < bytes.length; i++) {
bytes[i] = 0;
}
};
const { clearBytes } = Wallet.util;

const getExtendedAccountPublicKey = async ({
wallet,
Expand Down Expand Up @@ -1245,14 +1241,8 @@ export const useWalletManager = (): UseWalletManager => {
async (wallet: AnyWallet<Wallet.WalletMetadata, Wallet.AccountMetadata>, passphrase: Uint8Array) => {
switch (wallet.type) {
case WalletType.InMemory: {
const keyMaterialBytes = await Wallet.KeyManagement.emip3decrypt(
Buffer.from(wallet.encryptedSecrets.keyMaterial, 'hex'),
passphrase
);
const keyMaterialBuffer = Buffer.from(keyMaterialBytes);
const mnemonic = keyMaterialBuffer.toString('utf8').split(' ');
const mnemonic = await Wallet.util.decryptMnemonic(wallet.encryptedSecrets.keyMaterial, passphrase);
clearBytes(passphrase);
clearBytes(keyMaterialBytes);
return mnemonic;
}
case WalletType.Ledger:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/no-var-requires */
import { BundleAppApi, LmpBundleWallet, v1ApiGlobalProperty } from '@src/utils/lmp';
import { BundleAppApi, v1ApiGlobalProperty } from '@src/utils/lmp';
import { BehaviorSubject, firstValueFrom, map } from 'rxjs';
import { bitcoinWalletManager, walletManager, walletRepository } from '../wallet';
import { AnyBip32Wallet, AnyWallet, InMemoryWallet, WalletType } from '@cardano-sdk/web-extension';
Expand Down Expand Up @@ -51,7 +51,7 @@ const api: BundleAppApi = {
wallets$: walletRepository.wallets$.pipe(
map((wallets) =>
wallets.map(
(wallet): LmpBundleWallet => ({
(wallet): Wallet.LmpBundleWallet => ({
walletIcon: isBitcoinWallet(wallet) ? bitcoinLogo : cardanoLogo,
walletId: wallet.walletId,
walletName: wallet.metadata.name,
Expand Down
49 changes: 34 additions & 15 deletions v1/apps/browser-extension-wallet/src/utils/lmp.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,11 @@
// mostly duplicated in v1 and lmp module, could be a shared library
import { RemoteApiProperties, RemoteApiPropertyType, WalletType } from '@cardano-sdk/web-extension';
import { RemoteApiProperties, RemoteApiPropertyType } from '@cardano-sdk/web-extension';
import { Wallet } from '@lace/cardano';
import { Observable } from 'rxjs';
import { storage } from 'webextension-polyfill';
import { Language } from '@lace/translation';

export type BlockchainName = 'Bitcoin' | 'Cardano' | 'Midnight';

export type LmpBundleWallet = {
walletId: string;
walletName: string;
walletIcon: string;
encryptedRecoveryPhrase?: string;
blockchain: BlockchainName;
walletType: WalletType;
};

export type BundleAppApi = {
wallets$: Observable<LmpBundleWallet[]>;
wallets$: Observable<Wallet.LmpBundleWallet[]>;
activate(walletId: string): Promise<void>;
language$: Observable<Language>;
setLanguage(language: Language): Promise<void>;
Expand All @@ -27,10 +16,24 @@ export const bundleAppApiProps: RemoteApiProperties<BundleAppApi> = {
language$: RemoteApiPropertyType.HotObservable,
setLanguage: RemoteApiPropertyType.MethodReturningPromise
};

export const lmpApiBaseChannel = 'bundle-lmp';
export const v1ApiGlobalProperty = 'bundleV1';
export const STORAGE_KEY = {
APP_MODE: 'lace-app-mode'
APP_MODE: 'lace-app-mode',
ONBOARDING_PARAMS: 'lace-lmp-onboarding-params',
CAME_FROM_LMP: 'lace-came-from-lmp'
};

export type OnboardingParams = { mode: 'create' } | { mode: 'restore' };

export const onboardingParamsStorage = {
set: (params: OnboardingParams): Promise<void> => storage.local.set({ [STORAGE_KEY.ONBOARDING_PARAMS]: params }),
get: async (): Promise<OnboardingParams | undefined> => {
const result = await storage.local.get(STORAGE_KEY.ONBOARDING_PARAMS);
return result[STORAGE_KEY.ONBOARDING_PARAMS] as OnboardingParams | undefined;
},
clear: (): Promise<void> => storage.local.remove(STORAGE_KEY.ONBOARDING_PARAMS)
};
export enum APP_MODE {
LMP = 'LMP',
Expand All @@ -40,3 +43,19 @@ export enum APP_MODE {
export const lmpModeStorage = {
set: (mode: APP_MODE): Promise<void> => storage.local.set({ [STORAGE_KEY.APP_MODE]: mode })
};

export const cameFromLmpStorage = {
set: (): Promise<void> => storage.local.set({ [STORAGE_KEY.CAME_FROM_LMP]: true }),
get: async (): Promise<boolean> => {
const result = await storage.local.get(STORAGE_KEY.CAME_FROM_LMP);
return !!result[STORAGE_KEY.CAME_FROM_LMP];
},
clear: (): Promise<void> => storage.local.remove(STORAGE_KEY.CAME_FROM_LMP)
};

export const decryptMnemonic = async (encryptedRecoveryPhrase: string, password: string): Promise<string[]> => {
const passphrase = new Uint8Array(Buffer.from(password));
const mnemonic = await Wallet.util.decryptMnemonic(encryptedRecoveryPhrase, passphrase);
Wallet.util.clearBytes(passphrase);
return mnemonic;
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { WalletSetupConfirmationDialogProvider, WalletSetupFlow, WalletSetupFlowProvider } from '@lace/core';
import { useBackgroundPage } from '@providers/BackgroundPageProvider';
import { walletRoutePaths } from '@routes';
import React, { useCallback } from 'react';
import React, { useCallback, useEffect, useState } from 'react';
import { useHistory } from 'react-router-dom';
import styles from './MultiWallet.module.scss';
import { WalletOnboardingFlows } from './WalletOnboardingFlows';
Expand All @@ -12,21 +12,39 @@
import { usePostHogClientContext } from '@providers/PostHogClientProvider';
import { WalletSetupLayout } from '@views/browser/components';
import { Portal } from '@views/browser/features/wallet-setup/components/Portal';
import { APP_MODE, cameFromLmpStorage, lmpModeStorage } from '@src/utils/lmp';

export const MultiWallet = (): JSX.Element => {
const history = useHistory();
const posthogClient = usePostHogClientContext();
const { page, setBackgroundPage } = useBackgroundPage();
const [cameFromLMP, setCameFromLMP] = useState(false);

useEffect(() => {
const checkCameFromLMP = async () => {
const result = await cameFromLmpStorage.get();
if (result) {
setCameFromLMP(true);
}
};
void checkCameFromLMP();
}, []);

const handleOnCancel = useCallback(
(withConfirmationDialog: (callback: () => void) => () => void) => {
withConfirmationDialog(() => {
withConfirmationDialog(async () => {
if (cameFromLMP) {
await cameFromLmpStorage.clear();
await lmpModeStorage.set(APP_MODE.LMP);
window.location.href = '/tab.html';

Check warning on line 39 in v1/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/MultiWallet.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `globalThis` over `window`.

See more on https://sonarcloud.io/project/issues?id=input-output-hk_lace&issues=AZsARNAQXsFQzoBt5s0v&open=AZsARNAQXsFQzoBt5s0v&pullRequest=2103
return;
}
setBackgroundPage();
history.push(page);
window.location.reload();
})();
},
[history, page, setBackgroundPage]
[history, page, setBackgroundPage, cameFromLMP]
);

return (
Expand Down
Loading
Loading