0xtrails
Version:
SDK for Trails
1,218 lines (1,107 loc) • 31.9 kB
text/typescript
import { Databeat, type Event as DatabeatEvent } from "@databeat/tracker"
import { Bytes, Hash } from "ox"
import { DATABEAT_KEY, DATABEAT_SERVER } from "./constants.js"
import { getQueryParam } from "./queryParams.js"
import { getSequenceProjectAccessKey } from "./config.js"
import { logger } from "./logger.js"
let checkoutIdSingleton: string | null = null
export function hasCheckoutId() {
return checkoutIdSingleton !== null
}
export function resetCheckoutId() {
checkoutIdSingleton = null
try {
localStorage.removeItem("checkoutId")
} catch (error) {
logger.console.error("Error removing checkout id:", error)
}
}
export function getCheckoutUserId() {
try {
return localStorage.getItem("checkoutUserId")
} catch (error) {
logger.console.error("Error getting checkout user id:", error)
}
}
export function getNewCheckoutId(userAddress?: string) {
if (userAddress) {
try {
localStorage.setItem("checkoutUserId", userAddress.toLowerCase())
} catch (error) {
logger.console.error("Error setting checkout user id:", error)
}
}
generateCheckoutIdAndSet()
return getCheckoutId()
}
export function getCheckoutId() {
if (checkoutIdSingleton) {
return checkoutIdSingleton
}
try {
checkoutIdSingleton = localStorage.getItem("checkoutId")
} catch (error) {
logger.console.error("Error getting checkout id:", error)
}
return checkoutIdSingleton
}
export function generateCheckoutIdAndSet() {
resetCheckoutId()
checkoutIdSingleton = generateCheckoutId()
try {
localStorage.setItem("checkoutId", checkoutIdSingleton)
} catch (error) {
logger.console.error("Error setting checkout id:", error)
}
}
export function generateCheckoutId() {
try {
return crypto.randomUUID()
} catch (error) {
logger.console.error("Error generating checkout id:", error)
return Date.now().toString()
}
}
// Pseudonymize sensitive data like transaction hashes and addresses
export function pseudonymizeSimple(value: string): string {
if (!value) return value
// Use ox Hash function for proper synchronous hashing
// Convert any string to Bytes and then to hex
const normalizedValue = value?.toString()?.trim().toLowerCase() || ""
const inputBytes = Bytes.fromString(normalizedValue)
const hashBytes = Hash.sha256(inputBytes)
const hashHex = Bytes.toHex(hashBytes)
const id = `anon_${hashHex.replace("0x", "")}`
return id
}
// Pseudonymize user address using the same logic as Databeat with userIdHash: true
// This replicates the genUserId function from Databeat when privacy.userIdHash is enabled
export function pseudonymize(
address: string,
options?: {
userAgentSalt?: boolean
extraSalt?: string
},
): string {
if (!address) return ""
// Normalize the address (lowercase, as done in identifyUser)
let seed = address.toLowerCase()
// Add user agent salt if enabled (matches Databeat logic)
if (options?.userAgentSalt && typeof navigator !== "undefined") {
seed = `${seed}:${navigator.userAgent}`
}
// Add extra salt if provided (matches Databeat logic)
if (options?.extraSalt && options.extraSalt.length > 0) {
seed = `${seed}:${options.extraSalt}`
}
// Hash using SHA256 and convert to hex (same as Databeat)
const inputBytes = Bytes.fromString(seed)
const hashBytes = Hash.sha256(inputBytes)
const hashHex = Bytes.toHex(hashBytes)
// Return first 50 characters without 0x prefix (matches Databeat's substring(0, 50))
return hashHex.replace("0x", "").substring(0, 50)
}
export const EventType = {
PAGEVIEW: "PAGEVIEW" as const,
WIDGET_SCREEN: "WIDGET_SCREEN" as const,
PAYMENT_STARTED: "PAYMENT_STARTED" as const,
PAYMENT_COMPLETED: "PAYMENT_COMPLETED" as const,
PAYMENT_ERROR: "PAYMENT_ERROR" as const,
// Wallet events
WALLET_CONNECTED: "WALLET_CONNECTED" as const,
WALLET_DISCONNECTED: "WALLET_DISCONNECTED" as const,
// Intent events
INTENT_QUOTE_REQUESTED: "INTENT_QUOTE_REQUESTED" as const,
INTENT_QUOTE_RECEIVED: "INTENT_QUOTE_RECEIVED" as const,
INTENT_QUOTE_ERROR: "INTENT_QUOTE_ERROR" as const,
INTENT_COMMIT_STARTED: "INTENT_COMMIT_STARTED" as const,
INTENT_COMMIT_COMPLETED: "INTENT_COMMIT_COMPLETED" as const,
INTENT_COMMIT_ERROR: "INTENT_COMMIT_ERROR" as const,
// Transaction events
TRANSACTION_STARTED: "TRANSACTION_STARTED" as const,
TRANSACTION_SIGNED: "TRANSACTION_SIGNED" as const,
TRANSACTION_SUBMITTED: "TRANSACTION_SUBMITTED" as const,
TRANSACTION_CONFIRMED: "TRANSACTION_CONFIRMED" as const,
TRANSACTION_ERROR: "TRANSACTION_ERROR" as const,
// Relayer events
RELAYER_CALL_STARTED: "RELAYER_CALL_STARTED" as const,
RELAYER_CALL_COMPLETED: "RELAYER_CALL_COMPLETED" as const,
RELAYER_CALL_ERROR: "RELAYER_CALL_ERROR" as const,
}
export type EventTypes = keyof typeof EventType
export type Event = DatabeatEvent<EventTypes>
export type EventProps = any
abstract class BaseAnalytics {
private getCommonProps(): Record<string, string> {
return {
...this.getNavigatorProps(),
...this.getDocumentProps(),
...this.getWindowProps(),
sequenceProjectAccessKey: getSequenceProjectAccessKey(),
}
}
private getNavigatorProps(): Record<string, string> {
if (typeof navigator === "undefined") {
return {
userAgent: "",
}
}
return {
userAgent: navigator.userAgent,
}
}
private getDocumentProps(): Record<string, string> {
if (typeof window === "undefined") {
return {
userAgent: "",
title: "",
}
}
return {
title: document.title,
referrer: document.referrer,
}
}
private getWindowProps(): Record<string, string> {
if (typeof window === "undefined") {
return {
url: "",
origin: "",
hostname: "",
pathname: "",
screenWidth: "",
screenHeight: "",
screenPixelRatio: "",
screenResolution: "",
screenOrientation: "",
}
}
return {
url: window.location?.href,
origin: window?.origin || window?.location?.origin,
hostname: window.location?.hostname,
pathname: window.location?.pathname,
screenWidth: window.innerWidth?.toString(),
screenHeight: window.innerHeight?.toString(),
screenPixelRatio: window.devicePixelRatio?.toString(),
screenResolution: `${window.screen?.width}x${window.screen?.height}`,
screenOrientation: window.screen?.orientation?.type?.toString(),
}
}
abstract track(event: EventProps): void
trackPageview() {
this.track({
event: EventType.PAGEVIEW,
props: this.getCommonProps(),
})
}
trackWidgetScreen(data: {
screen: string
userAddress?: string
[key: string]: any
}) {
this.track({
event: EventType.WIDGET_SCREEN,
props: {
...this.getCommonProps(),
checkoutId: getCheckoutId(),
...data,
...(data.userAddress && {
userAddress: pseudonymize(data.userAddress),
}),
},
})
}
trackPaymentStarted(data: {
userAddress?: string
intentAddress?: string
originChainId?: number
destinationChainId?: number
originTokenAddress?: string
destinationTokenSymbol?: string
destinationTokenAddress?: string
destinationTokenAmount?: string
mode?: string
fundMethod?: string
sourceTokenPriceUsd?: string
destinationTokenPriceUsd?: string
}) {
resetCheckoutId()
this.track({
event: EventType.PAYMENT_STARTED,
props: {
...this.getCommonProps(),
checkoutId: getNewCheckoutId(data.userAddress),
...data,
...(data.userAddress && {
userAddress: pseudonymize(data.userAddress),
}),
...(data.intentAddress && {
intentAddress: pseudonymize(data.intentAddress),
}),
destinationChainId: data.destinationChainId?.toString(),
originChainId: data.originChainId?.toString(),
destinationTokenAmount: data.destinationTokenAmount?.toString(),
sourceTokenPriceUsd: data.sourceTokenPriceUsd?.toString(),
destinationTokenPriceUsd: data.destinationTokenPriceUsd?.toString(),
},
})
}
trackPaymentCompleted(data: {
userAddress?: string
intentAddress?: string
mode?: string
fundMethod?: string
depositTokenAmountUsd?: string
destinationTokenAmountUsd?: string
originChainId?: number
destinationChainId?: number
[key: string]: any
}) {
this.track({
event: EventType.PAYMENT_COMPLETED,
props: {
...this.getCommonProps(),
checkoutId: getCheckoutId(),
...data,
originChainId: data.originChainId?.toString(),
destinationChainId: data.destinationChainId?.toString(),
...(data.userAddress && {
userAddress: pseudonymize(data.userAddress),
}),
...(data.intentAddress && {
intentAddress: pseudonymize(data.intentAddress),
}),
depositTokenAmountUsd: data.depositTokenAmountUsd?.toString(),
destinationTokenAmountUsd: data.destinationTokenAmountUsd?.toString(),
},
})
resetCheckoutId()
}
trackPaymentError(data: {
error: string
userAddress?: string
intentAddress?: string
mode?: string
fundMethod?: string
originChainId?: number
destinationChainId?: number
[key: string]: any
}) {
this.track({
event: EventType.PAYMENT_ERROR,
props: {
...this.getCommonProps(),
checkoutId: getCheckoutId(),
...data,
...(data.userAddress && {
userAddress: pseudonymize(data.userAddress),
}),
...(data.intentAddress && {
intentAddress: pseudonymize(data.intentAddress),
}),
originChainId: data.originChainId?.toString(),
destinationChainId: data.destinationChainId?.toString(),
},
})
}
// Wallet tracking methods
trackWalletConnected(data: {
walletType: string
address: string
chainId: number
[key: string]: any
}) {
this.track({
event: EventType.WALLET_CONNECTED,
props: {
...this.getCommonProps(),
...data,
address: pseudonymize(data.address),
chainId: data.chainId?.toString(),
},
})
}
trackWalletDisconnected(data?: { [key: string]: any }) {
this.track({
event: EventType.WALLET_DISCONNECTED,
props: {
...this.getCommonProps(),
...(data || {}),
},
})
}
// Intent tracking methods
trackIntentQuoteRequested(data: {
originChainId: number
destinationChainId: number
originTokenAddress?: string
destinationTokenAddress?: string
userAddress?: string
mode?: string
fundMethod?: string
originTokenSymbol?: string
destinationTokenSymbol?: string
[key: string]: any
}) {
this.track({
event: EventType.INTENT_QUOTE_REQUESTED,
props: {
...this.getCommonProps(),
checkoutId: getCheckoutId(),
originChainId: data.originChainId?.toString(),
destinationChainId: data.destinationChainId?.toString(),
originTokenAddress: data.originTokenAddress,
destinationTokenAddress: data.destinationTokenAddress,
...(data.userAddress && {
userAddress: pseudonymize(data.userAddress),
}),
originTokenSymbol: data.originTokenSymbol,
destinationTokenSymbol: data.destinationTokenSymbol,
},
})
}
trackIntentQuoteReceived(data: {
quoteId: string
totalFeeUSD?: string
trailsFixedFeeUSD?: number
crossChainFeeTotalUSD?: number
takerFeeUSD?: number
providerFeeUSD?: number
trailsSwapFeeUSD?: number
gasFeesPerChainUSD?: number[]
originTokenTotalAmount?: string
destinationTokenAmount?: string
provider?: string
feeToken?: string
userAddress?: string
intentAddress?: string
mode?: string
fundMethod?: string
feeTokenSymbol?: string
originTokenSymbol?: string
destinationTokenSymbol?: string
depositTokenAmountUsd?: string
[key: string]: any
}) {
this.track({
event: EventType.INTENT_QUOTE_RECEIVED,
props: {
...this.getCommonProps(),
quoteId: pseudonymize(data.quoteId),
checkoutId: getCheckoutId(),
totalFeeUSD: data.totalFeeUSD?.toString(),
trailsFixedFeeUSD: data.trailsFixedFeeUSD?.toString(),
crossChainFeeTotalUSD: data.crossChainFeeTotalUSD?.toString(),
takerFeeUSD: data.takerFeeUSD?.toString(),
providerFeeUSD: data.providerFeeUSD?.toString(),
trailsSwapFeeUSD: data.trailsSwapFeeUSD?.toString(),
gasFeesPerChainUSD: data.gasFeesPerChainUSD
?.map((fee) => fee?.toString())
.join(","),
originTokenTotalAmount: data.originTokenTotalAmount?.toString(),
destinationTokenAmount: data.destinationTokenAmount?.toString(),
provider: data.provider,
feeToken: data.feeToken,
...(data.userAddress && {
userAddress: pseudonymize(data.userAddress),
}),
...(data.intentAddress && {
intentAddress: pseudonymize(data.intentAddress),
}),
feeTokenSymbol: data.feeTokenSymbol,
originTokenSymbol: data.originTokenSymbol,
destinationTokenSymbol: data.destinationTokenSymbol,
depositTokenAmountUsd: data.depositTokenAmountUsd?.toString(),
},
})
}
trackIntentQuoteError(data: {
error: string
userAddress?: string
originChainId?: number
destinationChainId?: number
originTokenAddress?: string
destinationTokenAddress?: string
mode?: string
fundMethod?: string
originTokenSymbol?: string
destinationTokenSymbol?: string
[key: string]: any
}) {
this.track({
event: EventType.INTENT_QUOTE_ERROR,
props: {
...this.getCommonProps(),
checkoutId: getCheckoutId(),
...data,
...(data.userAddress && {
userAddress: pseudonymize(data.userAddress),
}),
originChainId: data.originChainId?.toString(),
destinationChainId: data.destinationChainId?.toString(),
originTokenSymbol: data.originTokenSymbol,
destinationTokenSymbol: data.destinationTokenSymbol,
},
})
}
trackIntentCommitStarted(data: {
intentAddress: string
userAddress?: string
originTokenSymbol?: string
destinationTokenSymbol?: string
[key: string]: any
}) {
this.track({
event: EventType.INTENT_COMMIT_STARTED,
props: {
...this.getCommonProps(),
checkoutId: getCheckoutId(),
...data,
originChainId: data.originChainId?.toString(),
destinationChainId: data.destinationChainId?.toString(),
intentAddress: pseudonymize(data.intentAddress),
...(data.userAddress && {
userAddress: pseudonymize(data.userAddress),
}),
originTokenSymbol: data.originTokenSymbol,
destinationTokenSymbol: data.destinationTokenSymbol,
},
})
}
trackIntentCommitCompleted(data: {
intentAddress: string
userAddress?: string
originTokenSymbol?: string
destinationTokenSymbol?: string
[key: string]: any
}) {
this.track({
event: EventType.INTENT_COMMIT_COMPLETED,
props: {
...this.getCommonProps(),
checkoutId: getCheckoutId(),
...data,
originChainId: data.originChainId?.toString(),
destinationChainId: data.destinationChainId?.toString(),
intentAddress: pseudonymize(data.intentAddress),
...(data.userAddress && {
userAddress: pseudonymize(data.userAddress),
}),
originTokenSymbol: data.originTokenSymbol,
destinationTokenSymbol: data.destinationTokenSymbol,
},
})
}
trackIntentCommitError(data: {
error: string
userAddress?: string
intentAddress?: string
originChainId?: number
destinationChainId?: number
mode?: string
fundMethod?: string
originTokenSymbol?: string
destinationTokenSymbol?: string
[key: string]: any
}) {
this.track({
event: EventType.INTENT_COMMIT_ERROR,
props: {
...this.getCommonProps(),
checkoutId: getCheckoutId(),
...data,
...(data.userAddress && {
userAddress: pseudonymize(data.userAddress),
}),
...(data.intentAddress && {
intentAddress: pseudonymize(data.intentAddress),
}),
originChainId: data.originChainId?.toString(),
destinationChainId: data.destinationChainId?.toString(),
originTokenSymbol: data.originTokenSymbol,
destinationTokenSymbol: data.destinationTokenSymbol,
},
})
}
// Transaction tracking methods
trackTransactionStarted(data: {
transactionType: string
chainId: number
userAddress?: string
intentAddress?: string
mode?: string
fundMethod?: string
depositTokenAmountUsd?: string
[key: string]: any
}) {
this.track({
event: EventType.TRANSACTION_STARTED,
props: {
...this.getCommonProps(),
checkoutId: getCheckoutId(),
...data,
...(data.userAddress && {
userAddress: pseudonymize(data.userAddress),
}),
...(data.intentAddress && {
intentAddress: pseudonymize(data.intentAddress),
}),
chainId: data.chainId?.toString(),
depositTokenAmountUsd: data.depositTokenAmountUsd?.toString(),
},
})
}
trackTransactionSigned(data: {
transactionHash: string
userAddress?: string
intentAddress?: string
mode?: string
fundMethod?: string
depositTokenAmountUsd?: string
[key: string]: any
}) {
this.track({
event: EventType.TRANSACTION_SIGNED,
props: {
...this.getCommonProps(),
checkoutId: getCheckoutId(),
...data,
...(data.userAddress && {
userAddress: pseudonymize(data.userAddress),
}),
...(data.intentAddress && {
intentAddress: pseudonymize(data.intentAddress),
}),
depositTokenAmountUsd: data.depositTokenAmountUsd?.toString(),
},
})
}
trackTransactionSubmitted(data: {
transactionHash: string
chainId: number
userAddress?: string
intentAddress?: string
mode?: string
fundMethod?: string
depositTokenAmountUsd?: string
[key: string]: any
}) {
this.track({
event: EventType.TRANSACTION_SUBMITTED,
props: {
...this.getCommonProps(),
checkoutId: getCheckoutId(),
...data,
...(data.userAddress && {
userAddress: pseudonymize(data.userAddress),
}),
...(data.intentAddress && {
intentAddress: pseudonymize(data.intentAddress),
}),
chainId: data.chainId?.toString(),
depositTokenAmountUsd: data.depositTokenAmountUsd?.toString(),
},
})
}
trackTransactionConfirmed(data: {
transactionHash: string
blockNumber?: number
userAddress?: string
intentAddress?: string
mode?: string
fundMethod?: string
depositTokenAmountUsd?: string
[key: string]: any
}) {
this.track({
event: EventType.TRANSACTION_CONFIRMED,
props: {
...this.getCommonProps(),
checkoutId: getCheckoutId(),
...data,
chainId: data.chainId?.toString(),
...(data.userAddress && {
userAddress: pseudonymize(data.userAddress),
}),
...(data.intentAddress && {
intentAddress: pseudonymize(data.intentAddress),
}),
blockNumber: data.blockNumber?.toString(),
depositTokenAmountUsd: data.depositTokenAmountUsd?.toString(),
},
})
}
trackTransactionError(data: {
transactionHash: string
error: string
userAddress?: string
intentAddress?: string
mode?: string
fundMethod?: string
[key: string]: any
}) {
this.track({
event: EventType.TRANSACTION_ERROR,
props: {
...this.getCommonProps(),
checkoutId: getCheckoutId(),
...data,
...(data.userAddress && {
userAddress: pseudonymize(data.userAddress),
}),
...(data.intentAddress && {
intentAddress: pseudonymize(data.intentAddress),
}),
},
})
}
// Relayer tracking methods
trackRelayerCallStarted(data: {
walletAddress?: string
contractAddress?: string
chainId?: number
mode?: string
fundMethod?: string
[key: string]: any
}) {
this.track({
event: EventType.RELAYER_CALL_STARTED,
props: {
...this.getCommonProps(),
checkoutId: getCheckoutId(),
...data,
...(data.walletAddress && {
walletAddress: pseudonymize(data.walletAddress),
}),
...(data.contractAddress && {
contractAddress: pseudonymize(data.contractAddress),
}),
chainId: data.chainId?.toString(),
},
})
}
trackRelayerCallCompleted(data: {
walletAddress?: string
contractAddress?: string
chainId?: number
mode?: string
fundMethod?: string
[key: string]: any
}) {
this.track({
event: EventType.RELAYER_CALL_COMPLETED,
props: {
...this.getCommonProps(),
checkoutId: getCheckoutId(),
...data,
...(data.walletAddress && {
walletAddress: pseudonymize(data.walletAddress),
}),
...(data.contractAddress && {
contractAddress: pseudonymize(data.contractAddress),
}),
chainId: data.chainId?.toString(),
},
})
}
trackRelayerCallError(data: {
error: string
walletAddress?: string
contractAddress?: string
chainId?: number
mode?: string
fundMethod?: string
[key: string]: any
}) {
this.track({
event: EventType.RELAYER_CALL_ERROR,
props: {
...this.getCommonProps(),
checkoutId: getCheckoutId(),
...data,
...(data.walletAddress && {
walletAddress: pseudonymize(data.walletAddress),
}),
...(data.contractAddress && {
contractAddress: pseudonymize(data.contractAddress),
}),
chainId: data.chainId?.toString(),
},
})
}
}
class Analytics extends BaseAnalytics {
private databeat: Databeat<EventTypes>
private loggingEnabled: boolean = false
constructor(server: string, config: any) {
super()
this.databeat = new Databeat<EventTypes>(server, config)
}
identifyUser({ address }: { address: string }) {
if (getCheckoutUserId() !== address?.toLowerCase()) {
resetCheckoutId()
}
this.databeat.identify(address?.toLowerCase(), { userIdHash: true })
}
unidentifyUser() {
resetCheckoutId()
this.databeat.identify(undefined, { userIdHash: true })
}
track(event: EventProps) {
return this.databeat
.track(event)
.catch(() => {})
.then(() => this.logEvent(event))
}
enable() {
return this.databeat.enable()
}
allowTracking(allowTracking: boolean) {
return this.databeat.allowTracking(allowTracking)
}
disable() {
return this.databeat.disable()
}
logEvent(event: EventProps) {
if (this.loggingEnabled) {
logger.console.log("[trails-sdk] Analytics track:", event)
}
}
getSessionId() {
return this.databeat.getSessionId()
}
getUserId() {
return this.databeat.getUserId()
}
}
class MockAnalytics extends BaseAnalytics {
private loggingEnabled: boolean = false
constructor({ loggingEnabled }: { loggingEnabled: boolean }) {
super()
this.loggingEnabled = loggingEnabled
}
identifyUser(_data: { address: string }) {
return this
}
unidentifyUser() {
return this
}
enable() {
return this
}
allowTracking(_allowTracking: boolean) {
return this
}
track(event: EventProps) {
this.logEvent(event)
return this
}
disable() {
return this
}
logEvent(event: EventProps) {
if (this.loggingEnabled) {
logger.console.log("[trails-sdk] MockAnalytics track:", event)
}
}
getSessionId() {
return `0x${Math.random().toString(16).slice(2, 10)}`
}
getUserId() {
return `0x${Math.random().toString(16).slice(2, 10)}`
}
}
let singleton: Analytics | MockAnalytics | null = null
export const getAnalytics = () => {
if (singleton) {
return singleton
}
const debugLocalMode = getQueryParam("analyticsDebugLocal") === "true"
const debugProdMode = getQueryParam("analyticsDebugProd") === "true"
let isLocalhost = true
if (typeof window !== "undefined") {
isLocalhost =
window.location.hostname === "localhost" ||
window.location.hostname === "127.0.0.1"
}
const enableMockAnalytics =
(!DATABEAT_KEY || debugLocalMode || isLocalhost) && !debugProdMode
if (enableMockAnalytics) {
singleton = new MockAnalytics({ loggingEnabled: true }) // return a dummy analytics object
}
if (!singleton) {
singleton = new Analytics(DATABEAT_SERVER, {
jwt: DATABEAT_KEY,
})
}
logger.console.log(
"[trails-sdk] Analytics sessionId:",
singleton.getSessionId(),
"[trails-sdk] Analytics userId:",
singleton.getUserId(),
)
return singleton
}
export const enableAnalytics = () => {
const analytics = getAnalytics()
analytics.enable()
analytics.allowTracking(true)
analytics.trackPageview()
}
export const disableAnalytics = () => {
const analytics = getAnalytics()
analytics.disable()
analytics.allowTracking(false)
}
export const trackPageview = () => {
const analytics = getAnalytics()
analytics.trackPageview()
}
export const trackWidgetScreen = (data: {
screen: string
userAddress?: string
[key: string]: any
}) => {
const analytics = getAnalytics()
analytics.trackWidgetScreen(data)
}
export const trackPaymentStarted = (data: {
userAddress?: string
intentAddress?: string
originChainId?: number
destinationChainId?: number
originTokenAddress?: string
destinationTokenAddress?: string
destinationTokenSymbol?: string
destinationTokenAmount?: string
mode?: string
fundMethod?: string
sourceTokenPriceUsd?: string
destinationTokenPriceUsd?: string
}) => {
const analytics = getAnalytics()
analytics.trackPaymentStarted(data)
}
export const trackPaymentCompleted = (data: {
userAddress?: string
intentAddress?: string
mode?: string
fundMethod?: string
[key: string]: any
}) => {
const analytics = getAnalytics()
analytics.trackPaymentCompleted(data)
}
export const trackPaymentError = (data: {
error: string
userAddress?: string
intentAddress?: string
mode?: string
fundMethod?: string
[key: string]: any
}) => {
const analytics = getAnalytics()
analytics.trackPaymentError(data)
}
// Wallet tracking exports
export const trackWalletConnected = (data: {
walletType: string
address: string
chainId: number
[key: string]: any
}) => {
const analytics = getAnalytics()
analytics.identifyUser({ address: data.address })
analytics.trackWalletConnected(data)
}
export const trackWalletDisconnected = (data?: { [key: string]: any }) => {
const analytics = getAnalytics()
analytics.unidentifyUser()
analytics.trackWalletDisconnected(data)
}
// Intent tracking exports
export const trackIntentQuoteRequested = (data: {
originChainId: number
destinationChainId: number
tokenAddress?: string
userAddress?: string
mode?: string
fundMethod?: string
[key: string]: any
}) => {
const analytics = getAnalytics()
analytics.trackIntentQuoteRequested(data)
}
export const trackIntentQuoteReceived = (data: {
quoteId: string
totalFeeUSD?: string
trailsFixedFeeUSD?: number
crossChainFeeTotalUSD?: number
takerFeeUSD?: number
providerFeeUSD?: number
trailsSwapFeeUSD?: number
gasFeesPerChainUSD?: number[]
originTokenTotalAmount?: string
destinationTokenAmount?: string
provider?: string
feeToken?: string
userAddress?: string
intentAddress?: string
mode?: string
fundMethod?: string
[key: string]: any
}) => {
const analytics = getAnalytics()
analytics.trackIntentQuoteReceived(data)
}
export const trackIntentQuoteError = (data: {
error: string
userAddress?: string
originChainId?: number
destinationChainId?: number
originTokenAddress?: string
destinationTokenAddress?: string
mode?: string
fundMethod?: string
[key: string]: any
}) => {
const analytics = getAnalytics()
analytics.trackIntentQuoteError(data)
}
export const trackIntentCommitStarted = (data: {
intentAddress: string
userAddress?: string
originChainId?: number
destinationChainId?: number
mode?: string
fundMethod?: string
[key: string]: any
}) => {
const analytics = getAnalytics()
analytics.trackIntentCommitStarted(data)
}
export const trackIntentCommitCompleted = (data: {
intentAddress: string
userAddress?: string
originChainId?: number
destinationChainId?: number
mode?: string
fundMethod?: string
[key: string]: any
}) => {
const analytics = getAnalytics()
analytics.trackIntentCommitCompleted(data)
}
export const trackIntentCommitError = (data: {
error: string
userAddress?: string
intentAddress?: string
originChainId?: number
destinationChainId?: number
mode?: string
fundMethod?: string
[key: string]: any
}) => {
const analytics = getAnalytics()
analytics.trackIntentCommitError(data)
}
// Transaction tracking exports
export const trackTransactionStarted = (data: {
transactionType: string
chainId: number
userAddress?: string
intentAddress?: string
mode?: string
fundMethod?: string
[key: string]: any
}) => {
const analytics = getAnalytics()
analytics.trackTransactionStarted(data)
}
export const trackTransactionSigned = (data: {
transactionHash: string
userAddress?: string
intentAddress?: string
mode?: string
fundMethod?: string
[key: string]: any
}) => {
const analytics = getAnalytics()
analytics.trackTransactionSigned(data)
}
export const trackTransactionSubmitted = (data: {
transactionHash: string
chainId: number
userAddress?: string
intentAddress?: string
mode?: string
fundMethod?: string
[key: string]: any
}) => {
const analytics = getAnalytics()
analytics.trackTransactionSubmitted(data)
}
export const trackTransactionConfirmed = (data: {
transactionHash: string
blockNumber?: number
userAddress?: string
intentAddress?: string
mode?: string
fundMethod?: string
[key: string]: any
}) => {
const analytics = getAnalytics()
analytics.trackTransactionConfirmed(data)
}
export const trackTransactionError = (data: {
transactionHash: string
error: string
userAddress?: string
intentAddress?: string
mode?: string
fundMethod?: string
[key: string]: any
}) => {
const analytics = getAnalytics()
analytics.trackTransactionError(data)
}
// Relayer tracking exports
export const trackRelayerCallStarted = (data: {
walletAddress?: string
contractAddress?: string
chainId?: number
mode?: string
fundMethod?: string
[key: string]: any
}) => {
const analytics = getAnalytics()
analytics.trackRelayerCallStarted(data)
}
export const trackRelayerCallCompleted = (data: {
walletAddress?: string
contractAddress?: string
chainId?: number
mode?: string
fundMethod?: string
[key: string]: any
}) => {
const analytics = getAnalytics()
analytics.trackRelayerCallCompleted(data)
}
export const trackRelayerCallError = (data: {
error: string
walletAddress?: string
contractAddress?: string
chainId?: number
mode?: string
fundMethod?: string
[key: string]: any
}) => {
const analytics = getAnalytics()
analytics.trackRelayerCallError(data)
}
export function getSessionId() {
const analytics = getAnalytics()
return analytics.getSessionId()
}