From 6091ee732d208eaf9889087b8308dfd0427b9be5 Mon Sep 17 00:00:00 2001 From: fragosti Date: Wed, 31 Oct 2018 19:20:37 -0700 Subject: feat: modify public API to allow for passing in available assets, or fetch assets from SRA --- packages/instant/public/index.html | 8 +++-- .../instant/src/components/zero_ex_instant.tsx | 28 ++++++++++----- packages/instant/src/index.umd.ts | 6 ++-- packages/instant/src/redux/actions.ts | 4 ++- packages/instant/src/redux/async_data.ts | 25 +++++++++---- packages/instant/src/redux/reducer.ts | 9 ++++- packages/instant/src/util/assert.ts | 8 ++--- packages/instant/src/util/asset.ts | 41 ++++++++++++++++++++-- 8 files changed, 100 insertions(+), 29 deletions(-) (limited to 'packages') diff --git a/packages/instant/public/index.html b/packages/instant/public/index.html index 9f1dfdb64..74975678a 100644 --- a/packages/instant/public/index.html +++ b/packages/instant/public/index.html @@ -55,15 +55,17 @@ }; const queryParams = new Uri(window.location.search); const renderOptionsDefaults = { - liquiditySource: 'https://api.radarrelay.com/0x/v2/', + orderSource: 'https://api.radarrelay.com/0x/v2/', assetData: '0xf47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498', } - const liquiditySourceOverride = queryParams.getQueryParamValue('liquiditySource'); + const orderSourceOverride = queryParams.getQueryParamValue('orderSource'); const renderOptionsOverrides = { - liquiditySource: liquiditySourceOverride === 'provided' ? [providedOrder] : liquiditySourceOverride, + orderSource: orderSourceOverride === 'provided' ? [providedOrder] : orderSourceOverride, assetData: queryParams.getQueryParamValue('assetData'), networkId: +queryParams.getQueryParamValue('networkId') || undefined, defaultAssetBuyAmount: +queryParams.getQueryParamValue('defaultAssetBuyAmount') || undefined, + availableAssetIdentifiers: JSON.parse(queryParams.getQueryParamValue('availableAssetIdentifiers') || '[]'), + defaultSelectedAssetIdentifier: queryParams.getQueryParamValue('defaultSelectedAssetIdentifier'), } const renderOptions = Object.assign({}, renderOptionsDefaults, removeUndefined(renderOptionsOverrides)); zeroExInstant.render(renderOptions); diff --git a/packages/instant/src/components/zero_ex_instant.tsx b/packages/instant/src/components/zero_ex_instant.tsx index d54dfc153..ab6350010 100644 --- a/packages/instant/src/components/zero_ex_instant.tsx +++ b/packages/instant/src/components/zero_ex_instant.tsx @@ -24,13 +24,13 @@ fonts.include(); export type ZeroExInstantProps = ZeroExInstantRequiredProps & Partial; export interface ZeroExInstantRequiredProps { - // TODO: Change API when we allow the selection of different assetDatas - assetData: string; - liquiditySource: string | SignedOrder[]; + orderSource: string | SignedOrder[]; } export interface ZeroExInstantOptionalProps { + availableAssetDatas: string[]; defaultAssetBuyAmount?: number; + defaultSelectedAssetData?: string; additionalAssetMetaDataMap: ObjectMap; networkId: Network; } @@ -45,14 +45,14 @@ export class ZeroExInstant extends React.Component { networkId, }; let assetBuyer; - if (_.isString(props.liquiditySource)) { + if (_.isString(props.orderSource)) { assetBuyer = AssetBuyer.getAssetBuyerForStandardRelayerAPIUrl( provider, - props.liquiditySource, + props.orderSource, assetBuyerOptions, ); } else { - assetBuyer = AssetBuyer.getAssetBuyerForProvidedOrders(provider, props.liquiditySource, assetBuyerOptions); + assetBuyer = AssetBuyer.getAssetBuyerForProvidedOrders(provider, props.orderSource, assetBuyerOptions); } const completeAssetMetaDataMap = { ...props.additionalAssetMetaDataMap, @@ -62,7 +62,13 @@ export class ZeroExInstant extends React.Component { ...state, assetBuyer, network: networkId, - selectedAsset: assetUtils.createAssetFromAssetData(props.assetData, completeAssetMetaDataMap, networkId), + selectedAsset: _.isUndefined(props.defaultSelectedAssetData) + ? undefined + : assetUtils.createAssetFromAssetDataOrThrow( + props.defaultSelectedAssetData, + completeAssetMetaDataMap, + networkId, + ), selectedAssetAmount: _.isUndefined(props.defaultAssetBuyAmount) ? state.selectedAssetAmount : new BigNumberInput(props.defaultAssetBuyAmount), @@ -77,8 +83,14 @@ export class ZeroExInstant extends React.Component { } public componentDidMount(): void { + const state = this._store.getState(); // tslint:disable-next-line:no-floating-promises - asyncData.fetchAndDispatchToStore(this._store); + asyncData.fetchEthPriceAndDispatchToStore(this._store); + // fetch available assets if none are specified + if (_.isEmpty(state.availableAssets)) { + // tslint:disable-next-line:no-floating-promises + asyncData.fetchAvailableAssetDatasAndDispatchToStore(this._store); + } // warm up the gas price estimator cache just in case we can't // grab the gas price estimate when submitting the transaction diff --git a/packages/instant/src/index.umd.ts b/packages/instant/src/index.umd.ts index dabd45cae..77bdb66e1 100644 --- a/packages/instant/src/index.umd.ts +++ b/packages/instant/src/index.umd.ts @@ -7,8 +7,10 @@ import { ZeroExInstant, ZeroExInstantProps } from './index'; import { assert } from './util/assert'; export const render = (props: ZeroExInstantProps, selector: string = DEFAULT_ZERO_EX_CONTAINER_SELECTOR) => { - assert.isHexString('assetData', props.assetData); - assert.isValidLiquiditySource('liquiditySource', props.liquiditySource); + assert.isValidOrderSource('orderSource', props.orderSource); + if (!_.isUndefined(props.defaultSelectedAssetData)) { + assert.isHexString('defaultSelectedAssetData', props.defaultSelectedAssetData); + } if (!_.isUndefined(props.additionalAssetMetaDataMap)) { assert.isValidAssetMetaDataMap('additionalAssetMetaDataMap', props.additionalAssetMetaDataMap); } diff --git a/packages/instant/src/redux/actions.ts b/packages/instant/src/redux/actions.ts index bfae68e2b..eadd8b42c 100644 --- a/packages/instant/src/redux/actions.ts +++ b/packages/instant/src/redux/actions.ts @@ -4,7 +4,7 @@ import * as _ from 'lodash'; import { BigNumberInput } from '../util/big_number_input'; -import { ActionsUnion, OrderState } from '../types'; +import { ActionsUnion, Asset, OrderState } from '../types'; export interface PlainAction { type: T; @@ -28,6 +28,7 @@ export enum ActionTypes { UPDATE_BUY_ORDER_STATE = 'UPDATE_BUY_ORDER_STATE', UPDATE_LATEST_BUY_QUOTE = 'UPDATE_LATEST_BUY_QUOTE', UPDATE_SELECTED_ASSET = 'UPDATE_SELECTED_ASSET', + SET_AVAILABLE_ASSETS = 'SET_AVAILABLE_ASSETS', SET_QUOTE_REQUEST_STATE_PENDING = 'SET_QUOTE_REQUEST_STATE_PENDING', SET_QUOTE_REQUEST_STATE_FAILURE = 'SET_QUOTE_REQUEST_STATE_FAILURE', SET_ERROR_MESSAGE = 'SET_ERROR_MESSAGE', @@ -43,6 +44,7 @@ export const actions = { updateBuyOrderState: (orderState: OrderState) => createAction(ActionTypes.UPDATE_BUY_ORDER_STATE, orderState), updateLatestBuyQuote: (buyQuote?: BuyQuote) => createAction(ActionTypes.UPDATE_LATEST_BUY_QUOTE, buyQuote), updateSelectedAsset: (assetData?: string) => createAction(ActionTypes.UPDATE_SELECTED_ASSET, assetData), + setAvailableAssets: (availableAssets: Asset[]) => createAction(ActionTypes.SET_AVAILABLE_ASSETS, availableAssets), setQuoteRequestStatePending: () => createAction(ActionTypes.SET_QUOTE_REQUEST_STATE_PENDING), setQuoteRequestStateFailure: () => createAction(ActionTypes.SET_QUOTE_REQUEST_STATE_FAILURE), setErrorMessage: (errorMessage: string) => createAction(ActionTypes.SET_ERROR_MESSAGE, errorMessage), diff --git a/packages/instant/src/redux/async_data.ts b/packages/instant/src/redux/async_data.ts index 4ed89bdc3..f8dbe9fd4 100644 --- a/packages/instant/src/redux/async_data.ts +++ b/packages/instant/src/redux/async_data.ts @@ -1,22 +1,33 @@ +import * as _ from 'lodash'; + import { BIG_NUMBER_ZERO } from '../constants'; +import { assetUtils } from '../util/asset'; import { coinbaseApi } from '../util/coinbase_api'; -import { ActionTypes } from './actions'; - +import { actions } from './actions'; import { Store } from './store'; export const asyncData = { - fetchAndDispatchToStore: async (store: Store) => { + fetchEthPriceAndDispatchToStore: async (store: Store) => { let ethUsdPrice = BIG_NUMBER_ZERO; try { ethUsdPrice = await coinbaseApi.getEthUsdPrice(); } catch (e) { // ignore } finally { - store.dispatch({ - type: ActionTypes.UPDATE_ETH_USD_PRICE, - data: ethUsdPrice, - }); + store.dispatch(actions.updateEthUsdPrice(ethUsdPrice)); + } + }, + fetchAvailableAssetDatasAndDispatchToStore: async (store: Store) => { + const { assetBuyer, assetMetaDataMap, network } = store.getState(); + if (!_.isUndefined(assetBuyer)) { + try { + const assetDatas = await assetBuyer.getAvailableAssetDatasAsync(); + const assets = assetUtils.createAssetsFromAssetDatas(assetDatas, assetMetaDataMap, network); + store.dispatch(actions.setAvailableAssets(assets)); + } catch (e) { + // ignore + } } }, }; diff --git a/packages/instant/src/redux/reducer.ts b/packages/instant/src/redux/reducer.ts index dd9403052..c0a8c1771 100644 --- a/packages/instant/src/redux/reducer.ts +++ b/packages/instant/src/redux/reducer.ts @@ -24,6 +24,7 @@ export interface State { assetBuyer?: AssetBuyer; assetMetaDataMap: ObjectMap; selectedAsset?: Asset; + availableAssets: Asset[]; selectedAssetAmount?: BigNumberInput; buyOrderState: OrderState; ethUsdPrice?: BigNumber; @@ -36,6 +37,7 @@ export interface State { export const INITIAL_STATE: State = { network: Network.Mainnet, selectedAssetAmount: undefined, + availableAssets: [], assetMetaDataMap, buyOrderState: { processState: OrderProcessState.NONE }, ethUsdPrice: undefined, @@ -109,7 +111,7 @@ export const reducer = (state: State = INITIAL_STATE, action: Action): State => const newSelectedAssetData = action.data; let newSelectedAsset: Asset | undefined; if (!_.isUndefined(newSelectedAssetData)) { - newSelectedAsset = assetUtils.createAssetFromAssetData( + newSelectedAsset = assetUtils.createAssetFromAssetDataOrThrow( newSelectedAssetData, state.assetMetaDataMap, state.network, @@ -127,6 +129,11 @@ export const reducer = (state: State = INITIAL_STATE, action: Action): State => buyOrderState: { processState: OrderProcessState.NONE }, selectedAssetAmount: undefined, }; + case ActionTypes.SET_AVAILABLE_ASSETS: + return { + ...state, + availableAssets: action.data, + }; default: return state; } diff --git a/packages/instant/src/util/assert.ts b/packages/instant/src/util/assert.ts index 584d3d4b1..1267a1d23 100644 --- a/packages/instant/src/util/assert.ts +++ b/packages/instant/src/util/assert.ts @@ -8,12 +8,12 @@ import { AssetMetaData } from '../types'; export const assert = { ...sharedAssert, - isValidLiquiditySource(variableName: string, liquiditySource: string | SignedOrder[]): void { - if (_.isString(liquiditySource)) { - sharedAssert.isUri(variableName, liquiditySource); + isValidOrderSource(variableName: string, orderSource: string | SignedOrder[]): void { + if (_.isString(orderSource)) { + sharedAssert.isUri(variableName, orderSource); return; } - sharedAssert.doesConformToSchema(variableName, liquiditySource, schemas.signedOrdersSchema); + sharedAssert.doesConformToSchema(variableName, orderSource, schemas.signedOrdersSchema); }, isValidAssetMetaDataMap(variableName: string, metaDataMap: ObjectMap): void { _.forEach(metaDataMap, (metaData, assetData) => { diff --git a/packages/instant/src/util/asset.ts b/packages/instant/src/util/asset.ts index 630103c7b..0576a7b60 100644 --- a/packages/instant/src/util/asset.ts +++ b/packages/instant/src/util/asset.ts @@ -5,7 +5,31 @@ import { assetDataNetworkMapping } from '../data/asset_data_network_mapping'; import { Asset, AssetMetaData, ERC20Asset, Network, ZeroExInstantError } from '../types'; export const assetUtils = { - createAssetFromAssetData: ( + createAssetsFromAssetDatas: ( + assetDatas: string[], + assetMetaDataMap: ObjectMap, + network: Network, + ): Asset[] => { + const arrayOfAssetOrUndefined = _.map(assetDatas, assetData => + assetUtils.createAssetFromAssetDataIfExists(assetData, assetMetaDataMap, network), + ); + return _.compact(arrayOfAssetOrUndefined); + }, + createAssetFromAssetDataIfExists: ( + assetData: string, + assetMetaDataMap: ObjectMap, + network: Network, + ): Asset | undefined => { + const metaData = assetUtils.getMetaDataIfExists(assetData, assetMetaDataMap, network); + if (_.isUndefined(metaData)) { + return; + } + return { + assetData, + metaData, + }; + }, + createAssetFromAssetDataOrThrow: ( assetData: string, assetMetaDataMap: ObjectMap, network: Network, @@ -16,6 +40,17 @@ export const assetUtils = { }; }, getMetaDataOrThrow: (assetData: string, metaDataMap: ObjectMap, network: Network): AssetMetaData => { + const metaDataIfExists = assetUtils.getMetaDataIfExists(assetData, metaDataMap, network); + if (_.isUndefined(metaDataIfExists)) { + throw new Error(ZeroExInstantError.AssetMetaDataNotAvailable); + } + return metaDataIfExists; + }, + getMetaDataIfExists: ( + assetData: string, + metaDataMap: ObjectMap, + network: Network, + ): AssetMetaData | undefined => { let mainnetAssetData: string | undefined = assetData; if (network !== Network.Mainnet) { const mainnetAssetDataIfExists = assetUtils.getAssociatedAssetDataIfExists(assetData, network); @@ -24,11 +59,11 @@ export const assetUtils = { mainnetAssetData = mainnetAssetDataIfExists || assetData; } if (_.isUndefined(mainnetAssetData)) { - throw new Error(ZeroExInstantError.AssetMetaDataNotAvailable); + return; } const metaData = metaDataMap[mainnetAssetData]; if (_.isUndefined(metaData)) { - throw new Error(ZeroExInstantError.AssetMetaDataNotAvailable); + return; } return metaData; }, -- cgit v1.2.3