Mercury Documentation
  • Get Started with Mercury
    • Pricing
    • Endpoints
    • Authentication
  • The Definitive Guide to Understanding The Mercury's Stack Vision
  • Retroshades
    • Introduction to Retroshades
    • Get Started
      • Importing the Retroshades SDK
      • Writing and Emitting Retroshades
      • Deploying to Mercury Retroshades
      • Querying Retroshades
  • Zephyr: Full Customization
    • Introduction
    • General concepts
      • Zephyr Programs
      • Accessing the Ledger: Contract Entries
      • Accessing the Ledger Meta: Contract Events
      • Database Interactions
      • Create Custom Callable APIs: Serverless Functions
      • Catchups
      • Using Soroban inside Zephyr
    • Quickstart
    • Learn
      • Introduction to Mercury's Cloud and the Zephyr Stack
      • Get Started: Set Up and Manage the Project
        • Create the Project
        • Writing the Program
        • Local Testing
        • Deploy
        • Data catchups/backfill
        • Monitor Execution
        • Querying
      • Database Interactions
        • Zephyr.toml: Define the Tables Structure
        • Understanding Database Interactions
        • Database Operations
      • Accessing the Ledger
      • Accessing Ledger Transition: Soroban Events
      • Working with Contract Custom Types
      • Working with Soroban SDK Types
      • Web Requests, Automation and Alerts.
      • Zephyr.toml Extensions: Indexes And Dashboard
      • Reading From Indexes/External Tables
      • Custom APIs - Serverless Functions
        • General Concepts
        • Custom RPC-alike Endpoints
        • Querying APIs for Composable Data
      • Data Catchups/Backfill
      • Custom Dashboards
        • Creating the Dashboard
        • Plotting: Simple
        • Complex Plotting
    • Support
  • Mercury "Classic"
    • Subscriptions
      • API Definition
    • Queries
      • Contract Events
      • Contract Data Entry Updates
      • Stellar Operations, Balances, and Account Objects
  • TUTORIALS
    • Zephyr
      • Self-hosting Zephyr
      • No-RPC Dapp
      • Indexing a DeFi liquidity pool (Blend)
      • Building a Secure DeFi Real-Time Bot Through Smart Accounts
      • Monitoring Large Deposits with Zephyr and Sending Web Alerts
    • Mercury Classic
      • Index and query contract events
Powered by GitBook
On this page
  1. Zephyr: Full Customization
  2. Learn
  3. Custom Dashboards

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 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()
}
PreviousCreating the DashboardNextComplex Plotting

Last updated 8 months ago