Skip to content

Commit

Permalink
Merge pull request #363 from reservoirprotocol/feature/eclipse-connec…
Browse files Browse the repository at this point in the history
…tors

Custom eclipse connectors
  • Loading branch information
pedromcunha authored Nov 11, 2024
2 parents 8d1ef9b + 4fffded commit c27f710
Show file tree
Hide file tree
Showing 8 changed files with 117 additions and 42 deletions.
5 changes: 5 additions & 0 deletions .changeset/kind-windows-sparkle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@reservoir0x/relay-kit-ui': patch
---

Allow overriding chain to connector list
2 changes: 1 addition & 1 deletion demo/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import React, { ReactNode, FC, useState, useEffect } from 'react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { createConfig, http, WagmiProvider } from 'wagmi'
import { Chain, mainnet } from 'wagmi/chains'
import { RelayKitProvider } from '@reservoir0x/relay-kit-ui'
import { useRelayChains } from '@reservoir0x/relay-kit-hooks'
import {
LogLevel,
Expand All @@ -30,6 +29,7 @@ import { chainIdToAlchemyNetworkMap } from 'utils/chainIdToAlchemyNetworkMap'
import { useWalletFilter, WalletFilterProvider } from 'context/walletFilter'
import { pipe } from '@dynamic-labs/utils'
import { EclipseWalletConnectors } from '@dynamic-labs/eclipse'
import { RelayKitProvider } from '@reservoir0x/relay-kit-ui'

type AppWrapperProps = {
children: ReactNode
Expand Down
9 changes: 6 additions & 3 deletions packages/ui/src/components/common/CustomAddressModal.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type FC, useState, useEffect, useMemo } from 'react'
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'
Expand All @@ -17,7 +17,7 @@ import type { AdaptedWallet, RelayChain } from '@reservoir0x/relay-sdk'
import type { LinkedWallet } from '../../types/index.js'
import { truncateAddress } from '../../utils/truncate.js'
import { isValidAddress } from '../../utils/address.js'
import { eclipse, eclipseWallets } from '../../utils/solana.js'
import { ProviderOptionsContext } from '../../providers/RelayKitProvider.js'

type Props = {
open: boolean
Expand Down Expand Up @@ -50,6 +50,8 @@ export const CustomAddressModal: FC<Props> = ({
const connectedAddress = useWalletAddress(wallet, linkedWallets)
const [address, setAddress] = useState('')
const [input, setInput] = useState('')
const providerOptionsContext = useContext(ProviderOptionsContext)
const connectorKeyOverrides = providerOptionsContext.vmConnectorKeyOverrides

const availableWallets = useMemo(
() =>
Expand All @@ -58,7 +60,8 @@ export const CustomAddressModal: FC<Props> = ({
toChain?.vmType,
wallet.address,
toChain?.id,
wallet.connector
wallet.connector,
connectorKeyOverrides
)
),
[toChain, linkedWallets]
Expand Down
28 changes: 23 additions & 5 deletions packages/ui/src/components/common/MultiWalletDropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useMemo, useState, type FC } from 'react'
import { useContext, useMemo, useState, type FC } from 'react'
import { Dropdown, DropdownMenuItem } from '../primitives/Dropdown.js'
import { Box, Button, Flex, Text } from '../primitives/index.js'
import type { LinkedWallet } from '../../types/index.js'
Expand All @@ -10,6 +10,7 @@ import { eclipse, eclipseWallets, solana } from '../../utils/solana.js'
import { useENSResolver } from '../../hooks/index.js'
import { EventNames } from '../../constants/events.js'
import { isValidAddress } from '../../utils/address.js'
import { ProviderOptionsContext } from '../../providers/RelayKitProvider.js'

type MultiWalletDropdownProps = {
context: 'origin' | 'destination'
Expand All @@ -33,20 +34,30 @@ export const MultiWalletDropdown: FC<MultiWalletDropdownProps> = ({
setAddressModalOpen
}) => {
const [open, setOpen] = useState(false)
const providerOptionsContext = useContext(ProviderOptionsContext)
const connectorKeyOverrides = providerOptionsContext.vmConnectorKeyOverrides
const filteredWallets = useMemo(() => {
if (!chain) return wallets

let eclipseConnectorKeys: string[] | undefined = undefined
if (connectorKeyOverrides && connectorKeyOverrides[eclipse.id]) {
eclipseConnectorKeys = connectorKeyOverrides[eclipse.id]
} else if (chain.vmType === 'svm') {
eclipseConnectorKeys = eclipseWallets
}

return wallets.filter((wallet) => {
if (wallet.vmType !== chain.vmType) {
return false
}
if (
chain.id === eclipse.id &&
!eclipseWallets.includes(wallet.connector.toLowerCase())
!eclipseConnectorKeys!.includes(wallet.connector.toLowerCase())
) {
return false
} else if (
chain.id === solana.id &&
eclipseWallets.includes(wallet.connector.toLowerCase())
eclipseConnectorKeys!.includes(wallet.connector.toLowerCase())
) {
return false
}
Expand All @@ -65,9 +76,16 @@ export const MultiWalletDropdown: FC<MultiWalletDropdownProps> = ({
chain?.vmType,
selectedWalletAddress,
chain?.id,
selectedWallet?.connector
selectedWallet?.connector,
connectorKeyOverrides
),
[selectedWalletAddress, selectedWallet, chain?.vmType, chain?.id]
[
selectedWalletAddress,
selectedWallet,
chain?.vmType,
chain?.id,
connectorKeyOverrides
]
)

const showDropdown = context !== 'origin' || filteredWallets.length > 0
Expand Down
23 changes: 14 additions & 9 deletions packages/ui/src/components/widgets/SwapWidget/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Flex, Button, Text, Box } from '../../primitives/index.js'
import { useEffect, useState, type FC } from 'react'
import { useContext, useEffect, useState, type FC } from 'react'
import { useMounted, useRelayClient } from '../../../hooks/index.js'
import type { Address } from 'viem'
import { formatUnits, zeroAddress } from 'viem'
Expand Down Expand Up @@ -35,6 +35,7 @@ import {
ASSETS_RELAY_API
} from '@reservoir0x/relay-sdk'
import SwapRouteSelector from '../SwapRouteSelector.js'
import { ProviderOptionsContext } from '../../../providers/RelayKitProvider.js'

type BaseSwapWidgetProps = {
defaultFromToken?: Token
Expand Down Expand Up @@ -99,6 +100,8 @@ const SwapWidget: FC<SwapWidgetProps> = ({
onSwapError
}) => {
const relayClient = useRelayClient()
const providerOptionsContext = useContext(ProviderOptionsContext)
const connectorKeyOverrides = providerOptionsContext.vmConnectorKeyOverrides
const [transactionModalOpen, setTransactionModalOpen] = useState(false)
const [addressModalOpen, setAddressModalOpen] = useState(false)
const isMounted = useMounted()
Expand Down Expand Up @@ -223,7 +226,8 @@ const SwapWidget: FC<SwapWidgetProps> = ({
const supportedAddress = findSupportedWallet(
fromChain,
address,
linkedWallets
linkedWallets,
connectorKeyOverrides
)
if (supportedAddress) {
onSetPrimaryWallet?.(supportedAddress)
Expand All @@ -235,7 +239,8 @@ const SwapWidget: FC<SwapWidgetProps> = ({
address,
linkedWallets,
onSetPrimaryWallet,
isValidFromAddress
isValidFromAddress,
connectorKeyOverrides
])

const promptSwitchRoute =
Expand Down Expand Up @@ -412,9 +417,9 @@ const SwapWidget: FC<SwapWidgetProps> = ({
isSingleChainLocked
? [lockChainId]
: fromToken?.chainId !== undefined &&
fromToken?.chainId === lockChainId
? [fromToken?.chainId]
: undefined
fromToken?.chainId === lockChainId
? [fromToken?.chainId]
: undefined
}
restrictedTokensList={tokens?.filter(
(token) => token.chainId === fromToken?.chainId
Expand Down Expand Up @@ -760,9 +765,9 @@ const SwapWidget: FC<SwapWidgetProps> = ({
isSingleChainLocked
? [lockChainId]
: toToken?.chainId !== undefined &&
toToken?.chainId === lockChainId
? [toToken?.chainId]
: undefined
toToken?.chainId === lockChainId
? [toToken?.chainId]
: undefined
}
restrictedTokensList={tokens?.filter(
(token) => token.chainId === toToken?.chainId
Expand Down
13 changes: 9 additions & 4 deletions packages/ui/src/components/widgets/SwapWidgetRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ const SwapWidgetRenderer: FC<SwapWidgetRendererProps> = ({
onAnalyticEvent
}) => {
const providerOptionsContext = useContext(ProviderOptionsContext)
const connectorKeyOverrides = providerOptionsContext.vmConnectorKeyOverrides
const relayClient = useRelayClient()
const { connector } = useAccount()
const [customToAddress, setCustomToAddress] = useState<
Expand Down Expand Up @@ -207,7 +208,8 @@ const SwapWidgetRenderer: FC<SwapWidgetRendererProps> = ({
toChain?.id,
!customToAddress && _linkedWallet?.address === address
? _linkedWallet?.connector
: undefined
: undefined,
connectorKeyOverrides
)
if (
multiWalletSupportEnabled &&
Expand All @@ -218,7 +220,8 @@ const SwapWidgetRenderer: FC<SwapWidgetRendererProps> = ({
const supportedAddress = findSupportedWallet(
toChain,
customToAddress,
linkedWallets
linkedWallets,
connectorKeyOverrides
)

return supportedAddress
Expand Down Expand Up @@ -326,13 +329,15 @@ const SwapWidgetRenderer: FC<SwapWidgetRendererProps> = ({
fromChain?.vmType,
address ?? '',
fromChain?.id,
linkedWallet?.connector
linkedWallet?.connector,
connectorKeyOverrides
)
const fromAddressWithFallback = addressWithFallback(
fromChain?.vmType,
address,
fromChain?.id,
linkedWallet?.connector
linkedWallet?.connector,
connectorKeyOverrides
)

const isValidToAddress = isValidAddress(toChain?.vmType, recipient ?? '')
Expand Down
36 changes: 25 additions & 11 deletions packages/ui/src/providers/RelayKitProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,41 @@
import { createContext, useMemo } from 'react'
import type { FC, ReactNode } from 'react'
import { RelayClientProvider } from './RelayClientProvider.js'
import type { RelayClientOptions, paths } from '@reservoir0x/relay-sdk'
import type { ChainVM, RelayClientOptions, paths } from '@reservoir0x/relay-sdk'
import type { RelayKitTheme } from '../themes/index.js'
import { generateCssVars } from '../utils/theme.js'

export type CoinId = {
[key: string]: string
}
export type CoinGecko = {
proxy?: string
apiKey?: string
coinIds?: CoinId
}

export type AppFees =
paths['/quote']['post']['requestBody']['content']['application/json']['appFees']

type RelayKitProviderOptions = {
/**
* The name of the application
*/
appName?: string
/**
* An array of fee objects composing of a recipient address and the fee in BPS
*/
appFees?: AppFees
/**
* This key is used to fetch token balances, to improve the general UX and suggest relevant tokens
* Can be omitted and the ui will continue to function. Refer to the dune docs on how to get an api key
*/
duneApiKey?: string
/**
* Disable the powered by reservoir footer
*/
disablePoweredByReservoir?: boolean
/**
* An objecting mapping either a VM type (evm, svm, bvm) or a chain id to a connector key (metamask, backpacksol, etc).
* Connector keys are used for differentiating which wallet maps to which vm/chain.
* Only relevant for eclipse/solana at the moment.
*/
vmConnectorKeyOverrides?: {
[key in number | 'evm' | 'svm' | 'bvm']?: string[]
}
}

export interface RelayKitProviderProps {
children: ReactNode
options: RelayClientOptions & RelayKitProviderOptions
Expand Down Expand Up @@ -138,7 +151,8 @@ export const RelayKitProvider: FC<RelayKitProviderProps> = function ({
appName: options.appName,
appFees: options.appFees,
duneApiKey: options.duneApiKey,
disablePoweredByReservoir: options.disablePoweredByReservoir
disablePoweredByReservoir: options.disablePoweredByReservoir,
vmConnectorKeyOverrides: options.vmConnectorKeyOverrides
}),
[options]
)
Expand Down
43 changes: 34 additions & 9 deletions packages/ui/src/utils/address.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,32 +12,41 @@ import {
solana
} from '../utils/solana.js'
import type { LinkedWallet } from '../types/index.js'
import type { RelayKitProviderProps } from '../providers/RelayKitProvider.js'

export const isValidAddress = (
vmType?: ChainVM,
address?: string,
chainId?: number,
connector?: string
connector?: string,
connectorKeyOverrides?: RelayKitProviderProps['options']['vmConnectorKeyOverrides']
) => {
let eclipseConnectorKeys: string[] | undefined = undefined
if (connectorKeyOverrides && connectorKeyOverrides[eclipse.id]) {
eclipseConnectorKeys = connectorKeyOverrides[eclipse.id]
} else if (vmType === 'svm') {
eclipseConnectorKeys = eclipseWallets
}

if (address) {
if (vmType === 'evm' || !vmType) {
return isAddress(address)
} else if (vmType === 'svm') {
if (chainId && connector) {
if (
chainId === eclipse.id &&
!eclipseWallets.includes(connector.toLowerCase())
!eclipseConnectorKeys!.includes(connector.toLowerCase())
) {
return false
}
if (
chainId === solana.id &&
eclipseWallets.includes(connector.toLowerCase())
eclipseConnectorKeys!.includes(connector.toLowerCase())
) {
return false
}
}
//tood solana

return isSolanaAddress(address)
} else if (vmType === 'bvm') {
return isBitcoinAddress(address)
Expand All @@ -50,17 +59,26 @@ export const addressWithFallback = (
vmType?: ChainVM,
address?: string,
chainId?: number,
connector?: string
connector?: string,
connectorKeyOverrides?: Parameters<typeof isValidAddress>['4']
) => {
return address && isValidAddress(vmType ?? 'evm', address, chainId, connector)
return address &&
isValidAddress(
vmType ?? 'evm',
address,
chainId,
connector,
connectorKeyOverrides
)
? address
: getDeadAddress(vmType, chainId)
}

export function findSupportedWallet(
chain: RelayChain,
currentAddress: string | undefined,
linkedWallets: LinkedWallet[]
linkedWallets: LinkedWallet[],
connectorKeyOverrides?: Parameters<typeof isValidAddress>['4']
): string | undefined {
const currentWallet = linkedWallets.find(
(wallet) => wallet.address === currentAddress
Expand All @@ -72,11 +90,18 @@ export function findSupportedWallet(
chain.vmType,
currentWallet.address,
chain.id,
currentWallet.connector
currentWallet.connector,
connectorKeyOverrides
))
) {
const supportedWallet = linkedWallets.find((wallet) =>
isValidAddress(chain.vmType, wallet.address, chain.id, wallet.connector)
isValidAddress(
chain.vmType,
wallet.address,
chain.id,
wallet.connector,
connectorKeyOverrides
)
)
return supportedWallet?.address
}
Expand Down

0 comments on commit c27f710

Please sign in to comment.