Skip to content

Commit 8c423a0

Browse files
committed
Add wallet import
1 parent 56d6799 commit 8c423a0

File tree

9 files changed

+848
-205
lines changed

9 files changed

+848
-205
lines changed

docs/MANUAL_IMPORT_FEATURE.md

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# Manual Import Feature
2+
3+
The manual import feature allows users to import wallets by entering either a seed phrase (mnemonic) or a private key directly, along with selecting the appropriate cryptographic curve.
4+
5+
## Features
6+
7+
### 1. Import Methods
8+
- **Seed Phrase**: Import using a 12 or 24-word mnemonic phrase
9+
- **Private Key**: Import using a 64-character hexadecimal private key
10+
11+
### 2. Cryptographic Curve Selection
12+
- **secp256k1**: Standard Bitcoin/Ethereum curve (recommended)
13+
- **P-256**: NIST P-256 curve for specialized networks
14+
15+
### 3. Automatic Curve Detection
16+
When importing via private key, the system can automatically detect the appropriate curve:
17+
- Confidence indicators show detection reliability
18+
- Users can override auto-detection if needed
19+
- Real-time feedback during curve detection
20+
21+
## User Interface
22+
23+
### Import Type Selection
24+
Users can choose between two import methods:
25+
- **Seed Phrase**: For importing from mnemonic words
26+
- **Private Key**: For importing from raw private key
27+
28+
### Input Fields
29+
- **Wallet Name**: Custom name for the imported wallet
30+
- **Seed Phrase/Private Key**: The actual import data
31+
- **Curve Selection**: Choose or auto-detect the cryptographic curve
32+
33+
### Validation
34+
- Real-time validation of input format
35+
- Mnemonic phrase validation using BIP39 standards
36+
- Private key format validation (64 hex characters)
37+
- Clear error messages for invalid inputs
38+
39+
### Visual Feedback
40+
- Loading states during import process
41+
- Color-coded confidence indicators for curve detection
42+
- Clear success/error messaging
43+
44+
## Technical Implementation
45+
46+
### Key Components
47+
1. **ManualImportPage**: Main component handling the import UI
48+
2. **CurveSelector**: Component for curve selection
49+
3. **Curve Detection**: Automatic curve identification
50+
4. **Wallet Generation**: Create wallet objects from import data
51+
52+
### Security Features
53+
- Input sanitization and validation
54+
- Secure key generation using noble-curves
55+
- No storage of sensitive data during import process
56+
57+
### Error Handling
58+
- Comprehensive input validation
59+
- Graceful error recovery
60+
- Clear user feedback for all error states
61+
62+
## Usage Flow
63+
64+
1. **Select Import Method**: Choose between seed phrase or private key
65+
2. **Enter Wallet Name**: Provide a custom name for the wallet
66+
3. **Input Data**: Enter the seed phrase or private key
67+
4. **Select Curve**: Choose curve (or use auto-detection for private key)
68+
5. **Import**: Complete the import process
69+
70+
## Supported Formats
71+
72+
### Seed Phrase
73+
- 12-word mnemonic phrases
74+
- 24-word mnemonic phrases
75+
- Space-separated words
76+
- BIP39 compatible
77+
78+
### Private Key
79+
- 64-character hexadecimal strings
80+
- With or without '0x' prefix
81+
- Both uppercase and lowercase accepted
82+
83+
## Integration
84+
85+
The manual import feature integrates seamlessly with:
86+
- Existing wallet management system
87+
- Asset compatibility checking
88+
- Curve-based asset filtering
89+
- Wallet storage and retrieval

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "quasar",
3-
"version": "5.2.17",
3+
"version": "5.2.18",
44
"description": "Browser Wallet for Stellaris and Stellaris based chains",
55
"license": "MIT",
66
"repository": {

src/components/QRScanner.tsx

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export const QRScanner: React.FC<QRScannerProps> = ({ onScan, onClose, isOpen })
3131
// Not JSON, treat as plain text (probably just an address)
3232
onScan(text);
3333
}
34-
34+
3535
if (codeReader.current) {
3636
codeReader.current.reset();
3737
}
@@ -54,7 +54,7 @@ export const QRScanner: React.FC<QRScannerProps> = ({ onScan, onClose, isOpen })
5454
setError('');
5555

5656
const videoInputDevices = await codeReader.current.listVideoInputDevices();
57-
57+
5858
if (videoInputDevices.length === 0) {
5959
throw new Error('No camera devices found');
6060
}
@@ -85,17 +85,17 @@ export const QRScanner: React.FC<QRScannerProps> = ({ onScan, onClose, isOpen })
8585
const initializeScanner = async () => {
8686
try {
8787
setError('');
88-
88+
8989
// Check for camera permission
9090
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
9191
setHasPermission(true);
92-
92+
9393
// Stop the permission check stream
9494
stream.getTracks().forEach(track => track.stop());
95-
95+
9696
// Initialize the code reader
9797
codeReader.current = new BrowserMultiFormatReader();
98-
98+
9999
startScanning();
100100
} catch (err) {
101101
console.error('Camera permission denied or not available:', err);
@@ -123,17 +123,17 @@ export const QRScanner: React.FC<QRScannerProps> = ({ onScan, onClose, isOpen })
123123
const initializeScanner = async () => {
124124
try {
125125
setError('');
126-
126+
127127
// Check for camera permission
128128
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
129129
setHasPermission(true);
130-
130+
131131
// Stop the permission check stream
132132
stream.getTracks().forEach(track => track.stop());
133-
133+
134134
// Initialize the code reader
135135
codeReader.current = new BrowserMultiFormatReader();
136-
136+
137137
startScanning();
138138
} catch (err) {
139139
console.error('Camera permission denied or not available:', err);

src/components/ReceiveModal.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export const ReceiveModal: React.FC<ReceiveModalProps> = ({ wallet, onClose }) =
3737
const parser = new DOMParser();
3838
const doc = parser.parseFromString(qrElement, 'text/html');
3939
const img = doc.querySelector('img');
40-
40+
4141
if (img) {
4242
img.style.width = '100%';
4343
img.style.height = 'auto';
@@ -54,12 +54,12 @@ export const ReceiveModal: React.FC<ReceiveModalProps> = ({ wallet, onClose }) =
5454
if (wallet.address) {
5555
navigator.clipboard.writeText(wallet.address).then(() => {
5656
setCopied(true);
57-
57+
5858
// Clear any existing timeout
5959
if (copyTimeout.current) {
6060
clearTimeout(copyTimeout.current);
6161
}
62-
62+
6363
// Reset copied state after 2 seconds
6464
copyTimeout.current = setTimeout(() => {
6565
setCopied(false);
@@ -115,7 +115,7 @@ export const ReceiveModal: React.FC<ReceiveModalProps> = ({ wallet, onClose }) =
115115
{formatAddress(wallet.address)}
116116
</span>
117117
</div>
118-
<button
118+
<button
119119
className={`copy-btn ${copied ? 'copied' : ''}`}
120120
onClick={handleCopyAddress}
121121
title="Copy address"

0 commit comments

Comments
 (0)