CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-ledgerhq--errors

Comprehensive error handling system for Ledger hardware wallet applications and libraries

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

currency-transaction-errors.mddocs/

Currency and Transaction Errors

Error classes for cryptocurrency operations including address validation, transaction processing, fee estimation, and currency-specific issues.

Types

interface Transaction {
  from: string;
  to: string;
  amount: number;
  currency: string;
  destinationTag?: number;
}

interface FeeData {
  fee: number;
  gasLimit?: number;
  gasPrice?: number;
  estimatedGas?: number;
}

interface TransactionInput {
  amount: number;
  recipient: string;
  currency: string;
  senderBalance: number;
}

interface TezosAccount {
  type: "originated" | "implicit";
  canReceive: boolean;
  canSend: boolean;
  address: string;
}

Capabilities

Currency Support Errors

Errors related to currency support and compatibility.

const CurrencyNotSupported: CustomErrorFunc;
const CashAddrNotSupported: CustomErrorFunc;
const NotSupportedLegacyAddress: CustomErrorFunc;
const ETHAddressNonEIP: CustomErrorFunc;
const EthAppPleaseEnableContractData: CustomErrorFunc;

Usage Examples:

import { 
  CurrencyNotSupported,
  CashAddrNotSupported,
  NotSupportedLegacyAddress,
  ETHAddressNonEIP
} from "@ledgerhq/errors";

// Validate currency support
function validateCurrency(currency: string) {
  const supportedCurrencies = ["BTC", "ETH", "LTC", "XRP"];
  if (!supportedCurrencies.includes(currency)) {
    throw new CurrencyNotSupported(`Currency ${currency} is not supported`);
  }
}

// Handle Bitcoin Cash address format
function validateBitcoinAddress(address: string, allowCashAddr: boolean = false) {
  if (address.startsWith("bitcoincash:") && !allowCashAddr) {
    throw new CashAddrNotSupported("Cash address format is not supported");
  }
}

// Handle legacy address formats
function validateAddressFormat(address: string, currency: string) {
  if (currency === "BTC" && address.startsWith("1")) {
    throw new NotSupportedLegacyAddress("Legacy P2PKH addresses are not supported");
  }
}

// Handle Ethereum address compliance
function validateEthereumAddress(address: string) {
  if (!isEIP55Compliant(address)) {
    throw new ETHAddressNonEIP("Ethereum address must be EIP-55 compliant");
  }
}

// Handle Ethereum contract data requirement
try {
  await executeEthereumTransaction(contractTransaction);
} catch (error) {
  if (error instanceof EthAppPleaseEnableContractData) {
    console.log("Please enable contract data in your Ethereum app settings");
  }
}

Address Validation Errors

Errors related to cryptocurrency address validation and format checking.

const InvalidAddress: CustomErrorFunc;
const InvalidAddressBecauseDestinationIsAlsoSource: CustomErrorFunc;
const InvalidXRPTag: CustomErrorFunc;

Usage Examples:

import { 
  InvalidAddress,
  InvalidAddressBecauseDestinationIsAlsoSource,
  InvalidXRPTag
} from "@ledgerhq/errors";

// Validate address format
function validateAddress(address: string, currency: string) {
  if (!isValidAddressFormat(address, currency)) {
    throw new InvalidAddress(`Invalid ${currency} address format: ${address}`);
  }
}

// Prevent self-transactions
function validateTransactionAddresses(fromAddress: string, toAddress: string) {
  if (fromAddress === toAddress) {
    throw new InvalidAddressBecauseDestinationIsAlsoSource(
      "Cannot send funds to the same address"
    );
  }
}

// Validate XRP destination tags
function validateXRPTransaction(address: string, tag?: number) {
  validateAddress(address, "XRP");
  
  if (tag !== undefined && (tag < 0 || tag > 4294967295 || !Number.isInteger(tag))) {
    throw new InvalidXRPTag("XRP destination tag must be a valid 32-bit unsigned integer");
  }
}

// Example usage in transaction validation
function validateTransactionInput(transaction: Transaction) {
  // Basic address validation
  validateAddress(transaction.from, transaction.currency);
  validateAddress(transaction.to, transaction.currency);
  
  // Prevent self-transactions
  validateTransactionAddresses(transaction.from, transaction.to);
  
  // Currency-specific validation
  if (transaction.currency === "XRP") {
    validateXRPTransaction(transaction.to, transaction.destinationTag);
  } else if (transaction.currency === "ETH") {
    validateEthereumAddress(transaction.to);
  }
}

Transaction Requirements Errors

Errors related to missing required transaction parameters.

const AmountRequired: CustomErrorFunc;
const RecipientRequired: CustomErrorFunc;

Usage Examples:

import { 
  AmountRequired,
  RecipientRequired
} from "@ledgerhq/errors";

// Validate transaction amount
function validateTransactionAmount(amount?: number | string) {
  if (amount === undefined || amount === null || amount === "" || amount === 0) {
    throw new AmountRequired("Transaction amount is required");
  }
  
  if (typeof amount === "string" && isNaN(Number(amount))) {
    throw new AmountRequired("Transaction amount must be a valid number");
  }
}

// Validate recipient address
function validateRecipient(recipient?: string) {
  if (!recipient || recipient.trim() === "") {
    throw new RecipientRequired("Recipient address is required");
  }
}

// Example transaction builder
class TransactionBuilder {
  private amount?: number;
  private recipient?: string;
  
  setAmount(amount: number) {
    validateTransactionAmount(amount);
    this.amount = amount;
    return this;
  }
  
  setRecipient(recipient: string) {
    validateRecipient(recipient);
    this.recipient = recipient;
    return this;
  }
  
  build() {
    validateTransactionAmount(this.amount);
    validateRecipient(this.recipient);
    
    return {
      amount: this.amount!,
      recipient: this.recipient!
    };
  }
}

Fee and Gas Errors

Errors related to transaction fees and gas estimation.

const FeeEstimationFailed: CustomErrorFunc;
const FeeNotLoaded: CustomErrorFunc;
const FeeRequired: CustomErrorFunc;
const FeeTooHigh: CustomErrorFunc;
const NotEnoughGas: CustomErrorFunc;
const GasLessThanEstimate: CustomErrorFunc;

Usage Examples:

import { 
  FeeEstimationFailed,
  FeeNotLoaded,
  FeeRequired,
  FeeTooHigh,
  NotEnoughGas,
  GasLessThanEstimate
} from "@ledgerhq/errors";

// Handle fee estimation failures
async function estimateTransactionFee(transaction: Transaction) {
  try {
    return await feeEstimationService.estimate(transaction);
  } catch (error) {
    throw new FeeEstimationFailed("Unable to estimate transaction fee");
  }
}

// Validate fee availability
function validateFeeData(feeData?: FeeData) {
  if (!feeData) {
    throw new FeeNotLoaded("Fee data must be loaded before creating transaction");
  }
}

// Validate fee requirement
function validateTransactionFee(fee?: number) {
  if (fee === undefined || fee === null) {
    throw new FeeRequired("Transaction fee is required");
  }
  
  if (fee <= 0) {
    throw new FeeRequired("Transaction fee must be greater than zero");
  }
}

// Validate reasonable fee amounts
function validateFeeAmount(fee: number, amount: number, maxFeePercent: number = 0.1) {
  const feePercent = fee / amount;
  if (feePercent > maxFeePercent) {
    throw new FeeTooHigh(
      `Fee is ${(feePercent * 100).toFixed(2)}% of transaction amount (max ${maxFeePercent * 100}%)`
    );
  }
}

// Handle Ethereum gas validation
function validateEthereumGas(gasLimit: number, gasPrice: number, balance: number) {
  const totalGasCost = gasLimit * gasPrice;
  
  if (balance < totalGasCost) {
    throw new NotEnoughGas(
      `Insufficient ETH for gas: ${totalGasCost} required, ${balance} available`
    );
  }
}

// Validate gas limit against estimate
function validateGasLimit(gasLimit: number, estimatedGas: number) {
  if (gasLimit < estimatedGas) {
    throw new GasLessThanEstimate(
      `Gas limit ${gasLimit} is less than estimated ${estimatedGas}`
    );
  }
}

// Example fee validation workflow
async function prepareTransaction(transactionData: TransactionInput) {
  // Validate required fields
  validateTransactionAmount(transactionData.amount);
  validateRecipient(transactionData.recipient);
  
  // Estimate and validate fees
  let feeData;
  try {
    feeData = await estimateTransactionFee(transactionData);
  } catch (error) {
    if (error instanceof FeeEstimationFailed) {
      console.log("Using default fee due to estimation failure");
      feeData = getDefaultFeeData();
    } else {
      throw error;
    }
  }
  
  validateFeeData(feeData);
  validateTransactionFee(feeData.fee);
  validateFeeAmount(feeData.fee, transactionData.amount);
  
  // Additional validation for Ethereum
  if (transactionData.currency === "ETH") {
    validateEthereumGas(feeData.gasLimit, feeData.gasPrice, transactionData.senderBalance);
    validateGasLimit(feeData.gasLimit, feeData.estimatedGas);
  }
  
  return {
    ...transactionData,
    fee: feeData.fee,
    gasLimit: feeData.gasLimit,
    gasPrice: feeData.gasPrice
  };
}

Synchronization and Generic Errors

Errors related to data synchronization and general operation failures.

const SyncError: CustomErrorFunc;
const TimeoutTagged: CustomErrorFunc;

Usage Examples:

import { 
  SyncError,
  TimeoutTagged
} from "@ledgerhq/errors";

// Handle synchronization failures
async function syncAccountData(accountId: string) {
  try {
    await synchronizeAccount(accountId);
  } catch (error) {
    throw new SyncError(`Failed to synchronize account data: ${error.message}`);
  }
}

// Handle operation timeouts
async function executeWithTimeout<T>(
  operation: () => Promise<T>,
  timeoutMs: number,
  operationName: string
): Promise<T> {
  const timeoutPromise = new Promise<never>((_, reject) => {
    setTimeout(() => {
      reject(new TimeoutTagged(`${operationName} timed out after ${timeoutMs}ms`));
    }, timeoutMs);
  });
  
  return Promise.race([operation(), timeoutPromise]);
}

// Example usage with timeout
async function fetchAccountBalance(accountId: string) {
  try {
    return await executeWithTimeout(
      () => getAccountBalance(accountId),
      30000,
      "Account balance fetch"
    );
  } catch (error) {
    if (error instanceof TimeoutTagged) {
      console.log("Balance fetch timed out - using cached value");
      return getCachedBalance(accountId);
    }
    throw error;
  }
}

docs

account-balance-errors.md

application-manager-errors.md

currency-transaction-errors.md

database-errors.md

device-management-errors.md

error-utilities.md

index.md

network-api-errors.md

transport-errors.md

user-interaction-errors.md

tile.json