From 4f1507edec738a0cdbf36f9c42660bc1e9a7b6dd Mon Sep 17 00:00:00 2001 From: 0xtxbi Date: Thu, 18 Dec 2025 13:18:39 +0100 Subject: [PATCH 1/4] add event names --- packages/ui/src/constants/events.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/ui/src/constants/events.ts b/packages/ui/src/constants/events.ts index df890384..dd82ea8d 100644 --- a/packages/ui/src/constants/events.ts +++ b/packages/ui/src/constants/events.ts @@ -50,6 +50,13 @@ export const EventNames = { ONRAMP_PASSTHROUGH_SUCCESS: 'ONRAMP_PASSTHROUGH_SUCCESS', GAS_TOP_UP_TOGGLE: 'GAS_TOP_UP_TOGGLE', AGW_CHECK_ERROR: 'AGW_CHECK_ERROR', + // EOA Detection Events + EOA_DETECTION_SUCCESS: 'EOA_DETECTION_SUCCESS', + EOA_DETECTION_TIMEOUT: 'EOA_DETECTION_TIMEOUT', + EOA_DETECTION_ERROR: 'EOA_DETECTION_ERROR', + EOA_DETECTION_ZERO_BALANCE: 'EOA_DETECTION_ZERO_BALANCE', + EOA_DETECTION_LOW_TX_COUNT: 'EOA_DETECTION_LOW_TX_COUNT', + CHAIN_STARRED: 'CHAIN_STARRED', CHAIN_UNSTARRED: 'CHAIN_UNSTARRED', //Common From 08ba56e419c3b7a8bbdda36291f195f0b399c4fd Mon Sep 17 00:00:00 2001 From: 0xtxbi Date: Thu, 18 Dec 2025 13:19:13 +0100 Subject: [PATCH 2/4] capture events --- .../components/widgets/SwapWidgetRenderer.tsx | 5 +- .../widget/TokenWidgetRenderer.tsx | 3 +- packages/ui/src/hooks/useEOADetection.ts | 90 ++++++++++++++++++- 3 files changed, 92 insertions(+), 6 deletions(-) diff --git a/packages/ui/src/components/widgets/SwapWidgetRenderer.tsx b/packages/ui/src/components/widgets/SwapWidgetRenderer.tsx index 47b099e2..5b63866e 100644 --- a/packages/ui/src/components/widgets/SwapWidgetRenderer.tsx +++ b/packages/ui/src/components/widgets/SwapWidgetRenderer.tsx @@ -40,7 +40,7 @@ import { isValidAddress, findSupportedWallet } from '../../utils/address.js' -import { adaptViemWallet, getDeadAddress } from '@relayprotocol/relay-sdk' +import { adaptViemWallet } from '@relayprotocol/relay-sdk' import { errorToJSON } from '../../utils/errors.js' import { useSwapButtonCta } from '../../hooks/widget/useSwapButtonCta.js' import { sha256 } from '../../utils/hashing.js' @@ -483,7 +483,8 @@ const SwapWidgetRenderer: FC = ({ fromChain, address, fromBalance, - isFromNative + isFromNative, + onAnalyticEvent ) const shouldSetQuoteParameters = diff --git a/packages/ui/src/components/widgets/TokenWidget/widget/TokenWidgetRenderer.tsx b/packages/ui/src/components/widgets/TokenWidget/widget/TokenWidgetRenderer.tsx index 9631f9a7..658dac26 100644 --- a/packages/ui/src/components/widgets/TokenWidget/widget/TokenWidgetRenderer.tsx +++ b/packages/ui/src/components/widgets/TokenWidget/widget/TokenWidgetRenderer.tsx @@ -569,7 +569,8 @@ const TokenWidgetRenderer: FC = ({ fromChain, address, fromBalance, - isFromNative + isFromNative, + onAnalyticEvent ) const shouldSetQuoteParameters = diff --git a/packages/ui/src/hooks/useEOADetection.ts b/packages/ui/src/hooks/useEOADetection.ts index 809b3983..5b9d923a 100644 --- a/packages/ui/src/hooks/useEOADetection.ts +++ b/packages/ui/src/hooks/useEOADetection.ts @@ -2,6 +2,7 @@ import { useMemo, useEffect, useState, useRef } from 'react' import type { AdaptedWallet, RelayChain } from '@relayprotocol/relay-sdk' import useCurrencyBalance from './useCurrencyBalance.js' import useTransactionCount from './useTransactionCount.js' +import { EventNames } from '../constants/events.js' /** * Hook to detect if a wallet is an EOA and return the appropriate explicitDeposit flag @@ -15,7 +16,8 @@ const useEOADetection = ( fromChain?: RelayChain, userAddress?: string, fromBalance?: bigint, - isFromNative?: boolean + isFromNative?: boolean, + onAnalyticEvent?: (eventName: string, data?: any) => void ): boolean | undefined => { const [detectionState, setDetectionState] = useState<{ value: boolean | undefined @@ -86,6 +88,42 @@ const useEOADetection = ( hasLowTransactionCount ]) + // Track safety check conditions that force explicit deposit + const hasTrackedSafetyCheck = useRef(null) + + useEffect(() => { + if (isLoadingSafetyChecks) return + + const trackingKey = `${chainId}:${userAddress}:${hasZeroNativeBalance}:${hasLowTransactionCount}` + if (hasTrackedSafetyCheck.current === trackingKey) return + + const baseEventData = { + chain_id: chainId, + address: userAddress, + wallet_type: wallet?.vmType, + native_balance: effectiveNativeBalance?.toString(), + transaction_count: transactionCount + } + + if (hasZeroNativeBalance) { + onAnalyticEvent?.(EventNames.EOA_DETECTION_ZERO_BALANCE, baseEventData) + hasTrackedSafetyCheck.current = trackingKey + } else if (hasLowTransactionCount) { + onAnalyticEvent?.(EventNames.EOA_DETECTION_LOW_TX_COUNT, baseEventData) + hasTrackedSafetyCheck.current = trackingKey + } + }, [ + isLoadingSafetyChecks, + hasZeroNativeBalance, + hasLowTransactionCount, + chainId, + userAddress, + wallet?.vmType, + effectiveNativeBalance, + transactionCount, + onAnalyticEvent + ]) + // Synchronously return undefined when conditions change const explicitDeposit = useMemo(() => { if (isLoadingSafetyChecks) { @@ -117,6 +155,12 @@ const useEOADetection = ( } const detectEOA = async () => { + const baseEventData = { + chain_id: chainId, + address: userAddress, + wallet_type: wallet?.vmType + } + try { if (!wallet || !wallet?.isEOA) { setDetectionState((current) => @@ -132,6 +176,8 @@ const useEOADetection = ( abortController.abort() }, 1000) + const startTime = performance.now() + try { const eoaResult = await Promise.race([ wallet.isEOA(chainId!), @@ -143,9 +189,18 @@ const useEOADetection = ( ]) clearTimeout(timeoutId) + const duration = performance.now() - startTime const { isEOA, isEIP7702Delegated } = eoaResult const explicitDepositValue = !isEOA || isEIP7702Delegated + onAnalyticEvent?.(EventNames.EOA_DETECTION_SUCCESS, { + ...baseEventData, + duration_ms: Math.round(duration), + is_eoa: isEOA, + is_eip7702_delegated: isEIP7702Delegated, + explicit_deposit: explicitDepositValue + }) + setDetectionState((current) => current.conditionKey === conditionKey ? { value: explicitDepositValue, conditionKey } @@ -153,6 +208,22 @@ const useEOADetection = ( ) } catch (eoaError: any) { clearTimeout(timeoutId) + const duration = performance.now() - startTime + const isTimeout = eoaError?.message === 'EOA_DETECTION_TIMEOUT' + + if (isTimeout) { + onAnalyticEvent?.(EventNames.EOA_DETECTION_TIMEOUT, { + ...baseEventData, + duration_ms: Math.round(duration) + }) + } else { + onAnalyticEvent?.(EventNames.EOA_DETECTION_ERROR, { + ...baseEventData, + duration_ms: Math.round(duration), + error_message: eoaError?.message || 'Unknown error', + error_name: eoaError?.name + }) + } setDetectionState((current) => current.conditionKey === conditionKey @@ -160,7 +231,13 @@ const useEOADetection = ( : current ) } - } catch (error) { + } catch (error: any) { + onAnalyticEvent?.(EventNames.EOA_DETECTION_ERROR, { + ...baseEventData, + error_message: error?.message || 'Unknown error', + error_name: error?.name + }) + setDetectionState((current) => current.conditionKey === conditionKey ? { value: true, conditionKey } @@ -170,7 +247,14 @@ const useEOADetection = ( } detectEOA() - }, [conditionKey, shouldDetect, wallet, chainId]) + }, [ + conditionKey, + shouldDetect, + wallet, + chainId, + userAddress, + onAnalyticEvent + ]) if (!shouldDetect && chainVmType === 'evm') { return explicitDeposit ?? true From 1d77ddedcd548d1e5cdae74e663987df6fd85d3a Mon Sep 17 00:00:00 2001 From: 0xtxbi Date: Thu, 18 Dec 2025 13:19:58 +0100 Subject: [PATCH 3/4] feat: changeset --- .changeset/tasty-rivers-itch.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/tasty-rivers-itch.md diff --git a/.changeset/tasty-rivers-itch.md b/.changeset/tasty-rivers-itch.md new file mode 100644 index 00000000..f28246b7 --- /dev/null +++ b/.changeset/tasty-rivers-itch.md @@ -0,0 +1,5 @@ +--- +'@relayprotocol/relay-kit-ui': patch +--- + +Add tracking for eoa detection From 730786b76681b088f8f62040b53596cf9888bf26 Mon Sep 17 00:00:00 2001 From: 0xtxbi Date: Thu, 18 Dec 2025 16:49:04 +0100 Subject: [PATCH 4/4] cleaner logs --- .../components/widgets/SwapWidgetRenderer.tsx | 3 +- .../widget/TokenWidgetRenderer.tsx | 3 +- packages/ui/src/constants/events.ts | 7 -- packages/ui/src/hooks/useEOADetection.ts | 67 +++---------------- 4 files changed, 10 insertions(+), 70 deletions(-) diff --git a/packages/ui/src/components/widgets/SwapWidgetRenderer.tsx b/packages/ui/src/components/widgets/SwapWidgetRenderer.tsx index 5b63866e..c102cc47 100644 --- a/packages/ui/src/components/widgets/SwapWidgetRenderer.tsx +++ b/packages/ui/src/components/widgets/SwapWidgetRenderer.tsx @@ -483,8 +483,7 @@ const SwapWidgetRenderer: FC = ({ fromChain, address, fromBalance, - isFromNative, - onAnalyticEvent + isFromNative ) const shouldSetQuoteParameters = diff --git a/packages/ui/src/components/widgets/TokenWidget/widget/TokenWidgetRenderer.tsx b/packages/ui/src/components/widgets/TokenWidget/widget/TokenWidgetRenderer.tsx index 658dac26..9631f9a7 100644 --- a/packages/ui/src/components/widgets/TokenWidget/widget/TokenWidgetRenderer.tsx +++ b/packages/ui/src/components/widgets/TokenWidget/widget/TokenWidgetRenderer.tsx @@ -569,8 +569,7 @@ const TokenWidgetRenderer: FC = ({ fromChain, address, fromBalance, - isFromNative, - onAnalyticEvent + isFromNative ) const shouldSetQuoteParameters = diff --git a/packages/ui/src/constants/events.ts b/packages/ui/src/constants/events.ts index dd82ea8d..df890384 100644 --- a/packages/ui/src/constants/events.ts +++ b/packages/ui/src/constants/events.ts @@ -50,13 +50,6 @@ export const EventNames = { ONRAMP_PASSTHROUGH_SUCCESS: 'ONRAMP_PASSTHROUGH_SUCCESS', GAS_TOP_UP_TOGGLE: 'GAS_TOP_UP_TOGGLE', AGW_CHECK_ERROR: 'AGW_CHECK_ERROR', - // EOA Detection Events - EOA_DETECTION_SUCCESS: 'EOA_DETECTION_SUCCESS', - EOA_DETECTION_TIMEOUT: 'EOA_DETECTION_TIMEOUT', - EOA_DETECTION_ERROR: 'EOA_DETECTION_ERROR', - EOA_DETECTION_ZERO_BALANCE: 'EOA_DETECTION_ZERO_BALANCE', - EOA_DETECTION_LOW_TX_COUNT: 'EOA_DETECTION_LOW_TX_COUNT', - CHAIN_STARRED: 'CHAIN_STARRED', CHAIN_UNSTARRED: 'CHAIN_UNSTARRED', //Common diff --git a/packages/ui/src/hooks/useEOADetection.ts b/packages/ui/src/hooks/useEOADetection.ts index 5b9d923a..d9f20e60 100644 --- a/packages/ui/src/hooks/useEOADetection.ts +++ b/packages/ui/src/hooks/useEOADetection.ts @@ -2,7 +2,6 @@ import { useMemo, useEffect, useState, useRef } from 'react' import type { AdaptedWallet, RelayChain } from '@relayprotocol/relay-sdk' import useCurrencyBalance from './useCurrencyBalance.js' import useTransactionCount from './useTransactionCount.js' -import { EventNames } from '../constants/events.js' /** * Hook to detect if a wallet is an EOA and return the appropriate explicitDeposit flag @@ -16,8 +15,7 @@ const useEOADetection = ( fromChain?: RelayChain, userAddress?: string, fromBalance?: bigint, - isFromNative?: boolean, - onAnalyticEvent?: (eventName: string, data?: any) => void + isFromNative?: boolean ): boolean | undefined => { const [detectionState, setDetectionState] = useState<{ value: boolean | undefined @@ -88,42 +86,6 @@ const useEOADetection = ( hasLowTransactionCount ]) - // Track safety check conditions that force explicit deposit - const hasTrackedSafetyCheck = useRef(null) - - useEffect(() => { - if (isLoadingSafetyChecks) return - - const trackingKey = `${chainId}:${userAddress}:${hasZeroNativeBalance}:${hasLowTransactionCount}` - if (hasTrackedSafetyCheck.current === trackingKey) return - - const baseEventData = { - chain_id: chainId, - address: userAddress, - wallet_type: wallet?.vmType, - native_balance: effectiveNativeBalance?.toString(), - transaction_count: transactionCount - } - - if (hasZeroNativeBalance) { - onAnalyticEvent?.(EventNames.EOA_DETECTION_ZERO_BALANCE, baseEventData) - hasTrackedSafetyCheck.current = trackingKey - } else if (hasLowTransactionCount) { - onAnalyticEvent?.(EventNames.EOA_DETECTION_LOW_TX_COUNT, baseEventData) - hasTrackedSafetyCheck.current = trackingKey - } - }, [ - isLoadingSafetyChecks, - hasZeroNativeBalance, - hasLowTransactionCount, - chainId, - userAddress, - wallet?.vmType, - effectiveNativeBalance, - transactionCount, - onAnalyticEvent - ]) - // Synchronously return undefined when conditions change const explicitDeposit = useMemo(() => { if (isLoadingSafetyChecks) { @@ -189,18 +151,9 @@ const useEOADetection = ( ]) clearTimeout(timeoutId) - const duration = performance.now() - startTime const { isEOA, isEIP7702Delegated } = eoaResult const explicitDepositValue = !isEOA || isEIP7702Delegated - onAnalyticEvent?.(EventNames.EOA_DETECTION_SUCCESS, { - ...baseEventData, - duration_ms: Math.round(duration), - is_eoa: isEOA, - is_eip7702_delegated: isEIP7702Delegated, - explicit_deposit: explicitDepositValue - }) - setDetectionState((current) => current.conditionKey === conditionKey ? { value: explicitDepositValue, conditionKey } @@ -212,13 +165,15 @@ const useEOADetection = ( const isTimeout = eoaError?.message === 'EOA_DETECTION_TIMEOUT' if (isTimeout) { - onAnalyticEvent?.(EventNames.EOA_DETECTION_TIMEOUT, { + console.error('[EOA Detection]', { ...baseEventData, + error_type: 'timeout', duration_ms: Math.round(duration) }) } else { - onAnalyticEvent?.(EventNames.EOA_DETECTION_ERROR, { + console.error('[EOA Detection]', { ...baseEventData, + error_type: 'error', duration_ms: Math.round(duration), error_message: eoaError?.message || 'Unknown error', error_name: eoaError?.name @@ -232,8 +187,9 @@ const useEOADetection = ( ) } } catch (error: any) { - onAnalyticEvent?.(EventNames.EOA_DETECTION_ERROR, { + console.error('[EOA Detection]', { ...baseEventData, + error_type: 'error', error_message: error?.message || 'Unknown error', error_name: error?.name }) @@ -247,14 +203,7 @@ const useEOADetection = ( } detectEOA() - }, [ - conditionKey, - shouldDetect, - wallet, - chainId, - userAddress, - onAnalyticEvent - ]) + }, [conditionKey, shouldDetect, wallet, chainId, userAddress]) if (!shouldDetect && chainVmType === 'evm') { return explicitDeposit ?? true