MDK Logo

Financial reporting

Financial reporting components — cost, EBITDA, energy balance, hash balance, and subsidy fee views

@tetherto/mdk-react-devkit/foundation

Financial reporting components cover cost summary, EBITDA, energy balance, hash balance, and subsidy fee views. Each is a self-contained reporting page composite — supply your API data and a <TimeframeControls> period selector; the component handles layout, charts, and metric tiles.

For operational reporting see Operational. For shared controls see the Reporting tool overview.

Prerequisites

Components

ComponentDescription
CostCost summary reporting page with chart and metric tiles
EbitdaEBITDA reporting view with charts and metrics
EnergyBalanceEnergy balance reporting view with cost and revenue tabs
HashBalanceHash balance reporting view
SubsidyFeeSubsidy fee bar chart with single-stat metric

Cost

Single-site cost summary composite: page header, period selector slot, and a 2×2 grid of cost charts and metric tiles.

Import

import { Cost, buildCostSummaryViewModel } from '@tetherto/mdk-react-devkit/foundation'
import type { CostProps, CostSummaryResponse, FinancialDateRange } from '@tetherto/mdk-react-devkit/foundation'

Props

PropStatusTypeDefaultDescription
metricsRequiredCostSummaryDisplayMetrics | nullnoneDerived metric tiles from buildCostSummaryViewModel
costLogRequiredReadonlyArray<CostTimeSeriesEntry>noneMonthly cost time-series for the bar charts
btcPriceLogRequiredReadonlyArray<BtcPriceTimeSeriesEntry>noneBTC price overlay series
totalsRequiredCostSummaryMonetaryTotals | nullnoneAggregate totals for the summary tile
dateRangeRequiredFinancialDateRange | nullnoneActive period — drives chart x-axis
avgAllInCostDataOptionalReadonlyArray<AvgAllInCostDataPoint>noneRevenue vs cost series for the Avg All-in Cost panel
controlsRequiredReactElementnonePeriod selector slot — pass <TimeframeControls>
setCostActionOptionalReactElementnoneOptional header action slot (e.g. a "Set Monthly Cost" link)
isLoadingOptionalbooleanfalseShow a loading state
errorOptionalunknownnoneShow an error state when defined

Data preparation

Use buildCostSummaryViewModel to transform a raw API response into the props the component expects:

import { buildCostSummaryViewModel } from '@tetherto/mdk-react-devkit/foundation'
import type { CostSummaryResponse } from '@tetherto/mdk-react-devkit/foundation'

const viewModel = buildCostSummaryViewModel({ data: apiResponse })
// → { metrics, costLog, btcPriceLog, totals }

FinancialDateRange type

type FinancialDateRange = {
  start: number   // epoch ms
  end: number     // epoch ms
  period?: 'daily' | 'weekly' | 'monthly' | 'yearly'
}

Basic usage

import { useState, useMemo } from 'react'
import {
  Cost,
  buildCostSummaryViewModel,
  TimeframeControls,
  PERIOD,
} from '@tetherto/mdk-react-devkit/foundation'
import type { FinancialDateRange } from '@tetherto/mdk-react-devkit/foundation'
import { startOfMonth, endOfMonth } from 'date-fns'

export const CostPage = ({ apiResponse }: { apiResponse: CostSummaryResponse }) => {
  const [dateRange, setDateRange] = useState<FinancialDateRange>({
    start: startOfMonth(new Date()).getTime(),
    end: endOfMonth(new Date()).getTime(),
    period: PERIOD.MONTHLY,
  })

  const viewModel = useMemo(
    () => buildCostSummaryViewModel({ data: apiResponse }),
    [apiResponse],
  )

  return (
    <Cost
      {...viewModel}
      dateRange={dateRange}
      controls={
        <TimeframeControls
          isMonthSelectVisible
          isWeekSelectVisible={false}
          dateRange={{ start: dateRange.start, end: dateRange.end }}
          onRangeChange={(range, opts) =>
            setDateRange({ start: range[0].getTime(), end: range[1].getTime(), period: opts.period })
          }
        />
      }
    />
  )
}

Behaviour

Renders a "Cost Summary" page header, the controls slot (period selector), and the CostContent 2×2 grid of charts and metric tiles (production cost chart, operations energy chart, avg all-in cost chart, and cost metric cards).

Styling

  • .mdk-cost: Root element
  • .mdk-cost__header: Title and optional action row
  • .mdk-cost__page-title: "Cost Summary" heading
  • .mdk-cost__controls: Controls slot wrapper

Ebitda

Renders EBITDA charts and metric tiles from a display-metrics view model. Accepts bar chart data for revenue, cost, and EBITDA series.

Import

import { Ebitda } from '@tetherto/mdk-react-devkit/foundation'
import type { EbitdaProps } from '@tetherto/mdk-react-devkit/foundation'

Props

PropStatusTypeDefaultDescription
metricsRequiredCostSummaryDisplayMetrics | nullnoneDerived metric tiles from buildCostSummaryViewModel
ebitdaChartInputRequiredToBarChartDataInput | nullnoneEBITDA bar chart series
btcProducedChartInputRequiredToBarChartDataInput | nullnoneBTC produced bar chart series
hasBtcProducedAllZerosRequiredbooleannoneHides the BTC produced series when all values are zero
showEbitdaBarChartRequiredbooleannoneToggle the EBITDA bar chart panel
currentBTCPriceRequirednumbernoneLive BTC price used in metric calculations
datePickerRequiredReactElementnonePeriod selector slot
hasDateSelectionRequiredbooleannoneWhen false shows a "select a period" hint instead of empty data
isLoadingOptionalbooleanfalseShow a loading state
errorsOptionalstring[][]Error messages displayed as an alert
setCostHrefOptionalstringnoneWhen provided, shows a "Set Monthly Cost" gear link in the header

Basic usage

import { Ebitda } from '@tetherto/mdk-react-devkit/foundation'

<Ebitda
  metrics={ebitdaMetrics}
  ebitdaChartInput={ebitdaChartData}
  btcProducedChartInput={btcProducedData}
  hasBtcProducedAllZeros={false}
  showEbitdaBarChart={true}
  currentBTCPrice={65000}
  datePicker={<TimeframeControls {...timeframeProps} />}
  hasDateSelection={true}
/>

Behaviour

Renders an "EBITDA" page header, period selector slot, and (when hasDateSelection is true and data is available) the EbitdaMetrics card grid and EbitdaCharts panels. Shows a "please select a period" hint when hasDateSelection is false. A full-page spinner appears while isLoading is true.

Styling

  • .mdk-ebitda: Root element
  • .mdk-ebitda__header: Title and optional action row
  • .mdk-ebitda__page-title: "EBITDA" heading
  • .mdk-ebitda__header-actions: "Set Monthly Cost" link wrapper
  • .mdk-ebitda__controls: Date picker slot
  • .mdk-ebitda__loading: Spinner overlay
  • .mdk-ebitda__error: Error / empty state container

EnergyBalance

Tabbed reporting surface showing energy cost and energy revenue balance with bar charts and period-level metric tiles.

Import

import { EnergyBalance } from '@tetherto/mdk-react-devkit/foundation'
import type { EnergyBalanceProps, EnergyBalanceViewModel } from '@tetherto/mdk-react-devkit/foundation'

Props

PropStatusTypeDefaultDescription
viewModelRequiredEnergyBalanceViewModelnoneFull view-model from useEnergyBalance hook
onTabChangeRequiredfunctionnoneCalled when the Revenue/Cost tab changes
onRevenueDisplayModeChangeRequiredfunctionnoneCalled when the revenue chart display mode toggles
onCostDisplayModeChangeRequiredfunctionnoneCalled when the cost chart display mode toggles
timeframeControlsOptionalReactNodenonePeriod selector slot rendered above the tabs
setCostHrefOptionalstringnoneWhen provided, shows a "Set Monthly Cost" gear link in the header
isDemoModeOptionalbooleanfalseSuppresses error alerts (for demo/storybook use)

EnergyBalanceTab type

type EnergyBalanceTab = 'revenue' | 'cost'

Basic usage

import { EnergyBalance, TimeframeControls } from '@tetherto/mdk-react-devkit/foundation'
import { useEnergyBalance } from '@tetherto/mdk-react-devkit/foundation'

export const EnergyBalancePage = () => {
  const viewModel = useEnergyBalance({ data: apiData, dateRange })

  return (
    <EnergyBalance
      viewModel={viewModel}
      onTabChange={(tab) => setActiveTab(tab)}
      onRevenueDisplayModeChange={(mode) => setRevenueMode(mode)}
      onCostDisplayModeChange={(mode) => setCostMode(mode)}
      timeframeControls={<TimeframeControls {...timeframeProps} />}
    />
  )
}

Behaviour

Renders an "Energy Balance" page header, optional timeframe controls, and a two-tab layout:

  • Energy Revenue — revenue metrics cards, revenue bar chart, downtime chart, power chart
  • Energy Cost — cost metrics cards, cost bar chart, power chart

Empty and no-selection states are handled internally. A loading spinner overlays the view while data fetches.

Styling

  • .mdk-energy-balance: Root element
  • .mdk-energy-balance__header: Title and optional action row
  • .mdk-energy-balance__controls: Timeframe controls slot
  • .mdk-energy-balance__tabs: Tab container
  • .mdk-energy-balance__tabs-list: Tab switcher row
  • .mdk-energy-balance__tabs-trigger: Individual tab button
  • .mdk-energy-balance__tabs-content: Tab panel content area
  • .mdk-energy-balance__loading: Spinner overlay
  • .mdk-energy-balance__error: Error / empty state

HashBalance

Reporting composite for hash-rate balance across the pool, showing contribution, theoretical, and accepted hash metrics.

Import

import { HashBalance } from '@tetherto/mdk-react-devkit/foundation'
import type { HashBalanceProps, HashRevenueResponse } from '@tetherto/mdk-react-devkit/foundation'

Props

All props are optional (the component renders an empty/loading state when omitted).

PropStatusTypeDefaultDescription
dataOptionalHashRevenueResponse | nullHash revenue API response
isLoadingOptionalbooleanfalseShow a loading state
isErrorOptionalbooleanfalseShow an error alert
errorMessageOptionalstring'Error loading hash balance data…'Override error message text
initialDateRangeOptionalFinancialDateRangecurrent monthStarting period for the integrated timeframe controls
onDateRangeChangeOptionalfunctionFires when the user changes the period
classNameOptionalstringRoot class override
tabsClassNameOptionalstringClass on the tab container
tabsListClassNameOptionalstringClass on the tab list

Basic usage

import { HashBalance } from '@tetherto/mdk-react-devkit/foundation'

<HashBalance
  data={hashRevenueData}
  isLoading={isLoading}
  onDateRangeChange={(range, query) => fetchHashBalance(query)}
/>

Behaviour

Renders integrated TimeframeControls (year/month/week) with a reset button, and a two-tab layout:

  • Hash Revenue — site hash revenue, network hashrate, and network hashprice charts with metric cards; includes a currency toggle (USD / BTC)
  • Hash Cost — hash cost vs revenue comparison charts

Date range is managed internally unless initialDateRange is supplied; onDateRangeChange fires on every period change.

Styling

  • .mdk-hash-balance: Root element
  • .mdk-hash-balance__loading: Spinner overlay
  • .mdk-hash-balance__error: Error alert
  • .mdk-hash-balance__tabs: Tab container
  • .mdk-hash-balance__tabs-list: Tab row
  • .mdk-hash-balance__tabs-trigger: Individual tab button
  • .mdk-hash-balance__tabs-content: Tab panel

SubsidyFee

Bar chart of subsidy fees over a configurable time range alongside an average-fee metric tile.

Import

import { SubsidyFee } from '@tetherto/mdk-react-devkit/foundation'
import type { SubsidyFeesResponse } from '@tetherto/mdk-react-devkit/foundation'

Props

All props are optional.

PropStatusTypeDefaultDescription
dataOptionalHashRevenueResponse | nullHash revenue API response
logOptionalSubsidyFeesLogEntry[]Per-block log entries (alternative to data)
isLoadingOptionalbooleanfalseShow a loading state
isErrorOptionalbooleanfalseShow an error alert
errorMessageOptionalstring'Error loading hash balance data…'Override error message text
showSummaryCardsOptionalbooleanfalseShow Total Subsidy, Total Fees, and Average Fees stat cards above the charts
onDateRangeChangeOptionalfunctionFires when the user changes the period

Basic usage

import { SubsidyFee } from '@tetherto/mdk-react-devkit/foundation'

<SubsidyFee
  data={subsidyFeesData}
  isLoading={isLoading}
  showSummaryCards={true}
  onDateRangeChange={(range, query) => fetchSubsidyFees(query)}
/>

Behaviour

Renders integrated TimeframeControls (year/month/week) and two chart panels side by side:

  • Subsidy/Fees — stacked bar chart of mining reward breakdown (subsidy vs transaction fees) with a % fee ratio overlay on the right y-axis
  • Average Fees — average fees in sats/vbyte bar chart

When showSummaryCards is true, three SingleStatCard tiles (Total Subsidy, Total Fees, Average Fees) appear above the charts.

Styling

  • .mdk-subsidy-fee__panel: Each chart panel wrapper
  • .mdk-subsidy-fee__panel--primary: Primary variant

On this page