/** @file * Transaction Engine Result (TER) code taxonomy for the XRP Ledger. * * Defines the six result-code enumerations (tel/tem/tef/ter/tes/tec), * the strongly-typed `TERSubset` wrapper that enforces which * categories are permitted in a given context, the `NotTEC` and `TER` * aliases, comparison operators, and lookup utilities. * * Every code value is part of the wire protocol: numeric values are * stored in ledger metadata and consumed by `ripple-binary-codec`. * @see https://xrpl.org/transaction-results.html */ #pragma once // NOLINTBEGIN(readability-identifier-naming) #include #include #include #include #include #include namespace xrpl { /** Underlying integer type shared by all TER code enumerations. * * Using a named typedef allows `TERSubset` to store a plain `int` * without naming a specific enum, and lets `TERtoInt` overloads share * a single return type that triggers the comparison-operator SFINAE. * * @see https://xrpl.org/transaction-results.html */ using TERUnderlyingType = int; //------------------------------------------------------------------------------ /** Local-error result codes (range −399..−300). * * A `tel` result means this node alone rejected the transaction; the * decision is not propagated to the network. The transaction is not * forwarded to peers and no fee check is performed. Common causes: * fee below the local minimum, or path counts that exceed node-local * limits. These codes are only valid during non-consensus processing. * * @note Numeric values are stable and encoded in `ripple-binary-codec`. * Never renumber or remove existing enumerators. */ // Protocol-critical, mixed with custom TER wrapper type, hundreds of usages // NOLINTNEXTLINE(cppcoreguidelines-use-enum-class) enum TELcodes : TERUnderlyingType { // Note: Range is stable. // Exact numbers are used in ripple-binary-codec: // https://github.com/XRPLF/xrpl.js/blob/main/packages/ripple-binary-codec/src/enums/definitions.json // Use tokens. telLOCAL_ERROR = -399, telBAD_DOMAIN, telBAD_PATH_COUNT, telBAD_PUBLIC_KEY, telFAILED_PROCESSING, telINSUF_FEE_P, telNO_DST_PARTIAL, telCAN_NOT_QUEUE, telCAN_NOT_QUEUE_BALANCE, telCAN_NOT_QUEUE_BLOCKS, telCAN_NOT_QUEUE_BLOCKED, telCAN_NOT_QUEUE_FEE, telCAN_NOT_QUEUE_FULL, telWRONG_NETWORK, telREQUIRES_NETWORK_ID, telNETWORK_ID_MAKES_TX_NON_CANONICAL, telENV_RPC_FAILED }; //------------------------------------------------------------------------------ /** Malformed-transaction result codes (range −299..−200). * * A `tem` result means the transaction is structurally corrupt and * cannot succeed in any possible ledger state. The transaction is * rejected without being applied or forwarded, and no fee is charged. * * @note Numeric values are stable and encoded in `ripple-binary-codec`. * Never renumber or remove existing enumerators. */ // Protocol-critical, mixed with custom TER wrapper type, hundreds of usages // NOLINTNEXTLINE(cppcoreguidelines-use-enum-class) enum TEMcodes : TERUnderlyingType { // Note: Range is stable. // Exact numbers are used in ripple-binary-codec: // https://github.com/XRPLF/xrpl.js/blob/main/packages/ripple-binary-codec/src/enums/definitions.json // Use tokens. temMALFORMED = -299, temBAD_AMOUNT, temBAD_CURRENCY, temBAD_EXPIRATION, temBAD_FEE, temBAD_ISSUER, temBAD_LIMIT, temBAD_OFFER, temBAD_PATH, temBAD_PATH_LOOP, temBAD_REGKEY, temBAD_SEND_XRP_LIMIT, temBAD_SEND_XRP_MAX, temBAD_SEND_XRP_NO_DIRECT, temBAD_SEND_XRP_PARTIAL, temBAD_SEND_XRP_PATHS, temBAD_SEQUENCE, temBAD_SIGNATURE, temBAD_SRC_ACCOUNT, temBAD_TRANSFER_RATE, temDST_IS_SRC, temDST_NEEDED, temINVALID, temINVALID_FLAG, temREDUNDANT, temRIPPLE_EMPTY, temDISABLED, temBAD_SIGNER, temBAD_QUORUM, temBAD_WEIGHT, temBAD_TICK_SIZE, temINVALID_ACCOUNT_ID, temCANNOT_PREAUTH_SELF, temINVALID_COUNT, temUNCERTAIN, ///< Internal sentinel — in the process of determining a result; never returned to callers. temUNKNOWN, ///< Internal sentinel — logic not yet implemented; never returned to callers. temSEQ_AND_TICKET, temBAD_NFTOKEN_TRANSFER_FEE, temBAD_AMM_TOKENS, temXCHAIN_EQUAL_DOOR_ACCOUNTS, temXCHAIN_BAD_PROOF, temXCHAIN_BRIDGE_BAD_ISSUES, temXCHAIN_BRIDGE_NONDOOR_OWNER, temXCHAIN_BRIDGE_BAD_MIN_ACCOUNT_CREATE_AMOUNT, temXCHAIN_BRIDGE_BAD_REWARD_AMOUNT, temEMPTY_DID, temARRAY_EMPTY, temARRAY_TOO_LARGE, temBAD_TRANSFER_FEE, temINVALID_INNER_BATCH, temBAD_MPT, }; //------------------------------------------------------------------------------ /** Failure result codes (range −199..−100). * * A `tef` result means the transaction cannot be applied because of the * current ledger state (e.g., sequence already used, bad signature, or * an unexpected C++ exception). The transaction is not applied, not * forwarded, and no fee is charged. Unlike `tem`, a `tef` transaction * could theoretically succeed in a different ledger state. * * @note Numeric values are stable and encoded in `ripple-binary-codec`. * Never renumber or remove existing enumerators. */ // Protocol-critical, mixed with custom TER wrapper type, hundreds of usages // NOLINTNEXTLINE(cppcoreguidelines-use-enum-class) enum TEFcodes : TERUnderlyingType { // Note: Range is stable. // Exact numbers are used in ripple-binary-codec: // https://github.com/XRPLF/xrpl.js/blob/main/packages/ripple-binary-codec/src/enums/definitions.json // Use tokens. tefFAILURE = -199, tefALREADY, tefBAD_ADD_AUTH, tefBAD_AUTH, tefBAD_LEDGER, tefCREATED, tefEXCEPTION, tefINTERNAL, tefNO_AUTH_REQUIRED, // Can't set auth if auth is not required. tefPAST_SEQ, tefWRONG_PRIOR, tefMASTER_DISABLED, tefMAX_LEDGER, tefBAD_SIGNATURE, tefBAD_QUORUM, tefNOT_MULTI_SIGNING, tefBAD_AUTH_MASTER, tefINVARIANT_FAILED, tefTOO_BIG, tefNO_TICKET, tefNFTOKEN_IS_NOT_TRANSFERABLE, tefINVALID_LEDGER_FIX_TYPE, }; //------------------------------------------------------------------------------ /** Retry result codes (range −99..−1). * * A `ter` result means the transaction cannot succeed right now, but * might succeed after other transactions are applied — for example, * if the sequence number is too high or there are insufficient funds * for the fee. The transaction is not applied and leaves a sequence * gap that can block later transactions. It may be held in the * transaction queue (`terQUEUED`) to retry when fee levels drop. * * @note Numeric values are stable and encoded in `ripple-binary-codec`. * Never renumber or remove existing enumerators. */ // Protocol-critical, mixed with custom TER wrapper type, hundreds of usages // NOLINTNEXTLINE(cppcoreguidelines-use-enum-class) enum TERcodes : TERUnderlyingType { // Note: Range is stable. // Exact numbers are used in ripple-binary-codec: // https://github.com/XRPLF/xrpl.js/blob/main/packages/ripple-binary-codec/src/enums/definitions.json // Use tokens. terRETRY = -99, terFUNDS_SPENT, // DEPRECATED. terINSUF_FEE_B, // Can't pay fee, therefore don't burden network. terNO_ACCOUNT, // Can't pay fee, therefore don't burden network. terNO_AUTH, // Not authorized to hold IOUs. terNO_LINE, // Internal flag. terOWNERS, // Can't succeed with non-zero owner count. terPRE_SEQ, // Can't pay fee, no point in forwarding, so don't // burden network. terLAST, // DEPRECATED. terNO_RIPPLE, // Rippling not allowed terQUEUED, // Transaction is being held in TxQ until fee drops terPRE_TICKET, // Ticket is not yet in ledger but might be on its way terNO_AMM, // AMM doesn't exist for the asset pair terADDRESS_COLLISION, // Failed to allocate AccountID when trying to // create a pseudo-account terNO_DELEGATE_PERMISSION, // Delegate does not have permission terLOCKED, // MPT is locked }; //------------------------------------------------------------------------------ /** Success result code (value 0). * * `tesSUCCESS` is the sole member: the transaction was applied to the * ledger and forwarded to peers. Its numeric value (0) is stored in * ledger metadata and must never change. * * @note `TERSubset::operator bool()` returns `false` for this code * (success = falsy), mirroring the conventional C error-code idiom. */ // Protocol-critical, mixed with custom TER wrapper type, hundreds of usages // NOLINTNEXTLINE(cppcoreguidelines-use-enum-class) enum TEScodes : TERUnderlyingType { // Note: Exact number must stay stable. This code is stored by value // in metadata for historic transactions. tesSUCCESS = 0 }; //------------------------------------------------------------------------------ /** Fee-claim result codes (range 100..255). * * A `tec` result means the fee is consumed and the sequence number is * spent, but no other effect is applied to the ledger. The transaction * is still applied and forwarded to peers. Typical causes: a payment * with no valid path, or a transaction that is logically invalid but * well-formed enough to charge a fee. * * When `tapRETRY` is set during application, `tec` codes are demoted * to `terRETRY` so the transaction can be retried rather than * consuming the sequence number. * * @note **DO NOT CHANGE THESE NUMBERS.** They are stored by value in * ledger metadata and parsed by external tools such as * `ripple-binary-codec`. Append new codes; never renumber or remove. * @note Naming convention: use `tecNO_ENTRY` when the primary ledger * object targeted by the transaction is missing; use * `tecOBJECT_NOT_FOUND` when an auxiliary object required to * complete the transaction cannot be found. */ // Protocol-critical, mixed with custom TER wrapper type, hundreds of usages // NOLINTNEXTLINE(cppcoreguidelines-use-enum-class) enum TECcodes : TERUnderlyingType { // Note: Exact numbers must stay stable. These codes are stored by // value in metadata for historic transactions. tecCLAIM = 100, tecPATH_PARTIAL = 101, tecUNFUNDED_ADD = 102, // Unused legacy code tecUNFUNDED_OFFER = 103, tecUNFUNDED_PAYMENT = 104, tecFAILED_PROCESSING = 105, tecDIR_FULL = 121, tecINSUF_RESERVE_LINE = 122, tecINSUF_RESERVE_OFFER = 123, tecNO_DST = 124, tecNO_DST_INSUF_XRP = 125, tecNO_LINE_INSUF_RESERVE = 126, tecNO_LINE_REDUNDANT = 127, tecPATH_DRY = 128, tecUNFUNDED = 129, tecNO_ALTERNATIVE_KEY = 130, tecNO_REGULAR_KEY = 131, tecOWNERS = 132, tecNO_ISSUER = 133, tecNO_AUTH = 134, tecNO_LINE = 135, tecINSUFF_FEE = 136, tecFROZEN = 137, tecNO_TARGET = 138, tecNO_PERMISSION = 139, tecNO_ENTRY = 140, tecINSUFFICIENT_RESERVE = 141, tecNEED_MASTER_KEY = 142, tecDST_TAG_NEEDED = 143, tecINTERNAL = 144, tecOVERSIZE = 145, tecCRYPTOCONDITION_ERROR = 146, tecINVARIANT_FAILED = 147, tecEXPIRED = 148, tecDUPLICATE = 149, tecKILLED = 150, tecHAS_OBLIGATIONS = 151, tecTOO_SOON = 152, tecHOOK_REJECTED [[maybe_unused]] = 153, tecMAX_SEQUENCE_REACHED = 154, tecNO_SUITABLE_NFTOKEN_PAGE = 155, tecNFTOKEN_BUY_SELL_MISMATCH = 156, tecNFTOKEN_OFFER_TYPE_MISMATCH = 157, tecCANT_ACCEPT_OWN_NFTOKEN_OFFER = 158, tecINSUFFICIENT_FUNDS = 159, tecOBJECT_NOT_FOUND = 160, tecINSUFFICIENT_PAYMENT = 161, tecUNFUNDED_AMM = 162, tecAMM_BALANCE = 163, tecAMM_FAILED = 164, tecAMM_INVALID_TOKENS = 165, tecAMM_EMPTY = 166, tecAMM_NOT_EMPTY = 167, tecAMM_ACCOUNT = 168, tecINCOMPLETE = 169, tecXCHAIN_BAD_TRANSFER_ISSUE = 170, tecXCHAIN_NO_CLAIM_ID = 171, tecXCHAIN_BAD_CLAIM_ID = 172, tecXCHAIN_CLAIM_NO_QUORUM = 173, tecXCHAIN_PROOF_UNKNOWN_KEY = 174, tecXCHAIN_CREATE_ACCOUNT_NONXRP_ISSUE = 175, tecXCHAIN_WRONG_CHAIN = 176, tecXCHAIN_REWARD_MISMATCH = 177, tecXCHAIN_NO_SIGNERS_LIST = 178, tecXCHAIN_SENDING_ACCOUNT_MISMATCH = 179, tecXCHAIN_INSUFF_CREATE_AMOUNT = 180, tecXCHAIN_ACCOUNT_CREATE_PAST = 181, tecXCHAIN_ACCOUNT_CREATE_TOO_MANY = 182, tecXCHAIN_PAYMENT_FAILED = 183, tecXCHAIN_SELF_COMMIT = 184, tecXCHAIN_BAD_PUBLIC_KEY_ACCOUNT_PAIR = 185, tecXCHAIN_CREATE_ACCOUNT_DISABLED = 186, tecEMPTY_DID = 187, tecINVALID_UPDATE_TIME = 188, tecTOKEN_PAIR_NOT_FOUND = 189, tecARRAY_EMPTY = 190, tecARRAY_TOO_LARGE = 191, tecLOCKED = 192, tecBAD_CREDENTIALS = 193, tecWRONG_ASSET = 194, tecLIMIT_EXCEEDED = 195, tecPSEUDO_ACCOUNT = 196, tecPRECISION_LOSS = 197, }; //------------------------------------------------------------------------------ /** Convert a TEL/TEM/TEF/TER/TES/TEC code to its underlying integer value. * * These overloads form the single conversion point used by `TERSubset`, * comparison operators, and any code that needs to inspect the raw * integer. A free-function overload set is used rather than an explicit * conversion operator on `TERSubset` to prevent silent implicit * conversions in constructor-initialization contexts (e.g., `Status(TER)` * would compile silently even with `explicit` — a named function does not). * * A matching `friend` overload for `TERSubset` is defined inside * the class template; the six overloads below cover the raw enum types. * * @param v The enum code to convert. * @return The underlying `int` value of `v`. */ constexpr TERUnderlyingType TERtoInt(TELcodes v) { return safeCast(v); } /** @copydoc TERtoInt(TELcodes) */ constexpr TERUnderlyingType TERtoInt(TEMcodes v) { return safeCast(v); } /** @copydoc TERtoInt(TELcodes) */ constexpr TERUnderlyingType TERtoInt(TEFcodes v) { return safeCast(v); } /** @copydoc TERtoInt(TELcodes) */ constexpr TERUnderlyingType TERtoInt(TERcodes v) { return safeCast(v); } /** @copydoc TERtoInt(TELcodes) */ constexpr TERUnderlyingType TERtoInt(TEScodes v) { return safeCast(v); } /** @copydoc TERtoInt(TELcodes) */ constexpr TERUnderlyingType TERtoInt(TECcodes v) { return safeCast(v); } //------------------------------------------------------------------------------ /** Strongly-typed wrapper around a TER integer that restricts which * result-code categories may be implicitly assigned or constructed. * * The `Trait` policy class template determines which `TE*codes` enum * types are accepted. A specialization of `Trait` that inherits from * `std::true_type` permits `T`; one inheriting from `std::false_type` * rejects it at compile time via `std::enable_if`. This provides * category-level type safety without runtime overhead. * * Two concrete aliases are provided: * - `NotTEC` — permits `tel`, `tem`, `tef`, `ter`, `tes`; **excludes * `tec`**. Used as the return type of `preflight()` to prevent fee-theft * via unsigned transactions (see `CanCvtToNotTEC`). * - `TER` — permits all six categories including `tec` and `NotTEC` * (widening assignment from `NotTEC` to `TER` is always valid). * * Default-constructs to `tesSUCCESS`. Truthy (`operator bool`) when the * stored code is anything other than `tesSUCCESS`. * * @tparam Trait A class template whose specializations for each `TE*codes` * type inherit from `std::true_type` (allowed) or `std::false_type` * (disallowed). */ template