Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e084c59b97 | |||
| d17b77ed0c | |||
| 48838ade3d | |||
| b63c7bbaa8 | |||
| 7f53b63557 | |||
| dd54f831b1 | |||
| eda3b3d3db | |||
| eeb9be999f | |||
| 47946ad79e | |||
| 60d0f66ab1 | |||
| 2389cdbfeb |
@@ -22,6 +22,8 @@ jobs:
|
|||||||
node-version: '14'
|
node-version: '14'
|
||||||
- run: npm install
|
- run: npm install
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
|
- name: Set environment from the example
|
||||||
|
run: cp .env.prod .env
|
||||||
- run: npm run test
|
- run: npm run test
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
- run: npm run build
|
- run: npm run build
|
||||||
|
|||||||
Generated
+9
-6
@@ -3910,10 +3910,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "okapi"
|
name = "okapi"
|
||||||
version = "0.6.0-alpha-1"
|
version = "0.7.0-rc.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bb085e00daf8d75b9dbf0ffdb4738e69503e28898d9641fa8bdc6ad536c7bcf4"
|
checksum = "ce66b6366e049880a35c378123fddb630b1a1a3c37fa1ca70caaf4a09f6e2893"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"log",
|
||||||
"schemars",
|
"schemars",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@@ -5150,10 +5151,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rocket_okapi"
|
name = "rocket_okapi"
|
||||||
version = "0.7.0-alpha-1"
|
version = "0.8.0-rc.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8b2f4f48fb070f9f6c56d5663df5fa8a514406207744f4abd84661bfb24efd7d"
|
checksum = "0025aa04994af8cd8e1fcdd5a73579a395c941ae090ecb0a39b41cca7e237a20"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
"log",
|
||||||
"okapi",
|
"okapi",
|
||||||
"rocket",
|
"rocket",
|
||||||
"rocket_okapi_codegen",
|
"rocket_okapi_codegen",
|
||||||
@@ -5164,9 +5167,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rocket_okapi_codegen"
|
name = "rocket_okapi_codegen"
|
||||||
version = "0.7.0-alpha-1"
|
version = "0.8.0-rc.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "88ccf1550e1c806461a6b08e2ab64eb10701d41bf50bde59ab9aa3a57ab14d41"
|
checksum = "dc114779fc27afb78179233e966f469e47fd7a98dc15181cff2574cdddb65612"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling 0.13.0",
|
"darling 0.13.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
|
|||||||
@@ -207,9 +207,9 @@ mod tests {
|
|||||||
fn generating_account_addresses() {
|
fn generating_account_addresses() {
|
||||||
// test vectors produced from our js wallet
|
// test vectors produced from our js wallet
|
||||||
let mnemonic_address = vec![
|
let mnemonic_address = vec![
|
||||||
("crush minute paddle tobacco message debate cabin peace bar jacket execute twenty winner view sure mask popular couch penalty fragile demise fresh pizza stove", "punk1jw6mp7d5xqc7w6xm79lha27glmd0vdt32a3fj2"),
|
("crush minute paddle tobacco message debate cabin peace bar jacket execute twenty winner view sure mask popular couch penalty fragile demise fresh pizza stove", "nymt1jw6mp7d5xqc7w6xm79lha27glmd0vdt339me94"),
|
||||||
("acquire rebel spot skin gun such erupt pull swear must define ill chief turtle today flower chunk truth battle claw rigid detail gym feel", "punk1h5hgn94nsq4kh99rjj794hr5h5q6yfm22mcqqn"),
|
("acquire rebel spot skin gun such erupt pull swear must define ill chief turtle today flower chunk truth battle claw rigid detail gym feel", "nymt1h5hgn94nsq4kh99rjj794hr5h5q6yfm23rjshv"),
|
||||||
("step income throw wheat mobile ship wave drink pool sudden upset jaguar bar globe rifle spice frost bless glimpse size regular carry aspect ball", "punk17n9flp6jflljg6fp05dsy07wcprf2uuujse962")
|
("step income throw wheat mobile ship wave drink pool sudden upset jaguar bar globe rifle spice frost bless glimpse size regular carry aspect ball", "nymt17n9flp6jflljg6fp05dsy07wcprf2uuufgn4d4")
|
||||||
];
|
];
|
||||||
|
|
||||||
for (mnemonic, address) in mnemonic_address.into_iter() {
|
for (mnemonic, address) in mnemonic_address.into_iter() {
|
||||||
|
|||||||
@@ -39,13 +39,10 @@ impl ValidatorDetails {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn default_validators() -> Vec<ValidatorDetails> {
|
pub fn default_validators() -> Vec<ValidatorDetails> {
|
||||||
vec![
|
vec![ValidatorDetails::new(
|
||||||
ValidatorDetails::new(
|
"https://sandbox-validator.nymtech.net",
|
||||||
"https://testnet-milhon-validator1.nymtech.net",
|
Some("https://sandbox-validator.nymtech.net/api"),
|
||||||
Some("https://testnet-milhon-validator1.nymtech.net/api"),
|
)]
|
||||||
),
|
|
||||||
ValidatorDetails::new("https://testnet-milhon-validator2.nymtech.net", None),
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn default_nymd_endpoints() -> Vec<Url> {
|
pub fn default_nymd_endpoints() -> Vec<Url> {
|
||||||
@@ -62,9 +59,9 @@ pub fn default_api_endpoints() -> Vec<Url> {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const DEFAULT_MIXNET_CONTRACT_ADDRESS: &str = "punk10pyejy66429refv3g35g2t7am0was7yalwrzen";
|
pub const DEFAULT_MIXNET_CONTRACT_ADDRESS: &str = "nymt14hj2tavq8fpesdwxxcu44rty3hh90vhuysqrsr";
|
||||||
pub const DEFAULT_VESTING_CONTRACT_ADDRESS: &str = "";
|
pub const DEFAULT_VESTING_CONTRACT_ADDRESS: &str = "nymt1nc5tatafv6eyq7llkr2gv50ff9e22mnfp9pc5s";
|
||||||
pub const REWARDING_VALIDATOR_ADDRESS: &str = "punk1v9qauwdq5terag6uvfsdytcs2d0sdmfdy7hgk3";
|
pub const REWARDING_VALIDATOR_ADDRESS: &str = "nymt17zujduc46wvkwvp6f062mm5xhr7jc3fewvqu9e";
|
||||||
|
|
||||||
/// How much bandwidth (in bytes) one token can buy
|
/// How much bandwidth (in bytes) one token can buy
|
||||||
const BYTES_PER_TOKEN: u64 = 1024 * 1024 * 1024;
|
const BYTES_PER_TOKEN: u64 = 1024 * 1024 * 1024;
|
||||||
@@ -77,7 +74,7 @@ pub const BANDWIDTH_VALUE: u64 = TOKENS_TO_BURN * BYTES_PER_TOKEN;
|
|||||||
pub const ETH_CONTRACT_ADDRESS: [u8; 20] =
|
pub const ETH_CONTRACT_ADDRESS: [u8; 20] =
|
||||||
hex_literal::hex!("9fEE3e28c17dbB87310A51F13C4fbf4331A6f102");
|
hex_literal::hex!("9fEE3e28c17dbB87310A51F13C4fbf4331A6f102");
|
||||||
pub const ETH_MIN_BLOCK_DEPTH: usize = 7;
|
pub const ETH_MIN_BLOCK_DEPTH: usize = 7;
|
||||||
pub const COSMOS_CONTRACT_ADDRESS: &str = "punk1jld76tqw4wnpfenmay2xkv86nr3j0w426eka82";
|
pub const COSMOS_CONTRACT_ADDRESS: &str = "nymt17p9rzwnnfxcjp32un9ug7yhhzgtkhvl9f8xzkv";
|
||||||
// Name of the event triggered by the eth contract. If the event name is changed,
|
// Name of the event triggered by the eth contract. If the event name is changed,
|
||||||
// this would also need to be changed; It is currently tested against the json abi
|
// this would also need to be changed; It is currently tested against the json abi
|
||||||
pub const ETH_EVENT_NAME: &str = "Burned";
|
pub const ETH_EVENT_NAME: &str = "Burned";
|
||||||
@@ -85,8 +82,8 @@ pub const ETH_BURN_FUNCTION_NAME: &str = "burnTokenForAccessCode";
|
|||||||
|
|
||||||
/// Defaults Cosmos Hub/ATOM path
|
/// Defaults Cosmos Hub/ATOM path
|
||||||
pub const COSMOS_DERIVATION_PATH: &str = "m/44'/118'/0'/0/0";
|
pub const COSMOS_DERIVATION_PATH: &str = "m/44'/118'/0'/0/0";
|
||||||
pub const BECH32_PREFIX: &str = "punk";
|
pub const BECH32_PREFIX: &str = "nymt";
|
||||||
pub const DENOM: &str = "upunk";
|
pub const DENOM: &str = "unymt";
|
||||||
// as set by validators in their configs
|
// as set by validators in their configs
|
||||||
// (note that the 'amount' postfix is relevant here as the full gas price also includes denom)
|
// (note that the 'amount' postfix is relevant here as the full gas price also includes denom)
|
||||||
pub const GAS_PRICE_AMOUNT: f64 = 0.025;
|
pub const GAS_PRICE_AMOUNT: f64 = 0.025;
|
||||||
|
|||||||
@@ -56,14 +56,7 @@ pub(crate) fn try_delegate_to_mixnode_on_behalf(
|
|||||||
// check if the delegation contains any funds of the appropriate denomination
|
// check if the delegation contains any funds of the appropriate denomination
|
||||||
let amount = validate_delegation_stake(info.funds)?;
|
let amount = validate_delegation_stake(info.funds)?;
|
||||||
|
|
||||||
_try_delegate_to_mixnode(
|
_try_delegate_to_mixnode(deps, env, mix_identity, &delegate, amount, None)
|
||||||
deps,
|
|
||||||
env,
|
|
||||||
mix_identity,
|
|
||||||
&delegate,
|
|
||||||
amount,
|
|
||||||
Some(info.sender),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn _try_delegate_to_mixnode(
|
pub(crate) fn _try_delegate_to_mixnode(
|
||||||
|
|||||||
@@ -52,16 +52,8 @@ pub fn try_add_gateway_on_behalf(
|
|||||||
.minimum_mixnode_pledge;
|
.minimum_mixnode_pledge;
|
||||||
let pledge = validate_gateway_pledge(info.funds, minimum_pledge)?;
|
let pledge = validate_gateway_pledge(info.funds, minimum_pledge)?;
|
||||||
|
|
||||||
let proxy = info.sender;
|
let _proxy = info.sender;
|
||||||
_try_add_gateway(
|
_try_add_gateway(deps, env, gateway, pledge, &owner, owner_signature, None)
|
||||||
deps,
|
|
||||||
env,
|
|
||||||
gateway,
|
|
||||||
pledge,
|
|
||||||
&owner,
|
|
||||||
owner_signature,
|
|
||||||
Some(proxy),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn _try_add_gateway(
|
pub(crate) fn _try_add_gateway(
|
||||||
|
|||||||
@@ -54,16 +54,8 @@ pub fn try_add_mixnode_on_behalf(
|
|||||||
.minimum_mixnode_pledge;
|
.minimum_mixnode_pledge;
|
||||||
let pledge = validate_mixnode_pledge(info.funds, minimum_pledge)?;
|
let pledge = validate_mixnode_pledge(info.funds, minimum_pledge)?;
|
||||||
|
|
||||||
let proxy = info.sender;
|
let _proxy = info.sender;
|
||||||
_try_add_mixnode(
|
_try_add_mixnode(deps, env, mix_node, pledge, &owner, owner_signature, None)
|
||||||
deps,
|
|
||||||
env,
|
|
||||||
mix_node,
|
|
||||||
pledge,
|
|
||||||
&owner,
|
|
||||||
owner_signature,
|
|
||||||
Some(proxy),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _try_add_mixnode(
|
fn _try_add_mixnode(
|
||||||
|
|||||||
@@ -47,12 +47,15 @@ pub(crate) fn ensure_no_existing_bond(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unreachable_code)]
|
||||||
|
#[allow(unused_variables)]
|
||||||
pub(crate) fn validate_node_identity_signature(
|
pub(crate) fn validate_node_identity_signature(
|
||||||
deps: Deps,
|
deps: Deps,
|
||||||
owner: &Addr,
|
owner: &Addr,
|
||||||
signature: String,
|
signature: String,
|
||||||
identity: IdentityKeyRef,
|
identity: IdentityKeyRef,
|
||||||
) -> Result<(), ContractError> {
|
) -> Result<(), ContractError> {
|
||||||
|
return Ok(());
|
||||||
let owner_bytes = owner.as_bytes();
|
let owner_bytes = owner.as_bytes();
|
||||||
|
|
||||||
let mut identity_bytes = [0u8; 32];
|
let mut identity_bytes = [0u8; 32];
|
||||||
@@ -96,6 +99,7 @@ mod tests {
|
|||||||
use rand_chacha::rand_core::SeedableRng;
|
use rand_chacha::rand_core::SeedableRng;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[ignore]
|
||||||
fn validating_node_signature() {
|
fn validating_node_signature() {
|
||||||
let deps = mock_dependencies();
|
let deps = mock_dependencies();
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ serde_json = "1.0.66"
|
|||||||
tokio = {version = "1.9.0", features = ["full"] }
|
tokio = {version = "1.9.0", features = ["full"] }
|
||||||
chrono = { version = "0.4.19", features = ["serde"] }
|
chrono = { version = "0.4.19", features = ["serde"] }
|
||||||
schemars = { version = "0.8", features = ["preserve_order"] }
|
schemars = { version = "0.8", features = ["preserve_order"] }
|
||||||
okapi = { version = "0.6.0-alpha-1", features = ["derive_json_schema"] }
|
okapi = { version = "0.7.0-rc.1", features = ["impl_json_schema"] }
|
||||||
rocket_okapi = "0.7.0-alpha-1"
|
rocket_okapi = { version = "0.8.0-rc.1", features = ["swagger"] }
|
||||||
log = "0.4.0"
|
log = "0.4.0"
|
||||||
pretty_env_logger = "0.4.0"
|
pretty_env_logger = "0.4.0"
|
||||||
thiserror = "1.0.29"
|
thiserror = "1.0.29"
|
||||||
|
|||||||
@@ -15,6 +15,13 @@ impl GeoLocateTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn start(mut self) {
|
pub(crate) fn start(mut self) {
|
||||||
|
if ::std::env::var("GEO_IP_SERVICE_API_KEY").is_err() {
|
||||||
|
error!(
|
||||||
|
"Env var GEO_IP_SERVICE_API_KEY is not set. Geolocation tasks will not be started."
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
info!("Spawning mix node locator task runner...");
|
info!("Spawning mix node locator task runner...");
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let mut interval_timer = tokio::time::interval(std::time::Duration::from_millis(50));
|
let mut interval_timer = tokio::time::interval(std::time::Duration::from_millis(50));
|
||||||
|
|||||||
@@ -2,9 +2,12 @@ use crate::country_statistics::country_nodes_distribution::CountryNodesDistribut
|
|||||||
use crate::state::ExplorerApiStateContext;
|
use crate::state::ExplorerApiStateContext;
|
||||||
use rocket::serde::json::Json;
|
use rocket::serde::json::Json;
|
||||||
use rocket::{Route, State};
|
use rocket::{Route, State};
|
||||||
|
use rocket_okapi::okapi::openapi3::OpenApi;
|
||||||
|
use rocket_okapi::openapi_get_routes_spec;
|
||||||
|
use rocket_okapi::settings::OpenApiSettings;
|
||||||
|
|
||||||
pub fn country_statistics_make_default_routes() -> Vec<Route> {
|
pub fn country_statistics_make_default_routes(settings: &OpenApiSettings) -> (Vec<Route>, OpenApi) {
|
||||||
routes_with_openapi![index]
|
openapi_get_routes_spec![settings: index]
|
||||||
}
|
}
|
||||||
|
|
||||||
// We could either separate stuff by structure (like this, http is separate), or we could just
|
// We could either separate stuff by structure (like this, http is separate), or we could just
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
use log::info;
|
use log::info;
|
||||||
use rocket::http::Method;
|
use rocket::http::Method;
|
||||||
use rocket::Request;
|
use rocket::{Build, Request, Rocket};
|
||||||
use rocket_cors::{AllowedHeaders, AllowedOrigins};
|
use rocket_cors::{AllowedHeaders, AllowedOrigins};
|
||||||
use rocket_okapi::swagger_ui::make_swagger_ui;
|
use rocket_okapi::swagger_ui::make_swagger_ui;
|
||||||
|
|
||||||
use crate::country_statistics::http::country_statistics_make_default_routes;
|
use crate::country_statistics::http::country_statistics_make_default_routes;
|
||||||
use crate::http::swagger::get_docs;
|
use crate::http::swagger::get_docs;
|
||||||
use crate::mix_node::http::mix_node_make_default_routes;
|
use crate::mix_node::http::mix_node_make_default_routes;
|
||||||
|
use crate::mix_nodes::http::mix_nodes_make_default_routes;
|
||||||
use crate::ping::http::ping_make_default_routes;
|
use crate::ping::http::ping_make_default_routes;
|
||||||
use crate::state::ExplorerApiStateContext;
|
use crate::state::ExplorerApiStateContext;
|
||||||
|
|
||||||
@@ -15,7 +16,11 @@ mod swagger;
|
|||||||
pub(crate) fn start(state: ExplorerApiStateContext) {
|
pub(crate) fn start(state: ExplorerApiStateContext) {
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
info!("Starting up...");
|
info!("Starting up...");
|
||||||
|
configure_rocket(state).launch().await
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn configure_rocket(state: ExplorerApiStateContext) -> Rocket<Build> {
|
||||||
let allowed_origins = AllowedOrigins::all();
|
let allowed_origins = AllowedOrigins::all();
|
||||||
|
|
||||||
// You can also deserialize this
|
// You can also deserialize this
|
||||||
@@ -29,19 +34,25 @@ pub(crate) fn start(state: ExplorerApiStateContext) {
|
|||||||
.to_cors()
|
.to_cors()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
let openapi_settings = rocket_okapi::settings::OpenApiSettings::default();
|
||||||
let config = rocket::config::Config::release_default();
|
let config = rocket::config::Config::release_default();
|
||||||
rocket::build()
|
let mut building_rocket = rocket::build().configure(config);
|
||||||
.configure(config)
|
|
||||||
.mount("/countries", country_statistics_make_default_routes())
|
mount_endpoints_and_merged_docs! {
|
||||||
.mount("/ping", ping_make_default_routes())
|
building_rocket,
|
||||||
.mount("/mix-node", mix_node_make_default_routes())
|
"/v1".to_owned(),
|
||||||
|
openapi_settings,
|
||||||
|
"/ping" => ping_make_default_routes(&openapi_settings),
|
||||||
|
"/countries" => country_statistics_make_default_routes(&openapi_settings),
|
||||||
|
"/mix-node" => mix_node_make_default_routes(&openapi_settings),
|
||||||
|
"/mix-nodes" => mix_nodes_make_default_routes(&openapi_settings),
|
||||||
|
};
|
||||||
|
|
||||||
|
building_rocket
|
||||||
.mount("/swagger", make_swagger_ui(&get_docs()))
|
.mount("/swagger", make_swagger_ui(&get_docs()))
|
||||||
.register("/", catchers![not_found])
|
.register("/", catchers![not_found])
|
||||||
.manage(state)
|
.manage(state)
|
||||||
.attach(cors)
|
.attach(cors)
|
||||||
.launch()
|
|
||||||
.await
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[catch(404)]
|
#[catch(404)]
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
use rocket_okapi::swagger_ui::{SwaggerUIConfig, UrlObject};
|
use rocket_okapi::swagger_ui::SwaggerUIConfig;
|
||||||
|
|
||||||
pub(crate) fn get_docs() -> SwaggerUIConfig {
|
pub(crate) fn get_docs() -> SwaggerUIConfig {
|
||||||
SwaggerUIConfig {
|
SwaggerUIConfig {
|
||||||
urls: vec![
|
url: "../v1/openapi.json".to_owned(),
|
||||||
UrlObject::new("Country statistics", "/countries/openapi.json"),
|
|
||||||
UrlObject::new("Node ping", "/ping/openapi.json"),
|
|
||||||
UrlObject::new("Mix node", "/mix-node/openapi.json"),
|
|
||||||
],
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,11 @@ impl ExplorerApi {
|
|||||||
async fn run(&mut self) {
|
async fn run(&mut self) {
|
||||||
info!("Explorer API starting up...");
|
info!("Explorer API starting up...");
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"Using validator API - {}",
|
||||||
|
network_defaults::default_api_endpoints()[0].clone()
|
||||||
|
);
|
||||||
|
|
||||||
// spawn concurrent tasks
|
// spawn concurrent tasks
|
||||||
mix_nodes::tasks::MixNodesTasks::new(self.state.clone()).start();
|
mix_nodes::tasks::MixNodesTasks::new(self.state.clone()).start();
|
||||||
country_statistics::distribution::CountryStatisticsDistributionTask::new(
|
country_statistics::distribution::CountryStatisticsDistributionTask::new(
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
use reqwest::Error as ReqwestError;
|
use reqwest::Error as ReqwestError;
|
||||||
use rocket::serde::json::Json;
|
use rocket::serde::json::Json;
|
||||||
use rocket::{Route, State};
|
use rocket::{Route, State};
|
||||||
|
use rocket_okapi::okapi::openapi3::OpenApi;
|
||||||
|
use rocket_okapi::openapi_get_routes_spec;
|
||||||
|
use rocket_okapi::settings::OpenApiSettings;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use mixnet_contract::{Addr, Coin, Delegation, Layer, MixNode};
|
use mixnet_contract::{Addr, Coin, Delegation, Layer, MixNode};
|
||||||
@@ -9,13 +12,12 @@ use crate::mix_node::models::{NodeDescription, NodeStats};
|
|||||||
use crate::mix_nodes::{get_mixnode_delegations, get_single_mixnode_delegations, Location};
|
use crate::mix_nodes::{get_mixnode_delegations, get_single_mixnode_delegations, Location};
|
||||||
use crate::state::ExplorerApiStateContext;
|
use crate::state::ExplorerApiStateContext;
|
||||||
|
|
||||||
pub fn mix_node_make_default_routes() -> Vec<Route> {
|
pub fn mix_node_make_default_routes(settings: &OpenApiSettings) -> (Vec<Route>, OpenApi) {
|
||||||
routes_with_openapi![
|
openapi_get_routes_spec![
|
||||||
get_delegations,
|
settings: get_delegations,
|
||||||
get_all_delegations,
|
get_all_delegations,
|
||||||
get_description,
|
get_description,
|
||||||
get_stats,
|
get_stats,
|
||||||
list
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,14 +31,6 @@ pub(crate) struct PrettyMixNodeBondWithLocation {
|
|||||||
pub mix_node: MixNode,
|
pub mix_node: MixNode,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[openapi(tag = "mix_node")]
|
|
||||||
#[get("/")]
|
|
||||||
pub(crate) async fn list(
|
|
||||||
state: &State<ExplorerApiStateContext>,
|
|
||||||
) -> Json<Vec<PrettyMixNodeBondWithLocation>> {
|
|
||||||
Json(state.inner.mix_nodes.get_mixnodes_with_location().await)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[openapi(tag = "mix_node")]
|
#[openapi(tag = "mix_node")]
|
||||||
#[get("/<pubkey>/delegations")]
|
#[get("/<pubkey>/delegations")]
|
||||||
pub(crate) async fn get_delegations(pubkey: &str) -> Json<Vec<Delegation>> {
|
pub(crate) async fn get_delegations(pubkey: &str) -> Json<Vec<Delegation>> {
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
use rocket::serde::json::Json;
|
||||||
|
use rocket::{Route, State};
|
||||||
|
use rocket_okapi::okapi::openapi3::OpenApi;
|
||||||
|
use rocket_okapi::openapi_get_routes_spec;
|
||||||
|
use rocket_okapi::settings::OpenApiSettings;
|
||||||
|
|
||||||
|
use crate::mix_node::http::PrettyMixNodeBondWithLocation;
|
||||||
|
use crate::state::ExplorerApiStateContext;
|
||||||
|
|
||||||
|
pub fn mix_nodes_make_default_routes(settings: &OpenApiSettings) -> (Vec<Route>, OpenApi) {
|
||||||
|
openapi_get_routes_spec![settings: list]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[openapi(tag = "mix_nodes")]
|
||||||
|
#[get("/")]
|
||||||
|
pub(crate) async fn list(
|
||||||
|
state: &State<ExplorerApiStateContext>,
|
||||||
|
) -> Json<Vec<PrettyMixNodeBondWithLocation>> {
|
||||||
|
Json(state.inner.mix_nodes.get_mixnodes_with_location().await)
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
pub(crate) mod http;
|
||||||
pub(crate) mod tasks;
|
pub(crate) mod tasks;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ use std::time::Duration;
|
|||||||
|
|
||||||
use rocket::serde::json::Json;
|
use rocket::serde::json::Json;
|
||||||
use rocket::{Route, State};
|
use rocket::{Route, State};
|
||||||
|
use rocket_okapi::okapi::openapi3::OpenApi;
|
||||||
|
use rocket_okapi::openapi_get_routes_spec;
|
||||||
|
use rocket_okapi::settings::OpenApiSettings;
|
||||||
|
|
||||||
use mixnet_contract::MixNodeBond;
|
use mixnet_contract::MixNodeBond;
|
||||||
|
|
||||||
@@ -12,8 +15,8 @@ use crate::state::ExplorerApiStateContext;
|
|||||||
|
|
||||||
const CONNECTION_TIMEOUT_SECONDS: Duration = Duration::from_secs(10);
|
const CONNECTION_TIMEOUT_SECONDS: Duration = Duration::from_secs(10);
|
||||||
|
|
||||||
pub fn ping_make_default_routes() -> Vec<Route> {
|
pub fn ping_make_default_routes(settings: &OpenApiSettings) -> (Vec<Route>, OpenApi) {
|
||||||
routes_with_openapi![index]
|
openapi_get_routes_spec![settings: index]
|
||||||
}
|
}
|
||||||
|
|
||||||
#[openapi(tag = "ping")]
|
#[openapi(tag = "ping")]
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
EXPLORER_API_URL=https://sandbox-explorer.nymtech.net/api/v1
|
||||||
|
VALIDATOR_API_URL=https://sandbox-validator.nymtech.net
|
||||||
|
BIG_DIPPER_URL=https://sandbox-blocks.nymtech.net
|
||||||
|
CURRENCY_DENOM=unymt
|
||||||
|
CURRENCY_STAKING_DENOM=unyxt
|
||||||
Generated
+60
@@ -50,6 +50,7 @@
|
|||||||
"clean-webpack-plugin": "^4.0.0",
|
"clean-webpack-plugin": "^4.0.0",
|
||||||
"css-loader": "^6.2.0",
|
"css-loader": "^6.2.0",
|
||||||
"css-minimizer-webpack-plugin": "^3.0.2",
|
"css-minimizer-webpack-plugin": "^3.0.2",
|
||||||
|
"dotenv-webpack": "^7.0.3",
|
||||||
"eslint": "7.32.0",
|
"eslint": "7.32.0",
|
||||||
"eslint-config-airbnb": "18.2.1",
|
"eslint-config-airbnb": "18.2.1",
|
||||||
"eslint-config-prettier": "8.3.0",
|
"eslint-config-prettier": "8.3.0",
|
||||||
@@ -6198,6 +6199,39 @@
|
|||||||
"tslib": "^2.0.3"
|
"tslib": "^2.0.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dotenv-defaults": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/dotenv-defaults/-/dotenv-defaults-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-iOIzovWfsUHU91L5i8bJce3NYK5JXeAwH50Jh6+ARUdLiiGlYWfGw6UkzsYqaXZH/hjE/eCd/PlfM/qqyK0AMg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"dotenv": "^8.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/dotenv-defaults/node_modules/dotenv": {
|
||||||
|
"version": "8.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz",
|
||||||
|
"integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/dotenv-webpack": {
|
||||||
|
"version": "7.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/dotenv-webpack/-/dotenv-webpack-7.0.3.tgz",
|
||||||
|
"integrity": "sha512-O0O9pOEwrk+n1zzR3T2uuXRlw64QxHSPeNN1GaiNBloQFNaCUL9V8jxSVz4jlXXFP/CIqK8YecWf8BAvsSgMjw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"dotenv-defaults": "^2.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"webpack": "^4 || ^5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ecc-jsbn": {
|
"node_modules/ecc-jsbn": {
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
|
||||||
@@ -21882,6 +21916,32 @@
|
|||||||
"tslib": "^2.0.3"
|
"tslib": "^2.0.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"dotenv-defaults": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/dotenv-defaults/-/dotenv-defaults-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-iOIzovWfsUHU91L5i8bJce3NYK5JXeAwH50Jh6+ARUdLiiGlYWfGw6UkzsYqaXZH/hjE/eCd/PlfM/qqyK0AMg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"dotenv": "^8.2.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"dotenv": {
|
||||||
|
"version": "8.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz",
|
||||||
|
"integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dotenv-webpack": {
|
||||||
|
"version": "7.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/dotenv-webpack/-/dotenv-webpack-7.0.3.tgz",
|
||||||
|
"integrity": "sha512-O0O9pOEwrk+n1zzR3T2uuXRlw64QxHSPeNN1GaiNBloQFNaCUL9V8jxSVz4jlXXFP/CIqK8YecWf8BAvsSgMjw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"dotenv-defaults": "^2.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"ecc-jsbn": {
|
"ecc-jsbn": {
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
|
||||||
|
|||||||
@@ -45,6 +45,7 @@
|
|||||||
"clean-webpack-plugin": "^4.0.0",
|
"clean-webpack-plugin": "^4.0.0",
|
||||||
"css-loader": "^6.2.0",
|
"css-loader": "^6.2.0",
|
||||||
"css-minimizer-webpack-plugin": "^3.0.2",
|
"css-minimizer-webpack-plugin": "^3.0.2",
|
||||||
|
"dotenv-webpack": "^7.0.3",
|
||||||
"eslint": "7.32.0",
|
"eslint": "7.32.0",
|
||||||
"eslint-config-airbnb": "18.2.1",
|
"eslint-config-airbnb": "18.2.1",
|
||||||
"eslint-config-prettier": "8.3.0",
|
"eslint-config-prettier": "8.3.0",
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
// master APIs
|
// master APIs
|
||||||
export const MASTER_URL = 'https://testnet-milhon-explorer.nymtech.net';
|
export const API_BASE_URL = process.env.EXPLORER_API_URL;
|
||||||
export const MASTER_VALIDATOR_URL =
|
export const VALIDATOR_API_BASE_URL = process.env.VALIDATOR_API_URL;
|
||||||
'https://testnet-milhon-validator1.nymtech.net';
|
export const BIG_DIPPER = process.env.BIG_DIPPER_URL;
|
||||||
export const BIG_DIPPER = 'https://testnet-milhon-blocks.nymtech.net';
|
|
||||||
|
|
||||||
// specific API routes
|
// specific API routes
|
||||||
export const MIXNODE_PING = `${MASTER_URL}/api/ping`;
|
export const MIXNODE_PING = `${API_BASE_URL}/ping`;
|
||||||
export const MIXNODES_API = `${MASTER_URL}/api/mix-node`;
|
export const MIXNODES_API = `${API_BASE_URL}/mix-nodes`;
|
||||||
export const GATEWAYS_API = `${MASTER_VALIDATOR_URL}/api/v1/gateways`;
|
export const MIXNODE_API = `${API_BASE_URL}/mix-node`;
|
||||||
export const VALIDATORS_API = `${MASTER_VALIDATOR_URL}/validators`;
|
export const GATEWAYS_API = `${VALIDATOR_API_BASE_URL}/api/v1/gateways`;
|
||||||
export const BLOCK_API = `${MASTER_VALIDATOR_URL}/block`;
|
export const VALIDATORS_API = `${VALIDATOR_API_BASE_URL}/validators`;
|
||||||
export const COUNTRY_DATA_API = `${MASTER_URL}/api/countries`;
|
export const BLOCK_API = `${VALIDATOR_API_BASE_URL}/block`;
|
||||||
export const UPTIME_STORY_API = `${MASTER_VALIDATOR_URL}/api/v1/status/mixnode`; // add ID then '/history' to this.
|
export const COUNTRY_DATA_API = `${API_BASE_URL}/countries`;
|
||||||
|
export const UPTIME_STORY_API = `${VALIDATOR_API_BASE_URL}/api/v1/status/mixnode`; // add ID then '/history' to this.
|
||||||
|
|
||||||
// errors
|
// errors
|
||||||
export const MIXNODE_API_ERROR =
|
export const MIXNODE_API_ERROR =
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
COUNTRY_DATA_API,
|
COUNTRY_DATA_API,
|
||||||
MIXNODE_PING,
|
MIXNODE_PING,
|
||||||
UPTIME_STORY_API,
|
UPTIME_STORY_API,
|
||||||
|
MIXNODE_API,
|
||||||
} from './constants';
|
} from './constants';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -87,10 +88,10 @@ export class Api {
|
|||||||
static fetchDelegationsById = async (
|
static fetchDelegationsById = async (
|
||||||
id: string,
|
id: string,
|
||||||
): Promise<DelegationsResponse> =>
|
): Promise<DelegationsResponse> =>
|
||||||
(await fetch(`${MIXNODES_API}/${id}/delegations`)).json();
|
(await fetch(`${MIXNODE_API}/${id}/delegations`)).json();
|
||||||
|
|
||||||
static fetchStatsById = async (id: string): Promise<StatsResponse> =>
|
static fetchStatsById = async (id: string): Promise<StatsResponse> =>
|
||||||
(await fetch(`${MIXNODES_API}/${id}/stats`)).json();
|
(await fetch(`${MIXNODE_API}/${id}/stats`)).json();
|
||||||
|
|
||||||
static fetchStatusById = async (id: string): Promise<StatusResponse> =>
|
static fetchStatusById = async (id: string): Promise<StatusResponse> =>
|
||||||
(await fetch(`${MIXNODE_PING}/${id}`)).json();
|
(await fetch(`${MIXNODE_PING}/${id}`)).json();
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { printableCoin } from '@nymproject/nym-validator-client';
|
import { Alert, Box, CircularProgress, useMediaQuery } from '@mui/material';
|
||||||
import { Alert, CircularProgress, useMediaQuery, Box } from '@mui/material';
|
|
||||||
import { useTheme } from '@mui/material/styles';
|
import { useTheme } from '@mui/material/styles';
|
||||||
import Table from '@mui/material/Table';
|
import Table from '@mui/material/Table';
|
||||||
import TableBody from '@mui/material/TableBody';
|
import TableBody from '@mui/material/TableBody';
|
||||||
@@ -11,6 +10,7 @@ import TableRow from '@mui/material/TableRow';
|
|||||||
import Paper from '@mui/material/Paper';
|
import Paper from '@mui/material/Paper';
|
||||||
import { useMainContext } from 'src/context/main';
|
import { useMainContext } from 'src/context/main';
|
||||||
import { ExpandMore } from '@mui/icons-material';
|
import { ExpandMore } from '@mui/icons-material';
|
||||||
|
import { currencyToString } from '../utils/currency';
|
||||||
|
|
||||||
export const BondBreakdownTable: React.FC = () => {
|
export const BondBreakdownTable: React.FC = () => {
|
||||||
const { mixnodeDetailInfo, delegations } = useMainContext();
|
const { mixnodeDetailInfo, delegations } = useMainContext();
|
||||||
@@ -34,24 +34,23 @@ export const BondBreakdownTable: React.FC = () => {
|
|||||||
const thisMixnode = mixnodeDetailInfo?.data[0];
|
const thisMixnode = mixnodeDetailInfo?.data[0];
|
||||||
|
|
||||||
// delegations
|
// delegations
|
||||||
const decimalisedDelegations = printableCoin({
|
const decimalisedDelegations = currencyToString(
|
||||||
amount: thisMixnode.total_delegation.amount.toString(),
|
thisMixnode.total_delegation.amount.toString(),
|
||||||
denom: thisMixnode.total_delegation.denom,
|
thisMixnode.total_delegation.denom,
|
||||||
});
|
);
|
||||||
|
|
||||||
// pledges
|
// pledges
|
||||||
const decimalisedPledges = printableCoin({
|
const decimalisedPledges = currencyToString(
|
||||||
amount: thisMixnode.bond_amount.amount.toString(),
|
thisMixnode.pledge_amount.amount.toString(),
|
||||||
denom: thisMixnode.bond_amount.denom,
|
thisMixnode.pledge_amount.denom,
|
||||||
});
|
);
|
||||||
|
|
||||||
// bonds total (del + pledges)
|
// bonds total (del + pledges)
|
||||||
const pledgesSum = Number(thisMixnode.bond_amount.amount);
|
const pledgesSum = Number(thisMixnode.pledge_amount.amount);
|
||||||
const delegationsSum = Number(thisMixnode.total_delegation.amount);
|
const delegationsSum = Number(thisMixnode.total_delegation.amount);
|
||||||
const bondsTotal = printableCoin({
|
const bondsTotal = currencyToString(
|
||||||
amount: (delegationsSum + pledgesSum).toString(),
|
(delegationsSum + pledgesSum).toString(),
|
||||||
denom: 'upunk',
|
);
|
||||||
});
|
|
||||||
|
|
||||||
setBonds({
|
setBonds({
|
||||||
delegations: decimalisedDelegations,
|
delegations: decimalisedDelegations,
|
||||||
@@ -89,7 +88,7 @@ export const BondBreakdownTable: React.FC = () => {
|
|||||||
mixnodeDetailInfo.data[0].total_delegation.amount,
|
mixnodeDetailInfo.data[0].total_delegation.amount,
|
||||||
);
|
);
|
||||||
const rawPledgeAmount = Number(
|
const rawPledgeAmount = Number(
|
||||||
mixnodeDetailInfo.data[0].bond_amount.amount,
|
mixnodeDetailInfo.data[0].pledge_amount.amount,
|
||||||
);
|
);
|
||||||
const rawTotalBondsAmount = rawDelegationAmount + rawPledgeAmount;
|
const rawTotalBondsAmount = rawDelegationAmount + rawPledgeAmount;
|
||||||
return ((num * 100) / rawTotalBondsAmount).toFixed(1);
|
return ((num * 100) / rawTotalBondsAmount).toFixed(1);
|
||||||
@@ -203,7 +202,7 @@ export const BondBreakdownTable: React.FC = () => {
|
|||||||
{owner}
|
{owner}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell align="left">
|
<TableCell align="left">
|
||||||
{printableCoin({ amount: amount.toString(), denom })}
|
{currencyToString(amount.toString(), denom)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell align="left">
|
<TableCell align="left">
|
||||||
{calcBondPercentage(amount)}%
|
{calcBondPercentage(amount)}%
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ import {
|
|||||||
TableHead,
|
TableHead,
|
||||||
TableRow,
|
TableRow,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { printableCoin } from '@nymproject/nym-validator-client';
|
|
||||||
import { cellStyles } from './Universal-DataGrid';
|
import { cellStyles } from './Universal-DataGrid';
|
||||||
import { MixnodeRowType } from '../utils/index';
|
import { MixnodeRowType } from '../utils/index';
|
||||||
|
import { currencyToString } from '../utils/currency';
|
||||||
|
|
||||||
export type ColumnsType = {
|
export type ColumnsType = {
|
||||||
field: string;
|
field: string;
|
||||||
@@ -28,7 +28,7 @@ export interface UniversalTableProps {
|
|||||||
|
|
||||||
function formatCellValues(val: string | number, field: string) {
|
function formatCellValues(val: string | number, field: string) {
|
||||||
if (field === 'bond') {
|
if (field === 'bond') {
|
||||||
return printableCoin({ amount: val.toString(), denom: 'upunk' });
|
return currencyToString(val.toString());
|
||||||
}
|
}
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ interface ChartProps {
|
|||||||
loading: boolean;
|
loading: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
type FormattedDateRecord = [string, number, number];
|
type FormattedDateRecord = [string, number];
|
||||||
type FormattedChartHeadings = string[];
|
type FormattedChartHeadings = string[];
|
||||||
type FormattedChartData = [FormattedChartHeadings | FormattedDateRecord];
|
type FormattedChartData = [FormattedChartHeadings | FormattedDateRecord];
|
||||||
|
|
||||||
@@ -30,22 +30,19 @@ export const UptimeChart: React.FC<ChartProps> = ({
|
|||||||
const color = theme.palette.text.primary;
|
const color = theme.palette.text.primary;
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (uptimeStory.data?.history) {
|
if (uptimeStory.data?.history) {
|
||||||
const allFormattedChartData: FormattedChartData = [
|
const allFormattedChartData: FormattedChartData = [['Date', 'Uptime']];
|
||||||
['Date', 'UptimeV4', 'UptimeV6'],
|
|
||||||
];
|
|
||||||
uptimeStory.data.history.forEach((eachDate) => {
|
uptimeStory.data.history.forEach((eachDate) => {
|
||||||
const formattedDateUptimeRecord: FormattedDateRecord = [
|
const formattedDateUptimeRecord: FormattedDateRecord = [
|
||||||
format(new Date(eachDate.date), 'MMM dd'),
|
format(new Date(eachDate.date), 'MMM dd'),
|
||||||
eachDate.ipv4_uptime,
|
eachDate.uptime,
|
||||||
eachDate.ipv6_uptime,
|
|
||||||
];
|
];
|
||||||
allFormattedChartData.push(formattedDateUptimeRecord);
|
allFormattedChartData.push(formattedDateUptimeRecord);
|
||||||
});
|
});
|
||||||
setFormattedChartData(allFormattedChartData);
|
setFormattedChartData(allFormattedChartData);
|
||||||
} else {
|
} else {
|
||||||
const emptyData: any = [
|
const emptyData: any = [
|
||||||
['Date', 'UptimeV4', 'UptimeV6'],
|
['Date', 'Uptime'],
|
||||||
['Jul 27', 10, 10],
|
['Jul 27', 10],
|
||||||
];
|
];
|
||||||
setFormattedChartData(emptyData);
|
setFormattedChartData(emptyData);
|
||||||
}
|
}
|
||||||
@@ -65,8 +62,8 @@ export const UptimeChart: React.FC<ChartProps> = ({
|
|||||||
uptimeStory.data
|
uptimeStory.data
|
||||||
? formattedChartData
|
? formattedChartData
|
||||||
: [
|
: [
|
||||||
['Date', 'UptimeV4', 'UptimeV6'],
|
['Date', 'Uptime'],
|
||||||
[format(new Date(Date.now()), 'MMM dd'), 0, 0],
|
[format(new Date(Date.now()), 'MMM dd'), 0],
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
options={{
|
options={{
|
||||||
@@ -77,7 +74,7 @@ export const UptimeChart: React.FC<ChartProps> = ({
|
|||||||
color: uptimeStory.error
|
color: uptimeStory.error
|
||||||
? 'rgba(255, 255, 255, 0.4)'
|
? 'rgba(255, 255, 255, 0.4)'
|
||||||
: 'rgba(255, 255, 255, 1)',
|
: 'rgba(255, 255, 255, 1)',
|
||||||
colors: ['#FB7A21', '#CC808A'],
|
colors: ['#FB7A21'],
|
||||||
legend: {
|
legend: {
|
||||||
textStyle: {
|
textStyle: {
|
||||||
color,
|
color,
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Button, Card, Grid, Typography } from '@mui/material';
|
import { Button, Card, Grid, Typography } from '@mui/material';
|
||||||
import { GridColDef, GridRenderCellParams } from '@mui/x-data-grid';
|
import { GridColDef, GridRenderCellParams } from '@mui/x-data-grid';
|
||||||
import { printableCoin } from '@nymproject/nym-validator-client';
|
|
||||||
import { SelectChangeEvent } from '@mui/material/Select';
|
import { SelectChangeEvent } from '@mui/material/Select';
|
||||||
import { useMainContext } from 'src/context/main';
|
import { useMainContext } from 'src/context/main';
|
||||||
import { gatewayToGridRow } from 'src/utils';
|
import { gatewayToGridRow } from 'src/utils';
|
||||||
@@ -13,6 +12,7 @@ import {
|
|||||||
cellStyles,
|
cellStyles,
|
||||||
UniversalDataGrid,
|
UniversalDataGrid,
|
||||||
} from 'src/components/Universal-DataGrid';
|
} from 'src/components/Universal-DataGrid';
|
||||||
|
import { currencyToString } from '../../utils/currency';
|
||||||
|
|
||||||
export const PageGateways: React.FC = () => {
|
export const PageGateways: React.FC = () => {
|
||||||
const { gateways } = useMainContext();
|
const { gateways } = useMainContext();
|
||||||
@@ -79,17 +79,11 @@ export const PageGateways: React.FC = () => {
|
|||||||
renderHeader: () => <CustomColumnHeading headingTitle="Pledge" />,
|
renderHeader: () => <CustomColumnHeading headingTitle="Pledge" />,
|
||||||
headerClassName: 'MuiDataGrid-header-override',
|
headerClassName: 'MuiDataGrid-header-override',
|
||||||
headerAlign: 'left',
|
headerAlign: 'left',
|
||||||
renderCell: (params: GridRenderCellParams) => {
|
renderCell: (params: GridRenderCellParams) => (
|
||||||
const bondAsPunk = printableCoin({
|
|
||||||
amount: params.value as string,
|
|
||||||
denom: 'upunk',
|
|
||||||
});
|
|
||||||
return (
|
|
||||||
<Typography sx={cellStyles} data-testid="pledge-amount">
|
<Typography sx={cellStyles} data-testid="pledge-amount">
|
||||||
{bondAsPunk}
|
{currencyToString(params.value)}
|
||||||
</Typography>
|
</Typography>
|
||||||
);
|
),
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'host',
|
field: 'host',
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { GridColDef, GridRenderCellParams } from '@mui/x-data-grid';
|
import { GridColDef, GridRenderCellParams } from '@mui/x-data-grid';
|
||||||
import { printableCoin } from '@nymproject/nym-validator-client';
|
|
||||||
import { Button, Grid, Link as MuiLink, Card } from '@mui/material';
|
import { Button, Grid, Link as MuiLink, Card } from '@mui/material';
|
||||||
import { Link as RRDLink } from 'react-router-dom';
|
import { Link as RRDLink } from 'react-router-dom';
|
||||||
import { SelectChangeEvent } from '@mui/material/Select';
|
import { SelectChangeEvent } from '@mui/material/Select';
|
||||||
@@ -15,6 +14,7 @@ import {
|
|||||||
UniversalDataGrid,
|
UniversalDataGrid,
|
||||||
cellStyles,
|
cellStyles,
|
||||||
} from 'src/components/Universal-DataGrid';
|
} from 'src/components/Universal-DataGrid';
|
||||||
|
import { currencyToString } from '../../utils/currency';
|
||||||
|
|
||||||
export const PageMixnodes: React.FC = () => {
|
export const PageMixnodes: React.FC = () => {
|
||||||
const { mixnodes } = useMainContext();
|
const { mixnodes } = useMainContext();
|
||||||
@@ -92,21 +92,15 @@ export const PageMixnodes: React.FC = () => {
|
|||||||
headerClassName: 'MuiDataGrid-header-override',
|
headerClassName: 'MuiDataGrid-header-override',
|
||||||
width: 150,
|
width: 150,
|
||||||
headerAlign: 'left',
|
headerAlign: 'left',
|
||||||
renderCell: (params: GridRenderCellParams) => {
|
renderCell: (params: GridRenderCellParams) => (
|
||||||
const bondAsPunk = printableCoin({
|
|
||||||
amount: params.value as string,
|
|
||||||
denom: 'upunk',
|
|
||||||
});
|
|
||||||
return (
|
|
||||||
<MuiLink
|
<MuiLink
|
||||||
sx={cellStyles}
|
sx={cellStyles}
|
||||||
component={RRDLink}
|
component={RRDLink}
|
||||||
to={`/network-components/mixnodes/${params.row.identity_key}`}
|
to={`/network-components/mixnodes/${params.row.identity_key}`}
|
||||||
>
|
>
|
||||||
{bondAsPunk}
|
{currencyToString(params.value)}
|
||||||
</MuiLink>
|
</MuiLink>
|
||||||
);
|
),
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'location',
|
field: 'location',
|
||||||
|
|||||||
@@ -7,13 +7,14 @@ export interface ClientConfig {
|
|||||||
|
|
||||||
export interface MixNode {
|
export interface MixNode {
|
||||||
host: string;
|
host: string;
|
||||||
location: string;
|
mix_port: number;
|
||||||
|
http_api_port: number;
|
||||||
|
verloc_port: number;
|
||||||
sphinx_key: string;
|
sphinx_key: string;
|
||||||
identity_key: string;
|
identity_key: string;
|
||||||
version: string;
|
version: string;
|
||||||
mix_port: number;
|
profit_margin_percent: number;
|
||||||
verloc_port: number;
|
location: string;
|
||||||
http_api_port: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Gateway {
|
export interface Gateway {
|
||||||
@@ -32,7 +33,7 @@ export interface Amount {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface MixNodeResponseItem {
|
export interface MixNodeResponseItem {
|
||||||
bond_amount: Amount;
|
pledge_amount: Amount;
|
||||||
total_delegation: Amount;
|
total_delegation: Amount;
|
||||||
owner: string;
|
owner: string;
|
||||||
layer: string;
|
layer: string;
|
||||||
@@ -74,7 +75,7 @@ export type MixNodeHistoryResponse = StatsResponse;
|
|||||||
|
|
||||||
export interface GatewayResponseItem {
|
export interface GatewayResponseItem {
|
||||||
block_height: number;
|
block_height: number;
|
||||||
bond_amount: Amount;
|
pledge_amount: Amount;
|
||||||
total_delegation: Amount;
|
total_delegation: Amount;
|
||||||
owner: string;
|
owner: string;
|
||||||
gateway: Gateway;
|
gateway: Gateway;
|
||||||
@@ -156,8 +157,7 @@ export type StatusResponse = {
|
|||||||
|
|
||||||
export type UptimeTime = {
|
export type UptimeTime = {
|
||||||
date: string;
|
date: string;
|
||||||
ipv4_uptime: number;
|
uptime: number;
|
||||||
ipv6_uptime: number;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UptimeStoryResponse = {
|
export type UptimeStoryResponse = {
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { printableCoin } from '@nymproject/nym-validator-client';
|
||||||
|
|
||||||
|
const DENOM = process.env.CURRENCY_DENOM || 'unym';
|
||||||
|
const DENOM_STAKING = process.env.CURRENCY_STAKING_DENOM || 'unyx';
|
||||||
|
|
||||||
|
export const currencyToString = (amount: string, denom: string = DENOM) =>
|
||||||
|
printableCoin({
|
||||||
|
amount,
|
||||||
|
denom,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const stakingCurrencyToString = (
|
||||||
|
amount: string,
|
||||||
|
denom: string = DENOM_STAKING,
|
||||||
|
) =>
|
||||||
|
printableCoin({
|
||||||
|
amount,
|
||||||
|
denom,
|
||||||
|
});
|
||||||
@@ -69,7 +69,7 @@ export function mixnodeToGridRow(arrayOfMixnodes: MixNodeResponse): any {
|
|||||||
return !arrayOfMixnodes
|
return !arrayOfMixnodes
|
||||||
? []
|
? []
|
||||||
: arrayOfMixnodes.map((mn) => {
|
: arrayOfMixnodes.map((mn) => {
|
||||||
const pledge = Number(mn.bond_amount.amount) || 0;
|
const pledge = Number(mn.pledge_amount.amount) || 0;
|
||||||
const delegations = Number(mn.total_delegation.amount) || 0;
|
const delegations = Number(mn.total_delegation.amount) || 0;
|
||||||
const totalBond = pledge + delegations;
|
const totalBond = pledge + delegations;
|
||||||
const selfPercentage = ((pledge * 100) / totalBond).toFixed(2);
|
const selfPercentage = ((pledge * 100) / totalBond).toFixed(2);
|
||||||
@@ -96,7 +96,7 @@ export function gatewayToGridRow(
|
|||||||
owner: gw.owner,
|
owner: gw.owner,
|
||||||
identity_key: gw.gateway.identity_key || '',
|
identity_key: gw.gateway.identity_key || '',
|
||||||
location: gw?.gateway?.location || '',
|
location: gw?.gateway?.location || '',
|
||||||
bond: gw.bond_amount.amount || 0,
|
bond: gw.pledge_amount.amount || 0,
|
||||||
host: gw.gateway.host || '',
|
host: gw.gateway.host || '',
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
|
|||||||
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
|
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
|
||||||
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
|
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
|
||||||
const WebpackFavicons = require('webpack-favicons');
|
const WebpackFavicons = require('webpack-favicons');
|
||||||
|
const Dotenv = require('dotenv-webpack');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
entry: './src/index.tsx',
|
entry: './src/index.tsx',
|
||||||
@@ -78,6 +79,8 @@ module.exports = {
|
|||||||
new WebpackFavicons({
|
new WebpackFavicons({
|
||||||
src: 'src/logo.svg',
|
src: 'src/logo.svg',
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
new Dotenv(),
|
||||||
],
|
],
|
||||||
output: {
|
output: {
|
||||||
path: path.resolve(__dirname, 'dist'),
|
path: path.resolve(__dirname, 'dist'),
|
||||||
|
|||||||
Reference in New Issue
Block a user