[SDK] Feature: Pay analytics (#4490)

## Problem solved

Short description of the bug fixed or feature added

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on adding tracking events for various payment methods and transactions across different screens.

### Detailed summary
- Added a new function `trackPayEvent` for tracking payment events
- Added tracking events for different payment methods like buy, direct payment, and swap
- Updated multiple files to include tracking events for specific user actions

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->
This commit is contained in:
gregfromstl
2024-09-10 23:44:23 +00:00
parent 865c5e3b16
commit 2166dec9cb
9 changed files with 163 additions and 4 deletions
@@ -216,7 +216,7 @@ export function ConfirmationScreen(props: {
if (step === "swap") {
setStatus("pending");
try {
let tx = props.buyWithCryptoQuote.transactionRequest
let tx = props.buyWithCryptoQuote.transactionRequest
if (wallet?.walletId === walletIds.embeddedWallet) {
tx = {
+29
View File
@@ -20,6 +20,35 @@ export function trackConnect(args: {
});
}
/**
* @internal
*/
export function trackPayEvent(args: {
client: ThirdwebClient;
event: string;
walletAddress?: string;
walletType?: string;
fromToken?: string;
fromAmount?: string;
toToken?: string;
toAmount?: string;
chainId?: number;
dstChainId?: number;
}) {
track(args.client, {
source: "pay",
action: args.event,
clientId: args.client.clientId,
chainId: args.chainId,
walletAddress: args.walletAddress,
walletType: args.walletType,
tokenAddress: args.fromToken,
amountWei: args.fromAmount,
dstTokenAddress: args.toToken,
dstChainId: args.chainId,
});
}
/**
* @internal
*/
@@ -10,6 +10,7 @@ import {
} from "@radix-ui/react-icons";
import { useQuery } from "@tanstack/react-query";
import { useContext, useEffect, useState } from "react";
import { trackPayEvent } from "../../../../analytics/track.js";
import type { Chain } from "../../../../chains/types.js";
import type { ThirdwebClient } from "../../../../client/client.js";
import { getContract } from "../../../../contract/contract.js";
@@ -568,6 +569,12 @@ function DetailsModal(props: {
flex: 1,
}}
onClick={() => {
trackPayEvent({
event: "details_modal_buy_click",
client: client,
walletAddress: activeAccount?.address,
walletType: activeWallet?.id,
});
setScreen("buy");
}}
>
@@ -1,5 +1,6 @@
import { IdCardIcon } from "@radix-ui/react-icons";
import { useCallback, useMemo, useState } from "react";
import { trackPayEvent } from "../../../../../../analytics/track.js";
import type { Chain } from "../../../../../../chains/types.js";
import type { ThirdwebClient } from "../../../../../../client/client.js";
import { NATIVE_TOKEN_ADDRESS } from "../../../../../../constants/addresses.js";
@@ -528,7 +529,13 @@ function BuyScreenContent(props: BuyScreenContentProps) {
}}
>
{screen.id === "select-payment-method" && (
<PaymentMethodSelection setScreen={(id) => setScreen({ id })} />
<PaymentMethodSelection
mode={payOptions.mode}
client={client}
walletAddress={payer.account.address}
walletType={payer.wallet.id}
setScreen={(id) => setScreen({ id })}
/>
)}
{screen.id === "select-wallet" && (
@@ -891,7 +898,11 @@ function TokenSelectedLayout(props: {
}
function PaymentMethodSelection(props: {
client: ThirdwebClient;
walletAddress: string;
walletType: string;
setScreen: (screenId: "select-wallet" | "buy-with-fiat") => void;
mode?: "transaction" | "direct_payment" | "fund_wallet";
}) {
return (
<Container animate="fadein">
@@ -900,7 +911,15 @@ function PaymentMethodSelection(props: {
<Button
variant="outline"
bg="tertiaryBg"
onClick={() => props.setScreen("buy-with-fiat")}
onClick={() => {
trackPayEvent({
event: `pay_with_credit_card_${props.mode || "unknown"}_mode`,
client: props.client,
walletAddress: props.walletAddress,
walletType: props.walletType,
});
props.setScreen("buy-with-fiat");
}}
gap="sm"
style={{
justifyContent: "flex-start",
@@ -928,7 +947,16 @@ function PaymentMethodSelection(props: {
<Button
variant="outline"
bg="tertiaryBg"
onClick={() => props.setScreen("select-wallet")}
onClick={() => {
trackPayEvent({
event: `pay_with_crypto_${props.mode || "unknown"}_mode`,
client: props.client,
walletAddress: props.walletAddress,
walletType: props.walletType,
});
props.setScreen("select-wallet");
}}
style={{
justifyContent: "flex-start",
}}
@@ -1,4 +1,5 @@
import { useQuery } from "@tanstack/react-query";
import { trackPayEvent } from "../../../../../../analytics/track.js";
import type { Chain } from "../../../../../../chains/types.js";
import type { ThirdwebClient } from "../../../../../../client/client.js";
import { NATIVE_TOKEN_ADDRESS } from "../../../../../../constants/addresses.js";
@@ -242,6 +243,12 @@ export function DirectPaymentModeScreen(props: {
variant="accent"
fullWidth
onClick={() => {
trackPayEvent({
event: "choose_payment_method_direct_payment_mode",
client,
walletAddress: payerAccount.address,
walletType: activeWallet?.id,
});
onContinue(totalCost, paymentInfo.chain, token);
}}
>
@@ -1,3 +1,4 @@
import { trackPayEvent } from "../../../../../../analytics/track.js";
import type { Chain } from "../../../../../../chains/types.js";
import type { ThirdwebClient } from "../../../../../../client/client.js";
import { NATIVE_TOKEN_ADDRESS } from "../../../../../../constants/addresses.js";
@@ -225,6 +226,12 @@ export function TransactionModeScreen(props: {
) {
totalCostWei += transactionCostAndData.gasCostWei;
}
trackPayEvent({
event: "choose_payment_method_transaction_mode",
client,
walletAddress: payerAccount.address,
walletType: activeWallet?.id,
});
onContinue(
toTokens(totalCostWei, transactionCostAndData.decimals),
payUiOptions.transaction.chain,
@@ -1,4 +1,5 @@
import { useCallback, useState } from "react";
import { trackPayEvent } from "../../../../../../../analytics/track.js";
import type { ThirdwebClient } from "../../../../../../../client/client.js";
import type { BuyWithFiatQuote } from "../../../../../../../pay/buyWithFiat/getQuote.js";
import {
@@ -88,6 +89,12 @@ export function FiatFlow(props: {
step={1}
onContinue={() => {
const popup = openOnrampPopup(props.quote.onRampLink, props.theme);
trackPayEvent({
event: "open_onramp_popup",
client: props.client,
walletAddress: props.payer.account.address,
walletType: props.payer.wallet.id,
});
addPendingTx({
type: "fiat",
intentId: props.quote.intentId,
@@ -1,5 +1,6 @@
import { CrossCircledIcon } from "@radix-ui/react-icons";
import { useState } from "react";
import { trackPayEvent } from "../../../../../../../analytics/track.js";
import type { Chain } from "../../../../../../../chains/types.js";
import type { ThirdwebClient } from "../../../../../../../client/client.js";
import type { BuyWithCryptoQuote } from "../../../../../../../pay/buyWithCrypto/getQuote.js";
@@ -190,6 +191,19 @@ export function SwapConfirmationScreen(props: {
try {
setStatus("pending");
trackPayEvent({
event: "prompt_swap_approval",
client: props.client,
walletAddress: props.payer.account.address,
walletType: props.payer.wallet.id,
fromToken: props.quote.swapDetails.fromToken.tokenAddress,
fromAmount: props.quote.swapDetails.fromAmountWei,
toToken: props.quote.swapDetails.toToken.tokenAddress,
toAmount: props.quote.swapDetails.toAmountWei,
chainId: props.quote.swapDetails.fromToken.chainId,
dstChainId: props.quote.swapDetails.toToken.chainId,
});
const tx = await sendTransaction({
account: props.payer.account,
transaction: props.quote.approval,
@@ -198,6 +212,19 @@ export function SwapConfirmationScreen(props: {
await waitForReceipt({ ...tx, maxBlocksWaitTime: 50 });
// props.onQuoteFinalized(props.quote);
trackPayEvent({
event: "swap_approval_success",
client: props.client,
walletAddress: props.payer.account.address,
walletType: props.payer.wallet.id,
fromToken: props.quote.swapDetails.fromToken.tokenAddress,
fromAmount: props.quote.swapDetails.fromAmountWei,
toToken: props.quote.swapDetails.toToken.tokenAddress,
toAmount: props.quote.swapDetails.toAmountWei,
chainId: props.quote.swapDetails.fromToken.chainId,
dstChainId: props.quote.swapDetails.toToken.chainId,
});
setStep("swap");
setStatus("idle");
} catch (e) {
@@ -223,6 +250,19 @@ export function SwapConfirmationScreen(props: {
};
}
trackPayEvent({
event: "prompt_swap_execution",
client: props.client,
walletAddress: props.payer.account.address,
walletType: props.payer.wallet.id,
fromToken: props.quote.swapDetails.fromToken.tokenAddress,
fromAmount: props.quote.swapDetails.fromAmountWei,
toToken: props.quote.swapDetails.toToken.tokenAddress,
toAmount: props.quote.swapDetails.toAmountWei,
chainId: props.quote.swapDetails.fromToken.chainId,
dstChainId: props.quote.swapDetails.toToken.chainId,
});
const _swapTx = await sendTransaction({
account: props.payer.account,
transaction: tx,
@@ -230,6 +270,19 @@ export function SwapConfirmationScreen(props: {
await waitForReceipt({ ..._swapTx, maxBlocksWaitTime: 50 });
trackPayEvent({
event: "swap_execution_success",
client: props.client,
walletAddress: props.payer.account.address,
walletType: props.payer.wallet.id,
fromToken: props.quote.swapDetails.fromToken.tokenAddress,
fromAmount: props.quote.swapDetails.fromAmountWei,
toToken: props.quote.swapDetails.toToken.tokenAddress,
toAmount: props.quote.swapDetails.toAmountWei,
chainId: props.quote.swapDetails.toToken.chainId,
dstChainId: props.quote.swapDetails.toToken.chainId,
});
// do not add pending tx if the swap is part of fiat flow
if (!props.isFiatFlow) {
addPendingTx({
@@ -1,10 +1,14 @@
import { useQuery } from "@tanstack/react-query";
import { useState } from "react";
import { trackPayEvent } from "../../../../analytics/track.js";
import type { ThirdwebClient } from "../../../../client/client.js";
import type { WaitForReceiptOptions } from "../../../../transaction/actions/wait-for-tx-receipt.js";
import type { PreparedTransaction } from "../../../../transaction/prepare-transaction.js";
import { CustomThemeProvider } from "../../../core/design-system/CustomThemeProvider.js";
import type { Theme } from "../../../core/design-system/index.js";
import type { PayUIOptions } from "../../../core/hooks/connection/ConnectButtonProps.js";
import { useActiveAccount } from "../../../core/hooks/wallets/useActiveAccount.js";
import { useActiveWallet } from "../../../core/hooks/wallets/useActiveWallet.js";
import type { SupportedTokens } from "../../../core/utils/defaultTokens.js";
import { LoadingScreen } from "../../wallets/shared/LoadingScreen.js";
import { useConnectLocale } from "../ConnectWallet/locale/getConnectLocale.js";
@@ -27,6 +31,23 @@ export type ModalProps = {
};
export function TransactionModal(props: ModalProps) {
const account = useActiveAccount();
const wallet = useActiveWallet();
useQuery({
queryKey: ["transaction-modal-event"],
queryFn: () => {
if (!account || !wallet) return;
trackPayEvent({
client: props.client,
walletAddress: account.address,
walletType: wallet.id,
event: "open_pay_transaction_modal",
});
},
enabled: !!wallet,
});
return (
<CustomThemeProvider theme={props.theme}>
<Modal