diff --git a/packages/asset-swapper/src/utils/market_operation_utils/index.ts b/packages/asset-swapper/src/utils/market_operation_utils/index.ts index 00c635f5c9..1ff786341f 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/index.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/index.ts @@ -862,14 +862,9 @@ export class MarketOperationUtils { } private async _refreshPoolCacheIfRequiredAsync(takerToken: string, makerToken: string): Promise { - void Promise.all( - Object.values(this._sampler.poolsCaches).map(async cache => { - if (!cache || cache.isFresh(takerToken, makerToken)) { - return Promise.resolve([]); - } - return cache.getFreshPoolsForPairAsync(takerToken, makerToken); - }), - ); + _.values(this._sampler.poolsCaches) + .filter(cache => cache !== undefined && !cache.isFresh(takerToken, makerToken)) + .forEach(cache => cache?.getFreshPoolsForPairAsync(takerToken, makerToken)); } } diff --git a/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/balancer_utils.ts b/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/balancer_utils.ts index fac25ea671..3629344f44 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/balancer_utils.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/balancer_utils.ts @@ -23,7 +23,7 @@ interface BalancerPoolResponse { export class BalancerPoolsCache extends PoolsCache { constructor( private readonly _subgraphUrl: string = BALANCER_SUBGRAPH_URL, - cache: { [key: string]: CacheValue } = {}, + cache: Map = new Map(), private readonly maxPoolsFetched: number = BALANCER_MAX_POOLS_FETCHED, private readonly _topPoolsFetched: number = BALANCER_TOP_POOLS_FETCHED, private readonly _warningLogger: LogFunction = DEFAULT_WARNING_LOGGER, diff --git a/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/balancer_v2_utils.ts b/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/balancer_v2_utils.ts index c953e868dc..446797730e 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/balancer_v2_utils.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/balancer_v2_utils.ts @@ -63,7 +63,7 @@ export class BalancerV2PoolsCache extends PoolsCache { private readonly maxPoolsFetched: number = BALANCER_MAX_POOLS_FETCHED, private readonly _topPoolsFetched: number = BALANCER_TOP_POOLS_FETCHED, private readonly _warningLogger: LogFunction = DEFAULT_WARNING_LOGGER, - cache: { [key: string]: CacheValue } = {}, + cache: Map = new Map(), ) { super(cache); void this._loadTopPoolsAsync(); diff --git a/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/cream_utils.ts b/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/cream_utils.ts index 03202d64de..29654c5693 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/cream_utils.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/cream_utils.ts @@ -7,7 +7,7 @@ import { CacheValue, PoolsCache } from './pools_cache'; export class CreamPoolsCache extends PoolsCache { constructor( - _cache: { [key: string]: CacheValue } = {}, + _cache: Map = new Map(), private readonly maxPoolsFetched: number = BALANCER_MAX_POOLS_FETCHED, ) { super(_cache); diff --git a/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/pools_cache.ts b/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/pools_cache.ts index 4d08193a08..92afed38a7 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/pools_cache.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/pools_cache.ts @@ -10,15 +10,23 @@ export interface CacheValue { // tslint:disable:custom-no-magic-numbers // Cache results for 30mins const DEFAULT_CACHE_TIME_MS = (ONE_HOUR_IN_SECONDS / 2) * ONE_SECOND_MS; -const DEFAULT_TIMEOUT_MS = 1000; +const DEFAULT_TIMEOUT_MS = 3000; // tslint:enable:custom-no-magic-numbers export abstract class PoolsCache { - protected static _isExpired(value: CacheValue): boolean { + protected static _getKey(takerToken: string, makerToken: string): string { + return `${takerToken}-${makerToken}`; + } + + protected static _isExpired(value: CacheValue | undefined): boolean { + if (value === undefined) { + return true; + } return Date.now() >= value.expiresAt; } + constructor( - protected readonly _cache: { [key: string]: CacheValue }, + protected readonly _cache: Map, protected readonly _cacheTimeMs: number = DEFAULT_CACHE_TIME_MS, ) {} @@ -31,47 +39,42 @@ export abstract class PoolsCache { return Promise.race([this._getAndSaveFreshPoolsForPairAsync(takerToken, makerToken), timeout]); } - public getCachedPoolAddressesForPair( - takerToken: string, - makerToken: string, - ignoreExpired: boolean = true, - ): string[] | undefined { - const key = JSON.stringify([takerToken, makerToken]); - const value = this._cache[key]; - if (ignoreExpired) { - return value === undefined ? [] : value.pools.map(pool => pool.id); - } - if (!value) { - return undefined; - } - if (PoolsCache._isExpired(value)) { - return undefined; - } - return (value || []).pools.map(pool => pool.id); + /** + * Returns pool addresses (can be stale) for a pair. + * + * An empty array will be returned if cache does not exist. + */ + public getPoolAddressesForPair(takerToken: string, makerToken: string): string[] { + const value = this._getValue(takerToken, makerToken); + return value === undefined ? [] : value.pools.map(pool => pool.id); } public isFresh(takerToken: string, makerToken: string): boolean { - const cached = this.getCachedPoolAddressesForPair(takerToken, makerToken, false); - return cached !== undefined; + const value = this._getValue(takerToken, makerToken); + return !PoolsCache._isExpired(value); + } + + protected _getValue(takerToken: string, makerToken: string): CacheValue | undefined { + const key = PoolsCache._getKey(takerToken, makerToken); + return this._cache.get(key); } protected async _getAndSaveFreshPoolsForPairAsync(takerToken: string, makerToken: string): Promise { - const key = JSON.stringify([takerToken, makerToken]); - const value = this._cache[key]; - if (value === undefined || value.expiresAt >= Date.now()) { - const pools = await this._fetchPoolsForPairAsync(takerToken, makerToken); - const expiresAt = Date.now() + this._cacheTimeMs; - this._cachePoolsForPair(takerToken, makerToken, pools, expiresAt); + const key = PoolsCache._getKey(takerToken, makerToken); + const value = this._cache.get(key); + if (!PoolsCache._isExpired(value)) { + return value!.pools; } - return this._cache[key].pools; + + const pools = await this._fetchPoolsForPairAsync(takerToken, makerToken); + const expiresAt = Date.now() + this._cacheTimeMs; + this._cachePoolsForPair(takerToken, makerToken, pools, expiresAt); + return pools; } protected _cachePoolsForPair(takerToken: string, makerToken: string, pools: Pool[], expiresAt: number): void { - const key = JSON.stringify([takerToken, makerToken]); - this._cache[key] = { - pools, - expiresAt, - }; + const key = PoolsCache._getKey(takerToken, makerToken); + this._cache.set(key, { pools, expiresAt }); } protected abstract _fetchPoolsForPairAsync(takerToken: string, makerToken: string): Promise; diff --git a/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts b/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts index f749c85dee..d66a3ef1ce 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts @@ -1592,20 +1592,17 @@ export class SamplerOperations { ), ]; case ERC20BridgeSource.Balancer: - return ( - this.poolsCaches[ERC20BridgeSource.Balancer].getCachedPoolAddressesForPair( - takerToken, - makerToken, - ) || [] - ).map(balancerPool => - this.getBalancerSellQuotes( - balancerPool, - makerToken, - takerToken, - takerFillAmounts, - ERC20BridgeSource.Balancer, - ), - ); + return this.poolsCaches[ERC20BridgeSource.Balancer] + .getPoolAddressesForPair(takerToken, makerToken) + .map(balancerPool => + this.getBalancerSellQuotes( + balancerPool, + makerToken, + takerToken, + takerFillAmounts, + ERC20BridgeSource.Balancer, + ), + ); case ERC20BridgeSource.BalancerV2: { const cache = this.poolsCaches[source]; if (!cache) { @@ -1627,15 +1624,14 @@ export class SamplerOperations { if (cache === undefined) { return []; } - const poolIds = cache.getCachedPoolAddressesForPair(takerToken, makerToken) || []; - + const poolAddresses = cache.getPoolAddressesForPair(takerToken, makerToken); const vault = BEETHOVEN_X_VAULT_ADDRESS_BY_CHAIN[this.chainId]; if (vault === NULL_ADDRESS) { return []; } - return poolIds.map(poolId => + return poolAddresses.map(poolAddress => this.getBalancerV2SellQuotes( - { poolId, vault }, + { poolId: poolAddress, vault }, makerToken, takerToken, takerFillAmounts, @@ -1644,20 +1640,17 @@ export class SamplerOperations { ); } case ERC20BridgeSource.Cream: - return ( - this.poolsCaches[ERC20BridgeSource.Cream].getCachedPoolAddressesForPair( - takerToken, - makerToken, - ) || [] - ).map(creamPool => - this.getBalancerSellQuotes( - creamPool, - makerToken, - takerToken, - takerFillAmounts, - ERC20BridgeSource.Cream, - ), - ); + return this.poolsCaches[ERC20BridgeSource.Cream] + .getPoolAddressesForPair(takerToken, makerToken) + .map(creamPool => + this.getBalancerSellQuotes( + creamPool, + makerToken, + takerToken, + takerFillAmounts, + ERC20BridgeSource.Cream, + ), + ); case ERC20BridgeSource.Dodo: if (!isValidAddress(DODOV1_CONFIG_BY_CHAIN_ID[this.chainId].registry)) { return []; @@ -1948,20 +1941,17 @@ export class SamplerOperations { ), ]; case ERC20BridgeSource.Balancer: - return ( - this.poolsCaches[ERC20BridgeSource.Balancer].getCachedPoolAddressesForPair( - takerToken, - makerToken, - ) || [] - ).map(poolAddress => - this.getBalancerBuyQuotes( - poolAddress, - makerToken, - takerToken, - makerFillAmounts, - ERC20BridgeSource.Balancer, - ), - ); + return this.poolsCaches[ERC20BridgeSource.Balancer] + .getPoolAddressesForPair(takerToken, makerToken) + .map(poolAddress => + this.getBalancerBuyQuotes( + poolAddress, + makerToken, + takerToken, + makerFillAmounts, + ERC20BridgeSource.Balancer, + ), + ); case ERC20BridgeSource.BalancerV2: { const cache = this.poolsCaches[source]; if (!cache) { @@ -1989,8 +1979,7 @@ export class SamplerOperations { if (cache === undefined) { return []; } - const poolIds = cache.getCachedPoolAddressesForPair(takerToken, makerToken) || []; - + const poolIds = cache.getPoolAddressesForPair(takerToken, makerToken) || []; const vault = BEETHOVEN_X_VAULT_ADDRESS_BY_CHAIN[this.chainId]; if (vault === NULL_ADDRESS) { return []; @@ -2006,20 +1995,17 @@ export class SamplerOperations { ); } case ERC20BridgeSource.Cream: - return ( - this.poolsCaches[ERC20BridgeSource.Cream].getCachedPoolAddressesForPair( - takerToken, - makerToken, - ) || [] - ).map(poolAddress => - this.getBalancerBuyQuotes( - poolAddress, - makerToken, - takerToken, - makerFillAmounts, - ERC20BridgeSource.Cream, - ), - ); + return this.poolsCaches[ERC20BridgeSource.Cream] + .getPoolAddressesForPair(takerToken, makerToken) + .map(poolAddress => + this.getBalancerBuyQuotes( + poolAddress, + makerToken, + takerToken, + makerFillAmounts, + ERC20BridgeSource.Cream, + ), + ); case ERC20BridgeSource.Dodo: if (!isValidAddress(DODOV1_CONFIG_BY_CHAIN_ID[this.chainId].registry)) { return []; diff --git a/packages/asset-swapper/test/market_operation_utils_test.ts b/packages/asset-swapper/test/market_operation_utils_test.ts index 874fcc92d9..841c44c07c 100644 --- a/packages/asset-swapper/test/market_operation_utils_test.ts +++ b/packages/asset-swapper/test/market_operation_utils_test.ts @@ -100,7 +100,7 @@ async function getMarketBuyOrdersAsync( class MockPoolsCache extends PoolsCache { constructor(private readonly _handler: (takerToken: string, makerToken: string) => Pool[]) { - super({}); + super(new Map()); } protected async _fetchPoolsForPairAsync(takerToken: string, makerToken: string): Promise { return this._handler(takerToken, makerToken); diff --git a/packages/asset-swapper/test/pools_cache_test.ts b/packages/asset-swapper/test/pools_cache_test.ts index f87cfb9728..df0be3b568 100644 --- a/packages/asset-swapper/test/pools_cache_test.ts +++ b/packages/asset-swapper/test/pools_cache_test.ts @@ -28,7 +28,7 @@ describe('Pools Caches for Balancer-based sampling', () => { expect(pools.length).greaterThan(0, `Failed to find any pools for ${takerToken} and ${makerToken}`); expect(pools[0]).not.undefined(); expect(Object.keys(pools[0])).to.include.members(poolKeys); - const cachedPoolIds = cache.getCachedPoolAddressesForPair(takerToken, makerToken); + const cachedPoolIds = cache.getPoolAddressesForPair(takerToken, makerToken); expect(cachedPoolIds).to.deep.equal(pools.map(p => p.id)); }