-- Migration: Token Aggregation Schema -- Description: Creates tables for token market data, liquidity pools, OHLCV, and external API cache -- Supports ChainID 138 and 651940 -- Token Market Data - Aggregated market metrics per token CREATE TABLE IF NOT EXISTS token_market_data ( id BIGSERIAL PRIMARY KEY, chain_id INTEGER NOT NULL, token_address VARCHAR(42) NOT NULL, price_usd NUMERIC(30, 8), price_change_24h NUMERIC(10, 4), volume_24h NUMERIC(30, 8) DEFAULT 0, volume_7d NUMERIC(30, 8) DEFAULT 0, volume_30d NUMERIC(30, 8) DEFAULT 0, market_cap_usd NUMERIC(30, 8), liquidity_usd NUMERIC(30, 8) DEFAULT 0, holders_count INTEGER DEFAULT 0, transfers_24h INTEGER DEFAULT 0, last_updated TIMESTAMP WITH TIME ZONE DEFAULT NOW(), created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), UNIQUE(chain_id, token_address) ) PARTITION BY LIST (chain_id); -- Create partitions for supported chains CREATE TABLE IF NOT EXISTS token_market_data_chain_138 PARTITION OF token_market_data FOR VALUES IN (138); CREATE TABLE IF NOT EXISTS token_market_data_chain_651940 PARTITION OF token_market_data FOR VALUES IN (651940); CREATE INDEX idx_token_market_data_chain_token ON token_market_data(chain_id, token_address); CREATE INDEX idx_token_market_data_price ON token_market_data(price_usd) WHERE price_usd IS NOT NULL; CREATE INDEX idx_token_market_data_volume ON token_market_data(volume_24h) WHERE volume_24h > 0; CREATE INDEX idx_token_market_data_last_updated ON token_market_data(last_updated); -- Liquidity Pools - DEX pool information CREATE TABLE IF NOT EXISTS liquidity_pools ( id BIGSERIAL PRIMARY KEY, chain_id INTEGER NOT NULL, pool_address VARCHAR(42) NOT NULL, token0_address VARCHAR(42) NOT NULL, token1_address VARCHAR(42) NOT NULL, dex_type VARCHAR(20) NOT NULL CHECK (dex_type IN ('uniswap_v2', 'uniswap_v3', 'dodo', 'custom')), factory_address VARCHAR(42), router_address VARCHAR(42), reserve0 NUMERIC(78, 0) DEFAULT 0, reserve1 NUMERIC(78, 0) DEFAULT 0, reserve0_usd NUMERIC(30, 8) DEFAULT 0, reserve1_usd NUMERIC(30, 8) DEFAULT 0, total_liquidity_usd NUMERIC(30, 8) DEFAULT 0, volume_24h NUMERIC(30, 8) DEFAULT 0, fee_tier INTEGER, -- For UniswapV3 (500, 3000, 10000) created_at_block BIGINT, created_at_timestamp TIMESTAMP WITH TIME ZONE, last_updated TIMESTAMP WITH TIME ZONE DEFAULT NOW(), created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), UNIQUE(chain_id, pool_address) ) PARTITION BY LIST (chain_id); -- Create partitions for supported chains CREATE TABLE IF NOT EXISTS liquidity_pools_chain_138 PARTITION OF liquidity_pools FOR VALUES IN (138); CREATE TABLE IF NOT EXISTS liquidity_pools_chain_651940 PARTITION OF liquidity_pools FOR VALUES IN (651940); CREATE INDEX idx_liquidity_pools_chain_pool ON liquidity_pools(chain_id, pool_address); CREATE INDEX idx_liquidity_pools_token0 ON liquidity_pools(chain_id, token0_address); CREATE INDEX idx_liquidity_pools_token1 ON liquidity_pools(chain_id, token1_address); CREATE INDEX idx_liquidity_pools_dex_type ON liquidity_pools(chain_id, dex_type); CREATE INDEX idx_liquidity_pools_tvl ON liquidity_pools(total_liquidity_usd) WHERE total_liquidity_usd > 0; -- Pool Reserves History - Time-series snapshots of pool reserves CREATE TABLE IF NOT EXISTS pool_reserves_history ( id BIGSERIAL PRIMARY KEY, chain_id INTEGER NOT NULL, pool_address VARCHAR(42) NOT NULL, reserve0 NUMERIC(78, 0) NOT NULL, reserve1 NUMERIC(78, 0) NOT NULL, reserve0_usd NUMERIC(30, 8), reserve1_usd NUMERIC(30, 8), total_liquidity_usd NUMERIC(30, 8), block_number BIGINT NOT NULL, timestamp TIMESTAMP WITH TIME ZONE NOT NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ) PARTITION BY LIST (chain_id); -- Create partitions for supported chains CREATE TABLE IF NOT EXISTS pool_reserves_history_chain_138 PARTITION OF pool_reserves_history FOR VALUES IN (138); CREATE TABLE IF NOT EXISTS pool_reserves_history_chain_651940 PARTITION OF pool_reserves_history FOR VALUES IN (651940); -- Convert to hypertable for TimescaleDB time-series optimization SELECT create_hypertable('pool_reserves_history', 'timestamp', chunk_time_interval => INTERVAL '1 day', if_not_exists => TRUE ); CREATE INDEX idx_pool_reserves_history_pool_time ON pool_reserves_history(chain_id, pool_address, timestamp DESC); CREATE INDEX idx_pool_reserves_history_timestamp ON pool_reserves_history(timestamp DESC); -- Token OHLCV - Open, High, Low, Close, Volume data by interval CREATE TABLE IF NOT EXISTS token_ohlcv ( id BIGSERIAL PRIMARY KEY, chain_id INTEGER NOT NULL, token_address VARCHAR(42) NOT NULL, pool_address VARCHAR(42), -- Optional: specific pool, NULL = aggregated across all pools interval_type VARCHAR(10) NOT NULL CHECK (interval_type IN ('5m', '15m', '1h', '4h', '24h')), open_price NUMERIC(30, 8) NOT NULL, high_price NUMERIC(30, 8) NOT NULL, low_price NUMERIC(30, 8) NOT NULL, close_price NUMERIC(30, 8) NOT NULL, volume NUMERIC(30, 8) DEFAULT 0, volume_usd NUMERIC(30, 8) DEFAULT 0, timestamp TIMESTAMP WITH TIME ZONE NOT NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), UNIQUE(chain_id, token_address, pool_address, interval_type, timestamp) ) PARTITION BY LIST (chain_id); -- Create partitions for supported chains CREATE TABLE IF NOT EXISTS token_ohlcv_chain_138 PARTITION OF token_ohlcv FOR VALUES IN (138); CREATE TABLE IF NOT EXISTS token_ohlcv_chain_651940 PARTITION OF token_ohlcv FOR VALUES IN (651940); -- Convert to hypertable for TimescaleDB time-series optimization SELECT create_hypertable('token_ohlcv', 'timestamp', chunk_time_interval => INTERVAL '7 days', if_not_exists => TRUE ); CREATE INDEX idx_token_ohlcv_token_time ON token_ohlcv(chain_id, token_address, interval_type, timestamp DESC); CREATE INDEX idx_token_ohlcv_pool_time ON token_ohlcv(chain_id, pool_address, interval_type, timestamp DESC) WHERE pool_address IS NOT NULL; CREATE INDEX idx_token_ohlcv_timestamp ON token_ohlcv(timestamp DESC); -- External API Cache - Cached responses from external APIs CREATE TABLE IF NOT EXISTS external_api_cache ( id BIGSERIAL PRIMARY KEY, api_provider VARCHAR(50) NOT NULL CHECK (api_provider IN ('coingecko', 'coinmarketcap', 'dexscreener')), cache_key VARCHAR(255) NOT NULL, chain_id INTEGER, token_address VARCHAR(42), response_data JSONB NOT NULL, expires_at TIMESTAMP WITH TIME ZONE NOT NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), UNIQUE(api_provider, cache_key) ); CREATE INDEX idx_external_api_cache_provider_key ON external_api_cache(api_provider, cache_key); CREATE INDEX idx_external_api_cache_chain_token ON external_api_cache(chain_id, token_address) WHERE chain_id IS NOT NULL AND token_address IS NOT NULL; CREATE INDEX idx_external_api_cache_expires ON external_api_cache(expires_at); -- Token Signals - Trending and growth metrics CREATE TABLE IF NOT EXISTS token_signals ( id BIGSERIAL PRIMARY KEY, chain_id INTEGER NOT NULL, token_address VARCHAR(42) NOT NULL, tx_count_growth_24h NUMERIC(10, 4) DEFAULT 0, -- Percentage change unique_wallets_24h INTEGER DEFAULT 0, unique_wallets_growth_24h NUMERIC(10, 4) DEFAULT 0, swap_count_24h INTEGER DEFAULT 0, swap_count_growth_24h NUMERIC(10, 4) DEFAULT 0, new_lp_creations_24h INTEGER DEFAULT 0, attention_score NUMERIC(10, 4) DEFAULT 0, -- Composite score 0-100 trending_rank INTEGER, -- Rank among trending tokens timestamp TIMESTAMP WITH TIME ZONE NOT NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), UNIQUE(chain_id, token_address, timestamp) ) PARTITION BY LIST (chain_id); -- Create partitions for supported chains CREATE TABLE IF NOT EXISTS token_signals_chain_138 PARTITION OF token_signals FOR VALUES IN (138); CREATE TABLE IF NOT EXISTS token_signals_chain_651940 PARTITION OF token_signals FOR VALUES IN (651940); -- Convert to hypertable for TimescaleDB time-series optimization SELECT create_hypertable('token_signals', 'timestamp', chunk_time_interval => INTERVAL '1 day', if_not_exists => TRUE ); CREATE INDEX idx_token_signals_token_time ON token_signals(chain_id, token_address, timestamp DESC); CREATE INDEX idx_token_signals_attention ON token_signals(chain_id, attention_score DESC, timestamp DESC); CREATE INDEX idx_token_signals_trending ON token_signals(chain_id, trending_rank, timestamp DESC) WHERE trending_rank IS NOT NULL; -- Swap Events - Track individual swap events for volume calculation CREATE TABLE IF NOT EXISTS swap_events ( id BIGSERIAL PRIMARY KEY, chain_id INTEGER NOT NULL, pool_address VARCHAR(42) NOT NULL, transaction_hash VARCHAR(66) NOT NULL, block_number BIGINT NOT NULL, log_index INTEGER NOT NULL, token0_address VARCHAR(42) NOT NULL, token1_address VARCHAR(42) NOT NULL, amount0_in NUMERIC(78, 0) DEFAULT 0, amount1_in NUMERIC(78, 0) DEFAULT 0, amount0_out NUMERIC(78, 0) DEFAULT 0, amount1_out NUMERIC(78, 0) DEFAULT 0, amount_usd NUMERIC(30, 8), -- Calculated USD value sender VARCHAR(42), to_address VARCHAR(42), timestamp TIMESTAMP WITH TIME ZONE NOT NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), UNIQUE(chain_id, transaction_hash, log_index) ) PARTITION BY LIST (chain_id); -- Create partitions for supported chains CREATE TABLE IF NOT EXISTS swap_events_chain_138 PARTITION OF swap_events FOR VALUES IN (138); CREATE TABLE IF NOT EXISTS swap_events_chain_651940 PARTITION OF swap_events FOR VALUES IN (651940); -- Convert to hypertable for TimescaleDB time-series optimization SELECT create_hypertable('swap_events', 'timestamp', chunk_time_interval => INTERVAL '1 day', if_not_exists => TRUE ); CREATE INDEX idx_swap_events_pool_time ON swap_events(chain_id, pool_address, timestamp DESC); CREATE INDEX idx_swap_events_token0 ON swap_events(chain_id, token0_address, timestamp DESC); CREATE INDEX idx_swap_events_token1 ON swap_events(chain_id, token1_address, timestamp DESC); CREATE INDEX idx_swap_events_tx_hash ON swap_events(chain_id, transaction_hash); CREATE INDEX idx_swap_events_block ON swap_events(chain_id, block_number); -- Update triggers for last_updated CREATE TRIGGER update_token_market_data_updated_at BEFORE UPDATE ON token_market_data FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); CREATE TRIGGER update_liquidity_pools_updated_at BEFORE UPDATE ON liquidity_pools FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); -- Comments for documentation COMMENT ON TABLE token_market_data IS 'Aggregated market data per token including price, volume, market cap, and liquidity'; COMMENT ON TABLE liquidity_pools IS 'DEX liquidity pool information with reserves and TVL'; COMMENT ON TABLE pool_reserves_history IS 'Time-series history of pool reserve snapshots'; COMMENT ON TABLE token_ohlcv IS 'OHLCV (Open, High, Low, Close, Volume) data for token price charts'; COMMENT ON TABLE external_api_cache IS 'Cached responses from external APIs (CoinGecko, CMC, DexScreener)'; COMMENT ON TABLE token_signals IS 'Trending signals and growth metrics for tokens'; COMMENT ON TABLE swap_events IS 'Individual swap events from DEX pools for volume calculation';