Skip to content

feat(api): add 22 missing Transaction variants and Unknown catchall -- fixes #41#42

Open
leftygbalogh wants to merge 2 commits intogmosx:mainfrom
leftygbalogh:lefty/tests-transaction-types-clean
Open

feat(api): add 22 missing Transaction variants and Unknown catchall -- fixes #41#42
leftygbalogh wants to merge 2 commits intogmosx:mainfrom
leftygbalogh:lefty/tests-transaction-types-clean

Conversation

@leftygbalogh
Copy link
Copy Markdown

feat(api): add 22 missing Transaction variants and Unknown catchall

Problem

The Transaction enum in xrpl_api uses #[serde(tag = "TransactionType")] for deserialization. 22 XRPL protocol-level transaction types were missing from the enum. When any of these types appears in an inbound payload, Serde returns a hard Err("unknown variant \"…\""), with no recovery path.

In practice this means:

  • WebSocket subscription streams break — a single unrecognised Transaction in the stream aborts the entire TransactionEvent pipeline (the path issue Query Around TransactionType Support #41 reporter hit).
  • account_tx batch responses abort — a single unrecognised type in the Vec<AccountTransaction> fails the whole deserialization. A commented-out // pub tx: serde_json::Value in account_tx.rs shows a previous developer already hit this.
  • Any code embedding Transaction (e.g., TxResponse, AccountTxResponse) is affected.

Fixes #41. Related: #39.


Solution

1. 22 missing variants added

Each missing variant is added as VariantName(TransactionCommon). This preserves deserialization of all common fields (Account, Fee, Sequence, flags, memos, signers, etc.) while silently dropping variant-specific fields.

BASELINE contract — intentional lossy deserialization:
Variant-specific fields (e.g., Amount on AMMCreate, PriceDataSeries on OracleSet, XChainBridge on XChainCreateBridge) are not modelled and are silently dropped by Serde on deserialization. Each new variant carries a // BASELINE: variant-specific fields are not modelled and are silently dropped comment to make this explicit. Callers requiring variant-specific data should deserialize to serde_json::Value until those fields are modelled in follow-up PRs.

Variants added by amendment family (audited against xrpl.org/amendments.html, 2026-04-05):

Family Variants
AMM AMMBid, AMMCreate, AMMDelete, AMMDeposit, AMMVote, AMMWithdraw
Clawback Clawback
DID DIDDelete, DIDSet
Oracle OracleDelete, OracleSet
Pseudo-transactions EnableAmendment, SetFee, UNLModify
XChain XChainAccountCreateCommit, XChainAddAccountCreateAttestation, XChainAddClaimAttestation, XChainClaim, XChainCommit, XChainCreateBridge, XChainCreateClaimID, XChainModifyBridge

2. #[serde(other)] Unknown forward-compatibility catchall

A Unknown unit variant is added with #[serde(other)]. Any TransactionType string not present in the enum — including future amendment-gated types — deserializes to Unknown rather than returning Err. This prevents stream and batch aborts on types that postdate this PR.

Unknown contract:
Unknown is a unit variant (carries no inner data — a constraint of #[serde(other)] with an internally-tagged enum). transaction.common() and transaction.common_mut() are unreachable on Unknown and will panic with unreachable!() if called. Callers should match on Transaction::Unknown before calling common() when handling events from unknown transaction types.

3. common() / common_mut() updated

All 22 new named variants are handled identically to existing TransactionCommon-wrapping variants. Unknown arms use unreachable!().


Test coverage

50 tests pass, 0 failing, across the full workspace.

Test What it proves
22 × test_deserialize_<VariantName> One round-trip per new variant — common fields survive
test_deserialize_unknown_variant_does_not_panic Unknown catchall — unrecognised TransactionType no longer returns Err
test_deserialize_existing_variants_still_work Regression guard — pre-existing variants unaffected
test_account_tx_response_fails_on_unknown_transaction_type AccountTxResponse batch embedding path
test_tx_response_fails_on_unknown_transaction_type TxResponse single-lookup embedding path
test_transaction_event_fails_on_unknown_transaction_type TransactionEvent / WebSocket stream path
test_amm_create_real_payload_common_fields_correct_variant_fields_dropped AMMCreate real-world JSON — flat fields (Amount, Amount2, TradingFee) silently dropped
test_oracle_set_real_payload_nested_array_silently_dropped OracleSet real-world JSON — nested PriceDataSeries array silently dropped
test_x_chain_create_bridge_real_payload_nested_object_silently_dropped XChainCreateBridge real-world JSON — nested XChainBridge object silently dropped
test_amm_deposit_real_payload_multi_field_silently_dropped AMMDeposit real-world JSON — multi-field mix silently dropped

Fixture JSON payloads are sourced from official XRPL documentation examples (xrpl.org) and verify the BASELINE lossy contract under realistic conditions.


CI

cargo test             # full workspace: 50/50 passing
cargo clippy -p xrpl_api  # 0 warnings
cargo fmt --check      # files modified by this PR are clean

Out of scope / follow-up

  • Variant-specific field modelling — recommended as a separate PR per amendment family.
  • Option<&TransactionCommon> return type on common() / common_mut() — deferred; would be a breaking public API change; warrants its own discussion.

lefty added 2 commits April 5, 2026 16:29
 gmosx#41

Tests added across three production embedding paths where Transaction
deserialization failure manifests:

- xrpl_api/src/types/transaction.rs: 24 tests (22 per missing variant +
  Unknown catchall + regression guard for pre-existing variants)
- xrpl_api/src/api/account_tx.rs: AccountTxResponse batch path
- xrpl_api/src/api/tx.rs: TxResponse single-lookup path
- xrpl_api/src/events/transaction.rs: TransactionEvent / WebSocket stream path

All tests are currently failing. Implementation follows in next commit.
…ixes gmosx#41

The Transaction enum uses #[serde(tag = "TransactionType")]. 22 XRPL protocol
variants activated by amendments after the enum was written were missing,
causing a hard Err("unknown variant") on any payload for those types.

Fix:
- 22 variants added as VariantName(TransactionCommon). Each carries a
  BASELINE comment: variant-specific fields are not modelled and are
  silently dropped (progressive disclosure; field modelling follows in
  per-family PRs).
- #[serde(other)] Unknown unit variant added as a forward-compatibility
  catchall.  Any future or currently-unknown TransactionType string maps
  to Unknown instead of returning Err.
- common() / common_mut() updated for all new variants.
  Unknown arms use unreachable!() -- callers must match on Unknown before
  calling common() when handling stream events defensively.

Variants added by amendment family (audited against
xrpl.org/amendments.html, 2026-04-05):
  AMM: AMMBid, AMMCreate, AMMDelete, AMMDeposit, AMMVote, AMMWithdraw
  Clawback: Clawback
  DID: DIDDelete, DIDSet
  Oracle: OracleDelete, OracleSet
  Pseudo-tx: EnableAmendment, SetFee, UNLModify
  XChain: XChainAccountCreateCommit, XChainAddAccountCreateAttestation,
          XChainAddClaimAttestation, XChainClaim, XChainCommit,
          XChainCreateBridge, XChainCreateClaimID, XChainModifyBridge

Real-payload fixture tests (sourced from xrpl.org protocol docs, 2026-04-05):
- AMMCreate: flat variant fields (Amount, Amount2, TradingFee) dropped
- OracleSet: nested PriceDataSeries array dropped
- XChainCreateBridge: nested XChainBridge object (4 sub-fields) dropped
- AMMDeposit: multi-field mix (Asset, Asset2, Amount, Amount2) dropped
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Query Around TransactionType Support

1 participant