mirror of
https://github.com/basicswap/basicswap.git
synced 2025-05-08 05:42:17 +00:00
Better error handling API / Tooltips: Rate, Market.
This commit is contained in:
parent
1845f802a2
commit
4d928dc98e
1 changed files with 137 additions and 81 deletions
|
@ -1081,10 +1081,8 @@ function filterAndSortData() {
|
||||||
return filteredData;
|
return filteredData;
|
||||||
}
|
}
|
||||||
|
|
||||||
function calculateProfitLoss(fromCoin, toCoin, fromAmount, toAmount, isOwnOffer) {
|
async function calculateProfitLoss(fromCoin, toCoin, fromAmount, toAmount, isOwnOffer) {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
// console.log(`Calculating profit/loss for ${fromAmount} ${fromCoin} to ${toAmount} ${toCoin}, isOwnOffer: ${isOwnOffer}`);
|
|
||||||
|
|
||||||
if (!latestPrices) {
|
if (!latestPrices) {
|
||||||
console.error('Latest prices not available. Unable to calculate profit/loss.');
|
console.error('Latest prices not available. Unable to calculate profit/loss.');
|
||||||
resolve(null);
|
resolve(null);
|
||||||
|
@ -1111,8 +1109,8 @@ function calculateProfitLoss(fromCoin, toCoin, fromAmount, toAmount, isOwnOffer)
|
||||||
const fromPriceUSD = latestPrices[fromSymbol]?.usd;
|
const fromPriceUSD = latestPrices[fromSymbol]?.usd;
|
||||||
const toPriceUSD = latestPrices[toSymbol]?.usd;
|
const toPriceUSD = latestPrices[toSymbol]?.usd;
|
||||||
|
|
||||||
if (!fromPriceUSD || !toPriceUSD) {
|
if (fromPriceUSD === null || toPriceUSD === null ||
|
||||||
//console.warn(`Price data missing for ${fromSymbol} (${fromPriceUSD}) or ${toSymbol} (${toPriceUSD})`);
|
fromPriceUSD === undefined || toPriceUSD === undefined) {
|
||||||
resolve(null);
|
resolve(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1127,7 +1125,6 @@ function calculateProfitLoss(fromCoin, toCoin, fromAmount, toAmount, isOwnOffer)
|
||||||
percentDiff = ((fromValueUSD / toValueUSD) - 1) * 100;
|
percentDiff = ((fromValueUSD / toValueUSD) - 1) * 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log(`Percent difference: ${percentDiff.toFixed(2)}%`);
|
|
||||||
resolve(percentDiff);
|
resolve(percentDiff);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1168,9 +1165,86 @@ async function getMarketRate(fromCoin, toCoin) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function fetchPricesWithApiKey(baseUrl) {
|
||||||
|
try {
|
||||||
|
console.log('Attempting price fetch with API key...');
|
||||||
|
const apiKeys = getAPIKeys();
|
||||||
|
const urlWithKey = `${baseUrl}&api_key=${offersConfig.apiKeys.coinGecko}`;
|
||||||
|
|
||||||
|
const response = await fetch('/json/readurl', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
url: urlWithKey,
|
||||||
|
headers: {}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
if (data && !data.Error && Object.keys(data).length > 0) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Failed to fetch prices with API key:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchPricesWithoutApiKey(baseUrl) {
|
||||||
|
try {
|
||||||
|
console.log('Attempting price fetch without API key...');
|
||||||
|
const response = await fetch('/json/readurl', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
url: baseUrl,
|
||||||
|
headers: {}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
if (data && !data.Error && Object.keys(data).length > 0) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Failed to fetch prices without API key:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getEmptyPriceData() {
|
||||||
|
return {
|
||||||
|
'bitcoin': { usd: null, btc: null },
|
||||||
|
'bitcoin-cash': { usd: null, btc: null },
|
||||||
|
'dash': { usd: null, btc: null },
|
||||||
|
'dogecoin': { usd: null, btc: null },
|
||||||
|
'decred': { usd: null, btc: null },
|
||||||
|
'litecoin': { usd: null, btc: null },
|
||||||
|
'particl': { usd: null, btc: null },
|
||||||
|
'pivx': { usd: null, btc: null },
|
||||||
|
'monero': { usd: null, btc: null },
|
||||||
|
'zano': { usd: null, btc: null },
|
||||||
|
'wownero': { usd: null, btc: null },
|
||||||
|
'zcoin': { usd: null, btc: null }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
async function fetchLatestPrices() {
|
async function fetchLatestPrices() {
|
||||||
const PRICES_CACHE_KEY = 'prices_coingecko';
|
const PRICES_CACHE_KEY = 'prices_coingecko';
|
||||||
const apiKeys = getAPIKeys();
|
|
||||||
|
|
||||||
const cachedData = CacheManager.get(PRICES_CACHE_KEY);
|
const cachedData = CacheManager.get(PRICES_CACHE_KEY);
|
||||||
if (cachedData && cachedData.remainingTime > 60000) {
|
if (cachedData && cachedData.remainingTime > 60000) {
|
||||||
|
@ -1179,57 +1253,31 @@ async function fetchLatestPrices() {
|
||||||
return cachedData.value;
|
return cachedData.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = `${offersConfig.apiEndpoints.coinGecko}/simple/price?ids=bitcoin,bitcoin-cash,dash,dogecoin,decred,litecoin,particl,pivx,monero,zano,wownero,zcoin&vs_currencies=USD,BTC&api_key=${offersConfig.apiKeys.coinGecko}`;
|
const baseUrl = `${offersConfig.apiEndpoints.coinGecko}/simple/price?ids=bitcoin,bitcoin-cash,dash,dogecoin,decred,litecoin,particl,pivx,monero,zano,wownero,zcoin&vs_currencies=USD,BTC`;
|
||||||
|
|
||||||
|
let data = await fetchPricesWithApiKey(baseUrl);
|
||||||
|
|
||||||
try {
|
if (!data) {
|
||||||
console.log('Initiating fresh price data fetch...');
|
data = await fetchPricesWithoutApiKey(baseUrl);
|
||||||
const response = await fetch('/json/readurl', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
url: url,
|
|
||||||
headers: {}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`HTTP Error: ${response.status} ${response.statusText}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
|
|
||||||
if (data.Error) {
|
|
||||||
console.error('API Error:', data.Error);
|
|
||||||
throw new Error(data.Error);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data && Object.keys(data).length > 0) {
|
|
||||||
console.log('Processing fresh price data...');
|
|
||||||
latestPrices = data;
|
|
||||||
CacheManager.set(PRICES_CACHE_KEY, data, CACHE_DURATION);
|
|
||||||
const fallbackLog = {};
|
|
||||||
Object.entries(data).forEach(([coin, prices]) => {
|
|
||||||
tableRateModule.setFallbackValue(coin, prices.usd);
|
|
||||||
fallbackLog[coin] = prices.usd;
|
|
||||||
});
|
|
||||||
|
|
||||||
//console.log('Fallback Values Set:', fallbackLog);
|
|
||||||
|
|
||||||
return data;
|
|
||||||
} else {
|
|
||||||
console.warn('No price data received');
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Price Fetch Error:', {
|
|
||||||
message: error.message,
|
|
||||||
name: error.name,
|
|
||||||
stack: error.stack
|
|
||||||
});
|
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
console.log('Processing fresh price data...');
|
||||||
|
latestPrices = data;
|
||||||
|
CacheManager.set(PRICES_CACHE_KEY, data, CACHE_DURATION);
|
||||||
|
|
||||||
|
// Set fallback values
|
||||||
|
Object.entries(data).forEach(([coin, prices]) => {
|
||||||
|
tableRateModule.setFallbackValue(coin, prices.usd);
|
||||||
|
});
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.warn('All price fetch attempts failed, using N/A values');
|
||||||
|
const naData = getEmptyPriceData();
|
||||||
|
latestPrices = naData;
|
||||||
|
return naData;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchOffers(manualRefresh = false) {
|
async function fetchOffers(manualRefresh = false) {
|
||||||
|
@ -2189,7 +2237,6 @@ function createRecipientTooltip(uniqueId, identityInfo, identity, successRate, t
|
||||||
|
|
||||||
function createTooltipContent(isSentOffers, coinFrom, coinTo, fromAmount, toAmount, isOwnOffer) {
|
function createTooltipContent(isSentOffers, coinFrom, coinTo, fromAmount, toAmount, isOwnOffer) {
|
||||||
if (!coinFrom || !coinTo) {
|
if (!coinFrom || !coinTo) {
|
||||||
//console.error(`Invalid coin names: coinFrom=${coinFrom}, coinTo=${coinTo}`);
|
|
||||||
return `<p class="font-bold mb-1">Unable to calculate profit/loss</p>
|
return `<p class="font-bold mb-1">Unable to calculate profit/loss</p>
|
||||||
<p>Invalid coin data.</p>`;
|
<p>Invalid coin data.</p>`;
|
||||||
}
|
}
|
||||||
|
@ -2199,7 +2246,10 @@ function createTooltipContent(isSentOffers, coinFrom, coinTo, fromAmount, toAmou
|
||||||
|
|
||||||
const getPriceKey = (coin) => {
|
const getPriceKey = (coin) => {
|
||||||
const lowerCoin = coin.toLowerCase();
|
const lowerCoin = coin.toLowerCase();
|
||||||
return lowerCoin === 'firo' || lowerCoin === 'zcoin' ? 'zcoin' : coinNameToSymbol[coin] || lowerCoin;
|
return lowerCoin === 'firo' || lowerCoin === 'zcoin' ? 'zcoin' :
|
||||||
|
lowerCoin === 'bitcoin cash' ? 'bitcoin-cash' :
|
||||||
|
lowerCoin === 'particl anon' || lowerCoin === 'particl blind' ? 'particl' :
|
||||||
|
coinNameToSymbol[coin] || lowerCoin;
|
||||||
};
|
};
|
||||||
|
|
||||||
const fromSymbol = getPriceKey(coinFrom);
|
const fromSymbol = getPriceKey(coinFrom);
|
||||||
|
@ -2207,9 +2257,14 @@ function createTooltipContent(isSentOffers, coinFrom, coinTo, fromAmount, toAmou
|
||||||
const fromPriceUSD = latestPrices[fromSymbol]?.usd;
|
const fromPriceUSD = latestPrices[fromSymbol]?.usd;
|
||||||
const toPriceUSD = latestPrices[toSymbol]?.usd;
|
const toPriceUSD = latestPrices[toSymbol]?.usd;
|
||||||
|
|
||||||
if (!fromPriceUSD || !toPriceUSD) {
|
if (fromPriceUSD === null || toPriceUSD === null ||
|
||||||
return `<p class="font-bold mb-1">Unable to calculate profit/loss</p>
|
fromPriceUSD === undefined || toPriceUSD === undefined) {
|
||||||
<p>Price data is missing for one or both coins.</p>`;
|
return `<p class="font-bold mb-1">Price Information Unavailable</p>
|
||||||
|
<p>Current market prices are temporarily unavailable.</p>
|
||||||
|
<p class="mt-2">You are ${isSentOffers ? 'selling' : 'buying'} ${fromAmount.toFixed(8)} ${coinFrom}
|
||||||
|
for ${toAmount.toFixed(8)} ${coinTo}.</p>
|
||||||
|
<p class="font-bold mt-2">Note:</p>
|
||||||
|
<p>Profit/loss calculations will be available when price data is restored.</p>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fromValueUSD = fromAmount * fromPriceUSD;
|
const fromValueUSD = fromAmount * fromPriceUSD;
|
||||||
|
@ -2260,37 +2315,38 @@ function createTooltipContent(isSentOffers, coinFrom, coinTo, fromAmount, toAmou
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createCombinedRateTooltip(offer, coinFrom, coinTo, isSentOffers, treatAsSentOffer) {
|
function createCombinedRateTooltip(offer, coinFrom, coinTo, treatAsSentOffer) {
|
||||||
const rate = parseFloat(offer.rate);
|
const rate = parseFloat(offer.rate) || 0;
|
||||||
const inverseRate = 1 / rate;
|
const inverseRate = rate ? (1 / rate) : 0;
|
||||||
|
const fromSymbol = getCoinSymbolLowercase(coinFrom);
|
||||||
|
const toSymbol = getCoinSymbolLowercase(coinTo);
|
||||||
|
|
||||||
const getPriceKey = (coin) => {
|
const fromPriceUSD = latestPrices[fromSymbol]?.usd;
|
||||||
const lowerCoin = coin.toLowerCase();
|
const toPriceUSD = latestPrices[toSymbol]?.usd;
|
||||||
if (lowerCoin === 'firo' || lowerCoin === 'zcoin') {
|
|
||||||
return 'zcoin';
|
|
||||||
}
|
|
||||||
if (lowerCoin === 'bitcoin cash') {
|
|
||||||
return 'bitcoin-cash';
|
|
||||||
}
|
|
||||||
return coinNameToSymbol[coin] || lowerCoin;
|
|
||||||
};
|
|
||||||
|
|
||||||
const fromSymbol = getPriceKey(coinFrom);
|
if (fromPriceUSD === null || toPriceUSD === null ||
|
||||||
const toSymbol = getPriceKey(coinTo);
|
fromPriceUSD === undefined || toPriceUSD === undefined) {
|
||||||
|
return `
|
||||||
|
<p class="font-bold mb-1">Exchange Rate Information</p>
|
||||||
|
<p>Market price data is temporarily unavailable.</p>
|
||||||
|
<p class="font-bold mt-2">Current Offer Rates:</p>
|
||||||
|
<p>1 ${coinFrom} = ${rate.toFixed(8)} ${coinTo}</p>
|
||||||
|
<p>1 ${coinTo} = ${inverseRate.toFixed(8)} ${coinFrom}</p>
|
||||||
|
<p class="font-bold mt-2">Note:</p>
|
||||||
|
<p>Market comparison will be available when price data is restored.</p>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
const fromPriceUSD = latestPrices[fromSymbol]?.usd || 0;
|
|
||||||
const toPriceUSD = latestPrices[toSymbol]?.usd || 0;
|
|
||||||
const rateInUSD = rate * toPriceUSD;
|
const rateInUSD = rate * toPriceUSD;
|
||||||
|
|
||||||
const marketRate = fromPriceUSD / toPriceUSD;
|
const marketRate = fromPriceUSD / toPriceUSD;
|
||||||
|
|
||||||
const percentDiff = ((rate - marketRate) / marketRate) * 100;
|
const percentDiff = marketRate ? ((rate - marketRate) / marketRate) * 100 : 0;
|
||||||
const formattedPercentDiff = percentDiff.toFixed(2);
|
const formattedPercentDiff = percentDiff.toFixed(2);
|
||||||
const percentDiffDisplay = formattedPercentDiff === "0.00" ? "0.00" :
|
const percentDiffDisplay = formattedPercentDiff === "0.00" ? "0.00" :
|
||||||
(percentDiff > 0 ? `+${formattedPercentDiff}` : formattedPercentDiff);
|
(percentDiff > 0 ? `+${formattedPercentDiff}` : formattedPercentDiff);
|
||||||
const aboveOrBelow = percentDiff > 0 ? "above" : percentDiff < 0 ? "below" : "at";
|
const aboveOrBelow = percentDiff > 0 ? "above" : percentDiff < 0 ? "below" : "at";
|
||||||
|
|
||||||
const action = isSentOffers || treatAsSentOffer ? "selling" : "buying";
|
const action = treatAsSentOffer ? "selling" : "buying";
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<p class="font-bold mb-1">Exchange Rate Explanation:</p>
|
<p class="font-bold mb-1">Exchange Rate Explanation:</p>
|
||||||
|
|
Loading…
Reference in a new issue