Skip to content

Commit 263ef31

Browse files
authored
Merge pull request #212 from covenantalade/fix/issues-163-164-168
fix: resolve issues #163, #164, #168
2 parents 6e941d8 + e3f1dfb commit 263ef31

10 files changed

Lines changed: 702 additions & 161 deletions

File tree

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@
3333
"recharts": "^2.12.7",
3434
"zustand": "^4.5.4"
3535
},
36+
"optionalDependencies": {
37+
"@ledgerhq/hw-transport-webusb": "^6.29.4",
38+
"@ledgerhq/hw-transport-webhid": "^6.29.4",
39+
"@stellar/ledger": "^1.0.0"
40+
},
3641
"devDependencies": {
3742
"@babel/preset-env": "^7.29.3",
3843
"@babel/preset-react": "^7.24.7",

src/App.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ import {
5353
} from './lib/securityEvents'
5454
import { TourLauncher } from './components/tutorial'
5555
import SearchBar from './components/layout/SearchBar'
56+
import GlobalSearch from './components/search/GlobalSearch'
5657
import UserPreferences from './components/preferences/UserPreferences'
5758
import MobileNavigation from './components/layout/MobileNavigation'
5859
import KeyboardNavigation from './components/accessibility/KeyboardNavigation'
@@ -294,6 +295,10 @@ function DashboardLayout() {
294295
setActiveTab('account')
295296
return
296297
}
298+
if (result.type === 'contract') {
299+
setActiveTab('contracts')
300+
return
301+
}
297302
setActiveTab('overview')
298303
}
299304

@@ -313,7 +318,7 @@ function DashboardLayout() {
313318
<KeyboardNavigation />
314319
<div style={{ marginBottom: '12px', display: 'flex', alignItems: 'center', gap: '8px' }}>
315320
<div style={{ flex: 1 }}>
316-
<SearchBar onSelectResult={handleSearchResult} />
321+
<GlobalSearch onSelectResult={handleSearchResult} />
317322
</div>
318323
<button
319324
onClick={() => setPreferencesOpen(true)}

src/components/dashboard/TransactionSigner.jsx

Lines changed: 73 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import React, { useState } from 'react'
22
import { useStore } from '../../lib/store'
33
import { signTransactionWithFreighter } from '../../lib/wallet/freighter'
4+
import { signXdrWithLedger, isLedgerSupported, getActiveLedgerSession } from '../../lib/wallet/ledger'
5+
import { NETWORKS } from '../../lib/stellar'
46
import Card from './Card'
57

68
export default function TransactionSigner() {
@@ -10,6 +12,9 @@ export default function TransactionSigner() {
1012
const [signing, setSigning] = useState(false)
1113
const [error, setError] = useState(null)
1214
const [copied, setCopied] = useState(false)
15+
const [ledgerPrompt, setLedgerPrompt] = useState(false)
16+
17+
const networkPassphrase = NETWORKS[network]?.passphrase || NETWORKS.testnet.passphrase
1318

1419
const handleSign = async () => {
1520
if (!xdr.trim()) {
@@ -23,18 +28,15 @@ export default function TransactionSigner() {
2328

2429
try {
2530
let result = null
26-
const networkName = network === 'mainnet' ? 'PUBLIC' : 'TESTNET'
2731

2832
if (walletType === 'freighter') {
33+
const networkName = network === 'mainnet' ? 'PUBLIC' : 'TESTNET'
2934
result = await signTransactionWithFreighter(xdr.trim(), networkName)
3035
} else if (walletType === 'ledger') {
31-
setError('Ledger signing requires the device to be connected. Use the Builder tab to build and sign transactions.')
32-
setSigning(false)
33-
return
36+
await _signWithLedger()
37+
return // _signWithLedger manages its own state
3438
} else {
35-
setError('No wallet connected. Connect a wallet first.')
36-
setSigning(false)
37-
return
39+
throw new Error('No wallet connected. Connect a wallet first.')
3840
}
3941

4042
setSignedXdr(result)
@@ -45,6 +47,47 @@ export default function TransactionSigner() {
4547
}
4648
}
4749

50+
const _signWithLedger = async () => {
51+
// Check browser support first
52+
const supported = await isLedgerSupported()
53+
if (!supported) {
54+
setError(
55+
'WebUSB/WebHID is not supported in this browser. ' +
56+
'Please use Chrome or a Chromium-based browser to sign with Ledger.'
57+
)
58+
setSigning(false)
59+
return
60+
}
61+
62+
// If we already have a live stellarApp session from WalletConnect, use it.
63+
// Otherwise, prompt the user to reconnect via the Wallet tab.
64+
const { stellarApp, publicKey } = getActiveLedgerSession()
65+
if (!stellarApp) {
66+
setError(
67+
'Ledger session not found. Please connect your Ledger in the Wallet tab first, ' +
68+
'then return here to sign.'
69+
)
70+
setSigning(false)
71+
return
72+
}
73+
74+
try {
75+
setLedgerPrompt(true)
76+
const signed = await signXdrWithLedger(
77+
xdr.trim(),
78+
networkPassphrase,
79+
stellarApp,
80+
publicKey || walletPublicKey
81+
)
82+
setSignedXdr(signed)
83+
} catch (err) {
84+
setError(err.message)
85+
} finally {
86+
setLedgerPrompt(false)
87+
setSigning(false)
88+
}
89+
}
90+
4891
const handleCopy = () => {
4992
if (signedXdr) {
5093
navigator.clipboard.writeText(signedXdr)
@@ -85,17 +128,37 @@ export default function TransactionSigner() {
85128
<span style={{ fontFamily: 'var(--font-mono)' }}>
86129
{walletPublicKey?.slice(0, 8)}{walletPublicKey?.slice(-8)}
87130
</span>
131+
<span style={{ marginLeft: 'auto', opacity: 0.7 }}>{walletType}</span>
88132
</div>
89133

134+
{/* Ledger device prompt banner */}
135+
{ledgerPrompt && (
136+
<div style={{
137+
padding: '12px',
138+
background: 'var(--amber-glow, rgba(245,158,11,0.1))',
139+
border: '1px solid var(--amber, #f59e0b)',
140+
borderRadius: 'var(--radius-md)',
141+
fontSize: '12px',
142+
color: 'var(--amber, #f59e0b)',
143+
display: 'flex', alignItems: 'center', gap: '8px',
144+
}}>
145+
<span style={{ fontSize: '18px' }}>🔐</span>
146+
Review and confirm the transaction on your Ledger device…
147+
</div>
148+
)}
149+
90150
{/* XDR input */}
91151
<div>
92-
<label style={{ fontSize: '10px', color: 'var(--text-muted)', letterSpacing: '1px', textTransform: 'uppercase', display: 'block', marginBottom: '6px' }}>
152+
<label style={{
153+
fontSize: '10px', color: 'var(--text-muted)', letterSpacing: '1px',
154+
textTransform: 'uppercase', display: 'block', marginBottom: '6px',
155+
}}>
93156
TRANSACTION XDR
94157
</label>
95158
<textarea
96159
value={xdr}
97160
onChange={(e) => setXdr(e.target.value)}
98-
placeholder="Paste the transaction XDR envelope here..."
161+
placeholder="Paste the unsigned transaction XDR envelope here"
99162
rows={5}
100163
style={{
101164
width: '100%',
@@ -135,7 +198,7 @@ export default function TransactionSigner() {
135198
{signing ? (
136199
<>
137200
<div className="spinner" />
138-
Signing…
201+
{ledgerPrompt ? 'Waiting for Ledger…' : 'Signing…'}
139202
</>
140203
) : (
141204
'Sign Transaction'

src/components/layout/MobileNavigation.jsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import React from 'react'
22
import { useStore } from '../../lib/store'
33

4+
// Mirrors the most-used tabs; full nav is in the sidebar (hamburger menu).
45
const QUICK_NAV = [
5-
{ id: 'overview', label: 'Home', icon: '◈' },
6-
{ id: 'transactions', label: 'Txns', icon: '⇄' },
7-
{ id: 'contracts', label: 'Contracts', icon: '' },
8-
{ id: 'assets', label: 'Assets', icon: '💎' },
9-
{ id: 'settings', label: 'Settings', icon: '⚙' },
6+
{ id: 'overview', label: 'Home', icon: '◈' },
7+
{ id: 'transactions', label: 'Txns', icon: '⇄' },
8+
{ id: 'dex', label: 'DEX', icon: '' },
9+
{ id: 'wallet', label: 'Wallet', icon: '' },
10+
{ id: 'settings', label: 'Settings', icon: '⚙' },
1011
]
1112

1213
export default function MobileNavigation() {

0 commit comments

Comments
 (0)