Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 25 additions & 12 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
28 changes: 24 additions & 4 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ const WHITELIST = [
'preview.kasvault.io',
'privatepreview.kasvault.io',
'kasvault.vercel.app',
'bluetooth.kasvault.io',
];

export default function Home() {
Expand All @@ -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' } : {};
Expand All @@ -107,7 +111,23 @@ export default function Home() {
</h2>
<Text>(Replaced with bluetooth in the future)</Text>
</Stack>
) : null;
) : (
<Stack
className={styles.card}
onClick={() => {
getAppData(navigate, 'bluetooth');
}}
align='center'
>
<h3>
<Group style={smallStyles}>
<IconBluetooth style={smallStyles} /> Connect with Bluetooth
<span>-&gt;</span>
</Group>
</h3>
<Text>Nano X, Stax and Flex</Text>
</Stack>
);

return (
<Stack className={styles.main}>
Expand Down Expand Up @@ -138,11 +158,11 @@ export default function Home() {
}}
align='center'
>
<h2>
<h3>
<Group style={smallStyles}>
<IconUsb /> Connect with USB <span>-&gt;</span>
</Group>
</h2>
</h3>

<Text>All Ledger devices</Text>
</Stack>
Expand Down
94 changes: 73 additions & 21 deletions src/app/wallet/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -279,6 +280,7 @@ export default function Dashboard() {
const [enableGenerate, setEnableGenerate] = useState(false);
const [mempoolEntryToReplace, setMempoolEntryToReplace] = useState<IMempoolEntry | null>(null);
const [pendingTxId, setPendingTxId] = useState<string | null>(null);
const [showConnectModal, setShowConnectModal] = useState(false);

const { ref: containerRef, width: containerWidth, height: containerHeight } = useElementSize();

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -455,6 +462,51 @@ export default function Dashboard() {
<Breadcrumbs>{breadcrumbs}</Breadcrumbs>
</Header>

<Modal
centered
opened={showConnectModal}
withCloseButton={false}
onClose={() => setShowConnectModal(false)}
title={'Connect Ledger Device via ' + deviceType}
>
<Stack>
<Text>Please connect your Ledger device and open the Kaspa app.</Text>
<Button
onClick={() => {
setShowConnectModal(false);
initTransport(deviceType)
.then(() => {
setTransportInitialized(true);
return getXPubFromLedger().then((xpub) =>
setBIP32Base(
new KaspaBIP32(
xpub.compressedPublicKey,
xpub.chainCode,
),
),
);
})
.catch((e) => {
if (e.name === 'TransportOpenUserCancelled') {
setShowConnectModal(true);
} else {
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);
});
}}
>
Connect Device
</Button>
</Stack>
</Modal>

<Center>
<Tabs
value={activeTab}
Expand Down
2 changes: 1 addition & 1 deletion src/components/send-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ export default function SendForm(props: SendFormProps) {

if (deviceType == 'demo') {
simulateConfirmation(notifId);
} else if (deviceType == 'usb') {
} else if (deviceType == 'usb' || deviceType == 'bluetooth') {
try {
const { tx } = createTransaction(
kasToSompi(Number(form.values.amount)),
Expand Down
16 changes: 15 additions & 1 deletion src/lib/ledger.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import TransportWebHID from '@ledgerhq/hw-transport-webhid';
import BluetoothTransport from '@ledgerhq/hw-transport-web-ble';
import axios from 'axios';
import axiosRetry from 'axios-retry';

Expand All @@ -19,6 +20,7 @@ let transportState = {
transport: null,
initPromise: null,
type: null,
initialized: false,
};

const kaspaState = {
Expand Down Expand Up @@ -156,13 +158,25 @@ export async function initTransport(type = 'usb') {
return await transportState.initPromise;
}

transportState.initPromise = TransportWebHID.create();
if (type === 'usb') {
transportState.initPromise = TransportWebHID.create();
} else if (type === 'bluetooth') {
transportState.initPromise = BluetoothTransport.create();
} else {
throw new Error('Unknown device type');
}

transportState.transport = await transportState.initPromise;
transportState.type = type;
transportState.initialized = true;

return transportState.transport;
}

export function isLedgerTransportInitialized() {
return transportState.initialized;
}

export async function fetchTransactionCount(address) {
const { data: txCount } = await axios.get(
`https://api.kaspa.org/addresses/${address}/transactions-count`,
Expand Down
Loading