Context
In the Ark transaction list, tapping a delegated received VTXO opens an info sheet (_showDelegateInfo) showing the refresh/expiry estimate for that VTXO. To find the VtxoInfo for a tapped ArkTransactionSummary, the code matches on txid only:
app/lib/screens/ark/ark_screen.dart (_buildTransactionItem):
VtxoInfo? vtxo;
for (final v in mpcService.vtxos) {
if (v.txid == tx.txid) { vtxo = v; break; }
}
Problem
A single txid can carry multiple outputs to the same user, so txid-only matching picks an arbitrary (first) VTXO and may show the wrong expiry/refresh time for that row.
An amount-based tiebreaker was considered and rejected: two VTXOs can legitimately have the same amount, so amount does not uniquely identify the output either.
The correct unique key is txid + vout — but vout is not currently available on the transaction side to match against.
Why it's not a quick fix
ArkTransactionSummary has no vout field, and neither does the server struct that feeds it. Keying on vout requires plumbing it end-to-end:
- Add
vout to ArkTxEntry (cosigner-runtime/src/cosigner/types.rs) and populate it in the receive/send/settle/board handlers that build history entries.
- Add
vout to ArkTransactionSummary in protocol/protos/mpc_wallet.proto (field 5) and regenerate (make proto).
- Map the new field in the hand-written REST decoder (
app-core/lib/rest_wallet_api.dart) — note this is the same manual mapping that previously silently dropped auto_settle_safety_margin_secs, so any added field must be wired in by hand here.
- Match on
v.txid == tx.txid && v.vout == tx.vout.
Impact
Low / cosmetic: only affects which expiry/refresh estimate is shown in the info sheet when a user has multiple same-txid VTXOs. No funds or signing logic is affected.
Acceptance
vout available on ArkTransactionSummary end-to-end (server → proto → REST + gRPC clients).
- Info sheet matches the exact VTXO by
txid + vout.
- REST decoder explicitly maps the new field (guard against the silent-drop footgun).
Context
In the Ark transaction list, tapping a delegated received VTXO opens an info sheet (
_showDelegateInfo) showing the refresh/expiry estimate for that VTXO. To find theVtxoInfofor a tappedArkTransactionSummary, the code matches ontxidonly:app/lib/screens/ark/ark_screen.dart(_buildTransactionItem):Problem
A single txid can carry multiple outputs to the same user, so
txid-only matching picks an arbitrary (first) VTXO and may show the wrong expiry/refresh time for that row.An amount-based tiebreaker was considered and rejected: two VTXOs can legitimately have the same amount, so amount does not uniquely identify the output either.
The correct unique key is
txid + vout— butvoutis not currently available on the transaction side to match against.Why it's not a quick fix
ArkTransactionSummaryhas novoutfield, and neither does the server struct that feeds it. Keying onvoutrequires plumbing it end-to-end:vouttoArkTxEntry(cosigner-runtime/src/cosigner/types.rs) and populate it in the receive/send/settle/board handlers that build history entries.vouttoArkTransactionSummaryinprotocol/protos/mpc_wallet.proto(field 5) and regenerate (make proto).app-core/lib/rest_wallet_api.dart) — note this is the same manual mapping that previously silently droppedauto_settle_safety_margin_secs, so any added field must be wired in by hand here.v.txid == tx.txid && v.vout == tx.vout.Impact
Low / cosmetic: only affects which expiry/refresh estimate is shown in the info sheet when a user has multiple same-txid VTXOs. No funds or signing logic is affected.
Acceptance
voutavailable onArkTransactionSummaryend-to-end (server → proto → REST + gRPC clients).txid + vout.