-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcode_stats.sql
More file actions
147 lines (138 loc) · 5.66 KB
/
Copy pathcode_stats.sql
File metadata and controls
147 lines (138 loc) · 5.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
-- ============================================================
-- CodeTrack — per-code stats materialized views + RPCs
-- Run this whole file once in the Supabase SQL Editor.
--
-- Fixes: 7d/30d/gas/chart were computed in the browser from the
-- newest ~100 (stats) / ~1000 (chart, PostgREST cap) raw rows, so
-- mega codes capped at 100 and the chart collapsed to a single spike.
-- These views pre-aggregate on the server so every metric is exact.
--
-- `transactions.codes` is an array column, so we unnest(codes) to get
-- one row per (code, transaction).
-- ============================================================
-- ------------------------------------------------------------
-- 1. code_daily_stats — per (code, day)
-- Feeds: per-day chart, 7d/30d tx counts, total gas, all-time tx.
-- ------------------------------------------------------------
CREATE MATERIALIZED VIEW IF NOT EXISTS code_daily_stats AS
SELECT
c AS code,
(block_timestamp AT TIME ZONE 'UTC')::date AS day,
count(*)::bigint AS tx_count,
sum(gas_used::numeric) AS gas_used
FROM transactions, unnest(codes) AS c
GROUP BY c, (block_timestamp AT TIME ZONE 'UTC')::date;
-- Unique index is REQUIRED for REFRESH ... CONCURRENTLY.
CREATE UNIQUE INDEX IF NOT EXISTS ux_code_daily_stats
ON code_daily_stats (code, day);
CREATE INDEX IF NOT EXISTS ix_code_daily_stats_code_day
ON code_daily_stats (code, day DESC);
-- ------------------------------------------------------------
-- 2. code_lifetime_stats — per code, all-time unique senders
-- (count(distinct) can't be summed across days, so it lives
-- in its own view instead of code_daily_stats.)
-- ------------------------------------------------------------
CREATE MATERIALIZED VIEW IF NOT EXISTS code_lifetime_stats AS
SELECT
c AS code,
count(DISTINCT lower(from_address))::bigint AS unique_senders
FROM transactions, unnest(codes) AS c
GROUP BY c;
CREATE UNIQUE INDEX IF NOT EXISTS ux_code_lifetime_stats
ON code_lifetime_stats (code);
-- ------------------------------------------------------------
-- 3. get_code_stats(p_code)
-- total_tx / tx_7d / tx_30d / total_gas <- code_daily_stats
-- unique_senders <- code_lifetime_stats
-- SECURITY DEFINER so the anon key can call it without direct
-- SELECT grants on the materialized views.
-- ------------------------------------------------------------
CREATE OR REPLACE FUNCTION get_code_stats(p_code text)
RETURNS TABLE(
total_tx bigint,
tx_7d bigint,
tx_30d bigint,
unique_senders bigint,
total_gas numeric
)
LANGUAGE sql
STABLE
SECURITY DEFINER
SET search_path = public
AS $$
SELECT
coalesce((SELECT sum(tx_count)
FROM code_daily_stats
WHERE code = p_code), 0)::bigint,
coalesce((SELECT sum(tx_count)
FROM code_daily_stats
WHERE code = p_code
AND day >= current_date - 6), 0)::bigint,
coalesce((SELECT sum(tx_count)
FROM code_daily_stats
WHERE code = p_code
AND day >= current_date - 29), 0)::bigint,
coalesce((SELECT unique_senders
FROM code_lifetime_stats
WHERE code = p_code), 0)::bigint,
coalesce((SELECT sum(gas_used)
FROM code_daily_stats
WHERE code = p_code), 0)::numeric;
$$;
-- ------------------------------------------------------------
-- 4. get_code_daily_chart(p_code, p_days)
-- One row per day for the last p_days days, missing days = 0.
-- generate_series + LEFT JOIN does the gap-filling the frontend
-- used to do in JS. Returns oldest -> newest.
-- ------------------------------------------------------------
CREATE OR REPLACE FUNCTION get_code_daily_chart(p_code text, p_days int DEFAULT 30)
RETURNS TABLE(
day date,
tx_count bigint
)
LANGUAGE sql
STABLE
SECURITY DEFINER
SET search_path = public
AS $$
SELECT
d::date AS day,
coalesce(s.tx_count, 0)::bigint AS tx_count
FROM generate_series(
current_date - (p_days - 1),
current_date,
interval '1 day'
) AS d
LEFT JOIN code_daily_stats s
ON s.code = p_code
AND s.day = d::date
ORDER BY d;
$$;
-- Allow the public (anon) and logged-in keys to call the RPCs.
GRANT EXECUTE ON FUNCTION get_code_stats(text) TO anon, authenticated;
GRANT EXECUTE ON FUNCTION get_code_daily_chart(text, int) TO anon, authenticated;
-- ============================================================
-- MANUAL REFRESH
-- CREATE MATERIALIZED VIEW above already populates the views once.
-- After the indexer ingests new transactions, re-run these to update.
-- CONCURRENTLY keeps the views readable during the refresh and relies
-- on the unique indexes created above.
--
-- REFRESH MATERIALIZED VIEW CONCURRENTLY code_daily_stats;
-- REFRESH MATERIALIZED VIEW CONCURRENTLY code_lifetime_stats;
-- ============================================================
-- ============================================================
-- OPTIONAL: scheduled refresh (pg_cron) — hourly, not enabled by default.
-- Uncomment if/when you want automatic refreshes instead of manual ones.
--
-- CREATE EXTENSION IF NOT EXISTS pg_cron;
--
-- SELECT cron.schedule(
-- 'refresh_code_daily_stats', '0 * * * *',
-- $$REFRESH MATERIALIZED VIEW CONCURRENTLY code_daily_stats$$
-- );
-- SELECT cron.schedule(
-- 'refresh_code_lifetime_stats', '0 * * * *',
-- $$REFRESH MATERIALIZED VIEW CONCURRENTLY code_lifetime_stats$$
-- );
-- ============================================================