@maxosllc/smart-order-router
Version:
BlockDAG Smart Order Router
114 lines • 12 kB
JavaScript
import { getAddressLowerCase, log, metric, MetricLoggerUnit } from '../util';
import { DEFAULT_TOKEN_FEE_RESULT, } from './token-fee-fetcher';
import { DEFAULT_ALLOWLIST, TokenValidationResult, } from './token-validator-provider';
export const DEFAULT_TOKEN_PROPERTIES_RESULT = {
tokenFeeResult: DEFAULT_TOKEN_FEE_RESULT,
};
export const POSITIVE_CACHE_ENTRY_TTL = 1200; // 20 minutes in seconds
export const NEGATIVE_CACHE_ENTRY_TTL = 1200; // 20 minutes in seconds
export class TokenPropertiesProvider {
constructor(chainId, tokenPropertiesCache, tokenFeeFetcher, allowList = DEFAULT_ALLOWLIST, positiveCacheEntryTTL = POSITIVE_CACHE_ENTRY_TTL, negativeCacheEntryTTL = NEGATIVE_CACHE_ENTRY_TTL) {
this.chainId = chainId;
this.tokenPropertiesCache = tokenPropertiesCache;
this.tokenFeeFetcher = tokenFeeFetcher;
this.allowList = allowList;
this.positiveCacheEntryTTL = positiveCacheEntryTTL;
this.negativeCacheEntryTTL = negativeCacheEntryTTL;
this.CACHE_KEY = (chainId, address) => `token-properties-${chainId}-${address}`;
}
async getTokensProperties(currencies, providerConfig) {
const tokenToResult = {};
if (!(providerConfig === null || providerConfig === void 0 ? void 0 : providerConfig.enableFeeOnTransferFeeFetching)) {
return tokenToResult;
}
const addressesToFetchFeesOnchain = [];
const addressesRaw = this.buildAddressesRaw(currencies);
const addressesCacheKeys = this.buildAddressesCacheKeys(currencies);
const tokenProperties = await this.tokenPropertiesCache.batchGet(addressesCacheKeys);
// Check if we have cached token validation results for any tokens.
for (const address of addressesRaw) {
const cachedValue = tokenProperties[this.CACHE_KEY(this.chainId, address.toLowerCase())];
if (cachedValue) {
metric.putMetric('TokenPropertiesProviderBatchGetCacheHit', 1, MetricLoggerUnit.Count);
const tokenFee = cachedValue.tokenFeeResult;
const tokenFeeResultExists = tokenFee && (tokenFee.buyFeeBps || tokenFee.sellFeeBps);
if (tokenFeeResultExists) {
metric.putMetric(`TokenPropertiesProviderCacheHitTokenFeeResultExists${tokenFeeResultExists}`, 1, MetricLoggerUnit.Count);
}
else {
metric.putMetric(`TokenPropertiesProviderCacheHitTokenFeeResultNotExists`, 1, MetricLoggerUnit.Count);
}
tokenToResult[address] = cachedValue;
}
else if (this.allowList.has(address)) {
tokenToResult[address] = {
tokenValidationResult: TokenValidationResult.UNKN,
};
}
else {
addressesToFetchFeesOnchain.push(address);
}
}
if (addressesToFetchFeesOnchain.length > 0) {
let tokenFeeMap = {};
try {
tokenFeeMap = await this.tokenFeeFetcher.fetchFees(addressesToFetchFeesOnchain, providerConfig);
}
catch (err) {
log.error({ err }, `Error fetching fees for tokens ${addressesToFetchFeesOnchain}`);
}
await Promise.all(addressesToFetchFeesOnchain.map((address) => {
const tokenFee = tokenFeeMap[address];
const tokenFeeResultExists = tokenFee && (tokenFee.buyFeeBps || tokenFee.sellFeeBps);
if (tokenFeeResultExists) {
// we will leverage the metric to log the token fee result, if it exists
// the idea is that the token fee should not differ by too much across tokens,
// so that we can accurately log the token fee for a particular quote request (without breaching metrics dimensionality limit)
// in the form of metrics.
// if we log as logging, given prod traffic volume, the logging volume will be high.
metric.putMetric(`TokenPropertiesProviderTokenFeeResultCacheMissExists${tokenFeeResultExists}`, 1, MetricLoggerUnit.Count);
const tokenPropertiesResult = {
tokenFeeResult: tokenFee,
tokenValidationResult: TokenValidationResult.FOT,
};
tokenToResult[address] = tokenPropertiesResult;
metric.putMetric('TokenPropertiesProviderBatchGetCacheMiss', 1, MetricLoggerUnit.Count);
// update cache concurrently
// at this point, we are confident that the tokens are FOT, so we can hardcode the validation result
return this.tokenPropertiesCache.set(this.CACHE_KEY(this.chainId, address), tokenPropertiesResult, this.positiveCacheEntryTTL);
}
else {
metric.putMetric(`TokenPropertiesProviderTokenFeeResultCacheMissNotExists`, 1, MetricLoggerUnit.Count);
const tokenPropertiesResult = {
tokenFeeResult: undefined,
tokenValidationResult: undefined,
};
tokenToResult[address] = tokenPropertiesResult;
return this.tokenPropertiesCache.set(this.CACHE_KEY(this.chainId, address), tokenPropertiesResult, this.negativeCacheEntryTTL);
}
}));
}
return tokenToResult;
}
buildAddressesRaw(currencies) {
const addressesRaw = new Set();
for (const currency of currencies) {
const address = getAddressLowerCase(currency);
if (!addressesRaw.has(address)) {
addressesRaw.add(address);
}
}
return addressesRaw;
}
buildAddressesCacheKeys(currencies) {
const addressesCacheKeys = new Set();
for (const currency of currencies) {
const addressCacheKey = this.CACHE_KEY(this.chainId, getAddressLowerCase(currency));
if (!addressesCacheKeys.has(addressCacheKey)) {
addressesCacheKeys.add(addressCacheKey);
}
}
return addressesCacheKeys;
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9rZW4tcHJvcGVydGllcy1wcm92aWRlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9wcm92aWRlcnMvdG9rZW4tcHJvcGVydGllcy1wcm92aWRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFJQSxPQUFPLEVBQUUsbUJBQW1CLEVBQUUsR0FBRyxFQUFFLE1BQU0sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLFNBQVMsQ0FBQztBQUk3RSxPQUFPLEVBQ0wsd0JBQXdCLEdBSXpCLE1BQU0scUJBQXFCLENBQUM7QUFDN0IsT0FBTyxFQUNMLGlCQUFpQixFQUNqQixxQkFBcUIsR0FDdEIsTUFBTSw0QkFBNEIsQ0FBQztBQUVwQyxNQUFNLENBQUMsTUFBTSwrQkFBK0IsR0FBMEI7SUFDcEUsY0FBYyxFQUFFLHdCQUF3QjtDQUN6QyxDQUFDO0FBQ0YsTUFBTSxDQUFDLE1BQU0sd0JBQXdCLEdBQUcsSUFBSSxDQUFDLENBQUMsd0JBQXdCO0FBQ3RFLE1BQU0sQ0FBQyxNQUFNLHdCQUF3QixHQUFHLElBQUksQ0FBQyxDQUFDLHdCQUF3QjtBQWdCdEUsTUFBTSxPQUFPLHVCQUF1QjtJQUlsQyxZQUNVLE9BQWdCLEVBQ2hCLG9CQUFtRCxFQUNuRCxlQUFpQyxFQUNqQyxZQUFZLGlCQUFpQixFQUM3Qix3QkFBd0Isd0JBQXdCLEVBQ2hELHdCQUF3Qix3QkFBd0I7UUFMaEQsWUFBTyxHQUFQLE9BQU8sQ0FBUztRQUNoQix5QkFBb0IsR0FBcEIsb0JBQW9CLENBQStCO1FBQ25ELG9CQUFlLEdBQWYsZUFBZSxDQUFrQjtRQUNqQyxjQUFTLEdBQVQsU0FBUyxDQUFvQjtRQUM3QiwwQkFBcUIsR0FBckIscUJBQXFCLENBQTJCO1FBQ2hELDBCQUFxQixHQUFyQixxQkFBcUIsQ0FBMkI7UUFUbEQsY0FBUyxHQUFHLENBQUMsT0FBZ0IsRUFBRSxPQUFlLEVBQUUsRUFBRSxDQUN4RCxvQkFBb0IsT0FBTyxJQUFJLE9BQU8sRUFBRSxDQUFDO0lB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