1-- =====================================================
2-- Uniswap Pool 交易数据统计系统 - Database Schema
3-- Database: PostgreSQL (Supabase)
4-- Pool: ARB/WETH on Arbitrum
5-- =====================================================
6
7-- 启用必要的扩展
8CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
9CREATE EXTENSION IF NOT EXISTS "pg_stat_statements";
10
11-- =====================================================
12-- 1. 交易记录表 (Swap Events)
13-- =====================================================
14CREATE TABLE IF NOT EXISTS swaps (
15 id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
16
17 -- 区块链基础信息
18 transaction_hash VARCHAR(66) NOT NULL,
19 block_number BIGINT NOT NULL,
20 block_timestamp TIMESTAMP WITH TIME ZONE NOT NULL,
21 log_index INTEGER NOT NULL,
22
23 -- 交易参与方
24 sender VARCHAR(42) NOT NULL,
25 recipient VARCHAR(42) NOT NULL,
26
27 -- 交易数据
28 amount0 DECIMAL(78, 0) NOT NULL, -- token0 数量变化(wei)
29 amount1 DECIMAL(78, 0) NOT NULL, -- token1 数量变化(wei)
30 sqrt_price_x96 DECIMAL(78, 0) NOT NULL, -- 价格 (Q64.96 格式)
31 liquidity DECIMAL(78, 0) NOT NULL,
32 tick INTEGER NOT NULL,
33
34 -- 计算字段
35 amount0_readable DECIMAL(28, 18), -- token0 可读数量
36 amount1_readable DECIMAL(28, 18), -- token1 可读数量
37 price_token0 DECIMAL(28, 18), -- token0 价格(以 token1 计价)
38 price_token1 DECIMAL(28, 18), -- token1 价格(以 token0 计价)
39 swap_type VARCHAR(4) NOT NULL CHECK (swap_type IN ('BUY', 'SELL')), -- 买入/卖出 ARB
40 usd_value DECIMAL(18, 2), -- 交易价值(USD)
41
42 -- Gas 信息
43 gas_used BIGINT,
44 gas_price DECIMAL(28, 0), -- wei
45 transaction_fee DECIMAL(28, 18), -- ETH
46
47 -- 元数据
48 created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
49 updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
50
51 -- 唯一约束
52 UNIQUE(transaction_hash, log_index)
53);
54
55-- 索引优化
56CREATE INDEX idx_swaps_block_number ON swaps(block_number DESC);
57CREATE INDEX idx_swaps_block_timestamp ON swaps(block_timestamp DESC);
58CREATE INDEX idx_swaps_sender ON swaps(sender);
59CREATE INDEX idx_swaps_recipient ON swaps(recipient);
60CREATE INDEX idx_swaps_swap_type ON swaps(swap_type);
61CREATE INDEX idx_swaps_usd_value ON swaps(usd_value DESC) WHERE usd_value IS NOT NULL;
62CREATE INDEX idx_swaps_composite ON swaps(block_timestamp DESC, swap_type);
63
64-- 分区表(按月分区,提高大数据查询性能)
65-- CREATE TABLE swaps_2024_01 PARTITION OF swaps
66-- FOR VALUES FROM ('2024-01-01') TO ('2024-02-01');
67
68COMMENT ON TABLE swaps IS 'Uniswap V3 Swap 事件记录';
69COMMENT ON COLUMN swaps.amount0 IS '正数表示流入池子,负数表示流出池子';
70COMMENT ON COLUMN swaps.swap_type IS 'BUY=买入ARB,SELL=卖出ARB';
71
72-- =====================================================
73-- 2. 流动性事件表 (Mint/Burn Events)
74-- =====================================================
75CREATE TABLE IF NOT EXISTS liquidity_events (
76 id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
77
78 -- 区块链基础信息
79 transaction_hash VARCHAR(66) NOT NULL,
80 block_number BIGINT NOT NULL,
81 block_timestamp TIMESTAMP WITH TIME ZONE NOT NULL,
82 log_index INTEGER NOT NULL,
83
84 -- 事件类型
85 event_type VARCHAR(10) NOT NULL CHECK (event_type IN ('MINT', 'BURN', 'COLLECT')),
86
87 -- 用户信息
88 owner VARCHAR(42) NOT NULL,
89 sender VARCHAR(42),
90
91 -- 流动性信息
92 liquidity_delta DECIMAL(78, 0) NOT NULL, -- 流动性变化
93 tick_lower INTEGER NOT NULL,
94 tick_upper INTEGER NOT NULL,
95
96 -- Token 数量
97 amount0 DECIMAL(78, 0) NOT NULL,
98 amount1 DECIMAL(78, 0) NOT NULL,
99 amount0_readable DECIMAL(28, 18),
100 amount1_readable DECIMAL(28, 18),
101 usd_value DECIMAL(18, 2),
102
103 -- 元数据
104 created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
105
106 UNIQUE(transaction_hash, log_index)
107);
108
109CREATE INDEX idx_liquidity_events_block_timestamp ON liquidity_events(block_timestamp DESC);
110CREATE INDEX idx_liquidity_events_owner ON liquidity_events(owner);
111CREATE INDEX idx_liquidity_events_event_type ON liquidity_events(event_type);
112CREATE INDEX idx_liquidity_events_tick_range ON liquidity_events(tick_lower, tick_upper);
113
114COMMENT ON TABLE liquidity_events IS '流动性变化事件(添加/移除/收集)';
115
116-- =====================================================
117-- 3. Pool 状态快照表
118-- =====================================================
119CREATE TABLE IF NOT EXISTS pool_snapshots (
120 id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
121
122 -- 快照时间
123 snapshot_time TIMESTAMP WITH TIME ZONE NOT NULL,
124 block_number BIGINT NOT NULL,
125
126 -- Pool 状态
127 sqrt_price_x96 DECIMAL(78, 0) NOT NULL,
128 tick INTEGER NOT NULL,
129 liquidity DECIMAL(78, 0) NOT NULL,
130
131 -- 计算字段
132 price_token0 DECIMAL(28, 18),
133 price_token1 DECIMAL(28, 18),
134 tvl_usd DECIMAL(18, 2), -- Total Value Locked
135
136 -- Token 余额
137 token0_balance DECIMAL(28, 18),
138 token1_balance DECIMAL(28, 18),
139
140 -- 24h 统计
141 volume_24h_usd DECIMAL(18, 2),
142 fees_24h_usd DECIMAL(18, 2),
143 transactions_24h INTEGER,
144
145 created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
146
147 UNIQUE(snapshot_time)
148);
149
150CREATE INDEX idx_pool_snapshots_time ON pool_snapshots(snapshot_time DESC);
151CREATE INDEX idx_pool_snapshots_block ON pool_snapshots(block_number DESC);
152
153COMMENT ON TABLE pool_snapshots IS '每小时 Pool 状态快照';
154
155-- =====================================================
156-- 4. 小时统计表 (OHLC)
157-- =====================================================
158CREATE TABLE IF NOT EXISTS hourly_stats (
159 id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
160
161 -- 时间范围
162 hour_start TIMESTAMP WITH TIME ZONE NOT NULL,
163 hour_end TIMESTAMP WITH TIME ZONE NOT NULL,
164
165 -- OHLC 数据
166 open_price DECIMAL(28, 18) NOT NULL,
167 high_price DECIMAL(28, 18) NOT NULL,
168 low_price DECIMAL(28, 18) NOT NULL,
169 close_price DECIMAL(28, 18) NOT NULL,
170
171 -- 交易统计
172 total_transactions INTEGER NOT NULL DEFAULT 0,
173 buy_transactions INTEGER NOT NULL DEFAULT 0,
174 sell_transactions INTEGER NOT NULL DEFAULT 0,
175
176 -- 交易量
177 volume_token0 DECIMAL(28, 18) NOT NULL DEFAULT 0,
178 volume_token1 DECIMAL(28, 18) NOT NULL DEFAULT 0,
179 volume_usd DECIMAL(18, 2) NOT NULL DEFAULT 0,
180
181 -- 手续费
182 fees_token0 DECIMAL(28, 18) NOT NULL DEFAULT 0,
183 fees_token1 DECIMAL(28, 18) NOT NULL DEFAULT 0,
184 fees_usd DECIMAL(18, 2) NOT NULL DEFAULT 0,
185
186 -- 用户统计
187 unique_addresses INTEGER NOT NULL DEFAULT 0,
188 unique_senders INTEGER NOT NULL DEFAULT 0,
189
190 -- 流动性
191 avg_liquidity DECIMAL(78, 0),
192 min_liquidity DECIMAL(78, 0),
193 max_liquidity DECIMAL(78, 0),
194
195 created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
196 updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
197
198 UNIQUE(hour_start)
199);
200
201CREATE INDEX idx_hourly_stats_time ON hourly_stats(hour_start DESC);
202
203COMMENT ON TABLE hourly_stats IS '每小时交易统计和 OHLC 数据';
204
205-- =====================================================
206-- 5. 日统计表
207-- =====================================================
208CREATE TABLE IF NOT EXISTS daily_stats (
209 id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
210
211 -- 日期
212 date DATE NOT NULL,
213
214 -- OHLC 数据
215 open_price DECIMAL(28, 18) NOT NULL,
216 high_price DECIMAL(28, 18) NOT NULL,
217 low_price DECIMAL(28, 18) NOT NULL,
218 close_price DECIMAL(28, 18) NOT NULL,
219
220 -- 交易统计
221 total_transactions INTEGER NOT NULL DEFAULT 0,
222 buy_transactions INTEGER NOT NULL DEFAULT 0,
223 sell_transactions INTEGER NOT NULL DEFAULT 0,
224
225 -- 交易量
226 volume_token0 DECIMAL(28, 18) NOT NULL DEFAULT 0,
227 volume_token1 DECIMAL(28, 18) NOT NULL DEFAULT 0,
228 volume_usd DECIMAL(18, 2) NOT NULL DEFAULT 0,
229
230 -- 手续费
231 fees_token0 DECIMAL(28, 18) NOT NULL DEFAULT 0,
232 fees_token1 DECIMAL(28, 18) NOT NULL DEFAULT 0,
233 fees_usd DECIMAL(18, 2) NOT NULL DEFAULT 0,
234
235 -- 用户统计
236 unique_addresses INTEGER NOT NULL DEFAULT 0,
237 new_addresses INTEGER NOT NULL DEFAULT 0,
238
239 -- 流动性
240 avg_tvl_usd DECIMAL(18, 2),
241 end_tvl_usd DECIMAL(18, 2),
242
243 -- 大额交易
244 whale_transactions INTEGER NOT NULL DEFAULT 0, -- > 10,000 USD
245 largest_transaction_usd DECIMAL(18, 2),
246
247 created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
248 updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
249
250 UNIQUE(date)
251);
252
253CREATE INDEX idx_daily_stats_date ON daily_stats(date DESC);
254
255COMMENT ON TABLE daily_stats IS '每日交易统计汇总';
256
257-- =====================================================
258-- 6. 用户统计表
259-- =====================================================
260CREATE TABLE IF NOT EXISTS user_stats (
261 id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
262
263 -- 用户地址
264 address VARCHAR(42) NOT NULL,
265
266 -- 交易统计
267 total_transactions INTEGER NOT NULL DEFAULT 0,
268 buy_transactions INTEGER NOT NULL DEFAULT 0,
269 sell_transactions INTEGER NOT NULL DEFAULT 0,
270
271 -- 交易量
272 total_volume_usd DECIMAL(18, 2) NOT NULL DEFAULT 0,
273 largest_transaction_usd DECIMAL(18, 2),
274
275 -- 时间信息
276 first_transaction_at TIMESTAMP WITH TIME ZONE,
277 last_transaction_at TIMESTAMP WITH TIME ZONE,
278
279 -- 流动性提供
280 is_liquidity_provider BOOLEAN DEFAULT FALSE,
281 total_liquidity_provided_usd DECIMAL(18, 2) DEFAULT 0,
282
283 -- 标签
284 user_type VARCHAR(20) CHECK (user_type IN ('RETAIL', 'WHALE', 'BOT', 'LP', 'MEV')),
285
286 created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
287 updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
288
289 UNIQUE(address)
290);
291
292CREATE INDEX idx_user_stats_address ON user_stats(address);
293CREATE INDEX idx_user_stats_volume ON user_stats(total_volume_usd DESC);
294CREATE INDEX idx_user_stats_type ON user_stats(user_type);
295
296COMMENT ON TABLE user_stats IS '用户交易行为统计';
297
298-- =====================================================
299-- 7. 价格历史表(用于图表)
300-- =====================================================
301CREATE TABLE IF NOT EXISTS price_history (
302 id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
303
304 timestamp TIMESTAMP WITH TIME ZONE NOT NULL,
305 block_number BIGINT NOT NULL,
306
307 price DECIMAL(28, 18) NOT NULL,
308
309 created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
310
311 UNIQUE(timestamp)
312);
313
314CREATE INDEX idx_price_history_timestamp ON price_history(timestamp DESC);
315
316COMMENT ON TABLE price_history IS '价格历史记录(每笔交易后)';
317
318-- =====================================================
319-- 8. 系统监控表
320-- =====================================================
321CREATE TABLE IF NOT EXISTS sync_status (
322 id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
323
324 -- 同步状态
325 last_synced_block BIGINT NOT NULL,
326 last_synced_timestamp TIMESTAMP WITH TIME ZONE NOT NULL,
327 current_block BIGINT,
328
329 -- 统计
330 total_events_synced BIGINT NOT NULL DEFAULT 0,
331 sync_errors INTEGER NOT NULL DEFAULT 0,
332 last_error TEXT,
333 last_error_at TIMESTAMP WITH TIME ZONE,
334
335 -- 性能
336 avg_sync_time_ms INTEGER,
337
338 updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
339);
340
341-- 初始化同步状态
342INSERT INTO sync_status (last_synced_block, last_synced_timestamp, total_events_synced)
343VALUES (0, NOW() - INTERVAL '30 days', 0)
344ON CONFLICT DO NOTHING;
345
346COMMENT ON TABLE sync_status IS '数据同步状态监控';
347
348-- =====================================================
349-- 9. 触发器 - 自动更新 updated_at
350-- =====================================================
351CREATE OR REPLACE FUNCTION update_updated_at_column()
352RETURNS TRIGGER AS $$
353BEGIN
354 NEW.updated_at = NOW();
355 RETURN NEW;
356END;
357$$ LANGUAGE plpgsql;
358
359CREATE TRIGGER update_swaps_updated_at
360 BEFORE UPDATE ON swaps
361 FOR EACH ROW
362 EXECUTE FUNCTION update_updated_at_column();
363
364CREATE TRIGGER update_hourly_stats_updated_at
365 BEFORE UPDATE ON hourly_stats
366 FOR EACH ROW
367 EXECUTE FUNCTION update_updated_at_column();
368
369CREATE TRIGGER update_daily_stats_updated_at
370 BEFORE UPDATE ON daily_stats
371 FOR EACH ROW
372 EXECUTE FUNCTION update_updated_at_column();
373
374CREATE TRIGGER update_user_stats_updated_at
375 BEFORE UPDATE ON user_stats
376 FOR EACH ROW
377 EXECUTE FUNCTION update_updated_at_column();
378
379-- =====================================================
380-- 10. 视图 - 常用查询
381-- =====================================================
382
383-- 最近 24 小时交易统计
384CREATE OR REPLACE VIEW v_stats_24h AS
385SELECT
386 COUNT(*) as total_transactions,
387 COUNT(*) FILTER (WHERE swap_type = 'BUY') as buy_transactions,
388 COUNT(*) FILTER (WHERE swap_type = 'SELL') as sell_transactions,
389 SUM(usd_value) as total_volume_usd,
390 AVG(usd_value) as avg_transaction_size_usd,
391 MAX(usd_value) as largest_transaction_usd,
392 COUNT(DISTINCT sender) as unique_traders,
393 SUM(usd_value) * 0.0005 as estimated_fees_usd -- 0.05% fee
394FROM swaps
395WHERE block_timestamp > NOW() - INTERVAL '24 hours';
396
397-- 最近 7 天每日统计
398CREATE OR REPLACE VIEW v_stats_7d AS
399SELECT
400 DATE(block_timestamp) as date,
401 COUNT(*) as total_transactions,
402 SUM(usd_value) as volume_usd,
403 COUNT(DISTINCT sender) as unique_traders,
404 MIN(price_token0) as low_price,
405 MAX(price_token0) as high_price
406FROM swaps
407WHERE block_timestamp > NOW() - INTERVAL '7 days'
408GROUP BY DATE(block_timestamp)
409ORDER BY date DESC;
410
411-- 大额交易(Whale Alert)
412CREATE OR REPLACE VIEW v_whale_transactions AS
413SELECT
414 transaction_hash,
415 block_timestamp,
416 sender,
417 swap_type,
418 usd_value,
419 amount0_readable,
420 amount1_readable
421FROM swaps
422WHERE usd_value > 10000
423ORDER BY block_timestamp DESC;
424
425-- Top 交易者
426CREATE OR REPLACE VIEW v_top_traders AS
427SELECT
428 address,
429 total_transactions,
430 total_volume_usd,
431 user_type,
432 last_transaction_at
433FROM user_stats
434ORDER BY total_volume_usd DESC
435LIMIT 100;
436
437-- =====================================================
438-- 11. RLS (Row Level Security) 策略
439-- =====================================================
440-- 如果需要多租户或权限控制,可以启用 RLS
441
442-- ALTER TABLE swaps ENABLE ROW LEVEL SECURITY;
443-- CREATE POLICY "Public read access" ON swaps FOR SELECT USING (true);
444
445-- =====================================================
446-- 12. 性能优化 - 定期维护脚本
447-- =====================================================
448
449-- 定期 VACUUM 和 ANALYZE
450-- 可以通过 pg_cron 扩展或外部 cron job 执行
451-- VACUUM ANALYZE swaps;
452-- VACUUM ANALYZE liquidity_events;
453
454-- 清理旧数据(可选,保留最近 1 年数据)
455-- DELETE FROM swaps WHERE block_timestamp < NOW() - INTERVAL '1 year';
456
457-- =====================================================
458-- 13. 初始数据和测试
459-- =====================================================
460
461-- 插入测试数据(开发环境)
462-- INSERT INTO swaps (
463-- transaction_hash, block_number, block_timestamp, log_index,
464-- sender, recipient, amount0, amount1, sqrt_price_x96, liquidity, tick,
465-- amount0_readable, amount1_readable, price_token0, swap_type, usd_value
466-- ) VALUES (
467-- '0x1234...', 150000000, NOW(), 0,
468-- '0xabc...', '0xdef...', -1000000000000000000, 500000000000000000,
469-- 79228162514264337593543950336, 1000000000000000000, 100000,
470-- -1.0, 0.5, 0.5, 'SELL', 1000.00
471-- );
472
473-- =====================================================
474-- 完成
475-- =====================================================
476
477-- 检查所有表
478SELECT table_name
479FROM information_schema.tables
480WHERE table_schema = 'public'
481ORDER BY table_name;
482
483-- 检查索引
484SELECT
485 tablename,
486 indexname,
487 indexdef
488FROM pg_indexes
489WHERE schemaname = 'public'
490ORDER BY tablename, indexname;
491