aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrandon Millman <brandon.millman@gmail.com>2018-11-02 09:24:32 +0800
committerBrandon Millman <brandon.millman@gmail.com>2018-11-03 01:57:24 +0800
commit5e66cc8a40759658a8763f85996163e5ae013fcd (patch)
treeea397e40f9c438c8598538f00d3ef0c8bb02e237
parent4fda2a2d049843db7f87b930321c11c910e40ea3 (diff)
downloaddexon-0x-contracts-5e66cc8a40759658a8763f85996163e5ae013fcd.tar
dexon-0x-contracts-5e66cc8a40759658a8763f85996163e5ae013fcd.tar.gz
dexon-0x-contracts-5e66cc8a40759658a8763f85996163e5ae013fcd.tar.bz2
dexon-0x-contracts-5e66cc8a40759658a8763f85996163e5ae013fcd.tar.lz
dexon-0x-contracts-5e66cc8a40759658a8763f85996163e5ae013fcd.tar.xz
dexon-0x-contracts-5e66cc8a40759658a8763f85996163e5ae013fcd.tar.zst
dexon-0x-contracts-5e66cc8a40759658a8763f85996163e5ae013fcd.zip
feat(instant): implement affiliateFeeInfo prop
-rw-r--r--packages/instant/src/components/buy_button.tsx13
-rw-r--r--packages/instant/src/components/buy_order_state_buttons.tsx4
-rw-r--r--packages/instant/src/components/zero_ex_instant_provider.tsx6
-rw-r--r--packages/instant/src/containers/selected_asset_buy_order_state_buttons.ts4
-rw-r--r--packages/instant/src/containers/selected_erc20_asset_amount_input.ts28
-rw-r--r--packages/instant/src/index.umd.ts3
-rw-r--r--packages/instant/src/redux/reducer.ts3
-rw-r--r--packages/instant/src/types.ts5
-rw-r--r--packages/instant/src/util/assert.ts10
9 files changed, 61 insertions, 15 deletions
diff --git a/packages/instant/src/components/buy_button.tsx b/packages/instant/src/components/buy_button.tsx
index c00b1678d..12ac62601 100644
--- a/packages/instant/src/components/buy_button.tsx
+++ b/packages/instant/src/components/buy_button.tsx
@@ -1,10 +1,11 @@
import { AssetBuyer, AssetBuyerError, BuyQuote } from '@0x/asset-buyer';
import * as _ from 'lodash';
import * as React from 'react';
+import { oc } from 'ts-optchain';
import { WEB_3_WRAPPER_TRANSACTION_FAILED_ERROR_MSG_PREFIX } from '../constants';
import { ColorOption } from '../style/theme';
-import { ZeroExInstantError } from '../types';
+import { AffiliateInfo, ZeroExInstantError } from '../types';
import { getBestAddress } from '../util/address';
import { balanceUtil } from '../util/balance';
import { gasPriceEstimator } from '../util/gas_price_estimator';
@@ -16,6 +17,7 @@ import { Button, Text } from './ui';
export interface BuyButtonProps {
buyQuote?: BuyQuote;
assetBuyer?: AssetBuyer;
+ affiliateInfo?: AffiliateInfo;
onValidationPending: (buyQuote: BuyQuote) => void;
onValidationFail: (buyQuote: BuyQuote, errorMessage: AssetBuyerError | ZeroExInstantError) => void;
onSignatureDenied: (buyQuote: BuyQuote) => void;
@@ -42,7 +44,7 @@ export class BuyButton extends React.Component<BuyButtonProps> {
}
private readonly _handleClick = async () => {
// The button is disabled when there is no buy quote anyway.
- const { buyQuote, assetBuyer } = this.props;
+ const { buyQuote, assetBuyer, affiliateInfo } = this.props;
if (_.isUndefined(buyQuote) || _.isUndefined(assetBuyer)) {
return;
}
@@ -58,8 +60,13 @@ export class BuyButton extends React.Component<BuyButtonProps> {
let txHash: string | undefined;
const gasInfo = await gasPriceEstimator.getGasInfoAsync();
+ const feeRecipient = oc(affiliateInfo).feeRecipient();
try {
- txHash = await assetBuyer.executeBuyQuoteAsync(buyQuote, { takerAddress, gasPrice: gasInfo.gasPriceInWei });
+ txHash = await assetBuyer.executeBuyQuoteAsync(buyQuote, {
+ feeRecipient,
+ takerAddress,
+ gasPrice: gasInfo.gasPriceInWei,
+ });
} catch (e) {
if (e instanceof Error) {
if (e.message === AssetBuyerError.SignatureRequestDenied) {
diff --git a/packages/instant/src/components/buy_order_state_buttons.tsx b/packages/instant/src/components/buy_order_state_buttons.tsx
index 1d02f8cd9..5c074a67a 100644
--- a/packages/instant/src/components/buy_order_state_buttons.tsx
+++ b/packages/instant/src/components/buy_order_state_buttons.tsx
@@ -2,7 +2,7 @@ import { AssetBuyer, AssetBuyerError, BuyQuote } from '@0x/asset-buyer';
import * as React from 'react';
import { ColorOption } from '../style/theme';
-import { OrderProcessState, ZeroExInstantError } from '../types';
+import { AffiliateInfo, OrderProcessState, ZeroExInstantError } from '../types';
import { BuyButton } from './buy_button';
import { PlacingOrderButton } from './placing_order_button';
@@ -13,6 +13,7 @@ export interface BuyOrderStateButtonProps {
buyQuote?: BuyQuote;
buyOrderProcessingState: OrderProcessState;
assetBuyer?: AssetBuyer;
+ affiliateInfo?: AffiliateInfo;
onViewTransaction: () => void;
onValidationPending: (buyQuote: BuyQuote) => void;
onValidationFail: (buyQuote: BuyQuote, errorMessage: AssetBuyerError | ZeroExInstantError) => void;
@@ -50,6 +51,7 @@ export const BuyOrderStateButtons: React.StatelessComponent<BuyOrderStateButtonP
<BuyButton
buyQuote={props.buyQuote}
assetBuyer={props.assetBuyer}
+ affiliateInfo={props.affiliateInfo}
onValidationPending={props.onValidationPending}
onValidationFail={props.onValidationFail}
onSignatureDenied={props.onSignatureDenied}
diff --git a/packages/instant/src/components/zero_ex_instant_provider.tsx b/packages/instant/src/components/zero_ex_instant_provider.tsx
index a7aecab9c..fce03a280 100644
--- a/packages/instant/src/components/zero_ex_instant_provider.tsx
+++ b/packages/instant/src/components/zero_ex_instant_provider.tsx
@@ -9,7 +9,7 @@ import { asyncData } from '../redux/async_data';
import { INITIAL_STATE, State } from '../redux/reducer';
import { store, Store } from '../redux/store';
import { fonts } from '../style/fonts';
-import { AssetMetaData, Network } from '../types';
+import { AffiliateInfo, AssetMetaData, Network } from '../types';
import { assetUtils } from '../util/asset';
import { BigNumberInput } from '../util/big_number_input';
import { errorFlasher } from '../util/error_flasher';
@@ -29,9 +29,10 @@ export interface ZeroExInstantProviderRequiredProps {
}
export interface ZeroExInstantProviderOptionalProps {
- defaultAssetBuyAmount?: number;
+ defaultAssetBuyAmount: number;
additionalAssetMetaDataMap: ObjectMap<AssetMetaData>;
networkId: Network;
+ affiliateInfo: AffiliateInfo;
}
export class ZeroExInstantProvider extends React.Component<ZeroExInstantProviderProps> {
@@ -66,6 +67,7 @@ export class ZeroExInstantProvider extends React.Component<ZeroExInstantProvider
? state.selectedAssetAmount
: new BigNumberInput(props.defaultAssetBuyAmount),
assetMetaDataMap: completeAssetMetaDataMap,
+ affiliateInfo: props.affiliateInfo,
};
return storeStateFromProps;
}
diff --git a/packages/instant/src/containers/selected_asset_buy_order_state_buttons.ts b/packages/instant/src/containers/selected_asset_buy_order_state_buttons.ts
index 7c36fa4d0..72d99f844 100644
--- a/packages/instant/src/containers/selected_asset_buy_order_state_buttons.ts
+++ b/packages/instant/src/containers/selected_asset_buy_order_state_buttons.ts
@@ -7,7 +7,7 @@ import { Dispatch } from 'redux';
import { BuyOrderStateButtons } from '../components/buy_order_state_buttons';
import { Action, actions } from '../redux/actions';
import { State } from '../redux/reducer';
-import { OrderProcessState, ZeroExInstantError } from '../types';
+import { AffiliateInfo, OrderProcessState, ZeroExInstantError } from '../types';
import { errorFlasher } from '../util/error_flasher';
import { etherscanUtil } from '../util/etherscan';
@@ -15,6 +15,7 @@ interface ConnectedState {
buyQuote?: BuyQuote;
buyOrderProcessingState: OrderProcessState;
assetBuyer?: AssetBuyer;
+ affiliateInfo?: AffiliateInfo;
onViewTransaction: () => void;
}
@@ -32,6 +33,7 @@ const mapStateToProps = (state: State, _ownProps: SelectedAssetBuyOrderStateButt
buyOrderProcessingState: state.buyOrderState.processState,
assetBuyer: state.assetBuyer,
buyQuote: state.latestBuyQuote,
+ affiliateInfo: state.affiliateInfo,
onViewTransaction: () => {
if (
state.assetBuyer &&
diff --git a/packages/instant/src/containers/selected_erc20_asset_amount_input.ts b/packages/instant/src/containers/selected_erc20_asset_amount_input.ts
index f0e792e2f..7859261dd 100644
--- a/packages/instant/src/containers/selected_erc20_asset_amount_input.ts
+++ b/packages/instant/src/containers/selected_erc20_asset_amount_input.ts
@@ -6,12 +6,13 @@ import * as _ from 'lodash';
import * as React from 'react';
import { connect } from 'react-redux';
import { Dispatch } from 'redux';
+import { oc } from 'ts-optchain';
import { ERC20AssetAmountInput } from '../components/erc20_asset_amount_input';
import { Action, actions } from '../redux/actions';
import { State } from '../redux/reducer';
import { ColorOption } from '../style/theme';
-import { ERC20Asset, OrderProcessState } from '../types';
+import { AffiliateInfo, ERC20Asset, OrderProcessState } from '../types';
import { assetUtils } from '../util/asset';
import { BigNumberInput } from '../util/big_number_input';
import { errorFlasher } from '../util/error_flasher';
@@ -27,10 +28,16 @@ interface ConnectedState {
value?: BigNumberInput;
asset?: ERC20Asset;
isDisabled: boolean;
+ affiliateInfo?: AffiliateInfo;
}
interface ConnectedDispatch {
- updateBuyQuote: (assetBuyer?: AssetBuyer, value?: BigNumberInput, asset?: ERC20Asset) => void;
+ updateBuyQuote: (
+ assetBuyer?: AssetBuyer,
+ value?: BigNumberInput,
+ asset?: ERC20Asset,
+ affiliateInfo?: AffiliateInfo,
+ ) => void;
}
interface ConnectedProps {
@@ -60,6 +67,7 @@ const mapStateToProps = (state: State, _ownProps: SelectedERC20AssetAmountInputP
value: state.selectedAssetAmount,
asset: selectedAsset as ERC20Asset,
isDisabled,
+ affiliateInfo: state.affiliateInfo,
};
};
@@ -68,6 +76,7 @@ const updateBuyQuoteAsync = async (
dispatch: Dispatch<Action>,
asset: ERC20Asset,
assetAmount: BigNumber,
+ affiliateInfo?: AffiliateInfo,
): Promise<void> => {
// get a new buy quote.
const baseUnitValue = Web3Wrapper.toBaseUnitAmount(assetAmount, asset.metaData.decimals);
@@ -75,9 +84,10 @@ const updateBuyQuoteAsync = async (
// mark quote as pending
dispatch(actions.setQuoteRequestStatePending());
+ const feePercentage = oc(affiliateInfo).feePercentage();
let newBuyQuote: BuyQuote | undefined;
try {
- newBuyQuote = await assetBuyer.getBuyQuoteAsync(asset.assetData, baseUnitValue);
+ newBuyQuote = await assetBuyer.getBuyQuoteAsync(asset.assetData, baseUnitValue, { feePercentage });
} catch (error) {
dispatch(actions.setQuoteRequestStateFailure());
let errorMessage;
@@ -93,7 +103,11 @@ const updateBuyQuoteAsync = async (
const assetName = assetUtils.bestNameForAsset(asset, 'This asset');
errorMessage = `${assetName} is currently unavailable`;
}
- errorFlasher.flashNewErrorMessage(dispatch, errorMessage);
+ if (!_.isUndefined(errorMessage)) {
+ errorFlasher.flashNewErrorMessage(dispatch, errorMessage);
+ } else {
+ throw error;
+ }
return;
}
// We have a successful new buy quote
@@ -108,7 +122,7 @@ const mapDispatchToProps = (
dispatch: Dispatch<Action>,
_ownProps: SelectedERC20AssetAmountInputProps,
): ConnectedDispatch => ({
- updateBuyQuote: (assetBuyer, value, asset) => {
+ updateBuyQuote: (assetBuyer, value, asset, affiliateInfo) => {
// Update the input
dispatch(actions.updateSelectedAssetAmount(value));
// invalidate the last buy quote.
@@ -120,7 +134,7 @@ const mapDispatchToProps = (
// even if it's debounced, give them the illusion it's loading
dispatch(actions.setQuoteRequestStatePending());
// tslint:disable-next-line:no-floating-promises
- debouncedUpdateBuyQuoteAsync(assetBuyer, dispatch, asset, value);
+ debouncedUpdateBuyQuoteAsync(assetBuyer, dispatch, asset, value, affiliateInfo);
}
},
});
@@ -135,7 +149,7 @@ const mergeProps = (
asset: connectedState.asset,
value: connectedState.value,
onChange: (value, asset) => {
- connectedDispatch.updateBuyQuote(connectedState.assetBuyer, value, asset);
+ connectedDispatch.updateBuyQuote(connectedState.assetBuyer, value, asset, connectedState.affiliateInfo);
},
isDisabled: connectedState.isDisabled,
};
diff --git a/packages/instant/src/index.umd.ts b/packages/instant/src/index.umd.ts
index b12e65485..806187a16 100644
--- a/packages/instant/src/index.umd.ts
+++ b/packages/instant/src/index.umd.ts
@@ -24,6 +24,9 @@ export const render = (props: ZeroExInstantOverlayProps, selector: string = DEFA
if (!_.isUndefined(props.zIndex)) {
assert.isNumber('props.zIndex', props.zIndex);
}
+ if (!_.isUndefined(props.affiliateInfo)) {
+ assert.isValidaffiliateInfo('props.affiliateInfo', props.affiliateInfo);
+ }
const appendToIfExists = document.querySelector(selector);
assert.assert(!_.isNull(appendToIfExists), `Could not find div with selector: ${selector}`);
const appendTo = appendToIfExists as Element;
diff --git a/packages/instant/src/redux/reducer.ts b/packages/instant/src/redux/reducer.ts
index cf24f8488..d1537b49b 100644
--- a/packages/instant/src/redux/reducer.ts
+++ b/packages/instant/src/redux/reducer.ts
@@ -6,6 +6,7 @@ import * as _ from 'lodash';
import { assetMetaDataMap } from '../data/asset_meta_data_map';
import {
+ AffiliateInfo,
Asset,
AssetMetaData,
AsyncProcessState,
@@ -31,6 +32,7 @@ export interface State {
quoteRequestState: AsyncProcessState;
latestErrorMessage?: string;
latestErrorDisplayStatus: DisplayStatus;
+ affiliateInfo?: AffiliateInfo;
}
export const INITIAL_STATE: State = {
@@ -43,6 +45,7 @@ export const INITIAL_STATE: State = {
latestErrorMessage: undefined,
latestErrorDisplayStatus: DisplayStatus.Hidden,
quoteRequestState: AsyncProcessState.NONE,
+ affiliateInfo: undefined,
};
export const reducer = (state: State = INITIAL_STATE, action: Action): State => {
diff --git a/packages/instant/src/types.ts b/packages/instant/src/types.ts
index 288a6d111..82dc77d2e 100644
--- a/packages/instant/src/types.ts
+++ b/packages/instant/src/types.ts
@@ -80,3 +80,8 @@ export enum ZeroExInstantError {
AssetMetaDataNotAvailable = 'ASSET_META_DATA_NOT_AVAILABLE',
InsufficientETH = 'INSUFFICIENT_ETH',
}
+
+export interface AffiliateInfo {
+ feeRecipient: string;
+ feePercentage: number;
+}
diff --git a/packages/instant/src/util/assert.ts b/packages/instant/src/util/assert.ts
index 584d3d4b1..20f8ddaee 100644
--- a/packages/instant/src/util/assert.ts
+++ b/packages/instant/src/util/assert.ts
@@ -4,7 +4,7 @@ import { assetDataUtils } from '@0x/order-utils';
import { AssetProxyId, ObjectMap, SignedOrder } from '@0x/types';
import * as _ from 'lodash';
-import { AssetMetaData } from '../types';
+import { AffiliateInfo, AssetMetaData } from '../types';
export const assert = {
...sharedAssert,
@@ -41,4 +41,12 @@ export const assert = {
assert.isUri(`${variableName}.imageUrl`, metaData.imageUrl);
}
},
+ isValidaffiliateInfo(variableName: string, affiliateInfo: AffiliateInfo): void {
+ assert.isETHAddressHex(`${variableName}.recipientAddress`, affiliateInfo.feeRecipient);
+ assert.isNumber(`${variableName}.percentage`, affiliateInfo.feePercentage);
+ assert.assert(
+ affiliateInfo.feePercentage >= 0 && affiliateInfo.feePercentage <= 0.05,
+ `Expected ${variableName}.percentage to be between 0 and 0.05, but is ${affiliateInfo.feePercentage}`,
+ );
+ },
};