Skip to content
Merged
5 changes: 5 additions & 0 deletions .changeset/deep-beans-push.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@relayprotocol/relay-kit-ui': patch
---

Add transaction count check for explicit deposits
17 changes: 14 additions & 3 deletions packages/ui/src/components/widgets/SwapWidgetRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import {
useIsWalletCompatible,
useFallbackState,
useGasTopUpRequired,
useEOADetection
useEOADetection,
useTransactionCount
} from '../../hooks/index.js'
import type { Address, WalletClient } from 'viem'
import { formatUnits, parseUnits } from 'viem'
Expand Down Expand Up @@ -567,7 +568,7 @@ const SwapWidgetRenderer: FC<SwapWidgetRendererProps> = ({
const loadingProtocolVersion =
fromChain?.id && originChainSupportsProtocolv2 && isLoadingFromTokenPrice

// Get native balance only when not swapping from native token
// Get native balance only when not swapping from native token
const isFromNative = fromToken?.address === fromChain?.currency?.address
const { value: nativeBalance } = useCurrencyBalance({
chain: fromChain,
Expand All @@ -582,14 +583,24 @@ const SwapWidgetRenderer: FC<SwapWidgetRendererProps> = ({
const effectiveNativeBalance = isFromNative ? fromBalance : nativeBalance
const hasZeroNativeBalance = effectiveNativeBalance === 0n

const { data: transactionCount } = useTransactionCount({
address: address,
chainId: fromChain?.id,
enabled: fromToken !== undefined && !isFromNative
})

const hasLowTransactionCount =
transactionCount !== undefined && transactionCount <= 1

const eoaExplicitDeposit = useEOADetection(
wallet,
quoteProtocol,
fromToken?.chainId,
fromChain?.vmType
)

const explicitDeposit = hasZeroNativeBalance ? true : eoaExplicitDeposit
const explicitDeposit =
hasZeroNativeBalance || hasLowTransactionCount ? true : eoaExplicitDeposit
const normalizedSponsoredTokens = useMemo(() => {
const chainVms = relayClient?.chains.reduce(
(chains, chain) => {
Expand Down
4 changes: 3 additions & 1 deletion packages/ui/src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { useInternalRelayChains } from './useInternalRelayChains.js'
import useGasTopUpRequired from './useGasTopUpRequired.js'
import useHyperliquidUsdcBalance from './useHyperliquidUsdcBalance.js'
import useEOADetection from './useEOADetection.js'
import useTransactionCount from './useTransactionCount.js'

export {
useMounted,
Expand All @@ -47,5 +48,6 @@ export {
useInternalRelayChains,
useGasTopUpRequired,
useHyperliquidUsdcBalance,
useEOADetection
useEOADetection,
useTransactionCount
}
62 changes: 62 additions & 0 deletions packages/ui/src/hooks/useTransactionCount.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { type Address, isAddress } from 'viem'
import { useQuery } from '@tanstack/react-query'
import { usePublicClient } from 'wagmi'

type UseTransactionCountProps = {
address?: Address | string
chainId?: number
enabled?: boolean
refreshInterval?: number
}

type UseTransactionCountData = {
data?: number
isLoading: boolean
isError: boolean
error: Error | null
}

const useTransactionCount = ({
address,
chainId,
enabled = true,
refreshInterval = 60000
}: UseTransactionCountProps): UseTransactionCountData => {
const publicClient = usePublicClient({ chainId })
const isValidAddress = address && isAddress(address)

const {
data,
isLoading,
isError,
error
} = useQuery({
queryKey: ['transactionCount', chainId, address],
queryFn: async () => {
if (!publicClient || !address) {
throw new Error('Missing publicClient or address')
}

return await publicClient.getTransactionCount({
address: address as Address
})
},
enabled: Boolean(
enabled &&
publicClient &&
isValidAddress &&
chainId !== undefined
),
refetchInterval: refreshInterval,
refetchOnWindowFocus: false
})

return {
data,
isLoading,
isError,
error: error as Error | null
}
}

export default useTransactionCount