פיתוח מערכת אינדוקס אירועי בלוקצ'יין
עם שאילתות טיפוסיות כמו "הצג את כל העסקאות של המשתמש ב-30 הימים האחרונים" או "נפח מסחר ב-DEX", block range too large איטי. בצומת ארכיון מבלוק 0, השאילתה אורכת דקות; ב-RPC ציבורי, היא נגמרת בפסק זמן או מחזירה eth_getLogs. אנו מתכננים צינורות אינדוקס אירועים עמידים לתקלות עם סמנטיקה של בדיוק-פעם אחת וניטור בזמן אמת. חיסכון במשאבים: עד 80% זמן בשאילתות חיפוש, הפחתה של פי 10 בעלויות RPC. מקרה אמיתי: לקוח חסך עשרות אלפי דולרים בשנה על תשתית. לצוות שלנו יש ניסיון של 8+ שנים בפיתוח בלוקצ'יין ולמעלה מ-50 פרויקטים מיושמים. קבלו ייעוץ — נעריך את הפרויקט שלכם תוך יום אחד.
גישה לאחזור אירועים
ישנן שלוש דרכים לאחזר אירועים מצומת, לכל אחת יש פשרות:
| גישה | אחזור (Latency) | מורכבות | אמינות | תפוקה משוערת |
|---|---|---|---|---|
פולינג (lastIndexedBlock) |
בינוני (1-5 שניות) | נמוכה | גבוהה (בדיוק-פעם אחת) | עד 1000 בלוקים לבקשה |
| מנויי WebSocket | נמוך (< 1 שנייה) | בינונית | בינוני (תלוי ברשת) | מסירה מיידית |
| Firehose / StreamingFast | נמוך | גבוהה | גבוהה מאוד | זרם בלוקים בלתי מוגבל |
פולינג הוא הבחירה הנפוצה ביותר: עובד שואל את הצומת מעת לעת עבור טווח בלוקים, ומאחסן -- Таблица состояния индексера CREATE TABLE indexer_blocks ( block_number BIGINT PRIMARY KEY, block_hash VARCHAR(66) NOT NULL, indexed_at TIMESTAMPTZ DEFAULT NOW() ); -- Событие с привязкой к блоку для rollback CREATE TABLE indexed_events ( id BIGSERIAL PRIMARY KEY, block_number BIGINT NOT NULL REFERENCES indexer_blocks(block_number), log_index INT NOT NULL, tx_hash VARCHAR(66) NOT NULL, contract_addr VARCHAR(42) NOT NULL, event_name VARCHAR(100) NOT NULL, decoded_data JSONB NOT NULL, UNIQUE(tx_hash, log_index) ); . בעת הפעלה מחדש, הוא ממשיך מהבלוק האחרון שעובד. WebSocket מציע אחזור נמוך אך דורש חיבור מחדש אוטומטי וסנכרון של בלוקים שהוחמצו. Firehose הוא פתרון ארגוני למערכות בעלות תפוקה גבוהה.
טיפול בארגון מחדש של שרשרת (Reorgs)
Reorgs הם מקור הבאגים העיקרי. ב-Ethereum PoS, סופיות מתרחשת לאחר שתי תקופות (~12 דקות). ב-BSC או Polygon, reorgs של 3-5 בלוקים הם נפוצים.
אסטרטגיה: אינדוקס עם השהיה של N בלוקים מאושרים (13 עבור Ethereum), אחסון ה-hash של כל בלוק שעבר אינדוקס. אם מתגלה אי-התאמה ב-hash, חזור אחורה למצב התואם האחרון ובצע אינדוקס מחדש.
-- Таблица состояния индексера CREATE TABLE indexer_blocks ( block_number BIGINT PRIMARY KEY, block_hash VARCHAR(66) NOT NULL, indexed_at TIMESTAMPTZ DEFAULT NOW() ); -- Событие с привязкой к блоку для rollback CREATE TABLE indexed_events ( id BIGSERIAL PRIMARY KEY, block_number BIGINT NOT NULL REFERENCES indexer_blocks(block_number), log_index INT NOT NULL, tx_hash VARCHAR(66) NOT NULL, contract_addr VARCHAR(42) NOT NULL, event_name VARCHAR(100) NOT NULL, decoded_data JSONB NOT NULL, UNIQUE(tx_hash, log_index) ); כאשר מתגלה reorg, מחק רשומות עם block_number >= reorg_depth ובצע אינדוקס מחדש מנקודה זו.
פרט טכני: עומקי אישור לרשתות שונות
Ethereum: 13 בלוקים (המלצה). Polygon: 25 בלוקים. BSC: 15 בלוקים. Solana: 1 slot (~400 אלפיות השנייה). ניתן לשנות את הערך באמצעות משתנה הסביבה CONFIRMATIONS.
ניואנסים בפענוח אירועים
פענוח ABI הוא טריוויאלי עם viem או ethers.js, אך ישנם מלכודות. לפי תיעוד Solidity על אירועים, מורכבויות נוצרות עם:
- פרמטרים indexed לעומת non-indexed: פרמטרים indexed נכנסים ל-topics, non-indexed לתוך data. אירוע עם 3 פרמטרים indexed תופס 4 topics. פענוח topics עבור structs הוא בלתי אפשרי — הנתונים עוברים hashing.
- אירועים אנונימיים: אירועים ללא topic — נדירים, דורשים גישה לא סטנדרטית.
- חוזי Proxy (EIP-1967): אירועים נפלטים מכתובת ה-proxy, אך ה-ABI הוא מה-implementation. יש לפתור את ה-implementation דרך storage slot
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc. פרטים נוספים ב-EIP-1967.
דוגמת פענוח ב-TypeScript:
import { decodeEventLog } from 'viem' function parseSwapEvent(log: Log, abi: Abi): SwapEvent { const decoded = decodeEventLog({ abi, eventName: 'Swap', data: log.data, topics: log.topics, }) return { blockNumber: log.blockNumber, txHash: log.transactionHash, sender: decoded.args.sender, recipient: decoded.args.recipient, amount0: decoded.args.amount0, amount1: decoded.args.amount1, sqrtPriceX96: decoded.args.sqrtPriceX96, liquidity: decoded.args.liquidity, tick: decoded.args.tick, } } בחירת מסד נתונים לאחסון אירועים
| קריטריון | PostgreSQL (חלוקה) | TimescaleDB |
|---|---|---|
| התקנה | יצירת חלוקות ידנית | Hypertables אוטומטיים |
| דחיסה | אין מובנית | כן, לנתונים ישנים (הפחתה פי 10) |
| אגרגציות | תצוגות חומריות (Materialized views) | אגרגטים רציפים (עדכון אוטומטי) |
| ביצועים | מצוינים עם חלוקה טובה | פי 5 מהיר יותר לשאילתות סדרות זמן |
אנו משתמשים ב-PostgreSQL עם חלוקה לפי import { decodeEventLog } from 'viem' function parseSwapEvent(log: Log, abi: Abi): SwapEvent { const decoded = decodeEventLog({ abi, eventName: 'Swap', data: log.data, topics: log.topics, }) return { blockNumber: log.blockNumber, txHash: log.transactionHash, sender: decoded.args.sender, recipient: decoded.args.recipient, amount0: decoded.args.amount0, amount1: decoded.args.amount1, sqrtPriceX96: decoded.args.sqrtPriceX96, liquidity: decoded.args.liquidity, tick: decoded.args.tick, } } או TimescaleDB עבור חוזים בתדירות גבוהה. דוגמת חלוקה:
CREATE TABLE swap_events ( block_number BIGINT NOT NULL, event_timestamp TIMESTAMPTZ NOT NULL, event_data JSONB ) PARTITION BY RANGE (block_number); CREATE TABLE swap_events_0_5m PARTITION OF swap_events FOR VALUES FROM (0) TO (5000000); -- аналогично для следующих диапазонов TimescaleDB מספק אגרגטים רציפים למדדים: נפח מסחר שעתי, מספר עסקאות — ללא צורך במשימות רקע.
ניטור והתראות
כלול מדדים: block_number, CREATE TABLE swap_events ( block_number BIGINT NOT NULL, event_timestamp TIMESTAMPTZ NOT NULL, event_data JSONB ) PARTITION BY RANGE (block_number); CREATE TABLE swap_events_0_5m PARTITION OF swap_events FOR VALUES FROM (0) TO (5000000); -- аналогично для следующих диапазонов , indexer_lag_blocks. התריע כאשר הפיגור עולה על 50 בלוקים. הבריאות נבדקת דרך נקודת קצה (503 בעת חריגת סף). מחסנית: Go/Rust לעובד, PostgreSQL/TimescaleDB, Redis למצב, Grafana + Prometheus.
התהליך שלנו: איך אנחנו עובדים
- ניתוח: לימוד חוזים חכמים, זיהוי כל האירועים, קביעת תדירות הפליטה. זמן ממוצע: 1-2 ימים.
- עיצוב: בחירת גישה (פולינג לפשטות, firehose לעומס גבוה), עיצוב סכמת DB ו-API. יצירת דיאגרמת זרימת נתונים.
- יישום: כתיבת עובד ב-Go/Rust, הגדרת פענוח, טיפול ב-reorg, חלוקה. ציר זמן טיפוסי: 5 עד 15 ימים.
- בדיקות: סימולציה של פיגור, reorgs, עומס גבוה. שימוש ב-testnets (Sepolia, Holesky).
- פריסה: מיכלי Docker עם אורקסטרציה (Kubernetes או Docker Compose). אינדוקס ראשוני של 1000 הבלוקים האחרונים.
- ניטור ותמיכה: לוחות Grafana, התראות, דוחות חודשיים. בתוך החודש הראשון — תיקונים מהירים.
צירי זמן משוערים: אינדוקס בסיסי של חוזה אחד — מ-5 ימים; צינור מורכב עם חלוקה וניטור — משבועיים. העלות מחושבת באופן אישי — צרו קשר להערכה.
מה כלול
- עיצוב ארכיטקטורה: בחירת גישה, סכמת DB, מפרט API.
- יישום צינור: עובד, פענוח, טיפול ב-reorg, חלוקה.
- GraphQL API מבוסס מנויים (Hasura או resolver מותאם).
- ניטור והתראות: לוחות, מדדי פיגור.
- תיעוד: דיאגרמת נתונים, נקודות הרחבה, runbook.
- הכשרת צוות הלקוח.
- חודש תמיכה לאחר ההשקה.
הזמינו פיתוח מערכת אינדוקס סוהר. קבלו ייעוץ — נעריך את הפרויקט שלכם תוך יום אחד ונציע את הפתרון האופטימלי. צרו קשר.







