diff --git a/src/app/features/ledger/generic-steps/connect-device/connect-ledger.layout.tsx b/src/app/features/ledger/generic-steps/connect-device/connect-ledger.layout.tsx deleted file mode 100644 index 1bd56b374f6..00000000000 --- a/src/app/features/ledger/generic-steps/connect-device/connect-ledger.layout.tsx +++ /dev/null @@ -1,133 +0,0 @@ -import { Suspense, lazy, useMemo } from 'react'; - -import { Box, HStack, Stack, styled } from 'leather-styles/jsx'; - -import { SupportedBlockchains } from '@shared/constants'; -import { LEDGER_BITCOIN_ENABLED } from '@shared/environment'; - -import { useOnMount } from '@app/common/hooks/use-on-mount'; -import { LeatherButton } from '@app/components/button/button'; -import { ExternalLink } from '@app/components/external-link'; -import { BtcLedgerIcon } from '@app/components/icons/btc-ledger-icon'; -import { StxLedgerIcon } from '@app/components/icons/stx-ledger-icon'; -import { Divider } from '@app/components/layout/divider'; -import { useHasBitcoinLedgerKeychain } from '@app/store/accounts/blockchain/bitcoin/bitcoin.ledger'; - -import { LedgerWrapper } from '../../components/ledger-wrapper'; - -const PluggingInLedgerCableAnimation = lazy( - () => import('../../animations/plugging-in-cable.lottie') -); - -interface ConnectLedgerLayoutProps { - chain?: SupportedBlockchains; - awaitingLedgerConnection?: boolean; - warning?: React.ReactNode; - showInstructions?: boolean; - onConnectLedger?(): void; - connectBitcoin?(): void; - connectStacks?(): void; -} - -export function ConnectLedgerLayout(props: ConnectLedgerLayoutProps) { - const { - onConnectLedger, - connectBitcoin, - connectStacks, - warning, - showInstructions, - awaitingLedgerConnection, - } = props; - const hasBitcoinAccount = useHasBitcoinLedgerKeychain(); - - const showBitcoinConnectButton = useMemo(() => { - return LEDGER_BITCOIN_ENABLED && !hasBitcoinAccount; - }, [hasBitcoinAccount]); - - const instructions = useMemo( - () => [ - '1. Connect & unlock your Ledger', - `2. Open ${showBitcoinConnectButton ? 'Bitcoin or' : ''} Stacks app`, - '3. Click the button below', - ], - [showBitcoinConnectButton] - ); - - useOnMount(() => { - if (!onConnectLedger) { - return; - } - - // This is a hack to call function - setTimeout(() => { - onConnectLedger(); - }); - }); - return ( - - - - {} - - - - - Please follow the instructions: - - - {instructions.map((instruction, index) => ( - - {instruction} - - ))} - - - {showBitcoinConnectButton && ( - - - - Connect Bitcoin - - - )} - - - - Connect Stacks - - - - - {warning && ( - - {warning} - - )} - {showInstructions ? ( - - - - - First time using Ledger on Leather? - - - - Learn how to use Ledger device with Leather ↗ - - - - - ) : null} - - ); -} diff --git a/src/app/store/ledger/bitcoin/bitcoin-key.slice.ts b/src/app/store/ledger/bitcoin/bitcoin-key.slice.ts index fee611e46ff..63051a99fc4 100644 --- a/src/app/store/ledger/bitcoin/bitcoin-key.slice.ts +++ b/src/app/store/ledger/bitcoin/bitcoin-key.slice.ts @@ -7,10 +7,10 @@ function selectBitcoinKeysSlice(state: RootState) { return state.ledger.bitcoin; } +// ts-unused-exports:disable-next-line export const { slice: bitcoinKeysSlice, adapter } = generateLedgerChainKeyStorageSlice('bitcoin'); const selectors = adapter.getSelectors(selectBitcoinKeysSlice); export const selectDefaultWalletBitcoinKeyEntities = selectors.selectEntities; -export const selectDefaultWalletBitcoinKeys = selectors.selectAll; diff --git a/src/app/store/ledger/ledger.selectors.ts b/src/app/store/ledger/ledger.selectors.ts index 49a15007ac9..0065466e52f 100644 --- a/src/app/store/ledger/ledger.selectors.ts +++ b/src/app/store/ledger/ledger.selectors.ts @@ -12,7 +12,7 @@ const selectNumberOfLedgerKeysPersisted = createSelector(selectLedger, ledger => sumNumbers(Object.values(ledger).map(chain => Object.keys(chain.entities).length)) ); -export function useNumberOfLedgerKeysPersisted() { +function useNumberOfLedgerKeysPersisted() { return useSelector(selectNumberOfLedgerKeysPersisted); } diff --git a/src/app/store/ledger/stacks/stacks-key.slice.ts b/src/app/store/ledger/stacks/stacks-key.slice.ts index 8a084fa34c1..fcbc31bac79 100644 --- a/src/app/store/ledger/stacks/stacks-key.slice.ts +++ b/src/app/store/ledger/stacks/stacks-key.slice.ts @@ -7,11 +7,11 @@ function selectStacksKeysSlice(state: RootState) { return state.ledger.stacks; } +// ts-unused-exports:disable-next-line export const { slice: stacksKeysSlice, adapter } = generateLedgerChainKeyStorageSlice< StacksAppKeysResponseItem & { id: string } >('stacks'); const selectors = adapter.getSelectors(selectStacksKeysSlice); -export const selectDefaultWalletStacksKeyEntities = selectors.selectEntities; export const selectDefaultWalletStacksKeys = selectors.selectAll; diff --git a/src/shared/storage/redux-pesist.ts b/src/shared/storage/redux-pesist.ts index 6b59787e380..d45e2711b5c 100644 --- a/src/shared/storage/redux-pesist.ts +++ b/src/shared/storage/redux-pesist.ts @@ -76,7 +76,7 @@ const legacyPersistConfig: PersistConfig = { version: 1, storage, serialize: true, - whitelist: ['analytics', 'chains', 'keys', 'networks', 'onboarding', 'settings'], + whitelist: ['analytics', 'chains', 'software-keys', 'networks', 'onboarding', 'settings'], }; async function migrateToUsingNoSerialization() { @@ -114,7 +114,7 @@ export const persistConfig: PersistConfig & UntypedDeserializeOption 'analytics', 'chains', 'ordinals', - 'keys', + 'software-keys', 'ledger', 'networks', 'onboarding', diff --git a/tests/page-object-models/onboarding.page.ts b/tests/page-object-models/onboarding.page.ts index 7cb9dfd04f5..ff99ad5a65e 100644 --- a/tests/page-object-models/onboarding.page.ts +++ b/tests/page-object-models/onboarding.page.ts @@ -11,7 +11,7 @@ const TEST_ACCOUNT_SECRET_KEY = process.env.TEST_ACCOUNT_SECRET_KEY ?? ''; export const testSoftwareAccountDefaultWalletState = { analytics: { hasStxDeposits: { '1': true, '2147483648': true } }, chains: { stx: { default: { highestAccountIndex: 1, currentAccountIndex: 0 } } }, - keys: { + 'software-keys': { ids: ['default'], entities: { default: { @@ -27,6 +27,12 @@ export const testSoftwareAccountDefaultWalletState = { bitcoin: { entities: {}, ids: [], + targetId: '', + }, + stacks: { + entities: {}, + ids: [], + targetId: '', }, }, networks: { ids: [], entities: {}, currentNetworkId: 'mainnet' }, @@ -47,41 +53,150 @@ const testLedgerAccountDefaultWalletState = { _persist: { rehydrated: true, version: 1 }, analytics: { hasStxDeposits: { '1': false, '2147483648': true } }, chains: { stx: { default: { currentAccountIndex: 0, highestAccountIndex: 0 } } }, - keys: { - entities: { - default: { - id: 'default', - publicKeys: [ - { - dataPublicKey: - '044b6fdad95384e953804821151cbc98f0131d08b0595b9da722bf7ca285a1f4fe50e43f881ead4031eb721b4f10cd227d2a9a19bac5b985bf414e598ac9b411d3', - stxPublicKey: '021818dd91f5342e3b366abc0734fc5a05a8eb5bf28d43dd122d8d1ffcc664bcea', - }, - { - dataPublicKey: - '04798cb7cd3eafede5e59d530d574c70a374a15e14a9a1e81a8659cdcac1b2334be67d21275bfb2dcb383b0427999c7091c586786c61e7e3fca5e0118d7eb10f33', - stxPublicKey: '03dabcfe8913a49c66e80ddc8994855a5dc30288830f5c444b3fb776e2bb5d4332', - }, - { - dataPublicKey: - '0405a7f454cbbb2eb56a48048b490d9d2d3e27f9eeec93437b6c16729c4571a9951c3255ba6295bf19fd74d939d70058ae51d4dad76c50ec5ff9e18bd08860a050', - stxPublicKey: '0256723bcb20d7628f10108139d1160d5f288b1bc91906fb034bf48007ddf5376e', - }, - { - dataPublicKey: - '04ead5999a6da792d29b5dcb552284a68175cf8011743241a901655eebee063ffd7285536138dccfa93bcf4245258b0ba79a85c40ab4c3358d326f7535d60efb01', - stxPublicKey: '034b1553ecaee148cf191cc7c893da9265865327a15d25655608b2d57e64c45b20', - }, - { - dataPublicKey: - '0460ca5664c241224586b707c71f8fc0fb6d801926b32baec3ce85b75b920e651dd56949ee348cd8d3afe57e1b65eed150ad1c89b2e465edd1b93767e1cbad699e', - stxPublicKey: '020b0c3083f734922997d3224a543fc29b1dcb255e95667868b351ec08b919095c', - }, - ], - type: 'ledger', + 'software-keys': { + entities: {}, + ids: [], + }, + ledger: { + bitcoin: { + entities: { + "default/84'/0'/0'": { + id: "default/84'/0'/0'", + path: "m/84'/0'/0'", + policy: + "[e87a850b/84'/0'/0']xpub6BuKrNqTrGfsy8VAAdUW2KCxbHywuSKjg7hZuAXERXDv7GfuxUgUWdVRKNsgujcwdjEHCjaXWouPKi1m5gMgdWX8JpRcyMkrSxPe4Da3Lx8", + walletId: 'default', + }, + "default/84'/0'/1'": { + id: "default/84'/0'/1'", + path: "m/84'/0'/1'", + policy: + "[e87a850b/84'/0'/1']xpub6BuKrNqTrGft1dv2pR3Ey8VsBnSBkVVpehNsro8V8kaWMRGeUNv8yhJpTw62Ldqenm5kuVyC2bQqgc6yrKAruDKyzz18zi83Sg2FTwEHsrF", + walletId: 'default', + }, + "default/84'/0'/2'": { + id: "default/84'/0'/2'", + path: "m/84'/0'/2'", + policy: + "[e87a850b/84'/0'/2']xpub6BuKrNqTrGft5UhSiYcXtN1d9Cp8iwj9tBVLjfJtLUqUFYA2xjVmAiB4TbUP6uaX3qwNhrW3baGE1Fz49YNSFcEMTtcd4Uz25juszoCCy8w", + walletId: 'default', + }, + "default/84'/0'/3'": { + id: "default/84'/0'/3'", + path: "m/84'/0'/3'", + policy: + "[e87a850b/84'/0'/3']xpub6BuKrNqTrGft7h39ks3qJjcz3KusNtsDtr8t59t2MUneWoCqbGYLcqLeqRaXC5na2tWDDzncBBVNVPT55b6jLM4dT5f6aGvgaXEXV6VniL6", + walletId: 'default', + }, + "default/84'/0'/4'": { + id: "default/84'/0'/4'", + path: "m/84'/0'/4'", + policy: + "[e87a850b/84'/0'/4']xpub6BuKrNqTrGftAswPZxdCzxArCp1bsUh3JPizsMymSVanfVJqXR2wjsX7PBnwMXnXttiWU6pMdBgB82mR2BPDtSGcUfjD8QJTNca47iYkGD3", + walletId: 'default', + }, + "default/86'/0'/0'": { + id: "default/86'/0'/0'", + path: "m/86'/0'/0'", + policy: + "[e87a850b/86'/0'/0']xpub6C4MQD2bVDTfdnVe5AYKB6gE7BE4yQeKBRgukQ4Hi3phDB5fCYKEAdViQ2n7kZQ1t728QV4wKGgiR5qGigjNNrm5DCGWYUZDRVNWYb8ZWGK", + walletId: 'default', + }, + "default/86'/0'/1'": { + id: "default/86'/0'/1'", + path: "m/86'/0'/1'", + policy: + "[e87a850b/86'/0'/1']xpub6C4MQD2bVDTfgjjWZhmPMNDMFHFmrSmGzqJVpuf98XB8F5eNaQus6XmrcrTrTiiL2EscdC4cjztP5LfaW13vZ6eDuDHXXAq71W5KEHeEeKH", + walletId: 'default', + }, + "default/86'/0'/2'": { + id: "default/86'/0'/2'", + path: "m/86'/0'/2'", + policy: + "[e87a850b/86'/0'/2']xpub6C4MQD2bVDTfkGnARZXj6dRRF223bcyKAK2qCRKf9xyPQg7k4ZZc4FAHLcXhQ1NCVJCTVGEMd1YoRnBBDdgXKrmt4bm5XmF1ry9ox4Qsx3F", + walletId: 'default', + }, + "default/86'/0'/3'": { + id: "default/86'/0'/3'", + path: "m/86'/0'/3'", + policy: + "[e87a850b/86'/0'/3']xpub6C4MQD2bVDTfmbN4ZJfozbNRMqyD1jmMFcQTNRUNyjE2J6tdVggFoQ8KmxUpijsZX1E4iDciY5AmnHbq95BHMVGJAGZ1MAm7iupHkTBV6YE", + walletId: 'default', + }, + "default/86'/0'/4'": { + id: "default/86'/0'/4'", + path: "m/86'/0'/4'", + policy: + "[e87a850b/86'/0'/4']xpub6C4MQD2bVDTfq9RLtYxmqJRNsiviyuM51CFE1qqQbE6o8QN9Uix47Kvj4fqKFX5f88DyhxaX93L4H1WdSZChMZUWGUzPm54N9VfvsYJBvi9", + walletId: 'default', + }, }, + ids: [ + "default/84'/0'/0'", + "default/84'/0'/1'", + "default/84'/0'/2'", + "default/84'/0'/3'", + "default/84'/0'/4'", + "default/86'/0'/0'", + "default/86'/0'/1'", + "default/86'/0'/2'", + "default/86'/0'/3'", + "default/86'/0'/4'", + ], + targetId: '', + }, + stacks: { + entities: { + "default/44'/5757'/0'/0/0": { + path: "m/44'/5757'/0'/0/0", + stxPublicKey: '0329b076bc20f7b1592b2a1a5cb91dfefe8c966e50e256458e23dd2c5d63f8f1af', + dataPublicKey: + '04716759aa2d2ec9066ff699626c3404c5cc7e84e7295af6768a0fce2defcd1c50a9ee4b1fd1e63295abc47c81f602e77c497f4549fa68535c7abbe73854b62df7', + id: "default/44'/5757'/0'/0/0", + walletId: 'default', + }, + "default/44'/5757'/0'/0/1": { + path: "m/44'/5757'/0'/0/1", + stxPublicKey: '035c63a8042cd820ae59b50cfb225b886d0837c97a5f5daa190037fcadf60a1da6', + dataPublicKey: + '04c8fba749c7be4a817c1bee8c24b7464f3be6f7e78f5c9ab43a57710f703155e059ce8b5fcb33e8c8d0ff154e964f99c486eed8b8b19f108cf5137a07275a277f', + id: "default/44'/5757'/0'/0/1", + walletId: 'default', + }, + "default/44'/5757'/0'/0/2": { + path: "m/44'/5757'/0'/0/2", + stxPublicKey: '02dbcd4e19f13709889eebdb450f84b48195f8ada1673cd8e663ca409a09379740', + dataPublicKey: + '04614af2cb5b9a07fb9049713a860a09cd97549373e73104e32b814922392a97a3c6d938f2b7f6e771c5e6611be64b762919a435a242fa5796b5bb4b9728eb079e', + id: "default/44'/5757'/0'/0/2", + walletId: 'default', + }, + "default/44'/5757'/0'/0/3": { + path: "m/44'/5757'/0'/0/3", + stxPublicKey: '03a9ee7ccb82ecdd9de236b4d1909f79e75d93ba0ae68494f0cf710a5bf1e47837', + dataPublicKey: + '04e3c33077024159f2a1aa28e4e73811d477fac3303f6395bfb8937994bc61d1a3b762d52ea4a57d0f2ed36523a96ffec74d1f05676e4411601402013f16f16374', + id: "default/44'/5757'/0'/0/3", + walletId: 'default', + }, + "default/44'/5757'/0'/0/4": { + path: "m/44'/5757'/0'/0/4", + stxPublicKey: '03e8e4daeece139da8e03d06734712b3dce83175791b94f44185c3fdae9122d264', + dataPublicKey: + '04673e21fc8fb98131d843bcb10edb015dd3219bb1f730c81c6de13a9df91d5f1a709099cd0d41d535f45b3119d3458ccdc98614ee4833c99f09c7c62d654350fa', + id: "default/44'/5757'/0'/0/4", + walletId: 'default', + }, + }, + ids: [ + "default/44'/5757'/0'/0/0", + "default/44'/5757'/0'/0/1", + "default/44'/5757'/0'/0/2", + "default/44'/5757'/0'/0/3", + "default/44'/5757'/0'/0/4", + ], + targetId: '', }, - ids: ['default'], }, networks: { currentNetworkId: 'mainnet', entities: {}, ids: [] }, onboarding: { @@ -159,7 +274,7 @@ export class OnboardingPage { testSoftwareAccountDefaultWalletState ); await this.page.goto(`chrome-extension://${id}/index.html`); - await this.page.getByRole('textbox').type(TEST_PASSWORD); + await this.page.getByRole('textbox').fill(TEST_PASSWORD); await this.page.getByRole('button', { name: 'Continue' }).click(); await this.page.waitForURL('**' + RouteUrls.Home); await this.page.locator('text="Account 1"').waitFor(); diff --git a/tests/specs/ledger/ledger.spec.ts b/tests/specs/ledger/ledger.spec.ts index 828521cb7dc..398934113ff 100644 --- a/tests/specs/ledger/ledger.spec.ts +++ b/tests/specs/ledger/ledger.spec.ts @@ -1,3 +1,5 @@ +import { TEST_ACCOUNT_1_STX_ADDRESS } from '@tests/mocks/constants'; + import { test } from '../../fixtures/fixtures'; test.describe('App with Ledger', () => { @@ -13,8 +15,8 @@ test.describe('App with Ledger', () => { }); test('that receive modal opens', async ({ homePage }) => { - const address = await homePage.getReceiveStxAddress(); - test.expect(address).toEqual('SPSDM5RXY2E3V7JTFYKPFNRPDHG1B85788FKG2KN'); + const stacksAddress = await homePage.getReceiveStxAddress(); + test.expect(stacksAddress).toEqual(TEST_ACCOUNT_1_STX_ADDRESS); }); test('that you can navigate to activity page', async ({ homePage }) => { diff --git a/tests/specs/onboarding/onboarding.spec.ts b/tests/specs/onboarding/onboarding.spec.ts index cfa55668eda..383f6c8f543 100644 --- a/tests/specs/onboarding/onboarding.spec.ts +++ b/tests/specs/onboarding/onboarding.spec.ts @@ -24,10 +24,11 @@ test.describe('Onboarding an existing user', () => { ); // Deleting values that are known to differ at random - delete (walletState as any).keys.entities.default.encryptedSecretKey; - delete (walletState as any).keys.entities.default.salt; - delete (testSoftwareAccountDefaultWalletState as any).keys.entities.default.encryptedSecretKey; - delete (testSoftwareAccountDefaultWalletState as any).keys.entities.default.salt; + delete (walletState as any)['software-keys'].entities.default.encryptedSecretKey; + delete (walletState as any)['software-keys'].entities.default.salt; + delete (testSoftwareAccountDefaultWalletState as any)['software-keys'].entities.default + .encryptedSecretKey; + delete (testSoftwareAccountDefaultWalletState as any)['software-keys'].entities.default.salt; test.expect(walletState).toEqual(testSoftwareAccountDefaultWalletState); }); diff --git a/tests/specs/store-migrations/store-migrations.spec.ts b/tests/specs/store-migrations/store-migrations.spec.ts index 8d050b18594..0fe3132184e 100644 --- a/tests/specs/store-migrations/store-migrations.spec.ts +++ b/tests/specs/store-migrations/store-migrations.spec.ts @@ -10,7 +10,7 @@ test.describe('Store migrations', () => { test.describe('Migration 0 --> 1', () => { const previousSerializedState = - '{"analytics":"{\\"hasStxDeposits\\":{\\"1\\":true,\\"2147483648\\":true}}","chains":"{\\"stx\\":{\\"default\\":{\\"highestAccountIndex\\":16,\\"currentAccountIndex\\":0}}}","keys":"{\\"ids\\":[\\"default\\"],\\"entities\\":{\\"default\\":{\\"type\\":\\"software\\",\\"id\\":\\"default\\",\\"salt\\":\\"c4cccf33166051f7704cd877a2f03f93\\",\\"encryptedSecretKey\\":\\"b7f516798e7160eca15c50b62e588698937f8ecf3930efc42baa690ddc0c7a51b74e3e4b129859274ed272652bc47651c6b6effbddf4d72a3eb9d2ea657b64a833c9bdccb562e45d94f0cc1366154072f12d35290566a99a6f952cd234ca9259\\"}}}","networks":"{\\"ids\\":[],\\"entities\\":{},\\"currentNetworkId\\":\\"mainnet\\"}","onboarding":"{\\"hideSteps\\":true,\\"stepsStatus\\":{\\"Back up secret key\\":1,\\"Add some funds\\":0,\\"Explore apps\\":0,\\"Buy an NFT\\":0}}","settings":"{\\"userSelectedTheme\\":\\"system\\",\\"hasAllowedAnalytics\\":false,\\"dismissedMessages\\":[]}","_persist":"{\\"version\\":1,\\"rehydrated\\":true}"}'; + '{"analytics":"{\\"hasStxDeposits\\":{\\"1\\":true,\\"2147483648\\":true}}","chains":"{\\"stx\\":{\\"default\\":{\\"highestAccountIndex\\":16,\\"currentAccountIndex\\":0}}}","software-keys":"{\\"ids\\":[\\"default\\"],\\"entities\\":{\\"default\\":{\\"type\\":\\"software\\",\\"id\\":\\"default\\",\\"salt\\":\\"c4cccf33166051f7704cd877a2f03f93\\",\\"encryptedSecretKey\\":\\"b7f516798e7160eca15c50b62e588698937f8ecf3930efc42baa690ddc0c7a51b74e3e4b129859274ed272652bc47651c6b6effbddf4d72a3eb9d2ea657b64a833c9bdccb562e45d94f0cc1366154072f12d35290566a99a6f952cd234ca9259\\"}}}","networks":"{\\"ids\\":[],\\"entities\\":{},\\"currentNetworkId\\":\\"mainnet\\"}","onboarding":"{\\"hideSteps\\":true,\\"stepsStatus\\":{\\"Back up secret key\\":1,\\"Add some funds\\":0,\\"Explore apps\\":0,\\"Buy an NFT\\":0}}","settings":"{\\"userSelectedTheme\\":\\"system\\",\\"hasAllowedAnalytics\\":false,\\"dismissedMessages\\":[]}","_persist":"{\\"version\\":1,\\"rehydrated\\":true}"}'; test('that the app detects old store format', async ({ extensionId, globalPage }) => { const { page } = globalPage;