diff --git a/packages/abi-gen-wrappers/src/generated-wrappers/dutch_auction.ts b/packages/abi-gen-wrappers/src/generated-wrappers/dutch_auction.ts index 08556aaeee..36c9816ada 100644 --- a/packages/abi-gen-wrappers/src/generated-wrappers/dutch_auction.ts +++ b/packages/abi-gen-wrappers/src/generated-wrappers/dutch_auction.ts @@ -245,7 +245,7 @@ export class DutchAuctionContract extends BaseContract { return contractInstance; } constructor(abi: ContractAbi, address: string, supportedProvider: SupportedProvider, txDefaults?: Partial) { - super('DutchAuction', abi, address, providerUtils.standardizeOrThrow(supportedProvider), txDefaults); + super('DutchAuction', abi, address, supportedProvider, txDefaults); classUtils.bindAll(this, ['_abiEncoderByFunctionSignature', 'address', 'abi', '_web3Wrapper']); } } // tslint:disable:max-file-line-count diff --git a/packages/instant/public/index.html b/packages/instant/public/index.html index d10618c58f..fd135a7a74 100644 --- a/packages/instant/public/index.html +++ b/packages/instant/public/index.html @@ -74,6 +74,26 @@ EXPONENTIAL_AT: 1000, DECIMAL_PLACES: 78, }); + const nftOrders = [ + { + makerAddress: '0x34a745008a643eebc58920eaa29fb1165b4a288e', + takerAddress: '0x0000000000000000000000000000000000000000', + makerFee: '0', + takerFee: '0', + senderAddress: '0x0000000000000000000000000000000000000000', + makerAssetAmount: '1', + takerAssetAmount: '25000000000000000', + makerAssetData: + '0x02571792000000000000000000000000f5b0a3efb8e8e4c201e2a935f110eaaf3ffecb8d0000000000000000000000000000000000000000000000000000000000008849', + takerAssetData: '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + exchangeAddress: '0x4f833a24e1f95d70f028921e27040ca56e09ab0b', + feeRecipientAddress: '0x66a836664adc7c525c0cc4527dee8619d4faf669', + salt: '73683692897280689979350475364949461624706477613931387930893215569576520901319', + expirationTimeSeconds: '1557096652', + signature: + '0x1bf37ac01ba863c08f2ddba776b263af4f960326ed8d8a0a651acb4c7bce54592e5bd9826fb117ccd0d40392d26339de6e3377721fabbb3107043e4de444b24ae602', + }, + ]; const providedOrders = [ // Order selling REP { @@ -168,7 +188,8 @@ }; } const renderOptionsOverrides = { - orderSource: orderSourceOverride === 'provided' ? providedOrders : orderSourceOverride, + // orderSource: orderSourceOverride === 'provided' ? providedOrders : orderSourceOverride, + orderSource: nftOrders, networkId: +queryParams.getQueryParamValue('networkId') || undefined, defaultAssetBuyAmount: +queryParams.getQueryParamValue('defaultAssetBuyAmount') || undefined, availableAssetDatas: availableAssetDatasString ? JSON.parse(availableAssetDatasString) : undefined, @@ -176,6 +197,15 @@ affiliateInfo: affiliateInfoOverride, shouldDisablePushToHistory: !!queryParams.getQueryParamValue('shouldDisablePushToHistory'), walletDisplayName: queryParams.getQueryParamValue('walletDisplayName') || undefined, + additionalAssetMetaDataMap: { + '0x02571792000000000000000000000000f5b0a3efb8e8e4c201e2a935f110eaaf3ffecb8d0000000000000000000000000000000000000000000000000000000000008849': { + assetProxyId: '0x02571792', + name: 'Axie #34889', + imageUrl: + 'https://storage.opensea.io/0xf5b0a3efb8e8e4c201e2a935f110eaaf3ffecb8d/34889-1549990035.png', + primaryColor: '#b5d868', + }, + }, }; return renderOptionsOverrides; }; diff --git a/packages/instant/src/components/instant_heading.tsx b/packages/instant/src/components/instant_heading.tsx index e943f68d7d..c016967265 100644 --- a/packages/instant/src/components/instant_heading.tsx +++ b/packages/instant/src/components/instant_heading.tsx @@ -1,10 +1,11 @@ +import { AssetProxyId } from '@0x/types'; import { BigNumber } from '@0x/utils'; import * as _ from 'lodash'; import * as React from 'react'; import { SelectedERC20AssetAmountInput } from '../containers/selected_erc20_asset_amount_input'; import { ColorOption } from '../style/theme'; -import { AsyncProcessState, ERC20Asset, OrderProcessState, OrderState } from '../types'; +import { Asset, AsyncProcessState, ERC20Asset, ERC721Asset, OrderProcessState, OrderState } from '../types'; import { format } from '../util/format'; import { AmountPlaceholder } from './amount_placeholder'; @@ -15,6 +16,7 @@ import { Spinner } from './ui/spinner'; import { Text } from './ui/text'; export interface InstantHeadingProps { + selectedAsset?: Asset; selectedAssetUnitAmount?: BigNumber; totalEthBaseUnitAmount?: BigNumber; ethUsdPrice?: BigNumber; @@ -30,9 +32,36 @@ const ICON_COLOR = ColorOption.white; export class InstantHeading extends React.PureComponent { public render(): React.ReactNode { - const iconOrAmounts = this._renderIcon() || this._renderAmountsSection(); + const { selectedAsset } = this.props; return ( + {this._renderAssetHeadingContent()} + + ); + } + + private _renderAssetHeadingContent(): React.ReactNode { + const { selectedAsset } = this.props; + if (_.isUndefined(selectedAsset)) { + // TODO: Only the ERC20 flow supports selecting assets. + return this._renderERC20AssetHeading(); + } + if (selectedAsset.metaData.assetProxyId === AssetProxyId.ERC20) { + return this._renderERC20AssetHeading(); + } else if (selectedAsset.metaData.assetProxyId === AssetProxyId.ERC721) { + return this._renderERC721AssetHeading(selectedAsset as ERC721Asset); + } + return null; + } + + private _renderERC721AssetHeading(asset: ERC721Asset): React.ReactNode { + return ; + } + + private _renderERC20AssetHeading(): React.ReactNode { + const iconOrAmounts = this._renderIcon() || this._renderAmountsSection(); + return ( + {iconOrAmounts} - + ); } diff --git a/packages/instant/src/components/zero_ex_instant_container.tsx b/packages/instant/src/components/zero_ex_instant_container.tsx index e8c64d5d10..fdad3ad4f4 100644 --- a/packages/instant/src/components/zero_ex_instant_container.tsx +++ b/packages/instant/src/components/zero_ex_instant_container.tsx @@ -1,3 +1,5 @@ +import { AssetProxyId } from '@0x/types'; +import * as _ from 'lodash'; import * as React from 'react'; import PoweredByLogo from '../assets/powered_by_0x.svg'; @@ -11,7 +13,7 @@ import { SelectedAssetBuyOrderStateButtons } from '../containers/selected_asset_ import { SelectedAssetInstantHeading } from '../containers/selected_asset_instant_heading'; import { ColorOption } from '../style/theme'; import { zIndex } from '../style/z_index'; -import { SlideAnimationState } from '../types'; +import { Asset, SlideAnimationState } from '../types'; import { analytics, TokenSelectorClosedVia } from '../util/analytics'; import { CSSReset } from './css_reset'; @@ -84,11 +86,15 @@ export class ZeroExInstantContainer extends React.PureComponent< ); } - private readonly _handleSymbolClick = (): void => { - analytics.trackTokenSelectorOpened(); - this.setState({ - tokenSelectionPanelAnimationState: 'slidIn', - }); + private readonly _handleSymbolClick = (asset?: Asset): void => { + if (_.isUndefined(asset) || asset.metaData.assetProxyId === AssetProxyId.ERC20) { + analytics.trackTokenSelectorOpened(); + this.setState({ + tokenSelectionPanelAnimationState: 'slidIn', + }); + } else if (asset.metaData.assetProxyId === AssetProxyId.ERC721) { + // TODO: Link open sea. + } }; private readonly _handlePanelCloseClickedX = (): void => { this._handlePanelClose(TokenSelectorClosedVia.ClickedX); diff --git a/packages/instant/src/components/zero_ex_instant_provider.tsx b/packages/instant/src/components/zero_ex_instant_provider.tsx index ec8e82ee3c..1c88fcd4d0 100644 --- a/packages/instant/src/components/zero_ex_instant_provider.tsx +++ b/packages/instant/src/components/zero_ex_instant_provider.tsx @@ -18,6 +18,7 @@ import { gasPriceEstimator } from '../util/gas_price_estimator'; import { Heartbeater } from '../util/heartbeater'; import { generateAccountHeartbeater, generateBuyQuoteHeartbeater } from '../util/heartbeater_factory'; import { providerStateFactory } from '../util/provider_state_factory'; +import { AssetProxyId } from '@0x/types'; export type ZeroExInstantProviderProps = ZeroExInstantBaseConfig; @@ -46,22 +47,30 @@ export class ZeroExInstantProvider extends React.PureComponent key.toLowerCase()), ...defaultState.assetMetaDataMap, }; + + const selectedAsset = _.isUndefined(props.defaultSelectedAssetData) + ? undefined + : assetUtils.createAssetFromAssetDataOrThrow( + props.defaultSelectedAssetData, + completeAssetMetaDataMap, + networkId, + ); + + let selectedAssetUnitAmount: BigNumber | undefined = new BigNumber(1); + if (!_.isUndefined(selectedAsset) && selectedAsset.metaData.assetProxyId === AssetProxyId.ERC20) { + selectedAssetUnitAmount = _.isUndefined(props.defaultAssetBuyAmount) + ? undefined + : new BigNumber(props.defaultAssetBuyAmount); + } + // construct the final state const storeStateFromProps: State = { ...defaultState, providerState, network: networkId, walletDisplayName: props.walletDisplayName, - selectedAsset: _.isUndefined(props.defaultSelectedAssetData) - ? undefined - : assetUtils.createAssetFromAssetDataOrThrow( - props.defaultSelectedAssetData, - completeAssetMetaDataMap, - networkId, - ), - selectedAssetUnitAmount: _.isUndefined(props.defaultAssetBuyAmount) - ? undefined - : new BigNumber(props.defaultAssetBuyAmount), + selectedAsset, + selectedAssetUnitAmount, availableAssets: _.isUndefined(props.availableAssetDatas) ? undefined : assetUtils.createAssetsFromAssetDatas(props.availableAssetDatas, completeAssetMetaDataMap, networkId), diff --git a/packages/instant/src/constants.ts b/packages/instant/src/constants.ts index fcc5048c0d..c9533de479 100644 --- a/packages/instant/src/constants.ts +++ b/packages/instant/src/constants.ts @@ -16,7 +16,8 @@ export const ONE_SECOND_MS = 1000; export const ONE_MINUTE_MS = ONE_SECOND_MS * 60; export const GIT_SHA = process.env.GIT_SHA; export const NODE_ENV = process.env.NODE_ENV; -export const SLIPPAGE_PERCENTAGE = 0.2; +export const ERC20_BUY_QUOTE_SLIPPAGE_PERCENTAGE = 0.2; +export const ERC721_BUY_QUOTE_SLIPPAGE_PERCENTAGE = 0; export const NPM_PACKAGE_VERSION = process.env.NPM_PACKAGE_VERSION; export const DEFAULT_UNKOWN_ASSET_NAME = '???'; export const ACCOUNT_UPDATE_INTERVAL_TIME_MS = ONE_SECOND_MS * 5; diff --git a/packages/instant/src/containers/selected_asset_instant_heading.ts b/packages/instant/src/containers/selected_asset_instant_heading.ts index 8dc127e1d7..9b278de582 100644 --- a/packages/instant/src/containers/selected_asset_instant_heading.ts +++ b/packages/instant/src/containers/selected_asset_instant_heading.ts @@ -5,15 +5,16 @@ import { connect } from 'react-redux'; import { oc } from 'ts-optchain'; import { State } from '../redux/reducer'; -import { AsyncProcessState, ERC20Asset, OrderState } from '../types'; +import { Asset, AsyncProcessState, OrderState } from '../types'; import { InstantHeading } from '../components/instant_heading'; export interface InstantHeadingProps { - onSelectAssetClick?: (asset?: ERC20Asset) => void; + onSelectAssetClick?: (asset?: Asset) => void; } interface ConnectedState { + selectedAsset?: Asset; selectedAssetUnitAmount?: BigNumber; totalEthBaseUnitAmount?: BigNumber; ethUsdPrice?: BigNumber; @@ -22,6 +23,7 @@ interface ConnectedState { } const mapStateToProps = (state: State, _ownProps: InstantHeadingProps): ConnectedState => ({ + selectedAsset: state.selectedAsset, selectedAssetUnitAmount: state.selectedAssetUnitAmount, totalEthBaseUnitAmount: oc(state).latestBuyQuote.worstCaseQuoteInfo.totalEthAmount(), ethUsdPrice: state.ethUsdPrice, diff --git a/packages/instant/src/redux/async_data.ts b/packages/instant/src/redux/async_data.ts index f20fe319fa..0e9bb1e118 100644 --- a/packages/instant/src/redux/async_data.ts +++ b/packages/instant/src/redux/async_data.ts @@ -100,13 +100,12 @@ export const asyncData = { !_.isUndefined(selectedAssetUnitAmount) && !_.isUndefined(selectedAsset) && selectedAssetUnitAmount.isGreaterThan(BIG_NUMBER_ZERO) && - buyOrderState.processState === OrderProcessState.None && - selectedAsset.metaData.assetProxyId === AssetProxyId.ERC20 + buyOrderState.processState === OrderProcessState.None ) { await buyQuoteUpdater.updateBuyQuoteAsync( assetBuyer, dispatch, - selectedAsset as ERC20Asset, + selectedAsset, selectedAssetUnitAmount, fetchOrigin, { diff --git a/packages/instant/src/util/asset.ts b/packages/instant/src/util/asset.ts index b5c97913db..8eafec530e 100644 --- a/packages/instant/src/util/asset.ts +++ b/packages/instant/src/util/asset.ts @@ -109,17 +109,17 @@ export const assetUtils = { ); return _.compact(erc20sOrUndefined); }, - assetBuyerErrorMessage: (asset: ERC20Asset, error: Error): string | undefined => { + assetBuyerErrorMessage: (asset: Asset, error: Error): string | undefined => { if (error.message === AssetBuyerError.InsufficientAssetLiquidity) { const assetName = assetUtils.bestNameForAsset(asset, 'of this asset'); if ( error instanceof InsufficientAssetLiquidityError && error.amountAvailableToFill.isGreaterThan(BIG_NUMBER_ZERO) ) { - const unitAmountAvailableToFill = Web3Wrapper.toUnitAmount( - error.amountAvailableToFill, - asset.metaData.decimals, - ); + const unitAmountAvailableToFill = + asset.metaData.assetProxyId === AssetProxyId.ERC20 + ? Web3Wrapper.toUnitAmount(error.amountAvailableToFill, asset.metaData.decimals) + : error.amountAvailableToFill; const roundedUnitAmountAvailableToFill = unitAmountAvailableToFill.decimalPlaces( 2, BigNumber.ROUND_DOWN, diff --git a/packages/instant/src/util/buy_quote_updater.ts b/packages/instant/src/util/buy_quote_updater.ts index 37974e71cc..e4463c915d 100644 --- a/packages/instant/src/util/buy_quote_updater.ts +++ b/packages/instant/src/util/buy_quote_updater.ts @@ -1,13 +1,14 @@ import { AssetBuyer, BuyQuote } from '@0x/asset-buyer'; +import { AssetProxyId } from '@0x/types'; import { BigNumber } from '@0x/utils'; import { Web3Wrapper } from '@0x/web3-wrapper'; import * as _ from 'lodash'; import { Dispatch } from 'redux'; import { oc } from 'ts-optchain'; -import { SLIPPAGE_PERCENTAGE } from '../constants'; +import { ERC20_BUY_QUOTE_SLIPPAGE_PERCENTAGE, ERC721_BUY_QUOTE_SLIPPAGE_PERCENTAGE } from '../constants'; import { Action, actions } from '../redux/actions'; -import { AffiliateInfo, ERC20Asset, QuoteFetchOrigin } from '../types'; +import { AffiliateInfo, Asset, QuoteFetchOrigin } from '../types'; import { analytics } from '../util/analytics'; import { assetUtils } from '../util/asset'; import { errorFlasher } from '../util/error_flasher'; @@ -17,7 +18,7 @@ export const buyQuoteUpdater = { updateBuyQuoteAsync: async ( assetBuyer: AssetBuyer, dispatch: Dispatch, - asset: ERC20Asset, + asset: Asset, assetUnitAmount: BigNumber, fetchOrigin: QuoteFetchOrigin, options: { @@ -27,14 +28,20 @@ export const buyQuoteUpdater = { }, ): Promise => { // get a new buy quote. - const baseUnitValue = Web3Wrapper.toBaseUnitAmount(assetUnitAmount, asset.metaData.decimals); + const baseUnitValue = + asset.metaData.assetProxyId === AssetProxyId.ERC20 + ? Web3Wrapper.toBaseUnitAmount(assetUnitAmount, asset.metaData.decimals) + : assetUnitAmount; if (options.setPending) { // mark quote as pending dispatch(actions.setQuoteRequestStatePending()); } const feePercentage = oc(options.affiliateInfo).feePercentage(); let newBuyQuote: BuyQuote | undefined; - const slippagePercentage = SLIPPAGE_PERCENTAGE; + const slippagePercentage = + asset.metaData.assetProxyId === AssetProxyId.ERC20 + ? ERC20_BUY_QUOTE_SLIPPAGE_PERCENTAGE + : ERC721_BUY_QUOTE_SLIPPAGE_PERCENTAGE; try { newBuyQuote = await assetBuyer.getBuyQuoteAsync(asset.assetData, baseUnitValue, { feePercentage,