From 7f36853ee13dbb7bb6753fd313f132e39155ac7a Mon Sep 17 00:00:00 2001 From: Joaquim Verges Date: Wed, 10 Aug 2022 15:43:34 -0700 Subject: [PATCH 1/5] Resolve contract names when available + more explicit invalid marketplace listings errors --- etc/sdk.api.md | 5 +++- src/contracts/marketplace.ts | 18 ++++++----- src/core/classes/contract-metadata.ts | 15 ++++++++-- src/core/classes/marketplace-direct.ts | 41 ++++++++++++++++++-------- 4 files changed, 55 insertions(+), 24 deletions(-) diff --git a/etc/sdk.api.md b/etc/sdk.api.md index 19df11e73..c3ff10d16 100644 --- a/etc/sdk.api.md +++ b/etc/sdk.api.md @@ -3338,7 +3338,10 @@ export class MarketplaceDirect { getAddress(): string; getListing(listingId: BigNumberish): Promise; // @internal - isStillValidListing(listing: DirectListing, quantity?: BigNumberish): Promise; + isStillValidListing(listing: DirectListing, quantity?: BigNumberish): Promise<{ + valid: boolean; + error?: string; + }>; makeOffer(listingId: BigNumberish, quantityDesired: BigNumberish, currencyContractAddress: string, pricePerToken: Price, expirationDate?: Date): Promise; // @internal mapListing(listing: IMarketplace_2.ListingStruct): Promise; diff --git a/src/contracts/marketplace.ts b/src/contracts/marketplace.ts index c8925c5d4..0dc07be9b 100644 --- a/src/contracts/marketplace.ts +++ b/src/contracts/marketplace.ts @@ -227,7 +227,7 @@ export class Marketplace implements UpdateableNetwork { public async getActiveListings( filter?: MarketplaceFilter, ): Promise<(AuctionListing | DirectListing)[]> { - const rawListings = await this.getAllListingsNoFilter(); + const rawListings = await this.getAllListingsNoFilter(true); const filtered = this.applyFilter(rawListings, filter); const now = BigNumber.from(Math.floor(Date.now() / 1000)); return filtered.filter((l) => { @@ -255,7 +255,7 @@ export class Marketplace implements UpdateableNetwork { public async getAllListings( filter?: MarketplaceFilter, ): Promise<(AuctionListing | DirectListing)[]> { - const rawListings = await this.getAllListingsNoFilter(); + const rawListings = await this.getAllListingsNoFilter(false); return this.applyFilter(rawListings, filter); } @@ -444,9 +444,9 @@ export class Marketplace implements UpdateableNetwork { * PRIVATE FUNCTIONS *******************************/ - private async getAllListingsNoFilter(): Promise< - (AuctionListing | DirectListing)[] - > { + private async getAllListingsNoFilter( + filterInvalidListings: boolean, + ): Promise<(AuctionListing | DirectListing)[]> { const listings = await Promise.all( Array.from( Array( @@ -465,9 +465,11 @@ export class Marketplace implements UpdateableNetwork { return listing; } - const valid = await this.direct.isStillValidListing(listing); - if (!valid) { - return undefined; + if (filterInvalidListings) { + const { valid } = await this.direct.isStillValidListing(listing); + if (!valid) { + return undefined; + } } return listing; diff --git a/src/core/classes/contract-metadata.ts b/src/core/classes/contract-metadata.ts index aa2da3181..95dedd057 100644 --- a/src/core/classes/contract-metadata.ts +++ b/src/core/classes/contract-metadata.ts @@ -1,4 +1,4 @@ -import { IContractMetadata } from "contracts"; +import { IContractMetadata, IERC20Metadata } from "contracts"; import { z } from "zod"; import { IStorage } from "../interfaces/IStorage"; import { TransactionResult } from "../types"; @@ -6,6 +6,7 @@ import { ContractWrapper } from "./contract-wrapper"; import { detectContractFeature, fetchContractMetadataFromAddress, + hasFunction, } from "../../common"; import { DetectableFeature } from "../interfaces/DetectableFeature"; import { FEATURE_METADATA } from "../../constants/thirdweb-features"; @@ -87,14 +88,22 @@ export class ContractMetadata< if (!data) { try { - // try fetching metadata from bytecode + // try fetching metadata from bytecode and / or contract itself + let contractName: string | undefined; + try { + if (hasFunction("name", this.contractWrapper)) { + contractName = await this.contractWrapper.readContract.name(); + } + } catch (err) { + // no-op + } const publishedMetadata = await fetchContractMetadataFromAddress( this.contractWrapper.readContract.address, this.contractWrapper.getProvider(), this.storage, ); data = { - name: publishedMetadata.name, + name: contractName || publishedMetadata.name, description: publishedMetadata.info.title, }; } catch (e) { diff --git a/src/core/classes/marketplace-direct.ts b/src/core/classes/marketplace-direct.ts index ef0f3f499..d7a5671f3 100644 --- a/src/core/classes/marketplace-direct.ts +++ b/src/core/classes/marketplace-direct.ts @@ -359,11 +359,12 @@ export class MarketplaceDirect { receiver?: string, ): Promise { const listing = await this.validateListing(BigNumber.from(listingId)); - const valid = await this.isStillValidListing(listing, quantityDesired); + const { valid, error } = await this.isStillValidListing( + listing, + quantityDesired, + ); if (!valid) { - throw new Error( - "The asset on this listing has been moved from the lister's wallet, this listing is now invalid", - ); + throw new Error(`Listing ${listingId} is no longer valid. ${error}`); } const buyFor = receiver ? receiver @@ -501,7 +502,7 @@ export class MarketplaceDirect { public async isStillValidListing( listing: DirectListing, quantity?: BigNumberish, - ): Promise { + ): Promise<{ valid: boolean; error?: string }> { const approved = await isTokenApprovedForTransfer( this.contractWrapper.getProvider(), this.getAddress(), @@ -511,7 +512,10 @@ export class MarketplaceDirect { ); if (!approved) { - return false; + return { + valid: false, + error: `Token '${listing.tokenId}' from contract '${listing.assetContractAddress}' is not approved for transfer`, + }; } const provider = this.contractWrapper.getProvider(); @@ -528,10 +532,15 @@ export class MarketplaceDirect { ERC721Abi, provider, ) as IERC721; - return ( + const valid = (await asset.ownerOf(listing.tokenId)).toLowerCase() === - listing.sellerAddress.toLowerCase() - ); + listing.sellerAddress.toLowerCase(); + return { + valid, + error: valid + ? undefined + : `Seller is not the owner of Token '${listing.tokenId}' from contract '${listing.assetContractAddress} anymore'`, + }; } else if (isERC1155) { const asset = new Contract( listing.assetContractAddress, @@ -542,10 +551,18 @@ export class MarketplaceDirect { listing.sellerAddress, listing.tokenId, ); - return balance.gte(quantity || listing.quantity); + const valid = balance.gte(quantity || listing.quantity); + return { + valid, + error: valid + ? undefined + : `Seller does not have enough balance of Token '${listing.tokenId}' from contract '${listing.assetContractAddress} to fulfill the listing`, + }; } else { - console.error("Contract does not implement ERC 1155 or ERC 721."); - return false; + return { + valid: false, + error: "Contract does not implement ERC 1155 or ERC 721.", + }; } } } From cbcf29bcbc64846fdec7e08b32bd0d70afe23d7a Mon Sep 17 00:00:00 2001 From: Joaquim Verges Date: Wed, 10 Aug 2022 15:51:33 -0700 Subject: [PATCH 2/5] v2.3.35-2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a59e8f921..c7a09c5b3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@thirdweb-dev/sdk", - "version": "2.3.35-1", + "version": "2.3.35-2", "description": "The main thirdweb SDK.", "repository": { "type": "git", From bd71065f1b16d6a9cba971fa852fbaa3fc2f0324 Mon Sep 17 00:00:00 2001 From: Joaquim Verges Date: Wed, 10 Aug 2022 17:04:07 -0700 Subject: [PATCH 3/5] add analytics to predeploy metadata schema --- etc/sdk.api.md | 18 ++++++++++++++---- src/schema/contracts/custom.ts | 1 + 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/etc/sdk.api.md b/etc/sdk.api.md index c3ff10d16..285b5441b 100644 --- a/etc/sdk.api.md +++ b/etc/sdk.api.md @@ -2847,6 +2847,7 @@ export function fetchPreDeployMetadata(publishMetadataUri: string, storage: ISto // @internal (undocumented) export function fetchRawPredeployMetadata(publishMetadataUri: string, storage: IStorage): Promise<{ [x: string]: any; + analytics?: any; name: string; metadataUri: string; bytecodeUri: string; @@ -2919,6 +2920,7 @@ export const FullPublishMetadataSchema: z.ZodObject; }, { version: z.ZodEffects; displayName: z.ZodOptional; @@ -2933,6 +2935,7 @@ export const FullPublishMetadataSchema: z.ZodObject; }, "strip", z.ZodAny, { [x: string]: any; + analytics?: any; name: string; metadataUri: string; bytecodeUri: string; }, { [x: string]: any; + analytics?: any; name: string; metadataUri: string; bytecodeUri: string; @@ -4245,6 +4252,7 @@ export const PreDeployMetadataFetchedSchema: z.ZodObject; }, { name: z.ZodString; abi: z.ZodArray, "strip", z.ZodAny, { [x: string]: any; + analytics?: any; name: string; metadataUri: string; metadata: Record; @@ -4443,6 +4452,7 @@ export const PreDeployMetadataFetchedSchema: z.ZodObject; @@ -7083,10 +7093,10 @@ export class WrongListingTypeError extends Error { // Warnings were encountered during analysis: // -// dist/src/schema/contracts/custom.d.ts:1270:5 - (ae-incompatible-release-tags) The symbol "inputs" is marked as @public, but its signature references "AbiTypeSchema" which is marked as @internal -// dist/src/schema/contracts/custom.d.ts:1271:5 - (ae-incompatible-release-tags) The symbol "outputs" is marked as @public, but its signature references "AbiTypeSchema" which is marked as @internal -// dist/src/schema/contracts/custom.d.ts:1278:5 - (ae-incompatible-release-tags) The symbol "inputs" is marked as @public, but its signature references "AbiTypeSchema" which is marked as @internal -// dist/src/schema/contracts/custom.d.ts:1279:5 - (ae-incompatible-release-tags) The symbol "outputs" is marked as @public, but its signature references "AbiTypeSchema" which is marked as @internal +// dist/src/schema/contracts/custom.d.ts:1279:5 - (ae-incompatible-release-tags) The symbol "inputs" is marked as @public, but its signature references "AbiTypeSchema" which is marked as @internal +// dist/src/schema/contracts/custom.d.ts:1280:5 - (ae-incompatible-release-tags) The symbol "outputs" is marked as @public, but its signature references "AbiTypeSchema" which is marked as @internal +// dist/src/schema/contracts/custom.d.ts:1287:5 - (ae-incompatible-release-tags) The symbol "inputs" is marked as @public, but its signature references "AbiTypeSchema" which is marked as @internal +// dist/src/schema/contracts/custom.d.ts:1288:5 - (ae-incompatible-release-tags) The symbol "outputs" is marked as @public, but its signature references "AbiTypeSchema" which is marked as @internal // (No @packageDocumentation comment for this package) diff --git a/src/schema/contracts/custom.ts b/src/schema/contracts/custom.ts index a5617027b..41f85b9c9 100644 --- a/src/schema/contracts/custom.ts +++ b/src/schema/contracts/custom.ts @@ -70,6 +70,7 @@ export const PreDeployMetadata = z name: z.string(), metadataUri: z.string(), bytecodeUri: z.string(), + analytics: z.any().optional(), }) .catchall(z.any()); From ce6e59f4d70fde0a658819e16636c9a227b482ec Mon Sep 17 00:00:00 2001 From: Joaquim Verges Date: Wed, 10 Aug 2022 17:09:48 -0700 Subject: [PATCH 4/5] fix tests --- test/custom.test.ts | 2 +- test/marketplace.test.ts | 4 ++-- test/publisher.test.ts | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/custom.test.ts b/test/custom.test.ts index f1ccb2ad1..1675c2056 100644 --- a/test/custom.test.ts +++ b/test/custom.test.ts @@ -154,7 +154,7 @@ describe("Custom Contracts", async () => { invariant(c, "Contract undefined"); invariant(c.metadata, "Contract undefined"); const meta = await c.metadata.get(); - expect(meta.name).to.eq("Greeter"); + expect(meta.name).to.eq("MyToken"); }); it("should detect feature: roles", async () => { diff --git a/test/marketplace.test.ts b/test/marketplace.test.ts index 07bc29d1f..9fa035fcb 100644 --- a/test/marketplace.test.ts +++ b/test/marketplace.test.ts @@ -1096,13 +1096,13 @@ describe("Marketplace Contract", async () => { await sdk.updateSignerOrProvider(adminWallet); await dummyNftContract.transfer(samWallet.address, "0"); - const allListings = await marketplaceContract.getAllListings(); + const allListings = await marketplaceContract.getActiveListings(); const found = allListings.find( (l) => l.id.toString() === directListingId.toString(), ); assert.isUndefined( found, - "should not have found the listing becuase it is invalid", + "should not have found the listing because it is invalid", ); }); }); diff --git a/test/publisher.test.ts b/test/publisher.test.ts index fccaef55f..1000a4804 100644 --- a/test/publisher.test.ts +++ b/test/publisher.test.ts @@ -143,7 +143,7 @@ describe("Publishing", async () => { // fetch metadata back const c = await sdk.getContract(deployedAddr); const meta = await c.metadata.get(); - expect(meta.name).to.eq("Greeter"); + expect(meta.name).to.eq("MyToken"); }); it("should publish multiple versions", async () => { @@ -272,7 +272,7 @@ describe("Publishing", async () => { expect(all.length).to.eq(1); invariant(c.royalties, "no royalties detected"); const prevMeta = await c.metadata.get(); - expect(prevMeta.name).to.eq("CustomAzukiContract"); + expect(prevMeta.name).to.eq("AzukiMint"); expect(prevMeta.description).to.eq( "Azuki contract that can be fully used in the thirdweb dashboard", ); From 70253f3c2906e7467efdd9bcb3c9ab0cc38d074b Mon Sep 17 00:00:00 2001 From: Joaquim Verges Date: Wed, 10 Aug 2022 17:11:26 -0700 Subject: [PATCH 5/5] v2.3.35-3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c7a09c5b3..7332766ec 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@thirdweb-dev/sdk", - "version": "2.3.35-2", + "version": "2.3.35-3", "description": "The main thirdweb SDK.", "repository": { "type": "git",