aboutsummaryrefslogtreecommitdiffstats
path: root/src/stores/block_store.ts
blob: d1f6c329a2d9940be760b83cad55859a7c0070d7 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
import * as _ from 'lodash';
import * as Web3 from 'web3';
import {BigNumber} from 'bignumber.js';
import {BlockParamLiteral, InternalZeroExError, ZeroExError} from '../types';
import {Web3Wrapper} from '../web3_wrapper';
import {intervalUtils} from '../utils/interval_utils';

const DEFAULT_BLOCK_POLLING_INTERVAL_MS = 500;

/**
 * Store for a current latest block number
 */
export class BlockStore {
    private web3Wrapper?: Web3Wrapper;
    private latestBlockNumber?: number;
    private intervalId?: NodeJS.Timer;
    private blockPollingIntervalMs: number;
    constructor(web3Wrapper?: Web3Wrapper, blockPollingIntervalMs?: number) {
        this.web3Wrapper = web3Wrapper;
        this.blockPollingIntervalMs = blockPollingIntervalMs || DEFAULT_BLOCK_POLLING_INTERVAL_MS;
    }
    public getBlockNumberWithNConfirmations(numConfirmations: number): Web3.BlockParam {
        let blockNumber;
        if (numConfirmations === 0) {
            blockNumber = BlockParamLiteral.Pending;
        } else if (numConfirmations === 1) {
            // HACK: We special-case `numConfirmations === 1` so that we can use this block store without actually
            // setting `latestBlockNumber` when block number is latest (in order validation) whhich allows us to omit
            // an async call in a constructor of `ExchangeTransferSimulator`
            blockNumber = BlockParamLiteral.Latest;
        } else {
            if (_.isUndefined(this.latestBlockNumber)) {
                throw new Error(InternalZeroExError.LatestBlockNumberNotSet);
            }
            // Latest block already has 1 confirmation
            blockNumber = this.latestBlockNumber - numConfirmations + 1;
        }
        return blockNumber;
    }
    public async startAsync(): Promise<void> {
        await this.updateLatestBlockAsync();
        this.intervalId = intervalUtils.setAsyncExcludingInterval(
            this.updateLatestBlockAsync.bind(this), this.blockPollingIntervalMs,
        );
    }
    public stop(): void {
        if (!_.isUndefined(this.intervalId)) {
            intervalUtils.clearAsyncExcludingInterval(this.intervalId);
        }
    }
    private async updateLatestBlockAsync(): Promise<void> {
        if (_.isUndefined(this.web3Wrapper)) {
            throw new Error(InternalZeroExError.Web3WrapperRequiredToStartBlockStore);
        }
        const block = await this.web3Wrapper.getBlockAsync(BlockParamLiteral.Latest);
        if (_.isNull(block.number)) {
            throw new Error(ZeroExError.FailedToFetchLatestBlock);
        }
        this.latestBlockNumber = block.number;
    }
}