2022-01-12 21:06:22 +03:00
|
|
|
import { BigNumber } from '@ethersproject/bignumber'
|
|
|
|
import { hexZeroPad } from '@ethersproject/bytes'
|
|
|
|
import { namehash } from '@ethersproject/hash'
|
2022-07-01 02:38:02 +03:00
|
|
|
import { useWeb3React } from '@web3-react/core'
|
2022-01-13 19:54:08 +03:00
|
|
|
import { useSingleCallResult } from 'lib/hooks/multicall'
|
2022-01-11 20:28:02 +03:00
|
|
|
import uriToHttp from 'lib/utils/uriToHttp'
|
2021-12-02 21:35:39 +03:00
|
|
|
import { useEffect, useMemo, useState } from 'react'
|
2021-12-04 00:45:33 +03:00
|
|
|
import { safeNamehash } from 'utils/safeNamehash'
|
2021-12-02 21:35:39 +03:00
|
|
|
|
|
|
|
import { isAddress } from '../utils'
|
|
|
|
import isZero from '../utils/isZero'
|
|
|
|
import { useENSRegistrarContract, useENSResolverContract, useERC721Contract, useERC1155Contract } from './useContract'
|
|
|
|
import useDebounce from './useDebounce'
|
|
|
|
import useENSName from './useENSName'
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the ENS avatar URI, if available.
|
|
|
|
* Spec: https://gist.github.com/Arachnid/9db60bd75277969ee1689c8742b75182.
|
|
|
|
*/
|
|
|
|
export default function useENSAvatar(
|
|
|
|
address?: string,
|
|
|
|
enforceOwnership = true
|
|
|
|
): { avatar: string | null; loading: boolean } {
|
|
|
|
const debouncedAddress = useDebounce(address, 200)
|
|
|
|
const node = useMemo(() => {
|
|
|
|
if (!debouncedAddress || !isAddress(debouncedAddress)) return undefined
|
2021-12-04 00:45:33 +03:00
|
|
|
return namehash(`${debouncedAddress.toLowerCase().substr(2)}.addr.reverse`)
|
2021-12-02 21:35:39 +03:00
|
|
|
}, [debouncedAddress])
|
|
|
|
|
|
|
|
const addressAvatar = useAvatarFromNode(node)
|
2021-12-04 00:45:33 +03:00
|
|
|
const ENSName = useENSName(address).ENSName
|
|
|
|
const nameAvatar = useAvatarFromNode(ENSName === null ? undefined : safeNamehash(ENSName))
|
2021-12-02 21:35:39 +03:00
|
|
|
let avatar = addressAvatar.avatar || nameAvatar.avatar
|
|
|
|
|
|
|
|
const nftAvatar = useAvatarFromNFT(avatar, enforceOwnership)
|
|
|
|
avatar = nftAvatar.avatar || avatar
|
|
|
|
|
|
|
|
const http = avatar && uriToHttp(avatar)[0]
|
|
|
|
|
|
|
|
const changed = debouncedAddress !== address
|
2021-12-10 00:06:55 +03:00
|
|
|
return useMemo(
|
|
|
|
() => ({
|
|
|
|
avatar: changed ? null : http ?? null,
|
|
|
|
loading: changed || addressAvatar.loading || nameAvatar.loading || nftAvatar.loading,
|
|
|
|
}),
|
|
|
|
[addressAvatar.loading, changed, http, nameAvatar.loading, nftAvatar.loading]
|
|
|
|
)
|
2021-12-02 21:35:39 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
function useAvatarFromNode(node?: string): { avatar?: string; loading: boolean } {
|
|
|
|
const nodeArgument = useMemo(() => [node], [node])
|
|
|
|
const textArgument = useMemo(() => [node, 'avatar'], [node])
|
|
|
|
const registrarContract = useENSRegistrarContract(false)
|
|
|
|
const resolverAddress = useSingleCallResult(registrarContract, 'resolver', nodeArgument)
|
|
|
|
const resolverAddressResult = resolverAddress.result?.[0]
|
|
|
|
const resolverContract = useENSResolverContract(
|
|
|
|
resolverAddressResult && !isZero(resolverAddressResult) ? resolverAddressResult : undefined,
|
|
|
|
false
|
|
|
|
)
|
|
|
|
const avatar = useSingleCallResult(resolverContract, 'text', textArgument)
|
|
|
|
|
2021-12-10 00:06:55 +03:00
|
|
|
return useMemo(
|
|
|
|
() => ({
|
|
|
|
avatar: avatar.result?.[0],
|
|
|
|
loading: resolverAddress.loading || avatar.loading,
|
|
|
|
}),
|
|
|
|
[avatar.loading, avatar.result, resolverAddress.loading]
|
|
|
|
)
|
2021-12-02 21:35:39 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
function useAvatarFromNFT(nftUri = '', enforceOwnership: boolean): { avatar?: string; loading: boolean } {
|
|
|
|
const parts = nftUri.toLowerCase().split(':')
|
|
|
|
const protocol = parts[0]
|
|
|
|
// ignore the chain from eip155
|
|
|
|
// TODO: when we are able, pull only from the specified chain
|
|
|
|
const [, erc] = parts[1]?.split('/') ?? []
|
|
|
|
const [contractAddress, id] = parts[2]?.split('/') ?? []
|
|
|
|
const isERC721 = protocol === 'eip155' && erc === 'erc721'
|
|
|
|
const isERC1155 = protocol === 'eip155' && erc === 'erc1155'
|
|
|
|
const erc721 = useERC721Uri(isERC721 ? contractAddress : undefined, id, enforceOwnership)
|
|
|
|
const erc1155 = useERC1155Uri(isERC1155 ? contractAddress : undefined, id, enforceOwnership)
|
|
|
|
const uri = erc721.uri || erc1155.uri
|
|
|
|
const http = uri && uriToHttp(uri)[0]
|
|
|
|
|
|
|
|
const [loading, setLoading] = useState(false)
|
|
|
|
const [avatar, setAvatar] = useState(undefined)
|
|
|
|
useEffect(() => {
|
|
|
|
setAvatar(undefined)
|
|
|
|
if (http) {
|
|
|
|
setLoading(true)
|
|
|
|
fetch(http)
|
|
|
|
.then((res) => res.json())
|
|
|
|
.then(({ image }) => {
|
|
|
|
setAvatar(image)
|
|
|
|
})
|
|
|
|
.catch((e) => console.warn(e))
|
|
|
|
.finally(() => {
|
|
|
|
setLoading(false)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}, [http])
|
|
|
|
|
2021-12-10 00:06:55 +03:00
|
|
|
return useMemo(
|
|
|
|
() => ({ avatar, loading: erc721.loading || erc1155.loading || loading }),
|
|
|
|
[avatar, erc1155.loading, erc721.loading, loading]
|
|
|
|
)
|
2021-12-02 21:35:39 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
function useERC721Uri(
|
|
|
|
contractAddress: string | undefined,
|
|
|
|
id: string | undefined,
|
|
|
|
enforceOwnership: boolean
|
|
|
|
): { uri?: string; loading: boolean } {
|
|
|
|
const idArgument = useMemo(() => [id], [id])
|
2022-07-01 02:38:02 +03:00
|
|
|
const { account } = useWeb3React()
|
2021-12-02 21:35:39 +03:00
|
|
|
const contract = useERC721Contract(contractAddress)
|
|
|
|
const owner = useSingleCallResult(contract, 'ownerOf', idArgument)
|
|
|
|
const uri = useSingleCallResult(contract, 'tokenURI', idArgument)
|
2021-12-10 00:06:55 +03:00
|
|
|
return useMemo(
|
|
|
|
() => ({
|
|
|
|
uri: !enforceOwnership || account === owner.result?.[0] ? uri.result?.[0] : undefined,
|
|
|
|
loading: owner.loading || uri.loading,
|
|
|
|
}),
|
|
|
|
[account, enforceOwnership, owner.loading, owner.result, uri.loading, uri.result]
|
|
|
|
)
|
2021-12-02 21:35:39 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
function useERC1155Uri(
|
|
|
|
contractAddress: string | undefined,
|
|
|
|
id: string | undefined,
|
|
|
|
enforceOwnership: boolean
|
|
|
|
): { uri?: string; loading: boolean } {
|
2022-07-01 02:38:02 +03:00
|
|
|
const { account } = useWeb3React()
|
2021-12-02 21:35:39 +03:00
|
|
|
const idArgument = useMemo(() => [id], [id])
|
|
|
|
const accountArgument = useMemo(() => [account || '', id], [account, id])
|
|
|
|
const contract = useERC1155Contract(contractAddress)
|
|
|
|
const balance = useSingleCallResult(contract, 'balanceOf', accountArgument)
|
|
|
|
const uri = useSingleCallResult(contract, 'uri', idArgument)
|
2022-01-11 07:20:04 +03:00
|
|
|
// ERC-1155 allows a generic {id} in the URL, so prepare to replace if relevant,
|
|
|
|
// in lowercase hexadecimal (with no 0x prefix) and leading zero padded to 64 hex characters.
|
|
|
|
const idHex = id ? hexZeroPad(BigNumber.from(id).toHexString(), 32).substring(2) : id
|
2021-12-10 00:06:55 +03:00
|
|
|
return useMemo(
|
|
|
|
() => ({
|
2022-01-11 07:20:04 +03:00
|
|
|
uri: !enforceOwnership || balance.result?.[0] > 0 ? uri.result?.[0]?.replaceAll('{id}', idHex) : undefined,
|
2021-12-10 00:06:55 +03:00
|
|
|
loading: balance.loading || uri.loading,
|
|
|
|
}),
|
2022-01-11 07:20:04 +03:00
|
|
|
[balance.loading, balance.result, enforceOwnership, uri.loading, uri.result, idHex]
|
2021-12-10 00:06:55 +03:00
|
|
|
)
|
2021-12-02 21:35:39 +03:00
|
|
|
}
|