Ledger Hardware Wallet common interface of the communication layer
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Methods for configuring transport behavior, timeouts, and security settings. This includes scramble key management for app-specific encryption, timeout configuration for communication reliability, and debug settings.
Sets the encryption key used for secure communication with specific Ledger applications. Each app typically has its own scramble key for security isolation.
/**
* Set the "scramble key" for the next exchanges with the device
* Each App can have a different scramble key and they internally set it at instantiation
* @param key The scramble key string
*/
setScrambleKey(key: string): void;Usage Example:
import Transport from "@ledgerhq/hw-transport";
const transport = await MyTransport.create();
// Set scramble key for Bitcoin app
transport.setScrambleKey("BTC");
// Now all APDU exchanges will use the Bitcoin scramble key
const response = await transport.send(0xE0, 0x40, 0x00, 0x00);
// Switch to Ethereum app
transport.setScrambleKey("ETH");
const ethResponse = await transport.send(0xE0, 0x02, 0x00, 0x00);Common Scramble Keys:
"BTC" - Bitcoin and Bitcoin-based cryptocurrencies"ETH" - Ethereum and ERC-20 tokens"XRP" - Ripple"ADA" - Cardano"" (empty string) - No scrambling (for system commands)Controls how long to wait for APDU command responses before timing out. This helps prevent hanging operations when devices become unresponsive.
/**
* Set a timeout (in milliseconds) for the exchange call
* Only some transport implementations might implement it (e.g. U2F)
* @param exchangeTimeout Timeout in milliseconds
*/
setExchangeTimeout(exchangeTimeout: number): void;Usage Example:
const transport = await MyTransport.create();
// Set 10 second timeout for slow operations
transport.setExchangeTimeout(10000);
// This command will timeout after 10 seconds if no response
try {
const response = await transport.send(0xE0, 0x04, 0x00, 0x00, transactionData);
} catch (error) {
// Handle timeout or other errors
console.error("Command timed out or failed:", error);
}
// Reset to default timeout (30 seconds)
transport.setExchangeTimeout(30000);Configures how long to wait before emitting "unresponsive" events when a device stops responding during operations.
/**
* Define the delay before emitting "unresponsive" on an exchange that does not respond
* @param unresponsiveTimeout Timeout in milliseconds
*/
setExchangeUnresponsiveTimeout(unresponsiveTimeout: number): void;Usage Example:
const transport = await MyTransport.create();
// Monitor for unresponsiveness after 5 seconds
transport.setExchangeUnresponsiveTimeout(5000);
// Listen for unresponsive events
transport.on("unresponsive", () => {
console.warn("Device appears unresponsive - please check connection");
// Show user feedback about unresponsive device
});
transport.on("responsive", () => {
console.log("Device is responding again");
// Hide unresponsive warning
});
// Start a potentially slow operation
const signature = await transport.send(0xE0, 0x04, 0x01, 0x00, largeTransaction);Legacy method for enabling debug logging. This method is deprecated and no longer emits logs.
/**
* Enable or not logs of the binary exchange
* @deprecated Use @ledgerhq/logs instead. No logs are emitted in this anymore.
*/
setDebugMode(): void;Migration Example:
// Old approach (deprecated)
transport.setDebugMode();
// New approach - use @ledgerhq/logs
import { setDebugMode } from "@ledgerhq/logs";
setDebugMode(true);The Transport class provides default timeout configurations:
class Transport<Descriptor> {
/** Default timeout for APDU exchanges (30 seconds) */
exchangeTimeout: number = 30000;
/** Default timeout for unresponsive detection (15 seconds) */
unresponsiveTimeout: number = 15000;
/** Connected device model information */
deviceModel: DeviceModel | null = null;
/** Internal lock to prevent concurrent app API operations */
_appAPIlock: string | null = null;
/** Promise tracking current exchange operation for race condition prevention */
exchangeBusyPromise: Promise<void> | null = null;
}const transport = await MyTransport.create();
console.log("Current exchange timeout:", transport.exchangeTimeout);
console.log("Current unresponsive timeout:", transport.unresponsiveTimeout);
console.log("Connected device:", transport.deviceModel?.productName);
// Modify timeouts
transport.setExchangeTimeout(60000); // 1 minute
transport.setExchangeUnresponsiveTimeout(10000); // 10 seconds
console.log("New exchange timeout:", transport.exchangeTimeout);// Example: Context-aware scramble key usage
class LedgerWalletManager {
constructor(transport) {
this.transport = transport;
}
async switchToApp(appName) {
const scrambleKeys = {
'bitcoin': 'BTC',
'ethereum': 'ETH',
'ripple': 'XRP'
};
const key = scrambleKeys[appName];
if (!key) {
throw new Error(`Unknown app: ${appName}`);
}
this.transport.setScrambleKey(key);
return this.waitForApp(appName);
}
}Exchange Timeout: Set based on expected operation complexity
Unresponsive Timeout: Balance user experience with false positives
// Example: Operation-specific timeout configuration
async function performComplexOperation(transport, operationType) {
const timeouts = {
'quick_query': { exchange: 5000, unresponsive: 3000 },
'transaction_sign': { exchange: 30000, unresponsive: 10000 },
'firmware_update': { exchange: 300000, unresponsive: 60000 }
};
const config = timeouts[operationType];
if (config) {
transport.setExchangeTimeout(config.exchange);
transport.setExchangeUnresponsiveTimeout(config.unresponsive);
}
// Perform the operation...
}Different transport implementations may handle configuration differently:
// WebUSB transport - timeout affects browser API calls
import TransportWebUSB from "@ledgerhq/hw-transport-webusb";
const webTransport = await TransportWebUSB.create();
webTransport.setExchangeTimeout(15000); // Browser timeout
// Node.js HID transport - timeout affects system calls
import TransportNodeHid from "@ledgerhq/hw-transport-node-hid";
const hidTransport = await TransportNodeHid.create();
hidTransport.setExchangeTimeout(10000); // System timeoutDecorates multiple methods on an object with app API locking and scramble key management.
/**
* Decorates multiple methods on an object with app API locking and scramble key management
* @param self The object containing methods to decorate
* @param methods Array of method names to decorate
* @param scrambleKey The scramble key to use for these methods
*/
decorateAppAPIMethods(
self: Object,
methods: Array<string>,
scrambleKey: string
): void;Decorates a single method with app API locking and scramble key management.
/**
* Decorates a single method with app API locking and scramble key management
* @param methodName Name of the method for error reporting
* @param f The function to decorate
* @param ctx The context (this) to bind the function to
* @param scrambleKey The scramble key to use for this method
* @returns Decorated function with locking and scramble key management
*/
decorateAppAPIMethod<R, A: any[]>(
methodName: string,
f: (...args: A) => Promise<R>,
ctx: any,
scrambleKey: string
): (...args: A) => Promise<R>;Usage Example:
import Transport from "@ledgerhq/hw-transport";
class MyLedgerApp {
constructor(transport) {
this.transport = transport;
// Decorate multiple methods at once
this.transport.decorateAppAPIMethods(
this,
['getVersion', 'getPublicKey', 'signTransaction'],
'BTC'
);
// Or decorate individual methods
this.getAddress = this.transport.decorateAppAPIMethod(
'getAddress',
this._getRawAddress.bind(this),
this,
'BTC'
);
}
// These methods will be automatically decorated with locking
async getVersion() {
return await this.transport.send(0xB0, 0x01, 0x00, 0x00);
}
async getPublicKey(path) {
return await this.transport.send(0xE0, 0x40, 0x00, 0x00, path);
}
async signTransaction(txData) {
return await this.transport.send(0xE0, 0x44, 0x00, 0x00, txData);
}
// Raw method that gets decorated
async _getRawAddress(path, display) {
return await this.transport.send(0xE0, 0x42, display ? 0x01 : 0x00, 0x00, path);
}
}The transport maintains an internal lock to prevent concurrent app API operations.
class Transport<Descriptor> {
/** Internal lock to prevent concurrent app API operations */
_appAPIlock: string | null = null;
}When methods are decorated, they automatically:
_appAPIlock)This prevents race conditions and ensures proper scramble key isolation between different app operations.