Skip to content

Transactions list DX: show counterparty name, transaction type, and resolved token symbol (and persist counterpartyId on direct transfers) #588

Description

@resourcefulmind

Area: Payments / transactions list (Payments → Overview) + transfers API ·

The transactions list is the reconciliation surface, but a row is hard to read at a glance and one field isn't persisted at all. Four related gaps on the same surface:

1. Counterparty shows a wallet address, not a name, and for direct payments it isn't recorded

The "Counterparty" column renders transfer.destination / transfer.source (a raw wallet address, truncated, monospace) and ignores the counterpartyId the row already carries. So:

  • A ramp transfer that has counterpartyId (e.g. an off-ramp to a saved individual) shows the address, or "Unavailable" when destination is null, despite the id being present on GET /v1/payments/transfers/{id}.
  • A direct/Pay transfer never persists the counterparty at all: the dashboard createTransfer sends only {source, destination, token, amount, memo}, so counterpartyId is saved as null even when the user explicitly selected the payee. The link to who was paid is lost at write time. (This is the one data bug in this issue, not just display.)

2. No transaction Type in the row

The table shows a Direction column ("Inbound"/"Outbound") but no transaction Type. A plain transfer and an offramp both read "Outbound"; an onramp and an inbound transfer both read "Inbound", indistinguishable at a glance. A label helper (RAMP_TYPE_LABELS) already exists but isn't wired into the row.

3. Token is a raw mint for direct transfers (API side of the already-fixed UI issue)

In GET /v1/payments/transfers, token is a symbol for ramp rows (USDC) but the raw mint for direct transfers (4zMMC9…, the devnet USDC mint, which isn't in the SDK's well-known-token map). The dashboard was fixed to map the mint for display, but the API still returns the raw mint, so any other consumer inherits the inconsistency.

Reproduce

curl "$BASE/v1/payments/transfers?category=ramp"   -H "Authorization: Bearer $KEY"
#   token "USDC", counterpartyId present on detail
curl "$BASE/v1/payments/transfers?category=wallet" -H "Authorization: Bearer $KEY"
#   token "4zMMC9…" (raw mint), counterpartyId null even for a Pay to a selected counterparty

Then open Payments → Overview: rows show an address (or "Unavailable") under Counterparty, "Outbound"/"Inbound" with no type, and (for direct transfers via the API) the raw mint.

Impact: the reconciliation surface is hard to scan and, for direct payments, can't answer "who did we pay" at all because the counterparty was never stored.

Suggested fix

  • Persist counterpartyId on direct/Pay transfers (include it in createTransfer).
  • Resolve counterpartyId to a display name in the Counterparty column; keep the address on hover (the table already uses truncate + tooltip for signatures).
  • Add a Type column or badge using the existing RAMP_TYPE_LABELS.
  • Represent token consistently across transfer types in the API (resolve the symbol server-side, including devnet mints), or add a resolved-symbol field alongside the mint.

Metadata

Metadata

Assignees

Labels

QA improvementsIssues surfaced during the payments QA pass: bugs, gaps, and DX improvements.bugSomething isn't workingenhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions