From 74d3b9334cc320ab7a4a32b571882b8c18f838c6 Mon Sep 17 00:00:00 2001 From: David Sun Date: Tue, 4 Feb 2020 04:03:28 -0500 Subject: [PATCH] Add liquidity source breakdown to asset-swapper (#2465) * add liquidity source breakdown to asset-swapper * remove debug line * use OptimizedMarketOrder metadata * updated change-log * prettier + lint * bug fixes * fixes * Prettier * Fix types Co-authored-by: Jacob Evans --- packages/asset-swapper/CHANGELOG.json | 6 +++- packages/asset-swapper/src/index.ts | 1 + packages/asset-swapper/src/types.ts | 10 +++++- .../src/utils/swap_quote_calculator.ts | 35 ++++++++++++++++++- .../test/swap_quote_calculator_test.ts | 32 ++++++++--------- .../asset-swapper/test/utils/swap_quote.ts | 6 ++++ 6 files changed, 71 insertions(+), 19 deletions(-) diff --git a/packages/asset-swapper/CHANGELOG.json b/packages/asset-swapper/CHANGELOG.json index a0b92836e5..2b8fed53c4 100644 --- a/packages/asset-swapper/CHANGELOG.json +++ b/packages/asset-swapper/CHANGELOG.json @@ -1,10 +1,14 @@ [ { - "version": "4.0.2", + "version": "4.1.0", "changes": [ { "note": "Allow contract addresses to be passed as optional constructor ags instead of hardcoding", "pr": 2461 + }, + { + "note": "Add swap quote liquidity source breakdown", + "pr": 2465 } ] }, diff --git a/packages/asset-swapper/src/index.ts b/packages/asset-swapper/src/index.ts index 8e0dbd9f7f..fd5d5a2a4c 100644 --- a/packages/asset-swapper/src/index.ts +++ b/packages/asset-swapper/src/index.ts @@ -54,6 +54,7 @@ export { SwapQuoterOpts, SwapQuoteConsumerError, SignedOrderWithFillableAmounts, + SwapQuoteOrdersBreakdown, } from './types'; export { ERC20BridgeSource, diff --git a/packages/asset-swapper/src/types.ts b/packages/asset-swapper/src/types.ts index 68120224ff..a31953522a 100644 --- a/packages/asset-swapper/src/types.ts +++ b/packages/asset-swapper/src/types.ts @@ -1,4 +1,4 @@ -import { ContractAddresses } from '@0x/migrations'; +import { ContractAddresses } from '@0x/contract-wrappers'; import { SignedOrder } from '@0x/types'; import { BigNumber } from '@0x/utils'; @@ -142,6 +142,7 @@ export interface SwapQuoteBase { orders: SignedOrder[]; bestCaseQuoteInfo: SwapQuoteInfo; worstCaseQuoteInfo: SwapQuoteInfo; + sourceBreakdown: SwapQuoteOrdersBreakdown; } /** @@ -177,6 +178,13 @@ export interface SwapQuoteInfo { protocolFeeInWeiAmount: BigNumber; } +/** + * percentage breakdown of each liquidity source used in quote + */ +export interface SwapQuoteOrdersBreakdown { + [source: string]: BigNumber; +} + /** * slippagePercentage: The percentage buffer to add to account for slippage. Affects max ETH price estimates. Defaults to 0.2 (20%). * gasPrice: gas price to determine protocolFee amount, default to ethGasStation fast amount diff --git a/packages/asset-swapper/src/utils/swap_quote_calculator.ts b/packages/asset-swapper/src/utils/swap_quote_calculator.ts index 1ed0e2972f..71ee57c55c 100644 --- a/packages/asset-swapper/src/utils/swap_quote_calculator.ts +++ b/packages/asset-swapper/src/utils/swap_quote_calculator.ts @@ -11,7 +11,9 @@ import { MarketSellSwapQuote, SignedOrderWithFillableAmounts, SwapQuote, + SwapQuoteBase, SwapQuoteInfo, + SwapQuoteOrdersBreakdown, } from '../types'; import { fillableAmountsUtils } from './fillable_amounts_utils'; @@ -176,7 +178,9 @@ export class SwapQuoteCalculator { true, ); - const quoteBase = { + const breakdown = this._getSwapQuoteOrdersBreakdown(resultOrders, operation); + + const quoteBase: SwapQuoteBase = { takerAssetData, makerAssetData, // Remove fill metadata. @@ -184,6 +188,7 @@ export class SwapQuoteCalculator { bestCaseQuoteInfo, worstCaseQuoteInfo, gasPrice, + sourceBreakdown: breakdown, }; if (operation === MarketOperation.Buy) { @@ -398,6 +403,34 @@ export class SwapQuoteCalculator { protocolFeeInWeiAmount, }; } + + // tslint:disable-next-line: prefer-function-over-method + private _getSwapQuoteOrdersBreakdown( + orders: OptimizedMarketOrder[], + operation: MarketOperation, + ): SwapQuoteOrdersBreakdown { + // HACK: to shut up linter + const breakdown: SwapQuoteOrdersBreakdown = {}; + + // total asset amount (accounting for slippage protection) + const totalAssetAmount = BigNumber.sum( + ...[ + constants.ZERO_AMOUNT, + ...orders.map(o => (operation === MarketOperation.Buy ? o.makerAssetAmount : o.takerAssetAmount)), + ], + ); + + return orders.reduce((acc: SwapQuoteOrdersBreakdown, order: OptimizedMarketOrder): SwapQuoteOrdersBreakdown => { + const assetAmount = operation === MarketOperation.Buy ? order.makerAssetAmount : order.takerAssetAmount; + const { source } = order.fill; + return { + ...acc, + ...{ + [source]: (!!acc[source] ? acc[source].plus(assetAmount) : assetAmount).dividedBy(totalAssetAmount), + }, + }; + }, breakdown); + } } function getTakerAssetAmountBreakDown( diff --git a/packages/asset-swapper/test/swap_quote_calculator_test.ts b/packages/asset-swapper/test/swap_quote_calculator_test.ts index 8293777a42..cf8abb4773 100644 --- a/packages/asset-swapper/test/swap_quote_calculator_test.ts +++ b/packages/asset-swapper/test/swap_quote_calculator_test.ts @@ -98,7 +98,7 @@ describe('swapQuoteCalculator', () => { // exchangeAddress: contractAddresses.exchange, // chainId: TESTRPC_CHAIN_ID, // }); - // const swapQuoteCalculator = new SwapQuoteCalculator(protocolFeeUtils, marketOperationUtils); + // const swapQuoteCalculator = new SwapQuoteCalculator( protocolFeeUtils, marketOperationUtils); // const errorFunction = async () => { // await swapQuoteCalculator.calculateMarketSellSwapQuoteAsync( // testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEELESS, @@ -117,7 +117,7 @@ describe('swapQuoteCalculator', () => { // exchangeAddress: contractAddresses.exchange, // chainId: TESTRPC_CHAIN_ID, // }); - // const swapQuoteCalculator = new SwapQuoteCalculator(protocolFeeUtils, marketOperationUtils); + // const swapQuoteCalculator = new SwapQuoteCalculator( protocolFeeUtils, marketOperationUtils); // await swapQuoteCalculator.calculateMarketSellSwapQuoteAsync( // testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEELESS, // baseUnitAmount(10), @@ -134,7 +134,7 @@ describe('swapQuoteCalculator', () => { // exchangeAddress: contractAddresses.exchange, // chainId: TESTRPC_CHAIN_ID, // }); - // const swapQuoteCalculator = new SwapQuoteCalculator(protocolFeeUtils, marketOperationUtils); + // const swapQuoteCalculator = new SwapQuoteCalculator( protocolFeeUtils, marketOperationUtils); // const errorFunction = async () => { // await swapQuoteCalculator.calculateMarketSellSwapQuoteAsync( // testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_TAKER_ASSET, @@ -152,7 +152,7 @@ describe('swapQuoteCalculator', () => { // exchangeAddress: contractAddresses.exchange, // chainId: TESTRPC_CHAIN_ID, // }); - // const swapQuoteCalculator = new SwapQuoteCalculator(protocolFeeUtils, marketOperationUtils); + // const swapQuoteCalculator = new SwapQuoteCalculator( protocolFeeUtils, marketOperationUtils); // const errorFunction = async () => { // await swapQuoteCalculator.calculateMarketSellSwapQuoteAsync( // testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_TAKER_ASSET, @@ -170,7 +170,7 @@ describe('swapQuoteCalculator', () => { // exchangeAddress: contractAddresses.exchange, // chainId: TESTRPC_CHAIN_ID, // }); - // const swapQuoteCalculator = new SwapQuoteCalculator(protocolFeeUtils, marketOperationUtils); + // const swapQuoteCalculator = new SwapQuoteCalculator( protocolFeeUtils, marketOperationUtils); // const errorFunction = async () => { // await swapQuoteCalculator.calculateMarketSellSwapQuoteAsync( // testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_MAKER_ASSET, @@ -188,7 +188,7 @@ describe('swapQuoteCalculator', () => { // exchangeAddress: contractAddresses.exchange, // chainId: TESTRPC_CHAIN_ID, // }); - // const swapQuoteCalculator = new SwapQuoteCalculator(protocolFeeUtils, marketOperationUtils); + // const swapQuoteCalculator = new SwapQuoteCalculator( protocolFeeUtils, marketOperationUtils); // const errorFunction = async () => { // await swapQuoteCalculator.calculateMarketSellSwapQuoteAsync( // testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_MAKER_ASSET, @@ -206,7 +206,7 @@ describe('swapQuoteCalculator', () => { // exchangeAddress: contractAddresses.exchange, // chainId: TESTRPC_CHAIN_ID, // }); - // const swapQuoteCalculator = new SwapQuoteCalculator(protocolFeeUtils, marketOperationUtils); + // const swapQuoteCalculator = new SwapQuoteCalculator( protocolFeeUtils, marketOperationUtils); // const errorFunction = async () => { // await swapQuoteCalculator.calculateMarketSellSwapQuoteAsync( // MIXED_TEST_ORDERS, @@ -224,7 +224,7 @@ describe('swapQuoteCalculator', () => { // exchangeAddress: contractAddresses.exchange, // chainId: TESTRPC_CHAIN_ID, // }); - // const swapQuoteCalculator = new SwapQuoteCalculator(protocolFeeUtils, marketOperationUtils); + // const swapQuoteCalculator = new SwapQuoteCalculator( protocolFeeUtils, marketOperationUtils); // const errorFunction = async () => { // await swapQuoteCalculator.calculateMarketSellSwapQuoteAsync( // MIXED_TEST_ORDERS, @@ -485,7 +485,7 @@ describe('swapQuoteCalculator', () => { // exchangeAddress: contractAddresses.exchange, // chainId: TESTRPC_CHAIN_ID, // }); - // const swapQuoteCalculator = new SwapQuoteCalculator(protocolFeeUtils, marketOperationUtils); + // const swapQuoteCalculator = new SwapQuoteCalculator( protocolFeeUtils, marketOperationUtils); // const errorFunction = async () => { // await swapQuoteCalculator.calculateMarketBuySwapQuoteAsync( // testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEELESS, @@ -503,7 +503,7 @@ describe('swapQuoteCalculator', () => { // exchangeAddress: contractAddresses.exchange, // chainId: TESTRPC_CHAIN_ID, // }); - // const swapQuoteCalculator = new SwapQuoteCalculator(protocolFeeUtils, marketOperationUtils); + // const swapQuoteCalculator = new SwapQuoteCalculator( protocolFeeUtils, marketOperationUtils); // const errorFunction = async () => { // await swapQuoteCalculator.calculateMarketBuySwapQuoteAsync( // testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEELESS, @@ -521,7 +521,7 @@ describe('swapQuoteCalculator', () => { // exchangeAddress: contractAddresses.exchange, // chainId: TESTRPC_CHAIN_ID, // }); - // const swapQuoteCalculator = new SwapQuoteCalculator(protocolFeeUtils, marketOperationUtils); + // const swapQuoteCalculator = new SwapQuoteCalculator( protocolFeeUtils, marketOperationUtils); // const errorFunction = async () => { // await swapQuoteCalculator.calculateMarketBuySwapQuoteAsync( // testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_TAKER_ASSET, @@ -539,7 +539,7 @@ describe('swapQuoteCalculator', () => { // exchangeAddress: contractAddresses.exchange, // chainId: TESTRPC_CHAIN_ID, // }); - // const swapQuoteCalculator = new SwapQuoteCalculator(protocolFeeUtils, marketOperationUtils); + // const swapQuoteCalculator = new SwapQuoteCalculator( protocolFeeUtils, marketOperationUtils); // const errorFunction = async () => { // await swapQuoteCalculator.calculateMarketBuySwapQuoteAsync( // testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_TAKER_ASSET, @@ -557,7 +557,7 @@ describe('swapQuoteCalculator', () => { // exchangeAddress: contractAddresses.exchange, // chainId: TESTRPC_CHAIN_ID, // }); - // const swapQuoteCalculator = new SwapQuoteCalculator(protocolFeeUtils, marketOperationUtils); + // const swapQuoteCalculator = new SwapQuoteCalculator( protocolFeeUtils, marketOperationUtils); // const errorFunction = async () => { // await swapQuoteCalculator.calculateMarketBuySwapQuoteAsync( // testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_MAKER_ASSET, @@ -575,7 +575,7 @@ describe('swapQuoteCalculator', () => { // exchangeAddress: contractAddresses.exchange, // chainId: TESTRPC_CHAIN_ID, // }); - // const swapQuoteCalculator = new SwapQuoteCalculator(protocolFeeUtils, marketOperationUtils); + // const swapQuoteCalculator = new SwapQuoteCalculator( protocolFeeUtils, marketOperationUtils); // const errorFunction = async () => { // await swapQuoteCalculator.calculateMarketBuySwapQuoteAsync( // testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_MAKER_ASSET, @@ -593,7 +593,7 @@ describe('swapQuoteCalculator', () => { // exchangeAddress: contractAddresses.exchange, // chainId: TESTRPC_CHAIN_ID, // }); - // const swapQuoteCalculator = new SwapQuoteCalculator(protocolFeeUtils, marketOperationUtils); + // const swapQuoteCalculator = new SwapQuoteCalculator( protocolFeeUtils, marketOperationUtils); // const errorFunction = async () => { // await swapQuoteCalculator.calculateMarketBuySwapQuoteAsync( // MIXED_TEST_ORDERS, @@ -611,7 +611,7 @@ describe('swapQuoteCalculator', () => { // exchangeAddress: contractAddresses.exchange, // chainId: TESTRPC_CHAIN_ID, // }); - // const swapQuoteCalculator = new SwapQuoteCalculator(protocolFeeUtils, marketOperationUtils); + // const swapQuoteCalculator = new SwapQuoteCalculator( protocolFeeUtils, marketOperationUtils); // const errorFunction = async () => { // await swapQuoteCalculator.calculateMarketBuySwapQuoteAsync( // MIXED_TEST_ORDERS, diff --git a/packages/asset-swapper/test/utils/swap_quote.ts b/packages/asset-swapper/test/utils/swap_quote.ts index 0b08bd187c..7321745f9d 100644 --- a/packages/asset-swapper/test/utils/swap_quote.ts +++ b/packages/asset-swapper/test/utils/swap_quote.ts @@ -1,6 +1,7 @@ import { BigNumber } from '@0x/utils'; import * as _ from 'lodash'; +import { ERC20BridgeSource } from '../../src'; import { constants } from '../../src/constants'; import { MarketOperation, SignedOrderWithFillableAmounts, SwapQuote } from '../../src/types'; import { ProtocolFeeUtils } from '../../src/utils/protocol_fee_utils'; @@ -26,6 +27,10 @@ export async function getFullyFillableSwapQuoteWithNoFeesAsync( protocolFeeInWeiAmount: await protocolFeeUtils.calculateWorstCaseProtocolFeeAsync(orders, gasPrice), }; + const breakdown = { + [ERC20BridgeSource.Native]: new BigNumber(1), + }; + const quoteBase = { makerAssetData, takerAssetData, @@ -33,6 +38,7 @@ export async function getFullyFillableSwapQuoteWithNoFeesAsync( gasPrice, bestCaseQuoteInfo: quoteInfo, worstCaseQuoteInfo: quoteInfo, + sourceBreakdown: breakdown, }; if (operation === MarketOperation.Buy) {