From d35778d93ff569148c131971bd5020378cab3336 Mon Sep 17 00:00:00 2001 From: Leonel Gobbi <57540576+lgobbi-atix@users.noreply.github.com> Date: Wed, 11 Oct 2023 04:08:51 -0300 Subject: [PATCH 01/68] chore/Bump cardano-js-sdk (#559) * chore!: bump cardano-js-sdk packages to latest versions * ci: add cardano services urls parameters to build step * Feat/sanchonet network switch support (#612) * feat: update env to support switching to sanchonet * chore: remove old testnet * chore: bump cardano-sdk packages to latest version --------- Co-authored-by: mirceahasegan <105701265+mirceahasegan@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- .github/workflows/e2e-tests-linux.yml | 3 +++ .github/workflows/smoke-tests.yml | 3 +++ apps/browser-extension-wallet/.env.defaults | 8 ++++++-- apps/browser-extension-wallet/.env.example | 9 +++++++-- apps/browser-extension-wallet/src/config.ts | 16 ++++++++++++---- .../src/features/ada-handle/config.ts | 6 ++++-- .../context/AddressBookProvider.tsx | 3 ++- .../__tests__/useDelegationDetails.test.ts | 2 +- .../lib/scripts/migrations/versions/v0_6_0.ts | 6 ++++++ .../src/lib/translations/en.json | 2 +- .../slices/__tests__/wallet-info-slice.test.ts | 2 +- .../src/utils/__tests__/chain.test.ts | 6 ++++-- .../src/utils/chain.ts | 3 +++ .../src/utils/mocks/test-helpers.tsx | 3 ++- .../settings/components/NetworkChoice.tsx | 18 ++++++++++++------ .../test/__mocks__/set-env-vars.js | 5 +++-- apps/browser-extension-wallet/webpack-utils.js | 4 ++-- packages/cardano/src/wallet/types.ts | 4 ++-- packages/common/src/analytics/types.ts | 2 ++ yarn.lock | 6 +++--- 21 files changed, 80 insertions(+), 33 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7aa405e070..69d830848c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,7 +30,7 @@ jobs: run: yarn lint - name: Run unit tests, generate test coverage report env: - AVAILABLE_CHAINS: 'Preprod,Preview,Mainnet' + AVAILABLE_CHAINS: 'Preprod,Preview,Mainnet,Sanchonet' DEFAULT_CHAIN: 'Preprod' run: yarn test:coverage --maxWorkers=2 - name: Upload build diff --git a/.github/workflows/e2e-tests-linux.yml b/.github/workflows/e2e-tests-linux.yml index 71eec19cf4..b3066bb63e 100644 --- a/.github/workflows/e2e-tests-linux.yml +++ b/.github/workflows/e2e-tests-linux.yml @@ -50,6 +50,9 @@ jobs: uses: ./.github/shared/build with: LACE_EXTENSION_KEY: ${{ secrets.MANIFEST_PUBLIC_KEY }} + CARDANO_SERVICES_URL_MAINNET: ${{ secrets.CARDANO_SERVICES_URL_MAINNET }} + CARDANO_SERVICES_URL_PREPROD: ${{ secrets.CARDANO_SERVICES_URL_PREPROD }} + CARDANO_SERVICES_URL_PREVIEW: ${{ secrets.CARDANO_SERVICES_URL_PREVIEW }} - name: Start XVFB run: | Xvfb :99 & diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml index 5c2526ad4b..25d988c045 100644 --- a/.github/workflows/smoke-tests.yml +++ b/.github/workflows/smoke-tests.yml @@ -26,6 +26,9 @@ jobs: uses: ./.github/shared/build with: LACE_EXTENSION_KEY: ${{ secrets.MANIFEST_PUBLIC_KEY }} + CARDANO_SERVICES_URL_MAINNET: ${{ secrets.CARDANO_SERVICES_URL_MAINNET }} + CARDANO_SERVICES_URL_PREPROD: ${{ secrets.CARDANO_SERVICES_URL_PREPROD }} + CARDANO_SERVICES_URL_PREVIEW: ${{ secrets.CARDANO_SERVICES_URL_PREVIEW }} - name: Start XVFB run: | Xvfb :99 & diff --git a/apps/browser-extension-wallet/.env.defaults b/apps/browser-extension-wallet/.env.defaults index fded3830dc..a13fed41ec 100644 --- a/apps/browser-extension-wallet/.env.defaults +++ b/apps/browser-extension-wallet/.env.defaults @@ -4,7 +4,7 @@ DEFAULT_CHAIN=Mainnet WALLET_SYNC_TIMEOUT_IN_SEC=60 WALLET_INTERVAL_IN_SEC=30 DROP_CONSOLE_IN_PRODUCTION=false -AVAILABLE_CHAINS=Preprod,Preview,Mainnet +AVAILABLE_CHAINS=Preprod,Preview,Mainnet,Sanchonet ADA_PRICE_POLLING_IN_SEC=60 TOKEN_PRICE_POLLING_IN_SEC=300 SAVED_PRICE_DURATION_IN_MINUTES=720 @@ -60,17 +60,21 @@ POSTHOG_DEV_TOKEN_PREVIEW=phc_e8SaOOWpXpNE59TnpLumeUjWm4iv024AWjhQqU406jr CARDANO_SERVICES_URL_MAINNET=https://backend.live-mainnet.eks.lw.iog.io CARDANO_SERVICES_URL_PREPROD=https://backend.live-preprod.eks.lw.iog.io CARDANO_SERVICES_URL_PREVIEW=https://backend.live-preview.eks.lw.iog.io +# TODO: update this with a valid sanchonet url +CARDANO_SERVICES_URL_SANCHONET=https://backend.live-preprod.eks.lw.iog.io # Explorer URLs CEXPLORER_URL_MAINNET=https://cexplorer.io CEXPLORER_URL_PREVIEW=https://preview.cexplorer.io CEXPLORER_URL_PREPROD=https://preprod.cexplorer.io -CEXPLORER_URL_SANCHONET=https://sanchonet.cexplorer.io +CEXPLORER_URL_SANCHONET= # ADA Handle URLs ADA_HANDLE_URL_MAINNET=https://api.handle.me ADA_HANDLE_URL_PREVIEW=https://preview.api.handle.me ADA_HANDLE_URL_PREPROD=https://preprod.api.handle.me +# TODO: update this with a valid sanchonet url +ADA_HANDLE_URL_SANCHONET=https://preprod.api.handle.me # Manifest.json LACE_EXTENSION_KEY=gafhhkghbfjjkeiendhlofajokpaflmk diff --git a/apps/browser-extension-wallet/.env.example b/apps/browser-extension-wallet/.env.example index f3897cd232..3af21b976e 100644 --- a/apps/browser-extension-wallet/.env.example +++ b/apps/browser-extension-wallet/.env.example @@ -4,7 +4,7 @@ DEFAULT_CHAIN=Mainnet WALLET_SYNC_TIMEOUT_IN_SEC=60 WALLET_INTERVAL_IN_SEC=30 DROP_CONSOLE_IN_PRODUCTION=false -AVAILABLE_CHAINS=Preprod,Preview,Mainnet +AVAILABLE_CHAINS=Preprod,Preview,Mainnet,Sanchonet ADA_PRICE_POLLING_IN_SEC=60 TOKEN_PRICE_POLLING_IN_SEC=300 SAVED_PRICE_DURATION_IN_MINUTES=720 @@ -61,17 +61,22 @@ POSTHOG_DEV_TOKEN_PREVIEW=dev-preview-token CARDANO_SERVICES_URL_MAINNET=https://backend.live-mainnet.eks.lw.iog.io CARDANO_SERVICES_URL_PREPROD=https://backend.live-preprod.eks.lw.iog.io CARDANO_SERVICES_URL_PREVIEW=https://backend.live-preview.eks.lw.iog.io +# TODO: update this with a valid sanchonet url +CARDANO_SERVICES_URL_SANCHONET=https://backend.live-preprod.eks.lw.iog.io # Explorer URLs CEXPLORER_URL_MAINNET=https://cexplorer.io CEXPLORER_URL_PREVIEW=https://preview.cexplorer.io CEXPLORER_URL_PREPROD=https://preprod.cexplorer.io -CEXPLORER_URL_TESTNET=https://testnet.cexplorer.io +# TODO: update this with a valid sanchonet cexplorer +CEXPLORER_URL_SANCHONET=https://preprod.cexplorer.io # ADA Handle URLs ADA_HANDLE_URL_MAINNET=https://api.handle.me ADA_HANDLE_URL_PREVIEW=https://preview.api.handle.me ADA_HANDLE_URL_PREPROD=https://preprod.api.handle.me +# TODO: update this with a valid sanchonet url +ADA_HANDLE_URL_SANCHONET=https://preprod.api.handle.me # Manifest.json LACE_EXTENSION_KEY=gafhhkghbfjjkeiendhlofajokpaflmk diff --git a/apps/browser-extension-wallet/src/config.ts b/apps/browser-extension-wallet/src/config.ts index 99906fed84..45cbd53464 100644 --- a/apps/browser-extension-wallet/src/config.ts +++ b/apps/browser-extension-wallet/src/config.ts @@ -32,12 +32,18 @@ const envChecks = (chosenChain: Wallet.ChainName): void => { if ( !process.env.CARDANO_SERVICES_URL_MAINNET || !process.env.CARDANO_SERVICES_URL_PREPROD || - !process.env.CARDANO_SERVICES_URL_PREVIEW + !process.env.CARDANO_SERVICES_URL_PREVIEW || + !process.env.CARDANO_SERVICES_URL_SANCHONET ) { throw new Error('env vars not complete'); } - if (!process.env.CEXPLORER_URL_MAINNET || !process.env.CEXPLORER_URL_PREVIEW || !process.env.CEXPLORER_URL_PREPROD) { + if ( + !process.env.CEXPLORER_URL_MAINNET || + !process.env.CEXPLORER_URL_PREVIEW || + !process.env.CEXPLORER_URL_PREPROD || + !process.env.CEXPLORER_URL_SANCHONET + ) { throw new Error('explorer vars not complete'); } @@ -78,12 +84,14 @@ export const config = (): Config => { CARDANO_SERVICES_URLS: { Mainnet: process.env.CARDANO_SERVICES_URL_MAINNET, Preprod: process.env.CARDANO_SERVICES_URL_PREPROD, - Preview: process.env.CARDANO_SERVICES_URL_PREVIEW + Preview: process.env.CARDANO_SERVICES_URL_PREVIEW, + Sanchonet: process.env.CARDANO_SERVICES_URL_SANCHONET }, CEXPLORER_BASE_URL: { Mainnet: `${process.env.CEXPLORER_URL_MAINNET}`, Preprod: `${process.env.CEXPLORER_URL_PREPROD}`, - Preview: `${process.env.CEXPLORER_URL_PREVIEW}` + Preview: `${process.env.CEXPLORER_URL_PREVIEW}`, + Sanchonet: `${process.env.CEXPLORER_URL_SANCHONET}` }, CEXPLORER_URL_PATHS: { Tx: 'tx', diff --git a/apps/browser-extension-wallet/src/features/ada-handle/config.ts b/apps/browser-extension-wallet/src/features/ada-handle/config.ts index 17b0d4af28..0cfabe0985 100644 --- a/apps/browser-extension-wallet/src/features/ada-handle/config.ts +++ b/apps/browser-extension-wallet/src/features/ada-handle/config.ts @@ -3,8 +3,10 @@ import { Wallet } from '@lace/cardano'; export const ADA_HANDLE_POLICY_ID = Wallet.ADA_HANDLE_POLICY_ID; export const isAdaHandleEnabled = process.env.USE_ADA_HANDLE === 'true'; -export const HANDLE_SERVER_URLS: Record, string> = { +export const HANDLE_SERVER_URLS: Record = { [Cardano.NetworkMagics.Mainnet]: 'https://api.handle.me', [Cardano.NetworkMagics.Preprod]: 'https://preprod.api.handle.me', - [Cardano.NetworkMagics.Preview]: 'https://preview.api.handle.me' + [Cardano.NetworkMagics.Preview]: 'https://preview.api.handle.me', + // TODO: update once there's a valid sanchonet version available + [Cardano.NetworkMagics.Sanchonet]: 'https://sanchonet.api.handle.me' }; diff --git a/apps/browser-extension-wallet/src/features/address-book/context/AddressBookProvider.tsx b/apps/browser-extension-wallet/src/features/address-book/context/AddressBookProvider.tsx index 65fea4797a..c1604d2c97 100644 --- a/apps/browser-extension-wallet/src/features/address-book/context/AddressBookProvider.tsx +++ b/apps/browser-extension-wallet/src/features/address-book/context/AddressBookProvider.tsx @@ -18,7 +18,8 @@ export type AddressRecordParams = Pick { diff --git a/apps/browser-extension-wallet/src/hooks/__tests__/useDelegationDetails.test.ts b/apps/browser-extension-wallet/src/hooks/__tests__/useDelegationDetails.test.ts index 2a20ab0bdb..d6e507db6f 100644 --- a/apps/browser-extension-wallet/src/hooks/__tests__/useDelegationDetails.test.ts +++ b/apps/browser-extension-wallet/src/hooks/__tests__/useDelegationDetails.test.ts @@ -29,7 +29,7 @@ jest.mock('../../stores', () => ({ })); describe('Testing useBuildDelegation hook', () => { - process.env.AVAILABLE_CHAINS = process.env.AVAILABLE_CHAINS || 'Mainnet,Preprod,Preview'; + process.env.AVAILABLE_CHAINS = process.env.AVAILABLE_CHAINS || 'Mainnet,Preprod,Preview,Sanchonet'; process.env.DEFAULT_CHAIN = process.env.DEFAULT_CHAIN || 'Preprod'; test('should return use delegation details function', () => { diff --git a/apps/browser-extension-wallet/src/lib/scripts/migrations/versions/v0_6_0.ts b/apps/browser-extension-wallet/src/lib/scripts/migrations/versions/v0_6_0.ts index bef5ea99e9..cbcfe74444 100644 --- a/apps/browser-extension-wallet/src/lib/scripts/migrations/versions/v0_6_0.ts +++ b/apps/browser-extension-wallet/src/lib/scripts/migrations/versions/v0_6_0.ts @@ -94,6 +94,12 @@ export const v0_6_0: Migration = { ? { keyAgentData: newKeyAgentData } : { keyAgentData: { ...newKeyAgentData, chainId: Wallet.Cardano.ChainIds.Mainnet, knownAddresses: [] } + }, + Sanchonet: + keyAgentStoredChainName === 'Sanchonet' + ? { keyAgentData: newKeyAgentData } + : { + keyAgentData: { ...newKeyAgentData, chainId: Wallet.Cardano.ChainIds.Sanchonet, knownAddresses: [] } } }; diff --git a/apps/browser-extension-wallet/src/lib/translations/en.json b/apps/browser-extension-wallet/src/lib/translations/en.json index c0e1e0f1bb..636344952f 100644 --- a/apps/browser-extension-wallet/src/lib/translations/en.json +++ b/apps/browser-extension-wallet/src/lib/translations/en.json @@ -121,7 +121,7 @@ "mainnet": "Mainnet", "preprod": "Preprod", "preview": "Preview", - "legacyTestnet": "Legacy Testnet", + "sanchonet": "Sanchonet", "custom": "Custom", "offline": "Offline", "error": "Your internet connection is not working. You can still navigate the wallet based on the latest connection you had.", diff --git a/apps/browser-extension-wallet/src/stores/slices/__tests__/wallet-info-slice.test.ts b/apps/browser-extension-wallet/src/stores/slices/__tests__/wallet-info-slice.test.ts index 43223b6738..8b4fe6e4d6 100644 --- a/apps/browser-extension-wallet/src/stores/slices/__tests__/wallet-info-slice.test.ts +++ b/apps/browser-extension-wallet/src/stores/slices/__tests__/wallet-info-slice.test.ts @@ -68,7 +68,7 @@ describe('Testing wallet info slice', () => { describe('environment names set correctly', () => { let useWalletInfoHook: UseStore; - process.env.AVAILABLE_CHAINS = process.env.AVAILABLE_CHAINS || 'Mainnet,Preprod,Preview'; + process.env.AVAILABLE_CHAINS = process.env.AVAILABLE_CHAINS || 'Mainnet,Preprod,Preview,Sanchonet'; beforeEach(() => { useWalletInfoHook = create(mockWalletInfoStore); diff --git a/apps/browser-extension-wallet/src/utils/__tests__/chain.test.ts b/apps/browser-extension-wallet/src/utils/__tests__/chain.test.ts index fdbe1e9515..a2fd7e7542 100644 --- a/apps/browser-extension-wallet/src/utils/__tests__/chain.test.ts +++ b/apps/browser-extension-wallet/src/utils/__tests__/chain.test.ts @@ -9,7 +9,8 @@ describe('Testing getBaseUrlForChain function', () => { const CARDANO_SERVICES_URLS = { Mainnet: 'Mainnet', Preprod: 'Preprod', - Preview: 'Preview' + Preview: 'Preview', + Sanchonet: 'Sanchonet' }; beforeEach(() => { @@ -22,12 +23,13 @@ describe('Testing getBaseUrlForChain function', () => { }); test('should return proper url for chainName or throw', async () => { process.env.USE_DEV_ENDPOINTS = 'true'; - const AVAILABLE_CHAINS = ['Mainnet', 'Preprod', 'Preview'] as unknown as Wallet.ChainName[]; + const AVAILABLE_CHAINS = ['Mainnet', 'Preprod', 'Preview', 'Sanchonet'] as unknown as Wallet.ChainName[]; jest.spyOn(config, 'config').mockReturnValue({ CARDANO_SERVICES_URLS, AVAILABLE_CHAINS } as config.Config); expect(getBaseUrlForChain('Mainnet')).toBe(CARDANO_SERVICES_URLS.Mainnet); expect(getBaseUrlForChain('Preprod')).toBe(CARDANO_SERVICES_URLS.Preprod); expect(getBaseUrlForChain('Preview')).toBe(CARDANO_SERVICES_URLS.Preview); + expect(getBaseUrlForChain('Sanchonet')).toBe(CARDANO_SERVICES_URLS.Sanchonet); }); test('should throw in case chain is not suported', async () => { diff --git a/apps/browser-extension-wallet/src/utils/chain.ts b/apps/browser-extension-wallet/src/utils/chain.ts index 87d68ea4fe..f8077af81f 100644 --- a/apps/browser-extension-wallet/src/utils/chain.ts +++ b/apps/browser-extension-wallet/src/utils/chain.ts @@ -14,6 +14,9 @@ export const getBaseUrlForChain = (chainName: Wallet.ChainName): string => { case 'Preview': url = CARDANO_SERVICES_URLS.Preview; break; + case 'Sanchonet': + url = CARDANO_SERVICES_URLS.Sanchonet; + break; default: throw new Error('Incorrect chain supplied'); } diff --git a/apps/browser-extension-wallet/src/utils/mocks/test-helpers.tsx b/apps/browser-extension-wallet/src/utils/mocks/test-helpers.tsx index 3985b40bfb..2e5c487611 100644 --- a/apps/browser-extension-wallet/src/utils/mocks/test-helpers.tsx +++ b/apps/browser-extension-wallet/src/utils/mocks/test-helpers.tsx @@ -43,7 +43,8 @@ export const mockKeyAgentDataTestnet: Wallet.KeyManagement.SerializableKeyAgentD export const mockKeyAgentsByChain: Wallet.KeyAgentsByChain = { Mainnet: { keyAgentData: { ...mockKeyAgentDataTestnet, chainId: Wallet.Cardano.ChainIds.Mainnet } }, Preprod: { keyAgentData: { ...mockKeyAgentDataTestnet, chainId: Wallet.Cardano.ChainIds.Preprod } }, - Preview: { keyAgentData: { ...mockKeyAgentDataTestnet, chainId: Wallet.Cardano.ChainIds.Preview } } + Preview: { keyAgentData: { ...mockKeyAgentDataTestnet, chainId: Wallet.Cardano.ChainIds.Preview } }, + Sanchonet: { keyAgentData: { ...mockKeyAgentDataTestnet, chainId: Wallet.Cardano.ChainIds.Sanchonet } } }; export const mockInMemoryWallet = { diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/settings/components/NetworkChoice.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/settings/components/NetworkChoice.tsx index 288e5e6d4f..6f73351f8a 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/settings/components/NetworkChoice.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/features/settings/components/NetworkChoice.tsx @@ -17,23 +17,27 @@ const { AVAILABLE_CHAINS } = config(); type networkEventSettings = | PostHogAction.SettingsNetworkPreviewClick | PostHogAction.SettingsNetworkPreprodClick - | PostHogAction.SettingsNetworkMainnetClick; + | PostHogAction.SettingsNetworkMainnetClick + | PostHogAction.SettingsNetworkSanchonetClick; type networkEventUserWalletProfile = | PostHogAction.UserWalletProfileNetworkPreviewClick | PostHogAction.UserWalletProfileNetworkPreprodClick - | PostHogAction.UserWalletProfileNetworkMainnetClick; + | PostHogAction.UserWalletProfileNetworkMainnetClick + | PostHogAction.UserWalletProfileNetworkSanchonetClick; -const settingsEventByNetworkName: Partial> = { +const settingsEventByNetworkName: Record = { Mainnet: PostHogAction.SettingsNetworkMainnetClick, Preprod: PostHogAction.SettingsNetworkPreprodClick, - Preview: PostHogAction.SettingsNetworkPreviewClick + Preview: PostHogAction.SettingsNetworkPreviewClick, + Sanchonet: PostHogAction.SettingsNetworkSanchonetClick }; -const walletProfileEventByNetworkName: Partial> = { +const walletProfileEventByNetworkName: Record = { Mainnet: PostHogAction.UserWalletProfileNetworkMainnetClick, Preprod: PostHogAction.UserWalletProfileNetworkPreprodClick, - Preview: PostHogAction.UserWalletProfileNetworkPreviewClick + Preview: PostHogAction.UserWalletProfileNetworkPreviewClick, + Sanchonet: PostHogAction.UserWalletProfileNetworkSanchonetClick }; export const NetworkChoice = ({ section }: { section?: 'settings' | 'wallet-profile' }): React.ReactElement => { @@ -51,6 +55,8 @@ export const NetworkChoice = ({ section }: { section?: 'settings' | 'wallet-prof return t('general.networks.preprod'); case 'Preview': return t('general.networks.preview'); + case 'Sanchonet': + return t('general.networks.sanchonet'); default: return ''; } diff --git a/apps/browser-extension-wallet/test/__mocks__/set-env-vars.js b/apps/browser-extension-wallet/test/__mocks__/set-env-vars.js index ca04f6597b..a28abab98f 100644 --- a/apps/browser-extension-wallet/test/__mocks__/set-env-vars.js +++ b/apps/browser-extension-wallet/test/__mocks__/set-env-vars.js @@ -1,11 +1,12 @@ process.env.CARDANO_SERVICES_URL_PREVIEW = 'https://preview-prod.com'; process.env.CARDANO_SERVICES_URL_PREPROD = 'https://preprod-prod.com'; process.env.CARDANO_SERVICES_URL_MAINNET = 'https://mainnet-url.com'; -process.env.AVAILABLE_CHAINS = 'Preprod,Preview,Mainnet'; +process.env.CARDANO_SERVICES_URL_SANCHONET = 'https://sanchonet-url.com'; +process.env.AVAILABLE_CHAINS = 'Preprod,Preview,Mainnet,Sanchonet'; process.env.CEXPLORER_URL_MAINNET = 'https://cexplorer.io'; process.env.CEXPLORER_URL_PREVIEW = 'https://preview.cexplorer.io'; process.env.CEXPLORER_URL_PREPROD = 'https://preprod.cexplorer.io'; -process.env.CEXPLORER_URL_TESTNET = 'https://testnet.cexplorer.io'; +process.env.CEXPLORER_URL_SANCHONET = 'https://sanchonet.cexplorer.io'; process.env.USE_HIDE_MY_BALANCE = 'true'; process.env.USE_POSTHOG_ANALYTICS = 'true'; process.env.USE_POSTHOG_ANALYTICS_FOR_OPTED_OUT = 'false'; diff --git a/apps/browser-extension-wallet/webpack-utils.js b/apps/browser-extension-wallet/webpack-utils.js index 6e17477310..a84c27cc2a 100644 --- a/apps/browser-extension-wallet/webpack-utils.js +++ b/apps/browser-extension-wallet/webpack-utils.js @@ -9,11 +9,11 @@ const transformManifest = (content, mode) => { manifest.content_security_policy.extension_pages = manifest.content_security_policy.extension_pages .replace( '$CARDANO_SERVICES_URLS', - `${process.env.CARDANO_SERVICES_URL_MAINNET} ${process.env.CARDANO_SERVICES_URL_PREPROD} ${process.env.CARDANO_SERVICES_URL_PREVIEW}` + `${process.env.CARDANO_SERVICES_URL_MAINNET} ${process.env.CARDANO_SERVICES_URL_PREPROD} ${process.env.CARDANO_SERVICES_URL_PREVIEW} ${process.env.CARDANO_SERVICES_URL_SANCHONET}` ) .replace( '$ADA_HANDLE_URLS', - `${process.env.ADA_HANDLE_URL_MAINNET} ${process.env.ADA_HANDLE_URL_PREPROD} ${process.env.ADA_HANDLE_URL_PREVIEW}` + `${process.env.ADA_HANDLE_URL_MAINNET} ${process.env.ADA_HANDLE_URL_PREPROD} ${process.env.ADA_HANDLE_URL_PREVIEW} ${process.env.ADA_HANDLE_URL_SANCHONET}` ) .replace('$LOCALHOST_DEFAULT_SRC', mode === 'development' ? 'http://localhost:3000' : '') .replace('$LOCALHOST_SCRIPT_SRC', mode === 'development' ? 'http://localhost:3000' : '') diff --git a/packages/cardano/src/wallet/types.ts b/packages/cardano/src/wallet/types.ts index 75707709dd..4e9fda7b3e 100644 --- a/packages/cardano/src/wallet/types.ts +++ b/packages/cardano/src/wallet/types.ts @@ -46,8 +46,8 @@ export type Cip30SignTxAssetItem = { export enum WalletManagerProviderTypes { CARDANO_SERVICES_PROVIDER = 'cardano-services-provider' } -// Exclude Sanchonet until in main branch -export type ChainName = keyof Omit; + +export type ChainName = keyof typeof Cardano.ChainIds; export interface CreateHardwareWalletArgs { deviceConnection: DeviceConnection; diff --git a/packages/common/src/analytics/types.ts b/packages/common/src/analytics/types.ts index e0cac05cd9..ae5a741c22 100644 --- a/packages/common/src/analytics/types.ts +++ b/packages/common/src/analytics/types.ts @@ -134,6 +134,7 @@ export enum PostHogAction { SettingsNetworkPreviewClick = 'settings | network | preview | click', SettingsNetworkPreprodClick = 'settings | network | preprod | click', SettingsNetworkMainnetClick = 'settings | network | mainnet | click', + SettingsNetworkSanchonetClick = 'settings | network | sanchonet | click', SettingsNetworkXClick = 'settings | network | x | click', SettingsAuthorizedDappsClick = 'settings | authorized dapps | click', SettingsAuthorizedDappsTrashBinIconClick = 'settings | authorized dapps | trash bin icon | click', @@ -181,6 +182,7 @@ export enum PostHogAction { UserWalletProfileNetworkPreviewClick = 'user/wallet profile | network | preview | click', UserWalletProfileNetworkPreprodClick = 'user/wallet profile | network | preprod | click', UserWalletProfileNetworkMainnetClick = 'user/wallet profile | network | mainnet | click', + UserWalletProfileNetworkSanchonetClick = 'user/wallet profile | network | sanchonet | click', UserWalletProfileLockWalletClick = 'user/wallet profile | lock wallet | click', // Lace Logo WalletLaceClick = 'wallet | lace | click', diff --git a/yarn.lock b/yarn.lock index 89c685363e..7e8167c66a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6135,9 +6135,9 @@ __metadata: linkType: hard "@dcspark/cardano-multiplatform-lib-nodejs@npm:^3.1.1": - version: 3.1.1 - resolution: "@dcspark/cardano-multiplatform-lib-nodejs@npm:3.1.1" - checksum: e408d338fe199a29f0901364b77d284cc9455c0adc9041aadd8932b068a929040fda823a07eb80e5ca0df1396f148295cea3ea089de75339649141b23bdc00e3 + version: 3.1.2 + resolution: "@dcspark/cardano-multiplatform-lib-nodejs@npm:3.1.2" + checksum: b9afbe180758b8b564b2413b052d03f5225884d2425c23a33c8a65275c0a22de80f5a3e9ddda52a8e6a18072f7f898b5575aab70295e5216c3d3ae5e4c2daab0 languageName: node linkType: hard From 7914ecfc731c32125de428c126e26f343a9deeac Mon Sep 17 00:00:00 2001 From: Lucas Date: Tue, 24 Oct 2023 13:10:36 -0300 Subject: [PATCH 02/68] [LW-7983] [LW-7984] dRep confirmation screens (#554) * refactor: move logic out of component * fix: file path * feat(core): setup storybook * refactor(ui): extract insufficient funds warning component * feat(core): add lace/ui package * feat(core): create ConfirmDRepRegistration component * feat(extension): install @lace/ui * refactor: move functions to utils * fix: type * refactor: types file * feat: check for DRep transaction * feat: integrate confirm drep registration component * refactor(core): use Metadata component * refactor(core): use flexbox * refactor(core): remove insufficient funds message * refactor(core): create ConfirmDRepRetirement * refactor: remove insufficient funds warning; break down main component * feat: add certificate data * feat: add drep retirement container * fix: condition * fix: type * fix: style * fix: check for anchor * fix: check for undefined certificates * fix: merge conflict * fix: type error * feat: temp env changes * feat(extension): use consistent naming of DRep ID * feat(extension): show anchor metadata if exists * feat(extension): convert user facing DRepID to bech32 * fix: add cardano symbol to deposit amount * chore(core): downgrade storybook to v6 * fix: update lock file * fix: styles and text * fix: storybook build * fix: storybook build * feat: check retired drep key * fix: check retired drep key --------- Co-authored-by: Renan Ferreira Co-authored-by: Michael Chappell <7581002+mchappell@users.noreply.github.com> --- .github/workflows/e2e-tests-linux.yml | 6 +- .github/workflows/smoke-tests.yml | 6 +- apps/browser-extension-wallet/package.json | 1 + .../src/dapp-connector.tsx | 5 +- .../__tests__/ConfirmTransaction.test.tsx | 2 +- .../ConfirmDRepRegistrationContainer.tsx | 46 + .../ConfirmDRepRetirementContainer.tsx | 47 + .../ConfirmTransaction.module.scss | 6 +- .../ConfirmTransaction.tsx | 71 + .../ConfirmTransactionContent.tsx | 27 + .../DappTransactionContainer.tsx | 59 + .../components/confirm-transaction/hooks.ts | 175 + .../components/confirm-transaction/types.ts | 5 + .../components/confirm-transaction/utils.ts | 144 + .../src/features/dapp/config/ViewsConfig.tsx | 2 +- .../src/lib/translations/en.json | 16 + .../src/providers/UIThemeProvider/context.tsx | 17 + .../src/providers/UIThemeProvider/index.ts | 1 + packages/core/.babelrc.json | 18 + packages/core/.storybook/main.js | 51 + packages/core/.storybook/preview-head.html | 6 + packages/core/.storybook/preview.js | 26 + packages/core/.storybook/theme.scss | 36 + packages/core/package.json | 22 + packages/core/src/index.ts | 2 + .../ConfirmDRepRegistration.stories.ts | 50 + .../ConfirmDRepRegistration.tsx | 57 + .../ConfirmDRepRegistration/index.ts | 1 + .../ConfirmDRepRetirement.stories.ts | 46 + .../ConfirmDRepRetirement.tsx | 44 + .../components/ConfirmDRepRetirement/index.ts | 1 + .../DappTransaction.stories.ts | 60 + .../InsufficientFundsWarning.module.scss | 23 + .../InsufficientFundsWarning.tsx | 15 + .../InsufficientFundsWarning/index.ts | 1 + yarn.lock | 3241 ++++++++++++++++- 36 files changed, 4238 insertions(+), 98 deletions(-) create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmDRepRegistrationContainer.tsx create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmDRepRetirementContainer.tsx rename apps/browser-extension-wallet/src/features/dapp/components/{ => confirm-transaction}/ConfirmTransaction.module.scss (73%) create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.tsx create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransactionContent.tsx create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/DappTransactionContainer.tsx create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/hooks.ts create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/types.ts create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/utils.ts create mode 100644 apps/browser-extension-wallet/src/providers/UIThemeProvider/context.tsx create mode 100644 apps/browser-extension-wallet/src/providers/UIThemeProvider/index.ts create mode 100644 packages/core/.babelrc.json create mode 100644 packages/core/.storybook/main.js create mode 100644 packages/core/.storybook/preview-head.html create mode 100644 packages/core/.storybook/preview.js create mode 100644 packages/core/.storybook/theme.scss create mode 100644 packages/core/src/ui/components/ConfirmDRepRegistration/ConfirmDRepRegistration.stories.ts create mode 100644 packages/core/src/ui/components/ConfirmDRepRegistration/ConfirmDRepRegistration.tsx create mode 100644 packages/core/src/ui/components/ConfirmDRepRegistration/index.ts create mode 100644 packages/core/src/ui/components/ConfirmDRepRetirement/ConfirmDRepRetirement.stories.ts create mode 100644 packages/core/src/ui/components/ConfirmDRepRetirement/ConfirmDRepRetirement.tsx create mode 100644 packages/core/src/ui/components/ConfirmDRepRetirement/index.ts create mode 100644 packages/core/src/ui/components/DappTransaction/DappTransaction.stories.ts create mode 100644 packages/core/src/ui/components/InsufficientFundsWarning/InsufficientFundsWarning.module.scss create mode 100644 packages/core/src/ui/components/InsufficientFundsWarning/InsufficientFundsWarning.tsx create mode 100644 packages/core/src/ui/components/InsufficientFundsWarning/index.ts diff --git a/.github/workflows/e2e-tests-linux.yml b/.github/workflows/e2e-tests-linux.yml index b3066bb63e..47f32d5fc3 100644 --- a/.github/workflows/e2e-tests-linux.yml +++ b/.github/workflows/e2e-tests-linux.yml @@ -50,9 +50,9 @@ jobs: uses: ./.github/shared/build with: LACE_EXTENSION_KEY: ${{ secrets.MANIFEST_PUBLIC_KEY }} - CARDANO_SERVICES_URL_MAINNET: ${{ secrets.CARDANO_SERVICES_URL_MAINNET }} - CARDANO_SERVICES_URL_PREPROD: ${{ secrets.CARDANO_SERVICES_URL_PREPROD }} - CARDANO_SERVICES_URL_PREVIEW: ${{ secrets.CARDANO_SERVICES_URL_PREVIEW }} + CARDANO_SERVICES_URL_MAINNET: ${{ secrets.CARDANO_SERVICES_DEV_URL_MAINNET }} + CARDANO_SERVICES_URL_PREPROD: ${{ secrets.CARDANO_SERVICES_DEV_URL_PREPROD }} + CARDANO_SERVICES_URL_PREVIEW: ${{ secrets.CARDANO_SERVICES_DEV_URL_PREVIEW }} - name: Start XVFB run: | Xvfb :99 & diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml index 25d988c045..5066293a93 100644 --- a/.github/workflows/smoke-tests.yml +++ b/.github/workflows/smoke-tests.yml @@ -26,9 +26,9 @@ jobs: uses: ./.github/shared/build with: LACE_EXTENSION_KEY: ${{ secrets.MANIFEST_PUBLIC_KEY }} - CARDANO_SERVICES_URL_MAINNET: ${{ secrets.CARDANO_SERVICES_URL_MAINNET }} - CARDANO_SERVICES_URL_PREPROD: ${{ secrets.CARDANO_SERVICES_URL_PREPROD }} - CARDANO_SERVICES_URL_PREVIEW: ${{ secrets.CARDANO_SERVICES_URL_PREVIEW }} + CARDANO_SERVICES_URL_MAINNET: ${{ secrets.CARDANO_SERVICES_DEV_URL_MAINNET }} + CARDANO_SERVICES_URL_PREPROD: ${{ secrets.CARDANO_SERVICES_DEV_URL_PREPROD }} + CARDANO_SERVICES_URL_PREVIEW: ${{ secrets.CARDANO_SERVICES_DEV_URL_PREVIEW }} - name: Start XVFB run: | Xvfb :99 & diff --git a/apps/browser-extension-wallet/package.json b/apps/browser-extension-wallet/package.json index a9a4dc5fbc..3b931c044d 100644 --- a/apps/browser-extension-wallet/package.json +++ b/apps/browser-extension-wallet/package.json @@ -54,6 +54,7 @@ "@lace/common": "0.1.0", "@lace/core": "0.1.0", "@lace/staking": "0.1.0", + "@lace/ui": "^0.1.0", "@react-rxjs/core": "^0.9.8", "@react-rxjs/utils": "^0.9.5", "@vespaiach/axios-fetch-adapter": "^0.3.0", diff --git a/apps/browser-extension-wallet/src/dapp-connector.tsx b/apps/browser-extension-wallet/src/dapp-connector.tsx index a8bbd35dbd..e9d96606d2 100644 --- a/apps/browser-extension-wallet/src/dapp-connector.tsx +++ b/apps/browser-extension-wallet/src/dapp-connector.tsx @@ -14,6 +14,7 @@ import { } 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 { PostHogClientProvider } from '@providers/PostHogClientProvider'; @@ -34,7 +35,9 @@ const App = (): React.ReactElement => ( - + + + diff --git a/apps/browser-extension-wallet/src/features/dapp/components/__tests__/ConfirmTransaction.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/__tests__/ConfirmTransaction.test.tsx index c82c6724d6..5bf5130d4c 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/__tests__/ConfirmTransaction.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/__tests__/ConfirmTransaction.test.tsx @@ -9,7 +9,7 @@ const mockConsumeRemoteApi = jest.fn().mockReturnValue({ const mockCreateTxInspector = jest.fn().mockReturnValue(() => ({ minted: [] as any, burned: [] as any })); import * as React from 'react'; import { cleanup, render, waitFor } from '@testing-library/react'; -import { ConfirmTransaction } from '../ConfirmTransaction'; +import { ConfirmTransaction } from '../confirm-transaction/ConfirmTransaction'; import '@testing-library/jest-dom'; import { I18nextProvider } from 'react-i18next'; import { StoreProvider } from '@src/stores'; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmDRepRegistrationContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmDRepRegistrationContainer.tsx new file mode 100644 index 0000000000..f9a32a49c9 --- /dev/null +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmDRepRegistrationContainer.tsx @@ -0,0 +1,46 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { ConfirmDRepRegistration } from '@lace/core'; +import { SignTxData } from './types'; +import { dRepRegistrationInspector, drepIDasBech32FromHash } from './utils'; +import { Wallet } from '@lace/cardano'; +import { useWalletStore } from '@src/stores'; + +interface Props { + signTxData: SignTxData; + errorMessage?: string; +} + +export const ConfirmDRepRegistrationContainer = ({ signTxData, errorMessage }: Props): React.ReactElement => { + const { t } = useTranslation(); + const certificate = dRepRegistrationInspector(signTxData.tx); + const { + walletUI: { cardanoCoin } + } = useWalletStore(); + + const depositPaidWithCardanoSymbol = `${Wallet.util.lovelacesToAdaString(certificate.deposit.toString())} ${ + cardanoCoin.symbol + }`; + + return ( + + ); +}; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmDRepRetirementContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmDRepRetirementContainer.tsx new file mode 100644 index 0000000000..1bb2cb14ab --- /dev/null +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmDRepRetirementContainer.tsx @@ -0,0 +1,47 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { ConfirmDRepRetirement } from '@lace/core'; +import { SignTxData } from './types'; +import { dRepRetirementInspector, drepIDasBech32FromHash, getOwnRetirementMessageKey } from './utils'; +import { Wallet } from '@lace/cardano'; +import { useWalletStore } from '@src/stores'; +import { useIsOwnPubDRepKey } from './hooks'; + +interface Props { + signTxData: SignTxData; + errorMessage?: string; +} + +export const ConfirmDRepRetirementContainer = ({ signTxData, errorMessage }: Props): React.ReactElement => { + const { t } = useTranslation(); + const certificate = dRepRetirementInspector(signTxData.tx); + const { + walletUI: { cardanoCoin }, + inMemoryWallet + } = useWalletStore(); + + const depositPaidWithCardanoSymbol = `${Wallet.util.lovelacesToAdaString(certificate.deposit.toString())} ${ + cardanoCoin.symbol + }`; + + const isOwnRetirement = useIsOwnPubDRepKey(inMemoryWallet.getPubDRepKey, certificate.dRepCredential.hash); + const ownRetirementMessageKey = getOwnRetirementMessageKey(isOwnRetirement); + + return ( + + ); +}; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/ConfirmTransaction.module.scss b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.module.scss similarity index 73% rename from apps/browser-extension-wallet/src/features/dapp/components/ConfirmTransaction.module.scss rename to apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.module.scss index cfac6eebe5..9a02141f0c 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/ConfirmTransaction.module.scss +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.module.scss @@ -1,5 +1,5 @@ -@import '../../../../../../packages/common/src/ui/styles/theme.scss'; -@import '../../../../src/styles/rules/flex.scss'; +@import '../../../../../../../packages/common/src/ui/styles/theme.scss'; +@import '../../../../../src/styles/rules/flex.scss'; .actions { display: flex; @@ -20,7 +20,7 @@ gap: size_unit(1); padding: size_unit(2) size_unit(3) size_unit(2) size_unit(3); border-top: 2px solid var(--light-mode-light-grey-plus, var(--dark-mode-mid-grey)); - margin: 0 size_unit(-3) size_unit(-2) size_unit(-3); + margin: size_unit(4) size_unit(-3) size_unit(-2) size_unit(-3); position: sticky; bottom: 0; .actionBtn { diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.tsx new file mode 100644 index 0000000000..ee3ce584af --- /dev/null +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.tsx @@ -0,0 +1,71 @@ +import React from 'react'; +import { Button } from '@lace/common'; +import { useTranslation } from 'react-i18next'; +import { Layout } from '../Layout'; +import { useViewsFlowContext } from '@providers/ViewFlowProvider'; +import styles from './ConfirmTransaction.module.scss'; +import { Wallet } from '@lace/cardano'; +import { useWalletStore } from '@stores'; +import { useDisallowSignTx, useSignWithHardwareWallet, useSignTxData, useOnBeforeUnload } from './hooks'; +import { consumeRemoteApi, RemoteApiPropertyType } from '@cardano-sdk/web-extension'; +import { DappDataService } from '@lib/scripts/types'; +import { DAPP_CHANNELS } from '@src/utils/constants'; +import { runtime } from 'webextension-polyfill'; +import { getTitleKey, getTxType } from './utils'; +import { ConfirmTransactionContent } from './ConfirmTransactionContent'; + +const dappDataApi = consumeRemoteApi>( + { + baseChannel: DAPP_CHANNELS.dappData, + properties: { + getSignTxData: RemoteApiPropertyType.MethodReturningPromise + } + }, + { logger: console, runtime } +); + +export const ConfirmTransaction = (): React.ReactElement => { + const { t } = useTranslation(); + const { + utils: { setNextView } + } = useViewsFlowContext(); + const { getKeyAgentType } = useWalletStore(); + const { signTxData, errorMessage } = useSignTxData(dappDataApi.getSignTxData); + const keyAgentType = getKeyAgentType(); + const isUsingHardwareWallet = keyAgentType !== Wallet.KeyManagement.KeyAgentType.InMemory; + const disallowSignTx = useDisallowSignTx(); + const { isConfirmingTx, signWithHardwareWallet } = useSignWithHardwareWallet(); + const txType = signTxData ? getTxType(signTxData.tx) : undefined; + const title = txType ? t(getTitleKey(txType)) : ''; + + useOnBeforeUnload(disallowSignTx); + + return ( + + +
+ + +
+
+ ); +}; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransactionContent.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransactionContent.tsx new file mode 100644 index 0000000000..8cee30714e --- /dev/null +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransactionContent.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import { Skeleton } from 'antd'; +import { ConfirmDRepRegistrationContainer } from './ConfirmDRepRegistrationContainer'; +import { DappTransactionContainer } from './DappTransactionContainer'; +import { TxType } from './utils'; +import { SignTxData } from './types'; +import { ConfirmDRepRetirementContainer } from './ConfirmDRepRetirementContainer'; + +interface Props { + txType?: TxType; + signTxData?: SignTxData; + errorMessage?: string; +} + +export const ConfirmTransactionContent = ({ txType, signTxData, errorMessage }: Props): React.ReactElement => { + if (!signTxData) { + return ; + } + if (txType === TxType.DRepRegistration) { + return ; + } + if (txType === TxType.DRepRetirement) { + return ; + } + + return ; +}; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/DappTransactionContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/DappTransactionContainer.tsx new file mode 100644 index 0000000000..75b91c4412 --- /dev/null +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/DappTransactionContainer.tsx @@ -0,0 +1,59 @@ +import React from 'react'; +import { useObservable } from '@lace/common'; +import { useTranslation } from 'react-i18next'; +import { useWalletStore } from '@stores'; +import { useCreateAssetList, useTxSummary } from './hooks'; +import { Skeleton } from 'antd'; +import { DappTransaction } from '@lace/core'; +import { TokenInfo } from '@src/utils/get-assets-information'; +import { useAddressBookContext, withAddressBookContext } from '@src/features/address-book/context'; +import { AddressListType } from '@src/views/browser-view/features/activity'; +import { SignTxData } from './types'; + +interface Props { + signTxData: SignTxData; + errorMessage?: string; +} + +export const DappTransactionContainer = withAddressBookContext( + ({ signTxData, errorMessage }: Props): React.ReactElement => { + const { t } = useTranslation(); + const { + walletInfo, + inMemoryWallet, + blockchainProvider: { assetProvider } + } = useWalletStore(); + const { list: addressList } = useAddressBookContext() as { list: AddressListType[] }; + const assets = useObservable(inMemoryWallet.assetInfo$); + const createAssetList = useCreateAssetList({ + outputs: signTxData.tx.body.outputs, + assets, + assetProvider + }); + const txSummary = useTxSummary({ + addressList, + createAssetList, + tx: signTxData.tx, + walletInfo + }); + + if (!txSummary) { + return ; + } + + return ( + + ); + } +); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/hooks.ts b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/hooks.ts new file mode 100644 index 0000000000..df53acc898 --- /dev/null +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/hooks.ts @@ -0,0 +1,175 @@ +import { AssetProvider } from '@cardano-sdk/core'; +import { useRedirection } from '@hooks'; +import { Wallet } from '@lace/cardano'; +import { dAppRoutePaths } from '@routes'; +import { CardanoTxOut, WalletInfo } from '@src/types'; +import { TokenInfo, getAssetsInformation } from '@src/utils/get-assets-information'; +import { useCallback, useEffect, useMemo, useState } from 'react'; +import * as HardwareLedger from '@cardano-sdk/hardware-ledger'; +import { TxType, allowSignTx, pubDRepKeyToHash, disallowSignTx, getTransactionAssetsId, getTxType } from './utils'; +import { AddressListType } from '@src/views/browser-view/features/activity'; +import { GetSignTxData, SignTxData } from './types'; + +export const useCreateAssetList = ({ + assets, + outputs, + assetProvider +}: { + assets?: TokenInfo; + outputs?: CardanoTxOut[]; + assetProvider: AssetProvider; +}): ((txAssets: Wallet.Cardano.TokenMap) => Wallet.Cip30SignTxAssetItem[]) => { + const [assetsInfo, setAssetsInfo] = useState(); + const assetIds = useMemo(() => outputs && getTransactionAssetsId(outputs), [outputs]); + + useEffect(() => { + if (assetIds?.length > 0) { + getAssetsInformation(assetIds, assets, { + assetProvider, + extraData: { nftMetadata: true, tokenMetadata: true } + }) + .then((result) => setAssetsInfo(result)) + .catch((error) => { + console.error(error); + }); + } + }, [assetIds, assetProvider, assets]); + + return useCallback( + (txAssets: Wallet.Cardano.TokenMap) => { + if (!assetsInfo) return []; + const assetList: Wallet.Cip30SignTxAssetItem[] = []; + // eslint-disable-next-line unicorn/no-array-for-each + txAssets.forEach(async (value, key) => { + const walletAsset = assets.get(key) || assetsInfo?.get(key); + assetList.push({ + name: walletAsset.name.toString() || key.toString(), + ticker: walletAsset.tokenMetadata?.ticker || walletAsset.nftMetadata?.name, + amount: Wallet.util.calculateAssetBalance(value, walletAsset) + }); + }); + return assetList; + }, + [assets, assetsInfo] + ); +}; +export const useSignTxData = (getSignTxData: GetSignTxData): { signTxData?: SignTxData; errorMessage?: string } => { + const [signTxData, setSignTxData] = useState<{ dappInfo: Wallet.DappInfo; tx: Wallet.Cardano.Tx }>(); + const [errorMessage, setErrorMessage] = useState(); + + useEffect(() => { + getSignTxData() + .then((result) => { + setSignTxData(result); + }) + .catch((error) => { + setErrorMessage(error); + console.error(error); + }); + }, [getSignTxData, setSignTxData, setErrorMessage]); + + return { signTxData, errorMessage }; +}; + +export const useDisallowSignTx = (): ((close?: boolean) => void) => useCallback(disallowSignTx, []); + +export const useAllowSignTx = (): (() => void) => useCallback(allowSignTx, []); + +export const useSignWithHardwareWallet = (): { + signWithHardwareWallet: () => Promise; + isConfirmingTx: boolean; +} => { + const allow = useAllowSignTx(); + const disallow = useDisallowSignTx(); + const redirectToSignFailure = useRedirection>(dAppRoutePaths.dappTxSignFailure); + const [isConfirmingTx, setIsConfirmingTx] = useState(); + const signWithHardwareWallet = async () => { + setIsConfirmingTx(true); + try { + await HardwareLedger.LedgerKeyAgent.establishDeviceConnection(Wallet.KeyManagement.CommunicationType.Web); + allow(); + } catch (error) { + console.error('error', error); + disallow(false); + redirectToSignFailure({}); + } + }; + + return { isConfirmingTx, signWithHardwareWallet }; +}; + +export const useTxSummary = ({ + tx, + addressList, + walletInfo, + createAssetList +}: { + addressList: AddressListType[]; + walletInfo: WalletInfo; + tx: Wallet.Cardano.Tx; + createAssetList: (txAssets: Wallet.Cardano.TokenMap) => Wallet.Cip30SignTxAssetItem[]; +}): Wallet.Cip30SignTxSummary | undefined => + useMemo((): Wallet.Cip30SignTxSummary | undefined => { + const txType = getTxType(tx); + + const addressToNameMap = new Map( + addressList?.map((item: AddressListType) => [item.address, item.name]) + ); + + const externalOutputs = tx.body.outputs.filter((output) => { + if (txType === TxType.Send) { + return walletInfo.addresses.every((addr) => output.address !== addr.address); + } + return true; + }); + + // eslint-disable-next-line unicorn/no-array-reduce + const txSummaryOutputs: Wallet.Cip30SignTxSummary['outputs'] = externalOutputs.reduce((acc, txOut) => { + // Don't show withdrawl tx's etc + if (txOut.address.toString() === walletInfo.addresses[0].address.toString()) return acc; + + return [ + ...acc, + { + coins: Wallet.util.lovelacesToAdaString(txOut.value.coins.toString()), + recipient: addressToNameMap?.get(txOut.address.toString()) || txOut.address.toString(), + ...(txOut.value.assets?.size > 0 && { assets: createAssetList(txOut.value.assets) }) + } + ]; + }, []); + + return { + fee: Wallet.util.lovelacesToAdaString(tx.body.fee.toString()), + outputs: txSummaryOutputs, + type: txType.toString() as 'Send' | 'Mint' | 'Burn' + }; + }, [tx, walletInfo.addresses, createAssetList, addressList]); + +export const useOnBeforeUnload = (callBack: () => void): void => { + useEffect(() => { + window.addEventListener('beforeunload', callBack); + return () => { + window.removeEventListener('beforeunload', callBack); + }; + }, [callBack]); +}; + +export const useIsOwnPubDRepKey = ( + getOwnPubDRepKey: () => Promise, + drepHash: Wallet.Crypto.Hash28ByteBase16 +): boolean => { + const [isOwnDRepKey, setIsOwnDRepKey] = useState(); + + useEffect(() => { + const get = async () => { + const ownPubDRepKey = await getOwnPubDRepKey(); + const ownDRepKeyHash = await pubDRepKeyToHash(ownPubDRepKey); + + setIsOwnDRepKey(drepHash === ownDRepKeyHash); + }; + + get(); + }, [getOwnPubDRepKey, drepHash]); + + return isOwnDRepKey; +}; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/types.ts b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/types.ts new file mode 100644 index 0000000000..b6c1da3998 --- /dev/null +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/types.ts @@ -0,0 +1,5 @@ +import { Wallet } from '@lace/cardano'; +import { DappDataService } from '@lib/scripts/types'; + +export type GetSignTxData = DappDataService['getSignTxData']; +export type SignTxData = { dappInfo: Wallet.DappInfo; tx: Wallet.Cardano.Tx }; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/utils.ts b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/utils.ts new file mode 100644 index 0000000000..6b6611499d --- /dev/null +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/utils.ts @@ -0,0 +1,144 @@ +import { Wallet } from '@lace/cardano'; +import { assetsBurnedInspector, assetsMintedInspector, createTxInspector } from '@cardano-sdk/core'; +import { CardanoTxOut } from '@src/types'; +import { RemoteApiPropertyType, exposeApi } from '@cardano-sdk/web-extension'; +import { UserPromptService } from '@lib/scripts/background/services'; +import { DAPP_CHANNELS } from '@src/utils/constants'; +import { runtime } from 'webextension-polyfill'; +import { of } from 'rxjs'; +import { sectionTitle, DAPP_VIEWS } from '../../config'; + +const DAPP_TOAST_DURATION = 50; + +export enum TxType { + Send = 'Send', + Mint = 'Mint', + Burn = 'Burn', + DRepRegistration = 'DRepRegistration', + DRepRetirement = 'DRepRetirement' +} + +export const getTitleKey = (txType: TxType): string => { + if (txType === TxType.DRepRegistration) { + return 'core.drepRegistration.title'; + } + + if (txType === TxType.DRepRetirement) { + return 'core.drepRetirement.title'; + } + + return sectionTitle[DAPP_VIEWS.CONFIRM_TX]; +}; + +export const disallowSignTx = (close = false): void => { + exposeApi>( + { + api$: of({ + async allowSignTx(): Promise { + return Promise.reject(); + } + }), + baseChannel: DAPP_CHANNELS.userPrompt, + properties: { allowSignTx: RemoteApiPropertyType.MethodReturningPromise } + }, + { logger: console, runtime } + ); + close && setTimeout(() => window.close(), DAPP_TOAST_DURATION); +}; + +export const allowSignTx = (): void => { + exposeApi>( + { + api$: of({ + async allowSignTx(): Promise { + return Promise.resolve(true); + } + }), + baseChannel: DAPP_CHANNELS.userPrompt, + properties: { allowSignTx: RemoteApiPropertyType.MethodReturningPromise } + }, + { logger: console, runtime } + ); +}; + +export const getTransactionAssetsId = (outputs: CardanoTxOut[]): Wallet.Cardano.AssetId[] => { + const assetIds: Wallet.Cardano.AssetId[] = []; + const assetMaps = outputs.map((output) => output.value.assets); + for (const asset of assetMaps) { + if (asset) { + for (const id of asset.keys()) { + !assetIds.includes(id) && assetIds.push(id); + } + } + } + return assetIds; +}; + +const isDRepRegistrationCertificate = (type: Wallet.Cardano.CertificateType) => + type === Wallet.Cardano.CertificateType.RegisterDelegateRepresentative; + +const isDRepRetirementCertificate = (type: Wallet.Cardano.CertificateType) => + type === Wallet.Cardano.CertificateType.UnregisterDelegateRepresentative; + +export const dRepRegistrationInspector = ( + tx: Wallet.Cardano.Tx +): Wallet.Cardano.RegisterDelegateRepresentativeCertificate | undefined => + tx?.body?.certificates?.find(({ __typename }) => isDRepRegistrationCertificate(__typename)) as + | Wallet.Cardano.RegisterDelegateRepresentativeCertificate + | undefined; + +export const dRepRetirementInspector = ( + tx: Wallet.Cardano.Tx +): Wallet.Cardano.UnRegisterDelegateRepresentativeCertificate | undefined => + tx?.body?.certificates?.find(({ __typename }) => isDRepRetirementCertificate(__typename)) as + | Wallet.Cardano.UnRegisterDelegateRepresentativeCertificate + | undefined; + +export const getTxType = (tx: Wallet.Cardano.Tx): TxType => { + const inspector = createTxInspector({ + minted: assetsMintedInspector, + burned: assetsBurnedInspector, + dRepRegistration: dRepRegistrationInspector, + dRepRetirement: dRepRetirementInspector + }); + + const { minted, burned, dRepRegistration, dRepRetirement } = inspector(tx as Wallet.Cardano.HydratedTx); + const isMintTransaction = minted.length > 0; + const isBurnTransaction = burned.length > 0; + + if (isMintTransaction) { + return TxType.Mint; + } + + if (isBurnTransaction) { + return TxType.Burn; + } + + if (dRepRegistration) { + return TxType.DRepRegistration; + } + + if (dRepRetirement) { + return TxType.DRepRetirement; + } + + return TxType.Send; +}; + +export const drepIDasBech32FromHash = (value: Wallet.Crypto.Hash28ByteBase16): Wallet.Cardano.DRepID => + Wallet.Cardano.DRepID(Wallet.HexBlob.toTypedBech32('drep', Wallet.HexBlob(value))); + +export const pubDRepKeyToHash = async ( + pubDRepKeyHex: Wallet.Crypto.Ed25519PublicKeyHex +): Promise => { + const pubDRepKey = await Wallet.Crypto.Ed25519PublicKey.fromHex(pubDRepKeyHex); + const drepKeyHex = (await pubDRepKey.hash()).hex(); + return Wallet.Crypto.Hash28ByteBase16.fromEd25519KeyHashHex(drepKeyHex); +}; + +export const getOwnRetirementMessageKey = (isOwnRetirement: boolean | undefined): string => { + if (isOwnRetirement === undefined) { + return ''; + } + return isOwnRetirement ? 'core.drepRetirement.isOwnRetirement' : 'core.drepRetirement.isNotOwnRetirement'; +}; diff --git a/apps/browser-extension-wallet/src/features/dapp/config/ViewsConfig.tsx b/apps/browser-extension-wallet/src/features/dapp/config/ViewsConfig.tsx index 34624df137..dbb0144eff 100644 --- a/apps/browser-extension-wallet/src/features/dapp/config/ViewsConfig.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/config/ViewsConfig.tsx @@ -1,5 +1,5 @@ import { IViewsList } from '../../../types'; -import { ConfirmTransaction } from '../components/ConfirmTransaction'; +import { ConfirmTransaction } from '../components/confirm-transaction/ConfirmTransaction'; import { SignTransaction } from '../components/SignTransaction'; import { DappTransactionFail } from '../components/DappTransactionFail'; import { IViewAction, IViewState } from '../../../providers'; diff --git a/apps/browser-extension-wallet/src/lib/translations/en.json b/apps/browser-extension-wallet/src/lib/translations/en.json index 636344952f..3e55901003 100644 --- a/apps/browser-extension-wallet/src/lib/translations/en.json +++ b/apps/browser-extension-wallet/src/lib/translations/en.json @@ -1113,6 +1113,22 @@ "tryingToUseAssetNotInWallet": "This DApp is trying to use token not held in your wallet.", "noCollateral": "Wallet should not be able to sign dapp txs without collateral." }, + "drepRegistration": { + "title": "Confirm DRep Registration", + "metadata": "Metadata", + "url": "URL", + "hash": "Hash", + "drepId": "Drep ID", + "depositPaid": "Deposit paid" + }, + "drepRetirement": { + "title": "Confirm DRep Retirement", + "metadata": "Metadata", + "drepId": "Drep ID", + "depositReturned": "Deposit returned", + "isOwnRetirement": "This is your DRep retirement.", + "isNotOwnRetirement": "The presented DRepID does not match your wallet's DRepID." + }, "destinationAddressInput": { "recipientAddress": "Recipient's address or $handle" }, diff --git a/apps/browser-extension-wallet/src/providers/UIThemeProvider/context.tsx b/apps/browser-extension-wallet/src/providers/UIThemeProvider/context.tsx new file mode 100644 index 0000000000..b5a0cdff17 --- /dev/null +++ b/apps/browser-extension-wallet/src/providers/UIThemeProvider/context.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import { ThemeColorScheme, ThemeProvider } from '@lace/ui'; + +import { useTheme } from '@providers/ThemeProvider'; + +interface Props { + children: React.ReactNode; +} + +export const UIThemeProvider = ({ children }: Props): React.ReactElement => { + const { theme } = useTheme(); + return ( + + {children} + + ); +}; diff --git a/apps/browser-extension-wallet/src/providers/UIThemeProvider/index.ts b/apps/browser-extension-wallet/src/providers/UIThemeProvider/index.ts new file mode 100644 index 0000000000..c38e8e8215 --- /dev/null +++ b/apps/browser-extension-wallet/src/providers/UIThemeProvider/index.ts @@ -0,0 +1 @@ +export * from './context'; diff --git a/packages/core/.babelrc.json b/packages/core/.babelrc.json new file mode 100644 index 0000000000..9164ce7ce4 --- /dev/null +++ b/packages/core/.babelrc.json @@ -0,0 +1,18 @@ +{ + "sourceType": "unambiguous", + "presets": [ + [ + "@babel/preset-env", + { + "targets": { + "chrome": 100, + "safari": 15, + "firefox": 91 + } + } + ], + "@babel/preset-typescript", + "@babel/preset-react" + ], + "plugins": [] +} diff --git a/packages/core/.storybook/main.js b/packages/core/.storybook/main.js new file mode 100644 index 0000000000..0670843c07 --- /dev/null +++ b/packages/core/.storybook/main.js @@ -0,0 +1,51 @@ +module.exports = { + stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'], + addons: [ + '@storybook/addon-links', + '@storybook/addon-essentials', + '@storybook/addon-interactions', + 'storybook-addon-pseudo-states', + { + name: '@storybook/addon-styling', + options: { + sass: { + // Require your Sass preprocessor here + implementation: require('sass') + } + } + } + ], + framework: '@storybook/react', + webpackFinal: (config) => { + const fileLoaderRule = config.module.rules.find((rule) => rule.test?.test('.svg')); + fileLoaderRule.exclude = /\.svg$/; + + config.module.rules.push({ + test: /\.svg$/i, + issuer: /\.[jt]sx?$/, + use: [ + { + loader: '@svgr/webpack', + options: { + icon: true, + exportType: 'named' + } + } + ] + }); + + config.resolve.extensions.push('.svg'); + + return config; + }, + core: { + builder: 'webpack5', + options: { + lazyCompilation: true, + fsCache: true + } + }, + features: { + interactionsDebugger: true + } +}; diff --git a/packages/core/.storybook/preview-head.html b/packages/core/.storybook/preview-head.html new file mode 100644 index 0000000000..7819a8476c --- /dev/null +++ b/packages/core/.storybook/preview-head.html @@ -0,0 +1,6 @@ + + + + + +Lace UI toolkit diff --git a/packages/core/.storybook/preview.js b/packages/core/.storybook/preview.js new file mode 100644 index 0000000000..8a971ba68f --- /dev/null +++ b/packages/core/.storybook/preview.js @@ -0,0 +1,26 @@ +import React from 'react'; +import 'antd/dist/antd.css'; +import 'normalize.css'; +import './theme.scss'; +import { ThemeColorScheme, ThemeProvider } from '@lace/ui'; + +const preview = { + parameters: { + actions: { argTypesRegex: '^on[A-Z].*' }, + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/ + } + } + }, + decorators: [ + (Story) => ( + + + + ) + ] +}; + +export default preview; diff --git a/packages/core/.storybook/theme.scss b/packages/core/.storybook/theme.scss new file mode 100644 index 0000000000..853cd9dddd --- /dev/null +++ b/packages/core/.storybook/theme.scss @@ -0,0 +1,36 @@ +@import '../../common/src/ui/styles/abstracts/mixins'; +@import '../../common/src/ui/styles/themes/dark'; +@import '../../common/src/ui/styles/themes/light'; + +@mixin dark-theme { + /* + in case the theme provider is not being used, this media query allows us to use the theme set on the user system + */ + @media (prefers-color-scheme: dark) { + :root { + @include theme-custom-properties($dark-theme); + } + } + + html[data-theme='dark'] { + @include theme-custom-properties($dark-theme); + } +} + +@mixin light-theme { + /* + in case the theme provider is not being used, this media query allows us to use the theme set on the user system + */ + @media (prefers-color-scheme: light) { + :root { + @include theme-custom-properties($light-theme); + } + } + + html[data-theme='light'] { + @include theme-custom-properties($light-theme); + } +} + +@include dark-theme; +@include light-theme; diff --git a/packages/core/package.json b/packages/core/package.json index a74b1549fc..c1a1678406 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -27,11 +27,13 @@ ], "scripts": { "build": "run -T rollup -c rollup.config.js", + "build-storybook": "storybook build", "cleanup": "yarn exec rm -rf dist node_modules coverage .rollup.cache src/dist", "lint": "cd ../.. && yarn core:lint", "prepack": "yarn build", "prestart": "yarn build", "start": "node dist/index.js", + "storybook": "NODE_OPTIONS=--openssl-legacy-provider; start-storybook -p 6006", "test": "NODE_ENV=test run -T jest -c ./test/jest.config.js", "test:coverage": "yarn test --coverage", "watch": "yarn build --watch" @@ -39,6 +41,7 @@ "dependencies": { "@ant-design/icons": "^4.7.0", "@lace/common": "0.1.0", + "@lace/ui": "^0.1.0", "antd": "^4.24.10", "axios": "0.21.4", "axios-cache-adapter": "2.7.3", @@ -55,7 +58,26 @@ "zxcvbn": "^4.4.2" }, "devDependencies": { + "@babel/preset-env": "^7.22.20", + "@babel/preset-react": "^7.22.15", + "@babel/preset-typescript": "^7.22.15", + "@storybook/addon-actions": "^6.5.16", + "@storybook/addon-essentials": "^6.5.16", + "@storybook/addon-interactions": "^6.5.16", + "@storybook/addon-links": "^6.5.16", + "@storybook/addon-styling": "^1.3.7", + "@storybook/addon-styling-webpack": "^0.0.5", + "@storybook/builder-webpack5": "6.5.16", + "@storybook/core-events": "^6.5.16", + "@storybook/jest": "^0.0.10", + "@storybook/manager-webpack5": "6.5.16", + "@storybook/react": "^6.5.16", + "@storybook/test-runner": "^0.10.0", + "@storybook/testing-library": "^0.0.13", + "@types/babel__preset-env": "^7", "@types/debounce-promise": "^3.1.6", + "sass": "^1.68.0", + "storybook": "^7.4.3", "typescript": "^4.3.5" }, "peerDependencies": { diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 4aa7bab6ae..9b38a4b41a 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -28,3 +28,5 @@ export * from '@ui/components/DappTransaction'; export * from '@ui/components/Send/SendTransactionCost'; export * from '@ui/components/MnemonicWordsAutoComplete'; export * from '@ui/components/AddressCard'; +export * from '@ui/components/ConfirmDRepRegistration'; +export * from '@ui/components/ConfirmDRepRetirement'; diff --git a/packages/core/src/ui/components/ConfirmDRepRegistration/ConfirmDRepRegistration.stories.ts b/packages/core/src/ui/components/ConfirmDRepRegistration/ConfirmDRepRegistration.stories.ts new file mode 100644 index 0000000000..6434e8d54b --- /dev/null +++ b/packages/core/src/ui/components/ConfirmDRepRegistration/ConfirmDRepRegistration.stories.ts @@ -0,0 +1,50 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { ConfirmDRepRegistration } from './ConfirmDRepRegistration'; +import { ComponentProps } from 'react'; + +const meta: Meta = { + title: 'ConfirmDRepRegistration', + component: ConfirmDRepRegistration, + parameters: { + layout: 'centered' + } +}; + +export default meta; +type Story = StoryObj; + +const data: ComponentProps = { + dappInfo: { + logo: 'https://cdn.mint.handle.me/favicon.png', + name: 'Mint', + url: 'https://preprod.mint.handle.me' + }, + translations: { + labels: { + depositPaid: 'Deposit paid', + drepId: 'Drep ID', + hash: 'Hash', + url: 'URL' + }, + metadata: 'Metadata' + }, + metadata: { + depositPaid: '0.35 ADA', + drepId: '65ge6g54g5dd5', + hash: '9bba8233cdd086f0325daba465d568a88970d42536f9e71e92a80d5922ded885', + url: 'https://raw.githubusercontent.com/Ryun1/gov-metadata/main/governace-action/metadata.jsonldr1q99...uqvzlalu' + } +}; + +export const Overview: Story = { + args: { + ...data + } +}; +export const WithError: Story = { + args: { + ...data, + errorMessage: 'Something went wrong' + } +}; diff --git a/packages/core/src/ui/components/ConfirmDRepRegistration/ConfirmDRepRegistration.tsx b/packages/core/src/ui/components/ConfirmDRepRegistration/ConfirmDRepRegistration.tsx new file mode 100644 index 0000000000..7041015050 --- /dev/null +++ b/packages/core/src/ui/components/ConfirmDRepRegistration/ConfirmDRepRegistration.tsx @@ -0,0 +1,57 @@ +import React from 'react'; +import { Box, Cell, Grid, TransactionSummary, Flex } from '@lace/ui'; +import { DappInfo, DappInfoProps } from '../DappInfo'; +import { ErrorPane } from '@lace/common'; +interface Props { + dappInfo: Omit; + errorMessage?: string; + translations: { + labels: { + url: string; + hash: string; + drepId: string; + depositPaid: string; + }; + metadata: string; + }; + metadata: { + url: string; + hash: string; + drepId: string; + depositPaid: string; + }; +} + +export const ConfirmDRepRegistration = ({ dappInfo, errorMessage, translations, metadata }: Props): JSX.Element => ( + + + + + {errorMessage && ( + + + + )} + + + + + {metadata.url && ( + + + + )} + {metadata.hash && ( + + + + )} + + + + + + + + +); diff --git a/packages/core/src/ui/components/ConfirmDRepRegistration/index.ts b/packages/core/src/ui/components/ConfirmDRepRegistration/index.ts new file mode 100644 index 0000000000..cf5bded0ee --- /dev/null +++ b/packages/core/src/ui/components/ConfirmDRepRegistration/index.ts @@ -0,0 +1 @@ +export { ConfirmDRepRegistration } from './ConfirmDRepRegistration'; diff --git a/packages/core/src/ui/components/ConfirmDRepRetirement/ConfirmDRepRetirement.stories.ts b/packages/core/src/ui/components/ConfirmDRepRetirement/ConfirmDRepRetirement.stories.ts new file mode 100644 index 0000000000..6323a43ec5 --- /dev/null +++ b/packages/core/src/ui/components/ConfirmDRepRetirement/ConfirmDRepRetirement.stories.ts @@ -0,0 +1,46 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { ConfirmDRepRetirement } from './ConfirmDRepRetirement'; +import { ComponentProps } from 'react'; + +const meta: Meta = { + title: 'ConfirmDRepRetirement', + component: ConfirmDRepRetirement, + parameters: { + layout: 'centered' + } +}; + +export default meta; +type Story = StoryObj; + +const data: ComponentProps = { + dappInfo: { + logo: 'https://cdn.mint.handle.me/favicon.png', + name: 'Mint', + url: 'https://preprod.mint.handle.me' + }, + translations: { + labels: { + depositReturned: 'Deposit paid', + drepId: 'Drep ID' + }, + metadata: 'Metadata' + }, + metadata: { + depositReturned: '0.35 ADA', + drepId: '65ge6g54g5dd5' + } +}; + +export const Overview: Story = { + args: { + ...data + } +}; +export const WithError: Story = { + args: { + ...data, + errorMessage: 'Something went wrong' + } +}; diff --git a/packages/core/src/ui/components/ConfirmDRepRetirement/ConfirmDRepRetirement.tsx b/packages/core/src/ui/components/ConfirmDRepRetirement/ConfirmDRepRetirement.tsx new file mode 100644 index 0000000000..b658e268e6 --- /dev/null +++ b/packages/core/src/ui/components/ConfirmDRepRetirement/ConfirmDRepRetirement.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import { Box, Cell, Grid, TransactionSummary, Flex } from '@lace/ui'; +import { DappInfo, DappInfoProps } from '../DappInfo'; +import { ErrorPane } from '@lace/common'; + +interface Props { + dappInfo: Omit; + errorMessage?: string; + translations: { + labels: { + drepId: string; + depositReturned: string; + }; + metadata: string; + }; + metadata: { + drepId: string; + depositReturned: string; + }; +} + +export const ConfirmDRepRetirement = ({ dappInfo, errorMessage, translations, metadata }: Props): JSX.Element => ( + + + + + {errorMessage && ( + + + + )} + + + + + + + + + + + + +); diff --git a/packages/core/src/ui/components/ConfirmDRepRetirement/index.ts b/packages/core/src/ui/components/ConfirmDRepRetirement/index.ts new file mode 100644 index 0000000000..cb0e8d0d15 --- /dev/null +++ b/packages/core/src/ui/components/ConfirmDRepRetirement/index.ts @@ -0,0 +1 @@ +export { ConfirmDRepRetirement } from './ConfirmDRepRetirement'; diff --git a/packages/core/src/ui/components/DappTransaction/DappTransaction.stories.ts b/packages/core/src/ui/components/DappTransaction/DappTransaction.stories.ts new file mode 100644 index 0000000000..56f29d0531 --- /dev/null +++ b/packages/core/src/ui/components/DappTransaction/DappTransaction.stories.ts @@ -0,0 +1,60 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { DappTransaction } from './DappTransaction'; +import { ComponentProps } from 'react'; + +const meta: Meta = { + title: 'DappTransaction', + component: DappTransaction, + parameters: { + layout: 'centered' + } +}; + +export default meta; +type Story = StoryObj; + +const data: ComponentProps = { + dappInfo: { + logo: 'https://cdn.mint.handle.me/favicon.png', + name: 'Mint', + url: 'https://preprod.mint.handle.me' + }, + translations: { + recipient: 'Recipient', + amount: 'Amount', + adaFollowingNumericValue: 'ADA', + fee: 'Fee', + transaction: 'Transaction' + }, + transaction: { + fee: '0.17', + outputs: [ + { + coins: '1', + recipient: + 'addr_test1qrl0s3nqfljv8dfckn7c4wkzu5rl6wn4hakkddcz2mczt3szlqss933x0aag07qcgspcaglmay6ufl4y4lalmlpe02mqhl0fx2' + } + ], + type: 'Mint' + } +}; + +export const Overview: Story = { + args: { + ...data + } +}; + +export const WithInsufficientFunds: Story = { + args: { + ...data + } +}; + +export const WithError: Story = { + args: { + ...data, + errorMessage: 'Something went wrong' + } +}; diff --git a/packages/core/src/ui/components/InsufficientFundsWarning/InsufficientFundsWarning.module.scss b/packages/core/src/ui/components/InsufficientFundsWarning/InsufficientFundsWarning.module.scss new file mode 100644 index 0000000000..303cbebf6d --- /dev/null +++ b/packages/core/src/ui/components/InsufficientFundsWarning/InsufficientFundsWarning.module.scss @@ -0,0 +1,23 @@ +@import '../../styles/theme.scss'; +@import '../../../../../common/src/ui/styles/abstracts/_typography.scss'; + +.warningAlert { + flex-direction: row; + background: var(--lace-cream); + display: flex; + align-items: center; + border-radius: size_unit(2); + padding: size_unit(2) size_unit(3); + gap: size_unit(3); + + svg { + height: size_unit(3); + width: size_unit(3); + color: var(--data-orange); + } + + p { + @include text-body-semi-bold; + margin: 0; + } +} diff --git a/packages/core/src/ui/components/InsufficientFundsWarning/InsufficientFundsWarning.tsx b/packages/core/src/ui/components/InsufficientFundsWarning/InsufficientFundsWarning.tsx new file mode 100644 index 0000000000..7823ca8d43 --- /dev/null +++ b/packages/core/src/ui/components/InsufficientFundsWarning/InsufficientFundsWarning.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import styles from './InsufficientFundsWarning.module.scss'; +import { ReactComponent as WarningIcon } from '../../assets/icons/warning-icon.component.svg'; +import Icon from '@ant-design/icons'; + +export interface DappTransactionProps { + translations: string; +} + +export const InsufficientFundsWarning = ({ translations }: DappTransactionProps): React.ReactElement => ( +
+ +

{translations}

+
+); diff --git a/packages/core/src/ui/components/InsufficientFundsWarning/index.ts b/packages/core/src/ui/components/InsufficientFundsWarning/index.ts new file mode 100644 index 0000000000..fdd126c9d8 --- /dev/null +++ b/packages/core/src/ui/components/InsufficientFundsWarning/index.ts @@ -0,0 +1 @@ +export * from './InsufficientFundsWarning'; diff --git a/yarn.lock b/yarn.lock index 7e8167c66a..ecf24aa849 100644 --- a/yarn.lock +++ b/yarn.lock @@ -110,6 +110,17 @@ __metadata: languageName: node linkType: hard +"@aw-web-design/x-default-browser@npm:1.4.126": + version: 1.4.126 + resolution: "@aw-web-design/x-default-browser@npm:1.4.126" + dependencies: + default-browser-id: 3.0.0 + bin: + x-default-browser: bin/x-default-browser.js + checksum: f63b68a0ff41c8fe478b1b4822e169cac0d26c61b123c0400d5e16a8a5987732b85795aff16d6b21936f9c955f0d32bffbfc166890d3446f74a72a7a2c9633ea + languageName: node + linkType: hard + "@babel/code-frame@npm:7.12.11": version: 7.12.11 resolution: "@babel/code-frame@npm:7.12.11" @@ -164,6 +175,16 @@ __metadata: languageName: node linkType: hard +"@babel/code-frame@npm:^7.22.13": + version: 7.22.13 + resolution: "@babel/code-frame@npm:7.22.13" + dependencies: + "@babel/highlight": ^7.22.13 + chalk: ^2.4.2 + checksum: 22e342c8077c8b77eeb11f554ecca2ba14153f707b85294fcf6070b6f6150aae88a7b7436dd88d8c9289970585f3fe5b9b941c5aa3aa26a6d5a8ef3f292da058 + languageName: node + linkType: hard + "@babel/code-frame@npm:^7.22.5": version: 7.22.5 resolution: "@babel/code-frame@npm:7.22.5" @@ -215,6 +236,13 @@ __metadata: languageName: node linkType: hard +"@babel/compat-data@npm:^7.22.20, @babel/compat-data@npm:^7.22.6, @babel/compat-data@npm:^7.22.9": + version: 7.22.20 + resolution: "@babel/compat-data@npm:7.22.20" + checksum: efedd1d18878c10fde95e4d82b1236a9aba41395ef798cbb651f58dbf5632dbff475736c507b8d13d4c8f44809d41c0eb2ef0d694283af9ba5dd8339b6dab451 + languageName: node + linkType: hard + "@babel/compat-data@npm:^7.22.5": version: 7.22.5 resolution: "@babel/compat-data@npm:7.22.5" @@ -315,6 +343,29 @@ __metadata: languageName: node linkType: hard +"@babel/core@npm:^7.13.16, @babel/core@npm:^7.22.9": + version: 7.22.20 + resolution: "@babel/core@npm:7.22.20" + dependencies: + "@ampproject/remapping": ^2.2.0 + "@babel/code-frame": ^7.22.13 + "@babel/generator": ^7.22.15 + "@babel/helper-compilation-targets": ^7.22.15 + "@babel/helper-module-transforms": ^7.22.20 + "@babel/helpers": ^7.22.15 + "@babel/parser": ^7.22.16 + "@babel/template": ^7.22.15 + "@babel/traverse": ^7.22.20 + "@babel/types": ^7.22.19 + convert-source-map: ^1.7.0 + debug: ^4.1.0 + gensync: ^1.0.0-beta.2 + json5: ^2.2.3 + semver: ^6.3.1 + checksum: 73663a079194b5dc406b2e2e5e50db81977d443e4faf7ef2c27e5836cd9a359e81e551115193dc9b1a93471275351a972e54904f4d3aa6cb156f51e26abf6765 + languageName: node + linkType: hard + "@babel/core@npm:^7.15.5": version: 7.16.0 resolution: "@babel/core@npm:7.16.0" @@ -557,6 +608,18 @@ __metadata: languageName: node linkType: hard +"@babel/generator@npm:^7.22.15, @babel/generator@npm:^7.22.9": + version: 7.22.15 + resolution: "@babel/generator@npm:7.22.15" + dependencies: + "@babel/types": ^7.22.15 + "@jridgewell/gen-mapping": ^0.3.2 + "@jridgewell/trace-mapping": ^0.3.17 + jsesc: ^2.5.1 + checksum: 5b2a3ccdc3634f6ea86e0a442722bcd430238369432d31f15b428a4ee8013c2f4f917b5b135bf4fc1d0a3e2f87f10fd4ce5d07955ecc2d3b9400a05c2a481374 + languageName: node + linkType: hard + "@babel/generator@npm:^7.22.5": version: 7.22.5 resolution: "@babel/generator@npm:7.22.5" @@ -608,6 +671,15 @@ __metadata: languageName: node linkType: hard +"@babel/helper-annotate-as-pure@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-annotate-as-pure@npm:7.22.5" + dependencies: + "@babel/types": ^7.22.5 + checksum: 53da330f1835c46f26b7bf4da31f7a496dee9fd8696cca12366b94ba19d97421ce519a74a837f687749318f94d1a37f8d1abcbf35e8ed22c32d16373b2f6198d + languageName: node + linkType: hard + "@babel/helper-builder-binary-assignment-operator-visitor@npm:^7.14.5": version: 7.14.5 resolution: "@babel/helper-builder-binary-assignment-operator-visitor@npm:7.14.5" @@ -638,6 +710,15 @@ __metadata: languageName: node linkType: hard +"@babel/helper-builder-binary-assignment-operator-visitor@npm:^7.22.5": + version: 7.22.15 + resolution: "@babel/helper-builder-binary-assignment-operator-visitor@npm:7.22.15" + dependencies: + "@babel/types": ^7.22.15 + checksum: 639c697a1c729f9fafa2dd4c9af2e18568190299b5907bd4c2d0bc818fcbd1e83ffeecc2af24327a7faa7ac4c34edd9d7940510a5e66296c19bad17001cf5c7a + languageName: node + linkType: hard + "@babel/helper-compilation-targets@npm:^7.13.0, @babel/helper-compilation-targets@npm:^7.14.5, @babel/helper-compilation-targets@npm:^7.15.0": version: 7.15.0 resolution: "@babel/helper-compilation-targets@npm:7.15.0" @@ -710,6 +791,19 @@ __metadata: languageName: node linkType: hard +"@babel/helper-compilation-targets@npm:^7.22.15, @babel/helper-compilation-targets@npm:^7.22.6": + version: 7.22.15 + resolution: "@babel/helper-compilation-targets@npm:7.22.15" + dependencies: + "@babel/compat-data": ^7.22.9 + "@babel/helper-validator-option": ^7.22.15 + browserslist: ^4.21.9 + lru-cache: ^5.1.1 + semver: ^6.3.1 + checksum: ce85196769e091ae54dd39e4a80c2a9df1793da8588e335c383d536d54f06baf648d0a08fc873044f226398c4ded15c4ae9120ee18e7dfd7c639a68e3cdc9980 + languageName: node + linkType: hard + "@babel/helper-compilation-targets@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-compilation-targets@npm:7.22.5" @@ -809,6 +903,25 @@ __metadata: languageName: node linkType: hard +"@babel/helper-create-class-features-plugin@npm:^7.22.11, @babel/helper-create-class-features-plugin@npm:^7.22.15, @babel/helper-create-class-features-plugin@npm:^7.22.5": + version: 7.22.15 + resolution: "@babel/helper-create-class-features-plugin@npm:7.22.15" + dependencies: + "@babel/helper-annotate-as-pure": ^7.22.5 + "@babel/helper-environment-visitor": ^7.22.5 + "@babel/helper-function-name": ^7.22.5 + "@babel/helper-member-expression-to-functions": ^7.22.15 + "@babel/helper-optimise-call-expression": ^7.22.5 + "@babel/helper-replace-supers": ^7.22.9 + "@babel/helper-skip-transparent-expression-wrappers": ^7.22.5 + "@babel/helper-split-export-declaration": ^7.22.6 + semver: ^6.3.1 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 52c500d8d164abb3a360b1b7c4b8fff77bc4a5920d3a2b41ae6e1d30617b0dc0b972c1f5db35b1752007e04a748908b4a99bc872b73549ae837e87dcdde005a3 + languageName: node + linkType: hard + "@babel/helper-create-regexp-features-plugin@npm:^7.14.5": version: 7.14.5 resolution: "@babel/helper-create-regexp-features-plugin@npm:7.14.5" @@ -845,6 +958,19 @@ __metadata: languageName: node linkType: hard +"@babel/helper-create-regexp-features-plugin@npm:^7.22.5": + version: 7.22.15 + resolution: "@babel/helper-create-regexp-features-plugin@npm:7.22.15" + dependencies: + "@babel/helper-annotate-as-pure": ^7.22.5 + regexpu-core: ^5.3.1 + semver: ^6.3.1 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 0243b8d4854f1dc8861b1029a46d3f6393ad72f366a5a08e36a4648aa682044f06da4c6e87a456260e1e1b33c999f898ba591a0760842c1387bcc93fbf2151a6 + languageName: node + linkType: hard + "@babel/helper-define-polyfill-provider@npm:^0.1.5": version: 0.1.5 resolution: "@babel/helper-define-polyfill-provider@npm:0.1.5" @@ -915,6 +1041,21 @@ __metadata: languageName: node linkType: hard +"@babel/helper-define-polyfill-provider@npm:^0.4.2": + version: 0.4.2 + resolution: "@babel/helper-define-polyfill-provider@npm:0.4.2" + dependencies: + "@babel/helper-compilation-targets": ^7.22.6 + "@babel/helper-plugin-utils": ^7.22.5 + debug: ^4.1.1 + lodash.debounce: ^4.0.8 + resolve: ^1.14.2 + peerDependencies: + "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 + checksum: 1f6dec0c5d0876d278fe15b71238eccc5f74c4e2efa2c78aaafa8bc2cc96336b8e68d94cd1a78497356c96e8b91b8c1f4452179820624d1702aee2f9832e6569 + languageName: node + linkType: hard + "@babel/helper-environment-visitor@npm:^7.18.9": version: 7.18.9 resolution: "@babel/helper-environment-visitor@npm:7.18.9" @@ -929,6 +1070,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-environment-visitor@npm:^7.22.20": + version: 7.22.20 + resolution: "@babel/helper-environment-visitor@npm:7.22.20" + checksum: d80ee98ff66f41e233f36ca1921774c37e88a803b2f7dca3db7c057a5fea0473804db9fb6729e5dbfd07f4bed722d60f7852035c2c739382e84c335661590b69 + languageName: node + linkType: hard + "@babel/helper-environment-visitor@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-environment-visitor@npm:7.22.5" @@ -1124,6 +1272,15 @@ __metadata: languageName: node linkType: hard +"@babel/helper-member-expression-to-functions@npm:^7.22.15": + version: 7.22.15 + resolution: "@babel/helper-member-expression-to-functions@npm:7.22.15" + dependencies: + "@babel/types": ^7.22.15 + checksum: c7c5d01c402dd8902c2ec3093f203ed0fc3bc5f669328a084d2e663c4c06dd0415480ee8220c6f96ba9b2dc49545c0078f221fc3900ab1e65de69a12fe7b361f + languageName: node + linkType: hard + "@babel/helper-module-imports@npm:^7.0.0, @babel/helper-module-imports@npm:^7.18.6": version: 7.18.6 resolution: "@babel/helper-module-imports@npm:7.18.6" @@ -1160,6 +1317,15 @@ __metadata: languageName: node linkType: hard +"@babel/helper-module-imports@npm:^7.22.15": + version: 7.22.15 + resolution: "@babel/helper-module-imports@npm:7.22.15" + dependencies: + "@babel/types": ^7.22.15 + checksum: ecd7e457df0a46f889228f943ef9b4a47d485d82e030676767e6a2fdcbdaa63594d8124d4b55fd160b41c201025aec01fc27580352b1c87a37c9c6f33d116702 + languageName: node + linkType: hard + "@babel/helper-module-imports@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-module-imports@npm:7.22.5" @@ -1265,6 +1431,21 @@ __metadata: languageName: node linkType: hard +"@babel/helper-module-transforms@npm:^7.22.15, @babel/helper-module-transforms@npm:^7.22.20, @babel/helper-module-transforms@npm:^7.22.9": + version: 7.22.20 + resolution: "@babel/helper-module-transforms@npm:7.22.20" + dependencies: + "@babel/helper-environment-visitor": ^7.22.20 + "@babel/helper-module-imports": ^7.22.15 + "@babel/helper-simple-access": ^7.22.5 + "@babel/helper-split-export-declaration": ^7.22.6 + "@babel/helper-validator-identifier": ^7.22.20 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 8fce25362df8711bd4620f41c5c18769edfeafe7f8f1dae9691966ef368e57f9da68dfa1707cd63c834c89dc4eaa82c26f12ea33e88fd262ac62844b11dcc389 + languageName: node + linkType: hard + "@babel/helper-module-transforms@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-module-transforms@npm:7.22.5" @@ -1308,6 +1489,15 @@ __metadata: languageName: node linkType: hard +"@babel/helper-optimise-call-expression@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-optimise-call-expression@npm:7.22.5" + dependencies: + "@babel/types": ^7.22.5 + checksum: c70ef6cc6b6ed32eeeec4482127e8be5451d0e5282d5495d5d569d39eb04d7f1d66ec99b327f45d1d5842a9ad8c22d48567e93fc502003a47de78d122e355f7c + languageName: node + linkType: hard + "@babel/helper-plugin-utils@npm:7.10.4": version: 7.10.4 resolution: "@babel/helper-plugin-utils@npm:7.10.4" @@ -1336,6 +1526,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-plugin-utils@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-plugin-utils@npm:7.22.5" + checksum: c0fc7227076b6041acd2f0e818145d2e8c41968cc52fb5ca70eed48e21b8fe6dd88a0a91cbddf4951e33647336eb5ae184747ca706817ca3bef5e9e905151ff5 + languageName: node + linkType: hard + "@babel/helper-remap-async-to-generator@npm:^7.14.5": version: 7.14.5 resolution: "@babel/helper-remap-async-to-generator@npm:7.14.5" @@ -1372,6 +1569,19 @@ __metadata: languageName: node linkType: hard +"@babel/helper-remap-async-to-generator@npm:^7.22.5, @babel/helper-remap-async-to-generator@npm:^7.22.9": + version: 7.22.20 + resolution: "@babel/helper-remap-async-to-generator@npm:7.22.20" + dependencies: + "@babel/helper-annotate-as-pure": ^7.22.5 + "@babel/helper-environment-visitor": ^7.22.20 + "@babel/helper-wrap-function": ^7.22.20 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 2fe6300a6f1b58211dffa0aed1b45d4958506d096543663dba83bd9251fe8d670fa909143a65b45e72acb49e7e20fbdb73eae315d9ddaced467948c3329986e7 + languageName: node + linkType: hard + "@babel/helper-replace-supers@npm:^7.14.5, @babel/helper-replace-supers@npm:^7.15.0": version: 7.15.0 resolution: "@babel/helper-replace-supers@npm:7.15.0" @@ -1423,6 +1633,19 @@ __metadata: languageName: node linkType: hard +"@babel/helper-replace-supers@npm:^7.22.5, @babel/helper-replace-supers@npm:^7.22.9": + version: 7.22.20 + resolution: "@babel/helper-replace-supers@npm:7.22.20" + dependencies: + "@babel/helper-environment-visitor": ^7.22.20 + "@babel/helper-member-expression-to-functions": ^7.22.15 + "@babel/helper-optimise-call-expression": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: a0008332e24daedea2e9498733e3c39b389d6d4512637e000f96f62b797e702ee24a407ccbcd7a236a551590a38f31282829a8ef35c50a3c0457d88218cae639 + languageName: node + linkType: hard + "@babel/helper-simple-access@npm:^7.14.8": version: 7.14.8 resolution: "@babel/helper-simple-access@npm:7.14.8" @@ -1495,6 +1718,15 @@ __metadata: languageName: node linkType: hard +"@babel/helper-skip-transparent-expression-wrappers@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-skip-transparent-expression-wrappers@npm:7.22.5" + dependencies: + "@babel/types": ^7.22.5 + checksum: 1012ef2295eb12dc073f2b9edf3425661e9b8432a3387e62a8bc27c42963f1f216ab3124228015c748770b2257b4f1fda882ca8fa34c0bf485e929ae5bc45244 + languageName: node + linkType: hard + "@babel/helper-split-export-declaration@npm:^7.14.5": version: 7.14.5 resolution: "@babel/helper-split-export-declaration@npm:7.14.5" @@ -1531,6 +1763,15 @@ __metadata: languageName: node linkType: hard +"@babel/helper-split-export-declaration@npm:^7.22.6": + version: 7.22.6 + resolution: "@babel/helper-split-export-declaration@npm:7.22.6" + dependencies: + "@babel/types": ^7.22.5 + checksum: e141cace583b19d9195f9c2b8e17a3ae913b7ee9b8120246d0f9ca349ca6f03cb2c001fd5ec57488c544347c0bb584afec66c936511e447fd20a360e591ac921 + languageName: node + linkType: hard + "@babel/helper-string-parser@npm:^7.18.10": version: 7.18.10 resolution: "@babel/helper-string-parser@npm:7.18.10" @@ -1594,6 +1835,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-validator-identifier@npm:^7.22.19, @babel/helper-validator-identifier@npm:^7.22.20": + version: 7.22.20 + resolution: "@babel/helper-validator-identifier@npm:7.22.20" + checksum: 136412784d9428266bcdd4d91c32bcf9ff0e8d25534a9d94b044f77fe76bc50f941a90319b05aafd1ec04f7d127cd57a179a3716009ff7f3412ef835ada95bdc + languageName: node + linkType: hard + "@babel/helper-validator-identifier@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-validator-identifier@npm:7.22.5" @@ -1622,6 +1870,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-validator-option@npm:^7.22.15": + version: 7.22.15 + resolution: "@babel/helper-validator-option@npm:7.22.15" + checksum: 68da52b1e10002a543161494c4bc0f4d0398c8fdf361d5f7f4272e95c45d5b32d974896d44f6a0ea7378c9204988879d73613ca683e13bd1304e46d25ff67a8d + languageName: node + linkType: hard + "@babel/helper-validator-option@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-validator-option@npm:7.22.5" @@ -1665,6 +1920,17 @@ __metadata: languageName: node linkType: hard +"@babel/helper-wrap-function@npm:^7.22.20": + version: 7.22.20 + resolution: "@babel/helper-wrap-function@npm:7.22.20" + dependencies: + "@babel/helper-function-name": ^7.22.5 + "@babel/template": ^7.22.15 + "@babel/types": ^7.22.19 + checksum: 221ed9b5572612aeb571e4ce6a256f2dee85b3c9536f1dd5e611b0255e5f59a3d0ec392d8d46d4152149156a8109f92f20379b1d6d36abb613176e0e33f05fca + languageName: node + linkType: hard + "@babel/helpers@npm:^7.12.5": version: 7.15.3 resolution: "@babel/helpers@npm:7.15.3" @@ -1753,6 +2019,17 @@ __metadata: languageName: node linkType: hard +"@babel/helpers@npm:^7.22.15": + version: 7.22.15 + resolution: "@babel/helpers@npm:7.22.15" + dependencies: + "@babel/template": ^7.22.15 + "@babel/traverse": ^7.22.15 + "@babel/types": ^7.22.15 + checksum: 49f61a93cbae4df3328bda67af5db743fead659ae4242571226c3596b7df78546189cdf991fed1eca33b559de8abf396a90a001f474a1bab351418f07b7ae6ef + languageName: node + linkType: hard + "@babel/helpers@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helpers@npm:7.22.5" @@ -1808,6 +2085,17 @@ __metadata: languageName: node linkType: hard +"@babel/highlight@npm:^7.22.13": + version: 7.22.20 + resolution: "@babel/highlight@npm:7.22.20" + dependencies: + "@babel/helper-validator-identifier": ^7.22.20 + chalk: ^2.4.2 + js-tokens: ^4.0.0 + checksum: 84bd034dca309a5e680083cd827a766780ca63cef37308404f17653d32366ea76262bd2364b2d38776232f2d01b649f26721417d507e8b4b6da3e4e739f6d134 + languageName: node + linkType: hard + "@babel/highlight@npm:^7.22.5": version: 7.22.5 resolution: "@babel/highlight@npm:7.22.5" @@ -1837,6 +2125,15 @@ __metadata: languageName: node linkType: hard +"@babel/parser@npm:^7.13.16, @babel/parser@npm:^7.22.15, @babel/parser@npm:^7.22.16, @babel/parser@npm:^7.22.7": + version: 7.22.16 + resolution: "@babel/parser@npm:7.22.16" + bin: + parser: ./bin/babel-parser.js + checksum: 944c756b5bdeb07b9fec16ecef6b3c61aff9d4c4b924abadcf01afa1840a740b8e2357ae00482b5b37daad6d2bfd848c947f27ad65138d687b6fdc924bc59edd + languageName: node + linkType: hard + "@babel/parser@npm:^7.14.7, @babel/parser@npm:^7.18.10, @babel/parser@npm:^7.20.1, @babel/parser@npm:^7.20.2": version: 7.20.3 resolution: "@babel/parser@npm:7.20.3" @@ -1958,6 +2255,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.22.15": + version: 7.22.15 + resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.22.15" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 8910ca21a7ec7c06f7b247d4b86c97c5aa15ef321518f44f6f490c5912fdf82c605aaa02b90892e375d82ccbedeadfdeadd922c1b836c9dd4c596871bf654753 + languageName: node + linkType: hard + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.14.5": version: 7.14.5 resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:7.14.5" @@ -1997,6 +2305,19 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.22.15": + version: 7.22.15 + resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:7.22.15" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-skip-transparent-expression-wrappers": ^7.22.5 + "@babel/plugin-transform-optional-chaining": ^7.22.15 + peerDependencies: + "@babel/core": ^7.13.0 + checksum: fbefedc0da014c37f1a50a8094ce7dbbf2181ae93243f23d6ecba2499b5b20196c2124d6a4dfe3e9e0125798e80593103e456352a4beb4e5c6f7c75efb80fdac + languageName: node + linkType: hard + "@babel/plugin-proposal-async-generator-functions@npm:^7.14.9": version: 7.14.9 resolution: "@babel/plugin-proposal-async-generator-functions@npm:7.14.9" @@ -2049,27 +2370,27 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-class-properties@npm:^7.16.0": - version: 7.16.0 - resolution: "@babel/plugin-proposal-class-properties@npm:7.16.0" +"@babel/plugin-proposal-class-properties@npm:^7.13.0, @babel/plugin-proposal-class-properties@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-proposal-class-properties@npm:7.18.6" dependencies: - "@babel/helper-create-class-features-plugin": ^7.16.0 - "@babel/helper-plugin-utils": ^7.14.5 + "@babel/helper-create-class-features-plugin": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.6 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: b1665ced553e5cdb95eec2fda321cb226c5f255edd1a94b226b9d81e97e026472184b6898af26f2bb9ee64101fad1afe215b6fc469d3103dec78c55e732e49aa + checksum: 49a78a2773ec0db56e915d9797e44fd079ab8a9b2e1716e0df07c92532f2c65d76aeda9543883916b8e0ff13606afeffa67c5b93d05b607bc87653ad18a91422 languageName: node linkType: hard -"@babel/plugin-proposal-class-properties@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-proposal-class-properties@npm:7.18.6" +"@babel/plugin-proposal-class-properties@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-proposal-class-properties@npm:7.16.0" dependencies: - "@babel/helper-create-class-features-plugin": ^7.18.6 - "@babel/helper-plugin-utils": ^7.18.6 + "@babel/helper-create-class-features-plugin": ^7.16.0 + "@babel/helper-plugin-utils": ^7.14.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 49a78a2773ec0db56e915d9797e44fd079ab8a9b2e1716e0df07c92532f2c65d76aeda9543883916b8e0ff13606afeffa67c5b93d05b607bc87653ad18a91422 + checksum: b1665ced553e5cdb95eec2fda321cb226c5f255edd1a94b226b9d81e97e026472184b6898af26f2bb9ee64101fad1afe215b6fc469d3103dec78c55e732e49aa languageName: node linkType: hard @@ -2306,27 +2627,27 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-nullish-coalescing-operator@npm:^7.16.0": - version: 7.16.0 - resolution: "@babel/plugin-proposal-nullish-coalescing-operator@npm:7.16.0" +"@babel/plugin-proposal-nullish-coalescing-operator@npm:^7.13.8, @babel/plugin-proposal-nullish-coalescing-operator@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-proposal-nullish-coalescing-operator@npm:7.18.6" dependencies: - "@babel/helper-plugin-utils": ^7.14.5 + "@babel/helper-plugin-utils": ^7.18.6 "@babel/plugin-syntax-nullish-coalescing-operator": ^7.8.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: e50f94929970cdc5c6ee22ec4c95c46ae25cdd8c391baf601f7f3d3a3cec417efc663a3fafa9ae5bca82a6815d49687b07cab9857f5a10e9ea862438ecb81e4a + checksum: 949c9ddcdecdaec766ee610ef98f965f928ccc0361dd87cf9f88cf4896a6ccd62fce063d4494778e50da99dea63d270a1be574a62d6ab81cbe9d85884bf55a7d languageName: node linkType: hard -"@babel/plugin-proposal-nullish-coalescing-operator@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-proposal-nullish-coalescing-operator@npm:7.18.6" +"@babel/plugin-proposal-nullish-coalescing-operator@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/plugin-proposal-nullish-coalescing-operator@npm:7.16.0" dependencies: - "@babel/helper-plugin-utils": ^7.18.6 + "@babel/helper-plugin-utils": ^7.14.5 "@babel/plugin-syntax-nullish-coalescing-operator": ^7.8.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 949c9ddcdecdaec766ee610ef98f965f928ccc0361dd87cf9f88cf4896a6ccd62fce063d4494778e50da99dea63d270a1be574a62d6ab81cbe9d85884bf55a7d + checksum: e50f94929970cdc5c6ee22ec4c95c46ae25cdd8c391baf601f7f3d3a3cec417efc663a3fafa9ae5bca82a6815d49687b07cab9857f5a10e9ea862438ecb81e4a languageName: node linkType: hard @@ -2473,6 +2794,19 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-proposal-optional-chaining@npm:^7.13.12, @babel/plugin-proposal-optional-chaining@npm:^7.21.0": + version: 7.21.0 + resolution: "@babel/plugin-proposal-optional-chaining@npm:7.21.0" + dependencies: + "@babel/helper-plugin-utils": ^7.20.2 + "@babel/helper-skip-transparent-expression-wrappers": ^7.20.0 + "@babel/plugin-syntax-optional-chaining": ^7.8.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 11c5449e01b18bb8881e8e005a577fa7be2fe5688e2382c8822d51f8f7005342a301a46af7b273b1f5645f9a7b894c428eee8526342038a275ef6ba4c8d8d746 + languageName: node + linkType: hard + "@babel/plugin-proposal-optional-chaining@npm:^7.16.0": version: 7.16.0 resolution: "@babel/plugin-proposal-optional-chaining@npm:7.16.0" @@ -2499,19 +2833,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-optional-chaining@npm:^7.21.0": - version: 7.21.0 - resolution: "@babel/plugin-proposal-optional-chaining@npm:7.21.0" - dependencies: - "@babel/helper-plugin-utils": ^7.20.2 - "@babel/helper-skip-transparent-expression-wrappers": ^7.20.0 - "@babel/plugin-syntax-optional-chaining": ^7.8.3 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 11c5449e01b18bb8881e8e005a577fa7be2fe5688e2382c8822d51f8f7005342a301a46af7b273b1f5645f9a7b894c428eee8526342038a275ef6ba4c8d8d746 - languageName: node - linkType: hard - "@babel/plugin-proposal-private-methods@npm:^7.12.1, @babel/plugin-proposal-private-methods@npm:^7.14.5": version: 7.14.5 resolution: "@babel/plugin-proposal-private-methods@npm:7.14.5" @@ -2548,6 +2869,15 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-proposal-private-property-in-object@npm:7.21.0-placeholder-for-preset-env.2": + version: 7.21.0-placeholder-for-preset-env.2 + resolution: "@babel/plugin-proposal-private-property-in-object@npm:7.21.0-placeholder-for-preset-env.2" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: d97745d098b835d55033ff3a7fb2b895b9c5295b08a5759e4f20df325aa385a3e0bc9bd5ad8f2ec554a44d4e6525acfc257b8c5848a1345cb40f26a30e277e91 + languageName: node + linkType: hard + "@babel/plugin-proposal-private-property-in-object@npm:^7.12.1": version: 7.18.6 resolution: "@babel/plugin-proposal-private-property-in-object@npm:7.18.6" @@ -2753,6 +3083,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-syntax-flow@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-syntax-flow@npm:7.22.5" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 84c8c40fcfe8e78cecdd6fb90e8f97f419e3f3b27a33de8324ae97d5ce1b87cdd98a636fa21a68d4d2c37c7d63f3a279bb84b6956b849921affed6b806b6ffe7 + languageName: node + linkType: hard + "@babel/plugin-syntax-import-assertions@npm:^7.20.0": version: 7.20.0 resolution: "@babel/plugin-syntax-import-assertions@npm:7.20.0" @@ -2764,7 +3105,29 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-import-meta@npm:^7.8.3": +"@babel/plugin-syntax-import-assertions@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-syntax-import-assertions@npm:7.22.5" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 2b8b5572db04a7bef1e6cd20debf447e4eef7cb012616f5eceb8fa3e23ce469b8f76ee74fd6d1e158ba17a8f58b0aec579d092fb67c5a30e83ccfbc5754916c1 + languageName: node + linkType: hard + +"@babel/plugin-syntax-import-attributes@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-syntax-import-attributes@npm:7.22.5" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 197b3c5ea2a9649347f033342cb222ab47f4645633695205c0250c6bf2af29e643753b8bb24a2db39948bef08e7c540babfd365591eb57fc110cb30b425ffc47 + languageName: node + linkType: hard + +"@babel/plugin-syntax-import-meta@npm:^7.10.4, @babel/plugin-syntax-import-meta@npm:^7.8.3": version: 7.10.4 resolution: "@babel/plugin-syntax-import-meta@npm:7.10.4" dependencies: @@ -2830,6 +3193,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-syntax-jsx@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-syntax-jsx@npm:7.22.5" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 8829d30c2617ab31393d99cec2978e41f014f4ac6f01a1cecf4c4dd8320c3ec12fdc3ce121126b2d8d32f6887e99ca1a0bad53dedb1e6ad165640b92b24980ce + languageName: node + linkType: hard + "@babel/plugin-syntax-logical-assignment-operators@npm:^7.10.4, @babel/plugin-syntax-logical-assignment-operators@npm:^7.8.3": version: 7.10.4 resolution: "@babel/plugin-syntax-logical-assignment-operators@npm:7.10.4" @@ -2951,6 +3325,29 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-syntax-typescript@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-syntax-typescript@npm:7.22.5" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 8ab7718fbb026d64da93681a57797d60326097fd7cb930380c8bffd9eb101689e90142c760a14b51e8e69c88a73ba3da956cb4520a3b0c65743aee5c71ef360a + languageName: node + linkType: hard + +"@babel/plugin-syntax-unicode-sets-regex@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-syntax-unicode-sets-regex@npm:7.18.6" + dependencies: + "@babel/helper-create-regexp-features-plugin": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: a651d700fe63ff0ddfd7186f4ebc24447ca734f114433139e3c027bc94a900d013cf1ef2e2db8430425ba542e39ae160c3b05f06b59fd4656273a3df97679e9c + languageName: node + linkType: hard + "@babel/plugin-transform-arrow-functions@npm:^7.12.1, @babel/plugin-transform-arrow-functions@npm:^7.14.5": version: 7.14.5 resolution: "@babel/plugin-transform-arrow-functions@npm:7.14.5" @@ -2984,6 +3381,31 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-arrow-functions@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-arrow-functions@npm:7.22.5" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 35abb6c57062802c7ce8bd96b2ef2883e3124370c688bbd67609f7d2453802fb73944df8808f893b6c67de978eb2bcf87bbfe325e46d6f39b5fcb09ece11d01a + languageName: node + linkType: hard + +"@babel/plugin-transform-async-generator-functions@npm:^7.22.15": + version: 7.22.15 + resolution: "@babel/plugin-transform-async-generator-functions@npm:7.22.15" + dependencies: + "@babel/helper-environment-visitor": ^7.22.5 + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-remap-async-to-generator": ^7.22.9 + "@babel/plugin-syntax-async-generators": ^7.8.4 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: fad98786b446ce63bde0d14a221e2617eef5a7bbca62b49d96f16ab5e1694521234cfba6145b830fbf9af16d60a8a3dbf148e8694830bd91796fe333b0599e73 + languageName: node + linkType: hard + "@babel/plugin-transform-async-to-generator@npm:^7.14.5": version: 7.14.5 resolution: "@babel/plugin-transform-async-to-generator@npm:7.14.5" @@ -3023,6 +3445,19 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-async-to-generator@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-async-to-generator@npm:7.22.5" + dependencies: + "@babel/helper-module-imports": ^7.22.5 + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-remap-async-to-generator": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: b95f23f99dcb379a9f0a1c2a3bbea3f8dc0e1b16dc1ac8b484fe378370169290a7a63d520959a9ba1232837cf74a80e23f6facbe14fd42a3cda6d3c2d7168e62 + languageName: node + linkType: hard + "@babel/plugin-transform-block-scoped-functions@npm:^7.14.5": version: 7.14.5 resolution: "@babel/plugin-transform-block-scoped-functions@npm:7.14.5" @@ -3056,6 +3491,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-block-scoped-functions@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-block-scoped-functions@npm:7.22.5" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 416b1341858e8ca4e524dee66044735956ced5f478b2c3b9bc11ec2285b0c25d7dbb96d79887169eb938084c95d0a89338c8b2fe70d473bd9dc92e5d9db1732c + languageName: node + linkType: hard + "@babel/plugin-transform-block-scoping@npm:^7.12.12, @babel/plugin-transform-block-scoping@npm:^7.14.5": version: 7.15.3 resolution: "@babel/plugin-transform-block-scoping@npm:7.15.3" @@ -3100,6 +3546,42 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-block-scoping@npm:^7.22.15": + version: 7.22.15 + resolution: "@babel/plugin-transform-block-scoping@npm:7.22.15" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: c7091dc000b854ce0c471588ca0704ef1ce78cff954584a9f21c1668fd0669e7c8d5396fb72fe49a2216d9b96a400d435f424f27e41a097ef6c855f9c57df195 + languageName: node + linkType: hard + +"@babel/plugin-transform-class-properties@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-class-properties@npm:7.22.5" + dependencies: + "@babel/helper-create-class-features-plugin": ^7.22.5 + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: b830152dfc2ff2f647f0abe76e6251babdfbef54d18c4b2c73a6bf76b1a00050a5d998dac80dc901a48514e95604324943a9dd39317073fe0928b559e0e0c579 + languageName: node + linkType: hard + +"@babel/plugin-transform-class-static-block@npm:^7.22.11": + version: 7.22.11 + resolution: "@babel/plugin-transform-class-static-block@npm:7.22.11" + dependencies: + "@babel/helper-create-class-features-plugin": ^7.22.11 + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/plugin-syntax-class-static-block": ^7.14.5 + peerDependencies: + "@babel/core": ^7.12.0 + checksum: 69f040506fad66f1c6918d288d0e0edbc5c8a07c8b4462c1184ad2f9f08995d68b057126c213871c0853ae0c72afc60ec87492049dfacb20902e32346a448bcb + languageName: node + linkType: hard + "@babel/plugin-transform-classes@npm:^7.12.1, @babel/plugin-transform-classes@npm:^7.14.9": version: 7.14.9 resolution: "@babel/plugin-transform-classes@npm:7.14.9" @@ -3172,6 +3654,25 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-classes@npm:^7.22.15": + version: 7.22.15 + resolution: "@babel/plugin-transform-classes@npm:7.22.15" + dependencies: + "@babel/helper-annotate-as-pure": ^7.22.5 + "@babel/helper-compilation-targets": ^7.22.15 + "@babel/helper-environment-visitor": ^7.22.5 + "@babel/helper-function-name": ^7.22.5 + "@babel/helper-optimise-call-expression": ^7.22.5 + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-replace-supers": ^7.22.9 + "@babel/helper-split-export-declaration": ^7.22.6 + globals: ^11.1.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: d3f4d0c107dd8a3557ea3575cc777fab27efa92958b41e4a9822f7499725c1f554beae58855de16ddec0a7b694e45f59a26cea8fbde4275563f72f09c6e039a0 + languageName: node + linkType: hard + "@babel/plugin-transform-computed-properties@npm:^7.14.5": version: 7.14.5 resolution: "@babel/plugin-transform-computed-properties@npm:7.14.5" @@ -3206,6 +3707,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-computed-properties@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-computed-properties@npm:7.22.5" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/template": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: c2a77a0f94ec71efbc569109ec14ea2aa925b333289272ced8b33c6108bdbb02caf01830ffc7e49486b62dec51911924d13f3a76f1149f40daace1898009e131 + languageName: node + linkType: hard + "@babel/plugin-transform-destructuring@npm:^7.12.1, @babel/plugin-transform-destructuring@npm:^7.14.7": version: 7.14.7 resolution: "@babel/plugin-transform-destructuring@npm:7.14.7" @@ -3250,6 +3763,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-destructuring@npm:^7.22.15": + version: 7.22.15 + resolution: "@babel/plugin-transform-destructuring@npm:7.22.15" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 4bccb4765e5287f1d36119d930afb9941ea8f4f001bddb8febff716bac0e09dc58576624f3ec59470630513044dd342075fe11af16d8c1b234cb7406cffca9f0 + languageName: node + linkType: hard + "@babel/plugin-transform-dotall-regex@npm:^7.14.5, @babel/plugin-transform-dotall-regex@npm:^7.4.4": version: 7.14.5 resolution: "@babel/plugin-transform-dotall-regex@npm:7.14.5" @@ -3286,6 +3810,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-dotall-regex@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-dotall-regex@npm:7.22.5" + dependencies: + "@babel/helper-create-regexp-features-plugin": ^7.22.5 + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 409b658d11e3082c8f69e9cdef2d96e4d6d11256f005772425fb230cc48fd05945edbfbcb709dab293a1a2f01f9c8a5bb7b4131e632b23264039d9f95864b453 + languageName: node + linkType: hard + "@babel/plugin-transform-duplicate-keys@npm:^7.14.5": version: 7.14.5 resolution: "@babel/plugin-transform-duplicate-keys@npm:7.14.5" @@ -3319,6 +3855,29 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-duplicate-keys@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-duplicate-keys@npm:7.22.5" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: bb1280fbabaab6fab2ede585df34900712698210a3bd413f4df5bae6d8c24be36b496c92722ae676a7a67d060a4624f4d6c23b923485f906bfba8773c69f55b4 + languageName: node + linkType: hard + +"@babel/plugin-transform-dynamic-import@npm:^7.22.11": + version: 7.22.11 + resolution: "@babel/plugin-transform-dynamic-import@npm:7.22.11" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/plugin-syntax-dynamic-import": ^7.8.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 78fc9c532210bf9e8f231747f542318568ac360ee6c27e80853962c984283c73da3f8f8aebe83c2096090a435b356b092ed85de617a156cbe0729d847632be45 + languageName: node + linkType: hard + "@babel/plugin-transform-exponentiation-operator@npm:^7.14.5": version: 7.14.5 resolution: "@babel/plugin-transform-exponentiation-operator@npm:7.14.5" @@ -3355,6 +3914,30 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-exponentiation-operator@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-exponentiation-operator@npm:7.22.5" + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor": ^7.22.5 + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: f2d660c1b1d51ad5fec1cd5ad426a52187204068c4158f8c4aa977b31535c61b66898d532603eef21c15756827be8277f724c869b888d560f26d7fe848bb5eae + languageName: node + linkType: hard + +"@babel/plugin-transform-export-namespace-from@npm:^7.22.11": + version: 7.22.11 + resolution: "@babel/plugin-transform-export-namespace-from@npm:7.22.11" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/plugin-syntax-export-namespace-from": ^7.8.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 73af5883a321ed56a4bfd43c8a7de0164faebe619287706896fc6ee2f7a4e69042adaa1338c0b8b4bdb9f7e5fdceb016fb1d40694cb43ca3b8827429e8aac4bf + languageName: node + linkType: hard + "@babel/plugin-transform-flow-strip-types@npm:^7.14.5": version: 7.14.5 resolution: "@babel/plugin-transform-flow-strip-types@npm:7.14.5" @@ -3367,6 +3950,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-flow-strip-types@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-flow-strip-types@npm:7.22.5" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/plugin-syntax-flow": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 1ba48187d6f33814be01c6870489f0b1858256cf2b9dd7e62f02af8b30049bf375112f1d44692c5fed3cb9cd26ee2fb32e358cd79b6ad2360a51e8f993e861bf + languageName: node + linkType: hard + "@babel/plugin-transform-for-of@npm:^7.12.1, @babel/plugin-transform-for-of@npm:^7.14.5": version: 7.14.5 resolution: "@babel/plugin-transform-for-of@npm:7.14.5" @@ -3411,6 +4006,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-for-of@npm:^7.22.15": + version: 7.22.15 + resolution: "@babel/plugin-transform-for-of@npm:7.22.15" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: f395ae7bce31e14961460f56cf751b5d6e37dd27d7df5b1f4e49fec1c11b6f9cf71991c7ffbe6549878591e87df0d66af798cf26edfa4bfa6b4c3dba1fb2f73a + languageName: node + linkType: hard + "@babel/plugin-transform-function-name@npm:^7.14.5": version: 7.14.5 resolution: "@babel/plugin-transform-function-name@npm:7.14.5" @@ -3448,6 +4054,31 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-function-name@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-function-name@npm:7.22.5" + dependencies: + "@babel/helper-compilation-targets": ^7.22.5 + "@babel/helper-function-name": ^7.22.5 + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: cff3b876357999cb8ae30e439c3ec6b0491a53b0aa6f722920a4675a6dd5b53af97a833051df4b34791fe5b3dd326ccf769d5c8e45b322aa50ee11a660b17845 + languageName: node + linkType: hard + +"@babel/plugin-transform-json-strings@npm:^7.22.11": + version: 7.22.11 + resolution: "@babel/plugin-transform-json-strings@npm:7.22.11" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/plugin-syntax-json-strings": ^7.8.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 50665e5979e66358c50e90a26db53c55917f78175127ac2fa05c7888d156d418ffb930ec0a109353db0a7c5f57c756ce01bfc9825d24cbfd2b3ec453f2ed8cba + languageName: node + linkType: hard + "@babel/plugin-transform-literals@npm:^7.14.5": version: 7.14.5 resolution: "@babel/plugin-transform-literals@npm:7.14.5" @@ -3481,6 +4112,29 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-literals@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-literals@npm:7.22.5" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: ec37cc2ffb32667af935ab32fe28f00920ec8a1eb999aa6dc6602f2bebd8ba205a558aeedcdccdebf334381d5c57106c61f52332045730393e73410892a9735b + languageName: node + linkType: hard + +"@babel/plugin-transform-logical-assignment-operators@npm:^7.22.11": + version: 7.22.11 + resolution: "@babel/plugin-transform-logical-assignment-operators@npm:7.22.11" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/plugin-syntax-logical-assignment-operators": ^7.10.4 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: c664e9798e85afa7f92f07b867682dee7392046181d82f5d21bae6f2ca26dfe9c8375cdc52b7483c3fc09a983c1989f60eff9fbc4f373b0c0a74090553d05739 + languageName: node + linkType: hard + "@babel/plugin-transform-member-expression-literals@npm:^7.14.5": version: 7.14.5 resolution: "@babel/plugin-transform-member-expression-literals@npm:7.14.5" @@ -3514,6 +4168,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-member-expression-literals@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-member-expression-literals@npm:7.22.5" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: ec4b0e07915ddd4fda0142fd104ee61015c208608a84cfa13643a95d18760b1dc1ceb6c6e0548898b8c49e5959a994e46367260176dbabc4467f729b21868504 + languageName: node + linkType: hard + "@babel/plugin-transform-modules-amd@npm:^7.14.5": version: 7.14.5 resolution: "@babel/plugin-transform-modules-amd@npm:7.14.5" @@ -3552,6 +4217,31 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-modules-amd@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-modules-amd@npm:7.22.5" + dependencies: + "@babel/helper-module-transforms": ^7.22.5 + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 7da4c4ebbbcf7d182abb59b2046b22d86eee340caf8a22a39ef6a727da2d8acfec1f714fcdcd5054110b280e4934f735e80a6848d192b6834c5d4459a014f04d + languageName: node + linkType: hard + +"@babel/plugin-transform-modules-commonjs@npm:^7.13.8, @babel/plugin-transform-modules-commonjs@npm:^7.22.15": + version: 7.22.15 + resolution: "@babel/plugin-transform-modules-commonjs@npm:7.22.15" + dependencies: + "@babel/helper-module-transforms": ^7.22.15 + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-simple-access": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: f8fc85fefa6be8626a378ca38fb84c7359043e7c692c854e9ee250a05121553b7f4a58e127099efe12662ec6bebbfd304ce638a0b4563d7cbd5982f3d877321c + languageName: node + linkType: hard + "@babel/plugin-transform-modules-commonjs@npm:^7.15.0": version: 7.15.0 resolution: "@babel/plugin-transform-modules-commonjs@npm:7.15.0" @@ -3650,6 +4340,20 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-modules-systemjs@npm:^7.22.11": + version: 7.22.11 + resolution: "@babel/plugin-transform-modules-systemjs@npm:7.22.11" + dependencies: + "@babel/helper-hoist-variables": ^7.22.5 + "@babel/helper-module-transforms": ^7.22.9 + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-validator-identifier": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: d0991e4bdc3352b6a9f4d12b6662e3645d892cd5c3c005ba5f14e65f1e218c6a8f7f4497e64a51d82a046e507aaa7db3143b800b0270dca1824cbd214ff3363d + languageName: node + linkType: hard + "@babel/plugin-transform-modules-umd@npm:^7.14.5": version: 7.14.5 resolution: "@babel/plugin-transform-modules-umd@npm:7.14.5" @@ -3686,6 +4390,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-modules-umd@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-modules-umd@npm:7.22.5" + dependencies: + "@babel/helper-module-transforms": ^7.22.5 + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 46622834c54c551b231963b867adbc80854881b3e516ff29984a8da989bd81665bd70e8cba6710345248e97166689310f544aee1a5773e262845a8f1b3e5b8b4 + languageName: node + linkType: hard + "@babel/plugin-transform-named-capturing-groups-regex@npm:^7.14.9": version: 7.14.9 resolution: "@babel/plugin-transform-named-capturing-groups-regex@npm:7.14.9" @@ -3720,6 +4436,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-named-capturing-groups-regex@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-named-capturing-groups-regex@npm:7.22.5" + dependencies: + "@babel/helper-create-regexp-features-plugin": ^7.22.5 + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 3ee564ddee620c035b928fdc942c5d17e9c4b98329b76f9cefac65c111135d925eb94ed324064cd7556d4f5123beec79abea1d4b97d1c8a2a5c748887a2eb623 + languageName: node + linkType: hard + "@babel/plugin-transform-new-target@npm:^7.14.5": version: 7.14.5 resolution: "@babel/plugin-transform-new-target@npm:7.14.5" @@ -3753,6 +4481,56 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-new-target@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-new-target@npm:7.22.5" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 6b72112773487a881a1d6ffa680afde08bad699252020e86122180ee7a88854d5da3f15d9bca3331cf2e025df045604494a8208a2e63b486266b07c14e2ffbf3 + languageName: node + linkType: hard + +"@babel/plugin-transform-nullish-coalescing-operator@npm:^7.22.11": + version: 7.22.11 + resolution: "@babel/plugin-transform-nullish-coalescing-operator@npm:7.22.11" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/plugin-syntax-nullish-coalescing-operator": ^7.8.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 167babecc8b8fe70796a7b7d34af667ebbf43da166c21689502e5e8cc93180b7a85979c77c9f64b7cce431b36718bd0a6df9e5e0ffea4ae22afb22cfef886372 + languageName: node + linkType: hard + +"@babel/plugin-transform-numeric-separator@npm:^7.22.11": + version: 7.22.11 + resolution: "@babel/plugin-transform-numeric-separator@npm:7.22.11" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/plugin-syntax-numeric-separator": ^7.10.4 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: af064d06a4a041767ec396a5f258103f64785df290e038bba9f0ef454e6c914f2ac45d862bbdad8fac2c7ad47fa4e95356f29053c60c100a0160b02a995fe2a3 + languageName: node + linkType: hard + +"@babel/plugin-transform-object-rest-spread@npm:^7.22.15": + version: 7.22.15 + resolution: "@babel/plugin-transform-object-rest-spread@npm:7.22.15" + dependencies: + "@babel/compat-data": ^7.22.9 + "@babel/helper-compilation-targets": ^7.22.15 + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/plugin-syntax-object-rest-spread": ^7.8.3 + "@babel/plugin-transform-parameters": ^7.22.15 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 62197a6f12289c1c1bd57f3bed9f0f765ca32390bfe91e0b5561dd94dd9770f4480c4162dec98da094bc0ba99d2c2ebba68de47c019454041b0b7a68ba2ec66d + languageName: node + linkType: hard + "@babel/plugin-transform-object-super@npm:^7.14.5": version: 7.14.5 resolution: "@babel/plugin-transform-object-super@npm:7.14.5" @@ -3789,6 +4567,43 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-object-super@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-object-super@npm:7.22.5" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-replace-supers": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: b71887877d74cb64dbccb5c0324fa67e31171e6a5311991f626650e44a4083e5436a1eaa89da78c0474fb095d4ec322d63ee778b202d33aa2e4194e1ed8e62d7 + languageName: node + linkType: hard + +"@babel/plugin-transform-optional-catch-binding@npm:^7.22.11": + version: 7.22.11 + resolution: "@babel/plugin-transform-optional-catch-binding@npm:7.22.11" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/plugin-syntax-optional-catch-binding": ^7.8.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: f17abd90e1de67c84d63afea29c8021c74abb2794d3a6eeafb0bbe7372d3db32aefca386e392116ec63884537a4a2815d090d26264d259bacc08f6e3ed05294c + languageName: node + linkType: hard + +"@babel/plugin-transform-optional-chaining@npm:^7.22.15": + version: 7.22.15 + resolution: "@babel/plugin-transform-optional-chaining@npm:7.22.15" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-skip-transparent-expression-wrappers": ^7.22.5 + "@babel/plugin-syntax-optional-chaining": ^7.8.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 6b97abe0e50ca2dd8684fcef2c8d12607637e707aa9d513b7035f5e812efbde9305736b438d422103a7844e04124cad5efa4ff0e6226a57afa1210a1c7485c8e + languageName: node + linkType: hard + "@babel/plugin-transform-parameters@npm:^7.12.1, @babel/plugin-transform-parameters@npm:^7.14.5": version: 7.14.5 resolution: "@babel/plugin-transform-parameters@npm:7.14.5" @@ -3833,6 +4648,43 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-parameters@npm:^7.22.15": + version: 7.22.15 + resolution: "@babel/plugin-transform-parameters@npm:7.22.15" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 541188bb7d1876cad87687b5c7daf90f63d8208ae83df24acb1e2b05020ad1c78786b2723ca4054a83fcb74fb6509f30c4cacc5b538ee684224261ad5fb047c1 + languageName: node + linkType: hard + +"@babel/plugin-transform-private-methods@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-private-methods@npm:7.22.5" + dependencies: + "@babel/helper-create-class-features-plugin": ^7.22.5 + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 321479b4fcb6d3b3ef622ab22fd24001e43d46e680e8e41324c033d5810c84646e470f81b44cbcbef5c22e99030784f7cac92f1829974da7a47a60a7139082c3 + languageName: node + linkType: hard + +"@babel/plugin-transform-private-property-in-object@npm:^7.22.11": + version: 7.22.11 + resolution: "@babel/plugin-transform-private-property-in-object@npm:7.22.11" + dependencies: + "@babel/helper-annotate-as-pure": ^7.22.5 + "@babel/helper-create-class-features-plugin": ^7.22.11 + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/plugin-syntax-private-property-in-object": ^7.14.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 4d029d84901e53c46dead7a46e2990a7bc62470f4e4ca58a0d063394f86652fd58fe4eea1eb941da3669cd536b559b9d058b342b59300026346b7a2a51badac8 + languageName: node + linkType: hard + "@babel/plugin-transform-property-literals@npm:^7.14.5": version: 7.14.5 resolution: "@babel/plugin-transform-property-literals@npm:7.14.5" @@ -3866,6 +4718,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-property-literals@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-property-literals@npm:7.22.5" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 796176a3176106f77fcb8cd04eb34a8475ce82d6d03a88db089531b8f0453a2fb8b0c6ec9a52c27948bc0ea478becec449893741fc546dfc3930ab927e3f9f2e + languageName: node + linkType: hard + "@babel/plugin-transform-react-constant-elements@npm:^7.14.5": version: 7.16.0 resolution: "@babel/plugin-transform-react-constant-elements@npm:7.16.0" @@ -3921,6 +4784,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-react-display-name@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-react-display-name@npm:7.22.5" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: a12bfd1e4e93055efca3ace3c34722571bda59d9740dca364d225d9c6e3ca874f134694d21715c42cc63d79efd46db9665bd4a022998767f9245f1e29d5d204d + languageName: node + linkType: hard + "@babel/plugin-transform-react-jsx-development@npm:^7.14.5": version: 7.14.5 resolution: "@babel/plugin-transform-react-jsx-development@npm:7.14.5" @@ -3954,6 +4828,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-react-jsx-development@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-react-jsx-development@npm:7.22.5" + dependencies: + "@babel/plugin-transform-react-jsx": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 36bc3ff0b96bb0ef4723070a50cfdf2e72cfd903a59eba448f9fe92fea47574d6f22efd99364413719e1f3fb3c51b6c9b2990b87af088f8486a84b2a5f9e4560 + languageName: node + linkType: hard + "@babel/plugin-transform-react-jsx-self@npm:^7.18.6": version: 7.21.0 resolution: "@babel/plugin-transform-react-jsx-self@npm:7.21.0" @@ -4021,6 +4906,21 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-react-jsx@npm:^7.22.15, @babel/plugin-transform-react-jsx@npm:^7.22.5": + version: 7.22.15 + resolution: "@babel/plugin-transform-react-jsx@npm:7.22.15" + dependencies: + "@babel/helper-annotate-as-pure": ^7.22.5 + "@babel/helper-module-imports": ^7.22.15 + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/plugin-syntax-jsx": ^7.22.5 + "@babel/types": ^7.22.15 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 3899054e89550c3a0ef041af7c47ee266e2e934f498ee80fefeda778a6aa177b48aa8b4d2a8bf5848de977fec564571699ab952d9fa089c4c19b45ddb121df09 + languageName: node + linkType: hard + "@babel/plugin-transform-react-pure-annotations@npm:^7.14.5": version: 7.14.5 resolution: "@babel/plugin-transform-react-pure-annotations@npm:7.14.5" @@ -4057,6 +4957,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-react-pure-annotations@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-react-pure-annotations@npm:7.22.5" + dependencies: + "@babel/helper-annotate-as-pure": ^7.22.5 + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 092021c4f404e267002099ec20b3f12dd730cb90b0d83c5feed3dc00dbe43b9c42c795a18e7c6c7d7bddea20c7dd56221b146aec81b37f2e7eb5137331c61120 + languageName: node + linkType: hard + "@babel/plugin-transform-regenerator@npm:^7.14.5": version: 7.14.5 resolution: "@babel/plugin-transform-regenerator@npm:7.14.5" @@ -4091,6 +5003,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-regenerator@npm:^7.22.10": + version: 7.22.10 + resolution: "@babel/plugin-transform-regenerator@npm:7.22.10" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + regenerator-transform: ^0.15.2 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: e13678d62d6fa96f11cb8b863f00e8693491e7adc88bfca3f2820f80cbac8336e7dec3a596eee6a1c4663b7ececc3564f2cd7fb44ed6d4ce84ac2bb7f39ecc6e + languageName: node + linkType: hard + "@babel/plugin-transform-reserved-words@npm:^7.14.5": version: 7.14.5 resolution: "@babel/plugin-transform-reserved-words@npm:7.14.5" @@ -4124,6 +5048,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-reserved-words@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-reserved-words@npm:7.22.5" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 3ffd7dbc425fe8132bfec118b9817572799cab1473113a635d25ab606c1f5a2341a636c04cf6b22df3813320365ed5a965b5eeb3192320a10e4cc2c137bd8bfc + languageName: node + linkType: hard + "@babel/plugin-transform-runtime@npm:7.15.0": version: 7.15.0 resolution: "@babel/plugin-transform-runtime@npm:7.15.0" @@ -4173,6 +5108,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-shorthand-properties@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-shorthand-properties@npm:7.22.5" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: a5ac902c56ea8effa99f681340ee61bac21094588f7aef0bc01dff98246651702e677552fa6d10e548c4ac22a3ffad047dd2f8c8f0540b68316c2c203e56818b + languageName: node + linkType: hard + "@babel/plugin-transform-spread@npm:^7.12.1, @babel/plugin-transform-spread@npm:^7.14.6": version: 7.14.6 resolution: "@babel/plugin-transform-spread@npm:7.14.6" @@ -4209,6 +5155,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-spread@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-spread@npm:7.22.5" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-skip-transparent-expression-wrappers": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 5587f0deb60b3dfc9b274e269031cc45ec75facccf1933ea2ea71ced9fd3ce98ed91bb36d6cd26817c14474b90ed998c5078415f0eab531caf301496ce24c95c + languageName: node + linkType: hard + "@babel/plugin-transform-sticky-regex@npm:^7.14.5": version: 7.14.5 resolution: "@babel/plugin-transform-sticky-regex@npm:7.14.5" @@ -4242,6 +5200,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-sticky-regex@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-sticky-regex@npm:7.22.5" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 63b2c575e3e7f96c32d52ed45ee098fb7d354b35c2223b8c8e76840b32cc529ee0c0ceb5742fd082e56e91e3d82842a367ce177e82b05039af3d602c9627a729 + languageName: node + linkType: hard + "@babel/plugin-transform-template-literals@npm:^7.12.1, @babel/plugin-transform-template-literals@npm:^7.14.5": version: 7.14.5 resolution: "@babel/plugin-transform-template-literals@npm:7.14.5" @@ -4275,6 +5244,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-template-literals@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-template-literals@npm:7.22.5" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 27e9bb030654cb425381c69754be4abe6a7c75b45cd7f962cd8d604b841b2f0fb7b024f2efc1c25cc53f5b16d79d5e8cfc47cacbdaa983895b3aeefa3e7e24ff + languageName: node + linkType: hard + "@babel/plugin-transform-typeof-symbol@npm:^7.14.5": version: 7.14.5 resolution: "@babel/plugin-transform-typeof-symbol@npm:7.14.5" @@ -4308,6 +5288,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-typeof-symbol@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-typeof-symbol@npm:7.22.5" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 82a53a63ffc3010b689ca9a54e5f53b2718b9f4b4a9818f36f9b7dba234f38a01876680553d2716a645a61920b5e6e4aaf8d4a0064add379b27ca0b403049512 + languageName: node + linkType: hard + "@babel/plugin-transform-typescript@npm:^7.15.0": version: 7.15.0 resolution: "@babel/plugin-transform-typescript@npm:7.15.0" @@ -4347,6 +5338,20 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-typescript@npm:^7.22.15": + version: 7.22.15 + resolution: "@babel/plugin-transform-typescript@npm:7.22.15" + dependencies: + "@babel/helper-annotate-as-pure": ^7.22.5 + "@babel/helper-create-class-features-plugin": ^7.22.15 + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/plugin-syntax-typescript": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: c5d96cdbf0e1512707aa1c1e3ac6b370a25fd9c545d26008ce44eb13a47bd7fd67a1eb799c98b5ccc82e33a345fda55c0055e1fe3ed97646ed405dd13020b226 + languageName: node + linkType: hard + "@babel/plugin-transform-unicode-escapes@npm:^7.14.5": version: 7.14.5 resolution: "@babel/plugin-transform-unicode-escapes@npm:7.14.5" @@ -4380,6 +5385,29 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-unicode-escapes@npm:^7.22.10": + version: 7.22.10 + resolution: "@babel/plugin-transform-unicode-escapes@npm:7.22.10" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 807f40ed1324c8cb107c45358f1903384ca3f0ef1d01c5a3c5c9b271c8d8eec66936a3dcc8d75ddfceea9421420368c2e77ae3adef0a50557e778dfe296bf382 + languageName: node + linkType: hard + +"@babel/plugin-transform-unicode-property-regex@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-unicode-property-regex@npm:7.22.5" + dependencies: + "@babel/helper-create-regexp-features-plugin": ^7.22.5 + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 2495e5f663cb388e3d888b4ba3df419ac436a5012144ac170b622ddfc221f9ea9bdba839fa2bc0185cb776b578030666406452ec7791cbf0e7a3d4c88ae9574c + languageName: node + linkType: hard + "@babel/plugin-transform-unicode-regex@npm:^7.14.5": version: 7.14.5 resolution: "@babel/plugin-transform-unicode-regex@npm:7.14.5" @@ -4416,6 +5444,30 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-unicode-regex@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-unicode-regex@npm:7.22.5" + dependencies: + "@babel/helper-create-regexp-features-plugin": ^7.22.5 + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 6b5d1404c8c623b0ec9bd436c00d885a17d6a34f3f2597996343ddb9d94f6379705b21582dfd4cec2c47fd34068872e74ab6b9580116c0566b3f9447e2a7fa06 + languageName: node + linkType: hard + +"@babel/plugin-transform-unicode-sets-regex@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-unicode-sets-regex@npm:7.22.5" + dependencies: + "@babel/helper-create-regexp-features-plugin": ^7.22.5 + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: c042070f980b139547f8b0179efbc049ac5930abec7fc26ed7a41d89a048d8ab17d362200e204b6f71c3c20d6991a0e74415e1a412a49adc8131c2a40c04822e + languageName: node + linkType: hard + "@babel/preset-env@npm:7.15.0, @babel/preset-env@npm:^7.12.11": version: 7.15.0 resolution: "@babel/preset-env@npm:7.15.0" @@ -4753,6 +5805,96 @@ __metadata: languageName: node linkType: hard +"@babel/preset-env@npm:^7.22.20, @babel/preset-env@npm:^7.22.9": + version: 7.22.20 + resolution: "@babel/preset-env@npm:7.22.20" + dependencies: + "@babel/compat-data": ^7.22.20 + "@babel/helper-compilation-targets": ^7.22.15 + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-validator-option": ^7.22.15 + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": ^7.22.15 + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": ^7.22.15 + "@babel/plugin-proposal-private-property-in-object": 7.21.0-placeholder-for-preset-env.2 + "@babel/plugin-syntax-async-generators": ^7.8.4 + "@babel/plugin-syntax-class-properties": ^7.12.13 + "@babel/plugin-syntax-class-static-block": ^7.14.5 + "@babel/plugin-syntax-dynamic-import": ^7.8.3 + "@babel/plugin-syntax-export-namespace-from": ^7.8.3 + "@babel/plugin-syntax-import-assertions": ^7.22.5 + "@babel/plugin-syntax-import-attributes": ^7.22.5 + "@babel/plugin-syntax-import-meta": ^7.10.4 + "@babel/plugin-syntax-json-strings": ^7.8.3 + "@babel/plugin-syntax-logical-assignment-operators": ^7.10.4 + "@babel/plugin-syntax-nullish-coalescing-operator": ^7.8.3 + "@babel/plugin-syntax-numeric-separator": ^7.10.4 + "@babel/plugin-syntax-object-rest-spread": ^7.8.3 + "@babel/plugin-syntax-optional-catch-binding": ^7.8.3 + "@babel/plugin-syntax-optional-chaining": ^7.8.3 + "@babel/plugin-syntax-private-property-in-object": ^7.14.5 + "@babel/plugin-syntax-top-level-await": ^7.14.5 + "@babel/plugin-syntax-unicode-sets-regex": ^7.18.6 + "@babel/plugin-transform-arrow-functions": ^7.22.5 + "@babel/plugin-transform-async-generator-functions": ^7.22.15 + "@babel/plugin-transform-async-to-generator": ^7.22.5 + "@babel/plugin-transform-block-scoped-functions": ^7.22.5 + "@babel/plugin-transform-block-scoping": ^7.22.15 + "@babel/plugin-transform-class-properties": ^7.22.5 + "@babel/plugin-transform-class-static-block": ^7.22.11 + "@babel/plugin-transform-classes": ^7.22.15 + "@babel/plugin-transform-computed-properties": ^7.22.5 + "@babel/plugin-transform-destructuring": ^7.22.15 + "@babel/plugin-transform-dotall-regex": ^7.22.5 + "@babel/plugin-transform-duplicate-keys": ^7.22.5 + "@babel/plugin-transform-dynamic-import": ^7.22.11 + "@babel/plugin-transform-exponentiation-operator": ^7.22.5 + "@babel/plugin-transform-export-namespace-from": ^7.22.11 + "@babel/plugin-transform-for-of": ^7.22.15 + "@babel/plugin-transform-function-name": ^7.22.5 + "@babel/plugin-transform-json-strings": ^7.22.11 + "@babel/plugin-transform-literals": ^7.22.5 + "@babel/plugin-transform-logical-assignment-operators": ^7.22.11 + "@babel/plugin-transform-member-expression-literals": ^7.22.5 + "@babel/plugin-transform-modules-amd": ^7.22.5 + "@babel/plugin-transform-modules-commonjs": ^7.22.15 + "@babel/plugin-transform-modules-systemjs": ^7.22.11 + "@babel/plugin-transform-modules-umd": ^7.22.5 + "@babel/plugin-transform-named-capturing-groups-regex": ^7.22.5 + "@babel/plugin-transform-new-target": ^7.22.5 + "@babel/plugin-transform-nullish-coalescing-operator": ^7.22.11 + "@babel/plugin-transform-numeric-separator": ^7.22.11 + "@babel/plugin-transform-object-rest-spread": ^7.22.15 + "@babel/plugin-transform-object-super": ^7.22.5 + "@babel/plugin-transform-optional-catch-binding": ^7.22.11 + "@babel/plugin-transform-optional-chaining": ^7.22.15 + "@babel/plugin-transform-parameters": ^7.22.15 + "@babel/plugin-transform-private-methods": ^7.22.5 + "@babel/plugin-transform-private-property-in-object": ^7.22.11 + "@babel/plugin-transform-property-literals": ^7.22.5 + "@babel/plugin-transform-regenerator": ^7.22.10 + "@babel/plugin-transform-reserved-words": ^7.22.5 + "@babel/plugin-transform-shorthand-properties": ^7.22.5 + "@babel/plugin-transform-spread": ^7.22.5 + "@babel/plugin-transform-sticky-regex": ^7.22.5 + "@babel/plugin-transform-template-literals": ^7.22.5 + "@babel/plugin-transform-typeof-symbol": ^7.22.5 + "@babel/plugin-transform-unicode-escapes": ^7.22.10 + "@babel/plugin-transform-unicode-property-regex": ^7.22.5 + "@babel/plugin-transform-unicode-regex": ^7.22.5 + "@babel/plugin-transform-unicode-sets-regex": ^7.22.5 + "@babel/preset-modules": 0.1.6-no-external-plugins + "@babel/types": ^7.22.19 + babel-plugin-polyfill-corejs2: ^0.4.5 + babel-plugin-polyfill-corejs3: ^0.8.3 + babel-plugin-polyfill-regenerator: ^0.5.2 + core-js-compat: ^3.31.0 + semver: ^6.3.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 99357a5cb30f53bacdc0d1cd6dff0f052ea6c2d1ba874d969bba69897ef716e87283e84a59dc52fb49aa31fd1b6f55ed756c64c04f5678380700239f6030b881 + languageName: node + linkType: hard + "@babel/preset-flow@npm:^7.12.1": version: 7.14.5 resolution: "@babel/preset-flow@npm:7.14.5" @@ -4766,6 +5908,32 @@ __metadata: languageName: node linkType: hard +"@babel/preset-flow@npm:^7.13.13": + version: 7.22.15 + resolution: "@babel/preset-flow@npm:7.22.15" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-validator-option": ^7.22.15 + "@babel/plugin-transform-flow-strip-types": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 17f8b80b1012802f983227b423c8823990db9748aec4f8bfd56ff774d8d954e9bdea67377788abac526754b3d307215c063c9beadf5f1b4331b30d4ba0593286 + languageName: node + linkType: hard + +"@babel/preset-modules@npm:0.1.6-no-external-plugins": + version: 0.1.6-no-external-plugins + resolution: "@babel/preset-modules@npm:0.1.6-no-external-plugins" + dependencies: + "@babel/helper-plugin-utils": ^7.0.0 + "@babel/types": ^7.4.4 + esutils: ^2.0.2 + peerDependencies: + "@babel/core": ^7.0.0-0 || ^8.0.0-0 <8.0.0 + checksum: 4855e799bc50f2449fb5210f78ea9e8fd46cf4f242243f1e2ed838e2bd702e25e73e822e7f8447722a5f4baa5e67a8f7a0e403f3e7ce04540ff743a9c411c375 + languageName: node + linkType: hard + "@babel/preset-modules@npm:^0.1.4": version: 0.1.4 resolution: "@babel/preset-modules@npm:0.1.4" @@ -4844,6 +6012,22 @@ __metadata: languageName: node linkType: hard +"@babel/preset-react@npm:^7.22.15": + version: 7.22.15 + resolution: "@babel/preset-react@npm:7.22.15" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-validator-option": ^7.22.15 + "@babel/plugin-transform-react-display-name": ^7.22.5 + "@babel/plugin-transform-react-jsx": ^7.22.15 + "@babel/plugin-transform-react-jsx-development": ^7.22.5 + "@babel/plugin-transform-react-pure-annotations": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: c3ef99dfa2e9f57d2e08603e883aa20f47630a826c8e413888a93ae6e0084b5016871e463829be125329d40a1ba0a89f7c43d77b6dab52083c225cb43e63d10e + languageName: node + linkType: hard + "@babel/preset-typescript@npm:7.15.0, @babel/preset-typescript@npm:^7.12.7": version: 7.15.0 resolution: "@babel/preset-typescript@npm:7.15.0" @@ -4857,6 +6041,21 @@ __metadata: languageName: node linkType: hard +"@babel/preset-typescript@npm:^7.13.0, @babel/preset-typescript@npm:^7.22.15": + version: 7.22.15 + resolution: "@babel/preset-typescript@npm:7.22.15" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-validator-option": ^7.22.15 + "@babel/plugin-syntax-jsx": ^7.22.5 + "@babel/plugin-transform-modules-commonjs": ^7.22.15 + "@babel/plugin-transform-typescript": ^7.22.15 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 02ac4d5c812a52357c8f517f81584725f06f385d54ccfda89dd082e0ed89a94bd9f4d9b05fa1cbdcf426e3489c1921f04c93c5acc5deea83407a64c22ad2feb4 + languageName: node + linkType: hard + "@babel/preset-typescript@npm:^7.15.0, @babel/preset-typescript@npm:^7.16.0": version: 7.16.0 resolution: "@babel/preset-typescript@npm:7.16.0" @@ -4898,6 +6097,28 @@ __metadata: languageName: node linkType: hard +"@babel/register@npm:^7.13.16": + version: 7.22.15 + resolution: "@babel/register@npm:7.22.15" + dependencies: + clone-deep: ^4.0.1 + find-cache-dir: ^2.0.0 + make-dir: ^2.1.0 + pirates: ^4.0.5 + source-map-support: ^0.5.16 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 5497be6773608cd2d874210edd14499fce464ddbea170219da55955afe4c9173adb591164193458fd639e43b7d1314088a6186f4abf241476c59b3f0da6afd6f + languageName: node + linkType: hard + +"@babel/regjsgen@npm:^0.8.0": + version: 0.8.0 + resolution: "@babel/regjsgen@npm:0.8.0" + checksum: 89c338fee774770e5a487382170711014d49a68eb281e74f2b5eac88f38300a4ad545516a7786a8dd5702e9cf009c94c2f582d200f077ac5decd74c56b973730 + languageName: node + linkType: hard + "@babel/runtime-corejs3@npm:^7.10.2": version: 7.15.3 resolution: "@babel/runtime-corejs3@npm:7.15.3" @@ -5026,6 +6247,17 @@ __metadata: languageName: node linkType: hard +"@babel/template@npm:^7.22.15": + version: 7.22.15 + resolution: "@babel/template@npm:7.22.15" + dependencies: + "@babel/code-frame": ^7.22.13 + "@babel/parser": ^7.22.15 + "@babel/types": ^7.22.15 + checksum: 1f3e7dcd6c44f5904c184b3f7fe280394b191f2fed819919ffa1e529c259d5b197da8981b6ca491c235aee8dbad4a50b7e31304aa531271cb823a4a24a0dd8fd + languageName: node + linkType: hard + "@babel/template@npm:^7.22.5": version: 7.22.5 resolution: "@babel/template@npm:7.22.5" @@ -5197,6 +6429,24 @@ __metadata: languageName: node linkType: hard +"@babel/traverse@npm:^7.22.15, @babel/traverse@npm:^7.22.20, @babel/traverse@npm:^7.22.8": + version: 7.22.20 + resolution: "@babel/traverse@npm:7.22.20" + dependencies: + "@babel/code-frame": ^7.22.13 + "@babel/generator": ^7.22.15 + "@babel/helper-environment-visitor": ^7.22.20 + "@babel/helper-function-name": ^7.22.5 + "@babel/helper-hoist-variables": ^7.22.5 + "@babel/helper-split-export-declaration": ^7.22.6 + "@babel/parser": ^7.22.16 + "@babel/types": ^7.22.19 + debug: ^4.1.0 + globals: ^11.1.0 + checksum: 97da9afa7f8f505ce52c36ac2531129bc4a0e250880aaf9b467dc044f30a5bce2b756c1af4d961958bc225659546e811a7d536ab3d920fd60921087989b841b9 + languageName: node + linkType: hard + "@babel/traverse@npm:^7.22.5": version: 7.22.5 resolution: "@babel/traverse@npm:7.22.5" @@ -5322,6 +6572,17 @@ __metadata: languageName: node linkType: hard +"@babel/types@npm:^7.22.15, @babel/types@npm:^7.22.19": + version: 7.22.19 + resolution: "@babel/types@npm:7.22.19" + dependencies: + "@babel/helper-string-parser": ^7.22.5 + "@babel/helper-validator-identifier": ^7.22.19 + to-fast-properties: ^2.0.0 + checksum: 2d69740e69b55ba36ece0c17d5afb7b7213b34297157df39ef9ba24965aff677c56f014413052ecc5b2fbbf26910c63e5bb24a969df84d7a17153750cf75915e + languageName: node + linkType: hard + "@babel/types@npm:^7.8.3": version: 7.22.10 resolution: "@babel/types@npm:7.22.10" @@ -6352,6 +7613,15 @@ __metadata: languageName: node linkType: hard +"@emotion/use-insertion-effect-with-fallbacks@npm:^1.0.0": + version: 1.0.1 + resolution: "@emotion/use-insertion-effect-with-fallbacks@npm:1.0.1" + peerDependencies: + react: ">=16.8.0" + checksum: 700b6e5bbb37a9231f203bb3af11295eed01d73b2293abece0bc2a2237015e944d7b5114d4887ad9a79776504aa51ed2a8b0ddbc117c54495dd01a6b22f93786 + languageName: node + linkType: hard + "@emotion/utils@npm:0.11.3": version: 0.11.3 resolution: "@emotion/utils@npm:0.11.3" @@ -6425,6 +7695,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/android-arm64@npm:0.18.20" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/android-arm@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/android-arm@npm:0.16.17" @@ -6453,6 +7730,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/android-arm@npm:0.18.20" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + "@esbuild/android-x64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/android-x64@npm:0.16.17" @@ -6481,6 +7765,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/android-x64@npm:0.18.20" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + "@esbuild/darwin-arm64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/darwin-arm64@npm:0.16.17" @@ -6509,6 +7800,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-arm64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/darwin-arm64@npm:0.18.20" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/darwin-x64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/darwin-x64@npm:0.16.17" @@ -6537,6 +7835,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/darwin-x64@npm:0.18.20" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + "@esbuild/freebsd-arm64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/freebsd-arm64@npm:0.16.17" @@ -6565,6 +7870,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-arm64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/freebsd-arm64@npm:0.18.20" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/freebsd-x64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/freebsd-x64@npm:0.16.17" @@ -6593,6 +7905,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/freebsd-x64@npm:0.18.20" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/linux-arm64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/linux-arm64@npm:0.16.17" @@ -6621,6 +7940,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-arm64@npm:0.18.20" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/linux-arm@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/linux-arm@npm:0.16.17" @@ -6649,6 +7975,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-arm@npm:0.18.20" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + "@esbuild/linux-ia32@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/linux-ia32@npm:0.16.17" @@ -6677,6 +8010,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ia32@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-ia32@npm:0.18.20" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/linux-loong64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/linux-loong64@npm:0.16.17" @@ -6705,6 +8045,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-loong64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-loong64@npm:0.18.20" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + "@esbuild/linux-mips64el@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/linux-mips64el@npm:0.16.17" @@ -6733,6 +8080,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-mips64el@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-mips64el@npm:0.18.20" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + "@esbuild/linux-ppc64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/linux-ppc64@npm:0.16.17" @@ -6761,6 +8115,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ppc64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-ppc64@npm:0.18.20" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + "@esbuild/linux-riscv64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/linux-riscv64@npm:0.16.17" @@ -6789,6 +8150,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-riscv64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-riscv64@npm:0.18.20" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + "@esbuild/linux-s390x@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/linux-s390x@npm:0.16.17" @@ -6817,6 +8185,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-s390x@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-s390x@npm:0.18.20" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + "@esbuild/linux-x64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/linux-x64@npm:0.16.17" @@ -6845,6 +8220,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-x64@npm:0.18.20" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + "@esbuild/netbsd-x64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/netbsd-x64@npm:0.16.17" @@ -6873,6 +8255,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/netbsd-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/netbsd-x64@npm:0.18.20" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/openbsd-x64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/openbsd-x64@npm:0.16.17" @@ -6901,6 +8290,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/openbsd-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/openbsd-x64@npm:0.18.20" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/sunos-x64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/sunos-x64@npm:0.16.17" @@ -6929,6 +8325,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/sunos-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/sunos-x64@npm:0.18.20" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + "@esbuild/win32-arm64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/win32-arm64@npm:0.16.17" @@ -6957,6 +8360,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-arm64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/win32-arm64@npm:0.18.20" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/win32-ia32@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/win32-ia32@npm:0.16.17" @@ -6985,6 +8395,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-ia32@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/win32-ia32@npm:0.18.20" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/win32-x64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/win32-x64@npm:0.16.17" @@ -7013,6 +8430,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/win32-x64@npm:0.18.20" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@eslint-community/eslint-utils@npm:^4.1.2": version: 4.2.0 resolution: "@eslint-community/eslint-utils@npm:4.2.0" @@ -7107,6 +8531,13 @@ __metadata: languageName: node linkType: hard +"@fal-works/esbuild-plugin-global-externals@npm:^2.1.2": + version: 2.1.2 + resolution: "@fal-works/esbuild-plugin-global-externals@npm:2.1.2" + checksum: c59715902b9062aa7ff38973f298b509499fd146dbf564dc338b3f9e896da5bffb4ca676c27587fde79b3586003e24d65960acb62f009bca43dca34c76f8cbf7 + languageName: node + linkType: hard + "@figspec/components@npm:^1.0.0": version: 1.0.1 resolution: "@figspec/components@npm:1.0.1" @@ -7813,6 +9244,13 @@ __metadata: languageName: node linkType: hard +"@juggle/resize-observer@npm:^3.3.1": + version: 3.4.0 + resolution: "@juggle/resize-observer@npm:3.4.0" + checksum: 2505028c05cc2e17639fcad06218b1c4b60f932a4ebb4b41ab546ef8c157031ae377e3f560903801f6d01706dbefd4943b6c4704bf19ed86dfa1c62f1473a570 + languageName: node + linkType: hard + "@koralabs/handles-public-api-interfaces@npm:^1.6.6": version: 1.6.6 resolution: "@koralabs/handles-public-api-interfaces@npm:1.6.6" @@ -7841,6 +9279,7 @@ __metadata: "@lace/common": 0.1.0 "@lace/core": 0.1.0 "@lace/staking": 0.1.0 + "@lace/ui": ^0.1.0 "@react-rxjs/core": ^0.9.8 "@react-rxjs/utils": ^0.9.5 "@types/dotenv-webpack": 7.0.3 @@ -7961,7 +9400,25 @@ __metadata: resolution: "@lace/core@workspace:packages/core" dependencies: "@ant-design/icons": ^4.7.0 + "@babel/preset-env": ^7.22.20 + "@babel/preset-react": ^7.22.15 + "@babel/preset-typescript": ^7.22.15 "@lace/common": 0.1.0 + "@lace/ui": ^0.1.0 + "@storybook/addon-actions": ^6.5.16 + "@storybook/addon-essentials": ^6.5.16 + "@storybook/addon-interactions": ^6.5.16 + "@storybook/addon-links": ^6.5.16 + "@storybook/addon-styling": ^1.3.7 + "@storybook/addon-styling-webpack": ^0.0.5 + "@storybook/builder-webpack5": 6.5.16 + "@storybook/core-events": ^6.5.16 + "@storybook/jest": ^0.0.10 + "@storybook/manager-webpack5": 6.5.16 + "@storybook/react": ^6.5.16 + "@storybook/test-runner": ^0.10.0 + "@storybook/testing-library": ^0.0.13 + "@types/babel__preset-env": ^7 "@types/debounce-promise": ^3.1.6 antd: ^4.24.10 axios: 0.21.4 @@ -7976,6 +9433,8 @@ __metadata: react-dom: 17.0.2 react-i18next: 11.11.4 react-infinite-scroll-component: ^6.1.0 + sass: ^1.68.0 + storybook: ^7.4.3 typescript: ^4.3.5 zxcvbn: ^4.4.2 peerDependencies: @@ -8392,6 +9851,17 @@ __metadata: languageName: node linkType: hard +"@ndelangen/get-tarball@npm:^3.0.7": + version: 3.0.9 + resolution: "@ndelangen/get-tarball@npm:3.0.9" + dependencies: + gunzip-maybe: ^1.4.2 + pump: ^3.0.0 + tar-fs: ^2.1.1 + checksum: 7fa8ac40b4e85738a4ee6bf891bc27fce2445b65b4477e0ec86aed0fa62ab18bdf5d193ce04553ad9bfa639e1eef33b8b30da4ef3e7218f12bf95f24c8786e5b + languageName: node + linkType: hard + "@noble/hashes@npm:^1.2.0": version: 1.3.1 resolution: "@noble/hashes@npm:1.3.1" @@ -9468,6 +10938,34 @@ __metadata: languageName: node linkType: hard +"@radix-ui/react-roving-focus@npm:1.0.4": + version: 1.0.4 + resolution: "@radix-ui/react-roving-focus@npm:1.0.4" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/primitive": 1.0.1 + "@radix-ui/react-collection": 1.0.3 + "@radix-ui/react-compose-refs": 1.0.1 + "@radix-ui/react-context": 1.0.1 + "@radix-ui/react-direction": 1.0.1 + "@radix-ui/react-id": 1.0.1 + "@radix-ui/react-primitive": 1.0.3 + "@radix-ui/react-use-callback-ref": 1.0.1 + "@radix-ui/react-use-controllable-state": 1.0.1 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 69b1c82c2d9db3ba71549a848f2704200dab1b2cd22d050c1e081a78b9a567dbfdc7fd0403ee010c19b79652de69924d8ca2076cd031d6552901e4213493ffc7 + languageName: node + linkType: hard + "@radix-ui/react-scroll-area@npm:^0.1.3": version: 0.1.4 resolution: "@radix-ui/react-scroll-area@npm:0.1.4" @@ -9488,6 +10986,66 @@ __metadata: languageName: node linkType: hard +"@radix-ui/react-select@npm:^1.2.2": + version: 1.2.2 + resolution: "@radix-ui/react-select@npm:1.2.2" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/number": 1.0.1 + "@radix-ui/primitive": 1.0.1 + "@radix-ui/react-collection": 1.0.3 + "@radix-ui/react-compose-refs": 1.0.1 + "@radix-ui/react-context": 1.0.1 + "@radix-ui/react-direction": 1.0.1 + "@radix-ui/react-dismissable-layer": 1.0.4 + "@radix-ui/react-focus-guards": 1.0.1 + "@radix-ui/react-focus-scope": 1.0.3 + "@radix-ui/react-id": 1.0.1 + "@radix-ui/react-popper": 1.1.2 + "@radix-ui/react-portal": 1.0.3 + "@radix-ui/react-primitive": 1.0.3 + "@radix-ui/react-slot": 1.0.2 + "@radix-ui/react-use-callback-ref": 1.0.1 + "@radix-ui/react-use-controllable-state": 1.0.1 + "@radix-ui/react-use-layout-effect": 1.0.1 + "@radix-ui/react-use-previous": 1.0.1 + "@radix-ui/react-visually-hidden": 1.0.3 + aria-hidden: ^1.1.1 + react-remove-scroll: 2.5.5 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: af7b63cc9e2c6006ec08163392d244941e9e03534e7add1b7c5a86059d0eb8a0398d4f3e80d43ff22126874a02b985e44f1722d1de9218922f7aa653d09412e3 + languageName: node + linkType: hard + +"@radix-ui/react-separator@npm:1.0.3": + version: 1.0.3 + resolution: "@radix-ui/react-separator@npm:1.0.3" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/react-primitive": 1.0.3 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 42f8c95e404de2ce9387040d78049808a48d423cd4c3bad8cca92c4b0bcbdcb3566b5b52a920d4e939a74b51188697f20a012221f0e630fc7f56de64096c15d2 + languageName: node + linkType: hard + "@radix-ui/react-slider@npm:^1.1.2": version: 1.1.2 resolution: "@radix-ui/react-slider@npm:1.1.2" @@ -9635,6 +11193,80 @@ __metadata: languageName: node linkType: hard +"@radix-ui/react-toggle-group@npm:1.0.4": + version: 1.0.4 + resolution: "@radix-ui/react-toggle-group@npm:1.0.4" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/primitive": 1.0.1 + "@radix-ui/react-context": 1.0.1 + "@radix-ui/react-direction": 1.0.1 + "@radix-ui/react-primitive": 1.0.3 + "@radix-ui/react-roving-focus": 1.0.4 + "@radix-ui/react-toggle": 1.0.3 + "@radix-ui/react-use-controllable-state": 1.0.1 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: b6c11fbbc3ca857ff68c0fa31f293c0d0111bcc8aa0cde2566214c090907530bfcb3b862f81585c2b02d8989b5c7971acff4d5c07c429870d80bd5602e30d376 + languageName: node + linkType: hard + +"@radix-ui/react-toggle@npm:1.0.3": + version: 1.0.3 + resolution: "@radix-ui/react-toggle@npm:1.0.3" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/primitive": 1.0.1 + "@radix-ui/react-primitive": 1.0.3 + "@radix-ui/react-use-controllable-state": 1.0.1 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: ed5407f48254f20cda542017774f259d0b2c0007ea4bd7287d10d751016dbf269cb13d1142591432c269c3ab768cde2f1ba0344743027d36bbec10af909f19de + languageName: node + linkType: hard + +"@radix-ui/react-toolbar@npm:^1.0.4": + version: 1.0.4 + resolution: "@radix-ui/react-toolbar@npm:1.0.4" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/primitive": 1.0.1 + "@radix-ui/react-context": 1.0.1 + "@radix-ui/react-direction": 1.0.1 + "@radix-ui/react-primitive": 1.0.3 + "@radix-ui/react-roving-focus": 1.0.4 + "@radix-ui/react-separator": 1.0.3 + "@radix-ui/react-toggle-group": 1.0.4 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 7ebee1f8add6510108979433c5b38627e2de9d48ef2172ca15274b9edbbc106ff43bcd47ff733b03ed2215b92e7af364ff82c79e5a1728374847e2b1e315552c + languageName: node + linkType: hard + "@radix-ui/react-tooltip@npm:^1.0.6": version: 1.0.6 resolution: "@radix-ui/react-tooltip@npm:1.0.6" @@ -10539,6 +12171,62 @@ __metadata: languageName: node linkType: hard +"@storybook/addon-styling-webpack@npm:^0.0.5": + version: 0.0.5 + resolution: "@storybook/addon-styling-webpack@npm:0.0.5" + dependencies: + "@storybook/node-logger": ^7.0.12 + peerDependencies: + webpack: ^5.0.0 + checksum: 43462f6b05b660f9e6a8d70640efeedfaad314ebe65d7a172d84213c9ff9f1f78d69ec5d242914ea704ec6ae9c4fc12cf0a7130d10d29367397e5e2babe2b2ed + languageName: node + linkType: hard + +"@storybook/addon-styling@npm:^1.3.7": + version: 1.3.7 + resolution: "@storybook/addon-styling@npm:1.3.7" + dependencies: + "@babel/template": ^7.20.7 + "@babel/types": ^7.21.5 + "@storybook/api": ^7.0.12 + "@storybook/components": ^7.0.12 + "@storybook/core-common": ^7.0.12 + "@storybook/core-events": ^7.0.12 + "@storybook/manager-api": ^7.0.12 + "@storybook/node-logger": ^7.0.12 + "@storybook/preview-api": ^7.0.12 + "@storybook/theming": ^7.0.12 + "@storybook/types": ^7.0.12 + css-loader: ^6.7.3 + less-loader: ^11.1.0 + postcss-loader: ^7.2.4 + prettier: ^2.8.0 + resolve-url-loader: ^5.0.0 + sass-loader: ^13.2.2 + style-loader: ^3.3.2 + peerDependencies: + less: ^3.5.0 || ^4.0.0 + postcss: ^7.0.0 || ^8.0.1 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + webpack: ^5.0.0 + peerDependenciesMeta: + less: + optional: true + postcss: + optional: true + react: + optional: true + react-dom: + optional: true + webpack: + optional: true + bin: + addon-styling-setup: postinstall.js + checksum: 5bac1a4109d15a6371260270518b2eada77ab153ddd0b75bb3c4ae31ee8ea61b6acacbf258add5166ae8b8450654fffc6be105c08c2b0ebdc2540abf6dba15a1 + languageName: node + linkType: hard + "@storybook/addon-toolbars@npm:6.5.16": version: 6.5.16 resolution: "@storybook/addon-toolbars@npm:6.5.16" @@ -10840,6 +12528,48 @@ __metadata: languageName: node linkType: hard +"@storybook/api@npm:^7.0.12": + version: 7.5.0 + resolution: "@storybook/api@npm:7.5.0" + dependencies: + "@storybook/client-logger": 7.5.0 + "@storybook/manager-api": 7.5.0 + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + checksum: 82fafa5963324d33f4860d2938dd8a4b6d70b810e5ec7678f676a0f288dfd9a5f1265a122d6341c9fff19be63cd2b0dcb0cc6549c840a1d6928290a3ffcda52f + languageName: node + linkType: hard + +"@storybook/builder-manager@npm:7.4.3": + version: 7.4.3 + resolution: "@storybook/builder-manager@npm:7.4.3" + dependencies: + "@fal-works/esbuild-plugin-global-externals": ^2.1.2 + "@storybook/core-common": 7.4.3 + "@storybook/manager": 7.4.3 + "@storybook/node-logger": 7.4.3 + "@types/ejs": ^3.1.1 + "@types/find-cache-dir": ^3.2.1 + "@yarnpkg/esbuild-plugin-pnp": ^3.0.0-rc.10 + browser-assert: ^1.2.1 + ejs: ^3.1.8 + esbuild: ^0.18.0 + esbuild-plugin-alias: ^0.2.1 + express: ^4.17.3 + find-cache-dir: ^3.0.0 + fs-extra: ^11.1.0 + process: ^0.11.10 + util: ^0.12.4 + checksum: bdbdf39f997e088aac54591e0705300f21bb01b52df46f125beadb45f2f90fb8afbd35ae1e514cc4c29c9fb2105fa3697482712157196d628583249c2a2b5fa2 + languageName: node + linkType: hard + "@storybook/builder-webpack4@npm:6.5.10": version: 6.5.10 resolution: "@storybook/builder-webpack4@npm:6.5.10" @@ -11146,6 +12876,86 @@ __metadata: languageName: node linkType: hard +"@storybook/channels@npm:7.4.3": + version: 7.4.3 + resolution: "@storybook/channels@npm:7.4.3" + dependencies: + "@storybook/client-logger": 7.4.3 + "@storybook/core-events": 7.4.3 + "@storybook/global": ^5.0.0 + qs: ^6.10.0 + telejson: ^7.2.0 + tiny-invariant: ^1.3.1 + checksum: 4028d8c96dd71b72fc3d27d346ce88ea73c5042648da3ea80c960b4bfee99de1e782aed03b775023616b753e3c6089f3c305c5635da1af0040270cfddd768fdd + languageName: node + linkType: hard + +"@storybook/channels@npm:7.5.0": + version: 7.5.0 + resolution: "@storybook/channels@npm:7.5.0" + dependencies: + "@storybook/client-logger": 7.5.0 + "@storybook/core-events": 7.5.0 + "@storybook/global": ^5.0.0 + qs: ^6.10.0 + telejson: ^7.2.0 + tiny-invariant: ^1.3.1 + checksum: dd318b3ab8c615b63b7f1d1c1b05ca31fb4985ba527a0ebb47d02498c18a826c78241985c644deaeaab32a4869e1cf67d40b2c7638905c01cf9942d30c0eb16d + languageName: node + linkType: hard + +"@storybook/cli@npm:7.4.3": + version: 7.4.3 + resolution: "@storybook/cli@npm:7.4.3" + dependencies: + "@babel/core": ^7.22.9 + "@babel/preset-env": ^7.22.9 + "@babel/types": ^7.22.5 + "@ndelangen/get-tarball": ^3.0.7 + "@storybook/codemod": 7.4.3 + "@storybook/core-common": 7.4.3 + "@storybook/core-events": 7.4.3 + "@storybook/core-server": 7.4.3 + "@storybook/csf-tools": 7.4.3 + "@storybook/node-logger": 7.4.3 + "@storybook/telemetry": 7.4.3 + "@storybook/types": 7.4.3 + "@types/semver": ^7.3.4 + "@yarnpkg/fslib": 2.10.3 + "@yarnpkg/libzip": 2.3.0 + chalk: ^4.1.0 + commander: ^6.2.1 + cross-spawn: ^7.0.3 + detect-indent: ^6.1.0 + envinfo: ^7.7.3 + execa: ^5.0.0 + express: ^4.17.3 + find-up: ^5.0.0 + fs-extra: ^11.1.0 + get-npm-tarball-url: ^2.0.3 + get-port: ^5.1.1 + giget: ^1.0.0 + globby: ^11.0.2 + jscodeshift: ^0.14.0 + leven: ^3.1.0 + ora: ^5.4.1 + prettier: ^2.8.0 + prompts: ^2.4.0 + puppeteer-core: ^2.1.1 + read-pkg-up: ^7.0.1 + semver: ^7.3.7 + simple-update-notifier: ^2.0.0 + strip-json-comments: ^3.0.1 + tempy: ^1.0.1 + ts-dedent: ^2.0.0 + util-deprecate: ^1.0.2 + bin: + getstorybook: ./bin/index.js + sb: ./bin/index.js + checksum: cf762d80f422fa4bf448c080286b35b7081e274d7d432f7764cfbbe8f07af87fadd5eb81f7f6b73ff0c2a6d46e0d948514aee20ad0d51b1032ebc7e6de17ed97 + languageName: node + linkType: hard + "@storybook/client-api@npm:6.5.10": version: 6.5.10 resolution: "@storybook/client-api@npm:6.5.10" @@ -11267,6 +13077,46 @@ __metadata: languageName: node linkType: hard +"@storybook/client-logger@npm:7.4.3": + version: 7.4.3 + resolution: "@storybook/client-logger@npm:7.4.3" + dependencies: + "@storybook/global": ^5.0.0 + checksum: 442bd48c0b513c2bcce2d171e2ffc854f81a905597b1038fb384e2754a74bf1f59aa78d22480ad70fcb4efda0f5b70734021e8ee06cb77047a902eed37648ab1 + languageName: node + linkType: hard + +"@storybook/client-logger@npm:7.5.0": + version: 7.5.0 + resolution: "@storybook/client-logger@npm:7.5.0" + dependencies: + "@storybook/global": ^5.0.0 + checksum: 00f976e0d71107f9f07d98d3c1741ebb647cb50f3448377be481633198742a335a7178414cad5cb30f8327cfc5842f2726fbe5e5a7eafeed999a6f7cdab77e41 + languageName: node + linkType: hard + +"@storybook/codemod@npm:7.4.3": + version: 7.4.3 + resolution: "@storybook/codemod@npm:7.4.3" + dependencies: + "@babel/core": ^7.22.9 + "@babel/preset-env": ^7.22.9 + "@babel/types": ^7.22.5 + "@storybook/csf": ^0.1.0 + "@storybook/csf-tools": 7.4.3 + "@storybook/node-logger": 7.4.3 + "@storybook/types": 7.4.3 + "@types/cross-spawn": ^6.0.2 + cross-spawn: ^7.0.3 + globby: ^11.0.2 + jscodeshift: ^0.14.0 + lodash: ^4.17.21 + prettier: ^2.8.0 + recast: ^0.23.1 + checksum: ba33c1497960c19c9873581412052c13c23565882fd57e17408fdb4094fa6d572a330ef624ef662954d7f46b8b4c9b4b0ebb0e6f24caa2927f4d03fa1f7c5b63 + languageName: node + linkType: hard + "@storybook/components@npm:6.5.10": version: 6.5.10 resolution: "@storybook/components@npm:6.5.10" @@ -11305,6 +13155,27 @@ __metadata: languageName: node linkType: hard +"@storybook/components@npm:^7.0.12": + version: 7.5.0 + resolution: "@storybook/components@npm:7.5.0" + dependencies: + "@radix-ui/react-select": ^1.2.2 + "@radix-ui/react-toolbar": ^1.0.4 + "@storybook/client-logger": 7.5.0 + "@storybook/csf": ^0.1.0 + "@storybook/global": ^5.0.0 + "@storybook/theming": 7.5.0 + "@storybook/types": 7.5.0 + memoizerific: ^1.11.3 + use-resize-observer: ^9.1.0 + util-deprecate: ^1.0.2 + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: 5c8e194c2a827e9bf48f4237d00b4875022f1ad12855031a76f069cb4a679bc3a53ae6dfd15fe779578de8fdcaf3fbc28f2e0d681a2d8a2581ee39a5a29a020e + languageName: node + linkType: hard + "@storybook/components@npm:~6.3.0": version: 6.3.13 resolution: "@storybook/components@npm:6.3.13" @@ -11538,6 +13409,37 @@ __metadata: languageName: node linkType: hard +"@storybook/core-common@npm:7.4.3": + version: 7.4.3 + resolution: "@storybook/core-common@npm:7.4.3" + dependencies: + "@storybook/core-events": 7.4.3 + "@storybook/node-logger": 7.4.3 + "@storybook/types": 7.4.3 + "@types/find-cache-dir": ^3.2.1 + "@types/node": ^16.0.0 + "@types/node-fetch": ^2.6.4 + "@types/pretty-hrtime": ^1.0.0 + chalk: ^4.1.0 + esbuild: ^0.18.0 + esbuild-register: ^3.4.0 + file-system-cache: 2.3.0 + find-cache-dir: ^3.0.0 + find-up: ^5.0.0 + fs-extra: ^11.1.0 + glob: ^10.0.0 + handlebars: ^4.7.7 + lazy-universal-dotenv: ^4.0.0 + node-fetch: ^2.0.0 + picomatch: ^2.3.0 + pkg-dir: ^5.0.0 + pretty-hrtime: ^1.0.3 + resolve-from: ^5.0.0 + ts-dedent: ^2.0.0 + checksum: e81afc618648d9f41643eaeca68458bccdd350432023de43a4f3db1f5732e7d689e3527f077c2a2c48271918417684a303ee755b4a14e543edf0808c36295435 + languageName: node + linkType: hard + "@storybook/core-common@npm:^7.0.0-beta.0 || ^7.0.0-rc.0 || ^7.0.0": version: 7.0.2 resolution: "@storybook/core-common@npm:7.0.2" @@ -11565,6 +13467,37 @@ __metadata: languageName: node linkType: hard +"@storybook/core-common@npm:^7.0.12": + version: 7.5.0 + resolution: "@storybook/core-common@npm:7.5.0" + dependencies: + "@storybook/core-events": 7.5.0 + "@storybook/node-logger": 7.5.0 + "@storybook/types": 7.5.0 + "@types/find-cache-dir": ^3.2.1 + "@types/node": ^18.0.0 + "@types/node-fetch": ^2.6.4 + "@types/pretty-hrtime": ^1.0.0 + chalk: ^4.1.0 + esbuild: ^0.18.0 + esbuild-register: ^3.5.0 + file-system-cache: 2.3.0 + find-cache-dir: ^3.0.0 + find-up: ^5.0.0 + fs-extra: ^11.1.0 + glob: ^10.0.0 + handlebars: ^4.7.7 + lazy-universal-dotenv: ^4.0.0 + node-fetch: ^2.0.0 + picomatch: ^2.3.0 + pkg-dir: ^5.0.0 + pretty-hrtime: ^1.0.3 + resolve-from: ^5.0.0 + ts-dedent: ^2.0.0 + checksum: 88300d4a8fc61c2dcc87a24ed54d61d5a9591655694e59b4bad67fd053b8db842500082bf5487ca477209e8dc1fe35a2127958342d4b41ce5c6c2584a59995f7 + languageName: node + linkType: hard + "@storybook/core-events@npm:6.3.13, @storybook/core-events@npm:~6.3.0": version: 6.3.13 resolution: "@storybook/core-events@npm:6.3.13" @@ -11601,7 +13534,7 @@ __metadata: languageName: node linkType: hard -"@storybook/core-events@npm:6.5.16": +"@storybook/core-events@npm:6.5.16, @storybook/core-events@npm:^6.5.16": version: 6.5.16 resolution: "@storybook/core-events@npm:6.5.16" dependencies: @@ -11617,6 +13550,24 @@ __metadata: languageName: node linkType: hard +"@storybook/core-events@npm:7.4.3": + version: 7.4.3 + resolution: "@storybook/core-events@npm:7.4.3" + dependencies: + ts-dedent: ^2.0.0 + checksum: ffe6c5a0a7db32a42fcbe2fbe027977f071c458da621793468409ac29808a24573f94f083362200dde4b00bc14b00a66053ace4c9e3686b442ee2e4a126c9855 + languageName: node + linkType: hard + +"@storybook/core-events@npm:7.5.0, @storybook/core-events@npm:^7.0.12": + version: 7.5.0 + resolution: "@storybook/core-events@npm:7.5.0" + dependencies: + ts-dedent: ^2.0.0 + checksum: 829321419c5e5e21c17e72c4006004e73b24648e065ed5b278074317f5d57ce896a1a28deff51184ae44fd8a9d463519ab261bbe5a17736ca50ff7c5bbcb44f9 + languageName: node + linkType: hard + "@storybook/core-server@npm:6.5.10": version: 6.5.10 resolution: "@storybook/core-server@npm:6.5.10" @@ -11743,6 +13694,56 @@ __metadata: languageName: node linkType: hard +"@storybook/core-server@npm:7.4.3": + version: 7.4.3 + resolution: "@storybook/core-server@npm:7.4.3" + dependencies: + "@aw-web-design/x-default-browser": 1.4.126 + "@discoveryjs/json-ext": ^0.5.3 + "@storybook/builder-manager": 7.4.3 + "@storybook/channels": 7.4.3 + "@storybook/core-common": 7.4.3 + "@storybook/core-events": 7.4.3 + "@storybook/csf": ^0.1.0 + "@storybook/csf-tools": 7.4.3 + "@storybook/docs-mdx": ^0.1.0 + "@storybook/global": ^5.0.0 + "@storybook/manager": 7.4.3 + "@storybook/node-logger": 7.4.3 + "@storybook/preview-api": 7.4.3 + "@storybook/telemetry": 7.4.3 + "@storybook/types": 7.4.3 + "@types/detect-port": ^1.3.0 + "@types/node": ^16.0.0 + "@types/pretty-hrtime": ^1.0.0 + "@types/semver": ^7.3.4 + better-opn: ^3.0.2 + chalk: ^4.1.0 + cli-table3: ^0.6.1 + compression: ^1.7.4 + detect-port: ^1.3.0 + express: ^4.17.3 + fs-extra: ^11.1.0 + globby: ^11.0.2 + ip: ^2.0.0 + lodash: ^4.17.21 + open: ^8.4.0 + pretty-hrtime: ^1.0.3 + prompts: ^2.4.0 + read-pkg-up: ^7.0.1 + semver: ^7.3.7 + serve-favicon: ^2.5.0 + telejson: ^7.2.0 + tiny-invariant: ^1.3.1 + ts-dedent: ^2.0.0 + util: ^0.12.4 + util-deprecate: ^1.0.2 + watchpack: ^2.2.0 + ws: ^8.2.3 + checksum: 4465b9285d2b6ff036e00f4c0e9100b84ced1c51a43d107c6cd618c366fe09cbb3f10aeb7d7b96aeb2afb255893ff077d2cad8f36cb61b81027fa7a8a908be0c + languageName: node + linkType: hard + "@storybook/core@npm:6.5.10": version: 6.5.10 resolution: "@storybook/core@npm:6.5.10" @@ -11839,6 +13840,23 @@ __metadata: languageName: node linkType: hard +"@storybook/csf-tools@npm:7.4.3": + version: 7.4.3 + resolution: "@storybook/csf-tools@npm:7.4.3" + dependencies: + "@babel/generator": ^7.22.9 + "@babel/parser": ^7.22.7 + "@babel/traverse": ^7.22.8 + "@babel/types": ^7.22.5 + "@storybook/csf": ^0.1.0 + "@storybook/types": 7.4.3 + fs-extra: ^11.1.0 + recast: ^0.23.1 + ts-dedent: ^2.0.0 + checksum: 6910d1613c8fa1b411bda1d23e4d03259152b03589b56e952165d3586bebfdd1d70b8dfba3ed8a2e67dbec4da6f48804ff448d58dab14e94c945decf1c264475 + languageName: node + linkType: hard + "@storybook/csf-tools@npm:^7.0.0-beta.0 || ^7.0.0-rc.0 || ^7.0.0": version: 7.0.2 resolution: "@storybook/csf-tools@npm:7.0.2" @@ -11883,6 +13901,13 @@ __metadata: languageName: node linkType: hard +"@storybook/docs-mdx@npm:^0.1.0": + version: 0.1.0 + resolution: "@storybook/docs-mdx@npm:0.1.0" + checksum: a7770842c3947a761bcbe776a9c4fd35163d30c3274fca034169f69ff614242eaa4cacaa2c95fd215827081ef9a43f4774d521a6f43a4d063ea5f4ea14b1d69a + languageName: node + linkType: hard + "@storybook/docs-tools@npm:6.5.10": version: 6.5.10 resolution: "@storybook/docs-tools@npm:6.5.10" @@ -11967,6 +13992,32 @@ __metadata: languageName: node linkType: hard +"@storybook/manager-api@npm:7.5.0, @storybook/manager-api@npm:^7.0.12": + version: 7.5.0 + resolution: "@storybook/manager-api@npm:7.5.0" + dependencies: + "@storybook/channels": 7.5.0 + "@storybook/client-logger": 7.5.0 + "@storybook/core-events": 7.5.0 + "@storybook/csf": ^0.1.0 + "@storybook/global": ^5.0.0 + "@storybook/router": 7.5.0 + "@storybook/theming": 7.5.0 + "@storybook/types": 7.5.0 + dequal: ^2.0.2 + lodash: ^4.17.21 + memoizerific: ^1.11.3 + semver: ^7.3.7 + store2: ^2.14.2 + telejson: ^7.2.0 + ts-dedent: ^2.0.0 + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: c651ed1594f7bd704a846c2ba782785a0fdf83779d10b400b034711e65e40975e67d8cc484d5753523d2341112dd7e63b49dc71b04803b7a718b793b08adabf7 + languageName: node + linkType: hard + "@storybook/manager-webpack4@npm:6.5.10": version: 6.5.10 resolution: "@storybook/manager-webpack4@npm:6.5.10" @@ -12111,6 +14162,13 @@ __metadata: languageName: node linkType: hard +"@storybook/manager@npm:7.4.3": + version: 7.4.3 + resolution: "@storybook/manager@npm:7.4.3" + checksum: f7f78da50150dfc679242db97c8ebdf1da0592c6bf91e2c2daa70a263cd9e61a51e6d26cf331c3350b445bf5f3dd9f0a1e461c085fd2aaa52f35db4669be90d2 + languageName: node + linkType: hard + "@storybook/mdx1-csf@npm:^0.0.1": version: 0.0.1 resolution: "@storybook/mdx1-csf@npm:0.0.1" @@ -12168,6 +14226,20 @@ __metadata: languageName: node linkType: hard +"@storybook/node-logger@npm:7.4.3": + version: 7.4.3 + resolution: "@storybook/node-logger@npm:7.4.3" + checksum: 3d067b5e51f52622f4f950fb9bc3584dddc80e30cfd5348936c361ad6ea3006c99fcb23a29f57290b77c770c082f766ede177405d57a7fd68fad7881f6844bfc + languageName: node + linkType: hard + +"@storybook/node-logger@npm:7.5.0, @storybook/node-logger@npm:^7.0.12": + version: 7.5.0 + resolution: "@storybook/node-logger@npm:7.5.0" + checksum: e6288f345f2a0a1db7fb3a6605910b37d844d4e4fedc2459ae2825a924e8c15453436d074fd1d87e89e82108eab6f32bd3870402ec25f381f572024f73b67319 + languageName: node + linkType: hard + "@storybook/postinstall@npm:6.5.16": version: 6.5.16 resolution: "@storybook/postinstall@npm:6.5.16" @@ -12177,6 +14249,28 @@ __metadata: languageName: node linkType: hard +"@storybook/preview-api@npm:7.4.3": + version: 7.4.3 + resolution: "@storybook/preview-api@npm:7.4.3" + dependencies: + "@storybook/channels": 7.4.3 + "@storybook/client-logger": 7.4.3 + "@storybook/core-events": 7.4.3 + "@storybook/csf": ^0.1.0 + "@storybook/global": ^5.0.0 + "@storybook/types": 7.4.3 + "@types/qs": ^6.9.5 + dequal: ^2.0.2 + lodash: ^4.17.21 + memoizerific: ^1.11.3 + qs: ^6.10.0 + synchronous-promise: ^2.0.15 + ts-dedent: ^2.0.0 + util-deprecate: ^1.0.2 + checksum: 2347e122283847c4b02741e487f121a929288b0fcad589f2ef07b8b42352c0d652a33a31c44cbe492176a691929e3d27936232f53bed2834bb67146a2101f470 + languageName: node + linkType: hard + "@storybook/preview-api@npm:^7.0.0-beta.0 || ^7.0.0-rc.0 || ^7.0.0": version: 7.0.2 resolution: "@storybook/preview-api@npm:7.0.2" @@ -12200,6 +14294,28 @@ __metadata: languageName: node linkType: hard +"@storybook/preview-api@npm:^7.0.12": + version: 7.5.0 + resolution: "@storybook/preview-api@npm:7.5.0" + dependencies: + "@storybook/channels": 7.5.0 + "@storybook/client-logger": 7.5.0 + "@storybook/core-events": 7.5.0 + "@storybook/csf": ^0.1.0 + "@storybook/global": ^5.0.0 + "@storybook/types": 7.5.0 + "@types/qs": ^6.9.5 + dequal: ^2.0.2 + lodash: ^4.17.21 + memoizerific: ^1.11.3 + qs: ^6.10.0 + synchronous-promise: ^2.0.15 + ts-dedent: ^2.0.0 + util-deprecate: ^1.0.2 + checksum: 43984ba7e3aafe3b32ac88adebb2dc898d5f96a991fe98a37524b02c2e8f887d4757d5b12ea5c5b812db173310076f2367e46f85ba21c4005591e87a100debc5 + languageName: node + linkType: hard + "@storybook/preview-web@npm:6.5.10": version: 6.5.10 resolution: "@storybook/preview-web@npm:6.5.10" @@ -12496,6 +14612,20 @@ __metadata: languageName: node linkType: hard +"@storybook/router@npm:7.5.0": + version: 7.5.0 + resolution: "@storybook/router@npm:7.5.0" + dependencies: + "@storybook/client-logger": 7.5.0 + memoizerific: ^1.11.3 + qs: ^6.10.0 + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: be37e8e525c1c2d7ad6da883a1d588dbf3fe182143dc02222f10a43baaed3b7259e8c7c7bf32ac54e26bdc58cfb040bb418d898d8d97e8107a83a9d3c7ccd139 + languageName: node + linkType: hard + "@storybook/semver@npm:^7.3.2": version: 7.3.2 resolution: "@storybook/semver@npm:7.3.2" @@ -12621,6 +14751,22 @@ __metadata: languageName: node linkType: hard +"@storybook/telemetry@npm:7.4.3": + version: 7.4.3 + resolution: "@storybook/telemetry@npm:7.4.3" + dependencies: + "@storybook/client-logger": 7.4.3 + "@storybook/core-common": 7.4.3 + "@storybook/csf-tools": 7.4.3 + chalk: ^4.1.0 + detect-package-manager: ^2.0.1 + fetch-retry: ^5.0.2 + fs-extra: ^11.1.0 + read-pkg-up: ^7.0.1 + checksum: e2ecd2d6644cd0536554e270c552d45936c70d26230e24cc839a51c70340fdae3575017388a613ff549d5577238d748ca53ac8a1e9095ee76db291b3f0793b45 + languageName: node + linkType: hard + "@storybook/test-runner@npm:^0.10.0": version: 0.10.0 resolution: "@storybook/test-runner@npm:0.10.0" @@ -12757,6 +14903,21 @@ __metadata: languageName: node linkType: hard +"@storybook/theming@npm:7.5.0, @storybook/theming@npm:^7.0.12": + version: 7.5.0 + resolution: "@storybook/theming@npm:7.5.0" + dependencies: + "@emotion/use-insertion-effect-with-fallbacks": ^1.0.0 + "@storybook/client-logger": 7.5.0 + "@storybook/global": ^5.0.0 + memoizerific: ^1.11.3 + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: d7e94fb094daf3a064ab5ad65104f60d82ccf3cf7a9b194174725d9e96f888f1c3378e59a3c7dfc4a7e745354f504fd6f5e3145e69e315470ca6c004c3a7fdc5 + languageName: node + linkType: hard + "@storybook/types@npm:7.0.2": version: 7.0.2 resolution: "@storybook/types@npm:7.0.2" @@ -12769,6 +14930,30 @@ __metadata: languageName: node linkType: hard +"@storybook/types@npm:7.4.3": + version: 7.4.3 + resolution: "@storybook/types@npm:7.4.3" + dependencies: + "@storybook/channels": 7.4.3 + "@types/babel__core": ^7.0.0 + "@types/express": ^4.7.0 + file-system-cache: 2.3.0 + checksum: 4108d185086884b742258edc67b9f3837099e63f3b902889f927e0e9cb34ba4da37fbcbe94235388672ba20eefac6f397cd0113e8e873538f31eda521dfb1365 + languageName: node + linkType: hard + +"@storybook/types@npm:7.5.0, @storybook/types@npm:^7.0.12": + version: 7.5.0 + resolution: "@storybook/types@npm:7.5.0" + dependencies: + "@storybook/channels": 7.5.0 + "@types/babel__core": ^7.0.0 + "@types/express": ^4.7.0 + file-system-cache: 2.3.0 + checksum: e74f807660eb9a626c3aea1822d7eb412b4ea129e5254e4e2b8e6f2af292d4ea797ad73e1b64e36ba44fdd3015297f2fde095fdc80690f56f5e050fc1d5576f7 + languageName: node + linkType: hard + "@storybook/ui@npm:6.5.10": version: 6.5.10 resolution: "@storybook/ui@npm:6.5.10" @@ -14157,6 +16342,13 @@ __metadata: languageName: node linkType: hard +"@types/babel__preset-env@npm:^7": + version: 7.9.3 + resolution: "@types/babel__preset-env@npm:7.9.3" + checksum: 7e89a37c4a7ebe841e0bc610e19497fe0642d741e8651c52ea4727782e46ea47d2aaf2656927a684c6618d8981b0febc1fc8c82c4a1d84b4ec5793a283e1ef7f + languageName: node + linkType: hard + "@types/babel__template@npm:*": version: 7.4.1 resolution: "@types/babel__template@npm:7.4.1" @@ -14283,6 +16475,15 @@ __metadata: languageName: node linkType: hard +"@types/cross-spawn@npm:^6.0.2": + version: 6.0.3 + resolution: "@types/cross-spawn@npm:6.0.3" + dependencies: + "@types/node": "*" + checksum: 06d50fa1e1370ef60b9c9085b76adec7d7bc20728fbb02b3c2061d4d922312acf1ba56a7c94d88c27a22fc6241ab6b970c936f3294038a9c97a719fbc8eb8a76 + languageName: node + linkType: hard + "@types/d3-array@npm:^3.0.3": version: 3.0.5 resolution: "@types/d3-array@npm:3.0.5" @@ -14368,6 +16569,13 @@ __metadata: languageName: node linkType: hard +"@types/detect-port@npm:^1.3.0": + version: 1.3.3 + resolution: "@types/detect-port@npm:1.3.3" + checksum: 0dadb520286a5cfd2832d12189dc795cc3589dfd9166d1b033453fb94b0212c4067a847045833e85b0f7c73135c944cb4ccb49c8e683491845c2e8a3da5d5c1c + languageName: node + linkType: hard + "@types/docker-modem@npm:*": version: 3.0.2 resolution: "@types/docker-modem@npm:3.0.2" @@ -14399,6 +16607,20 @@ __metadata: languageName: node linkType: hard +"@types/ejs@npm:^3.1.1": + version: 3.1.2 + resolution: "@types/ejs@npm:3.1.2" + checksum: e4f0745b6ed53a63c08bdfdeb019a7d0e0c400896722b44d6732b4ee6bf6061d2dc965206186b8b0ae2ecd71303c29f1af1feddbca2df0acbd7bd234a74ca518 + languageName: node + linkType: hard + +"@types/emscripten@npm:^1.39.6": + version: 1.39.7 + resolution: "@types/emscripten@npm:1.39.7" + checksum: 9871e4495358cc06cc45b2798022cd097d8ac2eb5b2fae7c276c6c5cadea05507150fad053c73ed346d4cbd844c50a3438604e5d7c3c2a7446b703cacb1ce172 + languageName: node + linkType: hard + "@types/eslint-scope@npm:^3.7.0": version: 3.7.1 resolution: "@types/eslint-scope@npm:3.7.1" @@ -14538,6 +16760,13 @@ __metadata: languageName: node linkType: hard +"@types/find-cache-dir@npm:^3.2.1": + version: 3.2.1 + resolution: "@types/find-cache-dir@npm:3.2.1" + checksum: bf5c4e96da40247cd9e6327f54dfccda961a0fb2d70e3c71bd05def94de4c2e6fb310fe8ecb0f04ecf5dbc52214e184b55a2337b0f87250d4ae1e2e7d58321e4 + languageName: node + linkType: hard + "@types/flat@npm:5.0.2": version: 5.0.2 resolution: "@types/flat@npm:5.0.2" @@ -14786,6 +17015,13 @@ __metadata: languageName: node linkType: hard +"@types/mime-types@npm:^2.1.0": + version: 2.1.1 + resolution: "@types/mime-types@npm:2.1.1" + checksum: 106b5d556add46446a579ad25ff15d6b421851790d887edcad558c90c1e64b1defc72bfbaf4b08f208916e21d9cc45cdb951d77be51268b18221544cfe054a3c + languageName: node + linkType: hard + "@types/mime@npm:*": version: 3.0.1 resolution: "@types/mime@npm:3.0.1" @@ -14831,6 +17067,16 @@ __metadata: languageName: node linkType: hard +"@types/node-fetch@npm:^2.6.4": + version: 2.6.5 + resolution: "@types/node-fetch@npm:2.6.5" + dependencies: + "@types/node": "*" + form-data: ^4.0.0 + checksum: 686ee0d52bb82d73f82c0da0bb025d4af1ba7bd8df4edd09336f6179eb838b6d4b5f0a524677a8faec2b00918d1b08a1690d50fa592c9741a5df6a8041a52495 + languageName: node + linkType: hard + "@types/node@npm:*": version: 16.4.13 resolution: "@types/node@npm:16.4.13" @@ -14880,6 +17126,13 @@ __metadata: languageName: node linkType: hard +"@types/node@npm:^18.0.0": + version: 18.18.6 + resolution: "@types/node@npm:18.18.6" + checksum: a847639b8455fd3dfa6dbc2917274c82c9db789f1d41aaf69f94ac6c9e54c3c1dd29be6e1e1ccd7c17e54db3d78d7011bc4e70544c6447ceca253dccc0a187e1 + languageName: node + linkType: hard + "@types/node@npm:^18.11.18": version: 18.15.0 resolution: "@types/node@npm:18.15.0" @@ -15164,6 +17417,13 @@ __metadata: languageName: node linkType: hard +"@types/semver@npm:^7.3.4": + version: 7.5.2 + resolution: "@types/semver@npm:7.5.2" + checksum: 743aa8a2b58e20b329c19bd2459152cb049d12fafab7279b90ac11e0f268c97efbcb606ea0c681cca03f79015381b40d9b1244349b354270bec3f939ed49f6e9 + languageName: node + linkType: hard + "@types/serve-index@npm:^1.9.1": version: 1.9.1 resolution: "@types/serve-index@npm:1.9.1" @@ -16966,6 +19226,37 @@ __metadata: languageName: node linkType: hard +"@yarnpkg/esbuild-plugin-pnp@npm:^3.0.0-rc.10": + version: 3.0.0-rc.15 + resolution: "@yarnpkg/esbuild-plugin-pnp@npm:3.0.0-rc.15" + dependencies: + tslib: ^2.4.0 + peerDependencies: + esbuild: ">=0.10.0" + checksum: 04da15355a99773b441742814ba4d0f3453a83df47aa07e215f167e156f109ab8e971489c8b1a4ddf3c79d568d35213f496ad52e97298228597e1aacc22680aa + languageName: node + linkType: hard + +"@yarnpkg/fslib@npm:2.10.3": + version: 2.10.3 + resolution: "@yarnpkg/fslib@npm:2.10.3" + dependencies: + "@yarnpkg/libzip": ^2.3.0 + tslib: ^1.13.0 + checksum: 0ca693f61d47bcf165411a121ed9123f512b1b5bfa5e1c6c8f280b4ffdbea9bf2a6db418f99ecfc9624587fdc695b2b64eb0fe7b4028e44095914b25ca99655e + languageName: node + linkType: hard + +"@yarnpkg/libzip@npm:2.3.0, @yarnpkg/libzip@npm:^2.3.0": + version: 2.3.0 + resolution: "@yarnpkg/libzip@npm:2.3.0" + dependencies: + "@types/emscripten": ^1.39.6 + tslib: ^1.13.0 + checksum: 533a4883f69bb013f955d80dc19719881697e6849ea5f0cbe6d87ef1d582b05cbae8a453802f92ad0c852f976296cac3ff7834be79a7e415b65cdf213e448110 + languageName: node + linkType: hard + "@zxing/text-encoding@npm:0.9.0": version: 0.9.0 resolution: "@zxing/text-encoding@npm:0.9.0" @@ -17187,6 +19478,23 @@ __metadata: languageName: node linkType: hard +"adjust-sourcemap-loader@npm:^4.0.0": + version: 4.0.0 + resolution: "adjust-sourcemap-loader@npm:4.0.0" + dependencies: + loader-utils: ^2.0.0 + regex-parser: ^2.2.11 + checksum: d524ae23582f41e2275af5d88faab7a9dc09770ed588244e0a76d3196d0d6a90bf02760c71bc6213dbfef3aef4a86232ac9521bfd629752c32b7af37bc74c660 + languageName: node + linkType: hard + +"agent-base@npm:5": + version: 5.1.1 + resolution: "agent-base@npm:5.1.1" + checksum: 61ae789f3019f1dc10e8cba6d3ae9826949299a4e54aaa1cfa2fa37c95a108e70e95423b963bb987d7891a703fd9a5c383a506f4901819f3ee56f3147c0aa8ab + languageName: node + linkType: hard + "agent-base@npm:6, agent-base@npm:^6.0.2": version: 6.0.2 resolution: "agent-base@npm:6.0.2" @@ -18094,6 +20402,15 @@ __metadata: languageName: node linkType: hard +"ast-types@npm:0.15.2": + version: 0.15.2 + resolution: "ast-types@npm:0.15.2" + dependencies: + tslib: ^2.0.1 + checksum: 24f0d86bf9e4c8dae16fa24b13c1776f2c2677040bcfbd4eb4f27911db49020be4876885e45e6cfcc548ed4dfea3a0742d77e3346b84fae47379cb0b89e9daa0 + languageName: node + linkType: hard + "ast-types@npm:^0.13.4": version: 0.13.4 resolution: "ast-types@npm:0.13.4" @@ -18158,6 +20475,13 @@ __metadata: languageName: node linkType: hard +"async-limiter@npm:~1.0.0": + version: 1.0.1 + resolution: "async-limiter@npm:1.0.1" + checksum: 2b849695b465d93ad44c116220dee29a5aeb63adac16c1088983c339b0de57d76e82533e8e364a93a9f997f28bbfc6a92948cefc120652bd07f3b59f8d75cf2b + languageName: node + linkType: hard + "async-validator@npm:^4.0.2": version: 4.0.7 resolution: "async-validator@npm:4.0.7" @@ -18308,6 +20632,15 @@ __metadata: languageName: node linkType: hard +"babel-core@npm:^7.0.0-bridge.0": + version: 7.0.0-bridge.0 + resolution: "babel-core@npm:7.0.0-bridge.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 2a1cb879019dffb08d17bec36e13c3a6d74c94773f41c1fd8b14de13f149cc34b705b0a1e07b42fcf35917b49d78db6ff0c5c3b00b202a5235013d517b5c6bbb + languageName: node + linkType: hard + "babel-eslint@npm:10.0.3": version: 10.0.3 resolution: "babel-eslint@npm:10.0.3" @@ -18547,6 +20880,19 @@ __metadata: languageName: node linkType: hard +"babel-plugin-polyfill-corejs2@npm:^0.4.5": + version: 0.4.5 + resolution: "babel-plugin-polyfill-corejs2@npm:0.4.5" + dependencies: + "@babel/compat-data": ^7.22.6 + "@babel/helper-define-polyfill-provider": ^0.4.2 + semver: ^6.3.1 + peerDependencies: + "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 + checksum: 33a8e06aa54e2858d211c743d179f0487b03222f9ca1bfd7c4865bca243fca942a3358cb75f6bb894ed476cbddede834811fbd6903ff589f055821146f053e1a + languageName: node + linkType: hard + "babel-plugin-polyfill-corejs3@npm:^0.1.0": version: 0.1.7 resolution: "babel-plugin-polyfill-corejs3@npm:0.1.7" @@ -18595,6 +20941,18 @@ __metadata: languageName: node linkType: hard +"babel-plugin-polyfill-corejs3@npm:^0.8.3": + version: 0.8.3 + resolution: "babel-plugin-polyfill-corejs3@npm:0.8.3" + dependencies: + "@babel/helper-define-polyfill-provider": ^0.4.2 + core-js-compat: ^3.31.0 + peerDependencies: + "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 + checksum: dcbb30e551702a82cfd4d2c375da2c317658e55f95e9edcda93b9bbfdcc8fb6e5344efcb144e04d3406859e7682afce7974c60ededd9f12072a48a83dd22a0da + languageName: node + linkType: hard + "babel-plugin-polyfill-regenerator@npm:^0.2.2": version: 0.2.2 resolution: "babel-plugin-polyfill-regenerator@npm:0.2.2" @@ -18628,6 +20986,17 @@ __metadata: languageName: node linkType: hard +"babel-plugin-polyfill-regenerator@npm:^0.5.2": + version: 0.5.2 + resolution: "babel-plugin-polyfill-regenerator@npm:0.5.2" + dependencies: + "@babel/helper-define-polyfill-provider": ^0.4.2 + peerDependencies: + "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 + checksum: d962200f604016a9a09bc9b4aaf60a3db7af876bb65bcefaeac04d44ac9d9ec4037cf24ce117760cc141d7046b6394c7eb0320ba9665cb4a2ee64df2be187c93 + languageName: node + linkType: hard + "babel-plugin-react-docgen@npm:^4.2.1": version: 4.2.1 resolution: "babel-plugin-react-docgen@npm:4.2.1" @@ -18861,6 +21230,15 @@ __metadata: languageName: node linkType: hard +"better-opn@npm:^3.0.2": + version: 3.0.2 + resolution: "better-opn@npm:3.0.2" + dependencies: + open: ^8.0.4 + checksum: 1471552fa7f733561e7f49e812be074b421153006ca744de985fb6d38939807959fc5fe9cb819cf09f864782e294704fd3b31711ea14c115baf3330a2f1135de + languageName: node + linkType: hard + "big-integer@npm:1.6.36": version: 1.6.36 resolution: "big-integer@npm:1.6.36" @@ -19343,6 +21721,15 @@ __metadata: languageName: node linkType: hard +"browserify-zlib@npm:^0.1.4": + version: 0.1.4 + resolution: "browserify-zlib@npm:0.1.4" + dependencies: + pako: ~0.2.0 + checksum: abee4cb4349e8a21391fd874564f41b113fe691372913980e6fa06a777e4ea2aad4e942af14ab99bce190d5ac8f5328201432f4ef0eae48c6d02208bc212976f + languageName: node + linkType: hard + "browserify-zlib@npm:^0.2.0": version: 0.2.0 resolution: "browserify-zlib@npm:0.2.0" @@ -19397,6 +21784,20 @@ __metadata: languageName: node linkType: hard +"browserslist@npm:^4.21.10, browserslist@npm:^4.21.9": + version: 4.21.10 + resolution: "browserslist@npm:4.21.10" + dependencies: + caniuse-lite: ^1.0.30001517 + electron-to-chromium: ^1.4.477 + node-releases: ^2.0.13 + update-browserslist-db: ^1.0.11 + bin: + browserslist: cli.js + checksum: 1e27c0f111a35d1dd0e8fc2c61781b0daefabc2c9471b0b10537ce54843014bceb2a1ce4571af1a82b2bf1e6e6e05d38865916689a158f03bc2c7a4ec2577db8 + languageName: node + linkType: hard + "browserslist@npm:^4.21.3, browserslist@npm:^4.21.4": version: 4.21.4 resolution: "browserslist@npm:4.21.4" @@ -19982,7 +22383,7 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30001109, caniuse-lite@npm:^1.0.30001248, caniuse-lite@npm:^1.0.30001251, caniuse-lite@npm:^1.0.30001280, caniuse-lite@npm:^1.0.30001400": +"caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30001109, caniuse-lite@npm:^1.0.30001248, caniuse-lite@npm:^1.0.30001251, caniuse-lite@npm:^1.0.30001280, caniuse-lite@npm:^1.0.30001400, caniuse-lite@npm:^1.0.30001517": version: 1.0.30001538 resolution: "caniuse-lite@npm:1.0.30001538" checksum: 94c5d55757a339c7cc175f08a024671e2b4e7c04f130b1015793303d637061347efb6ad84447c3b8137333e742d150b8ad9672716bbf2482646c2e63a56f6c55 @@ -21099,7 +23500,7 @@ __metadata: languageName: node linkType: hard -"concat-stream@npm:^1.5.0": +"concat-stream@npm:^1.5.0, concat-stream@npm:^1.6.2": version: 1.6.2 resolution: "concat-stream@npm:1.6.2" dependencies: @@ -21633,6 +24034,15 @@ __metadata: languageName: node linkType: hard +"core-js-compat@npm:^3.31.0": + version: 3.32.2 + resolution: "core-js-compat@npm:3.32.2" + dependencies: + browserslist: ^4.21.10 + checksum: efca146ad71a542e6f196db5ba5aed617e48c615bdf1fbb065471b3267f833ac545bd5fc5ad0642c3d3974b955f0684ff0863d7471d7050ee0284e0a1313942e + languageName: node + linkType: hard + "core-js-compat@npm:^3.8.1": version: 3.16.3 resolution: "core-js-compat@npm:3.16.3" @@ -21755,6 +24165,23 @@ __metadata: languageName: node linkType: hard +"cosmiconfig@npm:^8.2.0": + version: 8.3.6 + resolution: "cosmiconfig@npm:8.3.6" + dependencies: + import-fresh: ^3.3.0 + js-yaml: ^4.1.0 + parse-json: ^5.2.0 + path-type: ^4.0.0 + peerDependencies: + typescript: ">=4.9.5" + peerDependenciesMeta: + typescript: + optional: true + checksum: dc339ebea427898c9e03bf01b56ba7afbac07fc7d2a2d5a15d6e9c14de98275a9565da949375aee1809591c152c0a3877bb86dbeaf74d5bd5aaa79955ad9e7a0 + languageName: node + linkType: hard + "cp-file@npm:^7.0.0": version: 7.0.0 resolution: "cp-file@npm:7.0.0" @@ -22040,6 +24467,24 @@ __metadata: languageName: node linkType: hard +"css-loader@npm:^6.7.3": + version: 6.8.1 + resolution: "css-loader@npm:6.8.1" + dependencies: + icss-utils: ^5.1.0 + postcss: ^8.4.21 + postcss-modules-extract-imports: ^3.0.0 + postcss-modules-local-by-default: ^4.0.3 + postcss-modules-scope: ^3.0.0 + postcss-modules-values: ^4.0.0 + postcss-value-parser: ^4.2.0 + semver: ^7.3.8 + peerDependencies: + webpack: ^5.0.0 + checksum: 7c1784247bdbe76dc5c55fb1ac84f1d4177a74c47259942c9cfdb7a8e6baef11967a0bc85ac285f26bd26d5059decb848af8154a03fdb4f4894f41212f45eef3 + languageName: node + linkType: hard + "css-mediaquery@npm:^0.1.2": version: 0.1.2 resolution: "css-mediaquery@npm:0.1.2" @@ -22658,6 +25103,16 @@ __metadata: languageName: node linkType: hard +"default-browser-id@npm:3.0.0, default-browser-id@npm:^3.0.0": + version: 3.0.0 + resolution: "default-browser-id@npm:3.0.0" + dependencies: + bplist-parser: ^0.2.0 + untildify: ^4.0.0 + checksum: 279c7ad492542e5556336b6c254a4eaf31b2c63a5433265655ae6e47301197b6cfb15c595a6fdc6463b2ff8e1a1a1ed3cba56038a60e1527ba4ab1628c6b9941 + languageName: node + linkType: hard + "default-browser-id@npm:^1.0.4": version: 1.0.4 resolution: "default-browser-id@npm:1.0.4" @@ -22671,16 +25126,6 @@ __metadata: languageName: node linkType: hard -"default-browser-id@npm:^3.0.0": - version: 3.0.0 - resolution: "default-browser-id@npm:3.0.0" - dependencies: - bplist-parser: ^0.2.0 - untildify: ^4.0.0 - checksum: 279c7ad492542e5556336b6c254a4eaf31b2c63a5433265655ae6e47301197b6cfb15c595a6fdc6463b2ff8e1a1a1ed3cba56038a60e1527ba4ab1628c6b9941 - languageName: node - linkType: hard - "default-browser@npm:^4.0.0": version: 4.0.0 resolution: "default-browser@npm:4.0.0" @@ -22798,6 +25243,13 @@ __metadata: languageName: node linkType: hard +"defu@npm:^6.1.2": + version: 6.1.2 + resolution: "defu@npm:6.1.2" + checksum: 2ec0ff8414d5a1ab2b8c7e9a79bbad6d97d23ea7ebf5dcf80c3c7ffd9715c30f84a3cc47b917379ea756b3db0dc4701ce6400e493a1ae1688dffcd0f884233b2 + languageName: node + linkType: hard + "degenerator@npm:^5.0.0": version: 5.0.1 resolution: "degenerator@npm:5.0.1" @@ -22915,7 +25367,7 @@ __metadata: languageName: node linkType: hard -"detect-indent@npm:^6.0.0": +"detect-indent@npm:^6.0.0, detect-indent@npm:^6.1.0": version: 6.1.0 resolution: "detect-indent@npm:6.1.0" checksum: ab953a73c72dbd4e8fc68e4ed4bfd92c97eb6c43734af3900add963fd3a9316f3bc0578b018b24198d4c31a358571eff5f0656e81a1f3b9ad5c547d58b2d093d @@ -23484,7 +25936,7 @@ __metadata: languageName: node linkType: hard -"duplexify@npm:^3.4.2, duplexify@npm:^3.6.0": +"duplexify@npm:^3.4.2, duplexify@npm:^3.5.0, duplexify@npm:^3.6.0": version: 3.7.1 resolution: "duplexify@npm:3.7.1" dependencies: @@ -23559,7 +26011,7 @@ __metadata: languageName: node linkType: hard -"ejs@npm:^3.1.9": +"ejs@npm:^3.1.8, ejs@npm:^3.1.9": version: 3.1.9 resolution: "ejs@npm:3.1.9" dependencies: @@ -23598,6 +26050,13 @@ __metadata: languageName: node linkType: hard +"electron-to-chromium@npm:^1.4.477": + version: 1.4.526 + resolution: "electron-to-chromium@npm:1.4.526" + checksum: db37adc0b39b8d3f600e86f5f451744025a174fbe2e42ac4a7dbda809d7b417a0624d2570a8df5cb8f9f1e30d6b9ca9a5079eb09288ddd4a9588c6a39e3c3a0a + languageName: node + linkType: hard + "elliptic@npm:^6.4.0, elliptic@npm:^6.5.3, elliptic@npm:^6.5.4": version: 6.5.4 resolution: "elliptic@npm:6.5.4" @@ -24098,6 +26557,13 @@ __metadata: languageName: node linkType: hard +"esbuild-plugin-alias@npm:^0.2.1": + version: 0.2.1 + resolution: "esbuild-plugin-alias@npm:0.2.1" + checksum: afe2d2c8b5f09d5321cb8d9c0825e8a9f6e03c2d50df92f953a291d4620cc29eddb3da9e33b238f6d8f77738e0277bdcb831f127399449fecf78fb84c04e5da9 + languageName: node + linkType: hard + "esbuild-plugin-svgr@npm:^2.0.0": version: 2.0.0 resolution: "esbuild-plugin-svgr@npm:2.0.0" @@ -24118,6 +26584,17 @@ __metadata: languageName: node linkType: hard +"esbuild-register@npm:^3.5.0": + version: 3.5.0 + resolution: "esbuild-register@npm:3.5.0" + dependencies: + debug: ^4.3.4 + peerDependencies: + esbuild: ">=0.12 <1" + checksum: f4307753c9672a2c901d04a1165031594a854f0a4c6f4c1db08aa393b68a193d38f2df483dc8ca0513e89f7b8998415e7e26fb9830989fb8cdccc5fb5f181c6b + languageName: node + linkType: hard + "esbuild-scss-modules-plugin@npm:^1.1.1": version: 1.1.1 resolution: "esbuild-scss-modules-plugin@npm:1.1.1" @@ -24439,6 +26916,83 @@ __metadata: languageName: node linkType: hard +"esbuild@npm:^0.18.0": + version: 0.18.20 + resolution: "esbuild@npm:0.18.20" + dependencies: + "@esbuild/android-arm": 0.18.20 + "@esbuild/android-arm64": 0.18.20 + "@esbuild/android-x64": 0.18.20 + "@esbuild/darwin-arm64": 0.18.20 + "@esbuild/darwin-x64": 0.18.20 + "@esbuild/freebsd-arm64": 0.18.20 + "@esbuild/freebsd-x64": 0.18.20 + "@esbuild/linux-arm": 0.18.20 + "@esbuild/linux-arm64": 0.18.20 + "@esbuild/linux-ia32": 0.18.20 + "@esbuild/linux-loong64": 0.18.20 + "@esbuild/linux-mips64el": 0.18.20 + "@esbuild/linux-ppc64": 0.18.20 + "@esbuild/linux-riscv64": 0.18.20 + "@esbuild/linux-s390x": 0.18.20 + "@esbuild/linux-x64": 0.18.20 + "@esbuild/netbsd-x64": 0.18.20 + "@esbuild/openbsd-x64": 0.18.20 + "@esbuild/sunos-x64": 0.18.20 + "@esbuild/win32-arm64": 0.18.20 + "@esbuild/win32-ia32": 0.18.20 + "@esbuild/win32-x64": 0.18.20 + dependenciesMeta: + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: 5d253614e50cdb6ec22095afd0c414f15688e7278a7eb4f3720a6dd1306b0909cf431e7b9437a90d065a31b1c57be60130f63fe3e8d0083b588571f31ee6ec7b + languageName: node + linkType: hard + "escalade@npm:^3.1.1": version: 3.1.1 resolution: "escalade@npm:3.1.1" @@ -25780,6 +28334,20 @@ __metadata: languageName: node linkType: hard +"extract-zip@npm:^1.6.6": + version: 1.7.0 + resolution: "extract-zip@npm:1.7.0" + dependencies: + concat-stream: ^1.6.2 + debug: ^2.6.9 + mkdirp: ^0.5.4 + yauzl: ^2.10.0 + bin: + extract-zip: cli.js + checksum: 011bab660d738614555773d381a6ba4815d98c1cfcdcdf027e154ebcc9fc8c9ef637b3ea5c9b2144013100071ee41722ed041fc9aacc60f6198ef747cac0c073 + languageName: node + linkType: hard + "extsprintf@npm:1.3.0": version: 1.3.0 resolution: "extsprintf@npm:1.3.0" @@ -26058,6 +28626,16 @@ __metadata: languageName: node linkType: hard +"file-system-cache@npm:2.3.0": + version: 2.3.0 + resolution: "file-system-cache@npm:2.3.0" + dependencies: + fs-extra: 11.1.1 + ramda: 0.29.0 + checksum: 74afa2870a062500643d41e02d1fbd47a3f30100f9e153dec5233d59f05545f4c8ada6085629d624e043479ac28c0cafc31824f7b49a3f997efab8cc5d05bfee + languageName: node + linkType: hard + "file-system-cache@npm:^1.0.5": version: 1.0.5 resolution: "file-system-cache@npm:1.0.5" @@ -26164,7 +28742,7 @@ __metadata: languageName: node linkType: hard -"find-cache-dir@npm:^3.2.0": +"find-cache-dir@npm:^3.0.0, find-cache-dir@npm:^3.2.0": version: 3.3.2 resolution: "find-cache-dir@npm:3.3.2" dependencies: @@ -26309,6 +28887,13 @@ __metadata: languageName: node linkType: hard +"flow-parser@npm:0.*": + version: 0.216.1 + resolution: "flow-parser@npm:0.216.1" + checksum: c3966f376b20763c842f84aa93f0d7a6014682823adc283715b3025263a5a0ab0acc0d4a9582ec512051936ce5a68c6c2d19cfb5e95356b992200daeeda1a72e + languageName: node + linkType: hard + "flush-write-stream@npm:^1.0.0": version: 1.1.1 resolution: "flush-write-stream@npm:1.1.1" @@ -26553,6 +29138,17 @@ __metadata: languageName: node linkType: hard +"fs-extra@npm:11.1.1, fs-extra@npm:^11.1.0": + version: 11.1.1 + resolution: "fs-extra@npm:11.1.1" + dependencies: + graceful-fs: ^4.2.0 + jsonfile: ^6.0.1 + universalify: ^2.0.0 + checksum: fb883c68245b2d777fbc1f2082c9efb084eaa2bbf9fddaa366130d196c03608eebef7fb490541276429ee1ca99f317e2d73e96f5ca0999eefedf5a624ae1edfd + languageName: node + linkType: hard + "fs-extra@npm:^0.30.0": version: 0.30.0 resolution: "fs-extra@npm:0.30.0" @@ -26577,17 +29173,6 @@ __metadata: languageName: node linkType: hard -"fs-extra@npm:^11.1.0": - version: 11.1.1 - resolution: "fs-extra@npm:11.1.1" - dependencies: - graceful-fs: ^4.2.0 - jsonfile: ^6.0.1 - universalify: ^2.0.0 - checksum: fb883c68245b2d777fbc1f2082c9efb084eaa2bbf9fddaa366130d196c03608eebef7fb490541276429ee1ca99f317e2d73e96f5ca0999eefedf5a624ae1edfd - languageName: node - linkType: hard - "fs-extra@npm:^8.1.0": version: 8.1.0 resolution: "fs-extra@npm:8.1.0" @@ -26910,6 +29495,13 @@ __metadata: languageName: node linkType: hard +"get-npm-tarball-url@npm:^2.0.3": + version: 2.0.3 + resolution: "get-npm-tarball-url@npm:2.0.3" + checksum: 8ad48a6f1126697665e12ebf053e0d1c3b15b3c4f29ea6c458387ac68d044ea1c08f0f2eb5c0fe35447fdd2da4f2fb5c9882feb5a2ea195c773f94e762c9b886 + languageName: node + linkType: hard + "get-own-enumerable-property-symbols@npm:^3.0.0": version: 3.0.2 resolution: "get-own-enumerable-property-symbols@npm:3.0.2" @@ -26947,6 +29539,13 @@ __metadata: languageName: node linkType: hard +"get-port@npm:^5.1.1": + version: 5.1.1 + resolution: "get-port@npm:5.1.1" + checksum: 0162663ffe5c09e748cd79d97b74cd70e5a5c84b760a475ce5767b357fb2a57cb821cee412d646aa8a156ed39b78aab88974eddaa9e5ee926173c036c0713787 + languageName: node + linkType: hard + "get-port@npm:^6.1.2": version: 6.1.2 resolution: "get-port@npm:6.1.2" @@ -27061,6 +29660,23 @@ __metadata: languageName: node linkType: hard +"giget@npm:^1.0.0": + version: 1.1.2 + resolution: "giget@npm:1.1.2" + dependencies: + colorette: ^2.0.19 + defu: ^6.1.2 + https-proxy-agent: ^5.0.1 + mri: ^1.2.0 + node-fetch-native: ^1.0.2 + pathe: ^1.1.0 + tar: ^6.1.13 + bin: + giget: dist/cli.mjs + checksum: 76ad0f7e792ee95dd6c4e1096697fdcce61a2a3235a6c21761fc3e0d1053342074ce71c80059d6d4363fd34152e5d7b2e58221412f300c852ff7d4a12d0321fe + languageName: node + linkType: hard + "git-hooks-list@npm:^3.0.0": version: 3.1.0 resolution: "git-hooks-list@npm:3.1.0" @@ -27209,6 +29825,21 @@ __metadata: languageName: node linkType: hard +"glob@npm:^10.0.0": + version: 10.3.5 + resolution: "glob@npm:10.3.5" + dependencies: + foreground-child: ^3.1.0 + jackspeak: ^2.0.3 + minimatch: ^9.0.1 + minipass: ^5.0.0 || ^6.0.2 || ^7.0.0 + path-scurry: ^1.10.1 + bin: + glob: dist/cjs/src/bin.js + checksum: 564f4799cae48c0bcc841c88a20b539b5701c27ed5596f8623f588b3c523262d3fc20eb1ea89cab9c75b0912faf40ca5501fc835f982225d0d0599282b09e97a + languageName: node + linkType: hard + "glob@npm:^10.2.2": version: 10.3.3 resolution: "glob@npm:10.3.3" @@ -27631,6 +30262,22 @@ __metadata: languageName: node linkType: hard +"gunzip-maybe@npm:^1.4.2": + version: 1.4.2 + resolution: "gunzip-maybe@npm:1.4.2" + dependencies: + browserify-zlib: ^0.1.4 + is-deflate: ^1.0.0 + is-gzip: ^1.0.0 + peek-stream: ^1.1.0 + pumpify: ^1.3.3 + through2: ^2.0.3 + bin: + gunzip-maybe: bin.js + checksum: bc4d4977c24a2860238df271de75d53dd72a359d19f1248d1c613807dc221d3b8ae09624e3085c8106663e3e1b59db62a85b261d1138c2cc24efad9df577d4e1 + languageName: node + linkType: hard + "handle-thing@npm:^2.0.0": version: 2.0.1 resolution: "handle-thing@npm:2.0.1" @@ -28552,7 +31199,7 @@ __metadata: languageName: node linkType: hard -"https-proxy-agent@npm:5.0.1": +"https-proxy-agent@npm:5.0.1, https-proxy-agent@npm:^5.0.1": version: 5.0.1 resolution: "https-proxy-agent@npm:5.0.1" dependencies: @@ -28562,6 +31209,16 @@ __metadata: languageName: node linkType: hard +"https-proxy-agent@npm:^4.0.0": + version: 4.0.0 + resolution: "https-proxy-agent@npm:4.0.0" + dependencies: + agent-base: 5 + debug: 4 + checksum: 19471d5aae3e747b1c98b17556647e2a1362e68220c6b19585a8527498f32e62e03c41d2872d059d8720d56846bd7460a80ac06f876bccfa786468ff40dd5eef + languageName: node + linkType: hard + "https-proxy-agent@npm:^5.0.0": version: 5.0.0 resolution: "https-proxy-agent@npm:5.0.0" @@ -28784,7 +31441,7 @@ __metadata: languageName: node linkType: hard -"import-fresh@npm:^3.0.0, import-fresh@npm:^3.1.0, import-fresh@npm:^3.2.1": +"import-fresh@npm:^3.0.0, import-fresh@npm:^3.1.0, import-fresh@npm:^3.2.1, import-fresh@npm:^3.3.0": version: 3.3.0 resolution: "import-fresh@npm:3.3.0" dependencies: @@ -29352,6 +32009,13 @@ __metadata: languageName: node linkType: hard +"is-deflate@npm:^1.0.0": + version: 1.0.0 + resolution: "is-deflate@npm:1.0.0" + checksum: c2f9f2d3db79ac50c5586697d1e69a55282a2b0cc5e437b3c470dd47f24e40b6216dcd7e024511e21381607bf57afa019343e3bd0e08a119032818b596004262 + languageName: node + linkType: hard + "is-descriptor@npm:^0.1.0": version: 0.1.6 resolution: "is-descriptor@npm:0.1.6" @@ -29496,6 +32160,13 @@ __metadata: languageName: node linkType: hard +"is-gzip@npm:^1.0.0": + version: 1.0.0 + resolution: "is-gzip@npm:1.0.0" + checksum: 0d28931c1f445fa29c900cf9f48e06e9d1d477a3bf7bd7332e7ce68f1333ccd8cb381de2f0f62a9a262d9c0912608a9a71b4a40e788e201b3dbd67072bb20d86 + languageName: node + linkType: hard + "is-hexadecimal@npm:^1.0.0": version: 1.0.4 resolution: "is-hexadecimal@npm:1.0.4" @@ -31141,6 +33812,15 @@ __metadata: languageName: node linkType: hard +"jiti@npm:^1.18.2": + version: 1.20.0 + resolution: "jiti@npm:1.20.0" + bin: + jiti: bin/jiti.js + checksum: 7924062b5675142e3e272a27735be84b7bfc0a0eb73217fc2dcafa034f37c4f7b4b9ffc07dd98bcff0f739a8811ce1544db205ae7e97b1c86f0df92c65ce3c72 + languageName: node + linkType: hard + "joi@npm:^17.3.0": version: 17.9.1 resolution: "joi@npm:17.9.1" @@ -31253,6 +33933,37 @@ __metadata: languageName: node linkType: hard +"jscodeshift@npm:^0.14.0": + version: 0.14.0 + resolution: "jscodeshift@npm:0.14.0" + dependencies: + "@babel/core": ^7.13.16 + "@babel/parser": ^7.13.16 + "@babel/plugin-proposal-class-properties": ^7.13.0 + "@babel/plugin-proposal-nullish-coalescing-operator": ^7.13.8 + "@babel/plugin-proposal-optional-chaining": ^7.13.12 + "@babel/plugin-transform-modules-commonjs": ^7.13.8 + "@babel/preset-flow": ^7.13.13 + "@babel/preset-typescript": ^7.13.0 + "@babel/register": ^7.13.16 + babel-core: ^7.0.0-bridge.0 + chalk: ^4.1.2 + flow-parser: 0.* + graceful-fs: ^4.2.4 + micromatch: ^4.0.4 + neo-async: ^2.5.0 + node-dir: ^0.1.17 + recast: ^0.21.0 + temp: ^0.8.4 + write-file-atomic: ^2.3.0 + peerDependencies: + "@babel/preset-env": ^7.1.6 + bin: + jscodeshift: bin/jscodeshift.js + checksum: 54ea6d639455883336f80b38a70648821c88b7942315dc0fbab01bc34a9ad0f0f78e3bd69304b5ab167e4262d6ed7e6284c6d32525ab01c89d9118df89b3e2a0 + languageName: node + linkType: hard + "jsdom@npm:^19.0.0": version: 19.0.0 resolution: "jsdom@npm:19.0.0" @@ -31898,6 +34609,16 @@ __metadata: languageName: node linkType: hard +"less-loader@npm:^11.1.0": + version: 11.1.3 + resolution: "less-loader@npm:11.1.3" + peerDependencies: + less: ^3.5.0 || ^4.0.0 + webpack: ^5.0.0 + checksum: fe0de6b5ab930a4521d04555d9bd77723164bfa0f71eb5724d91c45090af544000e2d7f598cd83ec4e1445e6b943cc0c0dd1445fb2e83fd7c12f4ad3a0db05c5 + languageName: node + linkType: hard + "level-codec@npm:9.0.2, level-codec@npm:^9.0.0": version: 9.0.2 resolution: "level-codec@npm:9.0.2" @@ -34332,7 +37053,7 @@ __metadata: languageName: node linkType: hard -"mime-types@npm:^2.1.18, mime-types@npm:^2.1.30, mime-types@npm:^2.1.31, mime-types@npm:~2.1.19, mime-types@npm:~2.1.34": +"mime-types@npm:^2.1.18, mime-types@npm:^2.1.25, mime-types@npm:^2.1.30, mime-types@npm:^2.1.31, mime-types@npm:~2.1.19, mime-types@npm:~2.1.34": version: 2.1.35 resolution: "mime-types@npm:2.1.35" dependencies: @@ -34350,6 +37071,15 @@ __metadata: languageName: node linkType: hard +"mime@npm:^2.0.3": + version: 2.6.0 + resolution: "mime@npm:2.6.0" + bin: + mime: cli.js + checksum: 1497ba7b9f6960694268a557eae24b743fd2923da46ec392b042469f4b901721ba0adcf8b0d3c2677839d0e243b209d76e5edcbd09cfdeffa2dfb6bb4df4b862 + languageName: node + linkType: hard + "mime@npm:^2.4.4": version: 2.5.2 resolution: "mime@npm:2.5.2" @@ -34693,7 +37423,7 @@ __metadata: languageName: node linkType: hard -"mkdirp@npm:>=0.5 0, mkdirp@npm:^0.5.6": +"mkdirp@npm:>=0.5 0, mkdirp@npm:^0.5.4, mkdirp@npm:^0.5.6": version: 0.5.6 resolution: "mkdirp@npm:0.5.6" dependencies: @@ -34808,7 +37538,7 @@ __metadata: languageName: node linkType: hard -"mri@npm:^1.1.0": +"mri@npm:^1.1.0, mri@npm:^1.2.0": version: 1.2.0 resolution: "mri@npm:1.2.0" checksum: 83f515abbcff60150873e424894a2f65d68037e5a7fcde8a9e2b285ee9c13ac581b63cfc1e6826c4732de3aeb84902f7c1e16b7aff46cd3f897a0f757a894e85 @@ -35050,7 +37780,7 @@ __metadata: languageName: node linkType: hard -"node-dir@npm:^0.1.10": +"node-dir@npm:^0.1.10, node-dir@npm:^0.1.17": version: 0.1.17 resolution: "node-dir@npm:0.1.17" dependencies: @@ -35059,6 +37789,13 @@ __metadata: languageName: node linkType: hard +"node-fetch-native@npm:^1.0.2": + version: 1.4.0 + resolution: "node-fetch-native@npm:1.4.0" + checksum: 92d25d3d480709bf110642876f0d12222641795d266a7730a10a5f117a6d4071ca6e205fba4e76347a29786c7bcce94956a5f2b212607064e81950b35f1af0ae + languageName: node + linkType: hard + "node-fetch@npm:^2.6.7": version: 2.6.7 resolution: "node-fetch@npm:2.6.7" @@ -35252,6 +37989,13 @@ __metadata: languageName: node linkType: hard +"node-releases@npm:^2.0.13": + version: 2.0.13 + resolution: "node-releases@npm:2.0.13" + checksum: 17ec8f315dba62710cae71a8dad3cd0288ba943d2ece43504b3b1aa8625bf138637798ab470b1d9035b0545996f63000a8a926e0f6d35d0996424f8b6d36dda3 + languageName: node + linkType: hard + "node-releases@npm:^2.0.6": version: 2.0.6 resolution: "node-releases@npm:2.0.6" @@ -36026,6 +38770,17 @@ __metadata: languageName: node linkType: hard +"open@npm:^8.0.4": + version: 8.4.2 + resolution: "open@npm:8.4.2" + dependencies: + define-lazy-prop: ^2.0.0 + is-docker: ^2.1.1 + is-wsl: ^2.2.0 + checksum: 6388bfff21b40cb9bd8f913f9130d107f2ed4724ea81a8fd29798ee322b361ca31fa2cdfb491a5c31e43a3996cfe9566741238c7a741ada8d7af1cb78d85cf26 + languageName: node + linkType: hard + "open@npm:^8.0.9, open@npm:^8.4.0": version: 8.4.0 resolution: "open@npm:8.4.0" @@ -36470,6 +39225,13 @@ __metadata: languageName: node linkType: hard +"pako@npm:~0.2.0": + version: 0.2.9 + resolution: "pako@npm:0.2.9" + checksum: 055f9487cd57fbb78df84315873bbdd089ba286f3499daed47d2effdc6253e981f5db6898c23486de76d4a781559f890d643bd3a49f70f1b4a18019c98aa5125 + languageName: node + linkType: hard + "pako@npm:~1.0.5": version: 1.0.11 resolution: "pako@npm:1.0.11" @@ -36830,6 +39592,17 @@ __metadata: languageName: node linkType: hard +"peek-stream@npm:^1.1.0": + version: 1.1.3 + resolution: "peek-stream@npm:1.1.3" + dependencies: + buffer-from: ^1.0.0 + duplexify: ^3.5.0 + through2: ^2.0.3 + checksum: a0e09d6d1a8a01158a3334f20d6b1cdd91747eba24eb06a1d742eefb620385593121a76d4378cc81f77cdce6a66df0575a41041b1189c510254aec91878afc99 + languageName: node + linkType: hard + "pend@npm:~1.2.0": version: 1.2.0 resolution: "pend@npm:1.2.0" @@ -37250,6 +40023,20 @@ __metadata: languageName: node linkType: hard +"postcss-loader@npm:^7.2.4": + version: 7.3.3 + resolution: "postcss-loader@npm:7.3.3" + dependencies: + cosmiconfig: ^8.2.0 + jiti: ^1.18.2 + semver: ^7.3.8 + peerDependencies: + postcss: ^7.0.0 || ^8.0.1 + webpack: ^5.0.0 + checksum: c724044d6ae56334535c26bb4efc9c151431d44d60bc8300157c760747281a242757d8dab32db72738434531175b38a408cb0b270bb96207c07584dcfcd899ff + languageName: node + linkType: hard + "postcss-media-query-parser@npm:^0.2.3": version: 0.2.3 resolution: "postcss-media-query-parser@npm:0.2.3" @@ -37379,6 +40166,19 @@ __metadata: languageName: node linkType: hard +"postcss-modules-local-by-default@npm:^4.0.3": + version: 4.0.3 + resolution: "postcss-modules-local-by-default@npm:4.0.3" + dependencies: + icss-utils: ^5.0.0 + postcss-selector-parser: ^6.0.2 + postcss-value-parser: ^4.1.0 + peerDependencies: + postcss: ^8.1.0 + checksum: 2f8083687f3d6067885f8863dd32dbbb4f779cfcc7e52c17abede9311d84faf6d3ed8760e7c54c6380281732ae1f78e5e56a28baf3c271b33f450a11c9e30485 + languageName: node + linkType: hard + "postcss-modules-scope@npm:^2.2.0": version: 2.2.0 resolution: "postcss-modules-scope@npm:2.2.0" @@ -37728,6 +40528,17 @@ __metadata: languageName: node linkType: hard +"postcss@npm:^8.2.14": + version: 8.4.31 + resolution: "postcss@npm:8.4.31" + dependencies: + nanoid: ^3.3.6 + picocolors: ^1.0.0 + source-map-js: ^1.0.2 + checksum: 1d8611341b073143ad90486fcdfeab49edd243377b1f51834dc4f6d028e82ce5190e4f11bb2633276864503654fb7cab28e67abdc0fbf9d1f88cad4a0ff0beea + languageName: node + linkType: hard + "postcss@npm:^8.3.5": version: 8.4.20 resolution: "postcss@npm:8.4.20" @@ -37879,6 +40690,15 @@ __metadata: languageName: node linkType: hard +"prettier@npm:^2.8.0": + version: 2.8.8 + resolution: "prettier@npm:2.8.8" + bin: + prettier: bin-prettier.js + checksum: b49e409431bf129dd89238d64299ba80717b57ff5a6d1c1a8b1a28b590d998a34e083fa13573bc732bb8d2305becb4c9a4407f8486c81fa7d55100eb08263cf8 + languageName: node + linkType: hard + "pretty-error@npm:^2.1.1": version: 2.1.2 resolution: "pretty-error@npm:2.1.2" @@ -38058,7 +40878,7 @@ __metadata: languageName: node linkType: hard -"progress@npm:2.0.3, progress@npm:^2.0.0, progress@npm:^2.0.3": +"progress@npm:2.0.3, progress@npm:^2.0.0, progress@npm:^2.0.1, progress@npm:^2.0.3": version: 2.0.3 resolution: "progress@npm:2.0.3" checksum: f67403fe7b34912148d9252cb7481266a354bd99ce82c835f79070643bb3c6583d10dbcfda4d41e04bbc1d8437e9af0fb1e1f2135727878f5308682a579429b7 @@ -38271,7 +41091,7 @@ __metadata: languageName: node linkType: hard -"proxy-from-env@npm:1.1.0, proxy-from-env@npm:^1.1.0": +"proxy-from-env@npm:1.1.0, proxy-from-env@npm:^1.0.0, proxy-from-env@npm:^1.1.0": version: 1.1.0 resolution: "proxy-from-env@npm:1.1.0" checksum: ed7fcc2ba0a33404958e34d95d18638249a68c430e30fcb6c478497d72739ba64ce9810a24f53a7d921d0c065e5b78e3822759800698167256b04659366ca4d4 @@ -38400,6 +41220,24 @@ __metadata: languageName: node linkType: hard +"puppeteer-core@npm:^2.1.1": + version: 2.1.1 + resolution: "puppeteer-core@npm:2.1.1" + dependencies: + "@types/mime-types": ^2.1.0 + debug: ^4.1.0 + extract-zip: ^1.6.6 + https-proxy-agent: ^4.0.0 + mime: ^2.0.3 + mime-types: ^2.1.25 + progress: ^2.0.1 + proxy-from-env: ^1.0.0 + rimraf: ^2.6.1 + ws: ^6.1.0 + checksum: 2ddb597ef1b2d162b4aa49833b977734129edf7c8fa558fc38c59d273e79aa1bd079481c642de87f7163665f7f37aa52683da2716bafb7d3cab68c262c36ec28 + languageName: node + linkType: hard + "puppeteer-core@npm:^20.9.0": version: 20.9.0 resolution: "puppeteer-core@npm:20.9.0" @@ -38575,6 +41413,13 @@ __metadata: languageName: node linkType: hard +"ramda@npm:0.29.0": + version: 0.29.0 + resolution: "ramda@npm:0.29.0" + checksum: 9ab26c06eb7545cbb7eebcf75526d6ee2fcaae19e338f165b2bf32772121e7b28192d6664d1ba222ff76188ba26ab307342d66e805dbb02c860560adc4d5dd57 + languageName: node + linkType: hard + "ramda@npm:^0.21.0": version: 0.21.0 resolution: "ramda@npm:0.21.0" @@ -40404,6 +43249,18 @@ __metadata: languageName: node linkType: hard +"recast@npm:^0.21.0": + version: 0.21.5 + resolution: "recast@npm:0.21.5" + dependencies: + ast-types: 0.15.2 + esprima: ~4.0.0 + source-map: ~0.6.1 + tslib: ^2.0.1 + checksum: 03cc7f57562238ba258d468be67bf7446ce7a707bc87a087891dad15afead46c36e9aaeedf2130e2ab5a465244a9c62bfd4127849761cf8f4085abe2f3e5f485 + languageName: node + linkType: hard + "recast@npm:^0.23.1": version: 0.23.1 resolution: "recast@npm:0.23.1" @@ -40606,6 +43463,15 @@ __metadata: languageName: node linkType: hard +"regenerator-transform@npm:^0.15.2": + version: 0.15.2 + resolution: "regenerator-transform@npm:0.15.2" + dependencies: + "@babel/runtime": ^7.8.4 + checksum: 20b6f9377d65954980fe044cfdd160de98df415b4bff38fbade67b3337efaf078308c4fed943067cd759827cc8cfeca9cb28ccda1f08333b85d6a2acbd022c27 + languageName: node + linkType: hard + "regex-not@npm:^1.0.0, regex-not@npm:^1.0.2": version: 1.0.2 resolution: "regex-not@npm:1.0.2" @@ -40616,6 +43482,13 @@ __metadata: languageName: node linkType: hard +"regex-parser@npm:^2.2.11": + version: 2.2.11 + resolution: "regex-parser@npm:2.2.11" + checksum: 78200331ec0cc372302d287a4946c38681eb5fe435453fca572cb53cac0ba579e5eb3b9e25eac24c0c80a555fb3ea7a637814a35da1e9bc88e8819110ae5de24 + languageName: node + linkType: hard + "regexp-match-indices@npm:1.0.2": version: 1.0.2 resolution: "regexp-match-indices@npm:1.0.2" @@ -40708,6 +43581,20 @@ __metadata: languageName: node linkType: hard +"regexpu-core@npm:^5.3.1": + version: 5.3.2 + resolution: "regexpu-core@npm:5.3.2" + dependencies: + "@babel/regjsgen": ^0.8.0 + regenerate: ^1.4.2 + regenerate-unicode-properties: ^10.1.0 + regjsparser: ^0.9.1 + unicode-match-property-ecmascript: ^2.0.0 + unicode-match-property-value-ecmascript: ^2.1.0 + checksum: 95bb97088419f5396e07769b7de96f995f58137ad75fac5811fb5fe53737766dfff35d66a0ee66babb1eb55386ef981feaef392f9df6d671f3c124812ba24da2 + languageName: node + linkType: hard + "registry-auth-token@npm:^4.0.0": version: 4.2.2 resolution: "registry-auth-token@npm:4.2.2" @@ -41119,6 +44006,19 @@ __metadata: languageName: node linkType: hard +"resolve-url-loader@npm:^5.0.0": + version: 5.0.0 + resolution: "resolve-url-loader@npm:5.0.0" + dependencies: + adjust-sourcemap-loader: ^4.0.0 + convert-source-map: ^1.7.0 + loader-utils: ^2.0.0 + postcss: ^8.2.14 + source-map: 0.6.1 + checksum: 6d483733a4c26f75ce930a61943113bf730b5ba33a7186791cf1ae9c2ca02c3e94610bc6484ca008a372ee9e31750eccea74856a89daf1a29b8437ff564d27f2 + languageName: node + linkType: hard + "resolve-url@npm:^0.2.1": version: 0.2.1 resolution: "resolve-url@npm:0.2.1" @@ -41262,7 +44162,7 @@ __metadata: languageName: node linkType: hard -"rimraf@npm:2, rimraf@npm:^2.2.8, rimraf@npm:^2.5.4, rimraf@npm:^2.6.2, rimraf@npm:^2.6.3": +"rimraf@npm:2, rimraf@npm:^2.2.8, rimraf@npm:^2.5.4, rimraf@npm:^2.6.1, rimraf@npm:^2.6.2, rimraf@npm:^2.6.3": version: 2.7.1 resolution: "rimraf@npm:2.7.1" dependencies: @@ -41284,6 +44184,17 @@ __metadata: languageName: node linkType: hard +"rimraf@npm:~2.6.2": + version: 2.6.3 + resolution: "rimraf@npm:2.6.3" + dependencies: + glob: ^7.1.3 + bin: + rimraf: ./bin.js + checksum: 3ea587b981a19016297edb96d1ffe48af7e6af69660e3b371dbfc73722a73a0b0e9be5c88089fbeeb866c389c1098e07f64929c7414290504b855f54f901ab10 + languageName: node + linkType: hard + "ripemd160@npm:^2.0.0, ripemd160@npm:^2.0.1": version: 2.0.2 resolution: "ripemd160@npm:2.0.2" @@ -41766,6 +44677,30 @@ __metadata: languageName: node linkType: hard +"sass-loader@npm:^13.2.2": + version: 13.3.2 + resolution: "sass-loader@npm:13.3.2" + dependencies: + neo-async: ^2.6.2 + peerDependencies: + fibers: ">= 3.1.0" + node-sass: ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 + sass: ^1.3.0 + sass-embedded: "*" + webpack: ^5.0.0 + peerDependenciesMeta: + fibers: + optional: true + node-sass: + optional: true + sass: + optional: true + sass-embedded: + optional: true + checksum: 7394a8d1b818a289b9caabd979543c907b83e28ae08bc80ccb836e0ccabc4ae574c077ab2fa520ba5fb8abb2ec3e7c9822a1cbd8c58a28ff30018be9d1dc6c27 + languageName: node + linkType: hard + "sass@npm:^1.35.2": version: 1.62.1 resolution: "sass@npm:1.62.1" @@ -41779,6 +44714,19 @@ __metadata: languageName: node linkType: hard +"sass@npm:^1.68.0": + version: 1.68.0 + resolution: "sass@npm:1.68.0" + dependencies: + chokidar: ">=3.0.0 <4.0.0" + immutable: ^4.0.0 + source-map-js: ">=0.6.2 <2.0.0" + bin: + sass: sass.js + checksum: 65ccede83c96768beeb8dcaf67957b7c76b12ff1276bfd2849d7be151d46ba1400048a67717e6e5e4969bc75e87348e5530f5f272833f2e60a891c21a33d8ab0 + languageName: node + linkType: hard + "saxes@npm:^5.0.1": version: 5.0.1 resolution: "saxes@npm:5.0.1" @@ -41988,6 +44936,26 @@ __metadata: languageName: node linkType: hard +"semver@npm:^6.3.1": + version: 6.3.1 + resolution: "semver@npm:6.3.1" + bin: + semver: bin/semver.js + checksum: ae47d06de28836adb9d3e25f22a92943477371292d9b665fb023fae278d345d508ca1958232af086d85e0155aee22e313e100971898bbb8d5d89b8b1d4054ca2 + languageName: node + linkType: hard + +"semver@npm:^7.5.3": + version: 7.5.4 + resolution: "semver@npm:7.5.4" + dependencies: + lru-cache: ^6.0.0 + bin: + semver: bin/semver.js + checksum: 12d8ad952fa353b0995bf180cdac205a4068b759a140e5d3c608317098b3575ac2f1e09182206bf2eb26120e1c0ed8fb92c48c592f6099680de56bb071423ca3 + languageName: node + linkType: hard + "send@npm:0.17.1": version: 0.17.1 resolution: "send@npm:0.17.1" @@ -42364,6 +45332,15 @@ __metadata: languageName: node linkType: hard +"simple-update-notifier@npm:^2.0.0": + version: 2.0.0 + resolution: "simple-update-notifier@npm:2.0.0" + dependencies: + semver: ^7.5.3 + checksum: 9ba00d38ce6a29682f64a46213834e4eb01634c2f52c813a9a7b8873ca49cdbb703696f3290f3b27dc067de6d9418b0b84bef22c3eb074acf352529b2d6c27fd + languageName: node + linkType: hard + "sisteransi@npm:^1.0.5": version: 1.0.5 resolution: "sisteransi@npm:1.0.5" @@ -42652,6 +45629,13 @@ __metadata: languageName: node linkType: hard +"source-map@npm:0.6.1, source-map@npm:^0.6.0, source-map@npm:^0.6.1, source-map@npm:~0.6.0, source-map@npm:~0.6.1": + version: 0.6.1 + resolution: "source-map@npm:0.6.1" + checksum: 59ce8640cf3f3124f64ac289012c2b8bd377c238e316fb323ea22fbfe83da07d81e000071d7242cad7a23cd91c7de98e4df8830ec3f133cb6133a5f6e9f67bc2 + languageName: node + linkType: hard + "source-map@npm:0.8.0-beta.0": version: 0.8.0-beta.0 resolution: "source-map@npm:0.8.0-beta.0" @@ -42668,13 +45652,6 @@ __metadata: languageName: node linkType: hard -"source-map@npm:^0.6.0, source-map@npm:^0.6.1, source-map@npm:~0.6.0, source-map@npm:~0.6.1": - version: 0.6.1 - resolution: "source-map@npm:0.6.1" - checksum: 59ce8640cf3f3124f64ac289012c2b8bd377c238e316fb323ea22fbfe83da07d81e000071d7242cad7a23cd91c7de98e4df8830ec3f133cb6133a5f6e9f67bc2 - languageName: node - linkType: hard - "source-map@npm:^0.7.0, source-map@npm:^0.7.4": version: 0.7.4 resolution: "source-map@npm:0.7.4" @@ -43111,6 +46088,13 @@ __metadata: languageName: node linkType: hard +"store2@npm:^2.14.2": + version: 2.14.2 + resolution: "store2@npm:2.14.2" + checksum: 6f270fc5bab99b63f45fcc7bd8b99c2714b4adf880f557ed7ffb5ed3987131251165bccde425a00928aaf044870aee79ddeef548576d093c68703ed2edec45d7 + languageName: node + linkType: hard + "storybook-addon-designs@npm:^6.3.1": version: 6.3.1 resolution: "storybook-addon-designs@npm:6.3.1" @@ -43203,6 +46187,18 @@ __metadata: languageName: node linkType: hard +"storybook@npm:^7.4.3": + version: 7.4.3 + resolution: "storybook@npm:7.4.3" + dependencies: + "@storybook/cli": 7.4.3 + bin: + sb: ./index.js + storybook: ./index.js + checksum: 91395d18f545dfa8ffe8f1e70e248f03f85e5a3cc55d07127228480379095efa23cb499265de5e751c2c853d39c1a45ad85d3a3a4d359df61c65df81afab37a0 + languageName: node + linkType: hard + "stream-browserify@npm:^2.0.1": version: 2.0.2 resolution: "stream-browserify@npm:2.0.2" @@ -43654,7 +46650,7 @@ __metadata: languageName: node linkType: hard -"strip-json-comments@npm:^3.1.0, strip-json-comments@npm:^3.1.1, strip-json-comments@npm:~3.1.1": +"strip-json-comments@npm:^3.0.1, strip-json-comments@npm:^3.1.0, strip-json-comments@npm:^3.1.1, strip-json-comments@npm:~3.1.1": version: 3.1.1 resolution: "strip-json-comments@npm:3.1.1" checksum: 492f73e27268f9b1c122733f28ecb0e7e8d8a531a6662efbd08e22cccb3f9475e90a1b82cab06a392f6afae6d2de636f977e231296400d0ec5304ba70f166443 @@ -43717,6 +46713,15 @@ __metadata: languageName: node linkType: hard +"style-loader@npm:^3.3.2": + version: 3.3.3 + resolution: "style-loader@npm:3.3.3" + peerDependencies: + webpack: ^5.0.0 + checksum: f59c953f56f6a935bd6a1dfa409f1128fed2b66b48ce4a7a75b85862a7156e5e90ab163878962762f528ec4d510903d828da645e143fbffd26f055dc1c094078 + languageName: node + linkType: hard + "style-search@npm:^0.1.0": version: 0.1.0 resolution: "style-search@npm:0.1.0" @@ -44123,7 +47128,7 @@ __metadata: languageName: node linkType: hard -"tar-fs@npm:2.1.1, tar-fs@npm:^2.0.0": +"tar-fs@npm:2.1.1, tar-fs@npm:^2.0.0, tar-fs@npm:^2.1.1": version: 2.1.1 resolution: "tar-fs@npm:2.1.1" dependencies: @@ -44237,6 +47242,15 @@ __metadata: languageName: node linkType: hard +"telejson@npm:^7.2.0": + version: 7.2.0 + resolution: "telejson@npm:7.2.0" + dependencies: + memoizerific: ^1.11.3 + checksum: 55a3380c9ff3c5ad84581bb6bda28fc33c6b7c4a0c466894637da687639b8db0d21b0ff4c1bc1a7a92ae6b70662549d09e7b9e8b1ec334b2ef93078762ecdfb9 + languageName: node + linkType: hard + "temp-dir@npm:^2.0.0": version: 2.0.0 resolution: "temp-dir@npm:2.0.0" @@ -44244,6 +47258,15 @@ __metadata: languageName: node linkType: hard +"temp@npm:^0.8.4": + version: 0.8.4 + resolution: "temp@npm:0.8.4" + dependencies: + rimraf: ~2.6.2 + checksum: f35bed78565355dfdf95f730b7b489728bd6b7e35071bcc6497af7c827fb6c111fbe9063afc7b8cbc19522a072c278679f9a0ee81e684aa2c8617cc0f2e9c191 + languageName: node + linkType: hard + "tempy@npm:^1.0.1": version: 1.0.1 resolution: "tempy@npm:1.0.1" @@ -44460,7 +47483,7 @@ __metadata: languageName: node linkType: hard -"through2@npm:^2.0.0": +"through2@npm:^2.0.0, through2@npm:^2.0.3": version: 2.0.5 resolution: "through2@npm:2.0.5" dependencies: @@ -44537,7 +47560,7 @@ __metadata: languageName: node linkType: hard -"tiny-invariant@npm:^1.1.0": +"tiny-invariant@npm:^1.1.0, tiny-invariant@npm:^1.3.1": version: 1.3.1 resolution: "tiny-invariant@npm:1.3.1" checksum: 872dbd1ff20a21303a2fd20ce3a15602cfa7fcf9b228bd694a52e2938224313b5385a1078cb667ed7375d1612194feaca81c4ecbe93121ca1baebe344de4f84c @@ -45056,7 +48079,7 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^1.8.1, tslib@npm:^1.9.0, tslib@npm:^1.9.3": +"tslib@npm:^1.13.0, tslib@npm:^1.8.1, tslib@npm:^1.9.0, tslib@npm:^1.9.3": version: 1.14.1 resolution: "tslib@npm:1.14.1" checksum: dbe628ef87f66691d5d2959b3e41b9ca0045c3ee3c7c7b906cc1e328b39f199bb1ad9e671c39025bd56122ac57dfbf7385a94843b1cc07c60a4db74795829acd @@ -45994,6 +49017,20 @@ __metadata: languageName: node linkType: hard +"update-browserslist-db@npm:^1.0.11": + version: 1.0.13 + resolution: "update-browserslist-db@npm:1.0.13" + dependencies: + escalade: ^3.1.1 + picocolors: ^1.0.0 + peerDependencies: + browserslist: ">= 4.21.0" + bin: + update-browserslist-db: cli.js + checksum: 1e47d80182ab6e4ad35396ad8b61008ae2a1330221175d0abd37689658bdb61af9b705bfc41057fd16682474d79944fb2d86767c5ed5ae34b6276b9bed353322 + languageName: node + linkType: hard + "update-browserslist-db@npm:^1.0.9": version: 1.0.10 resolution: "update-browserslist-db@npm:1.0.10" @@ -46148,6 +49185,18 @@ __metadata: languageName: node linkType: hard +"use-resize-observer@npm:^9.1.0": + version: 9.1.0 + resolution: "use-resize-observer@npm:9.1.0" + dependencies: + "@juggle/resize-observer": ^3.3.1 + peerDependencies: + react: 16.8.0 - 18 + react-dom: 16.8.0 - 18 + checksum: 92be0ac34a3b3cf884cd55847c90792b5b44833dc258e96d650152815ad246afe45825aa223332203004d836535a927ab74f18dc0313229e2c7c69510eddf382 + languageName: node + linkType: hard + "use-sidecar@npm:^1.1.2": version: 1.1.2 resolution: "use-sidecar@npm:1.1.2" @@ -46229,7 +49278,7 @@ __metadata: languageName: node linkType: hard -"util@npm:^0.12.0, util@npm:^0.12.3": +"util@npm:^0.12.0, util@npm:^0.12.3, util@npm:^0.12.4": version: 0.12.5 resolution: "util@npm:0.12.5" dependencies: @@ -48101,6 +51150,17 @@ __metadata: languageName: node linkType: hard +"write-file-atomic@npm:^2.3.0": + version: 2.4.3 + resolution: "write-file-atomic@npm:2.4.3" + dependencies: + graceful-fs: ^4.1.11 + imurmurhash: ^0.1.4 + signal-exit: ^3.0.2 + checksum: 2db81f92ae974fd87ab4a5e7932feacaca626679a7c98fcc73ad8fcea5a1950eab32fa831f79e9391ac99b562ca091ad49be37a79045bd65f595efbb8f4596ae + languageName: node + linkType: hard + "write-file-atomic@npm:^3.0.0": version: 3.0.3 resolution: "write-file-atomic@npm:3.0.3" @@ -48182,6 +51242,15 @@ __metadata: languageName: node linkType: hard +"ws@npm:^6.1.0": + version: 6.2.2 + resolution: "ws@npm:6.2.2" + dependencies: + async-limiter: ~1.0.0 + checksum: aec3154ec51477c094ac2cb5946a156e17561a581fa27005cbf22c53ac57f8d4e5f791dd4bbba6a488602cb28778c8ab7df06251d590507c3c550fd8ebeee949 + languageName: node + linkType: hard + "ws@npm:^7.4.6": version: 7.5.3 resolution: "ws@npm:7.5.3" From 5b5d96bb6bdd502a1139eeddf76557566e767076 Mon Sep 17 00:00:00 2001 From: Renan Valentin Date: Tue, 24 Oct 2023 13:28:26 -0300 Subject: [PATCH 03/68] feat(extension): [LW-7984] voting procedures (#655) * feat(ui): create metadata component * wip * feat(ui): text link component * chore(ui): fix rebase * feat(ui): create metadata link component * feat(core): create voting procedures component * chore(extension): fix rebase * fix(ui): add work break * feat(extension): voting procedures * refactor(core): make actions optional * refactor(core): use action as key prop * refactor(extension): add custom title for voting procedures --- .../ConfirmTransaction.module.scss | 1 + .../ConfirmTransactionContent.tsx | 4 + .../VotingProceduresContainer.tsx | 93 +++++++++++++ .../components/confirm-transaction/utils.ts | 21 ++- .../src/lib/translations/en.json | 16 +++ packages/core/.storybook/preview.js | 19 +-- packages/core/src/index.ts | 1 + .../VotingProcedures.stories.ts | 107 +++++++++++++++ .../VotingProcedures/VotingProcedures.tsx | 127 ++++++++++++++++++ .../ui/components/VotingProcedures/index.ts | 1 + packages/ui/package.json | 12 ++ packages/ui/src/design-system/index.ts | 2 +- .../ui/src/design-system/metadata/index.ts | 1 + .../metadata/metadata-link.component.tsx | 45 +++++++ .../design-system/text-link/text-link.css.ts | 1 + 15 files changed, 438 insertions(+), 13 deletions(-) create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/VotingProceduresContainer.tsx create mode 100644 packages/core/src/ui/components/VotingProcedures/VotingProcedures.stories.ts create mode 100644 packages/core/src/ui/components/VotingProcedures/VotingProcedures.tsx create mode 100644 packages/core/src/ui/components/VotingProcedures/index.ts create mode 100644 packages/ui/src/design-system/metadata/metadata-link.component.tsx diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.module.scss b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.module.scss index 9a02141f0c..9d25c19f3c 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.module.scss +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.module.scss @@ -26,4 +26,5 @@ .actionBtn { width: 100%; } + z-index: 10; } diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransactionContent.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransactionContent.tsx index 8cee30714e..9df9c6f88b 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransactionContent.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransactionContent.tsx @@ -5,6 +5,7 @@ import { DappTransactionContainer } from './DappTransactionContainer'; import { TxType } from './utils'; import { SignTxData } from './types'; import { ConfirmDRepRetirementContainer } from './ConfirmDRepRetirementContainer'; +import { VotingProceduresContainer } from './VotingProceduresContainer'; interface Props { txType?: TxType; @@ -22,6 +23,9 @@ export const ConfirmTransactionContent = ({ txType, signTxData, errorMessage }: if (txType === TxType.DRepRetirement) { return ; } + if (txType === TxType.VotingProcedures) { + return ; + } return ; }; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/VotingProceduresContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/VotingProceduresContainer.tsx new file mode 100644 index 0000000000..8104ef9196 --- /dev/null +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/VotingProceduresContainer.tsx @@ -0,0 +1,93 @@ +import React, { useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { VotingProcedures } from '@lace/core'; +import { SignTxData } from './types'; +import { votingProceduresInspector } from './utils'; +import { Wallet } from '@lace/cardano'; +import { useWalletStore } from '@src/stores'; +import { config } from '@src/config'; + +interface Props { + signTxData: SignTxData; + errorMessage?: string; +} + +const getVoterType = (voterType: Wallet.Cardano.VoterType): string => { + switch (voterType) { + case Wallet.Cardano.VoterType.ccHotKeyHash: + case Wallet.Cardano.VoterType.ccHotScriptHash: + return 'Constitutional Committee'; + case Wallet.Cardano.VoterType.stakePoolKeyHash: + return 'SPO'; + case Wallet.Cardano.VoterType.dRepKeyHash: + case Wallet.Cardano.VoterType.dRepScriptHash: + default: + return 'DRep'; + } +}; + +const getVote = (vote: Wallet.Cardano.Vote): string => { + switch (vote) { + case Wallet.Cardano.Vote.yes: + return 'Yes'; + case Wallet.Cardano.Vote.no: + return 'No'; + case Wallet.Cardano.Vote.abstain: + default: + return 'Abstain'; + } +}; + +export const VotingProceduresContainer = ({ signTxData, errorMessage }: Props): React.ReactElement => { + const { t } = useTranslation(); + const votingProcedures = votingProceduresInspector(signTxData.tx); + const { environmentName } = useWalletStore(); + const { CEXPLORER_BASE_URL, CEXPLORER_URL_PATHS } = config(); + + const explorerBaseUrl = useMemo( + () => `${CEXPLORER_BASE_URL[environmentName]}/${CEXPLORER_URL_PATHS.Tx}`, + [CEXPLORER_BASE_URL, CEXPLORER_URL_PATHS.Tx, environmentName] + ); + + return ( + ({ + voter: { + type: getVoterType(votingProcedure.voter.__typename), + dRepId: votingProcedure.voter.credential.hash.toString() + }, + votes: votingProcedure.votes.map((vote) => ({ + actionId: { + index: vote.actionId.actionIndex, + txHash: vote.actionId.id.toString(), + txHashUrl: `${explorerBaseUrl}/${vote.actionId.id}` + }, + votingProcedure: { + vote: getVote(vote.votingProcedure.vote), + anchor: { + url: vote.votingProcedure.anchor?.url, + hash: vote.votingProcedure.anchor?.dataHash.toString() + } + } + })) + }))} + translations={{ + voterType: t('core.votingProcedures.voterType'), + procedureTitle: t('core.votingProcedures.procedureTitle'), + actionIdTitle: t('core.votingProcedures.actionIdTitle'), + vote: t('core.votingProcedures.vote'), + actionId: { + index: t('core.votingProcedures.actionId.index'), + txHash: t('core.votingProcedures.actionId.txHash') + }, + anchor: { + hash: t('core.votingProcedures.anchor.hash'), + url: t('core.votingProcedures.anchor.url') + }, + dRepId: t('core.votingProcedures.dRepId') + }} + errorMessage={errorMessage} + /> + ); +}; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/utils.ts b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/utils.ts index 6b6611499d..e3765947d0 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/utils.ts +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/utils.ts @@ -15,7 +15,8 @@ export enum TxType { Mint = 'Mint', Burn = 'Burn', DRepRegistration = 'DRepRegistration', - DRepRetirement = 'DRepRetirement' + DRepRetirement = 'DRepRetirement', + VotingProcedures = 'VotingProcedures' } export const getTitleKey = (txType: TxType): string => { @@ -27,6 +28,10 @@ export const getTitleKey = (txType: TxType): string => { return 'core.drepRetirement.title'; } + if (txType === TxType.VotingProcedures) { + return 'core.votingProcedures.title'; + } + return sectionTitle[DAPP_VIEWS.CONFIRM_TX]; }; @@ -94,18 +99,28 @@ export const dRepRetirementInspector = ( | Wallet.Cardano.UnRegisterDelegateRepresentativeCertificate | undefined; +export const votingProceduresInspector = (tx: Wallet.Cardano.Tx): Wallet.Cardano.VotingProcedures | undefined => + tx?.body?.votingProcedures; + export const getTxType = (tx: Wallet.Cardano.Tx): TxType => { const inspector = createTxInspector({ minted: assetsMintedInspector, burned: assetsBurnedInspector, dRepRegistration: dRepRegistrationInspector, - dRepRetirement: dRepRetirementInspector + dRepRetirement: dRepRetirementInspector, + votingProcedures: votingProceduresInspector }); - const { minted, burned, dRepRegistration, dRepRetirement } = inspector(tx as Wallet.Cardano.HydratedTx); + const { minted, burned, dRepRegistration, dRepRetirement, votingProcedures } = inspector( + tx as Wallet.Cardano.HydratedTx + ); const isMintTransaction = minted.length > 0; const isBurnTransaction = burned.length > 0; + if (votingProcedures) { + return TxType.VotingProcedures; + } + if (isMintTransaction) { return TxType.Mint; } diff --git a/apps/browser-extension-wallet/src/lib/translations/en.json b/apps/browser-extension-wallet/src/lib/translations/en.json index 3e55901003..621892c142 100644 --- a/apps/browser-extension-wallet/src/lib/translations/en.json +++ b/apps/browser-extension-wallet/src/lib/translations/en.json @@ -1113,6 +1113,22 @@ "tryingToUseAssetNotInWallet": "This DApp is trying to use token not held in your wallet.", "noCollateral": "Wallet should not be able to sign dapp txs without collateral." }, + "votingProcedures": { + "title": "Confirm Vote", + "voterType": "Voter type", + "procedureTitle": "Procedure", + "actionIdTitle": "Action ID", + "vote": "Vote", + "actionId": { + "index": "Index", + "txHash": "TX Hash" + }, + "anchor": { + "hash": "Anchor Hash", + "url": "Anchor URL" + }, + "dRepId": "DRep ID" + }, "drepRegistration": { "title": "Confirm DRep Registration", "metadata": "Metadata", diff --git a/packages/core/.storybook/preview.js b/packages/core/.storybook/preview.js index 8a971ba68f..7ffa549eec 100644 --- a/packages/core/.storybook/preview.js +++ b/packages/core/.storybook/preview.js @@ -4,7 +4,7 @@ import 'normalize.css'; import './theme.scss'; import { ThemeColorScheme, ThemeProvider } from '@lace/ui'; -const preview = { +export const preview = { parameters: { actions: { argTypesRegex: '^on[A-Z].*' }, controls: { @@ -13,14 +13,15 @@ const preview = { date: /Date$/ } } - }, - decorators: [ - (Story) => ( + } +}; + +export const decorators = [ + (Story) => { + return ( - ) - ] -}; - -export default preview; + ); + } +]; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 9b38a4b41a..b6db918e66 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -30,3 +30,4 @@ export * from '@ui/components/MnemonicWordsAutoComplete'; export * from '@ui/components/AddressCard'; export * from '@ui/components/ConfirmDRepRegistration'; export * from '@ui/components/ConfirmDRepRetirement'; +export * from '@ui/components/VotingProcedures'; diff --git a/packages/core/src/ui/components/VotingProcedures/VotingProcedures.stories.ts b/packages/core/src/ui/components/VotingProcedures/VotingProcedures.stories.ts new file mode 100644 index 0000000000..72a297d798 --- /dev/null +++ b/packages/core/src/ui/components/VotingProcedures/VotingProcedures.stories.ts @@ -0,0 +1,107 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { VotingProcedures } from './VotingProcedures'; +import { ComponentProps } from 'react'; + +const meta: Meta = { + title: 'VotingProcedures', + component: VotingProcedures, + parameters: { + layout: 'centered' + } +}; + +export default meta; +type Story = StoryObj; + +const data: ComponentProps = { + dappInfo: { + logo: 'https://cdn.mint.handle.me/favicon.png', + name: 'Mint', + url: 'https://preprod.mint.handle.me' + }, + data: [ + { + voter: { + type: 'DRep', + dRepId: 'drep1cs234l5mtapethapx8cq97nkpa27xf84phruh5f6jqxa78ymlp4' + }, + votes: [ + { + actionId: { + index: 0, + txHash: '26bfdcc75a7f4d0cd8c71f0189bc5ca5ad2f4a3db6240c82b5a0edac7f9203e0', + txHashUrl: + 'https://cexplorer.io/address/addr1q9wlvfl74g9h8txw5v0lfew2gjsw9z56d5kj8mmv5d8tudcx9eh8zefr3cxuje02lu6tgy083xkl39rr5xkj483vvd6q8nlapq' + }, + votingProcedure: { + anchor: { + hash: '9067f223838d88b83f660c05eedf7f6f65c45de31e522c1bcb6a1eb287b17e89', + url: 'https://shorturl.at/eK145' + }, + vote: 'Yes' + } + } + ] + } + ], + translations: { + voterType: 'Voter type', + procedureTitle: 'Procedure', + actionIdTitle: 'Action ID', + vote: 'Vote', + actionId: { + index: 'Index', + txHash: 'TX Hash' + }, + anchor: { + hash: 'Anchor Hash', + url: 'Anchor URL' + }, + dRepId: 'DRep ID' + } +}; + +export const Overview: Story = { + args: { + ...data + } +}; +export const WithError: Story = { + args: { + ...data, + errorMessage: 'Something went wrong' + } +}; + +export const MultipleVotes: Story = { + args: { + ...data, + data: [ + ...data.data, + { + voter: { + type: 'DRep', + dRepId: 'drep1cs234l5mtapethapx8cq97nkpa27xf84phruh5f6jqxa78ymlp4' + }, + votes: [ + { + actionId: { + index: 0, + txHash: '26bfdcc75a7f4d0cd8c71f0189bc5ca5ad2f4a3db6240c82b5a0edac7f9203e0', + txHashUrl: + 'https://cexplorer.io/address/addr1q9wlvfl74g9h8txw5v0lfew2gjsw9z56d5kj8mmv5d8tudcx9eh8zefr3cxuje02lu6tgy083xkl39rr5xkj483vvd6q8nlapq' + }, + votingProcedure: { + anchor: { + hash: '9067f223838d88b83f660c05eedf7f6f65c45de31e522c1bcb6a1eb287b17e89', + url: 'https://shorturl.at/eK145' + }, + vote: 'Yes' + } + } + ] + } + ] + } +}; diff --git a/packages/core/src/ui/components/VotingProcedures/VotingProcedures.tsx b/packages/core/src/ui/components/VotingProcedures/VotingProcedures.tsx new file mode 100644 index 0000000000..654f4b76f5 --- /dev/null +++ b/packages/core/src/ui/components/VotingProcedures/VotingProcedures.tsx @@ -0,0 +1,127 @@ +import React, { Fragment } from 'react'; +import { Box, Cell, Grid, Flex, Metadata, MetadataLink, Text, Divider, sx } from '@lace/ui'; +import { DappInfo, DappInfoProps } from '../DappInfo'; +import { ErrorPane, Ellipsis } from '@lace/common'; + +type VotingProcedure = { + voter: { + type: string; + dRepId: string; + }; + votes: { + actionId: { + index: number; + txHash: string; + txHashUrl: string; + }; + votingProcedure: { + vote: string; + anchor?: { + url: string; + hash: string; + }; + }; + }[]; +}; + +interface Props { + dappInfo: Omit; + errorMessage?: string; + data: VotingProcedure[]; + translations: { + actionIdTitle: string; + actionId: { + index: string; + txHash: string; + }; + anchor: { + url: string; + hash: string; + }; + dRepId: string; + procedureTitle: string; + vote: string; + voterType: string; + }; +} + +const indexCounter = (text: string, idx: number, length: number): string => (length > 1 ? `${text} ${idx + 1}` : text); + +export const VotingProcedures = ({ dappInfo, errorMessage, data, translations }: Props): JSX.Element => { + const textCss = sx({ + color: '$text_primary' + }); + + return ( + + + + + {errorMessage && ( + + + + )} + {data.map(({ voter, votes }, idx) => ( + 0 ? '$40' : '$0'}> + + + + {indexCounter(translations.vote, idx, data.length)} + + + + + + + + + + + + {votes.map(({ actionId, votingProcedure }) => ( + + + + {indexCounter(translations.procedureTitle, idx, votes.length)} + + + + + + {votingProcedure.anchor && ( + <> + + + + + + + + )} + + + + + + {indexCounter(translations.actionIdTitle, idx, votes.length)} + + + + + + + + + + ))} + + + ))} + + ); +}; diff --git a/packages/core/src/ui/components/VotingProcedures/index.ts b/packages/core/src/ui/components/VotingProcedures/index.ts new file mode 100644 index 0000000000..0bc283f6b0 --- /dev/null +++ b/packages/core/src/ui/components/VotingProcedures/index.ts @@ -0,0 +1 @@ +export { VotingProcedures } from './VotingProcedures'; diff --git a/packages/ui/package.json b/packages/ui/package.json index 96b82cd53a..d132baa605 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -94,5 +94,17 @@ "wait-on": "^7.0.1", "webpack": "^5.76.1", "webpack-dev-server": "^4.11.1" + }, + "resolutions": { + "@storybook/addon-actions": "^6.5.16", + "@storybook/addon-essentials": "^6.5.16", + "@storybook/addon-interactions": "^6.5.16", + "@storybook/addon-links": "^6.5.16", + "@storybook/builder-webpack5": "6.5.16", + "@storybook/jest": "^0.0.10", + "@storybook/manager-webpack5": "6.5.16", + "@storybook/react": "^6.5.16", + "@storybook/test-runner": "^0.10.0", + "@storybook/testing-library": "^0.0.13" } } diff --git a/packages/ui/src/design-system/index.ts b/packages/ui/src/design-system/index.ts index 69b4af6b60..083cf49f03 100644 --- a/packages/ui/src/design-system/index.ts +++ b/packages/ui/src/design-system/index.ts @@ -24,5 +24,5 @@ export { ToastBar } from './toast-bar'; export * from './tooltip'; export { Message } from './message'; export { PasswordBox } from './password-box'; -export { Metadata } from './metadata'; +export { Metadata, MetadataLink } from './metadata'; export { TextLink } from './text-link'; diff --git a/packages/ui/src/design-system/metadata/index.ts b/packages/ui/src/design-system/metadata/index.ts index 6060859e44..d97b0957fc 100644 --- a/packages/ui/src/design-system/metadata/index.ts +++ b/packages/ui/src/design-system/metadata/index.ts @@ -1 +1,2 @@ export { Metadata } from './metadata.component'; +export { MetadataLink } from './metadata-link.component'; diff --git a/packages/ui/src/design-system/metadata/metadata-link.component.tsx b/packages/ui/src/design-system/metadata/metadata-link.component.tsx new file mode 100644 index 0000000000..5eb52078cf --- /dev/null +++ b/packages/ui/src/design-system/metadata/metadata-link.component.tsx @@ -0,0 +1,45 @@ +import React from 'react'; + +import { Flex } from '../flex'; +import { Grid, Cell } from '../grid'; +import { TextLink } from '../text-link'; +import * as Typography from '../typography'; + +import * as cx from './metadata.css'; + +import type { OmitClassName } from '../../types'; + +type Props = OmitClassName<'div'> & { + label: string; + text: string; + url: string; +}; + +export const MetadataLink = ({ + label, + text, + url, + ...props +}: Readonly): JSX.Element => { + return ( + + + + {label} + + + + + + + + + + + ); +}; diff --git a/packages/ui/src/design-system/text-link/text-link.css.ts b/packages/ui/src/design-system/text-link/text-link.css.ts index 010a970b83..681ccc4f64 100644 --- a/packages/ui/src/design-system/text-link/text-link.css.ts +++ b/packages/ui/src/design-system/text-link/text-link.css.ts @@ -48,6 +48,7 @@ export const label = style([ color: vars.colors.$text_link_label_color_disabled, }, }, + wordBreak: 'break-all', }, ]); From e5f30b61af08f2495e5b8c56587fc6d7c18d8183 Mon Sep 17 00:00:00 2001 From: Lucas Date: Tue, 24 Oct 2023 16:39:30 -0300 Subject: [PATCH 04/68] [LW-8491] Voting delegation confirmation (#648) * feat(core): setup storybook * feat(core): add lace/ui package * feat: check for DRep transaction * feat: integrate confirm drep registration component * refactor: remove insufficient funds warning; break down main component * feat: add certificate data * feat: add drep retirement container * feat(extension): use consistent naming of DRep ID * fix: add cardano symbol to deposit amount * refactor: certificate inspector factory * feat: voting delegation * fix: type errors * fix: merge conflicts --------- Co-authored-by: Michael Chappell <7581002+mchappell@users.noreply.github.com> --- .../ConfirmDRepRegistrationContainer.tsx | 9 ++- .../ConfirmDRepRetirementContainer.tsx | 9 ++- .../ConfirmTransactionContent.tsx | 4 ++ .../ConfirmVoteDelegationContainer.tsx | 42 +++++++++++ .../components/confirm-transaction/utils.ts | 43 ++++++------ .../src/lib/translations/en.json | 8 +++ packages/core/src/index.ts | 1 + .../ConfirmVoteDelegation.stories.ts | 69 +++++++++++++++++++ .../ConfirmVoteDelegation.tsx | 56 +++++++++++++++ .../components/ConfirmVoteDelegation/index.ts | 1 + .../VotingProcedures/VotingProcedures.tsx | 2 +- packages/ui/package.json | 24 +++---- 12 files changed, 226 insertions(+), 42 deletions(-) create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmVoteDelegationContainer.tsx create mode 100644 packages/core/src/ui/components/ConfirmVoteDelegation/ConfirmVoteDelegation.stories.ts create mode 100644 packages/core/src/ui/components/ConfirmVoteDelegation/ConfirmVoteDelegation.tsx create mode 100644 packages/core/src/ui/components/ConfirmVoteDelegation/index.ts diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmDRepRegistrationContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmDRepRegistrationContainer.tsx index f9a32a49c9..3b349e0452 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmDRepRegistrationContainer.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmDRepRegistrationContainer.tsx @@ -2,10 +2,12 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { ConfirmDRepRegistration } from '@lace/core'; import { SignTxData } from './types'; -import { dRepRegistrationInspector, drepIDasBech32FromHash } from './utils'; +import { certificateInspectorFactory, drepIDasBech32FromHash } from './utils'; import { Wallet } from '@lace/cardano'; import { useWalletStore } from '@src/stores'; +const { CertificateType } = Wallet.Cardano; + interface Props { signTxData: SignTxData; errorMessage?: string; @@ -13,11 +15,12 @@ interface Props { export const ConfirmDRepRegistrationContainer = ({ signTxData, errorMessage }: Props): React.ReactElement => { const { t } = useTranslation(); - const certificate = dRepRegistrationInspector(signTxData.tx); const { walletUI: { cardanoCoin } } = useWalletStore(); - + const certificate = certificateInspectorFactory( + CertificateType.RegisterDelegateRepresentative + )(signTxData.tx); const depositPaidWithCardanoSymbol = `${Wallet.util.lovelacesToAdaString(certificate.deposit.toString())} ${ cardanoCoin.symbol }`; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmDRepRetirementContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmDRepRetirementContainer.tsx index 1bb2cb14ab..dc926c88e3 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmDRepRetirementContainer.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmDRepRetirementContainer.tsx @@ -2,11 +2,13 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { ConfirmDRepRetirement } from '@lace/core'; import { SignTxData } from './types'; -import { dRepRetirementInspector, drepIDasBech32FromHash, getOwnRetirementMessageKey } from './utils'; +import { certificateInspectorFactory, drepIDasBech32FromHash, getOwnRetirementMessageKey } from './utils'; import { Wallet } from '@lace/cardano'; import { useWalletStore } from '@src/stores'; import { useIsOwnPubDRepKey } from './hooks'; +const { CertificateType } = Wallet.Cardano; + interface Props { signTxData: SignTxData; errorMessage?: string; @@ -14,12 +16,13 @@ interface Props { export const ConfirmDRepRetirementContainer = ({ signTxData, errorMessage }: Props): React.ReactElement => { const { t } = useTranslation(); - const certificate = dRepRetirementInspector(signTxData.tx); const { walletUI: { cardanoCoin }, inMemoryWallet } = useWalletStore(); - + const certificate = certificateInspectorFactory( + CertificateType.UnregisterDelegateRepresentative + )(signTxData.tx); const depositPaidWithCardanoSymbol = `${Wallet.util.lovelacesToAdaString(certificate.deposit.toString())} ${ cardanoCoin.symbol }`; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransactionContent.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransactionContent.tsx index 9df9c6f88b..85b8aed71c 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransactionContent.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransactionContent.tsx @@ -5,6 +5,7 @@ import { DappTransactionContainer } from './DappTransactionContainer'; import { TxType } from './utils'; import { SignTxData } from './types'; import { ConfirmDRepRetirementContainer } from './ConfirmDRepRetirementContainer'; +import { ConfirmVoteDelegationContainer } from './ConfirmVoteDelegationContainer'; import { VotingProceduresContainer } from './VotingProceduresContainer'; interface Props { @@ -23,6 +24,9 @@ export const ConfirmTransactionContent = ({ txType, signTxData, errorMessage }: if (txType === TxType.DRepRetirement) { return ; } + if (txType === TxType.VoteDelegation) { + return ; + } if (txType === TxType.VotingProcedures) { return ; } diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmVoteDelegationContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmVoteDelegationContainer.tsx new file mode 100644 index 0000000000..3e578cc900 --- /dev/null +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmVoteDelegationContainer.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { ConfirmVoteDelegation } from '@lace/core'; +import { SignTxData } from './types'; +import { certificateInspectorFactory } from './utils'; +import { Wallet } from '@lace/cardano'; + +const { CertificateType } = Wallet.Cardano; + +interface Props { + signTxData: SignTxData; + errorMessage?: string; +} + +export const ConfirmVoteDelegationContainer = ({ signTxData, errorMessage }: Props): React.ReactElement => { + const { t } = useTranslation(); + const certificate = certificateInspectorFactory( + CertificateType.VoteDelegation + )(signTxData.tx); + const dRep = certificate.dRep; + + return ( + + ); +}; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/utils.ts b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/utils.ts index e3765947d0..8381f0c291 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/utils.ts +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/utils.ts @@ -8,6 +8,8 @@ import { runtime } from 'webextension-polyfill'; import { of } from 'rxjs'; import { sectionTitle, DAPP_VIEWS } from '../../config'; +const { CertificateType } = Wallet.Cardano; + const DAPP_TOAST_DURATION = 50; export enum TxType { @@ -16,6 +18,7 @@ export enum TxType { Burn = 'Burn', DRepRegistration = 'DRepRegistration', DRepRetirement = 'DRepRetirement', + VoteDelegation = 'VoteDelegation', VotingProcedures = 'VotingProcedures' } @@ -28,6 +31,10 @@ export const getTitleKey = (txType: TxType): string => { return 'core.drepRetirement.title'; } + if (txType === TxType.VoteDelegation) { + return 'core.voteDelegation.title'; + } + if (txType === TxType.VotingProcedures) { return 'core.votingProcedures.title'; } @@ -79,25 +86,10 @@ export const getTransactionAssetsId = (outputs: CardanoTxOut[]): Wallet.Cardano. return assetIds; }; -const isDRepRegistrationCertificate = (type: Wallet.Cardano.CertificateType) => - type === Wallet.Cardano.CertificateType.RegisterDelegateRepresentative; - -const isDRepRetirementCertificate = (type: Wallet.Cardano.CertificateType) => - type === Wallet.Cardano.CertificateType.UnregisterDelegateRepresentative; - -export const dRepRegistrationInspector = ( - tx: Wallet.Cardano.Tx -): Wallet.Cardano.RegisterDelegateRepresentativeCertificate | undefined => - tx?.body?.certificates?.find(({ __typename }) => isDRepRegistrationCertificate(__typename)) as - | Wallet.Cardano.RegisterDelegateRepresentativeCertificate - | undefined; - -export const dRepRetirementInspector = ( - tx: Wallet.Cardano.Tx -): Wallet.Cardano.UnRegisterDelegateRepresentativeCertificate | undefined => - tx?.body?.certificates?.find(({ __typename }) => isDRepRetirementCertificate(__typename)) as - | Wallet.Cardano.UnRegisterDelegateRepresentativeCertificate - | undefined; +export const certificateInspectorFactory = + (type: Wallet.Cardano.CertificateType) => + (tx: Wallet.Cardano.Tx): T | undefined => + tx?.body?.certificates?.find((certificate) => certificate.__typename === type) as T | undefined; export const votingProceduresInspector = (tx: Wallet.Cardano.Tx): Wallet.Cardano.VotingProcedures | undefined => tx?.body?.votingProcedures; @@ -106,12 +98,13 @@ export const getTxType = (tx: Wallet.Cardano.Tx): TxType => { const inspector = createTxInspector({ minted: assetsMintedInspector, burned: assetsBurnedInspector, - dRepRegistration: dRepRegistrationInspector, - dRepRetirement: dRepRetirementInspector, - votingProcedures: votingProceduresInspector + votingProcedures: votingProceduresInspector, + dRepRegistration: certificateInspectorFactory(CertificateType.RegisterDelegateRepresentative), + dRepRetirement: certificateInspectorFactory(CertificateType.UnregisterDelegateRepresentative), + voteDelegation: certificateInspectorFactory(CertificateType.VoteDelegation) }); - const { minted, burned, dRepRegistration, dRepRetirement, votingProcedures } = inspector( + const { minted, burned, dRepRegistration, dRepRetirement, voteDelegation, votingProcedures } = inspector( tx as Wallet.Cardano.HydratedTx ); const isMintTransaction = minted.length > 0; @@ -137,6 +130,10 @@ export const getTxType = (tx: Wallet.Cardano.Tx): TxType => { return TxType.DRepRetirement; } + if (voteDelegation) { + return TxType.VoteDelegation; + } + return TxType.Send; }; diff --git a/apps/browser-extension-wallet/src/lib/translations/en.json b/apps/browser-extension-wallet/src/lib/translations/en.json index 621892c142..2ead13f289 100644 --- a/apps/browser-extension-wallet/src/lib/translations/en.json +++ b/apps/browser-extension-wallet/src/lib/translations/en.json @@ -1145,6 +1145,14 @@ "isOwnRetirement": "This is your DRep retirement.", "isNotOwnRetirement": "The presented DRepID does not match your wallet's DRepID." }, + "voteDelegation": { + "title": "Confirm vote delegation", + "metadata": "Metadata", + "drepId": "Drep ID", + "alwaysAbstain": "Abstain", + "alwaysNoConfidence": "No Confidence", + "option": "Yes" + }, "destinationAddressInput": { "recipientAddress": "Recipient's address or $handle" }, diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index b6db918e66..066310c1b4 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -30,4 +30,5 @@ export * from '@ui/components/MnemonicWordsAutoComplete'; export * from '@ui/components/AddressCard'; export * from '@ui/components/ConfirmDRepRegistration'; export * from '@ui/components/ConfirmDRepRetirement'; +export * from '@ui/components/ConfirmVoteDelegation'; export * from '@ui/components/VotingProcedures'; diff --git a/packages/core/src/ui/components/ConfirmVoteDelegation/ConfirmVoteDelegation.stories.ts b/packages/core/src/ui/components/ConfirmVoteDelegation/ConfirmVoteDelegation.stories.ts new file mode 100644 index 0000000000..5d23ca14bf --- /dev/null +++ b/packages/core/src/ui/components/ConfirmVoteDelegation/ConfirmVoteDelegation.stories.ts @@ -0,0 +1,69 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { ConfirmVoteDelegation } from './ConfirmVoteDelegation'; +import { ComponentProps } from 'react'; + +const meta: Meta = { + title: 'ConfirmVoteDelegation', + component: ConfirmVoteDelegation, + parameters: { + layout: 'centered' + } +}; + +export default meta; +type Story = StoryObj; + +const data: ComponentProps = { + dappInfo: { + logo: 'https://cdn.mint.handle.me/favicon.png', + name: 'Mint', + url: 'https://preprod.mint.handle.me' + }, + translations: { + labels: { + drepId: 'Drep ID', + alwaysAbstain: 'Abstain', + alwaysNoConfidence: 'No Confidence' + }, + option: 'Yes', + metadata: 'Metadata' + }, + metadata: { + drepId: 'drep1ruvgm0auzdplfn7g2jf3kcnpnw5mlhwxaxj8crag8h6t2ye9y9g', + alwaysAbstain: false, + alwaysNoConfidence: false + } +}; + +export const Overview: Story = { + args: { + ...data + } +}; +export const WithError: Story = { + args: { + ...data, + errorMessage: 'Something went wrong' + } +}; + +export const WithAbstain: Story = { + args: { + ...data, + metadata: { + alwaysAbstain: true, + alwaysNoConfidence: false + } + } +}; + +export const WithNoConfidence: Story = { + args: { + ...data, + metadata: { + alwaysAbstain: false, + alwaysNoConfidence: true + } + } +}; diff --git a/packages/core/src/ui/components/ConfirmVoteDelegation/ConfirmVoteDelegation.tsx b/packages/core/src/ui/components/ConfirmVoteDelegation/ConfirmVoteDelegation.tsx new file mode 100644 index 0000000000..cad483cc3c --- /dev/null +++ b/packages/core/src/ui/components/ConfirmVoteDelegation/ConfirmVoteDelegation.tsx @@ -0,0 +1,56 @@ +import React from 'react'; +import { Box, Cell, Grid, TransactionSummary, Flex } from '@lace/ui'; +import { DappInfo, DappInfoProps } from '../DappInfo'; +import { ErrorPane } from '@lace/common'; + +interface Props { + dappInfo: Omit; + errorMessage?: string; + translations: { + labels: { + drepId: string; + alwaysAbstain: string; + alwaysNoConfidence: string; + }; + option: string; + metadata: string; + }; + metadata: { + drepId?: string; + alwaysAbstain: boolean; + alwaysNoConfidence: boolean; + }; +} + +export const ConfirmVoteDelegation = ({ dappInfo, errorMessage, translations, metadata }: Props): JSX.Element => ( + + + + + {errorMessage && ( + + + + )} + + + + + {metadata.drepId && ( + + + + )} + {metadata.alwaysAbstain && ( + + + + )} + {metadata.alwaysNoConfidence && ( + + + + )} + + +); diff --git a/packages/core/src/ui/components/ConfirmVoteDelegation/index.ts b/packages/core/src/ui/components/ConfirmVoteDelegation/index.ts new file mode 100644 index 0000000000..33e8e67554 --- /dev/null +++ b/packages/core/src/ui/components/ConfirmVoteDelegation/index.ts @@ -0,0 +1 @@ +export { ConfirmVoteDelegation } from './ConfirmVoteDelegation'; diff --git a/packages/core/src/ui/components/VotingProcedures/VotingProcedures.tsx b/packages/core/src/ui/components/VotingProcedures/VotingProcedures.tsx index 654f4b76f5..c7824f9880 100644 --- a/packages/core/src/ui/components/VotingProcedures/VotingProcedures.tsx +++ b/packages/core/src/ui/components/VotingProcedures/VotingProcedures.tsx @@ -1,7 +1,7 @@ import React, { Fragment } from 'react'; import { Box, Cell, Grid, Flex, Metadata, MetadataLink, Text, Divider, sx } from '@lace/ui'; import { DappInfo, DappInfoProps } from '../DappInfo'; -import { ErrorPane, Ellipsis } from '@lace/common'; +import { ErrorPane } from '@lace/common'; type VotingProcedure = { voter: { diff --git a/packages/ui/package.json b/packages/ui/package.json index d132baa605..c5e31ae811 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -29,6 +29,18 @@ "typecheck": "tsc --noEmit", "watch": "yarn build --watch" }, + "resolutions": { + "@storybook/addon-actions": "^6.5.16", + "@storybook/addon-essentials": "^6.5.16", + "@storybook/addon-interactions": "^6.5.16", + "@storybook/addon-links": "^6.5.16", + "@storybook/builder-webpack5": "6.5.16", + "@storybook/jest": "^0.0.10", + "@storybook/manager-webpack5": "6.5.16", + "@storybook/react": "^6.5.16", + "@storybook/test-runner": "^0.10.0", + "@storybook/testing-library": "^0.0.13" + }, "dependencies": { "@radix-ui/react-alert-dialog": "^1.0.4", "@radix-ui/react-avatar": "^1.0.2", @@ -94,17 +106,5 @@ "wait-on": "^7.0.1", "webpack": "^5.76.1", "webpack-dev-server": "^4.11.1" - }, - "resolutions": { - "@storybook/addon-actions": "^6.5.16", - "@storybook/addon-essentials": "^6.5.16", - "@storybook/addon-interactions": "^6.5.16", - "@storybook/addon-links": "^6.5.16", - "@storybook/builder-webpack5": "6.5.16", - "@storybook/jest": "^0.0.10", - "@storybook/manager-webpack5": "6.5.16", - "@storybook/react": "^6.5.16", - "@storybook/test-runner": "^0.10.0", - "@storybook/testing-library": "^0.0.13" } } From fc0ba662870bd26707d1b30d441b8fffc0cbdf7e Mon Sep 17 00:00:00 2001 From: Michael Chappell <7581002+mchappell@users.noreply.github.com> Date: Wed, 25 Oct 2023 19:02:38 +0100 Subject: [PATCH 05/68] chore: modify sanchonet urls (#667) --- .github/workflows/e2e-tests-linux.yml | 3 --- .github/workflows/smoke-tests.yml | 3 --- apps/browser-extension-wallet/.env.defaults | 2 +- apps/browser-extension-wallet/.env.example | 2 +- 4 files changed, 2 insertions(+), 8 deletions(-) diff --git a/.github/workflows/e2e-tests-linux.yml b/.github/workflows/e2e-tests-linux.yml index 47f32d5fc3..71eec19cf4 100644 --- a/.github/workflows/e2e-tests-linux.yml +++ b/.github/workflows/e2e-tests-linux.yml @@ -50,9 +50,6 @@ jobs: uses: ./.github/shared/build with: LACE_EXTENSION_KEY: ${{ secrets.MANIFEST_PUBLIC_KEY }} - CARDANO_SERVICES_URL_MAINNET: ${{ secrets.CARDANO_SERVICES_DEV_URL_MAINNET }} - CARDANO_SERVICES_URL_PREPROD: ${{ secrets.CARDANO_SERVICES_DEV_URL_PREPROD }} - CARDANO_SERVICES_URL_PREVIEW: ${{ secrets.CARDANO_SERVICES_DEV_URL_PREVIEW }} - name: Start XVFB run: | Xvfb :99 & diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml index 5066293a93..5c2526ad4b 100644 --- a/.github/workflows/smoke-tests.yml +++ b/.github/workflows/smoke-tests.yml @@ -26,9 +26,6 @@ jobs: uses: ./.github/shared/build with: LACE_EXTENSION_KEY: ${{ secrets.MANIFEST_PUBLIC_KEY }} - CARDANO_SERVICES_URL_MAINNET: ${{ secrets.CARDANO_SERVICES_DEV_URL_MAINNET }} - CARDANO_SERVICES_URL_PREPROD: ${{ secrets.CARDANO_SERVICES_DEV_URL_PREPROD }} - CARDANO_SERVICES_URL_PREVIEW: ${{ secrets.CARDANO_SERVICES_DEV_URL_PREVIEW }} - name: Start XVFB run: | Xvfb :99 & diff --git a/apps/browser-extension-wallet/.env.defaults b/apps/browser-extension-wallet/.env.defaults index a13fed41ec..1f1d818e7c 100644 --- a/apps/browser-extension-wallet/.env.defaults +++ b/apps/browser-extension-wallet/.env.defaults @@ -61,7 +61,7 @@ CARDANO_SERVICES_URL_MAINNET=https://backend.live-mainnet.eks.lw.iog.io CARDANO_SERVICES_URL_PREPROD=https://backend.live-preprod.eks.lw.iog.io CARDANO_SERVICES_URL_PREVIEW=https://backend.live-preview.eks.lw.iog.io # TODO: update this with a valid sanchonet url -CARDANO_SERVICES_URL_SANCHONET=https://backend.live-preprod.eks.lw.iog.io +CARDANO_SERVICES_URL_SANCHONET=https://backend.dev-sanchonet.eks.lw.iog.io # Explorer URLs CEXPLORER_URL_MAINNET=https://cexplorer.io diff --git a/apps/browser-extension-wallet/.env.example b/apps/browser-extension-wallet/.env.example index 3af21b976e..f7630915a7 100644 --- a/apps/browser-extension-wallet/.env.example +++ b/apps/browser-extension-wallet/.env.example @@ -62,7 +62,7 @@ CARDANO_SERVICES_URL_MAINNET=https://backend.live-mainnet.eks.lw.iog.io CARDANO_SERVICES_URL_PREPROD=https://backend.live-preprod.eks.lw.iog.io CARDANO_SERVICES_URL_PREVIEW=https://backend.live-preview.eks.lw.iog.io # TODO: update this with a valid sanchonet url -CARDANO_SERVICES_URL_SANCHONET=https://backend.live-preprod.eks.lw.iog.io +CARDANO_SERVICES_URL_SANCHONET=https://backend.dev-sanchonet.eks.lw.iog.io # Explorer URLs CEXPLORER_URL_MAINNET=https://cexplorer.io From a8918c884c3e7eddd3e39459777d20c21cc60164 Mon Sep 17 00:00:00 2001 From: Lucas Date: Wed, 1 Nov 2023 13:24:50 -0300 Subject: [PATCH 06/68] feat: drep update certificate signing (#664) --- .../ConfirmDRepUpdateContainer.tsx | 40 +++++++++++++ .../ConfirmTransactionContent.tsx | 4 ++ .../components/confirm-transaction/utils.ts | 12 +++- .../src/lib/translations/en.json | 7 +++ packages/core/src/index.ts | 1 + .../ConfirmDRepUpdate.stories.ts | 58 +++++++++++++++++++ .../ConfirmDRepUpdate/ConfirmDRepUpdate.tsx | 53 +++++++++++++++++ .../ui/components/ConfirmDRepUpdate/index.ts | 1 + 8 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmDRepUpdateContainer.tsx create mode 100644 packages/core/src/ui/components/ConfirmDRepUpdate/ConfirmDRepUpdate.stories.ts create mode 100644 packages/core/src/ui/components/ConfirmDRepUpdate/ConfirmDRepUpdate.tsx create mode 100644 packages/core/src/ui/components/ConfirmDRepUpdate/index.ts diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmDRepUpdateContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmDRepUpdateContainer.tsx new file mode 100644 index 0000000000..17852419a8 --- /dev/null +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmDRepUpdateContainer.tsx @@ -0,0 +1,40 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { ConfirmDRepUpdate } from '@lace/core'; +import { SignTxData } from './types'; +import { certificateInspectorFactory, drepIDasBech32FromHash } from './utils'; +import { Wallet } from '@lace/cardano'; + +const { CertificateType } = Wallet.Cardano; + +interface Props { + signTxData: SignTxData; + errorMessage?: string; +} + +export const ConfirmDRepUpdateContainer = ({ signTxData, errorMessage }: Props): React.ReactElement => { + const { t } = useTranslation(); + const certificate = certificateInspectorFactory( + CertificateType.UpdateDelegateRepresentative + )(signTxData.tx); + + return ( + + ); +}; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransactionContent.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransactionContent.tsx index 85b8aed71c..59d3755812 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransactionContent.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransactionContent.tsx @@ -7,6 +7,7 @@ import { SignTxData } from './types'; import { ConfirmDRepRetirementContainer } from './ConfirmDRepRetirementContainer'; import { ConfirmVoteDelegationContainer } from './ConfirmVoteDelegationContainer'; import { VotingProceduresContainer } from './VotingProceduresContainer'; +import { ConfirmDRepUpdateContainer } from './ConfirmDRepUpdateContainer'; interface Props { txType?: TxType; @@ -24,6 +25,9 @@ export const ConfirmTransactionContent = ({ txType, signTxData, errorMessage }: if (txType === TxType.DRepRetirement) { return ; } + if (txType === TxType.DRepUpdate) { + return ; + } if (txType === TxType.VoteDelegation) { return ; } diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/utils.ts b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/utils.ts index 8381f0c291..8a8f838bd1 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/utils.ts +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/utils.ts @@ -18,6 +18,7 @@ export enum TxType { Burn = 'Burn', DRepRegistration = 'DRepRegistration', DRepRetirement = 'DRepRetirement', + DRepUpdate = 'DRepUpdate', VoteDelegation = 'VoteDelegation', VotingProcedures = 'VotingProcedures' } @@ -31,6 +32,10 @@ export const getTitleKey = (txType: TxType): string => { return 'core.drepRetirement.title'; } + if (txType === TxType.DRepUpdate) { + return 'core.drepUpdate.title'; + } + if (txType === TxType.VoteDelegation) { return 'core.voteDelegation.title'; } @@ -101,10 +106,11 @@ export const getTxType = (tx: Wallet.Cardano.Tx): TxType => { votingProcedures: votingProceduresInspector, dRepRegistration: certificateInspectorFactory(CertificateType.RegisterDelegateRepresentative), dRepRetirement: certificateInspectorFactory(CertificateType.UnregisterDelegateRepresentative), + dRepUpdate: certificateInspectorFactory(CertificateType.UpdateDelegateRepresentative), voteDelegation: certificateInspectorFactory(CertificateType.VoteDelegation) }); - const { minted, burned, dRepRegistration, dRepRetirement, voteDelegation, votingProcedures } = inspector( + const { minted, burned, dRepRegistration, dRepRetirement, dRepUpdate, voteDelegation, votingProcedures } = inspector( tx as Wallet.Cardano.HydratedTx ); const isMintTransaction = minted.length > 0; @@ -134,6 +140,10 @@ export const getTxType = (tx: Wallet.Cardano.Tx): TxType => { return TxType.VoteDelegation; } + if (dRepUpdate) { + return TxType.DRepUpdate; + } + return TxType.Send; }; diff --git a/apps/browser-extension-wallet/src/lib/translations/en.json b/apps/browser-extension-wallet/src/lib/translations/en.json index 2ead13f289..cf27811238 100644 --- a/apps/browser-extension-wallet/src/lib/translations/en.json +++ b/apps/browser-extension-wallet/src/lib/translations/en.json @@ -1145,6 +1145,13 @@ "isOwnRetirement": "This is your DRep retirement.", "isNotOwnRetirement": "The presented DRepID does not match your wallet's DRepID." }, + "drepUpdate": { + "title": "Confirm DRep update", + "metadata": "Metadata", + "drepId": "Drep ID", + "url": "URL", + "hash": "Hash" + }, "voteDelegation": { "title": "Confirm vote delegation", "metadata": "Metadata", diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 066310c1b4..cd74f39601 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -30,5 +30,6 @@ export * from '@ui/components/MnemonicWordsAutoComplete'; export * from '@ui/components/AddressCard'; export * from '@ui/components/ConfirmDRepRegistration'; export * from '@ui/components/ConfirmDRepRetirement'; +export * from '@ui/components/ConfirmDRepUpdate'; export * from '@ui/components/ConfirmVoteDelegation'; export * from '@ui/components/VotingProcedures'; diff --git a/packages/core/src/ui/components/ConfirmDRepUpdate/ConfirmDRepUpdate.stories.ts b/packages/core/src/ui/components/ConfirmDRepUpdate/ConfirmDRepUpdate.stories.ts new file mode 100644 index 0000000000..26028b8654 --- /dev/null +++ b/packages/core/src/ui/components/ConfirmDRepUpdate/ConfirmDRepUpdate.stories.ts @@ -0,0 +1,58 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { ConfirmDRepUpdate } from './ConfirmDRepUpdate'; +import { ComponentProps } from 'react'; + +const meta: Meta = { + title: 'ConfirmDRepUpdate', + component: ConfirmDRepUpdate, + parameters: { + layout: 'centered' + } +}; + +export default meta; +type Story = StoryObj; + +const data: ComponentProps = { + dappInfo: { + logo: 'https://cdn.mint.handle.me/favicon.png', + name: 'Mint', + url: 'https://preprod.mint.handle.me' + }, + translations: { + labels: { + drepId: 'Drep ID', + hash: 'Hash', + url: 'URL' + }, + metadata: 'Metadata' + }, + metadata: { + drepId: '65ge6g54g5dd5', + hash: '9bba8233cdd086f0325daba465d568a88970d42536f9e71e92a80d5922ded885', + url: 'https://raw.githubusercontent.com/Ryun1/gov-metadata/main/governace-action/metadata.jsonldr1q99...uqvzlalu' + } +}; + +export const Overview: Story = { + args: { + ...data + } +}; + +export const Empty: Story = { + args: { + ...data, + metadata: { + drepId: '65ge6g54g5dd5' + } + } +}; + +export const WithError: Story = { + args: { + ...data, + errorMessage: 'Something went wrong' + } +}; diff --git a/packages/core/src/ui/components/ConfirmDRepUpdate/ConfirmDRepUpdate.tsx b/packages/core/src/ui/components/ConfirmDRepUpdate/ConfirmDRepUpdate.tsx new file mode 100644 index 0000000000..10f148a23c --- /dev/null +++ b/packages/core/src/ui/components/ConfirmDRepUpdate/ConfirmDRepUpdate.tsx @@ -0,0 +1,53 @@ +import React from 'react'; +import { Box, Cell, Grid, TransactionSummary, Flex } from '@lace/ui'; +import { DappInfo, DappInfoProps } from '../DappInfo'; +import { ErrorPane } from '@lace/common'; + +interface Props { + dappInfo: Omit; + errorMessage?: string; + translations: { + labels: { + url: string; + hash: string; + drepId: string; + }; + metadata: string; + }; + metadata: { + url?: string; + hash?: string; + drepId: string; + }; +} + +export const ConfirmDRepUpdate = ({ dappInfo, errorMessage, translations, metadata }: Props): JSX.Element => ( + + + + + {errorMessage && ( + + + + )} + + + + + {metadata.url && ( + + + + )} + {metadata.hash && ( + + + + )} + + + + + +); diff --git a/packages/core/src/ui/components/ConfirmDRepUpdate/index.ts b/packages/core/src/ui/components/ConfirmDRepUpdate/index.ts new file mode 100644 index 0000000000..1017f7880e --- /dev/null +++ b/packages/core/src/ui/components/ConfirmDRepUpdate/index.ts @@ -0,0 +1 @@ +export { ConfirmDRepUpdate } from './ConfirmDRepUpdate'; From e4641e197088d1a963b453ddee8ba8936485437d Mon Sep 17 00:00:00 2001 From: Michael Chappell <7581002+mchappell@users.noreply.github.com> Date: Tue, 7 Nov 2023 08:53:35 +0000 Subject: [PATCH 07/68] fix(extension): updated padding and missing bech32 addressing --- .../ConfirmVoteDelegationContainer.tsx | 4 +- .../VotingProceduresContainer.tsx | 48 +++++++++++-------- .../ConfirmDRepRegistration.tsx | 4 +- .../ConfirmDRepRetirement.tsx | 6 +-- .../ConfirmDRepUpdate/ConfirmDRepUpdate.tsx | 4 +- .../ConfirmVoteDelegation.tsx | 4 +- .../VotingProcedures/VotingProcedures.tsx | 4 +- 7 files changed, 41 insertions(+), 33 deletions(-) diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmVoteDelegationContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmVoteDelegationContainer.tsx index 3e578cc900..bbd58e6461 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmVoteDelegationContainer.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmVoteDelegationContainer.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { ConfirmVoteDelegation } from '@lace/core'; import { SignTxData } from './types'; -import { certificateInspectorFactory } from './utils'; +import { certificateInspectorFactory, drepIDasBech32FromHash } from './utils'; import { Wallet } from '@lace/cardano'; const { CertificateType } = Wallet.Cardano; @@ -25,7 +25,7 @@ export const ConfirmVoteDelegationContainer = ({ signTxData, errorMessage }: Pro metadata={{ alwaysAbstain: Wallet.Cardano.isDRepAlwaysAbstain(dRep), alwaysNoConfidence: Wallet.Cardano.isDRepAlwaysNoConfidence(dRep), - ...(Wallet.Cardano.isDRepCredential(dRep) ? { drepId: dRep.hash } : {}) + ...(Wallet.Cardano.isDRepCredential(dRep) ? { drepId: drepIDasBech32FromHash(dRep.hash) } : {}) }} translations={{ metadata: t('core.voteDelegation.metadata'), diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/VotingProceduresContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/VotingProceduresContainer.tsx index 8104ef9196..ea135ee37d 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/VotingProceduresContainer.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/VotingProceduresContainer.tsx @@ -2,7 +2,7 @@ import React, { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { VotingProcedures } from '@lace/core'; import { SignTxData } from './types'; -import { votingProceduresInspector } from './utils'; +import { drepIDasBech32FromHash, votingProceduresInspector } from './utils'; import { Wallet } from '@lace/cardano'; import { useWalletStore } from '@src/stores'; import { config } from '@src/config'; @@ -12,7 +12,7 @@ interface Props { errorMessage?: string; } -const getVoterType = (voterType: Wallet.Cardano.VoterType): string => { +const getVoterType = (voterType: Wallet.Cardano.VoterType): 'Constitutional Committee' | 'SPO' | 'DRep' => { switch (voterType) { case Wallet.Cardano.VoterType.ccHotKeyHash: case Wallet.Cardano.VoterType.ccHotScriptHash: @@ -52,26 +52,34 @@ export const VotingProceduresContainer = ({ signTxData, errorMessage }: Props): return ( ({ - voter: { - type: getVoterType(votingProcedure.voter.__typename), - dRepId: votingProcedure.voter.credential.hash.toString() - }, - votes: votingProcedure.votes.map((vote) => ({ - actionId: { - index: vote.actionId.actionIndex, - txHash: vote.actionId.id.toString(), - txHashUrl: `${explorerBaseUrl}/${vote.actionId.id}` + data={votingProcedures.map((votingProcedure) => { + const voterType = getVoterType(votingProcedure.voter.__typename); + + const drepId = + voterType === 'DRep' + ? drepIDasBech32FromHash(votingProcedure.voter.credential.hash) + : votingProcedure.voter.credential.hash.toString(); + return { + voter: { + type: voterType, + dRepId: drepId }, - votingProcedure: { - vote: getVote(vote.votingProcedure.vote), - anchor: { - url: vote.votingProcedure.anchor?.url, - hash: vote.votingProcedure.anchor?.dataHash.toString() + votes: votingProcedure.votes.map((vote) => ({ + actionId: { + index: vote.actionId.actionIndex, + txHash: vote.actionId.id.toString(), + txHashUrl: `${explorerBaseUrl}/${vote.actionId.id}` + }, + votingProcedure: { + vote: getVote(vote.votingProcedure.vote), + anchor: !!vote.votingProcedure.anchor?.url && { + url: vote.votingProcedure.anchor?.url, + hash: vote.votingProcedure.anchor?.dataHash.toString() + } } - } - })) - }))} + })) + }; + })} translations={{ voterType: t('core.votingProcedures.voterType'), procedureTitle: t('core.votingProcedures.procedureTitle'), diff --git a/packages/core/src/ui/components/ConfirmDRepRegistration/ConfirmDRepRegistration.tsx b/packages/core/src/ui/components/ConfirmDRepRegistration/ConfirmDRepRegistration.tsx index 7041015050..9246cb3c8b 100644 --- a/packages/core/src/ui/components/ConfirmDRepRegistration/ConfirmDRepRegistration.tsx +++ b/packages/core/src/ui/components/ConfirmDRepRegistration/ConfirmDRepRegistration.tsx @@ -24,11 +24,11 @@ interface Props { export const ConfirmDRepRegistration = ({ dappInfo, errorMessage, translations, metadata }: Props): JSX.Element => ( - + {errorMessage && ( - + )} diff --git a/packages/core/src/ui/components/ConfirmDRepRetirement/ConfirmDRepRetirement.tsx b/packages/core/src/ui/components/ConfirmDRepRetirement/ConfirmDRepRetirement.tsx index b658e268e6..ece0e83d6a 100644 --- a/packages/core/src/ui/components/ConfirmDRepRetirement/ConfirmDRepRetirement.tsx +++ b/packages/core/src/ui/components/ConfirmDRepRetirement/ConfirmDRepRetirement.tsx @@ -21,15 +21,15 @@ interface Props { export const ConfirmDRepRetirement = ({ dappInfo, errorMessage, translations, metadata }: Props): JSX.Element => ( - + {errorMessage && ( - + )} - + diff --git a/packages/core/src/ui/components/ConfirmDRepUpdate/ConfirmDRepUpdate.tsx b/packages/core/src/ui/components/ConfirmDRepUpdate/ConfirmDRepUpdate.tsx index 10f148a23c..d712827f1a 100644 --- a/packages/core/src/ui/components/ConfirmDRepUpdate/ConfirmDRepUpdate.tsx +++ b/packages/core/src/ui/components/ConfirmDRepUpdate/ConfirmDRepUpdate.tsx @@ -23,11 +23,11 @@ interface Props { export const ConfirmDRepUpdate = ({ dappInfo, errorMessage, translations, metadata }: Props): JSX.Element => ( - + {errorMessage && ( - + )} diff --git a/packages/core/src/ui/components/ConfirmVoteDelegation/ConfirmVoteDelegation.tsx b/packages/core/src/ui/components/ConfirmVoteDelegation/ConfirmVoteDelegation.tsx index cad483cc3c..eebab72b21 100644 --- a/packages/core/src/ui/components/ConfirmVoteDelegation/ConfirmVoteDelegation.tsx +++ b/packages/core/src/ui/components/ConfirmVoteDelegation/ConfirmVoteDelegation.tsx @@ -24,11 +24,11 @@ interface Props { export const ConfirmVoteDelegation = ({ dappInfo, errorMessage, translations, metadata }: Props): JSX.Element => ( - + {errorMessage && ( - + )} diff --git a/packages/core/src/ui/components/VotingProcedures/VotingProcedures.tsx b/packages/core/src/ui/components/VotingProcedures/VotingProcedures.tsx index c7824f9880..1dcf20f244 100644 --- a/packages/core/src/ui/components/VotingProcedures/VotingProcedures.tsx +++ b/packages/core/src/ui/components/VotingProcedures/VotingProcedures.tsx @@ -16,7 +16,7 @@ type VotingProcedure = { }; votingProcedure: { vote: string; - anchor?: { + anchor: { url: string; hash: string; }; @@ -34,7 +34,7 @@ interface Props { index: string; txHash: string; }; - anchor: { + anchor?: { url: string; hash: string; }; From 481b966e381f6f0d22193ebb6845acade1faa04a Mon Sep 17 00:00:00 2001 From: Michael Chappell <7581002+mchappell@users.noreply.github.com> Date: Tue, 14 Nov 2023 14:22:23 +0000 Subject: [PATCH 08/68] fix: no explorer for sanchonet --- apps/browser-extension-wallet/.env.defaults | 1 + apps/browser-extension-wallet/src/config.ts | 13 ++++--------- .../VotingProceduresContainer.tsx | 13 ++++++++----- .../PostHogClientProvider/client/config.ts | 12 ++++++++---- .../components/TransactionDetailsProxy.tsx | 16 +++++++++++----- .../AssetDetailsDrawer/AssetDetails.tsx | 17 ++++++++++++++--- .../ActivityDetail/TransactionDetails.tsx | 4 ++-- .../VotingProcedures/VotingProcedures.tsx | 16 +++++++++++----- 8 files changed, 59 insertions(+), 33 deletions(-) diff --git a/apps/browser-extension-wallet/.env.defaults b/apps/browser-extension-wallet/.env.defaults index 1f1d818e7c..5925a8b29c 100644 --- a/apps/browser-extension-wallet/.env.defaults +++ b/apps/browser-extension-wallet/.env.defaults @@ -55,6 +55,7 @@ PRODUCTION_MODE_TRACKING=false POSTHOG_DEV_TOKEN_MAINNET=phc_gH96Lx5lEVXTTWEyytSdTFPDk3Xsxwi4BqG88mKObd1 POSTHOG_DEV_TOKEN_PREPROD=phc_Xlmldm6EYSfQVgB9Uxm3b2xC1noDlgFFXpF9AJ6SMfJ POSTHOG_DEV_TOKEN_PREVIEW=phc_e8SaOOWpXpNE59TnpLumeUjWm4iv024AWjhQqU406jr +POSTHOG_DEV_TOKEN_SANCHONET=phc_OUu6sPucDu5S6skRmYbWN5Jn8TpggWTQu1Y1ETkm3xt # Cardano Services CARDANO_SERVICES_URL_MAINNET=https://backend.live-mainnet.eks.lw.iog.io diff --git a/apps/browser-extension-wallet/src/config.ts b/apps/browser-extension-wallet/src/config.ts index 45cbd53464..bdb23b42bd 100644 --- a/apps/browser-extension-wallet/src/config.ts +++ b/apps/browser-extension-wallet/src/config.ts @@ -22,7 +22,7 @@ export type Config = { ADA_PRICE_CHECK_INTERVAL: number; TOKEN_PRICE_CHECK_INTERVAL: number; AVAILABLE_CHAINS: Wallet.ChainName[]; - CEXPLORER_BASE_URL: Record; + CEXPLORER_BASE_URL: Record, string>; CEXPLORER_URL_PATHS: CExplorerUrlPaths; SAVED_PRICE_DURATION: number; }; @@ -38,12 +38,8 @@ const envChecks = (chosenChain: Wallet.ChainName): void => { throw new Error('env vars not complete'); } - if ( - !process.env.CEXPLORER_URL_MAINNET || - !process.env.CEXPLORER_URL_PREVIEW || - !process.env.CEXPLORER_URL_PREPROD || - !process.env.CEXPLORER_URL_SANCHONET - ) { + // TODO Update if sanchonet explorer becomes available + if (!process.env.CEXPLORER_URL_MAINNET || !process.env.CEXPLORER_URL_PREVIEW || !process.env.CEXPLORER_URL_PREPROD) { throw new Error('explorer vars not complete'); } @@ -90,8 +86,7 @@ export const config = (): Config => { CEXPLORER_BASE_URL: { Mainnet: `${process.env.CEXPLORER_URL_MAINNET}`, Preprod: `${process.env.CEXPLORER_URL_PREPROD}`, - Preview: `${process.env.CEXPLORER_URL_PREVIEW}`, - Sanchonet: `${process.env.CEXPLORER_URL_SANCHONET}` + Preview: `${process.env.CEXPLORER_URL_PREVIEW}` }, CEXPLORER_URL_PATHS: { Tx: 'tx', diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/VotingProceduresContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/VotingProceduresContainer.tsx index ea135ee37d..7d6af94635 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/VotingProceduresContainer.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/VotingProceduresContainer.tsx @@ -44,10 +44,13 @@ export const VotingProceduresContainer = ({ signTxData, errorMessage }: Props): const { environmentName } = useWalletStore(); const { CEXPLORER_BASE_URL, CEXPLORER_URL_PATHS } = config(); - const explorerBaseUrl = useMemo( - () => `${CEXPLORER_BASE_URL[environmentName]}/${CEXPLORER_URL_PATHS.Tx}`, - [CEXPLORER_BASE_URL, CEXPLORER_URL_PATHS.Tx, environmentName] - ); + const explorerBaseUrl = useMemo(() => { + if (environmentName === 'Sanchonet') { + return; + } + // eslint-disable-next-line consistent-return + return `${CEXPLORER_BASE_URL[environmentName]}/${CEXPLORER_URL_PATHS.Tx}`; + }, [CEXPLORER_BASE_URL, CEXPLORER_URL_PATHS.Tx, environmentName]); return ( = { [Wallet.Cardano.NetworkMagics.Mainnet]: process.env.POSTHOG_DEV_TOKEN_MAINNET, [Wallet.Cardano.NetworkMagics.Preprod]: process.env.POSTHOG_DEV_TOKEN_PREPROD, - [Wallet.Cardano.NetworkMagics.Preview]: process.env.POSTHOG_DEV_TOKEN_PREVIEW + [Wallet.Cardano.NetworkMagics.Preview]: process.env.POSTHOG_DEV_TOKEN_PREVIEW, + [Wallet.Cardano.NetworkMagics.Sanchonet]: process.env.POSTHOG_DEV_TOKEN_SANCHONET }; export const PRODUCTION_NETWORK_ID_TO_POSTHOG_TOKEN_MAP: Record = { [Wallet.Cardano.NetworkMagics.Mainnet]: process.env.POSTHOG_PRODUCTION_TOKEN_MAINNET, [Wallet.Cardano.NetworkMagics.Preprod]: process.env.POSTHOG_PRODUCTION_TOKEN_PREPROD, - [Wallet.Cardano.NetworkMagics.Preview]: process.env.POSTHOG_PRODUCTION_TOKEN_PREVIEW + [Wallet.Cardano.NetworkMagics.Preview]: process.env.POSTHOG_PRODUCTION_TOKEN_PREVIEW, + [Wallet.Cardano.NetworkMagics.Sanchonet]: process.env.POSTHOG_PRODUCTION_TOKEN_SANCHONET }; export const DEV_NETWORK_ID_TO_POSTHOG_PROJECT_ID_MAP: Record = { [Wallet.Cardano.NetworkMagics.Mainnet]: 6315, [Wallet.Cardano.NetworkMagics.Preprod]: 6316, - [Wallet.Cardano.NetworkMagics.Preview]: 4874 + [Wallet.Cardano.NetworkMagics.Preview]: 4874, + [Wallet.Cardano.NetworkMagics.Sanchonet]: 11_178 }; export const PRODUCTION_NETWORK_ID_TO_POSTHOG_PROJECT_ID_MAP: Record = { [Wallet.Cardano.NetworkMagics.Mainnet]: 6621, [Wallet.Cardano.NetworkMagics.Preprod]: 6620, - [Wallet.Cardano.NetworkMagics.Preview]: 6619 + [Wallet.Cardano.NetworkMagics.Preview]: 6619, + [Wallet.Cardano.NetworkMagics.Sanchonet]: 11_179 }; diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/activity/components/TransactionDetailsProxy.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/activity/components/TransactionDetailsProxy.tsx index f96efee301..c80b7d22a8 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/activity/components/TransactionDetailsProxy.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/features/activity/components/TransactionDetailsProxy.tsx @@ -29,10 +29,12 @@ export const TransactionDetailsProxy = withAddressBookContext( const { list: addressList } = useAddressBookContext(); const { CEXPLORER_BASE_URL, CEXPLORER_URL_PATHS } = config(); - const explorerBaseUrl = useMemo( - () => `${CEXPLORER_BASE_URL[environmentName]}/${CEXPLORER_URL_PATHS.Tx}`, - [CEXPLORER_BASE_URL, CEXPLORER_URL_PATHS.Tx, environmentName] - ); + // TODO, remove if sanchonet gets an explorer + const explorerBaseUrl = useMemo(() => { + if (environmentName === 'Sanchonet') return; + // eslint-disable-next-line consistent-return + return `${CEXPLORER_BASE_URL[environmentName]}/${CEXPLORER_URL_PATHS.Tx}`; + }, [CEXPLORER_BASE_URL, CEXPLORER_URL_PATHS.Tx, environmentName]); const getHeaderDescription = () => { if (activityInfo.type === 'delegation') return '1 token'; return ` (${activityInfo?.assetAmount})`; @@ -72,6 +74,10 @@ export const TransactionDetailsProxy = withAddressBookContext( [addressList] ); + const environmentSpecificProps = { + ...(explorerBaseUrl && { openExternalLink: handleOpenExternalLink }) + }; + return ( // eslint-disable-next-line react/jsx-pascal-case analytics.sendEventToPostHog(PostHogAction.ActivityActivityDetailInputsClick)} sendAnalyticsOutputs={() => analytics.sendEventToPostHog(PostHogAction.ActivityActivityDetailOutputsClick)} + {...environmentSpecificProps} /> ); } diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/assets/components/AssetDetailsDrawer/AssetDetails.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/assets/components/AssetDetailsDrawer/AssetDetails.tsx index beac455ca5..89c670e2dc 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/assets/components/AssetDetailsDrawer/AssetDetails.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/features/assets/components/AssetDetailsDrawer/AssetDetails.tsx @@ -48,7 +48,12 @@ export const AssetDetails = ({ const { environmentName } = useWalletStore(); const openExternalLink = useExternalLinkOpener(); - const explorerBaseUrl = useMemo(() => CEXPLORER_BASE_URL[environmentName], [environmentName]); + // TODO remove if sanchonet gets an explorer + const explorerBaseUrl = useMemo(() => { + if (environmentName === 'Sanchonet') return; + // eslint-disable-next-line consistent-return + return CEXPLORER_BASE_URL[environmentName]; + }, [environmentName]); const isTxListLoading = activityListStatus === StateStatus.IDLE || activityListStatus === StateStatus.LOADING; return ( @@ -111,13 +116,19 @@ export const AssetDetails = ({ name: t('browserView.assetDetails.fingerprint'), value: fingerprint, showCopyIcon: true, - onClick: () => openExternalLink(`${explorerBaseUrl}/${CEXPLORER_URL_PATHS.Asset}/${fingerprint}`) + // TODO remove if sanchonet gets an explorer + ...(explorerBaseUrl && { + onClick: () => openExternalLink(`${explorerBaseUrl}/${CEXPLORER_URL_PATHS.Asset}/${fingerprint}`) + }) }, { name: t('browserView.assetDetails.policyId'), value: policyId, showCopyIcon: true, - onClick: () => openExternalLink(`${explorerBaseUrl}/${CEXPLORER_URL_PATHS.Policy}/${policyId}`) + // TODO remove if sanchonet gets an explorer + ...(explorerBaseUrl && { + onClick: () => openExternalLink(`${explorerBaseUrl}/${CEXPLORER_URL_PATHS.Policy}/${policyId}`) + }) } ]} /> diff --git a/packages/core/src/ui/components/ActivityDetail/TransactionDetails.tsx b/packages/core/src/ui/components/ActivityDetail/TransactionDetails.tsx index e54e20cc15..4c18e38c9c 100644 --- a/packages/core/src/ui/components/ActivityDetail/TransactionDetails.tsx +++ b/packages/core/src/ui/components/ActivityDetail/TransactionDetails.tsx @@ -141,8 +141,8 @@ export const TransactionDetails = ({
diff --git a/packages/core/src/ui/components/VotingProcedures/VotingProcedures.tsx b/packages/core/src/ui/components/VotingProcedures/VotingProcedures.tsx index 1dcf20f244..232b6aa43d 100644 --- a/packages/core/src/ui/components/VotingProcedures/VotingProcedures.tsx +++ b/packages/core/src/ui/components/VotingProcedures/VotingProcedures.tsx @@ -12,14 +12,14 @@ type VotingProcedure = { actionId: { index: number; txHash: string; - txHashUrl: string; + txHashUrl?: string; // Dependent on having an explorer to link }; votingProcedure: { vote: string; anchor: { url: string; hash: string; - }; + } | null; }; }[]; }; @@ -111,9 +111,15 @@ export const VotingProcedures = ({ dappInfo, errorMessage, data, translations }: {indexCounter(translations.actionIdTitle, idx, votes.length)} - - - + {actionId.txHashUrl && ( + + + + )} From dbf9ded9ed8a7be18739a66176e391299e232241 Mon Sep 17 00:00:00 2001 From: vetalcore Date: Thu, 23 Nov 2023 13:17:13 +0200 Subject: [PATCH 09/68] chore(extension): add unit tests for dapp/confirm-tx (#743) --- .../dapp/components/ConfirmTransaction.tsx | 280 ------------ .../__tests__/ConfirmTransaction.test.tsx | 133 ------ .../ConfirmTransaction.tsx | 28 +- .../VotingProceduresContainer.tsx | 17 +- .../ConfirmDRepRegistrationContainer.test.tsx | 177 ++++++++ .../ConfirmDRepRetirementContainer.test.tsx | 220 +++++++++ .../ConfirmDRepUpdateContainer.test.tsx | 168 +++++++ .../__tests__/ConfirmTransaction.test.tsx | 306 +++++++++++++ .../ConfirmTransactionContent.test.tsx | 213 +++++++++ .../ConfirmVoteDelegationContainer.test.tsx | 216 +++++++++ .../DappTransactionContainer.test.tsx | 235 ++++++++++ .../VotingProceduresContainer.test.tsx | 294 ++++++++++++ .../__tests__/hooks.test.tsx | 417 ++++++++++++++++++ .../__tests__/utils.test.tsx | 158 +++++++ .../components/confirm-transaction/hooks.ts | 15 +- .../components/confirm-transaction/utils.ts | 48 +- .../__tests__/useActionExecution.test.ts | 2 +- .../__tests__/activity-detail-slice.test.ts | 31 ++ .../stores/slices/activity-detail-slice.ts | 2 +- 19 files changed, 2479 insertions(+), 481 deletions(-) delete mode 100644 apps/browser-extension-wallet/src/features/dapp/components/ConfirmTransaction.tsx delete mode 100644 apps/browser-extension-wallet/src/features/dapp/components/__tests__/ConfirmTransaction.test.tsx create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepRegistrationContainer.test.tsx create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepRetirementContainer.test.tsx create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepUpdateContainer.test.tsx create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmTransaction.test.tsx create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmTransactionContent.test.tsx create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmVoteDelegationContainer.test.tsx create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/DappTransactionContainer.test.tsx create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/VotingProceduresContainer.test.tsx create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/hooks.test.tsx create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/utils.test.tsx create mode 100644 apps/browser-extension-wallet/src/stores/slices/__tests__/activity-detail-slice.test.ts diff --git a/apps/browser-extension-wallet/src/features/dapp/components/ConfirmTransaction.tsx b/apps/browser-extension-wallet/src/features/dapp/components/ConfirmTransaction.tsx deleted file mode 100644 index 90b912655e..0000000000 --- a/apps/browser-extension-wallet/src/features/dapp/components/ConfirmTransaction.tsx +++ /dev/null @@ -1,280 +0,0 @@ -import React, { useState, useEffect, useMemo, useCallback } from 'react'; -import { Button, PostHogAction, useObservable } from '@lace/common'; -import { useTranslation } from 'react-i18next'; -import { DappTransaction } from '@lace/core'; -import { Layout } from './Layout'; -import { useViewsFlowContext } from '@providers/ViewFlowProvider'; -import { sectionTitle, DAPP_VIEWS } from '../config'; -import styles from './ConfirmTransaction.module.scss'; -import { Wallet } from '@lace/cardano'; -import { useAddressBookContext, withAddressBookContext } from '@src/features/address-book/context'; -import { useWalletStore } from '@stores'; -import { AddressListType } from '@views/browser/features/activity'; -import { consumeRemoteApi, exposeApi, RemoteApiPropertyType } from '@cardano-sdk/web-extension'; -import { DappDataService } from '@lib/scripts/types'; -import { DAPP_CHANNELS } from '@src/utils/constants'; -import { runtime } from 'webextension-polyfill'; -import { useRedirection } from '@hooks'; -import { assetsBurnedInspector, assetsMintedInspector, createTxInspector } from '@cardano-sdk/core'; -import { Skeleton } from 'antd'; -import { dAppRoutePaths } from '@routes'; -import { UserPromptService } from '@lib/scripts/background/services'; -import { of } from 'rxjs'; -import { CardanoTxOut } from '@src/types'; -import { getAssetsInformation, TokenInfo } from '@src/utils/get-assets-information'; -import * as HardwareLedger from '../../../../../../node_modules/@cardano-sdk/hardware-ledger/dist/cjs'; -import { useAnalyticsContext } from '@providers'; -import { TX_CREATION_TYPE_KEY, TxCreationType } from '@providers/AnalyticsProvider/analyticsTracker'; - -const DAPP_TOAST_DURATION = 50; - -const dappDataApi = consumeRemoteApi>( - { - baseChannel: DAPP_CHANNELS.dappData, - properties: { - getSignTxData: RemoteApiPropertyType.MethodReturningPromise - } - }, - { logger: console, runtime } -); - -// eslint-disable-next-line sonarjs/cognitive-complexity -export const ConfirmTransaction = withAddressBookContext((): React.ReactElement => { - const { - utils: { setNextView } - } = useViewsFlowContext(); - const { t } = useTranslation(); - const { - walletInfo, - inMemoryWallet, - getKeyAgentType, - blockchainProvider: { assetProvider } - } = useWalletStore(); - const { list: addressList } = useAddressBookContext(); - const analytics = useAnalyticsContext(); - - const [tx, setTx] = useState(); - const assets = useObservable(inMemoryWallet.assetInfo$); - const [errorMessage, setErrorMessage] = useState(); - const redirectToSignFailure = useRedirection(dAppRoutePaths.dappTxSignFailure); - const [isConfirmingTx, setIsConfirmingTx] = useState(); - const keyAgentType = getKeyAgentType(); - const isUsingHardwareWallet = useMemo( - () => keyAgentType !== Wallet.KeyManagement.KeyAgentType.InMemory, - [keyAgentType] - ); - const [assetsInfo, setAssetsInfo] = useState(); - const [dappInfo, setDappInfo] = useState(); - - const getTransactionAssetsId = (outputs: CardanoTxOut[]) => { - const assetIds: Wallet.Cardano.AssetId[] = []; - const assetMaps = outputs.map((output) => output.value.assets); - for (const asset of assetMaps) { - if (asset) { - for (const id of asset.keys()) { - !assetIds.includes(id) && assetIds.push(id); - } - } - } - return assetIds; - }; - - const assetIds = useMemo(() => tx?.body?.outputs && getTransactionAssetsId(tx.body.outputs), [tx?.body?.outputs]); - - useEffect(() => { - if (assetIds?.length > 0) { - getAssetsInformation(assetIds, assets, { - assetProvider, - extraData: { nftMetadata: true, tokenMetadata: true } - }) - .then((result) => setAssetsInfo(result)) - .catch((error) => { - console.error(error); - }); - } - }, [assetIds, assetProvider, assets]); - - const cancelTransaction = useCallback((close = false) => { - exposeApi>( - { - api$: of({ - async allowSignTx(): Promise { - return Promise.reject(); - } - }), - baseChannel: DAPP_CHANNELS.userPrompt, - properties: { allowSignTx: RemoteApiPropertyType.MethodReturningPromise } - }, - { logger: console, runtime } - ); - close && setTimeout(() => window.close(), DAPP_TOAST_DURATION); - }, []); - - window.addEventListener('beforeunload', cancelTransaction); - - const signWithHardwareWallet = async () => { - setIsConfirmingTx(true); - try { - HardwareLedger.LedgerKeyAgent.establishDeviceConnection(Wallet.KeyManagement.CommunicationType.Web) - .then(() => { - exposeApi>( - { - api$: of({ - async allowSignTx(): Promise { - return Promise.resolve(true); - } - }), - baseChannel: DAPP_CHANNELS.userPrompt, - properties: { allowSignTx: RemoteApiPropertyType.MethodReturningPromise } - }, - { logger: console, runtime } - ); - }) - .catch((error) => { - throw error; - }); - } catch (error) { - console.error('error', error); - cancelTransaction(false); - redirectToSignFailure(); - } - }; - - useEffect(() => { - dappDataApi - .getSignTxData() - .then(({ dappInfo: backgroundDappInfo, tx: backgroundTx }) => { - setDappInfo(backgroundDappInfo); - setTx(backgroundTx); - }) - .catch((error) => { - setErrorMessage(error); - console.error(error); - }); - }, []); - - const createAssetList = useCallback( - (txAssets: Wallet.Cardano.TokenMap) => { - if (!assetsInfo) return []; - const assetList: Wallet.Cip30SignTxAssetItem[] = []; - // eslint-disable-next-line unicorn/no-array-for-each - txAssets.forEach(async (value, key) => { - const walletAsset = assets.get(key) || assetsInfo?.get(key); - assetList.push({ - name: walletAsset.name.toString() || key.toString(), - ticker: walletAsset.tokenMetadata?.ticker || walletAsset.nftMetadata?.name, - amount: Wallet.util.calculateAssetBalance(value, walletAsset) - }); - }); - return assetList; - }, - [assets, assetsInfo] - ); - - const addressToNameMap = useMemo( - () => new Map(addressList?.map((item: AddressListType) => [item.address, item.name])), - [addressList] - ); - - const txSummary: Wallet.Cip30SignTxSummary | undefined = useMemo(() => { - if (!tx) return; - const inspector = createTxInspector({ - minted: assetsMintedInspector, - burned: assetsBurnedInspector - }); - - const { minted, burned } = inspector(tx as Wallet.Cardano.HydratedTx); - const isMintTransaction = minted.length > 0; - const isBurnTransaction = burned.length > 0; - - let txType: 'Send' | 'Mint' | 'Burn'; - if (isMintTransaction) { - txType = 'Mint'; - } else if (isBurnTransaction) { - txType = 'Burn'; - } else { - txType = 'Send'; - } - - const externalOutputs = tx.body.outputs.filter((output) => { - if (txType === 'Send') { - return walletInfo.addresses.every((addr) => output.address !== addr.address); - } - return true; - }); - - // eslint-disable-next-line unicorn/no-array-reduce - const txSummaryOutputs: Wallet.Cip30SignTxSummary['outputs'] = externalOutputs.reduce((acc, txOut) => { - // Don't show withdrawl tx's etc - if (txOut.address.toString() === walletInfo.addresses[0].address.toString()) return acc; - - return [ - ...acc, - { - coins: Wallet.util.lovelacesToAdaString(txOut.value.coins.toString()), - recipient: addressToNameMap?.get(txOut.address.toString()) || txOut.address.toString(), - ...(txOut.value.assets?.size > 0 && { assets: createAssetList(txOut.value.assets) }) - } - ]; - }, []); - - // eslint-disable-next-line consistent-return - return { - fee: Wallet.util.lovelacesToAdaString(tx.body.fee.toString()), - outputs: txSummaryOutputs, - type: txType - }; - }, [tx, walletInfo.addresses, createAssetList, addressToNameMap]); - - const translations = { - transaction: t('core.dappTransaction.transaction'), - amount: t('core.dappTransaction.amount'), - recipient: t('core.dappTransaction.recipient'), - fee: t('core.dappTransaction.fee'), - adaFollowingNumericValue: t('general.adaFollowingNumericValue') - }; - - const onConfirm = () => { - analytics.sendEventToPostHog(PostHogAction.SendTransactionSummaryConfirmClick, { - [TX_CREATION_TYPE_KEY]: TxCreationType.External - }); - - isUsingHardwareWallet ? signWithHardwareWallet() : setNextView(); - }; - - return ( - - {tx && txSummary ? ( - - ) : ( - - )} -
- - -
-
- ); -}); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/__tests__/ConfirmTransaction.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/__tests__/ConfirmTransaction.test.tsx deleted file mode 100644 index 5bf5130d4c..0000000000 --- a/apps/browser-extension-wallet/src/features/dapp/components/__tests__/ConfirmTransaction.test.tsx +++ /dev/null @@ -1,133 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable import/imports-first */ -const mockGetKeyAgentType = jest.fn(); -const mockUseWalletStore = jest.fn(); -const error = 'error in getSignTxData'; -const mockConsumeRemoteApi = jest.fn().mockReturnValue({ - getSignTxData: async () => await Promise.reject(error) -}); -const mockCreateTxInspector = jest.fn().mockReturnValue(() => ({ minted: [] as any, burned: [] as any })); -import * as React from 'react'; -import { cleanup, render, waitFor } from '@testing-library/react'; -import { ConfirmTransaction } from '../confirm-transaction/ConfirmTransaction'; -import '@testing-library/jest-dom'; -import { I18nextProvider } from 'react-i18next'; -import { StoreProvider } from '@src/stores'; -import { - AnalyticsProvider, - AppSettingsProvider, - BackgroundServiceAPIProvider, - BackgroundServiceAPIProviderProps, - DatabaseProvider, - ViewFlowProvider -} from '@src/providers'; -import { APP_MODE_BROWSER } from '@src/utils/constants'; -import i18n from '@lib/i18n'; -import { BehaviorSubject } from 'rxjs'; -import { act } from 'react-dom/test-utils'; -import { sendViewsFlowState } from '../../config'; -import { PostHogClientProvider } from '@providers/PostHogClientProvider'; -import { postHogClientMocks } from '@src/utils/mocks/test-helpers'; - -const assetInfo$ = new BehaviorSubject(new Map()); -const available$ = new BehaviorSubject([]); - -const assetProvider = { - getAsset: () => ({}), - getAssets: (): any[] => [] -}; -const inMemoryWallet = { - assetInfo$, - balance: { - utxo: { - available$ - } - } -}; - -jest.mock('@src/stores', () => ({ - ...jest.requireActual('@src/stores'), - useWalletStore: mockUseWalletStore -})); - -jest.mock('@cardano-sdk/web-extension', () => { - const original = jest.requireActual('@cardano-sdk/web-extension'); - return { - __esModule: true, - ...original, - consumeRemoteApi: mockConsumeRemoteApi - }; -}); - -jest.mock('@cardano-sdk/core', () => { - const original = jest.requireActual('@cardano-sdk/core'); - return { - __esModule: true, - ...original, - createTxInspector: mockCreateTxInspector - }; -}); - -const testIds = { - dappTransactionConfirm: 'dapp-transaction-confirm' -}; - -const backgroundService = { - getBackgroundStorage: jest.fn(), - setBackgroundStorage: jest.fn() -} as unknown as BackgroundServiceAPIProviderProps['value']; - -const getWrapper = - () => - ({ children }: { children: React.ReactNode }) => - ( - - - - - - - - {children} - - - - - - - - ); - -describe('Testing ConfirmTransaction component', () => { - window.ResizeObserver = ResizeObserver; - describe('Testing errors', () => { - beforeEach(() => { - mockUseWalletStore.mockImplementation(() => ({ - getKeyAgentType: mockGetKeyAgentType, - inMemoryWallet, - walletUI: {}, - walletInfo: {}, - blockchainProvider: { assetProvider } - })); - }); - - afterEach(() => { - jest.resetModules(); - jest.resetAllMocks(); - cleanup(); - }); - - test('should disable confirm button and show proper error if getSignTxData throws', async () => { - let queryByTestId: any; - act(() => { - ({ queryByTestId } = render(, { - wrapper: getWrapper() - })); - }); - - await waitFor(async () => { - expect(queryByTestId(testIds.dappTransactionConfirm).closest('button')).toHaveAttribute('disabled'); - }); - }); - }); -}); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.tsx index ee3ce584af..ba6ea45711 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.tsx @@ -1,4 +1,5 @@ -import React from 'react'; +/* eslint-disable no-console */ +import React, { useMemo } from 'react'; import { Button } from '@lace/common'; import { useTranslation } from 'react-i18next'; import { Layout } from '../Layout'; @@ -14,21 +15,24 @@ import { runtime } from 'webextension-polyfill'; import { getTitleKey, getTxType } from './utils'; import { ConfirmTransactionContent } from './ConfirmTransactionContent'; -const dappDataApi = consumeRemoteApi>( - { - baseChannel: DAPP_CHANNELS.dappData, - properties: { - getSignTxData: RemoteApiPropertyType.MethodReturningPromise - } - }, - { logger: console, runtime } -); - export const ConfirmTransaction = (): React.ReactElement => { const { t } = useTranslation(); const { utils: { setNextView } } = useViewsFlowContext(); + const dappDataApi = useMemo( + () => + consumeRemoteApi>( + { + baseChannel: DAPP_CHANNELS.dappData, + properties: { + getSignTxData: RemoteApiPropertyType.MethodReturningPromise + } + }, + { logger: console, runtime } + ), + [] + ); const { getKeyAgentType } = useWalletStore(); const { signTxData, errorMessage } = useSignTxData(dappDataApi.getSignTxData); const keyAgentType = getKeyAgentType(); @@ -46,7 +50,7 @@ export const ConfirmTransaction = (): React.ReactElement => {
Date: Tue, 28 Nov 2023 10:54:19 +0100 Subject: [PATCH 15/68] fix(extension): translation keys casing and unit tests (#756) --- .../ConfirmDRepRegistrationContainer.test.tsx | 10 +++++----- .../ConfirmDRepRetirementContainer.test.tsx | 10 +++++----- .../ConfirmDRepUpdateContainer.test.tsx | 8 ++++---- .../ConfirmVoteDelegationContainer.test.tsx | 10 +++++----- .../VotingProceduresContainer.test.tsx | 18 +++++++++--------- .../__tests__/utils.test.tsx | 4 ++-- .../components/confirm-transaction/utils.ts | 2 +- 7 files changed, 31 insertions(+), 31 deletions(-) diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepRegistrationContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepRegistrationContainer.test.tsx index 13619e395b..d63017f1e1 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepRegistrationContainer.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepRegistrationContainer.test.tsx @@ -161,12 +161,12 @@ describe('Testing ConfirmDRepRegistrationContainer component', () => { url: certificate.anchor?.url }, translations: { - metadata: t('core.drepRegistration.metadata'), + metadata: t('core.DRepRegistration.metadata'), labels: { - depositPaid: t('core.drepRegistration.depositPaid'), - drepId: t('core.drepRegistration.drepId'), - hash: t('core.drepRegistration.hash'), - url: t('core.drepRegistration.url') + depositPaid: t('core.DRepRegistration.depositPaid'), + drepId: t('core.DRepRegistration.drepId'), + hash: t('core.DRepRegistration.hash'), + url: t('core.DRepRegistration.url') } }, errorMessage diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepRetirementContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepRetirementContainer.test.tsx index 3e0cae4ebf..bb0fe2b214 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepRetirementContainer.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepRetirementContainer.test.tsx @@ -173,10 +173,10 @@ describe('Testing ConfirmDRepRetirementContainer component', () => { drepId: Cardano.DRepID(HexBlob.toTypedBech32('drep', Wallet.HexBlob(certificate.dRepCredential.hash))) }, translations: { - metadata: t('core.drepRetirement.metadata'), + metadata: t('core.DRepRetirement.metadata'), labels: { - depositReturned: t('core.drepRetirement.depositReturned'), - drepId: t('core.drepRetirement.drepId') + depositReturned: t('core.DRepRetirement.depositReturned'), + drepId: t('core.DRepRetirement.drepId') } }, errorMessage @@ -198,7 +198,7 @@ describe('Testing ConfirmDRepRetirementContainer component', () => { expect(queryByTestId('ConfirmDRepRetirementContainer')).toBeInTheDocument(); expect( mockConfirmDRepRetirement.mock.calls[mockConfirmDRepRetirement.mock.calls.length - 1][0].errorMessage - ).toEqual(t('core.drepRetirement.isOwnRetirement')); + ).toEqual(t('core.DRepRetirement.isOwnRetirement')); }); test('should render ConfirmDRepRetirementContainer component with proper error for not own retirement', async () => { @@ -215,6 +215,6 @@ describe('Testing ConfirmDRepRetirementContainer component', () => { expect( mockConfirmDRepRetirement.mock.calls[mockConfirmDRepRetirement.mock.calls.length - 1][0].errorMessage - ).toEqual(t('core.drepRetirement.isNotOwnRetirement')); + ).toEqual(t('core.DRepRetirement.isNotOwnRetirement')); }); }); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepUpdateContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepUpdateContainer.test.tsx index 17c4e55e09..eba21c3337 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepUpdateContainer.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepUpdateContainer.test.tsx @@ -153,11 +153,11 @@ describe('Testing ConfirmDRepUpdateContainer component', () => { url: certificate.anchor?.url }, translations: { - metadata: t('core.drepUpdate.metadata'), + metadata: t('core.DRepUpdate.metadata'), labels: { - drepId: t('core.drepUpdate.drepId'), - hash: t('core.drepUpdate.hash'), - url: t('core.drepUpdate.url') + drepId: t('core.DRepUpdate.drepId'), + hash: t('core.DRepUpdate.hash'), + url: t('core.DRepUpdate.url') } }, errorMessage diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmVoteDelegationContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmVoteDelegationContainer.test.tsx index c462bfc452..dadcfafa27 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmVoteDelegationContainer.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmVoteDelegationContainer.test.tsx @@ -155,12 +155,12 @@ describe('Testing ConfirmVoteDelegationContainer component', () => { ) }, translations: { - metadata: t('core.voteDelegation.metadata'), - option: t('core.voteDelegation.option'), + metadata: t('core.VoteDelegation.metadata'), + option: t('core.VoteDelegation.option'), labels: { - drepId: t('core.voteDelegation.drepId'), - alwaysAbstain: t('core.voteDelegation.alwaysAbstain'), - alwaysNoConfidence: t('core.voteDelegation.alwaysNoConfidence') + drepId: t('core.VoteDelegation.drepId'), + alwaysAbstain: t('core.VoteDelegation.alwaysAbstain'), + alwaysNoConfidence: t('core.VoteDelegation.alwaysNoConfidence') } }, errorMessage diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/VotingProceduresContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/VotingProceduresContainer.test.tsx index c02aa5f70c..00a65675c8 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/VotingProceduresContainer.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/VotingProceduresContainer.test.tsx @@ -230,19 +230,19 @@ describe('Testing VotingProceduresContainer component', () => { })) })), translations: { - voterType: t('core.votingProcedures.voterType'), - procedureTitle: t('core.votingProcedures.procedureTitle'), - actionIdTitle: t('core.votingProcedures.actionIdTitle'), - vote: t('core.votingProcedures.vote'), + voterType: t('core.VotingProcedures.voterType'), + procedureTitle: t('core.VotingProcedures.procedureTitle'), + actionIdTitle: t('core.VotingProcedures.actionIdTitle'), + vote: t('core.VotingProcedures.vote'), actionId: { - index: t('core.votingProcedures.actionId.index'), - txHash: t('core.votingProcedures.actionId.txHash') + index: t('core.VotingProcedures.actionId.index'), + txHash: t('core.VotingProcedures.actionId.txHash') }, anchor: { - hash: t('core.votingProcedures.anchor.hash'), - url: t('core.votingProcedures.anchor.url') + hash: t('core.VotingProcedures.anchor.hash'), + url: t('core.VotingProcedures.anchor.url') }, - dRepId: t('core.votingProcedures.dRepId') + dRepId: t('core.VotingProcedures.dRepId') }, errorMessage }, diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/utils.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/utils.test.tsx index 0cc51248c7..2d8a3f39ff 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/utils.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/utils.test.tsx @@ -152,7 +152,7 @@ describe('Testing utils', () => { test('testing getOwnRetirementMessageKey', () => { expect(getOwnRetirementMessageKey(undefined)).toEqual(''); - expect(getOwnRetirementMessageKey(true)).toEqual('core.drepRetirement.isOwnRetirement'); - expect(getOwnRetirementMessageKey(false)).toEqual('core.drepRetirement.isNotOwnRetirement'); + expect(getOwnRetirementMessageKey(true)).toEqual('core.DRepRetirement.isOwnRetirement'); + expect(getOwnRetirementMessageKey(false)).toEqual('core.DRepRetirement.isNotOwnRetirement'); }); }); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/utils.ts b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/utils.ts index 756fc17999..4a6e3d99d9 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/utils.ts +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/utils.ts @@ -136,5 +136,5 @@ export const getOwnRetirementMessageKey = (isOwnRetirement: boolean | undefined) if (isOwnRetirement === undefined) { return ''; } - return isOwnRetirement ? 'core.drepRetirement.isOwnRetirement' : 'core.drepRetirement.isNotOwnRetirement'; + return isOwnRetirement ? 'core.DRepRetirement.isOwnRetirement' : 'core.DRepRetirement.isNotOwnRetirement'; }; From fcc660f3f775f31ef7ad90da73019b9f3d093818 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20W=C5=82odek?= Date: Tue, 28 Nov 2023 11:22:03 +0100 Subject: [PATCH 16/68] chore(all): exclude stories and tests from the rollup builds (#752) * chore(core): fix extension of stories .ts -> .tsx (impacts: rollup) * chore(all): exclude test files from rollup builds --- ...istration.stories.ts => ConfirmDRepRegistration.stories.tsx} | 0 ...pRetirement.stories.ts => ConfirmDRepRetirement.stories.tsx} | 0 ...nfirmDRepUpdate.stories.ts => ConfirmDRepUpdate.stories.tsx} | 0 ...eDelegation.stories.ts => ConfirmVoteDelegation.stories.tsx} | 0 .../{DappTransaction.stories.ts => DappTransaction.stories.tsx} | 0 ...VotingProcedures.stories.ts => VotingProcedures.stories.tsx} | 0 rollup.config.js | 2 +- 7 files changed, 1 insertion(+), 1 deletion(-) rename packages/core/src/ui/components/ConfirmDRepRegistration/{ConfirmDRepRegistration.stories.ts => ConfirmDRepRegistration.stories.tsx} (100%) rename packages/core/src/ui/components/ConfirmDRepRetirement/{ConfirmDRepRetirement.stories.ts => ConfirmDRepRetirement.stories.tsx} (100%) rename packages/core/src/ui/components/ConfirmDRepUpdate/{ConfirmDRepUpdate.stories.ts => ConfirmDRepUpdate.stories.tsx} (100%) rename packages/core/src/ui/components/ConfirmVoteDelegation/{ConfirmVoteDelegation.stories.ts => ConfirmVoteDelegation.stories.tsx} (100%) rename packages/core/src/ui/components/DappTransaction/{DappTransaction.stories.ts => DappTransaction.stories.tsx} (100%) rename packages/core/src/ui/components/VotingProcedures/{VotingProcedures.stories.ts => VotingProcedures.stories.tsx} (100%) diff --git a/packages/core/src/ui/components/ConfirmDRepRegistration/ConfirmDRepRegistration.stories.ts b/packages/core/src/ui/components/ConfirmDRepRegistration/ConfirmDRepRegistration.stories.tsx similarity index 100% rename from packages/core/src/ui/components/ConfirmDRepRegistration/ConfirmDRepRegistration.stories.ts rename to packages/core/src/ui/components/ConfirmDRepRegistration/ConfirmDRepRegistration.stories.tsx diff --git a/packages/core/src/ui/components/ConfirmDRepRetirement/ConfirmDRepRetirement.stories.ts b/packages/core/src/ui/components/ConfirmDRepRetirement/ConfirmDRepRetirement.stories.tsx similarity index 100% rename from packages/core/src/ui/components/ConfirmDRepRetirement/ConfirmDRepRetirement.stories.ts rename to packages/core/src/ui/components/ConfirmDRepRetirement/ConfirmDRepRetirement.stories.tsx diff --git a/packages/core/src/ui/components/ConfirmDRepUpdate/ConfirmDRepUpdate.stories.ts b/packages/core/src/ui/components/ConfirmDRepUpdate/ConfirmDRepUpdate.stories.tsx similarity index 100% rename from packages/core/src/ui/components/ConfirmDRepUpdate/ConfirmDRepUpdate.stories.ts rename to packages/core/src/ui/components/ConfirmDRepUpdate/ConfirmDRepUpdate.stories.tsx diff --git a/packages/core/src/ui/components/ConfirmVoteDelegation/ConfirmVoteDelegation.stories.ts b/packages/core/src/ui/components/ConfirmVoteDelegation/ConfirmVoteDelegation.stories.tsx similarity index 100% rename from packages/core/src/ui/components/ConfirmVoteDelegation/ConfirmVoteDelegation.stories.ts rename to packages/core/src/ui/components/ConfirmVoteDelegation/ConfirmVoteDelegation.stories.tsx diff --git a/packages/core/src/ui/components/DappTransaction/DappTransaction.stories.ts b/packages/core/src/ui/components/DappTransaction/DappTransaction.stories.tsx similarity index 100% rename from packages/core/src/ui/components/DappTransaction/DappTransaction.stories.ts rename to packages/core/src/ui/components/DappTransaction/DappTransaction.stories.tsx diff --git a/packages/core/src/ui/components/VotingProcedures/VotingProcedures.stories.ts b/packages/core/src/ui/components/VotingProcedures/VotingProcedures.stories.tsx similarity index 100% rename from packages/core/src/ui/components/VotingProcedures/VotingProcedures.stories.ts rename to packages/core/src/ui/components/VotingProcedures/VotingProcedures.stories.tsx diff --git a/rollup.config.js b/rollup.config.js index d2ef166611..aef7832d8f 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -11,7 +11,7 @@ export default ({ tsPluginOptions = { tsconfig: 'src/tsconfig.json', composite: false, - exclude: ['**/*.stories.tsx'] + exclude: ['**/*.stories.tsx', '**/*.test.ts', '**/*.test.tsx'] } } = {}) => ({ input: 'src/index.ts', From 5b81b19b201fea3c4a94948a5bb6199958b8a3c0 Mon Sep 17 00:00:00 2001 From: mirceahasegan <105701265+mirceahasegan@users.noreply.github.com> Date: Wed, 29 Nov 2023 17:44:49 +0100 Subject: [PATCH 17/68] Chore/update for sdk conway era (#734) * chore: stake pools sort by ros * chore: metrics require lastRos and ros --- apps/browser-extension-wallet/src/api/mock.ts | 4 ++- .../src/utils/mocks/test-helpers.tsx | 4 ++- .../StakePoolsTable/StakePoolsTable.tsx | 2 +- .../test/mocks/StakepoolSearchProviderStub.ts | 28 ++++++++++++++----- .../cardano/src/wallet/test/mocks/mock.ts | 4 ++- .../__tests__/stake-pool-transformer.test.ts | 4 ++- .../StakePoolsTable/StakePoolsTable.tsx | 2 +- 7 files changed, 35 insertions(+), 13 deletions(-) diff --git a/apps/browser-extension-wallet/src/api/mock.ts b/apps/browser-extension-wallet/src/api/mock.ts index 62dc74e7c7..54dc9047b8 100644 --- a/apps/browser-extension-wallet/src/api/mock.ts +++ b/apps/browser-extension-wallet/src/api/mock.ts @@ -126,7 +126,9 @@ const getDetailsForAll = (): PoolDetails => ({ livePledge: BigInt('2000000000'), saturation: Percent(0.95), size: undefined, - stake: undefined + stake: undefined, + lastRos: Percent(1), + ros: Percent(2) }, relays: undefined, rewardAccount: Wallet.Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj'), diff --git a/apps/browser-extension-wallet/src/utils/mocks/test-helpers.tsx b/apps/browser-extension-wallet/src/utils/mocks/test-helpers.tsx index ab736b8702..ee2bed4e96 100644 --- a/apps/browser-extension-wallet/src/utils/mocks/test-helpers.tsx +++ b/apps/browser-extension-wallet/src/utils/mocks/test-helpers.tsx @@ -262,7 +262,9 @@ export const cardanoStakePoolMock: Wallet.StakePoolSearchResults = { saturation: Percent(0.0512), stake: undefined, size: undefined, - apy: Percent(0.013) + apy: Percent(0.013), + lastRos: Percent(1), + ros: Percent(2) }, owners: [ Wallet.Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj'), diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/StakePoolsTable/StakePoolsTable.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/StakePoolsTable/StakePoolsTable.tsx index 9b671719ad..fd10a3e36c 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/StakePoolsTable/StakePoolsTable.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/StakePoolsTable/StakePoolsTable.tsx @@ -26,7 +26,7 @@ type stakePoolsTableProps = { }; const DEFAULT_SORT_OPTIONS: StakePoolSortOptions = { - field: 'apy', + field: 'ros', order: 'desc' }; diff --git a/packages/cardano/src/wallet/test/mocks/StakepoolSearchProviderStub.ts b/packages/cardano/src/wallet/test/mocks/StakepoolSearchProviderStub.ts index abacce350f..3a62f7ea9e 100644 --- a/packages/cardano/src/wallet/test/mocks/StakepoolSearchProviderStub.ts +++ b/packages/cardano/src/wallet/test/mocks/StakepoolSearchProviderStub.ts @@ -20,7 +20,9 @@ export const pools: Partial[] = [ livePledge: BigInt('2000000000'), saturation: Percent(0.211), size: undefined, - stake: undefined + stake: undefined, + lastRos: Percent(1), + ros: Percent(2) }, margin: { numerator: 2.01, @@ -58,7 +60,9 @@ export const pools: Partial[] = [ stake: { live: BigInt('201000000'), active: BigInt('201000000') - } + }, + lastRos: Percent(1), + ros: Percent(2) }, metadata: { name: 'THE AMSTERDAM NODE', @@ -92,7 +96,9 @@ export const pools: Partial[] = [ stake: { live: BigInt('77000000'), active: BigInt('77000000') - } + }, + lastRos: Percent(1), + ros: Percent(2) } }, { @@ -114,7 +120,9 @@ export const pools: Partial[] = [ stake: { live: BigInt('34000000'), active: BigInt('34000000') - } + }, + lastRos: Percent(1), + ros: Percent(2) }, metadata: { name: 'stakit.io Pool by TOBG', @@ -144,7 +152,9 @@ export const pools: Partial[] = [ stake: { live: BigInt('53000000'), active: BigInt('53000000') - } + }, + lastRos: Percent(1), + ros: Percent(2) }, margin: { numerator: 0.79, @@ -172,7 +182,9 @@ export const pools: Partial[] = [ stake: { live: BigInt('53000000'), active: BigInt('53000000') - } + }, + lastRos: Percent(1), + ros: Percent(2) }, metadata: { name: 'VEGASPool', @@ -202,7 +214,9 @@ const detailsForAll: PoolDetails = { stake: { live: BigInt('34000000'), active: BigInt('34000000') - } + }, + lastRos: Percent(1), + ros: Percent(2) }, relays: undefined, rewardAccount: Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj'), diff --git a/packages/cardano/src/wallet/test/mocks/mock.ts b/packages/cardano/src/wallet/test/mocks/mock.ts index 723ea89e24..023b48fcec 100644 --- a/packages/cardano/src/wallet/test/mocks/mock.ts +++ b/packages/cardano/src/wallet/test/mocks/mock.ts @@ -32,7 +32,9 @@ export const stakePoolMock: Cardano.StakePool = { livePledge: BigInt('2000000000'), saturation: Percent(0.5), stake: undefined, - size: undefined + size: undefined, + lastRos: Percent(1), + ros: Percent(2) }, owners: [ Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj'), diff --git a/packages/cardano/src/wallet/util/__tests__/stake-pool-transformer.test.ts b/packages/cardano/src/wallet/util/__tests__/stake-pool-transformer.test.ts index cd6dfaa23f..a8d343bf96 100644 --- a/packages/cardano/src/wallet/util/__tests__/stake-pool-transformer.test.ts +++ b/packages/cardano/src/wallet/util/__tests__/stake-pool-transformer.test.ts @@ -40,7 +40,9 @@ const cardanoStakePoolMock: StakePoolSearchResults = { saturation: Percent(0.0512), stake: undefined, size: undefined, - apy: Percent(0.013) + apy: Percent(0.013), + lastRos: Percent(1), + ros: Percent(2) }, owners: [ Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj'), diff --git a/packages/staking/src/features/BrowsePools/StakePoolsTable/StakePoolsTable.tsx b/packages/staking/src/features/BrowsePools/StakePoolsTable/StakePoolsTable.tsx index 610ceee531..390725ac0d 100644 --- a/packages/staking/src/features/BrowsePools/StakePoolsTable/StakePoolsTable.tsx +++ b/packages/staking/src/features/BrowsePools/StakePoolsTable/StakePoolsTable.tsx @@ -15,7 +15,7 @@ type StakePoolsTableProps = { }; const DEFAULT_SORT_OPTIONS: StakePoolSortOptions = { - field: 'apy', + field: 'ros', order: 'desc', }; From 049fd31b1bc7bf18e7bca07cc44395a3ed280d05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20W=C5=82odek?= Date: Thu, 30 Nov 2023 10:41:03 +0100 Subject: [PATCH 18/68] Revert "Chore/update for sdk conway era (#734)" (#767) This reverts commit 5b81b19b201fea3c4a94948a5bb6199958b8a3c0. --- apps/browser-extension-wallet/src/api/mock.ts | 4 +-- .../src/utils/mocks/test-helpers.tsx | 4 +-- .../StakePoolsTable/StakePoolsTable.tsx | 2 +- .../test/mocks/StakepoolSearchProviderStub.ts | 28 +++++-------------- .../cardano/src/wallet/test/mocks/mock.ts | 4 +-- .../__tests__/stake-pool-transformer.test.ts | 4 +-- .../StakePoolsTable/StakePoolsTable.tsx | 2 +- 7 files changed, 13 insertions(+), 35 deletions(-) diff --git a/apps/browser-extension-wallet/src/api/mock.ts b/apps/browser-extension-wallet/src/api/mock.ts index 54dc9047b8..62dc74e7c7 100644 --- a/apps/browser-extension-wallet/src/api/mock.ts +++ b/apps/browser-extension-wallet/src/api/mock.ts @@ -126,9 +126,7 @@ const getDetailsForAll = (): PoolDetails => ({ livePledge: BigInt('2000000000'), saturation: Percent(0.95), size: undefined, - stake: undefined, - lastRos: Percent(1), - ros: Percent(2) + stake: undefined }, relays: undefined, rewardAccount: Wallet.Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj'), diff --git a/apps/browser-extension-wallet/src/utils/mocks/test-helpers.tsx b/apps/browser-extension-wallet/src/utils/mocks/test-helpers.tsx index ee2bed4e96..ab736b8702 100644 --- a/apps/browser-extension-wallet/src/utils/mocks/test-helpers.tsx +++ b/apps/browser-extension-wallet/src/utils/mocks/test-helpers.tsx @@ -262,9 +262,7 @@ export const cardanoStakePoolMock: Wallet.StakePoolSearchResults = { saturation: Percent(0.0512), stake: undefined, size: undefined, - apy: Percent(0.013), - lastRos: Percent(1), - ros: Percent(2) + apy: Percent(0.013) }, owners: [ Wallet.Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj'), diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/StakePoolsTable/StakePoolsTable.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/StakePoolsTable/StakePoolsTable.tsx index fd10a3e36c..9b671719ad 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/StakePoolsTable/StakePoolsTable.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/StakePoolsTable/StakePoolsTable.tsx @@ -26,7 +26,7 @@ type stakePoolsTableProps = { }; const DEFAULT_SORT_OPTIONS: StakePoolSortOptions = { - field: 'ros', + field: 'apy', order: 'desc' }; diff --git a/packages/cardano/src/wallet/test/mocks/StakepoolSearchProviderStub.ts b/packages/cardano/src/wallet/test/mocks/StakepoolSearchProviderStub.ts index 3a62f7ea9e..abacce350f 100644 --- a/packages/cardano/src/wallet/test/mocks/StakepoolSearchProviderStub.ts +++ b/packages/cardano/src/wallet/test/mocks/StakepoolSearchProviderStub.ts @@ -20,9 +20,7 @@ export const pools: Partial[] = [ livePledge: BigInt('2000000000'), saturation: Percent(0.211), size: undefined, - stake: undefined, - lastRos: Percent(1), - ros: Percent(2) + stake: undefined }, margin: { numerator: 2.01, @@ -60,9 +58,7 @@ export const pools: Partial[] = [ stake: { live: BigInt('201000000'), active: BigInt('201000000') - }, - lastRos: Percent(1), - ros: Percent(2) + } }, metadata: { name: 'THE AMSTERDAM NODE', @@ -96,9 +92,7 @@ export const pools: Partial[] = [ stake: { live: BigInt('77000000'), active: BigInt('77000000') - }, - lastRos: Percent(1), - ros: Percent(2) + } } }, { @@ -120,9 +114,7 @@ export const pools: Partial[] = [ stake: { live: BigInt('34000000'), active: BigInt('34000000') - }, - lastRos: Percent(1), - ros: Percent(2) + } }, metadata: { name: 'stakit.io Pool by TOBG', @@ -152,9 +144,7 @@ export const pools: Partial[] = [ stake: { live: BigInt('53000000'), active: BigInt('53000000') - }, - lastRos: Percent(1), - ros: Percent(2) + } }, margin: { numerator: 0.79, @@ -182,9 +172,7 @@ export const pools: Partial[] = [ stake: { live: BigInt('53000000'), active: BigInt('53000000') - }, - lastRos: Percent(1), - ros: Percent(2) + } }, metadata: { name: 'VEGASPool', @@ -214,9 +202,7 @@ const detailsForAll: PoolDetails = { stake: { live: BigInt('34000000'), active: BigInt('34000000') - }, - lastRos: Percent(1), - ros: Percent(2) + } }, relays: undefined, rewardAccount: Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj'), diff --git a/packages/cardano/src/wallet/test/mocks/mock.ts b/packages/cardano/src/wallet/test/mocks/mock.ts index 023b48fcec..723ea89e24 100644 --- a/packages/cardano/src/wallet/test/mocks/mock.ts +++ b/packages/cardano/src/wallet/test/mocks/mock.ts @@ -32,9 +32,7 @@ export const stakePoolMock: Cardano.StakePool = { livePledge: BigInt('2000000000'), saturation: Percent(0.5), stake: undefined, - size: undefined, - lastRos: Percent(1), - ros: Percent(2) + size: undefined }, owners: [ Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj'), diff --git a/packages/cardano/src/wallet/util/__tests__/stake-pool-transformer.test.ts b/packages/cardano/src/wallet/util/__tests__/stake-pool-transformer.test.ts index a8d343bf96..cd6dfaa23f 100644 --- a/packages/cardano/src/wallet/util/__tests__/stake-pool-transformer.test.ts +++ b/packages/cardano/src/wallet/util/__tests__/stake-pool-transformer.test.ts @@ -40,9 +40,7 @@ const cardanoStakePoolMock: StakePoolSearchResults = { saturation: Percent(0.0512), stake: undefined, size: undefined, - apy: Percent(0.013), - lastRos: Percent(1), - ros: Percent(2) + apy: Percent(0.013) }, owners: [ Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj'), diff --git a/packages/staking/src/features/BrowsePools/StakePoolsTable/StakePoolsTable.tsx b/packages/staking/src/features/BrowsePools/StakePoolsTable/StakePoolsTable.tsx index 390725ac0d..610ceee531 100644 --- a/packages/staking/src/features/BrowsePools/StakePoolsTable/StakePoolsTable.tsx +++ b/packages/staking/src/features/BrowsePools/StakePoolsTable/StakePoolsTable.tsx @@ -15,7 +15,7 @@ type StakePoolsTableProps = { }; const DEFAULT_SORT_OPTIONS: StakePoolSortOptions = { - field: 'ros', + field: 'apy', order: 'desc', }; From 595aafecf4d44dec83de814211914a11bc9ba5ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojtek=20K=C5=82os?= <114915819+wklos-iohk@users.noreply.github.com> Date: Thu, 30 Nov 2023 11:50:05 +0100 Subject: [PATCH 19/68] test(extension): add test ids for governance actions (#765) --- .../ConfirmDRepRegistration.tsx | 18 ++++++++++++----- .../ConfirmDRepRetirement.tsx | 14 ++++++++++--- .../ConfirmDRepUpdate/ConfirmDRepUpdate.tsx | 12 +++++++---- .../ConfirmVoteDelegation.tsx | 20 +++++++++++++++---- .../transaction-summary-address.component.tsx | 14 +++++++++++-- ...transaction-summary-metadata.component.tsx | 13 ++++++++++-- 6 files changed, 71 insertions(+), 20 deletions(-) diff --git a/packages/core/src/ui/components/ConfirmDRepRegistration/ConfirmDRepRegistration.tsx b/packages/core/src/ui/components/ConfirmDRepRegistration/ConfirmDRepRegistration.tsx index 9246cb3c8b..644b457fed 100644 --- a/packages/core/src/ui/components/ConfirmDRepRegistration/ConfirmDRepRegistration.tsx +++ b/packages/core/src/ui/components/ConfirmDRepRegistration/ConfirmDRepRegistration.tsx @@ -34,23 +34,31 @@ export const ConfirmDRepRegistration = ({ dappInfo, errorMessage, translations, )} - + {metadata.url && ( - + )} {metadata.hash && ( - + )} - + - + diff --git a/packages/core/src/ui/components/ConfirmDRepRetirement/ConfirmDRepRetirement.tsx b/packages/core/src/ui/components/ConfirmDRepRetirement/ConfirmDRepRetirement.tsx index ece0e83d6a..59219813a8 100644 --- a/packages/core/src/ui/components/ConfirmDRepRetirement/ConfirmDRepRetirement.tsx +++ b/packages/core/src/ui/components/ConfirmDRepRetirement/ConfirmDRepRetirement.tsx @@ -31,13 +31,21 @@ export const ConfirmDRepRetirement = ({ dappInfo, errorMessage, translations, me )} - + - + - + diff --git a/packages/core/src/ui/components/ConfirmDRepUpdate/ConfirmDRepUpdate.tsx b/packages/core/src/ui/components/ConfirmDRepUpdate/ConfirmDRepUpdate.tsx index d712827f1a..e86ee2b772 100644 --- a/packages/core/src/ui/components/ConfirmDRepUpdate/ConfirmDRepUpdate.tsx +++ b/packages/core/src/ui/components/ConfirmDRepUpdate/ConfirmDRepUpdate.tsx @@ -33,20 +33,24 @@ export const ConfirmDRepUpdate = ({ dappInfo, errorMessage, translations, metada )} - + {metadata.url && ( - + )} {metadata.hash && ( - + )} - + diff --git a/packages/core/src/ui/components/ConfirmVoteDelegation/ConfirmVoteDelegation.tsx b/packages/core/src/ui/components/ConfirmVoteDelegation/ConfirmVoteDelegation.tsx index eebab72b21..4eae2a274b 100644 --- a/packages/core/src/ui/components/ConfirmVoteDelegation/ConfirmVoteDelegation.tsx +++ b/packages/core/src/ui/components/ConfirmVoteDelegation/ConfirmVoteDelegation.tsx @@ -34,21 +34,33 @@ export const ConfirmVoteDelegation = ({ dappInfo, errorMessage, translations, me )} - + {metadata.drepId && ( - + )} {metadata.alwaysAbstain && ( - + )} {metadata.alwaysNoConfidence && ( - + )} diff --git a/packages/ui/src/design-system/transaction-summary/transaction-summary-address.component.tsx b/packages/ui/src/design-system/transaction-summary/transaction-summary-address.component.tsx index 83234cfa01..087402100f 100644 --- a/packages/ui/src/design-system/transaction-summary/transaction-summary-address.component.tsx +++ b/packages/ui/src/design-system/transaction-summary/transaction-summary-address.component.tsx @@ -11,23 +11,33 @@ import type { OmitClassName } from '../../types'; type Props = OmitClassName<'div'> & { label: string; address: string; + testID?: string; }; export const Address = ({ label, address, + testID, ...props }: Readonly): JSX.Element => { return ( - + {label} - {address} + + {address} + diff --git a/packages/ui/src/design-system/transaction-summary/transaction-summary-metadata.component.tsx b/packages/ui/src/design-system/transaction-summary/transaction-summary-metadata.component.tsx index 82d4d0c05f..ccde339e44 100644 --- a/packages/ui/src/design-system/transaction-summary/transaction-summary-metadata.component.tsx +++ b/packages/ui/src/design-system/transaction-summary/transaction-summary-metadata.component.tsx @@ -11,23 +11,32 @@ import type { OmitClassName } from '../../types'; type Props = OmitClassName<'div'> & { label: string; text: string; + testID?: string; }; export const Metadata = ({ label, text, + testID, ...props }: Readonly): JSX.Element => { return ( - + {label} - + {text} From 1bc62f021227ab01cc4cba8a96953cbbbdbf655b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20W=C5=82odek?= Date: Fri, 1 Dec 2023 12:32:57 +0100 Subject: [PATCH 20/68] Merge main into feat/sanchonet (#763) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(staking): maintenance 24 Nov 2023 (#754) * feat(staking): maintenance 24 Nov 2023 * [LW-9151] Edit account name component (#746) * feat(ui): input component * feat(extension): edit account drawer * feat(extension): add unit test * feat: rename input component * feat(ui): use pseudo parameters * feat(ui): input width * feat(ui): input disabled style * feat(extension): add ui package * feat: update lock file * feat(ui): remove redundant styles * feat(extension): [LW-5806] update Minting transaction display from dapps (#608) * feat: identify minting txs from dapps and display proper UI to confirm refs: LW-5806 * feat(extension): update dapp transaction styling * refactor(extension,core): fix sonarcloud quality checks * ci(core): add @lace/ui to dependencies * test(extension): add mocks in ConfirmTransaction.test.tsx * test(extension): remove leftover from local testing * refactor(core): add missing newline at end of file --------- Co-authored-by: przemyslaw.wlodek * test(extension): maintenance 27 nov (#757) * test(extension): maintenance nov 27 * test(extension): clear failed request test logs * test(extension): add real time console test logging, remove unnecessary logs * test(extension): update wdio version * test(extension): add node option env variable for e2e github runs * test(extension): move node options to e2e test sections * test(extension): increase max-old-space-size * test(extension): downgrade wdio to stable version, remove node env variable * feat(staking): [LW-6437, LW-8877] past epochs rewards chart (#718) --------- Co-authored-by: refi93 Co-authored-by: przemyslaw.wlodek Co-authored-by: januszjanus * feat(extension): track manual re-sync and hd wallet discovery (#712) * feat(staking): [LW-8929, LW-8777] apply pool search also to selected pools (#706) --------- Co-authored-by: refi93 * feat(staking,common): enable manageDelegation CTA from pool details (#720) Co-authored-by: przemyslaw.wlodek * feat(staking): load multi-delegation for HW wallets (#686) --------- Signed-off-by: Kamil Džurman Co-authored-by: Rafael Korbaš Co-authored-by: januszjanus Co-authored-by: przemyslaw.wlodek * test(extension): test maintenance 29 Nov - fix Dapp e2e (#760) * feat(extension): [LW-9028] Add wallet concepts to menu (#742) * feat(extension): update dropdown menu with new wallet component * feat(extension): add add new wallet option to profile dropdown * refactor(ui): add test id * refactor(extension): add test id for multi wallet * refactor(extension): increase button width to match figma * refactor(extension): remove copy address tooltip from multi-wallet menu it em * refactor(extension): align top navigation buttons with side panel * refactor(extension): update the expand button styling * refactor(extension): update network pill and expand button styling * refactor(extension): update network pill border radius * refactor(extension): limit network pill change to popup only * fix(extension): fix moving left side bar * refactor(extension,core,cardano): post-merge fixes * chore(extension): fix unit tests + rename * chore(extension): fix unit tests and mocks * chore(extension): remove TODO comments * chore(core): review comments --------- Signed-off-by: Kamil Džurman Co-authored-by: Janusz Janus Co-authored-by: Lucas Co-authored-by: Leonel Gobbi <57540576+lgobbi-atix@users.noreply.github.com> Co-authored-by: bslabiak <112852128+bslabiak@users.noreply.github.com> Co-authored-by: Tomek Marciniak <16132011+mrcnk@users.noreply.github.com> Co-authored-by: refi93 Co-authored-by: Kamil Džurman Co-authored-by: Lukasz Jagiela <12641433+ljagiela@users.noreply.github.com> Co-authored-by: Renan Valentin --- apps/browser-extension-wallet/.env.defaults | 4 +- apps/browser-extension-wallet/.env.example | 3 +- .../DropdownMenu/DropdownMenu.module.scss | 4 + .../components/DropdownMenu/DropdownMenu.tsx | 50 ++- .../ExpandButton/ExpandButton.module.scss | 15 + .../components/ExpandButton/ExpandButton.tsx | 30 +- .../DropdownMenuOverlay.module.scss | 9 +- .../DropdownMenuOverlay.tsx | 4 +- .../components/AddNewWalletLink.tsx | 20 + .../components/UserInfo.tsx | 67 ++- .../DropdownMenuOverlay/components/index.ts | 1 + .../MainMenu/MainHeader.module.scss | 9 + .../src/components/MainMenu/MainHeader.tsx | 14 +- .../NetworkPill/NetworkPill.module.scss | 7 + .../components/NetworkPill/NetworkPill.tsx | 26 +- .../EditAccount/EditAccountDrawer.test.tsx | 57 +++ .../EditAccount/EditAccountDrawer.tsx | 60 +++ .../account/components/EditAccount/hooks.ts | 11 + .../ConfirmTransaction.tsx | 23 +- .../ConfirmTransactionContent.tsx | 14 +- .../DappTransactionContainer.tsx | 29 +- .../__tests__/ConfirmTransaction.test.tsx | 4 +- .../ConfirmTransactionContent.test.tsx | 27 +- .../DappTransactionContainer.test.tsx | 41 +- .../__tests__/hooks.test.tsx | 21 +- .../__tests__/utils.test.tsx | 29 +- .../components/confirm-transaction/hooks.ts | 126 +++++- .../components/confirm-transaction/utils.ts | 41 +- .../src/hooks/useMultiDelegationEnabled.ts | 16 +- .../src/lib/translations/en.json | 23 + .../src/providers/index.ts | 1 + .../src/routes/wallet-paths.ts | 6 + .../stores/slices/activity-detail-slice.ts | 17 +- .../src/utils/mocks/tx.ts | 6 + .../Layout/SectionLayout.modules.scss | 16 + .../components/Layout/SidePanel.tsx | 16 +- .../LeftSidePanel/LeftSidePanel.module.scss | 6 + .../LeftSidePanel/LeftSidePanel.tsx | 6 +- .../SendReceiveBox/SendReceiveBox.module.scss | 3 +- .../components/SettingsWalletBase.tsx | 16 +- .../components/HardwareWalletFlow.tsx | 9 + .../components/WalletSetupWizard.tsx | 10 + .../src/views/browser-view/index.tsx | 23 +- packages/cardano/src/wallet/types.ts | 27 +- packages/common/src/analytics/types.ts | 4 + .../TransactionDetails.module.scss | 25 +- .../ActivityDetail/TransactionDetails.tsx | 39 +- .../ActivityDetail/TransactionFee.module.scss | 109 +++++ .../ActivityDetail/TransactionFee.tsx | 42 ++ .../src/ui/components/ActivityDetail/index.ts | 1 + .../DappTransaction.module.scss | 96 +---- .../DappTransaction/DappTransaction.tsx | 141 +++--- .../DappTxAsset/DappTxAsset.module.scss | 41 ++ .../DappTxAsset/DappTxAsset.tsx | 23 + .../DappTransaction/DappTxAsset/index.ts | 1 + .../DappTxHeader/DappTxHeader.module.scss | 27 ++ .../DappTxHeader/DappTxHeader.tsx | 20 + .../DappTxOutput/DappTxOutput.module.scss | 67 +++ .../DappTxOutput/DappTxOutput.tsx | 42 ++ packages/core/src/ui/lib/translations/en.json | 12 + packages/e2e-tests/package.json | 18 +- .../src/assert/dAppConnectorAssert.ts | 10 +- .../dappConnector/confirmTransactionPage.ts | 20 +- .../src/features/EmptyStatesExtended.feature | 1 - .../src/features/EmptyStatesPopup.feature | 1 - .../src/features/FullExperiencePopup.feature | 1 - .../MultiDelegationPageExtended.feature | 12 - .../features/MultiDelegationPagePopup.feature | 1 - ...tidelegationDelegatedFundsExtended.feature | 2 - ...MultidelegationDelegatedFundsPopup.feature | 11 +- .../features/NavigationMainExtended.feature | 15 +- .../src/features/NavigationMainPopup.feature | 1 - .../analytics/AnalyticsSendExtended.feature | 3 +- ...elegationSwitchingPoolsExtendedE2E.feature | 1 - .../e2e-tests/src/hooks/beforeTagHooks.ts | 63 +-- .../src/pageobject/dAppConnectorPageObject.ts | 2 +- .../e2e-tests/src/utils/networkManager.ts | 9 +- packages/e2e-tests/src/utils/utils.ts | 1 - packages/e2e-tests/wdio.conf.base.ts | 7 +- packages/staking/package.json | 3 + .../StakePoolsTable/StakePoolsTable.tsx | 26 +- .../src/features/Drawer/StakePoolDetail.tsx | 25 +- .../Drawer/StakePoolDetailsDrawer.tsx | 23 +- .../src/features/Drawer/TransactionFail.tsx | 4 +- .../StakePoolConfirmationFooter.tsx | 59 +-- .../src/features/activity/Activity.tsx | 12 +- .../activity/EpochsSwitch.module.scss | 4 + .../src/features/activity/EpochsSwitch.tsx | 33 ++ .../PastEpochsRewards/PastEpochsRewards.tsx | 23 + .../PastEpochsRewards/PoolIndicator.tsx | 7 + .../PastEpochsRewards/RewardsChart.tsx | 41 ++ .../PastEpochsRewards/RewardsChartTooltip.tsx | 45 ++ .../hooks/useRewardsByEpoch.ts | 82 ++++ .../hooks/useRewardsChartPoolsColorMapper.tsx | 47 ++ .../activity/PastEpochsRewards/index.ts | 1 + .../src/features/activity/RewardsHistory.tsx | 2 +- .../src/features/i18n/translations/en.ts | 6 + packages/staking/src/features/i18n/types.ts | 8 + .../stateMachine/commands.ts | 37 +- .../stateMachine/processExpandedViewCases.ts | 86 ++-- .../stateMachine/types.ts | 3 + packages/ui/src/design-system/index.ts | 1 + .../profile-dropdown-trigger.component.tsx | 1 + ...profile-dropdown-wallet-card.component.tsx | 18 +- ...profile-dropdown-wallet-icon.component.tsx | 5 +- ...ofile-dropdown-wallet-option.component.tsx | 8 + .../ui/src/design-system/text-box/index.ts | 2 + .../text-box/text-box.component.tsx | 72 ++++ .../design-system/text-box/text-box.css.ts | 57 +++ .../text-box/text-box.stories.tsx | 96 +++++ yarn.lock | 402 ++++++++++++------ 111 files changed, 2349 insertions(+), 706 deletions(-) create mode 100644 apps/browser-extension-wallet/src/components/MainMenu/DropdownMenuOverlay/components/AddNewWalletLink.tsx create mode 100644 apps/browser-extension-wallet/src/features/account/components/EditAccount/EditAccountDrawer.test.tsx create mode 100644 apps/browser-extension-wallet/src/features/account/components/EditAccount/EditAccountDrawer.tsx create mode 100644 apps/browser-extension-wallet/src/features/account/components/EditAccount/hooks.ts create mode 100644 packages/core/src/ui/components/ActivityDetail/TransactionFee.module.scss create mode 100644 packages/core/src/ui/components/ActivityDetail/TransactionFee.tsx create mode 100644 packages/core/src/ui/components/DappTransaction/DappTxAsset/DappTxAsset.module.scss create mode 100644 packages/core/src/ui/components/DappTransaction/DappTxAsset/DappTxAsset.tsx create mode 100644 packages/core/src/ui/components/DappTransaction/DappTxAsset/index.ts create mode 100644 packages/core/src/ui/components/DappTransaction/DappTxHeader/DappTxHeader.module.scss create mode 100644 packages/core/src/ui/components/DappTransaction/DappTxHeader/DappTxHeader.tsx create mode 100644 packages/core/src/ui/components/DappTransaction/DappTxOutput/DappTxOutput.module.scss create mode 100644 packages/core/src/ui/components/DappTransaction/DappTxOutput/DappTxOutput.tsx create mode 100644 packages/staking/src/features/activity/EpochsSwitch.module.scss create mode 100644 packages/staking/src/features/activity/EpochsSwitch.tsx create mode 100644 packages/staking/src/features/activity/PastEpochsRewards/PastEpochsRewards.tsx create mode 100644 packages/staking/src/features/activity/PastEpochsRewards/PoolIndicator.tsx create mode 100644 packages/staking/src/features/activity/PastEpochsRewards/RewardsChart.tsx create mode 100644 packages/staking/src/features/activity/PastEpochsRewards/RewardsChartTooltip.tsx create mode 100644 packages/staking/src/features/activity/PastEpochsRewards/hooks/useRewardsByEpoch.ts create mode 100644 packages/staking/src/features/activity/PastEpochsRewards/hooks/useRewardsChartPoolsColorMapper.tsx create mode 100644 packages/staking/src/features/activity/PastEpochsRewards/index.ts create mode 100644 packages/ui/src/design-system/text-box/index.ts create mode 100644 packages/ui/src/design-system/text-box/text-box.component.tsx create mode 100644 packages/ui/src/design-system/text-box/text-box.css.ts create mode 100644 packages/ui/src/design-system/text-box/text-box.stories.tsx diff --git a/apps/browser-extension-wallet/.env.defaults b/apps/browser-extension-wallet/.env.defaults index f7da959487..006147c923 100644 --- a/apps/browser-extension-wallet/.env.defaults +++ b/apps/browser-extension-wallet/.env.defaults @@ -18,7 +18,8 @@ USE_DIFFERENT_MNEMONIC_LENGTHS=true USE_NFT_FOLDERS=true USE_MULTI_CURRENCY=true USE_HIDE_MY_BALANCE=true -USE_MULTI_DELEGATION_STAKING=true +USE_MULTI_DELEGATION_STAKING_LEDGER=false +USE_MULTI_DELEGATION_STAKING_TREZOR=false USE_ADA_HANDLE=true USE_DATA_CHECK=false USE_POSTHOG_ANALYTICS=true @@ -27,6 +28,7 @@ USE_MULTI_DELEGATION_STAKING_ACTIVITY=false USE_POSTHOG_ANALYTICS_FOR_OPTED_OUT=false USE_MATOMO_ANALYTICS_FOR_OPTED_OUT=false +USE_MULTI_WALLET=false # In App URLs CATALYST_GOOGLE_PLAY_URL=https://play.google.com/store/apps/details?id=io.iohk.vitvoting diff --git a/apps/browser-extension-wallet/.env.example b/apps/browser-extension-wallet/.env.example index 1f7505ca91..ae66e0ab8a 100644 --- a/apps/browser-extension-wallet/.env.example +++ b/apps/browser-extension-wallet/.env.example @@ -18,7 +18,8 @@ USE_DIFFERENT_MNEMONIC_LENGTHS=true USE_NFT_FOLDERS=true USE_MULTI_CURRENCY=true USE_HIDE_MY_BALANCE=true -USE_MULTI_DELEGATION_STAKING=true +USE_MULTI_DELEGATION_STAKING_LEDGER=false +USE_MULTI_DELEGATION_STAKING_TREZOR=false USE_ADA_HANDLE=true USE_HANDLE_AB=false USE_DATA_CHECK=false diff --git a/apps/browser-extension-wallet/src/components/DropdownMenu/DropdownMenu.module.scss b/apps/browser-extension-wallet/src/components/DropdownMenu/DropdownMenu.module.scss index 7d8322a7b9..5dbc2da419 100644 --- a/apps/browser-extension-wallet/src/components/DropdownMenu/DropdownMenu.module.scss +++ b/apps/browser-extension-wallet/src/components/DropdownMenu/DropdownMenu.module.scss @@ -45,3 +45,7 @@ transform: rotateX(180deg); } } + +.profileDropdownTrigger { + flex-shrink: 0; +} diff --git a/apps/browser-extension-wallet/src/components/DropdownMenu/DropdownMenu.tsx b/apps/browser-extension-wallet/src/components/DropdownMenu/DropdownMenu.tsx index f1d754cbb3..a91c2c635d 100644 --- a/apps/browser-extension-wallet/src/components/DropdownMenu/DropdownMenu.tsx +++ b/apps/browser-extension-wallet/src/components/DropdownMenu/DropdownMenu.tsx @@ -11,6 +11,9 @@ import { useWalletStore } from '@src/stores'; import { UserAvatar } from '../MainMenu/DropdownMenuOverlay/components'; import { useAnalyticsContext } from '@providers'; import { PostHogAction } from '@providers/AnalyticsProvider/analyticsTracker'; +import { ProfileDropdown } from '@lace/ui'; +import { useGetHandles } from '@hooks'; +import { getAssetImageUrl } from '@src/utils/get-asset-image-url'; export interface DropdownMenuProps { isPopup?: boolean; @@ -20,6 +23,8 @@ export const DropdownMenu = ({ isPopup }: DropdownMenuProps): React.ReactElement const analytics = useAnalyticsContext(); const { walletInfo } = useWalletStore(); const [open, setOpen] = useState(false); + const [handle] = useGetHandles(); + const handleImage = handle?.profilePic; const Chevron = isPopup ? ChevronSmall : ChevronNormal; const sendAnalyticsEvent = (event: PostHogAction) => { @@ -41,20 +46,39 @@ export const DropdownMenu = ({ isPopup }: DropdownMenuProps): React.ReactElement placement="bottomRight" trigger={['click']} > - +
+ ) : ( + + )} ); }; diff --git a/apps/browser-extension-wallet/src/components/ExpandButton/ExpandButton.module.scss b/apps/browser-extension-wallet/src/components/ExpandButton/ExpandButton.module.scss index dfa9d4f4f9..e16f9a4a5f 100644 --- a/apps/browser-extension-wallet/src/components/ExpandButton/ExpandButton.module.scss +++ b/apps/browser-extension-wallet/src/components/ExpandButton/ExpandButton.module.scss @@ -36,6 +36,21 @@ } } +.multiWallet { + max-width: size_unit(6); + width: size_unit(6); + height: size_unit(6); + border-radius: size_unit(2); + flex-shrink: 0; + + &:hover { + max-width: size_unit(6); + width: size_unit(6); + gap: 0px; + padding: 0; + } +} + .text { color: var(--text-color-secondary); white-space: nowrap; diff --git a/apps/browser-extension-wallet/src/components/ExpandButton/ExpandButton.tsx b/apps/browser-extension-wallet/src/components/ExpandButton/ExpandButton.tsx index 1e7da942e1..5abafd532a 100644 --- a/apps/browser-extension-wallet/src/components/ExpandButton/ExpandButton.tsx +++ b/apps/browser-extension-wallet/src/components/ExpandButton/ExpandButton.tsx @@ -1,11 +1,31 @@ -import React from 'react'; +/* eslint-disable react/no-multi-comp */ +import React, { ReactNode } from 'react'; import ExpandIcon from '../../assets/icons/expand.component.svg'; +import classnames from 'classnames'; +import { Tooltip } from 'antd'; import styles from './ExpandButton.module.scss'; +const RenderTooltipIfMultiWallet = ({ children, label }: { children: ReactNode; label: string }) => { + if (process.env.USE_MULTI_WALLET === 'true') { + return {children}; + } + + return <>{children}; +}; + export const ExpandButton = ({ label, onClick }: { label: string; onClick: () => void }): React.ReactElement => ( - - - {label} - + + + + {process.env.USE_MULTI_WALLET !== 'true' && {label}} + + ); diff --git a/apps/browser-extension-wallet/src/components/MainMenu/DropdownMenuOverlay/DropdownMenuOverlay.module.scss b/apps/browser-extension-wallet/src/components/MainMenu/DropdownMenuOverlay/DropdownMenuOverlay.module.scss index 098024b802..e43bee1f1f 100644 --- a/apps/browser-extension-wallet/src/components/MainMenu/DropdownMenuOverlay/DropdownMenuOverlay.module.scss +++ b/apps/browser-extension-wallet/src/components/MainMenu/DropdownMenuOverlay/DropdownMenuOverlay.module.scss @@ -17,7 +17,6 @@ .userInfoWrapper { display: flex; flex-direction: column; - padding: 10px size_unit(2) size_unit(2.75); gap: size_unit(2); .userInfo { @@ -50,6 +49,14 @@ } } + .singleWalletWrapper { + padding: 10px size_unit(2) size_unit(2.75); + } + + .multiWalletWrapper { + padding-bottom: size_unit(2.75); + } + .walletStatusInfo { cursor: default; display: flex; 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 da8edbfe45..b48c5e9270 100644 --- a/apps/browser-extension-wallet/src/components/MainMenu/DropdownMenuOverlay/DropdownMenuOverlay.tsx +++ b/apps/browser-extension-wallet/src/components/MainMenu/DropdownMenuOverlay/DropdownMenuOverlay.tsx @@ -8,7 +8,8 @@ import { ThemeSwitcher, LockWallet, UserInfo, - NetworkChoise + NetworkChoise, + AddNewWalletLink } from './components'; import styles from './DropdownMenuOverlay.module.scss'; import { NetworkInfo } from './components/NetworkInfo'; @@ -42,6 +43,7 @@ export const DropdownMenuOverlay: VFC = ({ <> {topSection} + {process.env.USE_MULTI_WALLET === 'true' && } diff --git a/apps/browser-extension-wallet/src/components/MainMenu/DropdownMenuOverlay/components/AddNewWalletLink.tsx b/apps/browser-extension-wallet/src/components/MainMenu/DropdownMenuOverlay/components/AddNewWalletLink.tsx new file mode 100644 index 0000000000..281fa80ea5 --- /dev/null +++ b/apps/browser-extension-wallet/src/components/MainMenu/DropdownMenuOverlay/components/AddNewWalletLink.tsx @@ -0,0 +1,20 @@ +import React from 'react'; +import { walletRoutePaths } from '@routes'; +import { Menu } from 'antd'; +import { useTranslation } from 'react-i18next'; +import { Link } from 'react-router-dom'; +import styles from '../DropdownMenuOverlay.module.scss'; + +const handleOnClicked = (): void => void 0; + +export const AddNewWalletLink = (): React.ReactElement => { + const { t } = useTranslation(); + + return ( + + + {t('browserView.sideMenu.links.addNewWallet')} + + + ); +}; 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 801c51ae89..e36d810bd7 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 @@ -11,6 +11,8 @@ import { UserAvatar } from './UserAvatar'; import { useGetHandles } 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'; const ADRESS_FIRST_PART_LENGTH = 10; const ADRESS_LAST_PART_LENGTH = 5; @@ -36,6 +38,7 @@ export const UserInfo = ({ avatarVisible = true }: UserInfoProps): React.ReactEl const walletName = addEllipsis(walletInfo.name.toString(), 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') }); @@ -44,29 +47,51 @@ export const UserInfo = ({ avatarVisible = true }: UserInfoProps): React.ReactEl return ( -
+
- - {handleName ? t('settings.copyHandle') : t('settings.copyAddress')} - - } - > -
- {avatarVisible && } -
-

- {walletName} -

-

- {handleName || shortenedWalletAddress} -

+ {process.env.USE_MULTI_WALLET === 'true' ? ( + + ) : ( + + {handleName ? t('settings.copyHandle') : t('settings.copyAddress')} + + } + > +
+ {avatarVisible && } +
+

+ {walletName} +

+

+ {handleName || shortenedWalletAddress} +

+
-
- + + )}
diff --git a/apps/browser-extension-wallet/src/components/MainMenu/DropdownMenuOverlay/components/index.ts b/apps/browser-extension-wallet/src/components/MainMenu/DropdownMenuOverlay/components/index.ts index 040f0a990c..5382f3cf6f 100644 --- a/apps/browser-extension-wallet/src/components/MainMenu/DropdownMenuOverlay/components/index.ts +++ b/apps/browser-extension-wallet/src/components/MainMenu/DropdownMenuOverlay/components/index.ts @@ -8,3 +8,4 @@ export * from './UserInfo'; export * from './UserAvatar'; export * from './NetworkChoise'; export * from './NetworkInfo'; +export * from './AddNewWalletLink'; diff --git a/apps/browser-extension-wallet/src/components/MainMenu/MainHeader.module.scss b/apps/browser-extension-wallet/src/components/MainMenu/MainHeader.module.scss index cb96dbc1c8..853218748c 100644 --- a/apps/browser-extension-wallet/src/components/MainMenu/MainHeader.module.scss +++ b/apps/browser-extension-wallet/src/components/MainMenu/MainHeader.module.scss @@ -36,3 +36,12 @@ } } } + +.multiWallet { + flex-wrap: wrap; +} + +.multiWalletNetworkPillBox { + width: 100%; + margin-bottom: 12px; +} diff --git a/apps/browser-extension-wallet/src/components/MainMenu/MainHeader.tsx b/apps/browser-extension-wallet/src/components/MainMenu/MainHeader.tsx index 734aad4c85..ac2c66bebf 100644 --- a/apps/browser-extension-wallet/src/components/MainMenu/MainHeader.tsx +++ b/apps/browser-extension-wallet/src/components/MainMenu/MainHeader.tsx @@ -4,6 +4,7 @@ import styles from './MainHeader.module.scss'; import LaceLogoMark from '../../assets/branding/lace-logo-mark.component.svg'; import { useTranslation } from 'react-i18next'; import { walletRoutePaths } from '@routes'; +import classNames from 'classnames'; import { DropdownMenu } from '@components/DropdownMenu'; import { ExpandButton } from '@components/ExpandButton'; @@ -30,7 +31,16 @@ export const MainHeader = (): React.ReactElement => { return (
-
+
+ {process.env.USE_MULTI_WALLET === 'true' && ( +
+ +
+ )} { onClick={() => analytics.sendEventToPostHog(PostHogAction.WalletLaceClick)} > - + {process.env.USE_MULTI_WALLET !== 'true' && }
{ +export const NetworkPill = ({ isExpandable, isPopup = false }: NetworkPillProp): ReactElement => { const { environmentName } = useWalletStore(); const { t } = useTranslation(); const { isOnline, isBackendFailing } = useNetwork(); @@ -19,10 +20,19 @@ export const NetworkPill = ({ isExpandable }: NetworkPillProp): ReactElement => if (isOnline && !isBackendFailing && environmentName !== 'Mainnet') { return (
- {environmentName} + + {environmentName} +
); } @@ -30,7 +40,10 @@ export const NetworkPill = ({ isExpandable }: NetworkPillProp): ReactElement => return (
@@ -45,7 +58,10 @@ export const NetworkPill = ({ isExpandable }: NetworkPillProp): ReactElement => return (
diff --git a/apps/browser-extension-wallet/src/features/account/components/EditAccount/EditAccountDrawer.test.tsx b/apps/browser-extension-wallet/src/features/account/components/EditAccount/EditAccountDrawer.test.tsx new file mode 100644 index 0000000000..7f06c9d6d2 --- /dev/null +++ b/apps/browser-extension-wallet/src/features/account/components/EditAccount/EditAccountDrawer.test.tsx @@ -0,0 +1,57 @@ +import React from 'react'; +import { render, fireEvent, screen } from '@testing-library/react'; +import { EditAccountDrawer } from './EditAccountDrawer'; +import '@testing-library/jest-dom'; + +jest.mock('react-i18next', () => ({ + useTranslation: () => ({ t: jest.fn() }) +})); + +describe('EditAccountDrawer', () => { + const onSaveMock = jest.fn(); + const hideMock = jest.fn(); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('displays default account name', () => { + render(); + + expect(screen.getByTestId('edit-account')).toBeInTheDocument(); + expect(screen.getByTestId('drawer-navigation-title')).toHaveTextContent('Account #1'); + expect(screen.getByTestId('edit-account-name-input')).toHaveValue(''); + expect(screen.getByTestId('edit-account-save-btn')).toBeDisabled(); + }); + + it('displays correct account name', () => { + render(); + + expect(screen.getByTestId('edit-account')).toBeInTheDocument(); + expect(screen.getByTestId('drawer-navigation-title')).toHaveTextContent('Test Account'); + expect(screen.getByTestId('edit-account-name-input')).toHaveValue('Test Account'); + expect(screen.getByTestId('edit-account-save-btn')).toBeDisabled(); + }); + + it('updates input value on change and enables save button', () => { + render(); + + const input = screen.getByTestId('edit-account-name-input'); + + fireEvent.change(input, { target: { value: 'New Account Name' } }); + fireEvent.click(screen.getByTestId('edit-account-save-btn')); + + expect(input).toHaveValue('New Account Name'); + expect(screen.getByTestId('drawer-navigation-title')).toHaveTextContent('Test Account'); + expect(screen.getByTestId('edit-account-save-btn')).toBeEnabled(); + expect(onSaveMock).toHaveBeenCalledWith('New Account Name'); + }); + + it('calls hide function when Cancel button is clicked', () => { + render(); + + fireEvent.click(screen.getByTestId('edit-account-cancel-btn')); + + expect(hideMock).toHaveBeenCalled(); + }); +}); diff --git a/apps/browser-extension-wallet/src/features/account/components/EditAccount/EditAccountDrawer.tsx b/apps/browser-extension-wallet/src/features/account/components/EditAccount/EditAccountDrawer.tsx new file mode 100644 index 0000000000..1e1f27b568 --- /dev/null +++ b/apps/browser-extension-wallet/src/features/account/components/EditAccount/EditAccountDrawer.tsx @@ -0,0 +1,60 @@ +import React, { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Box, Flex, Button, Text, TextBox } from '@lace/ui'; +import { Drawer, DrawerNavigation } from '@lace/common'; + +export type Props = { + onSave: (name: string) => void; + visible: boolean; + hide: () => void; + name: string; + index: string; +}; + +export const EditAccountDrawer = ({ name, index, visible, onSave, hide }: Props): React.ReactElement => { + const { t } = useTranslation(); + const [currentName, setCurrentName] = useState(name); + + return ( + } + footer={ + + + onSave(currentName)} + data-testid="edit-account-save-btn" + label={t('account.edit.footer.save')} + /> + + + + } + > +
+ + {t('account.edit.title')} + + + {t('account.edit.subtitle')} + + setCurrentName(e.target.value)} + /> +
+
+ ); +}; diff --git a/apps/browser-extension-wallet/src/features/account/components/EditAccount/hooks.ts b/apps/browser-extension-wallet/src/features/account/components/EditAccount/hooks.ts new file mode 100644 index 0000000000..f17ee3d5f8 --- /dev/null +++ b/apps/browser-extension-wallet/src/features/account/components/EditAccount/hooks.ts @@ -0,0 +1,11 @@ +import { useState } from 'react'; + +export const useEditAccountDrawer = (): { isOpen: boolean; open: () => void; hide: () => void } => { + const [visible, setVisible] = useState(false); + + return { + isOpen: visible, + open: () => setVisible(true), + hide: () => setVisible(false) + }; +}; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.tsx index ba6ea45711..f90eea8d72 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.tsx @@ -1,6 +1,6 @@ /* eslint-disable no-console */ import React, { useMemo } from 'react'; -import { Button } from '@lace/common'; +import { Button, PostHogAction } from '@lace/common'; import { useTranslation } from 'react-i18next'; import { Layout } from '../Layout'; import { useViewsFlowContext } from '@providers/ViewFlowProvider'; @@ -14,6 +14,9 @@ import { DAPP_CHANNELS } from '@src/utils/constants'; import { runtime } from 'webextension-polyfill'; import { getTitleKey, getTxType } from './utils'; import { ConfirmTransactionContent } from './ConfirmTransactionContent'; +import { TX_CREATION_TYPE_KEY, TxCreationType } from '@providers/AnalyticsProvider/analyticsTracker'; +import { txSubmitted$ } from '@providers/AnalyticsProvider/onChain'; +import { useAnalyticsContext } from '@providers'; export const ConfirmTransaction = (): React.ReactElement => { const { t } = useTranslation(); @@ -34,6 +37,7 @@ export const ConfirmTransaction = (): React.ReactElement => { [] ); const { getKeyAgentType } = useWalletStore(); + const analytics = useAnalyticsContext(); const { signTxData, errorMessage } = useSignTxData(dappDataApi.getSignTxData); const keyAgentType = getKeyAgentType(); const isUsingHardwareWallet = keyAgentType !== Wallet.KeyManagement.KeyAgentType.InMemory; @@ -41,6 +45,19 @@ export const ConfirmTransaction = (): React.ReactElement => { const { isConfirmingTx, signWithHardwareWallet } = useSignWithHardwareWallet(); const txType = signTxData ? getTxType(signTxData.tx) : undefined; const title = txType ? t(getTitleKey(txType)) : ''; + const onConfirm = () => { + analytics.sendEventToPostHog(PostHogAction.SendTransactionSummaryConfirmClick, { + [TX_CREATION_TYPE_KEY]: TxCreationType.External + }); + + txSubmitted$.next({ + id: signTxData.tx?.id.toString(), + date: new Date().toString(), + creationType: TxCreationType.External + }); + + isUsingHardwareWallet ? signWithHardwareWallet() : setNextView(); + }; useOnBeforeUnload(disallowSignTx); @@ -49,9 +66,7 @@ export const ConfirmTransaction = (): React.ReactElement => {
- {fee && fee !== '-' && ( -
-
-
{t('package.core.activityDetails.transactionFee')}
- - {Info ? ( - - ) : ( - - )} - -
- -
-
- {`${fee} ${coinSymbol}`} - - {amountTransformer(fee)} - -
-
-
+ + + )} - {deposit && renderDepositValueSection({ value: deposit, label: t('package.core.activityDetails.deposit') })} {depositReclaim && renderDepositValueSection({ diff --git a/packages/core/src/ui/components/ActivityDetail/TransactionFee.module.scss b/packages/core/src/ui/components/ActivityDetail/TransactionFee.module.scss new file mode 100644 index 0000000000..7ba4758637 --- /dev/null +++ b/packages/core/src/ui/components/ActivityDetail/TransactionFee.module.scss @@ -0,0 +1,109 @@ +@import '../../styles/theme.scss'; +@import '../../../../../common/src/ui/styles/abstracts/typography'; + +.txFeeContainer { + display: flex; + align-items: center; + justify-content: center; + gap: size_unit(1); +} + +.txfee { + color: var(--text-color-primary); + font-size: var(--body, 16px); + font-weight: 600; + line-height: size_unit(3); +} + +.details { + color: var(--text-color-primary, #ffffff); + display: flex; + justify-content: space-between; + align-items: flex-start; + width: 100%; + + .title { + display: flex; + flex: 0 0 50%; + align-self: baseline; + color: var(--text-color-primary, #ffffff); + @include text-body-semi-bold; + } + + .detail { + align-items: end; + display: flex; + flex-direction: column; + gap: size_unit(2); + color: var(--text-color-primary, #ffffff); + text-align: right; + word-break: break-all; + @include text-body-medium; + + @media (max-width: $breakpoint-popup) { + flex-direction: column; + } + + &.hash { + @include text-address; + font-weight: 500; + text-align: right; + cursor: pointer; + } + &.txLink { + color: var(--text-color-blue, #3489f7); + line-height: 17px; + @media (max-width: $breakpoint-popup) { + flex: 60%; + } + } + &.poolId { + color: var(--text-color-secondary); + font-size: var(--bodySmall); + font-weight: 500; + line-height: 17px; + } + } + + .timestamp { + flex: 0 0 35%; + } + + .amount { + display: flex; + flex-direction: column; + width: 100%; + align-items: flex-end; + + .ada { + color: var(--text-color-primary, #ffffff); + } + + .fiat { + color: var(--text-color-secondary, #878e9e); + } + + .addrName { + margin-bottom: size_unit(1); + } + } + + .addressDetail { + font-size: var(--bodySmall, 14px); + font-weight: 400; + line-height: size_unit(2); + text-align: right; + margin-bottom: size_unit(5); + @media (max-width: $breakpoint-popup) { + margin-bottom: size_unit(6); + } + } + + .metadataLabel { + display: flex; + flex: 0 0 50%; + align-self: baseline; + @include text-bodyLarge-bold; + color: var(--text-color-primary); + } +} diff --git a/packages/core/src/ui/components/ActivityDetail/TransactionFee.tsx b/packages/core/src/ui/components/ActivityDetail/TransactionFee.tsx new file mode 100644 index 0000000000..ba4f3fd62b --- /dev/null +++ b/packages/core/src/ui/components/ActivityDetail/TransactionFee.tsx @@ -0,0 +1,42 @@ +/* eslint-disable no-magic-numbers */ +import React from 'react'; +import { InfoCircleOutlined } from '@ant-design/icons'; +import { Tooltip } from 'antd'; +import styles from './TransactionFee.module.scss'; +import { ReactComponent as Info } from '../../assets/icons/info-icon.component.svg'; +import { useTranslate } from '@src/ui/hooks'; + +export interface TransactionFeeProps { + fee: string; + amountTransformer: (amount: string) => string; + coinSymbol: string; +} +export const TransactionFee = ({ fee, amountTransformer, coinSymbol }: TransactionFeeProps): React.ReactElement => { + const { t } = useTranslate(); + + return ( +
+
+
+ {t('package.core.activityDetails.transactionFee')} +
+ + {Info ? ( + + ) : ( + + )} + +
+ +
+
+ {`${fee} ${coinSymbol}`} + + {amountTransformer(fee)} + +
+
+
+ ); +}; diff --git a/packages/core/src/ui/components/ActivityDetail/index.ts b/packages/core/src/ui/components/ActivityDetail/index.ts index 26ccb7a4b5..7ba95eea83 100644 --- a/packages/core/src/ui/components/ActivityDetail/index.ts +++ b/packages/core/src/ui/components/ActivityDetail/index.ts @@ -3,3 +3,4 @@ export * from './RewardsDetails'; export * from './ActivityTypeIcon'; export * from './TransactionDetailAsset'; export * from './TransactionInputOutput'; +export * from './TransactionFee'; diff --git a/packages/core/src/ui/components/DappTransaction/DappTransaction.module.scss b/packages/core/src/ui/components/DappTransaction/DappTransaction.module.scss index ac80eec8e0..ea1eb99b34 100644 --- a/packages/core/src/ui/components/DappTransaction/DappTransaction.module.scss +++ b/packages/core/src/ui/components/DappTransaction/DappTransaction.module.scss @@ -1,5 +1,6 @@ @import '../../styles/theme.scss'; @import '../../../../../common/src/ui/styles/abstracts/_typography.scss'; + .dappInfo { margin: size_unit(1) 0px; } @@ -12,92 +13,14 @@ margin: size_unit(4) 0 size_unit(2) 0px; padding: size_unit(3) 0; border-top: 2px solid var(--light-mode-light-grey-plus, var(--dark-mode-mid-grey)); + gap: size_unit(3); } .error { margin: size_unit(2) 0px; } -.header { - font-size: var(--bodyLarge); - letter-spacing: -0.015em; - margin-bottom: size_unit(1); - display: flex; - justify-content: space-between; - align-items: center; - - .title { - font-weight: 600; - line-height: size_unit(3); - /* or 133% */ - /* Secondary - Black */ - color: var(--text-color-primary); - } - .type { - font-weight: 500; - line-height: size_unit(4); - /* or 178% */ - text-align: right; - /* Primary - Purple */ - color: var(--primary-default, #7f5af0); - } -} -.body { - display: flex; - flex-direction: column; - gap: size_unit(2); -} -.detail { - display: flex; - justify-content: space-between; - align-items: baseline; - - > * { - display: flex; - flex: 0 1 50%; - min-width: 0; - } - - .title { - font-weight: 500; - font-size: var(--body); - line-height: size_unit(3); - /* or 150% */ - /* Secondary - Black */ - color: var(--text-color-primary); - text-align: right; - } - .value { - display: flex; - align-items: flex-end; - flex-direction: column; - - font-size: var(--bodySmall); - font-weight: 500; - line-height: size_unit(2); - /* Secondary - Black */ - color: var(--text-color-primary); - - .bold { - font-weight: 600; - line-height: size_unit(3); - font-size: var(--body); - word-break: break-all; - } - - .rightAligned { - text-align: right; - > div { - justify-content: flex-end; - } - div, - p { - text-align: right; - } - } - } -} .warningAlert { flex-direction: row; background: var(--lace-cream); @@ -118,15 +41,12 @@ margin: 0; } } -:global(.__react_component_tooltip) { - @include tooltip-default; -} -.sub { - @include text-bodySmall-medium; - /* or 171% */ - letter-spacing: -0.015em; - /* Data - Dark Grey */ +.feeContainer { + display: flex; + flex-direction: row; + align-items: flex-start; + justify-content: space-between; + @include text-body-semi-bold; color: var(--text-color-primary); - text-align: right; } diff --git a/packages/core/src/ui/components/DappTransaction/DappTransaction.tsx b/packages/core/src/ui/components/DappTransaction/DappTransaction.tsx index d7c4da265c..6b0d0bce81 100644 --- a/packages/core/src/ui/components/DappTransaction/DappTransaction.tsx +++ b/packages/core/src/ui/components/DappTransaction/DappTransaction.tsx @@ -1,91 +1,84 @@ +/* eslint-disable sonarjs/no-duplicate-string */ import React from 'react'; -import { Ellipsis, ErrorPane } from '@lace/common'; +import { ErrorPane } from '@lace/common'; +import { Wallet } from '@lace/cardano'; import { DappInfo, DappInfoProps } from '../DappInfo'; +import { DappTxHeader } from './DappTxHeader/DappTxHeader'; +import { DappTxAsset } from './DappTxAsset/DappTxAsset'; +import { DappTxOutput } from './DappTxOutput/DappTxOutput'; import styles from './DappTransaction.module.scss'; -import { TranslationsFor } from '@ui/utils/types'; - -type TransactionDetails = { - fee: string; - outputs: { - coins: string; - recipient: string; - assets?: { - name: string; - amount: string; - ticker?: string; - }[]; - }[]; - type: 'Send' | 'Mint' | 'Burn'; -}; +import { useTranslate } from '@src/ui/hooks'; +import { TransactionFee } from '@ui/components/ActivityDetail'; export interface DappTransactionProps { /** Transaction details such as type, amount, fee and address */ - transaction: TransactionDetails; + transaction: Wallet.Cip30SignTxSummary; /** dApp information such as logo, name and url */ dappInfo: Omit; /** Optional error message */ errorMessage?: string; - translations: TranslationsFor<'transaction' | 'amount' | 'recipient' | 'fee' | 'adaFollowingNumericValue'>; + fiatCurrencyCode?: string; + fiatCurrencyPrice?: number; + coinSymbol?: string; } export const DappTransaction = ({ - transaction: { type, outputs, fee }, + transaction: { type, outputs, fee, mintedAssets, burnedAssets }, dappInfo, errorMessage, - translations -}: DappTransactionProps): React.ReactElement => ( -
- - {errorMessage && } -
-
-
- {translations.transaction} -
-
- {type} -
+ fiatCurrencyCode, + fiatCurrencyPrice, + coinSymbol +}: DappTransactionProps): React.ReactElement => { + const { t } = useTranslate(); + return ( +
+ + {errorMessage && } +
+ {type === Wallet.Cip30TxType.Mint && mintedAssets?.length > 0 && ( + <> + + {mintedAssets.map((asset) => ( + + ))} + + )} + {type === Wallet.Cip30TxType.Burn && burnedAssets?.length > 0 && ( + <> + 0 ? undefined : t('package.core.dappTransaction.transaction')} + subtitle={t('package.core.dappTransaction.burn')} + /> + {burnedAssets.map((asset) => ( + + ))} + + )} + {type === Wallet.Cip30TxType.Send && ( + <> + + {outputs.map((output) => ( + + ))} + + )} + {fee && fee !== '-' && ( + + `${Wallet.util.convertAdaToFiat({ ada, fiat: fiatCurrencyPrice })} ${fiatCurrencyCode}` + } + coinSymbol={coinSymbol} + /> + )}
- {outputs.map((output) => ( -
-
-
- {translations.amount} -
-
-
- {output.coins.toString()} ADA -
- {outputs.length === 1 && ( -
- {translations.fee}: {fee.toString()} ADA -
- )} - {output.assets && - output.assets.map((asset) => ( -
- {asset.amount} {asset.ticker || asset.name} -
- ))} -
-
-
-
- {translations.recipient} -
-
- -
-
-
- ))} - {outputs.length > 1 && ( -
-
- {translations.fee}: {fee.toString()} ADA -
-
- )}
-
-); + ); +}; diff --git a/packages/core/src/ui/components/DappTransaction/DappTxAsset/DappTxAsset.module.scss b/packages/core/src/ui/components/DappTransaction/DappTxAsset/DappTxAsset.module.scss new file mode 100644 index 0000000000..0d5bf78af1 --- /dev/null +++ b/packages/core/src/ui/components/DappTransaction/DappTxAsset/DappTxAsset.module.scss @@ -0,0 +1,41 @@ +@import '../../../styles/theme.scss'; +@import '../../../../../../common/src/ui/styles/abstracts/_typography.scss'; + +.body { + display: flex; + flex-direction: column; + gap: size_unit(3); + background-color: var(--light-mode-light-grey, var(--dark-mode-grey, #f9f9f9)); + border-radius: size_unit(2); + padding: size_unit(2); + + .detail { + @include text-body-semi-bold; + display: flex; + justify-content: space-between; + align-items: center; + + .title { + color: var(--text-color-primary); + line-height: 1; + } + + .value { + display: flex; + max-width: 50%; + align-items: flex-end; + flex-direction: column; + gap: size_unit(2); + + font-size: var(--bodySmall); + font-weight: 500; + line-height: 1; + /* Secondary - Black */ + color: var(--text-color-primary); + } + + .ellipsis > p { + margin: 0; + } + } +} diff --git a/packages/core/src/ui/components/DappTransaction/DappTxAsset/DappTxAsset.tsx b/packages/core/src/ui/components/DappTransaction/DappTxAsset/DappTxAsset.tsx new file mode 100644 index 0000000000..b775403436 --- /dev/null +++ b/packages/core/src/ui/components/DappTransaction/DappTxAsset/DappTxAsset.tsx @@ -0,0 +1,23 @@ +import React from 'react'; +import styles from './DappTxAsset.module.scss'; +import { Ellipsis } from '@lace/common'; +import { useTranslate } from '@src/ui/hooks'; +import { Wallet } from '@lace/cardano'; + +export const DappTxAsset = ({ amount, name, ticker }: Wallet.Cip30SignTxAssetItem): React.ReactElement => { + const { t } = useTranslate(); + return ( +
+
+
{t('package.core.dappTransaction.asset')}
+
+ +
+
+
+
{t('package.core.dappTransaction.quantity')}
+
{amount}
+
+
+ ); +}; diff --git a/packages/core/src/ui/components/DappTransaction/DappTxAsset/index.ts b/packages/core/src/ui/components/DappTransaction/DappTxAsset/index.ts new file mode 100644 index 0000000000..3418661f1c --- /dev/null +++ b/packages/core/src/ui/components/DappTransaction/DappTxAsset/index.ts @@ -0,0 +1 @@ +export { DappTxAsset } from './DappTxAsset'; diff --git a/packages/core/src/ui/components/DappTransaction/DappTxHeader/DappTxHeader.module.scss b/packages/core/src/ui/components/DappTransaction/DappTxHeader/DappTxHeader.module.scss new file mode 100644 index 0000000000..94eefef49f --- /dev/null +++ b/packages/core/src/ui/components/DappTransaction/DappTxHeader/DappTxHeader.module.scss @@ -0,0 +1,27 @@ +@import '../../../styles/theme.scss'; +@import '../../../../../../common/src/ui/styles/abstracts/_typography.scss'; + +.header { + font-size: var(--bodyLarge); + letter-spacing: -0.015em; + margin-bottom: size_unit(1); + display: flex; + justify-content: space-between; + align-items: center; + + .title { + font-weight: 600; + line-height: size_unit(3); + /* or 133% */ + /* Secondary - Black */ + color: var(--text-color-primary); + } + .type { + font-weight: 500; + line-height: size_unit(4); + /* or 178% */ + text-align: right; + /* Primary - Purple */ + color: var(--primary-default, #7f5af0); + } +} diff --git a/packages/core/src/ui/components/DappTransaction/DappTxHeader/DappTxHeader.tsx b/packages/core/src/ui/components/DappTransaction/DappTxHeader/DappTxHeader.tsx new file mode 100644 index 0000000000..8117fcd3e0 --- /dev/null +++ b/packages/core/src/ui/components/DappTransaction/DappTxHeader/DappTxHeader.tsx @@ -0,0 +1,20 @@ +import React from 'react'; +import styles from './DappTxHeader.module.scss'; + +export interface DappTxHeaderProps { + title?: string; + subtitle?: string; +} + +export const DappTxHeader = (props: DappTxHeaderProps): React.ReactElement => ( +
+
+ {props?.title ?? ''} +
+ {props?.subtitle && ( +
+ {props.subtitle} +
+ )} +
+); diff --git a/packages/core/src/ui/components/DappTransaction/DappTxOutput/DappTxOutput.module.scss b/packages/core/src/ui/components/DappTransaction/DappTxOutput/DappTxOutput.module.scss new file mode 100644 index 0000000000..04c3b12f0a --- /dev/null +++ b/packages/core/src/ui/components/DappTransaction/DappTxOutput/DappTxOutput.module.scss @@ -0,0 +1,67 @@ +@import '../../../styles/theme.scss'; +@import '../../../../../../common/src/ui/styles/abstracts/_typography.scss'; + +.body { + display: flex; + flex-direction: column; + gap: size_unit(3); + background-color: var(--light-mode-light-grey, var(--dark-mode-grey, #f9f9f9)); + border-radius: size_unit(2); + padding: size_unit(2); +} + +.detail { + display: flex; + justify-content: space-between; + align-items: baseline; + + > * { + display: flex; + flex: 0 1 50%; + min-width: 0; + } + + .title { + font-weight: 500; + font-size: var(--body); + line-height: size_unit(3); + /* or 150% */ + /* Secondary - Black */ + color: var(--text-color-primary); + text-align: right; + } + .value { + display: flex; + align-items: flex-end; + flex-direction: column; + gap: size_unit(2); + + font-size: var(--bodySmall); + font-weight: 500; + line-height: size_unit(2); + /* Secondary - Black */ + color: var(--text-color-primary); + + .bold { + font-weight: 600; + line-height: size_unit(3); + font-size: var(--body); + word-break: break-all; + } + + .rightAligned { + text-align: right; + > div { + justify-content: flex-end; + } + div, + p { + text-align: right; + } + } + } +} + +:global(.__react_component_tooltip) { + @include tooltip-default; +} diff --git a/packages/core/src/ui/components/DappTransaction/DappTxOutput/DappTxOutput.tsx b/packages/core/src/ui/components/DappTransaction/DappTxOutput/DappTxOutput.tsx new file mode 100644 index 0000000000..9eccd4e0e7 --- /dev/null +++ b/packages/core/src/ui/components/DappTransaction/DappTxOutput/DappTxOutput.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import { Ellipsis } from '@lace/common'; +import styles from './DappTxOutput.module.scss'; +import { useTranslate } from '@src/ui/hooks'; +import { Wallet } from '@lace/cardano'; + +export interface DappTxOutputProps { + coins: string; + recipient: string; + assets?: Wallet.Cip30SignTxAssetItem[]; +} + +export const DappTxOutput = ({ recipient, coins, assets }: DappTxOutputProps): React.ReactElement => { + const { t } = useTranslate(); + return ( +
+
+
+ {t('package.core.dappTransaction.sending')} +
+
+
+ {coins.toString()} ADA +
+ {assets?.map((asset) => ( +
+ {asset.amount} {asset.ticker || asset.name} +
+ ))} +
+
+
+
+ {t('package.core.dappTransaction.recipient')} +
+
+ +
+
+
+ ); +}; diff --git a/packages/core/src/ui/lib/translations/en.json b/packages/core/src/ui/lib/translations/en.json index c73e862cbd..7336752ffd 100644 --- a/packages/core/src/ui/lib/translations/en.json +++ b/packages/core/src/ui/lib/translations/en.json @@ -100,6 +100,18 @@ "noMatchPassword": "Oops! The passwords don't match.", "secondLevelPasswordStrengthFeedback": "Getting there! Add some symbols and numbers to make it stronger.", "firstLevelPasswordStrengthFeedback": "Weak password. Add some numbers and characters to make it stronger." + }, + "dappTransaction": { + "asset": "Asset", + "burn": "Burn", + "fee": "Transaction Fee", + "insufficientFunds": "You do not have enough funds to complete the transaction", + "mint": "Mint", + "quantity": "Quantity", + "recipient": "Recipient", + "send": "Send", + "sending": "Sending", + "transaction": "Transaction" } } } diff --git a/packages/e2e-tests/package.json b/packages/e2e-tests/package.json index 4af33bf873..55746c84ac 100755 --- a/packages/e2e-tests/package.json +++ b/packages/e2e-tests/package.json @@ -34,14 +34,14 @@ "@types/flat": "5.0.2", "@typescript-eslint/eslint-plugin": "6.0.0", "@typescript-eslint/parser": "6.0.0", - "@wdio/allure-reporter": "8.24.0", - "@wdio/cli": "8.24.0", - "@wdio/config": "8.24.0", - "@wdio/cucumber-framework": "8.24.0", - "@wdio/devtools-service": "8.24.0", - "@wdio/local-runner": "8.24.0", - "@wdio/spec-reporter": "8.24.0", - "@wdio/types": "8.24.0", + "@wdio/allure-reporter": "8.20.0", + "@wdio/cli": "8.20.0", + "@wdio/config": "8.20.0", + "@wdio/cucumber-framework": "8.20.0", + "@wdio/devtools-service": "8.20.0", + "@wdio/local-runner": "8.20.0", + "@wdio/spec-reporter": "8.20.0", + "@wdio/types": "8.20.0", "allure-commandline": "2.24.1", "clipboardy": "2.3.0", "eslint": "8.38.0", @@ -53,6 +53,6 @@ "ts-node": "10.9.1", "typescript": "4.9.5", "wdio-intercept-service": "4.4.0", - "webdriverio": "8.24.0" + "webdriverio": "8.20.0" } } diff --git a/packages/e2e-tests/src/assert/dAppConnectorAssert.ts b/packages/e2e-tests/src/assert/dAppConnectorAssert.ts index f19c85e0c4..cf484e4124 100644 --- a/packages/e2e-tests/src/assert/dAppConnectorAssert.ts +++ b/packages/e2e-tests/src/assert/dAppConnectorAssert.ts @@ -289,13 +289,19 @@ class DAppConnectorAssert { await ConfirmTransactionPage.transactionAmountTitle.waitForDisplayed(); expect(await ConfirmTransactionPage.transactionAmountTitle.getText()).to.equal( - await t('dapp.confirm.details.amount') + await t('package.core.dappTransaction.sending', 'core') ); await ConfirmTransactionPage.transactionAmountValue.waitForDisplayed(); expect(await ConfirmTransactionPage.transactionAmountValue.getText()).to.equal(expectedTransactionData.amountADA); - await ConfirmTransactionPage.transactionAmountFee.waitForDisplayed(); + await ConfirmTransactionPage.transactionFeeTitle.waitForDisplayed(); + expect(await ConfirmTransactionPage.transactionFeeTitle.getText()).to.equal( + await t('package.core.activityDetails.transactionFee', 'core') + ); + await ConfirmTransactionPage.transactionFeeTooltipIcon.waitForDisplayed(); + await ConfirmTransactionPage.transactionFeeValueAda.waitForDisplayed(); + await ConfirmTransactionPage.transactionFeeValueFiat.waitForDisplayed(); if (expectedTransactionData.amountAsset && expectedTransactionData.amountAsset !== '0') { await ConfirmTransactionPage.transactionAmountAsset.waitForDisplayed(); diff --git a/packages/e2e-tests/src/elements/dappConnector/confirmTransactionPage.ts b/packages/e2e-tests/src/elements/dappConnector/confirmTransactionPage.ts index ffdfc455fc..1b8636a7d7 100644 --- a/packages/e2e-tests/src/elements/dappConnector/confirmTransactionPage.ts +++ b/packages/e2e-tests/src/elements/dappConnector/confirmTransactionPage.ts @@ -7,7 +7,10 @@ class ConfirmTransactionPage extends CommonDappPageElements { private TRANSACTION_TYPE = '[data-testid="dapp-transaction-type"]'; private TRANSACTION_AMOUNT_TITLE = '[data-testid="dapp-transaction-amount-title"]'; private TRANSACTION_AMOUNT_VALUE = '[data-testid="dapp-transaction-amount-value"]'; - private TRANSACTION_AMOUNT_FEE = '[data-testid="dapp-transaction-amount-fee"]'; + private TRANSACTION_AMOUNT_FEE_TITLE = '[data-testid="tx-fee-title"]'; + private TRANSACTION_AMOUNT_FEE_TITLE_TOOLTIP_ICON = '[data-testid="tx-fee-tooltip-icon"]'; + private TRANSACTION_AMOUNT_FEE_VALUE_ADA = '[data-testid="tx-fee-ada"]'; + private TRANSACTION_AMOUNT_FEE_VALUE_FIAT = '[data-testid="tx-fee-fiat"]'; private TRANSACTION_AMOUNT_ASSET = '[data-testid="dapp-transaction-asset"]'; private TRANSACTION_RECIPIENT_TITLE = '[data-testid="dapp-transaction-recipient-title"]'; private TRANSACTION_RECIPIENT_ADDRESS = '[data-testid="dapp-transaction-recipient-address"]'; @@ -29,9 +32,20 @@ class ConfirmTransactionPage extends CommonDappPageElements { get transactionAmountValue(): ChainablePromiseElement { return $(this.TRANSACTION_AMOUNT_VALUE); } + get transactionFeeTitle(): ChainablePromiseElement { + return $(this.TRANSACTION_AMOUNT_FEE_TITLE); + } + + get transactionFeeTooltipIcon(): ChainablePromiseElement { + return $(this.TRANSACTION_AMOUNT_FEE_TITLE_TOOLTIP_ICON); + } + + get transactionFeeValueAda(): ChainablePromiseElement { + return $(this.TRANSACTION_AMOUNT_FEE_VALUE_ADA); + } - get transactionAmountFee(): ChainablePromiseElement { - return $(this.TRANSACTION_AMOUNT_FEE); + get transactionFeeValueFiat(): ChainablePromiseElement { + return $(this.TRANSACTION_AMOUNT_FEE_VALUE_FIAT); } get transactionAmountAsset(): ChainablePromiseElement { diff --git a/packages/e2e-tests/src/features/EmptyStatesExtended.feature b/packages/e2e-tests/src/features/EmptyStatesExtended.feature index b1437d70ee..273e140f50 100644 --- a/packages/e2e-tests/src/features/EmptyStatesExtended.feature +++ b/packages/e2e-tests/src/features/EmptyStatesExtended.feature @@ -29,7 +29,6 @@ Feature: Empty states @LW-8447 Scenario: Extended View - Staking empty state - When I disable showing Multidelegation beta banner And I navigate to Staking extended page Then I see empty state banner for Staking page in extended mode When I click "Copy" button on empty state banner diff --git a/packages/e2e-tests/src/features/EmptyStatesPopup.feature b/packages/e2e-tests/src/features/EmptyStatesPopup.feature index c58982208f..daa1864c58 100644 --- a/packages/e2e-tests/src/features/EmptyStatesPopup.feature +++ b/packages/e2e-tests/src/features/EmptyStatesPopup.feature @@ -28,7 +28,6 @@ Feature: Empty states @LW-8470 Scenario: Popup View - Staking empty state - When I disable showing Multidelegation beta banner And I navigate to Staking popup page Then I see empty state banner for Staking page in popup mode When I click "Copy" button on empty state banner diff --git a/packages/e2e-tests/src/features/FullExperiencePopup.feature b/packages/e2e-tests/src/features/FullExperiencePopup.feature index 9cbc58ea86..2209cd4938 100644 --- a/packages/e2e-tests/src/features/FullExperiencePopup.feature +++ b/packages/e2e-tests/src/features/FullExperiencePopup.feature @@ -12,7 +12,6 @@ Feature: Full experience - popup view @LW-3446 Scenario Outline: Popup View - opened - "Expand" button click - Given I disable showing Multidelegation beta banner And I am on popup page When I click on "Expand" button Then the page is displayed on a new tab in extended view diff --git a/packages/e2e-tests/src/features/MultiDelegationPageExtended.feature b/packages/e2e-tests/src/features/MultiDelegationPageExtended.feature index ca562f6571..1b571b3a7c 100644 --- a/packages/e2e-tests/src/features/MultiDelegationPageExtended.feature +++ b/packages/e2e-tests/src/features/MultiDelegationPageExtended.feature @@ -7,7 +7,6 @@ Feature: Staking Page - Extended View @LW-8931 @Testnet Scenario: Extended View - Start Staking component Given I save token: "Cardano" balance - And I disable showing Multidelegation beta banner When I navigate to Staking extended page Then I see Start Staking page in extended mode @@ -23,14 +22,12 @@ Feature: Staking Page - Extended View @LW-8449 @Testnet @Mainnet Scenario: Extended View - Staking search control is displayed with appropriate content - Given I disable showing Multidelegation beta banner When I navigate to Staking extended page And I click Browse pools tab Then I see the stake pool search control with appropriate content @LW-8448 @Testnet Scenario Outline: Extended View - Stake pool search for "" returns the expected number of results with appropriate content - Given I disable showing Multidelegation beta banner When I navigate to Staking extended page And I click Browse pools tab And I input "" into stake pool search bar @@ -49,7 +46,6 @@ Feature: Staking Page - Extended View @LW-8448 @Mainnet Scenario Outline: Extended View - Stake pool search for "" returns the expected number of results with appropriate content - Given I disable showing Multidelegation beta banner When I navigate to Staking extended page And I click Browse pools tab And I input "" into stake pool search bar @@ -69,12 +65,10 @@ Feature: Staking Page - Extended View @LW-8466 @Testnet @Mainnet Scenario: Extended View - "About staking" widget Given I am on Staking extended page - And I close Multi-delegation beta modal Then I see "About staking" widget with all relevant items @LW-8465 @Testnet @Mainnet Scenario Outline: Extended View - "About staking" widget item click - - Given I disable showing Multidelegation beta banner And I am on Staking extended page When I click on a widget item with subtitle: "" Then I see a "" article with title "" @@ -87,13 +81,11 @@ Feature: Staking Page - Extended View @LW-8469 @Testnet @Mainnet Scenario: Extended View - Network info component is present with expected content - Given I disable showing Multidelegation beta banner When I navigate to Staking extended page Then I see the Network Info component with the expected content @LW-8499 @Testnet @Mainnet Scenario Outline: Extended View - Staking - Show tooltip for column names in browse pools section - Given I disable showing Multidelegation beta banner When I navigate to Staking extended page And I click Browse pools tab When I hover over "" column name in stake pool list @@ -105,7 +97,6 @@ Feature: Staking Page - Extended View @LW-8637 @Testnet @Mainnet Scenario: Extended View - Staking password screen details - Given I disable showing Multidelegation beta banner When I navigate to Staking extended page And I click Overview tab And I click Browse pools tab @@ -118,7 +109,6 @@ Feature: Staking Page - Extended View @LW-8445 @Testnet Scenario: Extended View - Selecting stakepool from list opens drawer with appropriate details - Given I disable showing Multidelegation beta banner And I am on Staking extended page And I click Browse pools tab And I input "ADA Capital" into stake pool search bar @@ -127,7 +117,6 @@ Feature: Staking Page - Extended View @LW-8438 @Testnet Scenario: Extended View - Staking - Stakepool details drawer - Close drawer - Given I disable showing Multidelegation beta banner And I am on Staking extended page And I click Browse pools tab And I input "ADA Capital" into stake pool search bar @@ -137,7 +126,6 @@ Feature: Staking Page - Extended View @LW-8463 @Testnet @Mainnet Scenario: Extended View - Stake pool list item - Given I disable showing Multidelegation beta banner And I am on Staking extended page And I click Browse pools tab And I wait for stake pool list to be populated diff --git a/packages/e2e-tests/src/features/MultiDelegationPagePopup.feature b/packages/e2e-tests/src/features/MultiDelegationPagePopup.feature index da12730b12..f0810fafb1 100644 --- a/packages/e2e-tests/src/features/MultiDelegationPagePopup.feature +++ b/packages/e2e-tests/src/features/MultiDelegationPagePopup.feature @@ -7,7 +7,6 @@ Feature: Staking Page - Popup View @LW-8933 @Testnet Scenario: Popup View - Start Staking component Given I save token: "Cardano" balance - And I disable showing Multidelegation beta banner When I navigate to Staking popup page Then I see Start Staking page in popup mode diff --git a/packages/e2e-tests/src/features/MultidelegationDelegatedFundsExtended.feature b/packages/e2e-tests/src/features/MultidelegationDelegatedFundsExtended.feature index 422a2d405b..000d5aba03 100644 --- a/packages/e2e-tests/src/features/MultidelegationDelegatedFundsExtended.feature +++ b/packages/e2e-tests/src/features/MultidelegationDelegatedFundsExtended.feature @@ -6,7 +6,6 @@ Feature: Staking Page - Extended View @LW-8436 @LW-8439 @LW-8440 @LW-8598 Scenario Outline: Extended View - Staking - Close drawer - Given I disable showing Multidelegation beta banner When I navigate to Staking extended page And I click Browse pools tab And I pick "1" pools for delegation from browse pools view: "ADA Capital" @@ -28,7 +27,6 @@ Feature: Staking Page - Extended View @LW-8450 Scenario Outline: Extended View - Staking - Hover over currently staking element: - Given I disable showing Multidelegation beta banner And I navigate to Staking extended page When I hover over in currently staking component Then I see tooltip for element in currently staking component diff --git a/packages/e2e-tests/src/features/MultidelegationDelegatedFundsPopup.feature b/packages/e2e-tests/src/features/MultidelegationDelegatedFundsPopup.feature index a397fdee1b..a138ee1ae7 100644 --- a/packages/e2e-tests/src/features/MultidelegationDelegatedFundsPopup.feature +++ b/packages/e2e-tests/src/features/MultidelegationDelegatedFundsPopup.feature @@ -7,15 +7,14 @@ Feature: Staking Page - Popup View @LW-8330 Scenario Outline: Popup View - Delegation card displays correct data Given I open wallet: "" in: popup mode - And I disable showing Multidelegation beta banner And I disable showing Multidelegation persistence banner When I navigate to Staking popup page Then I see Delegation title displayed for multidelegation And I see Delegation card displaying correct data Examples: - | walletName | - | MultidelegationDelegatedSingle | - | MultidelegationDelegatedMulti | + | walletName | + | MultidelegationDelegatedSingle | + | MultidelegationDelegatedMulti | @LW-8338 Scenario Outline: Popup View - Delegated pools cards are present @@ -31,9 +30,7 @@ Feature: Staking Page - Popup View @LW-8480 Scenario Outline: Popup View - Staking - Hover over currently staking element: - Given Lace is ready for test - And I disable showing Multidelegation beta banner - And I navigate to Staking popup page + Given I navigate to Staking popup page When I hover over in currently staking component Then I see tooltip for element in currently staking component Examples: diff --git a/packages/e2e-tests/src/features/NavigationMainExtended.feature b/packages/e2e-tests/src/features/NavigationMainExtended.feature index a12cae4da7..d9d7cd27ed 100644 --- a/packages/e2e-tests/src/features/NavigationMainExtended.feature +++ b/packages/e2e-tests/src/features/NavigationMainExtended.feature @@ -3,7 +3,6 @@ Feature: Main Navigation - Extended view Background: Given Lace is ready for test - And I disable showing Multidelegation beta banner @LW-2692 @Smoke Scenario: Extended view - Main navigation is displayed with all items @@ -38,13 +37,13 @@ Feature: Main Navigation - Extended view When I click on the logo icon Then I see Tokens counter with total number of tokens displayed Examples: - | section | validateIfSectionIsDisplayed | - | Tokens | I see Tokens counter with total number of tokens displayed | - | NFTs | I see NFTs counter with total number of NFTs displayed | - | Transactions | Transactions section is displayed | - | Staking | I see Delegation title displayed for multidelegation | - | Settings | I see settings page | - | Address Book | I see address book title | + | section | validateIfSectionIsDisplayed | + | Tokens | I see Tokens counter with total number of tokens displayed | + | NFTs | I see NFTs counter with total number of NFTs displayed | + | Transactions | Transactions section is displayed | + | Staking | I see Delegation title displayed for multidelegation | + | Settings | I see settings page | + | Address Book | I see address book title | @LW-6662 Scenario Outline: Extended view - Main Navigation - Right side panel not displayed in
section diff --git a/packages/e2e-tests/src/features/NavigationMainPopup.feature b/packages/e2e-tests/src/features/NavigationMainPopup.feature index 9aba459aaa..86acaab892 100644 --- a/packages/e2e-tests/src/features/NavigationMainPopup.feature +++ b/packages/e2e-tests/src/features/NavigationMainPopup.feature @@ -32,7 +32,6 @@ Feature: Main Navigation - Popup View @LW-2610 Scenario Outline: Extended view - Click Lace logo -
And Wallet is synced - And I disable showing Multidelegation beta banner And I navigate to
popup page And When I click on the logo icon diff --git a/packages/e2e-tests/src/features/analytics/AnalyticsSendExtended.feature b/packages/e2e-tests/src/features/analytics/AnalyticsSendExtended.feature index f159bf2ea8..4a7edbc963 100644 --- a/packages/e2e-tests/src/features/analytics/AnalyticsSendExtended.feature +++ b/packages/e2e-tests/src/features/analytics/AnalyticsSendExtended.feature @@ -15,6 +15,7 @@ Feature: Analytics - Posthog - Sending - Extended View | ADA | Cardano | tADA | 1.1234 | When I click "Add bundle" button on "Send" page And I enter "$test_handle_1" in the bundle 2 recipient's address + And Green tick icon is displayed next to ADA handle And I enter a value of: 1 to the "tADA" asset in bundle 2 And I click "Review transaction" button on "Send" page Then I validate latest analytics single event "send | transaction data | review transaction | click" @@ -29,7 +30,7 @@ Feature: Analytics - Posthog - Sending - Extended View And I click "View transaction" button on submitted transaction page And Local storage unconfirmedTransaction contains tx with type: "internal" And I validate latest analytics single event "send | all done | view transaction | click" - When the Sent transaction is displayed with value: "1.12 tADA" and tokens count 1 + When the Sent transaction is displayed with value: "2.12 tADA" and tokens count 1 Then I validate latest analytics single event "send | transaction confirmed" And I validate that the "send | transaction confirmed" event includes property "tx_creation_type" with value "internal" in posthog And I validate that 7 analytics event(s) have been sent diff --git a/packages/e2e-tests/src/features/e2e/MultidelegationSwitchingPoolsExtendedE2E.feature b/packages/e2e-tests/src/features/e2e/MultidelegationSwitchingPoolsExtendedE2E.feature index e30451b57e..f441fc755b 100644 --- a/packages/e2e-tests/src/features/e2e/MultidelegationSwitchingPoolsExtendedE2E.feature +++ b/packages/e2e-tests/src/features/e2e/MultidelegationSwitchingPoolsExtendedE2E.feature @@ -3,7 +3,6 @@ Feature: Staking Page - Switching pools - Extended Browser View - E2E Background: Given Wallet is synced - And I disable showing Multidelegation beta banner And I navigate to Staking extended page @LW-7819 @Testnet @Pending diff --git a/packages/e2e-tests/src/hooks/beforeTagHooks.ts b/packages/e2e-tests/src/hooks/beforeTagHooks.ts index b58e8583e9..760d72fdd3 100644 --- a/packages/e2e-tests/src/hooks/beforeTagHooks.ts +++ b/packages/e2e-tests/src/hooks/beforeTagHooks.ts @@ -30,22 +30,31 @@ Before( { tags: '@AddressBook-extended or @Transactions-Extended or @Tokens-extended or @Staking-Extended or @LockWallet-extended or @Top-Navigation-Extended or @NFTs-Extended or @NFT-Folders-Extended or @SendTx-Bundles-Extended or @SendTx-Simple-Extended or @MainNavigation-Extended or @Send-Transaction-Metadata-Extended or @Settings-Extended or @DAppConnector or @DAppConnector-Extended' }, - async () => await extendedViewWalletInitialization() + async () => { + await extendedViewWalletInitialization(); + await localStorageInitializer.disableShowingMultidelegationBetaBanner(); + } ); Before( { tags: '@Tokens-popup or @Transactions-Popup or @Staking-Popup or @LockWallet-popup or @Top-Navigation-Popup or @AddressBook-popup or @Common-Popup or @SendTx-Simple-Popup or @MainNavigation-Popup or @Settings-Popup or @NFTs-Popup or @NFT-Folders-Popup or @Send-Transaction-Metadata-Popup or @ForgotPassword or @DAppConnector-Popup' }, - async () => await popupViewWalletInitialization() + async () => { + await popupViewWalletInitialization(); + await localStorageInitializer.disableShowingMultidelegationBetaBanner(); + } ); -Before( - { tags: '@EmptyStates-Extended' }, - async () => await extendedViewWalletInitialization(TestWalletName.TAWalletNoFunds) -); +Before({ tags: '@EmptyStates-Extended' }, async () => { + await extendedViewWalletInitialization(TestWalletName.TAWalletNoFunds); + await localStorageInitializer.disableShowingMultidelegationBetaBanner(); +}); -Before({ tags: '@EmptyStates-Popup' }, async () => await popupViewWalletInitialization(TestWalletName.TAWalletNoFunds)); +Before({ tags: '@EmptyStates-Popup' }, async () => { + await popupViewWalletInitialization(TestWalletName.TAWalletNoFunds); + await localStorageInitializer.disableShowingMultidelegationBetaBanner(); +}); Before( { tags: '@SendTx-MultipleSelection-Popup' }, @@ -104,15 +113,15 @@ Before( async () => await extendedViewWalletInitialization(TestWalletName.TAWalletDelegatedFunds) ); -Before( - { tags: '@Staking-NonDelegatedFunds-Extended' }, - async () => await extendedViewWalletInitialization(TestWalletName.TAWalletNonDelegatedFunds) -); +Before({ tags: '@Staking-NonDelegatedFunds-Extended' }, async () => { + await extendedViewWalletInitialization(TestWalletName.TAWalletNonDelegatedFunds); + await localStorageInitializer.disableShowingMultidelegationBetaBanner(); +}); -Before( - { tags: '@Staking-NonDelegatedFunds-Popup' }, - async () => await popupViewWalletInitialization(TestWalletName.TAWalletNonDelegatedFunds) -); +Before({ tags: '@Staking-NonDelegatedFunds-Popup' }, async () => { + await popupViewWalletInitialization(TestWalletName.TAWalletNonDelegatedFunds); + await localStorageInitializer.disableShowingMultidelegationBetaBanner(); +}); Before( { tags: '@Staking-SwitchingPools-Extended-E2E' }, @@ -126,10 +135,10 @@ Before( Before({ tags: '@AdaHandle-popup' }, async () => await popupViewWalletInitialization(TestWalletName.WalletAdaHandle)); -Before( - { tags: '@Multidelegation-SwitchingPools-Extended-E2E' }, - async () => await extendedViewWalletInitialization(TestWalletName.WalletMultidelegationSwitchPoolsE2E) -); +Before({ tags: '@Multidelegation-SwitchingPools-Extended-E2E' }, async () => { + await extendedViewWalletInitialization(TestWalletName.WalletMultidelegationSwitchPoolsE2E); + await localStorageInitializer.disableShowingMultidelegationBetaBanner(); +}); Before( { tags: '@HdWallet-extended' }, @@ -141,12 +150,12 @@ Before( async () => await extendedViewWalletInitialization(TestWalletName.WalletSendNftHdWalletE2E) ); -Before( - { tags: '@Multidelegation-DelegatedFunds-Popup' }, - async () => await popupViewWalletInitialization(TestWalletName.MultidelegationDelegatedSingle) -); +Before({ tags: '@Multidelegation-DelegatedFunds-Popup' }, async () => { + await popupViewWalletInitialization(TestWalletName.MultidelegationDelegatedSingle); + await localStorageInitializer.disableShowingMultidelegationBetaBanner(); +}); -Before( - { tags: '@Multidelegation-DelegatedFunds-Extended' }, - async () => await extendedViewWalletInitialization(TestWalletName.MultidelegationDelegatedSingle) -); +Before({ tags: '@Multidelegation-DelegatedFunds-Extended' }, async () => { + await extendedViewWalletInitialization(TestWalletName.MultidelegationDelegatedSingle); + await localStorageInitializer.disableShowingMultidelegationBetaBanner(); +}); diff --git a/packages/e2e-tests/src/pageobject/dAppConnectorPageObject.ts b/packages/e2e-tests/src/pageobject/dAppConnectorPageObject.ts index b451bb89cd..fcbdf3c560 100644 --- a/packages/e2e-tests/src/pageobject/dAppConnectorPageObject.ts +++ b/packages/e2e-tests/src/pageobject/dAppConnectorPageObject.ts @@ -96,7 +96,7 @@ class DAppConnectorPageObject { } async saveDappTransactionFeeValue() { - let feeValue = await ConfirmTransactionPage.transactionAmountFee.getText(); + let feeValue = await ConfirmTransactionPage.transactionFeeValueAda.getText(); feeValue = feeValue.replace(' ADA', '').replace('Fee: ', ''); await testContext.save('feeValueDAppTx', feeValue); } diff --git a/packages/e2e-tests/src/utils/networkManager.ts b/packages/e2e-tests/src/utils/networkManager.ts index aa78c6b04e..0d3ebcee42 100644 --- a/packages/e2e-tests/src/utils/networkManager.ts +++ b/packages/e2e-tests/src/utils/networkManager.ts @@ -107,7 +107,10 @@ export class NetworkManager { const approximateTimestamp = new Date().toString(); const combinedFailedRequestInfo = `URL:\n${request.response.url}\n\nRESPONSE CODE:\n${request.response.status}\n\nAPPROXIMATE TIME:\n${approximateTimestamp}\n\nRESPONSE BODY:\n${responseBody}\n\nREQUEST PAYLOAD:\n${requestPayload}`; allure.addAttachment('Failed request', combinedFailedRequestInfo, 'text/plain'); - console.error('Failed request', combinedFailedRequestInfo); + console.error( + 'Failed request', + `URL: ${request.response.url} | RESPONSE CODE: ${request.response.status}` + ); } }); }); @@ -125,8 +128,8 @@ export class NetworkManager { let postData = ''; try { postData = JSON.stringify(await client.send('Network.getRequestPostData', { requestId })); - } catch (error) { - Logger.warn(`${error}`); + } catch { + /* empty */ } return postData; }; diff --git a/packages/e2e-tests/src/utils/utils.ts b/packages/e2e-tests/src/utils/utils.ts index 78dcd470b0..52f00d4979 100644 --- a/packages/e2e-tests/src/utils/utils.ts +++ b/packages/e2e-tests/src/utils/utils.ts @@ -42,7 +42,6 @@ class ExtensionUtils { break; } } - Logger.log(`Using network: ${network} with id: ${id}`); return { name: network, id }; } diff --git a/packages/e2e-tests/wdio.conf.base.ts b/packages/e2e-tests/wdio.conf.base.ts index 65cd4f434c..572e840109 100755 --- a/packages/e2e-tests/wdio.conf.base.ts +++ b/packages/e2e-tests/wdio.conf.base.ts @@ -22,7 +22,12 @@ export const config: WebdriverIO.Config = { connectionRetryCount: 3, framework: 'cucumber', reporters: [ - 'spec', + [ + 'spec', + { + realtimeReporting: true + } + ], [ 'allure', { diff --git a/packages/staking/package.json b/packages/staking/package.json index 4d44c00627..bc2f8734e9 100644 --- a/packages/staking/package.json +++ b/packages/staking/package.json @@ -59,11 +59,14 @@ "i18next": "^22.5.1", "immer": "^10.0.2", "lodash": "4.17.21", + "rambda": "^8.5.0", "react-copy-to-clipboard": "^5.1.0", "react-i18next": "^12.3.1", + "recharts": "^2.9.2", "zustand": "^4.4.1" }, "devDependencies": { + "@cardano-sdk/core": "0.21.0", "@cardano-sdk/input-selection": "0.12.4", "@cardano-sdk/tx-construction": "0.14.2", "@cardano-sdk/util": "0.14.2", diff --git a/packages/staking/src/features/BrowsePools/StakePoolsTable/StakePoolsTable.tsx b/packages/staking/src/features/BrowsePools/StakePoolsTable/StakePoolsTable.tsx index 610ceee531..5d30e2bcf7 100644 --- a/packages/staking/src/features/BrowsePools/StakePoolsTable/StakePoolsTable.tsx +++ b/packages/staking/src/features/BrowsePools/StakePoolsTable/StakePoolsTable.tsx @@ -2,6 +2,7 @@ import { Wallet } from '@lace/cardano'; import { PostHogAction, Search, getRandomIcon } from '@lace/common'; import { Box } from '@lace/ui'; import debounce from 'lodash/debounce'; +import uniqBy from 'lodash/uniqBy'; import { useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { StateStatus, useOutsideHandles } from '../../outside-handles-provider'; @@ -92,13 +93,26 @@ export const StakePoolsTable = ({ scrollableTargetId }: StakePoolsTableProps) => setSearchValue(searchString); }; + // imitates apps/browser-extension-wallet/src/stores/slices/stake-pool-search-slice.ts + const naiveSelectedPoolsSearch = (searchString: string, pools: Wallet.Cardano.StakePool[]) => { + const lowerCaseSearchString = searchString.toLowerCase(); + return pools.filter( + (pool) => + pool.metadata?.name.toLowerCase().includes(lowerCaseSearchString) || + pool.metadata?.ticker.toLowerCase().includes(lowerCaseSearchString) || + pool.id.toLowerCase() === lowerCaseSearchString + ); + }; + const combinedUnique = useMemo(() => { - const combinedStakePools = [...selectedPortfolioStakePools, ...stakePools]; - const combinedUniqueIds = [...new Set(combinedStakePools.map((pool) => pool.id))]; - return combinedUniqueIds.map((id) => - combinedStakePools.find((pool) => pool.id === id) - ) as Wallet.Cardano.StakePool[]; - }, [stakePools, selectedPortfolioStakePools]); + const combinedStakePools = [ + ...(searchValue + ? naiveSelectedPoolsSearch(searchValue, selectedPortfolioStakePools) + : selectedPortfolioStakePools), + ...stakePools, + ]; + return uniqBy(combinedStakePools, (p) => p.id); + }, [stakePools, selectedPortfolioStakePools, searchValue]); const list = useMemo( () => diff --git a/packages/staking/src/features/Drawer/StakePoolDetail.tsx b/packages/staking/src/features/Drawer/StakePoolDetail.tsx index 53ef0295c3..7582ca944c 100644 --- a/packages/staking/src/features/Drawer/StakePoolDetail.tsx +++ b/packages/staking/src/features/Drawer/StakePoolDetail.tsx @@ -209,6 +209,12 @@ const makeActionButtons = ( ): ActionButtonSpec[] => ( [ + manageDelegation && { + callback: tmpNoop, + dataTestId: 'stake-pool-details-manage-delegation-btn', + label: t('drawer.details.manageDelegation'), + ...getSpecOverride(manageDelegation), + }, stakeOnThisPool && { callback: tmpNoop, dataTestId: 'stake-pool-details-stake-btn', @@ -233,12 +239,6 @@ const makeActionButtons = ( label: t('drawer.details.unselectPool'), ...getSpecOverride(unselectPool), }, - manageDelegation && { - callback: tmpNoop, - dataTestId: 'stake-pool-details-manage-delegation-btn', - label: t('drawer.details.manageDelegation'), - ...getSpecOverride(manageDelegation), - }, ] as (ActionButtonSpec | false)[] ).filter(Boolean) as ActionButtonSpec[]; @@ -291,13 +291,19 @@ export const StakePoolDetailFooter = ({ popupView }: StakePoolDetailFooterProps) }); }, [viewedStakePool, analytics, portfolioMutators]); + const onManageDelegationClick = useCallback(() => { + if (!viewedStakePool) return; + analytics.sendEventToPostHog(PostHogAction.StakingBrowsePoolsStakePoolDetailManageDelegation); + portfolioMutators.executeCommand({ + type: 'ManageDelegationFromDetails', + }); + }, [viewedStakePool, analytics, portfolioMutators]); + const actionButtons = useMemo( () => makeActionButtons(t, { addStakingPool: ableToSelect && !selectionsEmpty && { callback: onSelectClick }, - // TODO: disabling this button for now - // eslint-disable-next-line sonarjs/no-redundant-boolean - manageDelegation: false && poolInCurrentPortfolio, + manageDelegation: poolInCurrentPortfolio && { callback: onManageDelegationClick }, selectForMultiStaking: ableToSelect && selectionsEmpty && { callback: onSelectClick }, stakeOnThisPool: selectionsEmpty && ableToStakeOnlyOnThisPool && { callback: onStakeOnThisPool }, unselectPool: poolSelected && { callback: onUnselectClick }, @@ -306,6 +312,7 @@ export const StakePoolDetailFooter = ({ popupView }: StakePoolDetailFooterProps) t, ableToSelect, selectionsEmpty, + onManageDelegationClick, onSelectClick, poolInCurrentPortfolio, ableToStakeOnlyOnThisPool, diff --git a/packages/staking/src/features/Drawer/StakePoolDetailsDrawer.tsx b/packages/staking/src/features/Drawer/StakePoolDetailsDrawer.tsx index 6675b0c912..3e0c481dcf 100644 --- a/packages/staking/src/features/Drawer/StakePoolDetailsDrawer.tsx +++ b/packages/staking/src/features/Drawer/StakePoolDetailsDrawer.tsx @@ -72,17 +72,20 @@ export const StakePoolDetailsDrawer = ({ }); }, [password, portfolioMutators, backgroundServiceAPIContextSetWalletPassword, removePassword]); + const shouldShowBackIcon = + activeDrawerStep && typeof showBackIcon === 'function' ? showBackIcon(activeDrawerStep) : showBackIcon; + useKeyboardShortcut(['Escape'], () => { - if (activeDrawerStep && typeof showBackIcon === 'function' ? showBackIcon(activeDrawerStep) : showBackIcon) { + if (shouldShowBackIcon) { onGoBack(); } else { closeDrawer(); } }); - const createArrowIconCallback = () => { - if (activeDrawerStep && typeof showBackIcon === 'function' ? showBackIcon(activeDrawerStep) : showBackIcon) { - return popupView ? closeDrawer : onGoBack; + const arrowIconCallback = () => { + if (shouldShowBackIcon) { + return popupView ? closeDrawer() : onGoBack(); } // eslint-disable-next-line consistent-return, unicorn/no-useless-undefined return undefined; @@ -97,10 +100,14 @@ export const StakePoolDetailsDrawer = ({ { - onBackButtonClick?.(); - createArrowIconCallback(); - }} + onArrowIconClick={ + !shouldShowBackIcon + ? undefined + : () => { + onBackButtonClick?.(); + arrowIconCallback(); + } + } onCloseIconClick={() => { if ( activeDrawerStep && typeof showCloseIcon === 'function' ? showCloseIcon(activeDrawerStep) : showCloseIcon diff --git a/packages/staking/src/features/Drawer/TransactionFail.tsx b/packages/staking/src/features/Drawer/TransactionFail.tsx index 149b5d8479..369f861b5c 100644 --- a/packages/staking/src/features/Drawer/TransactionFail.tsx +++ b/packages/staking/src/features/Drawer/TransactionFail.tsx @@ -99,8 +99,8 @@ export const TransactionFailFooter = ({ popupView }: TransactionFailProps): Reac ) : ( +
+
+ ); +}; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/DappTransactionFail.tsx b/apps/browser-extension-wallet/src/features/dapp/components/DappTransactionFail.tsx index e008f2810f..465bbf40b7 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/DappTransactionFail.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/DappTransactionFail.tsx @@ -24,8 +24,8 @@ export const DappTransactionFail = (): React.ReactElement => { }, [analytics]); return ( -
-
+
+
{t('dapp.sign.failure.title')}
{t('dapp.sign.failure.description')}
diff --git a/apps/browser-extension-wallet/src/features/dapp/components/DappTransactionSuccess.tsx b/apps/browser-extension-wallet/src/features/dapp/components/DappTransactionSuccess.tsx index c69b4bf667..42650cdf24 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/DappTransactionSuccess.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/DappTransactionSuccess.tsx @@ -25,8 +25,8 @@ export const DappTransactionSuccess = (): React.ReactElement => { }, [analytics]); return ( -
-
+
+
{t('browserView.transaction.success.youCanSafelyCloseThisPanel')} diff --git a/apps/browser-extension-wallet/src/features/dapp/components/Layout.module.scss b/apps/browser-extension-wallet/src/features/dapp/components/Layout.module.scss index edad913a6a..58262eb769 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/Layout.module.scss +++ b/apps/browser-extension-wallet/src/features/dapp/components/Layout.module.scss @@ -26,7 +26,7 @@ padding-top: size_unit(4); } -.noWalletContainer { +.dappErrorContainer { align-items: center; display: flex; flex-direction: column; @@ -34,7 +34,7 @@ justify-content: space-between; width: 100%; - .noWalletContent { + .dappErrorContent { padding: 0 size_unit(3); display: flex; flex: 1; @@ -45,7 +45,7 @@ .heading { color: var(--text-color-secondary); font-size: var(--bodyLarge); - font-weight: 800; + font-weight: 600; letter-spacing: 0.02em; line-height: size_unit(3); margin-top: size_unit(2); @@ -55,6 +55,7 @@ .description { color: var(--text-color-secondary); font-size: var(--bodySmall); + font-weight: 500; letter-spacing: 0.02em; line-height: size_unit(3); margin-top: size_unit(2); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/NoWallet.tsx b/apps/browser-extension-wallet/src/features/dapp/components/NoWallet.tsx deleted file mode 100644 index 86050bb7ea..0000000000 --- a/apps/browser-extension-wallet/src/features/dapp/components/NoWallet.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import React from 'react'; -import { Image } from 'antd'; -import { useTranslation } from 'react-i18next'; -import Empty from '../../../assets/icons/empty.svg'; -import styles from './Layout.module.scss'; -import { Button } from '@lace/common'; -import { tabs } from 'webextension-polyfill'; - -const openCreatePage = () => { - tabs.create({ url: 'app.html#/setup' }); - window.close(); -}; - -export const NoWallet = (): React.ReactElement => { - const { t } = useTranslation(); - - return ( -
-
- -
- {t('dapp.noWallet.heading')} -
-
- {t('dapp.noWallet.description')} -
-
-
- -
-
- ); -}; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmDRepRetirementContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmDRepRetirementContainer.tsx index 42650c2b8e..2727c88121 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmDRepRetirementContainer.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmDRepRetirementContainer.tsx @@ -1,34 +1,76 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { ConfirmDRepRetirement } from '@lace/core'; -import { SignTxData } from './types'; -import { certificateInspectorFactory, drepIDasBech32FromHash, getOwnRetirementMessageKey } from './utils'; +import { certificateInspectorFactory, drepIDasBech32FromHash } from './utils'; import { Wallet } from '@lace/cardano'; import { useWalletStore } from '@src/stores'; -import { useIsOwnPubDRepKey } from './hooks'; - -const { CertificateType } = Wallet.Cardano; +import { SignTxData } from './types'; +import { useGetOwnPubDRepKeyHash } from './hooks'; +import { Skeleton } from 'antd'; +import { exposeApi, RemoteApiPropertyType } from '@cardano-sdk/web-extension'; +import { UserPromptService } from '@lib/scripts/background/services'; +import { of } from 'rxjs'; +import { ApiError, APIErrorCode } from '@cardano-sdk/dapp-connector'; +import { DAPP_CHANNELS } from '@utils/constants'; +import { runtime } from 'webextension-polyfill'; +import { DappError } from '../DappError'; interface Props { signTxData: SignTxData; errorMessage?: string; + onError: () => void; } -export const ConfirmDRepRetirementContainer = ({ signTxData, errorMessage }: Props): React.ReactElement => { +export const disallowSignTx = (): void => { + exposeApi>( + { + api$: of({ + async allowSignTx(): Promise { + return Promise.reject(new ApiError(APIErrorCode.InvalidRequest, 'DRep ID mismatch')); + } + }), + baseChannel: DAPP_CHANNELS.userPrompt, + properties: { allowSignTx: RemoteApiPropertyType.MethodReturningPromise } + }, + { logger: console, runtime } + ); +}; + +export const ConfirmDRepRetirementContainer = ({ signTxData, onError, errorMessage }: Props): React.ReactElement => { const { t } = useTranslation(); const { - walletUI: { cardanoCoin }, - inMemoryWallet + walletUI: { cardanoCoin } } = useWalletStore(); const certificate = certificateInspectorFactory( - CertificateType.UnregisterDelegateRepresentative + Wallet.Cardano.CertificateType.UnregisterDelegateRepresentative )(signTxData.tx); const depositPaidWithCardanoSymbol = `${Wallet.util.lovelacesToAdaString(certificate.deposit.toString())} ${ cardanoCoin.symbol }`; + const { loading: loadingOwnPubDRepKeyHash, ownPubDRepKeyHash } = useGetOwnPubDRepKeyHash(); + const isNotOwnDRepKey = certificate.dRepCredential.hash !== ownPubDRepKeyHash; + + if (loadingOwnPubDRepKeyHash) { + return ; + } - const isOwnRetirement = useIsOwnPubDRepKey(inMemoryWallet.getPubDRepKey, certificate.dRepCredential.hash); - const ownRetirementMessageKey = getOwnRetirementMessageKey(isOwnRetirement); + if (isNotOwnDRepKey) { + return ( + { + disallowSignTx(); + onError(); + }} + containerTestId="drep-id-mismatch-container" + imageTestId="drep-id-mismatch-image" + titleTestId="drep-id-mismatch-heading" + descriptionTestId="drep-id-mismatch-description" + closeButtonTestId="drep-id-mismatch-close-button" + /> + ); + } return ( ); }; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.module.scss b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.module.scss index 9d25c19f3c..3376b96ac5 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.module.scss +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.module.scss @@ -1,30 +1,26 @@ @import '../../../../../../../packages/common/src/ui/styles/theme.scss'; @import '../../../../../src/styles/rules/flex.scss'; -.actions { - display: flex; - gap: size_unit(1); - justify-content: space-evenly; - flex-direction: column; -} - .spaceBetween { justify-content: space-between; padding-top: size_unit(2); } +.layoutError { + padding: 0; +} + .actions { - background-color: var(--bg-color-body); @extend %flex-column; - justify-content: center; + background-color: var(--bg-color-body); gap: size_unit(1); padding: size_unit(2) size_unit(3) size_unit(2) size_unit(3); border-top: 2px solid var(--light-mode-light-grey-plus, var(--dark-mode-mid-grey)); margin: size_unit(4) size_unit(-3) size_unit(-2) size_unit(-3); position: sticky; bottom: 0; + z-index: 10; .actionBtn { width: 100%; } - z-index: 10; } diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.tsx index f90eea8d72..e17e48769d 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.tsx @@ -1,5 +1,6 @@ /* eslint-disable no-console */ -import React, { useMemo } from 'react'; +import React, { useMemo, useState } from 'react'; +import cn from 'classnames'; import { Button, PostHogAction } from '@lace/common'; import { useTranslation } from 'react-i18next'; import { Layout } from '../Layout'; @@ -38,7 +39,8 @@ export const ConfirmTransaction = (): React.ReactElement => { ); const { getKeyAgentType } = useWalletStore(); const analytics = useAnalyticsContext(); - const { signTxData, errorMessage } = useSignTxData(dappDataApi.getSignTxData); + const { signTxData, errorMessage: getSignTxDataError } = useSignTxData(dappDataApi.getSignTxData); + const [confirmTransactionError, setConfirmTransactionError] = useState(false); const keyAgentType = getKeyAgentType(); const isUsingHardwareWallet = keyAgentType !== Wallet.KeyManagement.KeyAgentType.InMemory; const disallowSignTx = useDisallowSignTx(); @@ -62,29 +64,40 @@ export const ConfirmTransaction = (): React.ReactElement => { useOnBeforeUnload(disallowSignTx); return ( - - -
- - -
+ + setConfirmTransactionError(true)} + errorMessage={getSignTxDataError} + /> + {!confirmTransactionError && ( +
+ + +
+ )}
); }; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransactionContent.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransactionContent.tsx index eb931efab5..4d35efc983 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransactionContent.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransactionContent.tsx @@ -13,9 +13,10 @@ interface Props { txType?: Wallet.Cip30TxType; signTxData?: SignTxData; errorMessage?: string; + onError?: () => void; } -export const ConfirmTransactionContent = ({ txType, signTxData, errorMessage }: Props): React.ReactElement => { +export const ConfirmTransactionContent = ({ txType, signTxData, onError, errorMessage }: Props): React.ReactElement => { if (!signTxData) { return ; } @@ -23,7 +24,7 @@ export const ConfirmTransactionContent = ({ txType, signTxData, errorMessage }: return ; } if (txType === Wallet.Cip30TxType.DRepRetirement) { - return ; + return ; } if (txType === Wallet.Cip30TxType.DRepUpdate) { return ; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepRetirementContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepRetirementContainer.test.tsx index bb0fe2b214..570bbdb16e 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepRetirementContainer.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepRetirementContainer.test.tsx @@ -2,11 +2,21 @@ /* eslint-disable unicorn/no-null */ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable import/imports-first */ + +import { UserPromptService } from '@lib/scripts/background/services'; +import { ExposeApiProps } from '@cardano-sdk/web-extension'; + const mockUseWalletStore = jest.fn(); const t = jest.fn().mockImplementation((res) => res); const mockUseTranslation = jest.fn(() => ({ t })); const mockConfirmDRepRetirement = jest.fn(); -const mockPubDRepKeyToHash = jest.fn(); +const mockDappError = jest.fn(); +const mockUseGetOwnPubDRepKeyHash = jest.fn(); +const mockExposeApi = jest.fn((props: ExposeApiProps>) => { + let returnValue; + props.api$.forEach((v) => (returnValue = v.allowSignTx())); + return returnValue; +}); import * as React from 'react'; import { cleanup, render } from '@testing-library/react'; import { ConfirmDRepRetirementContainer } from '../ConfirmDRepRetirementContainer'; @@ -29,7 +39,6 @@ import { postHogClientMocks } from '@src/utils/mocks/test-helpers'; import { buildMockTx } from '@src/utils/mocks/tx'; import { Wallet } from '@lace/cardano'; import BigNumber from 'bignumber.js'; - const LOVELACE_VALUE = 1_000_000; const DEFAULT_DECIMALS = 2; @@ -55,6 +64,24 @@ const cardanoCoinMock = { symbol: 'cardanoCoinMockSymbol' }; +jest.mock('@cardano-sdk/web-extension', () => { + const original = jest.requireActual('@cardano-sdk/web-extension'); + return { + __esModule: true, + ...original, + exposeApi: mockExposeApi + }; +}); + +jest.mock('../hooks.ts', () => { + const original = jest.requireActual('../hooks.ts'); + return { + __esModule: true, + ...original, + useGetOwnPubDRepKeyHash: mockUseGetOwnPubDRepKeyHash + }; +}); + jest.mock('@src/stores', () => ({ ...jest.requireActual('@src/stores'), useWalletStore: mockUseWalletStore @@ -69,21 +96,21 @@ jest.mock('@lace/core', () => { }; }); -jest.mock('react-i18next', () => { - const original = jest.requireActual('react-i18next'); +jest.mock('../../DappError', () => { + const original = jest.requireActual('../../DappError'); return { __esModule: true, ...original, - useTranslation: mockUseTranslation + DappError: mockDappError }; }); -jest.mock('../utils.ts', () => { - const original = jest.requireActual('../utils.ts'); +jest.mock('react-i18next', () => { + const original = jest.requireActual('react-i18next'); return { __esModule: true, ...original, - pubDRepKeyToHash: mockPubDRepKeyToHash + useTranslation: mockUseTranslation }; }); @@ -114,6 +141,11 @@ const getWrapper = describe('Testing ConfirmDRepRetirementContainer component', () => { beforeEach(() => { mockUseWalletStore.mockReset(); + mockExposeApi.mockRestore(); + mockUseGetOwnPubDRepKeyHash.mockImplementationOnce(() => ({ + loading: false, + ownPubDRepKeyHash: hash + })); mockUseWalletStore.mockImplementation(() => ({ inMemoryWallet, walletUI: { cardanoCoin: cardanoCoinMock }, @@ -121,8 +153,8 @@ describe('Testing ConfirmDRepRetirementContainer component', () => { })); mockConfirmDRepRetirement.mockReset(); mockConfirmDRepRetirement.mockReturnValue(); - mockPubDRepKeyToHash.mockReset(); - mockPubDRepKeyToHash.mockImplementation(async () => await '123'); + mockDappError.mockReset(); + mockDappError.mockReturnValue(); mockUseTranslation.mockReset(); mockUseTranslation.mockImplementation(() => ({ t })); }); @@ -150,12 +182,14 @@ describe('Testing ConfirmDRepRetirementContainer component', () => { certificates: [certificate] }); const errorMessage = 'errorMessage'; + // eslint-disable-next-line unicorn/consistent-function-scoping + const onErrorMock = jest.fn(); test('should render ConfirmDRepRetirementContainer component with proper props', async () => { let queryByTestId: any; await act(async () => { ({ queryByTestId } = render( - , + , { wrapper: getWrapper() } @@ -186,13 +220,14 @@ describe('Testing ConfirmDRepRetirementContainer component', () => { }); test('should render ConfirmDRepRetirementContainer component with proper error for own retirement', async () => { - mockPubDRepKeyToHash.mockReset(); - mockPubDRepKeyToHash.mockImplementation(async (_hash) => await _hash); let queryByTestId: any; await act(async () => { - ({ queryByTestId } = render(, { - wrapper: getWrapper() - })); + ({ queryByTestId } = render( + , + { + wrapper: getWrapper() + } + )); }); expect(queryByTestId('ConfirmDRepRetirementContainer')).toBeInTheDocument(); @@ -202,19 +237,21 @@ describe('Testing ConfirmDRepRetirementContainer component', () => { }); test('should render ConfirmDRepRetirementContainer component with proper error for not own retirement', async () => { - mockPubDRepKeyToHash.mockReset(); - mockPubDRepKeyToHash.mockImplementation(async () => await ''); + mockUseGetOwnPubDRepKeyHash.mockReset(); + mockUseGetOwnPubDRepKeyHash.mockImplementation(() => ({ + loading: false, + ownPubDRepKeyHash: Crypto.Hash28ByteBase16(Buffer.from('WRONG_dRepCredentialHashdRep').toString('hex')) + })); let queryByTestId: any; await act(async () => { - ({ queryByTestId } = render(, { - wrapper: getWrapper() - })); + ({ queryByTestId } = render( + , + { + wrapper: getWrapper() + } + )); }); - expect(queryByTestId('ConfirmDRepRetirementContainer')).toBeInTheDocument(); - - expect( - mockConfirmDRepRetirement.mock.calls[mockConfirmDRepRetirement.mock.calls.length - 1][0].errorMessage - ).toEqual(t('core.DRepRetirement.isNotOwnRetirement')); + expect(queryByTestId('DappError')).toBeInTheDocument(); }); }); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmTransaction.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmTransaction.test.tsx index 351276597b..6be8a2a2c9 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmTransaction.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmTransaction.test.tsx @@ -57,6 +57,11 @@ jest.mock('@src/stores', () => ({ useWalletStore: mockUseWalletStore })); +jest.mock('@stores', () => ({ + ...jest.requireActual('@stores'), + useWalletStore: mockUseWalletStore +})); + jest.mock('@cardano-sdk/web-extension', () => { const original = jest.requireActual('@cardano-sdk/web-extension'); return { @@ -165,8 +170,6 @@ describe('Testing ConfirmTransaction component', () => { mockUseViewsFlowContext.mockReturnValue({ utils: {} }); mockConfirmTransactionContent.mockReset(); mockConfirmTransactionContent.mockImplementation(() => ); - mockConfirmTransactionContent.mockReset(); - mockConfirmTransactionContent.mockImplementation(() => ); }); afterEach(() => { @@ -217,7 +220,9 @@ describe('Testing ConfirmTransaction component', () => { expect(mockConfirmTransactionContent).toHaveBeenLastCalledWith( { txType, - signTxData + signTxData, + errorMessage: undefined, + onError: expect.any(Function) }, {} ); @@ -300,7 +305,10 @@ describe('Testing ConfirmTransaction component', () => { }); expect(queryByTestId('ConfirmTransactionContent')).toBeInTheDocument(); - expect(mockConfirmTransactionContent).toHaveBeenLastCalledWith({ errorMessage: error }, {}); + expect(mockConfirmTransactionContent).toHaveBeenLastCalledWith( + { errorMessage: error, onError: expect.any(Function), signTxData: undefined, txType: undefined }, + {} + ); expect(queryByTestId(testIds.dappTransactionConfirm).closest('button')).toHaveAttribute('disabled'); }); }); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/hooks.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/hooks.test.tsx index 609255a03f..cddbbb6c29 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/hooks.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/hooks.test.tsx @@ -12,10 +12,11 @@ const mockGetTransactionAssetsId = jest.fn(); const mockGetAssetsInformation = jest.fn(); const mockCalculateAssetBalance = jest.fn(); const mockLovelacesToAdaString = jest.fn(); +const mockUseWalletStore = jest.fn(); import { act, cleanup } from '@testing-library/react'; import { useCreateAssetList, - useIsOwnPubDRepKey, + useGetOwnPubDRepKeyHash, useOnBeforeUnload, useSignTxData, useSignWithHardwareWallet, @@ -30,6 +31,11 @@ import { AddressListType } from '@src/views/browser-view/features/activity'; import { WalletInfo } from '@src/types'; import * as Core from '@cardano-sdk/core'; +jest.mock('@stores', () => ({ + ...jest.requireActual('@stores'), + useWalletStore: mockUseWalletStore +})); + jest.mock('@cardano-sdk/core', () => ({ ...jest.requireActual('@cardano-sdk/core'), createTxInspector: jest.fn() @@ -398,27 +404,25 @@ describe('Testing hooks', () => { removeEventListeners(); }); - test('useIsOwnPubDRepKey', async () => { + test('useGetOwnPubDRepKeyHash', async () => { const ed25519PublicKeyHexMock = 'ed25519PublicKeyHexMock'; mockPubDRepKeyToHash.mockReset(); mockPubDRepKeyToHash.mockImplementation(async (val: Wallet.Crypto.Ed25519PublicKeyHex) => await val); - - const hook = renderHook(() => - useIsOwnPubDRepKey( - async () => (await ed25519PublicKeyHexMock) as Wallet.Crypto.Ed25519PublicKeyHex, - ed25519PublicKeyHexMock as Wallet.Crypto.Hash28ByteBase16 - ) - ); - await hook.waitFor(() => { - expect(hook.result.current).toBe(true); + mockUseWalletStore.mockReset(); + mockUseWalletStore.mockReturnValue({ + inMemoryWallet: { + getPubDRepKey: jest.fn(async () => await ed25519PublicKeyHexMock) + } }); - mockPubDRepKeyToHash.mockReset(); - mockPubDRepKeyToHash.mockImplementation(async () => await 1); - hook.rerender(); + let hook: any; + await act(async () => { + hook = renderHook(() => useGetOwnPubDRepKeyHash()); + expect(hook.result.current.loading).toEqual(true); + }); await hook.waitFor(() => { - expect(hook.result.current).toBe(false); + expect(hook.result.current.ownPubDRepKeyHash).toEqual(ed25519PublicKeyHexMock); }); }); }); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/utils.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/utils.test.tsx index e420b4bdc2..d998977a9f 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/utils.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/utils.test.tsx @@ -16,7 +16,6 @@ import { certificateInspectorFactory, votingProceduresInspector, getTxType, - getOwnRetirementMessageKey, drepIDasBech32FromHash, pubDRepKeyToHash } from '../utils'; @@ -148,10 +147,4 @@ describe('Testing utils', () => { const pubDRepKeyHex = '_pubDRepKeyHex'; expect(await pubDRepKeyToHash(pubDRepKeyHex as Wallet.Crypto.Ed25519PublicKeyHex)).toEqual(pubDRepKeyHex); }); - - test('testing getOwnRetirementMessageKey', () => { - expect(getOwnRetirementMessageKey(undefined)).toEqual(''); - expect(getOwnRetirementMessageKey(true)).toEqual('core.DRepRetirement.isOwnRetirement'); - expect(getOwnRetirementMessageKey(false)).toEqual('core.DRepRetirement.isNotOwnRetirement'); - }); }); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/hooks.ts b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/hooks.ts index 4b3fc82e4c..602b689441 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/hooks.ts +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/hooks.ts @@ -17,6 +17,7 @@ import { getTransactionAssetsId } from '@src/stores/slices'; import { AddressListType } from '@src/views/browser-view/features/activity'; import { allowSignTx, pubDRepKeyToHash, disallowSignTx, getTxType } from './utils'; import { GetSignTxData, SignTxData } from './types'; +import { useWalletStore } from '@stores'; export const useCreateAssetList = ({ assets, @@ -163,6 +164,7 @@ export const useSignTxData = (getSignTxData: GetSignTxData): { signTxData?: Sign }) .catch((error) => { setErrorMessage(error); + // TODO: consider mocking or removing this log console.error(error); }); }, [getSignTxData, setSignTxData, setErrorMessage]); @@ -262,22 +264,27 @@ export const useOnBeforeUnload = (callBack: () => void): void => { }, [callBack]); }; -export const useIsOwnPubDRepKey = ( - getOwnPubDRepKey: () => Promise, - drepHash: Wallet.Crypto.Hash28ByteBase16 -): boolean => { - const [isOwnDRepKey, setIsOwnDRepKey] = useState(); +type UseGetOwnPubDRepKeyHash = { + loading: boolean; + ownPubDRepKeyHash: Wallet.Crypto.Hash28ByteBase16; +}; + +export const useGetOwnPubDRepKeyHash = (): UseGetOwnPubDRepKeyHash => { + const [ownPubDRepKeyHash, setOwnPubDRepKeyHash] = useState(); + const { inMemoryWallet } = useWalletStore(); useEffect(() => { + if (!inMemoryWallet) return; const get = async () => { - const ownPubDRepKey = await getOwnPubDRepKey(); + const ownPubDRepKey = await inMemoryWallet.getPubDRepKey(); const ownDRepKeyHash = await pubDRepKeyToHash(ownPubDRepKey); - setIsOwnDRepKey(drepHash === ownDRepKeyHash); + setOwnPubDRepKeyHash(ownDRepKeyHash); }; get(); - }, [getOwnPubDRepKey, drepHash]); + }, [inMemoryWallet]); - return isOwnDRepKey; + // TODO consider using Zustand or at least some common abstraction e.g. https://github.com/streamich/react-use/blob/master/src/useAsync.ts + return { loading: ownPubDRepKeyHash === undefined, ownPubDRepKeyHash }; }; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/utils.ts b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/utils.ts index e17be60297..681e9a86cc 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/utils.ts +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/utils.ts @@ -120,10 +120,3 @@ export const pubDRepKeyToHash = async ( const drepKeyHex = (await pubDRepKey.hash()).hex(); return Wallet.Crypto.Hash28ByteBase16.fromEd25519KeyHashHex(drepKeyHex); }; - -export const getOwnRetirementMessageKey = (isOwnRetirement: boolean | undefined): string => { - if (isOwnRetirement === undefined) { - return ''; - } - return isOwnRetirement ? 'core.DRepRetirement.isOwnRetirement' : 'core.DRepRetirement.isNotOwnRetirement'; -}; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/index.ts b/apps/browser-extension-wallet/src/features/dapp/components/index.ts index 4d3125556d..c96197a717 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/index.ts +++ b/apps/browser-extension-wallet/src/features/dapp/components/index.ts @@ -2,7 +2,6 @@ export * from './Connect'; export * from './SignTxFlowContainer'; export * from './DappTransactionSuccess'; export * from './DappTransactionFail'; -export * from './NoWallet'; export * from './ConfirmData'; export * from './BetaPill'; export * from './SignDataFlowContainer'; diff --git a/apps/browser-extension-wallet/src/lib/translations/en.json b/apps/browser-extension-wallet/src/lib/translations/en.json index 8bfd3ccbe9..4f53c319ed 100644 --- a/apps/browser-extension-wallet/src/lib/translations/en.json +++ b/apps/browser-extension-wallet/src/lib/translations/en.json @@ -328,7 +328,7 @@ "copyHandle": "Copy handle" }, "dapp": { - "nowallet.btn": "Create or restore a wallet", + "dappErrorPage.closeButton": "Cancel", "connect.header": "Authorize DApp", "connect.btn.accept": "Authorize", "connect.btn.cancel": "Cancel", @@ -367,6 +367,7 @@ "delete.confirm": "Disconnect DApp", "noWallet.heading": "You don't have a wallet right now", "noWallet.description": "You'll need to create or restore a wallet to connect to a dApp or make a transaction.", + "noWallet.closeButton": "Create or restore a wallet", "educationBanner.title": "DApp Guide", "betaModal": { "header": "DApp connector is now in Beta", @@ -1168,8 +1169,11 @@ "metadata": "Metadata", "drepId": "DRep ID", "depositReturned": "Deposit returned", - "isOwnRetirement": "This is your DRep retirement.", - "isNotOwnRetirement": "The presented DRepID does not match your wallet's DRepID." + "drepIdMismatchScreen": { + "title": "DRep ID mismatch", + "description": "The presented DRepID does not match your wallet's DRepID", + "cancel": "Cancel" + } }, "DRepUpdate": { "title": "Confirm DRep Update", diff --git a/apps/browser-extension-wallet/src/routes/DappConnectorView.tsx b/apps/browser-extension-wallet/src/routes/DappConnectorView.tsx index e223e57d02..5553484572 100644 --- a/apps/browser-extension-wallet/src/routes/DappConnectorView.tsx +++ b/apps/browser-extension-wallet/src/routes/DappConnectorView.tsx @@ -11,7 +11,6 @@ import { Connect as DappConnect, SignTxFlowContainer, SignDataFlowContainer, - NoWallet, DappTransactionSuccess, DappTransactionFail, DappCollateralContainer @@ -23,6 +22,9 @@ import { lockWalletSelector } from '@src/features/unlock-wallet/selectors'; import { useAppSettingsContext } from '@providers'; import dayjs from 'dayjs'; import duration from 'dayjs/plugin/duration'; +import { DappError } from '@src/features/dapp/components/DappError'; +import { tabs } from 'webextension-polyfill'; +import { useTranslation } from 'react-i18next'; dayjs.extend(duration); @@ -34,6 +36,7 @@ const isLastValidationExpired = (lastVerification: string, frequency: string): b // TODO: unify providers and logic to load wallet and such for popup, dapp and browser view in one place [LW-5341] export const DappConnectorView = (): React.ReactElement => { + const { t } = useTranslation(); const [{ lastMnemonicVerification, mnemonicVerificationFrequency }] = useAppSettingsContext(); const { inMemoryWallet, keyAgentData, currentChain, walletInfo, setKeyAgentData, initialHdDiscoveryCompleted } = useWalletStore(); @@ -76,7 +79,19 @@ export const DappConnectorView = (): React.ReactElement => { if (hasNoAvailableWallet) { return ( - + { + tabs.create({ url: 'app.html#/setup' }); + }} + containerTestId="no-wallet-container" + imageTestId="no-wallet-image" + titleTestId="no-wallet-heading" + descriptionTestId="no-wallet-description" + closeButtonTestId="create-or-restore-wallet-btn" + /> ); } @@ -97,7 +112,6 @@ export const DappConnectorView = (): React.ReactElement => { return ( - From bc0cf3b59ecb1ddd7da837efc5d0250fc3558ed9 Mon Sep 17 00:00:00 2001 From: Michael Chappell <7581002+mchappell@users.noreply.github.com> Date: Wed, 29 Nov 2023 13:27:37 +0000 Subject: [PATCH 22/68] chore: update cardano-sdk packages --- apps/browser-extension-wallet/package.json | 16 +- packages/cardano/package.json | 20 +- packages/common/package.json | 2 +- packages/staking/package.json | 16 +- yarn.lock | 362 +++++++++++++-------- 5 files changed, 249 insertions(+), 167 deletions(-) diff --git a/apps/browser-extension-wallet/package.json b/apps/browser-extension-wallet/package.json index b8cabd7278..53c9439733 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.14.4", - "@cardano-sdk/core": "0.21.0", - "@cardano-sdk/dapp-connector": "0.11.1", - "@cardano-sdk/input-selection": "0.12.4", - "@cardano-sdk/tx-construction": "0.14.2", - "@cardano-sdk/util": "0.14.2", - "@cardano-sdk/wallet": "0.25.1", - "@cardano-sdk/web-extension": "0.16.3", + "@cardano-sdk/cardano-services-client": "0.16.0", + "@cardano-sdk/core": "0.22.2", + "@cardano-sdk/dapp-connector": "0.11.4", + "@cardano-sdk/input-selection": "0.12.7", + "@cardano-sdk/tx-construction": "0.16.0", + "@cardano-sdk/util": "0.14.5", + "@cardano-sdk/wallet": "0.27.1", + "@cardano-sdk/web-extension": "0.17.1", "@emurgo/cip14-js": "~3.0.1", "@koralabs/handles-public-api-interfaces": "^1.6.6", "@lace/cardano": "0.1.0", diff --git a/packages/cardano/package.json b/packages/cardano/package.json index f39a733603..4f8fcf1f6b 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.14.4", - "@cardano-sdk/core": "0.21.0", - "@cardano-sdk/crypto": "0.1.15", - "@cardano-sdk/hardware-ledger": "0.6.1", - "@cardano-sdk/hardware-trezor": "0.2.1", - "@cardano-sdk/key-management": "0.14.0", - "@cardano-sdk/util": "0.14.2", - "@cardano-sdk/wallet": "0.25.1", - "@cardano-sdk/web-extension": "0.16.3", + "@cardano-sdk/cardano-services-client": "0.15.1", + "@cardano-sdk/core": "0.22.2", + "@cardano-sdk/crypto": "0.1.18", + "@cardano-sdk/hardware-ledger": "0.8.0", + "@cardano-sdk/hardware-trezor": "0.4.0", + "@cardano-sdk/key-management": "0.16.0", + "@cardano-sdk/util": "0.14.5", + "@cardano-sdk/wallet": "0.27.1", + "@cardano-sdk/web-extension": "0.17.1", "@dcspark/cardano-multiplatform-lib-browser": "^3.1.1", "@lace/common": "0.1.0", "@stablelib/chacha20poly1305": "1.0.1", @@ -68,7 +68,7 @@ "webextension-polyfill": "0.8.0" }, "devDependencies": { - "@cardano-sdk/util-dev": "0.17.3", + "@cardano-sdk/util-dev": "0.19.0", "@dcspark/cardano-multiplatform-lib-nodejs": "^3.1.1", "@emurgo/cardano-message-signing-browser": "1.0.1", "rollup-plugin-polyfill-node": "^0.8.0", diff --git a/packages/common/package.json b/packages/common/package.json index e8e8508be5..29f1ac5c6f 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -38,7 +38,7 @@ "watch": "yarn build --watch" }, "dependencies": { - "@cardano-sdk/util": "0.14.2", + "@cardano-sdk/util": "0.14.5", "antd": "^4.24.10", "classnames": "^2.3.1", "jdenticon": "3.1.0", diff --git a/packages/staking/package.json b/packages/staking/package.json index bc2f8734e9..89d861650e 100644 --- a/packages/staking/package.json +++ b/packages/staking/package.json @@ -66,10 +66,10 @@ "zustand": "^4.4.1" }, "devDependencies": { - "@cardano-sdk/core": "0.21.0", - "@cardano-sdk/input-selection": "0.12.4", - "@cardano-sdk/tx-construction": "0.14.2", - "@cardano-sdk/util": "0.14.2", + "@cardano-sdk/core": "0.22.2", + "@cardano-sdk/input-selection": "0.12.7", + "@cardano-sdk/tx-construction": "0.16.0", + "@cardano-sdk/util": "0.14.5", "@lace/cardano": "^0.1.0", "@lace/common": "^0.1.0", "@lace/core": "0.1.0", @@ -100,10 +100,10 @@ "wait-on": "^7.0.1" }, "peerDependencies": { - "@cardano-sdk/input-selection": "0.12.4", - "@cardano-sdk/tx-construction": "0.14.2", - "@cardano-sdk/util": "0.14.2", - "@cardano-sdk/wallet": "0.25.1", + "@cardano-sdk/input-selection": "0.12.7", + "@cardano-sdk/tx-construction": "0.16.0", + "@cardano-sdk/util": "0.14.5", + "@cardano-sdk/wallet": "0.27.1", "@lace/cardano": "^0.1.0", "@lace/common": "^0.1.0", "@lace/core": "0.1.0", diff --git a/yarn.lock b/yarn.lock index 3816aa8016..006bf92aa7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6651,28 +6651,42 @@ __metadata: languageName: node linkType: hard -"@cardano-sdk/cardano-services-client@npm:0.14.4": - version: 0.14.4 - resolution: "@cardano-sdk/cardano-services-client@npm:0.14.4" +"@cardano-sdk/cardano-services-client@npm:0.15.1": + version: 0.15.1 + resolution: "@cardano-sdk/cardano-services-client@npm:0.15.1" dependencies: - "@cardano-sdk/core": ~0.21.0 - "@cardano-sdk/util": ~0.14.2 + "@cardano-sdk/core": ~0.22.1 + "@cardano-sdk/util": ~0.14.4 axios: ^0.27.2 class-validator: ^0.14.0 json-bigint: ~1.0.0 ts-log: ^2.2.4 - checksum: 528d7687ac239af55c0997547532f5ce36706820676f0c493a83caa798a09070b7b0614d9d309488084724790c6e2e85d172cb1e05881ed7d95f8b6b2e6d1dbb + checksum: ae3375d9171dbc7a0972756f2ad53a97b8a76986b18d902334d31d46439db21ce6a059449d29ce8d1e7013e70ba929279b569f3a61d8c25184c50c4929936ef1 languageName: node linkType: hard -"@cardano-sdk/core@npm:0.21.0, @cardano-sdk/core@npm:~0.21.0": - version: 0.21.0 - resolution: "@cardano-sdk/core@npm:0.21.0" +"@cardano-sdk/cardano-services-client@npm:0.16.0": + version: 0.16.0 + resolution: "@cardano-sdk/cardano-services-client@npm:0.16.0" + dependencies: + "@cardano-sdk/core": ~0.22.2 + "@cardano-sdk/util": ~0.14.5 + axios: ^0.27.2 + class-validator: ^0.14.0 + json-bigint: ~1.0.0 + ts-log: ^2.2.4 + checksum: a0e8f607b729ad7931f03f1b5b1c0083f8c3954120372de17d37025b7ddf07df43a7ef7db2d84b9787efb52f0e9115461a99d9db7051448abf7e6980f782752c + languageName: node + linkType: hard + +"@cardano-sdk/core@npm:0.22.2, @cardano-sdk/core@npm:~0.22.2": + version: 0.22.2 + resolution: "@cardano-sdk/core@npm:0.22.2" dependencies: "@cardano-ogmios/client": 5.6.0 "@cardano-ogmios/schema": 5.6.0 - "@cardano-sdk/crypto": ~0.1.15 - "@cardano-sdk/util": ~0.14.2 + "@cardano-sdk/crypto": ~0.1.18 + "@cardano-sdk/util": ~0.14.5 "@foxglove/crc": ^0.0.3 "@scure/base": ^1.1.1 fraction.js: 4.0.1 @@ -6686,15 +6700,40 @@ __metadata: peerDependenciesMeta: rxjs: optional: true - checksum: 5228b248d511158e3aa02fc6468ee66906194e1aa38d53abc78800d7d6914e0d3d0af47e086786fb6e1e4d5115f94b23ab16d88019f62559cee98b17e4985ef7 + checksum: ad4bef7f6dcd54664cd83172cf613312387ed85a7a6ac849de6d909c4462c68749db33d72be91d54ab7e97e062634e53896cff6baa97588cbab9dce7ac816b78 languageName: node linkType: hard -"@cardano-sdk/crypto@npm:0.1.15, @cardano-sdk/crypto@npm:~0.1.15": - version: 0.1.15 - resolution: "@cardano-sdk/crypto@npm:0.1.15" +"@cardano-sdk/core@npm:~0.22.1": + version: 0.22.1 + resolution: "@cardano-sdk/core@npm:0.22.1" + dependencies: + "@cardano-ogmios/client": 5.6.0 + "@cardano-ogmios/schema": 5.6.0 + "@cardano-sdk/crypto": ~0.1.17 + "@cardano-sdk/util": ~0.14.4 + "@foxglove/crc": ^0.0.3 + "@scure/base": ^1.1.1 + fraction.js: 4.0.1 + ip-address: ^8.1.0 + lodash: ^4.17.21 + ts-custom-error: ^3.2.0 + ts-log: ^2.2.4 + web-encoding: ^1.1.5 + peerDependencies: + rxjs: ^7.4.0 + peerDependenciesMeta: + rxjs: + optional: true + checksum: 17287936a8ab3fd0efe7bf1ef443f9a032517e7648e09f89fb4261574f3366a210d15446d58a0a992925690de480a910d030b9b0ba29fbc19a6dd76e19d962e4 + languageName: node + linkType: hard + +"@cardano-sdk/crypto@npm:0.1.18, @cardano-sdk/crypto@npm:~0.1.18": + version: 0.1.18 + resolution: "@cardano-sdk/crypto@npm:0.1.18" dependencies: - "@cardano-sdk/util": ~0.14.2 + "@cardano-sdk/util": ~0.14.5 blake2b: ^2.1.4 bn.js: ^5.2.1 i: ^0.3.7 @@ -6715,84 +6754,113 @@ __metadata: optional: true "@dcspark/cardano-multiplatform-lib-nodejs": optional: true - checksum: fe446eca9df9293424909d67a0408c3c780a46a77cf1bf7d762b252ce1d5c3b3f5611b54e198bb3cfa2713d18e9eded7578664056a3a58f1c6a3b8a5caae54a3 + checksum: c1acde4d22b47a38f4de3eaf9af4c242e92615ca60c605942e5d82eaa516fc9b241111aa1664ca7307b52865aae258f2e1d48e4dae7600714bda5aa67ceb7928 languageName: node linkType: hard -"@cardano-sdk/dapp-connector@npm:0.11.1, @cardano-sdk/dapp-connector@npm:~0.11.1": - version: 0.11.1 - resolution: "@cardano-sdk/dapp-connector@npm:0.11.1" +"@cardano-sdk/crypto@npm:~0.1.17": + version: 0.1.17 + resolution: "@cardano-sdk/crypto@npm:0.1.17" dependencies: - "@cardano-sdk/core": ~0.21.0 - "@cardano-sdk/crypto": ~0.1.15 - "@cardano-sdk/util": ~0.14.2 + "@cardano-sdk/util": ~0.14.4 + blake2b: ^2.1.4 + bn.js: ^5.2.1 + i: ^0.3.7 + libsodium-wrappers-sumo: ^0.7.5 + lodash: ^4.17.21 + npm: ^9.3.0 + pbkdf2: ^3.1.2 + ts-custom-error: ^3.2.0 + ts-log: ^2.2.4 + peerDependencies: + "@dcspark/cardano-multiplatform-lib-asmjs": ^3.1.1 + "@dcspark/cardano-multiplatform-lib-browser": ^3.1.1 + "@dcspark/cardano-multiplatform-lib-nodejs": ^3.1.1 + peerDependenciesMeta: + "@dcspark/cardano-multiplatform-lib-asmjs": + optional: true + "@dcspark/cardano-multiplatform-lib-browser": + optional: true + "@dcspark/cardano-multiplatform-lib-nodejs": + optional: true + checksum: eddc9bc50acc20cea8d383b23800733780b426dd2a96b5afd2100cbbbffbf1db76afb78a481018306a346e8377aa7bba6efb00e30af70517501959b0c9e0734b + languageName: node + linkType: hard + +"@cardano-sdk/dapp-connector@npm:0.11.4, @cardano-sdk/dapp-connector@npm:~0.11.4": + version: 0.11.4 + resolution: "@cardano-sdk/dapp-connector@npm:0.11.4" + dependencies: + "@cardano-sdk/core": ~0.22.2 + "@cardano-sdk/crypto": ~0.1.18 + "@cardano-sdk/util": ~0.14.5 ts-custom-error: ^3.2.0 ts-log: ^2.2.4 webextension-polyfill: ^0.8.0 - checksum: 5ba41923642f85771a72c452fec5c3c602cad11ac1869dbf29172aae132d7584718b0c7c47f7996261744f5829529e0187fac3a48397c0931efd5d09bfe6e1ef + checksum: efc16b59070d5a8923809ef1413ca9e29ff86aa5cebe37becb2752515717199bdeec6f16e65a12fea4521bf25bcdc0cb463784843af2dd2e00b197a609d4cc3b languageName: node linkType: hard -"@cardano-sdk/hardware-ledger@npm:0.6.1, @cardano-sdk/hardware-ledger@npm:~0.6.1": - version: 0.6.1 - resolution: "@cardano-sdk/hardware-ledger@npm:0.6.1" +"@cardano-sdk/hardware-ledger@npm:0.8.0, @cardano-sdk/hardware-ledger@npm:~0.8.0": + version: 0.8.0 + resolution: "@cardano-sdk/hardware-ledger@npm:0.8.0" dependencies: "@cardano-foundation/ledgerjs-hw-app-cardano": ^6.0.0 - "@cardano-sdk/core": ~0.21.0 - "@cardano-sdk/crypto": ~0.1.15 - "@cardano-sdk/key-management": ~0.14.0 - "@cardano-sdk/tx-construction": ~0.14.2 - "@cardano-sdk/util": ~0.14.2 + "@cardano-sdk/core": ~0.22.2 + "@cardano-sdk/crypto": ~0.1.18 + "@cardano-sdk/key-management": ~0.16.0 + "@cardano-sdk/tx-construction": ~0.16.0 + "@cardano-sdk/util": ~0.14.5 "@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: a39c9f0ff5c06d012285e8cec33fa13b7d4b660f2a3269acc5af0b26eed2b8ec3ed2c998fa78ac1bf16886705d605d8c16a664801b49a843af1b489c4f8c937f + checksum: e7cd826f0774bc70359ec3092b98a15507c3d4ce3e1e64bad4f492aa53b7be20c796e06979b31803775730701915afc5051fa9c728c98ca23f28b317d430418c languageName: node linkType: hard -"@cardano-sdk/hardware-trezor@npm:0.2.1, @cardano-sdk/hardware-trezor@npm:~0.2.1": - version: 0.2.1 - resolution: "@cardano-sdk/hardware-trezor@npm:0.2.1" +"@cardano-sdk/hardware-trezor@npm:0.4.0, @cardano-sdk/hardware-trezor@npm:~0.4.0": + version: 0.4.0 + resolution: "@cardano-sdk/hardware-trezor@npm:0.4.0" dependencies: - "@cardano-sdk/core": ~0.21.0 - "@cardano-sdk/crypto": ~0.1.15 - "@cardano-sdk/key-management": ~0.14.0 - "@cardano-sdk/tx-construction": ~0.14.2 - "@cardano-sdk/util": ~0.14.2 + "@cardano-sdk/core": ~0.22.2 + "@cardano-sdk/crypto": ~0.1.18 + "@cardano-sdk/key-management": ~0.16.0 + "@cardano-sdk/tx-construction": ~0.16.0 + "@cardano-sdk/util": ~0.14.5 "@trezor/connect": 9.0.11 "@trezor/connect-web": 9.0.11 lodash: ^4.17.21 ts-custom-error: ^3.2.0 ts-log: ^2.2.4 - checksum: 0fa6fd0a3a2ebc20da3278920757e1784e96740c521014b8f76ecd4ae63cc0021a5c567a08b2daefb1085f9248fdea424a99d9c153935947c8c6789b39ffbf40 + checksum: 7aa33ccb67e4987147c5b40f1027671bcff1545f0e0718059a15e4a4192d32c6397baf6ebfff170690b99a42fd87b96cf87641d15c7da9c99b8a6d196e1aeebc languageName: node linkType: hard -"@cardano-sdk/input-selection@npm:0.12.4, @cardano-sdk/input-selection@npm:~0.12.4": - version: 0.12.4 - resolution: "@cardano-sdk/input-selection@npm:0.12.4" +"@cardano-sdk/input-selection@npm:0.12.7, @cardano-sdk/input-selection@npm:~0.12.7": + version: 0.12.7 + resolution: "@cardano-sdk/input-selection@npm:0.12.7" dependencies: - "@cardano-sdk/core": ~0.21.0 - "@cardano-sdk/key-management": ~0.14.0 - "@cardano-sdk/util": ~0.14.2 + "@cardano-sdk/core": ~0.22.2 + "@cardano-sdk/key-management": ~0.16.0 + "@cardano-sdk/util": ~0.14.5 bignumber.js: ^9.1.1 lodash: ^4.17.21 ts-custom-error: ^3.2.0 - checksum: 20511a0a92f064809308c9b3df7a3ed0fe5d6583d5c7752f4610da623d0d8599044423fab718cbe4d488ed02844f8827e4650589124c56bcfeeec51346750f2f + checksum: f92a0dda185c32020739260da9d78e4eccb243e30c82fbf82db4c2cd15ab4780100cacbea7f6a57e69d53e2161d7cacbed8229a4f3e99b04738523850b94f950 languageName: node linkType: hard -"@cardano-sdk/key-management@npm:0.14.0, @cardano-sdk/key-management@npm:~0.14.0": - version: 0.14.0 - resolution: "@cardano-sdk/key-management@npm:0.14.0" +"@cardano-sdk/key-management@npm:0.16.0, @cardano-sdk/key-management@npm:~0.16.0": + version: 0.16.0 + resolution: "@cardano-sdk/key-management@npm:0.16.0" dependencies: "@cardano-foundation/ledgerjs-hw-app-cardano": ^6.0.0 - "@cardano-sdk/core": ~0.21.0 - "@cardano-sdk/crypto": ~0.1.15 - "@cardano-sdk/dapp-connector": ~0.11.1 - "@cardano-sdk/util": ~0.14.2 + "@cardano-sdk/core": ~0.22.2 + "@cardano-sdk/crypto": ~0.1.18 + "@cardano-sdk/dapp-connector": ~0.11.4 + "@cardano-sdk/util": ~0.14.5 "@emurgo/cardano-message-signing-nodejs": ^1.0.1 "@trezor/connect": 9.0.11 "@trezor/connect-web": 9.0.11 @@ -6804,37 +6872,37 @@ __metadata: rxjs: ^7.4.0 ts-custom-error: ^3.2.0 ts-log: ^2.2.4 - checksum: 05de5f69e6665000139bd7fb78f64b2d9b73042e0175c4e87da1f04b7894954e2be0b19417df2e6e532f85132d3791a633211383ef077497b57f1bcadf0aa4a5 + checksum: c4f5d0b791a9aee1348ef9048cdd41aec1ce89827516ae819e62c8b85f98e01b78a1b14b0a44792b8787e4bf270c3429f15eab3c666744e920181466fda3122e languageName: node linkType: hard -"@cardano-sdk/tx-construction@npm:0.14.2, @cardano-sdk/tx-construction@npm:~0.14.2": - version: 0.14.2 - resolution: "@cardano-sdk/tx-construction@npm:0.14.2" - dependencies: - "@cardano-sdk/core": ~0.21.0 - "@cardano-sdk/crypto": ~0.1.15 - "@cardano-sdk/input-selection": ~0.12.4 - "@cardano-sdk/key-management": ~0.14.0 - "@cardano-sdk/util": ~0.14.2 - "@cardano-sdk/util-rxjs": ~0.6.0 +"@cardano-sdk/tx-construction@npm:0.16.0, @cardano-sdk/tx-construction@npm:~0.16.0": + version: 0.16.0 + resolution: "@cardano-sdk/tx-construction@npm:0.16.0" + dependencies: + "@cardano-sdk/core": ~0.22.2 + "@cardano-sdk/crypto": ~0.1.18 + "@cardano-sdk/input-selection": ~0.12.7 + "@cardano-sdk/key-management": ~0.16.0 + "@cardano-sdk/util": ~0.14.5 + "@cardano-sdk/util-rxjs": ~0.6.3 lodash: ^4.17.21 npm: ^9.3.0 rxjs: ^7.4.0 ts-custom-error: ^3.2.0 ts-log: ^2.2.4 - checksum: 8e6ef1f95cce96062415b00ac701553dc6a796e66850cecc8e18b78719ea5258a7226d29a1c5c7ed89d083899c81d088166c1e1cfb62572323407a202bc885f2 + checksum: 4a71c775f853a9bde3f99f63d9fae890cdc729058e0c103a29eac498e5ba97460babdc6c07c54fcbb8b18388c2ff859f5598926034dcea9d2d5ff5da4c92e804 languageName: node linkType: hard -"@cardano-sdk/util-dev@npm:0.17.3": - version: 0.17.3 - resolution: "@cardano-sdk/util-dev@npm:0.17.3" +"@cardano-sdk/util-dev@npm:0.19.0": + version: 0.19.0 + resolution: "@cardano-sdk/util-dev@npm:0.19.0" dependencies: - "@cardano-sdk/core": ~0.21.0 - "@cardano-sdk/crypto": ~0.1.15 - "@cardano-sdk/key-management": ~0.14.0 - "@cardano-sdk/util": ~0.14.2 + "@cardano-sdk/core": ~0.22.2 + "@cardano-sdk/crypto": ~0.1.18 + "@cardano-sdk/key-management": ~0.16.0 + "@cardano-sdk/util": ~0.14.5 "@types/dockerode": ^3.3.8 axios: ^0.27.2 delay: ^5.0.0 @@ -6846,24 +6914,24 @@ __metadata: lodash: ^4.17.21 rxjs: ^7.4.0 ts-log: ^2.2.4 - checksum: 3571c315884fb0980adfa2f0ef4d98e5e06bf3d54f643f25030a15b64e362d81fc89f9598e60f03dca036b92f2fd040be37d8eac4866754e52d6ffa030c5503a + checksum: 4fffa1826590ce4a40d9be5f0b88e3e10ed01deff7f16f261dee7200a64ea77758326bef25aef9c3f46901984e78be5703d19213cd5d9c6f422ce15c91e6a654 languageName: node linkType: hard -"@cardano-sdk/util-rxjs@npm:~0.6.0": - version: 0.6.0 - resolution: "@cardano-sdk/util-rxjs@npm:0.6.0" +"@cardano-sdk/util-rxjs@npm:~0.6.3": + version: 0.6.3 + resolution: "@cardano-sdk/util-rxjs@npm:0.6.3" dependencies: - "@cardano-sdk/util": ~0.14.2 + "@cardano-sdk/util": ~0.14.5 backoff-rxjs: ^7.0.0 rxjs: ^7.4.0 - checksum: 1c9aac9c7ffbd77364d2a0209cb782ccd002a360028c39f98d7ee51e0a2ded0a6e2b49360995c440cdd42c4664f718ecbb66282eaa3ddedf33758c70d080af91 + checksum: a939c81e8c147b8951d5882e135ffa9ae01f52fce6e1559efabcbf9817e48fa6ee2bc73cf882f4005121a3b79d012682386d62a9619c6e650b4cb98d80f8aee9 languageName: node linkType: hard -"@cardano-sdk/util@npm:0.14.2, @cardano-sdk/util@npm:~0.14.2": - version: 0.14.2 - resolution: "@cardano-sdk/util@npm:0.14.2" +"@cardano-sdk/util@npm:0.14.5, @cardano-sdk/util@npm:~0.14.5": + version: 0.14.5 + resolution: "@cardano-sdk/util@npm:0.14.5" dependencies: bech32: ^2.0.0 lodash: ^4.17.21 @@ -6871,24 +6939,38 @@ __metadata: ts-custom-error: ^3.2.0 ts-log: ^2.2.4 type-fest: ^2.19.0 - checksum: 4c95f512c3997eb6effdea7d6e15a0128822b8cec51769c1cc85690c61640a9d077ec874c705f75331ce09e8eef285147b20785d489706da295ad67d64cd543b + checksum: 5ecfd91828240e6ac4e37cceb08fd35b8b61f95df268d61a6b5fc2e492daed56ae3c83ebcf3ac481ab958374895be3ecdf021f8d09caefe6bcdbaafa0a82a226 languageName: node linkType: hard -"@cardano-sdk/wallet@npm:0.25.1, @cardano-sdk/wallet@npm:~0.25.1": - version: 0.25.1 - resolution: "@cardano-sdk/wallet@npm:0.25.1" - dependencies: - "@cardano-sdk/core": ~0.21.0 - "@cardano-sdk/crypto": ~0.1.15 - "@cardano-sdk/dapp-connector": ~0.11.1 - "@cardano-sdk/hardware-ledger": ~0.6.1 - "@cardano-sdk/hardware-trezor": ~0.2.1 - "@cardano-sdk/input-selection": ~0.12.4 - "@cardano-sdk/key-management": ~0.14.0 - "@cardano-sdk/tx-construction": ~0.14.2 - "@cardano-sdk/util": ~0.14.2 - "@cardano-sdk/util-rxjs": ~0.6.0 +"@cardano-sdk/util@npm:~0.14.4": + version: 0.14.4 + resolution: "@cardano-sdk/util@npm:0.14.4" + dependencies: + bech32: ^2.0.0 + lodash: ^4.17.21 + serialize-error: ^8 + ts-custom-error: ^3.2.0 + ts-log: ^2.2.4 + type-fest: ^2.19.0 + checksum: ec47978fa4ecbed7fb37f1be7ac68ad8d886e673e7ae527d91931bfc5766b5a41a3b778ceab63b2df3d4a7fcb133f79f2f82359e6e3850f1c9d72df8a6ff118f + languageName: node + linkType: hard + +"@cardano-sdk/wallet@npm:0.27.1, @cardano-sdk/wallet@npm:~0.27.1": + version: 0.27.1 + resolution: "@cardano-sdk/wallet@npm:0.27.1" + dependencies: + "@cardano-sdk/core": ~0.22.2 + "@cardano-sdk/crypto": ~0.1.18 + "@cardano-sdk/dapp-connector": ~0.11.4 + "@cardano-sdk/hardware-ledger": ~0.8.0 + "@cardano-sdk/hardware-trezor": ~0.4.0 + "@cardano-sdk/input-selection": ~0.12.7 + "@cardano-sdk/key-management": ~0.16.0 + "@cardano-sdk/tx-construction": ~0.16.0 + "@cardano-sdk/util": ~0.14.5 + "@cardano-sdk/util-rxjs": ~0.6.3 backoff-rxjs: ^7.0.0 bignumber.js: ^9.1.1 delay: ^5.0.0 @@ -6898,22 +6980,22 @@ __metadata: rxjs: ^7.4.0 ts-custom-error: ^3.2.0 ts-log: ^2.2.3 - checksum: 8a740feefd9e0aa825e90d97b1a0cba0789edff0afdf40048cde80fa5c83be9af8e54759bd0b516df9c852d1eceb19b84ebe45b5e7be9bf0f297ac1ae7bedcfb + checksum: c8ae069c699a385f664ddd2e0a1b49f702f54889baba29cce81d32fbc16f2d5ee9cdbf7d4eb7c1a8210653d3f2a3f241685d23ae216fe3735e5d2b60e433a4d3 languageName: node linkType: hard -"@cardano-sdk/web-extension@npm:0.16.3": - version: 0.16.3 - resolution: "@cardano-sdk/web-extension@npm:0.16.3" - dependencies: - "@cardano-sdk/core": ~0.21.0 - "@cardano-sdk/crypto": ~0.1.15 - "@cardano-sdk/dapp-connector": ~0.11.1 - "@cardano-sdk/key-management": ~0.14.0 - "@cardano-sdk/tx-construction": ~0.14.2 - "@cardano-sdk/util": ~0.14.2 - "@cardano-sdk/util-rxjs": ~0.6.0 - "@cardano-sdk/wallet": ~0.25.1 +"@cardano-sdk/web-extension@npm:0.17.1": + version: 0.17.1 + resolution: "@cardano-sdk/web-extension@npm:0.17.1" + dependencies: + "@cardano-sdk/core": ~0.22.2 + "@cardano-sdk/crypto": ~0.1.18 + "@cardano-sdk/dapp-connector": ~0.11.4 + "@cardano-sdk/key-management": ~0.16.0 + "@cardano-sdk/tx-construction": ~0.16.0 + "@cardano-sdk/util": ~0.14.5 + "@cardano-sdk/util-rxjs": ~0.6.3 + "@cardano-sdk/wallet": ~0.27.1 backoff-rxjs: ^7.0.0 lodash: ^4.17.21 rxjs: ^7.4.0 @@ -6921,7 +7003,7 @@ __metadata: ts-log: ^2.2.3 uuid: ^8.3.2 webextension-polyfill: ^0.8.0 - checksum: c6e00523bf43578d5d0ea4b58028216c21ddfc39aa157fdf96523d5c161e6740dd3632341bf2e3dc6aa753e3ba9761a0af6770913b1da976efd74b9028b117e7 + checksum: 48f05a5a49bfdf9aaaabb3f79afbca39283217fa5b501e6bd89b422fbe583ee37c4a92afaae80e01420a23613c5eeddae8172440ceb0479d5f491231f64d699c languageName: node linkType: hard @@ -9263,14 +9345,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.14.4 - "@cardano-sdk/core": 0.21.0 - "@cardano-sdk/dapp-connector": 0.11.1 - "@cardano-sdk/input-selection": 0.12.4 - "@cardano-sdk/tx-construction": 0.14.2 - "@cardano-sdk/util": 0.14.2 - "@cardano-sdk/wallet": 0.25.1 - "@cardano-sdk/web-extension": 0.16.3 + "@cardano-sdk/cardano-services-client": 0.16.0 + "@cardano-sdk/core": 0.22.2 + "@cardano-sdk/dapp-connector": 0.11.4 + "@cardano-sdk/input-selection": 0.12.7 + "@cardano-sdk/tx-construction": 0.16.0 + "@cardano-sdk/util": 0.14.5 + "@cardano-sdk/wallet": 0.27.1 + "@cardano-sdk/web-extension": 0.17.1 "@dcspark/cardano-multiplatform-lib-asmjs": ^3.1.0 "@emurgo/cardano-message-signing-asmjs": 1.0.1 "@emurgo/cip14-js": ~3.0.1 @@ -9335,16 +9417,16 @@ __metadata: version: 0.0.0-use.local resolution: "@lace/cardano@workspace:packages/cardano" dependencies: - "@cardano-sdk/cardano-services-client": 0.14.4 - "@cardano-sdk/core": 0.21.0 - "@cardano-sdk/crypto": 0.1.15 - "@cardano-sdk/hardware-ledger": 0.6.1 - "@cardano-sdk/hardware-trezor": 0.2.1 - "@cardano-sdk/key-management": 0.14.0 - "@cardano-sdk/util": 0.14.2 - "@cardano-sdk/util-dev": 0.17.3 - "@cardano-sdk/wallet": 0.25.1 - "@cardano-sdk/web-extension": 0.16.3 + "@cardano-sdk/cardano-services-client": 0.15.1 + "@cardano-sdk/core": 0.22.2 + "@cardano-sdk/crypto": 0.1.18 + "@cardano-sdk/hardware-ledger": 0.8.0 + "@cardano-sdk/hardware-trezor": 0.4.0 + "@cardano-sdk/key-management": 0.16.0 + "@cardano-sdk/util": 0.14.5 + "@cardano-sdk/util-dev": 0.19.0 + "@cardano-sdk/wallet": 0.27.1 + "@cardano-sdk/web-extension": 0.17.1 "@dcspark/cardano-multiplatform-lib-browser": ^3.1.1 "@dcspark/cardano-multiplatform-lib-nodejs": ^3.1.1 "@emurgo/cardano-message-signing-browser": 1.0.1 @@ -9377,7 +9459,7 @@ __metadata: version: 0.0.0-use.local resolution: "@lace/common@workspace:packages/common" dependencies: - "@cardano-sdk/util": 0.14.2 + "@cardano-sdk/util": 0.14.5 antd: ^4.24.10 classnames: ^2.3.1 jdenticon: 3.1.0 @@ -9485,10 +9567,10 @@ __metadata: resolution: "@lace/staking@workspace:packages/staking" dependencies: "@ant-design/icons": ^4.7.0 - "@cardano-sdk/core": 0.21.0 - "@cardano-sdk/input-selection": 0.12.4 - "@cardano-sdk/tx-construction": 0.14.2 - "@cardano-sdk/util": 0.14.2 + "@cardano-sdk/core": 0.22.2 + "@cardano-sdk/input-selection": 0.12.7 + "@cardano-sdk/tx-construction": 0.16.0 + "@cardano-sdk/util": 0.14.5 "@lace/cardano": ^0.1.0 "@lace/common": ^0.1.0 "@lace/core": 0.1.0 @@ -9529,10 +9611,10 @@ __metadata: wait-on: ^7.0.1 zustand: ^4.4.1 peerDependencies: - "@cardano-sdk/input-selection": 0.12.4 - "@cardano-sdk/tx-construction": 0.14.2 - "@cardano-sdk/util": 0.14.2 - "@cardano-sdk/wallet": 0.25.1 + "@cardano-sdk/input-selection": 0.12.7 + "@cardano-sdk/tx-construction": 0.16.0 + "@cardano-sdk/util": 0.14.5 + "@cardano-sdk/wallet": 0.27.1 "@lace/cardano": ^0.1.0 "@lace/common": ^0.1.0 "@lace/core": 0.1.0 From 658ebe85dcc893cbe16e90f26433d0da579faa16 Mon Sep 17 00:00:00 2001 From: Michael Chappell <7581002+mchappell@users.noreply.github.com> Date: Wed, 29 Nov 2023 15:05:20 +0000 Subject: [PATCH 23/68] refactor: update lace based on cardano-sdk breaking changes refactor: use internal wallet address discovery --- apps/browser-extension-wallet/.env.defaults | 2 +- apps/browser-extension-wallet/src/api/mock.ts | 4 +- .../src/hooks/useWalletManager.ts | 12 +- .../background/addresses-discoverer.ts | 5 +- .../src/lib/scripts/background/wallet.ts | 6 +- .../__tests__/versions/v0_6_0.test.ts | 420 ------------------ .../src/lib/scripts/migrations/migrations.ts | 2 +- .../lib/scripts/migrations/versions/index.ts | 1 - .../lib/scripts/migrations/versions/v0_6_0.ts | 244 ---------- .../src/utils/__tests__/inspectTxType.test.ts | 21 +- .../src/utils/data-validators.ts | 1 - .../src/utils/mocks/test-helpers.tsx | 5 +- .../StakePoolsTable/StakePoolsTable.tsx | 2 +- .../components/HardwareWalletFlow.tsx | 2 +- .../components/WalletSetupWizard.tsx | 10 +- packages/cardano/package.json | 2 +- .../StakePoolTableBrowser.tsx | 4 +- .../lib/__tests__/build-delegation.test.ts | 27 +- .../src/wallet/lib/build-delegation.ts | 18 +- .../cardano/src/wallet/lib/cardano-wallet.ts | 86 ++-- .../cardano/src/wallet/lib/hardware-wallet.ts | 169 ++----- .../src/wallet/test/mocks/ProviderStub.ts | 2 +- .../test/mocks/StakepoolSearchProviderStub.ts | 28 +- .../src/wallet/test/mocks/TestKeyAgent.ts | 5 +- .../src/wallet/test/mocks/mock-wallet.ts | 7 +- .../cardano/src/wallet/test/mocks/mock.ts | 4 +- .../__tests__/stake-pool-transformer.test.ts | 4 +- .../StakePoolTableBrowser.tsx | 6 +- .../StakePoolsTable/StakePoolsTable.tsx | 2 +- yarn.lock | 84 +--- 30 files changed, 178 insertions(+), 1007 deletions(-) delete mode 100644 apps/browser-extension-wallet/src/lib/scripts/migrations/__tests__/versions/v0_6_0.test.ts delete mode 100644 apps/browser-extension-wallet/src/lib/scripts/migrations/versions/v0_6_0.ts diff --git a/apps/browser-extension-wallet/.env.defaults b/apps/browser-extension-wallet/.env.defaults index ae1fc308b7..fd8968c01a 100644 --- a/apps/browser-extension-wallet/.env.defaults +++ b/apps/browser-extension-wallet/.env.defaults @@ -65,7 +65,7 @@ CARDANO_SERVICES_URL_MAINNET=https://backend.live-mainnet.eks.lw.iog.io CARDANO_SERVICES_URL_PREPROD=https://backend.live-preprod.eks.lw.iog.io CARDANO_SERVICES_URL_PREVIEW=https://backend.live-preview.eks.lw.iog.io # TODO: update this with a valid sanchonet url -CARDANO_SERVICES_URL_SANCHONET=https://backend.dev-sanchonet.eks.lw.iog.io +CARDANO_SERVICES_URL_SANCHONET=https://backend.live-sanchonet.eks.lw.iog.io # Explorer URLs CEXPLORER_URL_MAINNET=https://cexplorer.io diff --git a/apps/browser-extension-wallet/src/api/mock.ts b/apps/browser-extension-wallet/src/api/mock.ts index 62dc74e7c7..bcec30072c 100644 --- a/apps/browser-extension-wallet/src/api/mock.ts +++ b/apps/browser-extension-wallet/src/api/mock.ts @@ -126,7 +126,9 @@ const getDetailsForAll = (): PoolDetails => ({ livePledge: BigInt('2000000000'), saturation: Percent(0.95), size: undefined, - stake: undefined + stake: undefined, + ros: Percent(0.69), + lastRos: Percent(0.88) }, relays: undefined, rewardAccount: Wallet.Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj'), diff --git a/apps/browser-extension-wallet/src/hooks/useWalletManager.ts b/apps/browser-extension-wallet/src/hooks/useWalletManager.ts index e8a29a8a7c..1a6ad3f8bf 100644 --- a/apps/browser-extension-wallet/src/hooks/useWalletManager.ts +++ b/apps/browser-extension-wallet/src/hooks/useWalletManager.ts @@ -18,6 +18,7 @@ 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/matomo/config'; import { ILocalStorage } from '@src/types'; +import { firstValueFrom } from 'rxjs'; const { AVAILABLE_CHAINS, CHAIN } = config(); @@ -428,7 +429,8 @@ export const useWalletManager = (): UseWalletManager => { ); const keyAgentInLocalStorageIsTheOneWeExpect = networkOfKeyAgentAsIntended && networkOfAddressesEqualsNetworkOfKeyAgent; - const addressesUpToDate = isEqual(currentKeyAgentData.knownAddresses, addresses); + const currentAddresses = await firstValueFrom(walletManagerUi.wallet.addresses$); + const addressesUpToDate = isEqual(currentAddresses, addresses); if (!keyAgentInLocalStorageIsTheOneWeExpect) return; if (addressesUpToDate) { @@ -439,8 +441,7 @@ export const useWalletManager = (): UseWalletManager => { let newKeyAgentData; if (keyAgentInLocalStorageIsTheOneWeExpect) { newKeyAgentData = { - ...currentKeyAgentData, - knownAddresses: addresses + ...currentKeyAgentData }; saveValueInLocalStorage({ key: 'keyAgentData', @@ -455,8 +456,7 @@ export const useWalletManager = (): UseWalletManager => { const { keyAgentsByChain } = backgroundStorage; newKeyAgentData ??= { - ...keyAgentsByChain[currentChainName].keyAgentData, - knownAddresses: addresses + ...keyAgentsByChain[currentChainName].keyAgentData }; keyAgentsByChain[currentChainName].keyAgentData = newKeyAgentData; @@ -464,7 +464,7 @@ export const useWalletManager = (): UseWalletManager => { // TODO: update walletLock so after unlocking the wallet the discovery does not get triggered again - const newKeyAgent = await Wallet.createKeyAgent(walletManagerUi, newKeyAgentData, getPassword); + const newKeyAgent = await Wallet.createKeyAgent(newKeyAgentData, getPassword); setCardanoWallet({ asyncKeyAgent: Wallet.KeyManagement.util.createAsyncKeyAgent(newKeyAgent), keyAgent: newKeyAgent, diff --git a/apps/browser-extension-wallet/src/lib/scripts/background/addresses-discoverer.ts b/apps/browser-extension-wallet/src/lib/scripts/background/addresses-discoverer.ts index b29cc4b317..6af77a8dea 100644 --- a/apps/browser-extension-wallet/src/lib/scripts/background/addresses-discoverer.ts +++ b/apps/browser-extension-wallet/src/lib/scripts/background/addresses-discoverer.ts @@ -43,7 +43,10 @@ export const getDiscovererInstance = ({ chainName }: { chainName: Wallet.ChainNa const addressesDiscoverer: AddressesDiscoverer = { status$, - discover: async () => getDiscovererInstance({ chainName: currentChainName }).discover(currentAsyncKeyAgent), + discover: async () => { + const bip32Account = await Wallet.KeyManagement.Bip32Account.fromAsyncKeyAgent(currentAsyncKeyAgent); + return getDiscovererInstance({ chainName: currentChainName }).discover(bip32Account); + }, setup: ({ chainName, keyAgentChannelName }) => { currentChainName = chainName; currentAsyncKeyAgent = consumeKeyAgent(keyAgentChannelName); 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 c32b22aa0f..d8282164bb 100644 --- a/apps/browser-extension-wallet/src/lib/scripts/background/wallet.ts +++ b/apps/browser-extension-wallet/src/lib/scripts/background/wallet.ts @@ -31,11 +31,11 @@ const walletFactory: WalletFactory = { ? (props.provider.options as { chainName: Wallet.ChainName }).chainName : config().CHAIN; const providers = getProviders(chainName); + const bip32Account = await Wallet.KeyManagement.Bip32Account.fromAsyncKeyAgent(dependencies.keyAgent); return new PersonalWallet( { name: props.observableWalletName }, { - keyAgent: dependencies.keyAgent, logger, ...providers, stores: dependencies.stores, @@ -51,7 +51,9 @@ const walletFactory: WalletFactory = { adapter: axiosFetchAdapter, policyId: ADA_HANDLE_POLICY_ID }), - addressDiscovery: getDiscovererInstance({ chainName }) + addressDiscovery: getDiscovererInstance({ chainName }), + witnesser: Wallet.KeyManagement.util.createBip32Ed25519Witnesser(dependencies.keyAgent), + bip32Account } ); } diff --git a/apps/browser-extension-wallet/src/lib/scripts/migrations/__tests__/versions/v0_6_0.test.ts b/apps/browser-extension-wallet/src/lib/scripts/migrations/__tests__/versions/v0_6_0.test.ts deleted file mode 100644 index cbd7a01132..0000000000 --- a/apps/browser-extension-wallet/src/lib/scripts/migrations/__tests__/versions/v0_6_0.test.ts +++ /dev/null @@ -1,420 +0,0 @@ -/* eslint-disable sonarjs/no-identical-functions */ -/* eslint-disable unicorn/no-null */ -/* eslint-disable sonarjs/no-duplicate-string */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable no-magic-numbers */ -/* eslint-disable camelcase */ -import { Wallet } from '@lace/cardano'; -import { PromiseResolvedType } from '@src/types'; -import { mockKeyAgentDataTestnet, mockKeyAgentsByChain, mockWalletInfoTestnet } from '@src/utils/mocks/test-helpers'; -import { storage } from 'webextension-polyfill'; -import { InvalidMigrationData } from '../../errors'; -import { MigrationPersistance } from '../../migrations'; -import { v0_6_0 } from '../../versions'; - -const encryptData = async (data: any, password: string) => - Wallet.KeyManagement.emip3encrypt(Buffer.from(JSON.stringify(data)), Buffer.from(password)); - -interface OldWalletInfo { - keyAgentData?: any; - name?: string; - address?: any; - rewardAccount?: any; - walletId?: string; -} -interface OldAppSettings { - chainId?: string; - mnemonicVerificationFrequency?: string; - lastMnemonicVerification?: string; -} -interface OldStorage { - walletPassword?: string; - walletName?: string; - keyAgentType?: Wallet.KeyManagement.KeyAgentType; - appSettings?: OldAppSettings; - walletInfoData?: OldWalletInfo; - lock?: false | Uint8Array | null; -} - -const mockInitialStorage = async ({ - walletPassword = 'pass', - walletName = 'test wallet', - appSettings = { chainId: 'Preprod' }, - keyAgentType = Wallet.KeyManagement.KeyAgentType.InMemory, - walletInfoData = { - keyAgentData: { ...mockKeyAgentDataTestnet, __typename: keyAgentType }, - name: walletName, - address: mockWalletInfoTestnet.addresses[0].address, - rewardAccount: mockWalletInfoTestnet.addresses[0].rewardAccount, - walletId: 'abcdef1234567890' - }, - lock -}: OldStorage = {}): Promise => { - const oldLock = !lock && lock !== null ? await encryptData(walletInfoData, walletPassword) : null; - localStorage.setItem('wallet', JSON.stringify(walletInfoData)); - localStorage.setItem('appSettings', JSON.stringify(appSettings)); - localStorage.setItem('lock', JSON.stringify(oldLock)); - const name = walletInfoData.name ?? walletName; - await storage.local.set({ BACKGROUND_STORAGE: { walletName: name } }); - const agentType = walletInfoData.keyAgentData.__typename ?? keyAgentType; - return { walletPassword, walletInfoData, lock: oldLock, walletName: name, appSettings, keyAgentType: agentType }; -}; - -const mockTmpStorage = async (initialStorage: OldStorage) => { - let newLock = null; - const newAppSettings = JSON.stringify({ - chainName: initialStorage.appSettings.chainId, - lastMnemonicVerification: initialStorage.appSettings.lastMnemonicVerification, - mnemonicVerificationFrequency: initialStorage.appSettings.mnemonicVerificationFrequency - }); - const newWalletInfo = JSON.stringify({ name: initialStorage.walletName }); - const newKeyAgentData = JSON.stringify(initialStorage.walletInfoData.keyAgentData); - const newKeyAgentsByChain = { BACKGROUND_STORAGE: { keyAgentsByChain_tmp: mockKeyAgentsByChain } }; - localStorage.setItem('appSettings_tmp', newAppSettings); - localStorage.setItem('wallet_tmp', newWalletInfo); - localStorage.setItem('keyAgentData_tmp', newKeyAgentData); - if (initialStorage.lock) { - newLock = JSON.stringify(await encryptData(mockKeyAgentsByChain, initialStorage.walletPassword)); - localStorage.setItem('lock_tmp', newLock); - } - await storage.local.set(newKeyAgentsByChain); - return { newAppSettings, newWalletInfo, newKeyAgentData, newLock, newKeyAgentsByChain }; -}; - -type TmpStorage = PromiseResolvedType; - -describe('v0.6.0 migration', () => { - let localStorageSetSpy: jest.SpyInstance; - let localStorageRemoveSpy: jest.SpyInstance; - - beforeAll(() => { - localStorageSetSpy = jest.spyOn(Storage.prototype, 'setItem'); - localStorageRemoveSpy = jest.spyOn(Storage.prototype, 'removeItem'); - }); - - beforeEach(async () => { - localStorage.clear(); - await storage.local.clear(); - jest.clearAllMocks(); - }); - - afterAll(() => { - jest.restoreAllMocks(); - }); - - describe('requirePassword', () => { - test('is true if lock is in storage ', () => { - localStorage.setItem('lock', JSON.stringify(Buffer.from('asd'))); - expect(v0_6_0.requiresPassword()).toBe(true); - }); - test('is false if there is no lock in storage ', () => { - expect(v0_6_0.requiresPassword()).toBe(false); - }); - }); - describe('upgrade', () => { - describe('when no wallet exists', () => { - let migrationPersistance: MigrationPersistance; - beforeAll(async () => { - migrationPersistance = await v0_6_0.upgrade(); - }); - describe('prepare', () => { - test('does not save any information', async () => { - await migrationPersistance.prepare(); - expect(localStorageSetSpy).not.toHaveBeenCalled(); - expect(storage.local.set as jest.Mock).not.toHaveBeenCalled(); - }); - }); - describe('assert', () => { - test('throws an error when there is temporary wallet data', async () => { - localStorage.setItem('keyAgentData_tmp', JSON.stringify(mockKeyAgentDataTestnet)); - await expect(migrationPersistance.assert()).rejects.toThrow( - new InvalidMigrationData('0.6.0', 'Wallet data should not exist') - ); - }); - test('resolves to true if there is no temporary data', async () => { - await expect(migrationPersistance.assert()).resolves.toBe(true); - }); - }); - describe('persist', () => { - test('does not save any information but still try to clear tmp keys', async () => { - await migrationPersistance.persist(); - expect(localStorageSetSpy).not.toHaveBeenCalled(); - expect(localStorageRemoveSpy).toHaveBeenCalledTimes(4); - expect(localStorageRemoveSpy).toBeCalledWith('appSettings_tmp'); - expect(localStorageRemoveSpy).toBeCalledWith('wallet_tmp'); - expect(localStorageRemoveSpy).toBeCalledWith('keyAgentData_tmp'); - expect(localStorageRemoveSpy).toBeCalledWith('lock_tmp'); - expect(storage.local.set as jest.Mock).toHaveBeenCalledTimes(2); - }); - }); - describe('rollback', () => { - test('does not save any information but still try to clear tmp and new keys', async () => { - await migrationPersistance.rollback(); - expect(localStorageSetSpy).not.toHaveBeenCalled(); - expect(localStorageRemoveSpy).toHaveBeenCalledTimes(5); - expect(localStorageRemoveSpy).toBeCalledWith('appSettings_tmp'); - expect(localStorageRemoveSpy).toBeCalledWith('wallet_tmp'); - expect(localStorageRemoveSpy).toBeCalledWith('keyAgentData'); - expect(localStorageRemoveSpy).toBeCalledWith('keyAgentData_tmp'); - expect(localStorageRemoveSpy).toBeCalledWith('lock_tmp'); - expect(storage.local.set as jest.Mock).toHaveBeenCalledTimes(2); - }); - }); - }); - describe('when an InMemory wallet exists and is not locked', () => { - let migrationPersistance: MigrationPersistance; - let initialStorage: OldStorage; - beforeAll(async () => { - initialStorage = await mockInitialStorage(); - migrationPersistance = await v0_6_0.upgrade(initialStorage.walletPassword); - }); - describe('prepare', () => { - test('saves temporary storage with the updated information', async () => { - await migrationPersistance.prepare(); - expect(localStorageSetSpy).toHaveBeenCalledTimes(4); - assertCalledWithArg(localStorageSetSpy, 'wallet_tmp', 0); - assertCalledWithArg(localStorageSetSpy, 'appSettings_tmp', 0); - assertCalledWithArg(localStorageSetSpy, 'keyAgentData_tmp', 0); - assertCalledWithArg(localStorageSetSpy, 'lock_tmp', 0); - expect(storage.local.set as jest.Mock).toHaveBeenCalledTimes(1); - }); - }); - describe('assert', () => { - beforeEach(async () => { - await mockTmpStorage(initialStorage); - jest.clearAllMocks(); - }); - test('resolves to true if all temporary data is valid', async () => { - await expect(migrationPersistance.assert()).resolves.toBe(true); - }); - test('throws an error when there is no chainName in appSettings_tmp', async () => { - localStorage.setItem('appSettings_tmp', JSON.stringify({ chainId: 'Preprod' })); - await expect(migrationPersistance.assert()).rejects.toThrow( - new InvalidMigrationData('0.6.0', 'Missing chain name in app settings') - ); - }); - test('throws an error when keyAgentData_tmp is missing', async () => { - localStorage.removeItem('keyAgentData_tmp'); - await expect(migrationPersistance.assert()).rejects.toThrow( - new InvalidMigrationData('0.6.0', 'Wallet data missing') - ); - }); - test('throws an error when wallet_tmp is missing', async () => { - localStorage.removeItem('wallet_tmp'); - await expect(migrationPersistance.assert()).rejects.toThrow( - new InvalidMigrationData('0.6.0', 'Wallet data missing') - ); - }); - test('throws an error when keyAgentsByChain_tmp is missing', async () => { - await storage.local.set({ BACKGROUND_STORAGE: {} }); - await expect(migrationPersistance.assert()).rejects.toThrow( - new InvalidMigrationData('0.6.0', 'Wallet data missing') - ); - }); - test('throws an error when there is no name in wallet_tmp', async () => { - localStorage.setItem('wallet_tmp', JSON.stringify({})); - await expect(migrationPersistance.assert()).rejects.toThrow( - new InvalidMigrationData('0.6.0', 'Missing name in wallet info') - ); - }); - test('throws an error when keyAgentData_tmp is missing a field', async () => { - const keyAgentData_tmp = { ...mockKeyAgentDataTestnet }; - delete keyAgentData_tmp.__typename; - localStorage.setItem('keyAgentData_tmp', JSON.stringify(keyAgentData_tmp)); - await expect(migrationPersistance.assert()).rejects.toThrow( - new InvalidMigrationData('0.6.0', 'Missing field in key agent data') - ); - }); - test('throws an error when keyAgentDataByChain_tmp is missing a chain', async () => { - await storage.local.set({ - BACKGROUND_STORAGE: { keyAgentsByChain_tmp: { Preprod: mockKeyAgentDataTestnet } } - }); - await expect(migrationPersistance.assert()).rejects.toThrow( - new InvalidMigrationData('0.6.0', 'Missing key agent for one or more chains') - ); - }); - test('throws an error when decrypted lock_tmp is not the same as keyAgentsByChain_tmp', async () => { - const wrongLock = await encryptData(mockWalletInfoTestnet, initialStorage.walletPassword); - localStorage.setItem('lock_tmp', JSON.stringify(wrongLock)); - await expect(migrationPersistance.assert()).rejects.toThrow( - new InvalidMigrationData('0.6.0', 'Decrypted lock is not valid') - ); - }); - }); - describe('persist', () => { - let tmpStorage: TmpStorage; - beforeEach(async () => { - tmpStorage = await mockTmpStorage(initialStorage); - jest.clearAllMocks(); - }); - test('saves all temporary information as permanent before clearing it', async () => { - await migrationPersistance.persist(); - expect(localStorageSetSpy).toHaveBeenCalledTimes(4); - expect(localStorageSetSpy).toHaveBeenCalledWith('appSettings', tmpStorage.newAppSettings); - expect(localStorageSetSpy).toHaveBeenCalledWith('wallet', tmpStorage.newWalletInfo); - expect(localStorageSetSpy).toHaveBeenCalledWith('keyAgentData', tmpStorage.newKeyAgentData); - expect(localStorageSetSpy).toHaveBeenCalledWith('lock', tmpStorage.newLock); - expect(localStorageRemoveSpy).toHaveBeenCalledTimes(4); - expect(localStorageRemoveSpy).toBeCalledWith('appSettings_tmp'); - expect(localStorageRemoveSpy).toBeCalledWith('wallet_tmp'); - expect(localStorageRemoveSpy).toBeCalledWith('keyAgentData_tmp'); - expect(localStorageRemoveSpy).toBeCalledWith('lock_tmp'); - expect(storage.local.set as jest.Mock).toHaveBeenCalledTimes(3); - }); - }); - describe('rollback', () => { - beforeEach(async () => { - await mockTmpStorage(initialStorage); - jest.clearAllMocks(); - }); - test('restores the original stored data and clears the temporary storage', async () => { - await migrationPersistance.rollback(); - expect(localStorageSetSpy).toHaveBeenCalledTimes(3); - expect(localStorageSetSpy).toHaveBeenCalledWith('appSettings', JSON.stringify(initialStorage.appSettings)); - expect(localStorageSetSpy).toHaveBeenCalledWith('wallet', JSON.stringify(initialStorage.walletInfoData)); - expect(localStorageSetSpy).toHaveBeenCalledWith('lock', JSON.stringify(initialStorage.lock)); - expect(localStorageRemoveSpy).toHaveBeenCalledTimes(5); - expect(localStorageRemoveSpy).toBeCalledWith('keyAgentData'); - expect(localStorageRemoveSpy).toBeCalledWith('appSettings_tmp'); - expect(localStorageRemoveSpy).toBeCalledWith('wallet_tmp'); - expect(localStorageRemoveSpy).toBeCalledWith('keyAgentData_tmp'); - expect(localStorageRemoveSpy).toBeCalledWith('lock_tmp'); - expect(storage.local.set as jest.Mock).toHaveBeenCalledTimes(3); - }); - }); - }); - describe('when an InMemory wallet exists but it is locked', () => { - let migrationPersistance: MigrationPersistance; - let initialStorage: OldStorage; - beforeAll(async () => { - initialStorage = await mockInitialStorage({ - walletInfoData: { - keyAgentData: mockKeyAgentDataTestnet, - address: mockWalletInfoTestnet.addresses[0].address, - rewardAccount: mockWalletInfoTestnet.addresses[0].rewardAccount, - walletId: 'abcdef1234567890' - } - }); - storage.local.remove('wallet'); - migrationPersistance = await v0_6_0.upgrade(initialStorage.walletPassword); - }); - describe('prepare', () => { - test('saves wallet_tmp and keyAgentData_tmp with decrypted lock and name in background storage', async () => { - await migrationPersistance.prepare(); - expect(localStorageSetSpy).toHaveBeenCalledTimes(4); - expect(localStorageSetSpy).toHaveBeenCalledWith( - 'wallet_tmp', - JSON.stringify({ name: initialStorage.walletName }) - ); - expect(localStorageSetSpy).toHaveBeenCalledWith( - 'keyAgentData_tmp', - JSON.stringify(initialStorage.walletInfoData.keyAgentData) - ); - expect(storage.local.set as jest.Mock).toHaveBeenCalledTimes(1); - }); - }); - describe('assert', () => { - beforeEach(async () => { - await mockTmpStorage(initialStorage); - jest.clearAllMocks(); - }); - test('resolves to true if all temporary data is valid', async () => { - await expect(migrationPersistance.assert()).resolves.toBe(true); - }); - }); - describe('persist', () => { - beforeEach(async () => { - await mockTmpStorage(initialStorage); - jest.clearAllMocks(); - }); - test('saves all temporary information as permanent before clearing it', async () => { - await migrationPersistance.persist(); - expect(localStorageSetSpy).toHaveBeenCalledTimes(4); - expect(localStorageRemoveSpy).toHaveBeenCalledTimes(4); - expect(storage.local.set as jest.Mock).toHaveBeenCalledTimes(3); - }); - }); - describe('rollback', () => { - beforeEach(async () => { - await mockTmpStorage(initialStorage); - jest.clearAllMocks(); - }); - test('restores the original stored data and clears the temporary storage', async () => { - await migrationPersistance.rollback(); - expect(localStorageSetSpy).toHaveBeenCalledTimes(3); - expect(localStorageRemoveSpy).toHaveBeenCalledTimes(5); - expect(storage.local.set as jest.Mock).toHaveBeenCalledTimes(3); - }); - }); - }); - describe('when a hardware wallet exists', () => { - let migrationPersistance: MigrationPersistance; - let initialStorage: OldStorage; - beforeAll(async () => { - initialStorage = await mockInitialStorage({ - lock: null, - keyAgentType: Wallet.KeyManagement.KeyAgentType.Ledger - }); - migrationPersistance = await v0_6_0.upgrade(initialStorage.walletPassword); - }); - describe('prepare', () => { - test('saves temporary storage with the updated information', async () => { - await migrationPersistance.prepare(); - expect(localStorageSetSpy).toHaveBeenCalledTimes(3); - assertCalledWithArg(localStorageSetSpy, 'wallet_tmp', 0); - assertCalledWithArg(localStorageSetSpy, 'appSettings_tmp', 0); - assertCalledWithArg(localStorageSetSpy, 'keyAgentData_tmp', 0); - expect(storage.local.set as jest.Mock).toHaveBeenCalledTimes(1); - }); - }); - describe('assert', () => { - beforeEach(async () => { - await mockTmpStorage(initialStorage); - jest.clearAllMocks(); - }); - test('resolves to true if all temporary data is valid', async () => { - await expect(migrationPersistance.assert()).resolves.toBe(true); - }); - }); - describe('persist', () => { - let tmpStorage: TmpStorage; - beforeEach(async () => { - tmpStorage = await mockTmpStorage(initialStorage); - jest.clearAllMocks(); - }); - test('saves all temporary information as permanent before clearing it', async () => { - await migrationPersistance.persist(); - expect(localStorageSetSpy).toHaveBeenCalledTimes(3); - expect(localStorageSetSpy).toHaveBeenCalledWith('appSettings', tmpStorage.newAppSettings); - expect(localStorageSetSpy).toHaveBeenCalledWith('wallet', tmpStorage.newWalletInfo); - expect(localStorageSetSpy).toHaveBeenCalledWith('keyAgentData', tmpStorage.newKeyAgentData); - expect(localStorageRemoveSpy).toHaveBeenCalledTimes(4); - expect(localStorageRemoveSpy).toBeCalledWith('appSettings_tmp'); - expect(localStorageRemoveSpy).toBeCalledWith('wallet_tmp'); - expect(localStorageRemoveSpy).toBeCalledWith('keyAgentData_tmp'); - expect(localStorageRemoveSpy).toBeCalledWith('lock_tmp'); - expect(storage.local.set as jest.Mock).toHaveBeenCalledTimes(3); - }); - }); - describe('rollback', () => { - beforeEach(async () => { - await mockTmpStorage(initialStorage); - jest.clearAllMocks(); - }); - test('restores the original stored data and clears the temporary storage', async () => { - await migrationPersistance.rollback(); - expect(localStorageSetSpy).toHaveBeenCalledTimes(2); - expect(localStorageSetSpy).toHaveBeenCalledWith('appSettings', JSON.stringify(initialStorage.appSettings)); - expect(localStorageSetSpy).toHaveBeenCalledWith('wallet', JSON.stringify(initialStorage.walletInfoData)); - expect(localStorageRemoveSpy).toHaveBeenCalledTimes(5); - expect(localStorageRemoveSpy).toBeCalledWith('keyAgentData'); - expect(localStorageRemoveSpy).toBeCalledWith('appSettings_tmp'); - expect(localStorageRemoveSpy).toBeCalledWith('wallet_tmp'); - expect(localStorageRemoveSpy).toBeCalledWith('keyAgentData_tmp'); - expect(localStorageRemoveSpy).toBeCalledWith('lock_tmp'); - expect(storage.local.set as jest.Mock).toHaveBeenCalledTimes(3); - }); - }); - }); - }); -}); diff --git a/apps/browser-extension-wallet/src/lib/scripts/migrations/migrations.ts b/apps/browser-extension-wallet/src/lib/scripts/migrations/migrations.ts index 56ffffdb22..46d5496b86 100644 --- a/apps/browser-extension-wallet/src/lib/scripts/migrations/migrations.ts +++ b/apps/browser-extension-wallet/src/lib/scripts/migrations/migrations.ts @@ -43,7 +43,7 @@ export type Migration = { downgrade?: (password?: string) => MigrationPersistance | Promise; }; -const migrations: Migration[] = [versions.v0_6_0, versions.v_1_0_0]; +const migrations: Migration[] = [versions.v_1_0_0]; /** * Applies all migrations in order between the two version provided diff --git a/apps/browser-extension-wallet/src/lib/scripts/migrations/versions/index.ts b/apps/browser-extension-wallet/src/lib/scripts/migrations/versions/index.ts index 4a9785b14a..c0d8c76da5 100644 --- a/apps/browser-extension-wallet/src/lib/scripts/migrations/versions/index.ts +++ b/apps/browser-extension-wallet/src/lib/scripts/migrations/versions/index.ts @@ -1,2 +1 @@ -export * from './v0_6_0'; export * from './v1_0_0'; diff --git a/apps/browser-extension-wallet/src/lib/scripts/migrations/versions/v0_6_0.ts b/apps/browser-extension-wallet/src/lib/scripts/migrations/versions/v0_6_0.ts deleted file mode 100644 index cbcfe74444..0000000000 --- a/apps/browser-extension-wallet/src/lib/scripts/migrations/versions/v0_6_0.ts +++ /dev/null @@ -1,244 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable camelcase */ -/* eslint-disable sonarjs/cognitive-complexity */ -/* eslint-disable complexity */ -import { Wallet } from '@lace/cardano'; -import isEqual from 'lodash/isEqual'; -import isNil from 'lodash/isNil'; -import { clearBackgroundStorage, getBackgroundStorage, setBackgroundStorage } from '@lib/scripts/background/util'; -import { BackgroundStorage, BackgroundStorageKeys } from '@lib/scripts/types'; -import { bufferReviver } from '@src/utils/local-storage'; -import { Migration } from '../migrations'; -import { removeItemFromLocalStorage, getItemFromLocalStorage, setItemInLocalStorage } from '../util'; -import { InvalidMigrationData } from '../errors'; - -const MIGRATION_VERSION = '0.6.0'; -const throwInvalidDataError = (reason?: string) => { - throw new InvalidMigrationData(MIGRATION_VERSION, reason); -}; - -export const v0_6_0: Migration = { - version: MIGRATION_VERSION, - requiresPassword: (): boolean => !!getItemFromLocalStorage('lock', undefined, bufferReviver), - upgrade: async (password?: string) => { - // Get all information needed for the migration from storage - let oldWalletInfo = getItemFromLocalStorage('wallet'); - const oldAppSettings = getItemFromLocalStorage('appSettings'); - const oldLock: Uint8Array = getItemFromLocalStorage('lock', undefined, bufferReviver); - // This is needed because of a previous workaround, without this we lose the wallet name if locked - const backgroundStorage = (await getBackgroundStorage()) as unknown as { walletName: string }; - - // Declare new migrated objects - let newAppSettings: { - mnemonicVerificationFrequency?: string; - lastMnemonicVerification?: string; - chainName?: Wallet.ChainName; - }; - let newWalletInfo: { name: string }; - let newLock: Uint8Array; - let newKeyAgentData: Wallet.KeyManagement.SerializableKeyAgentData; - let newKeyAgentsByChain: Wallet.KeyAgentsByChain; - // Migrate `appSettings` - // If available, change chainId property to chainName but keep the same value - if (oldAppSettings?.chainId) { - const chainName = oldAppSettings.chainId as Wallet.ChainName; - newAppSettings = { - mnemonicVerificationFrequency: oldAppSettings.mnemonicVerificationFrequency, - lastMnemonicVerification: oldAppSettings.lastMnemonicVerification, - chainName - }; - } - - // If `wallet` and `lock` are not available, it means that the wallet was never created - const walletNotCreated = isNil(oldWalletInfo) && isNil(oldLock); - if (!walletNotCreated) { - // Migrate `wallet` - // If `wallet` is not available and `lock` is, it means that the wallet was locked - const walletWasLocked = isNil(oldWalletInfo) && !isNil(oldLock); - if (walletWasLocked) { - const decryptedLock = await Wallet.KeyManagement.emip3decrypt(oldLock, Buffer.from(password)); - if (isNil(decryptedLock)) throw new Error('Invalid data after recovering wallet with password'); - oldWalletInfo = JSON.parse(decryptedLock.toString()); - } - - // Wallet was not locked or was just unlocked above. Remove walletId and keyAgentData from wallet info - if (oldWalletInfo.keyAgentData) { - // Only name is needed in new wallet info - newWalletInfo = { name: backgroundStorage?.walletName ?? oldWalletInfo.name }; - newKeyAgentData = oldWalletInfo.keyAgentData; - - // Find chain name for stored key agent - const [keyAgentStoredChainName] = - Object.entries(Wallet.Cardano.ChainIds).find( - ([, id]) => - id.networkId === newKeyAgentData.chainId.networkId && - id.networkMagic === newKeyAgentData.chainId.networkMagic - ) ?? []; - - // Generate keyAgentsDataByChain and save it in background storage - newKeyAgentsByChain = { - Preprod: - keyAgentStoredChainName === 'Preprod' - ? { keyAgentData: newKeyAgentData } - : { - keyAgentData: { ...newKeyAgentData, chainId: Wallet.Cardano.ChainIds.Preprod, knownAddresses: [] } - }, - Preview: - keyAgentStoredChainName === 'Preview' - ? { keyAgentData: newKeyAgentData } - : { - keyAgentData: { ...newKeyAgentData, chainId: Wallet.Cardano.ChainIds.Preview, knownAddresses: [] } - }, - Mainnet: - keyAgentStoredChainName === 'Mainnet' - ? { keyAgentData: newKeyAgentData } - : { - keyAgentData: { ...newKeyAgentData, chainId: Wallet.Cardano.ChainIds.Mainnet, knownAddresses: [] } - }, - Sanchonet: - keyAgentStoredChainName === 'Sanchonet' - ? { keyAgentData: newKeyAgentData } - : { - keyAgentData: { ...newKeyAgentData, chainId: Wallet.Cardano.ChainIds.Sanchonet, knownAddresses: [] } - } - }; - - if (newKeyAgentData.__typename === Wallet.KeyManagement.KeyAgentType.InMemory) { - newLock = await Wallet.KeyManagement.emip3encrypt( - Buffer.from(JSON.stringify(newKeyAgentsByChain)), - Buffer.from(password) - ); - } - } - } - - return { - prepare: async () => { - // Save temporary storage. Revert and throw if something fails - try { - console.info('Saving temporary migration for', MIGRATION_VERSION); - if (newAppSettings) setItemInLocalStorage('appSettings_tmp', newAppSettings); - if (newWalletInfo) setItemInLocalStorage('wallet_tmp', newWalletInfo); - if (newKeyAgentData) setItemInLocalStorage('keyAgentData_tmp', newKeyAgentData); - if (newLock) setItemInLocalStorage('lock_tmp', newLock); - if (newKeyAgentsByChain) - await setBackgroundStorage({ keyAgentsByChain_tmp: newKeyAgentsByChain } as unknown as BackgroundStorage); - } catch (error) { - console.info(`Error saving temporary migrations for ${MIGRATION_VERSION}, deleting...`, error); - removeItemFromLocalStorage('appSettings_tmp'); - removeItemFromLocalStorage('wallet_tmp'); - removeItemFromLocalStorage('keyAgentData_tmp'); - removeItemFromLocalStorage('lock_tmp'); - await clearBackgroundStorage({ keys: ['keyAgentsByChain_tmp' as BackgroundStorageKeys] }); - throw error; - } - }, - assert: async (): Promise => { - console.info('Checking migrated data for version', MIGRATION_VERSION); - // Temporary storage - const tmpWalletInfo = getItemFromLocalStorage('wallet_tmp'); - const tmpAppSettings = getItemFromLocalStorage('appSettings_tmp'); - const tmpKeyAgentData = getItemFromLocalStorage('keyAgentData_tmp'); - const tmpLock: Uint8Array = getItemFromLocalStorage('lock_tmp', undefined, bufferReviver); - const tmpBackgroundStorage = (await getBackgroundStorage()) as unknown as { - keyAgentsByChain_tmp: Wallet.KeyAgentsByChain; - }; - - if (tmpAppSettings && !('chainName' in tmpAppSettings)) { - throwInvalidDataError('Missing chain name in app settings'); - } - - // If there is no wallet, there shouldn't be any info stored related to the wallet - if ( - walletNotCreated && - (tmpWalletInfo || tmpKeyAgentData || tmpLock || tmpBackgroundStorage?.keyAgentsByChain_tmp) - ) { - throwInvalidDataError('Wallet data should not exist'); - } else if (walletNotCreated) { - return true; - } - - // If there was a wallet, check mandatory things - if (!walletNotCreated && !(tmpWalletInfo && tmpKeyAgentData && tmpBackgroundStorage?.keyAgentsByChain_tmp)) { - throwInvalidDataError('Wallet data missing'); - } - - if (Object.keys(tmpWalletInfo).length !== 1 || !('name' in tmpWalletInfo)) { - throwInvalidDataError('Missing name in wallet info'); - } - - if ( - !( - '__typename' in tmpKeyAgentData && - 'chainId' in tmpKeyAgentData && - 'accountIndex' in tmpKeyAgentData && - 'knownAddresses' in tmpKeyAgentData && - 'extendedAccountPublicKey' in tmpKeyAgentData - ) - ) { - throwInvalidDataError('Missing field in key agent data'); - } - - if ( - !( - 'Preprod' in tmpBackgroundStorage.keyAgentsByChain_tmp && - 'Preview' in tmpBackgroundStorage.keyAgentsByChain_tmp && - 'Mainnet' in tmpBackgroundStorage.keyAgentsByChain_tmp - ) - ) { - throwInvalidDataError('Missing key agent for one or more chains'); - } - if (tmpLock) { - const decryptedLock = await Wallet.KeyManagement.emip3decrypt(tmpLock, Buffer.from(password)); - if (!decryptedLock?.toString()) throwInvalidDataError('Decrypted lock is empty'); - if (!isEqual(JSON.parse(decryptedLock.toString()), tmpBackgroundStorage.keyAgentsByChain_tmp)) { - throwInvalidDataError('Decrypted lock is not valid'); - } - } - - return true; - }, - persist: async () => { - console.info(`Persisting migrated data for ${MIGRATION_VERSION} upgrade`); - // Get temporary storage - const tmpAppSettings = getItemFromLocalStorage('appSettings_tmp'); - const tmpWalletInfo = getItemFromLocalStorage('wallet_tmp'); - const tmpKeyAgentData = getItemFromLocalStorage('keyAgentData_tmp'); - const tmpLock = getItemFromLocalStorage('lock_tmp'); - const tmpBackgroundStorage = (await getBackgroundStorage()) as unknown as { keyAgentsByChain_tmp: any }; - // Replace actual storage - if (tmpAppSettings) setItemInLocalStorage('appSettings', tmpAppSettings); - if (tmpWalletInfo) setItemInLocalStorage('wallet', tmpWalletInfo); - if (tmpKeyAgentData) setItemInLocalStorage('keyAgentData', tmpKeyAgentData); - if (tmpLock) setItemInLocalStorage('lock', tmpLock); - if (tmpBackgroundStorage?.keyAgentsByChain_tmp) { - await setBackgroundStorage({ keyAgentsByChain: tmpBackgroundStorage.keyAgentsByChain_tmp }); - } - await clearBackgroundStorage({ keys: ['walletName' as BackgroundStorageKeys] }); - // Delete temporary storage - removeItemFromLocalStorage('appSettings_tmp'); - removeItemFromLocalStorage('wallet_tmp'); - removeItemFromLocalStorage('keyAgentData_tmp'); - removeItemFromLocalStorage('lock_tmp'); - await clearBackgroundStorage({ keys: ['keyAgentsByChain_tmp' as BackgroundStorageKeys] }); - }, - rollback: async () => { - console.info(`Rollback migrated data for ${MIGRATION_VERSION} upgrade`); - // Restore actual storage to their original values - if (oldAppSettings) setItemInLocalStorage('appSettings', oldAppSettings); - if (oldWalletInfo) setItemInLocalStorage('wallet', oldWalletInfo); - if (oldLock) setItemInLocalStorage('lock', oldLock); - removeItemFromLocalStorage('keyAgentData'); - await clearBackgroundStorage({ keys: ['keyAgentsByChain'] }); - if (backgroundStorage?.walletName) - await setBackgroundStorage({ walletName: backgroundStorage.walletName } as BackgroundStorage); - // Delete any temporary storage that may have been created - removeItemFromLocalStorage('appSettings_tmp'); - removeItemFromLocalStorage('wallet_tmp'); - removeItemFromLocalStorage('keyAgentData_tmp'); - removeItemFromLocalStorage('lock_tmp'); - await clearBackgroundStorage({ keys: ['keyAgentsByChain_tmp' as BackgroundStorageKeys] }); - } - }; - } -}; 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 ce957c5adc..ae28a30004 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 { buildMockTx } from '../mocks/tx'; import { Wallet } from '@lace/cardano'; import { TxDirections } from '@src/types'; import { StakeDelegationCertificate } from '@cardano-sdk/core/dist/cjs/Cardano'; - +import { Hash28ByteBase16 } from '@cardano-sdk/crypto'; const ADDRESS_1 = Wallet.Cardano.PaymentAddress( 'addr_test1qq585l3hyxgj3nas2v3xymd23vvartfhceme6gv98aaeg9muzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475q2g7k3g' ); @@ -102,7 +102,10 @@ describe('testing tx-inspection utils', () => { certificates: [ { __typename: Wallet.Cardano.CertificateType.StakeDelegation, - stakeKeyHash: STAKE_KEY_HASH, + stakeCredential: { + hash: Hash28ByteBase16.fromEd25519KeyHashHex(STAKE_KEY_HASH), + type: Wallet.Cardano.CredentialType.KeyHash + }, poolId: POOL_ID } ] @@ -152,8 +155,11 @@ describe('testing tx-inspection utils', () => { const stakeKeyRegistrationTX = buildMockTx({ certificates: [ { - __typename: Wallet.Cardano.CertificateType.StakeKeyRegistration, - stakeKeyHash: STAKE_KEY_HASH + __typename: Wallet.Cardano.CertificateType.StakeRegistration, + stakeCredential: { + hash: Hash28ByteBase16.fromEd25519KeyHashHex(STAKE_KEY_HASH), + type: Wallet.Cardano.CredentialType.KeyHash + } } ] }); @@ -172,8 +178,11 @@ describe('testing tx-inspection utils', () => { const stakeKeyDeregistrationTX = buildMockTx({ certificates: [ { - __typename: Wallet.Cardano.CertificateType.StakeKeyDeregistration, - stakeKeyHash: STAKE_KEY_HASH + __typename: Wallet.Cardano.CertificateType.StakeDeregistration, + stakeCredential: { + hash: Hash28ByteBase16.fromEd25519KeyHashHex(STAKE_KEY_HASH), + type: Wallet.Cardano.CredentialType.KeyHash + } } ] }); diff --git a/apps/browser-extension-wallet/src/utils/data-validators.ts b/apps/browser-extension-wallet/src/utils/data-validators.ts index 616900a188..7d370a1086 100644 --- a/apps/browser-extension-wallet/src/utils/data-validators.ts +++ b/apps/browser-extension-wallet/src/utils/data-validators.ts @@ -5,7 +5,6 @@ export const isKeyAgentDataValid = (keyAgentData: Wallet.KeyManagement.Serializa '__typename' in keyAgentData && 'chainId' in keyAgentData && 'accountIndex' in keyAgentData && - 'knownAddresses' in keyAgentData && 'extendedAccountPublicKey' in keyAgentData; export const isKeyAgentsByChainValid = (keyAgentsByChain: Wallet.KeyAgentsByChain): boolean => diff --git a/apps/browser-extension-wallet/src/utils/mocks/test-helpers.tsx b/apps/browser-extension-wallet/src/utils/mocks/test-helpers.tsx index c8b101c43f..f76d145c3a 100644 --- a/apps/browser-extension-wallet/src/utils/mocks/test-helpers.tsx +++ b/apps/browser-extension-wallet/src/utils/mocks/test-helpers.tsx @@ -36,7 +36,6 @@ export const mockKeyAgentDataTestnet: Wallet.KeyManagement.SerializableKeyAgentD encryptedRootPrivateKeyBytes: [], // eslint-disable-next-line no-magic-numbers extendedAccountPublicKey: Wallet.Crypto.Bip32PublicKeyHex('0'.repeat(128)), - knownAddresses: [], chainId: Wallet.Cardano.ChainIds.Preprod }; @@ -263,7 +262,9 @@ export const cardanoStakePoolMock: Wallet.StakePoolSearchResults = { saturation: Percent(0.0512), stake: undefined, size: undefined, - apy: Percent(0.013) + apy: Percent(0.013), + ros: Percent(0.69), + lastRos: Percent(0.88) }, owners: [ Wallet.Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj'), diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/StakePoolsTable/StakePoolsTable.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/StakePoolsTable/StakePoolsTable.tsx index 9b671719ad..fd10a3e36c 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/StakePoolsTable/StakePoolsTable.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/StakePoolsTable/StakePoolsTable.tsx @@ -26,7 +26,7 @@ type stakePoolsTableProps = { }; const DEFAULT_SORT_OPTIONS: StakePoolSortOptions = { - field: 'apy', + field: 'ros', order: 'desc' }; 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 f8b24bc34c..ef42509022 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 @@ -175,7 +175,7 @@ export const HardwareWalletFlow = ({ setDoesUserAllowAnalytics( isAnalyticsAccepted ? EnhancedAnalyticsOptInStatus.OptedIn : EnhancedAnalyticsOptInStatus.OptedOut ); - const addressDiscoverySubscriber = wallet.asyncKeyAgent.knownAddresses$.subscribe((addresses) => { + const addressDiscoverySubscriber = wallet.wallet.addresses$.subscribe((addresses) => { if (addresses.length === 0) return; const hdWalletDiscovered = addresses.some((addr) => addr.index > 0); if (hdWalletDiscovered) { diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/WalletSetupWizard.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/WalletSetupWizard.tsx index 0f130ebf31..bdcabb9205 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/WalletSetupWizard.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/WalletSetupWizard.tsx @@ -23,7 +23,7 @@ import { WarningModal } from '@src/views/browser-view/components/WarningModal'; import { AnalyticsEventNames, EnhancedAnalyticsOptInStatus, - PostHogAction, + // PostHogAction, postHogOnboardingActions, UserTrackingType } from '@providers/AnalyticsProvider/analyticsTracker'; @@ -291,14 +291,6 @@ export const WalletSetupWizard = ({ await analytics.setOptedInForEnhancedAnalytics( isAnalyticsAccepted ? EnhancedAnalyticsOptInStatus.OptedIn : EnhancedAnalyticsOptInStatus.OptedOut ); - const addressDiscoverySubscriber = wallet.wallet.asyncKeyAgent.knownAddresses$.subscribe((addresses) => { - if (addresses.length === 0) return; - const hdWalletDiscovered = addresses.some((addr) => addr.index > 0); - if (setupType === SetupType.RESTORE && hdWalletDiscovered) { - analytics.sendEventToPostHog(PostHogAction.OnboardingRestoreHdWallet); - } - addressDiscoverySubscriber.unsubscribe(); - }); if (setupType === SetupType.FORGOT_PASSWORD) { deleteFromLocalStorage('isForgotPasswordFlow'); diff --git a/packages/cardano/package.json b/packages/cardano/package.json index 4f8fcf1f6b..7e75a3e14d 100644 --- a/packages/cardano/package.json +++ b/packages/cardano/package.json @@ -39,7 +39,7 @@ "watch": "yarn build --watch" }, "dependencies": { - "@cardano-sdk/cardano-services-client": "0.15.1", + "@cardano-sdk/cardano-services-client": "0.16.0", "@cardano-sdk/core": "0.22.2", "@cardano-sdk/crypto": "0.1.18", "@cardano-sdk/hardware-ledger": "0.8.0", diff --git a/packages/cardano/src/ui/components/StakePoolTableBrowser/StakePoolTableBrowser.tsx b/packages/cardano/src/ui/components/StakePoolTableBrowser/StakePoolTableBrowser.tsx index 4d2f7a25ac..dd65694b0b 100644 --- a/packages/cardano/src/ui/components/StakePoolTableBrowser/StakePoolTableBrowser.tsx +++ b/packages/cardano/src/ui/components/StakePoolTableBrowser/StakePoolTableBrowser.tsx @@ -11,7 +11,7 @@ import { StakePoolItemBrowser, StakePoolItemBrowserProps } from '../StakePoolIte import { TranslationsFor } from '@wallet/util/types'; import { ReactComponent as Arrow } from '../../assets/icons/arrow.component.svg'; -type SortKey = 'name' | 'apy' | 'cost' | 'saturation'; +type SortKey = 'name' | 'ros' | 'cost' | 'saturation'; type SortDirection = 'asc' | 'desc'; export type StakePoolSortOptions = { @@ -61,7 +61,7 @@ export const StakePoolTableBrowser = ({ }: StakePoolTableBrowserProps): React.ReactElement => { const headers: TableHeaders[] = [ { label: translations.poolName, value: 'name' }, - { label: translations.apy, value: 'apy' }, + { label: translations.apy, value: 'ros' }, { label: translations.cost, value: 'cost' }, { label: translations.saturation, value: 'saturation' } ]; diff --git a/packages/cardano/src/wallet/lib/__tests__/build-delegation.test.ts b/packages/cardano/src/wallet/lib/__tests__/build-delegation.test.ts index fa0e08a24b..bbfd3ea086 100644 --- a/packages/cardano/src/wallet/lib/__tests__/build-delegation.test.ts +++ b/packages/cardano/src/wallet/lib/__tests__/build-delegation.test.ts @@ -1,22 +1,31 @@ import { Cardano } from '@cardano-sdk/core'; +import { Hash28ByteBase16 } from '@cardano-sdk/crypto'; import { ObservableWallet } from '@cardano-sdk/wallet'; import { rewardAcountMock } from '@src/wallet/test/mocks/mock'; import { mockObservableWallet } from '@src/wallet/test/mocks'; import { firstValueFrom, of } from 'rxjs'; import { buildDelegation } from '../build-delegation'; - +const { + RewardAccount, + CredentialType: { KeyHash }, + CertificateType, + StakeKeyStatus +} = Cardano; describe('Testing buildDelegation', () => { const poolId = Cardano.PoolId('pool185g59xpqzt7gf0ljr8v8f3akl95qnmardf2f8auwr3ffx7atjj5'); - const stakeKeyHash = Cardano.RewardAccount.toHash(rewardAcountMock.address); - + const stakeKeyHash = RewardAccount.toHash(rewardAcountMock.address); + const stakeCredential = { + type: KeyHash, + hash: Hash28ByteBase16.fromEd25519KeyHashHex(stakeKeyHash) + }; const stakeKeyCertificate: Cardano.StakeAddressCertificate = { - __typename: Cardano.CertificateType.StakeKeyRegistration, - stakeKeyHash + __typename: CertificateType.StakeRegistration, + stakeCredential }; const delegationCertificate: Cardano.StakeDelegationCertificate = { - __typename: Cardano.CertificateType.StakeDelegation, - stakeKeyHash, + __typename: CertificateType.StakeDelegation, + stakeCredential, poolId }; @@ -24,7 +33,7 @@ describe('Testing buildDelegation', () => { const wallet = { ...mockObservableWallet, delegation: { - rewardAccounts$: of([{ ...rewardAcountMock, keyStatus: Cardano.StakeKeyStatus.Unregistered }]) + rewardAccounts$: of([{ ...rewardAcountMock, keyStatus: StakeKeyStatus.Unregistered }]) } } as unknown as ObservableWallet; const { certificates } = await buildDelegation(wallet, poolId); @@ -39,7 +48,7 @@ describe('Testing buildDelegation', () => { delegation: { rewardAccounts$: of([rewardAcountMock]) } } as unknown as ObservableWallet; const walletRewardAccount = (await firstValueFrom(wallet.delegation.rewardAccounts$))[0]; - walletRewardAccount.keyStatus = Cardano.StakeKeyStatus.Registered; + walletRewardAccount.keyStatus = StakeKeyStatus.Registered; const { certificates } = await buildDelegation(wallet, poolId); expect(certificates).toContainEqual(delegationCertificate); diff --git a/packages/cardano/src/wallet/lib/build-delegation.ts b/packages/cardano/src/wallet/lib/build-delegation.ts index c81040c53a..f8e39832ff 100644 --- a/packages/cardano/src/wallet/lib/build-delegation.ts +++ b/packages/cardano/src/wallet/lib/build-delegation.ts @@ -1,8 +1,14 @@ import { Cardano } from '@cardano-sdk/core'; import { ObservableWallet } from '@cardano-sdk/wallet'; import { InitializeTxProps } from '@cardano-sdk/tx-construction'; +import { Hash28ByteBase16 } from '@cardano-sdk/crypto'; import { firstValueFrom } from 'rxjs'; -const { CertificateType, StakeKeyStatus, RewardAccount } = Cardano; +const { + CertificateType, + StakeKeyStatus, + RewardAccount, + CredentialType: { KeyHash } +} = Cardano; const buildDelegationCertificates = ( walletRewardAccount: Cardano.RewardAccountInfo, @@ -12,16 +18,20 @@ const buildDelegationCertificates = ( const isStakeKeyRegistered = keyStatus === StakeKeyStatus.Registered; const stakeKeyHash = RewardAccount.toHash(rewardAccount); + const stakeCredential = { + hash: Hash28ByteBase16.fromEd25519KeyHashHex(stakeKeyHash), + type: KeyHash + }; const delegationCertificate: Cardano.StakeDelegationCertificate = { __typename: CertificateType.StakeDelegation, - stakeKeyHash, + stakeCredential, poolId: poolToDelegateId }; const stakeKeyCertificate: Cardano.StakeAddressCertificate = { - __typename: CertificateType.StakeKeyRegistration, - stakeKeyHash + __typename: CertificateType.StakeRegistration, + stakeCredential }; return [...(isStakeKeyRegistered ? [] : [stakeKeyCertificate]), delegationCertificate]; diff --git a/packages/cardano/src/wallet/lib/cardano-wallet.ts b/packages/cardano/src/wallet/lib/cardano-wallet.ts index d8f2c69edf..f07b9f5635 100644 --- a/packages/cardano/src/wallet/lib/cardano-wallet.ts +++ b/packages/cardano/src/wallet/lib/cardano-wallet.ts @@ -11,18 +11,15 @@ import { UtxoProvider } from '@cardano-sdk/core'; import { - setupWallet, ObservableWallet, PersonalWalletDependencies, storage, - SetupWalletProps, restoreKeyAgent } from '../../../../../node_modules/@cardano-sdk/wallet/dist/cjs'; import * as KeyManagement from '../../../../../node_modules/@cardano-sdk/key-management/dist/cjs'; import { WalletManagerActivateProps, WalletManagerUi } from '@cardano-sdk/web-extension'; import { ChainName, WalletManagerProviderTypes } from '../types'; import * as Crypto from '@cardano-sdk/crypto'; -import { createWalletUtil } from '@cardano-sdk/wallet'; // Using nodejs CML version to satisfy the tests requirements, but this gets replaced by webpack to the browser version in the build import * as CML from '@dcspark/cardano-multiplatform-lib-nodejs'; @@ -76,34 +73,27 @@ export const activateWallet = async ( export const createCardanoWalletsByChain = async ( mnemonicWords: string[], getPassword: () => Promise, - activeChainId: Cardano.ChainId, - createWallet: SetupWalletProps['createWallet'] -): Promise> => { + activeChainId: Cardano.ChainId +): Promise> => { const keyAgentsByChain: KeyAgentsByChain = {} as KeyAgentsByChain; let activeChainName: ChainName; const setup = async ({ chainId }: { chainId: Cardano.ChainId }) => { - const { keyAgent, ...rest } = await setupWallet({ - createKeyAgent: async (dependencies) => - await KeyManagement.InMemoryKeyAgent.fromBip39MnemonicWords( - { - mnemonicWords, - getPassphrase: getPassword, - chainId - }, - { ...dependencies } - ), - createWallet, - logger: console, - bip32Ed25519: new Crypto.CmlBip32Ed25519(CML) - }); - - return { keyAgent, ...rest }; + const keyAgent = await KeyManagement.InMemoryKeyAgent.fromBip39MnemonicWords( + { + mnemonicWords, + getPassphrase: getPassword, + chainId + }, + { logger: console, bip32Ed25519: new Crypto.CmlBip32Ed25519(CML) } + ); + + return { keyAgent }; }; // Key agent for wallet to activate // getPassphrase referrs to user's spending password. Change introduced in https://github.com/input-output-hk/cardano-js-sdk/releases/tag/%40cardano-sdk%2Fwallet%400.8.0 - const { keyAgent: activeKeyAgent, wallet } = await setup({ chainId: activeChainId }); + const { keyAgent: activeKeyAgent } = await setup({ chainId: activeChainId }); for (const [chainName, chainId] of Object.entries(Cardano.ChainIds)) { if (chainId.networkId === activeChainId.networkId && chainId.networkMagic === activeChainId.networkMagic) activeChainName = chainName as ChainName; @@ -124,7 +114,7 @@ export const createCardanoWalletsByChain = async ( }) ); - return { wallet, keyAgent: activeKeyAgent, keyAgentsByChain }; + return { keyAgent: activeKeyAgent, keyAgentsByChain }; }; const createAsyncKeyAgentWithCallback = ( @@ -166,13 +156,7 @@ export const createCardanoWallet = async ( ): Promise => { const { wallet } = walletManagerUi; - const createWallet = async () => wallet; - const { keyAgent, keyAgentsByChain } = await createCardanoWalletsByChain( - mnemonicWords, - getPassword, - activeChainId, - createWallet - ); + const { keyAgent, keyAgentsByChain } = await createCardanoWalletsByChain(mnemonicWords, getPassword, activeChainId); const asyncKeyAgent = KeyManagement.util.createAsyncKeyAgent(keyAgent); await walletManagerUi.activate({ keyAgent: asyncKeyAgent, observableWalletName: name }); @@ -185,16 +169,14 @@ export const createCardanoWallet = async ( */ export const restoreWallet = async ( keyAgentData: KeyManagement.SerializableKeyAgentData, - getPassword: () => Promise, - createWallet: SetupWalletProps['createWallet'] -): Promise<{ keyAgent: KeyManagement.KeyAgent; wallet: ObservableWallet }> => { - const { keyAgent, wallet } = await setupWallet({ - createKeyAgent: async (dependencies) => await restoreKeyAgent(keyAgentData, dependencies, getPassword), - createWallet, - logger: console, - bip32Ed25519: new Crypto.CmlBip32Ed25519(CML) - }); - return { keyAgent, wallet }; + getPassword: () => Promise +): Promise<{ keyAgent: KeyManagement.KeyAgent }> => { + const keyAgent = await restoreKeyAgent( + keyAgentData, + { logger: console, bip32Ed25519: new Crypto.CmlBip32Ed25519(CML) }, + getPassword + ); + return { keyAgent }; }; /** @@ -212,8 +194,7 @@ export const restoreWalletFromKeyAgent = async ( ): Promise => { const { wallet } = walletManagerUi; - const createWallet = async () => wallet; - const { keyAgent } = await restoreWallet(keyAgentData, getPassword, createWallet); + const { keyAgent } = await restoreWallet(keyAgentData, getPassword); // TODO: LW-7807 revise the sdk cip30 implementation const asyncKeyAgent = createAsyncKeyAgentWithCallback(keyAgent, callback); @@ -259,8 +240,7 @@ export const validateWalletPassword = async ( // Not needed for this { logger: console, - bip32Ed25519: new Crypto.CmlBip32Ed25519(CML), - inputResolver: { resolveInput: () => null } + bip32Ed25519: new Crypto.CmlBip32Ed25519(CML) }, getPassword ); @@ -286,8 +266,7 @@ export const validateWalletMnemonic = async ( // Not needed for this { logger: console, - bip32Ed25519: new Crypto.CmlBip32Ed25519(CML), - inputResolver: { resolveInput: () => null } + bip32Ed25519: new Crypto.CmlBip32Ed25519(CML) }, getPassword ); @@ -302,8 +281,7 @@ export const validateWalletMnemonic = async ( // Not needed for this { logger: console, - bip32Ed25519: new Crypto.CmlBip32Ed25519(CML), - inputResolver: { resolveInput: () => null } + bip32Ed25519: new Crypto.CmlBip32Ed25519(CML) } ); const validatingChallenge = await Challenge.sign(validatingKeyAgent); @@ -337,18 +315,14 @@ export const switchKeyAgents = async ( }; export const createKeyAgent = ( - walletManagerUi: WalletManagerUi, keyAgentData: KeyManagement.SerializableKeyAgentData, getPassword: () => Promise -): Promise => { - const walletUtil = createWalletUtil(walletManagerUi.wallet); - return restoreKeyAgent( +): Promise => + restoreKeyAgent( keyAgentData, { logger: console, - bip32Ed25519: new Crypto.CmlBip32Ed25519(CML), - inputResolver: walletUtil + bip32Ed25519: new Crypto.CmlBip32Ed25519(CML) }, getPassword ); -}; diff --git a/packages/cardano/src/wallet/lib/hardware-wallet.ts b/packages/cardano/src/wallet/lib/hardware-wallet.ts index e710ef69d2..00bd00bae9 100644 --- a/packages/cardano/src/wallet/lib/hardware-wallet.ts +++ b/packages/cardano/src/wallet/lib/hardware-wallet.ts @@ -1,6 +1,5 @@ /* eslint-disable unicorn/no-null */ import { Cardano } from '@cardano-sdk/core'; -import { ObservableWallet, setupWallet, SetupWalletProps } from '@cardano-sdk/wallet'; import * as KeyManagement from '../../../../../node_modules/@cardano-sdk/key-management/dist/cjs'; import { ChainName, DeviceConnection, CreateHardwareWalletArgs, HardwareWallets } from '../types'; import { CardanoWalletByChain, KeyAgentsByChain } from '@src/wallet'; @@ -8,7 +7,6 @@ import { WalletManagerUi } from '@cardano-sdk/web-extension'; import * as Crypto from '@cardano-sdk/crypto'; 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 { TrezorConfig } from '@cardano-sdk/key-management'; // Using nodejs CML version to satisfy the tests requirements, but this gets replaced by webpack to the browser version in the build import * as CML from '@dcspark/cardano-multiplatform-lib-nodejs'; @@ -53,115 +51,49 @@ const connectDevices: Record Promise> = export const connectDevice = async (model: HardwareWallets): Promise => await connectDevices[model](); -const createWithLedgerDeviceConnection = async ( - { - chainId, - accountIndex = 0, - communicationType, - extendedAccountPublicKey - }: Omit< - Parameters[0] & { - extendedAccountPublicKey?: Crypto.Bip32PublicKeyHex; - }, - 'deviceConnection' - >, - deviceConnection: DeviceConnection, - dependencies: Parameters[1] -) => { - // Throws an authentication error if called after the first key agent creation - const publicKey = - extendedAccountPublicKey ?? - (await HardwareLedger.LedgerKeyAgent.getXpub({ - accountIndex, - communicationType, - deviceConnection: deviceConnection as HardwareLedger.LedgerKeyAgent['deviceConnection'] - })); - - return new HardwareLedger.LedgerKeyAgent( - { - accountIndex, - chainId, - communicationType, - deviceConnection: deviceConnection as HardwareLedger.LedgerKeyAgent['deviceConnection'], - extendedAccountPublicKey: publicKey, - knownAddresses: [] - }, - dependencies - ); -}; -const createWithTrezorDeviceConnection = async ( - { - chainId, - accountIndex = 0, - extendedAccountPublicKey, - trezorConfig - }: { - chainId: Cardano.ChainId; - accountIndex?: number; - extendedAccountPublicKey: Crypto.Bip32PublicKeyHex; - trezorConfig: TrezorConfig; - }, - dependencies: Parameters[1] -) => { - // Throws an authentication error if called after the first key agent creation - const publicKey = - extendedAccountPublicKey ?? - (await HardwareTrezor.TrezorKeyAgent.getXpub({ - accountIndex, - communicationType: KeyManagement.CommunicationType.Web - })); - - return new HardwareTrezor.TrezorKeyAgent( - { - accountIndex, - chainId, - extendedAccountPublicKey: publicKey, - knownAddresses: [], - trezorConfig - }, - dependencies - ); -}; - // TODO: try to refactor the one in `cardano-wallet` to be able to use it here too. [LW-5459] // Biggest issue looks like it's the two different ways to create the key agents in this one export const createHardwareWalletsByChain = async ( accountIndex: number, deviceConnection: DeviceConnection, activeChainId: Cardano.ChainId, - createWallet: SetupWalletProps['createWallet'], connectedDevice: HardwareWallets -): Promise> => { +): Promise> => { const keyAgentsByChain: KeyAgentsByChain = {} as KeyAgentsByChain; let activeChainName: ChainName; + const setup = async ({ + chainId, + connectedDevice: currentconnectedDevice + }: { + chainId: Cardano.ChainId; + connectedDevice: KeyManagement.KeyAgentType; + }) => { + const keyAgent = + currentconnectedDevice === KeyManagement.KeyAgentType.Ledger + ? await HardwareLedger.LedgerKeyAgent.createWithDevice( + { + communicationType: DEFAULT_COMMUNICATION_TYPE, + accountIndex, + deviceConnection: deviceConnection as HardwareLedger.LedgerKeyAgent['deviceConnection'], + chainId + }, + { logger: console, bip32Ed25519: new Crypto.CmlBip32Ed25519(CML) } + ) + : await HardwareTrezor.TrezorKeyAgent.createWithDevice( + { + accountIndex, + trezorConfig: TREZOR_CONFIG, + chainId + }, + { logger: console, bip32Ed25519: new Crypto.CmlBip32Ed25519(CML) } + ); + return { keyAgent }; + }; + // Key agent for wallet to activate - const { keyAgent: activeKeyAgent, wallet } = await setupWallet({ - createKeyAgent: async (dependencies) => { - if (connectedDevice === KeyManagement.KeyAgentType.Ledger) { - return await HardwareLedger.LedgerKeyAgent.createWithDevice( - { - communicationType: DEFAULT_COMMUNICATION_TYPE, - accountIndex, - deviceConnection: deviceConnection as HardwareLedger.LedgerKeyAgent['deviceConnection'], - chainId: activeChainId - }, - dependencies - ); - } - return await HardwareTrezor.TrezorKeyAgent.createWithDevice( - { - accountIndex, - trezorConfig: TREZOR_CONFIG, - chainId: activeChainId - }, - dependencies - ); - }, - createWallet, - logger: console, - bip32Ed25519: new Crypto.CmlBip32Ed25519(CML) - }); + const { keyAgent: activeKeyAgent } = await setup({ chainId: activeChainId, connectedDevice }); + for (const [chainName, chainId] of Object.entries(Cardano.ChainIds)) { if (chainId.networkId === activeChainId.networkId && chainId.networkMagic === activeChainId.networkMagic) activeChainName = chainName as ChainName; @@ -175,43 +107,14 @@ export const createHardwareWalletsByChain = async ( .filter(([chainName]) => chainName !== activeChainName) .map(async ([chainName, chainId]) => { // Create a key agent for each chain id to save in storage - const { keyAgent } = await setupWallet({ - createKeyAgent: async (dependencies) => { - if (connectedDevice === KeyManagement.KeyAgentType.Ledger) { - // Cannot use `createWithDevice` several times because it throws a locked device error after the first one - return await createWithLedgerDeviceConnection( - { - communicationType: DEFAULT_COMMUNICATION_TYPE, - accountIndex, - chainId, - // Re-use public key from first key agent created - extendedAccountPublicKey: activeKeyAgent.extendedAccountPublicKey - }, - deviceConnection, - dependencies - ); - } - return await createWithTrezorDeviceConnection( - { - accountIndex, - chainId, - // Re-use public key from first key agent created - extendedAccountPublicKey: activeKeyAgent.extendedAccountPublicKey, - trezorConfig: TREZOR_CONFIG - }, - dependencies - ); - }, - createWallet, - logger: console, - bip32Ed25519: new Crypto.CmlBip32Ed25519(CML) - }); + const { keyAgent } = await await setup({ chainId, connectedDevice }); + // Build object with key agents for all chains to be able to switch to eventually keyAgentsByChain[chainName as ChainName] = { keyAgentData: keyAgent.serializableData }; }) ); - return { wallet, keyAgent: activeKeyAgent, keyAgentsByChain }; + return { keyAgent: activeKeyAgent, keyAgentsByChain }; }; export const createHardwareWallet = async ( @@ -226,12 +129,10 @@ export const createHardwareWallet = async ( ): Promise => { const { wallet } = walletManagerUi; - const createWallet = async () => wallet; const { keyAgent, keyAgentsByChain } = await createHardwareWalletsByChain( accountIndex, deviceConnection, activeChainId, - createWallet, connectedDevice ); diff --git a/packages/cardano/src/wallet/test/mocks/ProviderStub.ts b/packages/cardano/src/wallet/test/mocks/ProviderStub.ts index a753891e6c..eb51fa0610 100644 --- a/packages/cardano/src/wallet/test/mocks/ProviderStub.ts +++ b/packages/cardano/src/wallet/test/mocks/ProviderStub.ts @@ -102,7 +102,7 @@ export const queryTransactionsResult: Cardano.HydratedTx[] = [ body: { certificates: [ { - __typename: Cardano.CertificateType.StakeKeyRegistration + __typename: Cardano.CertificateType.StakeRegistration }, { __typename: Cardano.CertificateType.StakeDelegation, diff --git a/packages/cardano/src/wallet/test/mocks/StakepoolSearchProviderStub.ts b/packages/cardano/src/wallet/test/mocks/StakepoolSearchProviderStub.ts index abacce350f..3d3c2aa092 100644 --- a/packages/cardano/src/wallet/test/mocks/StakepoolSearchProviderStub.ts +++ b/packages/cardano/src/wallet/test/mocks/StakepoolSearchProviderStub.ts @@ -20,7 +20,9 @@ export const pools: Partial[] = [ livePledge: BigInt('2000000000'), saturation: Percent(0.211), size: undefined, - stake: undefined + stake: undefined, + ros: Percent(0.69), + lastRos: Percent(0.88) }, margin: { numerator: 2.01, @@ -58,7 +60,9 @@ export const pools: Partial[] = [ stake: { live: BigInt('201000000'), active: BigInt('201000000') - } + }, + ros: Percent(0.69), + lastRos: Percent(0.88) }, metadata: { name: 'THE AMSTERDAM NODE', @@ -92,7 +96,9 @@ export const pools: Partial[] = [ stake: { live: BigInt('77000000'), active: BigInt('77000000') - } + }, + ros: Percent(0.69), + lastRos: Percent(0.88) } }, { @@ -114,7 +120,9 @@ export const pools: Partial[] = [ stake: { live: BigInt('34000000'), active: BigInt('34000000') - } + }, + ros: Percent(0.69), + lastRos: Percent(0.88) }, metadata: { name: 'stakit.io Pool by TOBG', @@ -144,7 +152,9 @@ export const pools: Partial[] = [ stake: { live: BigInt('53000000'), active: BigInt('53000000') - } + }, + ros: Percent(0.69), + lastRos: Percent(0.88) }, margin: { numerator: 0.79, @@ -172,7 +182,9 @@ export const pools: Partial[] = [ stake: { live: BigInt('53000000'), active: BigInt('53000000') - } + }, + ros: Percent(0.69), + lastRos: Percent(0.88) }, metadata: { name: 'VEGASPool', @@ -202,7 +214,9 @@ const detailsForAll: PoolDetails = { stake: { live: BigInt('34000000'), active: BigInt('34000000') - } + }, + ros: Percent(0.69), + lastRos: Percent(0.88) }, relays: undefined, rewardAccount: Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj'), diff --git a/packages/cardano/src/wallet/test/mocks/TestKeyAgent.ts b/packages/cardano/src/wallet/test/mocks/TestKeyAgent.ts index add65fd7aa..35ff26458d 100644 --- a/packages/cardano/src/wallet/test/mocks/TestKeyAgent.ts +++ b/packages/cardano/src/wallet/test/mocks/TestKeyAgent.ts @@ -21,11 +21,8 @@ export const testKeyAgent = ({ mnemonicWords: mnemonicWords ?? KeyManagement.util.generateMnemonicWords(), chainId }, - // eslint-disable-next-line unicorn/no-null { logger: console, - bip32Ed25519: new Crypto.CmlBip32Ed25519(CML), - // eslint-disable-next-line unicorn/no-null - inputResolver: { resolveInput: jest.fn().mockResolvedValue(null) } + bip32Ed25519: new Crypto.CmlBip32Ed25519(CML) } ); diff --git a/packages/cardano/src/wallet/test/mocks/mock-wallet.ts b/packages/cardano/src/wallet/test/mocks/mock-wallet.ts index fb2ec5b6e1..8d82d60067 100644 --- a/packages/cardano/src/wallet/test/mocks/mock-wallet.ts +++ b/packages/cardano/src/wallet/test/mocks/mock-wallet.ts @@ -40,15 +40,14 @@ export const mockWallet = async (customKeyAgent?: KeyManagement.InMemoryKeyAgent address, rewardAccount }); - keyAgent.knownAddresses = [knownAddresses]; const utxoProvider = utxoProviderStub(); const chainHistoryProvider = chainHistoryProviderStub(); const rewardsProvider = rewardsHistoryProviderStub(); + const asyncKeyAgent = KeyManagement.util.createAsyncKeyAgent(keyAgent); const wallet = new PersonalWallet( { name }, { assetProvider, - keyAgent: KeyManagement.util.createAsyncKeyAgent(keyAgent), stakePoolProvider, networkInfoProvider, stores: storage.createInMemoryWalletStores(), @@ -56,7 +55,9 @@ export const mockWallet = async (customKeyAgent?: KeyManagement.InMemoryKeyAgent rewardsProvider, chainHistoryProvider, utxoProvider, - logger + logger, + witnesser: KeyManagement.util.createBip32Ed25519Witnesser(asyncKeyAgent), + bip32Account: new KeyManagement.Bip32Account(keyAgent) } ); diff --git a/packages/cardano/src/wallet/test/mocks/mock.ts b/packages/cardano/src/wallet/test/mocks/mock.ts index 723ea89e24..e906576329 100644 --- a/packages/cardano/src/wallet/test/mocks/mock.ts +++ b/packages/cardano/src/wallet/test/mocks/mock.ts @@ -32,7 +32,9 @@ export const stakePoolMock: Cardano.StakePool = { livePledge: BigInt('2000000000'), saturation: Percent(0.5), stake: undefined, - size: undefined + size: undefined, + ros: Percent(0.69), + lastRos: Percent(0.88) }, owners: [ Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj'), diff --git a/packages/cardano/src/wallet/util/__tests__/stake-pool-transformer.test.ts b/packages/cardano/src/wallet/util/__tests__/stake-pool-transformer.test.ts index cd6dfaa23f..2c2e1020aa 100644 --- a/packages/cardano/src/wallet/util/__tests__/stake-pool-transformer.test.ts +++ b/packages/cardano/src/wallet/util/__tests__/stake-pool-transformer.test.ts @@ -40,7 +40,9 @@ const cardanoStakePoolMock: StakePoolSearchResults = { saturation: Percent(0.0512), stake: undefined, size: undefined, - apy: Percent(0.013) + apy: Percent(0.013), + ros: Percent(0.69), + lastRos: Percent(0.88) }, owners: [ Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj'), diff --git a/packages/staking/src/features/BrowsePools/StakePoolsTable/StakePoolTableBrowser/StakePoolTableBrowser.tsx b/packages/staking/src/features/BrowsePools/StakePoolsTable/StakePoolTableBrowser/StakePoolTableBrowser.tsx index 21dc4accf0..9bde4bd2b9 100644 --- a/packages/staking/src/features/BrowsePools/StakePoolsTable/StakePoolTableBrowser/StakePoolTableBrowser.tsx +++ b/packages/staking/src/features/BrowsePools/StakePoolsTable/StakePoolTableBrowser/StakePoolTableBrowser.tsx @@ -16,7 +16,7 @@ import Arrow from './arrow.svg'; import styles from './StakePoolTableBrowser.module.scss'; type TranslationsFor = Record; -type SortKey = 'name' | 'apy' | 'saturation'; +type SortKey = 'name' | 'ros' | 'saturation'; type SortDirection = 'asc' | 'desc'; export type StakePoolSortOptions = { @@ -79,7 +79,7 @@ export const StakePoolTableBrowser = ({ { label: translations.apy, tooltipText: t('browsePools.stakePoolTableBrowser.tableHeader.ros.tooltip'), - value: 'apy', + value: 'ros', }, { label: translations.saturation, @@ -93,7 +93,7 @@ export const StakePoolTableBrowser = ({ field === activeSort?.field ? ((activeSort?.order === 'asc' ? 'desc' : 'asc') as SortDirection) : 'asc'; if (field === 'name') { analytics.sendEventToPostHog(PostHogAction.StakingBrowsePoolsPoolNameClick); - } else if (field === 'apy') { + } else if (field === 'ros') { analytics.sendEventToPostHog(PostHogAction.StakingBrowsePoolsRosClick); } else { analytics.sendEventToPostHog(PostHogAction.StakingBrowsePoolsSaturationClick); diff --git a/packages/staking/src/features/BrowsePools/StakePoolsTable/StakePoolsTable.tsx b/packages/staking/src/features/BrowsePools/StakePoolsTable/StakePoolsTable.tsx index 5d30e2bcf7..10ab0cc6f2 100644 --- a/packages/staking/src/features/BrowsePools/StakePoolsTable/StakePoolsTable.tsx +++ b/packages/staking/src/features/BrowsePools/StakePoolsTable/StakePoolsTable.tsx @@ -16,7 +16,7 @@ type StakePoolsTableProps = { }; const DEFAULT_SORT_OPTIONS: StakePoolSortOptions = { - field: 'apy', + field: 'ros', order: 'desc', }; diff --git a/yarn.lock b/yarn.lock index 006bf92aa7..bc38461983 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6651,20 +6651,6 @@ __metadata: languageName: node linkType: hard -"@cardano-sdk/cardano-services-client@npm:0.15.1": - version: 0.15.1 - resolution: "@cardano-sdk/cardano-services-client@npm:0.15.1" - dependencies: - "@cardano-sdk/core": ~0.22.1 - "@cardano-sdk/util": ~0.14.4 - axios: ^0.27.2 - class-validator: ^0.14.0 - json-bigint: ~1.0.0 - ts-log: ^2.2.4 - checksum: ae3375d9171dbc7a0972756f2ad53a97b8a76986b18d902334d31d46439db21ce6a059449d29ce8d1e7013e70ba929279b569f3a61d8c25184c50c4929936ef1 - languageName: node - linkType: hard - "@cardano-sdk/cardano-services-client@npm:0.16.0": version: 0.16.0 resolution: "@cardano-sdk/cardano-services-client@npm:0.16.0" @@ -6704,31 +6690,6 @@ __metadata: languageName: node linkType: hard -"@cardano-sdk/core@npm:~0.22.1": - version: 0.22.1 - resolution: "@cardano-sdk/core@npm:0.22.1" - dependencies: - "@cardano-ogmios/client": 5.6.0 - "@cardano-ogmios/schema": 5.6.0 - "@cardano-sdk/crypto": ~0.1.17 - "@cardano-sdk/util": ~0.14.4 - "@foxglove/crc": ^0.0.3 - "@scure/base": ^1.1.1 - fraction.js: 4.0.1 - ip-address: ^8.1.0 - lodash: ^4.17.21 - ts-custom-error: ^3.2.0 - ts-log: ^2.2.4 - web-encoding: ^1.1.5 - peerDependencies: - rxjs: ^7.4.0 - peerDependenciesMeta: - rxjs: - optional: true - checksum: 17287936a8ab3fd0efe7bf1ef443f9a032517e7648e09f89fb4261574f3366a210d15446d58a0a992925690de480a910d030b9b0ba29fbc19a6dd76e19d962e4 - languageName: node - linkType: hard - "@cardano-sdk/crypto@npm:0.1.18, @cardano-sdk/crypto@npm:~0.1.18": version: 0.1.18 resolution: "@cardano-sdk/crypto@npm:0.1.18" @@ -6758,35 +6719,6 @@ __metadata: languageName: node linkType: hard -"@cardano-sdk/crypto@npm:~0.1.17": - version: 0.1.17 - resolution: "@cardano-sdk/crypto@npm:0.1.17" - dependencies: - "@cardano-sdk/util": ~0.14.4 - blake2b: ^2.1.4 - bn.js: ^5.2.1 - i: ^0.3.7 - libsodium-wrappers-sumo: ^0.7.5 - lodash: ^4.17.21 - npm: ^9.3.0 - pbkdf2: ^3.1.2 - ts-custom-error: ^3.2.0 - ts-log: ^2.2.4 - peerDependencies: - "@dcspark/cardano-multiplatform-lib-asmjs": ^3.1.1 - "@dcspark/cardano-multiplatform-lib-browser": ^3.1.1 - "@dcspark/cardano-multiplatform-lib-nodejs": ^3.1.1 - peerDependenciesMeta: - "@dcspark/cardano-multiplatform-lib-asmjs": - optional: true - "@dcspark/cardano-multiplatform-lib-browser": - optional: true - "@dcspark/cardano-multiplatform-lib-nodejs": - optional: true - checksum: eddc9bc50acc20cea8d383b23800733780b426dd2a96b5afd2100cbbbffbf1db76afb78a481018306a346e8377aa7bba6efb00e30af70517501959b0c9e0734b - languageName: node - linkType: hard - "@cardano-sdk/dapp-connector@npm:0.11.4, @cardano-sdk/dapp-connector@npm:~0.11.4": version: 0.11.4 resolution: "@cardano-sdk/dapp-connector@npm:0.11.4" @@ -6943,20 +6875,6 @@ __metadata: languageName: node linkType: hard -"@cardano-sdk/util@npm:~0.14.4": - version: 0.14.4 - resolution: "@cardano-sdk/util@npm:0.14.4" - dependencies: - bech32: ^2.0.0 - lodash: ^4.17.21 - serialize-error: ^8 - ts-custom-error: ^3.2.0 - ts-log: ^2.2.4 - type-fest: ^2.19.0 - checksum: ec47978fa4ecbed7fb37f1be7ac68ad8d886e673e7ae527d91931bfc5766b5a41a3b778ceab63b2df3d4a7fcb133f79f2f82359e6e3850f1c9d72df8a6ff118f - languageName: node - linkType: hard - "@cardano-sdk/wallet@npm:0.27.1, @cardano-sdk/wallet@npm:~0.27.1": version: 0.27.1 resolution: "@cardano-sdk/wallet@npm:0.27.1" @@ -9417,7 +9335,7 @@ __metadata: version: 0.0.0-use.local resolution: "@lace/cardano@workspace:packages/cardano" dependencies: - "@cardano-sdk/cardano-services-client": 0.15.1 + "@cardano-sdk/cardano-services-client": 0.16.0 "@cardano-sdk/core": 0.22.2 "@cardano-sdk/crypto": 0.1.18 "@cardano-sdk/hardware-ledger": 0.8.0 From 0e385a4ecb12be958a537225ec210425cd53b26d Mon Sep 17 00:00:00 2001 From: Michael Chappell <7581002+mchappell@users.noreply.github.com> Date: Sat, 9 Dec 2023 13:08:52 +0000 Subject: [PATCH 24/68] fix: typos in translation --- apps/browser-extension-wallet/src/config.ts | 2 +- .../confirm-transaction/VotingProceduresContainer.tsx | 4 ++-- .../__tests__/ConfirmVoteDelegationContainer.test.tsx | 5 ++++- .../components/VotingProcedures/VotingProcedures.tsx | 10 ++++++---- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/apps/browser-extension-wallet/src/config.ts b/apps/browser-extension-wallet/src/config.ts index bdb23b42bd..2d4832a415 100644 --- a/apps/browser-extension-wallet/src/config.ts +++ b/apps/browser-extension-wallet/src/config.ts @@ -47,7 +47,7 @@ const envChecks = (chosenChain: Wallet.ChainName): void => { throw new Error('no available chains to connect to'); } - if (!process.env.AVAILABLE_CHAINS.includes('Mainnet')) { + if (!process.env.AVAILABLE_CHAINS.includes('Mainnet') && process.env.DEFAULT_CHAIN !== 'Sanchonet') { throw new Error('mainnet chain not available in env'); } diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/VotingProceduresContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/VotingProceduresContainer.tsx index 7fb85f6115..0ca9fc967d 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/VotingProceduresContainer.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/VotingProceduresContainer.tsx @@ -73,7 +73,7 @@ export const VotingProceduresContainer = ({ signTxData, errorMessage }: Props): : votingProcedure.voter.credential.hash.toString(); return { voter: { - type: t(`core.votingProcedures.voterType.${voterType}`), + type: t(`core.VotingProcedures.voterTypes.${voterType}`), dRepId: drepId }, votes: votingProcedure.votes.map((vote) => ({ @@ -83,7 +83,7 @@ export const VotingProceduresContainer = ({ signTxData, errorMessage }: Props): ...(explorerBaseUrl && { txHashUrl: `${explorerBaseUrl}/${vote.actionId.id}` }) }, votingProcedure: { - vote: t(`core.votingProcedures.votes.${getVote(vote.votingProcedure.vote)}`), + vote: t(`core.VotingProcedures.votes.${getVote(vote.votingProcedure.vote)}`), anchor: !!vote.votingProcedure.anchor?.url && { url: vote.votingProcedure.anchor?.url, hash: vote.votingProcedure.anchor?.dataHash.toString() diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmVoteDelegationContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmVoteDelegationContainer.test.tsx index dadcfafa27..cf89845c9e 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmVoteDelegationContainer.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmVoteDelegationContainer.test.tsx @@ -128,7 +128,10 @@ describe('Testing ConfirmVoteDelegationContainer component', () => { type: Wallet.Cardano.CredentialType.KeyHash, hash: Wallet.Crypto.Hash28ByteBase16(Buffer.from('dRepCredentialHashdRepCreden').toString('hex')) }, - stakeKeyHash: STAKE_KEY_HASH + stakeCredential: { + type: Wallet.Cardano.CredentialType.KeyHash, + hash: Wallet.Crypto.Hash28ByteBase16(Buffer.from(STAKE_KEY_HASH).toString('hex')) + } }; const tx = buildMockTx({ certificates: [certificate] diff --git a/packages/core/src/ui/components/VotingProcedures/VotingProcedures.tsx b/packages/core/src/ui/components/VotingProcedures/VotingProcedures.tsx index 232b6aa43d..cb7a124066 100644 --- a/packages/core/src/ui/components/VotingProcedures/VotingProcedures.tsx +++ b/packages/core/src/ui/components/VotingProcedures/VotingProcedures.tsx @@ -6,7 +6,7 @@ import { ErrorPane } from '@lace/common'; type VotingProcedure = { voter: { type: string; - dRepId: string; + dRepId?: string; }; votes: { actionId: { @@ -73,9 +73,11 @@ export const VotingProcedures = ({ dappInfo, errorMessage, data, translations }: - - - + {voter.dRepId && ( + + + + )} From 394dcf81a7d1098ae8f7c6d31290781b77eb932d Mon Sep 17 00:00:00 2001 From: Lucas Date: Mon, 11 Dec 2023 12:56:12 -0300 Subject: [PATCH 25/68] feat: stake and vote confirmation screens (#683) * chore/Bump cardano-js-sdk (#559) * chore!: bump cardano-js-sdk packages to latest versions * ci: add cardano services urls parameters to build step * Feat/sanchonet network switch support (#612) * feat: update env to support switching to sanchonet * chore: remove old testnet * chore: bump cardano-sdk packages to latest version --------- Co-authored-by: mirceahasegan <105701265+mirceahasegan@users.noreply.github.com> * [LW-7983] [LW-7984] dRep confirmation screens (#554) * refactor: move logic out of component * fix: file path * feat(core): setup storybook * refactor(ui): extract insufficient funds warning component * feat(core): add lace/ui package * feat(core): create ConfirmDRepRegistration component * feat(extension): install @lace/ui * refactor: move functions to utils * fix: type * refactor: types file * feat: check for DRep transaction * feat: integrate confirm drep registration component * refactor(core): use Metadata component * refactor(core): use flexbox * refactor(core): remove insufficient funds message * refactor(core): create ConfirmDRepRetirement * refactor: remove insufficient funds warning; break down main component * feat: add certificate data * feat: add drep retirement container * fix: condition * fix: type * fix: style * fix: check for anchor * fix: check for undefined certificates * fix: merge conflict * fix: type error * feat: temp env changes * feat(extension): use consistent naming of DRep ID * feat(extension): show anchor metadata if exists * feat(extension): convert user facing DRepID to bech32 * fix: add cardano symbol to deposit amount * chore(core): downgrade storybook to v6 * fix: update lock file * fix: styles and text * fix: storybook build * fix: storybook build * feat: check retired drep key * fix: check retired drep key --------- Co-authored-by: Renan Ferreira Co-authored-by: Michael Chappell <7581002+mchappell@users.noreply.github.com> * feat(extension): [LW-7984] voting procedures (#655) * feat(ui): create metadata component * wip * feat(ui): text link component * chore(ui): fix rebase * feat(ui): create metadata link component * feat(core): create voting procedures component * chore(extension): fix rebase * fix(ui): add work break * feat(extension): voting procedures * refactor(core): make actions optional * refactor(core): use action as key prop * refactor(extension): add custom title for voting procedures * [LW-8491] Voting delegation confirmation (#648) * feat(core): setup storybook * feat(core): add lace/ui package * feat: check for DRep transaction * feat: integrate confirm drep registration component * refactor: remove insufficient funds warning; break down main component * feat: add certificate data * feat: add drep retirement container * feat(extension): use consistent naming of DRep ID * fix: add cardano symbol to deposit amount * refactor: certificate inspector factory * feat: voting delegation * fix: type errors * fix: merge conflicts --------- Co-authored-by: Michael Chappell <7581002+mchappell@users.noreply.github.com> * chore: modify sanchonet urls (#667) * feat: drep update certificate signing (#664) * feat: stake and vote confirmation screens * fix: correct capitalisation of DRep ID * fix: voting translation titles * feat(extension): update staking credential path --------- Co-authored-by: Leonel Gobbi <57540576+lgobbi-atix@users.noreply.github.com> Co-authored-by: mirceahasegan <105701265+mirceahasegan@users.noreply.github.com> Co-authored-by: Renan Ferreira Co-authored-by: Michael Chappell <7581002+mchappell@users.noreply.github.com> --- apps/browser-extension-wallet/.env.example | 1 + ...rmStakeRegistrationDelegationContainer.tsx | 49 ++++++++++++ .../ConfirmStakeVoteDelegationContainer.tsx | 46 +++++++++++ ...akeVoteRegistrationDelegationContainer.tsx | 58 ++++++++++++++ .../ConfirmTransactionContent.tsx | 16 ++++ ...irmVoteRegistrationDelegationContainer.tsx | 53 +++++++++++++ .../ConfirmVoteDelegationContainer.test.tsx | 2 +- .../VotingProceduresContainer.test.tsx | 4 +- .../components/confirm-transaction/utils.ts | 47 +++++++++-- .../hooks/__tests__/useWalletManager.test.tsx | 4 +- .../src/lib/translations/en.json | 40 +++++++++- .../client/PostHogClient.ts | 2 +- packages/cardano/src/wallet/types.ts | 6 +- packages/core/src/index.ts | 4 + ...firmStakeRegistrationDelegation.stories.ts | 48 +++++++++++ .../ConfirmStakeRegistrationDelegation.tsx | 54 +++++++++++++ .../index.ts | 1 + .../ConfirmStakeVoteDelegation.stories.ts | 77 ++++++++++++++++++ .../ConfirmStakeVoteDelegation.tsx | 66 ++++++++++++++++ .../ConfirmStakeVoteDelegation/index.ts | 1 + ...StakeVoteRegistrationDelegation.stories.ts | 79 +++++++++++++++++++ ...ConfirmStakeVoteRegistrationDelegation.tsx | 76 ++++++++++++++++++ .../index.ts | 1 + ...nfirmVoteRegistrationDelegation.stories.ts | 77 ++++++++++++++++++ .../ConfirmVoteRegistrationDelegation.tsx | 71 +++++++++++++++++ .../index.ts | 1 + .../VotingProcedures/VotingProcedures.tsx | 4 +- packages/ui/src/design-system/index.ts | 2 +- 28 files changed, 873 insertions(+), 17 deletions(-) create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeRegistrationDelegationContainer.tsx create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeVoteDelegationContainer.tsx create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeVoteRegistrationDelegationContainer.tsx create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmVoteRegistrationDelegationContainer.tsx create mode 100644 packages/core/src/ui/components/ConfirmStakeRegistrationDelegation/ConfirmStakeRegistrationDelegation.stories.ts create mode 100644 packages/core/src/ui/components/ConfirmStakeRegistrationDelegation/ConfirmStakeRegistrationDelegation.tsx create mode 100644 packages/core/src/ui/components/ConfirmStakeRegistrationDelegation/index.ts create mode 100644 packages/core/src/ui/components/ConfirmStakeVoteDelegation/ConfirmStakeVoteDelegation.stories.ts create mode 100644 packages/core/src/ui/components/ConfirmStakeVoteDelegation/ConfirmStakeVoteDelegation.tsx create mode 100644 packages/core/src/ui/components/ConfirmStakeVoteDelegation/index.ts create mode 100644 packages/core/src/ui/components/ConfirmStakeVoteRegistrationDelegation/ConfirmStakeVoteRegistrationDelegation.stories.ts create mode 100644 packages/core/src/ui/components/ConfirmStakeVoteRegistrationDelegation/ConfirmStakeVoteRegistrationDelegation.tsx create mode 100644 packages/core/src/ui/components/ConfirmStakeVoteRegistrationDelegation/index.ts create mode 100644 packages/core/src/ui/components/ConfirmVoteRegistrationDelegation/ConfirmVoteRegistrationDelegation.stories.ts create mode 100644 packages/core/src/ui/components/ConfirmVoteRegistrationDelegation/ConfirmVoteRegistrationDelegation.tsx create mode 100644 packages/core/src/ui/components/ConfirmVoteRegistrationDelegation/index.ts diff --git a/apps/browser-extension-wallet/.env.example b/apps/browser-extension-wallet/.env.example index 586eddaa51..32524d075e 100644 --- a/apps/browser-extension-wallet/.env.example +++ b/apps/browser-extension-wallet/.env.example @@ -59,6 +59,7 @@ POSTHOG_PRODUCTION_TOKEN_PREVIEW=production-preview-token POSTHOG_DEV_TOKEN_MAINNET=dev-mainnet-token POSTHOG_DEV_TOKEN_PREPROD=dev-preprod-token POSTHOG_DEV_TOKEN_PREVIEW=dev-preview-token +POSTHOG_DEV_TOKEN_SANCHONET=dev-sanchonet-token # Cardano Services CARDANO_SERVICES_URL_MAINNET=https://backend.live-mainnet.eks.lw.iog.io diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeRegistrationDelegationContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeRegistrationDelegationContainer.tsx new file mode 100644 index 0000000000..c6f2ecea70 --- /dev/null +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeRegistrationDelegationContainer.tsx @@ -0,0 +1,49 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { ConfirmStakeRegistrationDelegation } from '@lace/core'; +import { SignTxData } from './types'; +import { certificateInspectorFactory } from './utils'; +import { Wallet } from '@lace/cardano'; +import { useWalletStore } from '@src/stores'; + +const { CertificateType } = Wallet.Cardano; + +interface Props { + signTxData: SignTxData; + errorMessage?: string; +} + +export const ConfirmStakeRegistrationDelegationContainer = ({ + signTxData, + errorMessage +}: Props): React.ReactElement => { + const { + walletUI: { cardanoCoin } + } = useWalletStore(); + const { t } = useTranslation(); + const certificate = certificateInspectorFactory( + CertificateType.StakeRegistrationDelegation + )(signTxData.tx); + const depositPaidWithCardanoSymbol = `${Wallet.util.lovelacesToAdaString(certificate.deposit.toString())} ${ + cardanoCoin.symbol + }`; + return ( + + ); +}; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeVoteDelegationContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeVoteDelegationContainer.tsx new file mode 100644 index 0000000000..3b73d16459 --- /dev/null +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeVoteDelegationContainer.tsx @@ -0,0 +1,46 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { ConfirmStakeVoteDelegation } from '@lace/core'; +import { SignTxData } from './types'; +import { certificateInspectorFactory } from './utils'; +import { Wallet } from '@lace/cardano'; + +const { CertificateType } = Wallet.Cardano; + +interface Props { + signTxData: SignTxData; + errorMessage?: string; +} + +export const ConfirmStakeVoteDelegationContainer = ({ signTxData, errorMessage }: Props): React.ReactElement => { + const { t } = useTranslation(); + const certificate = certificateInspectorFactory( + CertificateType.StakeVoteDelegation + )(signTxData.tx); + const dRep = certificate.dRep; + + return ( + + ); +}; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeVoteRegistrationDelegationContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeVoteRegistrationDelegationContainer.tsx new file mode 100644 index 0000000000..76d3881fe0 --- /dev/null +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeVoteRegistrationDelegationContainer.tsx @@ -0,0 +1,58 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { ConfirmStakeVoteRegistrationDelegation } from '@lace/core'; +import { SignTxData } from './types'; +import { certificateInspectorFactory } from './utils'; +import { Wallet } from '@lace/cardano'; +import { useWalletStore } from '@src/stores'; + +const { CertificateType } = Wallet.Cardano; + +interface Props { + signTxData: SignTxData; + errorMessage?: string; +} + +export const ConfirmStakeVoteRegistrationDelegationContainer = ({ + signTxData, + errorMessage +}: Props): React.ReactElement => { + const { t } = useTranslation(); + const { + walletUI: { cardanoCoin } + } = useWalletStore(); + const certificate = certificateInspectorFactory( + CertificateType.StakeVoteRegistrationDelegation + )(signTxData.tx); + const dRep = certificate.dRep; + const depositPaidWithCardanoSymbol = `${Wallet.util.lovelacesToAdaString(certificate.deposit.toString())} ${ + cardanoCoin.symbol + }`; + + return ( + + ); +}; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransactionContent.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransactionContent.tsx index 4d35efc983..03fea860f4 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransactionContent.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransactionContent.tsx @@ -7,6 +7,10 @@ import { ConfirmDRepRetirementContainer } from './ConfirmDRepRetirementContainer import { ConfirmVoteDelegationContainer } from './ConfirmVoteDelegationContainer'; import { VotingProceduresContainer } from './VotingProceduresContainer'; import { ConfirmDRepUpdateContainer } from './ConfirmDRepUpdateContainer'; +import { ConfirmVoteRegistrationDelegationContainer } from './ConfirmVoteRegistrationDelegationContainer'; +import { ConfirmStakeRegistrationDelegationContainer } from './ConfirmStakeRegistrationDelegationContainer'; +import { ConfirmStakeVoteRegistrationDelegationContainer } from './ConfirmStakeVoteRegistrationDelegationContainer'; +import { ConfirmStakeVoteDelegationContainer } from './ConfirmStakeVoteDelegationContainer'; import { Wallet } from '@lace/cardano'; interface Props { @@ -35,6 +39,18 @@ export const ConfirmTransactionContent = ({ txType, signTxData, onError, errorMe if (txType === Wallet.Cip30TxType.VotingProcedures) { return ; } + if (txType === Wallet.Cip30TxType.VoteRegistrationDelegation) { + return ; + } + if (txType === Wallet.Cip30TxType.StakeRegistrationDelegation) { + return ; + } + if (txType === Wallet.Cip30TxType.StakeVoteDelegationRegistration) { + return ; + } + if (txType === Wallet.Cip30TxType.StakeVoteDelegation) { + return ; + } return ; }; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmVoteRegistrationDelegationContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmVoteRegistrationDelegationContainer.tsx new file mode 100644 index 0000000000..5a0d86b9a2 --- /dev/null +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmVoteRegistrationDelegationContainer.tsx @@ -0,0 +1,53 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { ConfirmVoteRegistrationDelegation } from '@lace/core'; +import { SignTxData } from './types'; +import { certificateInspectorFactory } from './utils'; +import { Wallet } from '@lace/cardano'; +import { useWalletStore } from '@src/stores'; + +const { CertificateType } = Wallet.Cardano; + +interface Props { + signTxData: SignTxData; + errorMessage?: string; +} + +export const ConfirmVoteRegistrationDelegationContainer = ({ signTxData, errorMessage }: Props): React.ReactElement => { + const { t } = useTranslation(); + const { + walletUI: { cardanoCoin } + } = useWalletStore(); + const certificate = certificateInspectorFactory( + CertificateType.VoteRegistrationDelegation + )(signTxData.tx); + const dRep = certificate.dRep; + const depositPaidWithCardanoSymbol = `${Wallet.util.lovelacesToAdaString(certificate.deposit.toString())} ${ + cardanoCoin.symbol + }`; + + return ( + + ); +}; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmVoteDelegationContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmVoteDelegationContainer.test.tsx index cf89845c9e..3f2c77c6d7 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmVoteDelegationContainer.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmVoteDelegationContainer.test.tsx @@ -130,7 +130,7 @@ describe('Testing ConfirmVoteDelegationContainer component', () => { }, stakeCredential: { type: Wallet.Cardano.CredentialType.KeyHash, - hash: Wallet.Crypto.Hash28ByteBase16(Buffer.from(STAKE_KEY_HASH).toString('hex')) + hash: Wallet.Crypto.Hash28ByteBase16(STAKE_KEY_HASH) } }; const tx = buildMockTx({ diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/VotingProceduresContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/VotingProceduresContainer.test.tsx index 00a65675c8..d1ddb7fdcc 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/VotingProceduresContainer.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/VotingProceduresContainer.test.tsx @@ -211,7 +211,7 @@ describe('Testing VotingProceduresContainer component', () => { dappInfo, data: voters.map(({ __typename }, index) => ({ voter: { - type: t(`core.votingProcedures.voterType.${getVoterType(__typename)}`), + type: t(`core.VotingProcedures.voterTypes.${getVoterType(__typename)}`), dRepId: getExpectedDrepId(getVoterType(__typename))(voters[index].credential.hash) }, votes: votingProcedures[index].votes.map((vote) => ({ @@ -221,7 +221,7 @@ describe('Testing VotingProceduresContainer component', () => { txHashUrl: `${mockPreprodCexplorerBaseUrl}/${mockCexplorerUrlPathsTx}/${vote.actionId.id}` }, votingProcedure: { - vote: t(`core.votingProcedures.votes.${getVote(vote.votingProcedure.vote)}`), + vote: t(`core.VotingProcedures.votes.${getVote(vote.votingProcedure.vote)}`), anchor: !!vote.votingProcedure.anchor?.url && { url: vote.votingProcedure.anchor?.url, hash: vote.votingProcedure.anchor?.dataHash.toString() diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/utils.ts b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/utils.ts index 681e9a86cc..a92dc323c5 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/utils.ts +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/utils.ts @@ -1,4 +1,4 @@ -/* eslint-disable no-console */ +/* eslint-disable no-console, complexity */ import { Wallet } from '@lace/cardano'; import { assetsBurnedInspector, assetsMintedInspector, createTxInspector } from '@cardano-sdk/core'; import { RemoteApiPropertyType, exposeApi } from '@cardano-sdk/web-extension'; @@ -18,7 +18,11 @@ export const getTitleKey = (txType: Wallet.Cip30TxType): string => Wallet.Cip30TxType.DRepRetirement, Wallet.Cip30TxType.DRepUpdate, Wallet.Cip30TxType.VoteDelegation, - Wallet.Cip30TxType.VotingProcedures + Wallet.Cip30TxType.VotingProcedures, + Wallet.Cip30TxType.StakeVoteDelegation, + Wallet.Cip30TxType.VoteRegistrationDelegation, + Wallet.Cip30TxType.StakeRegistrationDelegation, + Wallet.Cip30TxType.StakeVoteDelegationRegistration ].includes(txType) ? `core.${txType}.title` : sectionTitle[DAPP_VIEWS.CONFIRM_TX]; @@ -62,6 +66,7 @@ export const certificateInspectorFactory = export const votingProceduresInspector = (tx: Wallet.Cardano.Tx): Wallet.Cardano.VotingProcedures | undefined => tx?.body?.votingProcedures; +// eslint-disable-next-line complexity export const getTxType = (tx: Wallet.Cardano.Tx): Wallet.Cip30TxType => { const inspector = createTxInspector({ minted: assetsMintedInspector, @@ -70,12 +75,26 @@ export const getTxType = (tx: Wallet.Cardano.Tx): Wallet.Cip30TxType => { dRepRegistration: certificateInspectorFactory(CertificateType.RegisterDelegateRepresentative), dRepRetirement: certificateInspectorFactory(CertificateType.UnregisterDelegateRepresentative), dRepUpdate: certificateInspectorFactory(CertificateType.UpdateDelegateRepresentative), - voteDelegation: certificateInspectorFactory(CertificateType.VoteDelegation) + voteDelegation: certificateInspectorFactory(CertificateType.VoteDelegation), + voteRegistrationDelegation: certificateInspectorFactory(CertificateType.VoteRegistrationDelegation), + stakeVoteDelegation: certificateInspectorFactory(CertificateType.StakeVoteDelegation), + stakeRegistrationDelegation: certificateInspectorFactory(CertificateType.StakeRegistrationDelegation), + stakeVoteDelegationRegistration: certificateInspectorFactory(CertificateType.StakeVoteRegistrationDelegation) }); - const { minted, burned, dRepRegistration, dRepRetirement, dRepUpdate, voteDelegation, votingProcedures } = inspector( - tx as Wallet.Cardano.HydratedTx - ); + const { + minted, + burned, + votingProcedures, + dRepRegistration, + dRepRetirement, + dRepUpdate, + voteDelegation, + stakeVoteDelegation, + voteRegistrationDelegation, + stakeRegistrationDelegation, + stakeVoteDelegationRegistration + } = inspector(tx as Wallet.Cardano.HydratedTx); const isMintTransaction = minted.length > 0; const isBurnTransaction = burned.length > 0; @@ -103,6 +122,22 @@ export const getTxType = (tx: Wallet.Cardano.Tx): Wallet.Cip30TxType => { return Wallet.Cip30TxType.VoteDelegation; } + if (stakeVoteDelegation) { + return Wallet.Cip30TxType.StakeVoteDelegation; + } + + if (voteRegistrationDelegation) { + return Wallet.Cip30TxType.VoteRegistrationDelegation; + } + + if (stakeRegistrationDelegation) { + return Wallet.Cip30TxType.StakeRegistrationDelegation; + } + + if (stakeVoteDelegationRegistration) { + return Wallet.Cip30TxType.StakeVoteDelegationRegistration; + } + if (dRepUpdate) { return Wallet.Cip30TxType.DRepUpdate; } 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 98c23aa068..d4e7b08915 100644 --- a/apps/browser-extension-wallet/src/hooks/__tests__/useWalletManager.test.tsx +++ b/apps/browser-extension-wallet/src/hooks/__tests__/useWalletManager.test.tsx @@ -431,7 +431,7 @@ describe('Testing useWalletManager hook', () => { wallet, name: 'name' } as any; - const chainName = 'Preview'; + const chainName = 'Preprod'; const mnemonicVerificationFrequency = 'mnemonicVerificationFrequency'; jest.spyOn(stores, 'useWalletStore').mockImplementation(() => ({ @@ -621,7 +621,7 @@ describe('Testing useWalletManager hook', () => { keyAgentsByChain, ...cardanoWallet } as any; - const chainName = 'Preview'; + const chainName = 'Preprod'; const saveValueInLocalStorage = jest.fn(); jest.spyOn(localStorage, 'saveValueInLocalStorage').mockImplementation(saveValueInLocalStorage); diff --git a/apps/browser-extension-wallet/src/lib/translations/en.json b/apps/browser-extension-wallet/src/lib/translations/en.json index 4f53c319ed..23463cdad6 100644 --- a/apps/browser-extension-wallet/src/lib/translations/en.json +++ b/apps/browser-extension-wallet/src/lib/translations/en.json @@ -1183,13 +1183,51 @@ "hash": "Hash" }, "VoteDelegation": { - "title": "Confirm vote delegation", + "title": "Confirm Vote Delegation", "metadata": "Metadata", "drepId": "DRep ID", "alwaysAbstain": "Abstain", "alwaysNoConfidence": "No Confidence", "option": "Yes" }, + "StakeVoteDelegation": { + "title": "Confirm Vote Delegation & Staking", + "metadata": "Metadata", + "drepId": "DRep ID", + "alwaysAbstain": "Abstain", + "alwaysNoConfidence": "No Confidence", + "option": "Yes", + "stakeKeyHash": "Stake Key Hash", + "poolId": "Pool ID" + }, + "StakeVoteDelegationRegistration": { + "title": "Confirm Vote Delegation & Staking", + "metadata": "Metadata", + "drepId": "DRep ID", + "alwaysAbstain": "Abstain", + "alwaysNoConfidence": "No Confidence", + "option": "Yes", + "stakeKeyHash": "Stake Key Hash", + "poolId": "Pool ID", + "depositPaid": "Deposit paid" + }, + "StakeRegistrationDelegation": { + "title": "Confirm Staking", + "metadata": "Metadata", + "stakeKeyHash": "Stake Key Hash", + "poolId": "Pool ID", + "depositPaid": "Deposit paid" + }, + "VoteRegistrationDelegation": { + "title": "Confirm Vote Delegation", + "metadata": "Metadata", + "drepId": "DRep ID", + "alwaysAbstain": "Abstain", + "alwaysNoConfidence": "No Confidence", + "option": "Yes", + "stakeKeyHash": "Stake Key Hash", + "depositPaid": "Deposit paid" + }, "destinationAddressInput": { "recipientAddress": "Recipient's address or $handle" }, diff --git a/apps/browser-extension-wallet/src/providers/PostHogClientProvider/client/PostHogClient.ts b/apps/browser-extension-wallet/src/providers/PostHogClientProvider/client/PostHogClient.ts index bfcdb4b9c2..f679ea61a4 100644 --- a/apps/browser-extension-wallet/src/providers/PostHogClientProvider/client/PostHogClient.ts +++ b/apps/browser-extension-wallet/src/providers/PostHogClientProvider/client/PostHogClient.ts @@ -42,7 +42,7 @@ export class PostHogClient { ) { if (!this.publicPostHogHost) throw new Error('PUBLIC_POSTHOG_HOST url has not been provided'); const token = this.getApiToken(this.chain); - if (!token) throw new Error('posthog token has not been provided'); + if (!token) throw new Error(`posthog token has not been provided for chain: ${this.chain.networkId}`); this.hasPostHogInitialized$ = new BehaviorSubject(false); this.userIdService diff --git a/packages/cardano/src/wallet/types.ts b/packages/cardano/src/wallet/types.ts index a193f1fbf0..7a4f78c0ad 100644 --- a/packages/cardano/src/wallet/types.ts +++ b/packages/cardano/src/wallet/types.ts @@ -36,7 +36,11 @@ export enum Cip30TxType { DRepRetirement = 'DRepRetirement', DRepUpdate = 'DRepUpdate', VoteDelegation = 'VoteDelegation', - VotingProcedures = 'VotingProcedures' + VotingProcedures = 'VotingProcedures', + VoteRegistrationDelegation = 'VoteRegistrationDelegation', + StakeRegistrationDelegation = 'StakeRegistrationDelegation', + StakeVoteDelegationRegistration = 'StakeVoteDelegationRegistration', + StakeVoteDelegation = 'StakeVoteDelegation' } export type Cip30SignTxOutput = { diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index cd74f39601..70019dfd2c 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -32,4 +32,8 @@ export * from '@ui/components/ConfirmDRepRegistration'; export * from '@ui/components/ConfirmDRepRetirement'; export * from '@ui/components/ConfirmDRepUpdate'; export * from '@ui/components/ConfirmVoteDelegation'; +export * from '@ui/components/ConfirmStakeVoteDelegation'; +export * from '@ui/components/ConfirmStakeRegistrationDelegation'; +export * from '@ui/components/ConfirmStakeVoteRegistrationDelegation'; +export * from '@ui/components/ConfirmVoteRegistrationDelegation'; export * from '@ui/components/VotingProcedures'; diff --git a/packages/core/src/ui/components/ConfirmStakeRegistrationDelegation/ConfirmStakeRegistrationDelegation.stories.ts b/packages/core/src/ui/components/ConfirmStakeRegistrationDelegation/ConfirmStakeRegistrationDelegation.stories.ts new file mode 100644 index 0000000000..1cc3a0c203 --- /dev/null +++ b/packages/core/src/ui/components/ConfirmStakeRegistrationDelegation/ConfirmStakeRegistrationDelegation.stories.ts @@ -0,0 +1,48 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { ConfirmStakeRegistrationDelegation } from './ConfirmStakeRegistrationDelegation'; +import { ComponentProps } from 'react'; + +const meta: Meta = { + title: 'ConfirmStakeRegistrationDelegation', + component: ConfirmStakeRegistrationDelegation, + parameters: { + layout: 'centered' + } +}; + +export default meta; +type Story = StoryObj; + +const data: ComponentProps = { + dappInfo: { + logo: 'https://cdn.mint.handle.me/favicon.png', + name: 'Mint', + url: 'https://preprod.mint.handle.me' + }, + translations: { + labels: { + stakeKeyHash: 'Stake key hash', + poolId: 'Pool Id', + depositPaid: 'Deposit paid' + }, + metadata: 'Metadata' + }, + metadata: { + stakeKeyHash: '13cf55d175ea848b87deb3e914febd7e028e2bf6534475d52fb9c3d0', + poolId: 'pool1zuevzm3xlrhmwjw87ec38mzs02tlkwec9wxpgafcaykmwg7efhh', + depositPaid: '0.35 ADA' + } +}; + +export const Overview: Story = { + args: { + ...data + } +}; +export const WithError: Story = { + args: { + ...data, + errorMessage: 'Something went wrong' + } +}; diff --git a/packages/core/src/ui/components/ConfirmStakeRegistrationDelegation/ConfirmStakeRegistrationDelegation.tsx b/packages/core/src/ui/components/ConfirmStakeRegistrationDelegation/ConfirmStakeRegistrationDelegation.tsx new file mode 100644 index 0000000000..ada22bfaf3 --- /dev/null +++ b/packages/core/src/ui/components/ConfirmStakeRegistrationDelegation/ConfirmStakeRegistrationDelegation.tsx @@ -0,0 +1,54 @@ +import React from 'react'; +import { Box, Cell, Grid, TransactionSummary, Flex } from '@lace/ui'; +import { DappInfo, DappInfoProps } from '../DappInfo'; +import { ErrorPane } from '@lace/common'; + +interface Props { + dappInfo: Omit; + errorMessage?: string; + translations: { + labels: { + poolId: string; + stakeKeyHash: string; + depositPaid: string; + }; + metadata: string; + }; + metadata: { + poolId: string; + stakeKeyHash: string; + depositPaid: string; + }; +} + +export const ConfirmStakeRegistrationDelegation = ({ + dappInfo, + errorMessage, + translations, + metadata +}: Props): JSX.Element => ( + + + + + {errorMessage && ( + + + + )} + + + + + + + + + + + + + + + +); diff --git a/packages/core/src/ui/components/ConfirmStakeRegistrationDelegation/index.ts b/packages/core/src/ui/components/ConfirmStakeRegistrationDelegation/index.ts new file mode 100644 index 0000000000..78be311a96 --- /dev/null +++ b/packages/core/src/ui/components/ConfirmStakeRegistrationDelegation/index.ts @@ -0,0 +1 @@ +export { ConfirmStakeRegistrationDelegation } from './ConfirmStakeRegistrationDelegation'; diff --git a/packages/core/src/ui/components/ConfirmStakeVoteDelegation/ConfirmStakeVoteDelegation.stories.ts b/packages/core/src/ui/components/ConfirmStakeVoteDelegation/ConfirmStakeVoteDelegation.stories.ts new file mode 100644 index 0000000000..e96651dcd3 --- /dev/null +++ b/packages/core/src/ui/components/ConfirmStakeVoteDelegation/ConfirmStakeVoteDelegation.stories.ts @@ -0,0 +1,77 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { ConfirmStakeVoteDelegation } from './ConfirmStakeVoteDelegation'; +import { ComponentProps } from 'react'; + +const meta: Meta = { + title: 'ConfirmStakeVoteDelegation', + component: ConfirmStakeVoteDelegation, + parameters: { + layout: 'centered' + } +}; + +export default meta; +type Story = StoryObj; + +const data: ComponentProps = { + dappInfo: { + logo: 'https://cdn.mint.handle.me/favicon.png', + name: 'Mint', + url: 'https://preprod.mint.handle.me' + }, + translations: { + labels: { + drepId: 'DRep ID', + alwaysAbstain: 'Abstain', + alwaysNoConfidence: 'No Confidence', + stakeKeyHash: 'Stake key hash', + poolId: 'Pool Id' + }, + option: 'Yes', + metadata: 'Metadata' + }, + metadata: { + drepId: 'drep1ruvgm0auzdplfn7g2jf3kcnpnw5mlhwxaxj8crag8h6t2ye9y9g', + alwaysAbstain: false, + alwaysNoConfidence: false, + stakeKeyHash: '13cf55d175ea848b87deb3e914febd7e028e2bf6534475d52fb9c3d0', + poolId: 'pool1zuevzm3xlrhmwjw87ec38mzs02tlkwec9wxpgafcaykmwg7efhh' + } +}; + +export const Overview: Story = { + args: { + ...data + } +}; +export const WithError: Story = { + args: { + ...data, + errorMessage: 'Something went wrong' + } +}; + +export const WithAbstain: Story = { + args: { + ...data, + metadata: { + ...data.metadata, + drepId: undefined, + alwaysAbstain: true, + alwaysNoConfidence: false + } + } +}; + +export const WithNoConfidence: Story = { + args: { + ...data, + metadata: { + ...data.metadata, + drepId: undefined, + alwaysAbstain: false, + alwaysNoConfidence: true + } + } +}; diff --git a/packages/core/src/ui/components/ConfirmStakeVoteDelegation/ConfirmStakeVoteDelegation.tsx b/packages/core/src/ui/components/ConfirmStakeVoteDelegation/ConfirmStakeVoteDelegation.tsx new file mode 100644 index 0000000000..77cfd32434 --- /dev/null +++ b/packages/core/src/ui/components/ConfirmStakeVoteDelegation/ConfirmStakeVoteDelegation.tsx @@ -0,0 +1,66 @@ +import React from 'react'; +import { Box, Cell, Grid, TransactionSummary, Flex } from '@lace/ui'; +import { DappInfo, DappInfoProps } from '../DappInfo'; +import { ErrorPane } from '@lace/common'; + +interface Props { + dappInfo: Omit; + errorMessage?: string; + translations: { + labels: { + poolId: string; + stakeKeyHash: string; + drepId: string; + alwaysAbstain: string; + alwaysNoConfidence: string; + }; + option: string; + metadata: string; + }; + metadata: { + poolId: string; + stakeKeyHash: string; + drepId?: string; + alwaysAbstain: boolean; + alwaysNoConfidence: boolean; + }; +} + +export const ConfirmStakeVoteDelegation = ({ dappInfo, errorMessage, translations, metadata }: Props): JSX.Element => ( + + + + + {errorMessage && ( + + + + )} + + + + + {metadata.drepId && ( + + + + )} + {metadata.alwaysAbstain && ( + + + + )} + {metadata.alwaysNoConfidence && ( + + + + )} + + + + + + + + +); diff --git a/packages/core/src/ui/components/ConfirmStakeVoteDelegation/index.ts b/packages/core/src/ui/components/ConfirmStakeVoteDelegation/index.ts new file mode 100644 index 0000000000..d532c9b448 --- /dev/null +++ b/packages/core/src/ui/components/ConfirmStakeVoteDelegation/index.ts @@ -0,0 +1 @@ +export { ConfirmStakeVoteDelegation } from './ConfirmStakeVoteDelegation'; diff --git a/packages/core/src/ui/components/ConfirmStakeVoteRegistrationDelegation/ConfirmStakeVoteRegistrationDelegation.stories.ts b/packages/core/src/ui/components/ConfirmStakeVoteRegistrationDelegation/ConfirmStakeVoteRegistrationDelegation.stories.ts new file mode 100644 index 0000000000..3a0b6b7ec0 --- /dev/null +++ b/packages/core/src/ui/components/ConfirmStakeVoteRegistrationDelegation/ConfirmStakeVoteRegistrationDelegation.stories.ts @@ -0,0 +1,79 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { ConfirmStakeVoteRegistrationDelegation } from './ConfirmStakeVoteRegistrationDelegation'; +import { ComponentProps } from 'react'; + +const meta: Meta = { + title: 'ConfirmStakeVoteRegistrationDelegation', + component: ConfirmStakeVoteRegistrationDelegation, + parameters: { + layout: 'centered' + } +}; + +export default meta; +type Story = StoryObj; + +const data: ComponentProps = { + dappInfo: { + logo: 'https://cdn.mint.handle.me/favicon.png', + name: 'Mint', + url: 'https://preprod.mint.handle.me' + }, + translations: { + labels: { + drepId: 'DRep ID', + alwaysAbstain: 'Abstain', + alwaysNoConfidence: 'No Confidence', + stakeKeyHash: 'Stake key hash', + poolId: 'Pool Id', + depositPaid: 'Deposit paid' + }, + option: 'Yes', + metadata: 'Metadata' + }, + metadata: { + drepId: 'drep1ruvgm0auzdplfn7g2jf3kcnpnw5mlhwxaxj8crag8h6t2ye9y9g', + alwaysAbstain: false, + alwaysNoConfidence: false, + stakeKeyHash: '13cf55d175ea848b87deb3e914febd7e028e2bf6534475d52fb9c3d0', + poolId: 'pool1zuevzm3xlrhmwjw87ec38mzs02tlkwec9wxpgafcaykmwg7efhh', + depositPaid: '0.35 ADA' + } +}; + +export const Overview: Story = { + args: { + ...data + } +}; +export const WithError: Story = { + args: { + ...data, + errorMessage: 'Something went wrong' + } +}; + +export const WithAbstain: Story = { + args: { + ...data, + metadata: { + ...data.metadata, + drepId: undefined, + alwaysAbstain: true, + alwaysNoConfidence: false + } + } +}; + +export const WithNoConfidence: Story = { + args: { + ...data, + metadata: { + ...data.metadata, + drepId: undefined, + alwaysAbstain: false, + alwaysNoConfidence: true + } + } +}; diff --git a/packages/core/src/ui/components/ConfirmStakeVoteRegistrationDelegation/ConfirmStakeVoteRegistrationDelegation.tsx b/packages/core/src/ui/components/ConfirmStakeVoteRegistrationDelegation/ConfirmStakeVoteRegistrationDelegation.tsx new file mode 100644 index 0000000000..a8e0789e42 --- /dev/null +++ b/packages/core/src/ui/components/ConfirmStakeVoteRegistrationDelegation/ConfirmStakeVoteRegistrationDelegation.tsx @@ -0,0 +1,76 @@ +import React from 'react'; +import { Box, Cell, Grid, TransactionSummary, Flex } from '@lace/ui'; +import { DappInfo, DappInfoProps } from '../DappInfo'; +import { ErrorPane } from '@lace/common'; + +interface Props { + dappInfo: Omit; + errorMessage?: string; + translations: { + labels: { + poolId: string; + stakeKeyHash: string; + drepId: string; + alwaysAbstain: string; + alwaysNoConfidence: string; + depositPaid: string; + }; + option: string; + metadata: string; + }; + metadata: { + poolId: string; + stakeKeyHash: string; + drepId?: string; + alwaysAbstain: boolean; + alwaysNoConfidence: boolean; + depositPaid: string; + }; +} + +export const ConfirmStakeVoteRegistrationDelegation = ({ + dappInfo, + errorMessage, + translations, + metadata +}: Props): JSX.Element => ( + + + + + {errorMessage && ( + + + + )} + + + + + {metadata.drepId && ( + + + + )} + {metadata.alwaysAbstain && ( + + + + )} + {metadata.alwaysNoConfidence && ( + + + + )} + + + + + + + + + + + +); diff --git a/packages/core/src/ui/components/ConfirmStakeVoteRegistrationDelegation/index.ts b/packages/core/src/ui/components/ConfirmStakeVoteRegistrationDelegation/index.ts new file mode 100644 index 0000000000..6e5483c0dc --- /dev/null +++ b/packages/core/src/ui/components/ConfirmStakeVoteRegistrationDelegation/index.ts @@ -0,0 +1 @@ +export { ConfirmStakeVoteRegistrationDelegation } from './ConfirmStakeVoteRegistrationDelegation'; diff --git a/packages/core/src/ui/components/ConfirmVoteRegistrationDelegation/ConfirmVoteRegistrationDelegation.stories.ts b/packages/core/src/ui/components/ConfirmVoteRegistrationDelegation/ConfirmVoteRegistrationDelegation.stories.ts new file mode 100644 index 0000000000..2ca239f068 --- /dev/null +++ b/packages/core/src/ui/components/ConfirmVoteRegistrationDelegation/ConfirmVoteRegistrationDelegation.stories.ts @@ -0,0 +1,77 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { ConfirmVoteRegistrationDelegation } from './ConfirmVoteRegistrationDelegation'; +import { ComponentProps } from 'react'; + +const meta: Meta = { + title: 'ConfirmVoteRegistrationDelegation', + component: ConfirmVoteRegistrationDelegation, + parameters: { + layout: 'centered' + } +}; + +export default meta; +type Story = StoryObj; + +const data: ComponentProps = { + dappInfo: { + logo: 'https://cdn.mint.handle.me/favicon.png', + name: 'Mint', + url: 'https://preprod.mint.handle.me' + }, + translations: { + labels: { + drepId: 'DRep ID', + alwaysAbstain: 'Abstain', + alwaysNoConfidence: 'No Confidence', + depositPaid: 'Deposit paid', + stakeKeyHash: 'Stake key hash' + }, + option: 'Yes', + metadata: 'Metadata' + }, + metadata: { + drepId: 'drep1ruvgm0auzdplfn7g2jf3kcnpnw5mlhwxaxj8crag8h6t2ye9y9g', + alwaysAbstain: false, + alwaysNoConfidence: false, + depositPaid: '0.35 ADA', + stakeKeyHash: '13cf55d175ea848b87deb3e914febd7e028e2bf6534475d52fb9c3d0' + } +}; + +export const Overview: Story = { + args: { + ...data + } +}; +export const WithError: Story = { + args: { + ...data, + errorMessage: 'Something went wrong' + } +}; + +export const WithAbstain: Story = { + args: { + ...data, + metadata: { + ...data.metadata, + drepId: undefined, + alwaysAbstain: true, + alwaysNoConfidence: false + } + } +}; + +export const WithNoConfidence: Story = { + args: { + ...data, + metadata: { + ...data.metadata, + drepId: undefined, + alwaysAbstain: false, + alwaysNoConfidence: true + } + } +}; diff --git a/packages/core/src/ui/components/ConfirmVoteRegistrationDelegation/ConfirmVoteRegistrationDelegation.tsx b/packages/core/src/ui/components/ConfirmVoteRegistrationDelegation/ConfirmVoteRegistrationDelegation.tsx new file mode 100644 index 0000000000..39f184e26b --- /dev/null +++ b/packages/core/src/ui/components/ConfirmVoteRegistrationDelegation/ConfirmVoteRegistrationDelegation.tsx @@ -0,0 +1,71 @@ +import React from 'react'; +import { Box, Cell, Grid, TransactionSummary, Flex } from '@lace/ui'; +import { DappInfo, DappInfoProps } from '../DappInfo'; +import { ErrorPane } from '@lace/common'; + +interface Props { + dappInfo: Omit; + errorMessage?: string; + translations: { + labels: { + drepId: string; + alwaysAbstain: string; + alwaysNoConfidence: string; + stakeKeyHash: string; + depositPaid: string; + }; + option: string; + metadata: string; + }; + metadata: { + drepId?: string; + alwaysAbstain: boolean; + alwaysNoConfidence: boolean; + stakeKeyHash: string; + depositPaid: string; + }; +} + +export const ConfirmVoteRegistrationDelegation = ({ + dappInfo, + errorMessage, + translations, + metadata +}: Props): JSX.Element => ( + + + + + {errorMessage && ( + + + + )} + + + + + {metadata.drepId && ( + + + + )} + {metadata.alwaysAbstain && ( + + + + )} + {metadata.alwaysNoConfidence && ( + + + + )} + + + + + + + + +); diff --git a/packages/core/src/ui/components/ConfirmVoteRegistrationDelegation/index.ts b/packages/core/src/ui/components/ConfirmVoteRegistrationDelegation/index.ts new file mode 100644 index 0000000000..fbcf388533 --- /dev/null +++ b/packages/core/src/ui/components/ConfirmVoteRegistrationDelegation/index.ts @@ -0,0 +1 @@ +export { ConfirmVoteRegistrationDelegation } from './ConfirmVoteRegistrationDelegation'; diff --git a/packages/core/src/ui/components/VotingProcedures/VotingProcedures.tsx b/packages/core/src/ui/components/VotingProcedures/VotingProcedures.tsx index cb7a124066..dc40824e78 100644 --- a/packages/core/src/ui/components/VotingProcedures/VotingProcedures.tsx +++ b/packages/core/src/ui/components/VotingProcedures/VotingProcedures.tsx @@ -34,10 +34,10 @@ interface Props { index: string; txHash: string; }; - anchor?: { + anchor: { url: string; hash: string; - }; + } | null; dRepId: string; procedureTitle: string; vote: string; diff --git a/packages/ui/src/design-system/index.ts b/packages/ui/src/design-system/index.ts index 3ecaa296e2..c6d0df5543 100644 --- a/packages/ui/src/design-system/index.ts +++ b/packages/ui/src/design-system/index.ts @@ -23,8 +23,8 @@ export * as TransactionSummary from './transaction-summary'; export { ToastBar } from './toast-bar'; export * from './tooltip'; export { Message } from './message'; -export { PasswordBox } from './password-box'; export { Metadata, MetadataLink } from './metadata'; +export { PasswordBox } from './password-box'; export { TextLink } from './text-link'; export * as ProfileDropdown from './profile-dropdown'; export { TextBox } from './text-box'; From a7e84ad921c78079de3cf265d9cdf8e85e2eab00 Mon Sep 17 00:00:00 2001 From: Renan Valentin Date: Thu, 14 Dec 2023 08:00:35 -0300 Subject: [PATCH 26/68] [LW-8490] Governance Actions (#670) * chore/Bump cardano-js-sdk (#559) * chore!: bump cardano-js-sdk packages to latest versions * ci: add cardano services urls parameters to build step * Feat/sanchonet network switch support (#612) * feat: update env to support switching to sanchonet * chore: remove old testnet * chore: bump cardano-sdk packages to latest version --------- Co-authored-by: mirceahasegan <105701265+mirceahasegan@users.noreply.github.com> * [LW-7983] [LW-7984] dRep confirmation screens (#554) * refactor: move logic out of component * fix: file path * feat(core): setup storybook * refactor(ui): extract insufficient funds warning component * feat(core): add lace/ui package * feat(core): create ConfirmDRepRegistration component * feat(extension): install @lace/ui * refactor: move functions to utils * fix: type * refactor: types file * feat: check for DRep transaction * feat: integrate confirm drep registration component * refactor(core): use Metadata component * refactor(core): use flexbox * refactor(core): remove insufficient funds message * refactor(core): create ConfirmDRepRetirement * refactor: remove insufficient funds warning; break down main component * feat: add certificate data * feat: add drep retirement container * fix: condition * fix: type * fix: style * fix: check for anchor * fix: check for undefined certificates * fix: merge conflict * fix: type error * feat: temp env changes * feat(extension): use consistent naming of DRep ID * feat(extension): show anchor metadata if exists * feat(extension): convert user facing DRepID to bech32 * fix: add cardano symbol to deposit amount * chore(core): downgrade storybook to v6 * fix: update lock file * fix: styles and text * fix: storybook build * fix: storybook build * feat: check retired drep key * fix: check retired drep key --------- Co-authored-by: Renan Ferreira Co-authored-by: Michael Chappell <7581002+mchappell@users.noreply.github.com> * feat(extension): [LW-7984] voting procedures (#655) * feat(ui): create metadata component * wip * feat(ui): text link component * chore(ui): fix rebase * feat(ui): create metadata link component * feat(core): create voting procedures component * chore(extension): fix rebase * fix(ui): add work break * feat(extension): voting procedures * refactor(core): make actions optional * refactor(core): use action as key prop * refactor(extension): add custom title for voting procedures * feat(core): create parameter change action * feat(core): create hard fork initiation action * feat(core): create new constitution action * feat(core): create no confidence action * feat(core): create treasury withdrawals action * feat(core): create info action * feat(core): create update comittee action * refactor(core): check if anchor is provided * refactor(core): update storybook path * feat(extension): add translations * refactor(core): remove ununsed prop * feat(staking): [LW-8684] add tooltip to piechart (#616) * feat: lw-8684-add tooltip to delegation piechart * fix: add custom tooltip dot style * feat(ui): add tooltip to metadata component * chore(core): fix pipeline * chore(core): update proposal procedures components to the latest ui * fix(extension): resolve pr comments --------- Co-authored-by: Leonel Gobbi <57540576+lgobbi-atix@users.noreply.github.com> Co-authored-by: mirceahasegan <105701265+mirceahasegan@users.noreply.github.com> Co-authored-by: Lucas Co-authored-by: Michael Chappell <7581002+mchappell@users.noreply.github.com> Co-authored-by: John Oshalusi Co-authored-by: Vitalii Vozniak --- .github/workflows/e2e-tests-linux.yml | 3 + .github/workflows/smoke-tests.yml | 3 + apps/browser-extension-wallet/.env.defaults | 2 +- .../ConfirmTransaction.tsx | 1 - .../ConfirmTransactionContent.tsx | 9 +- .../ProposalProceduresContainer.tsx | 86 +++ .../VotingProceduresContainer.tsx | 12 +- .../ConfirmDRepRegistrationContainer.test.tsx | 38 +- .../ConfirmDRepRetirementContainer.test.tsx | 38 +- .../ConfirmDRepUpdateContainer.test.tsx | 38 +- ...keRegistrationDelegationContainer.test.tsx | 149 ++++++ ...nfirmStakeVoteDelegationContainer.test.tsx | 168 ++++++ ...teRegistrationDelegationContainer.test.tsx | 180 +++++++ .../__tests__/ConfirmTransaction.test.tsx | 42 +- .../ConfirmTransactionContent.test.tsx | 213 ++++++++ .../ConfirmVoteDelegationContainer.test.tsx | 38 +- ...teRegistrationDelegationContainer.test.tsx | 175 ++++++ .../DappTransactionContainer.test.tsx | 38 +- .../ProposalProceduresContainer.test.tsx | 179 +++++++ .../VotingProceduresContainer.test.tsx | 38 +- .../__tests__/utils.test.tsx | 47 +- .../components/confirm-transaction/hooks.ts | 18 + .../HardForkInitiationActionContainer.tsx | 93 ++++ .../InfoActionContainer.tsx | 50 ++ .../NewConstitutionActionContainer.tsx | 98 ++++ .../NoConfidenceActionContainer.tsx | 81 +++ .../ParameterChangeActionContainer.tsx | 341 ++++++++++++ .../TreasuryWithdrawalsActionContainer.tsx | 91 ++++ .../UpdateCommitteeActionContainer.tsx | 107 ++++ ...HardForkInitiationActionContainer.test.tsx | 175 ++++++ .../__tests__/InfoActionContainer.test.tsx | 115 ++++ .../NewConstitutionActionContainer.test.tsx | 179 +++++++ .../NoConfidenceActionContainer.test.tsx | 160 ++++++ .../ParameterChangeActionContainer.test.tsx | 504 ++++++++++++++++++ ...reasuryWithdrawalsActionContainer.test.tsx | 165 ++++++ .../UpdateCommitteeActionContainer.test.tsx | 208 ++++++++ .../confirm-transaction/testing.utils.tsx | 38 ++ .../components/confirm-transaction/utils.ts | 11 +- .../src/lib/translations/en.json | 180 +++++++ packages/cardano/src/wallet/types.ts | 3 +- packages/core/.storybook/preview.js | 14 + packages/core/src/index.ts | 1 + ...rmStakeRegistrationDelegation.stories.tsx} | 0 ...=> ConfirmStakeVoteDelegation.stories.tsx} | 0 ...akeVoteRegistrationDelegation.stories.tsx} | 0 ...irmVoteRegistrationDelegation.stories.tsx} | 0 .../DappTransaction.stories.tsx | 10 +- .../HardForkInitiationAction.stories.tsx | 98 ++++ .../HardForkInitiationAction.tsx | 62 +++ .../HardForkInitiationActionTypes.ts | 25 + .../HardForkInitiationAction/index.ts | 1 + .../InfoAction/InfoAction.stories.tsx | 75 +++ .../InfoAction/InfoAction.tsx | 40 ++ .../InfoAction/InfoActionTypes.ts | 11 + .../ProposalProcedures/InfoAction/index.ts | 1 + .../NewConstitutionAction.stories.tsx | 103 ++++ .../NewConstitutionAction.tsx | 63 +++ .../NewConstitutionActionTypes.ts | 30 ++ .../NewConstitutionAction/index.ts | 1 + .../NoConfidenceAction.stories.tsx | 88 +++ .../NoConfidenceAction/NoConfidenceAction.tsx | 51 ++ .../NoConfidenceActionTypes.ts | 15 + .../NoConfidenceAction/index.ts | 1 + .../ParameterChangeAction/EconomicGroup.tsx | 81 +++ .../ParameterChangeAction/GovernanceGroup.tsx | 144 +++++ .../ParameterChangeAction/NetworkGroup.tsx | 59 ++ .../ParameterChangeAction.stories.tsx | 243 +++++++++ .../ParameterChangeAction.tsx | 84 +++ .../ParameterChangeActionTypes.ts | 185 +++++++ .../ParameterChangeAction/TechnicalGroup.tsx | 58 ++ .../ParameterChangeAction/index.ts | 1 + .../TreasuryWithdrawalsAction.stories.tsx | 99 ++++ .../TreasuryWithdrawalsAction.tsx | 71 +++ .../TreasuryWithdrawalsActionTypes.ts | 24 + .../TreasuryWithdrawalsAction/index.ts | 1 + .../UpdateCommitteeActionAction.stories.tsx | 130 +++++ .../UpdateCommitteeActionAction.tsx | 97 ++++ .../UpdateCommitteeActionTypes.ts | 50 ++ .../UpdateCommitteeAction/index.ts | 1 + .../components/ActionId.tsx | 30 ++ .../components/ActionIdTypes.ts | 10 + .../components/Card.module.scss | 3 + .../ProposalProcedures/components/Card.tsx | 94 ++++ .../components/Procedure.tsx | 32 ++ .../components/ProcedureTypes.ts | 15 + .../components/TransactionDetails.tsx | 37 ++ .../components/TransactionDetailsTypes.ts | 12 + .../ui/components/ProposalProcedures/index.ts | 7 + .../DelegationCard/DelegationTooltip.css.ts | 11 + .../metadata/metadata-link.component.tsx | 19 +- .../metadata/metadata.component.tsx | 19 +- .../metadata/metadata.stories.tsx | 1 + .../tooltip/tooltip-content.css.ts | 1 + 93 files changed, 6122 insertions(+), 301 deletions(-) create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ProposalProceduresContainer.tsx create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmStakeRegistrationDelegationContainer.test.tsx create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmStakeVoteDelegationContainer.test.tsx create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmStakeVoteRegistrationDelegationContainer.test.tsx create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmVoteRegistrationDelegationContainer.test.tsx create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ProposalProceduresContainer.test.tsx create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/HardForkInitiationActionContainer.tsx create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/InfoActionContainer.tsx create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/NewConstitutionActionContainer.tsx create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/NoConfidenceActionContainer.tsx create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/ParameterChangeActionContainer.tsx create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/TreasuryWithdrawalsActionContainer.tsx create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/UpdateCommitteeActionContainer.tsx create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/HardForkInitiationActionContainer.test.tsx create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/InfoActionContainer.test.tsx create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/NewConstitutionActionContainer.test.tsx create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/NoConfidenceActionContainer.test.tsx create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/ParameterChangeActionContainer.test.tsx create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/TreasuryWithdrawalsActionContainer.test.tsx create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/UpdateCommitteeActionContainer.test.tsx create mode 100644 apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/testing.utils.tsx rename packages/core/src/ui/components/ConfirmStakeRegistrationDelegation/{ConfirmStakeRegistrationDelegation.stories.ts => ConfirmStakeRegistrationDelegation.stories.tsx} (100%) rename packages/core/src/ui/components/ConfirmStakeVoteDelegation/{ConfirmStakeVoteDelegation.stories.ts => ConfirmStakeVoteDelegation.stories.tsx} (100%) rename packages/core/src/ui/components/ConfirmStakeVoteRegistrationDelegation/{ConfirmStakeVoteRegistrationDelegation.stories.ts => ConfirmStakeVoteRegistrationDelegation.stories.tsx} (100%) rename packages/core/src/ui/components/ConfirmVoteRegistrationDelegation/{ConfirmVoteRegistrationDelegation.stories.ts => ConfirmVoteRegistrationDelegation.stories.tsx} (100%) create mode 100644 packages/core/src/ui/components/ProposalProcedures/HardForkInitiationAction/HardForkInitiationAction.stories.tsx create mode 100644 packages/core/src/ui/components/ProposalProcedures/HardForkInitiationAction/HardForkInitiationAction.tsx create mode 100644 packages/core/src/ui/components/ProposalProcedures/HardForkInitiationAction/HardForkInitiationActionTypes.ts create mode 100644 packages/core/src/ui/components/ProposalProcedures/HardForkInitiationAction/index.ts create mode 100644 packages/core/src/ui/components/ProposalProcedures/InfoAction/InfoAction.stories.tsx create mode 100644 packages/core/src/ui/components/ProposalProcedures/InfoAction/InfoAction.tsx create mode 100644 packages/core/src/ui/components/ProposalProcedures/InfoAction/InfoActionTypes.ts create mode 100644 packages/core/src/ui/components/ProposalProcedures/InfoAction/index.ts create mode 100644 packages/core/src/ui/components/ProposalProcedures/NewConstitutionAction/NewConstitutionAction.stories.tsx create mode 100644 packages/core/src/ui/components/ProposalProcedures/NewConstitutionAction/NewConstitutionAction.tsx create mode 100644 packages/core/src/ui/components/ProposalProcedures/NewConstitutionAction/NewConstitutionActionTypes.ts create mode 100644 packages/core/src/ui/components/ProposalProcedures/NewConstitutionAction/index.ts create mode 100644 packages/core/src/ui/components/ProposalProcedures/NoConfidenceAction/NoConfidenceAction.stories.tsx create mode 100644 packages/core/src/ui/components/ProposalProcedures/NoConfidenceAction/NoConfidenceAction.tsx create mode 100644 packages/core/src/ui/components/ProposalProcedures/NoConfidenceAction/NoConfidenceActionTypes.ts create mode 100644 packages/core/src/ui/components/ProposalProcedures/NoConfidenceAction/index.ts create mode 100644 packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/EconomicGroup.tsx create mode 100644 packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/GovernanceGroup.tsx create mode 100644 packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/NetworkGroup.tsx create mode 100644 packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/ParameterChangeAction.stories.tsx create mode 100644 packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/ParameterChangeAction.tsx create mode 100644 packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/ParameterChangeActionTypes.ts create mode 100644 packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/TechnicalGroup.tsx create mode 100644 packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/index.ts create mode 100644 packages/core/src/ui/components/ProposalProcedures/TreasuryWithdrawalsAction/TreasuryWithdrawalsAction.stories.tsx create mode 100644 packages/core/src/ui/components/ProposalProcedures/TreasuryWithdrawalsAction/TreasuryWithdrawalsAction.tsx create mode 100644 packages/core/src/ui/components/ProposalProcedures/TreasuryWithdrawalsAction/TreasuryWithdrawalsActionTypes.ts create mode 100644 packages/core/src/ui/components/ProposalProcedures/TreasuryWithdrawalsAction/index.ts create mode 100644 packages/core/src/ui/components/ProposalProcedures/UpdateCommitteeAction/UpdateCommitteeActionAction.stories.tsx create mode 100644 packages/core/src/ui/components/ProposalProcedures/UpdateCommitteeAction/UpdateCommitteeActionAction.tsx create mode 100644 packages/core/src/ui/components/ProposalProcedures/UpdateCommitteeAction/UpdateCommitteeActionTypes.ts create mode 100644 packages/core/src/ui/components/ProposalProcedures/UpdateCommitteeAction/index.ts create mode 100644 packages/core/src/ui/components/ProposalProcedures/components/ActionId.tsx create mode 100644 packages/core/src/ui/components/ProposalProcedures/components/ActionIdTypes.ts create mode 100644 packages/core/src/ui/components/ProposalProcedures/components/Card.module.scss create mode 100644 packages/core/src/ui/components/ProposalProcedures/components/Card.tsx create mode 100644 packages/core/src/ui/components/ProposalProcedures/components/Procedure.tsx create mode 100644 packages/core/src/ui/components/ProposalProcedures/components/ProcedureTypes.ts create mode 100644 packages/core/src/ui/components/ProposalProcedures/components/TransactionDetails.tsx create mode 100644 packages/core/src/ui/components/ProposalProcedures/components/TransactionDetailsTypes.ts create mode 100644 packages/core/src/ui/components/ProposalProcedures/index.ts create mode 100644 packages/staking/src/features/DelegationCard/DelegationTooltip.css.ts diff --git a/.github/workflows/e2e-tests-linux.yml b/.github/workflows/e2e-tests-linux.yml index 71eec19cf4..47f32d5fc3 100644 --- a/.github/workflows/e2e-tests-linux.yml +++ b/.github/workflows/e2e-tests-linux.yml @@ -50,6 +50,9 @@ jobs: uses: ./.github/shared/build with: LACE_EXTENSION_KEY: ${{ secrets.MANIFEST_PUBLIC_KEY }} + CARDANO_SERVICES_URL_MAINNET: ${{ secrets.CARDANO_SERVICES_DEV_URL_MAINNET }} + CARDANO_SERVICES_URL_PREPROD: ${{ secrets.CARDANO_SERVICES_DEV_URL_PREPROD }} + CARDANO_SERVICES_URL_PREVIEW: ${{ secrets.CARDANO_SERVICES_DEV_URL_PREVIEW }} - name: Start XVFB run: | Xvfb :99 & diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml index 5c2526ad4b..5066293a93 100644 --- a/.github/workflows/smoke-tests.yml +++ b/.github/workflows/smoke-tests.yml @@ -26,6 +26,9 @@ jobs: uses: ./.github/shared/build with: LACE_EXTENSION_KEY: ${{ secrets.MANIFEST_PUBLIC_KEY }} + CARDANO_SERVICES_URL_MAINNET: ${{ secrets.CARDANO_SERVICES_DEV_URL_MAINNET }} + CARDANO_SERVICES_URL_PREPROD: ${{ secrets.CARDANO_SERVICES_DEV_URL_PREPROD }} + CARDANO_SERVICES_URL_PREVIEW: ${{ secrets.CARDANO_SERVICES_DEV_URL_PREVIEW }} - name: Start XVFB run: | Xvfb :99 & diff --git a/apps/browser-extension-wallet/.env.defaults b/apps/browser-extension-wallet/.env.defaults index fd8968c01a..0ebb89d0bc 100644 --- a/apps/browser-extension-wallet/.env.defaults +++ b/apps/browser-extension-wallet/.env.defaults @@ -4,7 +4,7 @@ DEFAULT_CHAIN=Mainnet WALLET_SYNC_TIMEOUT_IN_SEC=60 WALLET_INTERVAL_IN_SEC=30 DROP_CONSOLE_IN_PRODUCTION=false -AVAILABLE_CHAINS=Preprod,Preview,Mainnet +AVAILABLE_CHAINS=Preprod,Preview,Mainnet,Sanchonet ADA_PRICE_POLLING_IN_SEC=60 TOKEN_PRICE_POLLING_IN_SEC=300 SAVED_PRICE_DURATION_IN_MINUTES=720 diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.tsx index e17e48769d..03980ee8bb 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.tsx @@ -1,4 +1,3 @@ -/* eslint-disable no-console */ import React, { useMemo, useState } from 'react'; import cn from 'classnames'; import { Button, PostHogAction } from '@lace/common'; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransactionContent.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransactionContent.tsx index 03fea860f4..65379c1058 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransactionContent.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransactionContent.tsx @@ -1,8 +1,10 @@ +/* eslint-disable complexity */ import React from 'react'; import { Skeleton } from 'antd'; +import { Wallet } from '@lace/cardano'; +import { SignTxData } from './types'; import { ConfirmDRepRegistrationContainer } from './ConfirmDRepRegistrationContainer'; import { DappTransactionContainer } from './DappTransactionContainer'; -import { SignTxData } from './types'; import { ConfirmDRepRetirementContainer } from './ConfirmDRepRetirementContainer'; import { ConfirmVoteDelegationContainer } from './ConfirmVoteDelegationContainer'; import { VotingProceduresContainer } from './VotingProceduresContainer'; @@ -11,7 +13,7 @@ import { ConfirmVoteRegistrationDelegationContainer } from './ConfirmVoteRegistr import { ConfirmStakeRegistrationDelegationContainer } from './ConfirmStakeRegistrationDelegationContainer'; import { ConfirmStakeVoteRegistrationDelegationContainer } from './ConfirmStakeVoteRegistrationDelegationContainer'; import { ConfirmStakeVoteDelegationContainer } from './ConfirmStakeVoteDelegationContainer'; -import { Wallet } from '@lace/cardano'; +import { ProposalProceduresContainer } from './ProposalProceduresContainer'; interface Props { txType?: Wallet.Cip30TxType; @@ -51,6 +53,9 @@ export const ConfirmTransactionContent = ({ txType, signTxData, onError, errorMe if (txType === Wallet.Cip30TxType.StakeVoteDelegation) { return ; } + if (txType === Wallet.Cip30TxType.ProposalProcedures) { + return ; + } return ; }; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ProposalProceduresContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ProposalProceduresContainer.tsx new file mode 100644 index 0000000000..7c3df2b028 --- /dev/null +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ProposalProceduresContainer.tsx @@ -0,0 +1,86 @@ +/* eslint-disable consistent-return */ +/* eslint-disable complexity */ +import React, { useMemo } from 'react'; +import { Wallet } from '@lace/cardano'; +import { SignTxData } from './types'; +import { proposalProceduresInspector } from './utils'; +import { HardForkInitiationActionContainer } from './proposal-procedures/HardForkInitiationActionContainer'; +import { InfoActionContainer } from './proposal-procedures/InfoActionContainer'; +import { NewConstitutionActionContainer } from './proposal-procedures/NewConstitutionActionContainer'; +import { NoConfidenceActionContainer } from './proposal-procedures/NoConfidenceActionContainer'; +import { ParameterChangeActionContainer } from './proposal-procedures/ParameterChangeActionContainer'; +import { TreasuryWithdrawalsActionContainer } from './proposal-procedures/TreasuryWithdrawalsActionContainer'; +import { UpdateCommitteeActionContainer } from './proposal-procedures/UpdateCommitteeActionContainer'; + +interface Props { + signTxData: SignTxData; + errorMessage?: string; +} + +export const ProposalProceduresContainer = ({ + signTxData: { dappInfo, tx }, + errorMessage +}: Props): React.ReactElement => { + const proposalProcedures = proposalProceduresInspector(tx); + + const props = useMemo(() => ({ dappInfo, errorMessage }), [dappInfo, errorMessage]); + + return ( + <> + {proposalProcedures.map(({ deposit, rewardAccount, anchor, governanceAction }) => { + const key = `${governanceAction.__typename}_${anchor.dataHash}`; + if (governanceAction.__typename === Wallet.Cardano.GovernanceActionType.hard_fork_initiation_action) { + return ( + + ); + } + if (governanceAction.__typename === Wallet.Cardano.GovernanceActionType.info_action) { + return ; + } + if (governanceAction.__typename === Wallet.Cardano.GovernanceActionType.new_constitution) { + return ( + + ); + } + if (governanceAction.__typename === Wallet.Cardano.GovernanceActionType.no_confidence) { + return ( + + ); + } + if (governanceAction.__typename === Wallet.Cardano.GovernanceActionType.parameter_change_action) { + return ( + + ); + } + if (governanceAction.__typename === Wallet.Cardano.GovernanceActionType.treasury_withdrawals_action) { + return ( + + ); + } + if (governanceAction.__typename === Wallet.Cardano.GovernanceActionType.update_committee) { + return ( + + ); + } + })} + + ); +}; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/VotingProceduresContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/VotingProceduresContainer.tsx index 0ca9fc967d..8ebeefc4da 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/VotingProceduresContainer.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/VotingProceduresContainer.tsx @@ -1,11 +1,10 @@ -import React, { useMemo } from 'react'; +import React from 'react'; import { useTranslation } from 'react-i18next'; import { VotingProcedures } from '@lace/core'; import { SignTxData } from './types'; import { drepIDasBech32FromHash, votingProceduresInspector } from './utils'; import { Wallet } from '@lace/cardano'; -import { useWalletStore } from '@src/stores'; -import { config } from '@src/config'; +import { useCExpolorerBaseUrl } from './hooks'; interface Props { signTxData: SignTxData; @@ -53,13 +52,8 @@ export const getVote = (vote: Wallet.Cardano.Vote): Votes => { export const VotingProceduresContainer = ({ signTxData, errorMessage }: Props): React.ReactElement => { const { t } = useTranslation(); const votingProcedures = votingProceduresInspector(signTxData.tx); - const { environmentName } = useWalletStore(); - const { CEXPLORER_BASE_URL, CEXPLORER_URL_PATHS } = config(); - const explorerBaseUrl = useMemo( - () => (environmentName === 'Sanchonet' ? '' : `${CEXPLORER_BASE_URL[environmentName]}/${CEXPLORER_URL_PATHS.Tx}`), - [CEXPLORER_BASE_URL, CEXPLORER_URL_PATHS.Tx, environmentName] - ); + const explorerBaseUrl = useCExpolorerBaseUrl(); return ( { }; }); -const backgroundService = { - getBackgroundStorage: jest.fn(), - setBackgroundStorage: jest.fn() -} as unknown as BackgroundServiceAPIProviderProps['value']; - -const getWrapper = - () => - ({ children }: { children: React.ReactNode }) => - ( - - - - - - - {children} - - - - - - - ); - describe('Testing ConfirmDRepRegistrationContainer component', () => { beforeEach(() => { mockUseWalletStore.mockReset(); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepRetirementContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepRetirementContainer.test.tsx index 570bbdb16e..c7bdc6d5ae 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepRetirementContainer.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepRetirementContainer.test.tsx @@ -21,24 +21,12 @@ import * as React from 'react'; import { cleanup, render } from '@testing-library/react'; import { ConfirmDRepRetirementContainer } from '../ConfirmDRepRetirementContainer'; import '@testing-library/jest-dom'; -import { I18nextProvider } from 'react-i18next'; -import { StoreProvider } from '@src/stores'; -import { - AnalyticsProvider, - AppSettingsProvider, - BackgroundServiceAPIProvider, - BackgroundServiceAPIProviderProps, - DatabaseProvider -} from '@src/providers'; -import { APP_MODE_BROWSER } from '@src/utils/constants'; -import i18n from '@lib/i18n'; import { BehaviorSubject } from 'rxjs'; import { act } from 'react-dom/test-utils'; -import { PostHogClientProvider } from '@providers/PostHogClientProvider'; -import { postHogClientMocks } from '@src/utils/mocks/test-helpers'; import { buildMockTx } from '@src/utils/mocks/tx'; import { Wallet } from '@lace/cardano'; import BigNumber from 'bignumber.js'; +import { getWrapper } from '../testing.utils'; const LOVELACE_VALUE = 1_000_000; const DEFAULT_DECIMALS = 2; @@ -114,30 +102,6 @@ jest.mock('react-i18next', () => { }; }); -const backgroundService = { - getBackgroundStorage: jest.fn(), - setBackgroundStorage: jest.fn() -} as unknown as BackgroundServiceAPIProviderProps['value']; - -const getWrapper = - () => - ({ children }: { children: React.ReactNode }) => - ( - - - - - - - {children} - - - - - - - ); - describe('Testing ConfirmDRepRetirementContainer component', () => { beforeEach(() => { mockUseWalletStore.mockReset(); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepUpdateContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepUpdateContainer.test.tsx index eba21c3337..62aaaec965 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepUpdateContainer.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepUpdateContainer.test.tsx @@ -9,23 +9,11 @@ import * as React from 'react'; import { cleanup, render } from '@testing-library/react'; import { ConfirmDRepUpdateContainer } from '../ConfirmDRepUpdateContainer'; import '@testing-library/jest-dom'; -import { I18nextProvider } from 'react-i18next'; -import { StoreProvider } from '@src/stores'; -import { - AnalyticsProvider, - AppSettingsProvider, - BackgroundServiceAPIProvider, - BackgroundServiceAPIProviderProps, - DatabaseProvider -} from '@src/providers'; -import { APP_MODE_BROWSER } from '@src/utils/constants'; -import i18n from '@lib/i18n'; import { BehaviorSubject } from 'rxjs'; import { act } from 'react-dom/test-utils'; -import { PostHogClientProvider } from '@providers/PostHogClientProvider'; -import { postHogClientMocks } from '@src/utils/mocks/test-helpers'; import { buildMockTx } from '@src/utils/mocks/tx'; import { Wallet } from '@lace/cardano'; +import { getWrapper } from '../testing.utils'; const { Cardano, Crypto, HexBlob } = Wallet; @@ -68,30 +56,6 @@ jest.mock('react-i18next', () => { }; }); -const backgroundService = { - getBackgroundStorage: jest.fn(), - setBackgroundStorage: jest.fn() -} as unknown as BackgroundServiceAPIProviderProps['value']; - -const getWrapper = - () => - ({ children }: { children: React.ReactNode }) => - ( - - - - - - - {children} - - - - - - - ); - describe('Testing ConfirmDRepUpdateContainer component', () => { beforeEach(() => { mockUseWalletStore.mockReset(); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmStakeRegistrationDelegationContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmStakeRegistrationDelegationContainer.test.tsx new file mode 100644 index 0000000000..f8252136c2 --- /dev/null +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmStakeRegistrationDelegationContainer.test.tsx @@ -0,0 +1,149 @@ +/* eslint-disable unicorn/no-null */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable import/imports-first */ +const mockUseWalletStore = jest.fn(); +const t = jest.fn().mockImplementation((res) => res); +const mockUseTranslation = jest.fn(() => ({ t })); +const mockConfirmStakeRegistrationDelegation = jest.fn(); +const mockLovelacesToAdaString = jest.fn(); +import * as React from 'react'; +import { cleanup, render } from '@testing-library/react'; +import { ConfirmStakeRegistrationDelegationContainer } from '../ConfirmStakeRegistrationDelegationContainer'; +import '@testing-library/jest-dom'; +import { BehaviorSubject } from 'rxjs'; +import { act } from 'react-dom/test-utils'; +import { buildMockTx } from '@src/utils/mocks/tx'; +import { Wallet } from '@lace/cardano'; +import { getWrapper } from '../testing.utils'; + +const REWARD_ACCOUNT = Wallet.Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj'); +const STAKE_KEY_HASH = Wallet.Cardano.RewardAccount.toHash(REWARD_ACCOUNT); + +const assetInfo$ = new BehaviorSubject(new Map()); +const available$ = new BehaviorSubject([]); + +const inMemoryWallet = { + assetInfo$, + balance: { + utxo: { + available$ + } + } +}; + +const cardanoCoinMock = { + symbol: 'cardanoCoinMockSymbol' +}; + +jest.mock('@src/stores', () => ({ + ...jest.requireActual('@src/stores'), + useWalletStore: mockUseWalletStore +})); + +jest.mock('@lace/core', () => { + const original = jest.requireActual('@lace/core'); + return { + __esModule: true, + ...original, + ConfirmStakeRegistrationDelegation: mockConfirmStakeRegistrationDelegation + }; +}); + +jest.mock('react-i18next', () => { + const original = jest.requireActual('react-i18next'); + return { + __esModule: true, + ...original, + useTranslation: mockUseTranslation + }; +}); + +jest.mock('@lace/cardano', () => { + const actual = jest.requireActual('@lace/cardano'); + return { + __esModule: true, + ...actual, + Wallet: { + ...actual.Wallet, + util: { + ...actual.Wallet.util, + lovelacesToAdaString: mockLovelacesToAdaString + } + } + }; +}); + +describe('Testing ConfirmStakeRegistrationDelegationContainer component', () => { + beforeEach(() => { + mockUseWalletStore.mockReset(); + mockUseWalletStore.mockImplementation(() => ({ + inMemoryWallet, + walletUI: { cardanoCoin: cardanoCoinMock }, + walletInfo: {} + })); + mockConfirmStakeRegistrationDelegation.mockReset(); + mockConfirmStakeRegistrationDelegation.mockReturnValue(); + mockUseTranslation.mockReset(); + mockUseTranslation.mockImplementation(() => ({ t })); + mockLovelacesToAdaString.mockReset(); + mockLovelacesToAdaString.mockImplementation((val) => val); + }); + + afterEach(() => { + jest.resetModules(); + jest.resetAllMocks(); + cleanup(); + }); + + test('should render ConfirmStakeRegistrationDelegation component with proper props', async () => { + let queryByTestId: any; + + const dappInfo = { + name: 'dappName', + logo: 'dappLogo', + url: 'dappUrl' + }; + const certificate: Wallet.Cardano.Certificate = { + __typename: Wallet.Cardano.CertificateType.StakeRegistrationDelegation, + poolId: Wallet.Cardano.PoolId('pool126zlx7728y7xs08s8epg9qp393kyafy9rzr89g4qkvv4cv93zem'), + stakeCredential: { + type: Wallet.Cardano.CredentialType.KeyHash, + hash: Wallet.Crypto.Hash28ByteBase16(STAKE_KEY_HASH) + }, + deposit: BigInt('100000') + }; + const tx = buildMockTx({ + certificates: [certificate] + }); + const errorMessage = 'errorMessage'; + const props = { signTxData: { dappInfo, tx }, errorMessage }; + + await act(async () => { + ({ queryByTestId } = render(, { + wrapper: getWrapper() + })); + }); + + expect(queryByTestId('ConfirmStakeRegistrationDelegation')).toBeInTheDocument(); + expect(mockConfirmStakeRegistrationDelegation).toHaveBeenLastCalledWith( + { + dappInfo, + metadata: { + poolId: certificate.poolId, + stakeKeyHash: certificate.stakeCredential.hash, + depositPaid: `${certificate.deposit.toString()} ${cardanoCoinMock.symbol}` + }, + translations: { + metadata: t('core.StakeRegistrationDelegation.metadata'), + labels: { + poolId: t('core.StakeRegistrationDelegation.poolId'), + stakeKeyHash: t('core.StakeRegistrationDelegation.stakeKeyHash'), + depositPaid: t('core.StakeRegistrationDelegation.depositPaid') + } + }, + errorMessage + }, + {} + ); + }); +}); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmStakeVoteDelegationContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmStakeVoteDelegationContainer.test.tsx new file mode 100644 index 0000000000..ccd3dba70f --- /dev/null +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmStakeVoteDelegationContainer.test.tsx @@ -0,0 +1,168 @@ +/* eslint-disable unicorn/no-null */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable import/imports-first */ +const mockUseWalletStore = jest.fn(); +const t = jest.fn().mockImplementation((res) => res); +const mockUseTranslation = jest.fn(() => ({ t })); +const mockConfirmStakeVoteDelegation = jest.fn(); +const mockIsDRepAlwaysAbstain = jest.fn(); +const mockIsDRepAlwaysNoConfidence = jest.fn(); +const mockIsDRepCredential = jest.fn(); +import * as React from 'react'; +import { cleanup, render } from '@testing-library/react'; +import { ConfirmStakeVoteDelegationContainer } from '../ConfirmStakeVoteDelegationContainer'; +import '@testing-library/jest-dom'; +import { BehaviorSubject } from 'rxjs'; +import { act } from 'react-dom/test-utils'; +import { buildMockTx } from '@src/utils/mocks/tx'; +import { Wallet } from '@lace/cardano'; +import { getWrapper } from '../testing.utils'; + +const REWARD_ACCOUNT = Wallet.Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj'); +const STAKE_KEY_HASH = Wallet.Cardano.RewardAccount.toHash(REWARD_ACCOUNT); + +const assetInfo$ = new BehaviorSubject(new Map()); +const available$ = new BehaviorSubject([]); + +const inMemoryWallet = { + assetInfo$, + balance: { + utxo: { + available$ + } + } +}; + +const cardanoCoinMock = { + symbol: 'cardanoCoinMockSymbol' +}; + +jest.mock('@src/stores', () => ({ + ...jest.requireActual('@src/stores'), + useWalletStore: mockUseWalletStore +})); + +jest.mock('@lace/core', () => { + const original = jest.requireActual('@lace/core'); + return { + __esModule: true, + ...original, + ConfirmStakeVoteDelegation: mockConfirmStakeVoteDelegation + }; +}); + +jest.mock('react-i18next', () => { + const original = jest.requireActual('react-i18next'); + return { + __esModule: true, + ...original, + useTranslation: mockUseTranslation + }; +}); + +jest.mock('@lace/cardano', () => { + const actual = jest.requireActual('@lace/cardano'); + return { + __esModule: true, + ...actual, + Wallet: { + ...actual.Wallet, + Cardano: { + ...actual.Wallet.Cardano, + isDRepAlwaysAbstain: mockIsDRepAlwaysAbstain, + isDRepAlwaysNoConfidence: mockIsDRepAlwaysNoConfidence, + isDRepCredential: mockIsDRepCredential + } + } + }; +}); + +const isDRepAlwaysAbstainMocked = 'isDRepAlwaysAbstainMocked'; +const isDRepAlwaysNoConfidenceMocked = 'isDRepAlwaysNoConfidenceMocked'; + +describe('Testing ConfirmStakeVoteDelegationContainer component', () => { + beforeEach(() => { + mockUseWalletStore.mockReset(); + mockUseWalletStore.mockImplementation(() => ({ + inMemoryWallet, + walletUI: { cardanoCoin: cardanoCoinMock }, + walletInfo: {} + })); + mockConfirmStakeVoteDelegation.mockReset(); + mockConfirmStakeVoteDelegation.mockReturnValue(); + mockUseTranslation.mockReset(); + mockUseTranslation.mockImplementation(() => ({ t })); + mockIsDRepAlwaysAbstain.mockReset(); + mockIsDRepAlwaysAbstain.mockImplementation(() => isDRepAlwaysAbstainMocked); + mockIsDRepAlwaysNoConfidence.mockReset(); + mockIsDRepAlwaysNoConfidence.mockImplementation(() => isDRepAlwaysNoConfidenceMocked); + mockIsDRepCredential.mockReset(); + mockIsDRepCredential.mockImplementation(() => true); + }); + + afterEach(() => { + jest.resetModules(); + jest.resetAllMocks(); + cleanup(); + }); + + test('should render ConfirmStakeVoteDelegation component with proper props', async () => { + let queryByTestId: any; + + const dappInfo = { + name: 'dappName', + logo: 'dappLogo', + url: 'dappUrl' + }; + const certificate: Wallet.Cardano.Certificate = { + __typename: Wallet.Cardano.CertificateType.StakeVoteDelegation, + poolId: Wallet.Cardano.PoolId('pool126zlx7728y7xs08s8epg9qp393kyafy9rzr89g4qkvv4cv93zem'), + stakeCredential: { + type: Wallet.Cardano.CredentialType.KeyHash, + hash: Wallet.Crypto.Hash28ByteBase16(STAKE_KEY_HASH) + }, + dRep: { + type: Wallet.Cardano.CredentialType.KeyHash, + hash: Wallet.Crypto.Hash28ByteBase16(Buffer.from('dRepCredentialHashdRepCreden').toString('hex')) + } + }; + const tx = buildMockTx({ + certificates: [certificate] + }); + const errorMessage = 'errorMessage'; + const props = { signTxData: { dappInfo, tx }, errorMessage }; + + await act(async () => { + ({ queryByTestId } = render(, { + wrapper: getWrapper() + })); + }); + + expect(queryByTestId('ConfirmStakeVoteDelegation')).toBeInTheDocument(); + expect(mockConfirmStakeVoteDelegation).toHaveBeenLastCalledWith( + { + dappInfo, + metadata: { + poolId: certificate.poolId, + stakeKeyHash: certificate.stakeCredential.hash, + alwaysAbstain: isDRepAlwaysAbstainMocked, + alwaysNoConfidence: isDRepAlwaysNoConfidenceMocked, + drepId: (certificate.dRep as Wallet.Cardano.Credential).hash.toString() + }, + translations: { + metadata: t('core.StakeVoteDelegation.metadata'), + option: t('core.StakeVoteDelegation.option'), + labels: { + poolId: t('core.StakeVoteDelegation.poolId'), + stakeKeyHash: t('core.StakeVoteDelegation.stakeKeyHash'), + drepId: t('core.StakeVoteDelegation.drepId'), + alwaysAbstain: t('core.StakeVoteDelegation.alwaysAbstain'), + alwaysNoConfidence: t('core.StakeVoteDelegation.alwaysNoConfidence') + } + }, + errorMessage + }, + {} + ); + }); +}); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmStakeVoteRegistrationDelegationContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmStakeVoteRegistrationDelegationContainer.test.tsx new file mode 100644 index 0000000000..37aa8cb203 --- /dev/null +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmStakeVoteRegistrationDelegationContainer.test.tsx @@ -0,0 +1,180 @@ +/* eslint-disable unicorn/no-null */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable import/imports-first */ +const mockUseWalletStore = jest.fn(); +const t = jest.fn().mockImplementation((res) => res); +const mockUseTranslation = jest.fn(() => ({ t })); +const mockConfirmStakeVoteRegistrationDelegation = jest.fn(); +const mockIsDRepAlwaysAbstain = jest.fn(); +const mockIsDRepAlwaysNoConfidence = jest.fn(); +const mockIsDRepCredential = jest.fn(); +const mockLovelacesToAdaString = jest.fn(); +import * as React from 'react'; +import { cleanup, render } from '@testing-library/react'; +import { ConfirmStakeVoteRegistrationDelegationContainer } from '../ConfirmStakeVoteRegistrationDelegationContainer'; +import '@testing-library/jest-dom'; +import { BehaviorSubject } from 'rxjs'; +import { act } from 'react-dom/test-utils'; +import { buildMockTx } from '@src/utils/mocks/tx'; +import { Wallet } from '@lace/cardano'; +import { getWrapper } from '../testing.utils'; + +const REWARD_ACCOUNT = Wallet.Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj'); +const STAKE_KEY_HASH = Wallet.Cardano.RewardAccount.toHash(REWARD_ACCOUNT); + +const assetInfo$ = new BehaviorSubject(new Map()); +const available$ = new BehaviorSubject([]); + +const inMemoryWallet = { + assetInfo$, + balance: { + utxo: { + available$ + } + } +}; + +const cardanoCoinMock = { + symbol: 'cardanoCoinMockSymbol' +}; + +jest.mock('@src/stores', () => ({ + ...jest.requireActual('@src/stores'), + useWalletStore: mockUseWalletStore +})); + +jest.mock('@lace/core', () => { + const original = jest.requireActual('@lace/core'); + return { + __esModule: true, + ...original, + ConfirmStakeVoteRegistrationDelegation: mockConfirmStakeVoteRegistrationDelegation + }; +}); + +jest.mock('react-i18next', () => { + const original = jest.requireActual('react-i18next'); + return { + __esModule: true, + ...original, + useTranslation: mockUseTranslation + }; +}); + +jest.mock('@lace/cardano', () => { + const actual = jest.requireActual('@lace/cardano'); + return { + __esModule: true, + ...actual, + Wallet: { + ...actual.Wallet, + util: { + ...actual.Wallet.util, + lovelacesToAdaString: mockLovelacesToAdaString + }, + Cardano: { + ...actual.Wallet.Cardano, + isDRepAlwaysAbstain: mockIsDRepAlwaysAbstain, + isDRepAlwaysNoConfidence: mockIsDRepAlwaysNoConfidence, + isDRepCredential: mockIsDRepCredential + } + } + }; +}); + +const isDRepAlwaysAbstainMocked = 'isDRepAlwaysAbstainMocked'; +const isDRepAlwaysNoConfidenceMocked = 'isDRepAlwaysNoConfidenceMocked'; + +describe('Testing ConfirmStakeVoteRegistrationDelegationContainer component', () => { + beforeEach(() => { + mockUseWalletStore.mockReset(); + mockUseWalletStore.mockImplementation(() => ({ + inMemoryWallet, + walletUI: { cardanoCoin: cardanoCoinMock }, + walletInfo: {} + })); + mockConfirmStakeVoteRegistrationDelegation.mockReset(); + mockConfirmStakeVoteRegistrationDelegation.mockReturnValue( + + ); + mockUseTranslation.mockReset(); + mockUseTranslation.mockImplementation(() => ({ t })); + mockLovelacesToAdaString.mockReset(); + mockLovelacesToAdaString.mockImplementation((val) => val); + mockIsDRepAlwaysAbstain.mockReset(); + mockIsDRepAlwaysAbstain.mockImplementation(() => isDRepAlwaysAbstainMocked); + mockIsDRepAlwaysNoConfidence.mockReset(); + mockIsDRepAlwaysNoConfidence.mockImplementation(() => isDRepAlwaysNoConfidenceMocked); + mockIsDRepCredential.mockReset(); + mockIsDRepCredential.mockImplementation(() => true); + }); + + afterEach(() => { + jest.resetModules(); + jest.resetAllMocks(); + cleanup(); + }); + + test('should render ConfirmStakeVoteRegistrationDelegation component with proper props', async () => { + let queryByTestId: any; + + const dappInfo = { + name: 'dappName', + logo: 'dappLogo', + url: 'dappUrl' + }; + const certificate: Wallet.Cardano.Certificate = { + __typename: Wallet.Cardano.CertificateType.StakeVoteRegistrationDelegation, + poolId: Wallet.Cardano.PoolId('pool126zlx7728y7xs08s8epg9qp393kyafy9rzr89g4qkvv4cv93zem'), + stakeCredential: { + type: Wallet.Cardano.CredentialType.KeyHash, + hash: Wallet.Crypto.Hash28ByteBase16(STAKE_KEY_HASH) + }, + dRep: { + type: Wallet.Cardano.CredentialType.KeyHash, + hash: Wallet.Crypto.Hash28ByteBase16(Buffer.from('dRepCredentialHashdRepCreden').toString('hex')) + }, + deposit: BigInt('100000') + }; + const tx = buildMockTx({ + certificates: [certificate] + }); + const errorMessage = 'errorMessage'; + const props = { signTxData: { dappInfo, tx }, errorMessage }; + + await act(async () => { + ({ queryByTestId } = render(, { + wrapper: getWrapper() + })); + }); + + expect(queryByTestId('ConfirmStakeVoteRegistrationDelegation')).toBeInTheDocument(); + expect(mockConfirmStakeVoteRegistrationDelegation).toHaveBeenLastCalledWith( + { + dappInfo, + metadata: { + poolId: certificate.poolId, + stakeKeyHash: certificate.stakeCredential.hash, + depositPaid: `${certificate.deposit.toString()} ${cardanoCoinMock.symbol}`, + alwaysAbstain: isDRepAlwaysAbstainMocked, + alwaysNoConfidence: isDRepAlwaysNoConfidenceMocked, + drepId: (certificate.dRep as Wallet.Cardano.Credential).hash.toString() + }, + translations: { + metadata: t('core.StakeVoteDelegationRegistration.metadata'), + option: t('core.StakeVoteDelegationRegistration.option'), + labels: { + poolId: t('core.StakeVoteDelegationRegistration.poolId'), + stakeKeyHash: t('core.StakeVoteDelegationRegistration.stakeKeyHash'), + drepId: t('core.StakeVoteDelegationRegistration.drepId'), + alwaysAbstain: t('core.StakeVoteDelegationRegistration.alwaysAbstain'), + alwaysNoConfidence: t('core.StakeVoteDelegationRegistration.alwaysNoConfidence'), + depositPaid: t('core.StakeVoteDelegationRegistration.depositPaid') + } + }, + errorMessage + }, + {} + ); + }); +}); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmTransaction.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmTransaction.test.tsx index 6be8a2a2c9..d8b854952a 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmTransaction.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmTransaction.test.tsx @@ -18,23 +18,9 @@ import * as React from 'react'; import { cleanup, render, act, fireEvent } from '@testing-library/react'; import { ConfirmTransaction } from '../ConfirmTransaction'; import '@testing-library/jest-dom'; -import { I18nextProvider } from 'react-i18next'; -import { StoreProvider } from '@src/stores'; -import { - AnalyticsProvider, - AppSettingsProvider, - BackgroundServiceAPIProvider, - BackgroundServiceAPIProviderProps, - DatabaseProvider, - ViewFlowProvider -} from '@src/providers'; -import { APP_MODE_BROWSER } from '@src/utils/constants'; -import i18n from '@lib/i18n'; import { BehaviorSubject } from 'rxjs'; -import { sendViewsFlowState } from '../../../config'; -import { PostHogClientProvider } from '@providers/PostHogClientProvider'; -import { postHogClientMocks } from '@src/utils/mocks/test-helpers'; import { Wallet } from '@lace/cardano'; +import { getWrapper } from '../testing.utils'; const assetInfo$ = new BehaviorSubject(new Map()); const available$ = new BehaviorSubject([]); @@ -134,32 +120,6 @@ const testIds = { dappTransactionCancel: 'dapp-transaction-cancel' }; -const backgroundService = { - getBackgroundStorage: jest.fn(), - setBackgroundStorage: jest.fn() -} as unknown as BackgroundServiceAPIProviderProps['value']; - -const getWrapper = - () => - ({ children }: { children: React.ReactNode }) => - ( - - - - - - - - {children} - - - - - - - - ); - describe('Testing ConfirmTransaction component', () => { window.ResizeObserver = ResizeObserver; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmTransactionContent.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmTransactionContent.test.tsx index c2ad9259a2..3386aff6be 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmTransactionContent.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmTransactionContent.test.tsx @@ -9,6 +9,19 @@ const mockConfirmDRepRetirementContainer = jest.fn(() => ); const mockConfirmVoteDelegationContainer = jest.fn(() => ); const mockVotingProceduresContainer = jest.fn(() => ); +const mockProposalProceduresContainer = jest.fn(() => ); +const mockConfirmVoteRegistrationDelegationContainer = jest.fn(() => ( + +)); +const mockConfirmStakeRegistrationDelegationContainer = jest.fn(() => ( + +)); +const mockConfirmStakeVoteRegistrationDelegationContainer = jest.fn(() => ( + +)); +const mockConfirmStakeVoteDelegationContainer = jest.fn(() => ( + +)); const mockDappTransactionContainer = jest.fn(() => ); import * as React from 'react'; import { cleanup, render } from '@testing-library/react'; @@ -71,6 +84,51 @@ jest.mock('../VotingProceduresContainer', () => { }; }); +jest.mock('../ProposalProceduresContainer', () => { + const original = jest.requireActual('../ProposalProceduresContainer'); + return { + __esModule: true, + ...original, + ProposalProceduresContainer: mockProposalProceduresContainer + }; +}); + +jest.mock('../ConfirmVoteRegistrationDelegationContainer', () => { + const original = jest.requireActual('../ConfirmVoteRegistrationDelegationContainer'); + return { + __esModule: true, + ...original, + ConfirmVoteRegistrationDelegationContainer: mockConfirmVoteRegistrationDelegationContainer + }; +}); + +jest.mock('../ConfirmStakeRegistrationDelegationContainer', () => { + const original = jest.requireActual('../ConfirmStakeRegistrationDelegationContainer'); + return { + __esModule: true, + ...original, + ConfirmStakeRegistrationDelegationContainer: mockConfirmStakeRegistrationDelegationContainer + }; +}); + +jest.mock('../ConfirmStakeVoteRegistrationDelegationContainer', () => { + const original = jest.requireActual('../ConfirmStakeVoteRegistrationDelegationContainer'); + return { + __esModule: true, + ...original, + ConfirmStakeVoteRegistrationDelegationContainer: mockConfirmStakeVoteRegistrationDelegationContainer + }; +}); + +jest.mock('../ConfirmStakeVoteDelegationContainer', () => { + const original = jest.requireActual('../ConfirmStakeVoteDelegationContainer'); + return { + __esModule: true, + ...original, + ConfirmStakeVoteDelegationContainer: mockConfirmStakeVoteDelegationContainer + }; +}); + jest.mock('../DappTransactionContainer', () => { const original = jest.requireActual('../DappTransactionContainer'); return { @@ -106,6 +164,11 @@ describe('Testing ConfirmTransactionContent component', () => { expect(queryByTestId('ConfirmDRepUpdateContainer')).not.toBeInTheDocument(); expect(queryByTestId('ConfirmVoteDelegationContainer')).not.toBeInTheDocument(); expect(queryByTestId('VotingProceduresContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ProposalProceduresContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmVoteRegistrationDelegationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmStakeRegistrationDelegationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmStakeVoteRegistrationDelegationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmStakeVoteDelegationContainer')).not.toBeInTheDocument(); expect(queryByTestId('DappTransactionContainer')).not.toBeInTheDocument(); expect(mockSkeleton).toHaveBeenLastCalledWith({ loading: true }, {}); }); @@ -125,6 +188,11 @@ describe('Testing ConfirmTransactionContent component', () => { expect(queryByTestId('ConfirmDRepUpdateContainer')).not.toBeInTheDocument(); expect(queryByTestId('ConfirmVoteDelegationContainer')).not.toBeInTheDocument(); expect(queryByTestId('VotingProceduresContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ProposalProceduresContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmVoteRegistrationDelegationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmStakeRegistrationDelegationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmStakeVoteRegistrationDelegationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmStakeVoteDelegationContainer')).not.toBeInTheDocument(); expect(queryByTestId('DappTransactionContainer')).not.toBeInTheDocument(); expect(mockConfirmDRepRegistrationContainer).toHaveBeenLastCalledWith(props, {}); }); @@ -144,6 +212,11 @@ describe('Testing ConfirmTransactionContent component', () => { expect(queryByTestId('ConfirmDRepUpdateContainer')).not.toBeInTheDocument(); expect(queryByTestId('ConfirmVoteDelegationContainer')).not.toBeInTheDocument(); expect(queryByTestId('VotingProceduresContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ProposalProceduresContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmVoteRegistrationDelegationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmStakeRegistrationDelegationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmStakeVoteRegistrationDelegationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmStakeVoteDelegationContainer')).not.toBeInTheDocument(); expect(queryByTestId('DappTransactionContainer')).not.toBeInTheDocument(); expect(mockConfirmDRepRetirementContainer).toHaveBeenLastCalledWith(props, {}); }); @@ -163,6 +236,11 @@ describe('Testing ConfirmTransactionContent component', () => { expect(queryByTestId('ConfirmDRepUpdateContainer')).toBeInTheDocument(); expect(queryByTestId('ConfirmVoteDelegationContainer')).not.toBeInTheDocument(); expect(queryByTestId('VotingProceduresContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ProposalProceduresContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmVoteRegistrationDelegationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmStakeRegistrationDelegationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmStakeVoteRegistrationDelegationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmStakeVoteDelegationContainer')).not.toBeInTheDocument(); expect(queryByTestId('DappTransactionContainer')).not.toBeInTheDocument(); expect(mockConfirmDRepUpdateContainer).toHaveBeenLastCalledWith(props, {}); }); @@ -182,6 +260,11 @@ describe('Testing ConfirmTransactionContent component', () => { expect(queryByTestId('ConfirmDRepUpdateContainer')).not.toBeInTheDocument(); expect(queryByTestId('ConfirmVoteDelegationContainer')).toBeInTheDocument(); expect(queryByTestId('VotingProceduresContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ProposalProceduresContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmVoteRegistrationDelegationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmStakeRegistrationDelegationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmStakeVoteRegistrationDelegationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmStakeVoteDelegationContainer')).not.toBeInTheDocument(); expect(queryByTestId('DappTransactionContainer')).not.toBeInTheDocument(); expect(mockConfirmVoteDelegationContainer).toHaveBeenLastCalledWith(props, {}); }); @@ -201,10 +284,135 @@ describe('Testing ConfirmTransactionContent component', () => { expect(queryByTestId('ConfirmDRepUpdateContainer')).not.toBeInTheDocument(); expect(queryByTestId('ConfirmVoteDelegationContainer')).not.toBeInTheDocument(); expect(queryByTestId('VotingProceduresContainer')).toBeInTheDocument(); + expect(queryByTestId('ProposalProceduresContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmVoteRegistrationDelegationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmStakeRegistrationDelegationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmStakeVoteRegistrationDelegationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmStakeVoteDelegationContainer')).not.toBeInTheDocument(); expect(queryByTestId('DappTransactionContainer')).not.toBeInTheDocument(); expect(mockVotingProceduresContainer).toHaveBeenLastCalledWith(props, {}); }); + test('should render ProposalProceduresContainer with proper props', async () => { + let queryByTestId: any; + + await act(async () => { + ({ queryByTestId } = render( + + )); + }); + + expect(queryByTestId('skeleton')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmDRepRegistrationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmDRepRetirementContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmDRepUpdateContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmVoteDelegationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('VotingProceduresContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ProposalProceduresContainer')).toBeInTheDocument(); + expect(queryByTestId('ConfirmVoteRegistrationDelegationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmStakeRegistrationDelegationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmStakeVoteRegistrationDelegationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmStakeVoteDelegationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('DappTransactionContainer')).not.toBeInTheDocument(); + expect(mockProposalProceduresContainer).toHaveBeenLastCalledWith(props, {}); + }); + + test('should render ConfirmVoteRegistrationDelegationContainer with proper props', async () => { + let queryByTestId: any; + + await act(async () => { + ({ queryByTestId } = render( + + )); + }); + + expect(queryByTestId('skeleton')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmDRepRegistrationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmDRepRetirementContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmDRepUpdateContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmVoteDelegationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('VotingProceduresContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ProposalProceduresContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmVoteRegistrationDelegationContainer')).toBeInTheDocument(); + expect(queryByTestId('ConfirmStakeRegistrationDelegationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmStakeVoteRegistrationDelegationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmStakeVoteDelegationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('DappTransactionContainer')).not.toBeInTheDocument(); + expect(mockConfirmVoteRegistrationDelegationContainer).toHaveBeenLastCalledWith(props, {}); + }); + + test('should render ConfirmStakeRegistrationDelegationContainer with proper props', async () => { + let queryByTestId: any; + + await act(async () => { + ({ queryByTestId } = render( + + )); + }); + + expect(queryByTestId('skeleton')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmDRepRegistrationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmDRepRetirementContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmDRepUpdateContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmVoteDelegationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('VotingProceduresContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ProposalProceduresContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmVoteRegistrationDelegationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmStakeRegistrationDelegationContainer')).toBeInTheDocument(); + expect(queryByTestId('ConfirmStakeVoteRegistrationDelegationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmStakeVoteDelegationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('DappTransactionContainer')).not.toBeInTheDocument(); + expect(mockConfirmStakeRegistrationDelegationContainer).toHaveBeenLastCalledWith(props, {}); + }); + + test('should render ConfirmStakeVoteRegistrationDelegationContainer with proper props', async () => { + let queryByTestId: any; + + await act(async () => { + ({ queryByTestId } = render( + + )); + }); + + expect(queryByTestId('skeleton')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmDRepRegistrationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmDRepRetirementContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmDRepUpdateContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmVoteDelegationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('VotingProceduresContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ProposalProceduresContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmVoteRegistrationDelegationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmStakeRegistrationDelegationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmStakeVoteRegistrationDelegationContainer')).toBeInTheDocument(); + expect(queryByTestId('ConfirmStakeVoteDelegationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('DappTransactionContainer')).not.toBeInTheDocument(); + expect(mockConfirmStakeVoteRegistrationDelegationContainer).toHaveBeenLastCalledWith(props, {}); + }); + + test('should render ConfirmStakeVoteDelegationContainer with proper props', async () => { + let queryByTestId: any; + + await act(async () => { + ({ queryByTestId } = render( + + )); + }); + + expect(queryByTestId('skeleton')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmDRepRegistrationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmDRepRetirementContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmDRepUpdateContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmVoteDelegationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('VotingProceduresContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ProposalProceduresContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmVoteRegistrationDelegationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmStakeRegistrationDelegationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmStakeVoteRegistrationDelegationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmStakeVoteDelegationContainer')).toBeInTheDocument(); + expect(queryByTestId('DappTransactionContainer')).not.toBeInTheDocument(); + expect(mockConfirmStakeVoteDelegationContainer).toHaveBeenLastCalledWith(props, {}); + }); + test('should render DappTransactionContainer with proper props', async () => { let queryByTestId: any; @@ -220,6 +428,11 @@ describe('Testing ConfirmTransactionContent component', () => { expect(queryByTestId('ConfirmDRepUpdateContainer')).not.toBeInTheDocument(); expect(queryByTestId('ConfirmVoteDelegationContainer')).not.toBeInTheDocument(); expect(queryByTestId('VotingProceduresContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ProposalProceduresContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmVoteRegistrationDelegationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmStakeRegistrationDelegationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmStakeVoteRegistrationDelegationContainer')).not.toBeInTheDocument(); + expect(queryByTestId('ConfirmStakeVoteDelegationContainer')).not.toBeInTheDocument(); expect(queryByTestId('DappTransactionContainer')).toBeInTheDocument(); expect(mockDappTransactionContainer).toHaveBeenLastCalledWith(props, {}); }); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmVoteDelegationContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmVoteDelegationContainer.test.tsx index 3f2c77c6d7..1b4c2bdbe2 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmVoteDelegationContainer.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmVoteDelegationContainer.test.tsx @@ -9,23 +9,11 @@ import * as React from 'react'; import { cleanup, render } from '@testing-library/react'; import { ConfirmVoteDelegationContainer } from '../ConfirmVoteDelegationContainer'; import '@testing-library/jest-dom'; -import { I18nextProvider } from 'react-i18next'; -import { StoreProvider } from '@src/stores'; -import { - AnalyticsProvider, - AppSettingsProvider, - BackgroundServiceAPIProvider, - BackgroundServiceAPIProviderProps, - DatabaseProvider -} from '@src/providers'; -import { APP_MODE_BROWSER } from '@src/utils/constants'; -import i18n from '@lib/i18n'; import { BehaviorSubject } from 'rxjs'; import { act } from 'react-dom/test-utils'; -import { PostHogClientProvider } from '@providers/PostHogClientProvider'; -import { postHogClientMocks } from '@src/utils/mocks/test-helpers'; import { buildMockTx } from '@src/utils/mocks/tx'; import { Wallet } from '@lace/cardano'; +import { getWrapper } from '../testing.utils'; const REWARD_ACCOUNT = Wallet.Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj'); const STAKE_KEY_HASH = Wallet.Cardano.RewardAccount.toHash(REWARD_ACCOUNT); @@ -69,30 +57,6 @@ jest.mock('react-i18next', () => { }; }); -const backgroundService = { - getBackgroundStorage: jest.fn(), - setBackgroundStorage: jest.fn() -} as unknown as BackgroundServiceAPIProviderProps['value']; - -const getWrapper = - () => - ({ children }: { children: React.ReactNode }) => - ( - - - - - - - {children} - - - - - - - ); - describe('Testing ConfirmVoteDelegationContainer component', () => { beforeEach(() => { mockUseWalletStore.mockReset(); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmVoteRegistrationDelegationContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmVoteRegistrationDelegationContainer.test.tsx new file mode 100644 index 0000000000..ffc8debe9f --- /dev/null +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmVoteRegistrationDelegationContainer.test.tsx @@ -0,0 +1,175 @@ +/* eslint-disable unicorn/no-null */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable import/imports-first */ +const mockUseWalletStore = jest.fn(); +const t = jest.fn().mockImplementation((res) => res); +const mockUseTranslation = jest.fn(() => ({ t })); +const mockConfirmVoteRegistrationDelegation = jest.fn(); +const mockIsDRepAlwaysAbstain = jest.fn(); +const mockIsDRepAlwaysNoConfidence = jest.fn(); +const mockIsDRepCredential = jest.fn(); +const mockLovelacesToAdaString = jest.fn(); +import * as React from 'react'; +import { cleanup, render } from '@testing-library/react'; +import { ConfirmVoteRegistrationDelegationContainer } from '../ConfirmVoteRegistrationDelegationContainer'; +import '@testing-library/jest-dom'; +import { BehaviorSubject } from 'rxjs'; +import { act } from 'react-dom/test-utils'; +import { buildMockTx } from '@src/utils/mocks/tx'; +import { Wallet } from '@lace/cardano'; +import { getWrapper } from '../testing.utils'; + +const REWARD_ACCOUNT = Wallet.Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj'); +const STAKE_KEY_HASH = Wallet.Cardano.RewardAccount.toHash(REWARD_ACCOUNT); + +const assetInfo$ = new BehaviorSubject(new Map()); +const available$ = new BehaviorSubject([]); + +const inMemoryWallet = { + assetInfo$, + balance: { + utxo: { + available$ + } + } +}; + +const cardanoCoinMock = { + symbol: 'cardanoCoinMockSymbol' +}; + +jest.mock('@src/stores', () => ({ + ...jest.requireActual('@src/stores'), + useWalletStore: mockUseWalletStore +})); + +jest.mock('@lace/core', () => { + const original = jest.requireActual('@lace/core'); + return { + __esModule: true, + ...original, + ConfirmVoteRegistrationDelegation: mockConfirmVoteRegistrationDelegation + }; +}); + +jest.mock('react-i18next', () => { + const original = jest.requireActual('react-i18next'); + return { + __esModule: true, + ...original, + useTranslation: mockUseTranslation + }; +}); + +jest.mock('@lace/cardano', () => { + const actual = jest.requireActual('@lace/cardano'); + return { + __esModule: true, + ...actual, + Wallet: { + ...actual.Wallet, + util: { + ...actual.Wallet.util, + lovelacesToAdaString: mockLovelacesToAdaString + }, + Cardano: { + ...actual.Wallet.Cardano, + isDRepAlwaysAbstain: mockIsDRepAlwaysAbstain, + isDRepAlwaysNoConfidence: mockIsDRepAlwaysNoConfidence, + isDRepCredential: mockIsDRepCredential + } + } + }; +}); + +const isDRepAlwaysAbstainMocked = 'isDRepAlwaysAbstainMocked'; +const isDRepAlwaysNoConfidenceMocked = 'isDRepAlwaysNoConfidenceMocked'; + +describe('Testing ConfirmVoteRegistrationDelegationContainer component', () => { + beforeEach(() => { + mockUseWalletStore.mockReset(); + mockUseWalletStore.mockImplementation(() => ({ + inMemoryWallet, + walletUI: { cardanoCoin: cardanoCoinMock }, + walletInfo: {} + })); + mockConfirmVoteRegistrationDelegation.mockReset(); + mockConfirmVoteRegistrationDelegation.mockReturnValue(); + mockUseTranslation.mockReset(); + mockUseTranslation.mockImplementation(() => ({ t })); + mockLovelacesToAdaString.mockReset(); + mockLovelacesToAdaString.mockImplementation((val) => val); + mockIsDRepAlwaysAbstain.mockReset(); + mockIsDRepAlwaysAbstain.mockImplementation(() => isDRepAlwaysAbstainMocked); + mockIsDRepAlwaysNoConfidence.mockReset(); + mockIsDRepAlwaysNoConfidence.mockImplementation(() => isDRepAlwaysNoConfidenceMocked); + mockIsDRepCredential.mockReset(); + mockIsDRepCredential.mockImplementation(() => true); + }); + + afterEach(() => { + jest.resetModules(); + jest.resetAllMocks(); + cleanup(); + }); + + test('should render ConfirmVoteRegistrationDelegation component with proper props', async () => { + let queryByTestId: any; + + const dappInfo = { + name: 'dappName', + logo: 'dappLogo', + url: 'dappUrl' + }; + const certificate: Wallet.Cardano.Certificate = { + __typename: Wallet.Cardano.CertificateType.VoteRegistrationDelegation, + stakeCredential: { + type: Wallet.Cardano.CredentialType.KeyHash, + hash: Wallet.Crypto.Hash28ByteBase16(STAKE_KEY_HASH) + }, + dRep: { + type: Wallet.Cardano.CredentialType.KeyHash, + hash: Wallet.Crypto.Hash28ByteBase16(Buffer.from('dRepCredentialHashdRepCreden').toString('hex')) + }, + deposit: BigInt('100000') + }; + const tx = buildMockTx({ + certificates: [certificate] + }); + const errorMessage = 'errorMessage'; + const props = { signTxData: { dappInfo, tx }, errorMessage }; + + await act(async () => { + ({ queryByTestId } = render(, { + wrapper: getWrapper() + })); + }); + + expect(queryByTestId('ConfirmVoteRegistrationDelegation')).toBeInTheDocument(); + expect(mockConfirmVoteRegistrationDelegation).toHaveBeenLastCalledWith( + { + dappInfo, + metadata: { + depositPaid: `${certificate.deposit.toString()} ${cardanoCoinMock.symbol}`, + stakeKeyHash: certificate.stakeCredential.hash, + alwaysAbstain: isDRepAlwaysAbstainMocked, + alwaysNoConfidence: isDRepAlwaysNoConfidenceMocked, + drepId: (certificate.dRep as Wallet.Cardano.Credential).hash.toString() + }, + translations: { + metadata: t('core.VoteRegistrationDelegation.metadata'), + option: t('core.VoteRegistrationDelegation.option'), + labels: { + drepId: t('core.VoteRegistrationDelegation.drepId'), + alwaysAbstain: t('core.VoteRegistrationDelegation.alwaysAbstain'), + alwaysNoConfidence: t('core.VoteRegistrationDelegation.alwaysNoConfidence'), + depositPaid: t('core.VoteRegistrationDelegation.depositPaid'), + stakeKeyHash: t('core.VoteRegistrationDelegation.stakeKeyHash') + } + }, + errorMessage + }, + {} + ); + }); +}); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/DappTransactionContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/DappTransactionContainer.test.tsx index 4f58f3f937..2f6eca12f0 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/DappTransactionContainer.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/DappTransactionContainer.test.tsx @@ -19,24 +19,12 @@ import * as React from 'react'; import { cleanup, render } from '@testing-library/react'; import { DappTransactionContainer } from '../DappTransactionContainer'; import '@testing-library/jest-dom'; -import { I18nextProvider } from 'react-i18next'; -import { StoreProvider } from '@src/stores'; -import { - AnalyticsProvider, - AppSettingsProvider, - BackgroundServiceAPIProvider, - BackgroundServiceAPIProviderProps, - DatabaseProvider -} from '@src/providers'; -import { APP_MODE_BROWSER } from '@src/utils/constants'; -import i18n from '@lib/i18n'; import { BehaviorSubject } from 'rxjs'; import { act } from 'react-dom/test-utils'; -import { PostHogClientProvider } from '@providers/PostHogClientProvider'; -import { postHogClientMocks } from '@src/utils/mocks/test-helpers'; import { buildMockTx } from '@src/utils/mocks/tx'; import { Wallet } from '@lace/cardano'; import { SignTxData } from '../types'; +import { getWrapper } from '../testing.utils'; const { Cardano, Crypto } = Wallet; @@ -114,30 +102,6 @@ jest.mock('antd', () => { }; }); -const backgroundService = { - getBackgroundStorage: jest.fn(), - setBackgroundStorage: jest.fn() -} as unknown as BackgroundServiceAPIProviderProps['value']; - -const getWrapper = - () => - ({ children }: { children: React.ReactNode }) => - ( - - - - - - - {children} - - - - - - - ); - describe('Testing DappTransactionContainer component', () => { beforeEach(() => { mockUseWalletStore.mockReset(); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ProposalProceduresContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ProposalProceduresContainer.test.tsx new file mode 100644 index 0000000000..8dd390fba9 --- /dev/null +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ProposalProceduresContainer.test.tsx @@ -0,0 +1,179 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable no-magic-numbers */ +/* eslint-disable import/imports-first */ +const mockHardForkInitiationActionContainer = jest.fn(() => ); +const mockInfoActionContainer = jest.fn(() => ); +const mockNewConstitutionActionContainer = jest.fn(() => ); +const mockNoConfidenceActionContainer = jest.fn(() => ); +const mockParameterChangeActionContainer = jest.fn(() => ); +const mockTreasuryWithdrawalsActionContainer = jest.fn(() => ); +const mockUpdateCommitteeActionContainer = jest.fn(() => ); +import { Wallet } from '@lace/cardano'; +import * as React from 'react'; +import { cleanup, render } from '@testing-library/react'; +import { ProposalProceduresContainer } from '../ProposalProceduresContainer'; +import '@testing-library/jest-dom'; +import { act } from 'react-dom/test-utils'; +import { buildMockTx } from '@src/utils/mocks/tx'; + +jest.mock('../proposal-procedures/HardForkInitiationActionContainer', () => { + const original = jest.requireActual('../proposal-procedures/HardForkInitiationActionContainer'); + return { + __esModule: true, + ...original, + HardForkInitiationActionContainer: mockHardForkInitiationActionContainer + }; +}); + +jest.mock('../proposal-procedures/InfoActionContainer', () => { + const original = jest.requireActual('../proposal-procedures/InfoActionContainer'); + return { + __esModule: true, + ...original, + InfoActionContainer: mockInfoActionContainer + }; +}); + +jest.mock('../proposal-procedures/NewConstitutionActionContainer', () => { + const original = jest.requireActual('../proposal-procedures/NewConstitutionActionContainer'); + return { + __esModule: true, + ...original, + NewConstitutionActionContainer: mockNewConstitutionActionContainer + }; +}); + +jest.mock('../proposal-procedures/NoConfidenceActionContainer', () => { + const original = jest.requireActual('../proposal-procedures/NoConfidenceActionContainer'); + return { + __esModule: true, + ...original, + NoConfidenceActionContainer: mockNoConfidenceActionContainer + }; +}); + +jest.mock('../proposal-procedures/ParameterChangeActionContainer', () => { + const original = jest.requireActual('../proposal-procedures/ParameterChangeActionContainer'); + return { + __esModule: true, + ...original, + ParameterChangeActionContainer: mockParameterChangeActionContainer + }; +}); + +jest.mock('../proposal-procedures/TreasuryWithdrawalsActionContainer', () => { + const original = jest.requireActual('../proposal-procedures/TreasuryWithdrawalsActionContainer'); + return { + __esModule: true, + ...original, + TreasuryWithdrawalsActionContainer: mockTreasuryWithdrawalsActionContainer + }; +}); + +jest.mock('../proposal-procedures/UpdateCommitteeActionContainer', () => { + const original = jest.requireActual('../proposal-procedures/UpdateCommitteeActionContainer'); + return { + __esModule: true, + ...original, + UpdateCommitteeActionContainer: mockUpdateCommitteeActionContainer + }; +}); + +const dappInfo = { + name: 'dappName', + logo: 'dappLogo', + url: 'dappUrl' +}; +const errorMessage = 'errorMessage'; +const tx = buildMockTx(); +const deposit = BigInt('10000'); +const rewardAccount = Wallet.Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj'); +const anchor = { + url: 'anchorUrl', + dataHash: Wallet.Crypto.Hash32ByteBase16(Buffer.from('anchorDataHashanchorDataHashanch').toString('hex')) +}; + +const hardForkInitiationAction = { + __typename: Wallet.Cardano.GovernanceActionType.hard_fork_initiation_action +} as Wallet.Cardano.HardForkInitiationAction; +const infoAction = { + __typename: Wallet.Cardano.GovernanceActionType.info_action +} as Wallet.Cardano.InfoAction; +const newConstitution = { + __typename: Wallet.Cardano.GovernanceActionType.new_constitution +} as Wallet.Cardano.NewConstitution; +const noConfidence = { + __typename: Wallet.Cardano.GovernanceActionType.no_confidence +} as Wallet.Cardano.NoConfidence; +const parameterChangeAction = { + __typename: Wallet.Cardano.GovernanceActionType.parameter_change_action +} as Wallet.Cardano.ParameterChangeAction; +const treasuryWithdrawalsAction = { + __typename: Wallet.Cardano.GovernanceActionType.treasury_withdrawals_action +} as Wallet.Cardano.TreasuryWithdrawalsAction; +const updateCommittee = { + __typename: Wallet.Cardano.GovernanceActionType.update_committee +} as Wallet.Cardano.UpdateCommittee; + +const proposalProcedures = [ + { deposit, rewardAccount, anchor, governanceAction: hardForkInitiationAction }, + { deposit, rewardAccount, anchor, governanceAction: infoAction }, + { deposit, rewardAccount, anchor, governanceAction: newConstitution }, + { deposit, rewardAccount, anchor, governanceAction: noConfidence }, + { deposit, rewardAccount, anchor, governanceAction: parameterChangeAction }, + { deposit, rewardAccount, anchor, governanceAction: treasuryWithdrawalsAction }, + { deposit, rewardAccount, anchor, governanceAction: updateCommittee } +]; +const signTxData = { dappInfo, tx: { ...tx, body: { ...tx.body, proposalProcedures } } }; +const props = { signTxData, errorMessage }; + +describe('Testing ProposalProceduresContainer component', () => { + afterEach(() => { + jest.clearAllMocks(); + cleanup(); + }); + + test('should render proper procedure', async () => { + let queryByTestId: any; + + await act(async () => { + ({ queryByTestId } = render()); + }); + + expect(queryByTestId('HardForkInitiationActionContainer')).toBeInTheDocument(); + expect(queryByTestId('InfoActionContainer')).toBeInTheDocument(); + expect(queryByTestId('NewConstitutionActionContainer')).toBeInTheDocument(); + expect(queryByTestId('NoConfidenceActionContainer')).toBeInTheDocument(); + expect(queryByTestId('ParameterChangeActionContainer')).toBeInTheDocument(); + expect(queryByTestId('TreasuryWithdrawalsActionContainer')).toBeInTheDocument(); + expect(queryByTestId('UpdateCommitteeActionContainer')).toBeInTheDocument(); + + const expectedProps = { dappInfo: signTxData.dappInfo, errorMessage, deposit, rewardAccount, anchor }; + + expect(mockHardForkInitiationActionContainer).toHaveBeenLastCalledWith( + { ...expectedProps, governanceAction: hardForkInitiationAction }, + {} + ); + expect(mockInfoActionContainer).toHaveBeenLastCalledWith({ ...expectedProps, governanceAction: infoAction }, {}); + expect(mockNewConstitutionActionContainer).toHaveBeenLastCalledWith( + { ...expectedProps, governanceAction: newConstitution }, + {} + ); + expect(mockNoConfidenceActionContainer).toHaveBeenLastCalledWith( + { ...expectedProps, governanceAction: noConfidence }, + {} + ); + expect(mockParameterChangeActionContainer).toHaveBeenLastCalledWith( + { ...expectedProps, governanceAction: parameterChangeAction }, + {} + ); + expect(mockTreasuryWithdrawalsActionContainer).toHaveBeenLastCalledWith( + { ...expectedProps, governanceAction: treasuryWithdrawalsAction }, + {} + ); + expect(mockUpdateCommitteeActionContainer).toHaveBeenLastCalledWith( + { ...expectedProps, governanceAction: updateCommittee }, + {} + ); + }); +}); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/VotingProceduresContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/VotingProceduresContainer.test.tsx index d1ddb7fdcc..fd1c35b40e 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/VotingProceduresContainer.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/VotingProceduresContainer.test.tsx @@ -13,22 +13,10 @@ import * as React from 'react'; import { cleanup, render } from '@testing-library/react'; import { VoterType, Votes, VotingProceduresContainer, getVote, getVoterType } from '../VotingProceduresContainer'; import '@testing-library/jest-dom'; -import { I18nextProvider } from 'react-i18next'; -import { StoreProvider } from '@src/stores'; -import { - AnalyticsProvider, - AppSettingsProvider, - BackgroundServiceAPIProvider, - BackgroundServiceAPIProviderProps, - DatabaseProvider -} from '@src/providers'; -import { APP_MODE_BROWSER } from '@src/utils/constants'; -import i18n from '@lib/i18n'; import { act } from 'react-dom/test-utils'; -import { PostHogClientProvider } from '@providers/PostHogClientProvider'; -import { postHogClientMocks } from '@src/utils/mocks/test-helpers'; import { buildMockTx } from '@src/utils/mocks/tx'; import { Wallet } from '@lace/cardano'; +import { getWrapper } from '../testing.utils'; jest.mock('@src/stores', () => ({ ...jest.requireActual('@src/stores'), @@ -65,30 +53,6 @@ jest.mock('react-i18next', () => { }; }); -const backgroundService = { - getBackgroundStorage: jest.fn(), - setBackgroundStorage: jest.fn() -} as unknown as BackgroundServiceAPIProviderProps['value']; - -const getWrapper = - () => - ({ children }: { children: React.ReactNode }) => - ( - - - - - - - {children} - - - - - - - ); - describe('Testing VotingProceduresContainer component', () => { beforeEach(() => { mockUseWalletStore.mockReset(); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/utils.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/utils.test.tsx index d998977a9f..a43ecc32b0 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/utils.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/utils.test.tsx @@ -102,37 +102,66 @@ describe('Testing utils', () => { const txInspectorCurriedFnPayload = { minted: [], burned: [] } as unknown as any; const createTxInspectorSpy = jest .spyOn(Core, 'createTxInspector') - .mockReturnValueOnce(() => ({ ...txInspectorCurriedFnPayload, votingProcedures: true })); - expect(getTxType(tx)).toEqual(Wallet.Cip30TxType.VotingProcedures); + .mockReturnValueOnce(() => ({ ...txInspectorCurriedFnPayload, proposalProcedures: true })); + expect(getTxType(tx)).toEqual(Wallet.Cip30TxType.ProposalProcedures); expect(createTxInspectorSpy).toHaveBeenCalledTimes(1); + createTxInspectorSpy.mockReturnValueOnce(() => ({ ...txInspectorCurriedFnPayload, votingProcedures: true })); + expect(getTxType(tx)).toEqual(Wallet.Cip30TxType.VotingProcedures); + expect(createTxInspectorSpy).toHaveBeenCalledTimes(2); + createTxInspectorSpy.mockReturnValueOnce(() => ({ ...txInspectorCurriedFnPayload, minted: { length: 1 } })); expect(getTxType(tx)).toEqual(Wallet.Cip30TxType.Mint); - expect(createTxInspectorSpy).toHaveBeenCalledTimes(2); + expect(createTxInspectorSpy).toHaveBeenCalledTimes(3); createTxInspectorSpy.mockReturnValueOnce(() => ({ ...txInspectorCurriedFnPayload, burned: { length: 1 } })); expect(getTxType(tx)).toEqual(Wallet.Cip30TxType.Burn); - expect(createTxInspectorSpy).toHaveBeenCalledTimes(3); + expect(createTxInspectorSpy).toHaveBeenCalledTimes(4); createTxInspectorSpy.mockReturnValueOnce(() => ({ ...txInspectorCurriedFnPayload, dRepRegistration: true })); expect(getTxType(tx)).toEqual(Wallet.Cip30TxType.DRepRegistration); - expect(createTxInspectorSpy).toHaveBeenCalledTimes(4); + expect(createTxInspectorSpy).toHaveBeenCalledTimes(5); createTxInspectorSpy.mockReturnValueOnce(() => ({ ...txInspectorCurriedFnPayload, dRepRetirement: true })); expect(getTxType(tx)).toEqual(Wallet.Cip30TxType.DRepRetirement); - expect(createTxInspectorSpy).toHaveBeenCalledTimes(5); + expect(createTxInspectorSpy).toHaveBeenCalledTimes(6); createTxInspectorSpy.mockReturnValueOnce(() => ({ ...txInspectorCurriedFnPayload, voteDelegation: true })); expect(getTxType(tx)).toEqual(Wallet.Cip30TxType.VoteDelegation); - expect(createTxInspectorSpy).toHaveBeenCalledTimes(6); + expect(createTxInspectorSpy).toHaveBeenCalledTimes(7); createTxInspectorSpy.mockReturnValueOnce(() => ({ ...txInspectorCurriedFnPayload, dRepUpdate: true })); expect(getTxType(tx)).toEqual(Wallet.Cip30TxType.DRepUpdate); - expect(createTxInspectorSpy).toHaveBeenCalledTimes(7); + expect(createTxInspectorSpy).toHaveBeenCalledTimes(8); + + createTxInspectorSpy.mockReturnValueOnce(() => ({ ...txInspectorCurriedFnPayload, stakeVoteDelegation: true })); + expect(getTxType(tx)).toEqual(Wallet.Cip30TxType.StakeVoteDelegation); + expect(createTxInspectorSpy).toHaveBeenCalledTimes(9); + + createTxInspectorSpy.mockReturnValueOnce(() => ({ + ...txInspectorCurriedFnPayload, + voteRegistrationDelegation: true + })); + expect(getTxType(tx)).toEqual(Wallet.Cip30TxType.VoteRegistrationDelegation); + expect(createTxInspectorSpy).toHaveBeenCalledTimes(10); + + createTxInspectorSpy.mockReturnValueOnce(() => ({ + ...txInspectorCurriedFnPayload, + stakeRegistrationDelegation: true + })); + expect(getTxType(tx)).toEqual(Wallet.Cip30TxType.StakeRegistrationDelegation); + expect(createTxInspectorSpy).toHaveBeenCalledTimes(11); + + createTxInspectorSpy.mockReturnValueOnce(() => ({ + ...txInspectorCurriedFnPayload, + stakeVoteDelegationRegistration: true + })); + expect(getTxType(tx)).toEqual(Wallet.Cip30TxType.StakeVoteDelegationRegistration); + expect(createTxInspectorSpy).toHaveBeenCalledTimes(12); createTxInspectorSpy.mockReturnValueOnce(() => ({ ...txInspectorCurriedFnPayload })); expect(getTxType(tx)).toEqual(Wallet.Cip30TxType.Send); - expect(createTxInspectorSpy).toHaveBeenCalledTimes(8); + expect(createTxInspectorSpy).toHaveBeenCalledTimes(13); }); test('testing drepIDasBech32FromHash', () => { diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/hooks.ts b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/hooks.ts index 602b689441..105b686672 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/hooks.ts +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/hooks.ts @@ -12,6 +12,7 @@ import { dAppRoutePaths } from '@routes'; import { Wallet } from '@lace/cardano'; import { useRedirection } from '@hooks'; import { CardanoTxOut, WalletInfo } from '@src/types'; +import { config } from '@src/config'; import { TokenInfo, getAssetsInformation } from '@src/utils/get-assets-information'; import { getTransactionAssetsId } from '@src/stores/slices'; import { AddressListType } from '@src/views/browser-view/features/activity'; @@ -288,3 +289,20 @@ export const useGetOwnPubDRepKeyHash = (): UseGetOwnPubDRepKeyHash => { // TODO consider using Zustand or at least some common abstraction e.g. https://github.com/streamich/react-use/blob/master/src/useAsync.ts return { loading: ownPubDRepKeyHash === undefined, ownPubDRepKeyHash }; }; + +export const useCExpolorerBaseUrl = (): string => { + const [explorerBaseUrl, setExplorerBaseUrl] = useState(''); + const { environmentName } = useWalletStore(); + + const { CEXPLORER_BASE_URL, CEXPLORER_URL_PATHS } = config(); + + useEffect(() => { + const newUrl = + environmentName === 'Sanchonet' ? '' : `${CEXPLORER_BASE_URL[environmentName]}/${CEXPLORER_URL_PATHS.Tx}`; + if (newUrl !== explorerBaseUrl) { + setExplorerBaseUrl(newUrl); + } + }, [CEXPLORER_BASE_URL, CEXPLORER_URL_PATHS.Tx, environmentName, explorerBaseUrl]); + + return explorerBaseUrl; +}; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/HardForkInitiationActionContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/HardForkInitiationActionContainer.tsx new file mode 100644 index 0000000000..30b937804e --- /dev/null +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/HardForkInitiationActionContainer.tsx @@ -0,0 +1,93 @@ +import React, { useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Wallet } from '@lace/cardano'; +import { HardForkInitiationAction } from '@lace/core'; +import { useWalletStore } from '@src/stores'; +import { SignTxData } from '../types'; +import { useCExpolorerBaseUrl } from '../hooks'; + +interface Props { + dappInfo: SignTxData['dappInfo']; + governanceAction: Wallet.Cardano.HardForkInitiationAction; + deposit: Wallet.Cardano.ProposalProcedure['deposit']; + rewardAccount: Wallet.Cardano.ProposalProcedure['rewardAccount']; + anchor: Wallet.Cardano.ProposalProcedure['anchor']; + errorMessage?: string; +} + +export const HardForkInitiationActionContainer = ({ + dappInfo, + governanceAction, + deposit, + rewardAccount, + anchor, + errorMessage +}: Props): React.ReactElement => { + const { t } = useTranslation(); + const { + walletUI: { cardanoCoin } + } = useWalletStore(); + + const explorerBaseUrl = useCExpolorerBaseUrl(); + + const translations = useMemo[0]['translations']>( + () => ({ + txDetails: { + title: t('core.ProposalProcedure.txDetails.title'), + txType: t('core.ProposalProcedure.txDetails.txType'), + deposit: t('core.ProposalProcedure.txDetails.deposit'), + rewardAccount: t('core.ProposalProcedure.txDetails.rewardAccount') + }, + procedure: { + title: t('core.ProposalProcedure.procedure.title'), + anchor: { + url: t('core.ProposalProcedure.procedure.anchor.url'), + hash: t('core.ProposalProcedure.procedure.anchor.hash') + } + }, + protocolVersion: { + major: t('core.ProposalProcedure.governanceAction.hardForkInitiation.protocolVersion.major'), + minor: t('core.ProposalProcedure.governanceAction.hardForkInitiation.protocolVersion.minor'), + patch: t('core.ProposalProcedure.governanceAction.hardForkInitiation.protocolVersion.patch') + }, + actionId: { + title: t('core.ProposalProcedure.governanceAction.actionId.title'), + index: t('core.ProposalProcedure.governanceAction.actionId.index'), + txId: t('core.ProposalProcedure.governanceAction.actionId.txId') + } + }), + [t] + ); + + const { governanceActionId, protocolVersion } = governanceAction; + + const data: Parameters[0]['data'] = { + txDetails: { + txType: t('core.ProposalProcedure.governanceAction.hardForkInitiation.title'), + deposit: `${Wallet.util.lovelacesToAdaString(deposit.toString())} ${cardanoCoin.symbol}`, + rewardAccount + }, + procedure: { + anchor: { + url: anchor.url, + hash: anchor.dataHash, + ...(explorerBaseUrl && { txHashUrl: `${explorerBaseUrl}/${anchor.dataHash}` }) + } + }, + protocolVersion: { + major: protocolVersion.major.toString(), + minor: protocolVersion.minor.toString(), + patch: protocolVersion.patch?.toString() + }, + ...(governanceActionId && { + actionId: { + index: governanceActionId.actionIndex.toString(), + id: governanceActionId.id || '' + } + }) + }; + + return ( + + ); +}; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/InfoActionContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/InfoActionContainer.tsx new file mode 100644 index 0000000000..de46a3b62f --- /dev/null +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/InfoActionContainer.tsx @@ -0,0 +1,50 @@ +import React, { useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Wallet } from '@lace/cardano'; +import { InfoAction } from '@lace/core'; +import { SignTxData } from '../types'; +import { useCExpolorerBaseUrl } from '../hooks'; + +interface Props { + dappInfo: SignTxData['dappInfo']; + anchor: Wallet.Cardano.ProposalProcedure['anchor']; + errorMessage?: string; +} + +export const InfoActionContainer = ({ dappInfo, anchor, errorMessage }: Props): React.ReactElement => { + const { t } = useTranslation(); + + const explorerBaseUrl = useCExpolorerBaseUrl(); + + const translations = useMemo[0]['translations']>( + () => ({ + txDetails: { + title: t('core.ProposalProcedure.txDetails.title'), + txType: t('core.ProposalProcedure.txDetails.txType') + }, + procedure: { + title: t('core.ProposalProcedure.procedure.title'), + anchor: { + url: t('core.ProposalProcedure.procedure.anchor.url'), + hash: t('core.ProposalProcedure.procedure.anchor.hash') + } + } + }), + [t] + ); + + const data: Parameters[0]['data'] = { + txDetails: { + txType: t('core.ProposalProcedure.governanceAction.infoAction.title') + }, + procedure: { + anchor: { + url: anchor.url, + hash: anchor.dataHash, + ...(explorerBaseUrl && { txHashUrl: `${explorerBaseUrl}/${anchor.dataHash}` }) + } + } + }; + + return ; +}; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/NewConstitutionActionContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/NewConstitutionActionContainer.tsx new file mode 100644 index 0000000000..de84864942 --- /dev/null +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/NewConstitutionActionContainer.tsx @@ -0,0 +1,98 @@ +import React, { useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Wallet } from '@lace/cardano'; +import { NewConstitutionAction } from '@lace/core'; +import { useWalletStore } from '@src/stores'; +import { SignTxData } from '../types'; +import { useCExpolorerBaseUrl } from '../hooks'; + +interface Props { + dappInfo: SignTxData['dappInfo']; + governanceAction: Wallet.Cardano.NewConstitution; + deposit: Wallet.Cardano.ProposalProcedure['deposit']; + rewardAccount: Wallet.Cardano.ProposalProcedure['rewardAccount']; + anchor: Wallet.Cardano.ProposalProcedure['anchor']; + errorMessage?: string; +} + +export const NewConstitutionActionContainer = ({ + dappInfo, + governanceAction, + deposit, + rewardAccount, + anchor, + errorMessage +}: Props): React.ReactElement => { + const { t } = useTranslation(); + const { + walletUI: { cardanoCoin } + } = useWalletStore(); + + const explorerBaseUrl = useCExpolorerBaseUrl(); + + const translations = useMemo[0]['translations']>( + () => ({ + txDetails: { + title: t('core.ProposalProcedure.txDetails.title'), + txType: t('core.ProposalProcedure.txDetails.txType'), + deposit: t('core.ProposalProcedure.txDetails.deposit'), + rewardAccount: t('core.ProposalProcedure.txDetails.rewardAccount') + }, + procedure: { + title: t('core.ProposalProcedure.procedure.title'), + anchor: { + url: t('core.ProposalProcedure.procedure.anchor.url'), + hash: t('core.ProposalProcedure.procedure.anchor.hash') + } + }, + constitution: { + title: t('core.ProposalProcedure.governanceAction.newConstitutionAction.constitution.title'), + anchor: { + dataHash: t('core.ProposalProcedure.governanceAction.newConstitutionAction.constitution.anchor.dataHash'), + url: t('core.ProposalProcedure.governanceAction.newConstitutionAction.constitution.anchor.url') + }, + scriptHash: t('core.ProposalProcedure.governanceAction.newConstitutionAction.constitution.scriptHash') + }, + actionId: { + title: t('core.ProposalProcedure.governanceAction.actionId.title'), + index: t('core.ProposalProcedure.governanceAction.actionId.index'), + txId: t('core.ProposalProcedure.governanceAction.actionId.txId') + } + }), + [t] + ); + + const { governanceActionId, constitution } = governanceAction; + + const data: Parameters[0]['data'] = { + txDetails: { + txType: t('core.ProposalProcedure.governanceAction.newConstitutionAction.title'), + deposit: `${Wallet.util.lovelacesToAdaString(deposit.toString())} ${cardanoCoin.symbol}`, + rewardAccount + }, + procedure: { + anchor: { + url: anchor.url, + hash: anchor.dataHash, + ...(explorerBaseUrl && { txHashUrl: `${explorerBaseUrl}/${anchor.dataHash}` }) + } + }, + ...(governanceActionId && { + actionId: { + index: governanceActionId.actionIndex.toString(), + id: governanceActionId.id || '' + } + }), + constitution: { + anchor: { + dataHash: constitution.anchor.dataHash.toString(), + url: constitution.anchor.url.toString() + }, + ...(constitution.scriptHash && { scriptHash: constitution.scriptHash.toString() }) + } + }; + + return ( + + ); +}; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/NoConfidenceActionContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/NoConfidenceActionContainer.tsx new file mode 100644 index 0000000000..7605b54424 --- /dev/null +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/NoConfidenceActionContainer.tsx @@ -0,0 +1,81 @@ +import React, { useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Wallet } from '@lace/cardano'; +import { NoConfidenceAction } from '@lace/core'; +import { useWalletStore } from '@src/stores'; +import { SignTxData } from '../types'; +import { useCExpolorerBaseUrl } from '../hooks'; + +interface Props { + dappInfo: SignTxData['dappInfo']; + governanceAction: Wallet.Cardano.NoConfidence; + deposit: Wallet.Cardano.ProposalProcedure['deposit']; + rewardAccount: Wallet.Cardano.ProposalProcedure['rewardAccount']; + anchor: Wallet.Cardano.ProposalProcedure['anchor']; + errorMessage?: string; +} + +export const NoConfidenceActionContainer = ({ + dappInfo, + governanceAction, + deposit, + rewardAccount, + anchor, + errorMessage +}: Props): React.ReactElement => { + const { t } = useTranslation(); + const { + walletUI: { cardanoCoin } + } = useWalletStore(); + + const explorerBaseUrl = useCExpolorerBaseUrl(); + + const translations = useMemo[0]['translations']>( + () => ({ + txDetails: { + title: t('core.ProposalProcedure.txDetails.title'), + txType: t('core.ProposalProcedure.txDetails.txType'), + deposit: t('core.ProposalProcedure.txDetails.deposit'), + rewardAccount: t('core.ProposalProcedure.txDetails.rewardAccount') + }, + procedure: { + title: t('core.ProposalProcedure.procedure.title'), + anchor: { + url: t('core.ProposalProcedure.procedure.anchor.url'), + hash: t('core.ProposalProcedure.procedure.anchor.hash') + } + }, + actionId: { + title: t('core.ProposalProcedure.governanceAction.actionId.title'), + index: t('core.ProposalProcedure.governanceAction.actionId.index'), + txId: t('core.ProposalProcedure.governanceAction.actionId.txId') + } + }), + [t] + ); + + const { governanceActionId } = governanceAction; + + const data: Parameters[0]['data'] = { + txDetails: { + txType: t('core.ProposalProcedure.governanceAction.noConfidenceAction.title'), + deposit: `${Wallet.util.lovelacesToAdaString(deposit.toString())} ${cardanoCoin.symbol}`, + rewardAccount + }, + procedure: { + anchor: { + url: anchor.url, + hash: anchor.dataHash, + ...(explorerBaseUrl && { txHashUrl: `${explorerBaseUrl}/${anchor.dataHash}` }) + } + }, + ...(governanceActionId && { + actionId: { + index: governanceActionId.actionIndex.toString(), + id: governanceActionId.id || '' + } + }) + }; + + return ; +}; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/ParameterChangeActionContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/ParameterChangeActionContainer.tsx new file mode 100644 index 0000000000..31264a6be2 --- /dev/null +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/ParameterChangeActionContainer.tsx @@ -0,0 +1,341 @@ +/* eslint-disable unicorn/no-array-reduce */ +import React, { useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { formatPercentages } from '@lace/common'; +import { Wallet } from '@lace/cardano'; +import { ParameterChangeAction } from '@lace/core'; +import { useWalletStore } from '@src/stores'; +import { SignTxData } from '../types'; +import { useCExpolorerBaseUrl } from '../hooks'; + +interface Props { + dappInfo: SignTxData['dappInfo']; + governanceAction: Wallet.Cardano.ParameterChangeAction; + deposit: Wallet.Cardano.ProposalProcedure['deposit']; + rewardAccount: Wallet.Cardano.ProposalProcedure['rewardAccount']; + anchor: Wallet.Cardano.ProposalProcedure['anchor']; + errorMessage?: string; +} + +export const ParameterChangeActionContainer = ({ + dappInfo, + governanceAction, + deposit, + rewardAccount, + anchor, + errorMessage +}: Props): React.ReactElement => { + const { t } = useTranslation(); + const { + walletUI: { cardanoCoin } + } = useWalletStore(); + + const explorerBaseUrl = useCExpolorerBaseUrl(); + + const translations = useMemo[0]['translations']>( + () => ({ + txDetails: { + title: t('core.ProposalProcedure.txDetails.title'), + txType: t('core.ProposalProcedure.txDetails.txType'), + deposit: t('core.ProposalProcedure.txDetails.deposit'), + rewardAccount: t('core.ProposalProcedure.txDetails.rewardAccount') + }, + anchor: { + url: t('core.ProposalProcedure.procedure.anchor.url'), + hash: t('core.ProposalProcedure.procedure.anchor.hash') + }, + memory: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.memory'), + step: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.step'), + networkGroup: { + title: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.title'), + maxBBSize: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.maxBBSize'), + maxTxSize: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.maxTxSize'), + maxBHSize: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.maxBHSize'), + maxValSize: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.maxValSize'), + maxTxExUnits: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.maxTxExUnits'), + maxBlockExUnits: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.maxBlockExUnits'), + maxCollateralInputs: t( + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.maxCollateralInputs' + ), + coinsByUTXOByte: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.coinsByUTXOByte'), + tooltip: { + maxBBSize: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.tooltip.maxBBSize'), + maxTxSize: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.tooltip.maxTxSize'), + maxBHSize: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.tooltip.maxBHSize'), + maxValSize: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.tooltip.maxValSize'), + maxTxExUnits: t( + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.tooltip.maxTxExUnits' + ), + maxBlockExUnits: t( + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.tooltip.maxBlockExUnits' + ), + maxCollateralInputs: t( + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.tooltip.maxCollateralInputs' + ), + coinsByUTXOByte: t( + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.tooltip.coinsByUTXOByte' + ) + } + }, + economicGroup: { + title: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.title'), + minFeeA: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.minFeeA'), + minFeeB: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.minFeeB'), + keyDeposit: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.keyDeposit'), + poolDeposit: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.poolDeposit'), + rho: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.rho'), + tau: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.tau'), + minPoolCost: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.minPoolCost'), + coinsPerUTxOByte: t( + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.coinsPerUTxOByte' + ), + prices: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.prices'), + tooltip: { + minFeeA: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.tooltip.minFeeA'), + minFeeB: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.tooltip.minFeeB'), + keyDeposit: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.tooltip.keyDeposit'), + poolDeposit: t( + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.tooltip.poolDeposit' + ), + rho: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.tooltip.rho'), + tau: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.tooltip.tau'), + minPoolCost: t( + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.tooltip.minPoolCost' + ), + coinsPerUTxOByte: t( + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.tooltip.coinsPerUTxOByte' + ), + prices: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.tooltip.prices') + } + }, + technicalGroup: { + title: t('core.ProposalProcedure.governanceAction.technicalGroup.title'), + a0: t('core.ProposalProcedure.governanceAction.technicalGroup.a0'), + eMax: t('core.ProposalProcedure.governanceAction.technicalGroup.eMax'), + nOpt: t('core.ProposalProcedure.governanceAction.technicalGroup.nOpt'), + costModels: t('core.ProposalProcedure.governanceAction.technicalGroup.costModels'), + collateralPercentage: t('core.ProposalProcedure.governanceAction.technicalGroup.collateralPercentage'), + tooltip: { + a0: t('core.ProposalProcedure.governanceAction.technicalGroup.tooltip.a0'), + eMax: t('core.ProposalProcedure.governanceAction.technicalGroup.tooltip.eMax'), + nOpt: t('core.ProposalProcedure.governanceAction.technicalGroup.tooltip.nOpt'), + costModels: t('core.ProposalProcedure.governanceAction.technicalGroup.tooltip.costModels'), + collateralPercentage: t('core.ProposalProcedure.governanceAction.technicalGroup.tooltip.collateralPercentage') + } + }, + governanceGroup: { + title: t('core.ProposalProcedure.governanceAction.governanceGroup.title'), + govActionLifetime: t('core.ProposalProcedure.governanceAction.governanceGroup.govActionLifetime'), + govActionDeposit: t('core.ProposalProcedure.governanceAction.governanceGroup.govActionDeposit'), + drepDeposit: t('core.ProposalProcedure.governanceAction.governanceGroup.drepDeposit'), + drepActivity: t('core.ProposalProcedure.governanceAction.governanceGroup.drepActivity'), + ccMinSize: t('core.ProposalProcedure.governanceAction.governanceGroup.ccMinSize'), + ccMaxTermLength: t('core.ProposalProcedure.governanceAction.governanceGroup.ccMaxTermLength'), + dRepVotingThresholds: { + title: t('core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.title'), + motionNoConfidence: t( + 'core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.motionNoConfidence' + ), + committeeNormal: t( + 'core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.committeeNormal' + ), + committeeNoConfidence: t( + 'core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.committeeNoConfidence' + ), + updateConstitution: t( + 'core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.updateConstitution' + ), + hardForkInitiation: t( + 'core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.hardForkInitiation' + ), + ppNetworkGroup: t( + 'core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.ppNetworkGroup' + ), + ppEconomicGroup: t( + 'core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.ppEconomicGroup' + ), + ppTechnicalGroup: t( + 'core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.ppTechnicalGroup' + ), + ppGovernanceGroup: t( + 'core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.ppGovernanceGroup' + ), + treasuryWithdrawal: t( + 'core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.treasuryWithdrawal' + ) + }, + tooltip: { + govActionLifetime: t('core.ProposalProcedure.governanceAction.governanceGroup.tooltip.govActionLifetime'), + govActionDeposit: t('core.ProposalProcedure.governanceAction.governanceGroup.tooltip.govActionDeposit'), + drepDeposit: t('core.ProposalProcedure.governanceAction.governanceGroup.tooltip.drepDeposit'), + drepActivity: t('core.ProposalProcedure.governanceAction.governanceGroup.tooltip.drepActivity'), + ccMinSize: t('core.ProposalProcedure.governanceAction.governanceGroup.tooltip.ccMinSize'), + ccMaxTermLength: t('core.ProposalProcedure.governanceAction.governanceGroup.tooltip.ccMaxTermLength'), + dRepVotingThresholds: { + title: t('core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.title'), + motionNoConfidence: t( + 'core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.motionNoConfidence' + ), + committeeNormal: t( + 'core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.committeeNormal' + ), + committeeNoConfidence: t( + 'core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.committeeNoConfidence' + ), + updateConstitution: t( + 'core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.updateConstitution' + ), + hardForkInitiation: t( + 'core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.hardForkInitiation' + ), + ppNetworkGroup: t( + 'core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.ppNetworkGroup' + ), + ppEconomicGroup: t( + 'core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.ppEconomicGroup' + ), + ppTechnicalGroup: t( + 'core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.ppTechnicalGroup' + ), + ppGovernanceGroup: t( + 'core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.ppGovernanceGroup' + ), + treasuryWithdrawal: t( + 'core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.treasuryWithdrawal' + ) + } + } + } + }), + [t] + ); + + const { + protocolParamUpdate: { + maxBlockBodySize, + maxTxSize, + maxBlockHeaderSize, + maxValueSize, + maxExecutionUnitsPerTransaction, + maxExecutionUnitsPerBlock, + maxCollateralInputs, + stakeKeyDeposit, + poolDeposit, + minFeeCoefficient, + minFeeConstant, + treasuryExpansion, + monetaryExpansion, + minPoolCost, + coinsPerUtxoByte, + prices, + poolInfluence, + poolRetirementEpochBound, + desiredNumberOfPools, + costModels, + collateralPercentage, + governanceActionDeposit, + dRepDeposit, + governanceActionValidityPeriod, + dRepInactivityPeriod, + minCommitteeSize, + committeeTermLimit, + dRepVotingThresholds: { + motionNoConfidence, + committeeNormal, + commiteeNoConfidence, + updateConstitution, + hardForkInitiation, + ppNetworkGroup, + ppEconomicGroup, + ppTechnicalGroup, + ppGovernanceGroup, + treasuryWithdrawal + } + } + } = governanceAction; + + const data: Parameters[0]['data'] = { + txDetails: { + txType: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.title'), + deposit: `${Wallet.util.lovelacesToAdaString(deposit.toString())} ${cardanoCoin.symbol}`, + rewardAccount + }, + anchor: { + url: anchor.url, + hash: anchor.dataHash, + ...(explorerBaseUrl && { txHashUrl: `${explorerBaseUrl}/${anchor.dataHash}` }) + }, + protocolParamUpdate: { + maxTxExUnits: { + memory: maxExecutionUnitsPerTransaction.memory.toString(), + step: maxExecutionUnitsPerTransaction.steps.toString() + }, + maxBlockExUnits: { + memory: maxExecutionUnitsPerBlock.memory.toString(), + step: maxExecutionUnitsPerBlock.steps.toString() + }, + networkGroup: { + maxBBSize: maxBlockBodySize.toString(), + maxTxSize: maxTxSize.toString(), + maxBHSize: maxBlockHeaderSize.toString(), + maxValSize: maxValueSize.toString(), + maxCollateralInputs: maxCollateralInputs.toString() + }, + economicGroup: { + minFeeA: minFeeCoefficient.toString(), + minFeeB: minFeeConstant.toString(), + keyDeposit: stakeKeyDeposit.toString(), + poolDeposit: poolDeposit.toString(), + rho: monetaryExpansion, + tau: treasuryExpansion, + minPoolCost: minPoolCost.toString(), + coinsPerUTxOByte: coinsPerUtxoByte.toString(), + price: { + memory: prices.memory.toString(), + step: prices.steps.toString() + } + }, + technicalGroup: { + a0: poolInfluence, + eMax: poolRetirementEpochBound.toString(), + nOpt: desiredNumberOfPools.toString(), + // TODO: review cost model syntax/display + costModels: { + PlutusV1: Object.entries(costModels.get(Wallet.Cardano.PlutusLanguageVersion.V1)).reduce( + (acc, cur) => ({ ...acc, [cur[0]]: cur[1] }), + {} + ), + PlutusV2: Object.entries(costModels.get(Wallet.Cardano.PlutusLanguageVersion.V2)).reduce( + (acc, cur) => ({ ...acc, [cur[0]]: cur[1] }), + {} + ) + }, + collateralPercentage: collateralPercentage.toString() + }, + governanceGroup: { + govActionLifetime: governanceActionValidityPeriod.toString(), + govActionDeposit: governanceActionDeposit.toString(), + drepDeposit: dRepDeposit.toString(), + drepActivity: dRepInactivityPeriod.toString(), + ccMinSize: minCommitteeSize.toString(), + ccMaxTermLength: committeeTermLimit.toString(), + dRepVotingThresholds: { + motionNoConfidence: formatPercentages(motionNoConfidence.numerator / motionNoConfidence.denominator), + committeeNormal: formatPercentages(committeeNormal.numerator / committeeNormal.denominator), + committeeNoConfidence: formatPercentages(commiteeNoConfidence.numerator / commiteeNoConfidence.denominator), + updateToConstitution: formatPercentages(updateConstitution.numerator / updateConstitution.denominator), + hardForkInitiation: formatPercentages(hardForkInitiation.numerator / hardForkInitiation.denominator), + ppNetworkGroup: formatPercentages(ppNetworkGroup.numerator / ppNetworkGroup.denominator), + ppEconomicGroup: formatPercentages(ppEconomicGroup.numerator / ppEconomicGroup.denominator), + ppTechnicalGroup: formatPercentages(ppTechnicalGroup.numerator / ppTechnicalGroup.denominator), + ppGovGroup: formatPercentages(ppGovernanceGroup.numerator / ppGovernanceGroup.denominator), + treasuryWithdrawal: formatPercentages(treasuryWithdrawal.numerator / treasuryWithdrawal.denominator) + } + } + } + }; + + return ( + + ); +}; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/TreasuryWithdrawalsActionContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/TreasuryWithdrawalsActionContainer.tsx new file mode 100644 index 0000000000..6cb2d0d385 --- /dev/null +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/TreasuryWithdrawalsActionContainer.tsx @@ -0,0 +1,91 @@ +import React, { useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Wallet } from '@lace/cardano'; +import { TreasuryWithdrawalsAction } from '@lace/core'; +import { useWalletStore } from '@src/stores'; +import { SignTxData } from '../types'; +import { useCExpolorerBaseUrl } from '../hooks'; + +interface Props { + dappInfo: SignTxData['dappInfo']; + governanceAction: Wallet.Cardano.TreasuryWithdrawalsAction; + deposit: Wallet.Cardano.ProposalProcedure['deposit']; + rewardAccount: Wallet.Cardano.ProposalProcedure['rewardAccount']; + anchor: Wallet.Cardano.ProposalProcedure['anchor']; + errorMessage?: string; +} + +export const TreasuryWithdrawalsActionContainer = ({ + dappInfo, + governanceAction, + deposit, + rewardAccount, + anchor, + errorMessage +}: Props): React.ReactElement => { + const { t } = useTranslation(); + const { + walletUI: { cardanoCoin } + } = useWalletStore(); + + const explorerBaseUrl = useCExpolorerBaseUrl(); + + const translations = useMemo[0]['translations']>( + () => ({ + txDetails: { + title: t('core.ProposalProcedure.txDetails.title'), + txType: t('core.ProposalProcedure.txDetails.txType'), + deposit: t('core.ProposalProcedure.txDetails.deposit'), + rewardAccount: t('core.ProposalProcedure.txDetails.rewardAccount') + }, + procedure: { + title: t('core.ProposalProcedure.procedure.title'), + anchor: { + url: t('core.ProposalProcedure.procedure.anchor.url'), + hash: t('core.ProposalProcedure.procedure.anchor.hash') + } + }, + actionId: { + title: t('core.ProposalProcedure.governanceAction.actionId.title'), + index: t('core.ProposalProcedure.governanceAction.actionId.index'), + txId: t('core.ProposalProcedure.governanceAction.actionId.txId') + }, + withdrawals: { + title: t('core.ProposalProcedure.governanceAction.treasuryWithdrawals.title'), + rewardAccount: t('core.ProposalProcedure.governanceAction.treasuryWithdrawals.withdrawals.rewardAccount'), + lovelace: t('core.ProposalProcedure.governanceAction.treasuryWithdrawals.withdrawals.lovelace') + } + }), + [t] + ); + + const { withdrawals } = governanceAction; + + const data: Parameters[0]['data'] = { + txDetails: { + txType: t('core.ProposalProcedure.governanceAction.treasuryWithdrawals.title'), + deposit: `${Wallet.util.lovelacesToAdaString(deposit.toString())} ${cardanoCoin.symbol}`, + rewardAccount + }, + procedure: { + anchor: { + url: anchor.url, + hash: anchor.dataHash, + ...(explorerBaseUrl && { txHashUrl: `${explorerBaseUrl}/${anchor.dataHash}` }) + } + }, + withdrawals: [...withdrawals].map((withdrawal) => ({ + rewardAccount: withdrawal.rewardAccount.toString(), + lovelace: `${Wallet.util.lovelacesToAdaString(withdrawal.coin.toString())} ${cardanoCoin.symbol}` + })) + }; + + return ( + + ); +}; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/UpdateCommitteeActionContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/UpdateCommitteeActionContainer.tsx new file mode 100644 index 0000000000..0a01112d1f --- /dev/null +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/UpdateCommitteeActionContainer.tsx @@ -0,0 +1,107 @@ +import React, { useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Wallet } from '@lace/cardano'; +import { UpdateCommitteeAction } from '@lace/core'; +import { useWalletStore } from '@src/stores'; +import { SignTxData } from '../types'; +import { useCExpolorerBaseUrl } from '../hooks'; + +interface Props { + dappInfo: SignTxData['dappInfo']; + governanceAction: Wallet.Cardano.UpdateCommittee; + deposit: Wallet.Cardano.ProposalProcedure['deposit']; + rewardAccount: Wallet.Cardano.ProposalProcedure['rewardAccount']; + anchor: Wallet.Cardano.ProposalProcedure['anchor']; + errorMessage?: string; +} + +export const UpdateCommitteeActionContainer = ({ + dappInfo, + governanceAction, + deposit, + rewardAccount, + anchor, + errorMessage +}: Props): React.ReactElement => { + const { t } = useTranslation(); + const { + walletUI: { cardanoCoin } + } = useWalletStore(); + + const explorerBaseUrl = useCExpolorerBaseUrl(); + + const translations = useMemo[0]['translations']>( + () => ({ + txDetails: { + title: t('core.ProposalProcedure.txDetails.title'), + txType: t('core.ProposalProcedure.txDetails.txType'), + deposit: t('core.ProposalProcedure.txDetails.deposit'), + rewardAccount: t('core.ProposalProcedure.txDetails.rewardAccount') + }, + procedure: { + title: t('core.ProposalProcedure.procedure.title'), + anchor: { + url: t('core.ProposalProcedure.procedure.anchor.url'), + hash: t('core.ProposalProcedure.procedure.anchor.hash') + } + }, + actionId: { + title: t('core.ProposalProcedure.governanceAction.actionId.title'), + index: t('core.ProposalProcedure.governanceAction.actionId.index'), + txId: t('core.ProposalProcedure.governanceAction.actionId.txId') + }, + membersToBeAdded: { + title: t('core.ProposalProcedure.governanceAction.updateCommitteeAction.membersToBeAdded.title'), + coldCredential: { + hash: t('core.ProposalProcedure.governanceAction.updateCommitteeAction.membersToBeAdded.coldCredential.hash'), + epoch: t( + 'core.ProposalProcedure.governanceAction.updateCommitteeAction.membersToBeAdded.coldCredential.epoch' + ) + } + }, + membersToBeRemoved: { + title: t('core.ProposalProcedure.governanceAction.updateCommitteeAction.membersToBeRemoved.title'), + hash: t('core.ProposalProcedure.governanceAction.updateCommitteeAction.membersToBeRemoved.hash') + } + }), + [t] + ); + + const { membersToBeAdded, membersToBeRemoved, governanceActionId } = governanceAction; + + const data: Parameters[0]['data'] = { + txDetails: { + txType: t('core.ProposalProcedure.governanceAction.updateCommitteeAction.title'), + deposit: `${Wallet.util.lovelacesToAdaString(deposit.toString())} ${cardanoCoin.symbol}`, + rewardAccount + }, + procedure: { + anchor: { + url: anchor.url, + hash: anchor.dataHash, + ...(explorerBaseUrl && { txHashUrl: `${explorerBaseUrl}/${anchor.dataHash}` }) + } + }, + ...(governanceActionId && { + actionId: { + index: governanceActionId.actionIndex.toString(), + id: governanceActionId.id || '' + } + }), + membersToBeAdded: [...membersToBeAdded].map(({ coldCredential: { hash }, epoch }) => ({ + coldCredential: { + // TODO: use bech32 in future revision + hash: hash.toString() + }, + epoch: epoch.toString() + })), + membersToBeRemoved: [...membersToBeRemoved].map(({ hash }) => ({ + // TODO: use bech32 in future revision + hash: hash.toString() + })) + }; + + return ( + + ); +}; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/HardForkInitiationActionContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/HardForkInitiationActionContainer.test.tsx new file mode 100644 index 0000000000..3ff6302ac4 --- /dev/null +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/HardForkInitiationActionContainer.test.tsx @@ -0,0 +1,175 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable no-magic-numbers */ +/* eslint-disable import/imports-first */ +const cardanoCoinMock = { + symbol: 'cardanoCoinMockSymbol' +}; +const mockUseWalletStore = jest.fn(() => ({ + walletUI: { cardanoCoin: cardanoCoinMock }, + walletInfo: {} +})); +const t = jest.fn().mockImplementation((res) => res); +const mockUseTranslation = jest.fn(() => ({ t })); +const mockHardForkInitiationAction = jest.fn(() => ); +const mockLovelacesToAdaString = jest.fn((val) => val); +const mockedCExpolorerBaseUrl = 'mockedCExpolorerBaseUrl'; +const mockUseCExpolorerBaseUrl = jest.fn(() => mockedCExpolorerBaseUrl); +import { Wallet } from '@lace/cardano'; +import * as React from 'react'; +import { cleanup, render } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import { act } from 'react-dom/test-utils'; +import { HardForkInitiationActionContainer } from '../HardForkInitiationActionContainer'; +import { getWrapper } from '../../testing.utils'; + +jest.mock('react-i18next', () => { + const original = jest.requireActual('react-i18next'); + return { + __esModule: true, + ...original, + useTranslation: mockUseTranslation + }; +}); + +jest.mock('@lace/core', () => { + const original = jest.requireActual('@lace/core'); + return { + __esModule: true, + ...original, + HardForkInitiationAction: mockHardForkInitiationAction + }; +}); + +jest.mock('@src/stores', () => ({ + ...jest.requireActual('@src/stores'), + useWalletStore: mockUseWalletStore +})); + +jest.mock('../../hooks', () => { + const original = jest.requireActual('../../hooks'); + return { + __esModule: true, + ...original, + useCExpolorerBaseUrl: mockUseCExpolorerBaseUrl + }; +}); + +jest.mock('@lace/cardano', () => { + const actual = jest.requireActual('@lace/cardano'); + return { + __esModule: true, + ...actual, + Wallet: { + ...actual.Wallet, + util: { + ...actual.Wallet.util, + lovelacesToAdaString: mockLovelacesToAdaString + } + } + }; +}); + +const dappInfo = { + name: 'dappName', + logo: 'dappLogo', + url: 'dappUrl' +}; +const errorMessage = 'errorMessage'; +const deposit = BigInt('10000'); +const rewardAccount = Wallet.Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj'); +const anchor = { + url: 'anchorUrl', + dataHash: Wallet.Crypto.Hash32ByteBase16(Buffer.from('anchorDataHashanchorDataHashanch').toString('hex')) +}; + +const hardForkInitiationAction = { + protocolVersion: { + major: 123, + minor: 234, + patch: 456 + }, + governanceActionId: { + actionIndex: 123, + id: Wallet.Cardano.TransactionId('724a0a88b9470a714fc5bf84daf5851fa259a9b89e1a5453f6f5cd6595ad9821') + }, + __typename: Wallet.Cardano.GovernanceActionType.hard_fork_initiation_action +} as Wallet.Cardano.HardForkInitiationAction; + +describe('Testing ProposalProceduresContainer component', () => { + afterEach(() => { + jest.clearAllMocks(); + cleanup(); + }); + + test('should render HardForkInitiationAction component with proper props', async () => { + let queryByTestId: any; + + await act(async () => { + ({ queryByTestId } = render( + , + { + wrapper: getWrapper() + } + )); + }); + + expect(queryByTestId('HardForkInitiationAction')).toBeInTheDocument(); + expect(mockHardForkInitiationAction).toHaveBeenLastCalledWith( + { + dappInfo, + data: { + txDetails: { + txType: t('core.ProposalProcedure.governanceAction.hardForkInitiation.title'), + deposit: `${deposit.toString()} ${cardanoCoinMock.symbol}`, + rewardAccount + }, + procedure: { + anchor: { + url: anchor.url, + hash: anchor.dataHash, + txHashUrl: `${mockedCExpolorerBaseUrl}/${anchor.dataHash}` + } + }, + protocolVersion: { + major: hardForkInitiationAction.protocolVersion.major.toString(), + minor: hardForkInitiationAction.protocolVersion.minor.toString(), + patch: hardForkInitiationAction.protocolVersion.patch?.toString() + }, + actionId: { + index: hardForkInitiationAction.governanceActionId.actionIndex.toString(), + id: hardForkInitiationAction.governanceActionId.id || '' + } + }, + translations: { + txDetails: { + title: t('core.ProposalProcedure.txDetails.title'), + txType: t('core.ProposalProcedure.txDetails.txType'), + deposit: t('core.ProposalProcedure.txDetails.deposit'), + rewardAccount: t('core.ProposalProcedure.txDetails.rewardAccount') + }, + procedure: { + title: t('core.ProposalProcedure.procedure.title'), + anchor: { + url: t('core.ProposalProcedure.procedure.anchor.url'), + hash: t('core.ProposalProcedure.procedure.anchor.hash') + } + }, + protocolVersion: { + major: t('core.ProposalProcedure.governanceAction.hardForkInitiation.protocolVersion.major'), + minor: t('core.ProposalProcedure.governanceAction.hardForkInitiation.protocolVersion.minor'), + patch: t('core.ProposalProcedure.governanceAction.hardForkInitiation.protocolVersion.patch') + }, + actionId: { + title: t('core.ProposalProcedure.governanceAction.actionId.title'), + index: t('core.ProposalProcedure.governanceAction.actionId.index'), + txId: t('core.ProposalProcedure.governanceAction.actionId.txId') + } + }, + errorMessage + }, + {} + ); + }); +}); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/InfoActionContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/InfoActionContainer.test.tsx new file mode 100644 index 0000000000..e4b11d214c --- /dev/null +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/InfoActionContainer.test.tsx @@ -0,0 +1,115 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable no-magic-numbers */ +/* eslint-disable import/imports-first */ +const t = jest.fn().mockImplementation((res) => res); +const mockUseTranslation = jest.fn(() => ({ t })); +const mockInfoAction = jest.fn(() => ); +const mockedCExpolorerBaseUrl = 'mockedCExpolorerBaseUrl'; +const mockUseCExpolorerBaseUrl = jest.fn(() => mockedCExpolorerBaseUrl); +import { Wallet } from '@lace/cardano'; +import * as React from 'react'; +import { cleanup, render } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import { act } from 'react-dom/test-utils'; +import { InfoActionContainer } from '../InfoActionContainer'; +import { getWrapper } from '../../testing.utils'; + +jest.mock('react-i18next', () => { + const original = jest.requireActual('react-i18next'); + return { + __esModule: true, + ...original, + useTranslation: mockUseTranslation + }; +}); + +jest.mock('@lace/core', () => { + const original = jest.requireActual('@lace/core'); + return { + __esModule: true, + ...original, + InfoAction: mockInfoAction + }; +}); + +jest.mock('../../hooks', () => { + const original = jest.requireActual('../../hooks'); + return { + __esModule: true, + ...original, + useCExpolorerBaseUrl: mockUseCExpolorerBaseUrl + }; +}); + +const dappInfo = { + name: 'dappName', + logo: 'dappLogo', + url: 'dappUrl' +}; +const errorMessage = 'errorMessage'; +const deposit = BigInt('10000'); +const rewardAccount = Wallet.Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj'); +const anchor = { + url: 'anchorUrl', + dataHash: Wallet.Crypto.Hash32ByteBase16(Buffer.from('anchorDataHashanchorDataHashanch').toString('hex')) +}; + +const infoAction = { + __typename: Wallet.Cardano.GovernanceActionType.info_action +} as Wallet.Cardano.InfoAction; + +describe('Testing ProposalProceduresContainer component', () => { + afterEach(() => { + jest.clearAllMocks(); + cleanup(); + }); + + test('should render InfoAction component with proper props', async () => { + let queryByTestId: any; + + await act(async () => { + ({ queryByTestId } = render( + , + { + wrapper: getWrapper() + } + )); + }); + + expect(queryByTestId('InfoAction')).toBeInTheDocument(); + expect(mockInfoAction).toHaveBeenLastCalledWith( + { + dappInfo, + data: { + txDetails: { + txType: t('core.ProposalProcedure.governanceAction.infoAction.title') + }, + procedure: { + anchor: { + url: anchor.url, + hash: anchor.dataHash, + txHashUrl: `${mockedCExpolorerBaseUrl}/${anchor.dataHash}` + } + } + }, + translations: { + txDetails: { + title: t('core.ProposalProcedure.txDetails.title'), + txType: t('core.ProposalProcedure.txDetails.txType') + }, + procedure: { + title: t('core.ProposalProcedure.procedure.title'), + anchor: { + url: t('core.ProposalProcedure.procedure.anchor.url'), + hash: t('core.ProposalProcedure.procedure.anchor.hash') + } + } + }, + errorMessage + }, + {} + ); + }); +}); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/NewConstitutionActionContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/NewConstitutionActionContainer.test.tsx new file mode 100644 index 0000000000..468233c9b3 --- /dev/null +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/NewConstitutionActionContainer.test.tsx @@ -0,0 +1,179 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable no-magic-numbers */ +/* eslint-disable import/imports-first */ +const cardanoCoinMock = { + symbol: 'cardanoCoinMockSymbol' +}; +const mockUseWalletStore = jest.fn(() => ({ + walletUI: { cardanoCoin: cardanoCoinMock }, + walletInfo: {} +})); +const t = jest.fn().mockImplementation((res) => res); +const mockUseTranslation = jest.fn(() => ({ t })); +const mockNewConstitutionAction = jest.fn(() => ); +const mockLovelacesToAdaString = jest.fn((val) => val); +const mockedCExpolorerBaseUrl = 'mockedCExpolorerBaseUrl'; +const mockUseCExpolorerBaseUrl = jest.fn(() => mockedCExpolorerBaseUrl); +import { Wallet } from '@lace/cardano'; +import * as React from 'react'; +import { cleanup, render } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import { act } from 'react-dom/test-utils'; +import { NewConstitutionActionContainer } from '../NewConstitutionActionContainer'; +import { getWrapper } from '../../testing.utils'; + +jest.mock('react-i18next', () => { + const original = jest.requireActual('react-i18next'); + return { + __esModule: true, + ...original, + useTranslation: mockUseTranslation + }; +}); + +jest.mock('@lace/core', () => { + const original = jest.requireActual('@lace/core'); + return { + __esModule: true, + ...original, + NewConstitutionAction: mockNewConstitutionAction + }; +}); + +jest.mock('@src/stores', () => ({ + ...jest.requireActual('@src/stores'), + useWalletStore: mockUseWalletStore +})); + +jest.mock('../../hooks', () => { + const original = jest.requireActual('../../hooks'); + return { + __esModule: true, + ...original, + useCExpolorerBaseUrl: mockUseCExpolorerBaseUrl + }; +}); + +jest.mock('@lace/cardano', () => { + const actual = jest.requireActual('@lace/cardano'); + return { + __esModule: true, + ...actual, + Wallet: { + ...actual.Wallet, + util: { + ...actual.Wallet.util, + lovelacesToAdaString: mockLovelacesToAdaString + } + } + }; +}); + +const dappInfo = { + name: 'dappName', + logo: 'dappLogo', + url: 'dappUrl' +}; +const errorMessage = 'errorMessage'; +const deposit = BigInt('10000'); +const rewardAccount = Wallet.Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj'); +const anchor = { + url: 'anchorUrl', + dataHash: Wallet.Crypto.Hash32ByteBase16(Buffer.from('anchorDataHashanchorDataHashanch').toString('hex')) +}; + +const newConstitution = { + __typename: Wallet.Cardano.GovernanceActionType.new_constitution, + constitution: { + anchor, + scriptHash: Wallet.Crypto.Hash28ByteBase16(Buffer.from('newConstitutionscriptHashnew').toString('hex')) + }, + governanceActionId: { + actionIndex: 123, + id: Wallet.Cardano.TransactionId('724a0a88b9470a714fc5bf84daf5851fa259a9b89e1a5453f6f5cd6595ad9821') + } +} as Wallet.Cardano.NewConstitution; + +describe('Testing ProposalProceduresContainer component', () => { + afterEach(() => { + jest.clearAllMocks(); + cleanup(); + }); + + test('should render NewConstitutionAction component with proper props', async () => { + let queryByTestId: any; + + await act(async () => { + ({ queryByTestId } = render( + , + { + wrapper: getWrapper() + } + )); + }); + + expect(queryByTestId('NewConstitutionAction')).toBeInTheDocument(); + expect(mockNewConstitutionAction).toHaveBeenLastCalledWith( + { + dappInfo, + data: { + txDetails: { + txType: t('core.ProposalProcedure.governanceAction.newConstitutionAction.title'), + deposit: `${deposit.toString()} ${cardanoCoinMock.symbol}`, + rewardAccount + }, + procedure: { + anchor: { + url: anchor.url, + hash: anchor.dataHash, + txHashUrl: `${mockedCExpolorerBaseUrl}/${anchor.dataHash}` + } + }, + actionId: { + index: newConstitution.governanceActionId.actionIndex.toString(), + id: newConstitution.governanceActionId.id || '' + }, + constitution: { + anchor: { + dataHash: newConstitution.constitution.anchor.dataHash.toString(), + url: newConstitution.constitution.anchor.url.toString() + }, + scriptHash: newConstitution.constitution.scriptHash.toString() + } + }, + translations: { + txDetails: { + title: t('core.ProposalProcedure.txDetails.title'), + txType: t('core.ProposalProcedure.txDetails.txType'), + deposit: t('core.ProposalProcedure.txDetails.deposit'), + rewardAccount: t('core.ProposalProcedure.txDetails.rewardAccount') + }, + procedure: { + title: t('core.ProposalProcedure.procedure.title'), + anchor: { + url: t('core.ProposalProcedure.procedure.anchor.url'), + hash: t('core.ProposalProcedure.procedure.anchor.hash') + } + }, + constitution: { + title: t('core.ProposalProcedure.governanceAction.newConstitutionAction.constitution.title'), + anchor: { + dataHash: t('core.ProposalProcedure.governanceAction.newConstitutionAction.constitution.anchor.dataHash'), + url: t('core.ProposalProcedure.governanceAction.newConstitutionAction.constitution.anchor.url') + }, + scriptHash: t('core.ProposalProcedure.governanceAction.newConstitutionAction.constitution.scriptHash') + }, + actionId: { + title: t('core.ProposalProcedure.governanceAction.actionId.title'), + index: t('core.ProposalProcedure.governanceAction.actionId.index'), + txId: t('core.ProposalProcedure.governanceAction.actionId.txId') + } + }, + errorMessage + }, + {} + ); + }); +}); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/NoConfidenceActionContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/NoConfidenceActionContainer.test.tsx new file mode 100644 index 0000000000..5d0d1ee411 --- /dev/null +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/NoConfidenceActionContainer.test.tsx @@ -0,0 +1,160 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable no-magic-numbers */ +/* eslint-disable import/imports-first */ +const cardanoCoinMock = { + symbol: 'cardanoCoinMockSymbol' +}; +const mockUseWalletStore = jest.fn(() => ({ + walletUI: { cardanoCoin: cardanoCoinMock }, + walletInfo: {} +})); +const t = jest.fn().mockImplementation((res) => res); +const mockUseTranslation = jest.fn(() => ({ t })); +const mockNoConfidenceAction = jest.fn(() => ); +const mockLovelacesToAdaString = jest.fn((val) => val); +const mockedCExpolorerBaseUrl = 'mockedCExpolorerBaseUrl'; +const mockUseCExpolorerBaseUrl = jest.fn(() => mockedCExpolorerBaseUrl); +import { Wallet } from '@lace/cardano'; +import * as React from 'react'; +import { cleanup, render } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import { act } from 'react-dom/test-utils'; +import { NoConfidenceActionContainer } from '../NoConfidenceActionContainer'; +import { getWrapper } from '../../testing.utils'; + +jest.mock('react-i18next', () => { + const original = jest.requireActual('react-i18next'); + return { + __esModule: true, + ...original, + useTranslation: mockUseTranslation + }; +}); + +jest.mock('@lace/core', () => { + const original = jest.requireActual('@lace/core'); + return { + __esModule: true, + ...original, + NoConfidenceAction: mockNoConfidenceAction + }; +}); + +jest.mock('@src/stores', () => ({ + ...jest.requireActual('@src/stores'), + useWalletStore: mockUseWalletStore +})); + +jest.mock('../../hooks', () => { + const original = jest.requireActual('../../hooks'); + return { + __esModule: true, + ...original, + useCExpolorerBaseUrl: mockUseCExpolorerBaseUrl + }; +}); + +jest.mock('@lace/cardano', () => { + const actual = jest.requireActual('@lace/cardano'); + return { + __esModule: true, + ...actual, + Wallet: { + ...actual.Wallet, + util: { + ...actual.Wallet.util, + lovelacesToAdaString: mockLovelacesToAdaString + } + } + }; +}); + +const dappInfo = { + name: 'dappName', + logo: 'dappLogo', + url: 'dappUrl' +}; +const errorMessage = 'errorMessage'; +const deposit = BigInt('10000'); +const rewardAccount = Wallet.Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj'); +const anchor = { + url: 'anchorUrl', + dataHash: Wallet.Crypto.Hash32ByteBase16(Buffer.from('anchorDataHashanchorDataHashanch').toString('hex')) +}; + +const noConfidence = { + __typename: Wallet.Cardano.GovernanceActionType.no_confidence, + governanceActionId: { + actionIndex: 123, + id: Wallet.Cardano.TransactionId('724a0a88b9470a714fc5bf84daf5851fa259a9b89e1a5453f6f5cd6595ad9821') + } +} as Wallet.Cardano.NoConfidence; + +describe('Testing ProposalProceduresContainer component', () => { + afterEach(() => { + jest.clearAllMocks(); + cleanup(); + }); + + test('should render NoConfidenceAction component with proper props', async () => { + let queryByTestId: any; + + await act(async () => { + ({ queryByTestId } = render( + , + { + wrapper: getWrapper() + } + )); + }); + + expect(queryByTestId('NoConfidenceAction')).toBeInTheDocument(); + expect(mockNoConfidenceAction).toHaveBeenLastCalledWith( + { + dappInfo, + data: { + txDetails: { + txType: t('core.ProposalProcedure.governanceAction.noConfidenceAction.title'), + deposit: `${deposit.toString()} ${cardanoCoinMock.symbol}`, + rewardAccount + }, + procedure: { + anchor: { + url: anchor.url, + hash: anchor.dataHash, + txHashUrl: `${mockedCExpolorerBaseUrl}/${anchor.dataHash}` + } + }, + actionId: { + index: noConfidence.governanceActionId.actionIndex.toString(), + id: noConfidence.governanceActionId.id || '' + } + }, + translations: { + txDetails: { + title: t('core.ProposalProcedure.txDetails.title'), + txType: t('core.ProposalProcedure.txDetails.txType'), + deposit: t('core.ProposalProcedure.txDetails.deposit'), + rewardAccount: t('core.ProposalProcedure.txDetails.rewardAccount') + }, + procedure: { + title: t('core.ProposalProcedure.procedure.title'), + anchor: { + url: t('core.ProposalProcedure.procedure.anchor.url'), + hash: t('core.ProposalProcedure.procedure.anchor.hash') + } + }, + actionId: { + title: t('core.ProposalProcedure.governanceAction.actionId.title'), + index: t('core.ProposalProcedure.governanceAction.actionId.index'), + txId: t('core.ProposalProcedure.governanceAction.actionId.txId') + } + }, + errorMessage + }, + {} + ); + }); +}); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/ParameterChangeActionContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/ParameterChangeActionContainer.test.tsx new file mode 100644 index 0000000000..b0f46f2293 --- /dev/null +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/ParameterChangeActionContainer.test.tsx @@ -0,0 +1,504 @@ +/* eslint-disable unicorn/no-array-reduce */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable no-magic-numbers */ +/* eslint-disable import/imports-first */ +const cardanoCoinMock = { + symbol: 'cardanoCoinMockSymbol' +}; +const mockUseWalletStore = jest.fn(() => ({ + walletUI: { cardanoCoin: cardanoCoinMock }, + walletInfo: {} +})); +const t = jest.fn().mockImplementation((res) => res); +const mockUseTranslation = jest.fn(() => ({ t })); +const mockParameterChangeAction = jest.fn(() => ); +const mockLovelacesToAdaString = jest.fn((val) => val); +const mockedCExpolorerBaseUrl = 'mockedCExpolorerBaseUrl'; +const mockUseCExpolorerBaseUrl = jest.fn(() => mockedCExpolorerBaseUrl); +import { Wallet } from '@lace/cardano'; +import * as React from 'react'; +import { cleanup, render } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import { act } from 'react-dom/test-utils'; +import { ParameterChangeActionContainer } from '../ParameterChangeActionContainer'; +import { formatPercentages } from '@lace/common'; +import { getWrapper } from '../../testing.utils'; + +jest.mock('react-i18next', () => { + const original = jest.requireActual('react-i18next'); + return { + __esModule: true, + ...original, + useTranslation: mockUseTranslation + }; +}); + +jest.mock('@lace/core', () => { + const original = jest.requireActual('@lace/core'); + return { + __esModule: true, + ...original, + ParameterChangeAction: mockParameterChangeAction + }; +}); + +jest.mock('@src/stores', () => ({ + ...jest.requireActual('@src/stores'), + useWalletStore: mockUseWalletStore +})); + +jest.mock('../../hooks', () => { + const original = jest.requireActual('../../hooks'); + return { + __esModule: true, + ...original, + useCExpolorerBaseUrl: mockUseCExpolorerBaseUrl + }; +}); + +jest.mock('@lace/cardano', () => { + const actual = jest.requireActual('@lace/cardano'); + return { + __esModule: true, + ...actual, + Wallet: { + ...actual.Wallet, + util: { + ...actual.Wallet.util, + lovelacesToAdaString: mockLovelacesToAdaString + } + } + }; +}); + +const dappInfo = { + name: 'dappName', + logo: 'dappLogo', + url: 'dappUrl' +}; +const errorMessage = 'errorMessage'; +const deposit = BigInt('10000'); +const rewardAccount = Wallet.Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj'); +const anchor = { + url: 'anchorUrl', + dataHash: Wallet.Crypto.Hash32ByteBase16(Buffer.from('anchorDataHashanchorDataHashanch').toString('hex')) +}; + +const parameterChangeAction = { + protocolParamUpdate: { + maxBlockBodySize: 1, + maxTxSize: 2, + maxBlockHeaderSize: 3, + maxValueSize: 4, + maxExecutionUnitsPerTransaction: { + memory: 5, + steps: 6 + }, + maxExecutionUnitsPerBlock: { + memory: 7, + steps: 8 + }, + maxCollateralInputs: 9, + stakeKeyDeposit: 10, + poolDeposit: 11, + minFeeCoefficient: 12, + minFeeConstant: 13, + treasuryExpansion: '14', + monetaryExpansion: '15', + minPoolCost: 16, + coinsPerUtxoByte: 17, + prices: { + memory: 18, + steps: 19 + }, + poolInfluence: '20', + poolRetirementEpochBound: 21, + desiredNumberOfPools: 22, + costModels: new Map([ + [0, [23, 24]], + [1, [25, 26]] + ]), + collateralPercentage: 27, + governanceActionDeposit: 28, + dRepDeposit: 29, + governanceActionValidityPeriod: 30, + dRepInactivityPeriod: 31, + minCommitteeSize: 32, + committeeTermLimit: 33, + dRepVotingThresholds: { + motionNoConfidence: { + numerator: 34, + denominator: 35 + }, + committeeNormal: { + numerator: 36, + denominator: 37 + }, + commiteeNoConfidence: { + numerator: 38, + denominator: 39 + }, + updateConstitution: { + numerator: 40, + denominator: 41 + }, + hardForkInitiation: { + numerator: 42, + denominator: 43 + }, + ppNetworkGroup: { + numerator: 44, + denominator: 45 + }, + ppEconomicGroup: { + numerator: 46, + denominator: 47 + }, + ppTechnicalGroup: { + numerator: 48, + denominator: 49 + }, + ppGovernanceGroup: { + numerator: 50, + denominator: 51 + }, + treasuryWithdrawal: { + numerator: 55, + denominator: 56 + } + } + }, + governanceActionId: { + actionIndex: 123, + id: Wallet.Cardano.TransactionId('724a0a88b9470a714fc5bf84daf5851fa259a9b89e1a5453f6f5cd6595ad9821') + }, + __typename: Wallet.Cardano.GovernanceActionType.parameter_change_action +} as Wallet.Cardano.ParameterChangeAction; + +describe('Testing ProposalProceduresContainer component', () => { + afterEach(() => { + jest.clearAllMocks(); + cleanup(); + }); + + test('should render ParameterChangeAction component with proper props', async () => { + let queryByTestId: any; + + await act(async () => { + ({ queryByTestId } = render( + , + { + wrapper: getWrapper() + } + )); + }); + + expect(queryByTestId('ParameterChangeAction')).toBeInTheDocument(); + expect(mockParameterChangeAction).toHaveBeenLastCalledWith( + { + dappInfo, + data: { + txDetails: { + txType: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.title'), + deposit: `${deposit.toString()} ${cardanoCoinMock.symbol}`, + rewardAccount + }, + anchor: { + url: anchor.url, + hash: anchor.dataHash, + txHashUrl: `${mockedCExpolorerBaseUrl}/${anchor.dataHash}` + }, + protocolParamUpdate: { + maxTxExUnits: { + memory: parameterChangeAction.protocolParamUpdate.maxExecutionUnitsPerTransaction.memory.toString(), + step: parameterChangeAction.protocolParamUpdate.maxExecutionUnitsPerTransaction.steps.toString() + }, + maxBlockExUnits: { + memory: parameterChangeAction.protocolParamUpdate.maxExecutionUnitsPerBlock.memory.toString(), + step: parameterChangeAction.protocolParamUpdate.maxExecutionUnitsPerBlock.steps.toString() + }, + networkGroup: { + maxBBSize: parameterChangeAction.protocolParamUpdate.maxBlockBodySize.toString(), + maxTxSize: parameterChangeAction.protocolParamUpdate.maxTxSize.toString(), + maxBHSize: parameterChangeAction.protocolParamUpdate.maxBlockHeaderSize.toString(), + maxValSize: parameterChangeAction.protocolParamUpdate.maxValueSize.toString(), + maxCollateralInputs: parameterChangeAction.protocolParamUpdate.maxCollateralInputs.toString() + }, + economicGroup: { + minFeeA: parameterChangeAction.protocolParamUpdate.minFeeCoefficient.toString(), + minFeeB: parameterChangeAction.protocolParamUpdate.minFeeConstant.toString(), + keyDeposit: parameterChangeAction.protocolParamUpdate.stakeKeyDeposit.toString(), + poolDeposit: parameterChangeAction.protocolParamUpdate.poolDeposit.toString(), + rho: parameterChangeAction.protocolParamUpdate.monetaryExpansion, + tau: parameterChangeAction.protocolParamUpdate.treasuryExpansion, + minPoolCost: parameterChangeAction.protocolParamUpdate.minPoolCost.toString(), + coinsPerUTxOByte: parameterChangeAction.protocolParamUpdate.coinsPerUtxoByte.toString(), + price: { + memory: parameterChangeAction.protocolParamUpdate.prices.memory.toString(), + step: parameterChangeAction.protocolParamUpdate.prices.steps.toString() + } + }, + technicalGroup: { + a0: parameterChangeAction.protocolParamUpdate.poolInfluence, + eMax: parameterChangeAction.protocolParamUpdate.poolRetirementEpochBound.toString(), + nOpt: parameterChangeAction.protocolParamUpdate.desiredNumberOfPools.toString(), + costModels: { + PlutusV1: Object.entries( + parameterChangeAction.protocolParamUpdate.costModels.get(Wallet.Cardano.PlutusLanguageVersion.V1) + ).reduce((acc, cur) => ({ ...acc, [cur[0]]: cur[1] }), {}), + PlutusV2: Object.entries( + parameterChangeAction.protocolParamUpdate.costModels.get(Wallet.Cardano.PlutusLanguageVersion.V2) + ).reduce((acc, cur) => ({ ...acc, [cur[0]]: cur[1] }), {}) + }, + collateralPercentage: parameterChangeAction.protocolParamUpdate.collateralPercentage.toString() + }, + governanceGroup: { + govActionLifetime: parameterChangeAction.protocolParamUpdate.governanceActionValidityPeriod.toString(), + govActionDeposit: parameterChangeAction.protocolParamUpdate.governanceActionDeposit.toString(), + drepDeposit: parameterChangeAction.protocolParamUpdate.dRepDeposit.toString(), + drepActivity: parameterChangeAction.protocolParamUpdate.dRepInactivityPeriod.toString(), + ccMinSize: parameterChangeAction.protocolParamUpdate.minCommitteeSize.toString(), + ccMaxTermLength: parameterChangeAction.protocolParamUpdate.committeeTermLimit.toString(), + dRepVotingThresholds: { + motionNoConfidence: formatPercentages( + parameterChangeAction.protocolParamUpdate.dRepVotingThresholds.motionNoConfidence.numerator / + parameterChangeAction.protocolParamUpdate.dRepVotingThresholds.motionNoConfidence.denominator + ), + committeeNormal: formatPercentages( + parameterChangeAction.protocolParamUpdate.dRepVotingThresholds.committeeNormal.numerator / + parameterChangeAction.protocolParamUpdate.dRepVotingThresholds.committeeNormal.denominator + ), + committeeNoConfidence: formatPercentages( + parameterChangeAction.protocolParamUpdate.dRepVotingThresholds.commiteeNoConfidence.numerator / + parameterChangeAction.protocolParamUpdate.dRepVotingThresholds.commiteeNoConfidence.denominator + ), + updateToConstitution: formatPercentages( + parameterChangeAction.protocolParamUpdate.dRepVotingThresholds.updateConstitution.numerator / + parameterChangeAction.protocolParamUpdate.dRepVotingThresholds.updateConstitution.denominator + ), + hardForkInitiation: formatPercentages( + parameterChangeAction.protocolParamUpdate.dRepVotingThresholds.hardForkInitiation.numerator / + parameterChangeAction.protocolParamUpdate.dRepVotingThresholds.hardForkInitiation.denominator + ), + ppNetworkGroup: formatPercentages( + parameterChangeAction.protocolParamUpdate.dRepVotingThresholds.ppNetworkGroup.numerator / + parameterChangeAction.protocolParamUpdate.dRepVotingThresholds.ppNetworkGroup.denominator + ), + ppEconomicGroup: formatPercentages( + parameterChangeAction.protocolParamUpdate.dRepVotingThresholds.ppEconomicGroup.numerator / + parameterChangeAction.protocolParamUpdate.dRepVotingThresholds.ppEconomicGroup.denominator + ), + ppTechnicalGroup: formatPercentages( + parameterChangeAction.protocolParamUpdate.dRepVotingThresholds.ppTechnicalGroup.numerator / + parameterChangeAction.protocolParamUpdate.dRepVotingThresholds.ppTechnicalGroup.denominator + ), + ppGovGroup: formatPercentages( + parameterChangeAction.protocolParamUpdate.dRepVotingThresholds.ppGovernanceGroup.numerator / + parameterChangeAction.protocolParamUpdate.dRepVotingThresholds.ppGovernanceGroup.denominator + ), + treasuryWithdrawal: formatPercentages( + parameterChangeAction.protocolParamUpdate.dRepVotingThresholds.treasuryWithdrawal.numerator / + parameterChangeAction.protocolParamUpdate.dRepVotingThresholds.treasuryWithdrawal.denominator + ) + } + } + } + }, + translations: { + txDetails: { + title: t('core.ProposalProcedure.txDetails.title'), + txType: t('core.ProposalProcedure.txDetails.txType'), + deposit: t('core.ProposalProcedure.txDetails.deposit'), + rewardAccount: t('core.ProposalProcedure.txDetails.rewardAccount') + }, + anchor: { + url: t('core.ProposalProcedure.procedure.anchor.url'), + hash: t('core.ProposalProcedure.procedure.anchor.hash') + }, + memory: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.memory'), + step: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.step'), + networkGroup: { + title: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.title'), + maxBBSize: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.maxBBSize'), + maxTxSize: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.maxTxSize'), + maxBHSize: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.maxBHSize'), + maxValSize: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.maxValSize'), + maxTxExUnits: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.maxTxExUnits'), + maxBlockExUnits: t( + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.maxBlockExUnits' + ), + maxCollateralInputs: t( + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.maxCollateralInputs' + ), + coinsByUTXOByte: t( + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.coinsByUTXOByte' + ), + tooltip: { + maxBBSize: t( + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.tooltip.maxBBSize' + ), + maxTxSize: t( + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.tooltip.maxTxSize' + ), + maxBHSize: t( + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.tooltip.maxBHSize' + ), + maxValSize: t( + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.tooltip.maxValSize' + ), + maxTxExUnits: t( + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.tooltip.maxTxExUnits' + ), + maxBlockExUnits: t( + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.tooltip.maxBlockExUnits' + ), + maxCollateralInputs: t( + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.tooltip.maxCollateralInputs' + ), + coinsByUTXOByte: t( + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.tooltip.coinsByUTXOByte' + ) + } + }, + economicGroup: { + title: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.title'), + minFeeA: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.minFeeA'), + minFeeB: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.minFeeB'), + keyDeposit: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.keyDeposit'), + poolDeposit: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.poolDeposit'), + rho: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.rho'), + tau: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.tau'), + minPoolCost: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.minPoolCost'), + coinsPerUTxOByte: t( + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.coinsPerUTxOByte' + ), + prices: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.prices'), + tooltip: { + minFeeA: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.tooltip.minFeeA'), + minFeeB: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.tooltip.minFeeB'), + keyDeposit: t( + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.tooltip.keyDeposit' + ), + poolDeposit: t( + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.tooltip.poolDeposit' + ), + rho: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.tooltip.rho'), + tau: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.tooltip.tau'), + minPoolCost: t( + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.tooltip.minPoolCost' + ), + coinsPerUTxOByte: t( + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.tooltip.coinsPerUTxOByte' + ), + prices: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.tooltip.prices') + } + }, + technicalGroup: { + title: t('core.ProposalProcedure.governanceAction.technicalGroup.title'), + a0: t('core.ProposalProcedure.governanceAction.technicalGroup.a0'), + eMax: t('core.ProposalProcedure.governanceAction.technicalGroup.eMax'), + nOpt: t('core.ProposalProcedure.governanceAction.technicalGroup.nOpt'), + costModels: t('core.ProposalProcedure.governanceAction.technicalGroup.costModels'), + collateralPercentage: t('core.ProposalProcedure.governanceAction.technicalGroup.collateralPercentage'), + tooltip: { + a0: t('core.ProposalProcedure.governanceAction.technicalGroup.tooltip.a0'), + eMax: t('core.ProposalProcedure.governanceAction.technicalGroup.tooltip.eMax'), + nOpt: t('core.ProposalProcedure.governanceAction.technicalGroup.tooltip.nOpt'), + costModels: t('core.ProposalProcedure.governanceAction.technicalGroup.tooltip.costModels'), + collateralPercentage: t( + 'core.ProposalProcedure.governanceAction.technicalGroup.tooltip.collateralPercentage' + ) + } + }, + governanceGroup: { + title: t('core.ProposalProcedure.governanceAction.governanceGroup.title'), + govActionLifetime: t('core.ProposalProcedure.governanceAction.governanceGroup.govActionLifetime'), + govActionDeposit: t('core.ProposalProcedure.governanceAction.governanceGroup.govActionDeposit'), + drepDeposit: t('core.ProposalProcedure.governanceAction.governanceGroup.drepDeposit'), + drepActivity: t('core.ProposalProcedure.governanceAction.governanceGroup.drepActivity'), + ccMinSize: t('core.ProposalProcedure.governanceAction.governanceGroup.ccMinSize'), + ccMaxTermLength: t('core.ProposalProcedure.governanceAction.governanceGroup.ccMaxTermLength'), + dRepVotingThresholds: { + title: t('core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.title'), + motionNoConfidence: t( + 'core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.motionNoConfidence' + ), + committeeNormal: t( + 'core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.committeeNormal' + ), + committeeNoConfidence: t( + 'core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.committeeNoConfidence' + ), + updateConstitution: t( + 'core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.updateConstitution' + ), + hardForkInitiation: t( + 'core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.hardForkInitiation' + ), + ppNetworkGroup: t( + 'core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.ppNetworkGroup' + ), + ppEconomicGroup: t( + 'core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.ppEconomicGroup' + ), + ppTechnicalGroup: t( + 'core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.ppTechnicalGroup' + ), + ppGovernanceGroup: t( + 'core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.ppGovernanceGroup' + ), + treasuryWithdrawal: t( + 'core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.treasuryWithdrawal' + ) + }, + tooltip: { + govActionLifetime: t('core.ProposalProcedure.governanceAction.governanceGroup.tooltip.govActionLifetime'), + govActionDeposit: t('core.ProposalProcedure.governanceAction.governanceGroup.tooltip.govActionDeposit'), + drepDeposit: t('core.ProposalProcedure.governanceAction.governanceGroup.tooltip.drepDeposit'), + drepActivity: t('core.ProposalProcedure.governanceAction.governanceGroup.tooltip.drepActivity'), + ccMinSize: t('core.ProposalProcedure.governanceAction.governanceGroup.tooltip.ccMinSize'), + ccMaxTermLength: t('core.ProposalProcedure.governanceAction.governanceGroup.tooltip.ccMaxTermLength'), + dRepVotingThresholds: { + title: t('core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.title'), + motionNoConfidence: t( + 'core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.motionNoConfidence' + ), + committeeNormal: t( + 'core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.committeeNormal' + ), + committeeNoConfidence: t( + 'core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.committeeNoConfidence' + ), + updateConstitution: t( + 'core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.updateConstitution' + ), + hardForkInitiation: t( + 'core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.hardForkInitiation' + ), + ppNetworkGroup: t( + 'core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.ppNetworkGroup' + ), + ppEconomicGroup: t( + 'core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.ppEconomicGroup' + ), + ppTechnicalGroup: t( + 'core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.ppTechnicalGroup' + ), + ppGovernanceGroup: t( + 'core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.ppGovernanceGroup' + ), + treasuryWithdrawal: t( + 'core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.treasuryWithdrawal' + ) + } + } + } + }, + errorMessage + }, + {} + ); + }); +}); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/TreasuryWithdrawalsActionContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/TreasuryWithdrawalsActionContainer.test.tsx new file mode 100644 index 0000000000..701b0c2be3 --- /dev/null +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/TreasuryWithdrawalsActionContainer.test.tsx @@ -0,0 +1,165 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable no-magic-numbers */ +/* eslint-disable import/imports-first */ +const cardanoCoinMock = { + symbol: 'cardanoCoinMockSymbol' +}; +const mockUseWalletStore = jest.fn(() => ({ + walletUI: { cardanoCoin: cardanoCoinMock }, + walletInfo: {} +})); +const t = jest.fn().mockImplementation((res) => res); +const mockUseTranslation = jest.fn(() => ({ t })); +const mockTreasuryWithdrawalsAction = jest.fn(() => ); +const mockLovelacesToAdaString = jest.fn((val) => val); +const mockedCExpolorerBaseUrl = 'mockedCExpolorerBaseUrl'; +const mockUseCExpolorerBaseUrl = jest.fn(() => mockedCExpolorerBaseUrl); +import { Wallet } from '@lace/cardano'; +import * as React from 'react'; +import { cleanup, render } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import { act } from 'react-dom/test-utils'; +import { TreasuryWithdrawalsActionContainer } from '../TreasuryWithdrawalsActionContainer'; +import { getWrapper } from '../../testing.utils'; + +jest.mock('react-i18next', () => { + const original = jest.requireActual('react-i18next'); + return { + __esModule: true, + ...original, + useTranslation: mockUseTranslation + }; +}); + +jest.mock('@lace/core', () => { + const original = jest.requireActual('@lace/core'); + return { + __esModule: true, + ...original, + TreasuryWithdrawalsAction: mockTreasuryWithdrawalsAction + }; +}); + +jest.mock('@src/stores', () => ({ + ...jest.requireActual('@src/stores'), + useWalletStore: mockUseWalletStore +})); + +jest.mock('../../hooks', () => { + const original = jest.requireActual('../../hooks'); + return { + __esModule: true, + ...original, + useCExpolorerBaseUrl: mockUseCExpolorerBaseUrl + }; +}); + +jest.mock('@lace/cardano', () => { + const actual = jest.requireActual('@lace/cardano'); + return { + __esModule: true, + ...actual, + Wallet: { + ...actual.Wallet, + util: { + ...actual.Wallet.util, + lovelacesToAdaString: mockLovelacesToAdaString + } + } + }; +}); + +const dappInfo = { + name: 'dappName', + logo: 'dappLogo', + url: 'dappUrl' +}; +const errorMessage = 'errorMessage'; +const deposit = BigInt('10000'); +const rewardAccount = Wallet.Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj'); +const anchor = { + url: 'anchorUrl', + dataHash: Wallet.Crypto.Hash32ByteBase16(Buffer.from('anchorDataHashanchorDataHashanch').toString('hex')) +}; + +const treasuryWithdrawalsAction = { + withdrawals: new Set([ + { rewardAccount, coin: BigInt('10000000') }, + { rewardAccount, coin: BigInt('10000001') } + ]), + __typename: Wallet.Cardano.GovernanceActionType.treasury_withdrawals_action +} as Wallet.Cardano.TreasuryWithdrawalsAction; + +describe('Testing ProposalProceduresContainer component', () => { + afterEach(() => { + jest.clearAllMocks(); + cleanup(); + }); + + test('should render TreasuryWithdrawalsAction component with proper props', async () => { + let queryByTestId: any; + + await act(async () => { + ({ queryByTestId } = render( + , + { + wrapper: getWrapper() + } + )); + }); + + expect(queryByTestId('TreasuryWithdrawalsAction')).toBeInTheDocument(); + expect(mockTreasuryWithdrawalsAction).toHaveBeenLastCalledWith( + { + dappInfo, + data: { + txDetails: { + txType: t('core.ProposalProcedure.governanceAction.treasuryWithdrawals.title'), + deposit: `${deposit.toString()} ${cardanoCoinMock.symbol}`, + rewardAccount + }, + procedure: { + anchor: { + url: anchor.url, + hash: anchor.dataHash, + txHashUrl: `${mockedCExpolorerBaseUrl}/${anchor.dataHash}` + } + }, + withdrawals: [...treasuryWithdrawalsAction.withdrawals].map((withdrawal) => ({ + rewardAccount: withdrawal.rewardAccount.toString(), + lovelace: `${withdrawal.coin.toString()} ${cardanoCoinMock.symbol}` + })) + }, + translations: { + txDetails: { + title: t('core.ProposalProcedure.txDetails.title'), + txType: t('core.ProposalProcedure.txDetails.txType'), + deposit: t('core.ProposalProcedure.txDetails.deposit'), + rewardAccount: t('core.ProposalProcedure.txDetails.rewardAccount') + }, + procedure: { + title: t('core.ProposalProcedure.procedure.title'), + anchor: { + url: t('core.ProposalProcedure.procedure.anchor.url'), + hash: t('core.ProposalProcedure.procedure.anchor.hash') + } + }, + actionId: { + title: t('core.ProposalProcedure.governanceAction.actionId.title'), + index: t('core.ProposalProcedure.governanceAction.actionId.index'), + txId: t('core.ProposalProcedure.governanceAction.actionId.txId') + }, + withdrawals: { + title: t('core.ProposalProcedure.governanceAction.treasuryWithdrawals.title'), + rewardAccount: t('core.ProposalProcedure.governanceAction.treasuryWithdrawals.withdrawals.rewardAccount'), + lovelace: t('core.ProposalProcedure.governanceAction.treasuryWithdrawals.withdrawals.lovelace') + } + }, + errorMessage + }, + {} + ); + }); +}); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/UpdateCommitteeActionContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/UpdateCommitteeActionContainer.test.tsx new file mode 100644 index 0000000000..5c99335f63 --- /dev/null +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/UpdateCommitteeActionContainer.test.tsx @@ -0,0 +1,208 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable no-magic-numbers */ +/* eslint-disable import/imports-first */ +const cardanoCoinMock = { + symbol: 'cardanoCoinMockSymbol' +}; +const mockUseWalletStore = jest.fn(() => ({ + walletUI: { cardanoCoin: cardanoCoinMock }, + walletInfo: {} +})); +const t = jest.fn().mockImplementation((res) => res); +const mockUseTranslation = jest.fn(() => ({ t })); +const mockUpdateCommitteeAction = jest.fn(() => ); +const mockLovelacesToAdaString = jest.fn((val) => val); +const mockedCExpolorerBaseUrl = 'mockedCExpolorerBaseUrl'; +const mockUseCExpolorerBaseUrl = jest.fn(() => mockedCExpolorerBaseUrl); +import { Wallet } from '@lace/cardano'; +import * as React from 'react'; +import { cleanup, render } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import { act } from 'react-dom/test-utils'; +import { UpdateCommitteeActionContainer } from '../UpdateCommitteeActionContainer'; +import { getWrapper } from '../../testing.utils'; + +jest.mock('react-i18next', () => { + const original = jest.requireActual('react-i18next'); + return { + __esModule: true, + ...original, + useTranslation: mockUseTranslation + }; +}); + +jest.mock('@lace/core', () => { + const original = jest.requireActual('@lace/core'); + return { + __esModule: true, + ...original, + UpdateCommitteeAction: mockUpdateCommitteeAction + }; +}); + +jest.mock('@src/stores', () => ({ + ...jest.requireActual('@src/stores'), + useWalletStore: mockUseWalletStore +})); + +jest.mock('../../hooks', () => { + const original = jest.requireActual('../../hooks'); + return { + __esModule: true, + ...original, + useCExpolorerBaseUrl: mockUseCExpolorerBaseUrl + }; +}); + +jest.mock('@lace/cardano', () => { + const actual = jest.requireActual('@lace/cardano'); + return { + __esModule: true, + ...actual, + Wallet: { + ...actual.Wallet, + util: { + ...actual.Wallet.util, + lovelacesToAdaString: mockLovelacesToAdaString + } + } + }; +}); + +const dappInfo = { + name: 'dappName', + logo: 'dappLogo', + url: 'dappUrl' +}; +const errorMessage = 'errorMessage'; +const deposit = BigInt('10000'); +const rewardAccount = Wallet.Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj'); +const anchor = { + url: 'anchorUrl', + dataHash: Wallet.Crypto.Hash32ByteBase16(Buffer.from('anchorDataHashanchorDataHashanch').toString('hex')) +}; + +const updateCommittee = { + membersToBeAdded: new Set([ + { + coldCredential: { + type: 0, + hash: Wallet.Crypto.Hash28ByteBase16(Buffer.from('updateCommitteecoldCredenti1').toString('hex')) + }, + epoch: 1 + }, + { + coldCredential: { + type: 1, + hash: Wallet.Crypto.Hash28ByteBase16(Buffer.from('updateCommitteecoldCredenti2').toString('hex')) + }, + epoch: 2 + } + ]), + membersToBeRemoved: new Set([ + { + type: 0, + hash: Wallet.Crypto.Hash28ByteBase16(Buffer.from('updateCommitteecoldCredenti2').toString('hex')) + }, + { + type: 1, + hash: Wallet.Crypto.Hash28ByteBase16(Buffer.from('updateCommitteecoldCredenti3').toString('hex')) + } + ]), + governanceActionId: { + actionIndex: 123, + id: Wallet.Cardano.TransactionId('724a0a88b9470a714fc5bf84daf5851fa259a9b89e1a5453f6f5cd6595ad9821') + }, + __typename: Wallet.Cardano.GovernanceActionType.update_committee +} as Wallet.Cardano.UpdateCommittee; + +describe('Testing ProposalProceduresContainer component', () => { + afterEach(() => { + jest.clearAllMocks(); + cleanup(); + }); + + test('should render UpdateCommitteeAction component with proper props', async () => { + let queryByTestId: any; + + await act(async () => { + ({ queryByTestId } = render( + , + { + wrapper: getWrapper() + } + )); + }); + + expect(queryByTestId('UpdateCommitteeAction')).toBeInTheDocument(); + expect(mockUpdateCommitteeAction).toHaveBeenLastCalledWith( + { + dappInfo, + data: { + txDetails: { + txType: t('core.ProposalProcedure.governanceAction.updateCommitteeAction.title'), + deposit: `${deposit.toString()} ${cardanoCoinMock.symbol}`, + rewardAccount + }, + procedure: { + anchor: { + url: anchor.url, + hash: anchor.dataHash, + txHashUrl: `${mockedCExpolorerBaseUrl}/${anchor.dataHash}` + } + }, + actionId: { + index: updateCommittee.governanceActionId.actionIndex.toString(), + id: updateCommittee.governanceActionId.id || '' + }, + membersToBeAdded: [...updateCommittee.membersToBeAdded].map(({ coldCredential: { hash }, epoch }) => ({ + coldCredential: { + hash: hash.toString() + }, + epoch: epoch.toString() + })), + membersToBeRemoved: [...updateCommittee.membersToBeRemoved].map(({ hash }) => ({ hash: hash.toString() })) + }, + translations: { + txDetails: { + title: t('core.ProposalProcedure.txDetails.title'), + txType: t('core.ProposalProcedure.txDetails.txType'), + deposit: t('core.ProposalProcedure.txDetails.deposit'), + rewardAccount: t('core.ProposalProcedure.txDetails.rewardAccount') + }, + procedure: { + title: t('core.ProposalProcedure.procedure.title'), + anchor: { + url: t('core.ProposalProcedure.procedure.anchor.url'), + hash: t('core.ProposalProcedure.procedure.anchor.hash') + } + }, + actionId: { + title: t('core.ProposalProcedure.governanceAction.actionId.title'), + index: t('core.ProposalProcedure.governanceAction.actionId.index'), + txId: t('core.ProposalProcedure.governanceAction.actionId.txId') + }, + membersToBeAdded: { + title: t('core.ProposalProcedure.governanceAction.updateCommitteeAction.membersToBeAdded.title'), + coldCredential: { + hash: t( + 'core.ProposalProcedure.governanceAction.updateCommitteeAction.membersToBeAdded.coldCredential.hash' + ), + epoch: t( + 'core.ProposalProcedure.governanceAction.updateCommitteeAction.membersToBeAdded.coldCredential.epoch' + ) + } + }, + membersToBeRemoved: { + title: t('core.ProposalProcedure.governanceAction.updateCommitteeAction.membersToBeRemoved.title'), + hash: t('core.ProposalProcedure.governanceAction.updateCommitteeAction.membersToBeRemoved.hash') + } + }, + errorMessage + }, + {} + ); + }); +}); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/testing.utils.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/testing.utils.tsx new file mode 100644 index 0000000000..4d4cff1643 --- /dev/null +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/testing.utils.tsx @@ -0,0 +1,38 @@ +import { I18nextProvider } from 'react-i18next'; +import { StoreProvider } from '@src/stores'; +import { + AnalyticsProvider, + AppSettingsProvider, + BackgroundServiceAPIProvider, + BackgroundServiceAPIProviderProps, + DatabaseProvider +} from '@src/providers'; +import { APP_MODE_BROWSER } from '@src/utils/constants'; +import i18n from '@lib/i18n'; +import { PostHogClientProvider } from '@providers/PostHogClientProvider'; +import { postHogClientMocks } from '@src/utils/mocks/test-helpers'; +import React from 'react'; + +const backgroundService = { + getBackgroundStorage: jest.fn(), + setBackgroundStorage: jest.fn() +} as unknown as BackgroundServiceAPIProviderProps['value']; + +export const getWrapper = + () => + ({ children }: { children: React.ReactNode }): React.ReactElement => + ( + + + + + + + {children} + + + + + + + ); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/utils.ts b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/utils.ts index a92dc323c5..664bfe32d7 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/utils.ts +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/utils.ts @@ -67,11 +67,15 @@ export const votingProceduresInspector = (tx: Wallet.Cardano.Tx): Wallet.Cardano tx?.body?.votingProcedures; // eslint-disable-next-line complexity +export const proposalProceduresInspector = (tx: Wallet.Cardano.Tx): Wallet.Cardano.ProposalProcedure[] | undefined => + tx?.body?.proposalProcedures; + export const getTxType = (tx: Wallet.Cardano.Tx): Wallet.Cip30TxType => { const inspector = createTxInspector({ minted: assetsMintedInspector, burned: assetsBurnedInspector, votingProcedures: votingProceduresInspector, + proposalProcedures: proposalProceduresInspector, dRepRegistration: certificateInspectorFactory(CertificateType.RegisterDelegateRepresentative), dRepRetirement: certificateInspectorFactory(CertificateType.UnregisterDelegateRepresentative), dRepUpdate: certificateInspectorFactory(CertificateType.UpdateDelegateRepresentative), @@ -93,11 +97,16 @@ export const getTxType = (tx: Wallet.Cardano.Tx): Wallet.Cip30TxType => { stakeVoteDelegation, voteRegistrationDelegation, stakeRegistrationDelegation, - stakeVoteDelegationRegistration + stakeVoteDelegationRegistration, + proposalProcedures } = inspector(tx as Wallet.Cardano.HydratedTx); const isMintTransaction = minted.length > 0; const isBurnTransaction = burned.length > 0; + if (proposalProcedures) { + return Wallet.Cip30TxType.ProposalProcedures; + } + if (votingProcedures) { return Wallet.Cip30TxType.VotingProcedures; } diff --git a/apps/browser-extension-wallet/src/lib/translations/en.json b/apps/browser-extension-wallet/src/lib/translations/en.json index 23463cdad6..26e7307368 100644 --- a/apps/browser-extension-wallet/src/lib/translations/en.json +++ b/apps/browser-extension-wallet/src/lib/translations/en.json @@ -1130,6 +1130,186 @@ "tryingToUseAssetNotInWallet": "This DApp is trying to use token not held in your wallet.", "noCollateral": "Wallet should not be able to sign dapp txs without collateral." }, + "ProposalProcedure": { + "dRepId": "DRep ID", + "txDetails": { + "deposit": "Deposit", + "rewardAccount": "Reward account", + "title": "Transaction Details", + "txType": "Transaction Type" + }, + "procedure": { + "anchor": { + "hash": "Anchor Hash", + "url": "Anchor URL" + }, + "title": "Procedure", + "dRepId": "DRep ID" + }, + "governanceAction": { + "actionId": { + "title": "Action ID", + "index": "Index", + "txId": "TX ID" + }, + "hardForkInitiation": { + "title": "Hard Fork Initiation", + "protocolVersion": { + "major": "Protocol Version Major", + "minor": "Protocol Version Minor", + "patch": "Protocol Version Patch" + } + }, + "newConstitutionAction": { + "title": "New Constitution Action", + "constitution": { + "title": "Constitution Details", + "anchor": { + "dataHash": "Anchor Data Hash", + "url": "Constitution Anchor URL" + }, + "scriptHash": "Constitution Script Hash" + } + }, + "infoAction": { + "title": "Info Action" + }, + "noConfidenceAction": { + "title": "No Confidence" + }, + "protocolParamUpdate": { + "title": "Protocol Parameter Update", + "memory": "Memory", + "step": "Step", + "networkGroup": { + "title": "Network group", + "maxBBSize": "Max BB Size", + "maxTxSize": "Max Tx Size", + "maxBHSize": "Max BH Size", + "maxValSize": "Max Val Size", + "maxTxExUnits": "Max TX Ex Units", + "maxBlockExUnits": "Max Blk Ex Units", + "maxCollateralInputs": "Max Coll Inputs", + "tooltip": { + "maxBBSize": "Max block body size", + "maxTxSize": "Max transaction size", + "maxBHSize": "Max block header size", + "maxValSize": "Max size of a serialized asset value", + "maxTxExUnits": "Max script execution units in a single transaction", + "maxBlockExUnits": "Max script execution units in a single block", + "maxCollateralInputs": "Max number of collateral inputs" + } + }, + "economicGroup": { + "title": "Economic group", + "minFeeA": "Min Fee A", + "minFeeB": "Min Fee B", + "keyDeposit": "Key Deposit", + "poolDeposit": "Pool Deposit", + "rho": "Rho", + "tau": "Tau", + "minPoolCost": "Min Pool Cost", + "coinsPerUTxOByte": "Coins/UTxO Byte", + "prices": "Price", + "tooltip": { + "minFeeA": "Min fee coefficient", + "minFeeB": "Min fee constant", + "keyDeposit": "Delegation key Lovelace deposit", + "poolDeposit": "Pool registration Lovelace deposit", + "rho": "Monetary expansion", + "tau": "Treasury expansion", + "minPoolCost": "Min fixed rewards cut for pools", + "coinsPerUTxOByte": "Min Lovelace deposit per byte of serialized UTxO", + "prices": "Prices of Plutus execution units" + } + }, + "technicalGroup": { + "title": "Technical group", + "a0": "A0", + "eMax": "EMax", + "nOpt": "NOpt", + "costModels": "Cost Models", + "collateralPercentage": "Coll Percentage", + "tooltip": { + "a0": "Pool pledge influence", + "eMax": "Pool retirement maximum epoch", + "nOpt": "Desired number of pools", + "costModels": "Plutus execution cost models", + "collateralPercentage": "Proportion of collateral needed for scripts" + } + }, + "governanceGroup": { + "title": "Governance group", + "govActionLifetime": "Gov Act Lifetime", + "govActionDeposit": "Gov Act Deposit", + "drepDeposit": "DRep Deposit", + "drepActivity": "DRep Activity", + "ccMinSize": "CC Min Size", + "ccMaxTermLength": "CC Max Term Length", + "dRepVotingThresholds": { + "title": "Governance voting thresholds", + "motionNoConfidence": "Motion No Conf", + "committeeNormal": "Comm Normal", + "committeeNoConfidence": "Comm No Conf", + "updateConstitution": "Update Const", + "hardForkInitiation": "Hard Fork Init", + "ppNetworkGroup": "PP Network Grp", + "ppEconomicGroup": "PP Economic Grp", + "ppTechnicalGroup": "PP Technical Grp", + "ppGovernanceGroup": "PP Governance Grp", + "treasuryWithdrawal": "Treasury Withdraw" + }, + "tooltip": { + "govActionLifetime": "governance action maximum lifetime in epochs", + "govActionDeposit": "governance action deposit", + "drepDeposit": "DRep deposit amount", + "drepActivity": "DRep activity period in epochs", + "ccMinSize": "Min constitutional committee size", + "ccMaxTermLength": "Max term length (in epochs) for the constitutional committee members", + "dRepVotingThresholds": { + "title": "Governance voting thresholds", + "motionNoConfidence": "1. Motion of no-confidence", + "committeeNormal": "2a. New committee/threshold (normal state)", + "committeeNoConfidence": "2b. New committee/threshold (state of no-confidence)", + "updateConstitution": "3. Update to the Constitution or proposal policy", + "hardForkInitiation": "4. Hard-fork initiation", + "ppNetworkGroup": "5a. Protocol parameter changes, network group", + "ppEconomicGroup": "5b. Protocol parameter changes, economic group", + "ppTechnicalGroup": "5c. Protocol parameter changes, technical group", + "ppGovernanceGroup": "5d. Protocol parameter changes, governance group", + "treasuryWithdrawal": "6. Treasury withdrawal" + } + } + } + }, + "treasuryWithdrawals": { + "title": "Treasury Withdrawals", + "withdrawals": { + "lovelace": "Withdrawal Amount", + "rewardAccount": "Withdrawal Reward Account" + } + }, + "updateCommitteeAction": { + "title": "Update Committee Action", + "membersToBeAdded": { + "title": "Members To Be Added", + "coldCredential": { + "hash": "Cold Credential Hash", + "epoch": "Epoch" + } + }, + "membersToBeRemoved": { + "title": "Members To Be Removed", + "hash": "Hash" + }, + "newQuorumThreshold": { + "title": "New Quorum Threshold", + "denominator": "Denominator", + "numerator": "Numerator" + } + } + } + }, "VotingProcedures": { "title": "Confirm Vote", "voterType": "Voter type", diff --git a/packages/cardano/src/wallet/types.ts b/packages/cardano/src/wallet/types.ts index 7a4f78c0ad..9c2091494f 100644 --- a/packages/cardano/src/wallet/types.ts +++ b/packages/cardano/src/wallet/types.ts @@ -40,7 +40,8 @@ export enum Cip30TxType { VoteRegistrationDelegation = 'VoteRegistrationDelegation', StakeRegistrationDelegation = 'StakeRegistrationDelegation', StakeVoteDelegationRegistration = 'StakeVoteDelegationRegistration', - StakeVoteDelegation = 'StakeVoteDelegation' + StakeVoteDelegation = 'StakeVoteDelegation', + ProposalProcedures = 'ProposalProcedures' } export type Cip30SignTxOutput = { diff --git a/packages/core/.storybook/preview.js b/packages/core/.storybook/preview.js index 7ffa549eec..4a32e4e72b 100644 --- a/packages/core/.storybook/preview.js +++ b/packages/core/.storybook/preview.js @@ -4,6 +4,16 @@ import 'normalize.css'; import './theme.scss'; import { ThemeColorScheme, ThemeProvider } from '@lace/ui'; +export const customViewports = { + popup: { + name: 'Popup', + styles: { + width: '360px', + height: '600' + } + } +}; + export const preview = { parameters: { actions: { argTypesRegex: '^on[A-Z].*' }, @@ -12,6 +22,10 @@ export const preview = { color: /(background|color)$/i, date: /Date$/ } + }, + viewport: { + viewports: customViewports, + defaultViewport: 'Popup' } } }; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 70019dfd2c..d8cc6891e4 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -37,3 +37,4 @@ export * from '@ui/components/ConfirmStakeRegistrationDelegation'; export * from '@ui/components/ConfirmStakeVoteRegistrationDelegation'; export * from '@ui/components/ConfirmVoteRegistrationDelegation'; export * from '@ui/components/VotingProcedures'; +export * from '@ui/components/ProposalProcedures'; diff --git a/packages/core/src/ui/components/ConfirmStakeRegistrationDelegation/ConfirmStakeRegistrationDelegation.stories.ts b/packages/core/src/ui/components/ConfirmStakeRegistrationDelegation/ConfirmStakeRegistrationDelegation.stories.tsx similarity index 100% rename from packages/core/src/ui/components/ConfirmStakeRegistrationDelegation/ConfirmStakeRegistrationDelegation.stories.ts rename to packages/core/src/ui/components/ConfirmStakeRegistrationDelegation/ConfirmStakeRegistrationDelegation.stories.tsx diff --git a/packages/core/src/ui/components/ConfirmStakeVoteDelegation/ConfirmStakeVoteDelegation.stories.ts b/packages/core/src/ui/components/ConfirmStakeVoteDelegation/ConfirmStakeVoteDelegation.stories.tsx similarity index 100% rename from packages/core/src/ui/components/ConfirmStakeVoteDelegation/ConfirmStakeVoteDelegation.stories.ts rename to packages/core/src/ui/components/ConfirmStakeVoteDelegation/ConfirmStakeVoteDelegation.stories.tsx diff --git a/packages/core/src/ui/components/ConfirmStakeVoteRegistrationDelegation/ConfirmStakeVoteRegistrationDelegation.stories.ts b/packages/core/src/ui/components/ConfirmStakeVoteRegistrationDelegation/ConfirmStakeVoteRegistrationDelegation.stories.tsx similarity index 100% rename from packages/core/src/ui/components/ConfirmStakeVoteRegistrationDelegation/ConfirmStakeVoteRegistrationDelegation.stories.ts rename to packages/core/src/ui/components/ConfirmStakeVoteRegistrationDelegation/ConfirmStakeVoteRegistrationDelegation.stories.tsx diff --git a/packages/core/src/ui/components/ConfirmVoteRegistrationDelegation/ConfirmVoteRegistrationDelegation.stories.ts b/packages/core/src/ui/components/ConfirmVoteRegistrationDelegation/ConfirmVoteRegistrationDelegation.stories.tsx similarity index 100% rename from packages/core/src/ui/components/ConfirmVoteRegistrationDelegation/ConfirmVoteRegistrationDelegation.stories.ts rename to packages/core/src/ui/components/ConfirmVoteRegistrationDelegation/ConfirmVoteRegistrationDelegation.stories.tsx diff --git a/packages/core/src/ui/components/DappTransaction/DappTransaction.stories.tsx b/packages/core/src/ui/components/DappTransaction/DappTransaction.stories.tsx index 56f29d0531..07525eac9c 100644 --- a/packages/core/src/ui/components/DappTransaction/DappTransaction.stories.tsx +++ b/packages/core/src/ui/components/DappTransaction/DappTransaction.stories.tsx @@ -2,6 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react'; import { DappTransaction } from './DappTransaction'; import { ComponentProps } from 'react'; +import { Wallet } from '@lace/cardano'; const meta: Meta = { title: 'DappTransaction', @@ -20,13 +21,6 @@ const data: ComponentProps = { name: 'Mint', url: 'https://preprod.mint.handle.me' }, - translations: { - recipient: 'Recipient', - amount: 'Amount', - adaFollowingNumericValue: 'ADA', - fee: 'Fee', - transaction: 'Transaction' - }, transaction: { fee: '0.17', outputs: [ @@ -36,7 +30,7 @@ const data: ComponentProps = { 'addr_test1qrl0s3nqfljv8dfckn7c4wkzu5rl6wn4hakkddcz2mczt3szlqss933x0aag07qcgspcaglmay6ufl4y4lalmlpe02mqhl0fx2' } ], - type: 'Mint' + type: Wallet.Cip30TxType.Mint } }; diff --git a/packages/core/src/ui/components/ProposalProcedures/HardForkInitiationAction/HardForkInitiationAction.stories.tsx b/packages/core/src/ui/components/ProposalProcedures/HardForkInitiationAction/HardForkInitiationAction.stories.tsx new file mode 100644 index 0000000000..b50685d593 --- /dev/null +++ b/packages/core/src/ui/components/ProposalProcedures/HardForkInitiationAction/HardForkInitiationAction.stories.tsx @@ -0,0 +1,98 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { HardForkInitiationAction } from './HardForkInitiationAction'; +import { ComponentProps } from 'react'; + +const customViewports = { + popup: { + name: 'Popup', + styles: { + width: '360px', + height: '600' + } + } +}; + +const meta: Meta = { + title: 'ProposalProcedure/HardForkInitiationAction', + component: HardForkInitiationAction, + parameters: { + layout: 'centered', + viewport: { + viewports: customViewports, + defaultViewport: 'popup' + } + } +}; + +export default meta; +type Story = StoryObj; + +const data: ComponentProps = { + dappInfo: { + logo: 'https://cdn.mint.handle.me/favicon.png', + name: 'Mint', + url: 'https://preprod.mint.handle.me' + }, + data: { + txDetails: { + txType: 'Hard Fork Initiation', + deposit: '2000', + rewardAccount: 'stake1u89sasnfyjtmgk8ydqfv3fdl52f36x3djedfnzfc9rkgzrcss5vgr' + }, + procedure: { + anchor: { + hash: '26bfdcc75a7f4d0cd8c71f0189bc5ca5ad2f4a3db6240c82b5a0edac7f9203e0', + url: 'https://www.someurl.io', + txHashUrl: 'https://www.someurl.io' + } + }, + actionId: { + index: '0', + id: '26bfdcc75a7f4d0cd8c71f0189bc5ca5ad2f4a3db6240c82b5a0edac7f9203e0' + }, + protocolVersion: { + major: '5', + minor: '1', + patch: '1' + } + }, + translations: { + txDetails: { + title: 'Transaction Details', + txType: 'Transaction Type', + deposit: 'Deposit', + rewardAccount: 'Reward account' + }, + procedure: { + anchor: { + hash: 'Anchor Hash', + url: 'Anchor URL' + }, + title: 'Procedure' + }, + actionId: { + title: 'Action ID', + index: 'Index', + txId: 'TX ID' + }, + protocolVersion: { + major: 'Protocol Version Major', + minor: 'Protocol Version Minor', + patch: 'Protocol Version Patch' + } + } +}; + +export const Overview: Story = { + args: { + ...data + } +}; + +export const WithError: Story = { + args: { + ...data, + errorMessage: 'Something went wrong' + } +}; diff --git a/packages/core/src/ui/components/ProposalProcedures/HardForkInitiationAction/HardForkInitiationAction.tsx b/packages/core/src/ui/components/ProposalProcedures/HardForkInitiationAction/HardForkInitiationAction.tsx new file mode 100644 index 0000000000..6a722f1018 --- /dev/null +++ b/packages/core/src/ui/components/ProposalProcedures/HardForkInitiationAction/HardForkInitiationAction.tsx @@ -0,0 +1,62 @@ +import React from 'react'; +import { Box, Grid, Flex, Divider, Metadata, Cell } from '@lace/ui'; +import { DappInfo, DappInfoProps } from '../../DappInfo'; +import { ErrorPane } from '@lace/common'; +import * as Types from './HardForkInitiationActionTypes'; +import { TransactionDetails } from '../components/TransactionDetails'; +import { Procedure } from '../components/Procedure'; +import { ActionId } from '../components/ActionId'; + +export interface HardForkInitiationActionProps { + dappInfo: Omit; + errorMessage?: string; + data: Types.Data; + translations: Types.Translations; +} + +export const HardForkInitiationAction = ({ + dappInfo, + errorMessage, + data: { procedure, txDetails, actionId, protocolVersion }, + translations +}: HardForkInitiationActionProps): JSX.Element => ( + + + + + {errorMessage && ( + + + + )} + + {/* tx details section */} + + + + + {/* procedure section */} + + + + + + + + {protocolVersion.patch && ( + + + + )} + {/* action id section*/} + {actionId && ( + <> + + + + + + )} + + +); diff --git a/packages/core/src/ui/components/ProposalProcedures/HardForkInitiationAction/HardForkInitiationActionTypes.ts b/packages/core/src/ui/components/ProposalProcedures/HardForkInitiationAction/HardForkInitiationActionTypes.ts new file mode 100644 index 0000000000..bd8ae61b7d --- /dev/null +++ b/packages/core/src/ui/components/ProposalProcedures/HardForkInitiationAction/HardForkInitiationActionTypes.ts @@ -0,0 +1,25 @@ +import * as ProcedureTypes from '../components/ProcedureTypes'; +import * as ActionIdTypes from '../components/ActionIdTypes'; +import * as TxDetailsTypes from '../components/TransactionDetailsTypes'; + +export interface Data { + txDetails: TxDetailsTypes.TxDetails; + procedure: ProcedureTypes.Procedure; + actionId?: ActionIdTypes.Data; + protocolVersion: { + major: string; + minor: string; + patch?: string; + }; +} + +export interface Translations { + txDetails: TxDetailsTypes.Translations; + procedure: ProcedureTypes.Translations; + actionId?: ActionIdTypes.Translations; + protocolVersion: { + major: string; + minor: string; + patch: string; + }; +} diff --git a/packages/core/src/ui/components/ProposalProcedures/HardForkInitiationAction/index.ts b/packages/core/src/ui/components/ProposalProcedures/HardForkInitiationAction/index.ts new file mode 100644 index 0000000000..94f3dd74c4 --- /dev/null +++ b/packages/core/src/ui/components/ProposalProcedures/HardForkInitiationAction/index.ts @@ -0,0 +1 @@ +export { HardForkInitiationAction } from './HardForkInitiationAction'; diff --git a/packages/core/src/ui/components/ProposalProcedures/InfoAction/InfoAction.stories.tsx b/packages/core/src/ui/components/ProposalProcedures/InfoAction/InfoAction.stories.tsx new file mode 100644 index 0000000000..3f97136d22 --- /dev/null +++ b/packages/core/src/ui/components/ProposalProcedures/InfoAction/InfoAction.stories.tsx @@ -0,0 +1,75 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { InfoAction } from './InfoAction'; +import { ComponentProps } from 'react'; + +const customViewports = { + popup: { + name: 'Popup', + styles: { + width: '360px', + height: '600' + } + } +}; + +const meta: Meta = { + title: 'ProposalProcedure/InfoAction', + component: InfoAction, + parameters: { + layout: 'centered', + viewport: { + viewports: customViewports, + defaultViewport: 'popup' + } + } +}; + +export default meta; +type Story = StoryObj; + +const data: ComponentProps = { + dappInfo: { + logo: 'https://cdn.mint.handle.me/favicon.png', + name: 'Mint', + url: 'https://preprod.mint.handle.me' + }, + data: { + txDetails: { + txType: 'Info action' + }, + procedure: { + anchor: { + hash: '26bfdcc75a7f4d0cd8c71f0189bc5ca5ad2f4a3db6240c82b5a0edac7f9203e0', + url: 'https://www.someurl.io', + txHashUrl: 'https://www.someurl.io' + } + } + }, + translations: { + txDetails: { + title: 'Transaction Details', + txType: 'Transaction Type' + }, + procedure: { + anchor: { + hash: 'Anchor Hash', + url: 'Anchor URL' + }, + title: 'Procedure' + } + } +}; + +export const Overview: Story = { + args: { + ...data + } +}; + +export const WithError: Story = { + args: { + ...data, + errorMessage: 'Something went wrong' + } +}; diff --git a/packages/core/src/ui/components/ProposalProcedures/InfoAction/InfoAction.tsx b/packages/core/src/ui/components/ProposalProcedures/InfoAction/InfoAction.tsx new file mode 100644 index 0000000000..b84260bd9a --- /dev/null +++ b/packages/core/src/ui/components/ProposalProcedures/InfoAction/InfoAction.tsx @@ -0,0 +1,40 @@ +import React from 'react'; +import { Box, Grid, Flex, Divider, Cell } from '@lace/ui'; +import { DappInfo, DappInfoProps } from '../../DappInfo'; +import { ErrorPane } from '@lace/common'; +import * as Types from './InfoActionTypes'; +import { TransactionDetails } from '../components/TransactionDetails'; +import { Procedure } from '../components/Procedure'; + +export interface InfoActionProps { + dappInfo: Omit; + errorMessage?: string; + data: Types.Data; + translations: Types.Translations; +} + +export const InfoAction = ({ + dappInfo, + errorMessage, + data: { procedure, txDetails }, + translations +}: InfoActionProps): JSX.Element => ( + + + + + {errorMessage && ( + + + + )} + + + + + + {/* procedure section */} + + + +); diff --git a/packages/core/src/ui/components/ProposalProcedures/InfoAction/InfoActionTypes.ts b/packages/core/src/ui/components/ProposalProcedures/InfoAction/InfoActionTypes.ts new file mode 100644 index 0000000000..2f8853991e --- /dev/null +++ b/packages/core/src/ui/components/ProposalProcedures/InfoAction/InfoActionTypes.ts @@ -0,0 +1,11 @@ +import * as ProcedureTypes from '../components/ProcedureTypes'; +import * as TxDetailsTypes from '../components/TransactionDetailsTypes'; +export interface Data { + txDetails: TxDetailsTypes.TxDetails; + procedure: ProcedureTypes.Procedure; +} + +export interface Translations { + txDetails: TxDetailsTypes.Translations; + procedure: ProcedureTypes.Translations; +} diff --git a/packages/core/src/ui/components/ProposalProcedures/InfoAction/index.ts b/packages/core/src/ui/components/ProposalProcedures/InfoAction/index.ts new file mode 100644 index 0000000000..f6fae454f1 --- /dev/null +++ b/packages/core/src/ui/components/ProposalProcedures/InfoAction/index.ts @@ -0,0 +1 @@ +export { InfoAction } from './InfoAction'; diff --git a/packages/core/src/ui/components/ProposalProcedures/NewConstitutionAction/NewConstitutionAction.stories.tsx b/packages/core/src/ui/components/ProposalProcedures/NewConstitutionAction/NewConstitutionAction.stories.tsx new file mode 100644 index 0000000000..d6fc007a4c --- /dev/null +++ b/packages/core/src/ui/components/ProposalProcedures/NewConstitutionAction/NewConstitutionAction.stories.tsx @@ -0,0 +1,103 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { NewConstitutionAction } from './NewConstitutionAction'; +import { ComponentProps } from 'react'; + +const customViewports = { + popup: { + name: 'Popup', + styles: { + width: '360px', + height: '600' + } + } +}; + +const meta: Meta = { + title: 'ProposalProcedure/NewConstitutionAction', + component: NewConstitutionAction, + parameters: { + layout: 'centered', + viewport: { + viewports: customViewports, + defaultViewport: 'popup' + } + } +}; + +export default meta; +type Story = StoryObj; + +const data: ComponentProps = { + dappInfo: { + logo: 'https://cdn.mint.handle.me/favicon.png', + name: 'Mint', + url: 'https://preprod.mint.handle.me' + }, + data: { + txDetails: { + txType: 'New Constitution', + deposit: '2000', + rewardAccount: 'stake1u89sasnfyjtmgk8ydqfv3fdl52f36x3djedfnzfc9rkgzrcss5vgr' + }, + procedure: { + anchor: { + hash: '0000000000000000000000000000000000000000000000000000000000000000', + url: 'https://www.someurl.io', + txHashUrl: 'https://www.someurl.io/' + } + }, + actionId: { + index: '0', + id: '26bfdcc75a7f4d0cd8c71f0189bc5ca5ad2f4a3db6240c82b5a0edac7f9203e0' + }, + constitution: { + anchor: { + dataHash: '0000000000000000000000000000000000000000000000000000000000000000', + url: 'https://www.someurl.io' + }, + scriptHash: 'cb0ec2692497b458e46812c8a5bfa2931d1a2d965a99893828ec810f' + } + }, + translations: { + txDetails: { + title: 'Transaction Details', + txType: 'Transaction Type', + deposit: 'Deposit', + rewardAccount: 'Reward account' + }, + procedure: { + anchor: { + hash: 'Anchor Hash', + url: 'Anchor URL' + }, + title: 'Procedure' + }, + actionId: { + title: 'Action ID', + index: 'Index', + txId: 'TX ID' + }, + constitution: { + title: 'Constitution Details', + anchor: { + dataHash: 'Anchor Data Hash', + url: 'Anchor URL' + }, + scriptHash: 'Script Hash' + } + } +}; + +export const Overview: Story = { + args: { + ...data + } +}; + +export const WithError: Story = { + args: { + ...data, + errorMessage: 'Something went wrong' + } +}; diff --git a/packages/core/src/ui/components/ProposalProcedures/NewConstitutionAction/NewConstitutionAction.tsx b/packages/core/src/ui/components/ProposalProcedures/NewConstitutionAction/NewConstitutionAction.tsx new file mode 100644 index 0000000000..7fb710ab03 --- /dev/null +++ b/packages/core/src/ui/components/ProposalProcedures/NewConstitutionAction/NewConstitutionAction.tsx @@ -0,0 +1,63 @@ +import React from 'react'; +import { Box, Grid, Flex, Divider, Metadata, MetadataLink, Cell } from '@lace/ui'; +import { DappInfo, DappInfoProps } from '../../DappInfo'; +import { ErrorPane } from '@lace/common'; +import * as Types from './NewConstitutionActionTypes'; +import { TransactionDetails } from '../components/TransactionDetails'; +import { Procedure } from '../components/Procedure'; +import { ActionId } from '../components/ActionId'; + +export interface NewConstitutionActionProps { + dappInfo: Omit; + errorMessage?: string; + data: Types.Data; + translations: Types.Translations; +} + +export const NewConstitutionAction = ({ + dappInfo, + errorMessage, + data: { txDetails, procedure, constitution, actionId }, + translations +}: NewConstitutionActionProps): JSX.Element => ( + + + + + {errorMessage && ( + + + + )} + + {/* txDetails section */} + + + + + {/* procedure section */} + + + + + {constitution.scriptHash && ( + + + + )} + {/* action id section*/} + {actionId && ( + <> + + + + + + )} + + +); diff --git a/packages/core/src/ui/components/ProposalProcedures/NewConstitutionAction/NewConstitutionActionTypes.ts b/packages/core/src/ui/components/ProposalProcedures/NewConstitutionAction/NewConstitutionActionTypes.ts new file mode 100644 index 0000000000..4d53001a2d --- /dev/null +++ b/packages/core/src/ui/components/ProposalProcedures/NewConstitutionAction/NewConstitutionActionTypes.ts @@ -0,0 +1,30 @@ +import * as ProcedureTypes from '../components/ProcedureTypes'; +import * as ActionIdTypes from '../components/ActionIdTypes'; +import * as TxDetailsTypes from '../components/TransactionDetailsTypes'; + +export interface Data { + procedure: ProcedureTypes.Procedure; + actionId?: ActionIdTypes.Data; + txDetails: TxDetailsTypes.TxDetails; + constitution: { + anchor: { + dataHash: string; + url: string; + }; + scriptHash: string; + }; +} + +export interface Translations { + procedure: ProcedureTypes.Translations; + actionId?: ActionIdTypes.Translations; + txDetails: TxDetailsTypes.Translations; + constitution: { + title: string; + anchor: { + dataHash: string; + url: string; + }; + scriptHash: string; + }; +} diff --git a/packages/core/src/ui/components/ProposalProcedures/NewConstitutionAction/index.ts b/packages/core/src/ui/components/ProposalProcedures/NewConstitutionAction/index.ts new file mode 100644 index 0000000000..2e50ba16ea --- /dev/null +++ b/packages/core/src/ui/components/ProposalProcedures/NewConstitutionAction/index.ts @@ -0,0 +1 @@ +export { NewConstitutionAction } from './NewConstitutionAction'; diff --git a/packages/core/src/ui/components/ProposalProcedures/NoConfidenceAction/NoConfidenceAction.stories.tsx b/packages/core/src/ui/components/ProposalProcedures/NoConfidenceAction/NoConfidenceAction.stories.tsx new file mode 100644 index 0000000000..81092e9bfe --- /dev/null +++ b/packages/core/src/ui/components/ProposalProcedures/NoConfidenceAction/NoConfidenceAction.stories.tsx @@ -0,0 +1,88 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { NoConfidenceAction } from './NoConfidenceAction'; +import { ComponentProps } from 'react'; + +const customViewports = { + popup: { + name: 'Popup', + styles: { + width: '360px', + height: '600' + } + } +}; + +const meta: Meta = { + title: 'ProposalProcedure/NoConfidenceAction', + component: NoConfidenceAction, + parameters: { + layout: 'centered', + viewport: { + viewports: customViewports, + defaultViewport: 'popup' + } + } +}; + +export default meta; +type Story = StoryObj; + +const data: ComponentProps = { + dappInfo: { + logo: 'https://cdn.mint.handle.me/favicon.png', + name: 'Mint', + url: 'https://preprod.mint.handle.me' + }, + data: { + txDetails: { + txType: 'No Confidence', + deposit: '2000', + rewardAccount: 'stake1u89sasnfyjtmgk8ydqfv3fdl52f36x3djedfnzfc9rkgzrcss5vgr' + }, + procedure: { + anchor: { + hash: '0000000000000000000000000000000000000000000000000000000000000000', + url: 'https://www.someurl.io', + txHashUrl: 'https://www.someurl.io/' + } + }, + actionId: { + index: '0', + id: '26bfdcc75a7f4d0cd8c71f0189bc5ca5ad2f4a3db6240c82b5a0edac7f9203e0' + } + }, + translations: { + txDetails: { + title: 'Transaction Details', + txType: 'Transaction Type', + deposit: 'Deposit', + rewardAccount: 'Reward account' + }, + procedure: { + anchor: { + hash: 'Anchor Hash', + url: 'Anchor URL' + }, + title: 'Procedure' + }, + actionId: { + title: 'Action ID', + index: 'Index', + txId: 'TX ID' + } + } +}; + +export const Overview: Story = { + args: { + ...data + } +}; + +export const WithError: Story = { + args: { + ...data, + errorMessage: 'Something went wrong' + } +}; diff --git a/packages/core/src/ui/components/ProposalProcedures/NoConfidenceAction/NoConfidenceAction.tsx b/packages/core/src/ui/components/ProposalProcedures/NoConfidenceAction/NoConfidenceAction.tsx new file mode 100644 index 0000000000..73034a6fe9 --- /dev/null +++ b/packages/core/src/ui/components/ProposalProcedures/NoConfidenceAction/NoConfidenceAction.tsx @@ -0,0 +1,51 @@ +import React from 'react'; +import { Box, Grid, Flex, Divider, Cell } from '@lace/ui'; +import { DappInfo, DappInfoProps } from '../../DappInfo'; +import { ErrorPane } from '@lace/common'; +import * as Types from './NoConfidenceActionTypes'; +import { TransactionDetails } from '../components/TransactionDetails'; +import { Procedure } from '../components/Procedure'; +import { ActionId } from '../components/ActionId'; + +export interface NoConfidenceActionProps { + dappInfo: Omit; + errorMessage?: string; + data: Types.Data; + translations: Types.Translations; +} + +export const NoConfidenceAction = ({ + dappInfo, + errorMessage, + data: { procedure, txDetails, actionId }, + translations +}: NoConfidenceActionProps): JSX.Element => ( + + + + + {errorMessage && ( + + + + )} + + {/* tx details section */} + + + + + {/* procedure section */} + + {/* action id section*/} + {actionId && ( + <> + + + + + + )} + + +); diff --git a/packages/core/src/ui/components/ProposalProcedures/NoConfidenceAction/NoConfidenceActionTypes.ts b/packages/core/src/ui/components/ProposalProcedures/NoConfidenceAction/NoConfidenceActionTypes.ts new file mode 100644 index 0000000000..0cdfcea7d0 --- /dev/null +++ b/packages/core/src/ui/components/ProposalProcedures/NoConfidenceAction/NoConfidenceActionTypes.ts @@ -0,0 +1,15 @@ +import * as ProcedureTypes from '../components/ProcedureTypes'; +import * as ActionIdTypes from '../components/ActionIdTypes'; +import * as TxDetailsTypes from '../components/TransactionDetailsTypes'; + +export interface Data { + procedure: ProcedureTypes.Procedure; + actionId?: ActionIdTypes.Data; + txDetails: TxDetailsTypes.TxDetails; +} + +export interface Translations { + procedure: ProcedureTypes.Translations; + actionId?: ActionIdTypes.Translations; + txDetails: TxDetailsTypes.Translations; +} diff --git a/packages/core/src/ui/components/ProposalProcedures/NoConfidenceAction/index.ts b/packages/core/src/ui/components/ProposalProcedures/NoConfidenceAction/index.ts new file mode 100644 index 0000000000..2c9c805b7b --- /dev/null +++ b/packages/core/src/ui/components/ProposalProcedures/NoConfidenceAction/index.ts @@ -0,0 +1 @@ +export { NoConfidenceAction } from './NoConfidenceAction'; diff --git a/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/EconomicGroup.tsx b/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/EconomicGroup.tsx new file mode 100644 index 0000000000..0e92b3b4d3 --- /dev/null +++ b/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/EconomicGroup.tsx @@ -0,0 +1,81 @@ +import React from 'react'; +import { Metadata, Text, sx, Cell } from '@lace/ui'; +import { Card } from '../components/Card'; +import * as Types from './ParameterChangeActionTypes'; + +interface Props { + economicGroup: Types.EconomicGroup; + translations: Types.Translations['economicGroup']; +} + +export const EconomicGroup = ({ economicGroup, translations }: Props): JSX.Element => { + const textCss = sx({ + color: '$text_primary' + }); + + return ( + <> + + + {translations.title} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; diff --git a/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/GovernanceGroup.tsx b/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/GovernanceGroup.tsx new file mode 100644 index 0000000000..e0aa03098a --- /dev/null +++ b/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/GovernanceGroup.tsx @@ -0,0 +1,144 @@ +import React from 'react'; +import { Metadata, Text, sx, Divider, Cell } from '@lace/ui'; +import * as Types from './ParameterChangeActionTypes'; + +interface Props { + governanceGroup: Types.GovernanceGroup; + translations: Types.Translations['governanceGroup']; +} + +export const GovernanceGroup = ({ governanceGroup, translations }: Props): JSX.Element => { + const textCss = sx({ + color: '$text_primary' + }); + + return ( + <> + + + {translations.title} + + + + + + + + + + + + + + + + + + + + + + + + + + {translations.dRepVotingThresholds.title} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; diff --git a/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/NetworkGroup.tsx b/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/NetworkGroup.tsx new file mode 100644 index 0000000000..88605cbc76 --- /dev/null +++ b/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/NetworkGroup.tsx @@ -0,0 +1,59 @@ +import React from 'react'; +import { Metadata, Text, sx, Cell } from '@lace/ui'; +import * as Types from './ParameterChangeActionTypes'; + +interface Props { + networkGroup: Types.NetworkGroup; + translations: Types.Translations['networkGroup']; +} + +export const NetworkGroup = ({ networkGroup, translations }: Props): JSX.Element => { + const textCss = sx({ + color: '$text_primary' + }); + + return ( + <> + + + {translations.title} + + + + + + + + + + + + + + + + + + + ); +}; diff --git a/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/ParameterChangeAction.stories.tsx b/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/ParameterChangeAction.stories.tsx new file mode 100644 index 0000000000..2e4fababde --- /dev/null +++ b/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/ParameterChangeAction.stories.tsx @@ -0,0 +1,243 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { ParameterChangeAction } from './ParameterChangeAction'; +import { ComponentProps } from 'react'; + +const customViewports = { + popup: { + name: 'Popup', + styles: { + width: '360px', + height: '600' + } + } +}; + +const meta: Meta = { + title: 'ProposalProcedure/ParameterChangeAction', + component: ParameterChangeAction, + parameters: { + layout: 'centered', + viewport: { + viewports: customViewports, + defaultViewport: 'popup' + } + } +}; + +export default meta; +type Story = StoryObj; + +const data: ComponentProps = { + dappInfo: { + logo: 'https://cdn.mint.handle.me/favicon.png', + name: 'Mint', + url: 'https://preprod.mint.handle.me' + }, + data: { + txDetails: { + txType: 'Protocol Parameter Update', + deposit: '2000', + rewardAccount: 'stake1u89sasnfyjtmgk8ydqfv3fdl52f36x3djedfnzfc9rkgzrcss5vgr' + }, + anchor: { + hash: '0000000000000000000000000000000000000000000000000000000000000000', + url: 'https://www.someurl.io', + txHashUrl: 'https://www.someurl.io/' + }, + protocolParamUpdate: { + maxBlockExUnits: { + memory: '50000000', + step: '4000000000' + }, + maxTxExUnits: { + memory: '10000000', + step: '10000000000' + }, + networkGroup: { + maxBBSize: '65536', + maxBHSize: '1100', + maxTxSize: '16384', + maxCollateralInputs: '3', + maxValSize: '5000' + }, + economicGroup: { + minFeeA: '44', + minFeeB: '155381', + keyDeposit: '2000000', + poolDeposit: '500000000', + minPoolCost: '340000000', + coinsPerUTxOByte: '34482', + price: { + memory: '0.0577', + step: '0.0000721' + }, + rho: '0.003', + tau: '0.2' + }, + technicalGroup: { + a0: '0.3', + nOpt: '150', + collateralPercentage: '150', + costModels: { + PlutusV1: { + 'addInteger-cpu-arguments-intercept': '197_209', + 'addInteger-cpu-arguments-slope': '0' + }, + PlutusV2: { + 'addInteger-cpu-arguments-intercept': '197_209', + 'addInteger-cpu-arguments-slope': '0' + } + }, + eMax: '18' + }, + governanceGroup: { + govActionLifetime: '14', + govActionDeposit: '0', + ccMaxTermLength: '60', + ccMinSize: '0', + drepActivity: '0', + drepDeposit: '0', + dRepVotingThresholds: { + motionNoConfidence: '0.51', + committeeNormal: '0.51', + committeeNoConfidence: '0.51', + updateToConstitution: '0.51', + hardForkInitiation: '0.51', + ppNetworkGroup: '0.51', + ppEconomicGroup: '0.51', + ppTechnicalGroup: '0.51', + ppGovGroup: '0.51', + treasuryWithdrawal: '0.51' + } + } + } + }, + translations: { + txDetails: { + title: 'Transaction Details', + txType: 'Transaction Type', + deposit: 'Deposit', + rewardAccount: 'Reward account' + }, + memory: 'Memory', + step: 'Step', + anchor: { + hash: 'Anchor Hash', + url: 'Anchor URL' + }, + networkGroup: { + title: 'Network group', + maxBBSize: 'Max BB Size', + maxTxSize: 'Max Tx Size', + maxBHSize: 'Max BH Size', + maxValSize: 'Max Val Size', + maxTxExUnits: 'Max TX Ex Units', + maxBlockExUnits: 'Max Blk Ex Units', + maxCollateralInputs: 'Max Coll Inputs', + tooltip: { + maxBBSize: 'Max block body size', + maxTxSize: 'Max transaction size', + maxBHSize: 'Max block header size', + maxValSize: 'Max size of a serialized asset value', + maxTxExUnits: 'Max script execution units in a single transaction', + maxBlockExUnits: 'Max script execution units in a single block', + maxCollateralInputs: 'Max number of collateral inputs' + } + }, + economicGroup: { + title: 'Economic group', + minFeeA: 'Min Fee A', + minFeeB: 'Min Fee B', + keyDeposit: 'Key Deposit', + poolDeposit: 'Pool Deposit', + rho: 'Rho', + tau: 'Tau', + minPoolCost: 'Min Pool Cost', + coinsPerUTxOByte: 'Coins/UTxO Byte', + prices: 'Price', + tooltip: { + minFeeA: 'Min fee coefficient', + minFeeB: 'Min fee constant', + keyDeposit: 'Delegation key Lovelace deposit', + poolDeposit: 'Pool registration Lovelace deposit', + rho: 'Monetary expansion', + tau: 'Treasury expansion', + minPoolCost: 'Min fixed rewards cut for pools', + coinsPerUTxOByte: 'Min Lovelace deposit per byte of serialized UTxO', + prices: 'Prices of Plutus execution units' + } + }, + technicalGroup: { + title: 'Technical group', + a0: 'A0', + eMax: 'EMax', + nOpt: 'NOpt', + costModels: 'Cost Models', + collateralPercentage: 'Coll Percentage', + tooltip: { + a0: 'Pool pledge influence', + eMax: 'Pool retirement maximum epoch', + nOpt: 'Desired number of pools', + costModels: 'Plutus execution cost models', + collateralPercentage: 'Proportion of collateral needed for scripts' + } + }, + governanceGroup: { + title: 'Governance group', + govActionLifetime: 'Gov Act Lifetime', + govActionDeposit: 'Gov Act Deposit', + drepDeposit: 'DRep Deposit', + drepActivity: 'DRep Activity', + ccMinSize: 'CC Min Size', + ccMaxTermLength: 'CC Max Term Length', + dRepVotingThresholds: { + title: 'Governance voting thresholds', + motionNoConfidence: 'Motion No Conf', + committeeNormal: 'Comm Normal', + committeeNoConfidence: 'Comm No Conf', + updateConstitution: 'Update Const', + hardForkInitiation: 'Hard Fork Init', + ppNetworkGroup: 'PP Network Grp', + ppEconomicGroup: 'PP Economic Grp', + ppTechnicalGroup: 'PP Technical Grp', + ppGovernanceGroup: 'PP Governance Grp', + treasuryWithdrawal: 'Treasury Withdraw' + }, + tooltip: { + govActionLifetime: 'governance action maximum lifetime in epochs', + govActionDeposit: 'governance action deposit', + drepDeposit: 'DRep deposit amount', + drepActivity: 'DRep activity period in epochs', + ccMinSize: 'Min constitutional committee size', + ccMaxTermLength: 'Max term length (in epochs) for the constitutional committee members', + dRepVotingThresholds: { + title: 'DRep voting thresholds', + motionNoConfidence: '1. Motion of no-confidence', + committeeNormal: '2a. New committee/threshold (normal state)', + committeeNoConfidence: '2b. New committee/threshold (state of no-confidence)', + updateConstitution: '3. Update to the Constitution or proposal policy', + hardForkInitiation: '4. Hard-fork initiation', + ppNetworkGroup: '5a. Protocol parameter changes, network group', + ppEconomicGroup: '5b. Protocol parameter changes, economic group', + ppTechnicalGroup: '5c. Protocol parameter changes, technical group', + ppGovernanceGroup: '5d. Protocol parameter changes, governance group', + treasuryWithdrawal: '6. Treasury withdrawal' + } + } + } + } +}; + +export const Overview: Story = { + args: { + ...data + } +}; + +export const WithError: Story = { + args: { + ...data, + errorMessage: 'Something went wrong' + } +}; diff --git a/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/ParameterChangeAction.tsx b/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/ParameterChangeAction.tsx new file mode 100644 index 0000000000..871101f707 --- /dev/null +++ b/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/ParameterChangeAction.tsx @@ -0,0 +1,84 @@ +import React from 'react'; +import { Box, Grid, Flex, Divider, Metadata, MetadataLink, Cell } from '@lace/ui'; +import { DappInfo, DappInfoProps } from '../../DappInfo'; +import { ErrorPane } from '@lace/common'; +import { TransactionDetails } from '../components/TransactionDetails'; +import * as Types from './ParameterChangeActionTypes'; +import { EconomicGroup } from './EconomicGroup'; +import { NetworkGroup } from './NetworkGroup'; +import { TechnicalGroup } from './TechnicalGroup'; +import { GovernanceGroup } from './GovernanceGroup'; +import { Card } from '../components/Card'; + +interface ParameterChangeActionProps { + dappInfo: Omit; + errorMessage?: string; + data: Types.Data; + translations: Types.Translations; +} + +export const ParameterChangeAction = ({ + dappInfo, + errorMessage, + data: { txDetails, protocolParamUpdate, anchor }, + translations +}: ParameterChangeActionProps): JSX.Element => { + const { economicGroup, governanceGroup, networkGroup, technicalGroup, maxTxExUnits, maxBlockExUnits } = + protocolParamUpdate; + + return ( + + + + + {errorMessage && ( + + + + )} + + {/* tx details section */} + + <> + + + + + {anchor.txHashUrl ? ( + + ) : ( + + )} + + + + + + + + + + + + + + + + + + + + ); +}; diff --git a/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/ParameterChangeActionTypes.ts b/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/ParameterChangeActionTypes.ts new file mode 100644 index 0000000000..90efec01da --- /dev/null +++ b/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/ParameterChangeActionTypes.ts @@ -0,0 +1,185 @@ +import * as ProcedureTypes from '../components/ProcedureTypes'; +import * as TxDetailsTypes from '../components/TransactionDetailsTypes'; + +export interface Data { + protocolParamUpdate: ProtocolParamUpdate; + txDetails: TxDetailsTypes.TxDetails; + anchor: ProcedureTypes.Procedure['anchor']; +} + +export interface NetworkGroup { + maxBBSize: string; + maxTxSize: string; + maxBHSize: string; + maxValSize: string; + maxCollateralInputs: string; +} + +export interface EconomicGroup { + minFeeA: string; + minFeeB: string; + keyDeposit: string; + poolDeposit: string; + rho: string; + tau: string; + minPoolCost: string; + coinsPerUTxOByte: string; + price: { + memory: string; + step: string; + }; +} + +export interface TechnicalGroup { + a0: string; + eMax: string; + nOpt: string; + costModels: { + PlutusV1: Record; + PlutusV2: Record; + }; + collateralPercentage: string; +} + +export interface GovernanceGroup { + govActionLifetime: string; + govActionDeposit: string; + drepDeposit: string; + drepActivity: string; + ccMinSize: string; + ccMaxTermLength: string; + dRepVotingThresholds: { + motionNoConfidence: string; + committeeNormal: string; + committeeNoConfidence: string; + updateToConstitution: string; + hardForkInitiation: string; + ppNetworkGroup: string; + ppEconomicGroup: string; + ppTechnicalGroup: string; + ppGovGroup: string; + treasuryWithdrawal: string; + }; +} + +interface ProtocolParamUpdate { + maxTxExUnits: { + memory: string; + step: string; + }; + maxBlockExUnits: { + memory: string; + step: string; + }; + networkGroup: NetworkGroup; + economicGroup: EconomicGroup; + technicalGroup: TechnicalGroup; + governanceGroup: GovernanceGroup; +} + +export interface Translations { + txDetails: TxDetailsTypes.Translations; + anchor: ProcedureTypes.Translations['anchor']; + memory: string; + step: string; + networkGroup: { + title: string; + maxBBSize: string; + maxTxSize: string; + maxBHSize: string; + maxValSize: string; + maxTxExUnits: string; + maxBlockExUnits: string; + maxCollateralInputs: string; + tooltip: { + maxBBSize: string; + maxTxSize: string; + maxBHSize: string; + maxValSize: string; + maxTxExUnits: string; + maxBlockExUnits: string; + maxCollateralInputs: string; + }; + }; + economicGroup: { + title: string; + minFeeA: string; + minFeeB: string; + keyDeposit: string; + poolDeposit: string; + rho: string; + tau: string; + minPoolCost: string; + coinsPerUTxOByte: string; + prices: string; + tooltip: { + minFeeA: string; + minFeeB: string; + keyDeposit: string; + poolDeposit: string; + rho: string; + tau: string; + minPoolCost: string; + coinsPerUTxOByte: string; + prices: string; + }; + }; + technicalGroup: { + title: string; + a0: string; + eMax: string; + nOpt: string; + costModels: string; + collateralPercentage: string; + tooltip: { + a0: string; + eMax: string; + nOpt: string; + costModels: string; + collateralPercentage: string; + }; + }; + governanceGroup: { + title: string; + govActionLifetime: string; + govActionDeposit: string; + drepDeposit: string; + drepActivity: string; + ccMinSize: string; + ccMaxTermLength: string; + dRepVotingThresholds: { + title: string; + motionNoConfidence: string; + committeeNormal: string; + committeeNoConfidence: string; + updateConstitution: string; + hardForkInitiation: string; + ppNetworkGroup: string; + ppEconomicGroup: string; + ppTechnicalGroup: string; + ppGovernanceGroup: string; + treasuryWithdrawal: string; + }; + tooltip: { + govActionLifetime: string; + govActionDeposit: string; + drepDeposit: string; + drepActivity: string; + ccMinSize: string; + ccMaxTermLength: string; + dRepVotingThresholds: { + title: string; + motionNoConfidence: string; + committeeNormal: string; + committeeNoConfidence: string; + updateConstitution: string; + hardForkInitiation: string; + ppNetworkGroup: string; + ppEconomicGroup: string; + ppTechnicalGroup: string; + ppGovernanceGroup: string; + treasuryWithdrawal: string; + }; + }; + }; +} diff --git a/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/TechnicalGroup.tsx b/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/TechnicalGroup.tsx new file mode 100644 index 0000000000..fd3776d211 --- /dev/null +++ b/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/TechnicalGroup.tsx @@ -0,0 +1,58 @@ +import React from 'react'; +import { Metadata, Text, sx, Cell } from '@lace/ui'; +import * as Types from './ParameterChangeActionTypes'; + +interface Props { + technicalGroup: Types.TechnicalGroup; + translations: Types.Translations['technicalGroup']; +} + +export const TechnicalGroup = ({ technicalGroup, translations }: Props): JSX.Element => { + const textCss = sx({ + color: '$text_primary' + }); + + // TODO: review cost model syntax/display + // const costModels = Object.entries(technicalGroup.costModels).map(([key, value]) => ({ + // title: key, + // fields: Object.entries(value).map(([cKey, cValue]) => ({ + // label: cKey, + // value: cValue + // })) + // })); + + return ( + <> + + + {translations.title} + + + + + + + + + + + + + + + {/* TODO: review cost model syntax/display */} + {/* + + {translations.costModels} + + + + {costModels.map(({ title, fields }, idx) => ( + 0 ? '$24' : '$0'} mb={costModels.length === idx - 1 ? '$18' : '$0'} key={title}> + + + ))} + */} + + ); +}; diff --git a/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/index.ts b/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/index.ts new file mode 100644 index 0000000000..63caf5ba15 --- /dev/null +++ b/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/index.ts @@ -0,0 +1 @@ +export { ParameterChangeAction } from './ParameterChangeAction'; diff --git a/packages/core/src/ui/components/ProposalProcedures/TreasuryWithdrawalsAction/TreasuryWithdrawalsAction.stories.tsx b/packages/core/src/ui/components/ProposalProcedures/TreasuryWithdrawalsAction/TreasuryWithdrawalsAction.stories.tsx new file mode 100644 index 0000000000..951be959cd --- /dev/null +++ b/packages/core/src/ui/components/ProposalProcedures/TreasuryWithdrawalsAction/TreasuryWithdrawalsAction.stories.tsx @@ -0,0 +1,99 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { TreasuryWithdrawalsAction } from './TreasuryWithdrawalsAction'; +import { ComponentProps } from 'react'; + +const customViewports = { + popup: { + name: 'Popup', + styles: { + width: '360px', + height: '600' + } + } +}; + +const meta: Meta = { + title: 'ProposalProcedure/TreasuryWithdrawalsAction', + component: TreasuryWithdrawalsAction, + parameters: { + layout: 'centered', + viewport: { + viewports: customViewports, + defaultViewport: 'popup' + } + } +}; + +export default meta; +type Story = StoryObj; + +const data: ComponentProps = { + dappInfo: { + logo: 'https://cdn.mint.handle.me/favicon.png', + name: 'Mint', + url: 'https://preprod.mint.handle.me' + }, + data: { + txDetails: { + txType: 'Treasury Withdrawals', + deposit: '2000', + rewardAccount: 'stake1u89sasnfyjtmgk8ydqfv3fdl52f36x3djedfnzfc9rkgzrcss5vgr' + }, + procedure: { + anchor: { + hash: '26bfdcc75a7f4d0cd8c71f0189bc5ca5ad2f4a3db6240c82b5a0edac7f9203e0', + url: 'https://www.someurl.io', + txHashUrl: 'https://www.someurl.io' + } + }, + withdrawals: [ + { + rewardAccount: 'stake1u89sasnfyjtmgk8ydqfv3fdl52f36x3djedfnzfc9rkgzrcss5vgr', + lovelace: '1030939916423' + } + ], + actionId: { + index: '0', + id: '26bfdcc75a7f4d0cd8c71f0189bc5ca5ad2f4a3db6240c82b5a0edac7f9203e0' + } + }, + translations: { + txDetails: { + title: 'Transaction Details', + txType: 'Transaction Type', + deposit: 'Deposit', + rewardAccount: 'Reward account' + }, + procedure: { + anchor: { + hash: 'Anchor Hash', + url: 'Anchor URL' + }, + title: 'Procedure' + }, + actionId: { + title: 'Action ID', + index: 'Index', + txId: 'TX ID' + }, + withdrawals: { + title: 'Withdrawal Details', + lovelace: 'Lovelace Withdrawn', + rewardAccount: 'Reward account' + } + } +}; + +export const Overview: Story = { + args: { + ...data + } +}; + +export const WithError: Story = { + args: { + ...data, + errorMessage: 'Something went wrong' + } +}; diff --git a/packages/core/src/ui/components/ProposalProcedures/TreasuryWithdrawalsAction/TreasuryWithdrawalsAction.tsx b/packages/core/src/ui/components/ProposalProcedures/TreasuryWithdrawalsAction/TreasuryWithdrawalsAction.tsx new file mode 100644 index 0000000000..38a8d2196d --- /dev/null +++ b/packages/core/src/ui/components/ProposalProcedures/TreasuryWithdrawalsAction/TreasuryWithdrawalsAction.tsx @@ -0,0 +1,71 @@ +import React from 'react'; +import { Box, Grid, Flex, Divider, sx, Text, Metadata, Cell } from '@lace/ui'; +import { DappInfo, DappInfoProps } from '../../DappInfo'; +import { ErrorPane } from '@lace/common'; +import * as Types from './TreasuryWithdrawalsActionTypes'; +import { TransactionDetails } from '../components/TransactionDetails'; +import { ActionId } from '../components/ActionId'; +import { Procedure } from '../components/Procedure'; + +interface TreasuryWithdrawalsActionProps { + dappInfo: Omit; + errorMessage?: string; + data: Types.Data; + translations: Types.Translations; +} + +export const TreasuryWithdrawalsAction = ({ + dappInfo, + errorMessage, + data: { txDetails, procedure, withdrawals, actionId }, + translations +}: TreasuryWithdrawalsActionProps): JSX.Element => { + const textCss = sx({ + color: '$text_primary' + }); + + return ( + + + + + {errorMessage && ( + + + + )} + + {/* tx details section */} + + + + + {/* procedure section */} + + + + {translations.withdrawals.title} + + + {withdrawals.map((withdrawal) => ( + + + + + + + + + ))} + {actionId && ( + <> + + + + + + )} + + + ); +}; diff --git a/packages/core/src/ui/components/ProposalProcedures/TreasuryWithdrawalsAction/TreasuryWithdrawalsActionTypes.ts b/packages/core/src/ui/components/ProposalProcedures/TreasuryWithdrawalsAction/TreasuryWithdrawalsActionTypes.ts new file mode 100644 index 0000000000..7de7a0fc0d --- /dev/null +++ b/packages/core/src/ui/components/ProposalProcedures/TreasuryWithdrawalsAction/TreasuryWithdrawalsActionTypes.ts @@ -0,0 +1,24 @@ +import * as ProcedureTypes from '../components/ProcedureTypes'; +import * as ActionIdTypes from '../components/ActionIdTypes'; +import * as TxDetailsTypes from '../components/TransactionDetailsTypes'; + +export interface Data { + actionId?: ActionIdTypes.Data; + txDetails: TxDetailsTypes.TxDetails; + procedure: ProcedureTypes.Procedure; + withdrawals: Array<{ + rewardAccount: string; + lovelace: string; + }>; +} + +export interface Translations { + txDetails: TxDetailsTypes.Translations; + actionId?: ActionIdTypes.Translations; + procedure: ProcedureTypes.Translations; + withdrawals: { + title: string; + rewardAccount: string; + lovelace: string; + }; +} diff --git a/packages/core/src/ui/components/ProposalProcedures/TreasuryWithdrawalsAction/index.ts b/packages/core/src/ui/components/ProposalProcedures/TreasuryWithdrawalsAction/index.ts new file mode 100644 index 0000000000..2b32267088 --- /dev/null +++ b/packages/core/src/ui/components/ProposalProcedures/TreasuryWithdrawalsAction/index.ts @@ -0,0 +1 @@ +export { TreasuryWithdrawalsAction } from './TreasuryWithdrawalsAction'; diff --git a/packages/core/src/ui/components/ProposalProcedures/UpdateCommitteeAction/UpdateCommitteeActionAction.stories.tsx b/packages/core/src/ui/components/ProposalProcedures/UpdateCommitteeAction/UpdateCommitteeActionAction.stories.tsx new file mode 100644 index 0000000000..9750fa1551 --- /dev/null +++ b/packages/core/src/ui/components/ProposalProcedures/UpdateCommitteeAction/UpdateCommitteeActionAction.stories.tsx @@ -0,0 +1,130 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { UpdateCommitteeAction } from './UpdateCommitteeActionAction'; +import { ComponentProps } from 'react'; + +const customViewports = { + popup: { + name: 'Popup', + styles: { + width: '360px', + height: '600' + } + } +}; + +const meta: Meta = { + title: 'ProposalProcedure/UpdateCommitteeAction', + component: UpdateCommitteeAction, + parameters: { + layout: 'centered', + viewport: { + viewports: customViewports, + defaultViewport: 'popup' + } + } +}; + +export default meta; +type Story = StoryObj; + +const data: ComponentProps = { + dappInfo: { + logo: 'https://cdn.mint.handle.me/favicon.png', + name: 'Mint', + url: 'https://preprod.mint.handle.me' + }, + data: { + txDetails: { + txType: 'Hard Fork Initiation', + deposit: '2000', + rewardAccount: 'stake1u89sasnfyjtmgk8ydqfv3fdl52f36x3djedfnzfc9rkgzrcss5vgr' + }, + procedure: { + anchor: { + hash: '26bfdcc75a7f4d0cd8c71f0189bc5ca5ad2f4a3db6240c82b5a0edac7f9203e0', + url: 'https://www.someurl.io', + txHashUrl: 'https://www.someurl.io' + } + }, + actionId: { + index: '0', + id: '26bfdcc75a7f4d0cd8c71f0189bc5ca5ad2f4a3db6240c82b5a0edac7f9203e0' + }, + membersToBeAdded: [ + { + coldCredential: { + hash: '30000000000000000000000000000000000000000000000000000000' + }, + epoch: '1' + }, + { + coldCredential: { + hash: '40000000000000000000000000000000000000000000000000000000' + }, + epoch: '2' + } + ], + membersToBeRemoved: [ + { + hash: '00000000000000000000000000000000000000000000000000000000' + }, + { + hash: '20000000000000000000000000000000000000000000000000000000' + } + ], + newQuorumThreshold: { + denominator: '5', + numerator: '1' + } + }, + translations: { + txDetails: { + title: 'Transaction Details', + txType: 'Transaction Type', + deposit: 'Deposit', + rewardAccount: 'Reward account' + }, + procedure: { + anchor: { + hash: 'Anchor Hash', + url: 'Anchor URL' + }, + title: 'Procedure' + }, + actionId: { + title: 'Action ID', + index: 'Index', + txId: 'TX ID' + }, + membersToBeAdded: { + title: 'Members To Be Added', + coldCredential: { + hash: 'Cold Credential Hash', + epoch: 'Epoch' + } + }, + membersToBeRemoved: { + title: 'Members To Be Removed', + hash: 'Hash' + }, + newQuorumThreshold: { + title: 'New Quorum Threshold', + denominator: 'Denominator', + numerator: 'Numerator' + } + } +}; + +export const Overview: Story = { + args: { + ...data + } +}; + +export const WithError: Story = { + args: { + ...data, + errorMessage: 'Something went wrong' + } +}; diff --git a/packages/core/src/ui/components/ProposalProcedures/UpdateCommitteeAction/UpdateCommitteeActionAction.tsx b/packages/core/src/ui/components/ProposalProcedures/UpdateCommitteeAction/UpdateCommitteeActionAction.tsx new file mode 100644 index 0000000000..97237316d5 --- /dev/null +++ b/packages/core/src/ui/components/ProposalProcedures/UpdateCommitteeAction/UpdateCommitteeActionAction.tsx @@ -0,0 +1,97 @@ +import React from 'react'; +import { Box, Grid, Flex, Divider, sx, Text, Metadata, Cell } from '@lace/ui'; +import { DappInfo, DappInfoProps } from '../../DappInfo'; +import { ErrorPane } from '@lace/common'; +import * as Types from './UpdateCommitteeActionTypes'; +import { Procedure } from '../components/Procedure'; +import { TransactionDetails } from '../components/TransactionDetails'; +import { ActionId } from '../components/ActionId'; + +interface UpdateCommitteeActionProps { + dappInfo: Omit; + errorMessage?: string; + data: Types.Data; + translations: Types.Translations; +} + +export const UpdateCommitteeAction = ({ + dappInfo, + errorMessage, + data: { procedure, txDetails, membersToBeAdded, membersToBeRemoved, actionId }, + translations +}: UpdateCommitteeActionProps): JSX.Element => { + const textCss = sx({ + color: '$text_primary' + }); + + return ( + + + + + {errorMessage && ( + + + + )} + + {/* tx details section */} + + + + + {/* procedure section */} + + + + + {membersToBeAdded.length > 0 && ( + <> + + + {translations.membersToBeAdded.title} + + + {membersToBeAdded.map(({ coldCredential, epoch }) => ( + + + + + + + + + ))} + + )} + {membersToBeRemoved.length > 0 && ( + <> + + + {translations.membersToBeRemoved.title} + + + {membersToBeRemoved.map(({ hash }) => ( + + + + + + + + + ))} + + )} + {actionId && ( + <> + + + + + + )} + + + ); +}; diff --git a/packages/core/src/ui/components/ProposalProcedures/UpdateCommitteeAction/UpdateCommitteeActionTypes.ts b/packages/core/src/ui/components/ProposalProcedures/UpdateCommitteeAction/UpdateCommitteeActionTypes.ts new file mode 100644 index 0000000000..ce8b90745d --- /dev/null +++ b/packages/core/src/ui/components/ProposalProcedures/UpdateCommitteeAction/UpdateCommitteeActionTypes.ts @@ -0,0 +1,50 @@ +import * as ProcedureTypes from '../components/ProcedureTypes'; +import * as ActionIdTypes from '../components/ActionIdTypes'; +import * as TxDetailsTypes from '../components/TransactionDetailsTypes'; + +interface MembersToBeAdded { + coldCredential: { + hash: string; + }; + epoch: string; +} + +interface MembersToBeRemoved { + hash: string; +} + +interface NewQuorumThreshold { + denominator: string; + numerator: string; +} + +export interface Data { + actionId?: ActionIdTypes.Data; + txDetails: TxDetailsTypes.TxDetails; + procedure: ProcedureTypes.Procedure; + membersToBeAdded: MembersToBeAdded[]; + membersToBeRemoved: MembersToBeRemoved[]; + newQuorumThreshold?: NewQuorumThreshold; +} + +export interface Translations { + procedure: ProcedureTypes.Translations; + actionId?: ActionIdTypes.Translations; + txDetails: TxDetailsTypes.Translations; + membersToBeAdded: { + title: string; + coldCredential: { + hash: string; + epoch: string; + }; + }; + membersToBeRemoved: { + title: string; + hash: string; + }; + newQuorumThreshold?: { + title: string; + denominator: string; + numerator: string; + }; +} diff --git a/packages/core/src/ui/components/ProposalProcedures/UpdateCommitteeAction/index.ts b/packages/core/src/ui/components/ProposalProcedures/UpdateCommitteeAction/index.ts new file mode 100644 index 0000000000..b2b57cd254 --- /dev/null +++ b/packages/core/src/ui/components/ProposalProcedures/UpdateCommitteeAction/index.ts @@ -0,0 +1 @@ +export { UpdateCommitteeAction } from './UpdateCommitteeActionAction'; diff --git a/packages/core/src/ui/components/ProposalProcedures/components/ActionId.tsx b/packages/core/src/ui/components/ProposalProcedures/components/ActionId.tsx new file mode 100644 index 0000000000..27112d1a52 --- /dev/null +++ b/packages/core/src/ui/components/ProposalProcedures/components/ActionId.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import { Cell, sx, Metadata, Text } from '@lace/ui'; +import * as Types from './ActionIdTypes'; + +interface Props { + data: Types.Data; + translations: Types.Translations; +} + +export const ActionId = ({ data, translations }: Props): JSX.Element => { + const textCss = sx({ + color: '$text_primary' + }); + + return ( + <> + + + {translations.title} + + + + + + + + + + ); +}; diff --git a/packages/core/src/ui/components/ProposalProcedures/components/ActionIdTypes.ts b/packages/core/src/ui/components/ProposalProcedures/components/ActionIdTypes.ts new file mode 100644 index 0000000000..3dbb0d8824 --- /dev/null +++ b/packages/core/src/ui/components/ProposalProcedures/components/ActionIdTypes.ts @@ -0,0 +1,10 @@ +export interface Data { + index: string; + id: string; +} + +export interface Translations { + title?: string; + index: string; + txId: string; +} diff --git a/packages/core/src/ui/components/ProposalProcedures/components/Card.module.scss b/packages/core/src/ui/components/ProposalProcedures/components/Card.module.scss new file mode 100644 index 0000000000..bf8fa31cab --- /dev/null +++ b/packages/core/src/ui/components/ProposalProcedures/components/Card.module.scss @@ -0,0 +1,3 @@ +.text { + word-break: break-all; +} diff --git a/packages/core/src/ui/components/ProposalProcedures/components/Card.tsx b/packages/core/src/ui/components/ProposalProcedures/components/Card.tsx new file mode 100644 index 0000000000..44bed28c23 --- /dev/null +++ b/packages/core/src/ui/components/ProposalProcedures/components/Card.tsx @@ -0,0 +1,94 @@ +import React from 'react'; +import { Grid, Flex, Card as UICard, Box, sx, Text, TextLink, Tooltip, Cell } from '@lace/ui'; +import styles from './Card.module.scss'; + +interface Item { + label: string; + value: string; + tooltip?: string; + url?: string; +} + +interface Props { + title?: string; + tooltip?: string; + data: Item[]; +} + +export const Card = ({ title, tooltip, data }: Props): JSX.Element => { + const textCss = sx({ + color: '$text_primary' + }); + + const renderRow = (props: Item) => ( + <> + + + {props.tooltip ? ( + + + {props.label} + + + ) : ( + + {props.label} + + )} + + + + + {props.url ? ( + + + + ) : ( + + {props.value} + + )} + + + + ); + + const renderTitle = () => { + if (!title) return <>; + + if (tooltip) { + return ( + + + + {title} + + + + ); + } + + return ( + + + {title} + + + ); + }; + + return ( + + + + + {renderTitle()} + + {data.map((props) => renderRow(props))} + + + + + + ); +}; diff --git a/packages/core/src/ui/components/ProposalProcedures/components/Procedure.tsx b/packages/core/src/ui/components/ProposalProcedures/components/Procedure.tsx new file mode 100644 index 0000000000..682fcc21ff --- /dev/null +++ b/packages/core/src/ui/components/ProposalProcedures/components/Procedure.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import { Metadata, MetadataLink, Text, sx, Cell } from '@lace/ui'; +import * as Types from './ProcedureTypes'; + +interface Props { + data: Types.Procedure; + translations: Types.Translations; +} + +export const Procedure = ({ data, translations }: Props): JSX.Element => { + const textCss = sx({ + color: '$text_primary' + }); + + return ( + <> + + + {translations.title} + + + <> + + + + + + + + + ); +}; diff --git a/packages/core/src/ui/components/ProposalProcedures/components/ProcedureTypes.ts b/packages/core/src/ui/components/ProposalProcedures/components/ProcedureTypes.ts new file mode 100644 index 0000000000..a1ec2c00a7 --- /dev/null +++ b/packages/core/src/ui/components/ProposalProcedures/components/ProcedureTypes.ts @@ -0,0 +1,15 @@ +export interface Procedure { + anchor: { + url: string; + hash: string; + txHashUrl: string; + }; +} + +export interface Translations { + title: string; + anchor: { + url: string; + hash: string; + }; +} diff --git a/packages/core/src/ui/components/ProposalProcedures/components/TransactionDetails.tsx b/packages/core/src/ui/components/ProposalProcedures/components/TransactionDetails.tsx new file mode 100644 index 0000000000..8a21c6b005 --- /dev/null +++ b/packages/core/src/ui/components/ProposalProcedures/components/TransactionDetails.tsx @@ -0,0 +1,37 @@ +import React from 'react'; +import { Metadata, Text, sx, Cell } from '@lace/ui'; +import * as Types from './TransactionDetailsTypes'; + +interface Props { + data: Types.TxDetails; + translations: Types.Translations; +} + +export const TransactionDetails = ({ data, translations }: Props): JSX.Element => { + const textCss = sx({ + color: '$text_primary' + }); + + return ( + <> + + + {translations.title} + + + + + + {data.rewardAccount && ( + + + + )} + {data.deposit && ( + + + + )} + + ); +}; diff --git a/packages/core/src/ui/components/ProposalProcedures/components/TransactionDetailsTypes.ts b/packages/core/src/ui/components/ProposalProcedures/components/TransactionDetailsTypes.ts new file mode 100644 index 0000000000..744b832002 --- /dev/null +++ b/packages/core/src/ui/components/ProposalProcedures/components/TransactionDetailsTypes.ts @@ -0,0 +1,12 @@ +export interface TxDetails { + txType: string; + deposit?: string; + rewardAccount?: string; +} + +export interface Translations { + title: string; + txType: string; + deposit?: string; + rewardAccount?: string; +} diff --git a/packages/core/src/ui/components/ProposalProcedures/index.ts b/packages/core/src/ui/components/ProposalProcedures/index.ts new file mode 100644 index 0000000000..d8d0bc4008 --- /dev/null +++ b/packages/core/src/ui/components/ProposalProcedures/index.ts @@ -0,0 +1,7 @@ +export * from './HardForkInitiationAction'; +export * from './InfoAction'; +export * from './NewConstitutionAction'; +export * from './NoConfidenceAction'; +export * from './ParameterChangeAction'; +export * from './TreasuryWithdrawalsAction'; +export * from './UpdateCommitteeAction'; diff --git a/packages/staking/src/features/DelegationCard/DelegationTooltip.css.ts b/packages/staking/src/features/DelegationCard/DelegationTooltip.css.ts new file mode 100644 index 0000000000..c48a22fb36 --- /dev/null +++ b/packages/staking/src/features/DelegationCard/DelegationTooltip.css.ts @@ -0,0 +1,11 @@ +import { style } from '@vanilla-extract/css'; +import { theme } from '../theme'; + +export const tooltip = style({ + background: theme.colors.$tooltipBgColor, + borderRadius: theme.radius.$small, + boxShadow: theme.elevation.$tooltip, + margin: theme.spacing.$10, + maxWidth: theme.spacing.$214, + padding: theme.spacing.$16, +}); diff --git a/packages/ui/src/design-system/metadata/metadata-link.component.tsx b/packages/ui/src/design-system/metadata/metadata-link.component.tsx index 5eb52078cf..3d766be856 100644 --- a/packages/ui/src/design-system/metadata/metadata-link.component.tsx +++ b/packages/ui/src/design-system/metadata/metadata-link.component.tsx @@ -3,6 +3,7 @@ import React from 'react'; import { Flex } from '../flex'; import { Grid, Cell } from '../grid'; import { TextLink } from '../text-link'; +import { Tooltip } from '../tooltip'; import * as Typography from '../typography'; import * as cx from './metadata.css'; @@ -13,20 +14,32 @@ type Props = OmitClassName<'div'> & { label: string; text: string; url: string; + tooltip?: string; }; export const MetadataLink = ({ label, text, url, + tooltip, ...props }: Readonly): JSX.Element => { return ( - - {label} - + {tooltip == undefined ? ( + + {label} + + ) : ( + +
+ + {label} + +
+
+ )}
diff --git a/packages/ui/src/design-system/metadata/metadata.component.tsx b/packages/ui/src/design-system/metadata/metadata.component.tsx index f9d9fd0aa8..6f15a58c6b 100644 --- a/packages/ui/src/design-system/metadata/metadata.component.tsx +++ b/packages/ui/src/design-system/metadata/metadata.component.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { Flex } from '../flex'; import { Grid, Cell } from '../grid'; +import { Tooltip } from '../tooltip'; import * as Typography from '../typography'; import * as cx from './metadata.css'; @@ -11,19 +12,31 @@ import type { OmitClassName } from '../../types'; type Props = OmitClassName<'div'> & { label: string; text: string; + tooltip?: string; }; export const Metadata = ({ label, text, + tooltip, ...props }: Readonly): JSX.Element => { return ( - - {label} - + {tooltip == undefined ? ( + + {label} + + ) : ( + +
+ + {label} + +
+
+ )}
diff --git a/packages/ui/src/design-system/metadata/metadata.stories.tsx b/packages/ui/src/design-system/metadata/metadata.stories.tsx index a504ca41a2..d91634f4f3 100644 --- a/packages/ui/src/design-system/metadata/metadata.stories.tsx +++ b/packages/ui/src/design-system/metadata/metadata.stories.tsx @@ -42,6 +42,7 @@ const MainComponents = (): JSX.Element => ( diff --git a/packages/ui/src/design-system/tooltip/tooltip-content.css.ts b/packages/ui/src/design-system/tooltip/tooltip-content.css.ts index 29d7cd6999..803e3c84e5 100644 --- a/packages/ui/src/design-system/tooltip/tooltip-content.css.ts +++ b/packages/ui/src/design-system/tooltip/tooltip-content.css.ts @@ -15,5 +15,6 @@ export const tooltipContent = style([ }), { position: 'relative', + wordBreak: 'break-word', }, ]); From 8bbdbfc327987079f5df0b7d85260c62d6b380e0 Mon Sep 17 00:00:00 2001 From: vetalcore Date: Mon, 22 Jan 2024 16:59:59 +0200 Subject: [PATCH 27/68] fix(extension): resolve pr comments --- .../features/dapp/components/DappError.tsx | 4 ++-- .../components/confirm-transaction/hooks.ts | 3 ++- .../src/routes/DappConnectorView.tsx | 10 +++++---- .../components/WalletSetupWizard.tsx | 1 - .../DelegationCard/DelegationTooltip.css.ts | 22 +++++++++++-------- 5 files changed, 23 insertions(+), 17 deletions(-) diff --git a/apps/browser-extension-wallet/src/features/dapp/components/DappError.tsx b/apps/browser-extension-wallet/src/features/dapp/components/DappError.tsx index 438445600d..9c5c85e8d6 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/DappError.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/DappError.tsx @@ -31,12 +31,12 @@ export const DappError = ({ }: DappErrorProps): React.ReactElement => { const { t } = useTranslation(); const handleClose = useCallback(() => { - if (onCloseClick) onCloseClick(); + onCloseClick?.(); window.close(); }, [onCloseClick]); useEffect(() => { - if (onMount) onMount(); + onMount?.(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/hooks.ts b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/hooks.ts index 105b686672..e465b0ff9a 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/hooks.ts +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/hooks.ts @@ -1,3 +1,4 @@ +import isPlainObject from 'lodash/isPlainObject'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { AssetProvider, @@ -66,7 +67,7 @@ export const useCreateAssetList = ({ const convertMetadataArrayToObj = (arr: unknown[]): Record => { const result: Record = {}; for (const item of arr) { - if (typeof item === 'object' && !Array.isArray(item) && item !== null) { + if (isPlainObject(item)) { Object.assign(result, item); } } diff --git a/apps/browser-extension-wallet/src/routes/DappConnectorView.tsx b/apps/browser-extension-wallet/src/routes/DappConnectorView.tsx index 5553484572..3700593c3f 100644 --- a/apps/browser-extension-wallet/src/routes/DappConnectorView.tsx +++ b/apps/browser-extension-wallet/src/routes/DappConnectorView.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useCallback } from 'react'; import { useWalletStore } from '@stores'; import { UnlockWalletContainer } from '@src/features/unlock-wallet'; import { useRedirection, useWalletManager, useAppInit } from '@src/hooks'; @@ -76,6 +76,10 @@ export const DappConnectorView = (): React.ReactElement => { setIsLoadingWallet(true); }, [walletInfo, inMemoryWallet, isLoadingWallet, loadWallet, redirectToSignFailure, redirectToSignSuccess]); + const onCloseClick = useCallback(() => { + tabs.create({ url: 'app.html#/setup' }); + }, []); + if (hasNoAvailableWallet) { return ( @@ -83,9 +87,7 @@ export const DappConnectorView = (): React.ReactElement => { title={t('dapp.noWallet.heading')} description={t('dapp.noWallet.description')} closeButtonLabel={t('dapp.noWallet.closeButton')} - onCloseClick={() => { - tabs.create({ url: 'app.html#/setup' }); - }} + onCloseClick={onCloseClick} containerTestId="no-wallet-container" imageTestId="no-wallet-image" titleTestId="no-wallet-heading" diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/WalletSetupWizard.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/WalletSetupWizard.tsx index bdcabb9205..e9b4740926 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/WalletSetupWizard.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/WalletSetupWizard.tsx @@ -23,7 +23,6 @@ import { WarningModal } from '@src/views/browser-view/components/WarningModal'; import { AnalyticsEventNames, EnhancedAnalyticsOptInStatus, - // PostHogAction, postHogOnboardingActions, UserTrackingType } from '@providers/AnalyticsProvider/analyticsTracker'; diff --git a/packages/staking/src/features/DelegationCard/DelegationTooltip.css.ts b/packages/staking/src/features/DelegationCard/DelegationTooltip.css.ts index c48a22fb36..9a37418d18 100644 --- a/packages/staking/src/features/DelegationCard/DelegationTooltip.css.ts +++ b/packages/staking/src/features/DelegationCard/DelegationTooltip.css.ts @@ -1,11 +1,15 @@ -import { style } from '@vanilla-extract/css'; +import { style, sx } from '@lace/ui'; import { theme } from '../theme'; -export const tooltip = style({ - background: theme.colors.$tooltipBgColor, - borderRadius: theme.radius.$small, - boxShadow: theme.elevation.$tooltip, - margin: theme.spacing.$10, - maxWidth: theme.spacing.$214, - padding: theme.spacing.$16, -}); +export const tooltip = style([ + sx({ + borderRadius: '$small', + boxShadow: '$tooltip', + margin: '$10', + maxWidth: '$214', + padding: '$16', + }), + { + background: theme.colors.$tooltipBgColor, + }, +]); From b1100520818e0e6ee0806a8d7bc296e424bc1c92 Mon Sep 17 00:00:00 2001 From: vetalcore Date: Wed, 24 Jan 2024 11:29:51 +0200 Subject: [PATCH 28/68] fix(extension): remove code duplications for metadata and metadata link components --- .../metadata/metadata-link.component.tsx | 55 +++++-------------- .../design-system/metadata/metadata.base.tsx | 47 ++++++++++++++++ .../metadata/metadata.component.tsx | 42 +++----------- 3 files changed, 68 insertions(+), 76 deletions(-) create mode 100644 packages/ui/src/design-system/metadata/metadata.base.tsx diff --git a/packages/ui/src/design-system/metadata/metadata-link.component.tsx b/packages/ui/src/design-system/metadata/metadata-link.component.tsx index 3d766be856..8053656689 100644 --- a/packages/ui/src/design-system/metadata/metadata-link.component.tsx +++ b/packages/ui/src/design-system/metadata/metadata-link.component.tsx @@ -1,58 +1,31 @@ import React from 'react'; -import { Flex } from '../flex'; -import { Grid, Cell } from '../grid'; import { TextLink } from '../text-link'; -import { Tooltip } from '../tooltip'; -import * as Typography from '../typography'; -import * as cx from './metadata.css'; +import { MetadataBase } from './metadata.base'; -import type { OmitClassName } from '../../types'; +import type { Props as BaseProps } from './metadata.base'; -type Props = OmitClassName<'div'> & { - label: string; +type Props = BaseProps & { text: string; url: string; - tooltip?: string; }; export const MetadataLink = ({ - label, - text, url, - tooltip, + text, ...props }: Readonly): JSX.Element => { return ( - - - {tooltip == undefined ? ( - - {label} - - ) : ( - -
- - {label} - -
-
- )} -
- - - - - - - -
+ + + + + ); }; diff --git a/packages/ui/src/design-system/metadata/metadata.base.tsx b/packages/ui/src/design-system/metadata/metadata.base.tsx new file mode 100644 index 0000000000..fb9dbd65f6 --- /dev/null +++ b/packages/ui/src/design-system/metadata/metadata.base.tsx @@ -0,0 +1,47 @@ +import React from 'react'; + +import { Flex } from '../flex'; +import { Grid, Cell } from '../grid'; +import { Tooltip } from '../tooltip'; +import * as Typography from '../typography'; + +import * as cx from './metadata.css'; + +import type { OmitClassName } from '../../types'; + +export type Props = OmitClassName<'div'> & { + label: string; + tooltip?: string; +}; + +export const MetadataBase = ({ + label, + children, + tooltip, + ...props +}: Readonly): JSX.Element => { + return ( + + + {tooltip == undefined ? ( + + {label} + + ) : ( + +
+ + {label} + +
+
+ )} +
+ + + {children} + + +
+ ); +}; diff --git a/packages/ui/src/design-system/metadata/metadata.component.tsx b/packages/ui/src/design-system/metadata/metadata.component.tsx index 6f15a58c6b..f456c4b986 100644 --- a/packages/ui/src/design-system/metadata/metadata.component.tsx +++ b/packages/ui/src/design-system/metadata/metadata.component.tsx @@ -1,48 +1,20 @@ import React from 'react'; -import { Flex } from '../flex'; -import { Grid, Cell } from '../grid'; -import { Tooltip } from '../tooltip'; import * as Typography from '../typography'; +import { MetadataBase } from './metadata.base'; import * as cx from './metadata.css'; -import type { OmitClassName } from '../../types'; +import type { Props as BaseProps } from './metadata.base'; -type Props = OmitClassName<'div'> & { - label: string; +type Props = BaseProps & { text: string; - tooltip?: string; }; -export const Metadata = ({ - label, - text, - tooltip, - ...props -}: Readonly): JSX.Element => { +export const Metadata = ({ text, ...props }: Readonly): JSX.Element => { return ( - - - {tooltip == undefined ? ( - - {label} - - ) : ( - -
- - {label} - -
-
- )} -
- - - {text} - - -
+ + {text} + ); }; From 8f8b6e38c11df83035adaa4a4e7ffd1530191bbc Mon Sep 17 00:00:00 2001 From: vetalcore Date: Wed, 24 Jan 2024 15:39:42 +0200 Subject: [PATCH 29/68] feat(extension): lw-8600 (#819) * feat(extension): lw-8600 * feat(extension): fix translations * feat(extension): fix tx details mappers * fix(extension): lw-8600 move helpers to common util * fix(extension): resolve pr comment * fix(extension): unify tx details lists * fix(extension): resolve sdet comments * fix(extension): resolve sdet comments --- .../VotingProceduresContainer.tsx | 42 +-- .../VotingProceduresContainer.test.tsx | 25 +- .../src/lib/translations/en.json | 36 ++- .../transaction-detail-slice.test.ts | 11 +- .../stores/slices/activity-detail-slice.ts | 44 ++- .../stores/slices/wallet-activities-slice.ts | 19 +- .../src/stores/types.ts | 15 +- .../src/types/activity-detail.ts | 13 +- .../src/utils/__tests__/inspectTxType.test.ts | 156 ++++++++- .../src/utils/mocks/certificates.ts | 73 +++++ .../src/utils/mocks/governance.ts | 62 ++++ .../src/utils/mocks/raw-transactions.ts | 81 +++++ .../src/utils/tx-inspection.ts | 161 ++++++++-- .../activity/components/ActivityDetail.tsx | 20 +- .../components/TransactionDetailsProxy.tsx | 17 +- .../activity/helpers/common-tx-transformer.ts | 301 +++++++++++++++++- .../helpers/filter-outputs-by-tx-direction.ts | 4 +- .../helpers/reward-history-transformer.ts | 8 +- .../features/activity/helpers/types.ts | 19 +- .../ui/assets/icons/ticket-icon.component.svg | 7 + .../components/Activity/AssetActivityItem.tsx | 82 +++-- .../__tests__/AssetActivityItem.test.tsx | 3 +- .../__tests__/AssetActivityList.test.tsx | 3 +- .../GroupedAssetActivityList.test.tsx | 3 +- .../ActivityTypeIcon.module.scss | 14 + .../ActivityDetail/ActivityTypeIcon.tsx | 70 ++-- .../ActivityDetail/TransactionDetails.tsx | 95 +++++- .../TransactionInputOutput.module.scss | 5 + .../ActivityDetail/TxDetailsList.tsx | 76 +++++ .../components/DetailRow.module.scss | 33 ++ .../ActivityDetail/components/DetailRow.tsx | 19 ++ .../ActivityDetail/components/DetailRows.tsx | 31 ++ .../ActivityDetail/components/index.ts | 1 + .../src/ui/components/ActivityDetail/index.ts | 1 + .../src/ui/components/ActivityDetail/types.ts | 73 +++++ packages/core/src/ui/lib/translations/en.json | 94 +++++- .../hasPendingDelegationTransaction.ts | 10 +- 37 files changed, 1501 insertions(+), 226 deletions(-) create mode 100644 apps/browser-extension-wallet/src/utils/mocks/certificates.ts create mode 100644 apps/browser-extension-wallet/src/utils/mocks/governance.ts create mode 100644 apps/browser-extension-wallet/src/utils/mocks/raw-transactions.ts create mode 100644 packages/core/src/ui/assets/icons/ticket-icon.component.svg create mode 100644 packages/core/src/ui/components/ActivityDetail/ActivityTypeIcon.module.scss create mode 100644 packages/core/src/ui/components/ActivityDetail/TxDetailsList.tsx create mode 100644 packages/core/src/ui/components/ActivityDetail/components/DetailRow.module.scss create mode 100644 packages/core/src/ui/components/ActivityDetail/components/DetailRow.tsx create mode 100644 packages/core/src/ui/components/ActivityDetail/components/DetailRows.tsx create mode 100644 packages/core/src/ui/components/ActivityDetail/components/index.ts create mode 100644 packages/core/src/ui/components/ActivityDetail/types.ts diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/VotingProceduresContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/VotingProceduresContainer.tsx index 8ebeefc4da..72257f1a61 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/VotingProceduresContainer.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/VotingProceduresContainer.tsx @@ -3,52 +3,14 @@ import { useTranslation } from 'react-i18next'; import { VotingProcedures } from '@lace/core'; import { SignTxData } from './types'; import { drepIDasBech32FromHash, votingProceduresInspector } from './utils'; -import { Wallet } from '@lace/cardano'; import { useCExpolorerBaseUrl } from './hooks'; +import { VoterTypeEnum, getVote, getVoterType } from '@src/utils/tx-inspection'; interface Props { signTxData: SignTxData; errorMessage?: string; } -export enum VoterType { - CONSTITUTIONAL_COMMITTEE = 'constitutionalCommittee', - SPO = 'spo', - DREP = 'drep' -} - -export const getVoterType = (voterType: Wallet.Cardano.VoterType): VoterType => { - switch (voterType) { - case Wallet.Cardano.VoterType.ccHotKeyHash: - case Wallet.Cardano.VoterType.ccHotScriptHash: - return VoterType.CONSTITUTIONAL_COMMITTEE; - case Wallet.Cardano.VoterType.stakePoolKeyHash: - return VoterType.SPO; - case Wallet.Cardano.VoterType.dRepKeyHash: - case Wallet.Cardano.VoterType.dRepScriptHash: - default: - return VoterType.DREP; - } -}; - -export enum Votes { - YES = 'yes', - NO = 'no', - ABSTAIN = 'abstain' -} - -export const getVote = (vote: Wallet.Cardano.Vote): Votes => { - switch (vote) { - case Wallet.Cardano.Vote.yes: - return Votes.YES; - case Wallet.Cardano.Vote.no: - return Votes.NO; - case Wallet.Cardano.Vote.abstain: - default: - return Votes.ABSTAIN; - } -}; - export const VotingProceduresContainer = ({ signTxData, errorMessage }: Props): React.ReactElement => { const { t } = useTranslation(); const votingProcedures = votingProceduresInspector(signTxData.tx); @@ -62,7 +24,7 @@ export const VotingProceduresContainer = ({ signTxData, errorMessage }: Props): const voterType = getVoterType(votingProcedure.voter.__typename); const drepId = - voterType === VoterType.DREP + voterType === VoterTypeEnum.DREP ? drepIDasBech32FromHash(votingProcedure.voter.credential.hash) : votingProcedure.voter.credential.hash.toString(); return { diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/VotingProceduresContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/VotingProceduresContainer.test.tsx index fd1c35b40e..3477128747 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/VotingProceduresContainer.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/VotingProceduresContainer.test.tsx @@ -11,12 +11,13 @@ const mockPreprodCexplorerBaseUrl = 'PREPROD_CEXPLORER_BASE_URL'; const mockCexplorerUrlPathsTx = 'CEXPLORER_URL_PATHS.TX'; import * as React from 'react'; import { cleanup, render } from '@testing-library/react'; -import { VoterType, Votes, VotingProceduresContainer, getVote, getVoterType } from '../VotingProceduresContainer'; +import { VotingProceduresContainer } from '../VotingProceduresContainer'; import '@testing-library/jest-dom'; import { act } from 'react-dom/test-utils'; import { buildMockTx } from '@src/utils/mocks/tx'; import { Wallet } from '@lace/cardano'; import { getWrapper } from '../testing.utils'; +import { getVoterType, getVote, VoterTypeEnum, VotesEnum } from '@src/utils/tx-inspection'; jest.mock('@src/stores', () => ({ ...jest.requireActual('@src/stores'), @@ -167,7 +168,7 @@ describe('Testing VotingProceduresContainer component', () => { expect(queryByTestId('VotingProcedures')).toBeInTheDocument(); // eslint-disable-next-line unicorn/consistent-function-scoping const getExpectedDrepId = (type: string) => (hash: Wallet.Crypto.Hash28ByteBase16) => - type === VoterType.DREP + type === VoterTypeEnum.DREP ? Wallet.Cardano.DRepID(Wallet.HexBlob.toTypedBech32('drep', Wallet.HexBlob(hash))) : hash.toString(); expect(mockVotingProcedures).toHaveBeenLastCalledWith( @@ -243,16 +244,20 @@ describe('Testing VotingProceduresContainer component', () => { }); test('testing getVoterType', () => { - expect(getVoterType(constitutionalCommitteeKeyHashVoter.__typename)).toEqual(VoterType.CONSTITUTIONAL_COMMITTEE); - expect(getVoterType(constitutionalCommitteeScriptHashVoter.__typename)).toEqual(VoterType.CONSTITUTIONAL_COMMITTEE); - expect(getVoterType(drepKeyHashVoter.__typename)).toEqual(VoterType.DREP); - expect(getVoterType(drepScriptHashVoter.__typename)).toEqual(VoterType.DREP); - expect(getVoterType(stakePoolKeyHashVoter.__typename)).toEqual(VoterType.SPO); + expect(getVoterType(constitutionalCommitteeKeyHashVoter.__typename)).toEqual( + VoterTypeEnum.CONSTITUTIONAL_COMMITTEE + ); + expect(getVoterType(constitutionalCommitteeScriptHashVoter.__typename)).toEqual( + VoterTypeEnum.CONSTITUTIONAL_COMMITTEE + ); + expect(getVoterType(drepKeyHashVoter.__typename)).toEqual(VoterTypeEnum.DREP); + expect(getVoterType(drepScriptHashVoter.__typename)).toEqual(VoterTypeEnum.DREP); + expect(getVoterType(stakePoolKeyHashVoter.__typename)).toEqual(VoterTypeEnum.SPO); }); test('testing getVote', () => { - expect(getVote(Wallet.Cardano.Vote.yes)).toEqual(Votes.YES); - expect(getVote(Wallet.Cardano.Vote.no)).toEqual(Votes.NO); - expect(getVote(Wallet.Cardano.Vote.abstain)).toEqual(Votes.ABSTAIN); + expect(getVote(Wallet.Cardano.Vote.yes)).toEqual(VotesEnum.YES); + expect(getVote(Wallet.Cardano.Vote.no)).toEqual(VotesEnum.NO); + expect(getVote(Wallet.Cardano.Vote.abstain)).toEqual(VotesEnum.ABSTAIN); }); }); diff --git a/apps/browser-extension-wallet/src/lib/translations/en.json b/apps/browser-extension-wallet/src/lib/translations/en.json index d11cee6c28..aa75f3b324 100644 --- a/apps/browser-extension-wallet/src/lib/translations/en.json +++ b/apps/browser-extension-wallet/src/lib/translations/en.json @@ -23,7 +23,41 @@ "incoming": "Received", "outgoing": "Sent", "sending": "Sending", - "self": "Self Transaction" + "self": "Self Transaction", + "RegisterDelegateRepresentativeCertificate": "DRep Registration", + "UnregisterDelegateRepresentativeCertificate": "DRep De-Registration", + "UpdateDelegateRepresentativeCertificate": "DRep Update", + "ResignCommitteeColdCertificate": "Resign Committee", + "StakeRegistrationDelegationCertificate": "Stake Key Registration & Delgation", + "StakeVoteDelegationCertificate": "Stake Key Registration & DRep Delegation", + "StakeVoteRegistrationDelegationCertificate": "Stake Key Registration, Delegation, & DRep Delegation", + "VoteRegistrationDelegationCertificate": "Stake Key Registration & DRep Delegation", + "AuthorizeCommitteeHotCertificate": "Authorize Committee", + "VoteDelegationCertificate": "Vote Delegation", + "vote": "Vote Signing", + "submitProposal": "Governance Proposal" + }, + "certificates": { + "headings": { + "typename": "Certificate Type", + "anchor": "Anchor", + "drep": "DRep", + "deposit": "Deposit paid", + "coldCredential": "Cold credential", + "hotCredential": "Hot credential" + }, + "typenames": { + "VoteDelegationCertificate": "Vote Delegation", + "StakeVoteDelegationCertificate": "Stake Vote Delegation", + "StakeRegistrationDelegationCertificate": "Stake Registration Delegate", + "VoteRegistrationDelegationCertificate": "Vote Registration Delegate", + "StakeVoteRegistrationDelegationCertificate": "Stake Vote Registration Delegation", + "AuthorizeCommitteeHotCertificate": "Authorize Committee", + "ResignCommitteeColdCertificate": "Resign Committee", + "RegisterDelegateRepresentativeCertificate": "Register Delegate Representative", + "UnregisterDelegateRepresentativeCertificate": "Unregister Delegate Representative", + "UpdateDelegateRepresentativeCertificate": "Update Delegate Representative" + } } } }, diff --git a/apps/browser-extension-wallet/src/stores/slices/__tests__/transaction-detail-slice.test.ts b/apps/browser-extension-wallet/src/stores/slices/__tests__/transaction-detail-slice.test.ts index 88c16ff1bd..6dc3f35187 100644 --- a/apps/browser-extension-wallet/src/stores/slices/__tests__/transaction-detail-slice.test.ts +++ b/apps/browser-extension-wallet/src/stores/slices/__tests__/transaction-detail-slice.test.ts @@ -1,21 +1,22 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ import { renderHook, act } from '@testing-library/react-hooks'; -import { BlockchainProviderSlice, ActivityDetailSlice, WalletInfoSlice } from '../../types'; +import { BlockchainProviderSlice, ActivityDetailSlice, WalletInfoSlice, UISlice } from '../../types'; import { transactionMock } from '../../../utils/mocks/test-helpers'; import { activityDetailSlice } from '../activity-detail-slice'; import '@testing-library/jest-dom'; import create, { GetState, SetState } from 'zustand'; import { mockBlockchainProviders } from '@src/utils/mocks/blockchain-providers'; -import { ActivityStatus } from '@lace/core'; +import { ActivityStatus, TransactionActivityType } from '@lace/core'; const mockActivityDetailSlice = ( set: SetState, - get: GetState + get: GetState ): ActivityDetailSlice => { get = () => ({ blockchainProvider: mockBlockchainProviders() } as BlockchainProviderSlice & ActivityDetailSlice & - WalletInfoSlice); + WalletInfoSlice & + UISlice); return activityDetailSlice({ set, get }); }; @@ -45,7 +46,7 @@ describe('Testing createStoreHook slice', () => { act(() => { result.current.setTransactionActivityDetail({ - type: 'incoming', + type: TransactionActivityType.incoming, status: ActivityStatus.SUCCESS, activity: transactionMock.tx, direction: transactionMock.direction diff --git a/apps/browser-extension-wallet/src/stores/slices/activity-detail-slice.ts b/apps/browser-extension-wallet/src/stores/slices/activity-detail-slice.ts index 5147a6d344..f78de1de4b 100644 --- a/apps/browser-extension-wallet/src/stores/slices/activity-detail-slice.ts +++ b/apps/browser-extension-wallet/src/stores/slices/activity-detail-slice.ts @@ -1,7 +1,14 @@ /* eslint-disable complexity */ /* eslint-disable unicorn/no-array-reduce */ import isEmpty from 'lodash/isEmpty'; -import { ActivityDetailSlice, ZustandHandlers, BlockchainProviderSlice, WalletInfoSlice, SliceCreator } from '../types'; +import { + ActivityDetailSlice, + ZustandHandlers, + BlockchainProviderSlice, + WalletInfoSlice, + SliceCreator, + UISlice +} from '../types'; import { CardanoTxOut, Transaction, ActivityDetail, TransactionActivityDetail } from '../../types'; import { blockTransformer, inputOutputTransformer } from '../../api/transformers'; import { Wallet } from '@lace/cardano'; @@ -10,8 +17,14 @@ import { inspectTxValues } from '@src/utils/tx-inspection'; import { firstValueFrom } from 'rxjs'; import { getAssetsInformation } from '@src/utils/get-assets-information'; import { MAX_POOLS_COUNT } from '@lace/staking'; -import { ActivityStatus, ActivityType } from '@lace/core'; +import { ActivityStatus, DelegationTransactionType, TransactionActivityType } from '@lace/core'; +import type { ActivityType } from '@lace/core'; import { formatDate, formatTime } from '@src/utils/format-date'; +import { + certificateTransformer, + governanceProposalsTransformer, + votingProceduresTransformer +} from '@src/views/browser-view/features/activity/helpers/common-tx-transformer'; /** * validates if the transaction is confirmed @@ -52,11 +65,11 @@ const shouldIncludeFee = ( delegationInfo: Wallet.Cardano.StakeDelegationCertificate[] | undefined ) => !( - type === 'delegationRegistration' || + type === DelegationTransactionType.delegationRegistration || // Existence of any (new) delegationInfo means that this "de-registration" // activity is accompanied by a "delegation" activity, which carries the fees. // However, fees should be shown if de-registration activity is standalone. - (type === 'delegationDeregistration' && !!delegationInfo?.length) + (type === DelegationTransactionType.delegationDeregistration && !!delegationInfo?.length) ); const getPoolInfos = async (poolIds: Wallet.Cardano.PoolId[], stakePoolProvider: Wallet.StakePoolProvider) => { @@ -85,20 +98,21 @@ const buildGetActivityDetail = set, get }: ZustandHandlers< - ActivityDetailSlice & BlockchainProviderSlice & WalletInfoSlice + ActivityDetailSlice & BlockchainProviderSlice & WalletInfoSlice & UISlice >): ActivityDetailSlice['getActivityDetail'] => // eslint-disable-next-line max-statements, sonarjs/cognitive-complexity async ({ coinPrices, fiatCurrency }) => { const { blockchainProvider: { chainHistoryProvider, stakePoolProvider, assetProvider }, inMemoryWallet: wallet, + walletUI: { cardanoCoin }, activityDetail, walletInfo } = get(); set({ fetchingActivityInfo: true }); - if (activityDetail.type === 'rewards') { + if (activityDetail.type === TransactionActivityType.rewards) { const { activity, status, type } = activityDetail; const poolInfos = await getPoolInfos( activity.rewards.map(({ poolId }) => poolId), @@ -177,14 +191,14 @@ const buildGetActivityDetail = const deposit = // since one tx can be split into two (delegation, registration) actions, // ensure only the registration tx carries the deposit - implicitCoin.deposit && type === 'delegationRegistration' + implicitCoin.deposit && type === DelegationTransactionType.delegationRegistration ? Wallet.util.lovelacesToAdaString(implicitCoin.deposit.toString()) : undefined; const depositReclaimValue = Wallet.util.calculateDepositReclaim(implicitCoin); const depositReclaim = // since one tx can be split into two (delegation, de-registration) actions, // ensure only the de-registration tx carries the reclaimed deposit - depositReclaimValue && type === 'delegationDeregistration' + depositReclaimValue && type === DelegationTransactionType.delegationDeregistration ? Wallet.util.lovelacesToAdaString(depositReclaimValue.toString()) : undefined; const feeInAda = Wallet.util.lovelacesToAdaString(tx.body.fee.toString()); @@ -192,7 +206,7 @@ const buildGetActivityDetail = // Delegation tx additional data (LW-3324) const delegationInfo = tx.body.certificates?.filter( - (certificate) => certificate.__typename === 'StakeDelegationCertificate' + (certificate) => certificate.__typename === Wallet.Cardano.CertificateType.StakeDelegation ) as Wallet.Cardano.StakeDelegationCertificate[]; let transaction: ActivityDetail['activity'] = { @@ -205,10 +219,14 @@ const buildGetActivityDetail = addrOutputs: outputs, metadata: txMetadata, includedUtcDate: blocks?.utcDate, - includedUtcTime: blocks?.utcTime + includedUtcTime: blocks?.utcTime, + // TODO: store the raw data here and transform it later so we always have the raw data when needed.(LW-9570) + votingProcedures: votingProceduresTransformer(tx.body.votingProcedures), + proposalProcedures: governanceProposalsTransformer(cardanoCoin, tx.body.proposalProcedures), + certificates: certificateTransformer(cardanoCoin, tx.body.certificates) }; - if (type === 'delegation' && delegationInfo) { + if (type === DelegationTransactionType.delegation && delegationInfo) { const pools = await getPoolInfos( delegationInfo.map(({ poolId }) => poolId), stakePoolProvider @@ -236,7 +254,7 @@ const buildGetActivityDetail = * has all transactions search related actions and states */ export const activityDetailSlice: SliceCreator< - ActivityDetailSlice & BlockchainProviderSlice & WalletInfoSlice, + ActivityDetailSlice & BlockchainProviderSlice & WalletInfoSlice & UISlice, ActivityDetailSlice > = ({ set, get }) => ({ activityDetail: undefined, @@ -245,6 +263,6 @@ export const activityDetailSlice: SliceCreator< setTransactionActivityDetail: ({ activity, direction, status, type }) => set({ activityDetail: { activity, direction, status, type } }), setRewardsActivityDetail: ({ activity }) => - set({ activityDetail: { activity, status: ActivityStatus.SPENDABLE, type: 'rewards' } }), + set({ activityDetail: { activity, status: ActivityStatus.SPENDABLE, type: TransactionActivityType.rewards } }), resetActivityState: () => set({ activityDetail: undefined, fetchingActivityInfo: false }) }); diff --git a/apps/browser-extension-wallet/src/stores/slices/wallet-activities-slice.ts b/apps/browser-extension-wallet/src/stores/slices/wallet-activities-slice.ts index 91cd1f1f9b..c0d628b032 100644 --- a/apps/browser-extension-wallet/src/stores/slices/wallet-activities-slice.ts +++ b/apps/browser-extension-wallet/src/stores/slices/wallet-activities-slice.ts @@ -20,6 +20,7 @@ import { ActivityStatus, AssetActivityItemProps, AssetActivityListProps, + DelegationTransactionType, TransactionActivityType } from '@lace/core'; import { CurrencyInfo, TxDirections } from '@src/types'; @@ -63,32 +64,22 @@ type MappedActivityListProps = Omit & { items: ExtendedActivityProps[]; }; export type FetchWalletActivitiesReturn = Observable>; -export type DelegationTransactionType = Extract< - TransactionActivityType, - 'delegation' | 'delegationRegistration' | 'delegationDeregistration' ->; - -const delegationTransactionTypes: ReadonlySet = new Set([ - 'delegation', - 'delegationRegistration', - 'delegationDeregistration' -]); type DelegationActivityItemProps = Omit & { type: DelegationTransactionType; }; const isDelegationActivity = (activity: ExtendedActivityProps): activity is DelegationActivityItemProps => - delegationTransactionTypes.has(activity.type as DelegationTransactionType); + activity.type in DelegationTransactionType; const getDelegationAmount = (activity: DelegationActivityItemProps) => { const fee = new BigNumber(Number.parseFloat(activity.fee)); - if (activity.type === 'delegationRegistration') { + if (activity.type === DelegationTransactionType.delegationRegistration) { return fee.plus(activity.deposit); } - if (activity.type === 'delegationDeregistration') { + if (activity.type === DelegationTransactionType.delegationDeregistration) { return new BigNumber(activity.depositReclaim).minus(fee); } @@ -358,7 +349,7 @@ const getWalletActivitiesObservable = async ({ amount: `${getDelegationAmount(activity)} ${cardanoCoin.symbol}`, fiatAmount: `${getFiatAmount(getDelegationAmount(activity), cardanoFiatPrice)} ${fiatCurrency.code}` }), - ...(activity.type === 'self' && { + ...(activity.type === TransactionActivityType.self && { amount: `${activity.fee} ${cardanoCoin.symbol}`, fiatAmount: cardanoFiatPrice ? `${getFiatAmount(new BigNumber(activity.fee), cardanoFiatPrice)} ${fiatCurrency.code}` diff --git a/apps/browser-extension-wallet/src/stores/types.ts b/apps/browser-extension-wallet/src/stores/types.ts index f8aad894dc..d5344eab7e 100644 --- a/apps/browser-extension-wallet/src/stores/types.ts +++ b/apps/browser-extension-wallet/src/stores/types.ts @@ -1,12 +1,7 @@ import { SetState, State, GetState, StoreApi } from 'zustand'; import { Wallet, StakePoolSortOptions } from '@lace/cardano'; -import { - AssetActivityListProps, - ActivityStatus, - RewardsActivityType, - TransactionActivityType, - ActivityType -} from '@lace/core'; +import { AssetActivityListProps, ActivityStatus, TransactionActivityType } from '@lace/core'; +import type { ActivityType } from '@lace/core'; import { PriceResult } from '../hooks'; import { NetworkInformation, @@ -140,13 +135,13 @@ export interface ActivityDetailSlice { direction?: TxDirection; } & ( | { - type: RewardsActivityType; + type: TransactionActivityType.rewards; status: ActivityStatus.SPENDABLE; direction?: never; activity: { spendableEpoch: EpochNo; spendableDate: Date; rewards: Reward[] }; } | { - type: TransactionActivityType; + type: Exclude; activity: Wallet.Cardano.HydratedTx | Wallet.Cardano.Tx; direction: TxDirection; } @@ -156,7 +151,7 @@ export interface ActivityDetailSlice { activity: Wallet.Cardano.HydratedTx | Wallet.Cardano.Tx; direction: TxDirection; status: ActivityStatus; - type: TransactionActivityType; + type: Exclude; }) => void; setRewardsActivityDetail: (params: { activity: { spendableEpoch: EpochNo; spendableDate: Date; rewards: Reward[] }; diff --git a/apps/browser-extension-wallet/src/types/activity-detail.ts b/apps/browser-extension-wallet/src/types/activity-detail.ts index 2339669fe3..eb0339f6a4 100644 --- a/apps/browser-extension-wallet/src/types/activity-detail.ts +++ b/apps/browser-extension-wallet/src/types/activity-detail.ts @@ -4,7 +4,11 @@ import type { ActivityStatus, RewardsInfo, TransactionActivityType, - RewardsActivityType + ActivityType, + TxDetailsVotingProceduresTitles, + TxDetailsProposalProceduresTitles, + TxDetailsCertificateTitles, + TxDetails } from '@lace/core'; export enum TxDirections { @@ -33,6 +37,9 @@ type TransactionActivity = { addrOutputs?: TxOutputInput[]; metadata?: TransactionMetadataProps['metadata']; pools?: TransactionPool[]; + votingProcedures?: TxDetails[]; + proposalProcedures?: TxDetails[]; + certificates?: TxDetails[]; }; type RewardsActivity = { @@ -58,7 +65,7 @@ type BlocksInfo = { }; export type TransactionActivityDetail = { - type: TransactionActivityType; + type: Exclude; status: ActivityStatus; activity: TransactionActivity; blocks?: BlocksInfo; @@ -66,7 +73,7 @@ export type TransactionActivityDetail = { }; export type RewardsActivityDetail = { - type: RewardsActivityType; + type: TransactionActivityType.rewards; status: ActivityStatus.SPENDABLE; activity: RewardsActivity; }; 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 ae28a30004..a70605d58d 100644 --- a/apps/browser-extension-wallet/src/utils/__tests__/inspectTxType.test.ts +++ b/apps/browser-extension-wallet/src/utils/__tests__/inspectTxType.test.ts @@ -2,10 +2,12 @@ import '@testing-library/jest-dom'; import { inspectTxType, getTxDirection } from '../tx-inspection'; import { buildMockTx } from '../mocks/tx'; +import { mockConwayCertificates } from '../mocks/certificates'; import { Wallet } from '@lace/cardano'; import { TxDirections } from '@src/types'; import { StakeDelegationCertificate } from '@cardano-sdk/core/dist/cjs/Cardano'; import { Hash28ByteBase16 } from '@cardano-sdk/crypto'; +import { TransactionActivityType, ActivityType } from '@lace/core'; const ADDRESS_1 = Wallet.Cardano.PaymentAddress( 'addr_test1qq585l3hyxgj3nas2v3xymd23vvartfhceme6gv98aaeg9muzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475q2g7k3g' ); @@ -30,10 +32,10 @@ const STAKE_KEY_HASH = Wallet.Cardano.RewardAccount.toHash(REWARD_ACCOUNT); describe('testing tx-inspection utils', () => { describe('Testing getTxDirection function', () => { test('should return proper direction', () => { - expect(getTxDirection({ type: 'incoming' })).toEqual(TxDirections.Incoming); - expect(getTxDirection({ type: 'rewards' })).toEqual(TxDirections.Outgoing); - expect(getTxDirection({ type: 'outgoing' })).toEqual(TxDirections.Outgoing); - expect(getTxDirection({ type: 'self' })).toEqual(TxDirections.Self); + expect(getTxDirection({ type: TransactionActivityType.incoming })).toEqual(TxDirections.Incoming); + expect(getTxDirection({ type: TransactionActivityType.rewards })).toEqual(TxDirections.Outgoing); + expect(getTxDirection({ type: TransactionActivityType.outgoing })).toEqual(TxDirections.Outgoing); + expect(getTxDirection({ type: TransactionActivityType.self })).toEqual(TxDirections.Self); }); }); describe('Testing inspectTxType function', () => { @@ -294,5 +296,151 @@ describe('testing tx-inspection utils', () => { expect(result).toBe('incoming'); }); + + describe('conway era transaction types', () => { + describe('certificates', () => { + const conwayCertificates: { cert: Wallet.Cardano.Certificate; expectedReturn: ActivityType }[] = []; + + for (const [certificateType, certificate] of Object.entries(mockConwayCertificates)) { + conwayCertificates.push({ cert: certificate, expectedReturn: certificateType as ActivityType }); + } + + it.each(conwayCertificates)( + "should return '$expectedReturn' if a certificate of type $cert.__typename exists in the transaction body", + ({ cert, expectedReturn }) => { + const mockTx = buildMockTx({ + certificates: [cert], + inputs: [ + { + address: ADDRESS_1, + ...DEAFULT_TX_INPUT_INFO + } + ], + outputs: [ + { + address: ADDRESS_2, + ...DEFAULT_OUTPUT_VALUE + }, + { + address: ADDRESS_1, + ...DEFAULT_OUTPUT_VALUE + } + ] + }); + const result = inspectTxType({ + tx: mockTx, + walletAddresses: [ + { address: ADDRESS_1, rewardAccount: REWARD_ACCOUNT } + ] as Wallet.KeyManagement.GroupedAddress[] + }); + expect(result).toEqual(expectedReturn); + } + ); + }); + + describe('governance actions', () => { + it('should return "vote" if votingProcedures are present', () => { + const mockTx = buildMockTx({ + inputs: [ + { + address: ADDRESS_1, + ...DEAFULT_TX_INPUT_INFO + } + ], + outputs: [ + { + address: ADDRESS_2, + ...DEFAULT_OUTPUT_VALUE + }, + { + address: ADDRESS_1, + ...DEFAULT_OUTPUT_VALUE + } + ] + }); + const result = inspectTxType({ + tx: { + ...mockTx, + body: { + ...mockTx.body, + votingProcedures: [ + { + voter: { + __typename: Wallet.Cardano.VoterType.dRepKeyHash, + credential: { + hash: Wallet.Crypto.Hash28ByteBase16( + 'c780b43ca9577ea3f28f1fbd39a4d13c3ad9df6987051f5167815974' + ), + type: Wallet.Cardano.CredentialType.KeyHash + } + }, + votes: [ + { + actionId: { + actionIndex: 1, + id: DEAFULT_TX_INPUT_INFO.txId + }, + votingProcedure: { + vote: 1, + // eslint-disable-next-line unicorn/no-null + anchor: null + } + } + ] + } + ] + } + }, + walletAddresses: [ + { address: ADDRESS_1, rewardAccount: REWARD_ACCOUNT } + ] as Wallet.KeyManagement.GroupedAddress[] + }); + + expect(result).toEqual('vote'); + }); + + it('should return "submitProposal" if proposalProcedures are present', () => { + const mockTx = buildMockTx({ + inputs: [ + { + address: ADDRESS_1, + ...DEAFULT_TX_INPUT_INFO + } + ], + outputs: [ + { + address: ADDRESS_2, + ...DEFAULT_OUTPUT_VALUE + }, + { + address: ADDRESS_1, + ...DEFAULT_OUTPUT_VALUE + } + ] + }); + const result = inspectTxType({ + tx: { + ...mockTx, + body: { + ...mockTx.body, + proposalProcedures: [ + { + rewardAccount: REWARD_ACCOUNT, + // eslint-disable-next-line unicorn/no-null + anchor: null, + governanceAction: {} as Wallet.Cardano.GovernanceAction, + deposit: BigInt(1) + } + ] + } + }, + walletAddresses: [ + { address: ADDRESS_1, rewardAccount: REWARD_ACCOUNT } + ] as Wallet.KeyManagement.GroupedAddress[] + }); + expect(result).toEqual('submitProposal'); + }); + }); + }); }); }); diff --git a/apps/browser-extension-wallet/src/utils/mocks/certificates.ts b/apps/browser-extension-wallet/src/utils/mocks/certificates.ts new file mode 100644 index 0000000000..a039f264b8 --- /dev/null +++ b/apps/browser-extension-wallet/src/utils/mocks/certificates.ts @@ -0,0 +1,73 @@ +/* eslint-disable unicorn/no-null, no-magic-numbers */ +import { Wallet } from '@lace/cardano'; +import { ActivityType, ConwayEraCertificatesTypes } from '@lace/core'; + +const DEFAULT_DEPOSIT = BigInt(10_000); +const POOL_ID = Wallet.Cardano.PoolId('pool185g59xpqzt7gf0ljr8v8f3akl95qnmardf2f8auwr3ffx7atjj5'); +const CREDENTIAL = { + type: Wallet.Cardano.CredentialType.KeyHash, + hash: Wallet.Crypto.Hash28ByteBase16('0d94e174732ef9aae73f395ab44507bfa983d65023c11a951f0c32e4') +}; + +export const mockConwayCertificates: Partial< + { + [Type in ActivityType]: Wallet.Cardano.Certificate; + } +> = { + [ConwayEraCertificatesTypes.RegisterDelegateRepresentative]: { + __typename: Wallet.Cardano.CertificateType.RegisterDelegateRepresentative, + deposit: DEFAULT_DEPOSIT, + dRepCredential: CREDENTIAL, + anchor: null + }, + [ConwayEraCertificatesTypes.UnregisterDelegateRepresentative]: { + __typename: Wallet.Cardano.CertificateType.UnregisterDelegateRepresentative, + deposit: DEFAULT_DEPOSIT, + dRepCredential: CREDENTIAL + }, + [ConwayEraCertificatesTypes.UpdateDelegateRepresentative]: { + __typename: Wallet.Cardano.CertificateType.UpdateDelegateRepresentative, + dRepCredential: CREDENTIAL, + anchor: null + }, + [ConwayEraCertificatesTypes.VoteDelegation]: { + __typename: Wallet.Cardano.CertificateType.VoteDelegation, + dRep: CREDENTIAL, + stakeCredential: CREDENTIAL + }, + [ConwayEraCertificatesTypes.StakeVoteDelegation]: { + __typename: Wallet.Cardano.CertificateType.StakeVoteDelegation, + stakeCredential: CREDENTIAL, + dRep: CREDENTIAL, + poolId: POOL_ID + }, + [ConwayEraCertificatesTypes.VoteRegistrationDelegation]: { + __typename: Wallet.Cardano.CertificateType.VoteRegistrationDelegation, + stakeCredential: CREDENTIAL, + deposit: DEFAULT_DEPOSIT, + dRep: CREDENTIAL + }, + [ConwayEraCertificatesTypes.StakeVoteRegistrationDelegation]: { + __typename: Wallet.Cardano.CertificateType.StakeVoteRegistrationDelegation, + poolId: POOL_ID, + deposit: DEFAULT_DEPOSIT, + dRep: CREDENTIAL, + stakeCredential: CREDENTIAL + }, + [ConwayEraCertificatesTypes.StakeRegistrationDelegation]: { + __typename: Wallet.Cardano.CertificateType.StakeRegistrationDelegation, + stakeCredential: CREDENTIAL, + poolId: POOL_ID, + deposit: DEFAULT_DEPOSIT + }, + [ConwayEraCertificatesTypes.AuthorizeCommitteeHot]: { + __typename: Wallet.Cardano.CertificateType.AuthorizeCommitteeHot, + coldCredential: CREDENTIAL, + hotCredential: CREDENTIAL + }, + [ConwayEraCertificatesTypes.ResignCommitteeCold]: { + __typename: Wallet.Cardano.CertificateType.ResignCommitteeCold, + coldCredential: CREDENTIAL, + anchor: null + } +}; diff --git a/apps/browser-extension-wallet/src/utils/mocks/governance.ts b/apps/browser-extension-wallet/src/utils/mocks/governance.ts new file mode 100644 index 0000000000..d5b25abfdb --- /dev/null +++ b/apps/browser-extension-wallet/src/utils/mocks/governance.ts @@ -0,0 +1,62 @@ +import { Wallet } from '@lace/cardano'; + +const VOTER_CREDENTIAL = { + type: Wallet.Cardano.CredentialType.KeyHash, + hash: Wallet.Crypto.Hash28ByteBase16('0d94e174732ef9aae73f395ab44507bfa983d65023c11a951f0c32e4') +}; + +export const mockVotingProcedures: Wallet.Cardano.VotingProcedures = [ + { + voter: { + __typename: Wallet.Cardano.VoterType.dRepKeyHash, + credential: { + hash: Wallet.Crypto.Hash28ByteBase16('c780b43ca9577ea3f28f1fbd39a4d13c3ad9df6987051f5167815974'), + type: Wallet.Cardano.CredentialType.KeyHash + } + }, + votes: [ + { + actionId: { + actionIndex: 1, + id: Wallet.Cardano.TransactionId('bb217abaca60fc0ca68c1555eca6a96d2478547818ae76ce6836133f3cc546e0') + }, + votingProcedure: { + // eslint-disable-next-line unicorn/no-null + anchor: null, + vote: Wallet.Cardano.Vote.yes + } + } + ] + }, + { + voter: { + __typename: Wallet.Cardano.VoterType.dRepKeyHash, + credential: VOTER_CREDENTIAL + } as Wallet.Cardano.DrepKeyHashVoter, + votes: [ + // multiple voters + { + actionId: { + actionIndex: 1, + id: Wallet.Cardano.TransactionId('bb217abaca60fc0ca68c1555eca6a96d2478547818ae76ce6836133f3cc546e0') + }, + votingProcedure: { + // eslint-disable-next-line unicorn/no-null + anchor: null, + vote: Wallet.Cardano.Vote.abstain + } + }, + { + actionId: { + actionIndex: 1, + id: Wallet.Cardano.TransactionId('bb217abaca60fc0ca68c1555eca6a96d2478547818ae76ce6836133f3cc546e0') + }, + votingProcedure: { + // eslint-disable-next-line unicorn/no-null + anchor: null, + vote: Wallet.Cardano.Vote.no + } + } + ] + } +]; diff --git a/apps/browser-extension-wallet/src/utils/mocks/raw-transactions.ts b/apps/browser-extension-wallet/src/utils/mocks/raw-transactions.ts new file mode 100644 index 0000000000..31ce46f3fa --- /dev/null +++ b/apps/browser-extension-wallet/src/utils/mocks/raw-transactions.ts @@ -0,0 +1,81 @@ +/* eslint-disable no-magic-numbers */ +import { Wallet } from '@lace/cardano'; +import { ConwayEraCertificatesTypes } from '@lace/core'; +import { mockVotingProcedures } from './governance'; +import { mockConwayCertificates } from './certificates'; + +export const body: Wallet.Cardano.HydratedTxBody = { + fee: BigInt('168273'), + outputs: [ + { + address: Wallet.Cardano.PaymentAddress( + 'addr_test1qqplclydk8yhxl66ku2q2k869xkzxjtadumefazhcg2teewydv5nffw36jhxyf27hqldy5nwu9mwvrly047f9tqlru5st9mpv2' + ), + value: { + coins: BigInt('3000000'), + assets: new Map([ + [Wallet.Cardano.AssetId('6b8d07d69639e9413dd637a1a815a7323c69c86abbafb66dbfdb1aa7'), BigInt('3000000')] + ]) as Wallet.Cardano.TokenMap + } + } + ], + inputs: [ + { + index: 1, + txId: Wallet.Cardano.TransactionId('e6eb1c8c806ae7f4d9fe148e9c23853607ffba692ef0a464688911ad3374a932'), + address: Wallet.Cardano.PaymentAddress( + 'addr_test1qqplclydk8yhxl66ku2q2k869xkzxjtadumefazhcg2teewydv5nffw36jhxyf27hqldy5nwu9mwvrly047f9tqlru5st9mpv2' + ) + } + ] +}; + +export const partialBlockHeader: Wallet.Cardano.PartialBlockHeader = { + blockNo: Wallet.Cardano.BlockNo(500), + hash: Wallet.Cardano.BlockId('96fbe9b0d4930626fc87ea7f1b6360035e9b8a714e9514f1b836190e95edd59e'), + slot: Wallet.Cardano.Slot(3000) +}; + +export const tx: Wallet.Cardano.HydratedTx = { + id: Wallet.Cardano.TransactionId('e6eb1c8c806ae7f4d9fe148e9c23853607ffba692ef0a464688911ad3374a932'), + index: 0, + blockHeader: partialBlockHeader, + body, + txSize: 297, + inputSource: Wallet.Cardano.InputSource.inputs, + witness: { + signatures: new Map() + } +}; + +export const voteTx: Wallet.Cardano.HydratedTx = { + ...tx, + body: { + ...tx.body, + votingProcedures: mockVotingProcedures + } +}; + +export const drepRegistrationTx: Wallet.Cardano.HydratedTx = { + ...tx, + body: { + ...tx.body, + certificates: [mockConwayCertificates[ConwayEraCertificatesTypes.RegisterDelegateRepresentative]] + } +}; + +export const drepRetirementTx: Wallet.Cardano.HydratedTx = { + ...tx, + body: { + ...tx.body, + certificates: [mockConwayCertificates[ConwayEraCertificatesTypes.UnregisterDelegateRepresentative]] + } +}; + +export const voteDelegationTx: Wallet.Cardano.HydratedTx = { + ...tx, + body: { + ...tx.body, + certificates: [mockConwayCertificates[ConwayEraCertificatesTypes.VoteDelegation]] + } +}; diff --git a/apps/browser-extension-wallet/src/utils/tx-inspection.ts b/apps/browser-extension-wallet/src/utils/tx-inspection.ts index f9ceba29b8..be50897cf3 100644 --- a/apps/browser-extension-wallet/src/utils/tx-inspection.ts +++ b/apps/browser-extension-wallet/src/utils/tx-inspection.ts @@ -1,3 +1,4 @@ +/* eslint-disable unicorn/consistent-destructuring */ /* eslint-disable consistent-return, unicorn/no-array-reduce */ import { createTxInspector, @@ -9,9 +10,17 @@ import { totalAddressOutputsValueInspector } from '@cardano-sdk/core'; import { Wallet } from '@lace/cardano'; -import { ActivityType, TransactionActivityType } from '@lace/core'; +import { + ActivityType, + DelegationTransactionType, + TransactionActivityType, + ConwayEraGovernanceActions, + ConwayEraCertificatesTypes +} from '@lace/core'; import { TxDirection, TxDirections } from '@src/types'; +const { CertificateType } = Wallet.Cardano; + const hasWalletStakeAddress = ( withdrawals: Wallet.Cardano.HydratedTx['body']['withdrawals'], stakeAddress: Wallet.Cardano.RewardAccount @@ -23,17 +32,78 @@ interface TxTypeProps { export const getTxDirection = ({ type }: TxTypeProps): TxDirections => { switch (type) { - case 'incoming': + case TransactionActivityType.incoming: return TxDirections.Incoming; - case 'rewards': - return TxDirections.Outgoing; - case 'outgoing': + case ConwayEraGovernanceActions.vote: + case TransactionActivityType.rewards: + case TransactionActivityType.outgoing: return TxDirections.Outgoing; - case 'self': + case TransactionActivityType.self: return TxDirections.Self; } }; +const governanceCertificateInspection = ( + certificates: Wallet.Cardano.Certificate[] +): ConwayEraCertificatesTypes | ConwayEraGovernanceActions => { + const signedCertificateTypenames: Wallet.Cardano.CertificateType[] = certificates.reduce( + (acc, cert) => [...acc, cert.__typename], + [] + ); + // Assumes single certificate only, should update + + switch (true) { + case signedCertificateTypenames.includes(CertificateType.RegisterDelegateRepresentative): + // TODO: can we map to Cip30TxType instead? + return ConwayEraCertificatesTypes.RegisterDelegateRepresentative; + case signedCertificateTypenames.includes(CertificateType.UnregisterDelegateRepresentative): + return ConwayEraCertificatesTypes.UnregisterDelegateRepresentative; + case signedCertificateTypenames.includes(CertificateType.UpdateDelegateRepresentative): + return ConwayEraCertificatesTypes.UpdateDelegateRepresentative; + case signedCertificateTypenames.includes(CertificateType.VoteDelegation): + return ConwayEraCertificatesTypes.VoteDelegation; + case signedCertificateTypenames.includes(CertificateType.StakeVoteDelegation): + return ConwayEraCertificatesTypes.StakeVoteDelegation; + case signedCertificateTypenames.includes(CertificateType.StakeRegistrationDelegation): + return ConwayEraCertificatesTypes.StakeRegistrationDelegation; + case signedCertificateTypenames.includes(CertificateType.VoteRegistrationDelegation): + return ConwayEraCertificatesTypes.VoteRegistrationDelegation; + case signedCertificateTypenames.includes(CertificateType.StakeVoteRegistrationDelegation): + return ConwayEraCertificatesTypes.StakeVoteRegistrationDelegation; + case signedCertificateTypenames.includes(CertificateType.AuthorizeCommitteeHot): + return ConwayEraCertificatesTypes.AuthorizeCommitteeHot; + case signedCertificateTypenames.includes(CertificateType.ResignCommitteeCold): + return ConwayEraCertificatesTypes.ResignCommitteeCold; + } +}; + +const getWalletAccounts = (walletAddresses: Wallet.KeyManagement.GroupedAddress[]) => + walletAddresses.reduce( + (acc, curr) => ({ + paymentAddresses: [...acc.paymentAddresses, curr.address], + rewardAccounts: [...acc.rewardAccounts, curr.rewardAccount] + }), + { paymentAddresses: [], rewardAccounts: [] } + ); + +const txIncludesConwayCertificates = (certificates?: Wallet.Cardano.Certificate[]) => + certificates?.length > 0 + ? certificates.some((certificate) => + Object.values(ConwayEraCertificatesTypes).includes( + certificate.__typename as unknown as ConwayEraCertificatesTypes + ) + ) + : false; + +const isTxWithRewardsWithdrawal = ( + totalWithdrawals: bigint, + walletAddresses: Wallet.KeyManagement.GroupedAddress[], + txWithdrawals?: Wallet.Cardano.Withdrawal[] +) => + totalWithdrawals > BigInt(0) && + txWithdrawals.length > 0 && + walletAddresses.some((addr) => hasWalletStakeAddress(txWithdrawals, addr.rewardAccount)); + const selfTxInspector = (addresses: Wallet.Cardano.PaymentAddress[]) => (tx: Wallet.Cardano.HydratedTx) => { const notOwnInputs = tx.body.inputs.some((input) => !addresses.includes(input.address)); if (notOwnInputs) return false; @@ -47,14 +117,8 @@ export const inspectTxType = ({ }: { walletAddresses: Wallet.KeyManagement.GroupedAddress[]; tx: Wallet.Cardano.HydratedTx; -}): TransactionActivityType => { - const { paymentAddresses, rewardAccounts } = walletAddresses.reduce( - (acc, curr) => ({ - paymentAddresses: [...acc.paymentAddresses, curr.address], - rewardAccounts: [...acc.rewardAccounts, curr.rewardAccount] - }), - { paymentAddresses: [], rewardAccounts: [] } - ); +}): Exclude => { + const { paymentAddresses, rewardAccounts } = getWalletAccounts(walletAddresses); const inspectionProperties = createTxInspector({ sent: sentInspector({ @@ -68,26 +132,38 @@ export const inspectTxType = ({ selfTransaction: selfTxInspector(paymentAddresses) })(tx); - const withRewardsWithdrawal = - inspectionProperties.totalWithdrawals > BigInt(0) && - walletAddresses.some((addr) => hasWalletStakeAddress(tx.body.withdrawals, addr.rewardAccount)); + if (txIncludesConwayCertificates(tx.body.certificates)) { + return governanceCertificateInspection(tx.body.certificates); + } + + const withRewardsWithdrawal = isTxWithRewardsWithdrawal( + inspectionProperties.totalWithdrawals, + walletAddresses, + tx.body.withdrawals + ); if (inspectionProperties.sent.inputs.length > 0 || withRewardsWithdrawal) { switch (true) { case !!inspectionProperties.delegation[0]?.poolId: - return 'delegation'; + return DelegationTransactionType.delegation; case inspectionProperties.stakeKeyRegistration.length > 0: - return 'delegationRegistration'; + return DelegationTransactionType.delegationRegistration; case inspectionProperties.stakeKeyDeregistration.length > 0: - return 'delegationDeregistration'; + return DelegationTransactionType.delegationDeregistration; + // Voting procedures take priority over proposals + // TODO: use proper inspector when available on sdk side (LW-9569) + case tx.body.votingProcedures?.length > 0: + return ConwayEraGovernanceActions.vote; + case tx.body.proposalProcedures?.length > 0: + return ConwayEraGovernanceActions.submitProposal; case inspectionProperties.selfTransaction: - return 'self'; + return TransactionActivityType.self; default: - return 'outgoing'; + return TransactionActivityType.outgoing; } } - return 'incoming'; + return TransactionActivityType.incoming; }; export const inspectTxValues = ({ @@ -112,3 +188,42 @@ export const inspectTxValues = ({ return inspectionProperties.totalOutputsValue; }; + +export enum VoterTypeEnum { + CONSTITUTIONAL_COMMITTEE = 'constitutionalCommittee', + SPO = 'spo', + DREP = 'drep' +} + +export const getVoterType = (voterType: Wallet.Cardano.VoterType): VoterTypeEnum => { + switch (voterType) { + case Wallet.Cardano.VoterType.ccHotKeyHash: + case Wallet.Cardano.VoterType.ccHotScriptHash: + return VoterTypeEnum.CONSTITUTIONAL_COMMITTEE; + case Wallet.Cardano.VoterType.stakePoolKeyHash: + return VoterTypeEnum.SPO; + case Wallet.Cardano.VoterType.dRepKeyHash: + case Wallet.Cardano.VoterType.dRepScriptHash: + return VoterTypeEnum.DREP; + default: + return VoterTypeEnum.DREP; + } +}; + +export enum VotesEnum { + YES = 'yes', + NO = 'no', + ABSTAIN = 'abstain' +} + +export const getVote = (vote: Wallet.Cardano.Vote): VotesEnum => { + switch (vote) { + case Wallet.Cardano.Vote.yes: + return VotesEnum.YES; + case Wallet.Cardano.Vote.no: + return VotesEnum.NO; + case Wallet.Cardano.Vote.abstain: + default: + return VotesEnum.ABSTAIN; + } +}; diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/activity/components/ActivityDetail.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/activity/components/ActivityDetail.tsx index cc713a0dc1..5fea0d3f65 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/activity/components/ActivityDetail.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/features/activity/components/ActivityDetail.tsx @@ -8,10 +8,12 @@ import { ActivityStatus, TxOutputInput, TxSummary, - ActivityType, useTranslate, - RewardsDetails + RewardsDetails, + TransactionActivityType, + DelegationTransactionType } from '@lace/core'; +import type { ActivityType } from '@lace/core'; import { PriceResult } from '@hooks'; import { useWalletStore } from '@stores'; import { ActivityDetail as ActivityDetailType } from '@src/types'; @@ -78,12 +80,12 @@ interface ActivityDetailProps { } const getTypeLabel = (type: ActivityType, t: ReturnType['t']) => { - if (type === 'rewards') return t('package.core.activityDetails.rewards'); - if (type === 'delegation') return t('package.core.activityDetails.delegation'); - if (type === 'delegationRegistration') return t('package.core.activityDetails.registration'); - if (type === 'delegationDeregistration') return t('package.core.activityDetails.deregistration'); - if (type === 'incoming') return t('package.core.activityDetails.received'); - return t('package.core.activityDetails.sent'); + if (type === DelegationTransactionType.delegationRegistration) return t('package.core.activityDetails.registration'); + if (type === DelegationTransactionType.delegationDeregistration) + return t('package.core.activityDetails.deregistration'); + if (type === TransactionActivityType.incoming) return t('package.core.activityDetails.received'); + if (type === TransactionActivityType.outgoing) return t('package.core.activityDetails.sent'); + return t(`package.core.activityDetails.${type}`); }; export const ActivityDetail = ({ price }: ActivityDetailProps): ReactElement => { @@ -97,7 +99,7 @@ export const ActivityDetail = ({ price }: ActivityDetailProps): ReactElement => const currentTransactionStatus = useMemo( () => - activityDetail.type !== 'rewards' + activityDetail.type !== TransactionActivityType.rewards ? getCurrentTransactionStatus(walletActivities, activityDetail.activity.id) ?? activityInfo?.status : activityInfo?.status, [activityDetail.activity, activityDetail.type, activityInfo?.status, walletActivities] diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/activity/components/TransactionDetailsProxy.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/activity/components/TransactionDetailsProxy.tsx index c80b7d22a8..5fd4b4d3a6 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/activity/components/TransactionDetailsProxy.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/features/activity/components/TransactionDetailsProxy.tsx @@ -1,10 +1,11 @@ import React, { ReactElement, useMemo } from 'react'; -import { ActivityStatus, TransactionDetails } from '@lace/core'; +import { ActivityStatus, DelegationTransactionType, TransactionDetails } from '@lace/core'; import { AddressListType, getTransactionData } from './ActivityDetail'; import { useWalletStore } from '@src/stores'; import { useAnalyticsContext, useExternalLinkOpener } from '@providers'; import { useAddressBookContext, withAddressBookContext } from '@src/features/address-book/context'; import type { TransactionActivityDetail, TxDirection } from '@src/types'; +import { TxDirections } from '@src/types'; import { APP_MODE_POPUP } from '@src/utils/constants'; import { config } from '@src/config'; import { PostHogAction } from '@providers/AnalyticsProvider/analyticsTracker'; @@ -36,10 +37,10 @@ export const TransactionDetailsProxy = withAddressBookContext( return `${CEXPLORER_BASE_URL[environmentName]}/${CEXPLORER_URL_PATHS.Tx}`; }, [CEXPLORER_BASE_URL, CEXPLORER_URL_PATHS.Tx, environmentName]); const getHeaderDescription = () => { - if (activityInfo.type === 'delegation') return '1 token'; + if (activityInfo.type === DelegationTransactionType.delegation) return '1 token'; return ` (${activityInfo?.assetAmount})`; }; - const isIncomingTransaction = direction === 'Incoming'; + const isIncomingTransaction = direction === TxDirections.Incoming; const { addrOutputs, addrInputs, @@ -50,7 +51,10 @@ export const TransactionDetailsProxy = withAddressBookContext( pools, deposit, depositReclaim, - metadata + metadata, + proposalProcedures, + votingProcedures, + certificates } = activityInfo.activity; const txSummary = useMemo( () => @@ -66,7 +70,7 @@ export const TransactionDetailsProxy = withAddressBookContext( const handleOpenExternalLink = () => { analytics.sendEventToPostHog(PostHogAction.ActivityActivityDetailTransactionHashClick); const externalLink = `${explorerBaseUrl}/${hash}`; - externalLink && status === 'success' && openExternalLink(externalLink); + externalLink && status === ActivityStatus.SUCCESS && openExternalLink(externalLink); }; const addressToNameMap = useMemo( @@ -101,6 +105,9 @@ export const TransactionDetailsProxy = withAddressBookContext( isPopupView={isPopupView} sendAnalyticsInputs={() => analytics.sendEventToPostHog(PostHogAction.ActivityActivityDetailInputsClick)} sendAnalyticsOutputs={() => analytics.sendEventToPostHog(PostHogAction.ActivityActivityDetailOutputsClick)} + proposalProcedures={proposalProcedures} + votingProcedures={votingProcedures} + certificates={certificates} {...environmentSpecificProps} /> ); diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/activity/helpers/common-tx-transformer.ts b/apps/browser-extension-wallet/src/views/browser-view/features/activity/helpers/common-tx-transformer.ts index fdfb72a7ea..8588b74a0c 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/activity/helpers/common-tx-transformer.ts +++ b/apps/browser-extension-wallet/src/views/browser-view/features/activity/helpers/common-tx-transformer.ts @@ -1,13 +1,25 @@ import BigNumber from 'bignumber.js'; import { Wallet } from '@lace/cardano'; import { CurrencyInfo, TxDirections } from '@types'; -import { inspectTxValues, inspectTxType } from '@src/utils/tx-inspection'; +import { inspectTxValues, inspectTxType, getVoterType, VoterTypeEnum, getVote } from '@src/utils/tx-inspection'; import { formatDate, formatTime } from '@src/utils/format-date'; import { getTransactionTotalAmount } from '@src/utils/get-transaction-total-amount'; import type { TransformedActivity, TransformedTransactionActivity } from './types'; -import { ActivityStatus } from '@lace/core'; +import { + ActivityStatus, + DelegationTransactionType, + TxDetails, + TxDetailsCertificateTitles, + TxDetailsVotingProceduresTitles, + TxDetailsProposalProceduresTitles, + ConwayEraCertificatesTypes, + TxDetail +} from '@lace/core'; import capitalize from 'lodash/capitalize'; import dayjs from 'dayjs'; +import isEmpty from 'lodash/isEmpty'; + +const { util, GovernanceActionType } = Wallet.Cardano; export interface TxTransformerInput { tx: Wallet.TxInFlight | Wallet.Cardano.HydratedTx; @@ -42,13 +54,13 @@ const splitDelegationTx = (tx: TransformedActivity): TransformedTransactionActiv return [ { ...tx, - type: 'delegation', + type: DelegationTransactionType.delegation, // Deposit already shown in the delegationRegistration deposit: undefined }, { ...tx, - type: 'delegationRegistration', + type: DelegationTransactionType.delegationRegistration, // Let registration show just the deposit, // and the other transaction show fee to avoid duplicity fee: '0' @@ -58,13 +70,13 @@ const splitDelegationTx = (tx: TransformedActivity): TransformedTransactionActiv return [ { ...tx, - type: 'delegation', + type: DelegationTransactionType.delegation, // Reclaimed deposit already shown in the delegationDeregistration depositReclaim: undefined }, { ...tx, - type: 'delegationDeregistration', + type: DelegationTransactionType.delegationDeregistration, // Let de-registration show just the returned deposit, // and the other transaction show fee to avoid duplicity fee: '0' @@ -75,7 +87,7 @@ const splitDelegationTx = (tx: TransformedActivity): TransformedTransactionActiv return [ { ...tx, - type: 'delegation' + type: DelegationTransactionType.delegation } ]; }; @@ -116,7 +128,7 @@ export const txTransformer = async ({ status, resolveInput }: TxTransformerInput): Promise => { - const implicitCoin = Wallet.Cardano.util.computeImplicitCoin(protocolParameters, tx.body); + const implicitCoin = util.computeImplicitCoin(protocolParameters, tx.body); const deposit = implicitCoin.deposit ? Wallet.util.lovelacesToAdaString(implicitCoin.deposit.toString()) : undefined; const depositReclaimValue = Wallet.util.calculateDepositReclaim(implicitCoin); const depositReclaim = depositReclaimValue @@ -172,7 +184,7 @@ export const txTransformer = async ({ // SDK Ticket LW-8767 should fix the type of Input in TxInFlight to contain the address const type = inspectTxType({ walletAddresses, tx: tx as unknown as Wallet.Cardano.HydratedTx }); - if (type === 'delegation') { + if (type === DelegationTransactionType.delegation) { return splitDelegationTx(baseTransformedActivity); } @@ -184,3 +196,274 @@ export const txTransformer = async ({ } ]; }; + +type TransactionCertificate = { + __typename: Wallet.Cardano.CertificateType; + deposit?: BigInt; + dRep?: Wallet.Cardano.DelegateRepresentative; + coldCredential?: Wallet.Cardano.Credential; + hotCredential?: Wallet.Cardano.Credential; + dRepCredential?: Wallet.Cardano.Credential; + anchor?: Wallet.Cardano.Anchor; +}; + +type TransactionGovernanceProposal = { + deposit: BigInt; + rewardAccount: Wallet.Cardano.RewardAccount; + anchor: Wallet.Cardano.Anchor; + governanceAction: { + __typename: Wallet.Cardano.GovernanceActionType; + governanceActionId?: Wallet.Cardano.GovernanceActionId; + protocolParamUpdate?: Wallet.Cardano.ProtocolParametersUpdate; + protocolVersion?: Wallet.Cardano.ProtocolVersion; + withdrawals?: Set<{ + rewardAccount: Wallet.Cardano.RewardAccount; + coin: BigInt; + }>; + membersToBeRemoved?: Set; + membersToBeAdded?: Set; + newQuorumThreshold?: Wallet.Cardano.Fraction; + constitution?: Wallet.Cardano.Constitution; + }; +}; + +const drepMapper = (drep: Wallet.Cardano.DelegateRepresentative) => { + if (Wallet.Cardano.isDRepAlwaysAbstain(drep)) { + return 'alwaysAbstain'; + } else if (Wallet.Cardano.isDRepAlwaysNoConfidence(drep)) { + return 'alwaysNoConfidence'; + } + return Wallet.Cardano.DRepID(drep.hash); +}; + +export const certificateTransformer = ( + cardanoCoin: Wallet.CoinId, + certificates?: TransactionCertificate[] +): TxDetails[] => + // Currently only show enhanced certificate info for conway era certificates pending further discussion + certificates + ?.filter((certificate) => + Object.values(ConwayEraCertificatesTypes).includes( + certificate.__typename as unknown as ConwayEraCertificatesTypes + ) + ) + .map((conwayEraCertificate) => { + const transformedCertificate: TxDetails = [ + { title: 'certificateType', details: [conwayEraCertificate.__typename] } + ]; + + if (conwayEraCertificate.anchor) { + transformedCertificate.push({ + title: 'anchor', + details: [conwayEraCertificate.anchor.url, conwayEraCertificate.anchor.dataHash] + }); + } + + if (conwayEraCertificate.dRep) { + transformedCertificate.push({ + title: 'drep', + details: [drepMapper(conwayEraCertificate.dRep)] + }); + } + + if (conwayEraCertificate.coldCredential) { + transformedCertificate.push({ + title: 'coldCredential', + details: [conwayEraCertificate.coldCredential.hash] + }); + } + + if (conwayEraCertificate.hotCredential) { + transformedCertificate.push({ + title: 'hotCredential', + details: [conwayEraCertificate.hotCredential.hash] + }); + } + + if (conwayEraCertificate.dRepCredential) { + transformedCertificate.push({ + title: 'drepCredential', + details: [conwayEraCertificate.dRepCredential.hash] + }); + } + + if (conwayEraCertificate.deposit) { + transformedCertificate.push({ + title: 'depositPaid', + details: [ + `${Wallet.util.lovelacesToAdaString(conwayEraCertificate.deposit.toString())} ${cardanoCoin.symbol}` + ] + }); + } + + return transformedCertificate; + }); + +export const votingProceduresTransformer = ( + votingProcedures: Wallet.Cardano.VotingProcedures +): TxDetails[] => { + const votingProcedureDetails: TxDetails[] = []; + + votingProcedures?.forEach((procedure) => + procedure.votes.forEach((vote) => { + const voterType = getVoterType(procedure.voter.__typename); + const voterCredential = + voterType === VoterTypeEnum.DREP + ? Wallet.Cardano.DRepID(Wallet.HexBlob.toTypedBech32('drep', Wallet.HexBlob(procedure.voter.credential.hash))) + : procedure.voter.credential.hash; + const detail: TxDetails = [ + { + title: 'voterType', + details: [voterType] + }, + { + title: 'voterCredential', + details: [voterCredential] + }, + { title: 'vote', details: [getVote(vote.votingProcedure.vote)] }, + { ...(!!vote.votingProcedure.anchor && { title: 'anchor', details: [vote.votingProcedure.anchor.url] }) }, + { title: 'proposalTxHash', details: [vote.actionId.id] }, + { title: 'actionIndex', details: [vote.actionId.actionIndex.toString()] } + ]; + + votingProcedureDetails.push(detail.filter((el: TxDetail) => !isEmpty(el))); + }) + ); + + return votingProcedureDetails; +}; + +export const governanceProposalsTransformer = ( + cardanoCoin: Wallet.CoinId, + proposalProcedures?: TransactionGovernanceProposal[] +): TxDetails[] => + proposalProcedures?.map( + ({ + governanceAction: { + __typename, + governanceActionId: { id: actionId, actionIndex }, + protocolParamUpdate, + protocolVersion: { major, minor, patch }, + withdrawals, + membersToBeRemoved, + membersToBeAdded, + newQuorumThreshold, + constitution + }, + deposit, + anchor, + rewardAccount + }) => { + // Default details across all proposals + const transformedProposal: TxDetails = [ + { title: 'type', details: [__typename] }, + { + title: 'governanceActionId', + details: [Wallet.util.lovelacesToAdaString(deposit.toString()) + cardanoCoin.symbol] + }, + { + title: 'rewardAccount', + details: [rewardAccount] + }, + { + title: 'anchor', + details: [anchor.url, anchor.dataHash] + } + ]; + + // Proposal-specific properties + switch (__typename) { + case GovernanceActionType.parameter_change_action: { + transformedProposal.push({ + title: 'protocolParamUpdate', + details: Object.entries(protocolParamUpdate).map( + ([parameter, proposedValue]) => `${parameter}: ${proposedValue.toString()}` + ) + }); + break; + } + case GovernanceActionType.hard_fork_initiation_action: { + const compiledProtovolVersion = [major, minor, patch].filter((x) => !!x).join('.'); + if (actionId) { + transformedProposal.push({ + title: 'governanceActionId', + details: [actionId, actionIndex.toString()] + }); + } + + transformedProposal.push({ + title: 'protocolVersion', + details: [compiledProtovolVersion] + }); + break; + } + case GovernanceActionType.treasury_withdrawals_action: { + const treasuryWithdrawals: string[] = []; + + withdrawals.forEach(({ rewardAccount: withdrawalRewardAccount, coin }) => { + treasuryWithdrawals.push( + `${withdrawalRewardAccount}: ${Wallet.util.lovelacesToAdaString(coin.toString()) + cardanoCoin.symbol}` + ); + }); + + transformedProposal.push({ + title: 'withdrawals', + details: treasuryWithdrawals + }); + break; + } + case GovernanceActionType.no_confidence: { + if (actionId) { + transformedProposal.push({ + title: 'governanceActionId', + details: [actionId, actionIndex.toString()] + }); + } + break; + } + case GovernanceActionType.update_committee: { + const membersToBeRemovedDetails: string[] = []; + const membersToBeAddedDetails: string[] = []; + + membersToBeRemoved.forEach(({ hash }) => { + membersToBeRemovedDetails.push(hash); + }); + + membersToBeAdded.forEach(({ coldCredential }) => { + membersToBeAddedDetails.push(coldCredential.hash); + }); + + if (actionId) { + transformedProposal.push({ + title: 'governanceActionId', + details: [actionId, actionIndex.toString()] + }); + } + + transformedProposal.push( + { + title: 'membersToBeAdded', + details: membersToBeAddedDetails + }, + { + title: 'membersToBeRemoved', + details: membersToBeRemovedDetails + }, + { + title: 'newQuorumThreshold', + details: [`${newQuorumThreshold.numerator}\\${newQuorumThreshold.denominator}`] + } + ); + break; + } + case GovernanceActionType.new_constitution: { + transformedProposal.push({ + title: 'constitutionAnchor', + details: [constitution.anchor.url, constitution.anchor.dataHash] + }); + } + } + + return transformedProposal; + } + ); diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/activity/helpers/filter-outputs-by-tx-direction.ts b/apps/browser-extension-wallet/src/views/browser-view/features/activity/helpers/filter-outputs-by-tx-direction.ts index 079054796c..d7747862e2 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/activity/helpers/filter-outputs-by-tx-direction.ts +++ b/apps/browser-extension-wallet/src/views/browser-view/features/activity/helpers/filter-outputs-by-tx-direction.ts @@ -1,5 +1,5 @@ import { Wallet } from '@lace/cardano'; -import { CardanoTxOut, TxDirection } from '@src/types'; +import { CardanoTxOut, TxDirection, TxDirections } from '@src/types'; /** * filter outputs based on if is an incoming or outgoing tx @@ -9,7 +9,7 @@ export const filterOutputsByTxDirection = ( direction: TxDirection, destinationAddresses: Wallet.Cardano.PaymentAddress[] ): CardanoTxOut[] => { - const isIncomingTx = direction === 'Incoming'; + const isIncomingTx = direction === TxDirections.Incoming; return outputs.filter((output) => isIncomingTx ? destinationAddresses.includes(output.address) : !destinationAddresses.includes(output.address) ); diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/activity/helpers/reward-history-transformer.ts b/apps/browser-extension-wallet/src/views/browser-view/features/activity/helpers/reward-history-transformer.ts index a08f6c785b..0d38e5e99e 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/activity/helpers/reward-history-transformer.ts +++ b/apps/browser-extension-wallet/src/views/browser-view/features/activity/helpers/reward-history-transformer.ts @@ -4,9 +4,9 @@ import type { TransformedRewardsActivity } from './types'; import dayjs from 'dayjs'; import { formatDate, formatTime } from '@src/utils/format-date'; import BigNumber from 'bignumber.js'; -import type { CurrencyInfo } from '@src/types'; +import { TxDirections, CurrencyInfo } from '@src/types'; import type { Reward } from '@cardano-sdk/core'; -import { ActivityStatus } from '@lace/core'; +import { ActivityStatus, TransactionActivityType } from '@lace/core'; interface RewardHistoryTransformerInput { rewards: Reward[]; // TODO this supposes rewards grouped by epoch which is a bit fragile @@ -34,8 +34,8 @@ export const rewardHistoryTransformer = ({ const totalRewardsAmount = Wallet.BigIntMath.sum(rewards.map(({ rewards: _rewards }) => _rewards)); return { - type: 'rewards', - direction: 'Incoming', + type: TransactionActivityType.rewards, + direction: TxDirections.Incoming, amount: Wallet.util.getFormattedAmount({ amount: totalRewardsAmount.toString(), cardanoCoin }), fiatAmount: getFormattedFiatAmount({ amount: new BigNumber(totalRewardsAmount.toString()), diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/activity/helpers/types.ts b/apps/browser-extension-wallet/src/views/browser-view/features/activity/helpers/types.ts index 6a08ada9ef..7ad6c518fe 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/activity/helpers/types.ts +++ b/apps/browser-extension-wallet/src/views/browser-view/features/activity/helpers/types.ts @@ -1,10 +1,5 @@ -import type { - ActivityAssetProp, - ActivityStatus, - ActivityType, - RewardsActivityType, - TransactionActivityType -} from '@lace/core'; +import type { ActivityAssetProp, ActivityStatus, ActivityType, TransactionActivityType } from '@lace/core'; +import { TxDirection } from '@src/types'; export type TransformedActivity = { id?: string; @@ -24,7 +19,7 @@ export type TransformedActivity = { */ fiatAmount: string; /** - * Activity status: `sending` | `success` | 'error + * Activity status: `sending` | `success` | 'error' | 'spendable' */ status?: ActivityStatus; /** @@ -45,13 +40,15 @@ export type TransformedActivity = { * Direction: 'Incoming' | 'Outgoing' | 'Self' * TODO: Create a separate package for common types across apps/packages */ - direction?: 'Incoming' | 'Outgoing' | 'Self'; + direction?: TxDirection; /** * assets details */ assets?: ActivityAssetProp[]; }; -export type TransformedTransactionActivity = TransformedActivity & { type: TransactionActivityType }; +export type TransformedTransactionActivity = TransformedActivity & { + type: Exclude; +}; -export type TransformedRewardsActivity = TransformedActivity & { type: RewardsActivityType }; +export type TransformedRewardsActivity = TransformedActivity & { type: TransactionActivityType.rewards }; diff --git a/packages/core/src/ui/assets/icons/ticket-icon.component.svg b/packages/core/src/ui/assets/icons/ticket-icon.component.svg new file mode 100644 index 0000000000..d1bc0535bd --- /dev/null +++ b/packages/core/src/ui/assets/icons/ticket-icon.component.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/packages/core/src/ui/components/Activity/AssetActivityItem.tsx b/packages/core/src/ui/components/Activity/AssetActivityItem.tsx index 0d2afbde59..e2f308122f 100644 --- a/packages/core/src/ui/components/Activity/AssetActivityItem.tsx +++ b/packages/core/src/ui/components/Activity/AssetActivityItem.tsx @@ -6,11 +6,18 @@ import Icon from '@ant-design/icons'; import { getTextWidth } from '@lace/common'; import { ReactComponent as PendingIcon } from '../../assets/icons/pending.component.svg'; import { ReactComponent as ErrorIcon } from '../../assets/icons/error.component.svg'; -import styles from './AssetActivityItem.module.scss'; import pluralize from 'pluralize'; import { txIconSize } from '@src/ui/utils/icon-size'; import { useTranslate } from '@src/ui/hooks'; -import { ActivityTypeIcon, ActivityType } from '../ActivityDetail'; +import { + DelegationTransactionType, + TransactionActivityType, + ConwayEraGovernanceActions, + ConwayEraCertificatesTypes +} from '../ActivityDetail/types'; +import type { ActivityType } from '../ActivityDetail/types'; +import styles from './AssetActivityItem.module.scss'; +import { ActivityTypeIcon } from '../ActivityDetail/ActivityTypeIcon'; export type ActivityAssetInfo = { ticker: string }; export type ActivityAssetProp = { id: string; val: string; info?: ActivityAssetInfo }; @@ -61,7 +68,6 @@ export interface AssetActivityItemProps { assets?: ActivityAssetProp[]; } -const DelegationTransactionTypes = new Set(['delegation', 'delegationRegistration', 'delegationDeregistration']); const DELEGATION_ASSET_NUMBER = 1; interface ActivityStatusIconProps { @@ -77,7 +83,7 @@ const ActivityStatusIcon = ({ status, type }: ActivityStatusIconProps) => { case ActivityStatus.SUCCESS: return ; case ActivityStatus.SPENDABLE: - return ; + return ; case ActivityStatus.PENDING: return ; case ActivityStatus.ERROR: @@ -86,16 +92,6 @@ const ActivityStatusIcon = ({ status, type }: ActivityStatusIconProps) => { } }; -const translationTypes = { - delegation: 'package.core.assetActivityItem.entry.name.delegation', - delegationDeregistration: 'package.core.assetActivityItem.entry.name.delegationDeregistration', - delegationRegistration: 'package.core.assetActivityItem.entry.name.delegationRegistration', - rewards: 'package.core.assetActivityItem.entry.name.rewards', - incoming: 'package.core.assetActivityItem.entry.name.incoming', - outgoing: 'package.core.assetActivityItem.entry.name.outgoing', - self: 'package.core.assetActivityItem.entry.name.self' -}; - // TODO: Handle pluralization and i18n of assetsNumber when we will have more than Ada. export const AssetActivityItem = ({ amount, @@ -109,12 +105,47 @@ export const AssetActivityItem = ({ formattedTimestamp }: AssetActivityItemProps): React.ReactElement => { const { t } = useTranslate(); + + const translationTypes: Record = { + [TransactionActivityType.rewards]: 'package.core.assetActivityItem.entry.name.rewards', + [TransactionActivityType.incoming]: 'package.core.assetActivityItem.entry.name.incoming', + [TransactionActivityType.outgoing]: 'package.core.assetActivityItem.entry.name.outgoing', + [TransactionActivityType.self]: 'package.core.assetActivityItem.entry.name.self', + [DelegationTransactionType.delegation]: 'package.core.assetActivityItem.entry.name.delegation', + [DelegationTransactionType.delegationDeregistration]: + 'package.core.assetActivityItem.entry.name.delegationDeregistration', + [DelegationTransactionType.delegationRegistration]: + 'package.core.assetActivityItem.entry.name.delegationRegistration', + [ConwayEraGovernanceActions.vote]: 'package.core.assetActivityItem.entry.name.vote', + [ConwayEraGovernanceActions.submitProposal]: 'package.core.assetActivityItem.entry.name.submitProposal', + [ConwayEraCertificatesTypes.RegisterDelegateRepresentative]: + 'package.core.assetActivityItem.entry.name.RegisterDelegateRepresentativeCertificate', + [ConwayEraCertificatesTypes.UnregisterDelegateRepresentative]: + 'package.core.assetActivityItem.entry.name.UnregisterDelegateRepresentativeCertificate', + [ConwayEraCertificatesTypes.UpdateDelegateRepresentative]: + 'package.core.assetActivityItem.entry.name.UpdateDelegateRepresentativeCertificate', + [ConwayEraCertificatesTypes.StakeVoteDelegation]: + 'package.core.assetActivityItem.entry.name.StakeVoteDelegationCertificate', + [ConwayEraCertificatesTypes.StakeRegistrationDelegation]: + 'package.core.assetActivityItem.entry.name.StakeRegistrationDelegateCertificate', + [ConwayEraCertificatesTypes.StakeVoteRegistrationDelegation]: + 'package.core.assetActivityItem.entry.name.StakeVoteRegistrationDelegateCertificate', + [ConwayEraCertificatesTypes.VoteDelegation]: 'package.core.assetActivityItem.entry.name.VoteDelegationCertificate', + [ConwayEraCertificatesTypes.VoteRegistrationDelegation]: + 'package.core.assetActivityItem.entry.name.VoteRegistrationDelegateCertificate', + [ConwayEraCertificatesTypes.ResignCommitteeCold]: + 'package.core.assetActivityItem.entry.name.ResignCommitteeColdCertificate', + [ConwayEraCertificatesTypes.AuthorizeCommitteeHot]: + 'package.core.assetActivityItem.entry.name.AuthorizeCommitteeHotCertificate' + }; + const ref = useRef(null); const [assetsToShow, setAssetsToShow] = React.useState(0); const getText = useCallback( (items: number): { text: string; suffix: string } => { - if (DelegationTransactionTypes.has(type) || type === 'self') return { text: amount, suffix: '' }; + if (type in DelegationTransactionType || type === TransactionActivityType.self) + return { text: amount, suffix: '' }; const assetsIdsText = assets ?.slice(0, items) @@ -159,15 +190,16 @@ export const AssetActivityItem = ({ const isPendingTx = status === ActivityStatus.PENDING; const assetsText = useMemo(() => getText(assetsToShow), [getText, assetsToShow]); - const assetAmountContent = DelegationTransactionTypes.has(type) ? ( -

- {DELEGATION_ASSET_NUMBER} {t('package.core.assetActivityItem.entry.token')} -

- ) : ( -

- {pluralize('package.core.assetActivityItem.entry.token', assetsNumber, true)} -

- ); + const assetAmountContent = + type in DelegationTransactionType ? ( +

+ {DELEGATION_ASSET_NUMBER} {t('package.core.assetActivityItem.entry.token')} +

+ ) : ( +

+ {pluralize('package.core.assetActivityItem.entry.token', assetsNumber, true)} +

+ ); const descriptionContent = formattedTimestamp ? (

{formattedTimestamp} @@ -188,7 +220,7 @@ export const AssetActivityItem = ({

- {isPendingTx && type !== 'self' && !DelegationTransactionTypes.has(type) + {isPendingTx && type !== TransactionActivityType.self && !(type in DelegationTransactionType) ? t('package.core.assetActivityItem.entry.name.sending') : t(translationTypes[type])}
diff --git a/packages/core/src/ui/components/Activity/__tests__/AssetActivityItem.test.tsx b/packages/core/src/ui/components/Activity/__tests__/AssetActivityItem.test.tsx index b42bb1c35c..3744e5c2bd 100644 --- a/packages/core/src/ui/components/Activity/__tests__/AssetActivityItem.test.tsx +++ b/packages/core/src/ui/components/Activity/__tests__/AssetActivityItem.test.tsx @@ -3,13 +3,14 @@ import * as React from 'react'; import { render, within, fireEvent, queryByTestId } from '@testing-library/react'; import '@testing-library/jest-dom'; import { AssetActivityItem, AssetActivityItemProps, ActivityStatus } from '../AssetActivityItem'; +import { TransactionActivityType } from '../../ActivityDetail/types'; const assetsAmountTestId = 'asset-amount'; describe('Testing AssetActivityItem component', () => { const props: AssetActivityItemProps = { id: '1', - type: 'outgoing', + type: TransactionActivityType.outgoing, amount: '100', fiatAmount: '300 $', formattedTimestamp: 'Timestamp', diff --git a/packages/core/src/ui/components/Activity/__tests__/AssetActivityList.test.tsx b/packages/core/src/ui/components/Activity/__tests__/AssetActivityList.test.tsx index bc29dd1314..9c7b8e2e79 100644 --- a/packages/core/src/ui/components/Activity/__tests__/AssetActivityList.test.tsx +++ b/packages/core/src/ui/components/Activity/__tests__/AssetActivityList.test.tsx @@ -4,6 +4,7 @@ import { render, fireEvent } from '@testing-library/react'; import '@testing-library/jest-dom'; import { AssetActivityList, AssetActivityListProps } from '../AssetActivityList'; import { ActivityStatus } from '../AssetActivityItem'; +import { TransactionActivityType } from '../../ActivityDetail'; const activityTranslations = { asset: 'asset', @@ -22,7 +23,7 @@ describe('Testing AssetActivityList component', () => { const props: AssetActivityListProps = { onExpand: jest.fn(), items: Array.from({ length: 12 }, () => ({ - type: 'outgoing', + type: TransactionActivityType.outgoing, name: 'Sent', description: 'ADA', date: new Date('2021-01-01'), diff --git a/packages/core/src/ui/components/Activity/__tests__/GroupedAssetActivityList.test.tsx b/packages/core/src/ui/components/Activity/__tests__/GroupedAssetActivityList.test.tsx index 9d94a228fe..a3655b9085 100644 --- a/packages/core/src/ui/components/Activity/__tests__/GroupedAssetActivityList.test.tsx +++ b/packages/core/src/ui/components/Activity/__tests__/GroupedAssetActivityList.test.tsx @@ -4,9 +4,10 @@ import { render } from '@testing-library/react'; import '@testing-library/jest-dom'; import { GroupedAssetActivityList, GroupedAssetActivityListProps } from '../GroupedAssetActivityList'; import { AssetActivityItemProps } from '../AssetActivityItem'; +import { TransactionActivityType } from '../../ActivityDetail'; const activityItem: AssetActivityItemProps = { - type: 'outgoing', + type: TransactionActivityType.outgoing, amount: '100 ADA', fiatAmount: '300 $', formattedTimestamp: 'FormattedTimestamp', diff --git a/packages/core/src/ui/components/ActivityDetail/ActivityTypeIcon.module.scss b/packages/core/src/ui/components/ActivityDetail/ActivityTypeIcon.module.scss new file mode 100644 index 0000000000..471316e923 --- /dev/null +++ b/packages/core/src/ui/components/ActivityDetail/ActivityTypeIcon.module.scss @@ -0,0 +1,14 @@ +@import '../../styles/theme.scss'; +.governance { + svg { + path, + circle, + line, + polyline, + rect, + ellipse { + fill: var(--text-color-blue, #3489f7); + color: var(--text-color-blue, #3489f7); + } + } +} diff --git a/packages/core/src/ui/components/ActivityDetail/ActivityTypeIcon.tsx b/packages/core/src/ui/components/ActivityDetail/ActivityTypeIcon.tsx index 8d69396df2..663c6a605f 100644 --- a/packages/core/src/ui/components/ActivityDetail/ActivityTypeIcon.tsx +++ b/packages/core/src/ui/components/ActivityDetail/ActivityTypeIcon.tsx @@ -1,4 +1,8 @@ +/* eslint-disable no-console */ import React from 'react'; +import cn from 'classnames'; +import Icon, { QuestionOutlined } from '@ant-design/icons'; +import { txIconSize } from '@src/ui/utils/icon-size'; import { ReactComponent as OutgoingIcon } from '../../assets/icons/outgoing.component.svg'; import { ReactComponent as IncomingIcon } from '../../assets/icons/incoming.component.svg'; import { ReactComponent as SelfIcon } from '../../assets/icons/self-transaction.component.svg'; @@ -6,38 +10,62 @@ import { ReactComponent as DelegationIcon } from '../../assets/icons/delegation. import { ReactComponent as RegistrationIcon } from '../../assets/icons/registration.component.svg'; import { ReactComponent as DeregistrationIcon } from '../../assets/icons/deregistration.component.svg'; import { ReactComponent as RewardsIcon } from '../../assets/icons/rewards.component.svg'; -import Icon, { QuestionOutlined } from '@ant-design/icons'; -import { txIconSize } from '@src/ui/utils/icon-size'; +import { ReactComponent as VoteIcon } from '../../assets/icons/ticket-icon.component.svg'; +import { + ActivityType, + ConwayEraCertificatesTypes, + ConwayEraGovernanceActions, + DelegationTransactionType, + TransactionActivityType +} from './types'; -export type TransactionActivityType = - | 'outgoing' - | 'incoming' - | 'delegation' - | 'delegationRegistration' - | 'delegationDeregistration' - | 'self'; - -export type RewardsActivityType = 'rewards'; - -export type ActivityType = TransactionActivityType | RewardsActivityType; +import styles from './ActivityTypeIcon.module.scss'; export interface ActivityTypeIconProps { type: ActivityType; } const activityTypeIcon: Record>> = { - outgoing: OutgoingIcon, - incoming: IncomingIcon, - self: SelfIcon, - delegation: DelegationIcon, - delegationRegistration: RegistrationIcon, - delegationDeregistration: DeregistrationIcon, - rewards: RewardsIcon + [TransactionActivityType.rewards]: RewardsIcon, + [TransactionActivityType.incoming]: IncomingIcon, + [TransactionActivityType.outgoing]: OutgoingIcon, + [TransactionActivityType.self]: SelfIcon, + [DelegationTransactionType.delegation]: DelegationIcon, + [ConwayEraGovernanceActions.vote]: VoteIcon, + [ConwayEraGovernanceActions.submitProposal]: DelegationIcon, + [DelegationTransactionType.delegationDeregistration]: DeregistrationIcon, + [DelegationTransactionType.delegationRegistration]: RegistrationIcon, + [ConwayEraCertificatesTypes.RegisterDelegateRepresentative]: RegistrationIcon, + [ConwayEraCertificatesTypes.UnregisterDelegateRepresentative]: DeregistrationIcon, + [ConwayEraCertificatesTypes.StakeRegistrationDelegation]: RegistrationIcon, + [ConwayEraCertificatesTypes.StakeVoteRegistrationDelegation]: DelegationIcon, + [ConwayEraCertificatesTypes.VoteRegistrationDelegation]: DelegationIcon, + [ConwayEraCertificatesTypes.UpdateDelegateRepresentative]: RegistrationIcon, + [ConwayEraCertificatesTypes.StakeVoteDelegation]: DelegationIcon, + [ConwayEraCertificatesTypes.VoteDelegation]: DelegationIcon, + [ConwayEraCertificatesTypes.ResignCommitteeCold]: DelegationIcon, + [ConwayEraCertificatesTypes.AuthorizeCommitteeHot]: DelegationIcon }; export const ActivityTypeIcon = ({ type }: ActivityTypeIconProps): React.ReactElement => { const icon = type && activityTypeIcon[type]; const iconStyle = { fontSize: txIconSize() }; - return icon ? : ; + const isGovernanceTx = + Object.values(ConwayEraCertificatesTypes).includes(type as unknown as ConwayEraCertificatesTypes) || + type in ConwayEraGovernanceActions; + + // TODO: set fill color for every icon using currentColor, replace icon not to contain surrounding circle (LW-9566) + return icon ? ( + + ) : ( + + ); }; diff --git a/packages/core/src/ui/components/ActivityDetail/TransactionDetails.tsx b/packages/core/src/ui/components/ActivityDetail/TransactionDetails.tsx index d8ae4967bc..0b4982d6c9 100644 --- a/packages/core/src/ui/components/ActivityDetail/TransactionDetails.tsx +++ b/packages/core/src/ui/components/ActivityDetail/TransactionDetails.tsx @@ -6,11 +6,18 @@ import { Ellipsis, toast } from '@lace/common'; import { Box } from '@lace/ui'; import { useTranslate } from '@src/ui/hooks'; import CopyToClipboard from 'react-copy-to-clipboard'; -import type { ActivityStatus } from '../Activity'; +import { ActivityStatus } from '../Activity'; import styles from './TransactionDetails.module.scss'; import { TransactionInputOutput } from './TransactionInputOutput'; import { TransactionFee } from './TransactionFee'; import { ActivityDetailHeader } from './ActivityDetailHeader'; +import { TxDetailList } from './TxDetailsList'; +import { + TxDetailsVotingProceduresTitles, + TxDetailsProposalProceduresTitles, + TxDetailsCertificateTitles, + TxDetails +} from './types'; // eslint-disable-next-line @typescript-eslint/no-explicit-any const displayMetadataMsg = (value: any[]): string => value?.find((val: any) => val.hasOwnProperty('msg'))?.msg || ''; @@ -67,6 +74,9 @@ export interface TransactionDetailsProps { openExternalLink?: () => void; sendAnalyticsInputs?: () => void; sendAnalyticsOutputs?: () => void; + votingProcedures?: TxDetails[]; + proposalProcedures?: TxDetails[]; + certificates?: TxDetails[]; } const TOAST_DEFAULT_DURATION = 3; @@ -108,11 +118,34 @@ export const TransactionDetails = ({ isPopupView, openExternalLink, sendAnalyticsInputs, - sendAnalyticsOutputs + sendAnalyticsOutputs, + proposalProcedures, + votingProcedures, + certificates }: TransactionDetailsProps): React.ReactElement => { const { t } = useTranslate(); - const isSending = status === 'sending'; - const isSuccess = status === 'success'; + const isSending = status === ActivityStatus.PENDING; + const isSuccess = status === ActivityStatus.SUCCESS; + + // Translate certificate typenames + const translatedCertificates = certificates?.map((certificate) => + certificate?.map((detail) => ({ + ...detail, + ...(detail.title === 'certificateType' && { + details: [t(`package.core.assetActivityItem.entry.certificates.typenames.${detail.details[0]}`)] + }) + })) + ); + + // Translate governance proposal typenames + const translatedProposalProcedures = proposalProcedures?.map((proposal) => + proposal?.map((p) => ({ + ...p, + ...(p.title === 'type' && { + details: [t(`package.core.activityDetails.governanceActions.${p.details[0]}`)] + }) + })) + ); const renderDepositValueSection = ({ value, label }: { value: string; label: string }) => (
@@ -278,6 +311,60 @@ export const TransactionDetails = ({ })}
+ {votingProcedures?.length > 0 && ( + + testId="voting-procedures" + title={t('package.core.activityDetails.votingProcedures')} + lists={votingProcedures} + translations={{ + voterType: t('package.core.activityDetails.votingProcedureTitles.voterType'), + voterCredential: t('package.core.activityDetails.votingProcedureTitles.voterCredential'), + vote: t('package.core.activityDetails.votingProcedureTitles.vote'), + anchor: t('package.core.activityDetails.votingProcedureTitles.anchor'), + proposalTxHash: t('package.core.activityDetails.votingProcedureTitles.proposalTxHash'), + actionIndex: t('package.core.activityDetails.votingProcedureTitles.actionIndex') + }} + withSeparatorLine + /> + )} + {proposalProcedures?.length > 0 && ( + + testId="proposal-procedures" + title={t('package.core.activityDetails.proposalProcedures')} + lists={translatedProposalProcedures} + withSeparatorLine + translations={{ + type: t('package.core.activityDetails.proposalProcedureTitles.type'), + governanceActionId: t('package.core.activityDetails.proposalProcedureTitles.governanceActionId'), + rewardAccount: t('package.core.activityDetails.proposalProcedureTitles.rewardAccount'), + anchor: t('package.core.activityDetails.proposalProcedureTitles.anchor'), + protocolParamUpdate: t('package.core.activityDetails.proposalProcedureTitles.protocolParamUpdate'), + protocolVersion: t('package.core.activityDetails.proposalProcedureTitles.protocolVersion'), + withdrawals: t('package.core.activityDetails.proposalProcedureTitles.withdrawals'), + membersToBeRemoved: t('package.core.activityDetails.proposalProcedureTitles.membersToBeRemoved'), + membersToBeAdded: t('package.core.activityDetails.proposalProcedureTitles.membersToBeAdded'), + newQuorumThreshold: t('package.core.activityDetails.proposalProcedureTitles.newQuorumThreshold'), + constitutionAnchor: t('package.core.activityDetails.proposalProcedureTitles.constitutionAnchor') + }} + /> + )} + {certificates?.length > 0 && ( + + title={t('package.core.activityDetails.certificates')} + testId="certificates" + lists={translatedCertificates} + withSeparatorLine + translations={{ + certificateType: t('package.core.activityDetails.certificateTitles.certificateType'), + drep: t('package.core.activityDetails.certificateTitles.drep'), + anchor: t('package.core.activityDetails.certificateTitles.anchor'), + coldCredential: t('package.core.activityDetails.certificateTitles.coldCredential'), + hotCredential: t('package.core.activityDetails.certificateTitles.hotCredential'), + drepCredential: t('package.core.activityDetails.certificateTitles.drepCredential'), + depositPaid: t('package.core.activityDetails.certificateTitles.depositPaid') + }} + /> + )} {addrInputs?.length > 0 && ( { + testId: string; + title: string; + lists: TxDetails[]; + translations: TranslationsFor; + tooltipContent?: React.ReactNode; + withSeparatorLine?: boolean; +} + +export const TxDetailList = function TxDetailList({ + testId, + title, + lists, + tooltipContent, + withSeparatorLine, + translations +}: TxDetailListProps): React.ReactElement { + const [isVisible, setIsVisible] = useState(); + + const animation = isVisible ? rotateOpen : rotateClose; + const Icon = BracketDown ? : ; + + return ( +
+
+
+ {title} + {tooltipContent && ( + + + + )} +
+ +
+ {isVisible && ( +
+ {lists.map((list, idx) => ( +
0 })}> + translations={translations} testId={testId} list={list} /> +
+ ))} +
+ )} +
+ ); +}; diff --git a/packages/core/src/ui/components/ActivityDetail/components/DetailRow.module.scss b/packages/core/src/ui/components/ActivityDetail/components/DetailRow.module.scss new file mode 100644 index 0000000000..56029d936f --- /dev/null +++ b/packages/core/src/ui/components/ActivityDetail/components/DetailRow.module.scss @@ -0,0 +1,33 @@ +@import '../../../styles/theme.scss'; +@import '../../../../../../common/src/ui/styles/abstracts/typography'; + +.details { + color: var(--text-color-primary, #ffffff); + display: flex; + justify-content: space-between; + margin-bottom: size_unit(4); + width: 100%; + + .title { + display: flex; + flex: 0 0 50%; + align-self: baseline; + color: var(--text-color-primary, #ffffff); + @include text-body-semi-bold; + } + + .detail { + align-items: end; + display: flex; + flex-direction: column; + gap: size_unit(2); + color: var(--text-color-primary, #ffffff); + text-align: right; + word-break: break-all; + @include text-body-medium; + + @media (max-width: $breakpoint-popup) { + flex-direction: column; + } + } +} diff --git a/packages/core/src/ui/components/ActivityDetail/components/DetailRow.tsx b/packages/core/src/ui/components/ActivityDetail/components/DetailRow.tsx new file mode 100644 index 0000000000..e260057327 --- /dev/null +++ b/packages/core/src/ui/components/ActivityDetail/components/DetailRow.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import styles from './DetailRow.module.scss'; + +type DetailsRowProps = { + title: string; + dataTestId?: string; + details: string[]; +}; + +export const DetailRow = ({ title, details, dataTestId }: DetailsRowProps): React.ReactElement => ( +
+
{title}
+
+ {details.map((detail, idx) => ( + {detail} + ))} +
+
+); diff --git a/packages/core/src/ui/components/ActivityDetail/components/DetailRows.tsx b/packages/core/src/ui/components/ActivityDetail/components/DetailRows.tsx new file mode 100644 index 0000000000..d13b2f8446 --- /dev/null +++ b/packages/core/src/ui/components/ActivityDetail/components/DetailRows.tsx @@ -0,0 +1,31 @@ +/* eslint-disable react/no-multi-comp, sonarjs/no-identical-functions */ + +import React from 'react'; +import { DetailRow } from './DetailRow'; +import { TxDetails } from '../types'; +import { TranslationsFor } from '@src/ui/utils/types'; + +type DetailRowsProps = { + list: TxDetails; + testId: string; + translations: TranslationsFor; +}; + +export const DetailRows = function DetailRows({ + list, + testId, + translations +}: DetailRowsProps): React.ReactElement { + return ( + <> + {list.map(({ title, details }) => ( + + ))} + + ); +}; diff --git a/packages/core/src/ui/components/ActivityDetail/components/index.ts b/packages/core/src/ui/components/ActivityDetail/components/index.ts new file mode 100644 index 0000000000..c470084f1c --- /dev/null +++ b/packages/core/src/ui/components/ActivityDetail/components/index.ts @@ -0,0 +1 @@ +export * from './DetailRows'; diff --git a/packages/core/src/ui/components/ActivityDetail/index.ts b/packages/core/src/ui/components/ActivityDetail/index.ts index 7ba95eea83..139eec4aac 100644 --- a/packages/core/src/ui/components/ActivityDetail/index.ts +++ b/packages/core/src/ui/components/ActivityDetail/index.ts @@ -1,6 +1,7 @@ export * from './TransactionDetails'; export * from './RewardsDetails'; export * from './ActivityTypeIcon'; +export * from './types'; export * from './TransactionDetailAsset'; export * from './TransactionInputOutput'; export * from './TransactionFee'; diff --git a/packages/core/src/ui/components/ActivityDetail/types.ts b/packages/core/src/ui/components/ActivityDetail/types.ts new file mode 100644 index 0000000000..0ef6be2419 --- /dev/null +++ b/packages/core/src/ui/components/ActivityDetail/types.ts @@ -0,0 +1,73 @@ +import { Wallet } from '@lace/cardano'; + +// TODO: use Cip30TxType instead? +export enum ConwayEraCertificatesTypes { + 'AuthorizeCommitteeHot' = Wallet.Cardano.CertificateType.AuthorizeCommitteeHot, + 'RegisterDelegateRepresentative' = Wallet.Cardano.CertificateType.RegisterDelegateRepresentative, + 'ResignCommitteeCold' = Wallet.Cardano.CertificateType.ResignCommitteeCold, + 'VoteRegistrationDelegation' = Wallet.Cardano.CertificateType.VoteRegistrationDelegation, + 'VoteDelegation' = Wallet.Cardano.CertificateType.VoteDelegation, + 'UpdateDelegateRepresentative' = Wallet.Cardano.CertificateType.UpdateDelegateRepresentative, + 'UnregisterDelegateRepresentative' = Wallet.Cardano.CertificateType.UnregisterDelegateRepresentative, + 'StakeVoteRegistrationDelegation' = Wallet.Cardano.CertificateType.StakeVoteRegistrationDelegation, + 'StakeVoteDelegation' = Wallet.Cardano.CertificateType.StakeVoteDelegation, + 'StakeRegistrationDelegation' = Wallet.Cardano.CertificateType.StakeRegistrationDelegation +} + +export enum ConwayEraGovernanceActions { + 'vote' = 'vote', + 'submitProposal' = 'submitProposal' +} + +export enum DelegationTransactionType { + 'delegation' = 'delegation', + 'delegationRegistration' = 'delegationRegistration', + 'delegationDeregistration' = 'delegationDeregistration' +} + +export enum TransactionActivityType { + 'outgoing' = 'outgoing', + 'incoming' = 'incoming', + 'self' = 'self', + 'rewards' = 'rewards' +} + +export type TxDetailsCertificateTitles = + | 'certificateType' + | 'drep' + | 'anchor' + | 'coldCredential' + | 'hotCredential' + | 'drepCredential' + | 'depositPaid'; + +export type TxDetailsProposalProceduresTitles = + | 'type' + | 'governanceActionId' + | 'rewardAccount' + | 'anchor' + | 'protocolParamUpdate' + | 'protocolVersion' + | 'withdrawals' + | 'membersToBeRemoved' + | 'membersToBeAdded' + | 'newQuorumThreshold' + | 'constitutionAnchor'; + +export type TxDetailsVotingProceduresTitles = + | 'voterType' + | 'voterCredential' + | 'vote' + | 'anchor' + | 'proposalTxHash' + | 'actionIndex'; + +export type TxDetail = { + title: T; + details: string[]; +}; + +export type TxDetails = TxDetail[]; + +export type GovernanceTransactionTypes = ConwayEraCertificatesTypes | ConwayEraGovernanceActions; +export type ActivityType = DelegationTransactionType | TransactionActivityType | GovernanceTransactionTypes; diff --git a/packages/core/src/ui/lib/translations/en.json b/packages/core/src/ui/lib/translations/en.json index 7336752ffd..b8773ec10e 100644 --- a/packages/core/src/ui/lib/translations/en.json +++ b/packages/core/src/ui/lib/translations/en.json @@ -17,7 +17,43 @@ "incoming": "Received", "outgoing": "Sent", "sending": "Sending", - "self": "Self Transaction" + "self": "Self Transaction", + "RegisterDelegateRepresentativeCertificate": "DRep Registration", + "UnregisterDelegateRepresentativeCertificate": "DRep De-Registration", + "UpdateDelegateRepresentativeCertificate": "DRep Update", + "ResignCommitteeColdCertificate": "Resign Committee", + "StakeRegistrationDelegationCertificate": "Stake Key Registration & Delgation", + "StakeVoteDelegationCertificate": "Stake Key Registration & DRep Delegation", + "StakeVoteRegistrationDelegationCertificate": "Stake Key Registration, Delegation, & DRep Delegation", + "VoteRegistrationDelegationCertificate": "Stake Key Registration & DRep Delegation", + "AuthorizeCommitteeHotCertificate": "Authorize Committee", + "VoteDelegationCertificate": "Vote Delegation", + "vote": "Vote Signing", + "submitProposal": "Governance Proposal" + }, + "certificates": { + "headings": { + "certificates": "Certificates", + "typename": "Certificate Type", + "anchor": "Anchor", + "drep": "DRep", + "depositPaid": "Deposit paid", + "coldCredential": "Cold credential", + "hotCredential": "Hot credential", + "drepCredential": "DRep credential" + }, + "typenames": { + "VoteDelegationCertificate": "Vote Delegation", + "StakeVoteDelegationCertificate": "Stake Vote Delegation", + "StakeRegistrationDelegationCertificate": "Stake Registration Delegate", + "VoteRegistrationDelegationCertificate": "Vote Registration Delegate", + "StakeVoteRegistrationDelegationCertificate": "Stake Vote Registration Delegation", + "AuthorizeCommitteeHotCertificate": "Authorize Committee", + "ResignCommitteeColdCertificate": "Resign Committee", + "RegisterDelegateRepresentativeCertificate": "Register Delegate Representative", + "UnregisterDelegateRepresentativeCertificate": "Unregister Delegate Representative", + "UpdateDelegateRepresentativeCertificate": "Update Delegate Representative" + } } } }, @@ -80,7 +116,61 @@ "rewardsDescription": "When available, your rewards will be withdrawn automatically every time you send Tokens.", "copiedToClipboard": "Copied to clipboard", "pools": "Pool(s)", - "epoch": "Epoch" + "self": "Self Transaction", + "RegisterDelegateRepresentativeCertificate": "DRep Registration", + "UnregisterDelegateRepresentativeCertificate": "DRep De-Registration", + "UpdateDelegateRepresentativeCertificate": "DRep Update", + "ResignCommitteeColdCertificate": "Resign Committee", + "StakeRegistrationDelegationCertificate": "Stake Key Registration & Delgation", + "StakeVoteDelegationCertificate": "Stake Key Registration & DRep Delegation", + "StakeVoteRegistrationDelegationCertificate": "Stake Key Registration, Delegation, & DRep Delegation", + "VoteRegistrationDelegationCertificate": "Stake Key Registration & DRep Delegation", + "AuthorizeCommitteeHotCertificate": "Authorize Committee", + "VoteDelegationCertificate": "Vote Delegation", + "vote": "Vote Signing", + "submitProposal": "Governance Proposal", + "certificates": "Certificates", + "certificateTitles": { + "certificateType": "Certificate type", + "drep": "DRep", + "anchor": "Anchor", + "coldCredential": "Cold credential", + "hotCredential": "Hot credential", + "drepCredential": "DRep credential", + "depositPaid": "Deposit paid" + }, + "votingProcedures": "Voting Procedures", + "votingProcedureTitles": { + "voterType": "Voter type", + "voterCredential": "Voter Credential", + "vote": "Vote", + "anchor": "Anchor", + "proposalTxHash": "Proposal Tx hash", + "actionIndex": "Governance action index" + }, + "proposalProcedures": "Proposal Procedures", + "proposalProcedureTitles": { + "type": "Procedure type", + "governanceActionId": "Governance action ID", + "rewardAccount": "Reward account", + "anchor": "Anchor", + "protocolParamUpdate": "Protocol parameter update(s)", + "protocolVersion": "Protocol version", + "withdrawals": "Withdrawals", + "membersToBeRemoved": "Members to remove", + "membersToBeAdded": "Members to add", + "newQuorumThreshold": "New quorum threshold", + "constitutionAnchor": "Constitution anchor" + }, + "governanceActions": { + "info_action": "Info Action", + "hard_fork_initiation_action": "Hard Fork Initiation", + "parameter_change_action": "Parameter Change", + "treasury_withdrawals_action": "Treasury Withdrawals", + "no_confidence": "No Confidence", + "update_committee": "Update Committee", + "new_constitution": "New Constitution" + } }, "authorizeDapp": { "title": "Allow this site to", diff --git a/packages/staking/src/features/overview/helpers/hasPendingDelegationTransaction.ts b/packages/staking/src/features/overview/helpers/hasPendingDelegationTransaction.ts index 509a8073d1..b87f950438 100644 --- a/packages/staking/src/features/overview/helpers/hasPendingDelegationTransaction.ts +++ b/packages/staking/src/features/overview/helpers/hasPendingDelegationTransaction.ts @@ -1,13 +1,7 @@ +import { ActivityStatus, AssetActivityListProps, DelegationTransactionType } from '@lace/core'; import flatMap from 'lodash/flatMap'; -import type { ActivityType, AssetActivityListProps } from '@lace/core'; - -const DelegationTransactionTypes: Set = new Set([ - 'delegation', - 'delegationRegistration', - 'delegationDeregistration', -]); export const hasPendingDelegationTransaction = (walletActivities: AssetActivityListProps[]) => flatMap(walletActivities, ({ items }) => items).some( - ({ type, status }) => type && DelegationTransactionTypes.has(type) && status === 'sending' + ({ type, status }) => type && type in DelegationTransactionType && status === ActivityStatus.PENDING ); From 73bd8a26302e0cac32ed206de33a07a547e92009 Mon Sep 17 00:00:00 2001 From: Michael Chappell <7581002+mchappell@users.noreply.github.com> Date: Tue, 30 Jan 2024 13:48:03 +0000 Subject: [PATCH 30/68] refactor: remove need to target dev endpoints in CI --- .github/workflows/e2e-tests-linux.yml | 3 --- .github/workflows/smoke-tests.yml | 3 --- apps/browser-extension-wallet/.env.defaults | 1 - 3 files changed, 7 deletions(-) diff --git a/.github/workflows/e2e-tests-linux.yml b/.github/workflows/e2e-tests-linux.yml index 41ebc974c7..ff541671aa 100644 --- a/.github/workflows/e2e-tests-linux.yml +++ b/.github/workflows/e2e-tests-linux.yml @@ -50,9 +50,6 @@ jobs: uses: ./.github/shared/build with: LACE_EXTENSION_KEY: ${{ secrets.MANIFEST_PUBLIC_KEY }} - CARDANO_SERVICES_URL_MAINNET: ${{ secrets.CARDANO_SERVICES_DEV_URL_MAINNET }} - CARDANO_SERVICES_URL_PREPROD: ${{ secrets.CARDANO_SERVICES_DEV_URL_PREPROD }} - CARDANO_SERVICES_URL_PREVIEW: ${{ secrets.CARDANO_SERVICES_DEV_URL_PREVIEW }} - name: Start XVFB run: | Xvfb :99 & diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml index 9b0d7d5a97..24d88d4a4f 100644 --- a/.github/workflows/smoke-tests.yml +++ b/.github/workflows/smoke-tests.yml @@ -26,9 +26,6 @@ jobs: uses: ./.github/shared/build with: LACE_EXTENSION_KEY: ${{ secrets.MANIFEST_PUBLIC_KEY }} - CARDANO_SERVICES_URL_MAINNET: ${{ secrets.CARDANO_SERVICES_DEV_URL_MAINNET }} - CARDANO_SERVICES_URL_PREPROD: ${{ secrets.CARDANO_SERVICES_DEV_URL_PREPROD }} - CARDANO_SERVICES_URL_PREVIEW: ${{ secrets.CARDANO_SERVICES_DEV_URL_PREVIEW }} - name: Start XVFB run: | Xvfb :99 & diff --git a/apps/browser-extension-wallet/.env.defaults b/apps/browser-extension-wallet/.env.defaults index d77c020a48..5bab2dec51 100644 --- a/apps/browser-extension-wallet/.env.defaults +++ b/apps/browser-extension-wallet/.env.defaults @@ -81,7 +81,6 @@ CEXPLORER_URL_SANCHONET= ADA_HANDLE_URL_MAINNET=https://api.handle.me ADA_HANDLE_URL_PREVIEW=https://preview.api.handle.me ADA_HANDLE_URL_PREPROD=https://preprod.api.handle.me -# TODO: update this with a valid sanchonet url ADA_HANDLE_URL_SANCHONET= # Manifest.json From b2e8fa29505f52d4ca47d9156ad09bea067a71fb Mon Sep 17 00:00:00 2001 From: Michael Chappell <7581002+mchappell@users.noreply.github.com> Date: Wed, 31 Jan 2024 08:58:17 +0000 Subject: [PATCH 31/68] refactor: remove sanchonet as default network option --- apps/browser-extension-wallet/.env.defaults | 2 +- apps/browser-extension-wallet/.env.example | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/apps/browser-extension-wallet/.env.defaults b/apps/browser-extension-wallet/.env.defaults index 5bab2dec51..3401da79b0 100644 --- a/apps/browser-extension-wallet/.env.defaults +++ b/apps/browser-extension-wallet/.env.defaults @@ -5,7 +5,7 @@ DEFAULT_CHAIN=Mainnet WALLET_SYNC_TIMEOUT_IN_SEC=60 WALLET_INTERVAL_IN_SEC=30 DROP_CONSOLE_IN_PRODUCTION=false -AVAILABLE_CHAINS=Preprod,Preview,Mainnet,Sanchonet +AVAILABLE_CHAINS=Preprod,Preview,Mainnet ADA_PRICE_POLLING_IN_SEC=60 TOKEN_PRICE_POLLING_IN_SEC=300 SAVED_PRICE_DURATION_IN_MINUTES=720 diff --git a/apps/browser-extension-wallet/.env.example b/apps/browser-extension-wallet/.env.example index dd317188a0..dcec3461b2 100644 --- a/apps/browser-extension-wallet/.env.example +++ b/apps/browser-extension-wallet/.env.example @@ -4,7 +4,7 @@ DEFAULT_CHAIN=Mainnet WALLET_SYNC_TIMEOUT_IN_SEC=60 WALLET_INTERVAL_IN_SEC=30 DROP_CONSOLE_IN_PRODUCTION=false -AVAILABLE_CHAINS=Preprod,Preview,Mainnet,Sanchonet +AVAILABLE_CHAINS=Preprod,Preview,Mainnet ADA_PRICE_POLLING_IN_SEC=60 TOKEN_PRICE_POLLING_IN_SEC=300 SAVED_PRICE_DURATION_IN_MINUTES=720 @@ -67,22 +67,19 @@ POSTHOG_DEV_TOKEN_SANCHONET=dev-sanchonet-token CARDANO_SERVICES_URL_MAINNET=https://backend.live-mainnet.eks.lw.iog.io CARDANO_SERVICES_URL_PREPROD=https://backend.live-preprod.eks.lw.iog.io CARDANO_SERVICES_URL_PREVIEW=https://backend.live-preview.eks.lw.iog.io -# TODO: update this with a valid sanchonet url CARDANO_SERVICES_URL_SANCHONET=https://backend.dev-sanchonet.eks.lw.iog.io # Explorer URLs CEXPLORER_URL_MAINNET=https://cexplorer.io CEXPLORER_URL_PREVIEW=https://preview.cexplorer.io CEXPLORER_URL_PREPROD=https://preprod.cexplorer.io -# TODO: update this with a valid sanchonet cexplorer -CEXPLORER_URL_SANCHONET=https://preprod.cexplorer.io +CEXPLORER_URL_SANCHONET= # ADA Handle URLs ADA_HANDLE_URL_MAINNET=https://api.handle.me ADA_HANDLE_URL_PREVIEW=https://preview.api.handle.me ADA_HANDLE_URL_PREPROD=https://preprod.api.handle.me -# TODO: update this with a valid sanchonet url -ADA_HANDLE_URL_SANCHONET=https://preprod.api.handle.me +ADA_HANDLE_URL_SANCHONET= # Manifest.json LACE_EXTENSION_KEY=gafhhkghbfjjkeiendhlofajokpaflmk From 40a162518ffda3c18d28660ae760eef47c3c052b Mon Sep 17 00:00:00 2001 From: Michael Chappell <7581002+mchappell@users.noreply.github.com> Date: Wed, 31 Jan 2024 09:07:00 +0000 Subject: [PATCH 32/68] refactor: remove sanchonet explorer check --- apps/browser-extension-wallet/src/config.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/browser-extension-wallet/src/config.ts b/apps/browser-extension-wallet/src/config.ts index 6c8a994f36..4180e7af3a 100644 --- a/apps/browser-extension-wallet/src/config.ts +++ b/apps/browser-extension-wallet/src/config.ts @@ -32,13 +32,11 @@ const envChecks = (chosenChain: Wallet.ChainName): void => { if ( !process.env.CARDANO_SERVICES_URL_MAINNET || !process.env.CARDANO_SERVICES_URL_PREPROD || - !process.env.CARDANO_SERVICES_URL_PREVIEW || - !process.env.CARDANO_SERVICES_URL_SANCHONET + !process.env.CARDANO_SERVICES_URL_PREVIEW ) { throw new Error('env vars not complete'); } - // TODO Update if sanchonet explorer becomes available if (!process.env.CEXPLORER_URL_MAINNET || !process.env.CEXPLORER_URL_PREVIEW || !process.env.CEXPLORER_URL_PREPROD) { throw new Error('explorer vars not complete'); } From eecbe1d2fe5461ef775830d8ccb53610ff21a614 Mon Sep 17 00:00:00 2001 From: Michael Chappell <7581002+mchappell@users.noreply.github.com> Date: Wed, 31 Jan 2024 10:28:21 +0000 Subject: [PATCH 33/68] feat(extension): update drep mapper check --- .../activity/helpers/common-tx-transformer.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/activity/helpers/common-tx-transformer.ts b/apps/browser-extension-wallet/src/views/browser-view/features/activity/helpers/common-tx-transformer.ts index 8588b74a0c..53fe854859 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/activity/helpers/common-tx-transformer.ts +++ b/apps/browser-extension-wallet/src/views/browser-view/features/activity/helpers/common-tx-transformer.ts @@ -228,12 +228,16 @@ type TransactionGovernanceProposal = { }; const drepMapper = (drep: Wallet.Cardano.DelegateRepresentative) => { - if (Wallet.Cardano.isDRepAlwaysAbstain(drep)) { - return 'alwaysAbstain'; - } else if (Wallet.Cardano.isDRepAlwaysNoConfidence(drep)) { - return 'alwaysNoConfidence'; + switch (true) { + case Wallet.Cardano.isDRepAlwaysAbstain(drep): + return 'alwaysAbstain'; + case Wallet.Cardano.isDRepAlwaysNoConfidence(drep): + return 'alwaysNoConfidence'; + case Wallet.Cardano.isDRepCredential(drep): + return Wallet.Cardano.DRepID(drep.hash); + default: + throw new Error('incorrect drep supplied'); } - return Wallet.Cardano.DRepID(drep.hash); }; export const certificateTransformer = ( From c9b69a1c08d32736d3df19e87e0914bc1de8eefd Mon Sep 17 00:00:00 2001 From: vetalcore Date: Wed, 31 Jan 2024 14:07:19 +0200 Subject: [PATCH 34/68] fix(extension): conway-era tx activities line items (#860) * fix(extension): conway-era tx activiries line items * fix(extension): adjust tests * fix(extension): resolve pr comments --- .../src/lib/translations/en.json | 26 +++-- .../stores/slices/activity-detail-slice.ts | 12 +- .../stores/slices/wallet-activities-slice.ts | 10 +- .../src/utils/__tests__/inspectTxType.test.ts | 6 +- .../src/utils/mocks/certificates.ts | 5 - .../src/utils/mocks/raw-transactions.ts | 8 -- .../src/utils/tx-inspection.ts | 37 +++++-- .../activity/components/ActivityDetail.tsx | 7 +- .../components/TransactionDetailsProxy.tsx | 4 +- .../activity/helpers/common-tx-transformer.ts | 14 +-- .../arrow-diagonal-down-outline.component.svg | 3 + .../arrow-diagonal-up-outline.component.svg | 3 + .../icons/badge-check-outline.component.svg | 3 + .../ui/assets/icons/ban-outline.component.svg | 3 + .../icons/briefcase-back-icon.component.svg | 4 + .../icons/briefcase-outline.component.svg | 3 + .../clipboard-check-outline.component.svg | 3 + .../clipboard-copy-outline.component.svg | 3 + .../clipboard-list-outline.component.svg | 3 + .../icons/clipboard-x-outline.component.svg | 4 + .../ui/assets/icons/delegation.component.svg | 4 - .../icons/document-add-outline.component.svg | 3 + .../document-report-outline.component.svg | 3 + .../icons/document-text-outline.component.svg | 3 + .../icons/feather-outline.component.svg | 3 + .../assets/icons/gift-outline.component.svg | 3 + .../identification-outline.component.svg | 3 + .../ui/assets/icons/incoming.component.svg | 4 - .../assets/icons/info-outline.component.svg | 3 + .../ui/assets/icons/outgoing.component.svg | 4 - .../icons/receipt-right-outline.component.svg | 3 + .../icons/refresh-outline.component.svg | 3 + .../icons/self-transaction.component.svg | 4 - .../icons/terminal-outile.component.svg | 3 + .../icons/ticket-forward-icon.component.svg | 3 + .../ui/assets/icons/ticket-icon.component.svg | 7 -- .../icons/ticket-outline-icon.component.svg | 6 + .../components/Activity/AssetActivityItem.tsx | 53 ++------- .../ActivityTypeIcon.module.scss | 40 +++++-- .../ActivityDetail/ActivityTypeIcon.tsx | 104 +++++++++++------- .../src/ui/components/ActivityDetail/types.ts | 26 +++-- packages/core/src/ui/lib/translations/en.json | 26 +++-- .../hasPendingDelegationTransaction.ts | 4 +- 43 files changed, 284 insertions(+), 192 deletions(-) create mode 100644 packages/core/src/ui/assets/icons/arrow-diagonal-down-outline.component.svg create mode 100644 packages/core/src/ui/assets/icons/arrow-diagonal-up-outline.component.svg create mode 100644 packages/core/src/ui/assets/icons/badge-check-outline.component.svg create mode 100644 packages/core/src/ui/assets/icons/ban-outline.component.svg create mode 100644 packages/core/src/ui/assets/icons/briefcase-back-icon.component.svg create mode 100644 packages/core/src/ui/assets/icons/briefcase-outline.component.svg create mode 100644 packages/core/src/ui/assets/icons/clipboard-check-outline.component.svg create mode 100644 packages/core/src/ui/assets/icons/clipboard-copy-outline.component.svg create mode 100644 packages/core/src/ui/assets/icons/clipboard-list-outline.component.svg create mode 100644 packages/core/src/ui/assets/icons/clipboard-x-outline.component.svg delete mode 100644 packages/core/src/ui/assets/icons/delegation.component.svg create mode 100644 packages/core/src/ui/assets/icons/document-add-outline.component.svg create mode 100644 packages/core/src/ui/assets/icons/document-report-outline.component.svg create mode 100644 packages/core/src/ui/assets/icons/document-text-outline.component.svg create mode 100644 packages/core/src/ui/assets/icons/feather-outline.component.svg create mode 100644 packages/core/src/ui/assets/icons/gift-outline.component.svg create mode 100644 packages/core/src/ui/assets/icons/identification-outline.component.svg delete mode 100644 packages/core/src/ui/assets/icons/incoming.component.svg create mode 100644 packages/core/src/ui/assets/icons/info-outline.component.svg delete mode 100644 packages/core/src/ui/assets/icons/outgoing.component.svg create mode 100644 packages/core/src/ui/assets/icons/receipt-right-outline.component.svg create mode 100644 packages/core/src/ui/assets/icons/refresh-outline.component.svg delete mode 100644 packages/core/src/ui/assets/icons/self-transaction.component.svg create mode 100644 packages/core/src/ui/assets/icons/terminal-outile.component.svg create mode 100644 packages/core/src/ui/assets/icons/ticket-forward-icon.component.svg delete mode 100644 packages/core/src/ui/assets/icons/ticket-icon.component.svg create mode 100644 packages/core/src/ui/assets/icons/ticket-outline-icon.component.svg diff --git a/apps/browser-extension-wallet/src/lib/translations/en.json b/apps/browser-extension-wallet/src/lib/translations/en.json index a7c4bf2e62..32286c5987 100644 --- a/apps/browser-extension-wallet/src/lib/translations/en.json +++ b/apps/browser-extension-wallet/src/lib/translations/en.json @@ -24,18 +24,26 @@ "outgoing": "Sent", "sending": "Sending", "self": "Self Transaction", + "vote": "Vote Signing", + "HardForkInitiationAction": "Hard Fork Initiation Action", + "NewConstitution": "New Constitution Action", + "NoConfidence": "No Confidence Action", + "ParameterChangeAction": "Parameter Change Action", + "TreasuryWithdrawalsAction": "Treasury Withdrawals Action", + "UpdateCommittee": "Update Committee Action", + "InfoAction": "Info Action", + "UpdateDelegateRepresentativeCertificate": "Update DRep", + "StakeVoteDelegationCertificate": "Stake Vote Delegation Certificate", + "StakeRegistrationDelegationCertificate": "Stake Registration Delegation Certificate", + "VoteRegistrationDelegationCertificate": "Vote Registration Delegation Certificate", + "StakeVoteRegistrationDelegationCertificate": "Stake Vote Registration Delegation Certificate", + "ResignCommitteeColdCertificate": "Resign Committee", + "AuthorizeCommitteeHotCertificate": "Authorise Committee", "RegisterDelegateRepresentativeCertificate": "DRep Registration", "UnregisterDelegateRepresentativeCertificate": "DRep De-Registration", - "UpdateDelegateRepresentativeCertificate": "DRep Update", - "ResignCommitteeColdCertificate": "Resign Committee", - "StakeRegistrationDelegationCertificate": "Stake Key Registration & Delgation", - "StakeVoteDelegationCertificate": "Stake Key Registration & DRep Delegation", - "StakeVoteRegistrationDelegationCertificate": "Stake Key Registration, Delegation, & DRep Delegation", - "VoteRegistrationDelegationCertificate": "Stake Key Registration & DRep Delegation", - "AuthorizeCommitteeHotCertificate": "Authorize Committee", "VoteDelegationCertificate": "Vote Delegation", - "vote": "Vote Signing", - "submitProposal": "Governance Proposal" + + "submitProposal": "Confirm Vote" }, "certificates": { "headings": { diff --git a/apps/browser-extension-wallet/src/stores/slices/activity-detail-slice.ts b/apps/browser-extension-wallet/src/stores/slices/activity-detail-slice.ts index f78de1de4b..7600ed34ee 100644 --- a/apps/browser-extension-wallet/src/stores/slices/activity-detail-slice.ts +++ b/apps/browser-extension-wallet/src/stores/slices/activity-detail-slice.ts @@ -17,7 +17,7 @@ import { inspectTxValues } from '@src/utils/tx-inspection'; import { firstValueFrom } from 'rxjs'; import { getAssetsInformation } from '@src/utils/get-assets-information'; import { MAX_POOLS_COUNT } from '@lace/staking'; -import { ActivityStatus, DelegationTransactionType, TransactionActivityType } from '@lace/core'; +import { ActivityStatus, DelegationActivityType, TransactionActivityType } from '@lace/core'; import type { ActivityType } from '@lace/core'; import { formatDate, formatTime } from '@src/utils/format-date'; import { @@ -65,11 +65,11 @@ const shouldIncludeFee = ( delegationInfo: Wallet.Cardano.StakeDelegationCertificate[] | undefined ) => !( - type === DelegationTransactionType.delegationRegistration || + type === DelegationActivityType.delegationRegistration || // Existence of any (new) delegationInfo means that this "de-registration" // activity is accompanied by a "delegation" activity, which carries the fees. // However, fees should be shown if de-registration activity is standalone. - (type === DelegationTransactionType.delegationDeregistration && !!delegationInfo?.length) + (type === DelegationActivityType.delegationDeregistration && !!delegationInfo?.length) ); const getPoolInfos = async (poolIds: Wallet.Cardano.PoolId[], stakePoolProvider: Wallet.StakePoolProvider) => { @@ -191,14 +191,14 @@ const buildGetActivityDetail = const deposit = // since one tx can be split into two (delegation, registration) actions, // ensure only the registration tx carries the deposit - implicitCoin.deposit && type === DelegationTransactionType.delegationRegistration + implicitCoin.deposit && type === DelegationActivityType.delegationRegistration ? Wallet.util.lovelacesToAdaString(implicitCoin.deposit.toString()) : undefined; const depositReclaimValue = Wallet.util.calculateDepositReclaim(implicitCoin); const depositReclaim = // since one tx can be split into two (delegation, de-registration) actions, // ensure only the de-registration tx carries the reclaimed deposit - depositReclaimValue && type === DelegationTransactionType.delegationDeregistration + depositReclaimValue && type === DelegationActivityType.delegationDeregistration ? Wallet.util.lovelacesToAdaString(depositReclaimValue.toString()) : undefined; const feeInAda = Wallet.util.lovelacesToAdaString(tx.body.fee.toString()); @@ -226,7 +226,7 @@ const buildGetActivityDetail = certificates: certificateTransformer(cardanoCoin, tx.body.certificates) }; - if (type === DelegationTransactionType.delegation && delegationInfo) { + if (type === DelegationActivityType.delegation && delegationInfo) { const pools = await getPoolInfos( delegationInfo.map(({ poolId }) => poolId), stakePoolProvider diff --git a/apps/browser-extension-wallet/src/stores/slices/wallet-activities-slice.ts b/apps/browser-extension-wallet/src/stores/slices/wallet-activities-slice.ts index c0d628b032..ba053a463d 100644 --- a/apps/browser-extension-wallet/src/stores/slices/wallet-activities-slice.ts +++ b/apps/browser-extension-wallet/src/stores/slices/wallet-activities-slice.ts @@ -20,7 +20,7 @@ import { ActivityStatus, AssetActivityItemProps, AssetActivityListProps, - DelegationTransactionType, + DelegationActivityType, TransactionActivityType } from '@lace/core'; import { CurrencyInfo, TxDirections } from '@src/types'; @@ -66,20 +66,20 @@ type MappedActivityListProps = Omit & { export type FetchWalletActivitiesReturn = Observable>; type DelegationActivityItemProps = Omit & { - type: DelegationTransactionType; + type: DelegationActivityType; }; const isDelegationActivity = (activity: ExtendedActivityProps): activity is DelegationActivityItemProps => - activity.type in DelegationTransactionType; + activity.type in DelegationActivityType; const getDelegationAmount = (activity: DelegationActivityItemProps) => { const fee = new BigNumber(Number.parseFloat(activity.fee)); - if (activity.type === DelegationTransactionType.delegationRegistration) { + if (activity.type === DelegationActivityType.delegationRegistration) { return fee.plus(activity.deposit); } - if (activity.type === DelegationTransactionType.delegationDeregistration) { + if (activity.type === DelegationActivityType.delegationDeregistration) { return new BigNumber(activity.depositReclaim).minus(fee); } 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 a70605d58d..a62d2bc851 100644 --- a/apps/browser-extension-wallet/src/utils/__tests__/inspectTxType.test.ts +++ b/apps/browser-extension-wallet/src/utils/__tests__/inspectTxType.test.ts @@ -428,7 +428,9 @@ describe('testing tx-inspection utils', () => { rewardAccount: REWARD_ACCOUNT, // eslint-disable-next-line unicorn/no-null anchor: null, - governanceAction: {} as Wallet.Cardano.GovernanceAction, + governanceAction: { + __typename: Wallet.Cardano.GovernanceActionType.parameter_change_action + } as Wallet.Cardano.GovernanceAction, deposit: BigInt(1) } ] @@ -438,7 +440,7 @@ describe('testing tx-inspection utils', () => { { address: ADDRESS_1, rewardAccount: REWARD_ACCOUNT } ] as Wallet.KeyManagement.GroupedAddress[] }); - expect(result).toEqual('submitProposal'); + expect(result).toEqual('ParameterChangeAction'); }); }); }); diff --git a/apps/browser-extension-wallet/src/utils/mocks/certificates.ts b/apps/browser-extension-wallet/src/utils/mocks/certificates.ts index a039f264b8..d5cc7f53ea 100644 --- a/apps/browser-extension-wallet/src/utils/mocks/certificates.ts +++ b/apps/browser-extension-wallet/src/utils/mocks/certificates.ts @@ -30,11 +30,6 @@ export const mockConwayCertificates: Partial< dRepCredential: CREDENTIAL, anchor: null }, - [ConwayEraCertificatesTypes.VoteDelegation]: { - __typename: Wallet.Cardano.CertificateType.VoteDelegation, - dRep: CREDENTIAL, - stakeCredential: CREDENTIAL - }, [ConwayEraCertificatesTypes.StakeVoteDelegation]: { __typename: Wallet.Cardano.CertificateType.StakeVoteDelegation, stakeCredential: CREDENTIAL, diff --git a/apps/browser-extension-wallet/src/utils/mocks/raw-transactions.ts b/apps/browser-extension-wallet/src/utils/mocks/raw-transactions.ts index 31ce46f3fa..7f17269f9f 100644 --- a/apps/browser-extension-wallet/src/utils/mocks/raw-transactions.ts +++ b/apps/browser-extension-wallet/src/utils/mocks/raw-transactions.ts @@ -71,11 +71,3 @@ export const drepRetirementTx: Wallet.Cardano.HydratedTx = { certificates: [mockConwayCertificates[ConwayEraCertificatesTypes.UnregisterDelegateRepresentative]] } }; - -export const voteDelegationTx: Wallet.Cardano.HydratedTx = { - ...tx, - body: { - ...tx.body, - certificates: [mockConwayCertificates[ConwayEraCertificatesTypes.VoteDelegation]] - } -}; diff --git a/apps/browser-extension-wallet/src/utils/tx-inspection.ts b/apps/browser-extension-wallet/src/utils/tx-inspection.ts index be50897cf3..d7e8cbd45f 100644 --- a/apps/browser-extension-wallet/src/utils/tx-inspection.ts +++ b/apps/browser-extension-wallet/src/utils/tx-inspection.ts @@ -12,10 +12,11 @@ import { import { Wallet } from '@lace/cardano'; import { ActivityType, - DelegationTransactionType, + DelegationActivityType, TransactionActivityType, ConwayEraGovernanceActions, - ConwayEraCertificatesTypes + ConwayEraCertificatesTypes, + Cip1694GovernanceActivityType } from '@lace/core'; import { TxDirection, TxDirections } from '@src/types'; @@ -60,8 +61,6 @@ const governanceCertificateInspection = ( return ConwayEraCertificatesTypes.UnregisterDelegateRepresentative; case signedCertificateTypenames.includes(CertificateType.UpdateDelegateRepresentative): return ConwayEraCertificatesTypes.UpdateDelegateRepresentative; - case signedCertificateTypenames.includes(CertificateType.VoteDelegation): - return ConwayEraCertificatesTypes.VoteDelegation; case signedCertificateTypenames.includes(CertificateType.StakeVoteDelegation): return ConwayEraCertificatesTypes.StakeVoteDelegation; case signedCertificateTypenames.includes(CertificateType.StakeRegistrationDelegation): @@ -77,6 +76,28 @@ const governanceCertificateInspection = ( } }; +// Assumes single procedure only +export const cip1694GovernanceActionsInspection = ( + procedure: Wallet.Cardano.ProposalProcedure +): Cip1694GovernanceActivityType => { + switch (procedure.governanceAction.__typename) { + case Wallet.Cardano.GovernanceActionType.parameter_change_action: + return Cip1694GovernanceActivityType.ParameterChangeAction; + case Wallet.Cardano.GovernanceActionType.hard_fork_initiation_action: + return Cip1694GovernanceActivityType.HardForkInitiationAction; + case Wallet.Cardano.GovernanceActionType.treasury_withdrawals_action: + return Cip1694GovernanceActivityType.TreasuryWithdrawalsAction; + case Wallet.Cardano.GovernanceActionType.no_confidence: + return Cip1694GovernanceActivityType.NoConfidence; + case Wallet.Cardano.GovernanceActionType.update_committee: + return Cip1694GovernanceActivityType.UpdateCommittee; + case Wallet.Cardano.GovernanceActionType.new_constitution: + return Cip1694GovernanceActivityType.NewConstitution; + case Wallet.Cardano.GovernanceActionType.info_action: + return Cip1694GovernanceActivityType.InfoAction; + } +}; + const getWalletAccounts = (walletAddresses: Wallet.KeyManagement.GroupedAddress[]) => walletAddresses.reduce( (acc, curr) => ({ @@ -145,17 +166,17 @@ export const inspectTxType = ({ if (inspectionProperties.sent.inputs.length > 0 || withRewardsWithdrawal) { switch (true) { case !!inspectionProperties.delegation[0]?.poolId: - return DelegationTransactionType.delegation; + return DelegationActivityType.delegation; case inspectionProperties.stakeKeyRegistration.length > 0: - return DelegationTransactionType.delegationRegistration; + return DelegationActivityType.delegationRegistration; case inspectionProperties.stakeKeyDeregistration.length > 0: - return DelegationTransactionType.delegationDeregistration; + return DelegationActivityType.delegationDeregistration; // Voting procedures take priority over proposals // TODO: use proper inspector when available on sdk side (LW-9569) case tx.body.votingProcedures?.length > 0: return ConwayEraGovernanceActions.vote; case tx.body.proposalProcedures?.length > 0: - return ConwayEraGovernanceActions.submitProposal; + return cip1694GovernanceActionsInspection(tx.body.proposalProcedures[0]); case inspectionProperties.selfTransaction: return TransactionActivityType.self; default: diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/activity/components/ActivityDetail.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/activity/components/ActivityDetail.tsx index 5fea0d3f65..e899c48de4 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/activity/components/ActivityDetail.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/features/activity/components/ActivityDetail.tsx @@ -11,7 +11,7 @@ import { useTranslate, RewardsDetails, TransactionActivityType, - DelegationTransactionType + DelegationActivityType } from '@lace/core'; import type { ActivityType } from '@lace/core'; import { PriceResult } from '@hooks'; @@ -80,9 +80,8 @@ interface ActivityDetailProps { } const getTypeLabel = (type: ActivityType, t: ReturnType['t']) => { - if (type === DelegationTransactionType.delegationRegistration) return t('package.core.activityDetails.registration'); - if (type === DelegationTransactionType.delegationDeregistration) - return t('package.core.activityDetails.deregistration'); + if (type === DelegationActivityType.delegationRegistration) return t('package.core.activityDetails.registration'); + if (type === DelegationActivityType.delegationDeregistration) return t('package.core.activityDetails.deregistration'); if (type === TransactionActivityType.incoming) return t('package.core.activityDetails.received'); if (type === TransactionActivityType.outgoing) return t('package.core.activityDetails.sent'); return t(`package.core.activityDetails.${type}`); diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/activity/components/TransactionDetailsProxy.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/activity/components/TransactionDetailsProxy.tsx index 5fd4b4d3a6..4b0e63cc82 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/activity/components/TransactionDetailsProxy.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/features/activity/components/TransactionDetailsProxy.tsx @@ -1,5 +1,5 @@ import React, { ReactElement, useMemo } from 'react'; -import { ActivityStatus, DelegationTransactionType, TransactionDetails } from '@lace/core'; +import { ActivityStatus, DelegationActivityType, TransactionDetails } from '@lace/core'; import { AddressListType, getTransactionData } from './ActivityDetail'; import { useWalletStore } from '@src/stores'; import { useAnalyticsContext, useExternalLinkOpener } from '@providers'; @@ -37,7 +37,7 @@ export const TransactionDetailsProxy = withAddressBookContext( return `${CEXPLORER_BASE_URL[environmentName]}/${CEXPLORER_URL_PATHS.Tx}`; }, [CEXPLORER_BASE_URL, CEXPLORER_URL_PATHS.Tx, environmentName]); const getHeaderDescription = () => { - if (activityInfo.type === DelegationTransactionType.delegation) return '1 token'; + if (activityInfo.type === DelegationActivityType.delegation) return '1 token'; return ` (${activityInfo?.assetAmount})`; }; const isIncomingTransaction = direction === TxDirections.Incoming; diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/activity/helpers/common-tx-transformer.ts b/apps/browser-extension-wallet/src/views/browser-view/features/activity/helpers/common-tx-transformer.ts index 53fe854859..3fa5e9b354 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/activity/helpers/common-tx-transformer.ts +++ b/apps/browser-extension-wallet/src/views/browser-view/features/activity/helpers/common-tx-transformer.ts @@ -7,7 +7,7 @@ import { getTransactionTotalAmount } from '@src/utils/get-transaction-total-amou import type { TransformedActivity, TransformedTransactionActivity } from './types'; import { ActivityStatus, - DelegationTransactionType, + DelegationActivityType, TxDetails, TxDetailsCertificateTitles, TxDetailsVotingProceduresTitles, @@ -54,13 +54,13 @@ const splitDelegationTx = (tx: TransformedActivity): TransformedTransactionActiv return [ { ...tx, - type: DelegationTransactionType.delegation, + type: DelegationActivityType.delegation, // Deposit already shown in the delegationRegistration deposit: undefined }, { ...tx, - type: DelegationTransactionType.delegationRegistration, + type: DelegationActivityType.delegationRegistration, // Let registration show just the deposit, // and the other transaction show fee to avoid duplicity fee: '0' @@ -70,13 +70,13 @@ const splitDelegationTx = (tx: TransformedActivity): TransformedTransactionActiv return [ { ...tx, - type: DelegationTransactionType.delegation, + type: DelegationActivityType.delegation, // Reclaimed deposit already shown in the delegationDeregistration depositReclaim: undefined }, { ...tx, - type: DelegationTransactionType.delegationDeregistration, + type: DelegationActivityType.delegationDeregistration, // Let de-registration show just the returned deposit, // and the other transaction show fee to avoid duplicity fee: '0' @@ -87,7 +87,7 @@ const splitDelegationTx = (tx: TransformedActivity): TransformedTransactionActiv return [ { ...tx, - type: DelegationTransactionType.delegation + type: DelegationActivityType.delegation } ]; }; @@ -184,7 +184,7 @@ export const txTransformer = async ({ // SDK Ticket LW-8767 should fix the type of Input in TxInFlight to contain the address const type = inspectTxType({ walletAddresses, tx: tx as unknown as Wallet.Cardano.HydratedTx }); - if (type === DelegationTransactionType.delegation) { + if (type === DelegationActivityType.delegation) { return splitDelegationTx(baseTransformedActivity); } diff --git a/packages/core/src/ui/assets/icons/arrow-diagonal-down-outline.component.svg b/packages/core/src/ui/assets/icons/arrow-diagonal-down-outline.component.svg new file mode 100644 index 0000000000..d0611e4523 --- /dev/null +++ b/packages/core/src/ui/assets/icons/arrow-diagonal-down-outline.component.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/core/src/ui/assets/icons/arrow-diagonal-up-outline.component.svg b/packages/core/src/ui/assets/icons/arrow-diagonal-up-outline.component.svg new file mode 100644 index 0000000000..cf7c2fe17e --- /dev/null +++ b/packages/core/src/ui/assets/icons/arrow-diagonal-up-outline.component.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/core/src/ui/assets/icons/badge-check-outline.component.svg b/packages/core/src/ui/assets/icons/badge-check-outline.component.svg new file mode 100644 index 0000000000..b548d7cc7e --- /dev/null +++ b/packages/core/src/ui/assets/icons/badge-check-outline.component.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/core/src/ui/assets/icons/ban-outline.component.svg b/packages/core/src/ui/assets/icons/ban-outline.component.svg new file mode 100644 index 0000000000..37df0adaee --- /dev/null +++ b/packages/core/src/ui/assets/icons/ban-outline.component.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/core/src/ui/assets/icons/briefcase-back-icon.component.svg b/packages/core/src/ui/assets/icons/briefcase-back-icon.component.svg new file mode 100644 index 0000000000..6223a4a1eb --- /dev/null +++ b/packages/core/src/ui/assets/icons/briefcase-back-icon.component.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/core/src/ui/assets/icons/briefcase-outline.component.svg b/packages/core/src/ui/assets/icons/briefcase-outline.component.svg new file mode 100644 index 0000000000..c998d37659 --- /dev/null +++ b/packages/core/src/ui/assets/icons/briefcase-outline.component.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/core/src/ui/assets/icons/clipboard-check-outline.component.svg b/packages/core/src/ui/assets/icons/clipboard-check-outline.component.svg new file mode 100644 index 0000000000..c26f611c44 --- /dev/null +++ b/packages/core/src/ui/assets/icons/clipboard-check-outline.component.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/core/src/ui/assets/icons/clipboard-copy-outline.component.svg b/packages/core/src/ui/assets/icons/clipboard-copy-outline.component.svg new file mode 100644 index 0000000000..a81655b75e --- /dev/null +++ b/packages/core/src/ui/assets/icons/clipboard-copy-outline.component.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/core/src/ui/assets/icons/clipboard-list-outline.component.svg b/packages/core/src/ui/assets/icons/clipboard-list-outline.component.svg new file mode 100644 index 0000000000..e5c5f1f0be --- /dev/null +++ b/packages/core/src/ui/assets/icons/clipboard-list-outline.component.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/core/src/ui/assets/icons/clipboard-x-outline.component.svg b/packages/core/src/ui/assets/icons/clipboard-x-outline.component.svg new file mode 100644 index 0000000000..b301373908 --- /dev/null +++ b/packages/core/src/ui/assets/icons/clipboard-x-outline.component.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/core/src/ui/assets/icons/delegation.component.svg b/packages/core/src/ui/assets/icons/delegation.component.svg deleted file mode 100644 index 0d0e38c15e..0000000000 --- a/packages/core/src/ui/assets/icons/delegation.component.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/packages/core/src/ui/assets/icons/document-add-outline.component.svg b/packages/core/src/ui/assets/icons/document-add-outline.component.svg new file mode 100644 index 0000000000..a5dc33e574 --- /dev/null +++ b/packages/core/src/ui/assets/icons/document-add-outline.component.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/core/src/ui/assets/icons/document-report-outline.component.svg b/packages/core/src/ui/assets/icons/document-report-outline.component.svg new file mode 100644 index 0000000000..5fc6ced68f --- /dev/null +++ b/packages/core/src/ui/assets/icons/document-report-outline.component.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/core/src/ui/assets/icons/document-text-outline.component.svg b/packages/core/src/ui/assets/icons/document-text-outline.component.svg new file mode 100644 index 0000000000..42dd9ac667 --- /dev/null +++ b/packages/core/src/ui/assets/icons/document-text-outline.component.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/core/src/ui/assets/icons/feather-outline.component.svg b/packages/core/src/ui/assets/icons/feather-outline.component.svg new file mode 100644 index 0000000000..7936549916 --- /dev/null +++ b/packages/core/src/ui/assets/icons/feather-outline.component.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/core/src/ui/assets/icons/gift-outline.component.svg b/packages/core/src/ui/assets/icons/gift-outline.component.svg new file mode 100644 index 0000000000..7c91bda1c9 --- /dev/null +++ b/packages/core/src/ui/assets/icons/gift-outline.component.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/core/src/ui/assets/icons/identification-outline.component.svg b/packages/core/src/ui/assets/icons/identification-outline.component.svg new file mode 100644 index 0000000000..c358c79647 --- /dev/null +++ b/packages/core/src/ui/assets/icons/identification-outline.component.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/core/src/ui/assets/icons/incoming.component.svg b/packages/core/src/ui/assets/icons/incoming.component.svg deleted file mode 100644 index 9b30f162c6..0000000000 --- a/packages/core/src/ui/assets/icons/incoming.component.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/packages/core/src/ui/assets/icons/info-outline.component.svg b/packages/core/src/ui/assets/icons/info-outline.component.svg new file mode 100644 index 0000000000..26f63e2799 --- /dev/null +++ b/packages/core/src/ui/assets/icons/info-outline.component.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/core/src/ui/assets/icons/outgoing.component.svg b/packages/core/src/ui/assets/icons/outgoing.component.svg deleted file mode 100644 index d7a81aab49..0000000000 --- a/packages/core/src/ui/assets/icons/outgoing.component.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/packages/core/src/ui/assets/icons/receipt-right-outline.component.svg b/packages/core/src/ui/assets/icons/receipt-right-outline.component.svg new file mode 100644 index 0000000000..a9a0500e6b --- /dev/null +++ b/packages/core/src/ui/assets/icons/receipt-right-outline.component.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/core/src/ui/assets/icons/refresh-outline.component.svg b/packages/core/src/ui/assets/icons/refresh-outline.component.svg new file mode 100644 index 0000000000..c426d7d57b --- /dev/null +++ b/packages/core/src/ui/assets/icons/refresh-outline.component.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/core/src/ui/assets/icons/self-transaction.component.svg b/packages/core/src/ui/assets/icons/self-transaction.component.svg deleted file mode 100644 index 71df1564fe..0000000000 --- a/packages/core/src/ui/assets/icons/self-transaction.component.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/packages/core/src/ui/assets/icons/terminal-outile.component.svg b/packages/core/src/ui/assets/icons/terminal-outile.component.svg new file mode 100644 index 0000000000..53f81a73b7 --- /dev/null +++ b/packages/core/src/ui/assets/icons/terminal-outile.component.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/core/src/ui/assets/icons/ticket-forward-icon.component.svg b/packages/core/src/ui/assets/icons/ticket-forward-icon.component.svg new file mode 100644 index 0000000000..858186e12e --- /dev/null +++ b/packages/core/src/ui/assets/icons/ticket-forward-icon.component.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/core/src/ui/assets/icons/ticket-icon.component.svg b/packages/core/src/ui/assets/icons/ticket-icon.component.svg deleted file mode 100644 index d1bc0535bd..0000000000 --- a/packages/core/src/ui/assets/icons/ticket-icon.component.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/packages/core/src/ui/assets/icons/ticket-outline-icon.component.svg b/packages/core/src/ui/assets/icons/ticket-outline-icon.component.svg new file mode 100644 index 0000000000..5a1413c4dd --- /dev/null +++ b/packages/core/src/ui/assets/icons/ticket-outline-icon.component.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/core/src/ui/components/Activity/AssetActivityItem.tsx b/packages/core/src/ui/components/Activity/AssetActivityItem.tsx index e2f308122f..54bddccf8d 100644 --- a/packages/core/src/ui/components/Activity/AssetActivityItem.tsx +++ b/packages/core/src/ui/components/Activity/AssetActivityItem.tsx @@ -1,3 +1,4 @@ +/* eslint-disable unicorn/consistent-function-scoping */ /* eslint-disable react/no-multi-comp */ import React, { useMemo, useRef, useEffect, useCallback } from 'react'; import debounce from 'lodash/debounce'; @@ -9,12 +10,7 @@ import { ReactComponent as ErrorIcon } from '../../assets/icons/error.component. import pluralize from 'pluralize'; import { txIconSize } from '@src/ui/utils/icon-size'; import { useTranslate } from '@src/ui/hooks'; -import { - DelegationTransactionType, - TransactionActivityType, - ConwayEraGovernanceActions, - ConwayEraCertificatesTypes -} from '../ActivityDetail/types'; +import { DelegationActivityType, TransactionActivityType } from '../ActivityDetail/types'; import type { ActivityType } from '../ActivityDetail/types'; import styles from './AssetActivityItem.module.scss'; import { ActivityTypeIcon } from '../ActivityDetail/ActivityTypeIcon'; @@ -71,7 +67,7 @@ export interface AssetActivityItemProps { const DELEGATION_ASSET_NUMBER = 1; interface ActivityStatusIconProps { - status: string; + status: ActivityStatus; type: ActivityType; } @@ -105,47 +101,12 @@ export const AssetActivityItem = ({ formattedTimestamp }: AssetActivityItemProps): React.ReactElement => { const { t } = useTranslate(); - - const translationTypes: Record = { - [TransactionActivityType.rewards]: 'package.core.assetActivityItem.entry.name.rewards', - [TransactionActivityType.incoming]: 'package.core.assetActivityItem.entry.name.incoming', - [TransactionActivityType.outgoing]: 'package.core.assetActivityItem.entry.name.outgoing', - [TransactionActivityType.self]: 'package.core.assetActivityItem.entry.name.self', - [DelegationTransactionType.delegation]: 'package.core.assetActivityItem.entry.name.delegation', - [DelegationTransactionType.delegationDeregistration]: - 'package.core.assetActivityItem.entry.name.delegationDeregistration', - [DelegationTransactionType.delegationRegistration]: - 'package.core.assetActivityItem.entry.name.delegationRegistration', - [ConwayEraGovernanceActions.vote]: 'package.core.assetActivityItem.entry.name.vote', - [ConwayEraGovernanceActions.submitProposal]: 'package.core.assetActivityItem.entry.name.submitProposal', - [ConwayEraCertificatesTypes.RegisterDelegateRepresentative]: - 'package.core.assetActivityItem.entry.name.RegisterDelegateRepresentativeCertificate', - [ConwayEraCertificatesTypes.UnregisterDelegateRepresentative]: - 'package.core.assetActivityItem.entry.name.UnregisterDelegateRepresentativeCertificate', - [ConwayEraCertificatesTypes.UpdateDelegateRepresentative]: - 'package.core.assetActivityItem.entry.name.UpdateDelegateRepresentativeCertificate', - [ConwayEraCertificatesTypes.StakeVoteDelegation]: - 'package.core.assetActivityItem.entry.name.StakeVoteDelegationCertificate', - [ConwayEraCertificatesTypes.StakeRegistrationDelegation]: - 'package.core.assetActivityItem.entry.name.StakeRegistrationDelegateCertificate', - [ConwayEraCertificatesTypes.StakeVoteRegistrationDelegation]: - 'package.core.assetActivityItem.entry.name.StakeVoteRegistrationDelegateCertificate', - [ConwayEraCertificatesTypes.VoteDelegation]: 'package.core.assetActivityItem.entry.name.VoteDelegationCertificate', - [ConwayEraCertificatesTypes.VoteRegistrationDelegation]: - 'package.core.assetActivityItem.entry.name.VoteRegistrationDelegateCertificate', - [ConwayEraCertificatesTypes.ResignCommitteeCold]: - 'package.core.assetActivityItem.entry.name.ResignCommitteeColdCertificate', - [ConwayEraCertificatesTypes.AuthorizeCommitteeHot]: - 'package.core.assetActivityItem.entry.name.AuthorizeCommitteeHotCertificate' - }; - const ref = useRef(null); const [assetsToShow, setAssetsToShow] = React.useState(0); const getText = useCallback( (items: number): { text: string; suffix: string } => { - if (type in DelegationTransactionType || type === TransactionActivityType.self) - return { text: amount, suffix: '' }; + if (type in DelegationActivityType || type === TransactionActivityType.self) return { text: amount, suffix: '' }; const assetsIdsText = assets ?.slice(0, items) @@ -191,7 +152,7 @@ export const AssetActivityItem = ({ const assetsText = useMemo(() => getText(assetsToShow), [getText, assetsToShow]); const assetAmountContent = - type in DelegationTransactionType ? ( + type in DelegationActivityType ? (

{DELEGATION_ASSET_NUMBER} {t('package.core.assetActivityItem.entry.token')}

@@ -220,9 +181,9 @@ export const AssetActivityItem = ({
- {isPendingTx && type !== TransactionActivityType.self && !(type in DelegationTransactionType) + {isPendingTx && type !== TransactionActivityType.self && !(type in DelegationActivityType) ? t('package.core.assetActivityItem.entry.name.sending') - : t(translationTypes[type])} + : t(`package.core.assetActivityItem.entry.name.${type}`)}
{descriptionContent}
diff --git a/packages/core/src/ui/components/ActivityDetail/ActivityTypeIcon.module.scss b/packages/core/src/ui/components/ActivityDetail/ActivityTypeIcon.module.scss index 471316e923..8b94e7a1ad 100644 --- a/packages/core/src/ui/components/ActivityDetail/ActivityTypeIcon.module.scss +++ b/packages/core/src/ui/components/ActivityDetail/ActivityTypeIcon.module.scss @@ -1,14 +1,34 @@ +@import '../../../../../common/src/ui/styles/abstracts/typography'; @import '../../styles/theme.scss'; -.governance { + +.iconWrapper { + width: size_unit(6); + height: size_unit(6); + border-radius: 100%; + background: rgba(#2cb67d, 0.1); + &.governance { + background: rgba(#3489f7, 0.1); + } + + @media (max-width: $breakpoint-popup) { + width: size_unit(5); + height: size_unit(5); + } +} + +.icon { + color: var(--data-green, #2cb67d) !important; + font-size: 22px !important; + + @media (max-width: $breakpoint-popup) { + font-size: 16px !important; + } + + &.governance { + color: var(--text-color-blue, #3489f7) !important; + } + svg { - path, - circle, - line, - polyline, - rect, - ellipse { - fill: var(--text-color-blue, #3489f7); - color: var(--text-color-blue, #3489f7); - } + fill: none; } } diff --git a/packages/core/src/ui/components/ActivityDetail/ActivityTypeIcon.tsx b/packages/core/src/ui/components/ActivityDetail/ActivityTypeIcon.tsx index 663c6a605f..c1fd79e0be 100644 --- a/packages/core/src/ui/components/ActivityDetail/ActivityTypeIcon.tsx +++ b/packages/core/src/ui/components/ActivityDetail/ActivityTypeIcon.tsx @@ -3,48 +3,70 @@ import React from 'react'; import cn from 'classnames'; import Icon, { QuestionOutlined } from '@ant-design/icons'; import { txIconSize } from '@src/ui/utils/icon-size'; -import { ReactComponent as OutgoingIcon } from '../../assets/icons/outgoing.component.svg'; -import { ReactComponent as IncomingIcon } from '../../assets/icons/incoming.component.svg'; -import { ReactComponent as SelfIcon } from '../../assets/icons/self-transaction.component.svg'; -import { ReactComponent as DelegationIcon } from '../../assets/icons/delegation.component.svg'; -import { ReactComponent as RegistrationIcon } from '../../assets/icons/registration.component.svg'; -import { ReactComponent as DeregistrationIcon } from '../../assets/icons/deregistration.component.svg'; -import { ReactComponent as RewardsIcon } from '../../assets/icons/rewards.component.svg'; -import { ReactComponent as VoteIcon } from '../../assets/icons/ticket-icon.component.svg'; +import { ReactComponent as OutgoingIcon } from '../../assets/icons/arrow-diagonal-up-outline.component.svg'; +import { ReactComponent as IncomingIcon } from '../../assets/icons/arrow-diagonal-down-outline.component.svg'; +import { ReactComponent as RefreshOutlinedIcon } from '../../assets/icons/refresh-outline.component.svg'; +import { ReactComponent as DelegationIcon } from '../../assets/icons/receipt-right-outline.component.svg'; +import { ReactComponent as ClipboardCheckOutlineIcon } from '../../assets/icons/clipboard-check-outline.component.svg'; +import { ReactComponent as ClipboardXOutlineComponentIcon } from '../../assets/icons/clipboard-x-outline.component.svg'; +import { ReactComponent as RewardsIcon } from '../../assets/icons/gift-outline.component.svg'; +import { ReactComponent as VoteIcon } from '../../assets/icons/ticket-outline-icon.component.svg'; +import { ReactComponent as HardForkInitiationActionIcon } from '../../assets/icons/terminal-outile.component.svg'; +import { ReactComponent as ParameterChangeActionIcon } from '../../assets/icons/clipboard-list-outline.component.svg'; +import { ReactComponent as TreasuryWithdrawalsActionIcon } from '../../assets/icons/clipboard-copy-outline.component.svg'; +import { ReactComponent as UpdateCommitteeIcon } from '../../assets/icons/document-add-outline.component.svg'; +import { ReactComponent as InfoActionIcon } from '../../assets/icons/info-outline.component.svg'; +import { ReactComponent as StakeVoteDelegationIcon } from '../../assets/icons/document-report-outline.component.svg'; +import { ReactComponent as StakeRegistrationDelegationIcon } from '../../assets/icons/badge-check-outline.component.svg'; +import { ReactComponent as VoteRegistrationDelegationIcon } from '../../assets/icons/identification-outline.component.svg'; +import { ReactComponent as StakeVoteRegistrationDelegationIcon } from '../../assets/icons/document-text-outline.component.svg'; +import { ReactComponent as ResignCommitteeColdIcon } from '../../assets/icons/ban-outline.component.svg'; +import { ReactComponent as AuthorizeCommitteeHotIcon } from '../../assets/icons/feather-outline.component.svg'; +import { ReactComponent as RegisterDelegateRepresentativeIcon } from '../../assets/icons/briefcase-outline.component.svg'; +import { ReactComponent as UnregisterDelegateRepresentativeIcon } from '../../assets/icons/briefcase-back-icon.component.svg'; + import { ActivityType, ConwayEraCertificatesTypes, ConwayEraGovernanceActions, - DelegationTransactionType, - TransactionActivityType + DelegationActivityType, + TransactionActivityType, + Cip1694GovernanceActivityType } from './types'; import styles from './ActivityTypeIcon.module.scss'; +import { Flex } from '@lace/ui'; export interface ActivityTypeIconProps { type: ActivityType; } const activityTypeIcon: Record>> = { + [ConwayEraGovernanceActions.vote]: VoteIcon, + [Cip1694GovernanceActivityType.HardForkInitiationAction]: HardForkInitiationActionIcon, + [Cip1694GovernanceActivityType.NewConstitution]: ClipboardCheckOutlineIcon, + [Cip1694GovernanceActivityType.NoConfidence]: ClipboardXOutlineComponentIcon, + [Cip1694GovernanceActivityType.ParameterChangeAction]: ParameterChangeActionIcon, + [Cip1694GovernanceActivityType.TreasuryWithdrawalsAction]: TreasuryWithdrawalsActionIcon, + [Cip1694GovernanceActivityType.UpdateCommittee]: UpdateCommitteeIcon, + [Cip1694GovernanceActivityType.InfoAction]: InfoActionIcon, + [ConwayEraCertificatesTypes.UpdateDelegateRepresentative]: RefreshOutlinedIcon, + [ConwayEraCertificatesTypes.StakeVoteDelegation]: StakeVoteDelegationIcon, + [ConwayEraCertificatesTypes.StakeRegistrationDelegation]: StakeRegistrationDelegationIcon, + [ConwayEraCertificatesTypes.VoteRegistrationDelegation]: VoteRegistrationDelegationIcon, + [ConwayEraCertificatesTypes.StakeVoteRegistrationDelegation]: StakeVoteRegistrationDelegationIcon, + [ConwayEraCertificatesTypes.ResignCommitteeCold]: ResignCommitteeColdIcon, + [ConwayEraCertificatesTypes.AuthorizeCommitteeHot]: AuthorizeCommitteeHotIcon, + [ConwayEraCertificatesTypes.RegisterDelegateRepresentative]: RegisterDelegateRepresentativeIcon, + [ConwayEraCertificatesTypes.UnregisterDelegateRepresentative]: UnregisterDelegateRepresentativeIcon, + [TransactionActivityType.rewards]: RewardsIcon, [TransactionActivityType.incoming]: IncomingIcon, [TransactionActivityType.outgoing]: OutgoingIcon, - [TransactionActivityType.self]: SelfIcon, - [DelegationTransactionType.delegation]: DelegationIcon, - [ConwayEraGovernanceActions.vote]: VoteIcon, - [ConwayEraGovernanceActions.submitProposal]: DelegationIcon, - [DelegationTransactionType.delegationDeregistration]: DeregistrationIcon, - [DelegationTransactionType.delegationRegistration]: RegistrationIcon, - [ConwayEraCertificatesTypes.RegisterDelegateRepresentative]: RegistrationIcon, - [ConwayEraCertificatesTypes.UnregisterDelegateRepresentative]: DeregistrationIcon, - [ConwayEraCertificatesTypes.StakeRegistrationDelegation]: RegistrationIcon, - [ConwayEraCertificatesTypes.StakeVoteRegistrationDelegation]: DelegationIcon, - [ConwayEraCertificatesTypes.VoteRegistrationDelegation]: DelegationIcon, - [ConwayEraCertificatesTypes.UpdateDelegateRepresentative]: RegistrationIcon, - [ConwayEraCertificatesTypes.StakeVoteDelegation]: DelegationIcon, - [ConwayEraCertificatesTypes.VoteDelegation]: DelegationIcon, - [ConwayEraCertificatesTypes.ResignCommitteeCold]: DelegationIcon, - [ConwayEraCertificatesTypes.AuthorizeCommitteeHot]: DelegationIcon + [TransactionActivityType.self]: RefreshOutlinedIcon, + [DelegationActivityType.delegation]: DelegationIcon, + [DelegationActivityType.delegationDeregistration]: ClipboardXOutlineComponentIcon, + [DelegationActivityType.delegationRegistration]: ClipboardCheckOutlineIcon }; export const ActivityTypeIcon = ({ type }: ActivityTypeIconProps): React.ReactElement => { @@ -55,17 +77,23 @@ export const ActivityTypeIcon = ({ type }: ActivityTypeIconProps): React.ReactEl Object.values(ConwayEraCertificatesTypes).includes(type as unknown as ConwayEraCertificatesTypes) || type in ConwayEraGovernanceActions; - // TODO: set fill color for every icon using currentColor, replace icon not to contain surrounding circle (LW-9566) - return icon ? ( - - ) : ( - + return ( + + {icon ? ( + + ) : ( + + )} + ); }; diff --git a/packages/core/src/ui/components/ActivityDetail/types.ts b/packages/core/src/ui/components/ActivityDetail/types.ts index 0ef6be2419..96a1685b03 100644 --- a/packages/core/src/ui/components/ActivityDetail/types.ts +++ b/packages/core/src/ui/components/ActivityDetail/types.ts @@ -1,12 +1,11 @@ import { Wallet } from '@lace/cardano'; -// TODO: use Cip30TxType instead? +// supported certificates actions export enum ConwayEraCertificatesTypes { 'AuthorizeCommitteeHot' = Wallet.Cardano.CertificateType.AuthorizeCommitteeHot, 'RegisterDelegateRepresentative' = Wallet.Cardano.CertificateType.RegisterDelegateRepresentative, 'ResignCommitteeCold' = Wallet.Cardano.CertificateType.ResignCommitteeCold, 'VoteRegistrationDelegation' = Wallet.Cardano.CertificateType.VoteRegistrationDelegation, - 'VoteDelegation' = Wallet.Cardano.CertificateType.VoteDelegation, 'UpdateDelegateRepresentative' = Wallet.Cardano.CertificateType.UpdateDelegateRepresentative, 'UnregisterDelegateRepresentative' = Wallet.Cardano.CertificateType.UnregisterDelegateRepresentative, 'StakeVoteRegistrationDelegation' = Wallet.Cardano.CertificateType.StakeVoteRegistrationDelegation, @@ -14,12 +13,22 @@ export enum ConwayEraCertificatesTypes { 'StakeRegistrationDelegation' = Wallet.Cardano.CertificateType.StakeRegistrationDelegation } +// cip 1694 governance actions +export enum Cip1694GovernanceActivityType { + ParameterChangeAction = 'ParameterChangeAction', + HardForkInitiationAction = 'HardForkInitiationAction', + TreasuryWithdrawalsAction = 'TreasuryWithdrawalsAction', + NoConfidence = 'NoConfidence', + UpdateCommittee = 'UpdateCommittee', + NewConstitution = 'NewConstitution', + InfoAction = 'InfoAction' +} + export enum ConwayEraGovernanceActions { - 'vote' = 'vote', - 'submitProposal' = 'submitProposal' + 'vote' = 'vote' } -export enum DelegationTransactionType { +export enum DelegationActivityType { 'delegation' = 'delegation', 'delegationRegistration' = 'delegationRegistration', 'delegationDeregistration' = 'delegationDeregistration' @@ -69,5 +78,8 @@ export type TxDetail = { export type TxDetails = TxDetail[]; -export type GovernanceTransactionTypes = ConwayEraCertificatesTypes | ConwayEraGovernanceActions; -export type ActivityType = DelegationTransactionType | TransactionActivityType | GovernanceTransactionTypes; +export type GovernanceTransactionTypes = + | ConwayEraCertificatesTypes + | ConwayEraGovernanceActions + | Cip1694GovernanceActivityType; +export type ActivityType = DelegationActivityType | TransactionActivityType | GovernanceTransactionTypes; diff --git a/packages/core/src/ui/lib/translations/en.json b/packages/core/src/ui/lib/translations/en.json index b8773ec10e..73ec7b664c 100644 --- a/packages/core/src/ui/lib/translations/en.json +++ b/packages/core/src/ui/lib/translations/en.json @@ -18,18 +18,24 @@ "outgoing": "Sent", "sending": "Sending", "self": "Self Transaction", + "vote": "Vote Signing", + "HardForkInitiationAction": "Hard Fork Initiation Action", + "NewConstitution": "New Constitution Action", + "NoConfidence": "No Confidence Action", + "ParameterChangeAction": "Parameter Change Action", + "TreasuryWithdrawalsAction": "Treasury Withdrawals Action", + "UpdateCommittee": "Update Committee Action", + "InfoAction": "Info Action", + "UpdateDelegateRepresentativeCertificate": "Update DRep", + "StakeVoteDelegationCertificate": "Stake Vote Delegation Certificate", + "StakeRegistrationDelegationCertificate": "Stake Registration Delegation Certificate", + "VoteRegistrationDelegationCertificate": "Vote Registration Delegation Certificate", + "StakeVoteRegistrationDelegationCertificate": "Stake Vote Registration Delegation Certificate", + "ResignCommitteeColdCertificate": "Resign Committee", + "AuthorizeCommitteeHotCertificate": "Authorise Committee", "RegisterDelegateRepresentativeCertificate": "DRep Registration", "UnregisterDelegateRepresentativeCertificate": "DRep De-Registration", - "UpdateDelegateRepresentativeCertificate": "DRep Update", - "ResignCommitteeColdCertificate": "Resign Committee", - "StakeRegistrationDelegationCertificate": "Stake Key Registration & Delgation", - "StakeVoteDelegationCertificate": "Stake Key Registration & DRep Delegation", - "StakeVoteRegistrationDelegationCertificate": "Stake Key Registration, Delegation, & DRep Delegation", - "VoteRegistrationDelegationCertificate": "Stake Key Registration & DRep Delegation", - "AuthorizeCommitteeHotCertificate": "Authorize Committee", - "VoteDelegationCertificate": "Vote Delegation", - "vote": "Vote Signing", - "submitProposal": "Governance Proposal" + "VoteDelegationCertificate": "Vote Delegation" }, "certificates": { "headings": { diff --git a/packages/staking/src/features/overview/helpers/hasPendingDelegationTransaction.ts b/packages/staking/src/features/overview/helpers/hasPendingDelegationTransaction.ts index b87f950438..540e3cca6c 100644 --- a/packages/staking/src/features/overview/helpers/hasPendingDelegationTransaction.ts +++ b/packages/staking/src/features/overview/helpers/hasPendingDelegationTransaction.ts @@ -1,7 +1,7 @@ -import { ActivityStatus, AssetActivityListProps, DelegationTransactionType } from '@lace/core'; +import { ActivityStatus, AssetActivityListProps, DelegationActivityType } from '@lace/core'; import flatMap from 'lodash/flatMap'; export const hasPendingDelegationTransaction = (walletActivities: AssetActivityListProps[]) => flatMap(walletActivities, ({ items }) => items).some( - ({ type, status }) => type && type in DelegationTransactionType && status === ActivityStatus.PENDING + ({ type, status }) => type && type in DelegationActivityType && status === ActivityStatus.PENDING ); From 610107e05ac46ff7f7df1605d92c5e7d90fc2251 Mon Sep 17 00:00:00 2001 From: Michael Chappell <7581002+mchappell@users.noreply.github.com> Date: Wed, 31 Jan 2024 12:17:06 +0000 Subject: [PATCH 35/68] feat(extension): update drep mapper check --- .../activity/helpers/common-tx-transformer.ts | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/activity/helpers/common-tx-transformer.ts b/apps/browser-extension-wallet/src/views/browser-view/features/activity/helpers/common-tx-transformer.ts index 53fe854859..3e10d5077d 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/activity/helpers/common-tx-transformer.ts +++ b/apps/browser-extension-wallet/src/views/browser-view/features/activity/helpers/common-tx-transformer.ts @@ -228,16 +228,14 @@ type TransactionGovernanceProposal = { }; const drepMapper = (drep: Wallet.Cardano.DelegateRepresentative) => { - switch (true) { - case Wallet.Cardano.isDRepAlwaysAbstain(drep): - return 'alwaysAbstain'; - case Wallet.Cardano.isDRepAlwaysNoConfidence(drep): - return 'alwaysNoConfidence'; - case Wallet.Cardano.isDRepCredential(drep): - return Wallet.Cardano.DRepID(drep.hash); - default: - throw new Error('incorrect drep supplied'); + if (Wallet.Cardano.isDRepAlwaysAbstain(drep)) { + return 'alwaysAbstain'; + } else if (Wallet.Cardano.isDRepAlwaysNoConfidence(drep)) { + return 'alwaysNoConfidence'; + } else if (Wallet.Cardano.isDRepCredential(drep)) { + return Wallet.Cardano.DRepID(drep.hash); } + throw new Error('incorrect drep supplied'); }; export const certificateTransformer = ( From 8032c9a114356b1b1e7e8671af9465b54cc4818b Mon Sep 17 00:00:00 2001 From: vetalcore Date: Wed, 31 Jan 2024 14:25:12 +0200 Subject: [PATCH 36/68] fix(extension): convert drepid and stakekeyhash to bech32, fix translations --- .../ConfirmStakeRegistrationDelegationContainer.tsx | 4 ++-- .../ConfirmStakeVoteDelegationContainer.tsx | 6 +++--- .../ConfirmStakeVoteRegistrationDelegationContainer.tsx | 6 +++--- .../ConfirmVoteRegistrationDelegationContainer.tsx | 6 +++--- .../__tests__/ConfirmDRepRegistrationContainer.test.tsx | 5 +++-- .../__tests__/ConfirmDRepRetirementContainer.test.tsx | 5 +++-- .../__tests__/ConfirmDRepUpdateContainer.test.tsx | 5 +++-- .../ConfirmStakeRegistrationDelegationContainer.test.tsx | 3 ++- .../__tests__/ConfirmStakeVoteDelegationContainer.test.tsx | 5 +++-- ...ConfirmStakeVoteRegistrationDelegationContainer.test.tsx | 5 +++-- .../__tests__/ConfirmVoteDelegationContainer.test.tsx | 5 ++--- .../ConfirmVoteRegistrationDelegationContainer.test.tsx | 5 +++-- .../__tests__/VotingProceduresContainer.test.tsx | 5 ++--- .../features/activity/helpers/common-tx-transformer.ts | 5 +++-- packages/core/src/ui/lib/translations/en.json | 6 +++--- 15 files changed, 41 insertions(+), 35 deletions(-) diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeRegistrationDelegationContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeRegistrationDelegationContainer.tsx index c6f2ecea70..481fe71b7c 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeRegistrationDelegationContainer.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeRegistrationDelegationContainer.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { ConfirmStakeRegistrationDelegation } from '@lace/core'; import { SignTxData } from './types'; -import { certificateInspectorFactory } from './utils'; +import { certificateInspectorFactory, drepIDasBech32FromHash } from './utils'; import { Wallet } from '@lace/cardano'; import { useWalletStore } from '@src/stores'; @@ -32,7 +32,7 @@ export const ConfirmStakeRegistrationDelegationContainer = ({ dappInfo={signTxData.dappInfo} metadata={{ poolId: certificate.poolId, - stakeKeyHash: certificate.stakeCredential.hash, + stakeKeyHash: drepIDasBech32FromHash(certificate.stakeCredential.hash), depositPaid: depositPaidWithCardanoSymbol }} translations={{ diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeVoteDelegationContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeVoteDelegationContainer.tsx index 3b73d16459..73769237f5 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeVoteDelegationContainer.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeVoteDelegationContainer.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { ConfirmStakeVoteDelegation } from '@lace/core'; import { SignTxData } from './types'; -import { certificateInspectorFactory } from './utils'; +import { certificateInspectorFactory, drepIDasBech32FromHash } from './utils'; import { Wallet } from '@lace/cardano'; const { CertificateType } = Wallet.Cardano; @@ -24,10 +24,10 @@ export const ConfirmStakeVoteDelegationContainer = ({ signTxData, errorMessage } dappInfo={signTxData.dappInfo} metadata={{ poolId: certificate.poolId, - stakeKeyHash: certificate.stakeCredential.hash, + stakeKeyHash: drepIDasBech32FromHash(certificate.stakeCredential.hash), alwaysAbstain: Wallet.Cardano.isDRepAlwaysAbstain(dRep), alwaysNoConfidence: Wallet.Cardano.isDRepAlwaysNoConfidence(dRep), - ...(Wallet.Cardano.isDRepCredential(dRep) ? { drepId: dRep.hash } : {}) + ...(Wallet.Cardano.isDRepCredential(dRep) ? { drepId: drepIDasBech32FromHash(dRep.hash) } : {}) }} translations={{ metadata: t('core.StakeVoteDelegation.metadata'), diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeVoteRegistrationDelegationContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeVoteRegistrationDelegationContainer.tsx index 76d3881fe0..0113839942 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeVoteRegistrationDelegationContainer.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeVoteRegistrationDelegationContainer.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { ConfirmStakeVoteRegistrationDelegation } from '@lace/core'; import { SignTxData } from './types'; -import { certificateInspectorFactory } from './utils'; +import { certificateInspectorFactory, drepIDasBech32FromHash } from './utils'; import { Wallet } from '@lace/cardano'; import { useWalletStore } from '@src/stores'; @@ -34,11 +34,11 @@ export const ConfirmStakeVoteRegistrationDelegationContainer = ({ dappInfo={signTxData.dappInfo} metadata={{ poolId: certificate.poolId, - stakeKeyHash: certificate.stakeCredential.hash, + stakeKeyHash: drepIDasBech32FromHash(certificate.stakeCredential.hash), depositPaid: depositPaidWithCardanoSymbol, alwaysAbstain: Wallet.Cardano.isDRepAlwaysAbstain(dRep), alwaysNoConfidence: Wallet.Cardano.isDRepAlwaysNoConfidence(dRep), - ...(Wallet.Cardano.isDRepCredential(dRep) ? { drepId: dRep.hash } : {}) + ...(Wallet.Cardano.isDRepCredential(dRep) ? { drepId: drepIDasBech32FromHash(dRep.hash) } : {}) }} translations={{ metadata: t('core.StakeVoteDelegationRegistration.metadata'), diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmVoteRegistrationDelegationContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmVoteRegistrationDelegationContainer.tsx index 5a0d86b9a2..3d7a7a178d 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmVoteRegistrationDelegationContainer.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmVoteRegistrationDelegationContainer.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { ConfirmVoteRegistrationDelegation } from '@lace/core'; import { SignTxData } from './types'; -import { certificateInspectorFactory } from './utils'; +import { certificateInspectorFactory, drepIDasBech32FromHash } from './utils'; import { Wallet } from '@lace/cardano'; import { useWalletStore } from '@src/stores'; @@ -31,10 +31,10 @@ export const ConfirmVoteRegistrationDelegationContainer = ({ signTxData, errorMe dappInfo={signTxData.dappInfo} metadata={{ depositPaid: depositPaidWithCardanoSymbol, - stakeKeyHash: certificate.stakeCredential.hash, + stakeKeyHash: drepIDasBech32FromHash(certificate.stakeCredential.hash), alwaysAbstain: Wallet.Cardano.isDRepAlwaysAbstain(dRep), alwaysNoConfidence: Wallet.Cardano.isDRepAlwaysNoConfidence(dRep), - ...(Wallet.Cardano.isDRepCredential(dRep) ? { drepId: dRep.hash } : {}) + ...(Wallet.Cardano.isDRepCredential(dRep) ? { drepId: drepIDasBech32FromHash(dRep.hash) } : {}) }} translations={{ metadata: t('core.VoteRegistrationDelegation.metadata'), diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepRegistrationContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepRegistrationContainer.test.tsx index 56101f36a3..d3f3475914 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepRegistrationContainer.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepRegistrationContainer.test.tsx @@ -15,11 +15,12 @@ import { buildMockTx } from '@src/utils/mocks/tx'; import { Wallet } from '@lace/cardano'; import BigNumber from 'bignumber.js'; import { getWrapper } from '../testing.utils'; +import { drepIDasBech32FromHash } from '../utils'; const LOVELACE_VALUE = 1_000_000; const DEFAULT_DECIMALS = 2; -const { Cardano, Crypto, HexBlob } = Wallet; +const { Cardano, Crypto } = Wallet; const assetInfo$ = new BehaviorSubject(new Map()); const available$ = new BehaviorSubject([]); @@ -120,7 +121,7 @@ describe('Testing ConfirmDRepRegistrationContainer component', () => { depositPaid: `${new BigNumber(certificate.deposit.toString()) .dividedBy(LOVELACE_VALUE) .toFixed(DEFAULT_DECIMALS)} ${cardanoCoinMock.symbol}`, - drepId: Cardano.DRepID(HexBlob.toTypedBech32('drep', Wallet.HexBlob(certificate.dRepCredential.hash))), + drepId: drepIDasBech32FromHash(certificate.dRepCredential.hash), hash: certificate.anchor?.dataHash, url: certificate.anchor?.url }, diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepRetirementContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepRetirementContainer.test.tsx index c7bdc6d5ae..3b51826c82 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepRetirementContainer.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepRetirementContainer.test.tsx @@ -27,10 +27,11 @@ import { buildMockTx } from '@src/utils/mocks/tx'; import { Wallet } from '@lace/cardano'; import BigNumber from 'bignumber.js'; import { getWrapper } from '../testing.utils'; +import { drepIDasBech32FromHash } from '../utils'; const LOVELACE_VALUE = 1_000_000; const DEFAULT_DECIMALS = 2; -const { Cardano, Crypto, HexBlob } = Wallet; +const { Cardano, Crypto } = Wallet; const assetInfo$ = new BehaviorSubject(new Map()); const available$ = new BehaviorSubject([]); @@ -168,7 +169,7 @@ describe('Testing ConfirmDRepRetirementContainer component', () => { depositReturned: `${new BigNumber(certificate.deposit.toString()) .dividedBy(LOVELACE_VALUE) .toFixed(DEFAULT_DECIMALS)} ${cardanoCoinMock.symbol}`, - drepId: Cardano.DRepID(HexBlob.toTypedBech32('drep', Wallet.HexBlob(certificate.dRepCredential.hash))) + drepId: drepIDasBech32FromHash(certificate.dRepCredential.hash) }, translations: { metadata: t('core.DRepRetirement.metadata'), diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepUpdateContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepUpdateContainer.test.tsx index 62aaaec965..c224fd97e8 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepUpdateContainer.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepUpdateContainer.test.tsx @@ -14,8 +14,9 @@ import { act } from 'react-dom/test-utils'; import { buildMockTx } from '@src/utils/mocks/tx'; import { Wallet } from '@lace/cardano'; import { getWrapper } from '../testing.utils'; +import { drepIDasBech32FromHash } from '../utils'; -const { Cardano, Crypto, HexBlob } = Wallet; +const { Cardano, Crypto } = Wallet; const assetInfo$ = new BehaviorSubject(new Map()); const available$ = new BehaviorSubject([]); @@ -112,7 +113,7 @@ describe('Testing ConfirmDRepUpdateContainer component', () => { { dappInfo, metadata: { - drepId: Cardano.DRepID(HexBlob.toTypedBech32('drep', Wallet.HexBlob(certificate.dRepCredential.hash))), + drepId: drepIDasBech32FromHash(certificate.dRepCredential.hash), hash: certificate.anchor?.dataHash, url: certificate.anchor?.url }, diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmStakeRegistrationDelegationContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmStakeRegistrationDelegationContainer.test.tsx index f8252136c2..60d2149325 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmStakeRegistrationDelegationContainer.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmStakeRegistrationDelegationContainer.test.tsx @@ -15,6 +15,7 @@ import { act } from 'react-dom/test-utils'; import { buildMockTx } from '@src/utils/mocks/tx'; import { Wallet } from '@lace/cardano'; import { getWrapper } from '../testing.utils'; +import { drepIDasBech32FromHash } from '../utils'; const REWARD_ACCOUNT = Wallet.Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj'); const STAKE_KEY_HASH = Wallet.Cardano.RewardAccount.toHash(REWARD_ACCOUNT); @@ -130,7 +131,7 @@ describe('Testing ConfirmStakeRegistrationDelegationContainer component', () => dappInfo, metadata: { poolId: certificate.poolId, - stakeKeyHash: certificate.stakeCredential.hash, + stakeKeyHash: drepIDasBech32FromHash(certificate.stakeCredential.hash), depositPaid: `${certificate.deposit.toString()} ${cardanoCoinMock.symbol}` }, translations: { diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmStakeVoteDelegationContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmStakeVoteDelegationContainer.test.tsx index ccd3dba70f..7739dd36f6 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmStakeVoteDelegationContainer.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmStakeVoteDelegationContainer.test.tsx @@ -17,6 +17,7 @@ import { act } from 'react-dom/test-utils'; import { buildMockTx } from '@src/utils/mocks/tx'; import { Wallet } from '@lace/cardano'; import { getWrapper } from '../testing.utils'; +import { drepIDasBech32FromHash } from '../utils'; const REWARD_ACCOUNT = Wallet.Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj'); const STAKE_KEY_HASH = Wallet.Cardano.RewardAccount.toHash(REWARD_ACCOUNT); @@ -144,10 +145,10 @@ describe('Testing ConfirmStakeVoteDelegationContainer component', () => { dappInfo, metadata: { poolId: certificate.poolId, - stakeKeyHash: certificate.stakeCredential.hash, + stakeKeyHash: drepIDasBech32FromHash(certificate.stakeCredential.hash), alwaysAbstain: isDRepAlwaysAbstainMocked, alwaysNoConfidence: isDRepAlwaysNoConfidenceMocked, - drepId: (certificate.dRep as Wallet.Cardano.Credential).hash.toString() + drepId: drepIDasBech32FromHash((certificate.dRep as Wallet.Cardano.Credential).hash) }, translations: { metadata: t('core.StakeVoteDelegation.metadata'), diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmStakeVoteRegistrationDelegationContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmStakeVoteRegistrationDelegationContainer.test.tsx index 37aa8cb203..12ed965039 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmStakeVoteRegistrationDelegationContainer.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmStakeVoteRegistrationDelegationContainer.test.tsx @@ -18,6 +18,7 @@ import { act } from 'react-dom/test-utils'; import { buildMockTx } from '@src/utils/mocks/tx'; import { Wallet } from '@lace/cardano'; import { getWrapper } from '../testing.utils'; +import { drepIDasBech32FromHash } from '../utils'; const REWARD_ACCOUNT = Wallet.Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj'); const STAKE_KEY_HASH = Wallet.Cardano.RewardAccount.toHash(REWARD_ACCOUNT); @@ -154,11 +155,11 @@ describe('Testing ConfirmStakeVoteRegistrationDelegationContainer component', () dappInfo, metadata: { poolId: certificate.poolId, - stakeKeyHash: certificate.stakeCredential.hash, + stakeKeyHash: drepIDasBech32FromHash(certificate.stakeCredential.hash), depositPaid: `${certificate.deposit.toString()} ${cardanoCoinMock.symbol}`, alwaysAbstain: isDRepAlwaysAbstainMocked, alwaysNoConfidence: isDRepAlwaysNoConfidenceMocked, - drepId: (certificate.dRep as Wallet.Cardano.Credential).hash.toString() + drepId: drepIDasBech32FromHash((certificate.dRep as Wallet.Cardano.Credential).hash) }, translations: { metadata: t('core.StakeVoteDelegationRegistration.metadata'), diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmVoteDelegationContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmVoteDelegationContainer.test.tsx index 1b4c2bdbe2..f889dbb735 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmVoteDelegationContainer.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmVoteDelegationContainer.test.tsx @@ -14,6 +14,7 @@ import { act } from 'react-dom/test-utils'; import { buildMockTx } from '@src/utils/mocks/tx'; import { Wallet } from '@lace/cardano'; import { getWrapper } from '../testing.utils'; +import { drepIDasBech32FromHash } from '../utils'; const REWARD_ACCOUNT = Wallet.Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj'); const STAKE_KEY_HASH = Wallet.Cardano.RewardAccount.toHash(REWARD_ACCOUNT); @@ -117,9 +118,7 @@ describe('Testing ConfirmVoteDelegationContainer component', () => { metadata: { alwaysAbstain: false, alwaysNoConfidence: false, - drepId: Wallet.Cardano.DRepID( - Wallet.HexBlob.toTypedBech32('drep', Wallet.HexBlob((dRep as unknown as Wallet.Cardano.Credential).hash)) - ) + drepId: drepIDasBech32FromHash((dRep as unknown as Wallet.Cardano.Credential).hash) }, translations: { metadata: t('core.VoteDelegation.metadata'), diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmVoteRegistrationDelegationContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmVoteRegistrationDelegationContainer.test.tsx index ffc8debe9f..0edbb6f7d4 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmVoteRegistrationDelegationContainer.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmVoteRegistrationDelegationContainer.test.tsx @@ -18,6 +18,7 @@ import { act } from 'react-dom/test-utils'; import { buildMockTx } from '@src/utils/mocks/tx'; import { Wallet } from '@lace/cardano'; import { getWrapper } from '../testing.utils'; +import { drepIDasBech32FromHash } from '../utils'; const REWARD_ACCOUNT = Wallet.Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj'); const STAKE_KEY_HASH = Wallet.Cardano.RewardAccount.toHash(REWARD_ACCOUNT); @@ -151,10 +152,10 @@ describe('Testing ConfirmVoteRegistrationDelegationContainer component', () => { dappInfo, metadata: { depositPaid: `${certificate.deposit.toString()} ${cardanoCoinMock.symbol}`, - stakeKeyHash: certificate.stakeCredential.hash, + stakeKeyHash: drepIDasBech32FromHash(certificate.stakeCredential.hash), alwaysAbstain: isDRepAlwaysAbstainMocked, alwaysNoConfidence: isDRepAlwaysNoConfidenceMocked, - drepId: (certificate.dRep as Wallet.Cardano.Credential).hash.toString() + drepId: drepIDasBech32FromHash((certificate.dRep as Wallet.Cardano.Credential).hash) }, translations: { metadata: t('core.VoteRegistrationDelegation.metadata'), diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/VotingProceduresContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/VotingProceduresContainer.test.tsx index 3477128747..b9fc792993 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/VotingProceduresContainer.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/VotingProceduresContainer.test.tsx @@ -18,6 +18,7 @@ import { buildMockTx } from '@src/utils/mocks/tx'; import { Wallet } from '@lace/cardano'; import { getWrapper } from '../testing.utils'; import { getVoterType, getVote, VoterTypeEnum, VotesEnum } from '@src/utils/tx-inspection'; +import { drepIDasBech32FromHash } from '../utils'; jest.mock('@src/stores', () => ({ ...jest.requireActual('@src/stores'), @@ -168,9 +169,7 @@ describe('Testing VotingProceduresContainer component', () => { expect(queryByTestId('VotingProcedures')).toBeInTheDocument(); // eslint-disable-next-line unicorn/consistent-function-scoping const getExpectedDrepId = (type: string) => (hash: Wallet.Crypto.Hash28ByteBase16) => - type === VoterTypeEnum.DREP - ? Wallet.Cardano.DRepID(Wallet.HexBlob.toTypedBech32('drep', Wallet.HexBlob(hash))) - : hash.toString(); + type === VoterTypeEnum.DREP ? drepIDasBech32FromHash(hash) : hash.toString(); expect(mockVotingProcedures).toHaveBeenLastCalledWith( { dappInfo, diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/activity/helpers/common-tx-transformer.ts b/apps/browser-extension-wallet/src/views/browser-view/features/activity/helpers/common-tx-transformer.ts index 72dca0c404..8674215833 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/activity/helpers/common-tx-transformer.ts +++ b/apps/browser-extension-wallet/src/views/browser-view/features/activity/helpers/common-tx-transformer.ts @@ -18,6 +18,7 @@ import { import capitalize from 'lodash/capitalize'; import dayjs from 'dayjs'; import isEmpty from 'lodash/isEmpty'; +import { drepIDasBech32FromHash } from '@src/features/dapp/components/confirm-transaction/utils'; const { util, GovernanceActionType } = Wallet.Cardano; @@ -285,7 +286,7 @@ export const certificateTransformer = ( if (conwayEraCertificate.dRepCredential) { transformedCertificate.push({ title: 'drepCredential', - details: [conwayEraCertificate.dRepCredential.hash] + details: [drepIDasBech32FromHash(conwayEraCertificate.dRepCredential.hash)] }); } @@ -311,7 +312,7 @@ export const votingProceduresTransformer = ( const voterType = getVoterType(procedure.voter.__typename); const voterCredential = voterType === VoterTypeEnum.DREP - ? Wallet.Cardano.DRepID(Wallet.HexBlob.toTypedBech32('drep', Wallet.HexBlob(procedure.voter.credential.hash))) + ? drepIDasBech32FromHash(procedure.voter.credential.hash) : procedure.voter.credential.hash; const detail: TxDetails = [ { diff --git a/packages/core/src/ui/lib/translations/en.json b/packages/core/src/ui/lib/translations/en.json index 73ec7b664c..2f7cc1faa3 100644 --- a/packages/core/src/ui/lib/translations/en.json +++ b/packages/core/src/ui/lib/translations/en.json @@ -127,10 +127,10 @@ "UnregisterDelegateRepresentativeCertificate": "DRep De-Registration", "UpdateDelegateRepresentativeCertificate": "DRep Update", "ResignCommitteeColdCertificate": "Resign Committee", - "StakeRegistrationDelegationCertificate": "Stake Key Registration & Delgation", + "StakeRegistrationDelegateCertificate": "Stake Key Registration & Delgation", "StakeVoteDelegationCertificate": "Stake Key Registration & DRep Delegation", - "StakeVoteRegistrationDelegationCertificate": "Stake Key Registration, Delegation, & DRep Delegation", - "VoteRegistrationDelegationCertificate": "Stake Key Registration & DRep Delegation", + "StakeVoteRegistrationDelegateCertificate": "Stake Key Registration, Delegation, & DRep Delegation", + "VoteRegistrationDelegateCertificate": "Stake Key Registration & DRep Delegation", "AuthorizeCommitteeHotCertificate": "Authorize Committee", "VoteDelegationCertificate": "Vote Delegation", "vote": "Vote Signing", From 169b125f244aa05adf2e1d619d4692b19c8eb1b2 Mon Sep 17 00:00:00 2001 From: vetalcore Date: Wed, 31 Jan 2024 16:35:34 +0200 Subject: [PATCH 37/68] fix(extension): lw-9688: unable to see transaction details for conway-era transactions --- .../features/activity/helpers/common-tx-transformer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/activity/helpers/common-tx-transformer.ts b/apps/browser-extension-wallet/src/views/browser-view/features/activity/helpers/common-tx-transformer.ts index 8674215833..6441f0c68e 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/activity/helpers/common-tx-transformer.ts +++ b/apps/browser-extension-wallet/src/views/browser-view/features/activity/helpers/common-tx-transformer.ts @@ -286,7 +286,7 @@ export const certificateTransformer = ( if (conwayEraCertificate.dRepCredential) { transformedCertificate.push({ title: 'drepCredential', - details: [drepIDasBech32FromHash(conwayEraCertificate.dRepCredential.hash)] + details: [conwayEraCertificate.dRepCredential.hash] }); } From 46171d97d249138111c60715c24f2be57ce4b177 Mon Sep 17 00:00:00 2001 From: vetalcore Date: Thu, 1 Feb 2024 18:14:47 +0200 Subject: [PATCH 38/68] fix(extension): resolve lw-9621 pe comments --- .../browser-extension-wallet/src/lib/translations/en.json | 6 ++---- .../src/utils/mocks/raw-transactions.ts | 8 ++++++++ packages/core/src/ui/components/ActivityDetail/types.ts | 1 + packages/core/src/ui/lib/translations/en.json | 3 +-- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/apps/browser-extension-wallet/src/lib/translations/en.json b/apps/browser-extension-wallet/src/lib/translations/en.json index 32286c5987..340d292d8f 100644 --- a/apps/browser-extension-wallet/src/lib/translations/en.json +++ b/apps/browser-extension-wallet/src/lib/translations/en.json @@ -38,12 +38,10 @@ "VoteRegistrationDelegationCertificate": "Vote Registration Delegation Certificate", "StakeVoteRegistrationDelegationCertificate": "Stake Vote Registration Delegation Certificate", "ResignCommitteeColdCertificate": "Resign Committee", - "AuthorizeCommitteeHotCertificate": "Authorise Committee", + "AuthorizeCommitteeHotCertificate": "Authorize Committee", "RegisterDelegateRepresentativeCertificate": "DRep Registration", "UnregisterDelegateRepresentativeCertificate": "DRep De-Registration", - "VoteDelegationCertificate": "Vote Delegation", - - "submitProposal": "Confirm Vote" + "VoteDelegationCertificate": "Vote Delegation" }, "certificates": { "headings": { diff --git a/apps/browser-extension-wallet/src/utils/mocks/raw-transactions.ts b/apps/browser-extension-wallet/src/utils/mocks/raw-transactions.ts index 7f17269f9f..31ce46f3fa 100644 --- a/apps/browser-extension-wallet/src/utils/mocks/raw-transactions.ts +++ b/apps/browser-extension-wallet/src/utils/mocks/raw-transactions.ts @@ -71,3 +71,11 @@ export const drepRetirementTx: Wallet.Cardano.HydratedTx = { certificates: [mockConwayCertificates[ConwayEraCertificatesTypes.UnregisterDelegateRepresentative]] } }; + +export const voteDelegationTx: Wallet.Cardano.HydratedTx = { + ...tx, + body: { + ...tx.body, + certificates: [mockConwayCertificates[ConwayEraCertificatesTypes.VoteDelegation]] + } +}; diff --git a/packages/core/src/ui/components/ActivityDetail/types.ts b/packages/core/src/ui/components/ActivityDetail/types.ts index 96a1685b03..47566f939e 100644 --- a/packages/core/src/ui/components/ActivityDetail/types.ts +++ b/packages/core/src/ui/components/ActivityDetail/types.ts @@ -6,6 +6,7 @@ export enum ConwayEraCertificatesTypes { 'RegisterDelegateRepresentative' = Wallet.Cardano.CertificateType.RegisterDelegateRepresentative, 'ResignCommitteeCold' = Wallet.Cardano.CertificateType.ResignCommitteeCold, 'VoteRegistrationDelegation' = Wallet.Cardano.CertificateType.VoteRegistrationDelegation, + 'VoteDelegation' = Wallet.Cardano.CertificateType.VoteDelegation, 'UpdateDelegateRepresentative' = Wallet.Cardano.CertificateType.UpdateDelegateRepresentative, 'UnregisterDelegateRepresentative' = Wallet.Cardano.CertificateType.UnregisterDelegateRepresentative, 'StakeVoteRegistrationDelegation' = Wallet.Cardano.CertificateType.StakeVoteRegistrationDelegation, diff --git a/packages/core/src/ui/lib/translations/en.json b/packages/core/src/ui/lib/translations/en.json index 2f7cc1faa3..418dcb8c1a 100644 --- a/packages/core/src/ui/lib/translations/en.json +++ b/packages/core/src/ui/lib/translations/en.json @@ -32,7 +32,7 @@ "VoteRegistrationDelegationCertificate": "Vote Registration Delegation Certificate", "StakeVoteRegistrationDelegationCertificate": "Stake Vote Registration Delegation Certificate", "ResignCommitteeColdCertificate": "Resign Committee", - "AuthorizeCommitteeHotCertificate": "Authorise Committee", + "AuthorizeCommitteeHotCertificate": "Authorize Committee", "RegisterDelegateRepresentativeCertificate": "DRep Registration", "UnregisterDelegateRepresentativeCertificate": "DRep De-Registration", "VoteDelegationCertificate": "Vote Delegation" @@ -134,7 +134,6 @@ "AuthorizeCommitteeHotCertificate": "Authorize Committee", "VoteDelegationCertificate": "Vote Delegation", "vote": "Vote Signing", - "submitProposal": "Governance Proposal", "certificates": "Certificates", "certificateTitles": { "certificateType": "Certificate type", From eb630357865ec37189ee0ada79b061ed8849844e Mon Sep 17 00:00:00 2001 From: vetalcore Date: Thu, 1 Feb 2024 18:23:57 +0200 Subject: [PATCH 39/68] fix(extension): resolve lw-9621 pr comments --- .../browser-extension-wallet/src/utils/mocks/certificates.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/browser-extension-wallet/src/utils/mocks/certificates.ts b/apps/browser-extension-wallet/src/utils/mocks/certificates.ts index d5cc7f53ea..a039f264b8 100644 --- a/apps/browser-extension-wallet/src/utils/mocks/certificates.ts +++ b/apps/browser-extension-wallet/src/utils/mocks/certificates.ts @@ -30,6 +30,11 @@ export const mockConwayCertificates: Partial< dRepCredential: CREDENTIAL, anchor: null }, + [ConwayEraCertificatesTypes.VoteDelegation]: { + __typename: Wallet.Cardano.CertificateType.VoteDelegation, + dRep: CREDENTIAL, + stakeCredential: CREDENTIAL + }, [ConwayEraCertificatesTypes.StakeVoteDelegation]: { __typename: Wallet.Cardano.CertificateType.StakeVoteDelegation, stakeCredential: CREDENTIAL, From 26e71997dbf56e943ac6060fb5d74095b1cba0f7 Mon Sep 17 00:00:00 2001 From: vetalcore Date: Fri, 2 Feb 2024 14:53:24 +0200 Subject: [PATCH 40/68] fix(extension): lw-9621 add storybook showing all conway era governance activity items --- .../ticket-arrow-right-outline.component.svg | 3 + .../Activity/AssetActivityList.stories.tsx | 56 +++++++++++++++++++ .../ActivityDetail/ActivityTypeIcon.tsx | 3 +- .../NoSharedWalletFoundDialog.stories.ts | 3 +- 4 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 packages/core/src/ui/assets/icons/ticket-arrow-right-outline.component.svg create mode 100644 packages/core/src/ui/components/Activity/AssetActivityList.stories.tsx diff --git a/packages/core/src/ui/assets/icons/ticket-arrow-right-outline.component.svg b/packages/core/src/ui/assets/icons/ticket-arrow-right-outline.component.svg new file mode 100644 index 0000000000..858186e12e --- /dev/null +++ b/packages/core/src/ui/assets/icons/ticket-arrow-right-outline.component.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/core/src/ui/components/Activity/AssetActivityList.stories.tsx b/packages/core/src/ui/components/Activity/AssetActivityList.stories.tsx new file mode 100644 index 0000000000..481f793e80 --- /dev/null +++ b/packages/core/src/ui/components/Activity/AssetActivityList.stories.tsx @@ -0,0 +1,56 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { AssetActivityList } from './AssetActivityList'; +import { ComponentProps } from 'react'; +import { + ConwayEraGovernanceActions, + Cip1694GovernanceActivityType, + ConwayEraCertificatesTypes +} from '../ActivityDetail'; + +const meta: Meta = { + title: 'Activity/AssetActivityList', + component: AssetActivityList, + parameters: { + layout: 'centered' + } +}; + +export default meta; +type Story = StoryObj; + +const activityTypes = [ + ConwayEraGovernanceActions.vote, + Cip1694GovernanceActivityType.HardForkInitiationAction, + Cip1694GovernanceActivityType.NewConstitution, + Cip1694GovernanceActivityType.NoConfidence, + Cip1694GovernanceActivityType.ParameterChangeAction, + Cip1694GovernanceActivityType.TreasuryWithdrawalsAction, + Cip1694GovernanceActivityType.UpdateCommittee, + Cip1694GovernanceActivityType.InfoAction, + ConwayEraCertificatesTypes.UpdateDelegateRepresentative, + ConwayEraCertificatesTypes.StakeVoteDelegation, + ConwayEraCertificatesTypes.StakeRegistrationDelegation, + ConwayEraCertificatesTypes.VoteRegistrationDelegation, + ConwayEraCertificatesTypes.StakeVoteRegistrationDelegation, + ConwayEraCertificatesTypes.ResignCommitteeCold, + ConwayEraCertificatesTypes.AuthorizeCommitteeHot, + ConwayEraCertificatesTypes.RegisterDelegateRepresentative, + ConwayEraCertificatesTypes.UnregisterDelegateRepresentative, + ConwayEraCertificatesTypes.VoteDelegation +]; + +const data: ComponentProps = { + items: activityTypes.map((type) => ({ + amount: '0.17 ADA', + fiatAmount: '0.04 USD', + type, + formattedTimestamp: '00:00:00' + })) +}; + +export const Overview: Story = { + args: { + ...data + } +}; diff --git a/packages/core/src/ui/components/ActivityDetail/ActivityTypeIcon.tsx b/packages/core/src/ui/components/ActivityDetail/ActivityTypeIcon.tsx index c1fd79e0be..bf8e57f709 100644 --- a/packages/core/src/ui/components/ActivityDetail/ActivityTypeIcon.tsx +++ b/packages/core/src/ui/components/ActivityDetail/ActivityTypeIcon.tsx @@ -24,6 +24,7 @@ import { ReactComponent as ResignCommitteeColdIcon } from '../../assets/icons/ba import { ReactComponent as AuthorizeCommitteeHotIcon } from '../../assets/icons/feather-outline.component.svg'; import { ReactComponent as RegisterDelegateRepresentativeIcon } from '../../assets/icons/briefcase-outline.component.svg'; import { ReactComponent as UnregisterDelegateRepresentativeIcon } from '../../assets/icons/briefcase-back-icon.component.svg'; +import { ReactComponent as VoteDelegationIcon } from '../../assets/icons/ticket-arrow-right-outline.component.svg'; import { ActivityType, @@ -59,7 +60,7 @@ const activityTypeIcon: Record Date: Mon, 5 Feb 2024 16:31:40 +0100 Subject: [PATCH 41/68] chore(core): fix storybook build --- packages/core/.storybook/main.js | 49 ++++++++++++++++++++++++++++++++ packages/core/package.json | 1 + yarn.lock | 1 + 3 files changed, 51 insertions(+) diff --git a/packages/core/.storybook/main.js b/packages/core/.storybook/main.js index d670e8c079..0f7ef1ab8b 100644 --- a/packages/core/.storybook/main.js +++ b/packages/core/.storybook/main.js @@ -1,3 +1,5 @@ +const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin'); + module.exports = { stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'], addons: [ @@ -20,6 +22,53 @@ module.exports = { const fileLoaderRule = config.module.rules.find((rule) => rule.test?.test('.svg')); fileLoaderRule.exclude = /\.svg$/; + const jsRuleIndex = config.module.rules.findIndex((rule) => rule.test?.test('.js')); + + // Please refer to the apps/browser-extension-wallet/webpack.common.js + config.module.rules[jsRuleIndex] = { + test: /\.(js|jsx|ts|tsx)$/, + exclude: /node_modules\/(?!(@cardano-sdk)\/).*/, + loader: 'swc-loader', + options: { + jsc: { + parser: { + syntax: 'typescript', + tsx: true + }, + target: 'es2019', + loose: false + } + }, + resolve: { + fullySpecified: false + } + }; + + // Please refer to the apps/browser-extension-wallet/webpack.common.js + config.resolve = { + ...config.resolve, + fallback: { + ...config.resolve.fallback, + https: require.resolve('https-browserify'), + http: require.resolve('stream-http'), + 'get-port-please': false, + net: false, + fs: false, + os: false, + path: false, + events: require.resolve('events/'), + buffer: require.resolve('buffer/'), + stream: require.resolve('readable-stream'), + crypto: require.resolve('crypto-browserify'), + constants: require.resolve('constants-browserify'), + zlib: require.resolve('browserify-zlib'), + dns: false, + tls: false, + child_process: false + }, + plugins: [new TsconfigPathsPlugin({ configFile: 'src/tsconfig.json' })] + }; + config.module.rules.push({ test: /\.svg$/i, issuer: /\.[jt]sx?$/, diff --git a/packages/core/package.json b/packages/core/package.json index a9de3d37b7..5d010d489d 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -81,6 +81,7 @@ "@types/debounce-promise": "^3.1.6", "sass": "^1.68.0", "storybook": "^7.4.3", + "tsconfig-paths-webpack-plugin": "3.5.2", "typescript": "^4.3.5" }, "peerDependencies": { diff --git a/yarn.lock b/yarn.lock index c987c57516..9bf3cf9dab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10760,6 +10760,7 @@ __metadata: react-infinite-scroll-component: ^6.1.0 sass: ^1.68.0 storybook: ^7.4.3 + tsconfig-paths-webpack-plugin: 3.5.2 typescript: ^4.3.5 zxcvbn: ^4.4.2 peerDependencies: From 15648692986d9b2956efa3dc6ae31ffaad2fa020 Mon Sep 17 00:00:00 2001 From: Michael Chappell <7581002+mchappell@users.noreply.github.com> Date: Tue, 6 Feb 2024 20:35:48 +0000 Subject: [PATCH 42/68] fix: use correct stake address bech32 --- .../ConfirmStakeRegistrationDelegationContainer.tsx | 11 +++++++---- .../ConfirmStakeVoteDelegationContainer.tsx | 8 ++++++-- ...onfirmStakeVoteRegistrationDelegationContainer.tsx | 9 ++++++--- .../ConfirmVoteRegistrationDelegationContainer.tsx | 9 ++++++--- 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeRegistrationDelegationContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeRegistrationDelegationContainer.tsx index 481fe71b7c..0dd467f1e0 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeRegistrationDelegationContainer.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeRegistrationDelegationContainer.tsx @@ -2,11 +2,11 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { ConfirmStakeRegistrationDelegation } from '@lace/core'; import { SignTxData } from './types'; -import { certificateInspectorFactory, drepIDasBech32FromHash } from './utils'; +import { certificateInspectorFactory } from './utils'; import { Wallet } from '@lace/cardano'; import { useWalletStore } from '@src/stores'; -const { CertificateType } = Wallet.Cardano; +const { CertificateType, RewardAddress } = Wallet.Cardano; interface Props { signTxData: SignTxData; @@ -18,7 +18,8 @@ export const ConfirmStakeRegistrationDelegationContainer = ({ errorMessage }: Props): React.ReactElement => { const { - walletUI: { cardanoCoin } + walletUI: { cardanoCoin }, + currentChain } = useWalletStore(); const { t } = useTranslation(); const certificate = certificateInspectorFactory( @@ -32,7 +33,9 @@ export const ConfirmStakeRegistrationDelegationContainer = ({ dappInfo={signTxData.dappInfo} metadata={{ poolId: certificate.poolId, - stakeKeyHash: drepIDasBech32FromHash(certificate.stakeCredential.hash), + stakeKeyHash: RewardAddress.fromCredentials(currentChain.networkId, certificate.stakeCredential) + .toAddress() + .toBech32(), depositPaid: depositPaidWithCardanoSymbol }} translations={{ diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeVoteDelegationContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeVoteDelegationContainer.tsx index 73769237f5..6f16053d0a 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeVoteDelegationContainer.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeVoteDelegationContainer.tsx @@ -4,8 +4,9 @@ import { ConfirmStakeVoteDelegation } from '@lace/core'; import { SignTxData } from './types'; import { certificateInspectorFactory, drepIDasBech32FromHash } from './utils'; import { Wallet } from '@lace/cardano'; +import { useWalletStore } from '@src/stores'; -const { CertificateType } = Wallet.Cardano; +const { CertificateType, RewardAddress } = Wallet.Cardano; interface Props { signTxData: SignTxData; @@ -14,6 +15,7 @@ interface Props { export const ConfirmStakeVoteDelegationContainer = ({ signTxData, errorMessage }: Props): React.ReactElement => { const { t } = useTranslation(); + const { currentChain } = useWalletStore(); const certificate = certificateInspectorFactory( CertificateType.StakeVoteDelegation )(signTxData.tx); @@ -24,7 +26,9 @@ export const ConfirmStakeVoteDelegationContainer = ({ signTxData, errorMessage } dappInfo={signTxData.dappInfo} metadata={{ poolId: certificate.poolId, - stakeKeyHash: drepIDasBech32FromHash(certificate.stakeCredential.hash), + stakeKeyHash: RewardAddress.fromCredentials(currentChain.networkId, certificate.stakeCredential) + .toAddress() + .toBech32(), alwaysAbstain: Wallet.Cardano.isDRepAlwaysAbstain(dRep), alwaysNoConfidence: Wallet.Cardano.isDRepAlwaysNoConfidence(dRep), ...(Wallet.Cardano.isDRepCredential(dRep) ? { drepId: drepIDasBech32FromHash(dRep.hash) } : {}) diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeVoteRegistrationDelegationContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeVoteRegistrationDelegationContainer.tsx index 0113839942..41217d5fc2 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeVoteRegistrationDelegationContainer.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeVoteRegistrationDelegationContainer.tsx @@ -6,7 +6,7 @@ import { certificateInspectorFactory, drepIDasBech32FromHash } from './utils'; import { Wallet } from '@lace/cardano'; import { useWalletStore } from '@src/stores'; -const { CertificateType } = Wallet.Cardano; +const { CertificateType, RewardAddress } = Wallet.Cardano; interface Props { signTxData: SignTxData; @@ -19,7 +19,8 @@ export const ConfirmStakeVoteRegistrationDelegationContainer = ({ }: Props): React.ReactElement => { const { t } = useTranslation(); const { - walletUI: { cardanoCoin } + walletUI: { cardanoCoin }, + currentChain } = useWalletStore(); const certificate = certificateInspectorFactory( CertificateType.StakeVoteRegistrationDelegation @@ -34,7 +35,9 @@ export const ConfirmStakeVoteRegistrationDelegationContainer = ({ dappInfo={signTxData.dappInfo} metadata={{ poolId: certificate.poolId, - stakeKeyHash: drepIDasBech32FromHash(certificate.stakeCredential.hash), + stakeKeyHash: RewardAddress.fromCredentials(currentChain.networkId, certificate.stakeCredential) + .toAddress() + .toBech32(), depositPaid: depositPaidWithCardanoSymbol, alwaysAbstain: Wallet.Cardano.isDRepAlwaysAbstain(dRep), alwaysNoConfidence: Wallet.Cardano.isDRepAlwaysNoConfidence(dRep), diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmVoteRegistrationDelegationContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmVoteRegistrationDelegationContainer.tsx index 3d7a7a178d..d987f9aff1 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmVoteRegistrationDelegationContainer.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmVoteRegistrationDelegationContainer.tsx @@ -6,7 +6,7 @@ import { certificateInspectorFactory, drepIDasBech32FromHash } from './utils'; import { Wallet } from '@lace/cardano'; import { useWalletStore } from '@src/stores'; -const { CertificateType } = Wallet.Cardano; +const { CertificateType, RewardAddress } = Wallet.Cardano; interface Props { signTxData: SignTxData; @@ -16,7 +16,8 @@ interface Props { export const ConfirmVoteRegistrationDelegationContainer = ({ signTxData, errorMessage }: Props): React.ReactElement => { const { t } = useTranslation(); const { - walletUI: { cardanoCoin } + walletUI: { cardanoCoin }, + currentChain } = useWalletStore(); const certificate = certificateInspectorFactory( CertificateType.VoteRegistrationDelegation @@ -31,7 +32,9 @@ export const ConfirmVoteRegistrationDelegationContainer = ({ signTxData, errorMe dappInfo={signTxData.dappInfo} metadata={{ depositPaid: depositPaidWithCardanoSymbol, - stakeKeyHash: drepIDasBech32FromHash(certificate.stakeCredential.hash), + stakeKeyHash: RewardAddress.fromCredentials(currentChain.networkId, certificate.stakeCredential) + .toAddress() + .toBech32(), alwaysAbstain: Wallet.Cardano.isDRepAlwaysAbstain(dRep), alwaysNoConfidence: Wallet.Cardano.isDRepAlwaysNoConfidence(dRep), ...(Wallet.Cardano.isDRepCredential(dRep) ? { drepId: drepIDasBech32FromHash(dRep.hash) } : {}) From df468457a208edb5659ec303375a8fd574debbd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20W=C5=82odek?= Date: Wed, 7 Feb 2024 18:04:25 +0100 Subject: [PATCH 43/68] fix(core): sanchonet-related storybook errors [LW-9721] (#880) * fix(core): add custom viewports and proper structure to storybook * fix(core): react key prop errors --- .../ConfirmDRepRegistration.stories.tsx | 18 +++++++++++++++-- .../ConfirmDRepRetirement.stories.tsx | 18 +++++++++++++++-- .../ConfirmDRepUpdate.stories.tsx | 18 +++++++++++++++-- ...irmStakeRegistrationDelegation.stories.tsx | 18 +++++++++++++++-- .../ConfirmStakeVoteDelegation.stories.tsx | 18 +++++++++++++++-- ...takeVoteRegistrationDelegation.stories.tsx | 18 +++++++++++++++-- .../ConfirmVoteDelegation.stories.tsx | 18 +++++++++++++++-- ...firmVoteRegistrationDelegation.stories.tsx | 18 +++++++++++++++-- .../HardForkInitiationAction.stories.tsx | 2 +- .../InfoAction/InfoAction.stories.tsx | 2 +- .../NewConstitutionAction.stories.tsx | 2 +- .../NoConfidenceAction.stories.tsx | 2 +- .../ParameterChangeAction.stories.tsx | 2 +- .../TreasuryWithdrawalsAction.stories.tsx | 2 +- .../UpdateCommitteeActionAction.stories.tsx | 2 +- .../ProposalProcedures/components/Card.tsx | 4 ++-- .../VotingProcedures.stories.tsx | 20 ++++++++++++++++--- 17 files changed, 154 insertions(+), 28 deletions(-) diff --git a/packages/core/src/ui/components/ConfirmDRepRegistration/ConfirmDRepRegistration.stories.tsx b/packages/core/src/ui/components/ConfirmDRepRegistration/ConfirmDRepRegistration.stories.tsx index fd42d09034..94e3848d44 100644 --- a/packages/core/src/ui/components/ConfirmDRepRegistration/ConfirmDRepRegistration.stories.tsx +++ b/packages/core/src/ui/components/ConfirmDRepRegistration/ConfirmDRepRegistration.stories.tsx @@ -3,11 +3,25 @@ import type { Meta, StoryObj } from '@storybook/react'; import { ConfirmDRepRegistration } from './ConfirmDRepRegistration'; import { ComponentProps } from 'react'; +const customViewports = { + popup: { + name: 'Popup', + styles: { + width: '360px', + height: '600' + } + } +}; + const meta: Meta = { - title: 'ConfirmDRepRegistration', + title: 'Sanchonet/Certificates/ConfirmDRepRegistration', component: ConfirmDRepRegistration, parameters: { - layout: 'centered' + layout: 'centered', + viewport: { + viewports: customViewports, + defaultViewport: 'popup' + } } }; diff --git a/packages/core/src/ui/components/ConfirmDRepRetirement/ConfirmDRepRetirement.stories.tsx b/packages/core/src/ui/components/ConfirmDRepRetirement/ConfirmDRepRetirement.stories.tsx index 1bf53abd47..594a6fcc5f 100644 --- a/packages/core/src/ui/components/ConfirmDRepRetirement/ConfirmDRepRetirement.stories.tsx +++ b/packages/core/src/ui/components/ConfirmDRepRetirement/ConfirmDRepRetirement.stories.tsx @@ -3,11 +3,25 @@ import type { Meta, StoryObj } from '@storybook/react'; import { ConfirmDRepRetirement } from './ConfirmDRepRetirement'; import { ComponentProps } from 'react'; +const customViewports = { + popup: { + name: 'Popup', + styles: { + width: '360px', + height: '600' + } + } +}; + const meta: Meta = { - title: 'ConfirmDRepRetirement', + title: 'Sanchonet/Certificates/ConfirmDRepRetirement', component: ConfirmDRepRetirement, parameters: { - layout: 'centered' + layout: 'centered', + viewport: { + viewports: customViewports, + defaultViewport: 'popup' + } } }; diff --git a/packages/core/src/ui/components/ConfirmDRepUpdate/ConfirmDRepUpdate.stories.tsx b/packages/core/src/ui/components/ConfirmDRepUpdate/ConfirmDRepUpdate.stories.tsx index c24658cdea..5a72d541f7 100644 --- a/packages/core/src/ui/components/ConfirmDRepUpdate/ConfirmDRepUpdate.stories.tsx +++ b/packages/core/src/ui/components/ConfirmDRepUpdate/ConfirmDRepUpdate.stories.tsx @@ -3,11 +3,25 @@ import type { Meta, StoryObj } from '@storybook/react'; import { ConfirmDRepUpdate } from './ConfirmDRepUpdate'; import { ComponentProps } from 'react'; +const customViewports = { + popup: { + name: 'Popup', + styles: { + width: '360px', + height: '600' + } + } +}; + const meta: Meta = { - title: 'ConfirmDRepUpdate', + title: 'Sanchonet/Certificates/ConfirmDRepUpdate', component: ConfirmDRepUpdate, parameters: { - layout: 'centered' + layout: 'centered', + viewport: { + viewports: customViewports, + defaultViewport: 'popup' + } } }; diff --git a/packages/core/src/ui/components/ConfirmStakeRegistrationDelegation/ConfirmStakeRegistrationDelegation.stories.tsx b/packages/core/src/ui/components/ConfirmStakeRegistrationDelegation/ConfirmStakeRegistrationDelegation.stories.tsx index 1cc3a0c203..68e9b704b6 100644 --- a/packages/core/src/ui/components/ConfirmStakeRegistrationDelegation/ConfirmStakeRegistrationDelegation.stories.tsx +++ b/packages/core/src/ui/components/ConfirmStakeRegistrationDelegation/ConfirmStakeRegistrationDelegation.stories.tsx @@ -3,11 +3,25 @@ import type { Meta, StoryObj } from '@storybook/react'; import { ConfirmStakeRegistrationDelegation } from './ConfirmStakeRegistrationDelegation'; import { ComponentProps } from 'react'; +const customViewports = { + popup: { + name: 'Popup', + styles: { + width: '360px', + height: '600' + } + } +}; + const meta: Meta = { - title: 'ConfirmStakeRegistrationDelegation', + title: 'Sanchonet/Certificates/ConfirmStakeRegistrationDelegation', component: ConfirmStakeRegistrationDelegation, parameters: { - layout: 'centered' + layout: 'centered', + viewport: { + viewports: customViewports, + defaultViewport: 'popup' + } } }; diff --git a/packages/core/src/ui/components/ConfirmStakeVoteDelegation/ConfirmStakeVoteDelegation.stories.tsx b/packages/core/src/ui/components/ConfirmStakeVoteDelegation/ConfirmStakeVoteDelegation.stories.tsx index e96651dcd3..c044156a49 100644 --- a/packages/core/src/ui/components/ConfirmStakeVoteDelegation/ConfirmStakeVoteDelegation.stories.tsx +++ b/packages/core/src/ui/components/ConfirmStakeVoteDelegation/ConfirmStakeVoteDelegation.stories.tsx @@ -3,11 +3,25 @@ import type { Meta, StoryObj } from '@storybook/react'; import { ConfirmStakeVoteDelegation } from './ConfirmStakeVoteDelegation'; import { ComponentProps } from 'react'; +const customViewports = { + popup: { + name: 'Popup', + styles: { + width: '360px', + height: '600' + } + } +}; + const meta: Meta = { - title: 'ConfirmStakeVoteDelegation', + title: 'Sanchonet/Certificates/ConfirmStakeVoteDelegation', component: ConfirmStakeVoteDelegation, parameters: { - layout: 'centered' + layout: 'centered', + viewport: { + viewports: customViewports, + defaultViewport: 'popup' + } } }; diff --git a/packages/core/src/ui/components/ConfirmStakeVoteRegistrationDelegation/ConfirmStakeVoteRegistrationDelegation.stories.tsx b/packages/core/src/ui/components/ConfirmStakeVoteRegistrationDelegation/ConfirmStakeVoteRegistrationDelegation.stories.tsx index 3a0b6b7ec0..c5f8e313bb 100644 --- a/packages/core/src/ui/components/ConfirmStakeVoteRegistrationDelegation/ConfirmStakeVoteRegistrationDelegation.stories.tsx +++ b/packages/core/src/ui/components/ConfirmStakeVoteRegistrationDelegation/ConfirmStakeVoteRegistrationDelegation.stories.tsx @@ -3,11 +3,25 @@ import type { Meta, StoryObj } from '@storybook/react'; import { ConfirmStakeVoteRegistrationDelegation } from './ConfirmStakeVoteRegistrationDelegation'; import { ComponentProps } from 'react'; +const customViewports = { + popup: { + name: 'Popup', + styles: { + width: '360px', + height: '600' + } + } +}; + const meta: Meta = { - title: 'ConfirmStakeVoteRegistrationDelegation', + title: 'Sanchonet/Certificates/ConfirmStakeVoteRegistrationDelegation', component: ConfirmStakeVoteRegistrationDelegation, parameters: { - layout: 'centered' + layout: 'centered', + viewport: { + viewports: customViewports, + defaultViewport: 'popup' + } } }; diff --git a/packages/core/src/ui/components/ConfirmVoteDelegation/ConfirmVoteDelegation.stories.tsx b/packages/core/src/ui/components/ConfirmVoteDelegation/ConfirmVoteDelegation.stories.tsx index 370d321303..4856756924 100644 --- a/packages/core/src/ui/components/ConfirmVoteDelegation/ConfirmVoteDelegation.stories.tsx +++ b/packages/core/src/ui/components/ConfirmVoteDelegation/ConfirmVoteDelegation.stories.tsx @@ -3,11 +3,25 @@ import type { Meta, StoryObj } from '@storybook/react'; import { ConfirmVoteDelegation } from './ConfirmVoteDelegation'; import { ComponentProps } from 'react'; +const customViewports = { + popup: { + name: 'Popup', + styles: { + width: '360px', + height: '600' + } + } +}; + const meta: Meta = { - title: 'ConfirmVoteDelegation', + title: 'Sanchonet/Certificates/ConfirmVoteDelegation', component: ConfirmVoteDelegation, parameters: { - layout: 'centered' + layout: 'centered', + viewport: { + viewports: customViewports, + defaultViewport: 'popup' + } } }; diff --git a/packages/core/src/ui/components/ConfirmVoteRegistrationDelegation/ConfirmVoteRegistrationDelegation.stories.tsx b/packages/core/src/ui/components/ConfirmVoteRegistrationDelegation/ConfirmVoteRegistrationDelegation.stories.tsx index 2ca239f068..03427a18c5 100644 --- a/packages/core/src/ui/components/ConfirmVoteRegistrationDelegation/ConfirmVoteRegistrationDelegation.stories.tsx +++ b/packages/core/src/ui/components/ConfirmVoteRegistrationDelegation/ConfirmVoteRegistrationDelegation.stories.tsx @@ -3,11 +3,25 @@ import type { Meta, StoryObj } from '@storybook/react'; import { ConfirmVoteRegistrationDelegation } from './ConfirmVoteRegistrationDelegation'; import { ComponentProps } from 'react'; +const customViewports = { + popup: { + name: 'Popup', + styles: { + width: '360px', + height: '600' + } + } +}; + const meta: Meta = { - title: 'ConfirmVoteRegistrationDelegation', + title: 'Sanchonet/Certificates/ConfirmVoteRegistrationDelegation', component: ConfirmVoteRegistrationDelegation, parameters: { - layout: 'centered' + layout: 'centered', + viewport: { + viewports: customViewports, + defaultViewport: 'popup' + } } }; diff --git a/packages/core/src/ui/components/ProposalProcedures/HardForkInitiationAction/HardForkInitiationAction.stories.tsx b/packages/core/src/ui/components/ProposalProcedures/HardForkInitiationAction/HardForkInitiationAction.stories.tsx index b50685d593..b83903a3b6 100644 --- a/packages/core/src/ui/components/ProposalProcedures/HardForkInitiationAction/HardForkInitiationAction.stories.tsx +++ b/packages/core/src/ui/components/ProposalProcedures/HardForkInitiationAction/HardForkInitiationAction.stories.tsx @@ -14,7 +14,7 @@ const customViewports = { }; const meta: Meta = { - title: 'ProposalProcedure/HardForkInitiationAction', + title: 'Sanchonet/Proposal Procedures/HardForkInitiationAction', component: HardForkInitiationAction, parameters: { layout: 'centered', diff --git a/packages/core/src/ui/components/ProposalProcedures/InfoAction/InfoAction.stories.tsx b/packages/core/src/ui/components/ProposalProcedures/InfoAction/InfoAction.stories.tsx index 3f97136d22..12ee1f7ec3 100644 --- a/packages/core/src/ui/components/ProposalProcedures/InfoAction/InfoAction.stories.tsx +++ b/packages/core/src/ui/components/ProposalProcedures/InfoAction/InfoAction.stories.tsx @@ -14,7 +14,7 @@ const customViewports = { }; const meta: Meta = { - title: 'ProposalProcedure/InfoAction', + title: 'Sanchonet/Proposal Procedures/InfoAction', component: InfoAction, parameters: { layout: 'centered', diff --git a/packages/core/src/ui/components/ProposalProcedures/NewConstitutionAction/NewConstitutionAction.stories.tsx b/packages/core/src/ui/components/ProposalProcedures/NewConstitutionAction/NewConstitutionAction.stories.tsx index d6fc007a4c..1611deeb77 100644 --- a/packages/core/src/ui/components/ProposalProcedures/NewConstitutionAction/NewConstitutionAction.stories.tsx +++ b/packages/core/src/ui/components/ProposalProcedures/NewConstitutionAction/NewConstitutionAction.stories.tsx @@ -14,7 +14,7 @@ const customViewports = { }; const meta: Meta = { - title: 'ProposalProcedure/NewConstitutionAction', + title: 'Sanchonet/Proposal Procedures/NewConstitutionAction', component: NewConstitutionAction, parameters: { layout: 'centered', diff --git a/packages/core/src/ui/components/ProposalProcedures/NoConfidenceAction/NoConfidenceAction.stories.tsx b/packages/core/src/ui/components/ProposalProcedures/NoConfidenceAction/NoConfidenceAction.stories.tsx index 81092e9bfe..87489f87ff 100644 --- a/packages/core/src/ui/components/ProposalProcedures/NoConfidenceAction/NoConfidenceAction.stories.tsx +++ b/packages/core/src/ui/components/ProposalProcedures/NoConfidenceAction/NoConfidenceAction.stories.tsx @@ -14,7 +14,7 @@ const customViewports = { }; const meta: Meta = { - title: 'ProposalProcedure/NoConfidenceAction', + title: 'Sanchonet/Proposal Procedures/NoConfidenceAction', component: NoConfidenceAction, parameters: { layout: 'centered', diff --git a/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/ParameterChangeAction.stories.tsx b/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/ParameterChangeAction.stories.tsx index 2e4fababde..d34449ed19 100644 --- a/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/ParameterChangeAction.stories.tsx +++ b/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/ParameterChangeAction.stories.tsx @@ -14,7 +14,7 @@ const customViewports = { }; const meta: Meta = { - title: 'ProposalProcedure/ParameterChangeAction', + title: 'Sanchonet/Proposal Procedures/ParameterChangeAction', component: ParameterChangeAction, parameters: { layout: 'centered', diff --git a/packages/core/src/ui/components/ProposalProcedures/TreasuryWithdrawalsAction/TreasuryWithdrawalsAction.stories.tsx b/packages/core/src/ui/components/ProposalProcedures/TreasuryWithdrawalsAction/TreasuryWithdrawalsAction.stories.tsx index 951be959cd..e26c6d301b 100644 --- a/packages/core/src/ui/components/ProposalProcedures/TreasuryWithdrawalsAction/TreasuryWithdrawalsAction.stories.tsx +++ b/packages/core/src/ui/components/ProposalProcedures/TreasuryWithdrawalsAction/TreasuryWithdrawalsAction.stories.tsx @@ -14,7 +14,7 @@ const customViewports = { }; const meta: Meta = { - title: 'ProposalProcedure/TreasuryWithdrawalsAction', + title: 'Sanchonet/Proposal Procedures/TreasuryWithdrawalsAction', component: TreasuryWithdrawalsAction, parameters: { layout: 'centered', diff --git a/packages/core/src/ui/components/ProposalProcedures/UpdateCommitteeAction/UpdateCommitteeActionAction.stories.tsx b/packages/core/src/ui/components/ProposalProcedures/UpdateCommitteeAction/UpdateCommitteeActionAction.stories.tsx index 9750fa1551..ca3378c9cf 100644 --- a/packages/core/src/ui/components/ProposalProcedures/UpdateCommitteeAction/UpdateCommitteeActionAction.stories.tsx +++ b/packages/core/src/ui/components/ProposalProcedures/UpdateCommitteeAction/UpdateCommitteeActionAction.stories.tsx @@ -14,7 +14,7 @@ const customViewports = { }; const meta: Meta = { - title: 'ProposalProcedure/UpdateCommitteeAction', + title: 'Sanchonet/Proposal Procedures/UpdateCommitteeAction', component: UpdateCommitteeAction, parameters: { layout: 'centered', diff --git a/packages/core/src/ui/components/ProposalProcedures/components/Card.tsx b/packages/core/src/ui/components/ProposalProcedures/components/Card.tsx index 44bed28c23..503ff0ad69 100644 --- a/packages/core/src/ui/components/ProposalProcedures/components/Card.tsx +++ b/packages/core/src/ui/components/ProposalProcedures/components/Card.tsx @@ -21,7 +21,7 @@ export const Card = ({ title, tooltip, data }: Props): JSX.Element => { }); const renderRow = (props: Item) => ( - <> + {props.tooltip ? ( @@ -50,7 +50,7 @@ export const Card = ({ title, tooltip, data }: Props): JSX.Element => { )} - + ); const renderTitle = () => { diff --git a/packages/core/src/ui/components/VotingProcedures/VotingProcedures.stories.tsx b/packages/core/src/ui/components/VotingProcedures/VotingProcedures.stories.tsx index 72a297d798..3e1c6cf245 100644 --- a/packages/core/src/ui/components/VotingProcedures/VotingProcedures.stories.tsx +++ b/packages/core/src/ui/components/VotingProcedures/VotingProcedures.stories.tsx @@ -3,11 +3,25 @@ import type { Meta, StoryObj } from '@storybook/react'; import { VotingProcedures } from './VotingProcedures'; import { ComponentProps } from 'react'; +const customViewports = { + popup: { + name: 'Popup', + styles: { + width: '360px', + height: '600' + } + } +}; + const meta: Meta = { - title: 'VotingProcedures', + title: 'Sanchonet/Voting/Procedures', component: VotingProcedures, parameters: { - layout: 'centered' + layout: 'centered', + viewport: { + viewports: customViewports, + defaultViewport: 'popup' + } } }; @@ -82,7 +96,7 @@ export const MultipleVotes: Story = { { voter: { type: 'DRep', - dRepId: 'drep1cs234l5mtapethapx8cq97nkpa27xf84phruh5f6jqxa78ymlp4' + dRepId: 'drep1cs234l5mtapethapx8cq97nkpa27xf84phruh5f6jqxa78ymlp5' }, votes: [ { From 04966cca8f54a3460ddff0a1a0eb9d9dc96aad70 Mon Sep 17 00:00:00 2001 From: Michael Chappell <7581002+mchappell@users.noreply.github.com> Date: Thu, 8 Feb 2024 09:58:05 +0000 Subject: [PATCH 44/68] refactor: rename useCexplorerBaseUrl typo --- .../confirm-transaction/VotingProceduresContainer.tsx | 5 ++--- .../features/dapp/components/confirm-transaction/hooks.ts | 2 +- .../HardForkInitiationActionContainer.tsx | 4 ++-- .../proposal-procedures/InfoActionContainer.tsx | 4 ++-- .../proposal-procedures/NewConstitutionActionContainer.tsx | 4 ++-- .../proposal-procedures/NoConfidenceActionContainer.tsx | 4 ++-- .../proposal-procedures/ParameterChangeActionContainer.tsx | 4 ++-- .../TreasuryWithdrawalsActionContainer.tsx | 4 ++-- .../proposal-procedures/UpdateCommitteeActionContainer.tsx | 4 ++-- .../__tests__/HardForkInitiationActionContainer.test.tsx | 4 ++-- .../__tests__/InfoActionContainer.test.tsx | 4 ++-- .../__tests__/NewConstitutionActionContainer.test.tsx | 4 ++-- .../__tests__/NoConfidenceActionContainer.test.tsx | 4 ++-- .../__tests__/ParameterChangeActionContainer.test.tsx | 4 ++-- .../__tests__/TreasuryWithdrawalsActionContainer.test.tsx | 4 ++-- .../__tests__/UpdateCommitteeActionContainer.test.tsx | 4 ++-- 16 files changed, 31 insertions(+), 32 deletions(-) diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/VotingProceduresContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/VotingProceduresContainer.tsx index 72257f1a61..5e933f9e16 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/VotingProceduresContainer.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/VotingProceduresContainer.tsx @@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next'; import { VotingProcedures } from '@lace/core'; import { SignTxData } from './types'; import { drepIDasBech32FromHash, votingProceduresInspector } from './utils'; -import { useCExpolorerBaseUrl } from './hooks'; +import { useCexplorerBaseUrl } from './hooks'; import { VoterTypeEnum, getVote, getVoterType } from '@src/utils/tx-inspection'; interface Props { @@ -14,8 +14,7 @@ interface Props { export const VotingProceduresContainer = ({ signTxData, errorMessage }: Props): React.ReactElement => { const { t } = useTranslation(); const votingProcedures = votingProceduresInspector(signTxData.tx); - - const explorerBaseUrl = useCExpolorerBaseUrl(); + const explorerBaseUrl = useCexplorerBaseUrl(); return ( { return { loading: ownPubDRepKeyHash === undefined, ownPubDRepKeyHash }; }; -export const useCExpolorerBaseUrl = (): string => { +export const useCexplorerBaseUrl = (): string => { const [explorerBaseUrl, setExplorerBaseUrl] = useState(''); const { environmentName } = useWalletStore(); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/HardForkInitiationActionContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/HardForkInitiationActionContainer.tsx index 30b937804e..70f6bbcc51 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/HardForkInitiationActionContainer.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/HardForkInitiationActionContainer.tsx @@ -4,7 +4,7 @@ import { Wallet } from '@lace/cardano'; import { HardForkInitiationAction } from '@lace/core'; import { useWalletStore } from '@src/stores'; import { SignTxData } from '../types'; -import { useCExpolorerBaseUrl } from '../hooks'; +import { useCexplorerBaseUrl } from '../hooks'; interface Props { dappInfo: SignTxData['dappInfo']; @@ -28,7 +28,7 @@ export const HardForkInitiationActionContainer = ({ walletUI: { cardanoCoin } } = useWalletStore(); - const explorerBaseUrl = useCExpolorerBaseUrl(); + const explorerBaseUrl = useCexplorerBaseUrl(); const translations = useMemo[0]['translations']>( () => ({ diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/InfoActionContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/InfoActionContainer.tsx index de46a3b62f..7daa748597 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/InfoActionContainer.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/InfoActionContainer.tsx @@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next'; import { Wallet } from '@lace/cardano'; import { InfoAction } from '@lace/core'; import { SignTxData } from '../types'; -import { useCExpolorerBaseUrl } from '../hooks'; +import { useCexplorerBaseUrl } from '../hooks'; interface Props { dappInfo: SignTxData['dappInfo']; @@ -14,7 +14,7 @@ interface Props { export const InfoActionContainer = ({ dappInfo, anchor, errorMessage }: Props): React.ReactElement => { const { t } = useTranslation(); - const explorerBaseUrl = useCExpolorerBaseUrl(); + const explorerBaseUrl = useCexplorerBaseUrl(); const translations = useMemo[0]['translations']>( () => ({ diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/NewConstitutionActionContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/NewConstitutionActionContainer.tsx index de84864942..c5ae882e06 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/NewConstitutionActionContainer.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/NewConstitutionActionContainer.tsx @@ -4,7 +4,7 @@ import { Wallet } from '@lace/cardano'; import { NewConstitutionAction } from '@lace/core'; import { useWalletStore } from '@src/stores'; import { SignTxData } from '../types'; -import { useCExpolorerBaseUrl } from '../hooks'; +import { useCexplorerBaseUrl } from '../hooks'; interface Props { dappInfo: SignTxData['dappInfo']; @@ -28,7 +28,7 @@ export const NewConstitutionActionContainer = ({ walletUI: { cardanoCoin } } = useWalletStore(); - const explorerBaseUrl = useCExpolorerBaseUrl(); + const explorerBaseUrl = useCexplorerBaseUrl(); const translations = useMemo[0]['translations']>( () => ({ diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/NoConfidenceActionContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/NoConfidenceActionContainer.tsx index 7605b54424..b54a6b4427 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/NoConfidenceActionContainer.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/NoConfidenceActionContainer.tsx @@ -4,7 +4,7 @@ import { Wallet } from '@lace/cardano'; import { NoConfidenceAction } from '@lace/core'; import { useWalletStore } from '@src/stores'; import { SignTxData } from '../types'; -import { useCExpolorerBaseUrl } from '../hooks'; +import { useCexplorerBaseUrl } from '../hooks'; interface Props { dappInfo: SignTxData['dappInfo']; @@ -28,7 +28,7 @@ export const NoConfidenceActionContainer = ({ walletUI: { cardanoCoin } } = useWalletStore(); - const explorerBaseUrl = useCExpolorerBaseUrl(); + const explorerBaseUrl = useCexplorerBaseUrl(); const translations = useMemo[0]['translations']>( () => ({ diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/ParameterChangeActionContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/ParameterChangeActionContainer.tsx index 31264a6be2..4b115d3ea0 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/ParameterChangeActionContainer.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/ParameterChangeActionContainer.tsx @@ -6,7 +6,7 @@ import { Wallet } from '@lace/cardano'; import { ParameterChangeAction } from '@lace/core'; import { useWalletStore } from '@src/stores'; import { SignTxData } from '../types'; -import { useCExpolorerBaseUrl } from '../hooks'; +import { useCexplorerBaseUrl } from '../hooks'; interface Props { dappInfo: SignTxData['dappInfo']; @@ -30,7 +30,7 @@ export const ParameterChangeActionContainer = ({ walletUI: { cardanoCoin } } = useWalletStore(); - const explorerBaseUrl = useCExpolorerBaseUrl(); + const explorerBaseUrl = useCexplorerBaseUrl(); const translations = useMemo[0]['translations']>( () => ({ diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/TreasuryWithdrawalsActionContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/TreasuryWithdrawalsActionContainer.tsx index 6cb2d0d385..11c7dbd29e 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/TreasuryWithdrawalsActionContainer.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/TreasuryWithdrawalsActionContainer.tsx @@ -4,7 +4,7 @@ import { Wallet } from '@lace/cardano'; import { TreasuryWithdrawalsAction } from '@lace/core'; import { useWalletStore } from '@src/stores'; import { SignTxData } from '../types'; -import { useCExpolorerBaseUrl } from '../hooks'; +import { useCexplorerBaseUrl } from '../hooks'; interface Props { dappInfo: SignTxData['dappInfo']; @@ -28,7 +28,7 @@ export const TreasuryWithdrawalsActionContainer = ({ walletUI: { cardanoCoin } } = useWalletStore(); - const explorerBaseUrl = useCExpolorerBaseUrl(); + const explorerBaseUrl = useCexplorerBaseUrl(); const translations = useMemo[0]['translations']>( () => ({ diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/UpdateCommitteeActionContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/UpdateCommitteeActionContainer.tsx index 0a01112d1f..afc76340c8 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/UpdateCommitteeActionContainer.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/UpdateCommitteeActionContainer.tsx @@ -4,7 +4,7 @@ import { Wallet } from '@lace/cardano'; import { UpdateCommitteeAction } from '@lace/core'; import { useWalletStore } from '@src/stores'; import { SignTxData } from '../types'; -import { useCExpolorerBaseUrl } from '../hooks'; +import { useCexplorerBaseUrl } from '../hooks'; interface Props { dappInfo: SignTxData['dappInfo']; @@ -28,7 +28,7 @@ export const UpdateCommitteeActionContainer = ({ walletUI: { cardanoCoin } } = useWalletStore(); - const explorerBaseUrl = useCExpolorerBaseUrl(); + const explorerBaseUrl = useCexplorerBaseUrl(); const translations = useMemo[0]['translations']>( () => ({ diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/HardForkInitiationActionContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/HardForkInitiationActionContainer.test.tsx index 3ff6302ac4..c0f954cdeb 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/HardForkInitiationActionContainer.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/HardForkInitiationActionContainer.test.tsx @@ -13,7 +13,7 @@ const mockUseTranslation = jest.fn(() => ({ t })); const mockHardForkInitiationAction = jest.fn(() => ); const mockLovelacesToAdaString = jest.fn((val) => val); const mockedCExpolorerBaseUrl = 'mockedCExpolorerBaseUrl'; -const mockUseCExpolorerBaseUrl = jest.fn(() => mockedCExpolorerBaseUrl); +const mockuseCexplorerBaseUrl = jest.fn(() => mockedCExpolorerBaseUrl); import { Wallet } from '@lace/cardano'; import * as React from 'react'; import { cleanup, render } from '@testing-library/react'; @@ -50,7 +50,7 @@ jest.mock('../../hooks', () => { return { __esModule: true, ...original, - useCExpolorerBaseUrl: mockUseCExpolorerBaseUrl + useCexplorerBaseUrl: mockuseCexplorerBaseUrl }; }); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/InfoActionContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/InfoActionContainer.test.tsx index e4b11d214c..f6b6eb5fc1 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/InfoActionContainer.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/InfoActionContainer.test.tsx @@ -5,7 +5,7 @@ const t = jest.fn().mockImplementation((res) => res); const mockUseTranslation = jest.fn(() => ({ t })); const mockInfoAction = jest.fn(() => ); const mockedCExpolorerBaseUrl = 'mockedCExpolorerBaseUrl'; -const mockUseCExpolorerBaseUrl = jest.fn(() => mockedCExpolorerBaseUrl); +const mockuseCexplorerBaseUrl = jest.fn(() => mockedCExpolorerBaseUrl); import { Wallet } from '@lace/cardano'; import * as React from 'react'; import { cleanup, render } from '@testing-library/react'; @@ -37,7 +37,7 @@ jest.mock('../../hooks', () => { return { __esModule: true, ...original, - useCExpolorerBaseUrl: mockUseCExpolorerBaseUrl + useCexplorerBaseUrl: mockuseCexplorerBaseUrl }; }); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/NewConstitutionActionContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/NewConstitutionActionContainer.test.tsx index 468233c9b3..e5951e98fc 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/NewConstitutionActionContainer.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/NewConstitutionActionContainer.test.tsx @@ -13,7 +13,7 @@ const mockUseTranslation = jest.fn(() => ({ t })); const mockNewConstitutionAction = jest.fn(() => ); const mockLovelacesToAdaString = jest.fn((val) => val); const mockedCExpolorerBaseUrl = 'mockedCExpolorerBaseUrl'; -const mockUseCExpolorerBaseUrl = jest.fn(() => mockedCExpolorerBaseUrl); +const mockuseCexplorerBaseUrl = jest.fn(() => mockedCExpolorerBaseUrl); import { Wallet } from '@lace/cardano'; import * as React from 'react'; import { cleanup, render } from '@testing-library/react'; @@ -50,7 +50,7 @@ jest.mock('../../hooks', () => { return { __esModule: true, ...original, - useCExpolorerBaseUrl: mockUseCExpolorerBaseUrl + useCexplorerBaseUrl: mockuseCexplorerBaseUrl }; }); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/NoConfidenceActionContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/NoConfidenceActionContainer.test.tsx index 5d0d1ee411..ba52dd4f8a 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/NoConfidenceActionContainer.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/NoConfidenceActionContainer.test.tsx @@ -13,7 +13,7 @@ const mockUseTranslation = jest.fn(() => ({ t })); const mockNoConfidenceAction = jest.fn(() => ); const mockLovelacesToAdaString = jest.fn((val) => val); const mockedCExpolorerBaseUrl = 'mockedCExpolorerBaseUrl'; -const mockUseCExpolorerBaseUrl = jest.fn(() => mockedCExpolorerBaseUrl); +const mockuseCexplorerBaseUrl = jest.fn(() => mockedCExpolorerBaseUrl); import { Wallet } from '@lace/cardano'; import * as React from 'react'; import { cleanup, render } from '@testing-library/react'; @@ -50,7 +50,7 @@ jest.mock('../../hooks', () => { return { __esModule: true, ...original, - useCExpolorerBaseUrl: mockUseCExpolorerBaseUrl + useCexplorerBaseUrl: mockuseCexplorerBaseUrl }; }); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/ParameterChangeActionContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/ParameterChangeActionContainer.test.tsx index b0f46f2293..070f34057a 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/ParameterChangeActionContainer.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/ParameterChangeActionContainer.test.tsx @@ -14,7 +14,7 @@ const mockUseTranslation = jest.fn(() => ({ t })); const mockParameterChangeAction = jest.fn(() => ); const mockLovelacesToAdaString = jest.fn((val) => val); const mockedCExpolorerBaseUrl = 'mockedCExpolorerBaseUrl'; -const mockUseCExpolorerBaseUrl = jest.fn(() => mockedCExpolorerBaseUrl); +const mockuseCexplorerBaseUrl = jest.fn(() => mockedCExpolorerBaseUrl); import { Wallet } from '@lace/cardano'; import * as React from 'react'; import { cleanup, render } from '@testing-library/react'; @@ -52,7 +52,7 @@ jest.mock('../../hooks', () => { return { __esModule: true, ...original, - useCExpolorerBaseUrl: mockUseCExpolorerBaseUrl + useCexplorerBaseUrl: mockuseCexplorerBaseUrl }; }); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/TreasuryWithdrawalsActionContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/TreasuryWithdrawalsActionContainer.test.tsx index 701b0c2be3..ea8dc787e9 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/TreasuryWithdrawalsActionContainer.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/TreasuryWithdrawalsActionContainer.test.tsx @@ -13,7 +13,7 @@ const mockUseTranslation = jest.fn(() => ({ t })); const mockTreasuryWithdrawalsAction = jest.fn(() => ); const mockLovelacesToAdaString = jest.fn((val) => val); const mockedCExpolorerBaseUrl = 'mockedCExpolorerBaseUrl'; -const mockUseCExpolorerBaseUrl = jest.fn(() => mockedCExpolorerBaseUrl); +const mockuseCexplorerBaseUrl = jest.fn(() => mockedCExpolorerBaseUrl); import { Wallet } from '@lace/cardano'; import * as React from 'react'; import { cleanup, render } from '@testing-library/react'; @@ -50,7 +50,7 @@ jest.mock('../../hooks', () => { return { __esModule: true, ...original, - useCExpolorerBaseUrl: mockUseCExpolorerBaseUrl + useCexplorerBaseUrl: mockuseCexplorerBaseUrl }; }); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/UpdateCommitteeActionContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/UpdateCommitteeActionContainer.test.tsx index 5c99335f63..af15b693c9 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/UpdateCommitteeActionContainer.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/UpdateCommitteeActionContainer.test.tsx @@ -13,7 +13,7 @@ const mockUseTranslation = jest.fn(() => ({ t })); const mockUpdateCommitteeAction = jest.fn(() => ); const mockLovelacesToAdaString = jest.fn((val) => val); const mockedCExpolorerBaseUrl = 'mockedCExpolorerBaseUrl'; -const mockUseCExpolorerBaseUrl = jest.fn(() => mockedCExpolorerBaseUrl); +const mockuseCexplorerBaseUrl = jest.fn(() => mockedCExpolorerBaseUrl); import { Wallet } from '@lace/cardano'; import * as React from 'react'; import { cleanup, render } from '@testing-library/react'; @@ -50,7 +50,7 @@ jest.mock('../../hooks', () => { return { __esModule: true, ...original, - useCExpolorerBaseUrl: mockUseCExpolorerBaseUrl + useCexplorerBaseUrl: mockuseCexplorerBaseUrl }; }); From 5716562175ede939edd8773576f91291530f9af1 Mon Sep 17 00:00:00 2001 From: Michael Chappell <7581002+mchappell@users.noreply.github.com> Date: Thu, 8 Feb 2024 16:18:38 +0000 Subject: [PATCH 45/68] fix: add storybook back for core package --- packages/core/.storybook/main.js | 44 +++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/packages/core/.storybook/main.js b/packages/core/.storybook/main.js index 0f7ef1ab8b..e3e48abb63 100644 --- a/packages/core/.storybook/main.js +++ b/packages/core/.storybook/main.js @@ -1,4 +1,5 @@ const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin'); +const { NormalModuleReplacementPlugin, ProvidePlugin } = require('webpack'); module.exports = { stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'], @@ -69,6 +70,23 @@ module.exports = { plugins: [new TsconfigPathsPlugin({ configFile: 'src/tsconfig.json' })] }; + // Need to add similar plugins to apps/browser-extension-wallet webpack to avoid issues with stories not loading + config.plugins = [ + ...config.plugins, + new ProvidePlugin({ + Buffer: ['buffer', 'Buffer'], + process: 'process/browser' + }), + new NormalModuleReplacementPlugin( + /@dcspark\/cardano-multiplatform-lib-nodejs/, + '@dcspark/cardano-multiplatform-lib-browser' + ), + new NormalModuleReplacementPlugin( + /@emurgo\/cardano-message-signing-nodejs/, + '@emurgo/cardano-message-signing-browser' + ) + ]; + config.module.rules.push({ test: /\.svg$/i, issuer: /\.[jt]sx?$/, @@ -82,6 +100,27 @@ module.exports = { ] }); + // Required to avoid issues with components that use SDK features that might touch signing lib, review removal once we no longer require these libs + config.module.rules.push({ + test: /\.wasm$/, + type: 'javascript/auto', + use: { + loader: 'webassembly-loader-sw', + options: { + export: 'instance', + importObjectProps: + // eslint-disable-next-line max-len + `'./cardano_multiplatform_lib_bg.js': __webpack_require__("../../node_modules/@dcspark/cardano-multiplatform-lib-browser/cardano_multiplatform_lib_bg.js"), + './cardano_message_signing_bg.js': __webpack_require__("../../node_modules/@emurgo/cardano-message-signing-browser/cardano_message_signing_bg.js")` + } + } + }); + + config.experiments = { + ...config.experiments, + syncWebAssembly: true + }; + config.resolve.extensions.push('.svg'); return config; @@ -90,7 +129,10 @@ module.exports = { builder: 'webpack5', options: { lazyCompilation: true, - fsCache: true + fsCache: true, + builder: { + useSWC: true // This flag is automatically set by Storybook for all new Webpack5 projects (except Angular) in Storybook 7.6 + } } }, features: { From 8ae22a2cc3102105a7a28130d7d8a9e0df11a7cf Mon Sep 17 00:00:00 2001 From: Michael Chappell <7581002+mchappell@users.noreply.github.com> Date: Thu, 8 Feb 2024 16:19:09 +0000 Subject: [PATCH 46/68] feat: update stories for conway-era tx activity history --- apps/browser-extension-wallet/src/lib/translations/en.json | 5 ++++- apps/browser-extension-wallet/src/utils/tx-inspection.ts | 2 ++ .../ui/components/Activity/AssetActivityList.stories.tsx | 6 ++++-- .../src/ui/components/ActivityDetail/ActivityTypeIcon.tsx | 5 +++-- packages/core/src/ui/lib/translations/en.json | 5 ++++- 5 files changed, 17 insertions(+), 6 deletions(-) diff --git a/apps/browser-extension-wallet/src/lib/translations/en.json b/apps/browser-extension-wallet/src/lib/translations/en.json index 340d292d8f..0078544e59 100644 --- a/apps/browser-extension-wallet/src/lib/translations/en.json +++ b/apps/browser-extension-wallet/src/lib/translations/en.json @@ -41,7 +41,10 @@ "AuthorizeCommitteeHotCertificate": "Authorize Committee", "RegisterDelegateRepresentativeCertificate": "DRep Registration", "UnregisterDelegateRepresentativeCertificate": "DRep De-Registration", - "VoteDelegationCertificate": "Vote Delegation" + "VoteDelegationCertificate": "Vote Delegation", + "StakeRegistrationDelegateCertificate": "Stake Registration Delegation Certificate", + "StakeVoteRegistrationDelegateCertificate": "Stake Vote Registration Delegation Certificate", + "VoteRegistrationDelegateCertificate": "Vote Registration Delegation Certificate" }, "certificates": { "headings": { diff --git a/apps/browser-extension-wallet/src/utils/tx-inspection.ts b/apps/browser-extension-wallet/src/utils/tx-inspection.ts index d7e8cbd45f..db0194c6a8 100644 --- a/apps/browser-extension-wallet/src/utils/tx-inspection.ts +++ b/apps/browser-extension-wallet/src/utils/tx-inspection.ts @@ -67,6 +67,8 @@ const governanceCertificateInspection = ( return ConwayEraCertificatesTypes.StakeRegistrationDelegation; case signedCertificateTypenames.includes(CertificateType.VoteRegistrationDelegation): return ConwayEraCertificatesTypes.VoteRegistrationDelegation; + case signedCertificateTypenames.includes(CertificateType.VoteDelegation): + return ConwayEraCertificatesTypes.VoteDelegation; case signedCertificateTypenames.includes(CertificateType.StakeVoteRegistrationDelegation): return ConwayEraCertificatesTypes.StakeVoteRegistrationDelegation; case signedCertificateTypenames.includes(CertificateType.AuthorizeCommitteeHot): diff --git a/packages/core/src/ui/components/Activity/AssetActivityList.stories.tsx b/packages/core/src/ui/components/Activity/AssetActivityList.stories.tsx index 481f793e80..2de6bd4e5e 100644 --- a/packages/core/src/ui/components/Activity/AssetActivityList.stories.tsx +++ b/packages/core/src/ui/components/Activity/AssetActivityList.stories.tsx @@ -7,9 +7,10 @@ import { Cip1694GovernanceActivityType, ConwayEraCertificatesTypes } from '../ActivityDetail'; +import { ActivityStatus } from './AssetActivityItem'; const meta: Meta = { - title: 'Activity/AssetActivityList', + title: 'Sanchonet/ActivityHistory/AssetActivityList', component: AssetActivityList, parameters: { layout: 'centered' @@ -27,8 +28,8 @@ const activityTypes = [ Cip1694GovernanceActivityType.ParameterChangeAction, Cip1694GovernanceActivityType.TreasuryWithdrawalsAction, Cip1694GovernanceActivityType.UpdateCommittee, - Cip1694GovernanceActivityType.InfoAction, ConwayEraCertificatesTypes.UpdateDelegateRepresentative, + Cip1694GovernanceActivityType.InfoAction, ConwayEraCertificatesTypes.StakeVoteDelegation, ConwayEraCertificatesTypes.StakeRegistrationDelegation, ConwayEraCertificatesTypes.VoteRegistrationDelegation, @@ -45,6 +46,7 @@ const data: ComponentProps = { amount: '0.17 ADA', fiatAmount: '0.04 USD', type, + status: ActivityStatus.SUCCESS, formattedTimestamp: '00:00:00' })) }; diff --git a/packages/core/src/ui/components/ActivityDetail/ActivityTypeIcon.tsx b/packages/core/src/ui/components/ActivityDetail/ActivityTypeIcon.tsx index bf8e57f709..6e20b47f24 100644 --- a/packages/core/src/ui/components/ActivityDetail/ActivityTypeIcon.tsx +++ b/packages/core/src/ui/components/ActivityDetail/ActivityTypeIcon.tsx @@ -71,12 +71,13 @@ const activityTypeIcon: Record { - const icon = type && activityTypeIcon[type]; + const icon = (type && activityTypeIcon[type]) || RefreshOutlinedIcon; const iconStyle = { fontSize: txIconSize() }; const isGovernanceTx = Object.values(ConwayEraCertificatesTypes).includes(type as unknown as ConwayEraCertificatesTypes) || - type in ConwayEraGovernanceActions; + type in ConwayEraGovernanceActions || + type in Cip1694GovernanceActivityType; return ( Date: Thu, 8 Feb 2024 16:41:35 +0000 Subject: [PATCH 47/68] fix: add retry fallback for matching last active tab based on non-standard URL --- .../src/lib/scripts/background/util.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/apps/browser-extension-wallet/src/lib/scripts/background/util.ts b/apps/browser-extension-wallet/src/lib/scripts/background/util.ts index 492adfd70d..a69c997580 100644 --- a/apps/browser-extension-wallet/src/lib/scripts/background/util.ts +++ b/apps/browser-extension-wallet/src/lib/scripts/background/util.ts @@ -84,10 +84,18 @@ export const initMigrationState = async (): Promise => { await webStorage.local.set(INITIAL_STORAGE); }; -export const getLastActiveTab: (url?: string) => Promise = async (url?: string) => - await ( - await tabs.query({ currentWindow: true, active: true, url }) - )[0]; +export const getLastActiveTab: (url?: string) => Promise = async (url?: string) => { + try { + return await ( + await tabs.query({ currentWindow: true, active: true, url }) + )[0]; + } catch { + // fallback if cannot match on URL + return await ( + await tabs.query({ active: true, lastFocusedWindow: true }) + )[0]; + } +}; /** * getDappInfoFromLastActiveTab From e728967e144e43e5f5cc85ccbec61ca7f8af88a5 Mon Sep 17 00:00:00 2001 From: Michael Chappell <7581002+mchappell@users.noreply.github.com> Date: Mon, 12 Feb 2024 16:32:29 +0000 Subject: [PATCH 48/68] chore: update dev preview env file --- apps/browser-extension-wallet/.env.developerpreview | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/browser-extension-wallet/.env.developerpreview b/apps/browser-extension-wallet/.env.developerpreview index fb051f006f..c5a7370f6f 100644 --- a/apps/browser-extension-wallet/.env.developerpreview +++ b/apps/browser-extension-wallet/.env.developerpreview @@ -62,12 +62,13 @@ POSTHOG_DEV_TOKEN_PREVIEW=phc_e8SaOOWpXpNE59TnpLumeUjWm4iv024AWjhQqU406jr CARDANO_SERVICES_URL_MAINNET=https://dev-mainnet.lw.iog.io CARDANO_SERVICES_URL_PREPROD=https://dev-preprod.lw.iog.io CARDANO_SERVICES_URL_PREVIEW=https://dev-preview.lw.iog.io +CARDANO_SERVICES_URL_SANCHONET=https://backend.dev-sanchonet.eks.lw.iog.io # Explorer URLs CEXPLORER_URL_MAINNET=https://cexplorer.io CEXPLORER_URL_PREVIEW=https://preview.cexplorer.io CEXPLORER_URL_PREPROD=https://preprod.cexplorer.io -CEXPLORER_URL_SANCHONET=https://sanchonet.cexplorer.io +CEXPLORER_URL_SANCHONET= # ADA Handle URLs ADA_HANDLE_URL_MAINNET=https://api.handle.me From de50216c728545b233755cf7f3f508d19585f1cd Mon Sep 17 00:00:00 2001 From: vetalcore Date: Mon, 26 Feb 2024 14:45:12 +0200 Subject: [PATCH 49/68] feat(extension): update conway era tx details to the latest figma (#868) * feat(extension): update conway era tx details to the latest figma * feat(extension): handle multiline row items with subitems * feat(extension): handle parameter_change_action * fix(extension): resolve pr comments * fix(extension): update to the latest figma/AC * fix(extension): resolve pr comments * fix(extension): resolve sanchonet pr comments * fix(extension): after merge with sanchonet fixes * fix(extension): after merge with sanchonet fixes * fix(extension): resolve sanchonet pr comments --- apps/browser-extension-wallet/src/config.ts | 5 +- .../features/dapp/components/DappError.tsx | 10 +- .../collateral/CreateCollateral.tsx | 5 +- .../ConfirmDRepRegistrationContainer.tsx | 8 +- .../ConfirmDRepRetirementContainer.tsx | 28 +- .../ConfirmDRepUpdateContainer.tsx | 7 +- ...rmStakeRegistrationDelegationContainer.tsx | 7 +- .../ConfirmStakeVoteDelegationContainer.tsx | 7 +- ...akeVoteRegistrationDelegationContainer.tsx | 7 +- .../ConfirmTransaction.tsx | 14 +- .../ConfirmTransactionContent.tsx | 68 +- .../ConfirmVoteDelegationContainer.tsx | 7 +- ...irmVoteRegistrationDelegationContainer.tsx | 7 +- .../DappTransactionContainer.tsx | 5 + .../ProposalProceduresContainer.tsx | 91 +- .../VotingProceduresContainer.tsx | 9 +- .../ConfirmDRepRegistrationContainer.test.tsx | 8 +- .../ConfirmDRepRetirementContainer.test.tsx | 13 +- .../ConfirmDRepUpdateContainer.test.tsx | 8 +- ...keRegistrationDelegationContainer.test.tsx | 8 +- ...nfirmStakeVoteDelegationContainer.test.tsx | 8 +- ...teRegistrationDelegationContainer.test.tsx | 8 +- .../ConfirmTransactionContent.test.tsx | 22 - .../ConfirmVoteDelegationContainer.test.tsx | 24 +- ...teRegistrationDelegationContainer.test.tsx | 8 +- .../ProposalProceduresContainer.test.tsx | 6 +- .../VotingProceduresContainer.test.tsx | 8 +- .../__tests__/utils.test.tsx | 17 - .../components/confirm-transaction/hooks.ts | 7 +- .../HardForkInitiationActionContainer.tsx | 7 +- .../InfoActionContainer.tsx | 2 +- .../NewConstitutionActionContainer.tsx | 7 +- .../NoConfidenceActionContainer.tsx | 7 +- .../ParameterChangeActionContainer.tsx | 8 +- .../TreasuryWithdrawalsActionContainer.tsx | 12 +- .../UpdateCommitteeActionContainer.tsx | 10 +- .../components/confirm-transaction/utils.ts | 28 +- .../src/lib/translations/en.json | 27 +- .../src/routes/DappConnectorView.tsx | 5 +- .../stores/slices/activity-detail-slice.ts | 9 +- .../src/utils/tx-inspection.ts | 16 + .../components/TransactionDetailsProxy.tsx | 16 +- .../activity/helpers/common-tx-transformer.ts | 632 +++++++++---- .../AssetDetailsDrawer/AssetDetails.tsx | 17 +- .../send-transaction/components/Form/util.ts | 5 +- .../Collateral/send/CollateralStepSend.tsx | 5 +- .../StakePoolConfirmation.tsx | 10 +- .../components/CatalystRegistrationFlow.tsx | 12 +- .../ActivityDetailHeader.module.scss | 2 +- .../TransactionDetails.module.scss | 8 +- .../TransactionDetails.stories.tsx | 892 ++++++++++++++++++ .../ActivityDetail/TransactionDetails.tsx | 245 ++++- .../ActivityDetail/TransactionFee.module.scss | 4 +- .../TransactionInputOutput.module.scss | 31 +- .../ActivityDetail/TxDetailsList.tsx | 39 +- .../components/DetailRow.module.scss | 12 + .../ActivityDetail/components/DetailRow.tsx | 16 +- .../components/DetailRowSubitems.tsx | 16 + .../ActivityDetail/components/DetailRows.tsx | 33 +- .../ActivityDetail/components/InfoItem.tsx | 19 + .../src/ui/components/ActivityDetail/types.ts | 106 ++- .../ParameterChangeAction/EconomicGroup.tsx | 9 +- .../ParameterChangeAction.tsx | 9 +- packages/core/src/ui/lib/translations/en.json | 280 +++++- .../Drawer/confirmation/AmountInfo.tsx | 8 +- 65 files changed, 2299 insertions(+), 695 deletions(-) create mode 100644 packages/core/src/ui/components/ActivityDetail/TransactionDetails.stories.tsx create mode 100644 packages/core/src/ui/components/ActivityDetail/components/DetailRowSubitems.tsx create mode 100644 packages/core/src/ui/components/ActivityDetail/components/InfoItem.tsx diff --git a/apps/browser-extension-wallet/src/config.ts b/apps/browser-extension-wallet/src/config.ts index 4180e7af3a..3e6ad690f9 100644 --- a/apps/browser-extension-wallet/src/config.ts +++ b/apps/browser-extension-wallet/src/config.ts @@ -22,7 +22,7 @@ export type Config = { ADA_PRICE_CHECK_INTERVAL: number; TOKEN_PRICE_CHECK_INTERVAL: number; AVAILABLE_CHAINS: Wallet.ChainName[]; - CEXPLORER_BASE_URL: Record, string>; + CEXPLORER_BASE_URL: Record; CEXPLORER_URL_PATHS: CExplorerUrlPaths; SAVED_PRICE_DURATION: number; }; @@ -84,7 +84,8 @@ export const config = (): Config => { CEXPLORER_BASE_URL: { Mainnet: `${process.env.CEXPLORER_URL_MAINNET}`, Preprod: `${process.env.CEXPLORER_URL_PREPROD}`, - Preview: `${process.env.CEXPLORER_URL_PREVIEW}` + Preview: `${process.env.CEXPLORER_URL_PREVIEW}`, + Sanchonet: `${process.env.CEXPLORER_URL_SANCHONET}` }, CEXPLORER_URL_PATHS: { Tx: 'tx', diff --git a/apps/browser-extension-wallet/src/features/dapp/components/DappError.tsx b/apps/browser-extension-wallet/src/features/dapp/components/DappError.tsx index 9c5c85e8d6..042c74a5ab 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/DappError.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/DappError.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode, useCallback, useEffect } from 'react'; +import React, { ReactNode, useCallback } from 'react'; import { Image } from 'antd'; import { useTranslation } from 'react-i18next'; import Empty from '../../../assets/icons/empty.svg'; @@ -10,7 +10,6 @@ type DappErrorProps = { description: ReactNode; closeButtonLabel?: string; onCloseClick?: () => void; - onMount?: () => void; containerTestId: string; imageTestId: string; titleTestId: string; @@ -22,7 +21,6 @@ export const DappError = ({ description, closeButtonLabel, onCloseClick, - onMount, containerTestId, imageTestId, titleTestId, @@ -32,14 +30,8 @@ export const DappError = ({ const { t } = useTranslation(); const handleClose = useCallback(() => { onCloseClick?.(); - window.close(); }, [onCloseClick]); - useEffect(() => { - onMount?.(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - return (
diff --git a/apps/browser-extension-wallet/src/features/dapp/components/collateral/CreateCollateral.tsx b/apps/browser-extension-wallet/src/features/dapp/components/collateral/CreateCollateral.tsx index 7fe3a76c7d..a979c6a80c 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/collateral/CreateCollateral.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/collateral/CreateCollateral.tsx @@ -129,7 +129,10 @@ export const CreateCollateral = ({ })}
{renderAmountInfo( - `${Wallet.util.lovelacesToAdaString(collateralTx.fee.toString())} ${cardanoCoin.symbol}`, + Wallet.util.getFormattedAmount({ + amount: collateralTx.fee.toString(), + cardanoCoin + }), `${Wallet.util.convertAdaToFiat({ ada: Wallet.util.lovelacesToAdaString(collateralTx.fee.toString()), fiat: priceResult?.cardano?.price || 0 diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmDRepRegistrationContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmDRepRegistrationContainer.tsx index cfd8e7241b..7cba19e559 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmDRepRegistrationContainer.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmDRepRegistrationContainer.tsx @@ -9,11 +9,7 @@ import { Skeleton } from 'antd'; const { CertificateType } = Wallet.Cardano; -interface Props { - errorMessage?: string; -} - -export const ConfirmDRepRegistrationContainer = ({ errorMessage }: Props): React.ReactElement => { +export const ConfirmDRepRegistrationContainer = (): React.ReactElement => { const { t } = useTranslation(); const { walletUI: { cardanoCoin } @@ -42,6 +38,7 @@ export const ConfirmDRepRegistrationContainer = ({ errorMessage }: Props): React const depositPaidWithCardanoSymbol = depositPaidWithSymbol(certificate.deposit, cardanoCoin); + // TODO: might be changed in scope of https://input-output.atlassian.net/browse/LW-9034 return ( ); }; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmDRepRetirementContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmDRepRetirementContainer.tsx index 5779328c15..05b835ba91 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmDRepRetirementContainer.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmDRepRetirementContainer.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { ConfirmDRepRetirement } from '@lace/core'; import { certificateInspectorFactory, depositPaidWithSymbol, disallowSignTx, drepIDasBech32FromHash } from './utils'; @@ -12,11 +12,10 @@ import { useViewsFlowContext } from '@providers'; const { CertificateType } = Wallet.Cardano; interface Props { - errorMessage?: string; onError: () => void; } -export const ConfirmDRepRetirementContainer = ({ onError, errorMessage }: Props): React.ReactElement => { +export const ConfirmDRepRetirementContainer = ({ onError }: Props): React.ReactElement => { const { t } = useTranslation(); const { walletUI: { cardanoCoin } @@ -41,23 +40,31 @@ export const ConfirmDRepRetirementContainer = ({ onError, errorMessage }: Props) getCertificateData(); }, [request]); + const depositPaidWithCardanoSymbol = depositPaidWithSymbol(certificate.deposit, cardanoCoin); + const isNotOwnDRepKey = certificate.dRepCredential.hash !== ownPubDRepKeyHash; + + useEffect(() => { + if (ownPubDRepKeyHash && isNotOwnDRepKey) { + disallowSignTx(request, true); + onError(); + } + }, [ownPubDRepKeyHash, isNotOwnDRepKey, onError, request]); + + const onCloseClick = useCallback(() => { + window.close(); + }, []); + if (!certificate || loadingOwnPubDRepKeyHash) { return ; } - const depositPaidWithCardanoSymbol = depositPaidWithSymbol(certificate.deposit, cardanoCoin); - const isNotOwnDRepKey = certificate.dRepCredential.hash !== ownPubDRepKeyHash; - if (isNotOwnDRepKey) { return ( { - disallowSignTx(request, false); - onError(); - }} containerTestId="drep-id-mismatch-container" + onCloseClick={onCloseClick} imageTestId="drep-id-mismatch-image" titleTestId="drep-id-mismatch-heading" descriptionTestId="drep-id-mismatch-description" @@ -80,7 +87,6 @@ export const ConfirmDRepRetirementContainer = ({ onError, errorMessage }: Props) drepId: t('core.DRepRetirement.drepId') } }} - errorMessage={errorMessage} /> ); }; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmDRepUpdateContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmDRepUpdateContainer.tsx index e82a6e03b1..691529f971 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmDRepUpdateContainer.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmDRepUpdateContainer.tsx @@ -8,11 +8,7 @@ import { Skeleton } from 'antd'; const { CertificateType } = Wallet.Cardano; -interface Props { - errorMessage?: string; -} - -export const ConfirmDRepUpdateContainer = ({ errorMessage }: Props): React.ReactElement => { +export const ConfirmDRepUpdateContainer = (): React.ReactElement => { const { t } = useTranslation(); const { signTxRequest: { request }, @@ -51,7 +47,6 @@ export const ConfirmDRepUpdateContainer = ({ errorMessage }: Props): React.React url: t('core.DRepUpdate.url') } }} - errorMessage={errorMessage} /> ); }; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeRegistrationDelegationContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeRegistrationDelegationContainer.tsx index 068bc8f585..0d991fbf1b 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeRegistrationDelegationContainer.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeRegistrationDelegationContainer.tsx @@ -9,11 +9,7 @@ import { Skeleton } from 'antd'; const { CertificateType, RewardAddress } = Wallet.Cardano; -interface Props { - errorMessage?: string; -} - -export const ConfirmStakeRegistrationDelegationContainer = ({ errorMessage }: Props): React.ReactElement => { +export const ConfirmStakeRegistrationDelegationContainer = (): React.ReactElement => { const { walletUI: { cardanoCoin }, currentChain @@ -58,7 +54,6 @@ export const ConfirmStakeRegistrationDelegationContainer = ({ errorMessage }: Pr depositPaid: t('core.StakeRegistrationDelegation.depositPaid') } }} - errorMessage={errorMessage} /> ); }; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeVoteDelegationContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeVoteDelegationContainer.tsx index 2f5438b0b1..df1b8eca72 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeVoteDelegationContainer.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeVoteDelegationContainer.tsx @@ -9,11 +9,7 @@ import { Skeleton } from 'antd'; const { CertificateType, RewardAddress } = Wallet.Cardano; -interface Props { - errorMessage?: string; -} - -export const ConfirmStakeVoteDelegationContainer = ({ errorMessage }: Props): React.ReactElement => { +export const ConfirmStakeVoteDelegationContainer = (): React.ReactElement => { const { t } = useTranslation(); const { currentChain } = useWalletStore(); const { @@ -62,7 +58,6 @@ export const ConfirmStakeVoteDelegationContainer = ({ errorMessage }: Props): Re alwaysNoConfidence: t('core.StakeVoteDelegation.alwaysNoConfidence') } }} - errorMessage={errorMessage} /> ); }; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeVoteRegistrationDelegationContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeVoteRegistrationDelegationContainer.tsx index 8049b55186..55fcd66395 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeVoteRegistrationDelegationContainer.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmStakeVoteRegistrationDelegationContainer.tsx @@ -9,11 +9,7 @@ import { Skeleton } from 'antd'; const { CertificateType, RewardAddress } = Wallet.Cardano; -interface Props { - errorMessage?: string; -} - -export const ConfirmStakeVoteRegistrationDelegationContainer = ({ errorMessage }: Props): React.ReactElement => { +export const ConfirmStakeVoteRegistrationDelegationContainer = (): React.ReactElement => { const { t } = useTranslation(); const { walletUI: { cardanoCoin }, @@ -69,7 +65,6 @@ export const ConfirmStakeVoteRegistrationDelegationContainer = ({ errorMessage } depositPaid: t('core.StakeVoteDelegationRegistration.depositPaid') } }} - errorMessage={errorMessage} /> ); }; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.tsx index 1507fc50e5..e38ee78ba6 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import cn from 'classnames'; import { Button, PostHogAction } from '@lace/common'; import { useTranslation } from 'react-i18next'; @@ -8,7 +8,7 @@ import styles from './ConfirmTransaction.module.scss'; import { Wallet } from '@lace/cardano'; import { useWalletStore } from '@stores'; import { useDisallowSignTx, useSignWithHardwareWallet, useOnBeforeUnload } from './hooks'; -import { getTitleKey, getTxType } from './utils'; +import { getTxType } from './utils'; import { ConfirmTransactionContent } from './ConfirmTransactionContent'; import { TX_CREATION_TYPE_KEY, TxCreationType } from '@providers/AnalyticsProvider/analyticsTracker'; import { txSubmitted$ } from '@providers/AnalyticsProvider/onChain'; @@ -20,6 +20,7 @@ import { UserPromptService } from '@lib/scripts/background/services'; import { DAPP_CHANNELS } from '@src/utils/constants'; import { of, take } from 'rxjs'; import { runtime } from 'webextension-polyfill'; +import { Skeleton } from 'antd'; export const ConfirmTransaction = (): React.ReactElement => { const { t } = useTranslation(); @@ -45,7 +46,6 @@ export const ConfirmTransaction = (): React.ReactElement => { fetchTxType(); }, [req]); - const title = txType ? t(getTitleKey(txType)) : ''; const onConfirm = () => { analytics.sendEventToPostHog(PostHogAction.SendTransactionSummaryConfirmClick, { [TX_CREATION_TYPE_KEY]: TxCreationType.External @@ -87,13 +87,17 @@ export const ConfirmTransaction = (): React.ReactElement => { useOnBeforeUnload(disallowSignTx); + const onError = useCallback(() => { + setConfirmTransactionError(true); + }, []); + return ( - {req && txType && setConfirmTransactionError(true)} />} + {req && txType ? : } {!confirmTransactionError && (
+ +
+
+ + ); +}; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/VotingProceduresContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/VotingProceduresContainer.tsx index d85526fb16..771506372b 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/VotingProceduresContainer.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/VotingProceduresContainer.tsx @@ -1,11 +1,13 @@ import React, { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { VotingProcedures } from '@lace/core'; -import { drepIDasBech32FromHash, votingProceduresInspector } from './utils'; -import { useCexplorerBaseUrl } from './hooks'; -import { VoterTypeEnum, getVote, getVoterType } from '@src/utils/tx-inspection'; +import { getDRepId, hasValidDrepRegistration, votingProceduresInspector } from './utils'; +import { useCexplorerBaseUrl, useDisallowSignTx } from './hooks'; +import { getVote, getVoterType } from '@src/utils/tx-inspection'; import { Wallet } from '@lace/cardano'; +import { NonRegisteredUserModal } from './NonRegisteredUserModal/NonRegisteredUserModal'; import { useViewsFlowContext } from '@providers'; +import { useWalletStore } from '@src/stores'; export const VotingProceduresContainer = (): React.ReactElement => { const { t } = useTranslation(); @@ -13,7 +15,11 @@ export const VotingProceduresContainer = (): React.ReactElement => { signTxRequest: { request }, dappInfo } = useViewsFlowContext(); + const { walletState } = useWalletStore(); const [votingProcedures, setVotingProcedures] = useState([]); + const [isNonRegisteredUserModalVisible, setIsNonRegisteredUserModalVisible] = useState(false); + const [userAckNonRegisteredState, setUserAckNonRegisteredState] = useState(false); + const disallowSignTx = useDisallowSignTx(request); useEffect(() => { const getVotingProcedures = async () => { @@ -24,54 +30,65 @@ export const VotingProceduresContainer = (): React.ReactElement => { getVotingProcedures(); }, [request]); + useEffect(() => { + if (!walletState?.transactions.history || userAckNonRegisteredState) return; + setIsNonRegisteredUserModalVisible(!hasValidDrepRegistration(walletState.transactions.history)); + }, [walletState?.transactions.history, userAckNonRegisteredState]); + const explorerBaseUrl = useCexplorerBaseUrl(); return ( - { - const voterType = getVoterType(votingProcedure.voter.__typename); + <> + { + setUserAckNonRegisteredState(true); + setIsNonRegisteredUserModalVisible(false); + }} + onClose={() => disallowSignTx(true)} + /> + { + const voterType = getVoterType(votingProcedure.voter.__typename); - const drepId = - voterType === VoterTypeEnum.DREP - ? drepIDasBech32FromHash(votingProcedure.voter.credential.hash) - : votingProcedure.voter.credential.hash.toString(); - return { - voter: { - type: t(`core.VotingProcedures.voterTypes.${voterType}`), - dRepId: drepId - }, - votes: votingProcedure.votes.map((vote) => ({ - actionId: { - index: vote.actionId.actionIndex, - txHash: vote.actionId.id.toString(), - txHashUrl: `${explorerBaseUrl}/${vote.actionId.id}` + return { + voter: { + type: t(`core.VotingProcedures.voterTypes.${voterType}`), + dRepId: getDRepId(votingProcedure.voter) }, - votingProcedure: { - vote: t(`core.VotingProcedures.votes.${getVote(vote.votingProcedure.vote)}`), - anchor: !!vote.votingProcedure.anchor?.url && { - url: vote.votingProcedure.anchor?.url, - hash: vote.votingProcedure.anchor?.dataHash.toString() + votes: votingProcedure.votes.map((vote) => ({ + actionId: { + index: vote.actionId.actionIndex, + txHash: vote.actionId.id.toString(), + txHashUrl: `${explorerBaseUrl}/${vote.actionId.id}` + }, + votingProcedure: { + vote: t(`core.VotingProcedures.votes.${getVote(vote.votingProcedure.vote)}`), + anchor: !!vote.votingProcedure.anchor && { + url: vote.votingProcedure.anchor.url, + hash: vote.votingProcedure.anchor.dataHash.toString() + } } - } - })) - }; - })} - translations={{ - voterType: t('core.VotingProcedures.voterType'), - procedureTitle: t('core.VotingProcedures.procedureTitle'), - actionIdTitle: t('core.VotingProcedures.actionIdTitle'), - vote: t('core.VotingProcedures.vote'), - actionId: { - index: t('core.VotingProcedures.actionId.index'), - txHash: t('core.VotingProcedures.actionId.txHash') - }, - anchor: { - hash: t('core.VotingProcedures.anchor.hash'), - url: t('core.VotingProcedures.anchor.url') - }, - dRepId: t('core.VotingProcedures.dRepId') - }} - /> + })) + }; + })} + translations={{ + voterType: t('core.VotingProcedures.voterType'), + procedureTitle: t('core.VotingProcedures.procedureTitle'), + actionIdTitle: t('core.VotingProcedures.actionIdTitle'), + vote: t('core.VotingProcedures.vote'), + actionId: { + index: t('core.VotingProcedures.actionId.index'), + txHash: t('core.VotingProcedures.actionId.txHash') + }, + anchor: { + hash: t('core.VotingProcedures.anchor.hash'), + url: t('core.VotingProcedures.anchor.url') + }, + dRepId: t('core.VotingProcedures.dRepId') + }} + /> + ); }; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/VotingProceduresContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/VotingProceduresContainer.test.tsx index e8f382ee1c..bfb5f96462 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/VotingProceduresContainer.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/VotingProceduresContainer.test.tsx @@ -7,10 +7,13 @@ const mockUseWalletStore = jest.fn(); const t = jest.fn().mockImplementation((res) => res); const mockUseTranslation = jest.fn(() => ({ t })); const mockVotingProcedures = jest.fn(); +const mockNonRegisteredUserModal = jest.fn(); +const mockUseDisallowSignTx = jest.fn(); +const mockHasValidDrepRegistration = jest.fn(); const mockPreprodCexplorerBaseUrl = 'PREPROD_CEXPLORER_BASE_URL'; const mockCexplorerUrlPathsTx = 'CEXPLORER_URL_PATHS.TX'; import * as React from 'react'; -import { cleanup, render } from '@testing-library/react'; +import { cleanup, render, waitFor } from '@testing-library/react'; import { VotingProceduresContainer } from '../VotingProceduresContainer'; import '@testing-library/jest-dom'; import { act } from 'react-dom/test-utils'; @@ -47,6 +50,15 @@ jest.mock('@lace/core', () => { }; }); +jest.mock('../NonRegisteredUserModal/NonRegisteredUserModal', () => { + const original = jest.requireActual('../NonRegisteredUserModal/NonRegisteredUserModal'); + return { + __esModule: true, + ...original, + NonRegisteredUserModal: mockNonRegisteredUserModal + }; +}); + jest.mock('react-i18next', () => { const original = jest.requireActual('react-i18next'); return { @@ -56,6 +68,20 @@ jest.mock('react-i18next', () => { }; }); +jest.mock('../utils', () => ({ + ...jest.requireActual('../utils'), + hasValidDrepRegistration: mockHasValidDrepRegistration +})); + +jest.mock('../hooks', () => { + const original = jest.requireActual('../hooks'); + return { + __esModule: true, + ...original, + useDisallowSignTx: mockUseDisallowSignTx + }; +}); + const dappInfo = { name: 'dappName', logo: 'dappLogo', @@ -153,19 +179,21 @@ jest.mock('@providers', () => ({ describe('Testing VotingProceduresContainer component', () => { beforeEach(() => { + mockHasValidDrepRegistration.mockReset(); + mockHasValidDrepRegistration.mockReturnValue(true); mockUseWalletStore.mockReset(); mockUseWalletStore.mockImplementation(() => ({ environmentName: 'Preprod' })); mockVotingProcedures.mockReset(); mockVotingProcedures.mockReturnValue(); + mockNonRegisteredUserModal.mockReset(); + mockNonRegisteredUserModal.mockReturnValue(); mockUseTranslation.mockReset(); mockUseTranslation.mockImplementation(() => ({ t })); }); afterEach(() => { - jest.resetModules(); - jest.resetAllMocks(); cleanup(); }); @@ -225,6 +253,71 @@ describe('Testing VotingProceduresContainer component', () => { ); }); + test('should handle NonRegisteredUserModal onConfirm', async () => { + mockHasValidDrepRegistration.mockReset(); + mockHasValidDrepRegistration.mockReturnValue(false); + mockUseWalletStore.mockReset(); + mockUseWalletStore.mockImplementation(() => ({ + environmentName: 'Preprod', + walletState: { + transactions: { + history: [] + } + } + })); + + await act(async () => { + render(, { + wrapper: getWrapper() + }); + }); + + expect(mockNonRegisteredUserModal.mock.calls[mockNonRegisteredUserModal.mock.calls.length - 1][0].visible).toEqual( + true + ); + + await act(async () => { + mockNonRegisteredUserModal.mock.calls[mockNonRegisteredUserModal.mock.calls.length - 1][0].onConfirm(); + }); + + await waitFor(async () => { + expect( + mockNonRegisteredUserModal.mock.calls[mockNonRegisteredUserModal.mock.calls.length - 1][0].visible + ).toEqual(false); + }); + }); + + test('should handle NonRegisteredUserModal onClose', async () => { + const disallowSignTxMock = jest.fn(); + mockUseDisallowSignTx.mockReset(); + mockUseDisallowSignTx.mockReturnValue(disallowSignTxMock); + mockHasValidDrepRegistration.mockReset(); + mockHasValidDrepRegistration.mockReturnValue(false); + mockUseWalletStore.mockReset(); + mockUseWalletStore.mockImplementation(() => ({ + environmentName: 'Preprod', + walletState: { + transactions: { + history: [] + } + } + })); + + await act(async () => { + render(, { + wrapper: getWrapper() + }); + }); + + expect(disallowSignTxMock).not.toHaveBeenCalled(); + + await act(async () => { + mockNonRegisteredUserModal.mock.calls[mockNonRegisteredUserModal.mock.calls.length - 1][0].onClose(); + }); + + expect(disallowSignTxMock).toHaveBeenCalledWith(true); + }); + test('testing getVoterType', () => { expect(getVoterType(constitutionalCommitteeKeyHashVoter.__typename)).toEqual( VoterTypeEnum.CONSTITUTIONAL_COMMITTEE diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/utils.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/utils.test.tsx index 919dad9d21..ca920d6f46 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/utils.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/utils.test.tsx @@ -17,7 +17,8 @@ import { getTxType, drepIDasBech32FromHash, pubDRepKeyToHash, - depositPaidWithSymbol + depositPaidWithSymbol, + hasValidDrepRegistration } from '../utils'; jest.mock('@cardano-sdk/core', () => ({ @@ -170,4 +171,59 @@ describe('Testing utils', () => { 'coinId Unknown not supported' ); }); + + describe('hasValidDrepRegistration', () => { + test('should return false if there transactions', () => { + const transactions = [] as unknown as Wallet.Cardano.HydratedTx[]; + expect(hasValidDrepRegistration(transactions)).toBe(false); + }); + + test('should return false if there is no certificates', () => { + const transactions = [{ body: {} }, { body: { certificates: [] } }] as unknown as Wallet.Cardano.HydratedTx[]; + expect(hasValidDrepRegistration(transactions)).toBe(false); + }); + + test('should return true if first certificate has RegisterDelegateRepresentative __typename', () => { + const transactions = [ + { + body: { + certificates: [ + { __typename: Wallet.Cardano.CertificateType.RegisterDelegateRepresentative }, + { __typename: Wallet.Cardano.CertificateType.UnregisterDelegateRepresentative } + ] + } + }, + { + body: { + certificates: [ + { __typename: Wallet.Cardano.CertificateType.UnregisterDelegateRepresentative }, + { __typename: Wallet.Cardano.CertificateType.RegisterDelegateRepresentative } + ] + } + } + ] as unknown as Wallet.Cardano.HydratedTx[]; + expect(hasValidDrepRegistration(transactions)).toBe(true); + }); + test('should return false if first certificate has UnregisterDelegateRepresentative __typename', () => { + const transactions = [ + { + body: { + certificates: [ + { __typename: Wallet.Cardano.CertificateType.UnregisterDelegateRepresentative }, + { __typename: Wallet.Cardano.CertificateType.RegisterDelegateRepresentative } + ] + } + }, + { + body: { + certificates: [ + { __typename: Wallet.Cardano.CertificateType.RegisterDelegateRepresentative }, + { __typename: Wallet.Cardano.CertificateType.UnregisterDelegateRepresentative } + ] + } + } + ] as unknown as Wallet.Cardano.HydratedTx[]; + expect(hasValidDrepRegistration(transactions)).toBe(false); + }); + }); }); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/hooks.ts b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/hooks.ts index daa89a645a..78cca9fbd4 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/hooks.ts +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/hooks.ts @@ -161,7 +161,7 @@ export const useCreateMintedAssetList = ({ export const useDisallowSignTx = ( req: TransactionWitnessRequest -): ((close?: boolean) => void) => useCallback(() => disallowSignTx(req), [req]); +): ((close?: boolean) => void) => useCallback((close) => disallowSignTx(req, close), [req]); export const useAllowSignTx = ( req: TransactionWitnessRequest diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/utils.ts b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/utils.ts index 5345ff4549..aa50a42c26 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/utils.ts +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/utils.ts @@ -7,6 +7,7 @@ import type { UserPromptService } from '@lib/scripts/background/services'; import { DAPP_CHANNELS, cardanoCoin } from '@src/utils/constants'; import { runtime } from 'webextension-polyfill'; import { of } from 'rxjs'; +import { VoterTypeEnum, getVoterType } from '@src/utils/tx-inspection'; const { CertificateType } = Wallet.Cardano; @@ -166,3 +167,23 @@ export const depositPaidWithSymbol = (deposit: bigint, coinId: Wallet.CoinId): s throw new Error(`coinId ${coinId.name} not supported`); } }; + +export const hasValidDrepRegistration = (history: Wallet.Cardano.HydratedTx[]): boolean => { + for (const transaction of history) { + const drepRegistrationOrRetirementCerticicate = transaction.body.certificates?.find((cert) => + [CertificateType.UnregisterDelegateRepresentative, CertificateType.RegisterDelegateRepresentative].includes( + cert.__typename + ) + ); + + if (drepRegistrationOrRetirementCerticicate) { + return drepRegistrationOrRetirementCerticicate.__typename === CertificateType.RegisterDelegateRepresentative; + } + } + return false; +}; + +export const getDRepId = (voter: Wallet.Cardano.Voter): Wallet.Cardano.DRepID | string => + getVoterType(voter.__typename) === VoterTypeEnum.DREP + ? drepIDasBech32FromHash(voter.credential.hash) + : voter.credential.hash.toString(); diff --git a/apps/browser-extension-wallet/src/lib/translations/en.json b/apps/browser-extension-wallet/src/lib/translations/en.json index c3c1675866..a250ce6ed7 100644 --- a/apps/browser-extension-wallet/src/lib/translations/en.json +++ b/apps/browser-extension-wallet/src/lib/translations/en.json @@ -1430,6 +1430,14 @@ "yes": "Yes", "no": "No", "abstain": "Abstain" + }, + "NonRegisteredUserModal": { + "title": "You're not a registered DRep", + "description": "Proceeding with this vote won't have any impact. Do you still want to proceed?", + "cta": { + "ok": "Proceed anyway", + "cancel": "Cancel" + } } }, "DRepRegistration": { From 83af2e56d0a7935d82271ed62265dd39e1500201 Mon Sep 17 00:00:00 2001 From: wklos-iohk Date: Mon, 4 Mar 2024 16:30:56 +0100 Subject: [PATCH 63/68] fix(extension): fix labels --- .../browser-extension-wallet/src/lib/translations/en.json | 8 ++++---- .../ActivityDetail/TransactionDetails.stories.tsx | 4 ++-- packages/core/src/ui/lib/translations/en.json | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/browser-extension-wallet/src/lib/translations/en.json b/apps/browser-extension-wallet/src/lib/translations/en.json index a250ce6ed7..ac34de5a96 100644 --- a/apps/browser-extension-wallet/src/lib/translations/en.json +++ b/apps/browser-extension-wallet/src/lib/translations/en.json @@ -1276,7 +1276,7 @@ "memory": "Memory", "step": "Step", "networkGroup": { - "title": "Network group", + "title": "Network Group", "maxBBSize": "Max BB Size", "maxTxSize": "Max Tx Size", "maxBHSize": "Max BH Size", @@ -1295,7 +1295,7 @@ } }, "economicGroup": { - "title": "Economic group", + "title": "Economic Group", "minFeeA": "Min Fee A", "minFeeB": "Min Fee B", "keyDeposit": "Key Deposit", @@ -1318,7 +1318,7 @@ } }, "technicalGroup": { - "title": "Technical group", + "title": "Technical Group", "a0": "A0", "eMax": "EMax", "nOpt": "NOpt", @@ -1335,7 +1335,7 @@ } }, "governanceGroup": { - "title": "Governance group", + "title": "Governance Group", "govActionLifetime": "Gov Act Lifetime", "govActionDeposit": "Gov Act Deposit", "drepDeposit": "DRep Deposit", diff --git a/packages/core/src/ui/components/ActivityDetail/TransactionDetails.stories.tsx b/packages/core/src/ui/components/ActivityDetail/TransactionDetails.stories.tsx index 00a9b77075..414ddf07b8 100644 --- a/packages/core/src/ui/components/ActivityDetail/TransactionDetails.stories.tsx +++ b/packages/core/src/ui/components/ActivityDetail/TransactionDetails.stories.tsx @@ -129,7 +129,7 @@ const voteRegistrationDelegationCertificate = [ details: ['stake1u929x2y7nnfm797upl7v9rc39pqg87pk5cygvnn2edqmvuq6h48su'] }, { - title: 'drep', + title: 'drepId', details: ['drep1cs234l5mtapethapx8cq97nkpa27xf84phruh5f6jqxa78ymlp4'] }, { @@ -161,7 +161,7 @@ const stakeVoteRegistrationDelegationCertificate = [ details: ['pool1k0ucs0fau2vhr3p7qh7mnpfgrllwwda7petxjz2gzzaxkyp8f88'] }, { - title: 'drep', + title: 'drepId', details: ['drep1cs234l5mtapethapx8cq97nkpa27xf84phruh5f6jqxa78ymlp4'] }, { diff --git a/packages/core/src/ui/lib/translations/en.json b/packages/core/src/ui/lib/translations/en.json index 92014f956c..5a8c48b605 100644 --- a/packages/core/src/ui/lib/translations/en.json +++ b/packages/core/src/ui/lib/translations/en.json @@ -259,7 +259,7 @@ "memory": "Memory", "step": "Step", "networkGroup": { - "title": "Network group", + "title": "Network Group", "maxBBSize": "Max BB Size", "maxTxSize": "Max Tx Size", "maxBHSize": "Max BH Size", @@ -278,7 +278,7 @@ } }, "economicGroup": { - "title": "Economic group", + "title": "Economic Group", "minFeeA": "Min Fee A", "minFeeB": "Min Fee B", "keyDeposit": "Key Deposit", @@ -301,7 +301,7 @@ } }, "technicalGroup": { - "title": "Technical group", + "title": "Technical Group", "a0": "A0", "eMax": "EMax", "nOpt": "NOpt", @@ -318,7 +318,7 @@ } }, "governanceGroup": { - "title": "Governance group", + "title": "Governance Group", "govActionLifetime": "Gov Act Lifetime", "govActionDeposit": "Gov Act Deposit", "drepDeposit": "DRep Deposit", From 2353b30610107421d1ef39fd5a4833364a2c96b5 Mon Sep 17 00:00:00 2001 From: Michael Chappell <7581002+mchappell@users.noreply.github.com> Date: Tue, 5 Mar 2024 08:05:08 +0000 Subject: [PATCH 64/68] refactor: rename proprosal procedure call --- .../confirm-transaction/ProposalProceduresContainer.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ProposalProceduresContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ProposalProceduresContainer.tsx index 59adef766d..b72eb6d919 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ProposalProceduresContainer.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ProposalProceduresContainer.tsx @@ -19,12 +19,12 @@ export const ProposalProceduresContainer = (): React.ReactElement => { } = useViewsFlowContext(); useEffect(() => { - const getCertificateData = async () => { + const proposalProcedureData = async () => { const procedures = await proposalProceduresInspector(request.transaction.toCore()); setProposalProcedures(procedures); }; - getCertificateData(); + proposalProcedureData(); }, [request]); const containerPerTypeMap: Record< From 895e22145e51e7954f8362c8af1804bdd49613aa Mon Sep 17 00:00:00 2001 From: Michael Chappell <7581002+mchappell@users.noreply.github.com> Date: Tue, 5 Mar 2024 09:00:54 +0000 Subject: [PATCH 65/68] fix: broken hook test --- .../__tests__/hooks.test.tsx | 22 +++++++++++++------ .../components/confirm-transaction/hooks.ts | 6 ++--- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/hooks.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/hooks.test.tsx index 3f90772d7e..3e45399c1e 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/hooks.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/hooks.test.tsx @@ -31,7 +31,7 @@ import { TokenInfo } from '@src/utils/get-assets-information'; import { AddressListType } from '@src/views/browser-view/features/activity'; import { WalletInfo } from '@src/types'; import * as Core from '@cardano-sdk/core'; -import { TransactionWitnessRequest } from '@cardano-sdk/web-extension'; +import { TransactionWitnessRequest, WalletType } from '@cardano-sdk/web-extension'; import { mockWalletState } from '@src/utils/mocks/test-helpers'; jest.mock('@stores', () => ({ @@ -246,11 +246,20 @@ describe('Testing hooks', () => { const useRedirectionSpy = jest.spyOn(hooks, 'useRedirection').mockImplementation(() => redirectToSignFailure); mockEstablishDeviceConnection.mockReset(); mockEstablishDeviceConnection.mockImplementation(async () => await true); - const hook = renderHook(() => useSignWithHardwareWallet({} as any)); - + const mockSign = jest.fn().mockImplementation(async () => mockEstablishDeviceConnection()); + const hook = renderHook(() => + useSignWithHardwareWallet({ + sign: mockSign as any, + requestContext: {} as any, + reject: jest.fn(), + signContext: {} as any, + transaction: {} as any, + walletType: WalletType.Ledger + } as TransactionWitnessRequest) + ); await hook.waitFor(() => { expect(hook.result.current.isConfirmingTx).toBeFalsy; - expect(useRedirectionSpy).toHaveBeenLastCalledWith(dAppRoutePaths.dappTxSignFailure); + expect(useRedirectionSpy).toHaveBeenLastCalledWith(dAppRoutePaths.dappTxSignSuccess); }); await act(async () => { @@ -259,9 +268,8 @@ describe('Testing hooks', () => { await hook.waitFor(() => { expect(hook.result.current.isConfirmingTx).toBe(true); - expect(mockAllowSignTx).toHaveBeenCalledTimes(1); + expect(mockSign).toHaveBeenCalledTimes(1); expect(mockEstablishDeviceConnection).toHaveBeenCalledTimes(1); - expect(mockEstablishDeviceConnection).toHaveBeenLastCalledWith(Wallet.KeyManagement.CommunicationType.Web); expect(mockDisallowSignTx).not.toHaveBeenCalled(); }); @@ -277,7 +285,7 @@ describe('Testing hooks', () => { await hook.result.current.signWithHardwareWallet(); } catch { expect(hook.result.current.isConfirmingTx).toBe(true); - expect(mockAllowSignTx).toHaveBeenCalledTimes(1); + expect(mockSign).toHaveBeenCalledTimes(1); expect(mockDisallowSignTx).toHaveBeenCalledTimes(1); expect(mockDisallowSignTx).toHaveBeenLastCalledWith(false); expect(redirectToSignFailure).toHaveBeenCalledTimes(1); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/hooks.ts b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/hooks.ts index 78cca9fbd4..74a4652c3a 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/hooks.ts +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/hooks.ts @@ -173,11 +173,10 @@ export const useSignWithHardwareWallet = ( signWithHardwareWallet: () => Promise; isConfirmingTx: boolean; } => { - const allow = useAllowSignTx(req); const disallow = useDisallowSignTx(req); const redirectToSignFailure = useRedirection(dAppRoutePaths.dappTxSignFailure); const redirectToSignSuccess = useRedirection(dAppRoutePaths.dappTxSignSuccess); - const [isConfirmingTx, setIsConfirmingTx] = useState(); + const [isConfirmingTx, setIsConfirmingTx] = useState(false); const signWithHardwareWallet = useCallback(async () => { setIsConfirmingTx(true); try { @@ -191,8 +190,7 @@ export const useSignWithHardwareWallet = ( disallow(false); redirectToSignFailure(); } - }, [allow, disallow, redirectToSignFailure]); - + }, [disallow, redirectToSignFailure]); return { isConfirmingTx, signWithHardwareWallet }; }; From e9d9270fb325ecb6758496a2de2d584a2ed71faa Mon Sep 17 00:00:00 2001 From: Michael Chappell <7581002+mchappell@users.noreply.github.com> Date: Tue, 5 Mar 2024 13:33:45 +0000 Subject: [PATCH 66/68] feat: revert useTxSummary hook --- .../DappTransactionContainer.tsx | 216 +++++++++++++++--- .../DappTransactionContainer.test.tsx | 13 -- .../__tests__/hooks.test.tsx | 126 +--------- .../components/confirm-transaction/hooks.ts | 92 +------- 4 files changed, 190 insertions(+), 257 deletions(-) diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/DappTransactionContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/DappTransactionContainer.tsx index 70420b974f..a6fd224198 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/DappTransactionContainer.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/DappTransactionContainer.tsx @@ -1,20 +1,68 @@ -import React from 'react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useObservable } from '@lace/common'; import { useWalletStore } from '@stores'; -import { useCreateAssetList, useCreateMintedAssetList, useTxSummary } from './hooks'; import { Skeleton } from 'antd'; import { DappTransaction } from '@lace/core'; -import { TokenInfo } from '@src/utils/get-assets-information'; +import { TokenInfo, getAssetsInformation } from '@src/utils/get-assets-information'; import { useAddressBookContext, withAddressBookContext } from '@src/features/address-book/context'; import { AddressListType } from '@src/views/browser-view/features/activity'; import { useCurrencyStore } from '@providers/currency'; import { useFetchCoinPrice } from '@hooks'; import { useViewsFlowContext } from '@providers'; +import { Wallet } from '@lace/cardano'; +import { + AssetsMintedInspection, + createTxInspector, + assetsMintedInspector, + assetsBurnedInspector, + MintedAsset +} from '@cardano-sdk/core'; +import { useComputeTxCollateral } from '@hooks/useComputeTxCollateral'; interface Props { errorMessage?: string; } +const convertMetadataArrayToObj = (arr: unknown[]): Record => { + const result: Record = {}; + for (const item of arr) { + if (typeof item === 'object' && !Array.isArray(item) && item !== null) { + Object.assign(result, item); + } + } + return result; +}; + +// eslint-disable-next-line complexity, sonarjs/cognitive-complexity +const getAssetNameFromMintMetadata = (asset: MintedAsset, metadata: Wallet.Cardano.TxMetadata): string | undefined => { + if (!asset || !metadata) return; + const decodedAssetName = Buffer.from(asset.assetName, 'hex').toString(); + + // Tries to find the asset name in the tx metadata under label 721 or 20 + for (const [key, value] of metadata.entries()) { + // eslint-disable-next-line no-magic-numbers + if (key !== BigInt(721) && key !== BigInt(20)) return; + const cip25Metadata = Wallet.cardanoMetadatumToObj(value); + if (!Array.isArray(cip25Metadata)) return; + + // cip25Metadata should be an array containing all policies for the minted assets in the tx + const policyLevelMetadata = convertMetadataArrayToObj(cip25Metadata)[asset.policyId]; + if (!Array.isArray(policyLevelMetadata)) return; + + // policyLevelMetadata should be an array of objects with the minted assets names as key + // e.g. "policyId" = [{ "AssetName1": { ...metadataAsset1 } }, { "AssetName2": { ...metadataAsset2 } }]; + const assetProperties = convertMetadataArrayToObj(policyLevelMetadata)?.[decodedAssetName]; + if (!Array.isArray(assetProperties)) return; + + // assetProperties[decodedAssetName] should be an array of objects with the properties as keys + // e.g. [{ "name": "Asset Name" }, { "description": "An asset" }, ...] + const assetMetadataName = convertMetadataArrayToObj(assetProperties)?.name; + // eslint-disable-next-line consistent-return + return typeof assetMetadataName === 'string' ? assetMetadataName : undefined; + } +}; + +// eslint-disable-next-line complexity, sonarjs/cognitive-complexity export const DappTransactionContainer = withAddressBookContext(({ errorMessage }: Props): React.ReactElement => { const { walletInfo, @@ -30,39 +78,141 @@ export const DappTransactionContainer = withAddressBookContext(({ errorMessage } const currencyStore = useCurrencyStore(); const coinPrice = useFetchCoinPrice(); const { list: addressList } = useAddressBookContext() as { list: AddressListType[] }; + const tx = useMemo(() => request?.transaction.toCore(), [request?.transaction]); const assets = useObservable(inMemoryWallet.assetInfo$); + const [assetsInfo, setAssetsInfo] = useState(); - const tx = request?.transaction.toCore(); - if (!tx) { - return ; - } - const createAssetList = useCreateAssetList({ - outputs: tx.body.outputs, - assets, - assetProvider - }); - const createMintedAssetList = useCreateMintedAssetList({ - metadata: tx.auxiliaryData?.blob, - outputs: tx.body.outputs, - mint: tx.body.mint, - assets, - assetProvider - }); - const txSummary = useTxSummary({ - addressList, - createAssetList, - createMintedAssetList, - req: request, - walletInfo, - walletState - }); + const txCollateral = useComputeTxCollateral(walletState, tx); - // TODO: merge with the upper skeleton check - if (!txSummary) { - return ; - } + const assetIds = useMemo(() => { + if (!tx) return []; + const uniqueAssetIds = new Set(); + // Merge all assets (TokenMaps) from the tx outputs and mint + const assetMaps = tx.body?.outputs?.map((output) => output.value.assets) ?? []; + if (tx.body?.mint?.size > 0) assetMaps.push(tx.body.mint); + + // Extract all unique asset ids from the array of TokenMaps + for (const asset of assetMaps) { + if (asset) { + for (const id of asset.keys()) { + !uniqueAssetIds.has(id) && uniqueAssetIds.add(id); + } + } + } + return [...uniqueAssetIds.values()]; + }, [tx]); + + useEffect(() => { + if (assetIds?.length > 0) { + getAssetsInformation(assetIds, assets, { + assetProvider, + extraData: { nftMetadata: true, tokenMetadata: true } + }) + .then((result) => setAssetsInfo(result)) + .catch((error) => { + console.error(error); + }); + } + }, [assetIds, assetProvider, assets]); + + const createMintedList = useCallback( + (mintedAssets: AssetsMintedInspection) => { + if (!assetsInfo) return []; + return mintedAssets.map((asset) => { + const assetId = Wallet.Cardano.AssetId.fromParts(asset.policyId, asset.assetName); + const assetInfo = assets.get(assetId) || assetsInfo?.get(assetId); + // If it's a new asset or the name is being updated we should be getting it from the tx metadata + const metadataName = getAssetNameFromMintMetadata(asset, tx?.auxiliaryData?.blob); + return { + name: assetInfo?.name.toString() || asset.fingerprint || assetId, + ticker: + metadataName ?? + assetInfo?.nftMetadata?.name ?? + assetInfo?.tokenMetadata?.ticker ?? + assetInfo?.tokenMetadata?.name ?? + asset.fingerprint.toString(), + amount: Wallet.util.calculateAssetBalance(asset.quantity, assetInfo) + }; + }); + }, + [assets, assetsInfo, tx] + ); + + const createAssetList = useCallback( + (txAssets: Wallet.Cardano.TokenMap) => { + if (!assetsInfo) return []; + const assetList: Wallet.Cip30SignTxAssetItem[] = []; + txAssets.forEach(async (value, key) => { + const walletAsset = assets.get(key) || assetsInfo?.get(key); + assetList.push({ + name: walletAsset?.name.toString() || key.toString(), + ticker: walletAsset?.tokenMetadata?.ticker || walletAsset?.nftMetadata?.name, + amount: Wallet.util.calculateAssetBalance(value, walletAsset) + }); + }); + return assetList; + }, + [assets, assetsInfo] + ); + + const addressToNameMap = useMemo( + () => new Map(addressList?.map((item: AddressListType) => [item.address, item.name])), + [addressList] + ); + + const [txSummary, setTxSummary] = useState(); + + useEffect(() => { + if (!tx) { + setTxSummary(void 0); + return; + } + const getTxSummary = async () => { + const inspector = createTxInspector({ + minted: assetsMintedInspector, + burned: assetsBurnedInspector + }); + + const { minted, burned } = await inspector(tx as Wallet.Cardano.HydratedTx); + const isMintTransaction = minted.length > 0 || burned.length > 0; + + const txType = isMintTransaction ? Wallet.Cip30TxType.Mint : Wallet.Cip30TxType.Send; + + const externalOutputs = tx.body.outputs.filter((output) => { + if (txType === 'Send') { + return walletInfo.addresses.every((addr) => output.address !== addr.address); + } + return true; + }); + + const txSummaryOutputs: Wallet.Cip30SignTxSummary['outputs'] = externalOutputs.reduce((acc, txOut) => { + // Don't show withdrawl tx's etc + if (txOut.address.toString() === walletInfo.addresses[0].address.toString()) return acc; + + return [ + ...acc, + { + coins: Wallet.util.lovelacesToAdaString(txOut.value.coins.toString()), + recipient: addressToNameMap?.get(txOut.address.toString()) || txOut.address.toString(), + ...(txOut.value.assets?.size > 0 && { assets: createAssetList(txOut.value.assets) }) + } + ]; + }, []); + + // eslint-disable-next-line consistent-return + setTxSummary({ + fee: Wallet.util.lovelacesToAdaString(tx.body.fee.toString()), + outputs: txSummaryOutputs, + type: txType, + mintedAssets: createMintedList(minted), + burnedAssets: createMintedList(burned), + collateral: txCollateral ? Wallet.util.lovelacesToAdaString(txCollateral.toString()) : undefined + }); + }; + getTxSummary(); + }, [tx, walletInfo.addresses, createAssetList, createMintedList, addressToNameMap, setTxSummary, txCollateral]); - return ( + return tx && txSummary ? ( + ) : ( + ); }); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/DappTransactionContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/DappTransactionContainer.test.tsx index db88d5efa9..1c8c8eaa46 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/DappTransactionContainer.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/DappTransactionContainer.test.tsx @@ -10,7 +10,6 @@ const mockUseWalletStore = jest.fn(); const t = jest.fn().mockImplementation((res) => res); const mockUseTranslation = jest.fn(() => ({ t })); const mockDappTransaction = jest.fn(); -const mockUseTxSummary = jest.fn(); const mockUseCreateAssetList = jest.fn(); const mockUseCreateMintedAssetList = jest.fn(); const mockUseViewsFlowContext = jest.fn(); @@ -82,7 +81,6 @@ jest.mock('../hooks.ts', () => { return { __esModule: true, ...original, - useTxSummary: mockUseTxSummary, useCreateAssetList: mockUseCreateAssetList, useCreateMintedAssetList: mockUseCreateMintedAssetList }; @@ -175,8 +173,6 @@ describe('Testing DappTransactionContainer component', () => { const props = { errorMessage }; const txSummary = 'txSummary'; - mockUseTxSummary.mockReset(); - mockUseTxSummary.mockReturnValue(txSummary); const createAssetList = 'createAssetList'; mockUseCreateAssetList.mockReset(); mockUseCreateAssetList.mockReturnValue(createAssetList); @@ -203,13 +199,6 @@ describe('Testing DappTransactionContainer component', () => { metadata: tx.auxiliaryData.blob, mint: tx.body.mint }); - expect(mockUseTxSummary).toHaveBeenLastCalledWith({ - addressList, - createAssetList, - createMintedAssetList, - req: request, - walletInfo - }); expect(mockDappTransaction).toHaveBeenLastCalledWith( { dappInfo, @@ -246,8 +235,6 @@ describe('Testing DappTransactionContainer component', () => { test('should render loader in case there is no txSummary', async () => { let queryByTestId: any; - mockUseTxSummary.mockReset(); - mockUseTxSummary.mockReturnValue(null); mockUseCurrencyStore.mockRestore(); const signTxData = { tx: { body: {} } } as unknown as SignTxData; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/hooks.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/hooks.test.tsx index 3e45399c1e..56f0beb427 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/hooks.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/hooks.test.tsx @@ -4,8 +4,6 @@ /* eslint-disable import/imports-first */ /* eslint-disable sonarjs/no-identical-functions */ -import { AssetsMintedInspection } from '@cardano-sdk/core'; - const mockPubDRepKeyToHash = jest.fn(); const mockDisallowSignTx = jest.fn(); const mockAllowSignTx = jest.fn(); @@ -15,24 +13,15 @@ const mockGetAssetsInformation = jest.fn(); const mockCalculateAssetBalance = jest.fn(); const mockLovelacesToAdaString = jest.fn(); const mockUseWalletStore = jest.fn(); -import { act, cleanup, waitFor } from '@testing-library/react'; -import { - useCreateAssetList, - useGetOwnPubDRepKeyHash, - useOnBeforeUnload, - useTxSummary, - useSignWithHardwareWallet -} from '../hooks'; +import { act, cleanup } from '@testing-library/react'; +import { useCreateAssetList, useGetOwnPubDRepKeyHash, useOnBeforeUnload, useSignWithHardwareWallet } from '../hooks'; import { renderHook } from '@testing-library/react-hooks'; import { Wallet } from '@lace/cardano'; import * as hooks from '@hooks'; import { dAppRoutePaths } from '@routes/wallet-paths'; import { TokenInfo } from '@src/utils/get-assets-information'; -import { AddressListType } from '@src/views/browser-view/features/activity'; -import { WalletInfo } from '@src/types'; import * as Core from '@cardano-sdk/core'; import { TransactionWitnessRequest, WalletType } from '@cardano-sdk/web-extension'; -import { mockWalletState } from '@src/utils/mocks/test-helpers'; jest.mock('@stores', () => ({ ...jest.requireActual('@stores'), @@ -293,117 +282,6 @@ describe('Testing hooks', () => { }); }); - test('useTxSummary', async () => { - mockLovelacesToAdaString.mockReset(); - mockLovelacesToAdaString.mockImplementation((val) => val); - - const createTxInspectorSpy = jest - .spyOn(Core, 'createTxInspector') - .mockReturnValue(async () => await ({ minted: [], burned: [], votingProcedures: true } as any)); - - const tx = { - body: { - fee: 'txFee', - outputs: [ - { - value: { coins: 'output_1_coins', assets: { size: 1 } }, - address: 'address1' - }, - { - value: { coins: 'output_2_coins', assets: { size: 1 } }, - address: 'address2' - }, - { - value: { coins: 'output_3_coins', assets: { size: 0 } }, - address: 'address3' - } - ] - } - } as unknown as Wallet.Cardano.Tx; - const addressList = [{ address: 'address1', name: 'address1_name' }] as AddressListType[]; - const walletInfo = { - name: 'walletName', - addresses: [{ address: 'address2' }, { address: 'address1' }] - } as WalletInfo; - const createAssetList = (txAssets: Wallet.Cardano.TokenMap) => txAssets as unknown as Wallet.Cip30SignTxAssetItem[]; - const createMintedAssetList = (txAssets: AssetsMintedInspection) => - txAssets as unknown as Wallet.Cip30SignTxAssetItem[]; - createAssetList({} as any); - createMintedAssetList({} as any); - let hook: any; - - const req = { - transaction: { - toCore: () => tx - } - } as unknown as TransactionWitnessRequest; - - await act(async () => { - hook = renderHook(() => - useTxSummary({ - req, - addressList, - walletInfo, - createAssetList, - createMintedAssetList, - walletState: mockWalletState - }) - ); - }); - - await waitFor(async () => { - expect(hook.result.current).toEqual({ - fee: tx.body.fee.toString(), - burnedAssets: [], - mintedAssets: [], - outputs: [ - { - coins: tx.body.outputs[0].value.coins, - recipient: addressList[0].name, - assets: tx.body.outputs[0].value.assets - }, - { - coins: tx.body.outputs[2].value.coins, - recipient: tx.body.outputs[2].address - } - ], - type: Wallet.Cip30TxType.VotingProcedures - }); - }); - - hook.unmount(); - - await act(async () => { - createTxInspectorSpy.mockReturnValue(() => ({ minted: [], burned: [] } as any)); - - hook = renderHook(() => - useTxSummary({ - req, - addressList, - walletInfo, - createAssetList, - createMintedAssetList, - walletState: mockWalletState - }) - ); - }); - - await act(async () => { - expect(hook.result.current).toEqual({ - fee: tx.body.fee.toString(), - burnedAssets: [], - mintedAssets: [], - outputs: [ - { - coins: tx.body.outputs[2].value.coins, - recipient: tx.body.outputs[2].address - } - ], - type: Wallet.Cip30TxType.Send - }); - }); - }); - test('useOnBeforeUnload', async () => { patchAddEventListener(); const cb = jest.fn(); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/hooks.ts b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/hooks.ts index 74a4652c3a..99785804cc 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/hooks.ts +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/hooks.ts @@ -1,27 +1,17 @@ /* eslint-disable no-console */ import isPlainObject from 'lodash/isPlainObject'; import { useCallback, useEffect, useMemo, useState } from 'react'; -import { - AssetProvider, - assetsBurnedInspector, - AssetsMintedInspection, - assetsMintedInspector, - createTxInspector, - MintedAsset -} from '@cardano-sdk/core'; +import { AssetProvider, AssetsMintedInspection, MintedAsset } from '@cardano-sdk/core'; import { dAppRoutePaths } from '@routes'; import { Wallet } from '@lace/cardano'; import { useRedirection } from '@hooks'; -import { CardanoTxOut, WalletInfo } from '@src/types'; +import { CardanoTxOut } from '@src/types'; import { config } from '@src/config'; import { TokenInfo, getAssetsInformation } from '@src/utils/get-assets-information'; import { getTransactionAssetsId } from '@src/stores/slices'; -import { AddressListType } from '@src/views/browser-view/features/activity'; -import { allowSignTx, pubDRepKeyToHash, disallowSignTx, getTxType } from './utils'; +import { allowSignTx, pubDRepKeyToHash, disallowSignTx } from './utils'; import { useWalletStore } from '@stores'; import { TransactionWitnessRequest, WalletType } from '@cardano-sdk/web-extension'; -import { useComputeTxCollateral } from '@hooks/useComputeTxCollateral'; -import { ObservableWalletState } from '@hooks/useWalletState'; export const useCreateAssetList = ({ assets, @@ -190,84 +180,10 @@ export const useSignWithHardwareWallet = ( disallow(false); redirectToSignFailure(); } - }, [disallow, redirectToSignFailure]); + }, [disallow, redirectToSignFailure, redirectToSignSuccess, req]); return { isConfirmingTx, signWithHardwareWallet }; }; -export const useTxSummary = ({ - req, - addressList, - walletInfo, - createAssetList, - createMintedAssetList, - walletState -}: { - addressList: AddressListType[]; - walletInfo: WalletInfo; - req: TransactionWitnessRequest; - createAssetList: (txAssets: Wallet.Cardano.TokenMap) => Wallet.Cip30SignTxAssetItem[]; - createMintedAssetList: (txAssets: AssetsMintedInspection) => Wallet.Cip30SignTxAssetItem[]; - walletState: ObservableWalletState | null; -}): Wallet.Cip30SignTxSummary | undefined => { - const [txSummary, setTxSummary] = useState(); - const tx = useMemo(() => req?.transaction.toCore(), [req?.transaction]); - const txCollateral = useComputeTxCollateral(walletState, tx); - - useEffect(() => { - if (!tx) { - setTxSummary(void 0); - return; - } - const getTxSummary = async () => { - const inspector = createTxInspector({ - minted: assetsMintedInspector, - burned: assetsBurnedInspector - }); - - const { minted, burned } = await inspector(tx as Wallet.Cardano.HydratedTx); - - const txType = await getTxType(tx); - const addressToNameMap = new Map( - addressList?.map((item: AddressListType) => [item.address, item.name]) - ); - - const externalOutputs = tx.body.outputs.filter((output) => { - if (txType === 'Send') { - return walletInfo.addresses.every((addr) => output.address !== addr.address); - } - return true; - }); - - const txSummaryOutputs: Wallet.Cip30SignTxSummary['outputs'] = externalOutputs.reduce((acc, txOut) => { - // Don't show withdrawl tx's etc - if (txOut.address.toString() === walletInfo.addresses[0].address.toString()) return acc; - - return [ - ...acc, - { - coins: Wallet.util.lovelacesToAdaString(txOut.value.coins.toString()), - recipient: addressToNameMap?.get(txOut.address.toString()) || txOut.address.toString(), - ...(txOut.value.assets?.size > 0 && { assets: createAssetList(txOut.value.assets) }) - } - ]; - }, []); - - // eslint-disable-next-line consistent-return - setTxSummary({ - fee: Wallet.util.lovelacesToAdaString(tx.body.fee.toString()), - outputs: txSummaryOutputs, - type: txType, - mintedAssets: createMintedAssetList(minted), - burnedAssets: createMintedAssetList(burned), - collateral: txCollateral ? Wallet.util.lovelacesToAdaString(txCollateral.toString()) : undefined - }); - }; - getTxSummary(); - }, [tx, walletInfo.addresses, createAssetList, createMintedAssetList, setTxSummary, addressList, txCollateral]); - - return txSummary; -}; - export const useOnBeforeUnload = (callBack: () => void): void => { useEffect(() => { window.addEventListener('beforeunload', callBack); From 3605010d332aecac9c617fd1cfd46571574622fb Mon Sep 17 00:00:00 2001 From: vetalcore Date: Tue, 5 Mar 2024 22:15:44 +0200 Subject: [PATCH 67/68] feat(extension): fix test --- .../DappTransactionContainer.test.tsx | 126 +++++++++++++----- 1 file changed, 90 insertions(+), 36 deletions(-) diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/DappTransactionContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/DappTransactionContainer.test.tsx index 1c8c8eaa46..0922f14c43 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/DappTransactionContainer.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/DappTransactionContainer.test.tsx @@ -1,21 +1,24 @@ +/* eslint-disable no-magic-numbers */ /* eslint-disable sonarjs/no-identical-functions */ /* eslint-disable unicorn/no-null */ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable import/imports-first */ import * as CurrencyProvider from '@providers/currency'; import * as UseFetchCoinPrice from '@hooks/useFetchCoinPrice'; +import * as UseComputeTxCollateral from '@hooks/useComputeTxCollateral'; +import * as GetAssetsInformation from '@src/utils/get-assets-information'; const mockSkeleton = jest.fn(() => ); const mockUseWalletStore = jest.fn(); const t = jest.fn().mockImplementation((res) => res); const mockUseTranslation = jest.fn(() => ({ t })); const mockDappTransaction = jest.fn(); -const mockUseCreateAssetList = jest.fn(); -const mockUseCreateMintedAssetList = jest.fn(); const mockUseViewsFlowContext = jest.fn(); +const mockGetAssetsInformation = jest.fn().mockReturnValue(Promise.resolve(new Map())); const mockWithAddressBookContext = jest.fn((children) => children); const mockUseCurrencyStore = jest.fn().mockReturnValue({ fiatCurrency: { code: 'usd', symbol: '$' } }); const mockUseFetchCoinPrice = jest.fn().mockReturnValue({ priceResult: { cardano: { price: 2 }, tokens: new Map() } }); +const mockUseComputeTxCollateral = jest.fn().mockReturnValue(BigInt(1_000_000)); import * as React from 'react'; import { cleanup, render } from '@testing-library/react'; import { DappTransactionContainer } from '../DappTransactionContainer'; @@ -27,11 +30,17 @@ import { Wallet } from '@lace/cardano'; import { SignTxData } from '../types'; import { getWrapper } from '../testing.utils'; import { TransactionWitnessRequest } from '@cardano-sdk/web-extension'; +import { cardanoCoin } from '@src/utils/constants'; const { Cardano, Crypto } = Wallet; -const assetProvider = 'assetProvider'; -const walletInfo = 'walletInfo'; +const assetProvider = { + getAssets: jest.fn(() => ['assets']) +}; +const walletInfo = { + name: 'wall', + addresses: [{ address: 'address' }] +}; const mockedAssetsInfo = new Map([['id', 'data']]); const assetInfo$ = new BehaviorSubject(mockedAssetsInfo); const available$ = new BehaviorSubject([]); @@ -49,10 +58,22 @@ jest.mock('@src/stores', () => ({ ...jest.requireActual('@src/stores'), useWalletStore: mockUseWalletStore })); + jest.mock('@hooks/useFetchCoinPrice', (): typeof UseFetchCoinPrice => ({ ...jest.requireActual('@hooks/useFetchCoinPrice'), useFetchCoinPrice: mockUseFetchCoinPrice })); + +jest.mock('@hooks/useComputeTxCollateral', (): typeof UseComputeTxCollateral => ({ + ...jest.requireActual('@hooks/useComputeTxCollateral'), + useComputeTxCollateral: mockUseComputeTxCollateral +})); + +jest.mock('@src/utils/get-assets-information', (): typeof GetAssetsInformation => ({ + ...jest.requireActual('@src/utils/get-assets-information'), + getAssetsInformation: mockGetAssetsInformation +})); + jest.mock('@providers/currency', (): typeof CurrencyProvider => ({ ...jest.requireActual('@providers/currency'), useCurrencyStore: mockUseCurrencyStore @@ -76,17 +97,7 @@ jest.mock('react-i18next', () => { }; }); -jest.mock('../hooks.ts', () => { - const original = jest.requireActual('../hooks.ts'); - return { - __esModule: true, - ...original, - useCreateAssetList: mockUseCreateAssetList, - useCreateMintedAssetList: mockUseCreateMintedAssetList - }; -}); - -const addressList = 'addressList'; +const addressList = ['addressList']; jest.mock('@src/features/address-book/context', () => ({ // eslint-disable-next-line @typescript-eslint/no-explicit-any ...jest.requireActual('@src/features/address-book/context'), @@ -143,7 +154,7 @@ describe('Testing DappTransactionContainer component', () => { inMemoryWallet, blockchainProvider: { assetProvider }, walletInfo, - walletUI: { cardanoCoin: {} } + walletUI: { cardanoCoin } })); mockDappTransaction.mockReset(); mockDappTransaction.mockReturnValue(); @@ -172,13 +183,67 @@ describe('Testing DappTransactionContainer component', () => { const errorMessage = 'errorMessage'; const props = { errorMessage }; - const txSummary = 'txSummary'; - const createAssetList = 'createAssetList'; - mockUseCreateAssetList.mockReset(); - mockUseCreateAssetList.mockReturnValue(createAssetList); - const createMintedAssetList = 'createMintedAssetList'; - mockUseCreateMintedAssetList.mockReset(); - mockUseCreateMintedAssetList.mockReturnValue(createMintedAssetList); + const txSummary = { + burnedAssets: [], + collateral: '1.00', + fee: '0.17', + mintedAssets: [ + { + amount: '3', + name: 'asset1rqluyux4nxv6kjashz626c8usp8g88unmqwnyh', + ticker: 'asset1rqluyux4nxv6kjashz626c8usp8g88unmqwnyh' + } + ], + outputs: [ + { + coins: '5.00', + recipient: + 'addr_test1qpfhhfy2qgls50r9u4yh0l7z67xpg0a5rrhkmvzcuqrd0znuzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475q9gw0lz' + }, + { + assets: [ + { + amount: '3', + name: '659f2917fb63f12b33667463ee575eeac1845bbc736b9c0bbc40ba8254534c41', + ticker: undefined + }, + { + amount: '4', + name: '6b8d07d69639e9413dd637a1a815a7323c69c86abbafb66dbfdb1aa7', + ticker: undefined + } + ], + coins: '2.00', + recipient: + 'addr_test1qpfhhfy2qgls50r9u4yh0l7z67xpg0a5rrhkmvzcuqrd0znuzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475q9gw0lz' + }, + { + assets: [ + { + amount: '6', + name: '659f2917fb63f12b33667463ee575eeac1845bbc736b9c0bbc40ba8254534c41', + ticker: undefined + } + ], + coins: '2.00', + recipient: + 'addr_test1qpfhhfy2qgls50r9u4yh0l7z67xpg0a5rrhkmvzcuqrd0znuzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475q9gw0lz' + }, + { + assets: [ + { + amount: '1', + name: '659f2917fb63f12b33667463ee575eeac1845bbc736b9c0bbc40ba8254534c41', + ticker: undefined + } + ], + coins: '2.00', + recipient: + 'addr_test1qq585l3hyxgj3nas2v3xymd23vvartfhceme6gv98aaeg9muzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475q2g7k3g' + } + ], + type: 'Mint' + } as Wallet.Cip30SignTxSummary; await act(async () => { ({ queryByTestId } = render(, { @@ -187,25 +252,14 @@ describe('Testing DappTransactionContainer component', () => { }); expect(queryByTestId('DappTransaction')).toBeInTheDocument(); - expect(mockUseCreateAssetList).toHaveBeenLastCalledWith({ - outputs: tx.body.outputs, - assets: mockedAssetsInfo, - assetProvider - }); - expect(mockUseCreateMintedAssetList).toHaveBeenLastCalledWith({ - outputs: tx.body.outputs, - assets: mockedAssetsInfo, - assetProvider, - metadata: tx.auxiliaryData.blob, - mint: tx.body.mint - }); expect(mockDappTransaction).toHaveBeenLastCalledWith( { dappInfo, transaction: txSummary, fiatCurrencyCode: 'usd', fiatCurrencyPrice: 2, - errorMessage + errorMessage, + coinSymbol: 'ADA' }, {} ); From c21f9642b14b44492def93e35b12023625e7054a Mon Sep 17 00:00:00 2001 From: Michael Chappell <7581002+mchappell@users.noreply.github.com> Date: Wed, 6 Mar 2024 10:07:59 +0000 Subject: [PATCH 68/68] refactor: revert story change --- .../NoSharedWalletFoundDialog.stories.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/core/src/ui/components/SharedWallet/NoSharedWalletFoundDialog/NoSharedWalletFoundDialog.stories.ts b/packages/core/src/ui/components/SharedWallet/NoSharedWalletFoundDialog/NoSharedWalletFoundDialog.stories.ts index 0b7c5be5f0..d4118c4104 100644 --- a/packages/core/src/ui/components/SharedWallet/NoSharedWalletFoundDialog/NoSharedWalletFoundDialog.stories.ts +++ b/packages/core/src/ui/components/SharedWallet/NoSharedWalletFoundDialog/NoSharedWalletFoundDialog.stories.ts @@ -23,7 +23,8 @@ export const Overview: Story = { confirm: 'Proceed' }, events: { - handleOnConfirm: noop, + onCancel: noop, + onConfirm: noop, onOpenChanged: noop } }