diff --git a/src/handlers/swap_handlers.ts b/src/handlers/swap_handlers.ts index 988018197a..5e3739fdb7 100644 --- a/src/handlers/swap_handlers.ts +++ b/src/handlers/swap_handlers.ts @@ -92,8 +92,8 @@ export class SwapHandlers { res.status(HttpStatus.OK).send({ records: filteredTokens }); } // tslint:disable-next-line:prefer-function-over-method - public async getTokenPricesAsync(_req: express.Request, res: express.Response): Promise { - const prices = await this._swapService.getTokenPricesAsync(); + public async getTokenPricesAsync(req: express.Request, res: express.Response): Promise { + const prices = await this._swapService.getTokenPricesAsync(req.query.sellToken || 'WETH'); res.status(HttpStatus.OK).send({ prices }); } } diff --git a/src/services/orderbook_service.ts b/src/services/orderbook_service.ts index 534530ff3a..8a013f8552 100644 --- a/src/services/orderbook_service.ts +++ b/src/services/orderbook_service.ts @@ -3,7 +3,7 @@ import { WSClient } from '@0x/mesh-rpc-client'; import { assetDataUtils } from '@0x/order-utils'; import { AssetPairsItem, OrdersRequestOpts, SignedOrder } from '@0x/types'; import * as _ from 'lodash'; -import { Connection } from 'typeorm'; +import { Connection, In } from 'typeorm'; import { SignedOrderEntity } from '../entities'; import { ValidationError } from '../errors'; @@ -75,7 +75,6 @@ export class OrderBookService { where: { takerAssetData: quoteAssetData, makerAssetData: baseAssetData }, }), ]); - console.log(bidSignedOrderEntities.length, askSignedOrderEntities.length); const bidApiOrders: APIOrder[] = (bidSignedOrderEntities as Array>) .map(orderUtils.deserializeOrderToAPIOrder) .sort((orderA, orderB) => orderUtils.compareBidOrder(orderA.order, orderB.order)); @@ -157,6 +156,23 @@ export class OrderBookService { const paginatedApiOrders = paginationUtils.paginate(apiOrders, page, perPage); return paginatedApiOrders; } + public async getBatchOrdersAsync( + page: number, + perPage: number, + makerAssetDatas: string[], + takerAssetDatas: string[], + ): Promise> { + const filterObject = { + makerAssetData: In(makerAssetDatas), + takerAssetData: In(takerAssetDatas), + }; + const signedOrderEntities = (await this._connection.manager.find(SignedOrderEntity, { + where: filterObject, + })) as Array>; + const apiOrders = _.map(signedOrderEntities, orderUtils.deserializeOrderToAPIOrder); + const paginatedApiOrders = paginationUtils.paginate(apiOrders, page, perPage); + return paginatedApiOrders; + } constructor(connection: Connection, meshClient?: WSClient) { this._meshClient = meshClient; this._connection = connection; diff --git a/src/services/swap_service.ts b/src/services/swap_service.ts index 8437cac637..8ca2750d34 100644 --- a/src/services/swap_service.ts +++ b/src/services/swap_service.ts @@ -18,7 +18,7 @@ import { logger } from '../logger'; import { TokenMetadatasForChains } from '../token_metadatas_for_networks'; import { CalculateSwapQuoteParams, GetSwapQuoteResponse } from '../types'; import { orderUtils } from '../utils/order_utils'; -import { findTokenDecimalsIfExists } from '../utils/token_metadata_utils'; +import { findTokenDecimalsIfExists, getTokenMetadataIfExists } from '../utils/token_metadata_utils'; export class SwapService { private readonly _provider: SupportedProvider; @@ -48,9 +48,9 @@ export class SwapService { from, } = params; const assetSwapperOpts = { + ...ASSET_SWAPPER_MARKET_ORDERS_OPTS, slippagePercentage, gasPrice: providedGasPrice, - ...ASSET_SWAPPER_MARKET_ORDERS_OPTS, }; if (sellAmount !== undefined) { swapQuote = await this._swapQuoter.getMarketSellSwapQuoteAsync( @@ -130,32 +130,40 @@ export class SwapService { return apiSwapQuote; } - public async getTokenPricesAsync(): Promise> { - const baseAssetSymbol = 'WETH'; - const unitAmount = new BigNumber(1); - const baseAsset = TokenMetadatasForChains.find(m => m.symbol === baseAssetSymbol); + public async getTokenPricesAsync(baseAssetSymbol: string): Promise> { + const baseAsset = getTokenMetadataIfExists(baseAssetSymbol, CHAIN_ID); if (!baseAsset) { throw new Error('Invalid Base Asset'); } - const takerAssetData = assetDataUtils.encodeERC20AssetData(baseAsset.tokenAddresses[CHAIN_ID]); // WETH + const unitAmount = new BigNumber(1); + const takerAssetData = assetDataUtils.encodeERC20AssetData(baseAsset.tokenAddress); const queryAssetData = TokenMetadatasForChains.filter(m => m.symbol !== baseAssetSymbol); - const tokenMetadataChunks = _.chunk(queryAssetData, 10); + const chunkSize = 10; + const assetDataChunks = _.chunk(queryAssetData, chunkSize); const allResults = _.flatten( await Promise.all( - tokenMetadataChunks.map(async chunk => { - const makerAssetDatas = chunk.map(m => + assetDataChunks.map(async a => { + const encodedAssetData = a.map(m => assetDataUtils.encodeERC20AssetData(m.tokenAddresses[CHAIN_ID]), ); - const amounts = chunk.map(m => Web3Wrapper.toBaseUnitAmount(unitAmount, m.decimals)); - return this._swapQuoter.getMultipleMarketBuySwapQuoteForAssetDataAsync( - makerAssetDatas, + const amounts = a.map(m => Web3Wrapper.toBaseUnitAmount(unitAmount, m.decimals)); + const quotes = await this._swapQuoter.getBatchMarketBuySwapQuoteForAssetDataAsync( + encodedAssetData, takerAssetData, amounts, - { slippagePercentage: 0 }, + { + ...ASSET_SWAPPER_MARKET_ORDERS_OPTS, + slippagePercentage: 0, + bridgeSlippage: 0, + numSamples: 1, + runLimit: 1, + }, ); + return quotes; }), ), ); + const prices = allResults.map((quote, i) => { if (!quote) { return { symbol: queryAssetData[i].symbol, price: ZERO }; @@ -163,9 +171,11 @@ export class SwapService { const buyTokenDecimals = queryAssetData[i].decimals; const sellTokenDecimals = baseAsset.decimals; const { makerAssetAmount, totalTakerAssetAmount } = quote.bestCaseQuoteInfo; - const makerAssetUnitAmount = Web3Wrapper.toUnitAmount(makerAssetAmount, buyTokenDecimals); - const takerAssetUnitAmount = Web3Wrapper.toUnitAmount(totalTakerAssetAmount, sellTokenDecimals); - const price = makerAssetUnitAmount.dividedBy(takerAssetUnitAmount).decimalPlaces(sellTokenDecimals); + const unitMakerAssetAmount = Web3Wrapper.toUnitAmount(makerAssetAmount, buyTokenDecimals); + const unitTakerAssetAMount = Web3Wrapper.toUnitAmount(totalTakerAssetAmount, sellTokenDecimals); + const price = unitMakerAssetAmount + .dividedBy(unitTakerAssetAMount) + .decimalPlaces(sellTokenDecimals); return { symbol: queryAssetData[i].symbol, price, diff --git a/src/token_metadatas_for_networks.ts b/src/token_metadatas_for_networks.ts index a369a2eaec..98db960e85 100644 --- a/src/token_metadatas_for_networks.ts +++ b/src/token_metadatas_for_networks.ts @@ -16,22 +16,22 @@ export interface TokenMetadataAndChainAddresses { // tslint:disable:max-file-line-count export const TokenMetadatasForChains: TokenMetadataAndChainAddresses[] = [ { - symbol: 'DAI', - name: 'Dai Stablecoin', + symbol: 'ZRX', + name: '0x Protocol Token', decimals: 18, tokenAddresses: { - [ChainId.Mainnet]: '0x6b175474e89094c44da98b954eedeac495271d0f', - [ChainId.Kovan]: '0xc4375b7de8af5a38a93548eb8453a498222c4ff2', - [ChainId.Ganache]: '0x34d402f14d58e001d8efbe6585051bf9706aa064', + [ChainId.Mainnet]: '0xe41d2489571d322189246dafa5ebde1f4699f498', + [ChainId.Kovan]: '0x2002d3812f58e35f0ea1ffbf80a75a38c32175fa', + [ChainId.Ganache]: '0x871dd7c2b4b25e1aa18728e9d5f2af4c4e431f5c', }, }, { - symbol: 'REP', - name: 'Augur Reputation', + symbol: 'DAI', + name: 'Dai Stablecoin', decimals: 18, tokenAddresses: { - [ChainId.Mainnet]: '0x1985365e9f78359a9B6AD760e32412f4a445E862', - [ChainId.Kovan]: '0xb18845c260f680d5b9d84649638813e342e4f8c9', + [ChainId.Mainnet]: '0x6b175474e89094c44da98b954eedeac495271d0f', + [ChainId.Kovan]: '0xc4375b7de8af5a38a93548eb8453a498222c4ff2', [ChainId.Ganache]: '0x34d402f14d58e001d8efbe6585051bf9706aa064', }, }, @@ -45,16 +45,6 @@ export const TokenMetadatasForChains: TokenMetadataAndChainAddresses[] = [ [ChainId.Ganache]: '0x0b1ba0af832d7c05fd64161e0db78e85978e8082', }, }, - { - symbol: 'ZRX', - name: '0x Protocol Token', - decimals: 18, - tokenAddresses: { - [ChainId.Mainnet]: '0xe41d2489571d322189246dafa5ebde1f4699f498', - [ChainId.Kovan]: '0x2002d3812f58e35f0ea1ffbf80a75a38c32175fa', - [ChainId.Ganache]: '0x871dd7c2b4b25e1aa18728e9d5f2af4c4e431f5c', - }, - }, { symbol: 'USDC', name: 'USD Coin', @@ -65,6 +55,16 @@ export const TokenMetadatasForChains: TokenMetadataAndChainAddresses[] = [ [ChainId.Ganache]: NULL_ADDRESS, }, }, + { + symbol: 'REP', + name: 'Augur Reputation', + decimals: 18, + tokenAddresses: { + [ChainId.Mainnet]: '0x1985365e9f78359a9B6AD760e32412f4a445E862', + [ChainId.Kovan]: '0xb18845c260f680d5b9d84649638813e342e4f8c9', + [ChainId.Ganache]: '0x34d402f14d58e001d8efbe6585051bf9706aa064', + }, + }, { symbol: 'BAT', name: 'Basic Attention Token', @@ -165,16 +165,6 @@ export const TokenMetadatasForChains: TokenMetadataAndChainAddresses[] = [ [ChainId.Ganache]: NULL_ADDRESS, }, }, - { - decimals: 18, - symbol: 'GNT', - name: 'Golem Network Token', - tokenAddresses: { - [ChainId.Mainnet]: '0xa74476443119a942de498590fe1f2454d7d4ac0d', - [ChainId.Kovan]: NULL_ADDRESS, - [ChainId.Ganache]: NULL_ADDRESS, - }, - }, { decimals: 18, symbol: 'OMG', @@ -245,16 +235,6 @@ export const TokenMetadatasForChains: TokenMetadataAndChainAddresses[] = [ [ChainId.Ganache]: NULL_ADDRESS, }, }, - { - decimals: 8, - symbol: 'AION', - name: 'Aion Network', - tokenAddresses: { - [ChainId.Mainnet]: '0x4ceda7906a5ed2179785cd3a40a69ee8bc99c466', - [ChainId.Kovan]: NULL_ADDRESS, - [ChainId.Ganache]: NULL_ADDRESS, - }, - }, { decimals: 18, symbol: 'GEN', @@ -300,7 +280,7 @@ export const TokenMetadatasForChains: TokenMetadataAndChainAddresses[] = [ symbol: 'MLN', name: 'Melon', tokenAddresses: { - [ChainId.Mainnet]: '0xbeb9ef514a379b997e0798fdcc901ee474b6d9a1', + [ChainId.Mainnet]: '0xec67005c4e498ec7f55e092bd1d35cbc47c91892', [ChainId.Kovan]: NULL_ADDRESS, [ChainId.Ganache]: NULL_ADDRESS, }, @@ -335,16 +315,6 @@ export const TokenMetadatasForChains: TokenMetadataAndChainAddresses[] = [ [ChainId.Ganache]: NULL_ADDRESS, }, }, - { - decimals: 18, - symbol: 'ICN', - name: 'ICONOMI', - tokenAddresses: { - [ChainId.Mainnet]: '0x888666ca69e0f178ded6d75b5726cee99a87d698', - [ChainId.Kovan]: NULL_ADDRESS, - [ChainId.Ganache]: NULL_ADDRESS, - }, - }, { decimals: 9, symbol: 'DGD', @@ -377,7 +347,7 @@ export const TokenMetadatasForChains: TokenMetadataAndChainAddresses[] = [ }, { decimals: 8, - symbol: 'cDAI', + symbol: 'cSAI', name: 'Compound Dai', tokenAddresses: { [ChainId.Mainnet]: '0x5d3a536e4d6dbd6114cc1ead35777bab948e3643', @@ -405,16 +375,6 @@ export const TokenMetadatasForChains: TokenMetadataAndChainAddresses[] = [ [ChainId.Ganache]: NULL_ADDRESS, }, }, - { - decimals: 8, - symbol: 'cREP', - name: 'Compound Augur', - tokenAddresses: { - [ChainId.Mainnet]: '0x158079ee67fce2f58472a96584a73c7ab9ac95c1', - [ChainId.Kovan]: NULL_ADDRESS, - [ChainId.Ganache]: NULL_ADDRESS, - }, - }, { decimals: 8, symbol: 'cUSDC', @@ -485,16 +445,6 @@ export const TokenMetadatasForChains: TokenMetadataAndChainAddresses[] = [ [ChainId.Ganache]: NULL_ADDRESS, }, }, - { - decimals: 18, - symbol: 'ICX', - name: 'ICON', - tokenAddresses: { - [ChainId.Mainnet]: '0xb5a5f22694352c15b00323844ad545abb2b11028', - [ChainId.Kovan]: NULL_ADDRESS, - [ChainId.Ganache]: NULL_ADDRESS, - }, - }, { decimals: 18, symbol: 'NMR', diff --git a/src/utils/order_store_db_adapter.ts b/src/utils/order_store_db_adapter.ts index fe15480629..d0df7b27d3 100644 --- a/src/utils/order_store_db_adapter.ts +++ b/src/utils/order_store_db_adapter.ts @@ -33,20 +33,30 @@ export class OrderStoreDbAdapter extends OrderStore { } // Currently not handling deletes as this is handled by Mesh } + public async getBatchOrderSetsForAssetsAsync( + makerAssetDatas: string[], + takerAssetDatas: string[], + ): Promise { + const { records: apiOrders } = await this._orderbookService.getBatchOrdersAsync( + FIRST_PAGE, + MAX_QUERY_SIZE, + makerAssetDatas, + takerAssetDatas, + ); + const orderSets: { [makerAssetData: string]: OrderSet } = {}; + makerAssetDatas.forEach(m => + takerAssetDatas.forEach(t => (orderSets[OrderStore.getKeyForAssetPair(m, t)] = new OrderSet())), + ); + await Promise.all( + apiOrders.map(async o => + orderSets[OrderStore.getKeyForAssetPair(o.order.makerAssetData, o.order.takerAssetData)].addAsync(o), + ), + ); + return Object.values(orderSets); + } // tslint:disable-next-line:prefer-function-over-method public async hasAsync(_assetPairKey: string): Promise { return true; - // const [assetA, assetB] = OrderStore.assetPairKeyToAssets(assetPairKey); - // const { bids, asks } = await this._orderbookService.getOrderBookAsync( - // FIRST_PAGE, - // MAX_QUERY_SIZE, - // assetA, - // assetB, - // ); - // return bids.total !== 0 || asks.total !== 0; - // const [assetA, assetB] = OrderStore.assetPairKeyToAssets(assetPairKey); - // const pairs = await this._orderbookService.getAssetPairsAsync(FIRST_PAGE, MAX_QUERY_SIZE, assetA, assetB); - // return pairs.total !== 0; } public async valuesAsync(assetPairKey: string): Promise { return Array.from((await this.getOrderSetForAssetPairAsync(assetPairKey)).values());