Skip to content

Conversation

@katspaugh
Copy link
Member

Summary

  • ✨ Full Ledger hardware wallet integration for signing Safe multisig transactions
  • πŸ” EIP-712 signing support for Ledger devices
  • πŸ’° Display wallet balances for both private key and Ledger wallets
  • πŸ”Œ Device detection and connection management
  • πŸ—οΈ Transport layer abstraction for future migration to device-sdk-ts

Features Implemented

Ledger Import Command

  • Interactive wallet import-ledger command
  • Support for multiple derivation paths:
    • Ledger Live (default): m/44'/60'/0'/0/0
    • Legacy MEW: m/44'/60'/0'/0
    • Custom path with validation
  • Device connection detection with timeout
  • Address verification on Ledger screen

Transaction Signing

  • EIP-712 signing using Ledger device
  • Manual Safe transaction struct hash computation
  • Domain separator and message hash display for user verification
  • Proper signature format with v value normalization

Balance Display

  • Parallel balance fetching for multiple wallets
  • Smart number formatting (removes trailing zeros, shows more decimals for small amounts)
  • Balance shown in wallet list and after import
  • Uses default chain (Ethereum mainnet) for balance checks

Wallet Management

  • Extended wallet types with discriminated union (private-key | ledger)
  • Type-safe handling of different wallet types
  • Derivation path stored for Ledger wallets
  • Updated UI to show wallet type indicators (πŸ”‘ for private key, πŸ” for Ledger)

Technical Implementation

New Files

  • src/services/ledger-service.ts - Core Ledger integration with transport interface
  • src/commands/wallet/import-ledger.ts - Interactive Ledger import command
  • src/utils/balance.ts - Balance fetching utilities with parallel RPC calls

Modified Files

  • src/types/wallet.ts - Discriminated union for wallet types
  • src/storage/wallet-store.ts - Ledger wallet storage and retrieval
  • src/services/transaction-service.ts - Ledger signing with proper EIP-712 hash computation
  • src/commands/tx/sign.ts - Ledger signing flow integration
  • src/storage/config-store.ts - Added getDefaultChain() method
  • src/ui/screens/WalletImportSuccessScreen.tsx - Balance display on import
  • src/ui/screens/WalletListScreen.tsx - Balance and type display in list

Dependencies Added

  • @ledgerhq/hw-transport-node-hid - HID transport for USB connection
  • @ledgerhq/hw-app-eth - Ethereum app communication

Usage Example

# Import Ledger wallet
safe wallet import-ledger

# List wallets (shows type and balance)
safe wallet list

# Create transaction
safe tx create

# Sign with Ledger
safe tx sign <transaction-file>
# Domain hash and message hash displayed for verification on device

Testing

  • βœ… 715 tests passing
  • βœ… TypeScript checks passing
  • βœ… ESLint passing (0 errors, warnings only in test files)
  • βœ… Prettier formatting applied
  • βœ… Pre-commit hooks passing

Notes

  • Using LedgerJS (hw-transport-node-hid) instead of device-sdk-ts as Node.js support is not yet stable in the new SDK
  • Transport interface abstraction allows easy migration to device-sdk-ts in the future
  • ESM/CommonJS interop handled for tsx compatibility
  • Safe transaction struct hash computed manually for correct EIP-712 signing

πŸ€– Generated with Claude Code

Implemented full Ledger hardware wallet integration for Safe CLI, enabling users to import and use Ledger devices for signing Safe multisig transactions with EIP-712.

Key Features:
- Import Ledger wallets with flexible derivation paths (Ledger Live, Legacy MEW, Custom)
- Sign Safe transactions using Ledger device with EIP-712
- Display wallet balances for both private key and Ledger wallets
- Device detection and connection management
- Transport layer abstraction for future migration to device-sdk-ts

Technical Implementation:
- Added LedgerService with transport interface pattern
- Extended wallet types to discriminated union (private-key | ledger)
- Integrated @ledgerhq/hw-transport-node-hid and @ledgerhq/hw-app-eth
- Computed Safe transaction struct hash manually for proper EIP-712 signing
- Added balance fetching utilities with parallel RPC calls
- Updated UI components to show wallet types and balances

Files Changed:
- New: src/services/ledger-service.ts - Core Ledger integration
- New: src/commands/wallet/import-ledger.ts - Interactive import command
- New: src/utils/balance.ts - Balance fetching utilities
- Modified: src/types/wallet.ts - Discriminated union for wallet types
- Modified: src/storage/wallet-store.ts - Ledger wallet management
- Modified: src/services/transaction-service.ts - Ledger signing with EIP-712
- Modified: src/commands/tx/sign.ts - Ledger signing flow
- Modified: src/ui/screens/* - Display balances and wallet types
- Modified: README.md - Updated documentation with Ledger instructions

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings October 27, 2025 12:58
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@katspaugh
Copy link
Member Author

Fixed the failing test in CI. The method now behaves as an idempotent operation - it won't throw an error when attempting to remove a non-existent wallet. All 715 tests are now passing locally. βœ…

- Add unit tests for LedgerService (connection, device detection, derivation paths)
- Add unit tests for balance utilities (getBalance, getBalances, formatBalance)
- Add integration tests for Ledger wallet import and management
- Add tests for mixed wallet types (private-key + Ledger)
- All 755 tests passing
Copilot AI review requested due to automatic review settings October 27, 2025 13:10
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 17 out of 18 changed files in this pull request and generated 3 comments.


πŸ’‘ Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +51 to +53
if (num < 0.0001) {
return `${num.toFixed(8)} ${currency}`
}
Copy link

Copilot AI Oct 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The threshold value 0.0001 should be defined as a named constant to improve maintainability and make it easier to adjust if needed.

Copilot uses AI. Check for mistakes.

// For very small amounts, show more decimals
if (num < 0.0001) {
return `${num.toFixed(8)} ${currency}`
Copy link

Copilot AI Oct 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The decimal precision value 8 for very small amounts should be defined as a named constant, similar to how maxDecimals is parameterized for normal amounts.

Copilot uses AI. Check for mistakes.
Comment on lines +256 to +259
const cleanHash = hashStr.startsWith('0x') ? hashStr.slice(2) : hashStr
if (cleanHash.length !== 64) {
throw new SafeCLIError('Invalid hash: must be 32 bytes (64 hex characters)')
}
Copy link

Copilot AI Oct 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The expected hash length 64 (representing 32 bytes) should be defined as a named constant like HASH_HEX_LENGTH to improve code clarity and maintainability.

Copilot uses AI. Check for mistakes.
@katspaugh katspaugh merged commit db707f3 into main Oct 27, 2025
4 checks passed
@katspaugh katspaugh deleted the feat/ledger-hardware-wallet-support branch October 27, 2025 13:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants