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/slick-nights-act.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@relayprotocol/relay-kit-ui': patch
---

Add support for lighter evm address lookup
99 changes: 90 additions & 9 deletions packages/ui/src/components/common/CustomAddressModal.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { type FC, useState, useEffect, useMemo, useContext } from 'react'
import { Text, Flex, Button, Input, Pill } from '../primitives/index.js'
import { Modal } from '../common/Modal.js'
import { type Address } from 'viem'
import { type Address, isAddress } from 'viem'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { useENSResolver, useWalletAddress } from '../../hooks/index.js'
import {
useENSResolver,
useWalletAddress,
useLighterAccount
} from '../../hooks/index.js'
import { isENSName } from '../../utils/ens.js'
import { LoadingSpinner } from '../common/LoadingSpinner.js'
import { EventNames } from '../../constants/events.js'
Expand All @@ -24,6 +28,7 @@ import {
addCustomAddress,
getCustomAddresses
} from '../../utils/localStorage.js'
import { isLighterAddress } from '../../utils/lighter.js'

type Props = {
open: boolean
Expand Down Expand Up @@ -62,6 +67,27 @@ export const CustomAddressModal: FC<Props> = ({
const providerOptionsContext = useContext(ProviderOptionsContext)
const connectorKeyOverrides = providerOptionsContext.vmConnectorKeyOverrides

// Lighter: allow resolving EVM address to Lighter account ID (and vice versa)
const isLighterChain = toChain?.vmType === 'lvm'
const isEvmInput = isAddress(input)
const isLighterIndexInput = isLighterAddress(input)

const {
data: lighterAccount,
isLoading: isResolvingLighter,
isError: isLighterError
} = useLighterAccount(
isLighterChain && (isEvmInput || isLighterIndexInput) ? input : undefined
)

const resolvedLighterIndex = lighterAccount?.index?.toString()

const didResolveLighterFromEvm =
isLighterChain &&
isEvmInput &&
!!resolvedLighterIndex &&
isLighterAddress(resolvedLighterIndex)

const availableWallets = useMemo(
() =>
linkedWallets.filter((wallet) =>
Expand All @@ -84,10 +110,22 @@ export const CustomAddressModal: FC<Props> = ({
[recentCustomAddresses, toChain]
)

// For Lighter: check if the EVM address (input or resolved) matches connected wallet
const isLighterConnectedWallet =
isLighterChain &&
!!lighterAccount &&
// User entered EVM address - check if it matches connected wallet
((isEvmInput && input.toLowerCase() === connectedAddress?.toLowerCase()) ||
// User entered Lighter index - check if resolved l1_address matches connected wallet
(isLighterIndexInput &&
lighterAccount.l1_address?.toLowerCase() ===
connectedAddress?.toLowerCase()))

const connectedAddressSet =
(!address && !toAddress) ||
(toAddress === connectedAddress && address === connectedAddress) ||
availableWallets.some((wallet) => wallet.address === toAddress)
availableWallets.some((wallet) => wallet.address === toAddress) ||
isLighterConnectedWallet

useEffect(() => {
if (!open) {
Expand All @@ -104,19 +142,30 @@ export const CustomAddressModal: FC<Props> = ({
}
}, [open])

const { data: resolvedENS, isLoading } = useENSResolver(
const { data: resolvedENS, isLoading: isLoadingENS } = useENSResolver(
isENSName(input) ? input : ''
)

const isLoading = isLoadingENS || isResolvingLighter

useEffect(() => {
if (isValidAddress(toChain?.vmType, input, toChain?.id)) {
if (isLighterChain && isEvmInput) {
setAddress(resolvedLighterIndex ?? '')
} else if (isValidAddress(toChain?.vmType, input, toChain?.id)) {
setAddress(input)
} else if (resolvedENS?.address) {
setAddress(resolvedENS.address)
} else {
setAddress('')
}
}, [input, resolvedENS])
}, [
input,
resolvedENS,
resolvedLighterIndex,
isLighterChain,
isEvmInput,
toChain
])

return (
<Modal
Expand Down Expand Up @@ -164,7 +213,9 @@ export const CustomAddressModal: FC<Props> = ({
? 'Enter address'
: toChain.vmType === 'evm'
? 'Address or ENS'
: `Enter ${toChain.displayName} address`
: isLighterChain
? `${toChain.displayName} address or EVM address`
: `Enter ${toChain.displayName} address`
}
value={input}
onChange={(e) => {
Expand Down Expand Up @@ -212,12 +263,40 @@ export const CustomAddressModal: FC<Props> = ({
/>
)}
</Flex>
{!address && input.length ? (
{isLighterError ? (
<Text color="red" style="subtitle2">
Failed to resolve Lighter address
</Text>
) : isLighterChain &&
isEvmInput &&
!isResolvingLighter &&
!resolvedLighterIndex ? (
<Text color="red" style="subtitle2">
No Lighter account found for this EVM address
</Text>
) : !address && input.length && !isLoading ? (
<Text color="red" style="subtitle2">
Not a valid address
</Text>
) : null}

{didResolveLighterFromEvm && resolvedLighterIndex ? (
<Flex
css={{ bg: 'green2', p: '2', borderRadius: 8, gap: '2' }}
align="center"
>
<FontAwesomeIcon
icon={faCircleCheck}
color="#30A46C"
width={16}
height={16}
/>
<Text style="subtitle3">
Lighter Account ID: {resolvedLighterIndex}
</Text>
</Flex>
) : null}

{!connectedAddressSet && address && isConnected ? (
<Flex
css={{ bg: 'amber2', p: '2', borderRadius: 8, gap: '2' }}
Expand Down Expand Up @@ -304,7 +383,9 @@ export const CustomAddressModal: FC<Props> = ({
</Flex>
<Button
cta={true}
disabled={!isValidAddress(toChain?.vmType, address, toChain?.id)}
disabled={
isLoading || !isValidAddress(toChain?.vmType, address, toChain?.id)
}
css={{ justifyContent: 'center' }}
onClick={() => {
if (isValidAddress(toChain?.vmType, address, toChain?.id)) {
Expand Down
10 changes: 7 additions & 3 deletions packages/ui/src/components/common/MultiWalletDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,7 @@ export const MultiWalletDropdown: FC<MultiWalletDropdownProps> = ({
corners="pill"
css={{
gap: '2',
pl: '0 !important',
pr: '2 !important',
px: '2 !important',
py: '1',
cursor: 'pointer',
display: 'flex',
Expand All @@ -160,7 +159,12 @@ export const MultiWalletDropdown: FC<MultiWalletDropdownProps> = ({
{isSupportedSelectedWallet && selectedWallet?.walletLogoUrl ? (
<img
src={selectedWallet.walletLogoUrl}
style={{ width: 16, height: 16, borderRadius: 4, flexShrink: 0 }}
style={{
width: 16,
height: 16,
borderRadius: 4,
flexShrink: 0
}}
/>
) : selectedWalletAddress && !selectedWallet ? (
<Box css={{ color: 'amber11', flexShrink: 0 }}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ import {
} from 'react'
import {
useRelayClient,
useENSResolver,
useIsPassthrough,
useSupportedMoonPayCurrencyCode,
useFallbackState
useFallbackState,
useDisplayName
} from '../../../../hooks/index.js'
import { zeroAddress } from 'viem'
import { type ChainVM, type RelayChain } from '@relayprotocol/relay-sdk'
Expand Down Expand Up @@ -176,10 +176,11 @@ const OnrampWidgetRenderer: FC<OnrampWidgetRendererProps> = ({
const [recipient, setRecipient] = useState<string | undefined>(
defaultWalletAddress
)
const { displayName: toDisplayName } = useENSResolver(recipient, {
refetchOnWindowFocus: false,
enabled: toChain?.vmType === 'evm'
})
const { displayName: toDisplayName } = useDisplayName(
recipient,
toChain?.vmType,
toChain?.id
)

const [fiatCurrency, setFiatCurrency] = useState<FiatCurrency>({
name: 'US Dollar',
Expand Down
31 changes: 26 additions & 5 deletions packages/ui/src/components/widgets/SwapWidgetRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import type { Dispatch, FC, ReactNode, SetStateAction } from 'react'
import { useCallback, useContext, useEffect, useMemo, useState } from 'react'
import {
useCurrencyBalance,
useENSResolver,
useRelayClient,
useDebounceState,
useWalletAddress,
Expand All @@ -11,7 +10,9 @@ import {
useIsWalletCompatible,
useFallbackState,
useGasTopUpRequired,
useEOADetection
useEOADetection,
useDisplayName,
useLighterAccount
} from '../../hooks/index.js'
import type { Address, WalletClient } from 'viem'
import { formatUnits, parseUnits } from 'viem'
Expand Down Expand Up @@ -45,6 +46,7 @@ import { errorToJSON } from '../../utils/errors.js'
import { useSwapButtonCta } from '../../hooks/widget/useSwapButtonCta.js'
import { sha256 } from '../../utils/hashing.js'
import { get15MinuteInterval } from '../../utils/time.js'
import { isLighterAddress } from '../../utils/lighter.js'
import type { FeeBreakdown } from '../../types/FeeBreakdown.js'

export type TradeType = 'EXACT_INPUT' | 'EXPECTED_OUTPUT'
Expand Down Expand Up @@ -425,9 +427,11 @@ const SwapWidgetRenderer: FC<SwapWidgetRendererProps> = ({
toChain?.id
)

const { displayName: toDisplayName } = useENSResolver(recipient, {
enabled: toChain?.vmType === 'evm' && isValidToAddress
})
const { displayName: toDisplayName } = useDisplayName(
recipient,
toChain?.vmType,
toChain?.id
)

const [currentSlippageTolerance, setCurrentSlippageTolerance] = useState<
string | undefined
Expand Down Expand Up @@ -648,6 +652,23 @@ const SwapWidgetRenderer: FC<SwapWidgetRendererProps> = ({
setCustomToAddress(undefined)
})

// Auto-select Lighter account when switching to LVM chain
const isLighterChain = toChain?.vmType === 'lvm'
const { data: connectedLighterAccount } = useLighterAccount(
isLighterChain && address ? address : undefined
)

useEffect(() => {
if (
isLighterChain &&
connectedLighterAccount?.index &&
// Only auto-set if no valid Lighter address is already set
(!customToAddress || !isLighterAddress(customToAddress))
) {
setCustomToAddress(connectedLighterAccount.index.toString())
}
}, [isLighterChain, connectedLighterAccount, customToAddress])

useEffect(() => {
if (tradeType === 'EXACT_INPUT') {
const amountOut = quote?.details?.currencyOut?.amount ?? ''
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@ import type { Dispatch, FC, ReactNode, SetStateAction } from 'react'
import { useCallback, useContext, useEffect, useMemo, useState } from 'react'
import {
useCurrencyBalance,
useENSResolver,
useRelayClient,
useDebounceState,
useWalletAddress,
useDisconnected,
usePreviousValueChange,
useIsWalletCompatible,
useFallbackState,
useEOADetection
useEOADetection,
useDisplayName,
useLighterAccount
} from '../../../../hooks/index.js'
import type { Address, WalletClient } from 'viem'
import { formatUnits, parseUnits } from 'viem'
Expand Down Expand Up @@ -44,6 +45,7 @@ import { errorToJSON } from '../../../../utils/errors.js'
import { useSwapButtonCta } from '../../../../hooks/widget/useSwapButtonCta.js'
import { sha256 } from '../../../../utils/hashing.js'
import { get15MinuteInterval } from '../../../../utils/time.js'
import { isLighterAddress } from '../../../../utils/lighter.js'
import type { FeeBreakdown } from '../../../../types/FeeBreakdown.js'

export type TradeType = 'EXACT_INPUT' | 'EXPECTED_OUTPUT'
Expand Down Expand Up @@ -518,9 +520,11 @@ const TokenWidgetRenderer: FC<TokenWidgetRendererProps> = ({
toChain?.id
)

const { displayName: toDisplayName } = useENSResolver(recipient, {
enabled: toChain?.vmType === 'evm' && isValidToAddress
})
const { displayName: toDisplayName } = useDisplayName(
recipient,
toChain?.vmType,
toChain?.id
)

const [currentSlippageTolerance, setCurrentSlippageTolerance] = useState<
string | undefined
Expand Down Expand Up @@ -740,6 +744,23 @@ const TokenWidgetRenderer: FC<TokenWidgetRendererProps> = ({
setDestinationAddressOverride(undefined)
})

// Auto-select Lighter account when switching to LVM chain
const isLighterChain = toChain?.vmType === 'lvm'
const { data: connectedLighterAccount } = useLighterAccount(
isLighterChain && address ? address : undefined
)

useEffect(() => {
if (
isLighterChain &&
connectedLighterAccount?.index &&
// Only auto-set if no valid Lighter address is already set
(!customToAddress || !isLighterAddress(customToAddress))
) {
setCustomToAddress(connectedLighterAccount.index.toString())
}
}, [isLighterChain, connectedLighterAccount, customToAddress])

useEffect(() => {
if (tradeType === 'EXACT_INPUT') {
const amountOut = quote?.details?.currencyOut?.amount ?? ''
Expand Down
6 changes: 5 additions & 1 deletion packages/ui/src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import useHyperliquidBalance from './useHyperliquidBalance.js'
import useEOADetection from './useEOADetection.js'
import useTransactionCount from './useTransactionCount.js'
import useTronBalance from './useTronBalance.js'
import useLighterAccount from './useLighterAccount.js'
import useDisplayName from './useDisplayName.js'

export {
useMounted,
Expand Down Expand Up @@ -53,5 +55,7 @@ export {
useHyperliquidBalance,
useEOADetection,
useTransactionCount,
useTronBalance
useTronBalance,
useLighterAccount,
useDisplayName
}
Loading