diff --git a/packages/asset-swapper/src/utils/market_operation_utils/constants.ts b/packages/asset-swapper/src/utils/market_operation_utils/constants.ts index 35b27180bb..d0e12dd5e8 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/constants.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/constants.ts @@ -2176,7 +2176,6 @@ export const LIDO_INFO_BY_CHAIN = valueByChainId( }, ); -export const BALANCER_SUBGRAPH_URL = 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer'; export const BALANCER_TOP_POOLS_FETCHED = 250; export const BALANCER_MAX_POOLS_FETCHED = 3; 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 3629344f44..6840212840 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 @@ -1,16 +1,21 @@ +import { ChainId } from '@0x/contract-addresses'; import { getPoolsWithTokens, parsePoolData } from 'balancer-labs-sor-v1'; import { Pool } from 'balancer-labs-sor-v1/dist/types'; import { gql, request } from 'graphql-request'; import { DEFAULT_WARNING_LOGGER } from '../../../constants'; import { LogFunction } from '../../../types'; -import { BALANCER_MAX_POOLS_FETCHED, BALANCER_SUBGRAPH_URL, BALANCER_TOP_POOLS_FETCHED } from '../constants'; +import { BALANCER_MAX_POOLS_FETCHED, BALANCER_TOP_POOLS_FETCHED } from '../constants'; -import { CacheValue, PoolsCache } from './pools_cache'; +import { NoOpPoolsCache } from './no_op_pools_cache'; +import { AbstractPoolsCache, CacheValue, PoolsCache } from './pools_cache'; // tslint:disable:custom-no-magic-numbers const ONE_DAY_MS = 24 * 60 * 60 * 1000; // tslint:enable:custom-no-magic-numbers +// tslint:disable: member-ordering + +const BALANCER_SUBGRAPH_URL = 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer'; interface BalancerPoolResponse { id: string; @@ -20,8 +25,16 @@ interface BalancerPoolResponse { totalWeight: string; } -export class BalancerPoolsCache extends PoolsCache { - constructor( +export class BalancerPoolsCache extends AbstractPoolsCache { + public static create(chainId: ChainId): PoolsCache { + if (chainId !== ChainId.Mainnet) { + return new NoOpPoolsCache(); + } + + return new BalancerPoolsCache(); + } + + private constructor( private readonly _subgraphUrl: string = BALANCER_SUBGRAPH_URL, cache: Map = new Map(), private readonly maxPoolsFetched: number = BALANCER_MAX_POOLS_FETCHED, 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 446797730e..ee3b95cb4a 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 @@ -9,7 +9,10 @@ import { LogFunction } from '../../../types'; import { BALANCER_MAX_POOLS_FETCHED, BALANCER_TOP_POOLS_FETCHED } from '../constants'; import { parsePoolData } from './balancer_sor_v2'; -import { CacheValue, PoolsCache } from './pools_cache'; +import { NoOpPoolsCache } from './no_op_pools_cache'; +import { AbstractPoolsCache, CacheValue, PoolsCache } from './pools_cache'; + +// tslint:disable: member-ordering const BEETHOVEN_X_SUBGRAPH_URL_BY_CHAIN = new Map([ [ChainId.Fantom, 'https://api.thegraph.com/subgraphs/name/beethovenxfi/beethovenx'], @@ -28,11 +31,11 @@ interface BalancerPoolResponse { amp: string | null; } -export class BalancerV2PoolsCache extends PoolsCache { - public static createBeethovenXPoolCache(chainId: ChainId): BalancerV2PoolsCache | undefined { +export class BalancerV2PoolsCache extends AbstractPoolsCache { + public static createBeethovenXPoolCache(chainId: ChainId): PoolsCache { const subgraphUrl = BEETHOVEN_X_SUBGRAPH_URL_BY_CHAIN.get(chainId); if (subgraphUrl === undefined) { - return undefined; + return new NoOpPoolsCache(); } return new BalancerV2PoolsCache(subgraphUrl); @@ -58,7 +61,7 @@ export class BalancerV2PoolsCache extends PoolsCache { }; } - constructor( + private constructor( private readonly subgraphUrl: string, private readonly maxPoolsFetched: number = BALANCER_MAX_POOLS_FETCHED, private readonly _topPoolsFetched: number = BALANCER_TOP_POOLS_FETCHED, 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 29654c5693..77a3ae2b26 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 @@ -1,18 +1,20 @@ +import { ChainId } from '@0x/contract-addresses'; import { Pool } from 'balancer-labs-sor-v1/dist/types'; import { getPoolsWithTokens, parsePoolData } from 'cream-sor'; import { BALANCER_MAX_POOLS_FETCHED } from '../constants'; -import { CacheValue, PoolsCache } from './pools_cache'; +import { NoOpPoolsCache } from './no_op_pools_cache'; +import { AbstractPoolsCache, CacheValue, PoolsCache } from './pools_cache'; -export class CreamPoolsCache extends PoolsCache { - constructor( - _cache: Map = new Map(), - private readonly maxPoolsFetched: number = BALANCER_MAX_POOLS_FETCHED, - ) { - super(_cache); +export class CreamPoolsCache extends AbstractPoolsCache { + public static create(chainId: ChainId): PoolsCache { + if (chainId !== ChainId.Mainnet) { + return new NoOpPoolsCache(); + } + + return new CreamPoolsCache(); } - protected async _fetchPoolsForPairAsync(takerToken: string, makerToken: string): Promise { try { const poolData = (await getPoolsWithTokens(takerToken, makerToken)).pools; @@ -25,4 +27,10 @@ export class CreamPoolsCache extends PoolsCache { return []; } } + private constructor( + _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/index.ts b/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/index.ts index a881d890bf..5e6f699b17 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/index.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/index.ts @@ -1,4 +1,4 @@ export { BalancerPoolsCache } from './balancer_utils'; export { BalancerV2PoolsCache } from './balancer_v2_utils'; export { CreamPoolsCache } from './cream_utils'; -export { PoolsCache } from './pools_cache'; +export { AbstractPoolsCache, PoolsCache } from './pools_cache'; diff --git a/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/no_op_pools_cache.ts b/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/no_op_pools_cache.ts new file mode 100644 index 0000000000..a0edc94f14 --- /dev/null +++ b/packages/asset-swapper/src/utils/market_operation_utils/pools_cache/no_op_pools_cache.ts @@ -0,0 +1,21 @@ +import { Pool, PoolsCache } from './pools_cache'; + +// tslint:disable:prefer-function-over-method + +export class NoOpPoolsCache implements PoolsCache { + public async getFreshPoolsForPairAsync( + _takerToken: string, + _makerToken: string, + _timeoutMs?: number | undefined, + ): Promise { + return []; + } + + public getPoolAddressesForPair(_takerToken: string, _makerToken: string): string[] { + return []; + } + + public isFresh(_takerToken: string, _makerToken: string): boolean { + return true; + } +} 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 92afed38a7..4e1257a5c8 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 @@ -13,7 +13,13 @@ const DEFAULT_CACHE_TIME_MS = (ONE_HOUR_IN_SECONDS / 2) * ONE_SECOND_MS; const DEFAULT_TIMEOUT_MS = 3000; // tslint:enable:custom-no-magic-numbers -export abstract class PoolsCache { +export interface PoolsCache { + getFreshPoolsForPairAsync(takerToken: string, makerToken: string, timeoutMs?: number): Promise; + getPoolAddressesForPair(takerToken: string, makerToken: string): string[]; + isFresh(takerToken: string, makerToken: string): boolean; +} + +export abstract class AbstractPoolsCache implements PoolsCache { protected static _getKey(takerToken: string, makerToken: string): string { return `${takerToken}-${makerToken}`; } @@ -51,18 +57,18 @@ export abstract class PoolsCache { public isFresh(takerToken: string, makerToken: string): boolean { const value = this._getValue(takerToken, makerToken); - return !PoolsCache._isExpired(value); + return !AbstractPoolsCache._isExpired(value); } protected _getValue(takerToken: string, makerToken: string): CacheValue | undefined { - const key = PoolsCache._getKey(takerToken, makerToken); + const key = AbstractPoolsCache._getKey(takerToken, makerToken); return this._cache.get(key); } protected async _getAndSaveFreshPoolsForPairAsync(takerToken: string, makerToken: string): Promise { - const key = PoolsCache._getKey(takerToken, makerToken); + const key = AbstractPoolsCache._getKey(takerToken, makerToken); const value = this._cache.get(key); - if (!PoolsCache._isExpired(value)) { + if (!AbstractPoolsCache._isExpired(value)) { return value!.pools; } @@ -73,7 +79,7 @@ export abstract class PoolsCache { } protected _cachePoolsForPair(takerToken: string, makerToken: string, pools: Pool[], expiresAt: number): void { - const key = PoolsCache._getKey(takerToken, makerToken); + const key = AbstractPoolsCache._getKey(takerToken, makerToken); this._cache.set(key, { pools, expiresAt }); } 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 d66a3ef1ce..d977429d17 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 @@ -56,7 +56,7 @@ import { } from './constants'; import { getGeistInfoForPair } from './geist_utils'; import { getLiquidityProvidersForPair } from './liquidity_provider_utils'; -import { BalancerPoolsCache, BalancerV2PoolsCache, CreamPoolsCache } from './pools_cache'; +import { BalancerPoolsCache, BalancerV2PoolsCache, CreamPoolsCache, PoolsCache } from './pools_cache'; import { BalancerV2SwapInfoCache } from './pools_cache/balancer_v2_utils_new'; import { SamplerContractOperation } from './sampler_contract_operation'; import { SamplerNoOperation } from './sampler_no_operation'; @@ -114,10 +114,10 @@ export const TWO_HOP_SOURCE_FILTERS = SourceFilters.all().exclude([ export const BATCH_SOURCE_FILTERS = SourceFilters.all().exclude([ERC20BridgeSource.MultiHop, ERC20BridgeSource.Native]); export interface PoolsCacheMap { - [ERC20BridgeSource.Balancer]: BalancerPoolsCache; + [ERC20BridgeSource.Balancer]: PoolsCache; [ERC20BridgeSource.BalancerV2]: BalancerV2SwapInfoCache | undefined; - [ERC20BridgeSource.Beethovenx]: BalancerV2PoolsCache | undefined; - [ERC20BridgeSource.Cream]: CreamPoolsCache; + [ERC20BridgeSource.Beethovenx]: PoolsCache; + [ERC20BridgeSource.Cream]: PoolsCache; } // tslint:disable:no-inferred-empty-object-type no-unbound-method @@ -156,8 +156,8 @@ export class SamplerOperations { ? poolsCaches : { [ERC20BridgeSource.Beethovenx]: BalancerV2PoolsCache.createBeethovenXPoolCache(chainId), - [ERC20BridgeSource.Balancer]: new BalancerPoolsCache(), - [ERC20BridgeSource.Cream]: new CreamPoolsCache(), + [ERC20BridgeSource.Balancer]: BalancerPoolsCache.create(chainId), + [ERC20BridgeSource.Cream]: CreamPoolsCache.create(chainId), [ERC20BridgeSource.BalancerV2]: BALANCER_V2_VAULT_ADDRESS_BY_CHAIN[chainId] === NULL_ADDRESS ? undefined @@ -1621,9 +1621,6 @@ export class SamplerOperations { } case ERC20BridgeSource.Beethovenx: { const cache = this.poolsCaches[source]; - if (cache === undefined) { - return []; - } const poolAddresses = cache.getPoolAddressesForPair(takerToken, makerToken); const vault = BEETHOVEN_X_VAULT_ADDRESS_BY_CHAIN[this.chainId]; if (vault === NULL_ADDRESS) { @@ -1976,9 +1973,6 @@ export class SamplerOperations { } case ERC20BridgeSource.Beethovenx: { const cache = this.poolsCaches[source]; - if (cache === undefined) { - return []; - } const poolIds = cache.getPoolAddressesForPair(takerToken, makerToken) || []; const vault = BEETHOVEN_X_VAULT_ADDRESS_BY_CHAIN[this.chainId]; if (vault === NULL_ADDRESS) { diff --git a/packages/asset-swapper/test/market_operation_utils_test.ts b/packages/asset-swapper/test/market_operation_utils_test.ts index 841c44c07c..1ee7a0a2b7 100644 --- a/packages/asset-swapper/test/market_operation_utils_test.ts +++ b/packages/asset-swapper/test/market_operation_utils_test.ts @@ -24,7 +24,7 @@ import { SOURCE_FLAGS, ZERO_AMOUNT, } from '../src/utils/market_operation_utils/constants'; -import { PoolsCache } from '../src/utils/market_operation_utils/pools_cache'; +import { AbstractPoolsCache } from '../src/utils/market_operation_utils/pools_cache'; import { DexOrderSampler } from '../src/utils/market_operation_utils/sampler'; import { BATCH_SOURCE_FILTERS } from '../src/utils/market_operation_utils/sampler_operations'; import { SourceFilters } from '../src/utils/market_operation_utils/source_filters'; @@ -98,7 +98,7 @@ async function getMarketBuyOrdersAsync( return utils.getOptimizerResultAsync(nativeOrders, makerAmount, MarketOperation.Buy, opts); } -class MockPoolsCache extends PoolsCache { +class MockPoolsCache extends AbstractPoolsCache { constructor(private readonly _handler: (takerToken: string, makerToken: string) => Pool[]) { super(new Map()); } diff --git a/packages/asset-swapper/test/pools_cache_test.ts b/packages/asset-swapper/test/pools_cache_test.ts index df0be3b568..6bd0683702 100644 --- a/packages/asset-swapper/test/pools_cache_test.ts +++ b/packages/asset-swapper/test/pools_cache_test.ts @@ -33,7 +33,7 @@ describe('Pools Caches for Balancer-based sampling', () => { } describe('BalancerPoolsCache', () => { - const cache = new BalancerPoolsCache(); + const cache = BalancerPoolsCache.create(ChainId.Mainnet); it('fetches pools', async () => { const pairs = [ [usdcAddress, daiAddress], @@ -58,15 +58,14 @@ describe('Pools Caches for Balancer-based sampling', () => { [wftmAddress, fantomWethAddress], ]; - expect(cache).not.null(); await Promise.all( - pairs.map(async ([takerToken, makerToken]) => fetchAndAssertPoolsAsync(cache!, takerToken, makerToken)), + pairs.map(async ([takerToken, makerToken]) => fetchAndAssertPoolsAsync(cache, takerToken, makerToken)), ); }); }); describe('CreamPoolsCache', () => { - const cache = new CreamPoolsCache(); + const cache = CreamPoolsCache.create(ChainId.Mainnet); it('fetches pools', async () => { const pairs = [ [usdcAddress, creamAddress],