Skip to content

fix: decompose path payments into per-hop edges and guard against sel…#103

Merged
gelluisaac merged 3 commits intoTraqora:mainfrom
soma-enyi:fix/path-payment-graph-edges
Mar 27, 2026
Merged

fix: decompose path payments into per-hop edges and guard against sel…#103
gelluisaac merged 3 commits intoTraqora:mainfrom
soma-enyi:fix/path-payment-graph-edges

Conversation

@soma-enyi
Copy link
Copy Markdown
Contributor

Closes #67

fix: Correctly represent path payments as multi-hop edges and prevent self-loop cycles

Problem

Path payments on Stellar involve atomic multi-hop asset conversions (e.g. XLM → BTC → USDC), but the ingestion engine
was treating them as a single flat edge from sender to receiver. This meant:

  • All intermediate hops and asset conversions were silently discarded
  • source_amount, destination_amount, and the path field were ignored
  • A path payment where sender == receiver (e.g. an atomic same-account asset swap) would produce a self-loop edge,
    which causes infinite loops in graph traversal algorithms (BFS, DFS, GNN message passing)

Changes

astroml/ingestion/parsers.py

  • Added extract_path_payment_hops() which decomposes a path_payment_strict_send or path_payment_strict_receive
    operation into an ordered list of per-hop dicts
  • Each hop carries: from_account, to_account, asset, amount, hop_index, is_first_hop, is_last_hop
  • Intermediate accounts (not exposed by Horizon) use a path_ sentinel so downstream consumers can
    identify them
  • Fixed _extract_amount to fall back to destination_amount / source_amount for path payment operations

astroml/ingestion/normalizer.py

  • Added normalize_path_payment_hops() which emits one NormalizedTransaction per hop (hashes suffixed _hop0, _hop1,
    etc.)
  • Falls back to the existing normalize_operation() for all non-path-payment types, so callers can use it uniformly

astroml/features/transaction_graph.py

  • Added a self-loop guard in add_transaction(): edges where from_account == to_account are silently dropped,
    preventing infinite cycles during graph traversal

Example

A path payment GABC → GXYZ via XLM → BTC:GBTC → USDC:GCENTER now produces two edges:

GABC → __path__txhash_1 (asset: XLM, amount: source_amount)
__path__txhash_1 → GXYZ (asset: BTC:GBTC, amount: destination_amount)

Instead of the previous single lossy edge:
GABC → GXYZ (asset: USDC:GCENTER, amount: destination_amount)

Related

Closes #[issue] — Ensure the ingestion engine correctly represents path payments as multi-hop or atomic edges without
creating infinite loops in the graph

@drips-wave
Copy link
Copy Markdown

drips-wave bot commented Mar 26, 2026

@soma-enyi Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@gelluisaac gelluisaac merged commit a1d4e65 into Traqora:main Mar 27, 2026
1 of 2 checks passed
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.

[Bug] Handle Asset Circularity in Path Payment Ingestion

2 participants