feat: track gas price, avg block time, blocknumbers and blocks (#65)

* feat: block stats
* load last 10 blocks
This commit is contained in:
Richard Ramos 2020-02-10 08:43:46 -04:00 committed by GitHub
parent 4232fc3231
commit 5ee1234f0f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 113 additions and 13 deletions

View File

@ -1,4 +1,4 @@
import { ReplaySubject } from 'rxjs';
import { ReplaySubject, BehaviorSubject } from 'rxjs';
import { distinctUntilChanged, map } from 'rxjs/operators';
import equal from 'fast-deep-equal';
import Database from './database/database.js';
@ -26,18 +26,31 @@ export default class Subspace {
this.options.refreshLastNBlocks = options.refreshLastNBlocks || 12;
this.options.callInterval = options.callInterval || 0;
this.options.dbFilename = options.dbFilename || 'subspace.db';
this.latestBlockNumber = undefined;
this.disableDatabase = options.disableDatabase;
this.networkId = undefined;
this.isWebsocketProvider = options.disableSubscriptions ? false : !!provider.on;
// TODO: part of manager
this.latestBlockNumber = undefined;
this.latestGasPrice = undefined;
this.latestBlock = undefined;
this.latest10Blocks = [];
// TODO: part of manager
this.blockNumberObservable = null;
this.gasPriceObservable = null;
this.blockObservable = null;
this.blockTimeObservable = null;
this.newBlocksSubscription = null;
this.intervalTracker = null;
this.callables = [];
}
init() {
return new Promise((resolve, reject) => {
return new Promise(async (resolve, reject) => {
if (this.disableDatabase === true) {
this._db = new NullDatabase("", this.events);
} else {
@ -50,19 +63,35 @@ export default class Subspace {
this.networkId = netId;
});
this.web3.getBlock('latest').then(block => {
this.latestBlockNumber = block.number;
if(this.isWebsocketProvider){
this._initNewBlocksSubscription();
} else {
this.options.callInterval = this.options.callInterval || 1000;
this._initCallInterval();
const block = await this.web3.getBlock('latest');
const gasPrice = await this.web3.getGasPrice();
// Preload <= 10 blocks to calculate avg block time
if(block.number !== 0){
const minBlock = Math.max(0, block.number - 9);
for(let i = minBlock; i < block.number; i++){
this.latest10Blocks.push(this.web3.getBlock(i));
}
resolve();
})
})
this.latest10Blocks = await Promise.all(this.latest10Blocks);
}
// TODO: part of manager
this.latestBlockNumber = block.number;
this.latestGasPrice = gasPrice;
this.latestBlock = block;
this.latest10Blocks.push(block);
if(this.isWebsocketProvider){
this._initNewBlocksSubscription();
} else {
this.options.callInterval = this.options.callInterval || 1000;
this._initCallInterval();
}
resolve();
});
}
contract(contractInstance) {
@ -223,6 +252,61 @@ export default class Subspace {
return returnSub;
}
_addDistinctCallable(trackAttribute, cbBuilder, subject, subjectArg = undefined) {
if(this[trackAttribute]) return this[trackAttribute].pipe(distinctUntilChanged((a, b) => equal(a, b)));
this[trackAttribute] = new subject(subjectArg);
const cb = cbBuilder(this[trackAttribute]);
cb();
this.callables.push(cb);
return this[trackAttribute].pipe(distinctUntilChanged((a, b) => equal(a, b)));
}
trackBlock() {
const blockCB = (subject) => () => {
this.web3.getBlock('latest').then(block => {
if(this.latest10Blocks[this.latest10Blocks.length - 1].number === block.number) return;
this.latest10Blocks.push(block);
if(this.latest10Blocks.length > 10){
this.latest10Blocks.shift();
}
subject.next(block);
}).catch(error => subject.error(error));
};
return this._addDistinctCallable('blockObservable', blockCB, BehaviorSubject, this.latestBlock);
}
trackBlockNumber() {
const blockNumberCB = (subject) => () => {
this.web3.getBlockNumber().then(blockNumber => subject.next(blockNumber)).catch(error => subject.error(error));
};
return this._addDistinctCallable('blockNumberObservable', blockNumberCB, BehaviorSubject, this.latestBlockNumber);
}
trackGasPrice() {
const gasPriceCB = (subject) => () => {
this.web3.getGasPrice().then(gasPrice => subject.next(gasPrice)).catch(error => subject.error(error));
};
return this._addDistinctCallable('gasPriceObservable', gasPriceCB, BehaviorSubject, this.latestGasPrice);
}
trackAverageBlocktime() {
this.trackBlock()
const avgTimeCB = (subject) => () => {
const times = [];
for (let i = 1; i < this.latest10Blocks.length; i++) {
let time = this.latest10Blocks[i].timestamp - this.latest10Blocks[i - 1].timestamp;
times.push(time)
}
const average = times.length ? Math.round(times.reduce((a, b) => a + b) / times.length) * 1000 : 0;
subject.next(average);
};
return this._addDistinctCallable('blockTimeObservable', avgTimeCB, BehaviorSubject, 123456);
}
trackBalance(address, erc20Address) {
const sub = new ReplaySubject();

16
test/test8.js Normal file
View File

@ -0,0 +1,16 @@
const Web3Eth = require('web3-eth');
const Subspace = require('../dist/index.js').default;
let eth = new Web3Eth("https://mainnet.infura.io/v3/562ba55287324547adbdd59b1dabc869");
async function run() {
const subspace = new Subspace(eth.currentProvider);
await subspace.init()
subspace.trackBlockNumber().subscribe((v) => console.log("Blocknumber", v));
subspace.trackGasPrice().subscribe((v) => console.log("GasPrice", v));
subspace.trackBlock().subscribe((v) => console.log("Block.difficulty", v.difficulty));
subspace.trackAverageBlocktime().subscribe((v) => console.log("Blocktime", v));
}
run()