aboutsummaryrefslogtreecommitdiffstats
path: root/packages
diff options
context:
space:
mode:
Diffstat (limited to 'packages')
-rw-r--r--packages/instant/src/components/buy_button.tsx8
-rw-r--r--packages/instant/src/components/payment_method_dropdown.tsx2
-rw-r--r--packages/instant/src/components/zero_ex_instant_overlay.tsx9
-rw-r--r--packages/instant/src/constants.ts2
-rw-r--r--packages/instant/src/util/analytics.ts55
5 files changed, 73 insertions, 3 deletions
diff --git a/packages/instant/src/components/buy_button.tsx b/packages/instant/src/components/buy_button.tsx
index 8b6121e43..eeb42d6fc 100644
--- a/packages/instant/src/components/buy_button.tsx
+++ b/packages/instant/src/components/buy_button.tsx
@@ -8,6 +8,7 @@ import { oc } from 'ts-optchain';
import { WEB_3_WRAPPER_TRANSACTION_FAILED_ERROR_MSG_PREFIX } from '../constants';
import { ColorOption } from '../style/theme';
import { AffiliateInfo, ZeroExInstantError } from '../types';
+import { analytics } from '../util/analytics';
import { gasPriceEstimator } from '../util/gas_price_estimator';
import { util } from '../util/util';
@@ -59,6 +60,7 @@ export class BuyButton extends React.Component<BuyButtonProps> {
// if we don't have a balance for the user, let the transaction through, it will be handled by the wallet
const hasSufficientEth = _.isUndefined(accountEthBalanceInWei) || accountEthBalanceInWei.gte(ethNeededForBuy);
if (!hasSufficientEth) {
+ analytics.trackBuyNotEnoughEth(buyQuote);
this.props.onValidationFail(buyQuote, ZeroExInstantError.InsufficientETH);
return;
}
@@ -66,6 +68,7 @@ export class BuyButton extends React.Component<BuyButtonProps> {
const gasInfo = await gasPriceEstimator.getGasInfoAsync();
const feeRecipient = oc(affiliateInfo).feeRecipient();
try {
+ analytics.trackBuyStarted(buyQuote);
txHash = await assetBuyer.executeBuyQuoteAsync(buyQuote, {
feeRecipient,
takerAddress: accountAddress,
@@ -74,9 +77,11 @@ export class BuyButton extends React.Component<BuyButtonProps> {
} catch (e) {
if (e instanceof Error) {
if (e.message === AssetBuyerError.SignatureRequestDenied) {
+ analytics.trackBuySignatureDenied(buyQuote);
this.props.onSignatureDenied(buyQuote);
return;
} else if (e.message === AssetBuyerError.TransactionValueTooLow) {
+ analytics.trackBuySimulationFailed(buyQuote);
this.props.onValidationFail(buyQuote, AssetBuyerError.TransactionValueTooLow);
return;
}
@@ -87,14 +92,17 @@ export class BuyButton extends React.Component<BuyButtonProps> {
const expectedEndTimeUnix = startTimeUnix + gasInfo.estimatedTimeMs;
this.props.onBuyProcessing(buyQuote, txHash, startTimeUnix, expectedEndTimeUnix);
try {
+ analytics.trackBuyTxSubmitted(buyQuote, txHash, startTimeUnix, expectedEndTimeUnix);
await web3Wrapper.awaitTransactionSuccessAsync(txHash);
} catch (e) {
if (e instanceof Error && e.message.startsWith(WEB_3_WRAPPER_TRANSACTION_FAILED_ERROR_MSG_PREFIX)) {
+ analytics.trackBuyTxFailed(buyQuote, txHash, startTimeUnix, expectedEndTimeUnix);
this.props.onBuyFailure(buyQuote, txHash);
return;
}
throw e;
}
+ analytics.trackBuyTxSucceeded(buyQuote, txHash, startTimeUnix, expectedEndTimeUnix);
this.props.onBuySuccess(buyQuote, txHash);
};
}
diff --git a/packages/instant/src/components/payment_method_dropdown.tsx b/packages/instant/src/components/payment_method_dropdown.tsx
index 7d7fecbc2..872ac0831 100644
--- a/packages/instant/src/components/payment_method_dropdown.tsx
+++ b/packages/instant/src/components/payment_method_dropdown.tsx
@@ -1,5 +1,5 @@
import { BigNumber } from '@0x/utils';
-import copy from 'copy-to-clipboard';
+import * as copy from 'copy-to-clipboard';
import * as React from 'react';
import { Network } from '../types';
diff --git a/packages/instant/src/components/zero_ex_instant_overlay.tsx b/packages/instant/src/components/zero_ex_instant_overlay.tsx
index 2856ea3e3..b3fb57dd6 100644
--- a/packages/instant/src/components/zero_ex_instant_overlay.tsx
+++ b/packages/instant/src/components/zero_ex_instant_overlay.tsx
@@ -1,6 +1,7 @@
import * as React from 'react';
import { ZeroExInstantContainer } from '../components/zero_ex_instant_container';
+import { MAIN_CONTAINER_DIV_CLASS, OVERLAY_DIV_CLASS } from '../constants';
import { ColorOption } from '../style/theme';
import { Container } from './ui/container';
@@ -18,7 +19,7 @@ export const ZeroExInstantOverlay: React.StatelessComponent<ZeroExInstantOverlay
const { onClose, zIndex, ...rest } = props;
return (
<ZeroExInstantProvider {...rest}>
- <Overlay zIndex={zIndex}>
+ <Overlay zIndex={zIndex} className={OVERLAY_DIV_CLASS}>
<Flex height="100vh">
<Container position="absolute" top="0px" right="0px" display={{ default: 'initial', sm: 'none' }}>
<Icon
@@ -30,7 +31,11 @@ export const ZeroExInstantOverlay: React.StatelessComponent<ZeroExInstantOverlay
padding="2em 2em"
/>
</Container>
- <Container width={{ default: 'auto', sm: '100%' }} height={{ default: 'auto', sm: '100%' }}>
+ <Container
+ width={{ default: 'auto', sm: '100%' }}
+ height={{ default: 'auto', sm: '100%' }}
+ className={MAIN_CONTAINER_DIV_CLASS}
+ >
<ZeroExInstantContainer />
</Container>
</Flex>
diff --git a/packages/instant/src/constants.ts b/packages/instant/src/constants.ts
index be6077ca9..163be40b3 100644
--- a/packages/instant/src/constants.ts
+++ b/packages/instant/src/constants.ts
@@ -7,6 +7,8 @@ export const ETH_DECIMALS = 18;
export const DEFAULT_ZERO_EX_CONTAINER_SELECTOR = '#zeroExInstantContainer';
export const INJECTED_DIV_CLASS = 'zeroExInstantResetRoot';
export const INJECTED_DIV_ID = 'zeroExInstant';
+export const OVERLAY_DIV_CLASS = 'zeroExInstantOverlay';
+export const MAIN_CONTAINER_DIV_CLASS = 'zeroExInstantMainContainer';
export const WEB_3_WRAPPER_TRANSACTION_FAILED_ERROR_MSG_PREFIX = 'Transaction failed';
export const GWEI_IN_WEI = new BigNumber(1000000000);
export const ONE_SECOND_MS = 1000;
diff --git a/packages/instant/src/util/analytics.ts b/packages/instant/src/util/analytics.ts
index 05988965b..5bc9bb385 100644
--- a/packages/instant/src/util/analytics.ts
+++ b/packages/instant/src/util/analytics.ts
@@ -1,3 +1,6 @@
+import { BuyQuote } from '@0x/asset-buyer';
+import * as _ from 'lodash';
+
import { AffiliateInfo, Asset, Network, OrderSource, ProviderState } from '../types';
import { EventProperties, heapUtil } from './heap';
@@ -23,6 +26,13 @@ enum EventNames {
PAYMENT_METHOD_DROPDOWN_OPENED = 'Payment Method - Dropdown Opened',
PAYMENT_METHOD_OPENED_ETHERSCAN = 'Payment Method - Opened Etherscan',
PAYMENT_METHOD_COPIED_ADDRESS = 'Payment Method - Copied Address',
+ BUY_NOT_ENOUGH_ETH = 'Buy - Not Enough Eth',
+ BUY_STARTED = 'Buy - Started',
+ BUY_SIGNATURE_DENIED = 'Buy - Signature Denied',
+ BUY_SIMULATION_FAILED = 'Buy - Simulation Failed',
+ BUY_TX_SUBMITTED = 'Buy - Tx Submitted',
+ BUY_TX_SUCCEEDED = 'Buy - Tx Succeeded',
+ BUY_TX_FAILED = 'Buy - Tx Failed',
TOKEN_SELECTOR_OPENED = 'Token Selector - Opened',
TOKEN_SELECTOR_CLOSED = 'Token Selector - Closed',
TOKEN_SELECTOR_CHOSE = 'Token Selector - Chose',
@@ -46,6 +56,23 @@ function trackingEventFnWithPayload(eventName: EventNames): (eventProperties: Ev
};
}
+const buyQuoteEventProperties = (buyQuote: BuyQuote) => {
+ const assetBuyAmount = buyQuote.assetBuyAmount.toString();
+ const assetEthAmount = buyQuote.worstCaseQuoteInfo.assetEthAmount.toString();
+ const feeEthAmount = buyQuote.worstCaseQuoteInfo.feeEthAmount.toString();
+ const totalEthAmount = buyQuote.worstCaseQuoteInfo.totalEthAmount.toString();
+ const feePercentage = !_.isUndefined(buyQuote.feePercentage) ? buyQuote.feePercentage.toString() : 0;
+ const hasFeeOrders = !_.isEmpty(buyQuote.feeOrders) ? 'true' : 'false';
+ return {
+ assetBuyAmount,
+ assetEthAmount,
+ feeEthAmount,
+ totalEthAmount,
+ feePercentage,
+ hasFeeOrders,
+ };
+};
+
export interface AnalyticsUserOptions {
lastKnownEthAddress?: string;
ethBalanceInUnitAmount?: string;
@@ -115,6 +142,34 @@ export const analytics = {
trackPaymentMethodDropdownOpened: trackingEventFnWithoutPayload(EventNames.PAYMENT_METHOD_DROPDOWN_OPENED),
trackPaymentMethodOpenedEtherscan: trackingEventFnWithoutPayload(EventNames.PAYMENT_METHOD_OPENED_ETHERSCAN),
trackPaymentMethodCopiedAddress: trackingEventFnWithoutPayload(EventNames.PAYMENT_METHOD_COPIED_ADDRESS),
+ trackBuyNotEnoughEth: (buyQuote: BuyQuote) =>
+ trackingEventFnWithPayload(EventNames.BUY_NOT_ENOUGH_ETH)(buyQuoteEventProperties(buyQuote)),
+ trackBuyStarted: (buyQuote: BuyQuote) =>
+ trackingEventFnWithPayload(EventNames.BUY_STARTED)(buyQuoteEventProperties(buyQuote)),
+ trackBuySignatureDenied: (buyQuote: BuyQuote) =>
+ trackingEventFnWithPayload(EventNames.BUY_SIGNATURE_DENIED)(buyQuoteEventProperties(buyQuote)),
+ trackBuySimulationFailed: (buyQuote: BuyQuote) =>
+ trackingEventFnWithPayload(EventNames.BUY_SIMULATION_FAILED)(buyQuoteEventProperties(buyQuote)),
+ trackBuyTxSubmitted: (buyQuote: BuyQuote, txHash: string, startTimeUnix: number, expectedEndTimeUnix: number) =>
+ trackingEventFnWithPayload(EventNames.BUY_TX_SUBMITTED)({
+ ...buyQuoteEventProperties(buyQuote),
+ txHash,
+ expectedTxTimeMs: expectedEndTimeUnix - startTimeUnix,
+ }),
+ trackBuyTxSucceeded: (buyQuote: BuyQuote, txHash: string, startTimeUnix: number, expectedEndTimeUnix: number) =>
+ trackingEventFnWithPayload(EventNames.BUY_TX_SUCCEEDED)({
+ ...buyQuoteEventProperties(buyQuote),
+ txHash,
+ expectedTxTimeMs: expectedEndTimeUnix - startTimeUnix,
+ actualTxTimeMs: new Date().getTime() - startTimeUnix,
+ }),
+ trackBuyTxFailed: (buyQuote: BuyQuote, txHash: string, startTimeUnix: number, expectedEndTimeUnix: number) =>
+ trackingEventFnWithPayload(EventNames.BUY_TX_FAILED)({
+ ...buyQuoteEventProperties(buyQuote),
+ txHash,
+ expectedTxTimeMs: expectedEndTimeUnix - startTimeUnix,
+ actualTxTimeMs: new Date().getTime() - startTimeUnix,
+ }),
trackTokenSelectorOpened: trackingEventFnWithoutPayload(EventNames.TOKEN_SELECTOR_OPENED),
trackTokenSelectorClosed: (closedVia: TokenSelectorClosedVia) =>
trackingEventFnWithPayload(EventNames.TOKEN_SELECTOR_CLOSED)({ closedVia }),