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

Fix BAYC compatibility #170

Merged
merged 4 commits into from
Nov 23, 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
23 changes: 23 additions & 0 deletions jest-fetch-mock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// An incomplete fetch() mockup for jest
Object.defineProperty(window, "fetch", {
writable: true,
value: jest.fn().mockImplementation((input: RequestInfo) => {
if (typeof input !== "string" || !input.startsWith("data:text/json,")) {
throw new Error(
"Input type not implemented by the fetch() mock function, see jest-fetch-mock.ts"
)
}
const response = {
ok: true,
status: 200,
clone: () => response,
async json() {
return JSON.parse(await this.text())
},
async text() {
return decodeURIComponent(input.split(",")[1])
},
}
return response
}),
})
11 changes: 7 additions & 4 deletions src/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ type NftMetadataMixedInJsonSchema = {
}
}

const RARIBLE_MATCH_RE = /^https:\/\/rarible\.com\/token\/(0x[a-fA-F0-9]{40}):([0-9]+)/
const RARIBLE_MATCH_RE =
/^https:\/\/rarible\.com\/token\/(0x[a-fA-F0-9]{40}):([0-9]+)/

export function isAddress(value: string): value is Address {
return /^0x[a-fA-F0-9]{40}$/.test(value)
Expand Down Expand Up @@ -232,9 +233,11 @@ export function isNftMetadata(data: unknown): data is NftMetadata {
}
const _data = data as NftMetadata

// We don’t test for the exact type here, because some
// NFT minting services set some of the fields as null.
return "name" in _data && "image" in _data && "description" in _data
// We don’t test for the exact type here, because some NFT minting services
// set some of the fields as null.
// We also only test for the presence of either `name` or `image`, as some
// NFT formats don’t declare them all (e.g. BAYC only declares `image`).
return "name" in _data || "image" in _data
}

export function addressesEqual(addr1: Address, addr2: Address): boolean {
Expand Down
67 changes: 67 additions & 0 deletions test/fetchers/shared/fetch-metadata.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import "../../../jest-fetch-mock"
import { identity, ipfsUrlDefault, ipfsUrlFromString } from "../../../src/utils"
import { fetchMetadata } from "../../../src/fetchers/shared/fetch-metadata"

const FETCH_CONTEXT = {
imageProxy: identity,
ipfsUrl: ipfsUrlDefault,
jsonProxy: identity,
}

// https://ipfs.io/ipfs/QmTFMJ17s35Y2fHSomdTh29m8CdBPK8Cv8trXnAWTVJ1hc
const METADATA_COMPLETE = {
name: "The War on Crypto",
description: "A Series by Award Winning Artist Rebecca Hendin",
external_link: "http://pac.xyz/",
image: "ipfs://QmQ7UHJGMdUVgDESYTMaeDfDu1pPyoqyaLfrAqqbfRceLX",
attributes: [
{
trait_type: "Action NFT Type",
value: "Common",
},
],
}

// https://ipfs.io/ipfs/QmeSjSinHpPnmXmspMjwiXyN6zS4E9zccariGR3jxcaWtq/6262
const METADATA_INCOMPLETE = {
image: "ipfs://QmbnUuxBMEdpKsnfaR26JNxXSYWREfVvLhDUR2fJjGmpP4",
attributes: [
{ trait_type: "Eyes", value: "Zombie" },
{ trait_type: "Background", value: "Aquamarine" },
{ trait_type: "Fur", value: "Blue" },
{ trait_type: "Hat", value: "Sushi Chef Headband" },
{ trait_type: "Mouth", value: "Bored Cigarette" },
{ trait_type: "Clothes", value: "Caveman Pelt" },
],
}

function dataUrl(data: unknown) {
return "data:text/json," + encodeURIComponent(JSON.stringify(data))
}

// Below are two Boring Ape NFTs that error before expect() test. Note they
// both only have image and attributes keys in the metadata
describe("fetchMetadata()", () => {
it("works with complete metadata", async () => {
let data = await fetchMetadata(dataUrl(METADATA_COMPLETE), FETCH_CONTEXT)
expect(data).toStrictEqual({
name: METADATA_COMPLETE.name,
description: METADATA_COMPLETE.description,
image: ipfsUrlFromString(METADATA_COMPLETE.image, ipfsUrlDefault),
rawData: METADATA_COMPLETE,
})
})

it("works with incomplete metadata", async () => {
const data1 = await fetchMetadata(
dataUrl(METADATA_INCOMPLETE),
FETCH_CONTEXT
)
expect(data1).toStrictEqual({
name: "",
description: "",
image: ipfsUrlFromString(METADATA_INCOMPLETE.image, ipfsUrlDefault),
rawData: METADATA_INCOMPLETE,
})
})
})