aboutsummaryrefslogblamecommitdiffstats
path: root/packages/0x.js/src/order_watcher/expiration_watcher.ts
blob: 9a1bb1ca4ad30a77f1c938ebacc4a15a397725ad (plain) (tree)
1
2
3
4
5
6
7
8
9
                                       
                                

                            
                             


                                                      
 
                                       


                                                         

                                                                                                                        

                                
                                                        
                                                              
                                                      
                                       
                                                                     


                                                                     
                                                               
                                                                              
                                                                                               
                                                                                           
                                                                                                 
                                                                  
     
                                                                   
                                                                             

                                                                    
                                                                                                 
                                                                                                 


                                



                                                                                                  
                                                              
     

                                                                                    
                                                           

                                                 
                                                           
                                          
     
                                                                             
                                                                         
                      

                                                                                


                                                                                 
                                                                                          
                                                                     
              

                                                                                                       

                      

                                                                     
                                              
                                


         
import {BigNumber} from 'bignumber.js';
import {RBTree} from 'bintrees';
import * as _ from 'lodash';

import {ZeroEx} from '../0x';
import {SignedOrder, ZeroExError} from '../types';
import {intervalUtils} from '../utils/interval_utils';
import {utils} from '../utils/utils';

const DEFAULT_EXPIRATION_MARGIN_MS = 0;
const DEFAULT_ORDER_EXPIRATION_CHECKING_INTERVAL_MS = 50;

/**
 * This class includes the functionality to detect expired orders.
 * It stores them in a min heap by expiration time and checks for expired ones every `orderExpirationCheckingIntervalMs`
 */
export class ExpirationWatcher {
    private orderHashByExpirationRBTree: RBTree<string>;
    private expiration: {[orderHash: string]: BigNumber} = {};
    private orderExpirationCheckingIntervalMs: number;
    private expirationMarginMs: number;
    private orderExpirationCheckingIntervalIdIfExists?: NodeJS.Timer;
    constructor(expirationMarginIfExistsMs?: number,
                orderExpirationCheckingIntervalIfExistsMs?: number) {
        this.expirationMarginMs = expirationMarginIfExistsMs ||
                                  DEFAULT_EXPIRATION_MARGIN_MS;
        this.orderExpirationCheckingIntervalMs = expirationMarginIfExistsMs ||
                                                 DEFAULT_ORDER_EXPIRATION_CHECKING_INTERVAL_MS;
        const scoreFunction = (orderHash: string) => this.expiration[orderHash].toNumber();
        const comparator = (lhs: string, rhs: string) => scoreFunction(lhs) - scoreFunction(rhs);
        this.orderHashByExpirationRBTree = new RBTree(comparator);
    }
    public subscribe(callback: (orderHash: string) => void): void {
        if (!_.isUndefined(this.orderExpirationCheckingIntervalIdIfExists)) {
            throw new Error(ZeroExError.SubscriptionAlreadyPresent);
        }
        this.orderExpirationCheckingIntervalIdIfExists = intervalUtils.setAsyncExcludingInterval(
            this.pruneExpiredOrders.bind(this, callback), this.orderExpirationCheckingIntervalMs,
        );
    }
    public unsubscribe(): void {
        if (_.isUndefined(this.orderExpirationCheckingIntervalIdIfExists)) {
            throw new Error(ZeroExError.SubscriptionNotFound);
        }
        intervalUtils.clearAsyncExcludingInterval(this.orderExpirationCheckingIntervalIdIfExists);
        delete this.orderExpirationCheckingIntervalIdIfExists;
    }
    public addOrder(orderHash: string, expirationUnixTimestampMs: BigNumber): void {
        this.expiration[orderHash] = expirationUnixTimestampMs;
        this.orderHashByExpirationRBTree.insert(orderHash);
    }
    public removeOrder(orderHash: string): void {
        this.orderHashByExpirationRBTree.remove(orderHash);
        delete this.expiration[orderHash];
    }
    private pruneExpiredOrders(callback: (orderHash: string) => void): void {
        const currentUnixTimestampMs = utils.getCurrentUnixTimestampMs();
        while (true) {
            const hasTrakedOrders = this.orderHashByExpirationRBTree.size === 0;
            if (hasTrakedOrders) {
                break;
            }
            const nextOrderHashToExpire = this.orderHashByExpirationRBTree.min();
            const hasNoExpiredOrders = this.expiration[nextOrderHashToExpire].greaterThan(
                currentUnixTimestampMs.plus(this.expirationMarginMs),
            );
            const isSubscriptionActive = _.isUndefined(this.orderExpirationCheckingIntervalIdIfExists);
            if (hasNoExpiredOrders || isSubscriptionActive) {
                break;
            }
            const orderHash = this.orderHashByExpirationRBTree.min();
            this.orderHashByExpirationRBTree.remove(orderHash);
            delete this.expiration[orderHash];
            callback(orderHash);
        }
    }
}