Skip to content

Commit eefeb92

Browse files
authored
feat: add Paper Wallet [LW-8901] (#1332)
1 parent 02cf0ee commit eefeb92

File tree

97 files changed

+3951
-405
lines changed

Some content is hidden

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

97 files changed

+3951
-405
lines changed

apps/browser-extension-wallet/package.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
"@lace/core": "0.1.0",
5757
"@lace/staking": "0.1.0",
5858
"@lace/translation": "0.1.0",
59+
"@pdfme/generator": "^4.0.2",
5960
"@react-rxjs/core": "^0.9.8",
6061
"@react-rxjs/utils": "^0.9.5",
6162
"@vespaiach/axios-fetch-adapter": "^0.3.0",
@@ -73,12 +74,15 @@
7374
"graphql-tag": "2.12.5",
7475
"i18next": "^22.5.1",
7576
"intersection-observer-polyfill": "0.1.0",
77+
"jsqr": "^1.4.0",
7678
"lodash": "4.17.21",
7779
"node-abort-controller": "^3.1.1",
80+
"openpgp": "^5.11.2",
7881
"p-debounce": "^4.0.0",
7982
"pluralize": "^8.0.0",
8083
"posthog-js": "^1.68.4",
8184
"process": "^0.11.10",
85+
"qrcode": "^1.5.3",
8286
"react": "17.0.2",
8387
"react-dom": "17.0.2",
8488
"react-i18next": "^12.3.1",
@@ -93,9 +97,14 @@
9397
"devDependencies": {
9498
"@cardano-sdk/hardware-ledger": "0.11.0",
9599
"@emurgo/cardano-message-signing-asmjs": "1.0.1",
100+
"@openpgp/web-stream-tools": "0.0.11-patch-0",
101+
"@pdfme/common": "^4.0.2",
102+
"@pdfme/schemas": "^4.0.2",
96103
"@types/dotenv-webpack": "7.0.3",
97104
"@types/pluralize": "^0.0.29",
105+
"@types/qrcode": "^1",
98106
"@types/react-lottie": "^1.2.6",
107+
"@types/text-encoding-utf-8": "^1",
99108
"@types/uuid": "^8.3.4",
100109
"@types/w3c-web-hid": "^1.0.3",
101110
"@types/webextension-polyfill": "0.8.0",
@@ -105,6 +114,7 @@
105114
"fake-indexeddb": "3.1.3",
106115
"fork-ts-checker-webpack-plugin": "^7.2.1",
107116
"jest-webextension-mock": "^3.7.19",
117+
"text-encoding-utf-8": "^1.0.2",
108118
"tsconfig-paths-webpack-plugin": "3.5.2",
109119
"webassembly-loader-sw": "^1.1.0"
110120
}

apps/browser-extension-wallet/src/lib/scripts/types/storage.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { AuthorizedDappStorage } from '@src/types/dappConnector';
22
import type { Message } from './background-service';
33
import { ADAPrices } from './prices';
4+
import { ExperimentName } from '@providers/ExperimentsProvider/types';
45

56
export interface PendingMigrationState {
67
from: string;
@@ -28,7 +29,7 @@ export interface BackgroundStorage {
2829
fiatPrices?: { prices: ADAPrices; timestamp: number };
2930
userId?: string;
3031
usePersistentUserId?: boolean;
31-
experimentsConfiguration?: Record<string, string | boolean>;
32+
experimentsConfiguration?: Record<ExperimentName, string | boolean>;
3233
customSubmitTxUrl?: string;
3334
}
3435

apps/browser-extension-wallet/src/providers/AnalyticsProvider/analyticsTracker/events/multi-wallet.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,25 @@ const makeMultiWalletRestoreEvent = <E extends string>(eventSuffix: E) =>
99
`multiwallet | restore wallet revamp | ${eventSuffix}` as const;
1010
const makeMultiWalletHardwareEvent = <E extends string>(eventSuffix: E) =>
1111
`multiwallet | hardware wallet revamp | ${eventSuffix}` as const;
12+
const makePaperWalletOnboardingCreateEvent = <E extends string>(eventSuffix: E) =>
13+
`onboarding | new wallet revamp paper wallet | ${eventSuffix}` as const;
14+
const makePaperWalletOnboardingRestoreEvent = <E extends string>(eventSuffix: E) =>
15+
`onboarding | restore wallet revamp paper wallet | ${eventSuffix}` as const;
1216

1317
const multiWalletActions = {
1418
create: {
19+
CHOOSE_RECOVERY_MODE_MNEMONIC_CLICK: makePaperWalletOnboardingCreateEvent('choose mode | recovery phrase | click'),
20+
CHOOSE_RECOVERY_MODE_PAPER_CLICK: makePaperWalletOnboardingCreateEvent('choose mode | paper wallet | click'),
21+
CHOOSE_RECOVERY_MODE_NEXT_CLICK: makePaperWalletOnboardingCreateEvent('choose mode | next | click'),
22+
PGP_PUBLIC_KEY_PAGE_VIEW: makePaperWalletOnboardingCreateEvent('step: pgp key | pageview'),
23+
PGP_PUBLIC_KEY_NEXT_CLICK: makePaperWalletOnboardingCreateEvent('step: pgp key | next | click'),
24+
WALLET_SETUP_GENERATE_PAPER_WALLET_CLICK: makePaperWalletOnboardingCreateEvent(
25+
'step: wallet info | Generate paper wallet | click'
26+
),
27+
PAPER_WALLET_DOWNLOAD_PAGEVIEW: makePaperWalletOnboardingCreateEvent('step: download pdf | pageview'),
28+
DOWNLOAD_PAPER_WALLET_CLICK: makePaperWalletOnboardingCreateEvent('download pdf | download pdf | click'),
29+
PRINT_PAPER_WALLET_CLICK: makePaperWalletOnboardingCreateEvent('print pdf | print pdf | click'),
30+
PAPER_WALLET_COMPLETE_CLICK: makePaperWalletOnboardingCreateEvent('open wallet | open wallet | click'),
1531
SETUP_OPTION_CLICK: makeMultiWalletCreateEvent('create | click'),
1632
SAVE_RECOVERY_PHRASE_NEXT_CLICK: makeMultiWalletCreateEvent('save your recovery phrase | next | click'),
1733
ENTER_RECOVERY_PHRASE_NEXT_CLICK: makeMultiWalletCreateEvent('enter your recovery phrase | next | click'),
@@ -35,6 +51,18 @@ const multiWalletActions = {
3551
WALLET_ADDED: makeMultiWalletCreateEvent('added')
3652
},
3753
restore: {
54+
WALLET_SETUP_PAGEVIEW: makePaperWalletOnboardingCreateEvent('step: wallet info | pageview'),
55+
CHOOSE_RECOVERY_MODE_MNEMONIC_CLICK: makePaperWalletOnboardingRestoreEvent('choose mode | recovery phrase | click'),
56+
CHOOSE_RECOVERY_MODE_PAPER_CLICK: makePaperWalletOnboardingRestoreEvent('choose mode | paper wallet | click'),
57+
CHOOSE_RECOVERY_MODE_NEXT_CLICK: makePaperWalletOnboardingRestoreEvent('choose mode | next | click'),
58+
SCAN_QR_CODE_PAGEVIEW: makePaperWalletOnboardingRestoreEvent('step: scan qr code | pageview'),
59+
SCAN_QR_CODE_CAMERA_OK: makePaperWalletOnboardingRestoreEvent('step: scan qr code | camera ok'),
60+
SCAN_QR_CODE_CAMERA_ERROR: makePaperWalletOnboardingRestoreEvent('step: scan qr code | camera error'),
61+
SCAN_QR_CODE_READ_SUCCESS: makePaperWalletOnboardingRestoreEvent('step: scan qr code | read success'),
62+
SCAN_QR_CODE_READ_ERROR: makePaperWalletOnboardingRestoreEvent('step: scan qr code | read error'),
63+
WALLET_OVERVIEW_NEXT_CLICK: makePaperWalletOnboardingRestoreEvent('step: wallet overview | next | click'),
64+
ENTER_PGP_PRIVATE_KEY_PAGE_VIEW: makePaperWalletOnboardingRestoreEvent('step: pgp private key | pageview'),
65+
ENTER_PGP_PRIVATE_KEY_NEXT_CLICK: makePaperWalletOnboardingRestoreEvent('step: pgp private key | next | click'),
3866
SETUP_OPTION_CLICK: makeMultiWalletRestoreEvent('restore | click'),
3967
ENTER_WALLET: makeMultiWalletRestoreEvent("let's set up your new wallet | enter wallet | click"),
4068
ENTER_RECOVERY_PHRASE_NEXT_CLICK: makeMultiWalletRestoreEvent(' enter your recovery phrase | next | click'),

apps/browser-extension-wallet/src/providers/AnalyticsProvider/analyticsTracker/events/onboarding.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ const makeOnboardingHardwareEvent = <E extends string>(eventSuffix: E) =>
1313
`onboarding | hardware wallet revamp | ${eventSuffix}` as const;
1414
const makeForgotPasswordEvent = <E extends string>(eventSuffix: E) =>
1515
`unlock wallet | forgot password? | ${eventSuffix}` as const;
16+
const makePaperWalletOnboardingCreateEvent = <E extends string>(eventSuffix: E) =>
17+
`onboarding | new wallet revamp paper wallet | ${eventSuffix}` as const;
18+
const makePaperWalletOnboardingRestoreEvent = <E extends string>(eventSuffix: E) =>
19+
`onboarding | restore wallet revamp paper wallet | ${eventSuffix}` as const;
1620

1721
const onboardingActions = {
1822
onboarding: {
@@ -23,6 +27,18 @@ const onboardingActions = {
2327
PIN_EXTENSION_CLICK: makeOnboardingEvent('lace main view | pin the wallet extension | click')
2428
},
2529
create: {
30+
CHOOSE_RECOVERY_MODE_MNEMONIC_CLICK: makePaperWalletOnboardingCreateEvent('choose mode | recovery phrase | click'),
31+
CHOOSE_RECOVERY_MODE_PAPER_CLICK: makePaperWalletOnboardingCreateEvent('choose mode | paper wallet | click'),
32+
CHOOSE_RECOVERY_MODE_NEXT_CLICK: makePaperWalletOnboardingCreateEvent('choose mode | next | click'),
33+
PGP_PUBLIC_KEY_PAGE_VIEW: makePaperWalletOnboardingCreateEvent('step: pgp key | pageview'),
34+
PGP_PUBLIC_KEY_NEXT_CLICK: makePaperWalletOnboardingCreateEvent('step: pgp key | next | click'),
35+
WALLET_SETUP_GENERATE_PAPER_WALLET_CLICK: makePaperWalletOnboardingCreateEvent(
36+
'step: wallet info | Generate paper wallet | click'
37+
),
38+
PAPER_WALLET_DOWNLOAD_PAGEVIEW: makePaperWalletOnboardingCreateEvent('step: download pdf | pageview'),
39+
DOWNLOAD_PAPER_WALLET_CLICK: makePaperWalletOnboardingCreateEvent('download pdf | download pdf | click'),
40+
PRINT_PAPER_WALLET_CLICK: makePaperWalletOnboardingCreateEvent('print pdf | print pdf | click'),
41+
PAPER_WALLET_COMPLETE_CLICK: makePaperWalletOnboardingCreateEvent('open wallet | open wallet | click'),
2642
SETUP_OPTION_CLICK: makeOnboardingCreateEvent('create | click'),
2743
SAVE_RECOVERY_PHRASE_NEXT_CLICK: makeOnboardingCreateEvent('save your recovery phrase | next | click'),
2844
ENTER_RECOVERY_PHRASE_NEXT_CLICK: makeOnboardingCreateEvent('enter your recovery phrase | next | click'),
@@ -46,6 +62,18 @@ const onboardingActions = {
4662
WALLET_ADDED: makeOnboardingCreateEvent('added')
4763
},
4864
restore: {
65+
WALLET_SETUP_PAGEVIEW: makePaperWalletOnboardingCreateEvent('step: wallet info | pageview'),
66+
CHOOSE_RECOVERY_MODE_MNEMONIC_CLICK: makePaperWalletOnboardingRestoreEvent('choose mode | recovery phrase | click'),
67+
CHOOSE_RECOVERY_MODE_PAPER_CLICK: makePaperWalletOnboardingRestoreEvent('choose mode | paper wallet | click'),
68+
CHOOSE_RECOVERY_MODE_NEXT_CLICK: makePaperWalletOnboardingRestoreEvent('choose mode | next | click'),
69+
SCAN_QR_CODE_PAGEVIEW: makePaperWalletOnboardingRestoreEvent('step: scan qr code | pageview'),
70+
SCAN_QR_CODE_CAMERA_OK: makePaperWalletOnboardingRestoreEvent('step: scan qr code | camera ok'),
71+
SCAN_QR_CODE_CAMERA_ERROR: makePaperWalletOnboardingRestoreEvent('step: scan qr code | camera error'),
72+
SCAN_QR_CODE_READ_SUCCESS: makePaperWalletOnboardingRestoreEvent('step: scan qr code | read success'),
73+
SCAN_QR_CODE_READ_ERROR: makePaperWalletOnboardingRestoreEvent('step: scan qr code | read error'),
74+
WALLET_OVERVIEW_NEXT_CLICK: makePaperWalletOnboardingRestoreEvent('step: wallet overview | next | click'),
75+
ENTER_PGP_PRIVATE_KEY_PAGE_VIEW: makePaperWalletOnboardingRestoreEvent('step: pgp private key | pageview'),
76+
ENTER_PGP_PRIVATE_KEY_NEXT_CLICK: makePaperWalletOnboardingRestoreEvent('step: pgp private key | next | click'),
4977
SETUP_OPTION_CLICK: makeOnboardingRestoreEvent('restore | click'),
5078
ENTER_WALLET: makeOnboardingRestoreEvent("let's set up your new wallet | enter wallet | click"),
5179
ENTER_RECOVERY_PHRASE_NEXT_CLICK: makeOnboardingRestoreEvent(' enter your recovery phrase | next | click'),

apps/browser-extension-wallet/src/providers/AnalyticsProvider/analyticsTracker/events/types.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,32 @@ export type CreateFlowActions = Record<
99
| 'RECOVERY_PHRASE_PASTE_FROM_CLIPBOARD_CLICK'
1010
| 'RECOVERY_PHRASE_COPY_READ_MORE_CLICK'
1111
| 'RECOVERY_PHRASE_PASTE_READ_MORE_CLICK'
12-
| 'WALLET_ADDED',
12+
| 'WALLET_ADDED'
13+
| 'CHOOSE_RECOVERY_MODE_MNEMONIC_CLICK'
14+
| 'CHOOSE_RECOVERY_MODE_PAPER_CLICK'
15+
| 'CHOOSE_RECOVERY_MODE_NEXT_CLICK'
16+
| 'PGP_PUBLIC_KEY_PAGE_VIEW'
17+
| 'PGP_PUBLIC_KEY_NEXT_CLICK'
18+
| 'WALLET_SETUP_GENERATE_PAPER_WALLET_CLICK'
19+
| 'PAPER_WALLET_DOWNLOAD_PAGEVIEW'
20+
| 'DOWNLOAD_PAPER_WALLET_CLICK'
21+
| 'PRINT_PAPER_WALLET_CLICK'
22+
| 'PAPER_WALLET_COMPLETE_CLICK',
1323
string
1424
>;
1525
export type RestoreFlowActions = Record<
26+
| 'WALLET_SETUP_PAGEVIEW'
27+
| 'CHOOSE_RECOVERY_MODE_MNEMONIC_CLICK'
28+
| 'CHOOSE_RECOVERY_MODE_PAPER_CLICK'
29+
| 'CHOOSE_RECOVERY_MODE_NEXT_CLICK'
30+
| 'SCAN_QR_CODE_PAGEVIEW'
31+
| 'SCAN_QR_CODE_CAMERA_OK'
32+
| 'SCAN_QR_CODE_CAMERA_ERROR'
33+
| 'SCAN_QR_CODE_READ_SUCCESS'
34+
| 'SCAN_QR_CODE_READ_ERROR'
35+
| 'WALLET_OVERVIEW_NEXT_CLICK'
36+
| 'ENTER_PGP_PRIVATE_KEY_PAGE_VIEW'
37+
| 'ENTER_PGP_PRIVATE_KEY_NEXT_CLICK'
1638
| 'SETUP_OPTION_CLICK'
1739
| 'ENTER_RECOVERY_PHRASE_NEXT_CLICK'
1840
| 'ENTER_WALLET'
Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
11
import { ExperimentName, ExperimentsConfig, FallbackConfiguration } from './types';
22

33
export const fallbackConfiguration: FallbackConfiguration = {
4-
[ExperimentName.COMBINED_NAME_PASSWORD_ONBOARDING_SCREEN]: 'control'
4+
[ExperimentName.COMBINED_NAME_PASSWORD_ONBOARDING_SCREEN]: 'control',
5+
[ExperimentName.CREATE_PAPER_WALLET]: false,
6+
[ExperimentName.RESTORE_PAPER_WALLET]: false
57
};
68

79
export const experiments: ExperimentsConfig = {
810
[ExperimentName.COMBINED_NAME_PASSWORD_ONBOARDING_SCREEN]: {
911
variants: ['control', 'test'],
10-
defaultVariant: 'control'
12+
default: 'control'
13+
},
14+
[ExperimentName.CREATE_PAPER_WALLET]: {
15+
value: false,
16+
default: false
17+
},
18+
[ExperimentName.RESTORE_PAPER_WALLET]: {
19+
value: false,
20+
default: false
1121
}
1222
};

apps/browser-extension-wallet/src/providers/ExperimentsProvider/context.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { createContext, useCallback, useEffect, useMemo, useState } from 'react';
1+
import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
22
import { usePostHogClientContext } from '@providers/PostHogClientProvider';
33
import { ExperimentName, ExperimentsConfigStatus } from './types';
44

@@ -15,6 +15,12 @@ interface ExperimentsProviderProps {
1515
children: React.ReactNode;
1616
}
1717

18+
export const useExperimentsContext = (): ExperimentsContext => {
19+
const experiementsContext = useContext(ExperimentsContext);
20+
if (experiementsContext === null) throw new Error('ExperimentsContext not defined');
21+
return experiementsContext;
22+
};
23+
1824
export const ExperimentsProvider = ({ children }: ExperimentsProviderProps): React.ReactElement => {
1925
const postHogClient = usePostHogClientContext();
2026
const [experimentsConfigStatus, setExperimentsConfigStatus] = useState(ExperimentsConfigStatus.IDLE);

apps/browser-extension-wallet/src/providers/ExperimentsProvider/types.ts

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,26 @@ export enum ExperimentsConfigStatus {
66
}
77

88
export enum ExperimentName {
9-
COMBINED_NAME_PASSWORD_ONBOARDING_SCREEN = 'combined-setup-name-password'
9+
COMBINED_NAME_PASSWORD_ONBOARDING_SCREEN = 'combined-setup-name-password',
10+
CREATE_PAPER_WALLET = 'create-paper-wallet',
11+
RESTORE_PAPER_WALLET = 'restore-paper-wallet'
12+
}
13+
14+
interface FeatureFlag {
15+
value: boolean;
16+
default: boolean;
17+
}
18+
19+
type Variant = ReadonlyArray<string>;
20+
21+
interface ExperiementVariant {
22+
variants: Variant;
23+
default: string;
1024
}
1125

1226
export type CombinedSetupNamePasswordVariants = readonly ['control', 'test'];
27+
1328
export type ExperimentsConfig = {
14-
[ExperimentName.COMBINED_NAME_PASSWORD_ONBOARDING_SCREEN]: {
15-
variants: CombinedSetupNamePasswordVariants;
16-
defaultVariant: string;
17-
};
29+
[key in ExperimentName]: FeatureFlag | ExperiementVariant;
1830
};
19-
export type FallbackConfiguration = Record<ExperimentName, 'control'>;
31+
export type FallbackConfiguration = Record<ExperimentName, 'control' | boolean>;

apps/browser-extension-wallet/src/providers/PostHogClientProvider/client/PostHogClient.ts

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import { ExperimentName } from '@providers/ExperimentsProvider/types';
2323
import { Subscription, BehaviorSubject } from 'rxjs';
2424
import { PostHogAction, PostHogProperties } from '@lace/common';
2525

26+
type FeatureFlags = 'create-paper-wallet' | 'restore-paper-wallet';
27+
2628
/**
2729
* PostHog API reference:
2830
* https://posthog.com/docs/libraries/js
@@ -34,7 +36,9 @@ export class PostHogClient<Action extends string = string> {
3436
private hasPostHogInitialized$: BehaviorSubject<boolean>;
3537
private subscription: Subscription;
3638
private initSuccess: Promise<boolean>;
37-
39+
featureFlags: {
40+
[key in FeatureFlags]: string | boolean;
41+
};
3842
constructor(
3943
private chain: Wallet.Cardano.ChainId,
4044
private userIdService: UserIdService,
@@ -82,6 +86,7 @@ export class PostHogClient<Action extends string = string> {
8286
});
8387

8488
this.subscribeToDistinctIdUpdate();
89+
this.loadExperiments();
8590
}
8691

8792
static getInstance(
@@ -118,10 +123,6 @@ export class PostHogClient<Action extends string = string> {
118123
posthog.register({
119124
distinct_id: id
120125
});
121-
122-
if (type === UserTrackingType.Enhanced && !this.hasPostHogInitialized$.value) {
123-
this.loadExperiments();
124-
}
125126
}
126127
});
127128
}
@@ -194,27 +195,38 @@ export class PostHogClient<Action extends string = string> {
194195
posthog.featureFlags.override(flags);
195196
}
196197

197-
async getExperimentVariant(key: ExperimentName): Promise<string> {
198+
async getExperimentVariant(key: ExperimentName): Promise<string | boolean> {
198199
const variant = posthog?.getFeatureFlag(key, {
199200
send_event: false
200201
});
201202
// if we get a type of boolean means that the experiment is not running, so we return the fallback variant
202203
if (typeof variant === 'boolean') {
203-
return experiments[key].defaultVariant;
204+
return experiments[key].default;
204205
}
205206

206207
// if the variant does not exist, we need to check for out cache
207208
if (!variant) {
208209
const backgroundStorage = await this.backgroundServiceUtils.getBackgroundStorage();
209-
return (backgroundStorage?.experimentsConfiguration?.[key] as string) || experiments[key].defaultVariant;
210+
return (backgroundStorage?.experimentsConfiguration?.[key] as string) || experiments[key].default;
210211
}
211212

212213
return variant;
213214
}
214215

216+
isFeatureEnabled(key: ExperimentName | string): boolean {
217+
try {
218+
const isEnabled = posthog.isFeatureEnabled(key);
219+
return isEnabled || false;
220+
} catch {
221+
return false;
222+
}
223+
}
224+
215225
protected loadExperiments(): void {
216226
posthog.onFeatureFlags(async () => {
217-
const postHogExperimentConfiguration = posthog.featureFlags.getFlagVariants();
227+
const postHogExperimentConfiguration: Record<ExperimentName, string | boolean> =
228+
posthog.featureFlags.getFlagVariants();
229+
this.featureFlags = postHogExperimentConfiguration;
218230
const backgroundStorage = await this.backgroundServiceUtils.getBackgroundStorage();
219231
if (!backgroundStorage?.experimentsConfiguration && postHogExperimentConfiguration) {
220232
// save current posthog config in background storage

apps/browser-extension-wallet/src/types/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@ export * from './activity-detail';
88
export * from './dappConnector';
99
export * from './ui';
1010
export * from './side-menu';
11+
export * from './pgp';
12+
export * from './paperWallet';

0 commit comments

Comments
 (0)