From dc07bc1859b0cb25de89a3d3d172b1d5a414edc2 Mon Sep 17 00:00:00 2001 From: Michal Zielenkiewicz Date: Tue, 22 Aug 2023 10:44:59 +0200 Subject: [PATCH 1/2] Fix charts by syncing with the latest tx_volume stats API changes --- .changelog/812.bugfix.md | 1 + .../ParatimeDashboardPage/ActiveAccounts.tsx | 10 ++-- .../TotalTransactions.tsx | 4 +- .../TransactionsChartCard.tsx | 4 +- .../TransactionsStats.tsx | 4 +- src/app/utils/chart-utils.ts | 31 +++++++++---- src/oasis-nexus/generated/api.ts | 46 +++++++++---------- 7 files changed, 55 insertions(+), 45 deletions(-) create mode 100644 .changelog/812.bugfix.md diff --git a/.changelog/812.bugfix.md b/.changelog/812.bugfix.md new file mode 100644 index 000000000..52da039a2 --- /dev/null +++ b/.changelog/812.bugfix.md @@ -0,0 +1 @@ +Fix charts by syncing with the latest tx_volume stats API changes diff --git a/src/app/pages/ParatimeDashboardPage/ActiveAccounts.tsx b/src/app/pages/ParatimeDashboardPage/ActiveAccounts.tsx index 76d58a118..8b7b7db99 100644 --- a/src/app/pages/ParatimeDashboardPage/ActiveAccounts.tsx +++ b/src/app/pages/ParatimeDashboardPage/ActiveAccounts.tsx @@ -5,11 +5,7 @@ import startOfMonth from 'date-fns/startOfMonth' import { SnapshotCard } from '../../components/Snapshots/SnapshotCard' import { SnapshotCardDurationLabel } from '../../components/Snapshots/SnapshotCardDurationLabel' import { BarChart } from '../../components/charts/BarChart' -import { - useGetLayerStatsActiveAccounts, - GetLayerStatsActiveAccountsWindowStepSeconds, - type ActiveAccounts as Windows, -} from '../../../oasis-nexus/api' +import { useGetLayerStatsActiveAccounts, type ActiveAccounts as Windows } from '../../../oasis-nexus/api' import { ChartDuration, chartUseQueryStaleTimeMs, @@ -70,7 +66,7 @@ const getLabels = (t: TFunction): Record => ({ export const ActiveAccounts: FC = ({ scope, chartDuration }) => { const { t } = useTranslation() const labels = getLabels(t) - const { limit, bucket_size_seconds } = { + const { limit, window_step_seconds } = { ...durationToQueryParams[chartDuration], // By default we fetch data with additional buckets buffer, but it does not apply to active accounts. // Active accounts daily buckets are overlapping, so we cannot sum buckets like in other daily charts. @@ -84,7 +80,7 @@ export const ActiveAccounts: FC = ({ scope, chartDuration } scope.layer, { limit, - window_step_seconds: bucket_size_seconds as GetLayerStatsActiveAccountsWindowStepSeconds, + window_step_seconds, }, { query: { diff --git a/src/app/pages/ParatimeDashboardPage/TotalTransactions.tsx b/src/app/pages/ParatimeDashboardPage/TotalTransactions.tsx index 4240b401a..2da02578a 100644 --- a/src/app/pages/ParatimeDashboardPage/TotalTransactions.tsx +++ b/src/app/pages/ParatimeDashboardPage/TotalTransactions.tsx @@ -27,8 +27,8 @@ export const TotalTransactions: FC<{ scope: SearchScope }> = ({ scope }) => { }, }) - const buckets = dailyVolumeQuery.data?.data.buckets - ? cumulativeSum(dailyVolumeQuery.data?.data.buckets.slice().reverse(), 'tx_volume') + const buckets = dailyVolumeQuery.data?.data.windows + ? cumulativeSum(dailyVolumeQuery.data?.data.windows.slice().reverse(), 'tx_volume') : undefined return ( diff --git a/src/app/pages/ParatimeDashboardPage/TransactionsChartCard.tsx b/src/app/pages/ParatimeDashboardPage/TransactionsChartCard.tsx index 608972f15..e88812ca7 100644 --- a/src/app/pages/ParatimeDashboardPage/TransactionsChartCard.tsx +++ b/src/app/pages/ParatimeDashboardPage/TransactionsChartCard.tsx @@ -38,9 +38,9 @@ const TransactionsChartCardCmp: FC = ({ scope, chart }) const isDailyChart = isFetched && chartDuration === ChartDuration.TODAY - const buckets = data?.data?.buckets + const buckets = data?.data?.windows const lineChartData = isDailyChart - ? sumBucketsByStartDuration(buckets, 'tx_volume', 'bucket_start', startOfHour) + ? sumBucketsByStartDuration(buckets, 'tx_volume', 'window_end', startOfHour) : buckets const formatParams = isDailyChart ? { diff --git a/src/app/pages/ParatimeDashboardPage/TransactionsStats.tsx b/src/app/pages/ParatimeDashboardPage/TransactionsStats.tsx index f43a613fa..3e743f811 100644 --- a/src/app/pages/ParatimeDashboardPage/TransactionsStats.tsx +++ b/src/app/pages/ParatimeDashboardPage/TransactionsStats.tsx @@ -29,8 +29,8 @@ export const TransactionsStats: FC<{ scope: SearchScope }> = ({ scope }) => { }) const allTime = dailyVolumeQuery.isFetched && chartDuration === ChartDuration.ALL_TIME const buckets = allTime - ? getMonthlyBucketsDailyAverage(dailyVolumeQuery.data?.data.buckets) - : dailyVolumeQuery.data?.data.buckets + ? getMonthlyBucketsDailyAverage(dailyVolumeQuery.data?.data.windows) + : dailyVolumeQuery.data?.data.windows const formatParams = allTime ? { timestamp: { diff --git a/src/app/utils/chart-utils.ts b/src/app/utils/chart-utils.ts index bc48323b0..439ccbca7 100644 --- a/src/app/utils/chart-utils.ts +++ b/src/app/utils/chart-utils.ts @@ -4,6 +4,9 @@ import isSameMonth from 'date-fns/isSameMonth' import startOfMonth from 'date-fns/startOfMonth' import { GetLayerStatsTxVolumeParams, type TxVolume, type ActiveAccounts } from '../../oasis-nexus/api' +const fiveMinutesWindowSize = 60 * 5 +const oneDayWindowSize = 24 * 60 * 60 + export enum ChartDuration { TODAY = 'TODAY', WEEK = 'WEEK', @@ -13,26 +16,36 @@ export enum ChartDuration { export const dailyLimitWithoutBuffer = (60 / 5) * 24 // full day for 5 minutes buckets, +/* + window_size_seconds refers to the number of data points in one chunk + window_step_seconds is how much you move forward to create next chunk + both defaults to 86400 (1 day) if not specified + Example table of query params https://github.com/oasisprotocol/nexus/pull/477 +*/ export const durationToQueryParams = { [ChartDuration.TODAY]: { - bucket_size_seconds: 60 * 5, + window_size_seconds: fiveMinutesWindowSize, + window_step_seconds: fiveMinutesWindowSize, limit: dailyLimitWithoutBuffer + // daily data needs additional 2 buckets to make sure we have at least full 24 buckets (60 / 5) * 2, }, [ChartDuration.WEEK]: { - bucket_size_seconds: 24 * 60 * 60, + window_size_seconds: oneDayWindowSize, + window_step_seconds: oneDayWindowSize, limit: 7, offset: 1, // offset to skip the first day }, [ChartDuration.MONTH]: { - bucket_size_seconds: 24 * 60 * 60, + window_size_seconds: oneDayWindowSize, + window_step_seconds: oneDayWindowSize, limit: 30, // Defined as 30 days, should be more dynamic depending on the month offset: 1, // offset to skip the first day }, [ChartDuration.ALL_TIME]: { - bucket_size_seconds: 24 * 60 * 60, + window_size_seconds: oneDayWindowSize, + window_step_seconds: oneDayWindowSize, limit: 365, // Defined as a full year offset: 1, // offset to skip the first day }, @@ -45,20 +58,20 @@ export const chartDurationToDaysMap = { [ChartDuration.ALL_TIME]: 365, } -export const chartUseQueryStaleTimeMs = durationToQueryParams[ChartDuration.TODAY].bucket_size_seconds * 1000 +export const chartUseQueryStaleTimeMs = durationToQueryParams[ChartDuration.TODAY].window_size_seconds * 1000 type Buckets = TxVolume[] | undefined type MonthlyTxVolume = TxVolume & { numberOfItemsInGroup: number } -const groupBucketsByMonth = (buckets: Buckets) => { +const groupWindowsByMonth = (buckets: Buckets) => { return buckets?.reduce((acc: MonthlyTxVolume[], cur, index, arr) => { - if (index > 0 && isSameMonth(new Date(cur.bucket_start), new Date(arr[index - 1].bucket_start))) { + if (index > 0 && isSameMonth(new Date(cur.window_end), new Date(arr[index - 1].window_end))) { acc[acc.length - 1].tx_volume += cur.tx_volume acc[acc.length - 1].numberOfItemsInGroup += 1 return acc } acc.push({ - bucket_start: cur.bucket_start, + window_end: cur.window_end, tx_volume: cur.tx_volume, numberOfItemsInGroup: 1, }) @@ -67,7 +80,7 @@ const groupBucketsByMonth = (buckets: Buckets) => { } export const getMonthlyBucketsDailyAverage = (buckets: Buckets): Buckets => { - const monthlyBuckets = groupBucketsByMonth(buckets) + const monthlyBuckets = groupWindowsByMonth(buckets) return monthlyBuckets?.map(item => ({ ...item, diff --git a/src/oasis-nexus/generated/api.ts b/src/oasis-nexus/generated/api.ts index 194a90d80..68500f992 100644 --- a/src/oasis-nexus/generated/api.ts +++ b/src/oasis-nexus/generated/api.ts @@ -48,15 +48,6 @@ import GetRuntimeAccountsAddressMutator from '../replaceNetworkWithBaseURL'; import GetRuntimeStatusMutator from '../replaceNetworkWithBaseURL'; import GetLayerStatsTxVolumeMutator from '../replaceNetworkWithBaseURL'; import GetLayerStatsActiveAccountsMutator from '../replaceNetworkWithBaseURL'; -export type GetLayerStatsActiveAccountsWindowStepSeconds = typeof GetLayerStatsActiveAccountsWindowStepSeconds[keyof typeof GetLayerStatsActiveAccountsWindowStepSeconds]; - - -// eslint-disable-next-line @typescript-eslint/no-redeclare -export const GetLayerStatsActiveAccountsWindowStepSeconds = { - NUMBER_300: 300, - NUMBER_86400: 86400, -} as const; - export type GetLayerStatsActiveAccountsParams = { /** * The maximum numbers of items to return. @@ -74,7 +65,7 @@ The backend supports a limited number of step sizes: 300 (5 minutes) and 86400 (1 day). Requests with other values may be rejected. */ -window_step_seconds?: GetLayerStatsActiveAccountsWindowStepSeconds; +window_step_seconds?: number; }; export type GetLayerStatsTxVolumeParams = { @@ -89,12 +80,19 @@ limit?: number; */ offset?: number; /** - * The size of buckets into which the statistic is grouped, in seconds. -The backend supports a limited number of bucket sizes: 300 (5 minutes) and + * The size of windows into which the statistic is grouped, in seconds. +The backend supports a limited number of window sizes: 300 (5 minutes) and +86400 (1 day). Requests with other values may be rejected. + + */ +window_size_seconds?: number; +/** + * The size of the step between returned statistic windows, in seconds. +The backend supports a limited number of step sizes: 300 (5 minutes) and 86400 (1 day). Requests with other values may be rejected. */ -bucket_size_seconds?: number; +window_step_seconds?: number; }; export type GetRuntimeEvmTokensAddressHoldersParams = { @@ -562,7 +560,7 @@ export type HumanReadableErrorResponse = { export interface ActiveAccounts { /** The date for the end of the daily active accounts measurement window. */ window_end: string; - /** The number of active accounts for the 24hour window starting at bucket_start. */ + /** The number of active accounts for the 24hour window ending at window_end. */ active_accounts: number; } @@ -577,9 +575,9 @@ export interface ActiveAccountsList { } export interface TxVolume { - /** The date for this daily transaction volume measurement. */ - bucket_start: string; - /** The transaction volume on this day. */ + /** The end timestamp for this daily transaction volume measurement. */ + window_end: string; + /** The transaction volume for this window. */ tx_volume: number; } @@ -588,9 +586,9 @@ export interface TxVolume { */ export interface TxVolumeList { - bucket_size_seconds: number; + window_size_seconds: number; /** The list of daily transaction volumes. */ - buckets: TxVolume[]; + windows: TxVolume[]; } export interface AccountStats { @@ -856,6 +854,8 @@ it separately, so the field may be missing for very fresh contracts (or if the f process is stalled). */ runtime_bytecode?: string; + /** The total amount of gas used to create or call this contract. */ + gas_used: number; /** Additional information obtained from contract verification. Only available for smart contracts that have been verified successfully by Sourcify. */ @@ -998,6 +998,8 @@ export type RuntimeBlockListAllOf = { blocks: RuntimeBlock[]; }; +export type RuntimeBlockList = List & RuntimeBlockListAllOf; + export interface ProposalVote { /** The staking address casting this vote. */ address: string; @@ -1581,8 +1583,6 @@ the query would return with limit=infinity. is_total_count_clipped: boolean; } -export type RuntimeBlockList = List & RuntimeBlockListAllOf; - /** * A list of consensus blocks. @@ -3345,7 +3345,7 @@ export const useGetRuntimeEvmTokensAddress = Date: Tue, 22 Aug 2023 10:59:30 +0200 Subject: [PATCH 2/2] Follow new API naming convension --- .../ParatimeDashboardPage/ActiveAccounts.tsx | 8 +++--- .../TotalTransactions.tsx | 6 ++--- .../TransactionsChartCard.tsx | 8 +++--- .../TransactionsStats.tsx | 10 +++---- src/app/utils/chart-utils.ts | 26 +++++++++---------- 5 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/app/pages/ParatimeDashboardPage/ActiveAccounts.tsx b/src/app/pages/ParatimeDashboardPage/ActiveAccounts.tsx index 8b7b7db99..a9918b230 100644 --- a/src/app/pages/ParatimeDashboardPage/ActiveAccounts.tsx +++ b/src/app/pages/ParatimeDashboardPage/ActiveAccounts.tsx @@ -12,7 +12,7 @@ import { dailyLimitWithoutBuffer, durationToQueryParams, filterHourlyActiveAccounts, - sumBucketsByStartDuration, + sumWindowsByStartDuration, } from '../../utils/chart-utils' import { SearchScope } from '../../../types/searchScope' @@ -21,7 +21,7 @@ export const getActiveAccountsWindows = (duration: ChartDuration, windows: Windo case ChartDuration.TODAY: return filterHourlyActiveAccounts(windows) case ChartDuration.ALL_TIME: - return sumBucketsByStartDuration(windows, 'active_accounts', 'window_end', startOfMonth) + return sumWindowsByStartDuration(windows, 'active_accounts', 'window_end', startOfMonth) default: return windows } @@ -68,8 +68,8 @@ export const ActiveAccounts: FC = ({ scope, chartDuration } const labels = getLabels(t) const { limit, window_step_seconds } = { ...durationToQueryParams[chartDuration], - // By default we fetch data with additional buckets buffer, but it does not apply to active accounts. - // Active accounts daily buckets are overlapping, so we cannot sum buckets like in other daily charts. + // By default we fetch data with additional buffer, but it does not apply to active accounts. + // Active accounts daily windows are overlapping, so we cannot sum windows like in other daily charts. limit: chartDuration === ChartDuration.TODAY ? dailyLimitWithoutBuffer diff --git a/src/app/pages/ParatimeDashboardPage/TotalTransactions.tsx b/src/app/pages/ParatimeDashboardPage/TotalTransactions.tsx index 2da02578a..14ef2f364 100644 --- a/src/app/pages/ParatimeDashboardPage/TotalTransactions.tsx +++ b/src/app/pages/ParatimeDashboardPage/TotalTransactions.tsx @@ -27,7 +27,7 @@ export const TotalTransactions: FC<{ scope: SearchScope }> = ({ scope }) => { }, }) - const buckets = dailyVolumeQuery.data?.data.windows + const windows = dailyVolumeQuery.data?.data.windows ? cumulativeSum(dailyVolumeQuery.data?.data.windows.slice().reverse(), 'tx_volume') : undefined @@ -40,13 +40,13 @@ export const TotalTransactions: FC<{ scope: SearchScope }> = ({ scope }) => { title={t('totalTransactions.header')} /> - {buckets && ( + {windows && ( = ({ scope, chart }) const isDailyChart = isFetched && chartDuration === ChartDuration.TODAY - const buckets = data?.data?.windows + const windows = data?.data?.windows const lineChartData = isDailyChart - ? sumBucketsByStartDuration(buckets, 'tx_volume', 'window_end', startOfHour) - : buckets + ? sumWindowsByStartDuration(windows, 'tx_volume', 'window_end', startOfHour) + : windows const formatParams = isDailyChart ? { timestamp: { diff --git a/src/app/pages/ParatimeDashboardPage/TransactionsStats.tsx b/src/app/pages/ParatimeDashboardPage/TransactionsStats.tsx index 3e743f811..92e917280 100644 --- a/src/app/pages/ParatimeDashboardPage/TransactionsStats.tsx +++ b/src/app/pages/ParatimeDashboardPage/TransactionsStats.tsx @@ -7,7 +7,7 @@ import { useGetLayerStatsTxVolume } from '../../../oasis-nexus/api' import { chartUseQueryStaleTimeMs, durationToQueryParams, - getMonthlyBucketsDailyAverage, + getMonthlyWindowsDailyAverage, } from '../../utils/chart-utils' import { DurationPills } from './DurationPills' import { CardHeaderWithResponsiveActions } from './CardHeaderWithResponsiveActions' @@ -28,8 +28,8 @@ export const TransactionsStats: FC<{ scope: SearchScope }> = ({ scope }) => { }, }) const allTime = dailyVolumeQuery.isFetched && chartDuration === ChartDuration.ALL_TIME - const buckets = allTime - ? getMonthlyBucketsDailyAverage(dailyVolumeQuery.data?.data.windows) + const windows = allTime + ? getMonthlyWindowsDailyAverage(dailyVolumeQuery.data?.data.windows) : dailyVolumeQuery.data?.data.windows const formatParams = allTime ? { @@ -49,12 +49,12 @@ export const TransactionsStats: FC<{ scope: SearchScope }> = ({ scope }) => { title={t('transactionStats.header')} /> - {buckets && ( + {windows && ( t('transactionStats.perDay', { value: value.toLocaleString() }), diff --git a/src/app/utils/chart-utils.ts b/src/app/utils/chart-utils.ts index 439ccbca7..02aa67943 100644 --- a/src/app/utils/chart-utils.ts +++ b/src/app/utils/chart-utils.ts @@ -14,7 +14,7 @@ export enum ChartDuration { ALL_TIME = 'ALL_TIME', } -export const dailyLimitWithoutBuffer = (60 / 5) * 24 // full day for 5 minutes buckets, +export const dailyLimitWithoutBuffer = (60 / 5) * 24 // full day for 5 minutes windows /* window_size_seconds refers to the number of data points in one chunk @@ -28,7 +28,7 @@ export const durationToQueryParams = { window_step_seconds: fiveMinutesWindowSize, limit: dailyLimitWithoutBuffer + - // daily data needs additional 2 buckets to make sure we have at least full 24 buckets + // daily data needs additional 2 windows to make sure we have at least full 24 windows (60 / 5) * 2, }, [ChartDuration.WEEK]: { @@ -60,11 +60,11 @@ export const chartDurationToDaysMap = { export const chartUseQueryStaleTimeMs = durationToQueryParams[ChartDuration.TODAY].window_size_seconds * 1000 -type Buckets = TxVolume[] | undefined +type Windows = TxVolume[] | undefined type MonthlyTxVolume = TxVolume & { numberOfItemsInGroup: number } -const groupWindowsByMonth = (buckets: Buckets) => { - return buckets?.reduce((acc: MonthlyTxVolume[], cur, index, arr) => { +const groupWindowsByMonth = (windows: Windows) => { + return windows?.reduce((acc: MonthlyTxVolume[], cur, index, arr) => { if (index > 0 && isSameMonth(new Date(cur.window_end), new Date(arr[index - 1].window_end))) { acc[acc.length - 1].tx_volume += cur.tx_volume acc[acc.length - 1].numberOfItemsInGroup += 1 @@ -79,10 +79,10 @@ const groupWindowsByMonth = (buckets: Buckets) => { }, []) } -export const getMonthlyBucketsDailyAverage = (buckets: Buckets): Buckets => { - const monthlyBuckets = groupWindowsByMonth(buckets) +export const getMonthlyWindowsDailyAverage = (windows: Windows): Windows => { + const monthlyWindows = groupWindowsByMonth(windows) - return monthlyBuckets?.map(item => ({ + return monthlyWindows?.map(item => ({ ...item, tx_volume: Math.round((item.tx_volume / item.numberOfItemsInGroup) * 100) / 100, })) @@ -113,21 +113,21 @@ type StringOnly = { [key in keyof T as T[key] extends string | undefined ? key : never]: T[key] } -export const sumBucketsByStartDuration = < +export const sumWindowsByStartDuration = < T extends NumberOnly & StringOnly, N extends keyof NumberOnly, S extends keyof StringOnly, >( - buckets: T[] | undefined, + windows: T[] | undefined, sumKey: N, dateKey: S, startDurationFn: typeof startOfHour | typeof startOfDay | typeof startOfMonth, ) => { - if (!buckets) { + if (!windows) { return [] } - const durationMap = buckets.reduce( + const durationMap = windows.reduce( (accMap, item) => { const key = startDurationFn(new Date(item[dateKey])).toISOString() @@ -139,7 +139,7 @@ export const sumBucketsByStartDuration = < {} as { [key: string]: number }, ) - // For daily charts we want to skip the first and last buckets. + // For daily charts we want to skip the first and last windows. // They are not full and we want to avoid chart drop. const filteredDurationMap = startDurationFn === startOfHour