ui: Added wownero (chart/price/images), WIP on coingecko fallback system for chart/coin prices.

- Added wownero .png coin logos.
- (WIP) Added coingecko api / key as fallback system for the chart/coin prices.
- Change the api / keys for coingecko at settings.
- Added special function for getting wownero chart/coin data/price from CoinGecko and not CryptocCmpare.
2024-05-20_merge
gerlofvanek 9 months ago committed by tecnovert
parent d83555c25f
commit e90292fb2f
  1. 19
      basicswap/basicswap.py
  2. BIN
      basicswap/static/images/coins/Wownero-20.png
  3. BIN
      basicswap/static/images/coins/Wownero.png
  4. 191
      basicswap/templates/offers.html
  5. 11
      basicswap/templates/settings.html
  6. 13
      basicswap/ui/page_offers.py
  7. 8
      basicswap/ui/page_settings.py

@ -6447,6 +6447,25 @@ class BasicSwap(BaseApp):
settings_copy.pop('chart_api_key')
settings_changed = True
if 'coingecko_api_key' in data:
new_value = data['coingecko_api_key']
ensure(isinstance(new_value, str), 'New coingecko_api_key value not a string')
ensure(len(new_value) <= 128, 'New coingecko_api_keyvalue too long')
if all(c in string.hexdigits for c in new_value):
if settings_copy.get('coingecko_api_key', '') != new_value:
settings_copy['coingecko_api_key'] = new_value
if 'coingecko_api_key_enc' in settings_copy:
settings_copy.pop('coingecko_api_key_enc')
settings_changed = True
else:
# Encode value as hex to avoid escaping
new_value = new_value.encode('utf-8').hex()
if settings_copy.get('coingecko_api_key_enc', '') != new_value:
settings_copy['coingecko_api_key_enc'] = new_value
if 'coingecko_api_key' in settings_copy:
settings_copy.pop('coingecko_api_key')
settings_changed = True
if settings_changed:
settings_path = os.path.join(self.data_dir, cfg.CONFIG_FILENAME)
settings_path_new = settings_path + '.new'

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

@ -55,7 +55,7 @@
<div id="error-overlay" class="error-overlay hidden absolute inset-0 flex items-center justify-center">
<div id="error-content" class="error-content bg-coolGray-100 dark:bg-gray-500 rounded-md p-4 non-blurred">
<p class="text-red-600 font-semibold text-center">Error:</p>
<p class="text-sm text-gray-700 dark:text-gray-300 mt-5 text-center">To review or update your Chart API Key, navigate to <a href="/settings">Settings & Tools > Settings > General (TAB).</a></p>
<p class="text-sm text-gray-700 dark:text-gray-300 mt-5 text-center">To review or update your Chart API Key(s), navigate to <a href="/settings">Settings & Tools > Settings > General (TAB).</a></p>
</div>
</div>
</div>
@ -359,16 +359,77 @@
</div>
</div>
<div class="w-full sm:w-1/2 lg:w-1/6 p-3" id="wow-container">
<div class="px-5 py-3 h-full bg-coolGray-100 dark:bg-gray-500 rounded-2xl dark:text-white">
<div class="flex items-center">
<img src="/static/images/coins/Wownero.png" class="rounded-xl" style="width: 35px; height: 35px; object-fit: contain;" alt="WOWNERO">
<p class="ml-2 text-black text-md dark:text-white">
Wownero (WOW)
</p>
</div>
<div class="flex flex-col justify-start">
<p class="my-2 text-xl font-bold text-left text-gray-700 dark:text-gray-100" id="wow-price-usd-value">
<span class="text-sm">
<span id="wow-price-usd-value" style="min-width: 80px;"></span>
</span>
</p>
<div class="flex items-center text-sm">
<div class="w-auto">
<div id="wow-price-change-container" class="w-auto p-1"></div>
</div>
</div>
<div class="flex items-center text-xs text-gray-600 dark:text-gray-300 mt-2">
<span class="bold mr-2">VOL:</span>
<div id="wow-volume-24h">
</div>
</div>
<div class="flex items-center text-xs text-gray-600 dark:text-gray-300 mt-2 hidden">
<span class="bold mr-2">BTC:</span>
<span id="wow-price-btc">
</span>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<script>
<script>
window.addEventListener('load', function() {
const api_key = '{{chart_api_key}}';
const coins = ['BTC', 'PART', 'XMR', 'LTC', 'FIRO', 'DASH', 'PIVX', 'DOGE', 'ETH'];
coins.forEach(coin => {
fetch(`https://min-api.cryptocompare.com/data/pricemultifull?fsyms=${coin}&tsyms=USD,BTC&api_key=${api_key}`)
const coins = ['BTC', 'PART', 'XMR', 'LTC', 'FIRO', 'DASH', 'PIVX', 'DOGE', 'ETH', 'WOW'];
const api_key = '{{chart_api_key}}';
const coinGeckoApiKey = '{{coingecko_api_key}}';
coins.forEach(coin => {
if (coin === 'WOW') {
fetchWowneroData();
} else {
fetchCryptoCompareData(coin, api_key);
}
});
updateChart('BTC');
});
function fetchCryptoCompareData(coin, api_key) {
fetch(`https://min-api.cryptocompare.com/data/pricemultifull?fsyms=${coin}&tsyms=USD,BTC&api_key=${api_key}`)
.then(response => {
if (!response.ok) {
throw new Error(`Error fetching data. Status: ${response.status}`);
}
return response.json();
})
.then(data => {
displayCoinData(coin, data);
})
.catch(error => {
console.error(`Fetching ${coin} data:`, error);
displayErrorMessage(`Unable to fetch data. Please verify your API key or try again later.`);
});
}
function fetchWowneroData(coinGeckoApiKey) {
fetch(`https://api.coingecko.com/api/v3/coins/wownero/market_chart?vs_currency=usd&days=1&api_key=${coinGeckoApiKey}`)
.then(response => {
if (!response.ok) {
throw new Error(`Error fetching data. Status: ${response.status}`);
@ -376,15 +437,38 @@
return response.json();
})
.then(data => {
displayCoinData(coin, data);
displayWowneroData(data);
})
.catch(error => {
console.error(`Fetching ${coin} data:`, error);
displayErrorMessage(`Unable to fetch data. Please verify your API key or try again later.`);
console.error('Fetching Wownero data:', error);
displayErrorMessage('Unable to fetch data for Wownero. Please try again later.');
});
});
updateChart('BTC');
});
}
function displayWowneroData(data) {
const prices = data.prices;
const latestPriceUSD = prices[prices.length - 1][1];
const priceChange24h = data.market_caps[data.market_caps.length - 1][1] / data.market_caps[data.market_caps.length - 2][1] - 1;
const volume24h = data.total_volumes[data.total_volumes.length - 1][1];
document.getElementById('wow-price-usd-value').textContent = latestPriceUSD.toFixed(2) + ' $';
document.getElementById('wow-price-change-container').textContent = (priceChange24h * 100).toFixed(2) + '%';
document.getElementById('wow-volume-24h').textContent = volume24h.toFixed(0).replace(/\B(?=(\d{3})+(?!\d))/g, ',') + ' USD';
const priceChangeContainer = document.getElementById('wow-price-change-container');
if (priceChange24h >= 0) {
priceChangeContainer.innerHTML = positivePriceChangeHTML(priceChange24h * 100);
} else {
priceChangeContainer.innerHTML = negativePriceChangeHTML(priceChange24h * 100);
}
const latestPriceBTC = parseFloat(data.prices[data.prices.length - 1][1]);
// Todo fix value USD -> BTC
const priceBTC = latestPriceUSD / latestPriceBTC;
document.getElementById('wow-price-btc').textContent = priceBTC.toFixed(8) + ' BTC';
}
function displayCoinData(coin, data) {
const priceUSD = data.RAW[coin].USD.PRICE;
@ -451,7 +535,7 @@ function negativePriceChangeHTML(value) {
}
function setActiveContainer(containerId) {
const containerIds = ['btc-container', 'xmr-container', 'part-container', 'pivx-container', 'firo-container', 'dash-container', 'ltc-container', 'doge-container', 'eth-container'];
const containerIds = ['btc-container', 'xmr-container', 'part-container', 'pivx-container', 'firo-container', 'dash-container', 'ltc-container', 'doge-container', 'eth-container', 'wow-container'];
const activeClass = 'active-container';
containerIds.forEach(id => {
const container = document.getElementById(id);
@ -500,6 +584,11 @@ document.getElementById('eth-container').addEventListener('click', () => {
setActiveContainer('eth-container');
updateChart('ETH');
});
document.getElementById('wow-container').addEventListener('click', () => {
setActiveContainer('wow-container');
updateChart('WOW');
});
let coin;
const coinOptions = {
@ -508,38 +597,64 @@ const coinOptions = {
backgroundColor: 'rgba(77, 132, 240, 0.1)'
}
};
function updateChart(coinSymbol) {
coin = coinSymbol;
const api_key = '{{chart_api_key}}';
fetch(`https://min-api.cryptocompare.com/data/v2/histoday?fsym=${coin}&tsym=USD&limit=30&api_key=${api_key}`)
.then((response) => response.json())
.then((data) => {
const coinSettings = coinOptions[coinSymbol] || {};
const chartData = {
labels: data.Data.Data.map(d => formatDate(new Date(d.time * 1000))),
datasets: [{
data: data.Data.Data.map(d => d.close),
borderColor: coinSettings.lineColor || 'rgba(77, 132, 240, 1)',
tension: 0.1,
fill: true,
backgroundColor: coinSettings.backgroundColor || 'rgba(77, 132, 240, 0.1)',
pointStyle: 'circle',
pointRadius: 5,
pointHoverRadius: 10
}]
};
chart.data.labels = chartData.labels;
chart.data.datasets = chartData.datasets;
chart.options.scales.y.title.text = `Price (USD) - ${coin} 30 DAYS`;
chart.update();
})
.catch(error => console.error(`Error updating chart for ${coin}:`, error));
const coinGeckoApiKey = '{{coingecko_api_key}}';
if (coinSymbol === 'WOW') {
fetch(`https://api.coingecko.com/api/v3/coins/wownero/market_chart?vs_currency=usd&days=30&interval=daily&d&api_key=${coinGeckoApiKey}`)
.then(response => response.json())
.then(data => {
const chartData = {
labels: data.prices.map(entry => formatDate(new Date(entry[0]))),
datasets: [{
label: 'Wownero Price (USD)',
data: data.prices.map(entry => entry[1]),
borderColor: 'rgba(77, 132, 240, 1)',
backgroundColor: 'rgba(77, 132, 240, 0.1)',
fill: true
}]
};
chart.data = chartData;
chart.options.scales.y.title.text = 'Price (USD) - Wownero 30 DAYS';
chart.update();
})
.catch(error => console.error('Error updating chart for Wownero:', error));
} else {
fetch(`https://min-api.cryptocompare.com/data/v2/histoday?fsym=${coin}&tsym=USD&limit=30&api_key=${api_key}`)
.then(response => response.json())
.then(data => {
const coinSettings = coinOptions[coinSymbol] || {};
const chartData = {
labels: data.Data.Data.map(d => formatDate(new Date(d.time * 1000))),
datasets: [{
data: data.Data.Data.map(d => d.close),
borderColor: coinSettings.lineColor || 'rgba(77, 132, 240, 1)',
tension: 0.1,
fill: true,
backgroundColor: coinSettings.backgroundColor || 'rgba(77, 132, 240, 0.1)',
pointStyle: 'circle',
pointRadius: 5,
pointHoverRadius: 10
}]
};
chart.data = chartData;
chart.options.scales.y.title.text = `Price (USD) - ${coin} 30 DAYS`;
chart.update();
})
.catch(error => console.error(`Error updating chart for ${coin}:`, error));
}
}
function formatDate(date) {
const options = { day: '2-digit', month: '2-digit', year: '2-digit' };
return new Intl.DateTimeFormat('en-US', options).format(date);
}
const verticalLinePlugin = {
id: 'verticalLine',
beforeTooltipDraw: (chart, args) => {
@ -563,6 +678,7 @@ const verticalLinePlugin = {
}
}
};
Chart.register(verticalLinePlugin);
const ctx = document.getElementById('coin-chart').getContext('2d');
const chart = new Chart(ctx, {
@ -643,6 +759,7 @@ const chart = new Chart(ctx, {
}
});
</script>
{% endif %}
<section>
<div class="pl-6 pr-6 pt-0 pb-0 mt-5 h-full overflow-hidden">

@ -416,7 +416,7 @@
</td>
</tr>
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
<td class="py-3 px-6 bold">Chart API Key</td>
<td class="py-3 px-6 bold">Chart API Key (CryptoCompare)</td>
<td class="py-3 px-6">
<label for="message" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Chart API (free) Key at <a class="inline-block text-blue-500 hover:text-blue-600 hover:underline" href="https://min-api.cryptocompare.com/" target="_blank">CryptoCompare.com</a>
<br />
@ -424,6 +424,15 @@
<input name="chartapikey" type="text" class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-400 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0" name="" min="3" max="32" value="{{chart_settings.chart_api_key}}">
</td>
</tr>
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
<td class="py-3 px-6 bold">Chart API Key (CoinGecko)</td>
<td class="py-3 px-6">
<label for="message" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Chart API (free) Key at <a class="inline-block text-blue-500 hover:text-blue-600 hover:underline" href="https://coingecko.com/" target="_blank">CoinGecko.com</a>
<br />
</label>
<input name="coingeckoapikey" type="text" class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-400 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0" name="" min="3" max="32" value="{{chart_settings.coingecko_api_key}}">
</td>
</tr>
</table>
</div>
</div>

@ -41,6 +41,7 @@ from basicswap.chainparams import (
)
default_chart_api_key = '95dd900af910656e0e17c41f2ddc5dba77d01bf8b0e7d2787634a16bd976c553'
default_coingecko_api_key = 'CG-8hm3r9iLfpEXv4ied8oLbeUj'
def value_or_none(v):
@ -451,6 +452,11 @@ def page_newoffer(self, url_split, post_string):
chart_api_key_enc = swap_client.settings.get('chart_api_key_enc', '')
chart_api_key = default_chart_api_key if chart_api_key_enc == '' else bytes.fromhex(chart_api_key_enc).decode('utf-8')
coingecko_api_key = swap_client.settings.get('coingecko_api_key', '')
if coingecko_api_key == '':
coingecko_api_key_enc = swap_client.settings.get('coingecko_api_key_enc', '')
coingecko_api_key = default_coingecko_api_key if coingecko_api_key_enc == '' else bytes.fromhex(coingecko_api_key_enc).decode('utf-8')
return self.render_template(template, {
'messages': messages,
'err_messages': err_messages,
@ -464,6 +470,7 @@ def page_newoffer(self, url_split, post_string):
'swap_types': [(strSwapType(x), strSwapDesc(x)) for x in SwapTypes if strSwapType(x)],
'show_chart': swap_client.settings.get('show_chart', True),
'chart_api_key': chart_api_key,
'coingecko_api_key': coingecko_api_key,
})
@ -796,6 +803,11 @@ def page_offers(self, url_split, post_string, sent=False):
chart_api_key_enc = swap_client.settings.get('chart_api_key_enc', '')
chart_api_key = default_chart_api_key if chart_api_key_enc == '' else bytes.fromhex(chart_api_key_enc).decode('utf-8')
coingecko_api_key = swap_client.settings.get('coingecko_api_key', '')
if coingecko_api_key == '':
coingecko_api_key_enc = swap_client.settings.get('coingecko_api_key_enc', '')
coingecko_api_key = default_coingecko_api_key if coingecko_api_key_enc == '' else bytes.fromhex(coingecko_api_key_enc).decode('utf-8')
offers_count = len(formatted_offers)
template = server.env.get_template('offers.html')
@ -806,6 +818,7 @@ def page_offers(self, url_split, post_string, sent=False):
'messages': messages,
'show_chart': False if sent else swap_client.settings.get('show_chart', True),
'chart_api_key': chart_api_key,
'coingecko_api_key': coingecko_api_key,
'coins_from': coins_from,
'coins': coins_to,
'messages': messages,

@ -45,6 +45,7 @@ def page_settings(self, url_split, post_string):
data = {
'show_chart': toBool(get_data_entry(form_data, 'showchart')),
'chart_api_key': html.unescape(get_data_entry_or(form_data, 'chartapikey', '')),
'coingecko_api_key': html.unescape(get_data_entry_or(form_data, 'coingeckoapikey', '')),
}
swap_client.editGeneralSettings(data)
elif have_data_entry(form_data, 'apply_tor'):
@ -130,9 +131,16 @@ def page_settings(self, url_split, post_string):
chart_api_key = html.escape(bytes.fromhex(swap_client.settings.get('chart_api_key_enc', '')).decode('utf-8'))
else:
chart_api_key = swap_client.settings.get('chart_api_key', '')
if 'coingecko_api_key_enc' in swap_client.settings:
coingecko_api_key = html.escape(bytes.fromhex(swap_client.settings.get('coingecko_api_key_enc', '')).decode('utf-8'))
else:
coingecko_api_key = swap_client.settings.get('coingecko_api_key', '')
chart_settings = {
'show_chart': swap_client.settings.get('show_chart', True),
'chart_api_key': chart_api_key,
'coingecko_api_key': coingecko_api_key,
}
tor_control_password = '' if swap_client.tor_control_password is None else swap_client.tor_control_password

Loading…
Cancel
Save