Plotting: Simple
Learn how to easily plot dashboard data directly from your Mercury program.
Last updated
Learn how to easily plot dashboard data directly from your Mercury program.
Last updated
Simple plotting follows a higher-level SDK helper, DashboardBuilder
, that in the background relies on the same components as Complex Plotting.
The DashboardBuilder
object has three main functions:
pub fn add_table(mut self, title: &str, columns: Vec<String>, rows: Vec<Vec<String>>) -> Self
: returns a dashboard object with a new table.
pub fn add_bar_chart( mut self, title: &str, hover_title: &str, categories: Vec<&str>, data: Vec<i64>) -> Self
: returns a dashboard with a new bar chart. Note that categories
is generally the explicative x-axis and data the numeric y-axis.
pub fn add_line_chart( mut self, title: &str, categories: Vec<String>, series: Vec<(&str, Vec<i64>)>, ) -> Self
: returns a dashboard with a new line chart. Note that categories
is generally the x-axis. There can be more than one series specified, and each series ( (&str, Vec<i64>
) has a name (the first tuple element) and a set of data (the i64 vector).
Below is how it is implemented for the Creating the Dashboard tutorial:
pub fn build_dashboard<'a>(
env: &EnvClient,
aggregated_data: HashMap<&'a str, HashMap<&'a str, AggregatedData>>,
collaterals: &Vec<Collateral>,
borroweds: &Vec<Borrowed>,
) -> Dashboard {
let mut dashboard = DashboardBuilder::new(
"Blend Porotocol Dashboard",
"Explore the Blend protocol's mainnet activity",
);
for (pool, assets) in aggregated_data {
let positions_count = get_from_ledger(env, &pool);
dashboard = dashboard.add_table(
"Current Unique Users With Positions",
vec!["count".into()],
vec![vec![positions_count.to_string()]],
);
let val = get_from_instance(env, pool, "Name");
let ScVal::String(string) = val else { panic!() };
let pool = string.to_utf8_string().unwrap();
for (asset, data) in assets {
let meta: StellarAssetContractMetadata =
env.from_scval(&get_from_instance(env, asset, "METADATA"));
let denom = soroban_string_to_string(env, meta.symbol);
let asset = denom.clone();
{
let categories: Vec<&str> = vec!["Supply", "Collateral", "Borrowed"];
dashboard = dashboard.add_bar_chart(
"All-time distribution",
&format!("Pool: {}, Asset {}", pool, asset),
categories,
vec![
data.total_supply as i64 / STROOP as i64,
data.total_collateral as i64 / STROOP as i64,
data.total_borrowed as i64 / STROOP as i64,
],
);
};
{
let line_data: Vec<i64> = data
.collateral
.iter()
.map(|(_, value)| *value as i64 / STROOP as i64)
.collect();
let all_ledgers: Vec<String> = data
.collateral
.iter()
.map(|(ledger, _)| ledger.to_string())
.collect();
dashboard = dashboard.add_line_chart(
"Collateral supply evolution",
all_ledgers,
vec![(
&format!("Collateral of pool {} and asset {}", pool, asset),
line_data,
)],
);
};
{
let line_data: Vec<i64> = data
.borrowed
.iter()
.map(|(_, value)| *value as i64 / STROOP as i64)
.collect();
let all_ledgers: Vec<String> = data
.borrowed
.iter()
.map(|(ledger, _)| ledger.to_string())
.collect();
dashboard = dashboard.add_line_chart(
"Borrow supply evolution",
all_ledgers,
vec![(
&format!("Borrowed of pool {} and asset {}", pool, asset),
line_data,
)],
);
};
{
dashboard = dashboard.add_table(
&format!("{} pool {} volume", pool, asset),
vec!["Timeframe".into(), "Volume".into()],
vec![
vec![
"24hrs".into(),
format!("{} {}", data.volume_24hrs as u64 / STROOP as u64, denom),
],
vec![
"week".into(),
format!("{} {}", data.volume_week as u64 / STROOP as u64, denom),
],
vec![
"month".into(),
format!("{} {}", data.volume_month as u64 / STROOP as u64, denom),
],
],
)
};
}
}
{
let mut rows = Vec::new();
for entry in borroweds.iter().rev() {
let (kind, amount) = if entry.delta > 0 {
("borrow".into(), ((entry.delta as u128) as i64).to_string())
} else {
("repay".into(), ((entry.delta as u128) as i64).to_string())
};
rows.push(vec![
kind,
entry.timestamp.to_string(),
entry.ledger.to_string(),
entry.pool.to_string(),
entry.asset.to_string(),
entry.source.to_string(),
amount,
]);
}
dashboard = dashboard.add_table(
"Borrow Actions",
vec![
"type".into(),
"timestamp".into(),
"ledger".into(),
"pool".into(),
"asset".into(),
"source".into(),
"amount".into(),
],
rows,
);
};
{
let mut rows = Vec::new();
for entry in collaterals.iter().rev() {
let (kind, amount) = if entry.delta > 0 {
("supply".into(), ((entry.delta as u128) as i64).to_string())
} else {
(
"withdraw".into(),
((entry.delta as u128) as i64).to_string(),
)
};
rows.push(vec![
kind,
entry.timestamp.to_string(),
entry.ledger.to_string(),
entry.pool.to_string(),
entry.asset.to_string(),
entry.source.to_string(),
amount,
]);
}
dashboard = dashboard.add_table(
"Last Collateral Actions",
vec![
"type".into(),
"timestamp".into(),
"ledger".into(),
"pool".into(),
"asset".into(),
"source".into(),
"amount".into(),
],
rows,
);
};
env.log().debug("collateral table built", None);
dashboard.build()
}