Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove the supportsInterface() calls #90

Merged
merged 1 commit into from
May 25, 2021
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
54 changes: 7 additions & 47 deletions src/fetchers/ethereum/standard-nft.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,16 @@
import type { Address } from "../../types"
import type { EthereumFetcherConfig } from "./types"

import { addressesEqual, normalizeTokenUrl, promiseAny } from "../../utils"
import { CRYPTOVOXELS } from "../../known-contracts"
import { normalizeTokenUrl, promiseAny } from "../../utils"
import {
ERC1155_ID,
ERC721_ID,
decodeAddress,
decodeBoolean,
decodeString,
ethCall,
methodOwnerOfErc721,
methodUriErc1155,
methodUriErc721,
supportsInterfaceMethodErc165,
} from "./utils"

// Contracts implementing ERC721’s tokenURI()
// but are missing ERC165’s supportsInterface()
const KNOWN_ERC721_LIKE = [CRYPTOVOXELS]

export async function fetchStandardNftContractData(
contractAddress: Address,
tokenId: string,
Expand All @@ -29,11 +20,6 @@ export async function fetchStandardNftContractData(
return ["", ""]
}

const urlCall = async (method: string): Promise<string> => {
const result = await ethCall(ethereum, contractAddress, method)
return normalizeTokenUrl(decodeString(result), tokenId)
}

// call ownerOf() even on 1155 contracts, just in case it exists
const ownerPromise = ethCall(
ethereum,
Expand All @@ -43,43 +29,17 @@ export async function fetchStandardNftContractData(
.then(decodeAddress)
.catch(() => "")

const isKnown721Like = KNOWN_ERC721_LIKE.some((address) =>
addressesEqual(address, contractAddress)
)
if (isKnown721Like) {
return Promise.all([
urlCall(methodUriErc721(BigInt(tokenId))),
ownerPromise,
])
}

const calls = [
[
supportsInterfaceMethodErc165(ERC721_ID),
methodUriErc721(BigInt(tokenId)),
],
[
supportsInterfaceMethodErc165(ERC1155_ID),
methodUriErc1155(BigInt(tokenId)),
],
methodUriErc721(BigInt(tokenId)),
methodUriErc1155(BigInt(tokenId)),
]

try {
return promiseAny(
calls.map(async ([supportsMethod, uriMethod]) => {
// Check if the interface is supported first
const supported = await ethCall(
ethereum,
contractAddress,
supportsMethod
).then(decodeBoolean)

// throw for the Promise.any() to skip this branch
if (!supported) {
throw new Error("Unsupported method")
}

return Promise.all([urlCall(uriMethod), ownerPromise])
calls.map(async (callMethod) => {
const urlString = await ethCall(ethereum, contractAddress, callMethod)
const url = normalizeTokenUrl(decodeString(urlString), tokenId)
return Promise.all([url, ownerPromise])
})
)
} catch (err) {
Expand Down
16 changes: 0 additions & 16 deletions src/fetchers/ethereum/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,6 @@ import type { EthereumProviderEip1193 } from "./types"
const URI_METHOD_ERC721 = "0xc87b56dd" // tokenURI(uint256)
const URI_METHOD_ERC1155 = "0x0e89341c" // uri(uint256)
const OWNER_OF_METHOD_ERC721 = "0x6352211e" // ownerOf(uint256)
const SUPPORTS_INTERFACE_METHOD_ERC165 = "0x01ffc9a7" // supportsInterface(bytes4)

// ERC165 identifiers
export const ERC721_ID = "0x80ac58cd"
export const ERC1155_ID = "0xd9b67a26"

// Utilities adapted from https://github.com/Zoltu/ethereum-abi-encoder/
export function uint256Hex(value: bigint): string {
Expand Down Expand Up @@ -38,10 +33,6 @@ export function hexToUint8Array(hex: string): Uint8Array {
)
}

export function decodeBoolean(hex: string): boolean {
return bytesToBigInt(hexToUint8Array(hex)) !== BigInt(0)
}

export function decodeString(hex: string): string {
const data = hexToUint8Array(hex)
const pointer = Number(bytesToBigInt(data.subarray(0, 32)))
Expand Down Expand Up @@ -75,13 +66,6 @@ export function methodOwnerOfErc721(tokenId: bigint): string {
return OWNER_OF_METHOD_ERC721 + uint256Hex(tokenId)
}

export function supportsInterfaceMethodErc165(interfaceId: string): string {
return (
SUPPORTS_INTERFACE_METHOD_ERC165 +
interfaceId.replace(/^0x/, "").padEnd(64, "0")
)
}

export function ethCall(
ethereum: EthereumProviderEip1193,
to: Address,
Expand Down
5 changes: 1 addition & 4 deletions src/fetchers/ethers/standard-nft.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,18 @@ const ABI = [
"function ownerOf(uint256 _tokenId) external view returns (address)",
// ERC-1155
"function uri(uint256 _id) external view returns (string)",
// ERC-165
"function supportsInterface(bytes4 interfaceID) external view returns (bool)",
]

type NftContract = InstanceType<typeof Contract> & {
ownerOf: ContractFunction<string>
supportsInterface: ContractFunction<boolean>
tokenURI: ContractFunction<string>
uri: ContractFunction<string>
}

async function url(contract: NftContract, tokenId: string): Promise<string> {
const uri = await promiseAny([
contract.uri(tokenId),
contract.tokenURI(tokenId),
contract.uri(tokenId),
])
return normalizeTokenUrl(uri, tokenId)
}
Expand Down