Skip to content

Commit dc39c69

Browse files
committed
rolling stats on day
1 parent 26e7ba5 commit dc39c69

2 files changed

Lines changed: 228 additions & 12 deletions

File tree

src/handlers/getBridgeStatsOnDay.ts

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
import { IResponse, successResponse, errorResponse } from "../utils/lambda-response";
22
import wrap from "../utils/wrap";
33
import { getCurrentUnixTimestamp, getTimestampAtStartOfDay } from "../utils/date";
4-
import { queryAggregatedTokenStatsTop30 } from "../utils/wrappa/postgres/query";
4+
import {
5+
queryAggregatedTokenStatsTop30,
6+
queryAggregatedTokenStatsTop30Rolling,
7+
queryAggregatedTotalsRolling,
8+
queryAggregatedTotalsTimestampRange,
9+
} from "../utils/wrappa/postgres/query";
510
import { getLlamaPrices } from "../utils/prices";
611
import { importBridgeNetwork } from "../data/importBridgeNetwork";
712
import { normalizeChain, normlizeTokenSymbol } from "../utils/normalizeChain";
@@ -31,17 +36,35 @@ type StatsRow = {
3136
txs: number | null;
3237
};
3338

39+
type StatsTotals = {
40+
total_deposited_usd: string;
41+
total_withdrawn_usd: string;
42+
total_deposit_txs: number;
43+
total_withdrawal_txs: number;
44+
};
45+
3446
const isValidPriceId = (id?: string) => {
3547
if (!id) return false;
3648
if (id.includes("\\") || id.includes("/") || id.includes(" ")) return false;
3749
const parts = id.split(":");
3850
if (parts.length !== 2) return false;
3951
if (!parts[0] || !parts[1]) return false;
40-
if (parts[1] === "\\N" || parts[1] === "\\n" || parts[1].toLowerCase() === "null" || parts[1].toLowerCase() === "undefined") return false;
52+
if (
53+
parts[1] === "\\N" ||
54+
parts[1] === "\\n" ||
55+
parts[1].toLowerCase() === "null" ||
56+
parts[1].toLowerCase() === "undefined"
57+
)
58+
return false;
4159
return true;
4260
};
4361

44-
const getBridgeStatsOnDay = async (timestamp: string = "0", chain: string, bridgeId?: string) => {
62+
const getBridgeStatsOnDay = async (
63+
timestamp: string = "0",
64+
chain: string,
65+
bridgeId?: string,
66+
rollingHours?: number
67+
) => {
4568
let bridgeDbName = undefined as any;
4669
const queryChain = chain === "" ? "" : normalizeChain(chain);
4770
if (bridgeId) {
@@ -61,12 +84,17 @@ const getBridgeStatsOnDay = async (timestamp: string = "0", chain: string, bridg
6184
const currentTimestamp = getCurrentUnixTimestamp();
6285
const endTimestamp = Math.max(queryTimestamp, Math.min(maxEndTimestamp, currentTimestamp));
6386

64-
const rows = (await queryAggregatedTokenStatsTop30(
65-
queryTimestamp,
66-
endTimestamp,
67-
queryChain,
68-
bridgeDbName
69-
)) as StatsRow[];
87+
const [rows, totals] = (await Promise.all(
88+
rollingHours
89+
? [
90+
queryAggregatedTokenStatsTop30Rolling(rollingHours, queryChain, bridgeDbName),
91+
queryAggregatedTotalsRolling(rollingHours, queryChain, bridgeDbName),
92+
]
93+
: [
94+
queryAggregatedTokenStatsTop30(queryTimestamp, endTimestamp, queryChain, bridgeDbName),
95+
queryAggregatedTotalsTimestampRange(queryTimestamp, endTimestamp, queryChain, bridgeDbName),
96+
]
97+
)) as [StatsRow[], StatsTotals];
7098

7199
const dt = rows.filter((r) => r.kind === "dt");
72100
const wt = rows.filter((r) => r.kind === "wt");
@@ -127,6 +155,10 @@ const getBridgeStatsOnDay = async (timestamp: string = "0", chain: string, bridg
127155

128156
return {
129157
date: queryTimestamp,
158+
totalDepositedUSD: Number(totals?.total_deposited_usd ?? 0),
159+
totalWithdrawnUSD: Number(totals?.total_withdrawn_usd ?? 0),
160+
totalDepositTxs: totals?.total_deposit_txs ?? 0,
161+
totalWithdrawalTxs: totals?.total_withdrawal_txs ?? 0,
130162
totalTokensDeposited: buildTokenRecord(dt),
131163
totalTokensWithdrawn: buildTokenRecord(wt),
132164
totalAddressDeposited: buildAddressRecord(da),
@@ -143,7 +175,12 @@ const handler = async (event: AWSLambda.APIGatewayEvent): Promise<IResponse> =>
143175
}
144176
const chain = event.pathParameters?.chain?.toLowerCase() ?? "";
145177
const bridgeNetworkId = event.queryStringParameters?.id;
146-
const response = await getBridgeStatsOnDay(timestamp, chain, bridgeNetworkId);
178+
const rollingHoursParam = event.queryStringParameters?.rollingHours;
179+
const rollingHours =
180+
rollingHoursParam && Number.isInteger(Number(rollingHoursParam))
181+
? Math.min(Math.max(Number(rollingHoursParam), 1), 168)
182+
: undefined;
183+
const response = await getBridgeStatsOnDay(timestamp, chain, bridgeNetworkId, rollingHours);
147184
return successResponse(response, 10 * 60); // 10 mins cache
148185
};
149186

src/utils/wrappa/postgres/query.ts

Lines changed: 181 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -485,8 +485,8 @@ const queryAggregatedTokenStatsTop30 = async (
485485
bridgeNetworkName?: string,
486486
limit: number = 30
487487
) => {
488-
let conditions = sql`WHERE ha.ts >= to_timestamp(${startTimestamp})::date
489-
AND ha.ts <= to_timestamp(${endTimestamp})::date`;
488+
let conditions = sql`WHERE ha.ts >= to_timestamp(${startTimestamp})
489+
AND ha.ts < to_timestamp(${endTimestamp})`;
490490

491491
if (chain) {
492492
conditions = sql`${conditions} AND c.chain = ${chain}`;
@@ -558,6 +558,182 @@ const queryAggregatedTokenStatsTop30 = async (
558558
`;
559559
};
560560

561+
const queryAggregatedTokenStatsTop30Rolling = async (
562+
hours: number,
563+
chain?: string,
564+
bridgeNetworkName?: string,
565+
limit: number = 30
566+
) => {
567+
let latestConditions = sql`WHERE 1 = 1`;
568+
let baseConditions = sql``;
569+
570+
if (bridgeNetworkName) {
571+
latestConditions = sql`${latestConditions} AND c.bridge_name = ${bridgeNetworkName}`;
572+
baseConditions = sql`${baseConditions} AND c.bridge_name = ${bridgeNetworkName}`;
573+
}
574+
if (chain) {
575+
baseConditions = sql`${baseConditions} AND c.chain = ${chain}`;
576+
if (!bridgeNetworkName) {
577+
latestConditions = sql`${latestConditions} AND c.chain = ${chain}`;
578+
}
579+
}
580+
581+
return await sql<
582+
{
583+
kind: "dt" | "wt" | "da" | "wa";
584+
key: string;
585+
amount: string | null;
586+
usd_value: string;
587+
txs: number | null;
588+
}[]
589+
>`
590+
WITH latest_ts AS (
591+
SELECT MAX(ha.ts) AS max_ts
592+
FROM bridges.hourly_aggregated ha
593+
JOIN bridges.config c ON ha.bridge_id = c.id
594+
${latestConditions}
595+
),
596+
base AS (
597+
SELECT ha.total_tokens_deposited, ha.total_tokens_withdrawn,
598+
ha.total_address_deposited, ha.total_address_withdrawn
599+
FROM bridges.hourly_aggregated ha
600+
JOIN bridges.config c ON ha.bridge_id = c.id
601+
CROSS JOIN latest_ts lt
602+
WHERE lt.max_ts IS NOT NULL
603+
AND ha.ts > lt.max_ts - make_interval(hours => ${hours})
604+
AND ha.ts <= lt.max_ts
605+
${baseConditions}
606+
),
607+
dt AS (
608+
SELECT replace((tok).token, '''', '') AS key,
609+
trim_scale(SUM(replace(replace((tok).amount, '''', ''), ' ', '')::numeric))::text AS amount,
610+
SUM((tok).usd_value)::text AS usd_value
611+
FROM base CROSS JOIN LATERAL unnest(total_tokens_deposited) tok
612+
WHERE (tok).token IS NOT NULL
613+
GROUP BY replace((tok).token, '''', '')
614+
ORDER BY SUM((tok).usd_value) DESC NULLS LAST
615+
LIMIT ${limit}
616+
),
617+
wt AS (
618+
SELECT replace((tok).token, '''', '') AS key,
619+
trim_scale(SUM(replace(replace((tok).amount, '''', ''), ' ', '')::numeric))::text AS amount,
620+
SUM((tok).usd_value)::text AS usd_value
621+
FROM base CROSS JOIN LATERAL unnest(total_tokens_withdrawn) tok
622+
WHERE (tok).token IS NOT NULL
623+
GROUP BY replace((tok).token, '''', '')
624+
ORDER BY SUM((tok).usd_value) DESC NULLS LAST
625+
LIMIT ${limit}
626+
),
627+
da AS (
628+
SELECT replace((a).address, '''', '') AS key,
629+
SUM((a).usd_value)::text AS usd_value,
630+
COALESCE(SUM((a).txs), 0)::integer AS txs
631+
FROM base CROSS JOIN LATERAL unnest(total_address_deposited) a
632+
WHERE (a).address IS NOT NULL
633+
GROUP BY replace((a).address, '''', '')
634+
ORDER BY SUM((a).usd_value) DESC NULLS LAST
635+
LIMIT ${limit}
636+
),
637+
wa AS (
638+
SELECT replace((a).address, '''', '') AS key,
639+
SUM((a).usd_value)::text AS usd_value,
640+
COALESCE(SUM((a).txs), 0)::integer AS txs
641+
FROM base CROSS JOIN LATERAL unnest(total_address_withdrawn) a
642+
WHERE (a).address IS NOT NULL
643+
GROUP BY replace((a).address, '''', '')
644+
ORDER BY SUM((a).usd_value) DESC NULLS LAST
645+
LIMIT ${limit}
646+
)
647+
SELECT 'dt' AS kind, key, amount, usd_value, NULL::integer AS txs FROM dt
648+
UNION ALL SELECT 'wt', key, amount, usd_value, NULL::integer FROM wt
649+
UNION ALL SELECT 'da', key, NULL::text, usd_value, txs FROM da
650+
UNION ALL SELECT 'wa', key, NULL::text, usd_value, txs FROM wa
651+
`;
652+
};
653+
654+
const queryAggregatedTotalsTimestampRange = async (
655+
startTimestamp: number,
656+
endTimestamp: number,
657+
chain?: string,
658+
bridgeNetworkName?: string
659+
) => {
660+
let conditions = sql`WHERE ha.ts >= to_timestamp(${startTimestamp})
661+
AND ha.ts < to_timestamp(${endTimestamp})`;
662+
663+
if (chain) {
664+
conditions = sql`${conditions} AND c.chain = ${chain}`;
665+
}
666+
if (bridgeNetworkName) {
667+
conditions = sql`${conditions} AND c.bridge_name = ${bridgeNetworkName}`;
668+
}
669+
670+
return (
671+
await sql<
672+
{
673+
total_deposited_usd: string;
674+
total_withdrawn_usd: string;
675+
total_deposit_txs: number;
676+
total_withdrawal_txs: number;
677+
}[]
678+
>`
679+
SELECT
680+
COALESCE(SUM(ha.total_deposited_usd), 0)::text AS total_deposited_usd,
681+
COALESCE(SUM(ha.total_withdrawn_usd), 0)::text AS total_withdrawn_usd,
682+
COALESCE(SUM(ha.total_deposit_txs), 0)::integer AS total_deposit_txs,
683+
COALESCE(SUM(ha.total_withdrawal_txs), 0)::integer AS total_withdrawal_txs
684+
FROM bridges.hourly_aggregated ha
685+
JOIN bridges.config c ON ha.bridge_id = c.id
686+
${conditions}
687+
`
688+
)[0];
689+
};
690+
691+
const queryAggregatedTotalsRolling = async (hours: number, chain?: string, bridgeNetworkName?: string) => {
692+
let latestConditions = sql`WHERE 1 = 1`;
693+
let baseConditions = sql``;
694+
695+
if (bridgeNetworkName) {
696+
latestConditions = sql`${latestConditions} AND c.bridge_name = ${bridgeNetworkName}`;
697+
baseConditions = sql`${baseConditions} AND c.bridge_name = ${bridgeNetworkName}`;
698+
}
699+
if (chain) {
700+
baseConditions = sql`${baseConditions} AND c.chain = ${chain}`;
701+
if (!bridgeNetworkName) {
702+
latestConditions = sql`${latestConditions} AND c.chain = ${chain}`;
703+
}
704+
}
705+
706+
return (
707+
await sql<
708+
{
709+
total_deposited_usd: string;
710+
total_withdrawn_usd: string;
711+
total_deposit_txs: number;
712+
total_withdrawal_txs: number;
713+
}[]
714+
>`
715+
WITH latest_ts AS (
716+
SELECT MAX(ha.ts) AS max_ts
717+
FROM bridges.hourly_aggregated ha
718+
JOIN bridges.config c ON ha.bridge_id = c.id
719+
${latestConditions}
720+
)
721+
SELECT
722+
COALESCE(SUM(ha.total_deposited_usd), 0)::text AS total_deposited_usd,
723+
COALESCE(SUM(ha.total_withdrawn_usd), 0)::text AS total_withdrawn_usd,
724+
COALESCE(SUM(ha.total_deposit_txs), 0)::integer AS total_deposit_txs,
725+
COALESCE(SUM(ha.total_withdrawal_txs), 0)::integer AS total_withdrawal_txs
726+
FROM bridges.hourly_aggregated ha
727+
JOIN bridges.config c ON ha.bridge_id = c.id
728+
CROSS JOIN latest_ts lt
729+
WHERE lt.max_ts IS NOT NULL
730+
AND ha.ts > lt.max_ts - make_interval(hours => ${hours})
731+
AND ha.ts <= lt.max_ts
732+
${baseConditions}
733+
`
734+
)[0];
735+
};
736+
561737
export {
562738
getBridgeID,
563739
getConfigsWithDestChain,
@@ -574,4 +750,7 @@ export {
574750
queryBridgeTxCounts24h,
575751
getNetflows,
576752
queryAggregatedTokenStatsTop30,
753+
queryAggregatedTokenStatsTop30Rolling,
754+
queryAggregatedTotalsTimestampRange,
755+
queryAggregatedTotalsRolling,
577756
};

0 commit comments

Comments
 (0)