-
Notifications
You must be signed in to change notification settings - Fork 2.2k
fix(web): fix two-stage private key input validation to support 0x prefix #917
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…efix
## Problem
Users entering private keys with "0x" prefix failed validation incorrectly:
**Scenario:**
- User inputs: `0x1234...` (34 characters including "0x")
- Expected part1 length: 32 characters
- **Bug**: Code checks `part1.length < 32` → `34 < 32` → ❌ FALSE → "Key too long" error
- **Actual**: Should normalize to `1234...` (32 chars) → ✅ Valid
**Impact:**
- Users cannot paste keys from wallets (most include "0x")
- Confusing UX - valid keys rejected
- Forces manual "0x" removal
## Root Cause
**File**: `web/src/components/TwoStageKeyModal.tsx`
**Lines 77-84** (handleStage1Next):
```typescript
// ❌ Bug: Checks length before normalizing
if (part1.length < expectedPart1Length) {
// Fails for "0x..." inputs
}
```
**Lines 132-143** (handleStage2Complete):
```typescript
// ❌ Bug: Same issue
if (part2.length < expectedPart2Length) {
// Fails for "0x..." inputs
}
// ❌ Bug: Concatenates without normalizing part1
const fullKey = part1 + part2 // May have double "0x"
```
## Solution
### Fix 1: Normalize before validation
**Lines 77-79**:
```typescript
// ✅ Normalize first, then validate
const normalized1 = part1.startsWith('0x') ? part1.slice(2) : part1
if (normalized1.length < expectedPart1Length) {
// Now correctly handles both "0x..." and "1234..."
}
```
**Lines 134-136**:
```typescript
// ✅ Same for part2
const normalized2 = part2.startsWith('0x') ? part2.slice(2) : part2
if (normalized2.length < expectedPart2Length) {
// ...
}
```
### Fix 2: Normalize before concatenation
**Lines 145-147**:
```typescript
// ✅ Remove "0x" from both parts before concatenating
const normalized1 = part1.startsWith('0x') ? part1.slice(2) : part1
const fullKey = normalized1 + normalized2
// Result: Always 64 characters without "0x"
```
## Testing
**Manual Test Cases:**
| Input Type | Part 1 | Part 2 | Before | After |
|------------|--------|--------|--------|-------|
| **No prefix** | `1234...` (32) | `5678...` (32) | ✅ Pass | ✅ Pass |
| **With prefix** | `0x1234...` (34) | `0x5678...` (34) | ❌ Fail | ✅ Pass |
| **Mixed** | `0x1234...` (34) | `5678...` (32) | ❌ Fail | ✅ Pass |
| **Both prefixed** | `0x1234...` (34) | `0x5678...` (34) | ❌ Fail | ✅ Pass |
**Validation consistency:**
- Before: `validatePrivateKeyFormat` normalizes, but input checks don't ❌
- After: Both normalize the same way ✅
## Impact
- ✅ Users can paste keys directly from wallets
- ✅ Supports both `0x1234...` and `1234...` formats
- ✅ Consistent with `validatePrivateKeyFormat` logic
- ✅ Better UX - no manual "0x" removal needed
**Files changed**: 1 frontend file
- web/src/components/TwoStageKeyModal.tsx (+6 lines, -2 lines)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
🤖 Advisory Check ResultsThese are advisory checks to help improve code quality. They won't block your PR from being merged. 📋 PR InformationTitle Format: ✅ Good - Follows Conventional Commits 🔧 Backend ChecksGo Formatting: Files needing formattingGo Vet: ✅ Good Fix locally: go fmt ./... # Format code
go vet ./... # Check for issues
go test ./... # Run tests⚛️ Frontend ChecksBuild & Type Check: ✅ Success Fix locally: cd web
npm run build # Test build (includes type checking)📖 ResourcesQuestions? Feel free to ask in the comments! 🙏 These checks are advisory and won't block your PR from being merged. This comment is automatically generated from pr-checks-run.yml. |
Includes: - PR NoFxAiOS#931: Fix Go formatting for test files - PR NoFxAiOS#800: Data staleness detection (Part 2/3) - already in z-dev-v2 - PR NoFxAiOS#918: Improve UX messages for empty states and error feedback - PR NoFxAiOS#922: Fix missing system_prompt_template field in trader edit - PR NoFxAiOS#921: Remove duplicate exchange config fields (Aster & Hyperliquid) - PR NoFxAiOS#917: Fix two-stage private key input validation (0x prefix support) - PR NoFxAiOS#713: Add backend safety checks for partial_close - PR NoFxAiOS#908: Web Crypto environment check (0xEmberZz) - PR NoFxAiOS#638: Decision limit selector with 5/10/20/50 options (xqliu) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> # Conflicts: # market/data.go # web/src/components/TwoStageKeyModal.tsx
## Problem PR NoFxAiOS#917 fixed the validation logic but missed fixing the button disabled state: **Issue:** - Button enabled/disabled check uses raw input length (includes "0x") - Validation logic uses normalized length (excludes "0x") - **Result:** Button can be enabled with insufficient hex characters **Example scenario:** 1. User inputs: `0x` + 30 hex chars = 32 total chars 2. Button check: `32 < 32` → false → ✅ Button enabled 3. User clicks button 4. Validation: normalized to 30 hex chars → `30 < 32` → ❌ Error 5. Error message: "需要至少 32 個字符" (confusing!) ## Root Cause **Lines 230 & 301**: Button disabled conditions don't normalize input ```typescript // ❌ Before: Checks raw length including "0x" disabled={part1.length < expectedPart1Length || processing} disabled={part2.length < expectedPart2Length} ``` ## Solution Normalize input before checking length in disabled conditions: ```typescript // ✅ After: Normalize before checking disabled={ (part1.startsWith('0x') ? part1.slice(2) : part1).length < expectedPart1Length || processing } disabled={ (part2.startsWith('0x') ? part2.slice(2) : part2).length < expectedPart2Length } ``` ## Testing | Input | Total Length | Normalized Length | Button (Before) | Button (After) | Click Result | |-------|--------------|-------------------|-----------------|----------------|--------------| | `0x` + 30 hex | 32 | 30 | ✅ Enabled (bug) | ❌ Disabled | N/A | | `0x` + 32 hex | 34 | 32 | ✅ Enabled | ✅ Enabled | ✅ Valid | | 32 hex | 32 | 32 | ✅ Enabled | ✅ Enabled | ✅ Valid | ## Impact - ✅ Button state now consistent with validation logic - ✅ Users won't see confusing "need 32 chars" errors when button is enabled - ✅ Better UX - button only enabled when input is truly valid **Related:** Follow-up to PR NoFxAiOS#917 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
## Problem PR NoFxAiOS#917 fixed the validation logic but missed fixing the button disabled state: **Issue:** - Button enabled/disabled check uses raw input length (includes "0x") - Validation logic uses normalized length (excludes "0x") - **Result:** Button can be enabled with insufficient hex characters **Example scenario:** 1. User inputs: `0x` + 30 hex chars = 32 total chars 2. Button check: `32 < 32` → false → ✅ Button enabled 3. User clicks button 4. Validation: normalized to 30 hex chars → `30 < 32` → ❌ Error 5. Error message: "需要至少 32 個字符" (confusing!) ## Root Cause **Lines 230 & 301**: Button disabled conditions don't normalize input ```typescript // ❌ Before: Checks raw length including "0x" disabled={part1.length < expectedPart1Length || processing} disabled={part2.length < expectedPart2Length} ``` ## Solution Normalize input before checking length in disabled conditions: ```typescript // ✅ After: Normalize before checking disabled={ (part1.startsWith('0x') ? part1.slice(2) : part1).length < expectedPart1Length || processing } disabled={ (part2.startsWith('0x') ? part2.slice(2) : part2).length < expectedPart2Length } ``` ## Testing | Input | Total Length | Normalized Length | Button (Before) | Button (After) | Click Result | |-------|--------------|-------------------|-----------------|----------------|--------------| | `0x` + 30 hex | 32 | 30 | ✅ Enabled (bug) | ❌ Disabled | N/A | | `0x` + 32 hex | 34 | 32 | ✅ Enabled | ✅ Enabled | ✅ Valid | | 32 hex | 32 | 32 | ✅ Enabled | ✅ Enabled | ✅ Valid | ## Impact - ✅ Button state now consistent with validation logic - ✅ Users won't see confusing "need 32 chars" errors when button is enabled - ✅ Better UX - button only enabled when input is truly valid **Related:** Follow-up to PR NoFxAiOS#917 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
## Problem PR #917 fixed the validation logic but missed fixing the button disabled state: **Issue:** - Button enabled/disabled check uses raw input length (includes "0x") - Validation logic uses normalized length (excludes "0x") - **Result:** Button can be enabled with insufficient hex characters **Example scenario:** 1. User inputs: `0x` + 30 hex chars = 32 total chars 2. Button check: `32 < 32` → false → ✅ Button enabled 3. User clicks button 4. Validation: normalized to 30 hex chars → `30 < 32` → ❌ Error 5. Error message: "需要至少 32 個字符" (confusing!) ## Root Cause **Lines 230 & 301**: Button disabled conditions don't normalize input ```typescript // ❌ Before: Checks raw length including "0x" disabled={part1.length < expectedPart1Length || processing} disabled={part2.length < expectedPart2Length} ``` ## Solution Normalize input before checking length in disabled conditions: ```typescript // ✅ After: Normalize before checking disabled={ (part1.startsWith('0x') ? part1.slice(2) : part1).length < expectedPart1Length || processing } disabled={ (part2.startsWith('0x') ? part2.slice(2) : part2).length < expectedPart2Length } ``` ## Testing | Input | Total Length | Normalized Length | Button (Before) | Button (After) | Click Result | |-------|--------------|-------------------|-----------------|----------------|--------------| | `0x` + 30 hex | 32 | 30 | ✅ Enabled (bug) | ❌ Disabled | N/A | | `0x` + 32 hex | 34 | 32 | ✅ Enabled | ✅ Enabled | ✅ Valid | | 32 hex | 32 | 32 | ✅ Enabled | ✅ Enabled | ✅ Valid | ## Impact - ✅ Button state now consistent with validation logic - ✅ Users won't see confusing "need 32 chars" errors when button is enabled - ✅ Better UX - button only enabled when input is truly valid **Related:** Follow-up to PR #917 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: the-dev-z <the-dev-z@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com>
…aracter requirements
## Problem
Users were confused about the character count requirements for the two-stage private key input:
**Issues:**
- Description said "32 characters" without specifying hexadecimal
- No mention of whether "0x" prefix should be included
- Users didn't know if they should input 32 total chars or 32 hex chars
- Led to confusion: "I have 32 characters but it says incomplete!"
**User Feedback:**
- "所以32是不包含0x?" (Does 32 include 0x or not?)
- Users need clear guidance on input format
## Solution
Added help text below each input field to clarify:
**English:**
- "💡 Accepts 32 hex characters with or without "0x" prefix"
**Chinese:**
- "💡 可包含或省略 "0x" 前綴(32 位 hex 字符)"
**Additional improvements:**
- Updated descriptions to say "hex characters" instead of just "characters"
- Help text appears in small gray font below input field
- Non-intrusive but clear guidance
## Changes
### 1. translations.ts (Lines 823-825, 1616-1617)
**English:**
```typescript
stage1Description: 'Enter the first {length} hex characters of your private key',
helpText: '💡 Accepts {length} hex characters with or without "0x" prefix',
```
**Chinese:**
```typescript
stage1Description: '请输入私钥的前 {length} 位十六进制字符',
helpText: '💡 可包含或省略 "0x" 前綴({length} 位 hex 字符)',
```
### 2. TwoStageKeyModal.tsx (Lines 223-227, 302-306)
Added help text below input fields:
```tsx
<div className="text-gray-400 text-xs mt-1">
{t('twoStageKey.helpText', language, {
length: expectedPart1Length,
})}
</div>
```
## UI Impact
**Before:**
```
第一部分 (32 位字符)
[輸入框]
```
**After:**
```
第一部分 (32 位字符)
[輸入框]
💡 可包含或省略 "0x" 前綴(32 位 hex 字符)
```
## Benefits
- ✅ Clear guidance: Users know they need 32 **hex** characters
- ✅ Format flexibility: Both "0x1234..." and "1234..." are valid
- ✅ Reduces confusion: No more "why is my 32-char input rejected?"
- ✅ Better UX: Proactive help instead of error messages
- ✅ Bilingual support: Both EN and ZH have clear explanations
## Testing
**Manual Test Cases:**
| Input Format | Display | Expected Behavior |
|--------------|---------|-------------------|
| User sees Stage 1 | Help text shown | ✅ "💡 Accepts 32 hex characters..." |
| User sees Stage 2 | Help text shown | ✅ "💡 Accepts 32 hex characters..." |
| Input `0x` + 32 hex | Button enabled | ✅ Accepted (34 total chars) |
| Input 32 hex (no prefix) | Button enabled | ✅ Accepted (32 total chars) |
| Input `0x` + 30 hex | Button disabled | ✅ Rejected (only 30 hex chars) |
**Related:** Follow-up UX improvement for PR NoFxAiOS#917 and PR NoFxAiOS#937
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
…aracter requirements
## Problem
Users were confused about the character count requirements for the two-stage private key input:
**Issues:**
- Description said "32 characters" without specifying hexadecimal
- No mention of whether "0x" prefix should be included
- Users didn't know if they should input 32 total chars or 32 hex chars
- Led to confusion: "I have 32 characters but it says incomplete!"
**User Feedback:**
- "所以32是不包含0x?" (Does 32 include 0x or not?)
- Users need clear guidance on input format
## Solution
Added help text below each input field to clarify:
**English:**
- "💡 Accepts 32 hex characters with or without "0x" prefix"
**Chinese:**
- "💡 可包含或省略 "0x" 前綴(32 位 hex 字符)"
**Additional improvements:**
- Updated descriptions to say "hex characters" instead of just "characters"
- Help text appears in small gray font below input field
- Non-intrusive but clear guidance
## Changes
### 1. translations.ts (Lines 823-825, 1616-1617)
**English:**
```typescript
stage1Description: 'Enter the first {length} hex characters of your private key',
helpText: '💡 Accepts {length} hex characters with or without "0x" prefix',
```
**Chinese:**
```typescript
stage1Description: '请输入私钥的前 {length} 位十六进制字符',
helpText: '💡 可包含或省略 "0x" 前綴({length} 位 hex 字符)',
```
### 2. TwoStageKeyModal.tsx (Lines 223-227, 302-306)
Added help text below input fields:
```tsx
<div className="text-gray-400 text-xs mt-1">
{t('twoStageKey.helpText', language, {
length: expectedPart1Length,
})}
</div>
```
## UI Impact
**Before:**
```
第一部分 (32 位字符)
[輸入框]
```
**After:**
```
第一部分 (32 位字符)
[輸入框]
💡 可包含或省略 "0x" 前綴(32 位 hex 字符)
```
## Benefits
- ✅ Clear guidance: Users know they need 32 **hex** characters
- ✅ Format flexibility: Both "0x1234..." and "1234..." are valid
- ✅ Reduces confusion: No more "why is my 32-char input rejected?"
- ✅ Better UX: Proactive help instead of error messages
- ✅ Bilingual support: Both EN and ZH have clear explanations
## Testing
**Manual Test Cases:**
| Input Format | Display | Expected Behavior |
|--------------|---------|-------------------|
| User sees Stage 1 | Help text shown | ✅ "💡 Accepts 32 hex characters..." |
| User sees Stage 2 | Help text shown | ✅ "💡 Accepts 32 hex characters..." |
| Input `0x` + 32 hex | Button enabled | ✅ Accepted (34 total chars) |
| Input 32 hex (no prefix) | Button enabled | ✅ Accepted (32 total chars) |
| Input `0x` + 30 hex | Button disabled | ✅ Rejected (only 30 hex chars) |
**Related:** Follow-up UX improvement for PR NoFxAiOS#917 and PR NoFxAiOS#937
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
…efix (NoFxAiOS#917) ## Problem Users entering private keys with "0x" prefix failed validation incorrectly: **Scenario:** - User inputs: `0x1234...` (34 characters including "0x") - Expected part1 length: 32 characters - **Bug**: Code checks `part1.length < 32` → `34 < 32` → ❌ FALSE → "Key too long" error - **Actual**: Should normalize to `1234...` (32 chars) → ✅ Valid **Impact:** - Users cannot paste keys from wallets (most include "0x") - Confusing UX - valid keys rejected - Forces manual "0x" removal ## Root Cause **File**: `web/src/components/TwoStageKeyModal.tsx` **Lines 77-84** (handleStage1Next): ```typescript // ❌ Bug: Checks length before normalizing if (part1.length < expectedPart1Length) { // Fails for "0x..." inputs } ``` **Lines 132-143** (handleStage2Complete): ```typescript // ❌ Bug: Same issue if (part2.length < expectedPart2Length) { // Fails for "0x..." inputs } // ❌ Bug: Concatenates without normalizing part1 const fullKey = part1 + part2 // May have double "0x" ``` ## Solution ### Fix 1: Normalize before validation **Lines 77-79**: ```typescript // ✅ Normalize first, then validate const normalized1 = part1.startsWith('0x') ? part1.slice(2) : part1 if (normalized1.length < expectedPart1Length) { // Now correctly handles both "0x..." and "1234..." } ``` **Lines 134-136**: ```typescript // ✅ Same for part2 const normalized2 = part2.startsWith('0x') ? part2.slice(2) : part2 if (normalized2.length < expectedPart2Length) { // ... } ``` ### Fix 2: Normalize before concatenation **Lines 145-147**: ```typescript // ✅ Remove "0x" from both parts before concatenating const normalized1 = part1.startsWith('0x') ? part1.slice(2) : part1 const fullKey = normalized1 + normalized2 // Result: Always 64 characters without "0x" ``` ## Testing **Manual Test Cases:** | Input Type | Part 1 | Part 2 | Before | After | |------------|--------|--------|--------|-------| | **No prefix** | `1234...` (32) | `5678...` (32) | ✅ Pass | ✅ Pass | | **With prefix** | `0x1234...` (34) | `0x5678...` (34) | ❌ Fail | ✅ Pass | | **Mixed** | `0x1234...` (34) | `5678...` (32) | ❌ Fail | ✅ Pass | | **Both prefixed** | `0x1234...` (34) | `0x5678...` (34) | ❌ Fail | ✅ Pass | **Validation consistency:** - Before: `validatePrivateKeyFormat` normalizes, but input checks don't ❌ - After: Both normalize the same way ✅ ## Impact - ✅ Users can paste keys directly from wallets - ✅ Supports both `0x1234...` and `1234...` formats - ✅ Consistent with `validatePrivateKeyFormat` logic - ✅ Better UX - no manual "0x" removal needed **Files changed**: 1 frontend file - web/src/components/TwoStageKeyModal.tsx (+6 lines, -2 lines) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: the-dev-z <the-dev-z@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com>
…AiOS#937) ## Problem PR NoFxAiOS#917 fixed the validation logic but missed fixing the button disabled state: **Issue:** - Button enabled/disabled check uses raw input length (includes "0x") - Validation logic uses normalized length (excludes "0x") - **Result:** Button can be enabled with insufficient hex characters **Example scenario:** 1. User inputs: `0x` + 30 hex chars = 32 total chars 2. Button check: `32 < 32` → false → ✅ Button enabled 3. User clicks button 4. Validation: normalized to 30 hex chars → `30 < 32` → ❌ Error 5. Error message: "需要至少 32 個字符" (confusing!) ## Root Cause **Lines 230 & 301**: Button disabled conditions don't normalize input ```typescript // ❌ Before: Checks raw length including "0x" disabled={part1.length < expectedPart1Length || processing} disabled={part2.length < expectedPart2Length} ``` ## Solution Normalize input before checking length in disabled conditions: ```typescript // ✅ After: Normalize before checking disabled={ (part1.startsWith('0x') ? part1.slice(2) : part1).length < expectedPart1Length || processing } disabled={ (part2.startsWith('0x') ? part2.slice(2) : part2).length < expectedPart2Length } ``` ## Testing | Input | Total Length | Normalized Length | Button (Before) | Button (After) | Click Result | |-------|--------------|-------------------|-----------------|----------------|--------------| | `0x` + 30 hex | 32 | 30 | ✅ Enabled (bug) | ❌ Disabled | N/A | | `0x` + 32 hex | 34 | 32 | ✅ Enabled | ✅ Enabled | ✅ Valid | | 32 hex | 32 | 32 | ✅ Enabled | ✅ Enabled | ✅ Valid | ## Impact - ✅ Button state now consistent with validation logic - ✅ Users won't see confusing "need 32 chars" errors when button is enabled - ✅ Better UX - button only enabled when input is truly valid **Related:** Follow-up to PR NoFxAiOS#917 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: the-dev-z <the-dev-z@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com>
…efix (#917) ## Problem Users entering private keys with "0x" prefix failed validation incorrectly: **Scenario:** - User inputs: `0x1234...` (34 characters including "0x") - Expected part1 length: 32 characters - **Bug**: Code checks `part1.length < 32` → `34 < 32` → ❌ FALSE → "Key too long" error - **Actual**: Should normalize to `1234...` (32 chars) → ✅ Valid **Impact:** - Users cannot paste keys from wallets (most include "0x") - Confusing UX - valid keys rejected - Forces manual "0x" removal ## Root Cause **File**: `web/src/components/TwoStageKeyModal.tsx` **Lines 77-84** (handleStage1Next): ```typescript // ❌ Bug: Checks length before normalizing if (part1.length < expectedPart1Length) { // Fails for "0x..." inputs } ``` **Lines 132-143** (handleStage2Complete): ```typescript // ❌ Bug: Same issue if (part2.length < expectedPart2Length) { // Fails for "0x..." inputs } // ❌ Bug: Concatenates without normalizing part1 const fullKey = part1 + part2 // May have double "0x" ``` ## Solution ### Fix 1: Normalize before validation **Lines 77-79**: ```typescript // ✅ Normalize first, then validate const normalized1 = part1.startsWith('0x') ? part1.slice(2) : part1 if (normalized1.length < expectedPart1Length) { // Now correctly handles both "0x..." and "1234..." } ``` **Lines 134-136**: ```typescript // ✅ Same for part2 const normalized2 = part2.startsWith('0x') ? part2.slice(2) : part2 if (normalized2.length < expectedPart2Length) { // ... } ``` ### Fix 2: Normalize before concatenation **Lines 145-147**: ```typescript // ✅ Remove "0x" from both parts before concatenating const normalized1 = part1.startsWith('0x') ? part1.slice(2) : part1 const fullKey = normalized1 + normalized2 // Result: Always 64 characters without "0x" ``` ## Testing **Manual Test Cases:** | Input Type | Part 1 | Part 2 | Before | After | |------------|--------|--------|--------|-------| | **No prefix** | `1234...` (32) | `5678...` (32) | ✅ Pass | ✅ Pass | | **With prefix** | `0x1234...` (34) | `0x5678...` (34) | ❌ Fail | ✅ Pass | | **Mixed** | `0x1234...` (34) | `5678...` (32) | ❌ Fail | ✅ Pass | | **Both prefixed** | `0x1234...` (34) | `0x5678...` (34) | ❌ Fail | ✅ Pass | **Validation consistency:** - Before: `validatePrivateKeyFormat` normalizes, but input checks don't ❌ - After: Both normalize the same way ✅ ## Impact - ✅ Users can paste keys directly from wallets - ✅ Supports both `0x1234...` and `1234...` formats - ✅ Consistent with `validatePrivateKeyFormat` logic - ✅ Better UX - no manual "0x" removal needed **Files changed**: 1 frontend file - web/src/components/TwoStageKeyModal.tsx (+6 lines, -2 lines) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: the-dev-z <the-dev-z@users.noreply.github.com> Co-authored-by: tinkle-community <tinklefund@gmail.com>
## Problem PR #917 fixed the validation logic but missed fixing the button disabled state: **Issue:** - Button enabled/disabled check uses raw input length (includes "0x") - Validation logic uses normalized length (excludes "0x") - **Result:** Button can be enabled with insufficient hex characters **Example scenario:** 1. User inputs: `0x` + 30 hex chars = 32 total chars 2. Button check: `32 < 32` → false → ✅ Button enabled 3. User clicks button 4. Validation: normalized to 30 hex chars → `30 < 32` → ❌ Error 5. Error message: "需要至少 32 個字符" (confusing!) ## Root Cause **Lines 230 & 301**: Button disabled conditions don't normalize input ```typescript // ❌ Before: Checks raw length including "0x" disabled={part1.length < expectedPart1Length || processing} disabled={part2.length < expectedPart2Length} ``` ## Solution Normalize input before checking length in disabled conditions: ```typescript // ✅ After: Normalize before checking disabled={ (part1.startsWith('0x') ? part1.slice(2) : part1).length < expectedPart1Length || processing } disabled={ (part2.startsWith('0x') ? part2.slice(2) : part2).length < expectedPart2Length } ``` ## Testing | Input | Total Length | Normalized Length | Button (Before) | Button (After) | Click Result | |-------|--------------|-------------------|-----------------|----------------|--------------| | `0x` + 30 hex | 32 | 30 | ✅ Enabled (bug) | ❌ Disabled | N/A | | `0x` + 32 hex | 34 | 32 | ✅ Enabled | ✅ Enabled | ✅ Valid | | 32 hex | 32 | 32 | ✅ Enabled | ✅ Enabled | ✅ Valid | ## Impact - ✅ Button state now consistent with validation logic - ✅ Users won't see confusing "need 32 chars" errors when button is enabled - ✅ Better UX - button only enabled when input is truly valid **Related:** Follow-up to PR #917 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: the-dev-z <the-dev-z@users.noreply.github.com> Co-authored-by: tinkle-community <tinklefund@gmail.com>
…efix (#917) ## Problem Users entering private keys with "0x" prefix failed validation incorrectly: **Scenario:** - User inputs: `0x1234...` (34 characters including "0x") - Expected part1 length: 32 characters - **Bug**: Code checks `part1.length < 32` → `34 < 32` → ❌ FALSE → "Key too long" error - **Actual**: Should normalize to `1234...` (32 chars) → ✅ Valid **Impact:** - Users cannot paste keys from wallets (most include "0x") - Confusing UX - valid keys rejected - Forces manual "0x" removal ## Root Cause **File**: `web/src/components/TwoStageKeyModal.tsx` **Lines 77-84** (handleStage1Next): ```typescript // ❌ Bug: Checks length before normalizing if (part1.length < expectedPart1Length) { // Fails for "0x..." inputs } ``` **Lines 132-143** (handleStage2Complete): ```typescript // ❌ Bug: Same issue if (part2.length < expectedPart2Length) { // Fails for "0x..." inputs } // ❌ Bug: Concatenates without normalizing part1 const fullKey = part1 + part2 // May have double "0x" ``` ## Solution ### Fix 1: Normalize before validation **Lines 77-79**: ```typescript // ✅ Normalize first, then validate const normalized1 = part1.startsWith('0x') ? part1.slice(2) : part1 if (normalized1.length < expectedPart1Length) { // Now correctly handles both "0x..." and "1234..." } ``` **Lines 134-136**: ```typescript // ✅ Same for part2 const normalized2 = part2.startsWith('0x') ? part2.slice(2) : part2 if (normalized2.length < expectedPart2Length) { // ... } ``` ### Fix 2: Normalize before concatenation **Lines 145-147**: ```typescript // ✅ Remove "0x" from both parts before concatenating const normalized1 = part1.startsWith('0x') ? part1.slice(2) : part1 const fullKey = normalized1 + normalized2 // Result: Always 64 characters without "0x" ``` ## Testing **Manual Test Cases:** | Input Type | Part 1 | Part 2 | Before | After | |------------|--------|--------|--------|-------| | **No prefix** | `1234...` (32) | `5678...` (32) | ✅ Pass | ✅ Pass | | **With prefix** | `0x1234...` (34) | `0x5678...` (34) | ❌ Fail | ✅ Pass | | **Mixed** | `0x1234...` (34) | `5678...` (32) | ❌ Fail | ✅ Pass | | **Both prefixed** | `0x1234...` (34) | `0x5678...` (34) | ❌ Fail | ✅ Pass | **Validation consistency:** - Before: `validatePrivateKeyFormat` normalizes, but input checks don't ❌ - After: Both normalize the same way ✅ ## Impact - ✅ Users can paste keys directly from wallets - ✅ Supports both `0x1234...` and `1234...` formats - ✅ Consistent with `validatePrivateKeyFormat` logic - ✅ Better UX - no manual "0x" removal needed **Files changed**: 1 frontend file - web/src/components/TwoStageKeyModal.tsx (+6 lines, -2 lines) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: the-dev-z <the-dev-z@users.noreply.github.com> Co-authored-by: tinkle-community <tinklefund@gmail.com>
## Problem PR #917 fixed the validation logic but missed fixing the button disabled state: **Issue:** - Button enabled/disabled check uses raw input length (includes "0x") - Validation logic uses normalized length (excludes "0x") - **Result:** Button can be enabled with insufficient hex characters **Example scenario:** 1. User inputs: `0x` + 30 hex chars = 32 total chars 2. Button check: `32 < 32` → false → ✅ Button enabled 3. User clicks button 4. Validation: normalized to 30 hex chars → `30 < 32` → ❌ Error 5. Error message: "需要至少 32 個字符" (confusing!) ## Root Cause **Lines 230 & 301**: Button disabled conditions don't normalize input ```typescript // ❌ Before: Checks raw length including "0x" disabled={part1.length < expectedPart1Length || processing} disabled={part2.length < expectedPart2Length} ``` ## Solution Normalize input before checking length in disabled conditions: ```typescript // ✅ After: Normalize before checking disabled={ (part1.startsWith('0x') ? part1.slice(2) : part1).length < expectedPart1Length || processing } disabled={ (part2.startsWith('0x') ? part2.slice(2) : part2).length < expectedPart2Length } ``` ## Testing | Input | Total Length | Normalized Length | Button (Before) | Button (After) | Click Result | |-------|--------------|-------------------|-----------------|----------------|--------------| | `0x` + 30 hex | 32 | 30 | ✅ Enabled (bug) | ❌ Disabled | N/A | | `0x` + 32 hex | 34 | 32 | ✅ Enabled | ✅ Enabled | ✅ Valid | | 32 hex | 32 | 32 | ✅ Enabled | ✅ Enabled | ✅ Valid | ## Impact - ✅ Button state now consistent with validation logic - ✅ Users won't see confusing "need 32 chars" errors when button is enabled - ✅ Better UX - button only enabled when input is truly valid **Related:** Follow-up to PR #917 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: the-dev-z <the-dev-z@users.noreply.github.com> Co-authored-by: tinkle-community <tinklefund@gmail.com>
…efix (#917) ## Problem Users entering private keys with "0x" prefix failed validation incorrectly: **Scenario:** - User inputs: `0x1234...` (34 characters including "0x") - Expected part1 length: 32 characters - **Bug**: Code checks `part1.length < 32` → `34 < 32` → ❌ FALSE → "Key too long" error - **Actual**: Should normalize to `1234...` (32 chars) → ✅ Valid **Impact:** - Users cannot paste keys from wallets (most include "0x") - Confusing UX - valid keys rejected - Forces manual "0x" removal ## Root Cause **File**: `web/src/components/TwoStageKeyModal.tsx` **Lines 77-84** (handleStage1Next): ```typescript // ❌ Bug: Checks length before normalizing if (part1.length < expectedPart1Length) { // Fails for "0x..." inputs } ``` **Lines 132-143** (handleStage2Complete): ```typescript // ❌ Bug: Same issue if (part2.length < expectedPart2Length) { // Fails for "0x..." inputs } // ❌ Bug: Concatenates without normalizing part1 const fullKey = part1 + part2 // May have double "0x" ``` ## Solution ### Fix 1: Normalize before validation **Lines 77-79**: ```typescript // ✅ Normalize first, then validate const normalized1 = part1.startsWith('0x') ? part1.slice(2) : part1 if (normalized1.length < expectedPart1Length) { // Now correctly handles both "0x..." and "1234..." } ``` **Lines 134-136**: ```typescript // ✅ Same for part2 const normalized2 = part2.startsWith('0x') ? part2.slice(2) : part2 if (normalized2.length < expectedPart2Length) { // ... } ``` ### Fix 2: Normalize before concatenation **Lines 145-147**: ```typescript // ✅ Remove "0x" from both parts before concatenating const normalized1 = part1.startsWith('0x') ? part1.slice(2) : part1 const fullKey = normalized1 + normalized2 // Result: Always 64 characters without "0x" ``` ## Testing **Manual Test Cases:** | Input Type | Part 1 | Part 2 | Before | After | |------------|--------|--------|--------|-------| | **No prefix** | `1234...` (32) | `5678...` (32) | ✅ Pass | ✅ Pass | | **With prefix** | `0x1234...` (34) | `0x5678...` (34) | ❌ Fail | ✅ Pass | | **Mixed** | `0x1234...` (34) | `5678...` (32) | ❌ Fail | ✅ Pass | | **Both prefixed** | `0x1234...` (34) | `0x5678...` (34) | ❌ Fail | ✅ Pass | **Validation consistency:** - Before: `validatePrivateKeyFormat` normalizes, but input checks don't ❌ - After: Both normalize the same way ✅ ## Impact - ✅ Users can paste keys directly from wallets - ✅ Supports both `0x1234...` and `1234...` formats - ✅ Consistent with `validatePrivateKeyFormat` logic - ✅ Better UX - no manual "0x" removal needed **Files changed**: 1 frontend file - web/src/components/TwoStageKeyModal.tsx (+6 lines, -2 lines) Co-authored-by: the-dev-z <the-dev-z@users.noreply.github.com> Co-authored-by: tinkle-community <tinklefund@gmail.com>
## Problem PR #917 fixed the validation logic but missed fixing the button disabled state: **Issue:** - Button enabled/disabled check uses raw input length (includes "0x") - Validation logic uses normalized length (excludes "0x") - **Result:** Button can be enabled with insufficient hex characters **Example scenario:** 1. User inputs: `0x` + 30 hex chars = 32 total chars 2. Button check: `32 < 32` → false → ✅ Button enabled 3. User clicks button 4. Validation: normalized to 30 hex chars → `30 < 32` → ❌ Error 5. Error message: "需要至少 32 個字符" (confusing!) ## Root Cause **Lines 230 & 301**: Button disabled conditions don't normalize input ```typescript // ❌ Before: Checks raw length including "0x" disabled={part1.length < expectedPart1Length || processing} disabled={part2.length < expectedPart2Length} ``` ## Solution Normalize input before checking length in disabled conditions: ```typescript // ✅ After: Normalize before checking disabled={ (part1.startsWith('0x') ? part1.slice(2) : part1).length < expectedPart1Length || processing } disabled={ (part2.startsWith('0x') ? part2.slice(2) : part2).length < expectedPart2Length } ``` ## Testing | Input | Total Length | Normalized Length | Button (Before) | Button (After) | Click Result | |-------|--------------|-------------------|-----------------|----------------|--------------| | `0x` + 30 hex | 32 | 30 | ✅ Enabled (bug) | ❌ Disabled | N/A | | `0x` + 32 hex | 34 | 32 | ✅ Enabled | ✅ Enabled | ✅ Valid | | 32 hex | 32 | 32 | ✅ Enabled | ✅ Enabled | ✅ Valid | ## Impact - ✅ Button state now consistent with validation logic - ✅ Users won't see confusing "need 32 chars" errors when button is enabled - ✅ Better UX - button only enabled when input is truly valid **Related:** Follow-up to PR #917 Co-authored-by: the-dev-z <the-dev-z@users.noreply.github.com> Co-authored-by: tinkle-community <tinklefund@gmail.com>
Pull Request - Frontend | 前端 PR
📝 Description | 描述
English:
This PR fixes a validation bug in the two-stage private key input modal that rejected valid keys with "0x" prefix:
Problem:
0x1234...) got "incomplete key" errorsSolution:
0x1234...and1234...validatePrivateKeyFormatlogic中文:
本 PR 修復了兩階段私鑰輸入模態框的驗證 bug,該 bug 錯誤地拒絕了帶有 "0x" 前綴的有效私鑰:
問題:
0x1234...)時收到"私鑰不完整"錯誤解決方案:
0x1234...和1234...validatePrivateKeyFormat邏輯保持一致🎯 Type of Change | 变更类型
🔗 Related Issues | 相关 Issue
Fixes:
Improves:
📋 Changes Made | 具体变更
Bug Details
File:
web/src/components/TwoStageKeyModal.tsxBefore (❌ Wrong order):
After (✅ Correct order):
Changes Summary
1. Fix handleStage1Next() validation (lines 77-79):
2. Fix handleStage2Complete() validation (lines 134-136):
3. Fix key concatenation (lines 145-147):
🧪 Testing | 测试
Test Cases | 测试用例
1234...(32)5678...(32)0x1234...(34)0x5678...(34)0x1234...(34)5678...(32)0x1234...(34)Validation Consistency
Before:
handleStage1Next: ❌ Does NOT normalize before checkinghandleStage2Complete: ❌ Does NOT normalize before checkingvalidatePrivateKeyFormat: ✅ Normalizes correctlyAfter:
handleStage1Next: ✅ Normalizes before checkinghandleStage2Complete: ✅ Normalizes before checkingvalidatePrivateKeyFormat: ✅ Normalizes correctlyManual Testing | 手动测试
validatePrivateKeyFormat→ ✅ Passes📊 Impact Analysis | 影响分析
User Experience
Before:
0x1234567890abcdef...After:
0x1234567890abcdef...Consistency
validatePrivateKeyFormathandleStage1Next(before)handleStage1Next(after)handleStage2Complete(before)handleStage2Complete(after)Result: All validation paths now consistent ✅
✅ Checklist | 检查清单
Code Quality | 代码质量
Testing | 测试
Documentation | 文档
Git
devbranch | 已 rebase 到最新dev分支💡 Additional Notes | 补充说明
Why this matters
Security consideration:
Code simplicity:
startsWith('0x') ? slice(2) : valueWhy "0x" prefix exists
The "0x" prefix is a standard in Ethereum and many blockchains:
Supporting it improves compatibility.
By submitting this PR, I confirm | 提交此 PR,我确认:
🌟 Thank you for reviewing! | 感谢审阅!
This PR improves wallet integration and user experience.