Plotting: Simple
Learn how to easily plot dashboard data directly from your Mercury program.
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 thatcategories
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 thatcategories
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()
}
Last updated