import 'dotenv/config.js'; import { WebSocketProvider, formatUnits } from 'ethers'; import { sortBy } from 'lodash'; import { Observable, throwError } from 'rxjs'; import { catchError, map, scan, retry } from 'rxjs/operators'; import { GasPrices } from '../types/gasPrices'; const rpcUrl = process.env.RPC_URL || "wss://ropsten.infura.io/ws/v3/YOUR_INFURA_PROJECT_ID"; const provider = new WebSocketProvider(rpcUrl); const blockGasPricesObservable = new Observable((observer) => { provider.on('block', async (blockNumber) => { try { const block = await provider.getBlock(blockNumber, true); if (!block) throw new Error(`Error fetching block! ${blockNumber}`); const gasPrices = sortBy(block.prefetchedTransactions, ['gasPrice']).map(x => x.gasPrice); const fast = Number(formatUnits(gasPrices[Math.floor(gasPrices.length * 0.9)], "gwei")); const average = Number(formatUnits(gasPrices[Math.floor(gasPrices.length / 2)], "gwei")); const slow = Number(formatUnits(gasPrices[Math.floor(gasPrices.length * 0.05)], "gwei")); // Log averages every 10 blocks if (blockNumber % 10 == 0) console.log(`Found new block data for ${blockNumber}! Gas price spreads: ⚡ ${fast.toFixed(2)} ⦚⦚ 🚶 ${average.toFixed(2)} ⦚⦚ 🐢 ${slow.toFixed(2)} Gwei`) observer.next({ fast, average, slow } as GasPrices); } catch (error) { observer.error(`Error fetching block! ${error}`); } }); }); const averageGasPricesObservable = blockGasPricesObservable.pipe( scan((acc, curr) => [...acc.slice(-19), curr], [] as GasPrices[]), map((blocks) => { const { fast, average, slow } = blocks .reduce((sum, block) => ({ // Find sums fast: sum.fast + block.fast, average: sum.average + block.average, slow: sum.slow + block.slow }), { fast: 0, average: 0, slow: 0 } as GasPrices); return { // Then average them fast: Math.round(fast / blocks.length), average: Math.round(average / blocks.length), slow: Math.round(slow / blocks.length), } as GasPrices; }), catchError(err => throwError(() => new Error(err))), retry(2) ); export { averageGasPricesObservable };