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
45 changes: 34 additions & 11 deletions src/components/dashboard/MinerRatesTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,21 +49,39 @@ const pairStr = (m: Miner) =>
const statusRank = (m: Miner) =>
!m.isActive ? 3 : m.hasActiveSwap ? 2 : m.isReserved ? 1 : 0;

// Sort by the stronger of the two rates so bidirectional miners aren't penalized
// by a low counter side, and one-way miners still sort by their single quote.
const maxRate = (m: Miner) =>
Math.max(parseRate(m.rate), parseRate(m.counterRate));
// Forward (BTC→TAO) and reverse (TAO→BTC) are both quoted as TAO per 1 BTC, but
// "good" runs in opposite directions: higher forward = customer receives more TAO,
// lower reverse = customer pays less TAO. A naive max() of the two treats a
// quote-rejecting reverse value (e.g. 1,000,000 τ) as "the best rate" and sorts
// the miner to the top. Score by whichever side matches the active direction
// filter so sort desc always means "most attractive counterparty first."
const rateScore = (m: Miner, filter: DirectionFilter): number => {
const fwd = parseRate(m.rate);
const rev = parseRate(m.counterRate);
switch (filter) {
case 'reverse':
return rev > 0 ? -rev : -Infinity;
case 'both':
return fwd > 0 && rev > 0 ? -(rev - fwd) : -Infinity;
case 'forward':
case 'all':
default:
return fwd > 0 ? fwd : -Infinity;
}
};

const getSortValue = (m: Miner, key: SortKey): string | number => {
const getSortValue = (
m: Miner,
key: SortKey,
filter: DirectionFilter,
): string | number => {
switch (key) {
case 'uid':
return m.uid;
case 'pair':
return pairStr(m);
case 'rate': {
const v = maxRate(m);
return v > 0 ? v : -1;
}
case 'rate':
return rateScore(m, filter);
case 'collateral':
return parseInt(m.collateralRao, 10) || 0;
case 'status':
Expand Down Expand Up @@ -163,8 +181,13 @@ const MinerRatesTable: React.FC = () => {
}
});
const sorted = [...directionFiltered].sort((a, b) => {
const av = getSortValue(a, sortKey);
const bv = getSortValue(b, sortKey);
// Status is always the primary key so the dashboard reads top-to-bottom
// as "who can I trade with right now": Available → Reserved → Exchanging
// → Inactive. The user's chosen column orders within each group.
const statusCmp = statusRank(a) - statusRank(b);
if (statusCmp !== 0) return statusCmp;
const av = getSortValue(a, sortKey, direction);
const bv = getSortValue(b, sortKey, direction);
const cmp = av < bv ? -1 : av > bv ? 1 : 0;
return sortDir === 'asc' ? cmp : -cmp;
});
Expand Down
5 changes: 5 additions & 0 deletions src/components/dashboard/OrderbookDepth.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,11 @@ const OrderbookDepth: React.FC = () => {
const groups: Record<string, number> = {}; // key = rate, val = collateral TAO

miners.forEach((m) => {
// Inactive miners aren't tradeable depth — they still have a quote
// stored on-chain but no one can hit it. Including them produces
// ghost rows (dust collateral at extreme rates) that stretch the
// rate axis and contradict this panel's "active miners" framing.
if (!m.isActive) return;
if (!m.collateralRao) return;
const s = m.sourceChain?.toLowerCase();
const d = m.destChain?.toLowerCase();
Expand Down
Loading