* AMM

* LINT + Fixes

* Remove unused global variables.

* BLACK

* BLACK

* AMM - Various Fixes/Features/Bug Fixes.

* FLAKE

* FLAKE

* BLACK

* Small fix

* Fix

* Auto-start option AMM + Various fixes/bugs/styling.

* Updated createoffers.py

* BLACK

* AMM Styling

* Update bid_xmr template confirm model.

* Fixed bug with Create Default Configuration + Added confirm modal.

* Fix: Better redirect.

* Fixed adjust_rates_based_on_market + Removed debug / extra logging + Various fixes.

* GUI v3.2.2

* Fix sub-header your-offers count when created offers by AMM.

* Fix math.

* Added USD prices + Add offers/bids checkbox enabled always checked.

* Donation page.

* Updated header.html + Typo.

* Update on createoffer.py + BLACK

* AMM: html updates.

* AMM: Add all, minrate, and static options.

* AMM: Amount step default 0.001

* Fix global settings.

* Update createoffers.py

* Fixed bug with autostart when save global settings + Various layout fixes.

* Fixed bug with autostart with add/edit  + Added new option Orderbook (Auto-Accept)

* Fixed debug + New feature attempt bids first.

* Fix: Orderbook (Auto-Accept)

* Added bidding strategy:  Only bid on auto-accept offers (best rates from auto-accept only)

* Fix: with_extra_info

* Small fix automation_strat_id

* Various fixes.

* Final fixes
This commit is contained in:
Gerlof van Ek
2025-06-08 17:43:01 +02:00
committed by GitHub
parent 13847e129b
commit d08e09061f
19 changed files with 8374 additions and 354 deletions

View File

@@ -0,0 +1,255 @@
const AmmCounterManager = (function() {
const config = {
refreshInterval: 10000,
ammStatusEndpoint: '/amm/status',
retryDelay: 5000,
maxRetries: 3,
debug: false
};
let refreshTimer = null;
let fetchRetryCount = 0;
let lastAmmStatus = null;
function isDebugEnabled() {
return localStorage.getItem('amm_debug_enabled') === 'true' || config.debug;
}
function debugLog(message, data) {
// if (isDebugEnabled()) {
// if (data) {
// console.log(`[AmmCounter] ${message}`, data);
// } else {
// console.log(`[AmmCounter] ${message}`);
// }
// }
}
function updateAmmCounter(count, status) {
const ammCounter = document.getElementById('amm-counter');
const ammCounterMobile = document.getElementById('amm-counter-mobile');
debugLog(`Updating AMM counter: count=${count}, status=${status}`);
if (ammCounter) {
ammCounter.textContent = count;
ammCounter.classList.remove('bg-blue-500', 'bg-gray-400');
ammCounter.classList.add(status === 'running' && count > 0 ? 'bg-blue-500' : 'bg-gray-400');
}
if (ammCounterMobile) {
ammCounterMobile.textContent = count;
ammCounterMobile.classList.remove('bg-blue-500', 'bg-gray-400');
ammCounterMobile.classList.add(status === 'running' && count > 0 ? 'bg-blue-500' : 'bg-gray-400');
}
updateAmmTooltips(count, status);
}
function updateAmmTooltips(count, status) {
debugLog(`updateAmmTooltips called with count=${count}, status=${status}`);
const subheaderTooltip = document.getElementById('tooltip-amm-subheader');
debugLog('Looking for tooltip-amm-subheader element:', subheaderTooltip);
if (subheaderTooltip) {
const statusText = status === 'running' ? 'Active' : 'Inactive';
const newContent = `
<p><b>Status:</b> ${statusText}</p>
<p><b>Currently active offers/bids:</b> ${count}</p>
`;
const statusParagraph = subheaderTooltip.querySelector('p:first-child');
const countParagraph = subheaderTooltip.querySelector('p:last-child');
if (statusParagraph && countParagraph) {
statusParagraph.innerHTML = `<b>Status:</b> ${statusText}`;
countParagraph.innerHTML = `<b>Currently active offers/bids:</b> ${count}`;
debugLog(`Updated AMM subheader tooltip paragraphs: status=${statusText}, count=${count}`);
} else {
subheaderTooltip.innerHTML = newContent;
debugLog(`Replaced AMM subheader tooltip content: status=${statusText}, count=${count}`);
}
refreshTooltipInstances('tooltip-amm-subheader', newContent);
} else {
debugLog('AMM subheader tooltip element not found - checking all tooltip elements');
const allTooltips = document.querySelectorAll('[id*="tooltip"]');
debugLog('All tooltip elements found:', Array.from(allTooltips).map(el => el.id));
}
}
function refreshTooltipInstances(tooltipId, newContent) {
const triggers = document.querySelectorAll(`[data-tooltip-target="${tooltipId}"]`);
triggers.forEach(trigger => {
if (trigger._tippy) {
trigger._tippy.setContent(newContent);
debugLog(`Updated Tippy instance content for ${tooltipId}`);
} else {
if (window.TooltipManager && typeof window.TooltipManager.create === 'function') {
window.TooltipManager.create(trigger, newContent, {
placement: trigger.getAttribute('data-tooltip-placement') || 'top'
});
debugLog(`Created new Tippy instance for ${tooltipId}`);
}
}
});
if (window.TooltipManager && typeof window.TooltipManager.refreshTooltip === 'function') {
window.TooltipManager.refreshTooltip(tooltipId, newContent);
debugLog(`Refreshed tooltip via TooltipManager for ${tooltipId}`);
}
if (window.TooltipManager && typeof window.TooltipManager.initializeTooltips === 'function') {
setTimeout(() => {
window.TooltipManager.initializeTooltips(`[data-tooltip-target="${tooltipId}"]`);
debugLog(`Re-initialized tooltips for ${tooltipId}`);
}, 50);
}
}
function fetchAmmStatus() {
debugLog('Fetching AMM status...');
let url = config.ammStatusEndpoint;
if (isDebugEnabled()) {
url += '?debug=true';
}
return fetch(url, {
headers: {
'Accept': 'application/json',
'Cache-Control': 'no-cache',
'Pragma': 'no-cache'
}
})
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.json();
})
.then(data => {
lastAmmStatus = data;
debugLog('AMM status data received:', data);
updateAmmCounter(data.amm_active_count, data.status);
fetchRetryCount = 0;
return data;
})
.catch(error => {
if (isDebugEnabled()) {
console.error('[AmmCounter] AMM status fetch error:', error);
}
if (fetchRetryCount < config.maxRetries) {
fetchRetryCount++;
debugLog(`Retrying AMM status fetch (${fetchRetryCount}/${config.maxRetries}) in ${config.retryDelay/1000}s`);
return new Promise(resolve => {
setTimeout(() => {
resolve(fetchAmmStatus());
}, config.retryDelay);
});
} else {
fetchRetryCount = 0;
throw error;
}
});
}
function startRefreshTimer() {
stopRefreshTimer();
debugLog('Starting AMM status refresh timer');
fetchAmmStatus()
.then(() => {})
.catch(() => {});
refreshTimer = setInterval(() => {
fetchAmmStatus()
.then(() => {})
.catch(() => {});
}, config.refreshInterval);
}
function stopRefreshTimer() {
if (refreshTimer) {
debugLog('Stopping AMM status refresh timer');
clearInterval(refreshTimer);
refreshTimer = null;
}
}
function setupWebSocketHandler() {
if (window.WebSocketManager && typeof window.WebSocketManager.addMessageHandler === 'function') {
debugLog('Setting up WebSocket handler for AMM status updates');
window.WebSocketManager.addMessageHandler('message', (data) => {
if (data && data.event) {
debugLog('WebSocket event received, refreshing AMM status');
fetchAmmStatus()
.then(() => {})
.catch(() => {});
}
});
}
}
function setupDebugListener() {
const debugCheckbox = document.getElementById('amm_debug');
if (debugCheckbox) {
debugLog('Found AMM debug checkbox, setting up listener');
localStorage.setItem('amm_debug_enabled', debugCheckbox.checked ? 'true' : 'false');
debugCheckbox.addEventListener('change', function() {
localStorage.setItem('amm_debug_enabled', this.checked ? 'true' : 'false');
debugLog(`Debug mode ${this.checked ? 'enabled' : 'disabled'}`);
});
}
}
const publicAPI = {
initialize: function(options = {}) {
Object.assign(config, options);
setupWebSocketHandler();
setupDebugListener();
startRefreshTimer();
debugLog('AMM Counter Manager initialized');
if (window.CleanupManager) {
window.CleanupManager.registerResource('ammCounterManager', this, (mgr) => mgr.dispose());
}
return this;
},
fetchAmmStatus: fetchAmmStatus,
updateCounter: updateAmmCounter,
updateTooltips: updateAmmTooltips,
startRefreshTimer: startRefreshTimer,
stopRefreshTimer: stopRefreshTimer,
dispose: function() {
debugLog('Disposing AMM Counter Manager');
stopRefreshTimer();
}
};
return publicAPI;
})();
document.addEventListener('DOMContentLoaded', function() {
if (!window.ammCounterManagerInitialized) {
window.AmmCounterManager = AmmCounterManager.initialize();
window.ammCounterManagerInitialized = true;
}
});

File diff suppressed because it is too large Load Diff

View File

@@ -30,7 +30,7 @@ const state = {
document.addEventListener('tabactivated', function(event) {
if (event.detail && event.detail.tabId) {
const tabType = event.detail.type || (event.detail.tabId === '#all' ? 'all' :
const tabType = event.detail.type || (event.detail.tabId === '#all' ? 'all' :
(event.detail.tabId === '#sent' ? 'sent' : 'received'));
//console.log('Tab activation event received for:', tabType);
state.currentTab = tabType;
@@ -555,7 +555,7 @@ function filterAndSortData(bids) {
const coinName = selectedOption?.textContent.trim();
if (coinName) {
const coinToMatch = state.currentTab === 'all'
const coinToMatch = state.currentTab === 'all'
? (bid.source === 'sent' ? bid.coin_to : bid.coin_from)
: (state.currentTab === 'sent' ? bid.coin_to : bid.coin_from);
if (!coinMatches(coinToMatch, coinName)) {
@@ -614,7 +614,7 @@ function filterAndSortData(bids) {
let matchesDisplayedLabel = false;
if (!matchesLabel && document) {
try {
const tableId = state.currentTab === 'sent' ? 'sent' :
const tableId = state.currentTab === 'sent' ? 'sent' :
(state.currentTab === 'received' ? 'received' : 'all');
const cells = document.querySelectorAll(`#${tableId} a[href^="/identity/"]`);
@@ -1056,6 +1056,24 @@ async function fetchAllBids() {
receivedResponse.json()
]);
if (sentData.error || receivedData.error) {
const errorData = sentData.error ? sentData : receivedData;
if (errorData.locked) {
const tbody = elements.allBidsBody;
if (tbody) {
tbody.innerHTML = `
<tr>
<td colspan="8" class="text-center py-4 text-yellow-600 dark:text-yellow-400">
${errorData.error}
</td>
</tr>`;
}
return [];
} else {
throw new Error(errorData.error);
}
}
const filteredSentData = filterAndSortData(sentData).map(bid => ({ ...bid, source: 'sent' }));
const filteredReceivedData = filterAndSortData(receivedData).map(bid => ({ ...bid, source: 'received' }));
@@ -1090,7 +1108,7 @@ const createTableRow = async (bid) => {
const timeColor = getTimeStrokeColor(bid.expire_at);
const currentTabIsAll = state.currentTab === 'all';
const isSent = currentTabIsAll ? (bid.source === 'sent') : (state.currentTab === 'sent');
const sourceIndicator = currentTabIsAll ?
const sourceIndicator = currentTabIsAll ?
`<span class="ml-1 px-1.5 py-0.5 text-xs font-medium ${isSent ? 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300' : 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300'} rounded">
${isSent ? 'Sent' : 'Received'}
</span>` : '';
@@ -1321,6 +1339,24 @@ async function fetchBids(type = state.currentTab) {
}
const data = await response.json();
if (data.error) {
if (data.locked) {
const tbody = elements[`${type}BidsBody`];
if (tbody) {
tbody.innerHTML = `
<tr>
<td colspan="8" class="text-center py-4 text-yellow-600 dark:text-yellow-400">
${data.error}
</td>
</tr>`;
}
return [];
} else {
throw new Error(data.error);
}
}
//console.log(`Received raw ${type} data:`, data.length, 'bids');
state.filters.with_expired = includeExpired;
@@ -1509,14 +1545,13 @@ const updateBidsTable = async () => {
updateLoadingState(true);
let bids;
if (state.currentTab === 'all') {
bids = await fetchAllBids();
} else {
bids = await fetchBids();
}
// Add identity preloading if we're searching
if (state.filters.searchQuery && state.filters.searchQuery.length > 0) {
await preloadIdentitiesForSearch(bids);
}
@@ -1774,7 +1809,7 @@ const setupRefreshButtons = () => {
state.data[lowerType] = data;
}
await updateTableContent(lowerType);
updatePaginationControls(lowerType);
@@ -1802,7 +1837,7 @@ const switchTab = (tabId) => {
tooltipIdsToCleanup.clear();
state.currentTab = tabId === '#all' ? 'all' :
state.currentTab = tabId === '#all' ? 'all' :
(tabId === '#sent' ? 'sent' : 'received');
elements.allContent.classList.add('hidden');
@@ -1888,7 +1923,7 @@ const setupEventListeners = () => {
elements.sentContent.classList.toggle('hidden', targetId !== '#sent');
elements.receivedContent.classList.toggle('hidden', targetId !== '#received');
state.currentTab = targetId === '#all' ? 'all' :
state.currentTab = targetId === '#all' ? 'all' :
(targetId === '#sent' ? 'sent' : 'received');
state.currentPage[state.currentTab] = 1;
@@ -2101,7 +2136,7 @@ function initialize() {
}, 100);
setupMemoryMonitoring();
window.cleanupBidsTable = cleanup;
}

View File

@@ -4,7 +4,8 @@ const SummaryManager = (function() {
summaryEndpoint: '/json',
retryDelay: 5000,
maxRetries: 3,
requestTimeout: 15000
requestTimeout: 15000,
debug: false
};
let refreshTimer = null;
@@ -60,12 +61,15 @@ const SummaryManager = (function() {
updateElement('network-offers-counter', data.num_network_offers);
updateElement('offers-counter', data.num_sent_active_offers);
updateElement('offers-counter-mobile', data.num_sent_active_offers);
updateElement('sent-bids-counter', data.num_sent_active_bids);
updateElement('recv-bids-counter', data.num_recv_active_bids);
updateElement('bid-requests-counter', data.num_available_bids);
updateElement('swaps-counter', data.num_swapping);
updateElement('watched-outputs-counter', data.num_watched_outputs);
updateTooltips(data);
const shutdownButtons = document.querySelectorAll('.shutdown-button');
shutdownButtons.forEach(button => {
button.setAttribute('data-active-swaps', data.num_swapping);
@@ -81,6 +85,100 @@ const SummaryManager = (function() {
});
}
function updateTooltips(data) {
debugLog(`updateTooltips called with data:`, data);
const yourOffersTooltip = document.getElementById('tooltip-your-offers');
debugLog('Looking for tooltip-your-offers element:', yourOffersTooltip);
if (yourOffersTooltip) {
const newContent = `
<p><b>Total offers:</b> ${data.num_sent_offers || 0}</p>
<p><b>Active offers:</b> ${data.num_sent_active_offers || 0}</p>
`;
const totalParagraph = yourOffersTooltip.querySelector('p:first-child');
const activeParagraph = yourOffersTooltip.querySelector('p:last-child');
debugLog('Found paragraphs:', { totalParagraph, activeParagraph });
if (totalParagraph && activeParagraph) {
totalParagraph.innerHTML = `<b>Total offers:</b> ${data.num_sent_offers || 0}`;
activeParagraph.innerHTML = `<b>Active offers:</b> ${data.num_sent_active_offers || 0}`;
debugLog(`Updated Your Offers tooltip paragraphs: total=${data.num_sent_offers}, active=${data.num_sent_active_offers}`);
} else {
yourOffersTooltip.innerHTML = newContent;
debugLog(`Replaced Your Offers tooltip content: total=${data.num_sent_offers}, active=${data.num_sent_active_offers}`);
}
refreshTooltipInstances('tooltip-your-offers', newContent);
} else {
debugLog('Your Offers tooltip element not found - checking all tooltip elements');
const allTooltips = document.querySelectorAll('[id*="tooltip"]');
debugLog('All tooltip elements found:', Array.from(allTooltips).map(el => el.id));
}
const bidsTooltip = document.getElementById('tooltip-bids');
if (bidsTooltip) {
const newBidsContent = `
<p><b>Sent bids:</b> ${data.num_sent_bids || 0} (${data.num_sent_active_bids || 0} active)</p>
<p><b>Received bids:</b> ${data.num_recv_bids || 0} (${data.num_recv_active_bids || 0} active)</p>
`;
const sentParagraph = bidsTooltip.querySelector('p:first-child');
const recvParagraph = bidsTooltip.querySelector('p:last-child');
if (sentParagraph && recvParagraph) {
sentParagraph.innerHTML = `<b>Sent bids:</b> ${data.num_sent_bids || 0} (${data.num_sent_active_bids || 0} active)`;
recvParagraph.innerHTML = `<b>Received bids:</b> ${data.num_recv_bids || 0} (${data.num_recv_active_bids || 0} active)`;
debugLog(`Updated Bids tooltip: sent=${data.num_sent_bids}(${data.num_sent_active_bids}), recv=${data.num_recv_bids}(${data.num_recv_active_bids})`);
} else {
bidsTooltip.innerHTML = newBidsContent;
debugLog(`Replaced Bids tooltip content: sent=${data.num_sent_bids}(${data.num_sent_active_bids}), recv=${data.num_recv_bids}(${data.num_recv_active_bids})`);
}
refreshTooltipInstances('tooltip-bids', newBidsContent);
} else {
debugLog('Bids tooltip element not found');
}
}
function refreshTooltipInstances(tooltipId, newContent) {
const triggers = document.querySelectorAll(`[data-tooltip-target="${tooltipId}"]`);
triggers.forEach(trigger => {
if (trigger._tippy) {
trigger._tippy.setContent(newContent);
debugLog(`Updated Tippy instance content for ${tooltipId}`);
} else {
if (window.TooltipManager && typeof window.TooltipManager.create === 'function') {
window.TooltipManager.create(trigger, newContent, {
placement: trigger.getAttribute('data-tooltip-placement') || 'top'
});
debugLog(`Created new Tippy instance for ${tooltipId}`);
}
}
});
if (window.TooltipManager && typeof window.TooltipManager.refreshTooltip === 'function') {
window.TooltipManager.refreshTooltip(tooltipId, newContent);
debugLog(`Refreshed tooltip via TooltipManager for ${tooltipId}`);
}
if (window.TooltipManager && typeof window.TooltipManager.initializeTooltips === 'function') {
setTimeout(() => {
window.TooltipManager.initializeTooltips(`[data-tooltip-target="${tooltipId}"]`);
debugLog(`Re-initialized tooltips for ${tooltipId}`);
}, 50);
}
}
function debugLog(message) {
if (config.debug && console && console.log) {
console.log(`[SummaryManager] ${message}`);
}
}
function cacheSummaryData(data) {
if (!data) return;
@@ -303,6 +401,14 @@ const SummaryManager = (function() {
});
},
updateTooltips: function(data) {
updateTooltips(data || lastSuccessfulData);
},
updateUI: function(data) {
updateUIFromData(data || lastSuccessfulData);
},
startRefreshTimer: function() {
startRefreshTimer();
},

View File

@@ -217,8 +217,8 @@ function filterAndSortData() {
const sentFromFilter = filters.sent_from || 'any';
filteredData = filteredData.filter(offer => {
const isMatch = sentFromFilter === 'public' ? offer.is_public :
sentFromFilter === 'private' ? !offer.is_public :
const isMatch = sentFromFilter === 'public' ? offer.is_public :
sentFromFilter === 'private' ? !offer.is_public :
true;
return isMatch;
});
@@ -232,7 +232,7 @@ function filterAndSortData() {
const coinToSelect = document.getElementById('coin_to');
const selectedOption = coinToSelect?.querySelector(`option[value="${filters.coin_to}"]`);
const coinName = selectedOption?.textContent.trim();
if (coinName && !coinMatches(offer.coin_to, coinName)) {
return false;
}
@@ -254,13 +254,13 @@ function filterAndSortData() {
let statusMatch = false;
switch (filters.status) {
case 'active':
case 'active':
statusMatch = !isExpired && !isRevoked;
break;
case 'expired':
case 'expired':
statusMatch = isExpired && !isRevoked;
break;
case 'revoked':
case 'revoked':
statusMatch = isRevoked;
break;
}
@@ -275,7 +275,7 @@ function filterAndSortData() {
if (currentSortColumn === 7) {
const offersWithPercentages = [];
for (const offer of filteredData) {
const fromAmount = parseFloat(offer.amount_from) || 0;
const toAmount = parseFloat(offer.amount_to) || 0;
@@ -293,7 +293,7 @@ function filterAndSortData() {
if (fromPriceUSD && toPriceUSD && !isNaN(fromPriceUSD) && !isNaN(toPriceUSD)) {
const fromValueUSD = fromAmount * fromPriceUSD;
const toValueUSD = toAmount * toPriceUSD;
if (fromValueUSD && toValueUSD) {
if (offer.is_own_offer || isSentOffers) {
percentDiff = ((toValueUSD / fromValueUSD) - 1) * 100;
@@ -302,7 +302,7 @@ function filterAndSortData() {
}
}
}
offersWithPercentages.push({
offer: offer,
percentDiff: percentDiff
@@ -353,7 +353,7 @@ function filterAndSortData() {
const bRate = parseFloat(b.rate) || 0;
const aPriceUSD = latestPrices && aSymbol ? latestPrices[aSymbol]?.usd : null;
const bPriceUSD = latestPrices && bSymbol ? latestPrices[bSymbol]?.usd : null;
aValue = aPriceUSD && !isNaN(aPriceUSD) ? aRate * aPriceUSD : 0;
bValue = bPriceUSD && !isNaN(bPriceUSD) ? bRate * bPriceUSD : 0;
break;
@@ -367,8 +367,8 @@ function filterAndSortData() {
}
if (typeof aValue === 'string' && typeof bValue === 'string') {
return currentSortDirection === 'asc'
? aValue.localeCompare(bValue)
return currentSortDirection === 'asc'
? aValue.localeCompare(bValue)
: bValue.localeCompare(aValue);
}
@@ -395,7 +395,7 @@ async function calculateProfitLoss(fromCoin, toCoin, fromAmount, toAmount, isOwn
normalizedCoin = window.CoinManager.getPriceKey(coin) || normalizedCoin;
} else {
if (normalizedCoin === 'zcoin') normalizedCoin = 'firo';
if (normalizedCoin === 'bitcoincash' || normalizedCoin === 'bitcoin cash')
if (normalizedCoin === 'bitcoincash' || normalizedCoin === 'bitcoin cash')
normalizedCoin = 'bitcoin-cash';
if (normalizedCoin.includes('particl')) normalizedCoin = 'particl';
}
@@ -481,6 +481,29 @@ async function fetchOffers() {
}
const data = await offersResponse.json();
if (data.error) {
if (data.locked) {
if (typeof ui !== 'undefined' && ui.displayErrorMessage) {
ui.displayErrorMessage(data.error);
} else {
offersBody.innerHTML = `
<tr>
<td colspan="9" class="text-center py-8">
<div class="flex flex-col items-center justify-center text-yellow-600 dark:text-yellow-400">
<svg class="w-8 h-8 mb-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L4.082 16.5c-.77.833.192 2.5 1.732 2.5z"></path>
</svg>
<span class="font-medium">${data.error}</span>
</div>
</td>
</tr>`;
}
return;
} else {
throw new Error(data.error);
}
}
const processedData = Array.isArray(data) ? data : Object.values(data);
jsonData = formatInitialData(processedData);
@@ -980,7 +1003,7 @@ function createTableRow(offer, identity = null) {
} = offer;
let coinFromSymbol, coinToSymbol;
if (window.CoinManager) {
coinFromSymbol = window.CoinManager.getSymbol(coinFrom) || coinFrom.toLowerCase();
coinToSymbol = window.CoinManager.getSymbol(coinTo) || coinTo.toLowerCase();
@@ -1572,7 +1595,7 @@ function createCombinedRateTooltip(offer, coinFrom, coinTo, treatAsSentOffer) {
const getPriceKey = (coin) => {
if (!coin) return null;
const lowerCoin = coin.toLowerCase();
if (lowerCoin === 'zcoin') return 'firo';
@@ -1807,7 +1830,7 @@ function getPriceKey(coin) {
}
if (!coin) return null;
const lowerCoin = coin.toLowerCase();
if (lowerCoin === 'zcoin') {
@@ -1818,7 +1841,7 @@ function getPriceKey(coin) {
return 'bitcoin-cash';
}
if (lowerCoin === 'part' || lowerCoin === 'particl' ||
if (lowerCoin === 'part' || lowerCoin === 'particl' ||
lowerCoin.includes('particl')) {
return 'particl';
}
@@ -2221,7 +2244,7 @@ document.addEventListener('DOMContentLoaded', async function() {
}
await updateOffersTable();
updateProfitLossDisplays();
document.querySelectorAll('.usd-value').forEach(usdValue => {