Skip to content

Commit

Permalink
Merge pull request #39 from elsirion/2024-09-totals
Browse files Browse the repository at this point in the history
Add totals view to main page
  • Loading branch information
elsirion authored Sep 4, 2024
2 parents 5f2c485 + 437386c commit 597577a
Show file tree
Hide file tree
Showing 12 changed files with 220 additions and 20 deletions.
13 changes: 12 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions fmo_api_types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@ use fedimint_core::config::FederationId;
use fedimint_core::Amount;
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FedimintTotals {
pub federations: u64,
pub tx_volume: Amount,
pub tx_count: u64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FederationSummary {
pub id: FederationId,
Expand Down
1 change: 1 addition & 0 deletions fmo_frontend/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ futures = "0.3.30"
leptos = { version = "0.6", features = ["csr"] }
leptos-chartistry = "0.1.6"
leptos_router = { version = "0.6.13", features = ["csr"] }
num-format = "0.4.4"
reqwest = { version = "0.12.5", default-features = false, features = [ "json" ] }
serde_json = "1.0.122"
tokio = {version = "1.39.2"}
Expand Down
3 changes: 2 additions & 1 deletion fmo_frontend/src/components/alert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ pub fn Alert(

view! {
<div class=class role="alert">
<span class="font-bold">{title}</span> {message}
<span class="font-bold">{title}</span>
{message}
</div>
}
}
Expand Down
10 changes: 7 additions & 3 deletions fmo_frontend/src/components/federation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ pub fn Federation() -> impl IntoView {
url: guardian.url.to_string(),
})
.collect()/>
<General config={config.clone()}/>
<General config=config.clone()/>
</div>
<Tabs default="Activity">
<Tab name="Activity">
Expand All @@ -87,17 +87,21 @@ pub fn Federation() -> impl IntoView {
</Tab>
<Tab name="Config">
<div class="w-full overflow-x-scroll my-4">
<pre>{serde_json::to_string_pretty(&config).expect("can be encoded")}</pre>
<pre>
{serde_json::to_string_pretty(&config)
.expect("can be encoded")}
</pre>
</div>
</Tab>
</Tabs>
}
.into_view()
}
Some(Err(e)) => view! { { format!("Error: {}", e) } }.into_view(),
Some(Err(e)) => view! { {format!("Error: {}", e)} }.into_view(),
None => view! { "Loading..." }.into_view(),
}
}}

</div>
</Show>
}
Expand Down
13 changes: 7 additions & 6 deletions fmo_frontend/src/components/federation/utxos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ pub fn Utxos(federation_id: FederationId) -> impl IntoView {
view! {
<tr class="bg-white border-b dark:bg-gray-800 dark:border-gray-700">
<td class="px-6 py-4">
<a href=format!(
<a
href=format!(
"https://mempool.space/address/{}",
utxo.address.clone().assume_checked().to_string(),
)

class="text-blue-600 underline dark:text-blue-500 hover:no-underline"
>
<pre>
Expand All @@ -42,7 +44,6 @@ pub fn Utxos(federation_id: FederationId) -> impl IntoView {
}
})
.collect::<Vec<_>>();

view! {
<div>
<Alert
Expand All @@ -54,16 +55,16 @@ pub fn Utxos(federation_id: FederationId) -> impl IntoView {
<thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
<tr>
<th scope="col" class="px-6 py-3">
"UTXOs (" { utxos.len() } " total)"
"UTXOs ("
{utxos.len()}
" total)"
</th>
<th scope="col" class="px-6 py-3">
Amount
</th>
</tr>
</thead>
<tbody>
{ rows }
</tbody>
<tbody>{rows}</tbody>
</table>
</div>
}
Expand Down
20 changes: 19 additions & 1 deletion fmo_frontend/src/components/federations/federation_row.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::str::FromStr;

use fedimint_core::config::FederationId;
use fedimint_core::Amount;
use leptos::{component, view, IntoView};
Expand All @@ -14,6 +16,11 @@ pub fn FederationRow(
avg_txs: f64,
avg_volume: Amount,
) -> impl IntoView {
let degraded_federations = vec![FederationId::from_str(
"4b13a146ee4ba732b2b8914a72a0a2e5873e3e942da2d4eeefd85a5fe41f27ba",
)
.expect("can be parsed")];

view! {
<tr class="bg-white border-b dark:bg-gray-800 dark:border-gray-700">
<th
Expand All @@ -28,7 +35,18 @@ pub fn FederationRow(
</a>
</th>
<td class="px-6 py-4">
<Copyable text=invite/>

{if degraded_federations.contains(&id) {
view! {
<span class="bg-yellow-100 text-yellow-800 text-xs font-medium me-2 px-2.5 py-0.5 rounded dark:bg-gray-700 dark:text-yellow-300 border border-yellow-300">
"Degraded"
</span>
}
.into_view()
} else {
view! { <Copyable text=invite/> }.into_view()
}}

</td>
<td class="px-6 py-4">{total_assets.as_bitcoin(6).to_string()}</td>
<td class="px-6 py-4">
Expand Down
5 changes: 5 additions & 0 deletions fmo_frontend/src/components/federations/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
mod federation_row;
mod totals;

use fedimint_core::Amount;
use fmo_api_types::FederationSummary;
use leptos::{component, create_resource, view, IntoView, SignalGet};

use crate::components::federations::federation_row::FederationRow;
use crate::components::federations::totals::Totals;
use crate::BASE_URL;

#[component]
Expand Down Expand Up @@ -37,6 +39,9 @@ pub fn Federations() -> impl IntoView {
};

view! {
<div class="my-16">
<Totals/>
</div>
<div class="relative overflow-x-auto shadow-md sm:rounded-lg">
<table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400">
<caption class="p-5 text-lg font-semibold text-left rtl:text-right text-gray-900 bg-white dark:text-white dark:bg-gray-800">
Expand Down
113 changes: 113 additions & 0 deletions fmo_frontend/src/components/federations/totals.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
use std::time::Duration;

use fedimint_core::runtime::sleep;
use fedimint_core::util::{retry, FibonacciBackoff};
use fedimint_core::Amount;
use fmo_api_types::FedimintTotals;
use leptos::{component, create_resource, view, IntoView, SignalGet};
use num_format::{Locale, ToFormattedString};
use tracing::error;

use crate::util::{AsBitcoin, FmtBitcoin};

#[component]
pub fn Totals() -> impl IntoView {
let totals_res = create_resource(
|| (),
|_| async {
retry(
"fetching federation totals",
FibonacciBackoff::default().with_max_times(usize::MAX),
|| fetch_federation_totals(),
)
.await
.expect("Will never return Err")
},
);

view! {
<div class="flex items-center justify-center space-x-10">
<div class="text-center">
{move || {
match totals_res.get() {
Some(totals) => {
view! {
<div class="text-4xl font-bold mb-2">
{totals.federations.to_formatted_string(&Locale::en)}
</div>
}
}
None => {
view! {
<div class="text-4xl h-4 bg-gray-200 rounded-full dark:bg-gray-700 w-48 my-4 animate-pulse"></div>
}
}
}
}}
<div class="text-gray-500">Public Federations</div>
</div>
<div class="border-l border-gray-300 h-12"></div>
<div class="text-center">
{move || {
match totals_res.get() {
Some(totals) => {
view! {
<div class="text-4xl font-bold mb-2">
{totals.tx_count.to_formatted_string(&Locale::en)}
</div>
}
}
None => {
view! {
<div class="text-4xl h-4 bg-gray-200 rounded-full dark:bg-gray-700 w-48 my-4 animate-pulse"></div>
}
}
}
}}
<div class="text-gray-500">Total Transactions</div>
</div>
<div class="border-l border-gray-300 h-12"></div>
<div class="text-center">
{move || {
match totals_res.get() {
Some(totals) => {
view! {
<div class="text-4xl font-bold">
<svg
xmlns="http://www.w3.org/2000/svg"
width="40"
height="40"
fill="currentColor"
class="bi bi-currency-bitcoin"
viewBox="0 0 16 16"
class="inline mb-2"
>
<path d="M5.5 13v1.25c0 .138.112.25.25.25h1a.25.25 0 0 0 .25-.25V13h.5v1.25c0 .138.112.25.25.25h1a.25.25 0 0 0 .25-.25V13h.084c1.992 0 3.416-1.033 3.416-2.82 0-1.502-1.007-2.323-2.186-2.44v-.088c.97-.242 1.683-.974 1.683-2.19C11.997 3.93 10.847 3 9.092 3H9V1.75a.25.25 0 0 0-.25-.25h-1a.25.25 0 0 0-.25.25V3h-.573V1.75a.25.25 0 0 0-.25-.25H5.75a.25.25 0 0 0-.25.25V3l-1.998.011a.25.25 0 0 0-.25.25v.989c0 .137.11.25.248.25l.755-.005a.75.75 0 0 1 .745.75v5.505a.75.75 0 0 1-.75.75l-.748.011a.25.25 0 0 0-.25.25v1c0 .138.112.25.25.25zm1.427-8.513h1.719c.906 0 1.438.498 1.438 1.312 0 .871-.575 1.362-1.877 1.362h-1.28zm0 4.051h1.84c1.137 0 1.756.58 1.756 1.524 0 .953-.626 1.45-2.158 1.45H6.927z"></path>
</svg>
{format!(
"{:.*}",
5,
totals.tx_volume.msats as f64 / 100_000_000_000f64,
)}

</div>
}
}
None => {
view! {
<div class="text-4xl h-4 bg-gray-200 rounded-full dark:bg-gray-700 w-48 my-4 animate-pulse"></div>
}
}
}
}}
<div class="text-gray-500">Total Volume</div>
</div>
</div>
}
}

async fn fetch_federation_totals() -> anyhow::Result<FedimintTotals> {
let url = format!("{}/federations/totals", crate::BASE_URL);
let res = reqwest::get(&url).await?;
Ok(res.json().await?)
}
15 changes: 10 additions & 5 deletions fmo_frontend/src/components/tabs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,17 @@ pub fn Tabs(#[prop(into)] default: String, children: Children) -> impl IntoView
<li class="me-2">
<a
href="#"
class=move || { if tab_name_a == active_tab.get() { ACTIVE_CLASSES } else { INACTIVE_CLASSES } }
class=move || {
if tab_name_a == active_tab.get() {
ACTIVE_CLASSES
} else {
INACTIVE_CLASSES
}
}

on:click=move |_| set_active_tab.set(tab_name_c.clone())
>
{ tab_name }
{tab_name}
</a>
</li>
}
Expand All @@ -62,9 +69,7 @@ pub fn Tabs(#[prop(into)] default: String, children: Children) -> impl IntoView

view! {
<div class="text-sm font-medium text-center text-gray-500 border-b border-gray-200 dark:text-gray-400 dark:border-gray-700">
<ul class="flex flex-wrap -mb-px">
{ tabs }
</ul>
<ul class="flex flex-wrap -mb-px">{tabs}</ul>
</div>
{move || (get_tab_content(active_tab.get()))()}
}
Expand Down
9 changes: 8 additions & 1 deletion fmo_server/src/federation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use fedimint_core::api::InviteCode;
use fedimint_core::config::{ClientConfig, FederationId, JsonClientConfig};
use fedimint_core::core::ModuleInstanceId;
use fedimint_core::module::registry::ModuleDecoderRegistry;
use fmo_api_types::FederationSummary;
use fmo_api_types::{FederationSummary, FedimintTotals};
use serde_json::json;

use crate::federation::meta::get_federation_meta;
Expand All @@ -28,6 +28,7 @@ pub fn get_federations_routes() -> Router<AppState> {
Router::new()
.route("/", get(list_observed_federations))
.route("/", put(add_observed_federation))
.route("/totals", get(get_federation_totals))
.route("/:federation_id", get(get_federation_overview))
.route(
"/:federation_id/config",
Expand Down Expand Up @@ -128,6 +129,12 @@ async fn get_federation_utxos(
Ok(utxos.into())
}

async fn get_federation_totals(
State(state): State<AppState>,
) -> crate::error::Result<Json<FedimintTotals>> {
Ok(state.federation_observer.totals().await?.into())
}

fn decoders_from_config(config: &ClientConfig) -> ModuleDecoderRegistry {
get_decoders(
config
Expand Down
Loading

0 comments on commit 597577a

Please sign in to comment.