diff --git a/Explorer.toml b/Explorer.toml index 95a7c76..cd1dd2d 100644 --- a/Explorer.toml +++ b/Explorer.toml @@ -16,6 +16,20 @@ api_secret_path = "~/.grin/main/.api_secret" # Foreign API secret path. foreign_api_secret_path = "~/.grin/main/.foreign_api_secret" -# Path to Grin directory +# Path to Grin directory. grin_dir = "~/.grin" +# CoinGecko API on/off switch. +coingecko_api = "on" + + +# Testnet config +# ip = "127.0.0.1" +# port = "13413" +# proto = "http" +# user = "grin" +# api_secret_path = "~/.grin/test/.api_secret" +# foreign_api_secret_path = "~/.grin/test/.foreign_api_secret" +# grin_dir = "~/.grin" +# coingecko_api = "off" + diff --git a/src/data.rs b/src/data.rs index a8a462c..caca842 100644 --- a/src/data.rs +++ b/src/data.rs @@ -35,6 +35,8 @@ pub struct Dashboard { // mempool pub txns: String, pub stem: String, + // coingecko api + pub cg_api: String, } impl Dashboard { @@ -64,6 +66,7 @@ impl Dashboard { breakeven_cost: String::new(), txns: String::new(), stem: String::new(), + cg_api: String::new(), } } } @@ -141,6 +144,7 @@ pub struct ExplorerConfig { pub grin_dir: String, pub api_secret: String, pub foreign_api_secret: String, + pub coingecko_api: String, } impl ExplorerConfig { @@ -155,6 +159,7 @@ impl ExplorerConfig { grin_dir: String::new(), api_secret: String::new(), foreign_api_secret: String::new(), + coingecko_api: String::new(), } } } diff --git a/src/main.rs b/src/main.rs index a907f89..fa93a9d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,9 +25,10 @@ fn index(dashboard: &State>>) -> Template { let data = dashboard.lock().unwrap(); Template::render("index", context! { - route: "index", - node_ver: &data.node_ver, + route: "index", + node_ver: &data.node_ver, proto_ver: &data.proto_ver, + cg_api: &data.cg_api, }) } @@ -211,7 +212,13 @@ fn soft_supply(dashboard: &State>>) -> String { let data = dashboard.lock().unwrap(); if data.supply.is_empty() == false { - return format!("{} % ({}M/3150M)", data.soft_supply, &data.supply[..3]); + // 9 digits plus 2 commas, e.g. 168,038,400 + if data.supply.len() == 11 { + return format!("{} % ({}M/3150M)", data.soft_supply, &data.supply[..3]); + // 10 digits plus 2 commas + } else if data.supply.len() == 12 { + return format!("{} % ({}M/3150M)", data.soft_supply, &data.supply[..4]); + } } "3150M".to_string() @@ -491,7 +498,7 @@ fn block_weight(count: usize, blocks: &State>>>) -> String fn block_list_index(dashboard: &State>>) -> String { let data = dashboard.lock().unwrap(); - if data.height.is_empty() == false { + if data.height.is_empty() == false && data.height.parse::().unwrap() > 10 { return format!("

", data.height.parse::().unwrap() - 10); diff --git a/src/requests.rs b/src/requests.rs index b758888..d3ac911 100644 --- a/src/requests.rs +++ b/src/requests.rs @@ -36,6 +36,7 @@ lazy_static! { "api_secret_path" => cfg.api_secret_path = value, "foreign_api_secret_path" => cfg.foreign_api_secret_path = value, "grin_dir" => cfg.grin_dir = value, + "coingecko_api" => cfg.coingecko_api = value, _ => println!("{} Unknown config setting '{}'.", "[ ERROR ]".red(), name), } } @@ -140,15 +141,21 @@ pub async fn get_connected_peers(dashboard: Arc>) -> Result<(), // Collecting: supply, inflation, price_usd, price_btc, volume_usd, volume_btc, cap_usd, cap_btc. pub async fn get_market(dashboard: Arc>) -> Result<(), Error> { - let client = reqwest::Client::new(); - let result = client.get("https://api.coingecko.com/api/v3/simple/price?ids=grin&vs_currencies=usd%2Cbtc&include_24hr_vol=true") - .send() - .await?; + let client; + let result; + let mut val = Value::Null; + + if CONFIG.coingecko_api == "on" { + client = reqwest::Client::new(); + result = client.get("https://api.coingecko.com/api/v3/simple/price?ids=grin&vs_currencies=usd%2Cbtc&include_24hr_vol=true").send().await?; + val = serde_json::from_str(&result.text().await.unwrap()).unwrap(); + } - let val: Value = serde_json::from_str(&result.text().await.unwrap()).unwrap(); - let mut data = dashboard.lock().unwrap(); - + + // Save the setting into Dashboard structure for later use + data.cg_api = CONFIG.coingecko_api.clone(); + if data.height.is_empty() == false { // Calculating coin supply // Adding +1 as block index starts with 0 @@ -157,28 +164,32 @@ pub async fn get_market(dashboard: Arc>) -> Result<(), Error> { // 31536000 seconds in a year let inflation = (31536000.0 / (supply as f64)) * 100.0; - data.inflation = format!("{:.2}", inflation); - data.supply = supply.to_formatted_string(&Locale::en); + data.inflation = format!("{:.2}", inflation); + data.supply = supply.to_formatted_string(&Locale::en); // https://john-tromp.medium.com/a-case-for-using-soft-total-supply-1169a188d153 data.soft_supply = format!("{:.2}", - supply.to_string().parse::().unwrap() / 3150000000.0 * 100.0); + supply.to_string().parse::().unwrap() / 3150000000.0 * 100.0); - // Check if CoingGecko API returned error - if let Some(status) = val.get("status") { - println!("{} {}.", "[ WARNING ]".yellow(), - status["error_message"].as_str().unwrap().to_string()); - } else { - data.price_usd = format!("{:.3}", val["grin"]["usd"].to_string().parse::().unwrap()); - data.price_btc = format!("{:.8}", val["grin"]["btc"].to_string().parse::().unwrap()); - data.volume_usd = (val["grin"]["usd_24h_vol"].to_string().parse::().unwrap() as u64) - .to_formatted_string(&Locale::en); - data.volume_btc = format!("{:.2}", val["grin"]["btc_24h_vol"].to_string().parse::() - .unwrap()); - data.cap_usd = (((supply as f64) * data.price_usd.parse::().unwrap()) as u64) - .to_formatted_string(&Locale::en); - data.cap_btc = (((supply as f64) * data.price_btc.parse::().unwrap()) as u64) - .to_formatted_string(&Locale::en); + if CONFIG.coingecko_api == "on" && val != Value::Null { + // Check if CoingGecko API returned error + if let Some(status) = val.get("status") { + println!("{} {}.", "[ WARNING ]".yellow(), + status["error_message"].as_str().unwrap().to_string()); + } else { + data.price_usd = format!("{:.3}", val["grin"]["usd"].to_string().parse::() + .unwrap()); + data.price_btc = format!("{:.8}", val["grin"]["btc"].to_string().parse::() + .unwrap()); + data.volume_usd = (val["grin"]["usd_24h_vol"].to_string().parse::().unwrap() as u64) + .to_formatted_string(&Locale::en); + data.volume_btc = format!("{:.2}", val["grin"]["btc_24h_vol"].to_string().parse::() + .unwrap()); + data.cap_usd = (((supply as f64) * data.price_usd.parse::().unwrap()) as u64) + .to_formatted_string(&Locale::en); + data.cap_btc = (((supply as f64) * data.price_btc.parse::().unwrap()) as u64) + .to_formatted_string(&Locale::en); + } } } @@ -189,8 +200,13 @@ pub async fn get_market(dashboard: Arc>) -> Result<(), Error> { // Collecting: disk_usage. pub fn get_disk_usage(dashboard: Arc>) -> Result<(), Error> { let mut data = dashboard.lock().unwrap(); + let chain_data; - let chain_data = format!("{}/main/chain_data", CONFIG.grin_dir); + if CONFIG.coingecko_api == "on" { + chain_data = format!("{}/main/chain_data", CONFIG.grin_dir); + } else { + chain_data = format!("{}/test/chain_data", CONFIG.grin_dir); + } data.disk_usage = format!("{:.2}", (get_size(chain_data).unwrap() as f64) / 1000.0 / 1000.0 / 1000.0); @@ -204,7 +220,7 @@ pub async fn get_mining_stats(dashboard: Arc>) -> Result<(), Er let difficulty_window = 1440; let height = get_current_height(dashboard.clone()); - if height.is_empty() == false { + if height.is_empty() == false && height.parse::().unwrap() > 1440 { let params1 = &format!("[{}, null, null]", height)[..]; let params2 = &format!("[{}, null, null]", height.parse::().unwrap() - difficulty_window)[..]; @@ -227,17 +243,19 @@ pub async fn get_mining_stats(dashboard: Arc>) -> Result<(), Er data.hashrate = format!("{:.2}", hashrate / 1000.0); data.difficulty = net_diff.to_string(); - // Calculating G1-mini production per hour - let coins_per_hour = 1.2 / hashrate * 60.0 * 60.0; + if CONFIG.coingecko_api == "on" { + // Calculating G1-mini production per hour + let coins_per_hour = 1.2 / hashrate * 60.0 * 60.0; - // Calculating production cost of 1 grin - // Assuming $0.07 per kW/h - data.production_cost = format!("{:.3}", 120.0 / 1000.0 * 0.07 * (1.0 / coins_per_hour)); + // Calculating production cost of 1 grin + // Assuming $0.07 per kW/h + data.production_cost = format!("{:.3}", 120.0 / 1000.0 * 0.07 * (1.0 / coins_per_hour)); - data.reward_ratio = format!("{:.2}", data.price_usd.parse::().unwrap() - / data.production_cost.parse::().unwrap()); - data.breakeven_cost = format!("{:.2}", data.price_usd.parse::().unwrap() - / (120.0 / 1000.0 * (1.0 / coins_per_hour))); + data.reward_ratio = format!("{:.2}", data.price_usd.parse::().unwrap() + / data.production_cost.parse::().unwrap()); + data.breakeven_cost = format!("{:.2}", data.price_usd.parse::().unwrap() + / (120.0 / 1000.0 * (1.0 / coins_per_hour))); + } } } @@ -421,7 +439,7 @@ pub async fn get_txn_stats(dashboard: Arc>, let mut blocks = Vec::::new(); let height = get_current_height(dashboard.clone()); - if height.is_empty() == false { + if height.is_empty() == false && height.parse::().unwrap() > 1440 { // get_blocks grin rpc has limit of maximum of 1000 blocks request // https://github.com/mimblewimble/grin/blob/master/api/src/handlers/blocks_api.rs#L27 // So, collecting kernels 2 times by 720 blocks to get a day of blocks @@ -484,7 +502,7 @@ pub async fn get_recent_blocks(dashboard: Arc>, let mut i = 0; let height_str = get_current_height(dashboard.clone()); - if height_str.is_empty() == false { + if height_str.is_empty() == false && height_str.parse::().unwrap() > 0 { let height = height_str.parse::().unwrap(); let mut blocks_vec = Vec::::new(); diff --git a/templates/index.html.tera b/templates/index.html.tera index b4a8f36..ac1e8c0 100644 --- a/templates/index.html.tera +++ b/templates/index.html.tera @@ -4,6 +4,11 @@ +{# We have different UI to display if CoinGecko API is disabled by user #} + +{% if cg_api == "on" %} +{# CoinGecko API is enabled #} +
@@ -456,6 +461,349 @@
+{% else %} +{# CoinGecko API is disabled #} + +
+
+
+
+
MARKET
+
+
+
Yearly Inflation Rate 
%
+
+
+
+
Coin Supply 
+
+
+
+
Soft Total Supply + + +
+
+
+
+ +
+
+
MINING
+
+
+
Hashrate 
KG/s
+
+
+
+
Difficulty 
+
+
+
+
Block Reward 
ツ 60
+
+
+
+
+ + +
+
+
+
BLOCKCHAIN
+
+
+
Size 
+
+
+
+
Block Height 
+
+
+
+
Time Since Last Block 
+
+
+
+
+
+
TRANSACTIONS & FEES
+
+
+
1H Period 
+
+
+
+
24H Period 
+
+
+
+
+ + +
+
+
+
MEMPOOL
+
+
+
Transactions 
+
+
+
+
Stem 
+
+
+
+
+
+
CONNECTIONS
+
+
+
Inbound 
+
+
+
+
Outbound 
+
+
+
+
+
+
NODE
+
+
+
Version 
{{ node_ver }}
+
+
+
+
Protocol 
{{ proto_ver }}
+
+
+
+
Sync Status 
+
+
+
+
+
+ + +
+ +
+
+
MARKET
+
+
+
Yearly Inflation Rate 
%
+
+
+
+
Coin Supply 
+
+
+
+
Soft Total Supply + + +
+
+
+
+ + +
+
+
+
BLOCKCHAIN
+
+
+
+
Size 
+
+
+
+
Block Height 
+
+
+
+
Time Since Last Block 
+
+
+
+
+
+
MINING
+
+
+
Hashrate 
KG/s
+
+
+
+
Difficulty 
+
+
+
+
Block Reward 
ツ 60
+
+
+
+
+
+
TRANSACTIONS & FEES
+
+
+
1H Period 
+
+
+
+
24H Period 
+
+
+
+ + + +
+
+
MEMPOOL
+
+
+
Transactions 
+
+
+
+
Stem 
+
+
+
+
+
+
CONNECTIONS
+
+
+
Inbound 
+
+
+
+
Outbound 
+
+
+
+
+
+
NODE
+
+
+
Version 
{{ node_ver }}
+
+
+
+
Protocol 
{{ proto_ver }}
+
+
+
+
Sync Status 
+
+
+
+ +
+ + + +
+ + + + + + + + + + +
+ + +{% endif %} +
{% endblock content%}