# Creating the Dashboard

{% hint style="warning" %}
Just like for serialization in custom APIs, do not rely on `i128` for your dashboard due to a bug we're still investigating related to out client-side integration with the soroban-sdk.
{% endhint %}

Now that we've understood what Mercury dashboards are and how they can be used, let's finally learn how to create them with some practical examples.

## Workflow

The process of creating a dashboard involves three phases:

1. **Retrieving Data:** You can retrieve data by creating your ingestion logic and storing it in tables or by accessing other publicly indexed tables. This process is already familiar to us.

2. **Aggregating Data:** Create data structures with the data pre-aggregated for easy reading and iteration, facilitating the charting functions we will use later.

3. **Plotting Data:** Using the aggregated data, you can now create dashboards and plot it in tables or charts. With our integration with `Charming` and ApacheCharts, you can generate a wide variety of beautiful and customizable interactive charts.

To build your dashboards and examine the various steps in detail, we recommend using the [Blend Dashboard example](https://github.com/xycloo/zephyr-examples/tree/master/zephyr-blend-mainnet-dashboard) as a reference and following along with it. In this chapter, we will provide the fundamentals for creating your own dashboards by covering the most crucial steps and showcasing examples from that repository. If you feel something is missing, refer to the complete example or ask for help on our Discord server.

## 1. Retrieve the Data

We already covered this step in the previous chapters and explored the different ways in which this can be done. Reference the previous parts of this documentation to perform this.

## 2. Aggregate Data

Also in this case you have various approaches you could follow, depending on the specific need. But as a general rule, we need an iterator where the different data is categorized, aggregated, and stored. Let’s look at an example:

```rust
pub fn aggregate_data<'a>(
    timestamp: i64,
    supplies: &'a Vec<Supply>,
    collaterals: &'a Vec<Collateral>,
    borroweds: &'a Vec<Borrowed>,
) -> HashMap<&'a str, HashMap<&'a str, AggregatedData>> {
    let env = EnvClient::empty();
    let mut aggregated_data: HashMap<&'a str, HashMap<&'a str, AggregatedData>> = HashMap::new();

    env.log().debug("hashmaps", None);

    for supply in supplies {
        let pool = &supply.pool;  // Convert pool to string for hashmap key
        let asset = &supply.asset;  // Convert asset to string for hashmap key
        let supply_value = supply.supply;
        let ledger =supply.ledger;

        aggregated_data
        .entry(&pool)
            .or_insert_with(HashMap::new)
            .entry(&asset)
            .or_insert_with(AggregatedData::new)
            .add_supply(ledger, supply_value);
    }
    aggregated_data
}

```

What we see here is a `aggregate_data` function where we perform the aggregation logic. Here we are analyzing just the aggregation of token supply data for each Blend liquidity pool taken into account. The function takes as argument a `Vec<Supply>` (a vector of objects of custom type `Supply`), that is the data we have previously indexed and we now have to aggregate.&#x20;

First of all, we have the Declaration of `aggregated_data`:

```rust
let mut aggregated_data: HashMap<&'a str, HashMap<&'a str, AggregatedData>> = HashMap::new();
```

* This line declares a mutable `HashMap` named `aggregated_data`.
* The outer `HashMap` uses a string slice (`&'a str`) as the key and another `HashMap` as the value.
* The inner `HashMap` also uses a string slice (`&'a str`) as the key and a custom struct or type `AggregatedData` as the value.

Then, for each element in `Vec<Supply>`, we update `aggregated_data`:

```rust
aggregated_data
    .entry(&pool)
    .or_insert_with(HashMap::new)
    .entry(&asset)
    .or_insert_with(AggregatedData::new)
    .add_supply(ledger, supply_value);
}
```

Let's break it down further:

`aggregated_data.entry(&pool):`

* This accesses the entry in the outer `HashMap` corresponding to the key `&pool`.
* If the key `&pool` does not exist, `or_insert_with(HashMap::new)` inserts a new `HashMap` as the value for this key.

`.entry(&asset)`:

* This accesses the entry in the inner `HashMap` (which is the value of the key `&pool`) corresponding to the key `&asset`.
* If the key `&asset` does not exist in this inner `HashMap`, `or_insert_with(AggregatedData::new)` inserts a new `AggregatedData` instance as the value for this key.

`.add_supply(ledger, supply_value)`:

* This calls the `add_supply` method on the `AggregatedData` instance that is the value of the key `&asset` in the inner `HashMap`.
* The `add_supply` method simply sets the new `total_supply`.

Finally, we return the `aggregated_data` HashMap. To summarize we have created an iterator (a map) that for each pool and each asset in that pool, points to an `AggregateData` object that stores all the needed aggregated data. The object's structure looks like this:

```rust
#[derive(Default, Serialize)]
pub struct AggregatedData {
    pub borrowed: Vec<(u32, i128)>,
    pub supplied: Vec<(u32, i128)>,
    pub collateral: Vec<(u32, i128)>,
    pub total_borrowed: i128,
    pub total_supply: i128,
    pub total_collateral: i128,
    pub volume_24hrs: i128,
    pub volume_week: i128,
    pub volume_month: i128,
}
```

Of course, now we're only working with `total_supply`, but this can give you an idea of all the different metrics, associated in this case with a pool, that you can aggregate data for.&#x20;

## 3. Plot Data

The zephyr-sdk offers various helpers in the SDK to construct dashboard objects that can be plotted with the widely-utilized Apache Echarts frontend library. The SDK contains two helpers:

* (Recommended) Higher-level API wrapper: the simplest and most straightforward way to plot the data. Learn more at [plotting-simple](https://docs.mercurydata.app/zephyr-full-customization/learn/custom-dashboards/plotting-simple "mention").
* Lower-level API: full chart customization, learn more at [complex-plotting](https://docs.mercurydata.app/zephyr-full-customization/learn/custom-dashboards/complex-plotting "mention").

### Last Step: Creating the Dashboard Function

Now that we have everything ready, we can put it all together. The final step is to create the `dashboard` function, a serverless function that is called on demand and returns the dashboard with up-to-date data.

```rust
#[no_mangle]
pub extern "C" fn dashboard() {
    let env = EnvClient::empty();
    env.log().debug("Starting program", None);
    let dasboard = {
        let supplies = env.read();
        let collaterals = env.read();
        let borroweds = env.read();
        env.log().debug("Aggregating data", None);
        let timestamp = env.soroban().ledger().timestamp();
        env.log().debug(format!("Timestamp is {}", timestamp), None);
        let aggregated = aggregate_data(timestamp as i64, &supplies, &collaterals, &borroweds);
        env.log().debug("Data aggregated", None);
        let dashboard = build_dashboard(&env, aggregated, &collaterals, &borroweds);

        env.log().debug("chart built", None);
        dashboard
    };

    env.conclude(&dasboard)
}
```

The function does the following:

1. Retrieve the supply, collateral, and borrow data indexed in our tables by calling the `read()` method.
2. Aggregate this data using the `aggregate_data()` function we defined earlier.
3. Build the dashboard using our `build_dashboard()` method.

***

Now we have the basic knowledge to build any type of dashboard. For a complete understanding of a more complex workflow and to see how to plot different types of interactive charts, you can refer to the [full example](https://github.com/xycloo/zephyr-examples/tree/master/zephyr-blend-mainnet-dashboard).


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.mercurydata.app/zephyr-full-customization/learn/custom-dashboards/creating-the-dashboard.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
