blob: 7d6f8df65aca80c357fba3bcf39b58096a6e54ee (
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
62
63
64
65
66
67
68
69
70
71
72
73
|
import * as _ from 'lodash';
import {BigNumber} from 'bignumber.js';
import {RBTree} from 'bintrees';
import {utils} from '../utils/utils';
import {intervalUtils} from '../utils/interval_utils';
import {SignedOrder, ZeroExError} from '../types';
import {ZeroEx} from '../0x';
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 orderHashRBTreeByExpiration: RBTree<string>;
private expiration: {[orderHash: string]: BigNumber} = {};
private callbackIfExists?: (orderHash: string) => void;
private orderExpirationCheckingIntervalMs: number;
private expirationMarginMs: number;
private orderExpirationCheckingIntervalIdIfExists?: NodeJS.Timer;
constructor(expirationMarginIfExistsMs?: number,
orderExpirationCheckingIntervalIfExistsMs?: number) {
this.expirationMarginMs = expirationMarginIfExistsMs ||
DEFAULT_ORDER_EXPIRATION_CHECKING_INTERVAL_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.orderHashRBTreeByExpiration = new RBTree(comparator);
}
public subscribe(callback: (orderHash: string) => void): void {
if (!_.isUndefined(this.callbackIfExists)) {
throw new Error(ZeroExError.SubscriptionAlreadyPresent);
}
this.callbackIfExists = callback;
this.orderExpirationCheckingIntervalIdIfExists = intervalUtils.setAsyncExcludingInterval(
this.pruneExpiredOrders.bind(this), this.orderExpirationCheckingIntervalMs,
);
}
public unsubscribe(): void {
if (_.isUndefined(this.orderExpirationCheckingIntervalIdIfExists)) {
throw new Error(ZeroExError.SubscriptionNotFound);
}
intervalUtils.clearAsyncExcludingInterval(this.orderExpirationCheckingIntervalIdIfExists);
delete this.callbackIfExists;
delete this.orderExpirationCheckingIntervalIdIfExists;
}
public addOrder(orderHash: string, expirationUnixTimestampMs: BigNumber): void {
this.expiration[orderHash] = expirationUnixTimestampMs;
this.orderHashRBTreeByExpiration.insert(orderHash);
}
public removeOrder(orderHash: string): void {
this.orderHashRBTreeByExpiration.remove(orderHash);
delete this.expiration[orderHash];
}
private pruneExpiredOrders(): void {
const currentUnixTimestampMs = utils.getCurrentUnixTimestampMs();
while (
this.orderHashRBTreeByExpiration.size !== 0 &&
this.expiration[this.orderHashRBTreeByExpiration.min()].lessThan(
currentUnixTimestampMs.plus(this.expirationMarginMs),
) &&
!_.isUndefined(this.callbackIfExists)
) {
const orderHash = this.orderHashRBTreeByExpiration.min();
this.orderHashRBTreeByExpiration.remove(orderHash);
delete this.expiration[orderHash];
this.callbackIfExists(orderHash);
}
}
}
|