diff --git a/package-lock.json b/package-lock.json index cc26d7b..5a76858 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@emotion/server": "^11.11.0", "@ledgerhq/errors": "^6.16.0", "@ledgerhq/hw-transport": "^6.30.0", + "@ledgerhq/hw-transport-web-ble": "^6.29.4", "@ledgerhq/hw-transport-webhid": "^6.28.0", "@mantine/core": "^7.1.5", "@mantine/form": "^7.2.2", @@ -3350,32 +3351,44 @@ } }, "node_modules/@ledgerhq/devices": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/@ledgerhq/devices/-/devices-8.2.2.tgz", - "integrity": "sha512-SKahGA4p0mZ3ovypOJ2wa5mUvUkArE3HBrwWKYf+cRs+t/Licp3OJfhj+DHIxP3AfyH2xR6CFFWECYHeKwGsDQ==", + "version": "8.4.4", + "resolved": "https://registry.npmjs.org/@ledgerhq/devices/-/devices-8.4.4.tgz", + "integrity": "sha512-sz/ryhe/R687RHtevIE9RlKaV8kkKykUV4k29e7GAVwzHX1gqG+O75cu1NCJUHLbp3eABV5FdvZejqRUlLis9A==", "dependencies": { - "@ledgerhq/errors": "^6.16.3", + "@ledgerhq/errors": "^6.19.1", "@ledgerhq/logs": "^6.12.0", "rxjs": "^7.8.1", "semver": "^7.3.5" } }, "node_modules/@ledgerhq/errors": { - "version": "6.16.3", - "resolved": "https://registry.npmjs.org/@ledgerhq/errors/-/errors-6.16.3.tgz", - "integrity": "sha512-3w7/SJVXOPa9mpzyll7VKoKnGwDD3BzWgN1Nom8byR40DiQvOKjHX+kKQausCedTHVNBn9euzPCNsftZ9+mxfw==" + "version": "6.19.1", + "resolved": "https://registry.npmjs.org/@ledgerhq/errors/-/errors-6.19.1.tgz", + "integrity": "sha512-75yK7Nnit/Gp7gdrJAz0ipp31CCgncRp+evWt6QawQEtQKYEDfGo10QywgrrBBixeRxwnMy1DP6g2oCWRf1bjw==" }, "node_modules/@ledgerhq/hw-transport": { - "version": "6.30.5", - "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport/-/hw-transport-6.30.5.tgz", - "integrity": "sha512-JMl//7BgPBvWxrWyMu82jj6JEYtsQyOyhYtonWNgtxn6KUZWht3gU4gxmLpeIRr+DiS7e50mW7m3GA+EudZmmA==", + "version": "6.31.4", + "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport/-/hw-transport-6.31.4.tgz", + "integrity": "sha512-6c1ir/cXWJm5dCWdq55NPgCJ3UuKuuxRvf//Xs36Bq9BwkV2YaRQhZITAkads83l07NAdR16hkTWqqpwFMaI6A==", "dependencies": { - "@ledgerhq/devices": "^8.2.2", - "@ledgerhq/errors": "^6.16.3", + "@ledgerhq/devices": "^8.4.4", + "@ledgerhq/errors": "^6.19.1", "@ledgerhq/logs": "^6.12.0", "events": "^3.3.0" } }, + "node_modules/@ledgerhq/hw-transport-web-ble": { + "version": "6.29.4", + "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport-web-ble/-/hw-transport-web-ble-6.29.4.tgz", + "integrity": "sha512-OJyp6CryvyFlg1L9uifo5hYYdDt+WPw8/0ijBixYhYmGvlRz2W6/F2c5rG/zBQWcNnNydPOLjLJM0vR070RfCw==", + "dependencies": { + "@ledgerhq/devices": "^8.4.4", + "@ledgerhq/errors": "^6.19.1", + "@ledgerhq/hw-transport": "^6.31.4", + "@ledgerhq/logs": "^6.12.0", + "rxjs": "^7.8.1" + } + }, "node_modules/@ledgerhq/hw-transport-webhid": { "version": "6.28.0", "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport-webhid/-/hw-transport-webhid-6.28.0.tgz", diff --git a/package.json b/package.json index 422503d..f233ba4 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "@emotion/server": "^11.11.0", "@ledgerhq/errors": "^6.16.0", "@ledgerhq/hw-transport": "^6.30.0", + "@ledgerhq/hw-transport-web-ble": "^6.29.4", "@ledgerhq/hw-transport-webhid": "^6.28.0", "@mantine/core": "^7.1.5", "@mantine/form": "^7.2.2", diff --git a/src/app/page.tsx b/src/app/page.tsx index 81354f5..77d2d37 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -67,6 +67,7 @@ const WHITELIST = [ 'preview.kasvault.io', 'privatepreview.kasvault.io', 'kasvault.vercel.app', + 'bluetooth.kasvault.io', ]; export default function Home() { @@ -87,7 +88,10 @@ export default function Home() { } } - setIsShowDemo(window.location.hostname !== 'kasvault.io'); + setIsShowDemo( + window.location.hostname === 'preview.kasvault.io' || + window.location.search.includes('demo'), + ); }, []); const smallStyles = width <= 48 * 16 ? { fontSize: '1rem' } : {}; @@ -107,7 +111,23 @@ export default function Home() { (Replaced with bluetooth in the future) - ) : null; + ) : ( + { + getAppData(navigate, 'bluetooth'); + }} + align='center' + > +

+ + Connect with Bluetooth + -> + +

+ Nano X, Stax and Flex +
+ ); return ( @@ -138,11 +158,11 @@ export default function Home() { }} align='center' > -

+

Connect with USB -> -

+ All Ledger devices
diff --git a/src/app/wallet/page.tsx b/src/app/wallet/page.tsx index 9b962a5..17b684c 100644 --- a/src/app/wallet/page.tsx +++ b/src/app/wallet/page.tsx @@ -6,9 +6,10 @@ import { fetchAddressDetails, initTransport, fetchAddressBalance, + isLedgerTransportInitialized, } from '../../lib/ledger'; import { useState, useEffect } from 'react'; -import { Stack, Tabs, Breadcrumbs, Anchor, Button, Center } from '@mantine/core'; +import { Stack, Tabs, Breadcrumbs, Anchor, Button, Center, Modal, Text } from '@mantine/core'; import Header from '../../components/header'; import AddressesTab from './addresses-tab'; import OverviewTab from './overview-tab'; @@ -279,6 +280,7 @@ export default function Dashboard() { const [enableGenerate, setEnableGenerate] = useState(false); const [mempoolEntryToReplace, setMempoolEntryToReplace] = useState(null); const [pendingTxId, setPendingTxId] = useState(null); + const [showConnectModal, setShowConnectModal] = useState(false); const { ref: containerRef, width: containerWidth, height: containerHeight } = useElementSize(); @@ -376,27 +378,32 @@ export default function Dashboard() { let unloaded = false; - initTransport(deviceType) - .then(() => { - if (!unloaded) { - setTransportInitialized(true); - - return getXPubFromLedger().then((xpub) => - setBIP32Base(new KaspaBIP32(xpub.compressedPublicKey, xpub.chainCode)), - ); - } + if (!isLedgerTransportInitialized()) { + setShowConnectModal(true); + } else { + initTransport(deviceType) + .then(() => { + if (!unloaded) { + setTransportInitialized(true); + + return getXPubFromLedger().then((xpub) => + setBIP32Base(new KaspaBIP32(xpub.compressedPublicKey, xpub.chainCode)), + ); + } - return null; - }) - .catch((e) => { - notifications.show({ - title: 'Error', - color: 'red', - message: 'Please make sure your device is unlocked and the Kaspa app is open', - autoClose: false, + return null; + }) + .catch((e) => { + notifications.show({ + title: 'Error', + color: 'red', + message: + 'Please make sure your device is unlocked and the Kaspa app is open', + autoClose: false, + }); + console.error(e); }); - console.error(e); - }); + } return () => { unloaded = true; @@ -426,7 +433,7 @@ export default function Dashboard() { return; } - if (deviceType === 'usb') { + if (deviceType === 'usb' || deviceType === 'bluetooth') { loadOrScanAddressBatch(bip32base, setAddresses, setRawAddresses, userSettings).finally( () => { setEnableGenerate(true); @@ -455,6 +462,51 @@ export default function Dashboard() { {breadcrumbs} + setShowConnectModal(false)} + title={'Connect Ledger Device via ' + deviceType} + > + + Please connect your Ledger device and open the Kaspa app. + + + +