Skip to content

Conversation

@felipemadero
Copy link
Collaborator

Why this should be merged

This PR adds Ledger hardware wallet integration to the SDK, enabling users to perform secure transaction signing using their Ledger devices for Avalanche transactions. Ledger hardware wallets are widely-used secure key storage devices that provide enterprise-grade key management for blockchain operations by keeping private keys isolated in hardware.

This implementation allows users to:

  • Sign P-Chain, X-Chain, and C-Chain transactions using Ledger devices
  • Integrate seamlessly with existing Avalanche wallet infrastructure
  • Derive addresses using BIP-32/BIP-44 hierarchical deterministic paths
  • Support both Avalanche native addresses and Ethereum-compatible addresses

How this works

The PR adds a new keychain/ledger package that implements both keychain.Keychain and c.EthKeychain interfaces:

  • ledger_device.go - Device implementation that communicates with Ledger hardware via the Avalanche Ledger app
  • ledger_keychain.go - Core keychain implementation that manages address derivation and provides signing capabilities for Avalanche addresses
  • Supports both Avalanche native addresses (P/X chains) and Ethereum addresses (C-Chain)
  • Automatic address derivation using configurable BIP-32 paths (default: m/44'/9000'/0'/0)
  • Comprehensive README with usage examples and complete example program demonstrating all transaction types

The implementation includes:

  • examples/validate-ledger-txs.go - Comprehensive example demonstrating subnet creation, blockchain creation, validator addition, P→C and C→P cross-chain transfers, and C-Chain EVM operations

How this was tested

  • Unit tests
  • Included example program for all P/X/C txs, most for custom local network, some on fuji

Need to be documented in RELEASES.md?

Add comprehensive documentation for the cubesigner keychain package
including setup instructions, usage examples for subnet creation and
cross-chain transfers, and integration details with Avalanche wallet.
Fix import grouping to comply with gci linter rules.
Replace icm-services dependency with local type definitions for
AggregateSignatureRequest and AggregateSignatureResponse to avoid
dependency conflicts with avalanchego/libevm/subnet-evm versions.

This resolves linter errors related to incompatible icm-services
peer package versions.
Updates the generated mock to reference the correct source package path and adds required golang.org/x/tools dependency for mockgen.
Implements a keychain that integrates with Ledger hardware wallets for
secure transaction signing with physical device confirmation.

Features:
- Hardware-secured transaction signing via Ledger device
- Support for P-Chain, X-Chain, and C-Chain operations
- Compatible with keychain.Keychain and c.EthKeychain interfaces
- Automatic key derivation using BIP-44 path (m/44'/9000'/0'/0/n)
- Support for both full transaction signing and hash signing
- Retry logic for transient USB communication errors

The implementation uses github.com/ava-labs/ledger-avalanche-go for
hardware wallet communication.
Adds a tree diagram showing the package file structure and descriptions
of the core files to help users understand the organization.
@felipemadero felipemadero changed the base branch from main to add-cubesigner-keychain October 10, 2025 19:49
This commit introduces automatic chain detection for transactions and refactors the keychain signing interface to simplify usage by removing the need for explicit chain specification.

Key changes:
- Add utils/txs.go with AutoDetectChain function that detects P/X/C chain transactions by attempting to unmarshal with each chain's codec
- Add ChainAlias type and constants (PChainAlias, XChainAlias, CChainAlias, UndefinedAlias) to constants package
- Refactor ledger keychain Sign method to use auto-detection instead of requiring ChainAlias option
- Remove cubesigner keychain implementation and related files
- Update signature aggregator to use icm-services API types
- Fix evm.go import to use subnet-evm predicate package
- Add comprehensive unit tests covering all P-chain, X-chain, and C-chain transaction types
@felipemadero felipemadero changed the base branch from add-cubesigner-keychain to main October 16, 2025 16:19
Update the test mock expectations to account for all BlockNumber() calls
made during SetupProposerVM execution. The implementation calls BlockNumber()
more frequently than initially expected:
- Initial call before the loop in CreateDummyBlocks
- Inside the loop for each block
- During WaitForNewBlock for each block

This fix adds the missing mock expectations while preserving the original
test structure and comments.
Comment on lines +115 to +163
import (
"context"
"fmt"
"log"
"time"

"github.com/ava-labs/avalanche-tooling-sdk-go/keychain/ledger"
"github.com/ava-labs/avalanchego/utils/constants"
"github.com/ava-labs/avalanchego/utils/units"
"github.com/ava-labs/avalanchego/vms/components/avax"
"github.com/ava-labs/avalanchego/vms/secp256k1fx"
"github.com/ava-labs/avalanchego/wallet/subnet/primary"
"github.com/ava-labs/avalanchego/wallet/subnet/primary/common"
)

func main() {
// Connect to Ledger
device, err := ledger.New()
if err != nil {
log.Fatalf("Failed to connect to Ledger: %v", err)
}
defer device.Disconnect()

// Create keychain
kc, err := ledger.NewKeychain(device, 1)
if err != nil {
log.Fatalf("Failed to create keychain: %v", err)
}

// Create wallet
ctx := context.Background()
wallet, err := primary.MakeWallet(
ctx,
primary.FujiAPIURI,
kc,
kc,
primary.WalletConfig{},
)
if err != nil {
log.Fatalf("Failed to create wallet: %v", err)
}

// Get chain and asset IDs
cChainID := wallet.C().Builder().Context().BlockchainID
avaxAssetID := wallet.P().Builder().Context().AVAXAssetID

// Export 0.5 AVAX from P-Chain to C-Chain
exportOwner := &secp256k1fx.OutputOwners{
Threshold: 1,
Copy link
Collaborator

Choose a reason for hiding this comment

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

all the code should go to examples, not read me

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I believe it is ok for a README to also contain examples, as our main readme. But, I will add the code also to examples. Where do you think should go? SDK/examples ? or SDK/keychain/ledger/examples/

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.

3 participants