Розробка трекера стоимості газу (gas tracker)
Gas tracker — це не просто "показуємо eth_gasPrice". Корисний інструмент показує історичну динаміку, передбачає оптимальний час транзакції, розділяє EIP-1559 компоненти та дає оцінки по типам транзакцій. Давайте розберемо що всередині у нормального gas tracker-а.
EIP-1559: Як працює pricing газу на Ethereum
Після EIP-1559 (Лондонський хардфорк, серпень 2021) ціна газу складається з двох частин:
baseFeePerGas — базова комісія, спалюється. Визначається протоколом: якщо блок > 50% заповнений, base fee зростає на 12.5%, якщо < 50% — падає. Передбачуваний на 1-2 блоки вперед.
maxPriorityFeePerGas (tip) — чаївники майнеру/валідатору. Ринкова частина — конкуруєте за включення в блок.
Реальна стоимість транзакції: min(maxFeePerGas, baseFeePerGas + priorityFee) * gasUsed.
Для gas tracker це важливо: відслідковуйте обидва компоненти окремо, не просто сумарну ціну.
Архітектура: Сбір та зберігання даних
Джерела даних
eth_feeHistory — основний метод для історичних даних. Повертає baseFee, gasUsedRatio та percentile-статистику priorityFee за вказаний діапазон блоків:
import { createPublicClient, http } from 'viem'
import { mainnet } from 'viem/chains'
const client = createPublicClient({ chain: mainnet, transport: http(RPC_URL) })
// Отримуємо дані за останні 100 блоків
// percentiles: [25, 50, 75] — slow/standard/fast
const feeHistory = await client.getFeeHistory({
blockCount: 100,
rewardPercentiles: [25, 50, 75, 95],
})
// feeHistory.baseFeePerGas: BigInt[] (на 1 більше — включає наступний блок)
// feeHistory.reward: BigInt[][] (матриця: блок x percentile)
// feeHistory.gasUsedRatio: number[] (0..1)
eth_gasPrice — актуальна ціна на момент запиту. Legacy метод, для EIP-1559 мереж повертає baseFee + мінімальний tip.
Mempool дані — pending транзакції. eth_getBlockByNumber("pending") або підписка на pending txs через WebSocket. Потрібно для real-time аналізу конкуренції.
Схема зберігання
TimescaleDB (PostgreSQL extension) — оптимально для time-series газових даних:
CREATE TABLE gas_stats (
time TIMESTAMPTZ NOT NULL,
block_number BIGINT NOT NULL,
base_fee_gwei NUMERIC(20, 9) NOT NULL,
tip_slow NUMERIC(20, 9), -- 25th percentile
tip_standard NUMERIC(20, 9), -- 50th percentile
tip_fast NUMERIC(20, 9), -- 75th percentile
tip_instant NUMERIC(20, 9), -- 95th percentile
gas_used_ratio NUMERIC(5, 4), -- 0..1, заповнення блоку
network VARCHAR(50) NOT NULL DEFAULT 'ethereum'
);
SELECT create_hypertable('gas_stats', 'time');
-- Агрегати по годинам для історичних графіків
SELECT add_continuous_aggregate_policy('gas_stats_hourly', ...);
Retention policy: детальні дані по блоках — 7 днів, годинні агрегати — 1 рік, денні агрегати — невизначено.
Колектор даних
Колектор запускається у фоні та пише дані по кожному новому блоку:
async function collectGasData() {
const client = createWsClient(WS_RPC_URL)
// Підписка на нові блоки через WebSocket
client.watchBlocks({
onBlock: async (block) => {
const baseFee = block.baseFeePerGas
? Number(block.baseFeePerGas) / 1e9
: null
// Отримуємо percentile статистику за цей блок
const history = await client.getFeeHistory({
blockCount: 1,
blockNumber: block.number,
rewardPercentiles: [25, 50, 75, 95],
})
const [slow, standard, fast, instant] = history.reward[0].map(
(r) => Number(r) / 1e9
)
await db.query(
`INSERT INTO gas_stats (time, block_number, base_fee_gwei, tip_slow, tip_standard, tip_fast, tip_instant, gas_used_ratio)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`,
[new Date(Number(block.timestamp) * 1000), block.number, baseFee, slow, standard, fast, instant, history.gasUsedRatio[0]]
)
},
onError: (err) => handleCollectorError(err),
})
}
Алгоритм передбачення газу
Простий вивід поточного baseFee недостатній. Потрібна оцінка: "скільки поставити щоб попасти в наступний блок з вірогідністю X%?"
Наступний baseFee передбачувано точно:
function predictNextBaseFee(currentBaseFee: bigint, gasUsedRatio: number): bigint {
// EIP-1559: baseFee змінюється пропорційно відхиленню від 50% цілі
const targetRatio = 0.5
const maxChangeDenominator = 8n // 12.5% максимальна зміна
const delta = gasUsedRatio - targetRatio
const change = (currentBaseFee * BigInt(Math.round(delta * 1000))) / (maxChangeDenominator * 1000n)
return currentBaseFee + change
}
Priority fee — ринкова, передбачити складніше. Використовуємо EMA (exponential moving average) останніх N блоків для кожного percentile:
def calculate_priority_fee_estimate(
recent_tips: list[float], # останні 20 блоків, 50th percentile
alpha: float = 0.3, # вага нових даних
) -> float:
ema = recent_tips[0]
for tip in recent_tips[1:]:
ema = alpha * tip + (1 - alpha) * ema
return ema
Паттерни по часу доби. Газ значно дешевше в UTC 02:00-08:00 (ніч Європи та ранок Азії до відкриття ринку). Для несріних транзакцій — показуємо "оптимальне вікно":
-- Середній газ за годину UTC за останні 30 днів
SELECT
EXTRACT(HOUR FROM time) AS hour_utc,
AVG(base_fee_gwei + tip_standard) AS avg_total_fee
FROM gas_stats
WHERE time > NOW() - INTERVAL '30 days'
GROUP BY hour_utc
ORDER BY avg_total_fee ASC;
API та фронтенд
REST endpoints для інтеграції:
GET /api/gas/current — поточні рекомендації {slow, standard, fast, instant}
GET /api/gas/history?period=24h — історія з агрегацією
GET /api/gas/predict?blocks=5 — прогноз на N блоків
GET /api/gas/networks — дані по кількох мережах
Для реального часу — WebSocket subscription: клієнт підписується та отримує оновлення на кожний новий блок без polling.
Мультичейн поддержка — окремий колектор на кожну мережу. L2s (Arbitrum, Optimism, Base) мають двохкомпонентний газ: L2 execution fee + L1 data fee. Для Optimism L1 data fee рахується через precompile GasPriceOracle (0x420000000000000000000000000000000000000F).
Технологічний стек
| Компонент | Вибір |
|---|---|
| Колектор | TypeScript + viem |
| База даних | TimescaleDB (PostgreSQL) |
| API | Fastify або Express |
| Кеш | Redis (поточні рекомендації) |
| Фронтенд | React + Recharts/Highcharts |
| Деплой | Docker Compose |
Розробка базової версії (Ethereum mainnet, історія + поточні рекомендації) — 5-8 днів. Мультичейн з передбаченнями та детальною аналітикою — 2-3 тижні.







