sameSendRequest function

bool sameSendRequest(
  1. WalletSendRequest a,
  2. WalletSendRequest b
)

Whether two requests are THE SAME request to the send flow — the identity a final "no transaction" attaches to (stage S8 deadline, R05): after the entry's grace revoked a request, a host re-navigation onto the send path with the same request is refused, and one with a different request is a new host act that pays.

The rule, in the host's terms: when BOTH requests carry a tag (WalletSendRequest.correlationId) the host has said what identifies them, so the tags decide — the same tag is the same request, a different tag is a different one, whatever the fields say. Otherwise the payment fields decide: untagged on both sides, which is what makes two const literals with the same fields — one object in Dart — the same request without a special case; and tagged on ONE side only, because a tag the other side never carried is not a host's statement that two payments differ (the diff review, ADR-0558: an untagged request the grace revoked, re-navigated with the same fields plus a retry tag, paid — the paying direction on a money predicate; the fields are the safe side, and a fresh push is always a new grant). Pure + total.

Implementation

bool sameSendRequest(WalletSendRequest a, WalletSendRequest b) {
  if (identical(a, b)) return true;
  final tagA = a.correlationId;
  final tagB = b.correlationId;
  if (tagA != null && tagB != null) return tagA == tagB;
  final memoA = a.machineMemo;
  final memoB = b.machineMemo;
  final sameMachineMemo = memoA == null || memoB == null
      ? memoA == memoB
      : memoA.purpose == memoB.purpose && listEquals(memoA.bytes, memoB.bytes);
  return a.address == b.address &&
      a.amountZat == b.amountZat &&
      a.memo == b.memo &&
      a.label == b.label &&
      a.lockRecipient == b.lockRecipient &&
      sameMachineMemo;
}