Files
grin-web-wallet/scripts/prices.js
2024-12-20 18:08:44 -08:00

762 lines
19 KiB
JavaScript
Executable File

// Use strict
"use strict";
// Classes
// Prices class
class Prices {
// Public
// Constructor
constructor(settings) {
// Set settings
this.settings = settings;
// Set prices obtained
this.pricesObtained = false;
// Set update prices timeout to no timeout
this.updatePricesTimeout = Prices.NO_TIMEOUT;
// Set prices
this.prices = {};
// Set enable price to setting's default value
this.enablePrice = Prices.SETTINGS_ENABLE_PRICE_DEFAULT_VALUE;
// Set update price interval minutes to setting's default value
this.updatePriceIntervalMinutes = Prices.SETTINGS_UPDATE_PRICE_INTERVAL_MINUTES_DEFAULT_VALUE;
// Can get prices
this.canGetPrices = false;
// Create database
Database.createDatabase(function(database, currentVersion, databaseTransaction) {
// Create or get prices object store
var pricesObjectStore = (currentVersion === Database.NO_CURRENT_VERSION) ? database.createObjectStore(Prices.OBJECT_STORE_NAME, {
// Key path
"keyPath": [
// Wallet type
Database.toKeyPath(Prices.DATABASE_WALLET_TYPE_NAME),
// Currency
Database.toKeyPath(Prices.DATABASE_CURRENCY_NAME)
]
}) : databaseTransaction.objectStore(Prices.OBJECT_STORE_NAME);
// Check if no database version exists
if(currentVersion === Database.NO_CURRENT_VERSION) {
// Create index to search prices object store by wallet type
pricesObjectStore.createIndex(Prices.DATABASE_WALLET_TYPE_NAME, Database.toKeyPath(Prices.DATABASE_WALLET_TYPE_NAME), {
// Unique
"unique": false
});
}
});
// Set self
var self = this;
// Once database is initialized
Database.onceInitialized(function() {
// Return promise
return new Promise(function(resolve, reject) {
// Return creating settings
return Promise.all([
// Create enable price setting
self.settings.createValue(Prices.SETTINGS_ENABLE_PRICE_NAME, Prices.SETTINGS_ENABLE_PRICE_DEFAULT_VALUE),
// Create update price interval minutes setting
self.settings.createValue(Prices.SETTINGS_UPDATE_PRICE_INTERVAL_MINUTES_NAME, Prices.SETTINGS_UPDATE_PRICE_INTERVAL_MINUTES_DEFAULT_VALUE)
]).then(function() {
// Initialize settings
var settings = [
// Enable price setting
Prices.SETTINGS_ENABLE_PRICE_NAME,
// Update price interval minutes setting
Prices.SETTINGS_UPDATE_PRICE_INTERVAL_MINUTES_NAME
];
// Return getting settings' values
return Promise.all(settings.map(function(setting) {
// Return getting setting's value
return self.settings.getValue(setting);
})).then(function(settingValues) {
// Set enable price to setting's value
self.enablePrice = settingValues[settings.indexOf(Prices.SETTINGS_ENABLE_PRICE_NAME)];
// Set update price interval minutes to setting's value
self.updatePriceIntervalMinutes = settingValues[settings.indexOf(Prices.SETTINGS_UPDATE_PRICE_INTERVAL_MINUTES_NAME)];
// Return getting all prices with the wallet type in the database
return Database.getResults(Prices.OBJECT_STORE_NAME, Database.GET_ALL_RESULTS, Database.GET_ALL_RESULTS, Prices.DATABASE_WALLET_TYPE_NAME, Consensus.getWalletType()).then(function(results) {
// Set can get prices
self.canGetPrices = true;
// Go through all prices
for(var i = 0; i < results["length"]; ++i) {
// Get price's currency
var currency = results[i][Database.toKeyPath(Prices.DATABASE_CURRENCY_NAME)];
// Get price's price or zero if network type isn't mainnet
var price = new BigNumber((Consensus.getNetworkType() === Consensus.MAINNET_NETWORK_TYPE) ? results[i][Database.toKeyPath(Prices.DATABASE_PRICE_NAME)] : 0);
// Add price to list of prices
self.prices[currency] = price;
}
// Check if price is enabled and network type is mainnet
if(self.enablePrice === true && Consensus.getNetworkType() === Consensus.MAINNET_NETWORK_TYPE) {
// Check if update prices timeout exists
if(self.updatePricesTimeout !== Prices.NO_TIMEOUT) {
// Clear update prices timeout
clearTimeout(self.updatePricesTimeout);
// Set updates prices timeout to no timeout
self.updatePricesTimeout = Prices.NO_TIMEOUT;
}
// Update prices
self.updatePrices();
}
// Otherwise
else {
// Refresh prices timeout
self.refreshPricesTimeout();
}
// Resolve
resolve();
// Catch errors
}).catch(function(error) {
// Reject
reject();
});
// Catch errors
}).catch(function(error) {
// Reject
reject();
});
// Catch errors
}).catch(function(error) {
// Reject
reject();
});
});
});
// Settings change event
$(this.settings).on(Settings.CHANGE_EVENT, function(event, setting) {
// Check what setting was changes
switch(setting[Settings.DATABASE_SETTING_NAME]) {
// Enable price setting
case Prices.SETTINGS_ENABLE_PRICE_NAME:
// Set enable price to setting's value
self.enablePrice = setting[Settings.DATABASE_VALUE_NAME];
// Check if price is enabled and network type is mainnet
if(self.enablePrice === true && Consensus.getNetworkType() === Consensus.MAINNET_NETWORK_TYPE) {
// Check if update prices timeout exists
if(self.updatePricesTimeout !== Prices.NO_TIMEOUT) {
// Clear update prices timeout
clearTimeout(self.updatePricesTimeout);
// Set updates prices timeout to no timeout
self.updatePricesTimeout = Prices.NO_TIMEOUT;
}
// Trigger change event
$(self).trigger(Prices.CHANGE_EVENT, self.prices);
// Update prices
self.updatePrices();
}
// Otherwise
else {
// Refresh prices timeout
self.refreshPricesTimeout();
// Trigger change event
$(self).trigger(Prices.CHANGE_EVENT, self.prices);
}
// Break
break;
// Update price interval minutes setting
case Prices.SETTINGS_UPDATE_PRICE_INTERVAL_MINUTES_NAME:
// Set update price interval minutes to setting's value
self.updatePriceIntervalMinutes = setting[Settings.DATABASE_VALUE_NAME];
// Refresh prices timeout
self.refreshPricesTimeout();
// Break
break;
}
});
// Window online event
$(window).on("online", function() {
// Check if prices haven't been obtained, the price is enabled, and network type is mainnet
if(self.pricesObtained === false && self.enablePrice === true && Consensus.getNetworkType() === Consensus.MAINNET_NETWORK_TYPE) {
// Check if update prices timeout exists
if(self.updatePricesTimeout !== Prices.NO_TIMEOUT) {
// Clear update prices timeout
clearTimeout(self.updatePricesTimeout);
// Set updates prices timeout to no timeout
self.updatePricesTimeout = Prices.NO_TIMEOUT;
}
// Update prices
self.updatePrices();
}
});
}
// Get prices
getPrices() {
// Check if price is enabled and prices exist
if(this.enablePrice === true && Object.keys(this.prices)["length"] !== 0) {
// Return copy of prices
return JSONBigNumber.parse(JSONBigNumber.stringify(this.prices));
}
// Otherwise
else {
// Return no prices found
return Prices.NO_PRICES_FOUND;
}
}
// Get price
getPrice(currency = Prices.CURRENT_LANGUAGE_CURRENCY, prices = Prices.CURRENT_PRICES) {
// Check if using current prices
if(prices === Prices.CURRENT_PRICES) {
// Set prices to the current prices
prices = this.prices;
}
// Check if price is enabled
if(this.enablePrice === true) {
// Check if network type is mainnet
if(Consensus.getNetworkType() === Consensus.MAINNET_NETWORK_TYPE) {
// Check if currency is the current language's currency
if(currency === Prices.CURRENT_LANGUAGE_CURRENCY)
// Set currency to current language's currency
currency = Prices.getCurrentLanguageCurrency();
// Return price for currency if found or no price found otherwise
return (currency.toUpperCase() in prices === true) ? prices[currency.toUpperCase()] : Prices.NO_PRICE_FOUND;
}
// Otherwise
else {
// Return zero
return new BigNumber(0);
}
}
// Otherwise
else {
// Return no price found
return Prices.NO_PRICE_FOUND;
}
}
// Refresh
refresh() {
// Check if price is enabled and network type is mainnet
if(this.enablePrice === true && Consensus.getNetworkType() === Consensus.MAINNET_NETWORK_TYPE) {
// Check if update prices timeout exists
if(this.updatePricesTimeout !== Prices.NO_TIMEOUT) {
// Clear update prices timeout
clearTimeout(this.updatePricesTimeout);
// Set updates prices timeout to no timeout
this.updatePricesTimeout = Prices.NO_TIMEOUT;
}
// Update prices
this.updatePrices();
}
}
// Bitcoin currency name
static get BITCOIN_CURRENCY_NAME() {
// Return Bitcoin currency name
return "BTC";
}
// Bitcoin number base
static get BITCOIN_NUMBER_BASE() {
// Return Bitcoin number base
return 1E8;
}
// Ethereum currency name
static get ETHEREUM_CURRENCY_NAME() {
// Return Ethereum currency name
return "ETH";
}
// Ethereum number base
static get ETHEREUM_NUMBER_BASE() {
// Return Ethereum number base
return 1E18;
}
// No price found
static get NO_PRICE_FOUND() {
// Return no price found
return null;
}
// No prices found
static get NO_PRICES_FOUND() {
// Return no prices found
return null;
}
// Change event
static get CHANGE_EVENT() {
// Return change event
return "PricesChangeEvent";
}
// Currency language currency
static get CURRENT_LANGUAGE_CURRENCY() {
// Return currency language currency
return null;
}
// Current prices
static get CURRENT_PRICES() {
// Return current prices
return null;
}
// Private
// Update prices
updatePrices() {
// Check if can get prices
if(this.canGetPrices === true) {
// Log message
Log.logMessage(Language.getDefaultTranslation('Trying to connect to the prices server at %1$y.'), [
[
// Text
Prices.API_URL,
// Is raw data
true
]
]);
// Initialize unique currencies to include default currencies
var uniqueCurrencies = new Set(Prices.DEFAULT_CURRENCIES);
// Go through all currencies
var allCurrencies = Prices.getAllCurrencies();
Object.keys(allCurrencies).forEach(function(language) {
// Append currency to list of unique currencies
uniqueCurrencies.add(allCurrencies[language].toUpperCase());
});
// Set self
var self = this;
// Get price for each currency
$.ajax({
// URL
"url": Prices.getApiRequest(uniqueCurrencies),
// Data type
"dataType": "text"
// Done
}).done(function(response) {
// Log message
Log.logMessage(Language.getDefaultTranslation('Successfully connected to the prices server.'));
// Try
try {
// Parse response as JSON
response = JSONBigNumber.parse(response);
}
// Catch errors
catch(error) {
// Log message
Log.logMessage(Language.getDefaultTranslation('Received an invalid response from the prices server.'));
// Return
return;
}
// Initialize new prices
var newPrices = [];
// Check if response is valid
if(Object.isObject(response) === true && Prices.API_CURRENCY_ID in response === true && Object.isObject(response[Prices.API_CURRENCY_ID]) === true) {
// Go through price for each currency
Object.keys(response[Prices.API_CURRENCY_ID]).forEach(function(currency) {
// Check if currency and price are valid
if(typeof currency === "string" && uniqueCurrencies.has(currency.toUpperCase()) === true && response[Prices.API_CURRENCY_ID][currency] instanceof BigNumber === true && response[Prices.API_CURRENCY_ID][currency].isPositive() === true && response[Prices.API_CURRENCY_ID][currency].isFinite() === true) {
// Append new price to list
newPrices.push({
// Wallet type
[Database.toKeyPath(Prices.DATABASE_WALLET_TYPE_NAME)]: Consensus.getWalletType(),
// Currency
[Database.toKeyPath(Prices.DATABASE_CURRENCY_NAME)]: currency.toUpperCase(),
// Price
[Database.toKeyPath(Prices.DATABASE_PRICE_NAME)]: response[Prices.API_CURRENCY_ID][currency].toFixed()
});
// Update prices in list
self.prices[currency.toUpperCase()] = response[Prices.API_CURRENCY_ID][currency];
}
});
}
// Check if new prices exist
if(newPrices["length"] !== 0) {
// Set prices obtained
self.pricesObtained = true;
// Save new prices in the database
Database.saveResults(Prices.OBJECT_STORE_NAME, newPrices, [], Database.CREATE_NEW_TRANSACTION, Database.STRICT_DURABILITY).then(function() {
// Log message
Log.logMessage(Language.getDefaultTranslation('Successfully updated the prices.'));
// Trigger change event
$(self).trigger(Prices.CHANGE_EVENT, self.prices);
// Catch errors
}).catch(function(error) {
// Log message
Log.logMessage(Language.getDefaultTranslation('Updating the prices failed.'));
});
}
// Otherwise
else {
// Log message
Log.logMessage(Language.getDefaultTranslation('Received an invalid response from the prices server.'));
}
// Catch errors
}).fail(function(request) {
// Check if connecting to the prices server failed
if(request["status"] === Common.HTTP_NO_RESPONSE_STATUS) {
// Log message
Log.logMessage(Language.getDefaultTranslation('Connecting to the prices server failed.'));
}
// Otherwise
else {
// Log message
Log.logMessage(Language.getDefaultTranslation('Successfully connected to the prices server.'));
// Log message
Log.logMessage(Language.getDefaultTranslation('Received an invalid response from the prices server.'));
}
// Always
}).always(function() {
// Refresh prices timeout
self.refreshPricesTimeout();
});
}
// Otherwise
else {
// Refresh prices timeout
this.refreshPricesTimeout();
}
}
// Refresh prices timeout
refreshPricesTimeout() {
// Check if update prices timeout exists
if(this.updatePricesTimeout !== Prices.NO_TIMEOUT) {
// Clear update prices timeout
clearTimeout(this.updatePricesTimeout);
// Set updates prices timeout to no timeout
this.updatePricesTimeout = Prices.NO_TIMEOUT;
}
// Check if enable price and network type is mainnet
if(this.enablePrice === true && Consensus.getNetworkType() === Consensus.MAINNET_NETWORK_TYPE) {
// Set self
var self = this;
// Set update prices timeout
this.updatePricesTimeout = setTimeout(function() {
// Update prices again
self.updatePrices();
}, this.updatePriceIntervalMinutes * Common.SECONDS_IN_A_MINUTE * Common.MILLISECONDS_IN_A_SECOND);
}
}
// Get current language currency
static getCurrentLanguageCurrency() {
// Return currency for the current language
return Language.getConstant(Language.CURRENCY_CONSTANT);
}
// Get all currencies
static getAllCurrencies() {
// Initialize currencies
var currencies = {};
// Check if available languages exist
if(typeof AVAILABLE_LANGUAGES !== "undefined") {
// Go through all available languages
Object.keys(AVAILABLE_LANGUAGES).forEach(function(availableLanguage) {
// Append currency for the available language to list of currencies
currencies[availableLanguage] = Language.getConstant(Language.CURRENCY_CONSTANT, availableLanguage);
});
}
// Return currencies
return currencies;
}
// Get API request
static getApiRequest(currencies) {
// Return API query string
return Prices.API_URL + Common.URL_QUERY_STRING_SEPARATOR + $.param({
// IDs
"ids": Prices.API_CURRENCY_ID,
// VS currencies
"vs_currencies": Array.from(currencies).join(Prices.API_CURRENCIES_SEPARATOR)
});
}
// Object store name
static get OBJECT_STORE_NAME() {
// Return object store name
return "Prices";
}
// Database wallet type name
static get DATABASE_WALLET_TYPE_NAME() {
// Return database wallet type name
return "Wallet Type";
}
// Database currency name
static get DATABASE_CURRENCY_NAME() {
// Return database currency name
return "Currency";
}
// Database price name
static get DATABASE_PRICE_NAME() {
// Return database price name
return "Price";
}
// Default currencies
static get DEFAULT_CURRENCIES() {
// Return default currencies
return [
// Bitcoin currency name
Prices.BITCOIN_CURRENCY_NAME.toUpperCase(),
// Ethereum currency name
Prices.ETHEREUM_CURRENCY_NAME.toUpperCase()
];
}
// API URL
static get API_URL() {
// Return API URL
return "https://api.coingecko.com/api/v3/simple/price";
}
// API currency ID
static get API_CURRENCY_ID() {
// Check wallet type
switch(Consensus.getWalletType()) {
// MWC wallet
case Consensus.MWC_WALLET_TYPE:
// Return API currency ID
return "mimblewimblecoin";
// GRIN wallet
case Consensus.GRIN_WALLET_TYPE:
// Return API currency ID
return "grin";
// EPIC wallet
case Consensus.EPIC_WALLET_TYPE:
// Return API currency ID
return "epic-cash";
}
}
// API currencies separator
static get API_CURRENCIES_SEPARATOR() {
// Return API currencies separator
return ",";
}
// No timeout
static get NO_TIMEOUT() {
// Return no timeout
return null;
}
// Settings enable price name
static get SETTINGS_ENABLE_PRICE_NAME() {
// Return settings enable price name
return "Enable Price";
}
// Settings enable price default value
static get SETTINGS_ENABLE_PRICE_DEFAULT_VALUE() {
// Return settings enable price default value
return true;
}
// Settings update price interval minutes name
static get SETTINGS_UPDATE_PRICE_INTERVAL_MINUTES_NAME() {
// Return settings update price interval minutes name
return "Update Price Interval Minutes";
}
// Settings update price interval minutes default value
static get SETTINGS_UPDATE_PRICE_INTERVAL_MINUTES_DEFAULT_VALUE() {
// Return settings update price interval minutes default value
return 10;
}
}
// Main function
// Set global object's prices
globalThis["Prices"] = Prices;