Problem
src/lib/api.ts's fetchTransactionsFromHorizon maps raw Horizon transaction records into the app's Transaction type. Setting aside the hardcoded amount/assetCode values already tracked in #6, there is a separate, distinct bug in the same mapping in how counterparty is derived:
counterparty:
r.source_account === publicKey ? publicKey : r.source_account,
When r.source_account === publicKey (i.e. direction: 'sent'), the ternary evaluates to publicKey — the connected wallet's own address — instead of the actual recipient. The Horizon /transactions endpoint used here doesn't even carry a destination account at the transaction level (that lives on the nested operations, which this endpoint doesn't return), so there's no correct value available to plug in here without a further request — but returning the user's own address as their transaction's "counterparty" is actively misleading rather than merely incomplete, and is worth tracking as its own defect since fixing #6's amount/endpoint issue alone would not fix this.
Why it matters
Any UI that renders counterparty for a sent transaction (transaction history lists, receipts, exports) will display the user's own address as who they paid — actively wrong information, not just a missing feature. A user reviewing their sent-payments history would see their own public key listed as the recipient of every outgoing payment, which is confusing at best and could mask a genuine misdirected payment at worst (if the real destination were ever wrong, this bug would hide that fact by showing the user's own address instead of surfacing anything to double-check against).
Reproduction
- Call
useRecentTransactions() (which calls this function) for any account with at least one outgoing payment.
- Inspect the returned
transactions[i].counterparty for a direction: 'sent' entry — it will equal publicKey, the account's own address, not the recipient.
Suggested fix
This should be fixed as part of the same endpoint migration tracked in #6 (moving from /transactions to /accounts/{id}/payments, which returns per-operation from/to fields) — once that migration lands, counterparty can be derived correctly as "whichever of from/to isn't the current account" instead of needing a same-endpoint workaround. Until that migration happens, at minimum this specific ternary should be fixed to return null/undefined for the unknown-destination case rather than a plausible-looking but wrong value (the user's own address), so consuming UI can render "recipient unavailable" instead of confidently wrong data.
Edge cases
Multi-operation transactions (this app's own batch-send and escrow features produce these, e.g. buildBatchPaymentTransaction) have no single "counterparty" at all — the fix needs a defined behavior for that case (e.g. counterparty: null or a list) rather than picking one arbitrary value, which is also relevant to #6's endpoint-migration work.
Testing strategy
Unit test fetchTransactionsFromHorizon (or its post-migration replacement) against a fixture Horizon response with a known outgoing payment and assert counterparty equals the actual destination, never publicKey.
Related issues in this batch
Directly complements #6, which tracks the hardcoded amount/assetCode bug in this same mapping function — both stem from the same root cause (the /transactions endpoint lacking operation-level data) and should likely be fixed together as part of the endpoint migration #6 describes.
Problem
src/lib/api.ts'sfetchTransactionsFromHorizonmaps raw Horizon transaction records into the app'sTransactiontype. Setting aside the hardcodedamount/assetCodevalues already tracked in #6, there is a separate, distinct bug in the same mapping in howcounterpartyis derived:When
r.source_account === publicKey(i.e.direction: 'sent'), the ternary evaluates topublicKey— the connected wallet's own address — instead of the actual recipient. The Horizon/transactionsendpoint used here doesn't even carry a destination account at the transaction level (that lives on the nested operations, which this endpoint doesn't return), so there's no correct value available to plug in here without a further request — but returning the user's own address as their transaction's "counterparty" is actively misleading rather than merely incomplete, and is worth tracking as its own defect since fixing #6's amount/endpoint issue alone would not fix this.Why it matters
Any UI that renders
counterpartyfor a sent transaction (transaction history lists, receipts, exports) will display the user's own address as who they paid — actively wrong information, not just a missing feature. A user reviewing their sent-payments history would see their own public key listed as the recipient of every outgoing payment, which is confusing at best and could mask a genuine misdirected payment at worst (if the real destination were ever wrong, this bug would hide that fact by showing the user's own address instead of surfacing anything to double-check against).Reproduction
useRecentTransactions()(which calls this function) for any account with at least one outgoing payment.transactions[i].counterpartyfor adirection: 'sent'entry — it will equalpublicKey, the account's own address, not the recipient.Suggested fix
This should be fixed as part of the same endpoint migration tracked in #6 (moving from
/transactionsto/accounts/{id}/payments, which returns per-operationfrom/tofields) — once that migration lands,counterpartycan be derived correctly as "whichever offrom/toisn't the current account" instead of needing a same-endpoint workaround. Until that migration happens, at minimum this specific ternary should be fixed to returnnull/undefinedfor the unknown-destination case rather than a plausible-looking but wrong value (the user's own address), so consuming UI can render "recipient unavailable" instead of confidently wrong data.Edge cases
Multi-operation transactions (this app's own batch-send and escrow features produce these, e.g.
buildBatchPaymentTransaction) have no single "counterparty" at all — the fix needs a defined behavior for that case (e.g.counterparty: nullor a list) rather than picking one arbitrary value, which is also relevant to #6's endpoint-migration work.Testing strategy
Unit test
fetchTransactionsFromHorizon(or its post-migration replacement) against a fixture Horizon response with a known outgoing payment and assertcounterpartyequals the actual destination, neverpublicKey.Related issues in this batch
Directly complements #6, which tracks the hardcoded
amount/assetCodebug in this same mapping function — both stem from the same root cause (the/transactionsendpoint lacking operation-level data) and should likely be fixed together as part of the endpoint migration #6 describes.