or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

account-balance-errors.mdapplication-manager-errors.mdcurrency-transaction-errors.mddatabase-errors.mddevice-management-errors.mderror-utilities.mdindex.mdnetwork-api-errors.mdtransport-errors.mduser-interaction-errors.md
tile.json

currency-transaction-errors.mddocs/

0

# Currency and Transaction Errors

1

2

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

3

4

## Types

5

6

```typescript { .api }

7

interface Transaction {

8

from: string;

9

to: string;

10

amount: number;

11

currency: string;

12

destinationTag?: number;

13

}

14

15

interface FeeData {

16

fee: number;

17

gasLimit?: number;

18

gasPrice?: number;

19

estimatedGas?: number;

20

}

21

22

interface TransactionInput {

23

amount: number;

24

recipient: string;

25

currency: string;

26

senderBalance: number;

27

}

28

29

interface TezosAccount {

30

type: "originated" | "implicit";

31

canReceive: boolean;

32

canSend: boolean;

33

address: string;

34

}

35

```

36

37

## Capabilities

38

39

### Currency Support Errors

40

41

Errors related to currency support and compatibility.

42

43

```typescript { .api }

44

const CurrencyNotSupported: CustomErrorFunc;

45

const CashAddrNotSupported: CustomErrorFunc;

46

const NotSupportedLegacyAddress: CustomErrorFunc;

47

const ETHAddressNonEIP: CustomErrorFunc;

48

const EthAppPleaseEnableContractData: CustomErrorFunc;

49

```

50

51

**Usage Examples:**

52

53

```typescript

54

import {

55

CurrencyNotSupported,

56

CashAddrNotSupported,

57

NotSupportedLegacyAddress,

58

ETHAddressNonEIP

59

} from "@ledgerhq/errors";

60

61

// Validate currency support

62

function validateCurrency(currency: string) {

63

const supportedCurrencies = ["BTC", "ETH", "LTC", "XRP"];

64

if (!supportedCurrencies.includes(currency)) {

65

throw new CurrencyNotSupported(`Currency ${currency} is not supported`);

66

}

67

}

68

69

// Handle Bitcoin Cash address format

70

function validateBitcoinAddress(address: string, allowCashAddr: boolean = false) {

71

if (address.startsWith("bitcoincash:") && !allowCashAddr) {

72

throw new CashAddrNotSupported("Cash address format is not supported");

73

}

74

}

75

76

// Handle legacy address formats

77

function validateAddressFormat(address: string, currency: string) {

78

if (currency === "BTC" && address.startsWith("1")) {

79

throw new NotSupportedLegacyAddress("Legacy P2PKH addresses are not supported");

80

}

81

}

82

83

// Handle Ethereum address compliance

84

function validateEthereumAddress(address: string) {

85

if (!isEIP55Compliant(address)) {

86

throw new ETHAddressNonEIP("Ethereum address must be EIP-55 compliant");

87

}

88

}

89

90

// Handle Ethereum contract data requirement

91

try {

92

await executeEthereumTransaction(contractTransaction);

93

} catch (error) {

94

if (error instanceof EthAppPleaseEnableContractData) {

95

console.log("Please enable contract data in your Ethereum app settings");

96

}

97

}

98

```

99

100

### Address Validation Errors

101

102

Errors related to cryptocurrency address validation and format checking.

103

104

```typescript { .api }

105

const InvalidAddress: CustomErrorFunc;

106

const InvalidAddressBecauseDestinationIsAlsoSource: CustomErrorFunc;

107

const InvalidXRPTag: CustomErrorFunc;

108

```

109

110

**Usage Examples:**

111

112

```typescript

113

import {

114

InvalidAddress,

115

InvalidAddressBecauseDestinationIsAlsoSource,

116

InvalidXRPTag

117

} from "@ledgerhq/errors";

118

119

// Validate address format

120

function validateAddress(address: string, currency: string) {

121

if (!isValidAddressFormat(address, currency)) {

122

throw new InvalidAddress(`Invalid ${currency} address format: ${address}`);

123

}

124

}

125

126

// Prevent self-transactions

127

function validateTransactionAddresses(fromAddress: string, toAddress: string) {

128

if (fromAddress === toAddress) {

129

throw new InvalidAddressBecauseDestinationIsAlsoSource(

130

"Cannot send funds to the same address"

131

);

132

}

133

}

134

135

// Validate XRP destination tags

136

function validateXRPTransaction(address: string, tag?: number) {

137

validateAddress(address, "XRP");

138

139

if (tag !== undefined && (tag < 0 || tag > 4294967295 || !Number.isInteger(tag))) {

140

throw new InvalidXRPTag("XRP destination tag must be a valid 32-bit unsigned integer");

141

}

142

}

143

144

// Example usage in transaction validation

145

function validateTransactionInput(transaction: Transaction) {

146

// Basic address validation

147

validateAddress(transaction.from, transaction.currency);

148

validateAddress(transaction.to, transaction.currency);

149

150

// Prevent self-transactions

151

validateTransactionAddresses(transaction.from, transaction.to);

152

153

// Currency-specific validation

154

if (transaction.currency === "XRP") {

155

validateXRPTransaction(transaction.to, transaction.destinationTag);

156

} else if (transaction.currency === "ETH") {

157

validateEthereumAddress(transaction.to);

158

}

159

}

160

```

161

162

### Transaction Requirements Errors

163

164

Errors related to missing required transaction parameters.

165

166

```typescript { .api }

167

const AmountRequired: CustomErrorFunc;

168

const RecipientRequired: CustomErrorFunc;

169

```

170

171

**Usage Examples:**

172

173

```typescript

174

import {

175

AmountRequired,

176

RecipientRequired

177

} from "@ledgerhq/errors";

178

179

// Validate transaction amount

180

function validateTransactionAmount(amount?: number | string) {

181

if (amount === undefined || amount === null || amount === "" || amount === 0) {

182

throw new AmountRequired("Transaction amount is required");

183

}

184

185

if (typeof amount === "string" && isNaN(Number(amount))) {

186

throw new AmountRequired("Transaction amount must be a valid number");

187

}

188

}

189

190

// Validate recipient address

191

function validateRecipient(recipient?: string) {

192

if (!recipient || recipient.trim() === "") {

193

throw new RecipientRequired("Recipient address is required");

194

}

195

}

196

197

// Example transaction builder

198

class TransactionBuilder {

199

private amount?: number;

200

private recipient?: string;

201

202

setAmount(amount: number) {

203

validateTransactionAmount(amount);

204

this.amount = amount;

205

return this;

206

}

207

208

setRecipient(recipient: string) {

209

validateRecipient(recipient);

210

this.recipient = recipient;

211

return this;

212

}

213

214

build() {

215

validateTransactionAmount(this.amount);

216

validateRecipient(this.recipient);

217

218

return {

219

amount: this.amount!,

220

recipient: this.recipient!

221

};

222

}

223

}

224

```

225

226

### Fee and Gas Errors

227

228

Errors related to transaction fees and gas estimation.

229

230

```typescript { .api }

231

const FeeEstimationFailed: CustomErrorFunc;

232

const FeeNotLoaded: CustomErrorFunc;

233

const FeeRequired: CustomErrorFunc;

234

const FeeTooHigh: CustomErrorFunc;

235

const NotEnoughGas: CustomErrorFunc;

236

const GasLessThanEstimate: CustomErrorFunc;

237

```

238

239

**Usage Examples:**

240

241

```typescript

242

import {

243

FeeEstimationFailed,

244

FeeNotLoaded,

245

FeeRequired,

246

FeeTooHigh,

247

NotEnoughGas,

248

GasLessThanEstimate

249

} from "@ledgerhq/errors";

250

251

// Handle fee estimation failures

252

async function estimateTransactionFee(transaction: Transaction) {

253

try {

254

return await feeEstimationService.estimate(transaction);

255

} catch (error) {

256

throw new FeeEstimationFailed("Unable to estimate transaction fee");

257

}

258

}

259

260

// Validate fee availability

261

function validateFeeData(feeData?: FeeData) {

262

if (!feeData) {

263

throw new FeeNotLoaded("Fee data must be loaded before creating transaction");

264

}

265

}

266

267

// Validate fee requirement

268

function validateTransactionFee(fee?: number) {

269

if (fee === undefined || fee === null) {

270

throw new FeeRequired("Transaction fee is required");

271

}

272

273

if (fee <= 0) {

274

throw new FeeRequired("Transaction fee must be greater than zero");

275

}

276

}

277

278

// Validate reasonable fee amounts

279

function validateFeeAmount(fee: number, amount: number, maxFeePercent: number = 0.1) {

280

const feePercent = fee / amount;

281

if (feePercent > maxFeePercent) {

282

throw new FeeTooHigh(

283

`Fee is ${(feePercent * 100).toFixed(2)}% of transaction amount (max ${maxFeePercent * 100}%)`

284

);

285

}

286

}

287

288

// Handle Ethereum gas validation

289

function validateEthereumGas(gasLimit: number, gasPrice: number, balance: number) {

290

const totalGasCost = gasLimit * gasPrice;

291

292

if (balance < totalGasCost) {

293

throw new NotEnoughGas(

294

`Insufficient ETH for gas: ${totalGasCost} required, ${balance} available`

295

);

296

}

297

}

298

299

// Validate gas limit against estimate

300

function validateGasLimit(gasLimit: number, estimatedGas: number) {

301

if (gasLimit < estimatedGas) {

302

throw new GasLessThanEstimate(

303

`Gas limit ${gasLimit} is less than estimated ${estimatedGas}`

304

);

305

}

306

}

307

308

// Example fee validation workflow

309

async function prepareTransaction(transactionData: TransactionInput) {

310

// Validate required fields

311

validateTransactionAmount(transactionData.amount);

312

validateRecipient(transactionData.recipient);

313

314

// Estimate and validate fees

315

let feeData;

316

try {

317

feeData = await estimateTransactionFee(transactionData);

318

} catch (error) {

319

if (error instanceof FeeEstimationFailed) {

320

console.log("Using default fee due to estimation failure");

321

feeData = getDefaultFeeData();

322

} else {

323

throw error;

324

}

325

}

326

327

validateFeeData(feeData);

328

validateTransactionFee(feeData.fee);

329

validateFeeAmount(feeData.fee, transactionData.amount);

330

331

// Additional validation for Ethereum

332

if (transactionData.currency === "ETH") {

333

validateEthereumGas(feeData.gasLimit, feeData.gasPrice, transactionData.senderBalance);

334

validateGasLimit(feeData.gasLimit, feeData.estimatedGas);

335

}

336

337

return {

338

...transactionData,

339

fee: feeData.fee,

340

gasLimit: feeData.gasLimit,

341

gasPrice: feeData.gasPrice

342

};

343

}

344

```

345

346

### Synchronization and Generic Errors

347

348

Errors related to data synchronization and general operation failures.

349

350

```typescript { .api }

351

const SyncError: CustomErrorFunc;

352

const TimeoutTagged: CustomErrorFunc;

353

```

354

355

**Usage Examples:**

356

357

```typescript

358

import {

359

SyncError,

360

TimeoutTagged

361

} from "@ledgerhq/errors";

362

363

// Handle synchronization failures

364

async function syncAccountData(accountId: string) {

365

try {

366

await synchronizeAccount(accountId);

367

} catch (error) {

368

throw new SyncError(`Failed to synchronize account data: ${error.message}`);

369

}

370

}

371

372

// Handle operation timeouts

373

async function executeWithTimeout<T>(

374

operation: () => Promise<T>,

375

timeoutMs: number,

376

operationName: string

377

): Promise<T> {

378

const timeoutPromise = new Promise<never>((_, reject) => {

379

setTimeout(() => {

380

reject(new TimeoutTagged(`${operationName} timed out after ${timeoutMs}ms`));

381

}, timeoutMs);

382

});

383

384

return Promise.race([operation(), timeoutPromise]);

385

}

386

387

// Example usage with timeout

388

async function fetchAccountBalance(accountId: string) {

389

try {

390

return await executeWithTimeout(

391

() => getAccountBalance(accountId),

392

30000,

393

"Account balance fetch"

394

);

395

} catch (error) {

396

if (error instanceof TimeoutTagged) {

397

console.log("Balance fetch timed out - using cached value");

398

return getCachedBalance(accountId);

399

}

400

throw error;

401

}

402

}

403

```