tidy up tests, add other tests

This commit is contained in:
Daniel Pyrathon 2020-02-28 12:12:58 -08:00
parent 7181be8768
commit e5df51a83a
6 changed files with 93 additions and 37 deletions

View File

@ -144,7 +144,7 @@ export class SwapQuoter {
* @return An instance of SwapQuoter * @return An instance of SwapQuoter
*/ */
constructor(supportedProvider: SupportedProvider, orderbook: Orderbook, options: Partial<SwapQuoterOpts> = {}) { constructor(supportedProvider: SupportedProvider, orderbook: Orderbook, options: Partial<SwapQuoterOpts> = {}) {
const { chainId, expiryBufferMs, permittedOrderFeeTypes, samplerGasLimit, plpAddress } = _.merge( const { chainId, expiryBufferMs, permittedOrderFeeTypes, samplerGasLimit, plpRegistryAddress } = _.merge(
{}, {},
constants.DEFAULT_SWAP_QUOTER_OPTS, constants.DEFAULT_SWAP_QUOTER_OPTS,
options, options,
@ -170,7 +170,7 @@ export class SwapQuoter {
this._marketOperationUtils = new MarketOperationUtils(sampler, this._contractAddresses, { this._marketOperationUtils = new MarketOperationUtils(sampler, this._contractAddresses, {
chainId, chainId,
exchangeAddress: this._contractAddresses.exchange, exchangeAddress: this._contractAddresses.exchange,
}, plpAddress); }, plpRegistryAddress);
this._swapQuoteCalculator = new SwapQuoteCalculator(this._protocolFeeUtils, this._marketOperationUtils); this._swapQuoteCalculator = new SwapQuoteCalculator(this._protocolFeeUtils, this._marketOperationUtils);
} }

View File

@ -212,7 +212,7 @@ export interface SwapQuoterOpts extends OrderPrunerOpts {
expiryBufferMs: number; expiryBufferMs: number;
contractAddresses?: ContractAddresses; contractAddresses?: ContractAddresses;
samplerGasLimit?: number; samplerGasLimit?: number;
plpAddress?: string; plpRegistryAddress?: string;
} }
/** /**

View File

@ -79,19 +79,21 @@ export class MarketOperationUtils {
makerToken.toLowerCase() === this._wethAddress.toLowerCase() makerToken.toLowerCase() === this._wethAddress.toLowerCase()
? DexOrderSampler.ops.constant(new BigNumber(1)) ? DexOrderSampler.ops.constant(new BigNumber(1))
: DexOrderSampler.ops.getMedianSellRate( : DexOrderSampler.ops.getMedianSellRate(
difference(FEE_QUOTE_SOURCES, _opts.excludedSources), difference(FEE_QUOTE_SOURCES, _opts.excludedSources).concat(this._plpSourceIfAvailable()),
makerToken, makerToken,
this._wethAddress, this._wethAddress,
ONE_ETHER, ONE_ETHER,
this._plpRegistryAddress, this._plpRegistryAddress,
), ),
DexOrderSampler.ops.getSellQuotes( DexOrderSampler.ops.getSellQuotes(
difference(SELL_SOURCES, _opts.excludedSources), difference(SELL_SOURCES, _opts.excludedSources).concat(this._plpSourceIfAvailable()),
makerToken, makerToken,
takerToken, takerToken,
getSampleAmounts(takerAmount, _opts.numSamples, _opts.sampleDistributionBase), getSampleAmounts(takerAmount, _opts.numSamples, _opts.sampleDistributionBase),
this._plpRegistryAddress,
), ),
); );
const nativeOrdersWithFillableAmounts = createSignedOrdersWithFillableAmounts( const nativeOrdersWithFillableAmounts = createSignedOrdersWithFillableAmounts(
nativeOrders, nativeOrders,
fillableAmounts, fillableAmounts,
@ -158,21 +160,24 @@ export class MarketOperationUtils {
...opts, ...opts,
}; };
const [makerToken, takerToken] = getOrderTokens(nativeOrders[0]); const [makerToken, takerToken] = getOrderTokens(nativeOrders[0]);
const [fillableAmounts, ethToTakerAssetRate, dexQuotes] = await this._sampler.executeAsync( const [fillableAmounts, plpPoolAddress, ethToTakerAssetRate, dexQuotes] = await this._sampler.executeAsync(
DexOrderSampler.ops.getOrderFillableMakerAmounts(nativeOrders), DexOrderSampler.ops.getOrderFillableMakerAmounts(nativeOrders),
DexOrderSampler.ops.getLiquidityProviderFromRegistry(this._plpRegistryAddress, takerToken, makerToken),
takerToken.toLowerCase() === this._wethAddress.toLowerCase() takerToken.toLowerCase() === this._wethAddress.toLowerCase()
? DexOrderSampler.ops.constant(new BigNumber(1)) ? DexOrderSampler.ops.constant(new BigNumber(1))
: DexOrderSampler.ops.getMedianSellRate( : DexOrderSampler.ops.getMedianSellRate(
difference(FEE_QUOTE_SOURCES, _opts.excludedSources), difference(FEE_QUOTE_SOURCES, _opts.excludedSources).concat(this._plpSourceIfAvailable()),
takerToken, takerToken,
this._wethAddress, this._wethAddress,
ONE_ETHER, ONE_ETHER,
this._plpRegistryAddress,
), ),
DexOrderSampler.ops.getBuyQuotes( DexOrderSampler.ops.getBuyQuotes(
difference(BUY_SOURCES, _opts.excludedSources), difference(BUY_SOURCES, _opts.excludedSources).concat(this._plpSourceIfAvailable()),
makerToken, makerToken,
takerToken, takerToken,
getSampleAmounts(makerAmount, _opts.numSamples, _opts.sampleDistributionBase), getSampleAmounts(makerAmount, _opts.numSamples, _opts.sampleDistributionBase),
this._plpRegistryAddress,
), ),
); );
const signedOrderWithFillableAmounts = this._createBuyOrdersPathFromSamplerResultIfExists( const signedOrderWithFillableAmounts = this._createBuyOrdersPathFromSamplerResultIfExists(
@ -182,6 +187,7 @@ export class MarketOperationUtils {
dexQuotes, dexQuotes,
ethToTakerAssetRate, ethToTakerAssetRate,
_opts, _opts,
plpPoolAddress,
); );
if (!signedOrderWithFillableAmounts) { if (!signedOrderWithFillableAmounts) {
throw new Error(AggregationError.NoOptimalPath); throw new Error(AggregationError.NoOptimalPath);
@ -192,6 +198,9 @@ export class MarketOperationUtils {
/** /**
* gets the orders required for a batch of market buy operations by (potentially) merging native orders with * gets the orders required for a batch of market buy operations by (potentially) merging native orders with
* generated bridge orders. * generated bridge orders.
*
* NOTE: Currently `getBatchMarketBuyOrdersAsync()` does not support external liquidity providers.
*
* @param batchNativeOrders Batch of Native orders. * @param batchNativeOrders Batch of Native orders.
* @param makerAmounts Array amount of maker asset to buy for each batch. * @param makerAmounts Array amount of maker asset to buy for each batch.
* @param opts Options object. * @param opts Options object.
@ -244,6 +253,10 @@ export class MarketOperationUtils {
); );
} }
private _plpSourceIfAvailable(): ERC20BridgeSource[] {
return this._plpRegistryAddress !== NULL_ADDRESS ? [ERC20BridgeSource.Plp] : [];
}
private _createBuyOrdersPathFromSamplerResultIfExists( private _createBuyOrdersPathFromSamplerResultIfExists(
nativeOrders: SignedOrder[], nativeOrders: SignedOrder[],
makerAmount: BigNumber, makerAmount: BigNumber,
@ -251,6 +264,7 @@ export class MarketOperationUtils {
dexQuotes: DexSample[][], dexQuotes: DexSample[][],
ethToTakerAssetRate: BigNumber, ethToTakerAssetRate: BigNumber,
opts: GetMarketOrdersOpts, opts: GetMarketOrdersOpts,
plpPoolAddress?: string | undefined,
): OptimizedMarketOrder[] | undefined { ): OptimizedMarketOrder[] | undefined {
const nativeOrdersWithFillableAmounts = createSignedOrdersWithFillableAmounts( const nativeOrdersWithFillableAmounts = createSignedOrdersWithFillableAmounts(
nativeOrders, nativeOrders,
@ -293,6 +307,7 @@ export class MarketOperationUtils {
outputToken, outputToken,
collapsePath(optimalPath, true), collapsePath(optimalPath, true),
opts.bridgeSlippage, opts.bridgeSlippage,
plpPoolAddress,
); );
} }
} }
@ -474,6 +489,9 @@ function sourceToFillFlags(source: ERC20BridgeSource): number {
if (source === ERC20BridgeSource.Uniswap) { if (source === ERC20BridgeSource.Uniswap) {
return FillFlags.SourceUniswap; return FillFlags.SourceUniswap;
} }
if (source === ERC20BridgeSource.Plp) {
return FillFlags.SourcePlp;
}
return FillFlags.SourceNative; return FillFlags.SourceNative;
} }

View File

@ -61,6 +61,7 @@ export enum FillFlags {
SourceUniswap = 0x2, SourceUniswap = 0x2,
SourceEth2Dai = 0x4, SourceEth2Dai = 0x4,
SourceKyber = 0x8, SourceKyber = 0x8,
SourcePlp = 0x10,
} }
/** /**

View File

@ -329,7 +329,7 @@ describe('DexSampler tests', () => {
expect(quotes).to.deep.eq(expectedQuotes); expect(quotes).to.deep.eq(expectedQuotes);
}); });
describe.only('PLP Operations', () => { describe('PLP Operations', () => {
const xAsset = randomAddress(); const xAsset = randomAddress();
const yAsset = randomAddress(); const yAsset = randomAddress();
const zAsset = randomAddress(); const zAsset = randomAddress();

View File

@ -21,6 +21,7 @@ import { constants as marketOperationUtilConstants } from '../src/utils/market_o
import { DexOrderSampler } from '../src/utils/market_operation_utils/sampler'; import { DexOrderSampler } from '../src/utils/market_operation_utils/sampler';
import { DexSample, ERC20BridgeSource } from '../src/utils/market_operation_utils/types'; import { DexSample, ERC20BridgeSource } from '../src/utils/market_operation_utils/types';
import { Web3Wrapper } from '@0x/dev-utils'; import { Web3Wrapper } from '@0x/dev-utils';
import { OnlyCallableIfNotInCatastrophicFailureError } from '@0x/utils/lib/src/revert_errors/staking/staking_revert_errors';
const { BUY_SOURCES, SELL_SOURCES } = marketOperationUtilConstants; const { BUY_SOURCES, SELL_SOURCES } = marketOperationUtilConstants;
@ -143,6 +144,7 @@ describe('MarketOperationUtils tests', () => {
makerToken: string, makerToken: string,
takerToken: string, takerToken: string,
fillAmounts: BigNumber[], fillAmounts: BigNumber[],
plpRegistryAddress?: string | undefined,
) => DexSample[][]; ) => DexSample[][];
function createGetMultipleSellQuotesOperationFromRates(rates: RatesBySource): GetMultipleQuotesOperation { function createGetMultipleSellQuotesOperationFromRates(rates: RatesBySource): GetMultipleQuotesOperation {
@ -157,6 +159,21 @@ describe('MarketOperationUtils tests', () => {
}; };
} }
function createGetMultipleSellQuotesOperationFromRatesAndRetainPLPParams(rates: RatesBySource): [{sources: ERC20BridgeSource[], plpRegistryAddress?: string}, GetMultipleQuotesOperation] {
const plpParams: {sources: ERC20BridgeSource[], plpRegistryAddress?: string} = {
sources: [],
plpRegistryAddress: undefined,
}
const fn = (sources: ERC20BridgeSource[], makerToken: string, takerToken: string, fillAmounts: BigNumber[], plpRegistryAddress: string | undefined) => {
plpParams.plpRegistryAddress = plpRegistryAddress;
plpParams.sources = sources;
return createGetMultipleSellQuotesOperationFromRates(rates)(
sources, makerToken, takerToken, fillAmounts, plpRegistryAddress,
);
};
return [plpParams, fn];
}
function createGetMultipleBuyQuotesOperationFromRates(rates: RatesBySource): GetMultipleQuotesOperation { function createGetMultipleBuyQuotesOperationFromRates(rates: RatesBySource): GetMultipleQuotesOperation {
return (sources: ERC20BridgeSource[], makerToken: string, takerToken: string, fillAmounts: BigNumber[]) => { return (sources: ERC20BridgeSource[], makerToken: string, takerToken: string, fillAmounts: BigNumber[]) => {
return sources.map(s => return sources.map(s =>
@ -174,6 +191,7 @@ describe('MarketOperationUtils tests', () => {
makerToken: string, makerToken: string,
takerToken: string, takerToken: string,
fillAmounts: BigNumber[], fillAmounts: BigNumber[],
plpRegistryAddress?: string | undefined,
) => BigNumber; ) => BigNumber;
type GetLiquidityProviderFromRegistryOperation = ( type GetLiquidityProviderFromRegistryOperation = (
@ -194,6 +212,21 @@ describe('MarketOperationUtils tests', () => {
} }
} }
function getLiquidityProviderFromRegistryAndReturnCallParameters(liquidityPoolAddress: string = NULL_ADDRESS): [{registryAddress?: string, takerToken?: string, makerToken?: string}, GetLiquidityProviderFromRegistryOperation] {
const callArgs: {registryAddress?: string, takerToken?: string, makerToken?: string} = {
registryAddress: undefined,
takerToken: undefined,
makerToken: undefined,
}
const fn = (registryAddress: string, takerToken: string, makerToken: string): string => {
callArgs.makerToken = makerToken;
callArgs.takerToken = takerToken;
callArgs.registryAddress = registryAddress;
return liquidityPoolAddress;
}
return [callArgs, fn];
}
function createDecreasingRates(count: number): BigNumber[] { function createDecreasingRates(count: number): BigNumber[] {
const rates: BigNumber[] = []; const rates: BigNumber[] = [];
const initialRate = getRandomFloat(1e-3, 1e2); const initialRate = getRandomFloat(1e-3, 1e2);
@ -218,6 +251,7 @@ describe('MarketOperationUtils tests', () => {
[ERC20BridgeSource.CurveUsdcDai]: _.times(NUM_SAMPLES, () => 0), [ERC20BridgeSource.CurveUsdcDai]: _.times(NUM_SAMPLES, () => 0),
[ERC20BridgeSource.CurveUsdcDaiUsdt]: _.times(NUM_SAMPLES, () => 0), [ERC20BridgeSource.CurveUsdcDaiUsdt]: _.times(NUM_SAMPLES, () => 0),
[ERC20BridgeSource.CurveUsdcDaiUsdtTusd]: _.times(NUM_SAMPLES, () => 0), [ERC20BridgeSource.CurveUsdcDaiUsdtTusd]: _.times(NUM_SAMPLES, () => 0),
[ERC20BridgeSource.Plp]: _.times(NUM_SAMPLES, () => 0),
}; };
function findSourceWithMaxOutput(rates: RatesBySource): ERC20BridgeSource { function findSourceWithMaxOutput(rates: RatesBySource): ERC20BridgeSource {
@ -331,6 +365,21 @@ describe('MarketOperationUtils tests', () => {
expect(sourcesPolled.sort()).to.deep.eq(SELL_SOURCES.slice().sort()); expect(sourcesPolled.sort()).to.deep.eq(SELL_SOURCES.slice().sort());
}); });
it('polls the liquidity provider when the registry is provided in the arguments', async () => {
const [args, fn] = createGetMultipleSellQuotesOperationFromRatesAndRetainPLPParams(DEFAULT_RATES);
replaceSamplerOps({
getSellQuotes: fn,
});
const registryAddress = randomAddress();
const newMarketOperationUtils = new MarketOperationUtils(MOCK_SAMPLER, contractAddresses, ORDER_DOMAIN, registryAddress);
await newMarketOperationUtils.getMarketSellOrdersAsync(ORDERS, FILL_AMOUNT, {
...DEFAULT_OPTS,
excludedSources: [],
});
expect(args.sources.sort()).to.deep.eq(SELL_SOURCES.concat([ERC20BridgeSource.Plp]).sort());
expect(args.plpRegistryAddress).to.eql(registryAddress);
})
it('does not poll DEXes in `excludedSources`', async () => { it('does not poll DEXes in `excludedSources`', async () => {
const excludedSources = _.sampleSize(SELL_SOURCES, _.random(1, SELL_SOURCES.length)); const excludedSources = _.sampleSize(SELL_SOURCES, _.random(1, SELL_SOURCES.length));
let sourcesPolled: ERC20BridgeSource[] = []; let sourcesPolled: ERC20BridgeSource[] = [];
@ -782,35 +831,22 @@ describe('MarketOperationUtils tests', () => {
expect(orderSources).to.deep.eq(expectedSources); expect(orderSources).to.deep.eq(expectedSources);
}); });
it.only('is able to create a order from PLP', async () => { it('is able to create a order from PLP', async () => {
const registryAddress = randomAddress(); const registryAddress = randomAddress();
const liquidityPoolAddress = randomAddress(); const liquidityPoolAddress = randomAddress();
const xAsset = randomAddress(); const xAsset = randomAddress();
const yAsset = randomAddress(); const yAsset = randomAddress();
const toSell = Web3Wrapper.toBaseUnitAmount(10, 18); const toSell = Web3Wrapper.toBaseUnitAmount(10, 18);
const swapAmount = Web3Wrapper.toBaseUnitAmount(10.5, 18);
const MOCK_SAMPLER = ({ const [getSellQuiotesParams, getSellQuotesFn] = createGetMultipleSellQuotesOperationFromRatesAndRetainPLPParams({
async executeAsync(...ops: any[]): Promise<any[]> { [ERC20BridgeSource.Plp]: createDecreasingRates(5),
return [ });
[new BigNumber(0)], const [getLiquidityProviderParams, getLiquidityProviderFn] = getLiquidityProviderFromRegistryAndReturnCallParameters(liquidityPoolAddress);
liquidityPoolAddress, replaceSamplerOps({
new BigNumber(1), getOrderFillableTakerAmounts: () => [new BigNumber(0)],
[ getSellQuotes: getSellQuotesFn,
[ getLiquidityProviderFromRegistry: getLiquidityProviderFn,
{ });
source: 'PLP',
output: swapAmount,
input: toSell,
},
],
],
]
},
async executeBatchAsync(ops: any[]): Promise<any[]> {
return ops;
},
} as any) as DexOrderSampler;
const sampler = new MarketOperationUtils(MOCK_SAMPLER, contractAddresses, ORDER_DOMAIN, registryAddress); const sampler = new MarketOperationUtils(MOCK_SAMPLER, contractAddresses, ORDER_DOMAIN, registryAddress);
const result = await sampler.getMarketSellOrdersAsync( const result = await sampler.getMarketSellOrdersAsync(
@ -821,6 +857,7 @@ describe('MarketOperationUtils tests', () => {
}), }),
], ],
Web3Wrapper.toBaseUnitAmount(10, 18), Web3Wrapper.toBaseUnitAmount(10, 18),
{excludedSources: SELL_SOURCES, numSamples: 4}
); );
expect(result.length).to.eql(1); expect(result.length).to.eql(1);
expect(result[0].makerAddress).to.eql(liquidityPoolAddress); expect(result[0].makerAddress).to.eql(liquidityPoolAddress);
@ -830,11 +867,11 @@ describe('MarketOperationUtils tests', () => {
expect(decodedAssetData.assetProxyId).to.eql(AssetProxyId.ERC20Bridge); expect(decodedAssetData.assetProxyId).to.eql(AssetProxyId.ERC20Bridge);
expect(decodedAssetData.bridgeAddress).to.eql(liquidityPoolAddress); expect(decodedAssetData.bridgeAddress).to.eql(liquidityPoolAddress);
expect(result[0].takerAssetAmount).to.bignumber.eql(toSell); expect(result[0].takerAssetAmount).to.bignumber.eql(toSell);
expect(getSellQuiotesParams.sources).contains(ERC20BridgeSource.Plp);
const makerAmountWithSlippage = swapAmount.times( expect(getSellQuiotesParams.plpRegistryAddress).is.eql(registryAddress);
1 - marketOperationUtilConstants.DEFAULT_GET_MARKET_ORDERS_OPTS.bridgeSlippage, expect(getLiquidityProviderParams.registryAddress).is.eql(registryAddress);
); expect(getLiquidityProviderParams.makerToken).is.eql(xAsset);
expect(result[0].makerAssetAmount).to.eql(makerAmountWithSlippage); expect(getLiquidityProviderParams.takerToken).is.eql(yAsset);
}); });
}); });
}); });