Skip to content

fix: muxed-address receives mislabeled as sent (#2841)#2856

Open
PrazwalR wants to merge 1 commit into
stellar:masterfrom
PrazwalR:fix/2841-muxed-history-direction
Open

fix: muxed-address receives mislabeled as sent (#2841)#2856
PrazwalR wants to merge 1 commit into
stellar:masterfrom
PrazwalR:fix/2841-muxed-history-direction

Conversation

@PrazwalR

Copy link
Copy Markdown

Summary

Fixes #2841. Payments and Soroban transfers received at a muxed (M…)
sub-account were shown as "Sent" in transaction history.

Root cause

The direction checks compared the muxed M… destination against the active
account's base G… public key. An M… string can never equal its base G…,
so isReceiving was always false for muxed receives. Horizon already resolves
a muxed destination to its base account in to and exposes the M… separately
in to_muxed (the code even documents this), but the comparison used the M…
value. Second symptom: in asset_balance_changes, a change to/from the muxed
form was filtered out as "not involving this account", dropping it from history.

Change

  • Add a shared toBaseAccount() helper resolving M… → base G… (via existing
    getBaseAccount); G…/C…/empty pass through unchanged.
  • Apply to every ownership check: classic payment direction,
    asset_balance_changes involvement filter + credit/debit, Soroban mint/transfer.
  • Display unchanged — counterparty still shows the M… address.
  • Remove an unreachable if (!isValidatingMemo) branch in
    freighterApiMessageListener.

Tests

16 regression tests using real Keypair/MuxedAccount decode and a real
invoke_host_function envelope XDR (classic payment received/sent/self/path,
balance-change credit/debit to muxed, malformed input, decode mechanism).
Verified red-on-revert (reverting turns exactly the 6 muxed assertions red),
tsc clean, prettier clean, message-listener + AssetDetail suites green (139 pass).

🤖 Generated with Claude Code

Transaction history compared the muxed (M...) destination against the base-G active public key, which can never be equal, so payments and Soroban transfers received at a muxed sub-account were labeled Sent. Muxed-from balance changes were also dropped from history entirely.

Resolve every ownership check to the base account via a shared toBaseAccount() helper: classic payment direction, asset_balance_changes credit + involvement filter, and Soroban mint/transfer. The displayed counterparty still shows the M-address. Also remove an unreachable memo-validation branch in freighterApiMessageListener.

Add 16 regression tests using real keypair/MuxedAccount decode and a real invoke_host_function envelope XDR.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 18, 2026 20:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d0722258dd

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

const isCredit = change.to === publicKey;
// Determine if this is a credit (receiving) or debit (sending). Compare on
// the base account so a credit to a muxed (M...) destination is detected.
const isCredit = toBaseAccount(change.to) === publicKey;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep self muxed balance changes marked as sent

When asset_balance_changes contains a transfer from the active base G... account to one of its own muxed M... addresses, this now sets isCredit to true because the destination is normalized back to the same base account. That regresses the previous behavior for this scenario from Sent/- to Received/+, unlike the classic payment path just below which still guards against self-transfers by also checking the sender base account. Consider applying the same sender self-guard here before treating a muxed destination as a credit.

Useful? React with 👍 / 👎.

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.

Transaction history misclassifies payments/transfers received to a muxed (M...) address as sent

2 participants