UNPKG

@ledgerhq/coin-stellar

Version:
139 lines 6.47 kB
"use strict"; // SPDX-FileCopyrightText: © 2026 LEDGER SAS // SPDX-License-Identifier: Apache-2.0 Object.defineProperty(exports, "__esModule", { value: true }); exports.isHorizonErrorBody = isHorizonErrorBody; exports.getHorizonErrorBody = getHorizonErrorBody; exports.documentationSummaryFromHorizonBody = documentationSummaryFromHorizonBody; const stellar_sdk_1 = require("@stellar/stellar-sdk"); const GENERIC_TRANSACTION_FAILED_DETAIL = /The transaction failed when submitted to the Stellar network/i; /** Horizon `extras.result_codes.transaction` → one-line description (Stellar docs). */ const HORIZON_TRANSACTION_RESULT_CODE_DOCUMENTATION = { tx_success: 'The transaction succeeded.', tx_failed: 'One of the operations failed (none were applied).', tx_too_early: 'The ledger closeTime was before the minTime.', tx_too_late: 'The ledger closeTime was after the maxTime.', tx_missing_operation: 'No operation was specified.', tx_bad_seq: 'Sequence number does not match source account.', tx_bad_auth: 'Too few valid signatures / wrong network.', tx_insufficient_balance: 'Fee would bring account below reserve.', tx_no_source_account: 'Source account not found.', tx_insufficient_fee: 'Fee is too small.', tx_bad_auth_extra: 'Unused signatures attached to transaction.', tx_internal_error: 'An unknown error occurred.', tx_fee_bump_inner_success: 'Fee bump inner transaction succeeded.', tx_fee_bump_inner_failed: 'Fee bump inner transaction failed.', tx_not_supported: 'Transaction type not supported.', tx_bad_sponsorship: 'Sponsorship is invalid.', tx_bad_min_seq_age_or_gap: 'Minimum sequence age or gap precondition failed.', tx_malformed: 'Transaction is malformed.', tx_soroban_invalid: 'Soroban transaction is invalid.', }; /** Horizon `extras.result_codes.operations[]` → one-line description (Stellar docs). */ const HORIZON_OPERATION_RESULT_CODE_DOCUMENTATION = { op_inner: 'The inner object result is valid and the operation was a success.', op_bad_auth: 'There are too few valid signatures, or the transaction was submitted to the wrong network.', op_no_source_account: 'The source account was not found.', op_no_account: 'The source account was not found.', op_not_supported: 'The operation is not supported at this time.', op_too_many_subentries: 'Max number of subentries (1000) already reached.', op_exceeded_work_limit: 'Operation did too much work.', op_malformed: 'The operation input is invalid.', op_underfunded: 'The source account does not have enough balance to send the payment amount while maintaining its minimum reserve.', op_src_no_trust: 'The source account does not have a trustline for the asset it is trying to send.', op_src_not_authorized: 'The source account is not authorized to send this asset.', op_no_destination: 'The destination account does not exist.', op_no_trust: 'The destination account does not have a trustline for the asset being sent.', op_not_authorized: 'The destination account is not authorized to hold this asset.', op_line_full: 'The destination account does not have sufficient limits to receive the amount and still satisfy its buying liabilities.', op_no_issuer: 'The issuer of the asset does not exist.', }; /** Operation result codes that mark success for a prior op in a multi-op failure list. */ const HORIZON_OPERATION_SUCCESS_RESULT_CODES = new Set(['op_inner']); function failingOperationCode(operations) { return operations?.find((code) => !HORIZON_OPERATION_SUCCESS_RESULT_CODES.has(code)); } function isHorizonErrorBody(data) { return !!(data && typeof data === 'object' && 'title' in data && typeof data.title === 'string' && 'status' in data && typeof data.status === 'number'); } /** * Horizon failures are usually {@link BadResponseError} with `response` = problem+json body. * Axios rejects first; the SDK forwards it unchanged, so the body lives on `error.response.data`. */ function getHorizonErrorBody(error) { if (error instanceof stellar_sdk_1.BadResponseError && isHorizonErrorBody(error.response)) { return error.response; } if (error && typeof error === 'object' && 'response' in error) { const data = error.response?.data; if (isHorizonErrorBody(data)) { return data; } } return null; } function lookupOperationDocumentation(operationCode) { if (!operationCode) { return undefined; } return HORIZON_OPERATION_RESULT_CODE_DOCUMENTATION[operationCode]; } function lookupTransactionDocumentation(transactionCode) { if (!transactionCode) { return undefined; } return HORIZON_TRANSACTION_RESULT_CODE_DOCUMENTATION[transactionCode]; } function detailDocumentation(body) { const detail = body.detail?.trim(); if (!detail || GENERIC_TRANSACTION_FAILED_DETAIL.test(detail)) { return undefined; } return detail; } function extrasReasonDocumentation(body) { const reason = body.extras?.reason?.trim(); if (!reason) { return undefined; } const invalidField = body.extras?.invalid_field?.trim(); return invalidField ? `${reason} (field: ${invalidField})` : reason; } /** * Best-effort human-readable summary for logs (`errorExtras.documentationSummary`). * Prefers operation result codes, then transaction codes, then Horizon `detail` / `extras.reason`. */ function documentationSummaryFromHorizonBody(body) { const transactionCode = body.extras?.result_codes?.transaction; const operationCode = failingOperationCode(body.extras?.result_codes?.operations); const operationSummary = lookupOperationDocumentation(operationCode); if (operationSummary) { return operationSummary; } const transactionSummary = lookupTransactionDocumentation(transactionCode); if (transactionSummary && transactionCode !== 'tx_failed') { return transactionSummary; } const detail = detailDocumentation(body); if (detail) { return detail; } const extrasReason = extrasReasonDocumentation(body); if (extrasReason) { return extrasReason; } if (transactionSummary) { return transactionSummary; } const title = body.title?.trim(); if (title) { return title; } return 'Unknown Horizon error.'; } //# sourceMappingURL=horizonErrorBody.js.map