diff --git a/packages/asset-swapper/CHANGELOG.json b/packages/asset-swapper/CHANGELOG.json index 9ec1bf7e6e..33e1d02764 100644 --- a/packages/asset-swapper/CHANGELOG.json +++ b/packages/asset-swapper/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "16.49.7", + "changes": [ + { + "note": "Fix native order handling for very small quotes and bump `neon-router` dependency", + "pr": 425 + } + ] + }, { "version": "16.49.6", "changes": [ diff --git a/packages/asset-swapper/package.json b/packages/asset-swapper/package.json index 1a8a13804c..2a04e0095d 100644 --- a/packages/asset-swapper/package.json +++ b/packages/asset-swapper/package.json @@ -66,7 +66,7 @@ "@0x/contracts-zero-ex": "^0.30.1", "@0x/dev-utils": "^4.2.9", "@0x/json-schemas": "^6.3.0", - "@0x/neon-router": "^0.3.1", + "@0x/neon-router": "^0.3.2", "@0x/protocol-utils": "^1.10.1", "@0x/quote-server": "^6.0.6", "@0x/types": "^3.3.4", diff --git a/packages/asset-swapper/src/utils/market_operation_utils/fills.ts b/packages/asset-swapper/src/utils/market_operation_utils/fills.ts index 0cabbf240b..d86ba8406e 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/fills.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/fills.ts @@ -96,6 +96,7 @@ export function nativeOrdersToFills( outputAmountPerEth: BigNumber, inputAmountPerEth: BigNumber, fees: FeeSchedule, + filterNegativeAdjustedRateOrders: boolean = true, ): Fill[] { const sourcePathId = hexUtils.random(); // Create a single path from all orders. @@ -126,8 +127,8 @@ export function nativeOrdersToFills( side === MarketOperation.Sell ? clippedOutput.minus(outputPenalty) : clippedOutput.plus(outputPenalty); const adjustedRate = side === MarketOperation.Sell ? adjustedOutput.div(clippedInput) : clippedInput.div(adjustedOutput); - // Skip orders with rates that are <= 0. - if (adjustedRate.lte(0)) { + // Optionally skip orders with rates that are <= 0. + if (filterNegativeAdjustedRateOrders && adjustedRate.lte(0)) { continue; } fills.push({ diff --git a/packages/asset-swapper/src/utils/market_operation_utils/path_optimizer.ts b/packages/asset-swapper/src/utils/market_operation_utils/path_optimizer.ts index 76469464b0..1b7a85ba3c 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/path_optimizer.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/path_optimizer.ts @@ -5,9 +5,10 @@ import { BigNumber, hexUtils } from '@0x/utils'; import * as _ from 'lodash'; import { performance } from 'perf_hooks'; +import { DEFAULT_WARNING_LOGGER } from '../../constants'; import { MarketOperation, NativeOrderWithFillableAmounts } from '../../types'; -import { VIP_ERC20_BRIDGE_SOURCES_BY_CHAIN_ID, ZERO_AMOUNT } from '../market_operation_utils/constants'; +import { VIP_ERC20_BRIDGE_SOURCES_BY_CHAIN_ID, ZERO_AMOUNT } from './constants'; import { dexSamplesToFills, ethToOutputAmount, nativeOrdersToFills } from './fills'; import { DEFAULT_PATH_PENALTY_OPTS, Path, PathPenaltyOpts } from './path'; import { DexSample, ERC20BridgeSource, FeeSchedule, Fill, FillData, SamplerMetrics } from './types'; @@ -154,8 +155,10 @@ function findRoutesAndCreateOptimalPath( const inputs = []; const outputs = []; const outputFees = []; - for (let i = 1; i <= 13; i++) { - const fraction = i / 13; + // NOTE: We start at 0 here because the native order might be much larger than the amount + // By starting at 0 we make sure we can always use a portion of the native order to fill/partial fill + for (let i = 0; i <= 12; i++) { + const fraction = i / 12; const currentInput = BigNumber.min(normalizedOrderInput.times(fraction), normalizedOrderInput); const currentOutput = BigNumber.min(normalizedOrderOutput.times(fraction), normalizedOrderOutput); const id = `${ERC20BridgeSource.Native}-${serializedPaths.length}-${idx}-${i}`; @@ -210,7 +213,11 @@ function findRoutesAndCreateOptimalPath( const scale = input.dividedBy(totalRoutedAmount); for (const [routeInput, routeSamplesAndNativeOrders, outputAmount, sourcePathId] of routesAndSamplesAndOutputs) { - if (!routeInput || !routeSamplesAndNativeOrders || !outputAmount || !Number.isFinite(outputAmount)) { + if (!Number.isFinite(outputAmount)) { + DEFAULT_WARNING_LOGGER(rustArgs, `neon-router: invalid route outputAmount ${outputAmount}`); + return undefined; + } + if (!routeInput || !routeSamplesAndNativeOrders || !outputAmount) { continue; } // TODO(kimpers): [TKR-241] amounts are sometimes clipped in the router due to precision loss for number/f64 @@ -229,6 +236,7 @@ function findRoutesAndCreateOptimalPath( opts.outputAmountPerEth, opts.inputAmountPerEth, fees, + false, )[0] as Fill | undefined; // Note: If the order has an adjusted rate of less than or equal to 0 it will be skipped // and nativeFill will be `undefined` diff --git a/yarn.lock b/yarn.lock index 3277fe2ad0..b734e42e08 100644 --- a/yarn.lock +++ b/yarn.lock @@ -959,10 +959,10 @@ typedoc "~0.16.11" yargs "^10.0.3" -"@0x/neon-router@^0.3.1": - version "0.3.1" - resolved "https://registry.yarnpkg.com/@0x/neon-router/-/neon-router-0.3.1.tgz#4ec13e750d1435357c4928d7f2521a2b4376f27e" - integrity sha512-M4ypTov9KyxsGJpYwobrld3Y2JOlR7U0XjR6BEQE2gQ1k3nie/1wNEI2J4ZjKw++RLDxdv/RCqhgA5VnINzjxA== +"@0x/neon-router@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@0x/neon-router/-/neon-router-0.3.2.tgz#dc68d0a108060d607b48e3d32ce0ff46f8dc0cc2" + integrity sha512-AdSPeCxRcjdpmWDkJI1wg+X4q14tmLE21vM0AixtMQQI5+f22sIeUCrPqU9FFKqMQTOW0/3d8tVXzxdollahbA== dependencies: "@mapbox/node-pre-gyp" "^1.0.5"