Skip to content

Commit

Permalink
Additional tooling (#1459)
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
6 people authored Oct 22, 2024
1 parent c636a4b commit 248659b
Show file tree
Hide file tree
Showing 173 changed files with 5,697 additions and 7,242 deletions.
2 changes: 2 additions & 0 deletions apps/browser-extension-wallet/.env.defaults
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ CEXPLORER_URL_SANCHONET=https://sancho.cexplorer.io

# Manifest.json
LACE_EXTENSION_KEY=gafhhkghbfjjkeiendhlofajokpaflmk
LACE_EXTENSION_ID=gafhhkghbfjjkeiendhlofajokpaflmk
NAMI_EXTENSION_ID=lpfcbjknijpeeillifnkikgncikgfhdo

# Extension uninstall redirect
LACE_EXTENSION_UNINSTALL_REDIRECT_URL=https://forms.gle/XNcPfWafY8XgxkYw7
Expand Down
2 changes: 2 additions & 0 deletions apps/browser-extension-wallet/.env.developerpreview
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ CEXPLORER_URL_SANCHONET=https://sancho.cexplorer.io

# Manifest.json
LACE_EXTENSION_KEY=djcdfchkaijggdjokfomholkalbffgil
LACE_EXTENSION_ID=djcdfchkaijggdjokfomholkalbffgil
NAMI_EXTENSION_ID=djcdfchkaijggdjokfomholkalbffgil

# Extension uninstall redirect
LACE_EXTENSION_UNINSTALL_REDIRECT_URL=
Expand Down
2 changes: 2 additions & 0 deletions apps/browser-extension-wallet/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ CEXPLORER_URL_SANCHONET=https://sancho.cexplorer.io

# Manifest.json
LACE_EXTENSION_KEY=gafhhkghbfjjkeiendhlofajokpaflmk
LACE_EXTENSION_ID=gafhhkghbfjjkeiendhlofajokpaflmk
NAMI_EXTENSION_ID=gafhhkghbfjjkeiendhlofajokpaflmk

# Midnight
MIDNIGHT_EVENT_BANNER_REMINDER_TIME=129600000
Expand Down
1 change: 0 additions & 1 deletion apps/browser-extension-wallet/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@
"@react-rxjs/core": "^0.9.8",
"@react-rxjs/utils": "^0.9.5",
"@shiroyasha9/axios-fetch-adapter": "^1.0.3",
"@xsy/nami-migration-tool": "file:./xsy-nami-migration-tool-0.0.39.tgz",
"antd": "^4.24.10",
"are-you-es5": "^2.1.2",
"bignumber.js": "9.0.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const UserAvatar = ({ walletName, isPopup, avatar }: UserAvatarProps): Re
{avatar ? (
<Image src={avatar} className={styles.userAvatarImage} preview={false} />
) : (
<span>{walletName && walletName[0]?.toUpperCase()}</span>
<span>{walletName?.[0]?.toUpperCase()}</span>
)}
</div>
);
78 changes: 49 additions & 29 deletions apps/browser-extension-wallet/src/dapp-connector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,43 +5,63 @@ import { StoreProvider } from '@stores';
import '@lib/i18n';
import 'antd/dist/antd.css';
import { CurrencyStoreProvider } from '@providers/currency';
import { DatabaseProvider, AppSettingsProvider, AnalyticsProvider } from '@providers';
import { DatabaseProvider, AppSettingsProvider, AnalyticsProvider, ExternalLinkOpenerProvider } from '@providers';
import { HashRouter } from 'react-router-dom';
import { ThemeProvider } from '@providers/ThemeProvider';
import { UIThemeProvider } from '@providers/UIThemeProvider';
import { BackgroundServiceAPIProvider } from '@providers/BackgroundServiceAPI';
import { APP_MODE_POPUP } from './utils/constants';
import { APP_MODE_POPUP, POPUP_WINDOW_NAMI_TITLE } from './utils/constants';
import { PostHogClientProvider } from '@providers/PostHogClientProvider';
import { ExperimentsProvider } from '@providers/ExperimentsProvider/context';
import { AddressesDiscoveryOverlay } from 'components/AddressesDiscoveryOverlay';
import { useEffect, useState } from 'react';
import { getBackgroundStorage } from '@lib/scripts/background/storage';
import { NamiDappConnector } from './views/nami-mode/indexInternal';

const App = (): React.ReactElement => (
<BackgroundServiceAPIProvider>
<AppSettingsProvider>
<DatabaseProvider>
<StoreProvider appMode={APP_MODE_POPUP}>
<CurrencyStoreProvider>
<HashRouter>
<PostHogClientProvider>
<ExperimentsProvider>
<AnalyticsProvider>
<ThemeProvider>
<AddressesDiscoveryOverlay>
<UIThemeProvider>
<DappConnectorView />
</UIThemeProvider>
</AddressesDiscoveryOverlay>
</ThemeProvider>
</AnalyticsProvider>
</ExperimentsProvider>
</PostHogClientProvider>
</HashRouter>
</CurrencyStoreProvider>
</StoreProvider>
</DatabaseProvider>
</AppSettingsProvider>
</BackgroundServiceAPIProvider>
);
const App = (): React.ReactElement => {
const [mode, setMode] = useState<'lace' | 'nami'>();
useEffect(() => {
const getWalletMode = async () => {
const { namiMigration } = await getBackgroundStorage();
if (namiMigration?.mode === 'nami') {
document.title = POPUP_WINDOW_NAMI_TITLE;
}
setMode(namiMigration?.mode || 'lace');
};

getWalletMode();
}, []);

return (
<BackgroundServiceAPIProvider>
<AppSettingsProvider>
<DatabaseProvider>
<StoreProvider appMode={APP_MODE_POPUP}>
<CurrencyStoreProvider>
<HashRouter>
<PostHogClientProvider>
<ExperimentsProvider>
<AnalyticsProvider>
<ThemeProvider>
<ExternalLinkOpenerProvider>
<AddressesDiscoveryOverlay>
<UIThemeProvider>
{mode === 'nami' ? <NamiDappConnector /> : <DappConnectorView />}
</UIThemeProvider>
</AddressesDiscoveryOverlay>
</ExternalLinkOpenerProvider>
</ThemeProvider>
</AnalyticsProvider>
</ExperimentsProvider>
</PostHogClientProvider>
</HashRouter>
</CurrencyStoreProvider>
</StoreProvider>
</DatabaseProvider>
</AppSettingsProvider>
</BackgroundServiceAPIProvider>
);
};

const mountNode = document.querySelector('#lace-popup');
render(<App />, mountNode);
Original file line number Diff line number Diff line change
Expand Up @@ -148,29 +148,30 @@ export const DappCollateralContainer = (): React.ReactElement => {
}
}, [redirectToCreateFailure]);

if (!isCalculatingCollateral) {
if (insufficientBalance) {
return <InsufficientFunds />;
} else if (lockableUtxos.length > 0 && isInstanceOfCollateralInfoWithCollateralAmount(collateralInfo)) {
return (
<DappSetCollateral
dappInfo={dappInfo}
collateralInfo={collateralInfo}
reject={reject}
confirm={() => confirmCollateral(lockableUtxos)}
/>
);
}
// Allow user to create tx to set collateral
if (isCalculatingCollateral || !inMemoryWallet) {
return <MainLoader text={t('dapp.collateral.calculating')} />;
}

if (insufficientBalance) {
return <InsufficientFunds />;
} else if (lockableUtxos.length > 0 && isInstanceOfCollateralInfoWithCollateralAmount(collateralInfo)) {
return (
<CreateCollateral
<DappSetCollateral
dappInfo={dappInfo}
collateralInfo={collateralInfo}
confirm={(utxos: Wallet.Cardano.Utxo[]) => confirmCollateral(utxos)}
reject={reject}
confirm={() => confirmCollateral(lockableUtxos)}
/>
);
}

return <MainLoader text={t('dapp.collateral.calculating')} />;
// Allow user to create tx to set collateral
return (
<CreateCollateral
dappInfo={dappInfo}
collateralInfo={collateralInfo}
confirm={(utxos: Wallet.Cardano.Utxo[]) => confirmCollateral(utxos)}
reject={reject}
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const ConfirmTransaction = (): React.ReactElement => {
setDappInfo,
signTxRequest: { request: req, set: setSignTxRequest }
} = useViewsFlowContext();
const { walletType, isHardwareWallet } = useWalletStore();
const { walletType, isHardwareWallet, walletInfo, inMemoryWallet } = useWalletStore();
const analytics = useAnalyticsContext();
const [confirmTransactionError] = useState(false);
const disallowSignTx = useDisallowSignTx(req);
Expand Down Expand Up @@ -83,7 +83,7 @@ export const ConfirmTransaction = (): React.ReactElement => {

return (
<Layout layoutClassname={cn(confirmTransactionError && styles.layoutError)} pageClassname={styles.spaceBetween}>
{req ? <DappTransactionContainer /> : <Skeleton loading />}
{req && walletInfo && inMemoryWallet ? <DappTransactionContainer /> : <Skeleton loading />}
{!confirmTransactionError && (
<div className={styles.actions}>
<Button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { runtime } from 'webextension-polyfill';
import { useHistory } from 'react-router-dom';
import { walletRoutePaths as routes } from '@routes/wallet-paths';
import { useCurrencyStore } from '@providers/currency';
import { MigrationState } from '@xsy/nami-migration-tool/dist/migrator/migration-state.data';
import { MigrationState } from './migration-tool/migrator/migration-state.data';
import { useTheme } from '@providers/ThemeProvider/context';

const namiMigrationRemoteApi = consumeRemoteApi<Pick<NamiMigrationAPI, 'startMigration' | 'checkMigrationStatus'>>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { walletRoutePaths } from '@routes';
import { setBackgroundStorage } from '@lib/scripts/background/storage';
import { useAnalyticsContext } from '@providers';
import { postHogNamiMigrationActions } from '@providers/AnalyticsProvider/analyticsTracker';
import * as laceMigrationClient from '@src/features/nami-migration/migration-tool/cross-extension-messaging/lace-migration-client.extension';

export const Customize = (): JSX.Element => {
const history = useHistory();
Expand All @@ -31,6 +32,8 @@ export const Customize = (): JSX.Element => {
}
});

laceMigrationClient.completeMigration();

if (mode === 'lace') {
history.push(walletRoutePaths.assets);
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { consumeRemoteApi, RemoteApiPropertyType } from '@cardano-sdk/web-extension';
import { NamiMigrationAPI, NamiMigrationChannels } from '@lib/scripts/background/nami-migration';
import { getBackgroundStorage } from '@lib/scripts/background/storage';
import { MigrationState } from '@xsy/nami-migration-tool/dist/migrator/migration-state.data';
import { MigrationState } from './migration-tool/migrator/migration-state.data';
import React, { Fragment, useCallback, useEffect, useState } from 'react';
import { Route, Switch, useHistory, useLocation } from 'react-router-dom';
import { runtime, storage } from 'webextension-polyfill';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/* eslint-disable no-console */
import { runtime, tabs } from 'webextension-polyfill';
import { State as MigrationData } from '../migrator/migration-data.data';
import { MigrationState } from '../migrator/migration-state.data';
import { LaceMessages } from './shared/types';
import { createLaceMigrationPingListener } from './lace/create-lace-migration-ping-listener';
import { NAMI_EXTENSION_ID } from './lace/environment';
import { createLaceMigrationOpenListener } from './lace/create-lace-migration-open-listener';
import { LACE_EXTENSION_ID } from './nami/environment';

type CheckMigrationStatus = () => Promise<MigrationState>;

export const checkMigrationStatus: CheckMigrationStatus = () => {
const message: LaceMessages = { type: 'status' };

return runtime.sendMessage(NAMI_EXTENSION_ID, message);
};

type RequestMigrationData = () => Promise<MigrationData>;

export const requestMigrationData: RequestMigrationData = () => {
const message: LaceMessages = { type: 'data' };
return runtime.sendMessage(NAMI_EXTENSION_ID, message);
};

type AbortMigration = () => Promise<void>;

export const abortMigration: AbortMigration = () => {
const message: LaceMessages = { type: 'abort' };
return runtime.sendMessage(NAMI_EXTENSION_ID, message);
};

type CompleteMigration = () => Promise<void>;

export const completeMigration: CompleteMigration = () => {
const message: LaceMessages = { type: 'completed' };
return runtime.sendMessage(NAMI_EXTENSION_ID, message);
};

export const handleNamiRequests = (): void => {
console.log('[NAMI MIGRATION] createLaceMigrationPingListener');
runtime.onMessageExternal.addListener(createLaceMigrationPingListener(NAMI_EXTENSION_ID));
console.log('[NAMI MIGRATION] createLaceMigrationOpenListener');
runtime.onMessageExternal.addListener(
createLaceMigrationOpenListener(NAMI_EXTENSION_ID, LACE_EXTENSION_ID, tabs.create)
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/* eslint-disable no-console */
import { closeAllLaceWindows } from '@lib/scripts/background/util';
import { MessageSender, NamiMessages } from '../shared/types';

export const createLaceMigrationOpenListener =
(namiExtensionId: string, laceExtensionId: string, createTab: ({ url }: { url: string }) => void) =>
async (message: NamiMessages, sender: MessageSender): Promise<void> => {
console.log('[NAMI MIGRATION] createLaceMigrationOpenListener', message, sender);
if (message === NamiMessages.open && sender.id === namiExtensionId) {
// First close all open lace tabs
await closeAllLaceWindows();
createTab({ url: `chrome-extension://${laceExtensionId}/app.html` });
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { createLaceMigrationPingListener } from './create-lace-migration-ping-listener';
import { NamiLacePingProtocol } from '../shared/types';

describe('create lace migration ping listener', () => {
const namiId = 'fakeNamiId';

it('should answer ping messages from Nami', async () => {
const listener = createLaceMigrationPingListener(namiId);
const response = await listener(NamiLacePingProtocol.ping, { id: namiId });
expect(response).toBe(NamiLacePingProtocol.pong);
});

it('should ignore messages not coming from Nami extension', async () => {
const listener = createLaceMigrationPingListener(namiId);
const response = await listener(NamiLacePingProtocol.ping, {
id: 'otherId'
});
expect(response).toBeUndefined();
});

it('should ignore other messages coming from Nami', async () => {
const listener = createLaceMigrationPingListener(namiId);
const response = await listener('other' as NamiLacePingProtocol, {
id: namiId
});
expect(response).toBeUndefined();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/* eslint-disable consistent-return */
/* eslint-disable no-console */
import { MessageSender, NamiLacePingProtocol } from '../shared/types';

export const createLaceMigrationPingListener =
(namiExtensionId: string) =>
async (message: NamiLacePingProtocol, sender: MessageSender): Promise<void | NamiLacePingProtocol.pong> => {
console.log('[NAMI MIGRATION] createLaceMigrationPingListener', message, sender);
if (message === NamiLacePingProtocol.ping && sender.id === namiExtensionId) {
console.log('[NAMI MIGRATION] Sending pong message to Nami');
return NamiLacePingProtocol.pong;
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
if (process.env.NAMI_EXTENSION_ID === undefined) {
throw new Error('process.env.NAMI_EXTENSION_ID must be defined');
}
export const NAMI_EXTENSION_ID = process.env.NAMI_EXTENSION_ID;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
if (process.env.LACE_EXTENSION_ID === undefined) {
throw new Error('process.env.LACE_EXTENSION_ID must be defined');
}
export const LACE_EXTENSION_ID = process.env.LACE_EXTENSION_ID;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum MigrationExceptions {
NotInProgress = 'not-in-progress',
FailedToParse = 'failed-to-parse'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { State } from '../../migrator/nami-storage.data';

export const createMockNamiStore = (mockedState: Partial<State> = {}): { set: jest.Mock; get: jest.Mock } => {
const store = {
set: jest.fn(),
get: jest.fn()
};
store.get.mockResolvedValue(mockedState);
return store;
};
Loading

0 comments on commit 248659b

Please sign in to comment.