Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/ten-files-tell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@relayprotocol/relay-kit-ui': patch
---

Support hyperliquid balance fetching for spot coins
4 changes: 2 additions & 2 deletions packages/ui/src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import useFallbackState from './useFallbackState.js'
import useMoonPayTransaction from './useMoonPayTransaction.js'
import { useInternalRelayChains } from './useInternalRelayChains.js'
import useGasTopUpRequired from './useGasTopUpRequired.js'
import useHyperliquidUsdcBalance from './useHyperliquidUsdcBalance.js'
import useHyperliquidBalance from './useHyperliquidBalance.js'
import useEOADetection from './useEOADetection.js'
import useTransactionCount from './useTransactionCount.js'
import useTronBalance from './useTronBalance.js'
Expand Down Expand Up @@ -50,7 +50,7 @@ export {
useMoonPayTransaction,
useInternalRelayChains,
useGasTopUpRequired,
useHyperliquidUsdcBalance,
useHyperliquidBalance,
useEOADetection,
useTransactionCount,
useTronBalance
Expand Down
40 changes: 22 additions & 18 deletions packages/ui/src/hooks/useCurrencyBalance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { isValidAddress } from '../utils/address.js'
import useRelayClient from './useRelayClient.js'
import useEclipseBalance from '../hooks/useEclipseBalance.js'
import { eclipse } from '../utils/solana.js'
import useHyperliquidUsdcBalance from './useHyperliquidUsdcBalance.js'
import useHyperliquidBalance from './useHyperliquidBalance.js'
import useTronBalance from '../hooks/useTronBalance.js'

type UseBalanceProps = {
Expand Down Expand Up @@ -168,18 +168,22 @@ const useCurrencyBalance = ({
)
})

const hyperliquidUsdcBalance = useHyperliquidUsdcBalance(address, {
enabled: Boolean(
!adaptedWalletBalanceIsEnabled &&
chain &&
chain.vmType === 'hypevm' &&
address &&
_isValidAddress &&
enabled
),
gcTime: refreshInterval,
staleTime: refreshInterval
})
const hyperliquidBalance = useHyperliquidBalance(
address,
currency as string,
{
enabled: Boolean(
!adaptedWalletBalanceIsEnabled &&
chain &&
chain.vmType === 'hypevm' &&
address &&
_isValidAddress &&
enabled
),
gcTime: refreshInterval,
staleTime: refreshInterval
}
)

const tronBalance = useTronBalance(address, currency, {
enabled: Boolean(
Expand Down Expand Up @@ -297,11 +301,11 @@ const useCurrencyBalance = ({
}
} else if (chain?.vmType === 'hypevm') {
return {
value: hyperliquidUsdcBalance.balance,
queryKey: hyperliquidUsdcBalance.queryKey,
isLoading: hyperliquidUsdcBalance.isLoading,
isError: hyperliquidUsdcBalance.isError,
error: hyperliquidUsdcBalance.error,
value: hyperliquidBalance.balance,
queryKey: hyperliquidBalance.queryKey,
isLoading: hyperliquidBalance.isLoading,
isError: hyperliquidBalance.isError,
error: hyperliquidBalance.error,
isDuneBalance: false
}
} else if (chain?.vmType === 'tvm') {
Expand Down
117 changes: 117 additions & 0 deletions packages/ui/src/hooks/useHyperliquidBalance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { isAddress, parseUnits } from 'viem'
import {
useQuery,
type DefaultError,
type QueryKey
} from '@tanstack/react-query'

export type HyperliquidMarginSummary = {
accountValue?: string
totalNtlPos?: string
totalRawUsd?: string
totalMarginUsed?: string
}

export type HyperLiquidPerpsResponse = {
marginSummary?: HyperliquidMarginSummary
crossMarginSummary?: HyperliquidMarginSummary
crossMaintenanceMarginUsed?: string
withdrawable?: string
assetPositions?: any[]
time?: number
}

export type HyperliquidSpotBalance = {
coin: string
token: number
hold: string
total: string
entryNtl: string
}

export type HyperliquidSpotResponse = {
balances: HyperliquidSpotBalance[]
}

type QueryType = typeof useQuery<
string | undefined,
DefaultError,
string | undefined,
QueryKey
>
type QueryOptions = Parameters<QueryType>['0']

// Perps USDC uses zero address
const PERPS_USDC_ADDRESS = '0x00000000000000000000000000000000'

// Map currency addresses to Hyperliquid spot coin symbols and decimals
const SPOT_TOKEN_CONFIG: Record<string, { coin: string; decimals: number }> = {
'0x2e6d84f2d7ca82e6581e03523e4389f7': { coin: 'USDe', decimals: 2 },
'0x54e00a5988577cb0b0c9ab0cb6ef7f4b': { coin: 'USDH', decimals: 2 }
}

export default (
address?: string,
currency: string = PERPS_USDC_ADDRESS,
queryOptions?: Partial<QueryOptions>
) => {
const isEvmAddress = isAddress(address ?? '')
const isPerps = currency === PERPS_USDC_ADDRESS
const spotConfig = SPOT_TOKEN_CONFIG[currency.toLowerCase()]
const decimals = isPerps ? 8 : (spotConfig?.decimals ?? 2)

const queryKey = ['useHyperliquidBalance', address, currency]

const response = (useQuery as QueryType)({
queryKey,
queryFn: async () => {
if (!address || !isEvmAddress) {
return undefined
}

if (isPerps) {
// Fetch perps balance
const res = await fetch('https://api.hyperliquid.xyz/info', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
type: 'clearinghouseState',
user: address
})
})
const data = (await res.json()) as HyperLiquidPerpsResponse
return data?.withdrawable
} else if (spotConfig) {
// Fetch spot balances
const res = await fetch('https://api.hyperliquid.xyz/info', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
type: 'spotClearinghouseState',
user: address
})
})
const data = (await res.json()) as HyperliquidSpotResponse
// Find the balance matching the coin symbol
const tokenBalance = data?.balances?.find(
(b) => b.coin.toLowerCase() === spotConfig.coin.toLowerCase()
)
return tokenBalance?.total
}
return undefined
},
enabled: address !== undefined && isEvmAddress,
...queryOptions
})

const balance = parseUnits(response.data ?? '0', decimals)

return {
...response,
balance,
queryKey
} as ReturnType<QueryType> & {
balance: bigint
queryKey: (string | undefined)[]
}
}
72 changes: 0 additions & 72 deletions packages/ui/src/hooks/useHyperliquidUsdcBalance.ts

This file was deleted.