Integrated CREAM into asset-swapper

This commit is contained in:
Alex Kroeger 2020-10-13 20:27:08 -07:00
parent c6b9ea5723
commit c72aa653e8
14 changed files with 357 additions and 7 deletions

View File

@ -65,6 +65,10 @@
{
"note": "Added `Shell` into FQT",
"pr": 2722
},
{
"note": "Added `CREAM` into FQT",
"pr": 2715
}
]
},

View File

@ -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,

View File

@ -24,6 +24,7 @@ contract MixinAdapterAddresses
struct AdapterAddresses {
// Bridges
address balancerBridge;
address creamBridge;
address curveBridge;
address kyberBridge;
address mooniswapBridge;

View File

@ -75,6 +75,7 @@ blockchainTests.resets('FillQuoteTransformer', env => {
weth: NULL_ADDRESS,
shellBridge: NULL_ADDRESS,
shell: NULL_ADDRESS,
creamBridge: NULL_ADDRESS,
},
);
transformer = await FillQuoteTransformerContract.deployFrom0xArtifactAsync(

View File

@ -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",

View File

@ -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<BalancerPool[]> {
const timeout = new Promise<BalancerPool[]>(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<BalancerPool[]> {
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<BalancerPool[]> {
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 [];
}
}
}

View File

@ -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,

View File

@ -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. */

View File

@ -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<CreamFillData> {
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<CreamFillData> {
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<Array<Array<DexSample<CreamFillData>>>> {
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<Array<Array<DexSample<BalancerFillData>>>> {
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:

View File

@ -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[];
}

View File

@ -504,6 +504,7 @@ describe('DexSampler tests', () => {
undefined, // sampler overrides
provider,
undefined, // balancer cache
undefined, // cream cache
() => bancorService,
);
const quotes = await dexOrderSampler.getBancorSellQuotesOffChainAsync(

View File

@ -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<typeof DEFAULT_OPS> = {}): 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,

View File

@ -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,
},

View File

@ -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"