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
4 changes: 2 additions & 2 deletions src/core/EVM/EVM.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { ChainType } from '@lifi/types'
import { isAddress } from 'viem'
import type { StepExecutorOptions } from '../types.js'
import { EVMStepExecutor } from './EVMStepExecutor.js'
import { getENSAddress } from './getENSAddress.js'
import { getEVMBalance } from './getEVMBalance.js'
import { resolveEVMAddress } from './resolveEVMAddress.js'
import type { EVMProvider, EVMProviderOptions } from './types.js'

export function EVM(options?: EVMProviderOptions): EVMProvider {
Expand All @@ -16,7 +16,7 @@ export function EVM(options?: EVMProviderOptions): EVMProvider {
return _options
},
isAddress,
resolveAddress: getENSAddress,
resolveAddress: resolveEVMAddress,
getBalance: getEVMBalance,
getWalletClient: _options.getWalletClient,
async getStepExecutor(
Expand Down
14 changes: 13 additions & 1 deletion src/core/EVM/publicClient.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { ChainId, ChainType } from '@lifi/types'
import type { Client } from 'viem'
import { createClient, fallback, http, webSocket } from 'viem'
import { type Address, createClient, fallback, http, webSocket } from 'viem'
import { type Chain, mainnet } from 'viem/chains'
import { config } from '../../config.js'
import { getRpcUrls } from '../rpc.js'
import type { EVMProvider } from './types.js'
import { UNS_PROXY_READER_ADDRESSES } from './uns/constants.js'

// cached providers
const publicClients: Record<number, Client> = {}
Expand Down Expand Up @@ -46,6 +47,17 @@ export const getPublicClient = async (chainId: number): Promise<Client> => {
...chain.contracts,
}
}

// Add UNS contracts for supported chains
if (chain.id === ChainId.ETH || chain.id === ChainId.POL) {
const unsProxyAddress = UNS_PROXY_READER_ADDRESSES[chain.id]

chain.contracts = {
...chain.contracts,
unsProxyReader: { address: unsProxyAddress as Address },
}
}

const provider = config.getProvider(ChainType.EVM) as EVMProvider | undefined
publicClients[chainId] = createClient({
chain: chain,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { ChainId } from '@lifi/types'
import { getEnsAddress, normalize } from 'viem/ens'
import { getPublicClient } from './publicClient.js'

export const getENSAddress = async (
export const resolveENSAddress = async (
name: string
): Promise<string | undefined> => {
try {
Expand Down
15 changes: 15 additions & 0 deletions src/core/EVM/resolveEVMAddress.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { ChainId, CoinKey } from '@lifi/types'
import { ChainType } from '@lifi/types'
import { resolveENSAddress } from './resolveENSAddress.js'
import { resolveUNSAddress } from './uns/resolveUNSAddress.js'

export async function resolveEVMAddress(
name: string,
chainId?: ChainId,
token?: CoinKey
): Promise<string | undefined> {
return (
(await resolveENSAddress(name)) ||
(await resolveUNSAddress(name, ChainType.EVM, chainId, token))
)
}
81 changes: 81 additions & 0 deletions src/core/EVM/uns/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { ChainId, ChainType } from '@lifi/types'
import type { Address } from 'viem'

export const UNS_PROXY_READER_ADDRESSES: Record<number, Address> = {
[ChainId.ETH]: '0x578853aa776Eef10CeE6c4dd2B5862bdcE767A8B',
[ChainId.POL]: '0x91EDd8708062bd4233f4Dd0FCE15A7cb4d500091',
} as const

export const getUNSProxyAddress = (chainId: number): Address | undefined =>
UNS_PROXY_READER_ADDRESSES[chainId]

export const UNSProxyReaderABI = [
{
constant: true,
inputs: [
{
internalType: 'string[]',
name: 'keys',
type: 'string[]',
},
{
internalType: 'uint256',
name: 'tokenId',
type: 'uint256',
},
],
name: 'getData',
outputs: [
{
internalType: 'address',
name: 'resolver',
type: 'address',
},
{
internalType: 'address',
name: 'owner',
type: 'address',
},
{
internalType: 'string[]',
name: 'values',
type: 'string[]',
},
],
payable: false,
stateMutability: 'view',
type: 'function',
},
{
inputs: [{ internalType: 'uint256', name: 'tokenId', type: 'uint256' }],
name: 'exists',
outputs: [{ internalType: 'bool', name: '', type: 'bool' }],
stateMutability: 'view',
type: 'function',
},
] as const

export const CHAIN_TYPE_UNS_CHAIN_MAP: Record<ChainType, string> = {
[ChainType.EVM]: 'ETH',
[ChainType.MVM]: 'SUI',
[ChainType.SVM]: 'SOL',
[ChainType.UTXO]: 'BTC',
}

export const CHAIN_ID_UNS_CHAIN_MAP: Partial<Record<ChainId, string>> = {
[ChainId.ETH]: 'ETH',
[ChainId.BTC]: 'BTC',
[ChainId.SUI]: 'SUI',
[ChainId.SOL]: 'SOL',
[ChainId.BAS]: 'BASE',
[ChainId.POL]: 'MATIC',
[ChainId.ARB]: 'ARB1',
[ChainId.AVA]: 'AVAX',
}

export const CHAIN_TYPE_FAMILY_MAP: Record<ChainType, string> = {
[ChainType.EVM]: 'EVM',
[ChainType.UTXO]: 'BTC',
[ChainType.SVM]: 'SOL',
[ChainType.MVM]: 'SUI',
}
123 changes: 123 additions & 0 deletions src/core/EVM/uns/resolveUNSAddress.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { ChainId, type ChainType, type CoinKey } from '@lifi/types'
import type { Address, Client } from 'viem'
import { readContract } from 'viem/actions'
import { namehash } from 'viem/ens'
import { getAction, trim } from 'viem/utils'
import { getPublicClient } from '../publicClient.js'

import {
CHAIN_ID_UNS_CHAIN_MAP,
CHAIN_TYPE_FAMILY_MAP,
CHAIN_TYPE_UNS_CHAIN_MAP,
getUNSProxyAddress,
UNSProxyReaderABI,
} from './constants.js'

export const resolveUNSAddress = async (
name: string,
chainType: ChainType,
chain?: ChainId,
token?: CoinKey
): Promise<string | undefined> => {
try {
const L1Client = await getPublicClient(ChainId.ETH)
const L2Client = await getPublicClient(ChainId.POL)

const nameHash = namehash(name)
const keys: string[] = []

// handle token based resolution
if (chain) {
const family = CHAIN_TYPE_FAMILY_MAP[chainType]
const unschain = CHAIN_ID_UNS_CHAIN_MAP[chain]

if (family) {
if (token) {
keys.push(`token.${family}.${unschain}.${token}.address`)
}
if (unschain) {
keys.push(`token.${family}.${unschain}.address`)
}

keys.push(`token.${family}.address`)
}
}

// fallback to chain based resolution
const unschainType = CHAIN_TYPE_UNS_CHAIN_MAP[chainType]
keys.push(`crypto.${unschainType}.address`)

for (const key of keys) {
const address =
(await getUnsAddress(L2Client, { name: nameHash, key })) ||
(await getUnsAddress(L1Client, { name: nameHash, key }))
if (address) {
return address
}
}
return undefined
} catch {
return undefined
}
}

type GetUnsAddressParameters = {
key: string
name: string
}

type GetUnsAddressReturnType = Address | undefined

async function getUnsAddress(
client: Client,
params: GetUnsAddressParameters
): Promise<GetUnsAddressReturnType> {
const { name, key } = params

const chainId = client.chain?.id
if (!chainId) {
throw new Error('Chain ID not available')
}

const proxyAddress = getUNSProxyAddress(chainId)
if (!proxyAddress) {
throw new Error(`UNS contracts are not deployed on chain ${chainId}`)
}

const readContractAction = getAction(client, readContract, 'readContract')

const existsReadContractParameters = {
abi: UNSProxyReaderABI,
address: proxyAddress,
functionName: 'exists',
args: [BigInt(name)],
} as const

const exists = await readContractAction(existsReadContractParameters)

if (!exists) {
return undefined
}

const readContractParameters = {
abi: UNSProxyReaderABI,
address: proxyAddress,
functionName: 'getData',
args: [[key], BigInt(name)],
} as const

const res = await readContractAction(readContractParameters)
const [, , addresses] = res

const address = addresses[0]

if (
address === '0x' ||
address === '' ||
trim(address as Address) === '0x00'
) {
return undefined
}

return address as Address
}
4 changes: 2 additions & 2 deletions src/core/Solana/Solana.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { ChainType } from '@lifi/types'
import type { StepExecutorOptions } from '../types.js'
import { getSNSAddress } from './getSNSAddress.js'
import { getSolanaBalance } from './getSolanaBalance.js'
import { isSVMAddress } from './isSVMAddress.js'
import { resolveSolanaAddress } from './resolveSolanaAddress.js'
import { SolanaStepExecutor } from './SolanaStepExecutor.js'
import type { SolanaProvider, SolanaProviderOptions } from './types.js'

Expand All @@ -13,7 +13,7 @@ export function Solana(options?: SolanaProviderOptions): SolanaProvider {
return ChainType.SVM
},
isAddress: isSVMAddress,
resolveAddress: getSNSAddress,
resolveAddress: resolveSolanaAddress,
getBalance: getSolanaBalance,
async getStepExecutor(
options: StepExecutorOptions
Expand Down
7 changes: 7 additions & 0 deletions src/core/Solana/resolveSolanaAddress.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { getSNSAddress } from './getSNSAddress.js'

export async function resolveSolanaAddress(
name: string
): Promise<string | undefined> {
return await getSNSAddress(name)
}
4 changes: 2 additions & 2 deletions src/core/Sui/Sui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { ChainType } from '@lifi/types'
import { isValidSuiAddress } from '@mysten/sui/utils'
import type { StepExecutorOptions } from '../types.js'
import { getSuiBalance } from './getSuiBalance.js'
import { getSuiNSAddress } from './getSuiNSAddress.js'
import { resolveSuiAddress } from './resolveSuiAddress.js'
import { SuiStepExecutor } from './SuiStepExecutor.js'
import type { SuiProvider, SuiProviderOptions } from './types.js'

Expand All @@ -13,7 +13,7 @@ export function Sui(options?: SuiProviderOptions): SuiProvider {
return ChainType.MVM
},
isAddress: isValidSuiAddress,
resolveAddress: getSuiNSAddress,
resolveAddress: resolveSuiAddress,
getBalance: getSuiBalance,
async getStepExecutor(
options: StepExecutorOptions
Expand Down
7 changes: 7 additions & 0 deletions src/core/Sui/resolveSuiAddress.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { getSuiNSAddress } from './getSuiNSAddress.js'

export async function resolveSuiAddress(
name: string
): Promise<string | undefined> {
return await getSuiNSAddress(name)
}
6 changes: 2 additions & 4 deletions src/core/UTXO/UTXO.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { isUTXOAddress } from '@bigmi/core'
import { ChainType } from '@lifi/types'
import type { StepExecutorOptions } from '../types.js'
import { getUTXOBalance } from './getUTXOBalance.js'
import { resolveUTXOAddress } from './resolveUTXOAddress.js'
import type { UTXOProvider, UTXOProviderOptions } from './types.js'
import { UTXOStepExecutor } from './UTXOStepExecutor.js'

Expand All @@ -12,10 +13,7 @@ export function UTXO(options?: UTXOProviderOptions): UTXOProvider {
return ChainType.UTXO
},
isAddress: isUTXOAddress,
async resolveAddress(name) {
// Not supported on UTXO yet
return name
},
resolveAddress: resolveUTXOAddress,
getBalance: getUTXOBalance,
async getStepExecutor(
options: StepExecutorOptions
Expand Down
6 changes: 6 additions & 0 deletions src/core/UTXO/resolveUTXOAddress.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export async function resolveUTXOAddress(
name: string
): Promise<string | undefined> {
// Not supported on UTXO yet
return name
}
8 changes: 7 additions & 1 deletion src/core/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import type {
ChainId,
ChainType,
CoinKey,
FeeCost,
GasCost,
LiFiStep,
Expand All @@ -14,7 +16,11 @@ import type { Client } from 'viem'
export interface SDKProvider {
readonly type: ChainType
isAddress(address: string): boolean
resolveAddress(name: string): Promise<string | undefined>
resolveAddress(
name: string,
chainId?: ChainId,
token?: CoinKey
): Promise<string | undefined>
getStepExecutor(options: StepExecutorOptions): Promise<StepExecutor>
getBalance(walletAddress: string, tokens: Token[]): Promise<TokenAmount[]>
}
Expand Down
Loading