Skip to content

Commit 248659b

Browse files
vetalcoremirceahaseganAngelCastilloBprzemyslaw-wlodekgreatertomi
authored
Additional tooling (#1459)
* feat: nami mode dapp connector * feat(extension): conditional inject based on wallet mode (#91) Implements LW-11590 and LW-11630 * chore: resolve sonarcloud issues * fixup! feat(extension): conditional inject based on wallet mode (#91) * fixup! feat: nami mode dapp connector * feat(extension): collateral logic * fixup! feat: nami mode dapp connector * fixup! feat: nami mode dapp connector * chore: bump sdk version * fix: hardware wallet integration * fix: banner display condition and popup size * chore: resolve ci issues * chore: fix ui discrepancies * fixup! fix: hardware wallet integration * fixup! fixup! fix: hardware wallet integration * feat(nami): await posthog events that occur before window.close Window.close could occur before the posthog event is sent. Await on sending the event before closing the window. * fixup! chore: resolve ci issues * fixup! fix: banner display condition and popup size * fixup! fix: hardware wallet integration * fixup! fix: banner display condition and popup size * fix(extension): debounce on CIP-30 endpoint calls now starts after first event (#1458) * fix(extension): debounce on CIP-30 endpoint calls now starts after first event * perf: optimize loading states in Dapp connector views * chore: separate dapp view * feat: optimize loading states in Dapp connector views --------- Co-authored-by: Przemysław Włodek <[email protected]> Co-authored-by: Mircea Hasegan <[email protected]> Co-authored-by: vetalcore <[email protected]> * fix: improve error handling for hardware wallet * fixup! fix: hardware wallet integration * chore: extract more tools (#1461) * chore: extract more tools * chore: update assets * fix: preserve location * fix: hide show recovery phrase setting * fix: [LW-11665] dApp connector proportions for hardware wallets (#1467) * fix: change password flow (#1466) * fix: change password flow * chore: fix tests * chore(extension): ensure tabs are closed (#1464) * fix: delete wallet from popup (#1465) * chore: update copy * chore: remove tslint issues * chore: resolve sonarcloud issues * fix: correct copy paste error for extension IDs * fix: scrollbar ref potentially not active * fix: metadata controlled input lag * chore: fix get collateral utxo test * fix: collateral confirmation toast * fix(nami): tx collateral inputs must be part of marked collaterals * fix: handle server urls * fix: fix unresponsive send page on metadata update with multiple assets * chore: update switch to nami mode copies * fix: store recent addresses per env name for send flow * fix: resolve eslint/tslint fixes commit comments * chore: update switch to nami mode copies * chore: add missing dependencies into dapp connector view * feat: show asset info in assets modal * fix: redirect to trezor sign page * fix: allow additional properties while sending events to posthog * chore: fix tslint issues * fix: fix change password flow * chore: fix tslint issues * fix: refresh tx history if new tx appears * fix: cache address and balance per env name * chore(nami): add missing typings * fix: reset states after submitting tx with trezor * fix: close all lace windows before executing migration * fix: show tx history of multi-delegated accounts * feat(nami): implement migration guard on popup - only complete migration via explicit complete in final onboarding step * fix: show all accounts of all hw instead of just a first one * fixup! feat: show asset info in assets modal * fix: use explicit bigint conversion in both sides of abs function * fix: use explicit bigint conversion in both sides of abs function * refactor: address sonarlint issues * fix: update account tests * fixup! fix(nami): tx collateral inputs must be part of marked collaterals * fix: remove second scrollbar in history tab * refactor: remove dead code * feat(nami): send analytic event for trezor dapp tx sign * fix: add analytic event for ledger send tx * fix(nami): asset meatadata with bigint values * fix: add send transaction confirmation click event for trezor * fix: open HWFlow modal if wallet type is hardware * fix(nami): compute datumHash if contract * fix: nami package storybook * fix: confirm modal event capture relative paths need to be used * fix(nami): broken HW connect UI (#1476) --------- Co-authored-by: Mircea Hasegan <[email protected]> Co-authored-by: Angel Castillo <[email protected]> Co-authored-by: Przemysław Włodek <[email protected]> Co-authored-by: John Oshalusi <[email protected]> Co-authored-by: Michael Chappell <[email protected]>
1 parent c636a4b commit 248659b

File tree

173 files changed

+5697
-7242
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

173 files changed

+5697
-7242
lines changed

apps/browser-extension-wallet/.env.defaults

+2
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ CEXPLORER_URL_SANCHONET=https://sancho.cexplorer.io
8181

8282
# Manifest.json
8383
LACE_EXTENSION_KEY=gafhhkghbfjjkeiendhlofajokpaflmk
84+
LACE_EXTENSION_ID=gafhhkghbfjjkeiendhlofajokpaflmk
85+
NAMI_EXTENSION_ID=lpfcbjknijpeeillifnkikgncikgfhdo
8486

8587
# Extension uninstall redirect
8688
LACE_EXTENSION_UNINSTALL_REDIRECT_URL=https://forms.gle/XNcPfWafY8XgxkYw7

apps/browser-extension-wallet/.env.developerpreview

+2
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ CEXPLORER_URL_SANCHONET=https://sancho.cexplorer.io
8181

8282
# Manifest.json
8383
LACE_EXTENSION_KEY=djcdfchkaijggdjokfomholkalbffgil
84+
LACE_EXTENSION_ID=djcdfchkaijggdjokfomholkalbffgil
85+
NAMI_EXTENSION_ID=djcdfchkaijggdjokfomholkalbffgil
8486

8587
# Extension uninstall redirect
8688
LACE_EXTENSION_UNINSTALL_REDIRECT_URL=

apps/browser-extension-wallet/.env.example

+2
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ CEXPLORER_URL_SANCHONET=https://sancho.cexplorer.io
7979

8080
# Manifest.json
8181
LACE_EXTENSION_KEY=gafhhkghbfjjkeiendhlofajokpaflmk
82+
LACE_EXTENSION_ID=gafhhkghbfjjkeiendhlofajokpaflmk
83+
NAMI_EXTENSION_ID=gafhhkghbfjjkeiendhlofajokpaflmk
8284

8385
# Midnight
8486
MIDNIGHT_EVENT_BANNER_REMINDER_TIME=129600000

apps/browser-extension-wallet/package.json

-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@
6262
"@react-rxjs/core": "^0.9.8",
6363
"@react-rxjs/utils": "^0.9.5",
6464
"@shiroyasha9/axios-fetch-adapter": "^1.0.3",
65-
"@xsy/nami-migration-tool": "file:./xsy-nami-migration-tool-0.0.39.tgz",
6665
"antd": "^4.24.10",
6766
"are-you-es5": "^2.1.2",
6867
"bignumber.js": "9.0.1",

apps/browser-extension-wallet/src/components/MainMenu/DropdownMenuOverlay/components/UserAvatar.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export const UserAvatar = ({ walletName, isPopup, avatar }: UserAvatarProps): Re
1515
{avatar ? (
1616
<Image src={avatar} className={styles.userAvatarImage} preview={false} />
1717
) : (
18-
<span>{walletName && walletName[0]?.toUpperCase()}</span>
18+
<span>{walletName?.[0]?.toUpperCase()}</span>
1919
)}
2020
</div>
2121
);

apps/browser-extension-wallet/src/dapp-connector.tsx

+49-29
Original file line numberDiff line numberDiff line change
@@ -5,43 +5,63 @@ import { StoreProvider } from '@stores';
55
import '@lib/i18n';
66
import 'antd/dist/antd.css';
77
import { CurrencyStoreProvider } from '@providers/currency';
8-
import { DatabaseProvider, AppSettingsProvider, AnalyticsProvider } from '@providers';
8+
import { DatabaseProvider, AppSettingsProvider, AnalyticsProvider, ExternalLinkOpenerProvider } from '@providers';
99
import { HashRouter } from 'react-router-dom';
1010
import { ThemeProvider } from '@providers/ThemeProvider';
1111
import { UIThemeProvider } from '@providers/UIThemeProvider';
1212
import { BackgroundServiceAPIProvider } from '@providers/BackgroundServiceAPI';
13-
import { APP_MODE_POPUP } from './utils/constants';
13+
import { APP_MODE_POPUP, POPUP_WINDOW_NAMI_TITLE } from './utils/constants';
1414
import { PostHogClientProvider } from '@providers/PostHogClientProvider';
1515
import { ExperimentsProvider } from '@providers/ExperimentsProvider/context';
1616
import { AddressesDiscoveryOverlay } from 'components/AddressesDiscoveryOverlay';
17+
import { useEffect, useState } from 'react';
18+
import { getBackgroundStorage } from '@lib/scripts/background/storage';
19+
import { NamiDappConnector } from './views/nami-mode/indexInternal';
1720

18-
const App = (): React.ReactElement => (
19-
<BackgroundServiceAPIProvider>
20-
<AppSettingsProvider>
21-
<DatabaseProvider>
22-
<StoreProvider appMode={APP_MODE_POPUP}>
23-
<CurrencyStoreProvider>
24-
<HashRouter>
25-
<PostHogClientProvider>
26-
<ExperimentsProvider>
27-
<AnalyticsProvider>
28-
<ThemeProvider>
29-
<AddressesDiscoveryOverlay>
30-
<UIThemeProvider>
31-
<DappConnectorView />
32-
</UIThemeProvider>
33-
</AddressesDiscoveryOverlay>
34-
</ThemeProvider>
35-
</AnalyticsProvider>
36-
</ExperimentsProvider>
37-
</PostHogClientProvider>
38-
</HashRouter>
39-
</CurrencyStoreProvider>
40-
</StoreProvider>
41-
</DatabaseProvider>
42-
</AppSettingsProvider>
43-
</BackgroundServiceAPIProvider>
44-
);
21+
const App = (): React.ReactElement => {
22+
const [mode, setMode] = useState<'lace' | 'nami'>();
23+
useEffect(() => {
24+
const getWalletMode = async () => {
25+
const { namiMigration } = await getBackgroundStorage();
26+
if (namiMigration?.mode === 'nami') {
27+
document.title = POPUP_WINDOW_NAMI_TITLE;
28+
}
29+
setMode(namiMigration?.mode || 'lace');
30+
};
31+
32+
getWalletMode();
33+
}, []);
34+
35+
return (
36+
<BackgroundServiceAPIProvider>
37+
<AppSettingsProvider>
38+
<DatabaseProvider>
39+
<StoreProvider appMode={APP_MODE_POPUP}>
40+
<CurrencyStoreProvider>
41+
<HashRouter>
42+
<PostHogClientProvider>
43+
<ExperimentsProvider>
44+
<AnalyticsProvider>
45+
<ThemeProvider>
46+
<ExternalLinkOpenerProvider>
47+
<AddressesDiscoveryOverlay>
48+
<UIThemeProvider>
49+
{mode === 'nami' ? <NamiDappConnector /> : <DappConnectorView />}
50+
</UIThemeProvider>
51+
</AddressesDiscoveryOverlay>
52+
</ExternalLinkOpenerProvider>
53+
</ThemeProvider>
54+
</AnalyticsProvider>
55+
</ExperimentsProvider>
56+
</PostHogClientProvider>
57+
</HashRouter>
58+
</CurrencyStoreProvider>
59+
</StoreProvider>
60+
</DatabaseProvider>
61+
</AppSettingsProvider>
62+
</BackgroundServiceAPIProvider>
63+
);
64+
};
4565

4666
const mountNode = document.querySelector('#lace-popup');
4767
render(<App />, mountNode);

apps/browser-extension-wallet/src/features/dapp/components/collateral/CollateralContainer.tsx

+18-17
Original file line numberDiff line numberDiff line change
@@ -148,29 +148,30 @@ export const DappCollateralContainer = (): React.ReactElement => {
148148
}
149149
}, [redirectToCreateFailure]);
150150

151-
if (!isCalculatingCollateral) {
152-
if (insufficientBalance) {
153-
return <InsufficientFunds />;
154-
} else if (lockableUtxos.length > 0 && isInstanceOfCollateralInfoWithCollateralAmount(collateralInfo)) {
155-
return (
156-
<DappSetCollateral
157-
dappInfo={dappInfo}
158-
collateralInfo={collateralInfo}
159-
reject={reject}
160-
confirm={() => confirmCollateral(lockableUtxos)}
161-
/>
162-
);
163-
}
164-
// Allow user to create tx to set collateral
151+
if (isCalculatingCollateral || !inMemoryWallet) {
152+
return <MainLoader text={t('dapp.collateral.calculating')} />;
153+
}
154+
155+
if (insufficientBalance) {
156+
return <InsufficientFunds />;
157+
} else if (lockableUtxos.length > 0 && isInstanceOfCollateralInfoWithCollateralAmount(collateralInfo)) {
165158
return (
166-
<CreateCollateral
159+
<DappSetCollateral
167160
dappInfo={dappInfo}
168161
collateralInfo={collateralInfo}
169-
confirm={(utxos: Wallet.Cardano.Utxo[]) => confirmCollateral(utxos)}
170162
reject={reject}
163+
confirm={() => confirmCollateral(lockableUtxos)}
171164
/>
172165
);
173166
}
174167

175-
return <MainLoader text={t('dapp.collateral.calculating')} />;
168+
// Allow user to create tx to set collateral
169+
return (
170+
<CreateCollateral
171+
dappInfo={dappInfo}
172+
collateralInfo={collateralInfo}
173+
confirm={(utxos: Wallet.Cardano.Utxo[]) => confirmCollateral(utxos)}
174+
reject={reject}
175+
/>
176+
);
176177
};

apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export const ConfirmTransaction = (): React.ReactElement => {
2727
setDappInfo,
2828
signTxRequest: { request: req, set: setSignTxRequest }
2929
} = useViewsFlowContext();
30-
const { walletType, isHardwareWallet } = useWalletStore();
30+
const { walletType, isHardwareWallet, walletInfo, inMemoryWallet } = useWalletStore();
3131
const analytics = useAnalyticsContext();
3232
const [confirmTransactionError] = useState(false);
3333
const disallowSignTx = useDisallowSignTx(req);
@@ -83,7 +83,7 @@ export const ConfirmTransaction = (): React.ReactElement => {
8383

8484
return (
8585
<Layout layoutClassname={cn(confirmTransactionError && styles.layoutError)} pageClassname={styles.spaceBetween}>
86-
{req ? <DappTransactionContainer /> : <Skeleton loading />}
86+
{req && walletInfo && inMemoryWallet ? <DappTransactionContainer /> : <Skeleton loading />}
8787
{!confirmTransactionError && (
8888
<div className={styles.actions}>
8989
<Button

apps/browser-extension-wallet/src/features/nami-migration/Activating.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { runtime } from 'webextension-polyfill';
77
import { useHistory } from 'react-router-dom';
88
import { walletRoutePaths as routes } from '@routes/wallet-paths';
99
import { useCurrencyStore } from '@providers/currency';
10-
import { MigrationState } from '@xsy/nami-migration-tool/dist/migrator/migration-state.data';
10+
import { MigrationState } from './migration-tool/migrator/migration-state.data';
1111
import { useTheme } from '@providers/ThemeProvider/context';
1212

1313
const namiMigrationRemoteApi = consumeRemoteApi<Pick<NamiMigrationAPI, 'startMigration' | 'checkMigrationStatus'>>(

apps/browser-extension-wallet/src/features/nami-migration/Customize.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { walletRoutePaths } from '@routes';
88
import { setBackgroundStorage } from '@lib/scripts/background/storage';
99
import { useAnalyticsContext } from '@providers';
1010
import { postHogNamiMigrationActions } from '@providers/AnalyticsProvider/analyticsTracker';
11+
import * as laceMigrationClient from '@src/features/nami-migration/migration-tool/cross-extension-messaging/lace-migration-client.extension';
1112

1213
export const Customize = (): JSX.Element => {
1314
const history = useHistory();
@@ -31,6 +32,8 @@ export const Customize = (): JSX.Element => {
3132
}
3233
});
3334

35+
laceMigrationClient.completeMigration();
36+
3437
if (mode === 'lace') {
3538
history.push(walletRoutePaths.assets);
3639
} else {

apps/browser-extension-wallet/src/features/nami-migration/NamiMigrationGuard.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { consumeRemoteApi, RemoteApiPropertyType } from '@cardano-sdk/web-extension';
22
import { NamiMigrationAPI, NamiMigrationChannels } from '@lib/scripts/background/nami-migration';
33
import { getBackgroundStorage } from '@lib/scripts/background/storage';
4-
import { MigrationState } from '@xsy/nami-migration-tool/dist/migrator/migration-state.data';
4+
import { MigrationState } from './migration-tool/migrator/migration-state.data';
55
import React, { Fragment, useCallback, useEffect, useState } from 'react';
66
import { Route, Switch, useHistory, useLocation } from 'react-router-dom';
77
import { runtime, storage } from 'webextension-polyfill';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/* eslint-disable no-console */
2+
import { runtime, tabs } from 'webextension-polyfill';
3+
import { State as MigrationData } from '../migrator/migration-data.data';
4+
import { MigrationState } from '../migrator/migration-state.data';
5+
import { LaceMessages } from './shared/types';
6+
import { createLaceMigrationPingListener } from './lace/create-lace-migration-ping-listener';
7+
import { NAMI_EXTENSION_ID } from './lace/environment';
8+
import { createLaceMigrationOpenListener } from './lace/create-lace-migration-open-listener';
9+
import { LACE_EXTENSION_ID } from './nami/environment';
10+
11+
type CheckMigrationStatus = () => Promise<MigrationState>;
12+
13+
export const checkMigrationStatus: CheckMigrationStatus = () => {
14+
const message: LaceMessages = { type: 'status' };
15+
16+
return runtime.sendMessage(NAMI_EXTENSION_ID, message);
17+
};
18+
19+
type RequestMigrationData = () => Promise<MigrationData>;
20+
21+
export const requestMigrationData: RequestMigrationData = () => {
22+
const message: LaceMessages = { type: 'data' };
23+
return runtime.sendMessage(NAMI_EXTENSION_ID, message);
24+
};
25+
26+
type AbortMigration = () => Promise<void>;
27+
28+
export const abortMigration: AbortMigration = () => {
29+
const message: LaceMessages = { type: 'abort' };
30+
return runtime.sendMessage(NAMI_EXTENSION_ID, message);
31+
};
32+
33+
type CompleteMigration = () => Promise<void>;
34+
35+
export const completeMigration: CompleteMigration = () => {
36+
const message: LaceMessages = { type: 'completed' };
37+
return runtime.sendMessage(NAMI_EXTENSION_ID, message);
38+
};
39+
40+
export const handleNamiRequests = (): void => {
41+
console.log('[NAMI MIGRATION] createLaceMigrationPingListener');
42+
runtime.onMessageExternal.addListener(createLaceMigrationPingListener(NAMI_EXTENSION_ID));
43+
console.log('[NAMI MIGRATION] createLaceMigrationOpenListener');
44+
runtime.onMessageExternal.addListener(
45+
createLaceMigrationOpenListener(NAMI_EXTENSION_ID, LACE_EXTENSION_ID, tabs.create)
46+
);
47+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/* eslint-disable no-console */
2+
import { closeAllLaceWindows } from '@lib/scripts/background/util';
3+
import { MessageSender, NamiMessages } from '../shared/types';
4+
5+
export const createLaceMigrationOpenListener =
6+
(namiExtensionId: string, laceExtensionId: string, createTab: ({ url }: { url: string }) => void) =>
7+
async (message: NamiMessages, sender: MessageSender): Promise<void> => {
8+
console.log('[NAMI MIGRATION] createLaceMigrationOpenListener', message, sender);
9+
if (message === NamiMessages.open && sender.id === namiExtensionId) {
10+
// First close all open lace tabs
11+
await closeAllLaceWindows();
12+
createTab({ url: `chrome-extension://${laceExtensionId}/app.html` });
13+
}
14+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { createLaceMigrationPingListener } from './create-lace-migration-ping-listener';
2+
import { NamiLacePingProtocol } from '../shared/types';
3+
4+
describe('create lace migration ping listener', () => {
5+
const namiId = 'fakeNamiId';
6+
7+
it('should answer ping messages from Nami', async () => {
8+
const listener = createLaceMigrationPingListener(namiId);
9+
const response = await listener(NamiLacePingProtocol.ping, { id: namiId });
10+
expect(response).toBe(NamiLacePingProtocol.pong);
11+
});
12+
13+
it('should ignore messages not coming from Nami extension', async () => {
14+
const listener = createLaceMigrationPingListener(namiId);
15+
const response = await listener(NamiLacePingProtocol.ping, {
16+
id: 'otherId'
17+
});
18+
expect(response).toBeUndefined();
19+
});
20+
21+
it('should ignore other messages coming from Nami', async () => {
22+
const listener = createLaceMigrationPingListener(namiId);
23+
const response = await listener('other' as NamiLacePingProtocol, {
24+
id: namiId
25+
});
26+
expect(response).toBeUndefined();
27+
});
28+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/* eslint-disable consistent-return */
2+
/* eslint-disable no-console */
3+
import { MessageSender, NamiLacePingProtocol } from '../shared/types';
4+
5+
export const createLaceMigrationPingListener =
6+
(namiExtensionId: string) =>
7+
async (message: NamiLacePingProtocol, sender: MessageSender): Promise<void | NamiLacePingProtocol.pong> => {
8+
console.log('[NAMI MIGRATION] createLaceMigrationPingListener', message, sender);
9+
if (message === NamiLacePingProtocol.ping && sender.id === namiExtensionId) {
10+
console.log('[NAMI MIGRATION] Sending pong message to Nami');
11+
return NamiLacePingProtocol.pong;
12+
}
13+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
if (process.env.NAMI_EXTENSION_ID === undefined) {
2+
throw new Error('process.env.NAMI_EXTENSION_ID must be defined');
3+
}
4+
export const NAMI_EXTENSION_ID = process.env.NAMI_EXTENSION_ID;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
if (process.env.LACE_EXTENSION_ID === undefined) {
2+
throw new Error('process.env.LACE_EXTENSION_ID must be defined');
3+
}
4+
export const LACE_EXTENSION_ID = process.env.LACE_EXTENSION_ID;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export enum MigrationExceptions {
2+
NotInProgress = 'not-in-progress',
3+
FailedToParse = 'failed-to-parse'
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { State } from '../../migrator/nami-storage.data';
2+
3+
export const createMockNamiStore = (mockedState: Partial<State> = {}): { set: jest.Mock; get: jest.Mock } => {
4+
const store = {
5+
set: jest.fn(),
6+
get: jest.fn()
7+
};
8+
store.get.mockResolvedValue(mockedState);
9+
return store;
10+
};

0 commit comments

Comments
 (0)