diff --git a/contracts/zero-ex/CHANGELOG.json b/contracts/zero-ex/CHANGELOG.json index 2bf7c0f902..56153459ab 100644 --- a/contracts/zero-ex/CHANGELOG.json +++ b/contracts/zero-ex/CHANGELOG.json @@ -65,6 +65,10 @@ { "note": "Added `Shell` into FQT", "pr": 2722 + }, + { + "note": "Added `CREAM` into FQT", + "pr": 2715 } ] }, diff --git a/contracts/zero-ex/contracts/src/transformers/bridges/BridgeAdapter.sol b/contracts/zero-ex/contracts/src/transformers/bridges/BridgeAdapter.sol index 761ec15da1..3c60a8f436 100644 --- a/contracts/zero-ex/contracts/src/transformers/bridges/BridgeAdapter.sol +++ b/contracts/zero-ex/contracts/src/transformers/bridges/BridgeAdapter.sol @@ -46,6 +46,7 @@ contract BridgeAdapter is { address private immutable BALANCER_BRIDGE_ADDRESS; + address private immutable CREAM_BRIDGE_ADDRESS; address private immutable CURVE_BRIDGE_ADDRESS; address private immutable KYBER_BRIDGE_ADDRESS; address private immutable MOONISWAP_BRIDGE_ADDRESS; @@ -93,6 +94,7 @@ contract BridgeAdapter is SHELL_BRIDGE_ADDRESS = addresses.shellBridge; UNISWAP_BRIDGE_ADDRESS = addresses.uniswapBridge; UNISWAP_V2_BRIDGE_ADDRESS = addresses.uniswapV2Bridge; + CREAM_BRIDGE_ADDRESS = addresses.creamBridge; } function trade( @@ -170,6 +172,12 @@ contract BridgeAdapter is sellAmount, bridgeData ); + } else if (bridgeAddress == CREAM_BRIDGE_ADDRESS) { + boughtAmount = _tradeBalancer( + buyToken, + sellAmount, + bridgeData + ); } else { boughtAmount = _tradeZeroExBridge( bridgeAddress, diff --git a/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinAdapterAddresses.sol b/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinAdapterAddresses.sol index c9d13f5731..33e2de618d 100644 --- a/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinAdapterAddresses.sol +++ b/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinAdapterAddresses.sol @@ -24,6 +24,7 @@ contract MixinAdapterAddresses struct AdapterAddresses { // Bridges address balancerBridge; + address creamBridge; address curveBridge; address kyberBridge; address mooniswapBridge; diff --git a/contracts/zero-ex/test/transformers/fill_quote_transformer_test.ts b/contracts/zero-ex/test/transformers/fill_quote_transformer_test.ts index fbeffdf643..724688da44 100644 --- a/contracts/zero-ex/test/transformers/fill_quote_transformer_test.ts +++ b/contracts/zero-ex/test/transformers/fill_quote_transformer_test.ts @@ -75,6 +75,7 @@ blockchainTests.resets('FillQuoteTransformer', env => { weth: NULL_ADDRESS, shellBridge: NULL_ADDRESS, shell: NULL_ADDRESS, + creamBridge: NULL_ADDRESS, }, ); transformer = await FillQuoteTransformerContract.deployFrom0xArtifactAsync( diff --git a/packages/asset-swapper/package.json b/packages/asset-swapper/package.json index 8673709871..21aae41755 100644 --- a/packages/asset-swapper/package.json +++ b/packages/asset-swapper/package.json @@ -73,6 +73,7 @@ "@bancor/sdk": "^0.2.9", "axios": "^0.19.2", "axios-mock-adapter": "^1.18.1", + "cream-sor": "^0.3.3", "decimal.js": "^10.2.0", "ethereum-types": "^3.2.0", "ethereumjs-util": "^5.1.1", diff --git a/packages/asset-swapper/src/utils/market_operation_utils/cream_utils.ts b/packages/asset-swapper/src/utils/market_operation_utils/cream_utils.ts new file mode 100644 index 0000000000..f263270de3 --- /dev/null +++ b/packages/asset-swapper/src/utils/market_operation_utils/cream_utils.ts @@ -0,0 +1,104 @@ +import { getPoolsWithTokens, parsePoolData } from 'cream-sor'; + +import { BalancerPool } from './balancer_utils'; + +// tslint:disable:boolean-naming + +interface CacheValue { + timestamp: number; + pools: BalancerPool[]; +} + +// tslint:disable:custom-no-magic-numbers +const FIVE_SECONDS_MS = 5 * 1000; +const ONE_DAY_MS = 24 * 60 * 60 * 1000; +const DEFAULT_TIMEOUT_MS = 1000; +const MAX_POOLS_FETCHED = 3; +// tslint:enable:custom-no-magic-numbers + +export class CreamPoolsCache { + constructor( + private readonly _cache: { [key: string]: CacheValue } = {}, + private readonly maxPoolsFetched: number = MAX_POOLS_FETCHED, + ) {} + + public async getPoolsForPairAsync( + takerToken: string, + makerToken: string, + timeoutMs: number = DEFAULT_TIMEOUT_MS, + ): Promise { + const timeout = new Promise(resolve => setTimeout(resolve, timeoutMs, [])); + return Promise.race([this._getPoolsForPairAsync(takerToken, makerToken), timeout]); + } + + public getCachedPoolAddressesForPair( + takerToken: string, + makerToken: string, + cacheExpiryMs?: number, + ): string[] | undefined { + const key = JSON.stringify([takerToken, makerToken]); + const value = this._cache[key]; + if (cacheExpiryMs === undefined) { + return value === undefined ? [] : value.pools.map(pool => pool.id); + } + const minTimestamp = Date.now() - cacheExpiryMs; + if (value === undefined || value.timestamp < minTimestamp) { + return undefined; + } else { + return value.pools.map(pool => pool.id); + } + } + + public howToSampleCream( + takerToken: string, + makerToken: string, + isAllowedSource: boolean, + ): { onChain: boolean; offChain: boolean } { + // If Balancer is excluded as a source, do not sample. + if (!isAllowedSource) { + return { onChain: false, offChain: false }; + } + const cachedCreamPools = this.getCachedPoolAddressesForPair(takerToken, makerToken, ONE_DAY_MS); + // Sample CREAM on-chain (i.e. via the ERC20BridgeSampler contract) if: + // - Cached values are not stale + // - There is at least one CREAM pool for this pair + const onChain = cachedCreamPools !== undefined && cachedCreamPools.length > 0; + // Sample Balancer off-chain (i.e. via GraphQL query + `computeCreamBuy/SellQuote`) + // if cached values are stale + const offChain = cachedCreamPools === undefined; + return { onChain, offChain }; + } + + protected async _getPoolsForPairAsync( + takerToken: string, + makerToken: string, + cacheExpiryMs: number = FIVE_SECONDS_MS, + ): Promise { + const key = JSON.stringify([takerToken, makerToken]); + const value = this._cache[key]; + const minTimestamp = Date.now() - cacheExpiryMs; + if (value === undefined || value.timestamp < minTimestamp) { + const pools = await this._fetchPoolsForPairAsync(takerToken, makerToken); + const timestamp = Date.now(); + this._cache[key] = { + pools, + timestamp, + }; + } + return this._cache[key].pools; + } + + // tslint:disable-next-line:prefer-function-over-method + protected async _fetchPoolsForPairAsync(takerToken: string, makerToken: string): Promise { + try { + const poolData = (await getPoolsWithTokens(takerToken, makerToken)).pools; + // Sort by maker token balance (descending) + const pools = parsePoolData(poolData, takerToken, makerToken).sort((a, b) => + b.balanceOut.minus(a.balanceOut).toNumber(), + ); + return pools.length > this.maxPoolsFetched ? pools.slice(0, this.maxPoolsFetched) : pools; + } catch (err) { + return []; + } + } +} 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 58fcf395c5..145527804f 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/index.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/index.ts @@ -150,6 +150,23 @@ export class MarketOperationUtils { quoteSourceFilters.isAllowed(ERC20BridgeSource.Balancer), ); + const { + onChain: sampleCreamOnChain, + offChain: sampleCreamOffChain, + } = this._sampler.creamPoolsCache.howToSampleCream( + takerToken, + makerToken, + quoteSourceFilters.isAllowed(ERC20BridgeSource.Cream), + ); + + let excludedSources: ERC20BridgeSource[] = []; + if (!sampleCreamOnChain) { + excludedSources = excludedSources.concat(ERC20BridgeSource.Cream); + } + if (!sampleBalancerOnChain) { + excludedSources = excludedSources.concat(ERC20BridgeSource.Balancer); + } + // Call the sampler contract. const samplerPromise = this._sampler.executeAsync( // Get native order fillable amounts. @@ -176,7 +193,7 @@ export class MarketOperationUtils { ), // Get sell quotes for taker -> maker. this._sampler.getSellQuotes( - quoteSourceFilters.exclude(sampleBalancerOnChain ? [] : ERC20BridgeSource.Balancer).sources, + quoteSourceFilters.exclude(excludedSources).sources, makerToken, takerToken, sampleAmounts, @@ -209,6 +226,10 @@ export class MarketOperationUtils { ? this._sampler.getBalancerSellQuotesOffChainAsync(makerToken, takerToken, sampleAmounts) : Promise.resolve([]); + const offChainCreamPromise = sampleCreamOffChain + ? this._sampler.getCreamSellQuotesOffChainAsync(makerToken, takerToken, sampleAmounts) + : Promise.resolve([]); + const offChainBancorPromise = quoteSourceFilters.isAllowed(ERC20BridgeSource.Bancor) ? this._sampler.getBancorSellQuotesOffChainAsync(makerToken, takerToken, [takerAmount]) : Promise.resolve([]); @@ -217,15 +238,16 @@ export class MarketOperationUtils { [orderFillableAmounts, ethToMakerAssetRate, ethToTakerAssetRate, dexQuotes, twoHopQuotes], rfqtIndicativeQuotes, offChainBalancerQuotes, + offChainCreamQuotes, offChainBancorQuotes, - ] = await Promise.all([samplerPromise, rfqtPromise, offChainBalancerPromise, offChainBancorPromise]); + ] = await Promise.all([samplerPromise, rfqtPromise, offChainBalancerPromise, offChainCreamPromise, offChainBancorPromise]); return { side: MarketOperation.Sell, inputAmount: takerAmount, inputToken: takerToken, outputToken: makerToken, - dexQuotes: dexQuotes.concat([...offChainBalancerQuotes, offChainBancorQuotes]), + dexQuotes: dexQuotes.concat([...offChainBalancerQuotes, ...offChainCreamQuotes, offChainBancorQuotes]), nativeOrders, orderFillableAmounts, ethToOutputRate: ethToMakerAssetRate, @@ -266,6 +288,23 @@ export class MarketOperationUtils { quoteSourceFilters.isAllowed(ERC20BridgeSource.Balancer), ); + const { + onChain: sampleCreamOnChain, + offChain: sampleCreamOffChain, + } = this._sampler.creamPoolsCache.howToSampleCream( + takerToken, + makerToken, + quoteSourceFilters.isAllowed(ERC20BridgeSource.Cream), + ); + + let excludedSources: ERC20BridgeSource[] = []; + if (!sampleCreamOnChain) { + excludedSources = excludedSources.concat(ERC20BridgeSource.Cream); + } + if (!sampleBalancerOnChain) { + excludedSources = excludedSources.concat(ERC20BridgeSource.Balancer); + } + // Call the sampler contract. const samplerPromise = this._sampler.executeAsync( // Get native order fillable amounts. @@ -292,7 +331,7 @@ export class MarketOperationUtils { ), // Get buy quotes for taker -> maker. this._sampler.getBuyQuotes( - quoteSourceFilters.exclude(sampleBalancerOnChain ? [] : ERC20BridgeSource.Balancer).sources, + quoteSourceFilters.exclude(excludedSources).sources, makerToken, takerToken, sampleAmounts, @@ -324,11 +363,16 @@ export class MarketOperationUtils { ? this._sampler.getBalancerBuyQuotesOffChainAsync(makerToken, takerToken, sampleAmounts) : Promise.resolve([]); + const offChainCreamPromise = sampleCreamOffChain + ? this._sampler.getCreamBuyQuotesOffChainAsync(makerToken, takerToken, sampleAmounts) + : Promise.resolve([]); + const [ [orderFillableAmounts, ethToMakerAssetRate, ethToTakerAssetRate, dexQuotes, twoHopQuotes], rfqtIndicativeQuotes, offChainBalancerQuotes, - ] = await Promise.all([samplerPromise, rfqtPromise, offChainBalancerPromise]); + offChainCreamQuotes, + ] = await Promise.all([samplerPromise, rfqtPromise, offChainBalancerPromise, offChainCreamPromise]); // Attach the MultiBridge address to the sample fillData (dexQuotes.find(quotes => quotes[0] && quotes[0].source === ERC20BridgeSource.MultiBridge) || []).forEach( q => (q.fillData = { poolAddress: this._multiBridge }), @@ -338,7 +382,7 @@ export class MarketOperationUtils { inputAmount: makerAmount, inputToken: makerToken, outputToken: takerToken, - dexQuotes: dexQuotes.concat(offChainBalancerQuotes), + dexQuotes: dexQuotes.concat(offChainBalancerQuotes, offChainCreamQuotes), nativeOrders, orderFillableAmounts, ethToOutputRate: ethToTakerAssetRate, diff --git a/packages/asset-swapper/src/utils/market_operation_utils/sampler.ts b/packages/asset-swapper/src/utils/market_operation_utils/sampler.ts index d17bbd6831..5c6f0cc1db 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/sampler.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/sampler.ts @@ -6,6 +6,7 @@ import { ERC20BridgeSamplerContract } from '../../wrappers'; import { BalancerPoolsCache } from './balancer_utils'; import { BancorService } from './bancor_service'; +import { CreamPoolsCache } from './cream_utils'; import { SamplerOperations } from './sampler_operations'; import { BatchedOperation } from './types'; @@ -37,9 +38,10 @@ export class DexOrderSampler extends SamplerOperations { private readonly _samplerOverrides?: SamplerOverrides, provider?: SupportedProvider, balancerPoolsCache?: BalancerPoolsCache, + creamPoolsCache?: CreamPoolsCache, getBancorServiceFn?: () => BancorService, ) { - super(_samplerContract, provider, balancerPoolsCache, getBancorServiceFn); + super(_samplerContract, provider, balancerPoolsCache, creamPoolsCache, getBancorServiceFn); } /* Type overloads for `executeAsync()`. Could skip this if we would upgrade TS. */ 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 1ecf83e7a0..70f4d58a77 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 @@ -8,6 +8,7 @@ import { ERC20BridgeSamplerContract } from '../../wrappers'; import { BalancerPoolsCache, computeBalancerBuyQuote, computeBalancerSellQuote } from './balancer_utils'; import { BancorService } from './bancor_service'; import { MAINNET_SUSHI_SWAP_ROUTER, MAX_UINT256, NULL_BYTES, ZERO_AMOUNT } from './constants'; +import { CreamPoolsCache } from './cream_utils'; import { getCurveInfosForPair, getSwerveInfosForPair } from './curve_utils'; import { getKyberReserveIdsForPair } from './kyber_utils'; import { getMultiBridgeIntermediateToken } from './multibridge_utils'; @@ -18,6 +19,7 @@ import { BalancerFillData, BancorFillData, BatchedOperation, + CreamFillData, CurveFillData, CurveInfo, DexSample, @@ -72,6 +74,7 @@ export class SamplerOperations { protected readonly _samplerContract: ERC20BridgeSamplerContract, public readonly provider?: SupportedProvider, public readonly balancerPoolsCache: BalancerPoolsCache = new BalancerPoolsCache(), + public readonly creamPoolsCache: CreamPoolsCache = new CreamPoolsCache(), protected readonly getBancorServiceFn?: () => BancorService, // for dependency injection in tests ) {} @@ -466,6 +469,68 @@ export class SamplerOperations { ); } + public getCreamSellQuotes( + poolAddress: string, + makerToken: string, + takerToken: string, + takerFillAmounts: BigNumber[], + ): SourceQuoteOperation { + return new SamplerContractOperation({ + source: ERC20BridgeSource.Cream, + fillData: { poolAddress }, + contract: this._samplerContract, + function: this._samplerContract.sampleSellsFromBalancer, + params: [poolAddress, takerToken, makerToken, takerFillAmounts], + }); + } + + public getCreamBuyQuotes( + poolAddress: string, + makerToken: string, + takerToken: string, + makerFillAmounts: BigNumber[], + ): SourceQuoteOperation { + return new SamplerContractOperation({ + source: ERC20BridgeSource.Cream, + fillData: { poolAddress }, + contract: this._samplerContract, + function: this._samplerContract.sampleBuysFromBalancer, + params: [poolAddress, takerToken, makerToken, makerFillAmounts], + }); + } + + public async getCreamSellQuotesOffChainAsync( + makerToken: string, + takerToken: string, + takerFillAmounts: BigNumber[], + ): Promise>>> { + const pools = await this.creamPoolsCache.getPoolsForPairAsync(takerToken, makerToken); + return pools.map(pool => + takerFillAmounts.map(amount => ({ + source: ERC20BridgeSource.Balancer, + output: computeBalancerSellQuote(pool, amount), + input: amount, + fillData: { poolAddress: pool.id }, + })), + ); + } + + public async getCreamBuyQuotesOffChainAsync( + makerToken: string, + takerToken: string, + makerFillAmounts: BigNumber[], + ): Promise>>> { + const pools = await this.creamPoolsCache.getPoolsForPairAsync(takerToken, makerToken); + return pools.map(pool => + makerFillAmounts.map(amount => ({ + source: ERC20BridgeSource.Balancer, + output: computeBalancerBuyQuote(pool, amount), + input: amount, + fillData: { poolAddress: pool.id }, + })), + ); + } + public getMStableSellQuotes( makerToken: string, takerToken: string, @@ -997,6 +1062,12 @@ export class SamplerOperations { .map(poolAddress => this.getBalancerSellQuotes(poolAddress, makerToken, takerToken, takerFillAmounts), ); + case ERC20BridgeSource.Cream: + return this.creamPoolsCache + .getCachedPoolAddressesForPair(takerToken, makerToken)! + .map(poolAddress => + this.getBalancerSellQuotes(poolAddress, makerToken, takerToken, takerFillAmounts), + ); case ERC20BridgeSource.Shell: return this.getShellSellQuotes(makerToken, takerToken, takerFillAmounts); default: @@ -1086,6 +1157,12 @@ export class SamplerOperations { .map(poolAddress => this.getBalancerBuyQuotes(poolAddress, makerToken, takerToken, makerFillAmounts), ); + case ERC20BridgeSource.Cream: + return this.creamPoolsCache + .getCachedPoolAddressesForPair(takerToken, makerToken)! + .map(poolAddress => + this.getBalancerBuyQuotes(poolAddress, makerToken, takerToken, makerFillAmounts), + ); case ERC20BridgeSource.Shell: return this.getShellBuyQuotes(makerToken, takerToken, makerFillAmounts); default: diff --git a/packages/asset-swapper/src/utils/market_operation_utils/types.ts b/packages/asset-swapper/src/utils/market_operation_utils/types.ts index 0afc145d47..68c178d174 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/types.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/types.ts @@ -37,6 +37,7 @@ export enum ERC20BridgeSource { LiquidityProvider = 'LiquidityProvider', MultiBridge = 'MultiBridge', Balancer = 'Balancer', + Cream = 'CREAM', Bancor = 'Bancor', MStable = 'mStable', Mooniswap = 'Mooniswap', @@ -103,6 +104,10 @@ export interface BalancerFillData extends FillData { poolAddress: string; } +export interface CreamFillData extends FillData { + poolAddress: string; +} + export interface UniswapV2FillData extends FillData { tokenAddressPath: string[]; } diff --git a/packages/asset-swapper/test/dex_sampler_test.ts b/packages/asset-swapper/test/dex_sampler_test.ts index b267e0d170..8d2706b0e3 100644 --- a/packages/asset-swapper/test/dex_sampler_test.ts +++ b/packages/asset-swapper/test/dex_sampler_test.ts @@ -504,6 +504,7 @@ describe('DexSampler tests', () => { undefined, // sampler overrides provider, undefined, // balancer cache + undefined, // cream cache () => bancorService, ); const quotes = await dexOrderSampler.getBancorSellQuotesOffChainAsync( diff --git a/packages/asset-swapper/test/market_operation_utils_test.ts b/packages/asset-swapper/test/market_operation_utils_test.ts index facd6aebda..10e1e6455b 100644 --- a/packages/asset-swapper/test/market_operation_utils_test.ts +++ b/packages/asset-swapper/test/market_operation_utils_test.ts @@ -25,6 +25,7 @@ import { SOURCE_FLAGS, ZERO_AMOUNT, } from '../src/utils/market_operation_utils/constants'; +import { CreamPoolsCache } from '../src/utils/market_operation_utils/cream_utils'; import { createFills } from '../src/utils/market_operation_utils/fills'; import { DexOrderSampler } from '../src/utils/market_operation_utils/sampler'; import { BATCH_SOURCE_FILTERS } from '../src/utils/market_operation_utils/sampler_operations'; @@ -51,6 +52,7 @@ const DEFAULT_EXCLUDED = [ ERC20BridgeSource.SushiSwap, ERC20BridgeSource.MultiHop, ERC20BridgeSource.Shell, + ERC20BridgeSource.Cream, ]; const BUY_SOURCES = BUY_SOURCE_FILTER.sources; const SELL_SOURCES = SELL_SOURCE_FILTER.sources; @@ -288,6 +290,7 @@ describe('MarketOperationUtils tests', () => { [ERC20BridgeSource.SushiSwap]: _.times(NUM_SAMPLES, () => 0), [ERC20BridgeSource.MultiHop]: _.times(NUM_SAMPLES, () => 0), [ERC20BridgeSource.Shell]: _.times(NUM_SAMPLES, () => 0), + [ERC20BridgeSource.Cream]: _.times(NUM_SAMPLES, () => 0), }; const DEFAULT_RATES: RatesBySource = { @@ -334,6 +337,7 @@ describe('MarketOperationUtils tests', () => { [ERC20BridgeSource.Native]: { order: createOrder() }, [ERC20BridgeSource.MultiHop]: {}, [ERC20BridgeSource.Shell]: {}, + [ERC20BridgeSource.Cream]: { poolAddress: randomAddress() }, }; const DEFAULT_OPS = { @@ -370,6 +374,30 @@ describe('MarketOperationUtils tests', () => { DEFAULT_FILL_DATA[ERC20BridgeSource.Balancer], ), ], + getCreamSellQuotesOffChainAsync: ( + _makerToken: string, + _takerToken: string, + takerFillAmounts: BigNumber[], + ) => [ + createSamplesFromRates( + ERC20BridgeSource.Cream, + takerFillAmounts, + createDecreasingRates(takerFillAmounts.length), + DEFAULT_FILL_DATA[ERC20BridgeSource.Cream], + ), + ], + getCreamBuyQuotesOffChainAsync: ( + _makerToken: string, + _takerToken: string, + makerFillAmounts: BigNumber[], + ) => [ + createSamplesFromRates( + ERC20BridgeSource.Cream, + makerFillAmounts, + createDecreasingRates(makerFillAmounts.length).map(r => new BigNumber(1).div(r)), + DEFAULT_FILL_DATA[ERC20BridgeSource.Cream], + ), + ], getBancorSellQuotesOffChainAsync: (_makerToken: string, _takerToken: string, takerFillAmounts: BigNumber[]) => createSamplesFromRates( ERC20BridgeSource.Bancor, @@ -389,6 +417,7 @@ describe('MarketOperationUtils tests', () => { return ops; }, balancerPoolsCache: new BalancerPoolsCache(), + creamPoolsCache: new CreamPoolsCache(), } as any) as DexOrderSampler; function replaceSamplerOps(ops: Partial = {}): void { @@ -492,6 +521,14 @@ describe('MarketOperationUtils tests', () => { sourcesPolled = sourcesPolled.concat(ERC20BridgeSource.Balancer); return DEFAULT_OPS.getBalancerSellQuotesOffChainAsync(makerToken, takerToken, takerFillAmounts); }, + getCreamSellQuotesOffChainAsync: ( + makerToken: string, + takerToken: string, + takerFillAmounts: BigNumber[], + ) => { + sourcesPolled = sourcesPolled.concat(ERC20BridgeSource.Cream); + return DEFAULT_OPS.getCreamSellQuotesOffChainAsync(makerToken, takerToken, takerFillAmounts); + }, }); await marketOperationUtils.getMarketSellOrdersAsync(ORDERS, FILL_AMOUNT, { ...DEFAULT_OPTS, @@ -522,6 +559,14 @@ describe('MarketOperationUtils tests', () => { args.sources = args.sources.concat(ERC20BridgeSource.Balancer); return DEFAULT_OPS.getBalancerSellQuotesOffChainAsync(makerToken, takerToken, takerFillAmounts); }, + getCreamSellQuotesOffChainAsync: ( + makerToken: string, + takerToken: string, + takerFillAmounts: BigNumber[], + ) => { + args.sources = args.sources.concat(ERC20BridgeSource.Cream); + return DEFAULT_OPS.getCreamSellQuotesOffChainAsync(makerToken, takerToken, takerFillAmounts); + }, }); const registryAddress = randomAddress(); const newMarketOperationUtils = new MarketOperationUtils( @@ -563,6 +608,14 @@ describe('MarketOperationUtils tests', () => { sourcesPolled = sourcesPolled.concat(ERC20BridgeSource.Balancer); return DEFAULT_OPS.getBalancerSellQuotesOffChainAsync(makerToken, takerToken, takerFillAmounts); }, + getCreamSellQuotesOffChainAsync: ( + makerToken: string, + takerToken: string, + takerFillAmounts: BigNumber[], + ) => { + sourcesPolled = sourcesPolled.concat(ERC20BridgeSource.Cream); + return DEFAULT_OPS.getCreamSellQuotesOffChainAsync(makerToken, takerToken, takerFillAmounts); + }, }); await marketOperationUtils.getMarketSellOrdersAsync(ORDERS, FILL_AMOUNT, { ...DEFAULT_OPTS, @@ -594,6 +647,14 @@ describe('MarketOperationUtils tests', () => { sourcesPolled = sourcesPolled.concat(ERC20BridgeSource.Balancer); return DEFAULT_OPS.getBalancerSellQuotesOffChainAsync(makerToken, takerToken, takerFillAmounts); }, + getCreamSellQuotesOffChainAsync: ( + makerToken: string, + takerToken: string, + takerFillAmounts: BigNumber[], + ) => { + sourcesPolled = sourcesPolled.concat(ERC20BridgeSource.Cream); + return DEFAULT_OPS.getCreamSellQuotesOffChainAsync(makerToken, takerToken, takerFillAmounts); + }, }); await marketOperationUtils.getMarketSellOrdersAsync(ORDERS, FILL_AMOUNT, { ...DEFAULT_OPTS, @@ -984,6 +1045,14 @@ describe('MarketOperationUtils tests', () => { sourcesPolled = sourcesPolled.concat(ERC20BridgeSource.Balancer); return DEFAULT_OPS.getBalancerBuyQuotesOffChainAsync(makerToken, takerToken, makerFillAmounts); }, + getCreamBuyQuotesOffChainAsync: ( + makerToken: string, + takerToken: string, + makerFillAmounts: BigNumber[], + ) => { + sourcesPolled = sourcesPolled.concat(ERC20BridgeSource.Cream); + return DEFAULT_OPS.getCreamBuyQuotesOffChainAsync(makerToken, takerToken, makerFillAmounts); + }, }); await marketOperationUtils.getMarketBuyOrdersAsync(ORDERS, FILL_AMOUNT, { ...DEFAULT_OPTS, @@ -1014,6 +1083,14 @@ describe('MarketOperationUtils tests', () => { args.sources = args.sources.concat(ERC20BridgeSource.Balancer); return DEFAULT_OPS.getBalancerBuyQuotesOffChainAsync(makerToken, takerToken, makerFillAmounts); }, + getCreamBuyQuotesOffChainAsync: ( + makerToken: string, + takerToken: string, + makerFillAmounts: BigNumber[], + ) => { + args.sources = args.sources.concat(ERC20BridgeSource.Cream); + return DEFAULT_OPS.getCreamBuyQuotesOffChainAsync(makerToken, takerToken, makerFillAmounts); + }, }); const registryAddress = randomAddress(); const newMarketOperationUtils = new MarketOperationUtils( @@ -1055,6 +1132,14 @@ describe('MarketOperationUtils tests', () => { sourcesPolled = sourcesPolled.concat(ERC20BridgeSource.Balancer); return DEFAULT_OPS.getBalancerBuyQuotesOffChainAsync(makerToken, takerToken, makerFillAmounts); }, + getCreamBuyQuotesOffChainAsync: ( + makerToken: string, + takerToken: string, + makerFillAmounts: BigNumber[], + ) => { + sourcesPolled = sourcesPolled.concat(ERC20BridgeSource.Cream); + return DEFAULT_OPS.getCreamBuyQuotesOffChainAsync(makerToken, takerToken, makerFillAmounts); + }, }); await marketOperationUtils.getMarketBuyOrdersAsync(ORDERS, FILL_AMOUNT, { ...DEFAULT_OPTS, @@ -1086,6 +1171,14 @@ describe('MarketOperationUtils tests', () => { sourcesPolled = sourcesPolled.concat(ERC20BridgeSource.Balancer); return DEFAULT_OPS.getBalancerBuyQuotesOffChainAsync(makerToken, takerToken, makerFillAmounts); }, + getCreamBuyQuotesOffChainAsync: ( + makerToken: string, + takerToken: string, + makerFillAmounts: BigNumber[], + ) => { + sourcesPolled = sourcesPolled.concat(ERC20BridgeSource.Cream); + return DEFAULT_OPS.getCreamBuyQuotesOffChainAsync(makerToken, takerToken, makerFillAmounts); + }, }); await marketOperationUtils.getMarketBuyOrdersAsync(ORDERS, FILL_AMOUNT, { ...DEFAULT_OPTS, diff --git a/packages/migrations/src/migration.ts b/packages/migrations/src/migration.ts index 8c96994fba..bdc487dada 100644 --- a/packages/migrations/src/migration.ts +++ b/packages/migrations/src/migration.ts @@ -325,6 +325,7 @@ export async function runMigrationsAsync( uniswapExchangeFactory: NULL_ADDRESS, mStable: NULL_ADDRESS, shellBridge: NULL_ADDRESS, + creamBridge: NULL_ADDRESS, shell: NULL_ADDRESS, weth: etherToken.address, }, diff --git a/yarn.lock b/yarn.lock index f5a89efc2b..7c3a875798 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5510,6 +5510,14 @@ coveralls@^3.0.0: minimist "^1.2.0" request "^2.79.0" +cream-sor@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/cream-sor/-/cream-sor-0.3.3.tgz#ae7ab50c68cfd36a89e2101187ceebbb79e1b14c" + integrity sha512-taQSvCUunPhwyebwbjxh1l9NDp39lsre+Q2oRMK+gqzbu+Wlbg5GAquwoV2/GLgzia70gi4x1rJCJsUmc5Kygg== + dependencies: + bignumber.js "^9.0.0" + isomorphic-fetch "^2.2.1" + create-ecdh@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.1.tgz#44223dfed533193ba5ba54e0df5709b89acf1f82"