diff --git a/packages/asset-swapper/CHANGELOG.json b/packages/asset-swapper/CHANGELOG.json index 7a17537350..01adea1a91 100644 --- a/packages/asset-swapper/CHANGELOG.json +++ b/packages/asset-swapper/CHANGELOG.json @@ -13,6 +13,10 @@ { "note": "Added `getBatchMarketBuySwapQuoteForAssetDataAsync` on `SwapQuoter`", "pr": 2427 + }, + { + "note": "Add exponential sampling distribution and `sampleDistributionBase` option to `SwapQuoter`", + "pr": 2427 } ] }, 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 ca0e6d981f..03b36c73c8 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/constants.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/constants.ts @@ -24,12 +24,14 @@ export const SELL_SOURCES = [ERC20BridgeSource.Uniswap, ERC20BridgeSource.Eth2Da export const BUY_SOURCES = [ERC20BridgeSource.Uniswap, ERC20BridgeSource.Eth2Dai]; export const DEFAULT_GET_MARKET_ORDERS_OPTS: GetMarketOrdersOpts = { - runLimit: 4096, + // tslint:disable-next-line: custom-no-magic-numbers + runLimit: 2 ** 15, excludedSources: [], bridgeSlippage: 0.0005, - dustFractionThreshold: 0.01, - numSamples: 10, + dustFractionThreshold: 0.0025, + numSamples: 12, noConflicts: true, + sampleDistributionBase: 1.25, }; export const constants = { @@ -40,5 +42,5 @@ export const constants = { DEFAULT_GET_MARKET_ORDERS_OPTS, ERC20_PROXY_ID: '0xf47261b0', WALLET_SIGNATURE: '0x04', - SAMPLER_CONTRACT_GAS_LIMIT: 10e6, + SAMPLER_CONTRACT_GAS_LIMIT: 16e6, }; 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 2f64cc1ecd..a5bbf087e5 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/index.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/index.ts @@ -63,7 +63,7 @@ export class MarketOperationUtils { }; const [fillableAmounts, dexQuotes] = await this._dexSampler.getFillableAmountsAndSampleMarketSellAsync( nativeOrders, - DexOrderSampler.getSampleAmounts(takerAmount, _opts.numSamples), + DexOrderSampler.getSampleAmounts(takerAmount, _opts.numSamples, _opts.sampleDistributionBase), difference(SELL_SOURCES, _opts.excludedSources), ); const nativeOrdersWithFillableAmounts = createSignedOrdersWithFillableAmounts( @@ -78,7 +78,6 @@ export class MarketOperationUtils { _opts.dustFractionThreshold, ); const clippedNativePath = clipPathToInput(prunedNativePath, takerAmount); - const dexPaths = createPathsFromDexQuotes(dexQuotes, _opts.noConflicts); const allPaths = [...dexPaths]; const allFills = flattenDexPaths(dexPaths); @@ -134,7 +133,7 @@ export class MarketOperationUtils { const [fillableAmounts, dexQuotes] = await this._dexSampler.getFillableAmountsAndSampleMarketBuyAsync( nativeOrders, - DexOrderSampler.getSampleAmounts(makerAmount, _opts.numSamples), + DexOrderSampler.getSampleAmounts(makerAmount, _opts.numSamples, _opts.sampleDistributionBase), difference(BUY_SOURCES, _opts.excludedSources), ); const signedOrderWithFillableAmounts = this._createBuyOrdersPathFromSamplerResultIfExists( 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 3a2e81628f..078b5cd9cf 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/sampler.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/sampler.ts @@ -13,16 +13,14 @@ export class DexOrderSampler { /** * Generate sample amounts up to `maxFillAmount`. */ - public static getSampleAmounts(maxFillAmount: BigNumber, numSamples: number): BigNumber[] { - const amounts = []; - for (let i = 0; i < numSamples; i++) { - amounts.push( - maxFillAmount - .times(i + 1) - .div(numSamples) - .integerValue(BigNumber.ROUND_UP), - ); - } + public static getSampleAmounts(maxFillAmount: BigNumber, numSamples: number, expBase: number = 1): BigNumber[] { + const distribution = [...Array(numSamples)].map((v, i) => new BigNumber(expBase).pow(i)); + const stepSizes = distribution.map(d => d.div(BigNumber.sum(...distribution))); + const amounts = stepSizes.map((s, i) => { + return maxFillAmount + .times(BigNumber.sum(...[0, ...stepSizes.slice(0, i + 1)])) + .integerValue(BigNumber.ROUND_UP); + }); return amounts; } 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 20741162d4..4ffc55f496 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/types.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/types.ts @@ -114,4 +114,12 @@ export interface GetMarketOrdersOpts { * Default is 0.01 (100 basis points). */ dustFractionThreshold: number; + /** + * The exponential sampling distribution base. + * A value of 1 will result in evenly spaced samples. + * > 1 will result in more samples at lower sizes. + * < 1 will result in more samples at higher sizes. + * Default: 1.25. + */ + sampleDistributionBase: number; } diff --git a/packages/asset-swapper/src/utils/protocol_fee_utils.ts b/packages/asset-swapper/src/utils/protocol_fee_utils.ts index 7040202ba7..a505c7e2c2 100644 --- a/packages/asset-swapper/src/utils/protocol_fee_utils.ts +++ b/packages/asset-swapper/src/utils/protocol_fee_utils.ts @@ -9,9 +9,9 @@ export class ProtocolFeeUtils { public gasPriceEstimation: BigNumber; private readonly _gasPriceHeart: any; - constructor(gasPricePollingIntervalInMs: number) { + constructor(gasPricePollingIntervalInMs: number, initialGasPrice: BigNumber = constants.ZERO_AMOUNT) { this._gasPriceHeart = heartbeats.createHeart(gasPricePollingIntervalInMs); - this.gasPriceEstimation = constants.ZERO_AMOUNT; + this.gasPriceEstimation = initialGasPrice; this._initializeHeartBeat(); } diff --git a/packages/asset-swapper/test/exchange_swap_quote_consumer_test.ts b/packages/asset-swapper/test/exchange_swap_quote_consumer_test.ts index 1143f5d477..3321aaac5f 100644 --- a/packages/asset-swapper/test/exchange_swap_quote_consumer_test.ts +++ b/packages/asset-swapper/test/exchange_swap_quote_consumer_test.ts @@ -123,7 +123,7 @@ describe('ExchangeSwapQuoteConsumer', () => { }; const privateKey = devConstants.TESTRPC_PRIVATE_KEYS[userAddresses.indexOf(makerAddress)]; orderFactory = new OrderFactory(privateKey, defaultOrderParams); - protocolFeeUtils = new ProtocolFeeUtils(constants.PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS); + protocolFeeUtils = new ProtocolFeeUtils(constants.PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS, new BigNumber(1)); expectMakerAndTakerBalancesForTakerAssetAsync = expectMakerAndTakerBalancesAsyncFactory( erc20TakerTokenContract, makerAddress, diff --git a/packages/asset-swapper/test/forwarder_swap_quote_consumer_test.ts b/packages/asset-swapper/test/forwarder_swap_quote_consumer_test.ts index 06d902cb50..0113fe86ee 100644 --- a/packages/asset-swapper/test/forwarder_swap_quote_consumer_test.ts +++ b/packages/asset-swapper/test/forwarder_swap_quote_consumer_test.ts @@ -126,7 +126,7 @@ describe('ForwarderSwapQuoteConsumer', () => { }; const privateKey = devConstants.TESTRPC_PRIVATE_KEYS[userAddresses.indexOf(makerAddress)]; orderFactory = new OrderFactory(privateKey, defaultOrderParams); - protocolFeeUtils = new ProtocolFeeUtils(constants.PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS); + protocolFeeUtils = new ProtocolFeeUtils(constants.PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS, new BigNumber(1)); expectMakerAndTakerBalancesAsync = expectMakerAndTakerBalancesAsyncFactory( erc20TokenContract, makerAddress, diff --git a/packages/asset-swapper/test/market_operation_utils_test.ts b/packages/asset-swapper/test/market_operation_utils_test.ts index f7b5575044..0145930f09 100644 --- a/packages/asset-swapper/test/market_operation_utils_test.ts +++ b/packages/asset-swapper/test/market_operation_utils_test.ts @@ -306,7 +306,7 @@ describe('MarketOperationUtils tests', () => { _.times(3, () => SOURCE_RATES[ERC20BridgeSource.Native][0]), ); const DEFAULT_SAMPLER = createSamplerFromSellRates(SOURCE_RATES); - const DEFAULT_OPTS = { numSamples: 3, runLimit: 0 }; + const DEFAULT_OPTS = { numSamples: 3, runLimit: 0, sampleDistributionBase: 1 }; const defaultMarketOperationUtils = new MarketOperationUtils( DEFAULT_SAMPLER, contractAddresses, @@ -552,7 +552,7 @@ describe('MarketOperationUtils tests', () => { _.times(3, () => SOURCE_RATES[ERC20BridgeSource.Native][0]), ); const DEFAULT_SAMPLER = createSamplerFromBuyRates(SOURCE_RATES); - const DEFAULT_OPTS = { numSamples: 3, runLimit: 0 }; + const DEFAULT_OPTS = { numSamples: 3, runLimit: 0, sampleDistributionBase: 1 }; const defaultMarketOperationUtils = new MarketOperationUtils( DEFAULT_SAMPLER, contractAddresses, diff --git a/packages/asset-swapper/test/swap_quote_consumer_utils_test.ts b/packages/asset-swapper/test/swap_quote_consumer_utils_test.ts index 392f378dc1..5c80acaee0 100644 --- a/packages/asset-swapper/test/swap_quote_consumer_utils_test.ts +++ b/packages/asset-swapper/test/swap_quote_consumer_utils_test.ts @@ -119,7 +119,7 @@ describe('swapQuoteConsumerUtils', () => { }; const privateKey = devConstants.TESTRPC_PRIVATE_KEYS[userAddresses.indexOf(makerAddress)]; orderFactory = new OrderFactory(privateKey, defaultOrderParams); - protocolFeeUtils = new ProtocolFeeUtils(constants.PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS); + protocolFeeUtils = new ProtocolFeeUtils(constants.PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS, new BigNumber(1)); forwarderOrderFactory = new OrderFactory(privateKey, defaultForwarderOrderParams); swapQuoteConsumer = new SwapQuoteConsumer(provider, {