Extreme MVP of buying a ERC721 works
This commit is contained in:
		@@ -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;
 | 
			
		||||
            };
 | 
			
		||||
 
 | 
			
		||||
@@ -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<InstantHeadingProps, {}> {
 | 
			
		||||
    public render(): React.ReactNode {
 | 
			
		||||
        const iconOrAmounts = this._renderIcon() || this._renderAmountsSection();
 | 
			
		||||
        const { selectedAsset } = this.props;
 | 
			
		||||
        return (
 | 
			
		||||
            <Container backgroundColor={ColorOption.primaryColor} width="100%" padding="20px">
 | 
			
		||||
                {this._renderAssetHeadingContent()}
 | 
			
		||||
            </Container>
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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 <Container><img src={asset.metaData.imageUrl}/> </Container>;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private _renderERC20AssetHeading(): React.ReactNode {
 | 
			
		||||
        const iconOrAmounts = this._renderIcon() || this._renderAmountsSection();
 | 
			
		||||
        return (
 | 
			
		||||
            <React.Fragment>
 | 
			
		||||
                <Container marginBottom="5px">
 | 
			
		||||
                    <Text
 | 
			
		||||
                        letterSpacing="1px"
 | 
			
		||||
@@ -56,7 +85,7 @@ export class InstantHeading extends React.PureComponent<InstantHeadingProps, {}>
 | 
			
		||||
                        {iconOrAmounts}
 | 
			
		||||
                    </Flex>
 | 
			
		||||
                </Flex>
 | 
			
		||||
            </Container>
 | 
			
		||||
            </React.Fragment>
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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<
 | 
			
		||||
            </React.Fragment>
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
    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);
 | 
			
		||||
 
 | 
			
		||||
@@ -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<ZeroExInstantProv
 | 
			
		||||
            ..._.mapKeys(props.additionalAssetMetaDataMap || {}, (value, key) => 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),
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 
 | 
			
		||||
@@ -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,
 | 
			
		||||
 
 | 
			
		||||
@@ -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,
 | 
			
		||||
                {
 | 
			
		||||
 
 | 
			
		||||
@@ -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,
 | 
			
		||||
 
 | 
			
		||||
@@ -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<Action>,
 | 
			
		||||
        asset: ERC20Asset,
 | 
			
		||||
        asset: Asset,
 | 
			
		||||
        assetUnitAmount: BigNumber,
 | 
			
		||||
        fetchOrigin: QuoteFetchOrigin,
 | 
			
		||||
        options: {
 | 
			
		||||
@@ -27,14 +28,20 @@ export const buyQuoteUpdater = {
 | 
			
		||||
        },
 | 
			
		||||
    ): Promise<void> => {
 | 
			
		||||
        // 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,
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user