diff options
Diffstat (limited to 'packages')
-rw-r--r-- | packages/asset-buyer/CHANGELOG.json | 9 | ||||
-rw-r--r-- | packages/asset-buyer/src/asset_buyer.ts | 5 | ||||
-rw-r--r-- | packages/instant/package.json | 2 | ||||
-rw-r--r-- | packages/instant/public/index.html | 43 | ||||
-rw-r--r-- | packages/instant/src/components/sliding_error.tsx | 16 | ||||
-rw-r--r-- | packages/instant/src/components/zero_ex_instant.tsx | 61 | ||||
-rw-r--r-- | packages/instant/src/index.umd.ts | 13 | ||||
-rw-r--r-- | packages/instant/src/types.ts | 2 | ||||
-rw-r--r-- | packages/instant/src/util/assert.ts | 44 | ||||
-rw-r--r-- | packages/instant/src/util/asset.ts | 5 | ||||
-rw-r--r-- | packages/instant/src/util/error.ts | 5 |
11 files changed, 173 insertions, 32 deletions
diff --git a/packages/asset-buyer/CHANGELOG.json b/packages/asset-buyer/CHANGELOG.json index 7ebcd8c2f..5d6604ea9 100644 --- a/packages/asset-buyer/CHANGELOG.json +++ b/packages/asset-buyer/CHANGELOG.json @@ -1,5 +1,14 @@ [ { + "version": "2.2.0", + "changes": [ + { + "note": "`getAssetBuyerForProvidedOrders` factory function now takes 3 args instead of 4", + "pr": 1187 + } + ] + }, + { "version": "2.1.0", "changes": [ { diff --git a/packages/asset-buyer/src/asset_buyer.ts b/packages/asset-buyer/src/asset_buyer.ts index 74f3cb471..34e2d9639 100644 --- a/packages/asset-buyer/src/asset_buyer.ts +++ b/packages/asset-buyer/src/asset_buyer.ts @@ -52,16 +52,13 @@ export class AssetBuyer { public static getAssetBuyerForProvidedOrders( provider: Provider, orders: SignedOrder[], - feeOrders: SignedOrder[] = [], options: Partial<AssetBuyerOpts> = {}, ): AssetBuyer { assert.isWeb3Provider('provider', provider); assert.doesConformToSchema('orders', orders, schemas.signedOrdersSchema); - assert.doesConformToSchema('feeOrders', feeOrders, schemas.signedOrdersSchema); assert.areValidProvidedOrders('orders', orders); - assert.areValidProvidedOrders('feeOrders', feeOrders); assert.assert(orders.length !== 0, `Expected orders to contain at least one order`); - const orderProvider = new BasicOrderProvider(_.concat(orders, feeOrders)); + const orderProvider = new BasicOrderProvider(orders); const assetBuyer = new AssetBuyer(provider, orderProvider, options); return assetBuyer; } diff --git a/packages/instant/package.json b/packages/instant/package.json index be85b5062..977126867 100644 --- a/packages/instant/package.json +++ b/packages/instant/package.json @@ -44,7 +44,9 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/instant/README.md", "dependencies": { + "@0x/assert": "^1.0.14", "@0x/asset-buyer": "^2.1.0", + "@0x/json-schemas": "^2.0.0", "@0x/order-utils": "^2.0.0", "@0x/types": "^1.2.0", "@0x/typescript-typings": "^3.0.3", diff --git a/packages/instant/public/index.html b/packages/instant/public/index.html index 14555fc64..9f1dfdb64 100644 --- a/packages/instant/public/index.html +++ b/packages/instant/public/index.html @@ -6,6 +6,8 @@ <meta name="viewport" content="width=device-width, initial-scale=1"> <title>0x Instant Dev Environment</title> <script type="text/javascript" src="/main.bundle.js" charset="utf-8"></script> + <script type="text/javascript" src="https://unpkg.com/jsuri@1.3.1/Uri.js" charset="utf-8"></script> + <script type="text/javascript" src="https://unpkg.com/bignumber.js@4.1.0/bignumber.js" charset="utf-8"></script> <style> #zeroExInstantContainer { display: flex; @@ -24,10 +26,47 @@ <body> <div id="zeroExInstantContainer"></div> <script> - zeroExInstant.render({ + const removeUndefined = (obj) => { + for (let k in obj) if (obj[k] === undefined) delete obj[k]; + return obj; + } + BigNumber.config({ + EXPONENTIAL_AT: 1000, + DECIMAL_PLACES: 78, + }); + const providedOrder = { + senderAddress: '0x0000000000000000000000000000000000000000', + makerAddress: '0x14e2f1f157e7dd4057d02817436d628a37120fd1', + takerAddress: '0x0000000000000000000000000000000000000000', + makerFee: new BigNumber('0'), + takerFee: new BigNumber('0'), + makerAssetAmount: new BigNumber('100000000000000000000'), + takerAssetAmount: new BigNumber('10000000000000000'), + makerAssetData: '0xf47261b00000000000000000000000002002d3812f58e35f0ea1ffbf80a75a38c32175fa', + takerAssetData: '0xf47261b0000000000000000000000000d0a1e359811322d97991e03f863a0c30c2cf029c', + expirationTimeSeconds: new BigNumber('1591858800'), + feeRecipientAddress: '0x0000000000000000000000000000000000000000', + salt: new BigNumber( + '54983920541892966634674340965984367456810207583416050222519063020710969340046', + ), + signature: + '0x1b949656218421c845995457303569a656764afa2b979d41dcefff0009d57ce15001490268bc7caa4269894fd83b741465fc5a7a53eda6ece17eb91fb32655d83703', + exchangeAddress: '0x35dd2932454449b14cee11a94d3674a936d5d7b2', + }; + const queryParams = new Uri(window.location.search); + const renderOptionsDefaults = { liquiditySource: 'https://api.radarrelay.com/0x/v2/', assetData: '0xf47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498', - }); + } + const liquiditySourceOverride = queryParams.getQueryParamValue('liquiditySource'); + const renderOptionsOverrides = { + liquiditySource: liquiditySourceOverride === 'provided' ? [providedOrder] : liquiditySourceOverride, + assetData: queryParams.getQueryParamValue('assetData'), + networkId: +queryParams.getQueryParamValue('networkId') || undefined, + defaultAssetBuyAmount: +queryParams.getQueryParamValue('defaultAssetBuyAmount') || undefined, + } + const renderOptions = Object.assign({}, renderOptionsDefaults, removeUndefined(renderOptionsOverrides)); + zeroExInstant.render(renderOptions); </script> </body> diff --git a/packages/instant/src/components/sliding_error.tsx b/packages/instant/src/components/sliding_error.tsx index 3865a8797..cc9abb7dd 100644 --- a/packages/instant/src/components/sliding_error.tsx +++ b/packages/instant/src/components/sliding_error.tsx @@ -4,7 +4,7 @@ import { ColorOption } from '../style/theme'; import { SlideDownAnimation, SlideUpAnimation } from './animations/slide_animations'; -import { Container, Text } from './ui'; +import { Container, Flex, Text } from './ui'; export interface ErrorProps { icon: string; @@ -20,12 +20,14 @@ export const Error: React.StatelessComponent<ErrorProps> = props => ( borderRadius="6px" marginBottom="10px" > - <Container marginRight="5px" display="inline" top="3px" position="relative"> - <Text fontSize="20px">{props.icon}</Text> - </Container> - <Text fontWeight="500" fontColor={ColorOption.darkOrange}> - {props.message} - </Text> + <Flex justify="flex-start"> + <Container marginRight="5px" display="inline" top="3px" position="relative"> + <Text fontSize="20px">{props.icon}</Text> + </Container> + <Text fontWeight="500" fontColor={ColorOption.darkOrange}> + {props.message} + </Text> + </Flex> </Container> ); diff --git a/packages/instant/src/components/zero_ex_instant.tsx b/packages/instant/src/components/zero_ex_instant.tsx index ffa5a8250..8454de43c 100644 --- a/packages/instant/src/components/zero_ex_instant.tsx +++ b/packages/instant/src/components/zero_ex_instant.tsx @@ -1,5 +1,7 @@ import { AssetBuyer } from '@0x/asset-buyer'; -import { ObjectMap } from '@0x/types'; +import { ObjectMap, SignedOrder } from '@0x/types'; +import { BigNumber } from '@0x/utils'; +import * as _ from 'lodash'; import * as React from 'react'; import { Provider } from 'react-redux'; @@ -10,7 +12,9 @@ import { store, Store } from '../redux/store'; import { fonts } from '../style/fonts'; import { AssetMetaData, Network } from '../types'; import { assetUtils } from '../util/asset'; +import { errorUtil } from '../util/error'; import { getProvider } from '../util/provider'; +import { web3Wrapper } from '../util/web3_wrapper'; import { ZeroExInstantContainer } from './zero_ex_instant_container'; @@ -21,28 +25,34 @@ export type ZeroExInstantProps = ZeroExInstantRequiredProps & Partial<ZeroExInst export interface ZeroExInstantRequiredProps { // TODO: Change API when we allow the selection of different assetDatas assetData: string; - // TODO: Allow for a function that returns orders - liquiditySource: string; + liquiditySource: string | SignedOrder[]; } export interface ZeroExInstantOptionalProps { + defaultAssetBuyAmount?: number; additionalAssetMetaDataMap: ObjectMap<AssetMetaData>; - network: Network; + networkId: Network; } export class ZeroExInstant extends React.Component<ZeroExInstantProps> { private readonly _store: Store; private static _mergeInitialStateWithProps(props: ZeroExInstantProps, state: State = INITIAL_STATE): State { - // Create merged object such that properties in props override default settings - const optionalPropsWithDefaults: ZeroExInstantOptionalProps = { - additionalAssetMetaDataMap: props.additionalAssetMetaDataMap || {}, - network: props.network || state.network, - }; - const { network } = optionalPropsWithDefaults; + const networkId = props.networkId || state.network; // TODO: Provider needs to not be hard-coded to injected web3. - const assetBuyer = AssetBuyer.getAssetBuyerForStandardRelayerAPIUrl(getProvider(), props.liquiditySource, { - networkId: network, - }); + const provider = getProvider(); + const assetBuyerOptions = { + networkId, + }; + let assetBuyer; + if (_.isString(props.liquiditySource)) { + assetBuyer = AssetBuyer.getAssetBuyerForStandardRelayerAPIUrl( + provider, + props.liquiditySource, + assetBuyerOptions, + ); + } else { + assetBuyer = AssetBuyer.getAssetBuyerForProvidedOrders(provider, props.liquiditySource, assetBuyerOptions); + } const completeAssetMetaDataMap = { ...props.additionalAssetMetaDataMap, ...state.assetMetaDataMap, @@ -50,17 +60,26 @@ export class ZeroExInstant extends React.Component<ZeroExInstantProps> { const storeStateFromProps: State = { ...state, assetBuyer, - network, - selectedAsset: assetUtils.createAssetFromAssetData(props.assetData, completeAssetMetaDataMap, network), + network: networkId, + selectedAsset: assetUtils.createAssetFromAssetData(props.assetData, completeAssetMetaDataMap, networkId), + selectedAssetAmount: _.isUndefined(props.defaultAssetBuyAmount) + ? state.selectedAssetAmount + : new BigNumber(props.defaultAssetBuyAmount), assetMetaDataMap: completeAssetMetaDataMap, }; return storeStateFromProps; } constructor(props: ZeroExInstantProps) { super(props); - this._store = store.create(ZeroExInstant._mergeInitialStateWithProps(this.props, INITIAL_STATE)); + const initialAppState = ZeroExInstant._mergeInitialStateWithProps(this.props, INITIAL_STATE); + this._store = store.create(initialAppState); + } + + public componentDidMount(): void { // tslint:disable-next-line:no-floating-promises asyncData.fetchAndDispatchToStore(this._store); + // tslint:disable-next-line:no-floating-promises + this._flashErrorIfWrongNetwork(); } public render(): React.ReactNode { @@ -72,4 +91,14 @@ export class ZeroExInstant extends React.Component<ZeroExInstantProps> { </Provider> ); } + + private readonly _flashErrorIfWrongNetwork = async (): Promise<void> => { + const msToShowError = 30000; // 30 seconds + const network = this._store.getState().network; + const networkOfProvider = await web3Wrapper.getNetworkIdAsync(); + if (network !== networkOfProvider) { + const errorMessage = `Wrong network detected. Try switching to ${Network[network]}.`; + errorUtil.errorFlasher.flashNewError(this._store.dispatch, errorMessage, msToShowError); + } + }; } diff --git a/packages/instant/src/index.umd.ts b/packages/instant/src/index.umd.ts index f648b37f2..dabd45cae 100644 --- a/packages/instant/src/index.umd.ts +++ b/packages/instant/src/index.umd.ts @@ -1,9 +1,22 @@ +import * as _ from 'lodash'; import * as React from 'react'; import * as ReactDOM from 'react-dom'; import { DEFAULT_ZERO_EX_CONTAINER_SELECTOR } from './constants'; 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); + if (!_.isUndefined(props.additionalAssetMetaDataMap)) { + assert.isValidAssetMetaDataMap('additionalAssetMetaDataMap', props.additionalAssetMetaDataMap); + } + if (!_.isUndefined(props.defaultAssetBuyAmount)) { + assert.isNumber('defaultAssetBuyAmount', props.defaultAssetBuyAmount); + } + if (!_.isUndefined(props.networkId)) { + assert.isNumber('networkId', props.networkId); + } ReactDOM.render(React.createElement(ZeroExInstant, props), document.querySelector(selector)); }; diff --git a/packages/instant/src/types.ts b/packages/instant/src/types.ts index c63371fb4..e02a815f9 100644 --- a/packages/instant/src/types.ts +++ b/packages/instant/src/types.ts @@ -44,7 +44,7 @@ export interface ERC20AssetMetaData { export interface ERC721AssetMetaData { assetProxyId: AssetProxyId.ERC721; name: string; - representationUrl?: string; + imageUrl?: string; primaryColor?: string; } diff --git a/packages/instant/src/util/assert.ts b/packages/instant/src/util/assert.ts new file mode 100644 index 000000000..584d3d4b1 --- /dev/null +++ b/packages/instant/src/util/assert.ts @@ -0,0 +1,44 @@ +import { assert as sharedAssert } from '@0x/assert'; +import { schemas } from '@0x/json-schemas'; +import { assetDataUtils } from '@0x/order-utils'; +import { AssetProxyId, ObjectMap, SignedOrder } from '@0x/types'; +import * as _ from 'lodash'; + +import { AssetMetaData } from '../types'; + +export const assert = { + ...sharedAssert, + isValidLiquiditySource(variableName: string, liquiditySource: string | SignedOrder[]): void { + if (_.isString(liquiditySource)) { + sharedAssert.isUri(variableName, liquiditySource); + return; + } + sharedAssert.doesConformToSchema(variableName, liquiditySource, schemas.signedOrdersSchema); + }, + isValidAssetMetaDataMap(variableName: string, metaDataMap: ObjectMap<AssetMetaData>): void { + _.forEach(metaDataMap, (metaData, assetData) => { + assert.isHexString(`key ${assetData} of ${variableName}`, assetData); + assert.isValidAssetMetaData(`${variableName}.${assetData}`, metaData); + const assetDataProxyId = assetDataUtils.decodeAssetProxyId(assetData); + assert.assert( + metaData.assetProxyId === assetDataProxyId, + `Expected meta data for assetData ${assetData} to have asset proxy id of ${assetDataProxyId}, but instead got ${ + metaData.assetProxyId + }`, + ); + }); + }, + isValidAssetMetaData(variableName: string, metaData: AssetMetaData): void { + assert.isHexString(`${variableName}.assetProxyId`, metaData.assetProxyId); + if (!_.isUndefined(metaData.primaryColor)) { + assert.isString(`${variableName}.primaryColor`, metaData.primaryColor); + } + if (metaData.assetProxyId === AssetProxyId.ERC20) { + assert.isNumber(`${variableName}.decimals`, metaData.decimals); + assert.isString(`${variableName}.symbol`, metaData.symbol); + } else if (metaData.assetProxyId === AssetProxyId.ERC721) { + assert.isString(`${variableName}.name`, metaData.name); + assert.isUri(`${variableName}.imageUrl`, metaData.imageUrl); + } + }, +}; diff --git a/packages/instant/src/util/asset.ts b/packages/instant/src/util/asset.ts index 2c5b6325d..630103c7b 100644 --- a/packages/instant/src/util/asset.ts +++ b/packages/instant/src/util/asset.ts @@ -18,7 +18,10 @@ export const assetUtils = { getMetaDataOrThrow: (assetData: string, metaDataMap: ObjectMap<AssetMetaData>, network: Network): AssetMetaData => { let mainnetAssetData: string | undefined = assetData; if (network !== Network.Mainnet) { - mainnetAssetData = assetUtils.getAssociatedAssetDataIfExists(assetData, network); + const mainnetAssetDataIfExists = assetUtils.getAssociatedAssetDataIfExists(assetData, network); + // Just so we don't fail in the case where we are on a non-mainnet network, + // but pass in a valid mainnet assetData. + mainnetAssetData = mainnetAssetDataIfExists || assetData; } if (_.isUndefined(mainnetAssetData)) { throw new Error(ZeroExInstantError.AssetMetaDataNotAvailable); diff --git a/packages/instant/src/util/error.ts b/packages/instant/src/util/error.ts index 64c1f4885..844a28d8b 100644 --- a/packages/instant/src/util/error.ts +++ b/packages/instant/src/util/error.ts @@ -1,4 +1,5 @@ import { AssetBuyerError } from '@0x/asset-buyer'; +import * as _ from 'lodash'; import { Dispatch } from 'redux'; import { Action, actions } from '../redux/actions'; @@ -10,7 +11,6 @@ class ErrorFlasher { private _timeoutId?: number; public flashNewError(dispatch: Dispatch<Action>, error: any, delayMs: number = 7000): void { this._clearTimeout(); - // dispatch new message dispatch(actions.setError(error)); @@ -60,6 +60,9 @@ export const errorUtil = { if (error instanceof Error) { bestMessage = humanReadableMessageForError(error, asset); } + if (_.isString(error)) { + bestMessage = error; + } return { icon: '😢', message: bestMessage || 'Something went wrong...', |