This page will help you understand and create a dashboard using the Top Ledger platform.
Introduction
Top Ledger allows Web3 growth, analytics & data science teams to create powerful dashboards from decoded solana program data. Our backend infra pulls in raw data from the Solana blockchain and labels and indexes them into different tables on a daily basis.
Letβs take an example of a project called βProject Xβ. Project X is a major DeFi project on Solana. We will calculate some important metrics for Project X and add them in a dashboard.
Sample SQL Query
with cte_markets as (select pd.input_accounts.market, pd.input_accounts.baseMint, pd.input_accounts.quoteMint, ts_base.symbol as baseMintName, ts_quote.symbol as quoteMintName, ts_quote.decimalsfrom phoenix.parsed pdjoin token.solana_token ts_base on ts_base.address = pd.input_accounts.baseMintjoin token.solana_token ts_quote on ts_quote.address = pd.input_accounts.quoteMintwhere1=1and pd.instruction_type ='InitializeMarket'), market_names as (select market, baseMint, quoteMint, baseMintName ||' - '|| quoteMintName as marketName, decimalsfrom cte_markets)select block_date,market, marketName, trades, sum(trades) over(order by block_date) as cum_tradesfrom(select block_date,market, marketName, count(distinct tx_id) as tradesfrom phoenix.parsed as p cross join unnest(args.market_events) as market_eventjoin market_names as m on p.args.header_fields.header.market = m.marketwhere1=1and instruction_type ='Log'and market_event ='FillSummary'and p.args.header_fields.header.market ='{{market}}'and market_event.fields.total_quote_lots_filled >0group by1,2,3) as a order by1desc
Trades
We will start with calculating number of trades on daily basis and weekly top five pools based on number of trades. The below widget shows total number of trades for project X.
Also, in the widgets shown below you can see Trade trends along with weekly top 5 pools based on the number of trades for the project. On 11th Dec there was a spike in user activity which could be related to some internal or external event.
It also shows all time top 5 pools based on number of trades.
Let's go through the SQL code for the widgets you have seen so far. The query runs on parsed table available in the TL platform.
with cte_markets as (select pd.input_accounts.market, pd.input_accounts.baseMint, pd.input_accounts.quoteMint, ts_base.symbol as baseMintName, ts_quote.symbol as quoteMintName, ts_quote.decimalsfrom phoenix.parsed pdjoin token.solana_token ts_base on ts_base.address = pd.input_accounts.baseMintjoin token.solana_token ts_quote on ts_quote.address = pd.input_accounts.quoteMintwhere1=1and pd.instruction_type ='InitializeMarket'),market_names as (select market, baseMint, quoteMint, baseMintName ||' - '|| quoteMintName as marketName, decimalsfrom cte_markets), daily_volumes_fees as (select p.partition_0, p.args.header_fields.header.market as market, mn.marketName, mn.baseMint, mn.quoteMint,count(tx_id) as trades,sum(market_event.fields.total_quote_lots_filled / pow(10, mn.decimals)) as daily_volume,sum(market_event.fields.total_fee_in_quote_lots / pow(10, mn.decimals)) as daily_feesfrom phoenix.parsed as pcross join unnest(p.args.market_events) as market_eventjoin market_names mn on mn.market = p.args.header_fields.header.marketwhere1=1and instruction_type ='Log'and market_event ='FillSummary'and market_event.fields.total_quote_lots_filled >0group by1, 2, 3, 4,5), volume_fees_in_usd as (selectdate(dv.partition_0) as block_date, dv.market, dv.marketName, dv.baseMint, dv.quoteMint, trades, dv.daily_volume, dv.daily_fees,casewhen dv.quoteMint in ('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB') then dv.daily_volumeelse dv.daily_volume * tp.price_in_usdendas daily_volume_usd,casewhen dv.quoteMint in ('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB') then dv.daily_feeselse dv.daily_fees * tp.price_in_usdendas daily_fees_usdfrom daily_volumes_fees dvleft join tl_solana.prices tp on dv.quoteMint = tp.mint anddate(dv.partition_0) = tp.block_date), weekly as(select date_trunc('week', block_date) asweek, market, marketName,sum(trades) as weekly_trades,sum(daily_volume_usd) as weekly_volume_usd,sum(daily_fees_usd) as weekly_fees_usdfrom volume_fees_in_usdgroup by1,2,3),ranked_markets as(select*,rank() over (partitionbyweekorder by weekly_volume_usd desc) as rankfrom weekly)select*from ranked_marketswhere rank <=5order byweekdesc, weekly_volume_usd desc
We need to begin with extracting and organizing market details, such as base and quote tokens. Subsequently, it concatenates token names for clarity. Then we calculates daily trading volumes and fees for each market, considering the associated token decimals. These daily metrics are further converted into USD values based on token prices.
The data is then aggregated on a weekly basis, summing up trades, volume, and fees. Finally, the markets are ranked weekly by trading volume in USD, and the top 5 markets are selected and displayed. This comprehensive analysis offers a snapshot of the most active markets on Solana, providing valuable insights into their weekly performance.
Trade Volume and Fees
Now letβs calculate how much trade volume Project X is making. We will check the total volume and fees for trades per day and cumulative amount of the two metrics. We can infer a lot of insights by relating this metric with real world events and can measure the impact.
Here, we see 6 widgets. 1) Total trade volume, 2) daily trade volume, 3) cumulative trade volume, 4) total trade fees, 5) daily fees for trades, and 6)cumulative fees for trades. We can infer that the volume of fees is quite high and which in a way is a sign of good health and profits for the liquidity provider and for the projectX.
with cte_markets as (select pd.input_accounts.market, pd.input_accounts.baseMint, pd.input_accounts.quoteMint, ts_base.symbol as baseMintName, ts_quote.symbol as quoteMintName, ts_quote.decimalsfrom phoenix.parsed pdjoin token.solana_token ts_base on ts_base.address = pd.input_accounts.baseMintjoin token.solana_token ts_quote on ts_quote.address = pd.input_accounts.quoteMintwhere1=1and pd.instruction_type ='InitializeMarket'), market_names as (select market, baseMint, quoteMint, baseMintName ||' - '|| quoteMintName as marketName, decimalsfrom cte_markets), volumes_fees as (select p.partition_0, quoteMint,count(tx_id) as trades,sum(market_event.fields.total_quote_lots_filled / pow(10, mn.decimals)) as daily_volume,sum(market_event.fields.total_fee_in_quote_lots / pow(10, mn.decimals)) as daily_feesfrom phoenix.parsed pcross join unnest(p.args.market_events) as market_eventjoin market_names mn on mn.market = p.args.header_fields.header.marketwhere1=1and p.instruction_type ='Log'and market_event ='FillSummary'group by1,2), volume_fees_in_usd as (select dv.partition_0, trades, dv.daily_volume, dv.daily_fees,casewhen dv.quoteMint in ('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB') then dv.daily_volumeelse dv.daily_volume * tp.price_in_usdendas daily_volume_usd,casewhen dv.quoteMint in ('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB') then dv.daily_feeselse dv.daily_fees * tp.price_in_usdendas daily_fees_usdfrom volumes_fees dvleft join tl_solana.prices tp on dv.quoteMint = tp.mint anddate(dv.partition_0) = tp.block_date),daily_volume_fee_usd as(select partition_0, sum(trades) as trades,sum(daily_volume_usd) as daily_volume_usd,sum(daily_fees_usd) as daily_fees_usdfrom volume_fees_in_usdgroup by1)select partition_0, trades, daily_volume_usd, daily_fees_usd,sum(trades) over(order by partition_0) as cumulative_trades,sum(daily_volume_usd) over (order by partition_0) as cumulative_volume_usd,sum(daily_fees_usd) over (order by partition_0) as cumulative_fees_usdfrom daily_volume_fee_usdorder by1desc
Here we starts with extracting and processing data related to ProjectX markets. It involves several common table expressions (CTEs) to organize and aggregate the information. Then it calculates daily trade metrics, including the number of trades, daily volume, and daily fees in both native and USD denominations. Additionally, it computes cumulative metrics over time, providing a comprehensive view of market activity. The query draws data from various tables, such as phoenix.parsed, token.solana_token, and tl_solana.prices, utilizing their relationships to derive meaningful insights into ProjectX market performance.
Market Analysis
As many platforms wants, here we will add some key metrics regarding specific markets provided to users by the ProjectX. This includes total number of trades, trade volume, fees generated, users participated in a particular market. As shown in the widget below are the numbers for SOL-USDC market on the ProjectX.
Letβs go through the SQL code for how to calculate volume for Project X.
WITH buys AS (SELECT id, block_date, pre_balances, post_balances, account_keys[1] as unique_walletFROM"tl-solana-merged-data"."transactions"CROSS JOIN UNNEST(instructions) AS instructionWHERE1=1AND success = TrueAND block_date >= date_trunc('day', now() - interval '30'day) AND ( instruction.executing_account ='JUP4Fb2cqiRUcaTHdrPC8h2gNsA2ETXiPDD33WcGuJB'-- program address of project X--check in inner_instructionsOR contains(transform(CAST(instruction.inner_instructions ASarray<JSON>), x -> json_extract_scalar(x, '$.executing_account')), 'JUP4Fb2cqiRUcaTHdrPC8h2gNsA2ETXiPDD33WcGuJB') )GROUP BY1,2,3,4,5 ),buys_summed AS (SELECT id, block_date,SUM(IF( post_balances[i] - pre_balances[i] >0, post_balances[i] - pre_balances[i],0 )) *1.0/ 1e9 AS sol_approx, -- 1 SOL = 10^9 lamports unique_walletFROM buysCROSS JOIN UNNEST(pre_balances) WITH ORDINALITY AS b (pre_balance, i)GROUP BY id, block_date, unique_wallet ), vol_raw AS (SELECT date_trunc('day', block_date) ASday,SUM(sol_approx) AS sol,COUNT(distinct unique_wallet) as unique_walletsFROM buys_summed GROUP BY1 )SELECTday , sol , unique_wallets , SUM(sol) over (order bydayascrowsbetweenunboundedprecedingand current row) AS cumulative_solFROM vol_rawORDER BY1DESC
Once again we will start with extracting and processing data related to ProjectX markets. It begins by identifying markets and their associated details, including base and quote tokens. Subsequently, it calculates daily trading volumes and fees for each market, converting them into USD based on token prices. The final result provides a comprehensive summary of daily and cumulative trading metrics for the specified markets, offering insights into market activity over time.
Here is the link to the detailed dashboard for your reference -