Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 30 additions & 6 deletions src/frontend/src/lib/utils/transactions.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -315,21 +315,45 @@ const isMicroTransaction = ({
return false;
};

// Ranks transaction types for the same-timestamp tie-breaker in `sortTransactions`: the received
// leg of a pair sorts above the sent one; any other type keeps a stable position after them.
const sameTimestampTypeRank = (type: AnyTransactionUi['type']): number => {
if (type === 'receive') {
return 0;
}

if (type === 'send') {
return 1;
}

return 2;
};

export const sortTransactions = ({
transactionA: { timestamp: timestampA },
transactionB: { timestamp: timestampB }
transactionA: { timestamp: timestampA, type: typeA },
transactionB: { timestamp: timestampB, type: typeB }
}: {
transactionA: AnyTransactionUi;
transactionB: AnyTransactionUi;
}): number => {
if (nonNullish(timestampA) && nonNullish(timestampB)) {
return (
const bySeconds =
Number(normalizeTimestampToSeconds(timestampB)) -
Number(normalizeTimestampToSeconds(timestampA))
);
Number(normalizeTimestampToSeconds(timestampA));

// The two legs of one operation (a swap, or a self-transfer) share the same block timestamp
// and tie here. Break the tie deterministically — received leg above the sent one — so these
// pairs render consistently instead of in an arbitrary insertion order.
return bySeconds !== 0
? bySeconds
: sameTimestampTypeRank(typeA) - sameTimestampTypeRank(typeB);
}

if (nonNullish(timestampA)) {
return -1;
}

return nonNullish(timestampA) ? -1 : 1;
return nonNullish(timestampB) ? 1 : 0;
};

export const isTransactionsStoreInitialized = ({
Expand Down
21 changes: 21 additions & 0 deletions src/frontend/src/tests/lib/utils/transactions.utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -784,6 +784,27 @@ describe('transactions.utils', () => {

expect(result).toEqual([transaction2, transaction1, transactionWithNullTimestamp]);
});

it('should place the received leg above the sent leg when timestamps tie', () => {
const sent = { timestamp: 5, type: 'send' } as AnyTransactionUi;
const received = { timestamp: 5, type: 'receive' } as AnyTransactionUi;

// Deterministic regardless of input order: the received leg always ends up above the sent one.
expect(
[sent, received].sort((a, b) => sortTransactions({ transactionA: a, transactionB: b }))
).toEqual([received, sent]);
expect(
[received, sent].sort((a, b) => sortTransactions({ transactionA: a, transactionB: b }))
).toEqual([received, sent]);
});
Comment thread
Copilot marked this conversation as resolved.

it('should return 0 when both timestamps are nullish', () => {
const a = { timestamp: undefined } as AnyTransactionUi;
const b = { timestamp: undefined } as AnyTransactionUi;

expect(sortTransactions({ transactionA: a, transactionB: b })).toBe(0);
expect(sortTransactions({ transactionA: b, transactionB: a })).toBe(0);
});
});

describe('isTransactionsStoreInitialized', () => {
Expand Down
Loading