diff --git a/docs/sdk.delayedreveal.featurename.md b/docs/sdk.delayedreveal.featurename.md new file mode 100644 index 000000000..7cf1413d7 --- /dev/null +++ b/docs/sdk.delayedreveal.featurename.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@thirdweb-dev/sdk](./sdk.md) > [DelayedReveal](./sdk.delayedreveal.md) > [featureName](./sdk.delayedreveal.featurename.md) + +## DelayedReveal.featureName property + +Signature: + +```typescript +featureName: "ERC721Revealable"; +``` diff --git a/docs/sdk.delayedreveal.md b/docs/sdk.delayedreveal.md index 2001bcdbb..a499e7422 100644 --- a/docs/sdk.delayedreveal.md +++ b/docs/sdk.delayedreveal.md @@ -9,7 +9,7 @@ Handles delayed reveal logic Signature: ```typescript -export declare class DelayedReveal +export declare class DelayedReveal ``` ## Constructors @@ -18,6 +18,12 @@ export declare class DelayedReveal | --- | --- | --- | | [(constructor)(contractWrapper, storage)](./sdk.delayedreveal._constructor_.md) | | Constructs a new instance of the DelayedReveal class | +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [featureName](./sdk.delayedreveal.featurename.md) | | "ERC721Revealable" | | + ## Methods | Method | Modifiers | Description | diff --git a/docs/sdk.erc721.md b/docs/sdk.erc721.md index 7547069af..7af46465c 100644 --- a/docs/sdk.erc721.md +++ b/docs/sdk.erc721.md @@ -41,6 +41,7 @@ await contract.nft.transfer(walletAddress, tokenId); | [mint](./sdk.erc721.mint.md) | | [Erc721Mintable](./sdk.erc721mintable.md) \| undefined | | | [options](./sdk.erc721.options.md) | | [SDKOptions](./sdk.sdkoptions.md) | | | [query](./sdk.erc721.query.md) | | [Erc721Supply](./sdk.erc721supply.md) \| undefined | | +| [revealer](./sdk.erc721.revealer.md) | | [DelayedReveal](./sdk.delayedreveal.md)<BaseDelayedRevealERC721 \| DropERC721> \| undefined | | | [storage](./sdk.erc721.storage.md) | | [IStorage](./sdk.istorage.md) | | ## Methods diff --git a/docs/sdk.erc721.revealer.md b/docs/sdk.erc721.revealer.md new file mode 100644 index 000000000..479b07c8d --- /dev/null +++ b/docs/sdk.erc721.revealer.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@thirdweb-dev/sdk](./sdk.md) > [Erc721](./sdk.erc721.md) > [revealer](./sdk.erc721.revealer.md) + +## Erc721.revealer property + +Signature: + +```typescript +revealer: DelayedReveal | undefined; +``` diff --git a/etc/sdk.api.md b/etc/sdk.api.md index 1a89c54fc..2e194b498 100644 --- a/etc/sdk.api.md +++ b/etc/sdk.api.md @@ -855,15 +855,17 @@ export const DEFAULT_IPFS_GATEWAY = "https://gateway.ipfscdn.io/ipfs/"; // @internal (undocumented) export const DEFAULT_QUERY_ALL_COUNT = 100; -// Warning: (ae-forgotten-export) The symbol "SignatureDrop" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "DropERC721" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "BaseDelayedRevealERC721" needs to be exported by the entry point index.d.ts // // @public -export class DelayedReveal { +export class DelayedReveal { constructor(contractWrapper: ContractWrapper, storage: IStorage); createDelayedRevealBatch(placeholder: NFTMetadataInput, metadatas: NFTMetadataInput[], password: string, options?: { onProgress: (event: UploadProgressEvent) => void; }): Promise; + // (undocumented) + featureName: "ERC721Revealable"; getBatchesToReveal(): Promise; reveal(batchId: BigNumberish, password: string): Promise; } @@ -1682,6 +1684,7 @@ export type ERC20Wrappable = { }; // Warning: (ae-forgotten-export) The symbol "Multiwrap" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "SignatureDrop" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "TokenERC721" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "BaseERC721" needs to be exported by the entry point index.d.ts // @@ -1713,6 +1716,8 @@ export class Erc721; // (undocumented) query: Erc721Supply | undefined; + // (undocumented) + revealer: DelayedReveal | undefined; // @internal setApprovalForAll(operator: string, approved: boolean): Promise; // @internal @@ -4216,7 +4221,6 @@ export class SignatureDrop extends Erc721 { metadata: ContractMetadata; // (undocumented) platformFees: ContractPlatformFee; - revealer: DelayedReveal; // (undocumented) roles: ContractRoles; royalties: ContractRoyalty; @@ -4338,8 +4342,6 @@ export class SignatureDrop extends Erc721 { }>; }; // Warning: (ae-forgotten-export) The symbol "Erc721WithQuantitySignatureMinting" needs to be exported by the entry point index.d.ts - // - // (undocumented) signature: Erc721WithQuantitySignatureMinting; totalClaimedSupply(): Promise; totalSupply(): Promise; diff --git a/src/constants/contract-features.ts b/src/constants/contract-features.ts index 0c5909480..197ff0753 100644 --- a/src/constants/contract-features.ts +++ b/src/constants/contract-features.ts @@ -5,6 +5,7 @@ import { FEATURE_NFT_LAZY_MINTABLE, FEATURE_NFT_MINTABLE, FEATURE_NFT_SUPPLY, + FEATURE_NFT_REVEALABLE, } from "./erc721-features"; import { FEATURE_TOKEN, @@ -46,7 +47,8 @@ export type Feature = | typeof FEATURE_PLATFORM_FEE | typeof FEATURE_PRIMARY_SALE | typeof FEATURE_PERMISSIONS - | typeof FEATURE_METADATA; + | typeof FEATURE_METADATA + | typeof FEATURE_NFT_REVEALABLE; /** * @internal diff --git a/src/constants/erc721-features.ts b/src/constants/erc721-features.ts index c4a92537b..36898ec01 100644 --- a/src/constants/erc721-features.ts +++ b/src/constants/erc721-features.ts @@ -4,6 +4,7 @@ import Erc721SupplyAbi from "../../abis/IERC721Supply.json"; import IMintableERC721Abi from "../../abis/IMintableERC721.json"; import MulticallAbi from "../../abis/IMulticall.json"; import LazyMintERC721Abi from "../../abis/LazyMintERC721.json"; +import DelayedRevealAbi from "../../abis/DelayedReveal.json"; export const FEATURE_NFT_LAZY_MINTABLE = { name: "ERC721LazyMintable", @@ -64,6 +65,17 @@ export const FEATURE_NFT_SUPPLY = { }, } as const; +export const FEATURE_NFT_REVEALABLE = { + name: "ERC721Revealable", + namespace: "nft.revealer", + docLinks: { + sdk: "sdk.delayedreveal", + contracts: "IDelayedReveal", + }, + abis: [Erc721Abi, DelayedRevealAbi, LazyMintERC721Abi], + features: {}, +} as const; + export const FEATURE_NFT = { name: "ERC721", namespace: "nft", @@ -76,5 +88,6 @@ export const FEATURE_NFT = { [FEATURE_NFT_SUPPLY.name]: FEATURE_NFT_SUPPLY, [FEATURE_NFT_MINTABLE.name]: FEATURE_NFT_MINTABLE, [FEATURE_NFT_LAZY_MINTABLE.name]: FEATURE_NFT_LAZY_MINTABLE, + [FEATURE_NFT_REVEALABLE.name]: FEATURE_NFT_REVEALABLE, }, } as const; diff --git a/src/contracts/nft-drop.ts b/src/contracts/nft-drop.ts index 5496df633..d59c486fd 100644 --- a/src/contracts/nft-drop.ts +++ b/src/contracts/nft-drop.ts @@ -4,9 +4,9 @@ import { BigNumber, BigNumberish, BytesLike, + constants, ethers, utils, - constants, } from "ethers"; import { ContractMetadata } from "../core/classes/contract-metadata"; import { ContractRoyalty } from "../core/classes/contract-royalty"; @@ -159,7 +159,7 @@ export class NFTDrop extends Erc721 { * await contract.revealer.reveal(batchId, "my secret password"); * ``` */ - public revealer: DelayedReveal; + public override revealer: DelayedReveal; private _query = this.query as Erc721Supply; private _owned = this._query.owned as Erc721Enumerable; diff --git a/src/contracts/signature-drop.ts b/src/contracts/signature-drop.ts index 82a32da6d..21161960b 100644 --- a/src/contracts/signature-drop.ts +++ b/src/contracts/signature-drop.ts @@ -31,7 +31,6 @@ import { Erc721 } from "../core/classes/erc-721"; import { ContractPrimarySale } from "../core/classes/contract-sales"; import { prepareClaim } from "../common/claim-conditions"; import { ContractEncoder } from "../core/classes/contract-encoder"; -import { DelayedReveal } from "../core/classes/delayed-reveal"; import { Erc721Enumerable, Erc721Supply, @@ -160,8 +159,6 @@ export class SignatureDrop extends Erc721 { * await contract.revealer.reveal(batchId, "my secret password"); * ``` */ - public revealer: DelayedReveal; - public signature: Erc721WithQuantitySignatureMinting; private _query = this.query as Erc721Supply; @@ -196,10 +193,6 @@ export class SignatureDrop extends Erc721 { this.estimator = new GasCostEstimator(this.contractWrapper); this.events = new ContractEvents(this.contractWrapper); this.platformFees = new ContractPlatformFee(this.contractWrapper); - this.revealer = new DelayedReveal( - this.contractWrapper, - this.storage, - ); this.interceptor = new ContractInterceptor(this.contractWrapper); this.signature = new Erc721WithQuantitySignatureMinting( this.contractWrapper, diff --git a/src/core/classes/delayed-reveal.ts b/src/core/classes/delayed-reveal.ts index 858ea8303..57640d048 100644 --- a/src/core/classes/delayed-reveal.ts +++ b/src/core/classes/delayed-reveal.ts @@ -1,6 +1,6 @@ import { BigNumber, BigNumberish, ethers } from "ethers"; import { ContractWrapper } from "./contract-wrapper"; -import { DropERC721, SignatureDrop } from "contracts"; +import { DropERC721 } from "contracts"; import { CommonNFTInput, NFTMetadata, @@ -11,12 +11,17 @@ import { fetchTokenMetadata } from "../../common/nft"; import { BatchToReveal } from "../../types/delayed-reveal"; import { TokensLazyMintedEvent } from "contracts/DropERC721"; import { UploadProgressEvent } from "../../types/events"; +import { BaseDelayedRevealERC721 } from "../../types/eips"; +import { hasFunction } from "../../common"; +import { FEATURE_NFT_REVEALABLE } from "../../constants/erc721-features"; /** * Handles delayed reveal logic * @public */ -export class DelayedReveal { +export class DelayedReveal { + featureName = FEATURE_NFT_REVEALABLE.name; + private contractWrapper: ContractWrapper; private storage: IStorage; @@ -167,17 +172,26 @@ export class DelayedReveal { const countRangeArray = Array.from(Array(count.toNumber()).keys()); - const contractType = ethers.utils.toUtf8String( - await this.contractWrapper.readContract.contractType(), - ); - // map over to get the base uri indices, which should be the end token id of every batch const uriIndices = await Promise.all( - countRangeArray.map((i) => - this.isSignatureDrop(this.contractWrapper.readContract, contractType) - ? this.contractWrapper.readContract.getBatchIdAtIndex(i) - : this.contractWrapper.readContract.baseURIIndices(i), - ), + countRangeArray.map((i) => { + if ( + hasFunction( + "getBatchIdAtIndex", + this.contractWrapper, + ) + ) { + return this.contractWrapper.readContract.getBatchIdAtIndex(i); + } + + if (hasFunction("baseURIIndices", this.contractWrapper)) { + return this.contractWrapper.readContract.baseURIIndices(i); + } + + throw new Error( + "Contract does not have getBatchIdAtIndex or baseURIIndices.", + ); + }), ); // first batch always start from 0. don't need to fetch the last batch so pop it from the range array @@ -235,11 +249,4 @@ export class DelayedReveal { const tokenUri = await this.contractWrapper.readContract.tokenURI(tokenId); return fetchTokenMetadata(tokenId, tokenUri, this.storage); } - - private isSignatureDrop( - _contract: SignatureDrop | DropERC721, - contractType: string, - ): _contract is SignatureDrop { - return contractType.includes("SignatureDrop"); - } } diff --git a/src/core/classes/erc-721.ts b/src/core/classes/erc-721.ts index 2e290ed0a..78153dff8 100644 --- a/src/core/classes/erc-721.ts +++ b/src/core/classes/erc-721.ts @@ -22,10 +22,11 @@ import { } from "contracts"; import { Erc721Supply } from "./erc-721-supply"; import { Erc721Mintable } from "./erc-721-mintable"; -import { BaseERC721 } from "../../types/eips"; +import { BaseDelayedRevealERC721, BaseERC721 } from "../../types/eips"; import { FEATURE_NFT } from "../../constants/erc721-features"; import { DetectableFeature } from "../interfaces/DetectableFeature"; import { Erc721LazyMintable } from "./erc-721-lazy-mintable"; +import { DelayedReveal } from "./delayed-reveal"; /** * Standard ERC721 NFT functions @@ -47,13 +48,15 @@ export class Erc721< > implements UpdateableNetwork, DetectableFeature { featureName = FEATURE_NFT.name; - protected contractWrapper: ContractWrapper; - protected storage: IStorage; - protected options: SDKOptions; - public query: Erc721Supply | undefined; public mint: Erc721Mintable | undefined; public lazy: Erc721LazyMintable | undefined; + public revealer: + | DelayedReveal + | undefined; + protected contractWrapper: ContractWrapper; + protected storage: IStorage; + protected options: SDKOptions; constructor( contractWrapper: ContractWrapper, @@ -74,6 +77,7 @@ export class Erc721< this.query = this.detectErc721Enumerable(); this.mint = this.detectErc721Mintable(); this.lazy = this.detectErc721LazyMintable(); + this.revealer = this.detectErc721Revealable(); } /** @@ -273,4 +277,18 @@ export class Erc721< } return undefined; } + + private detectErc721Revealable(): + | DelayedReveal + | undefined { + if ( + detectContractFeature( + this.contractWrapper, + "ERC721Revealable", + ) + ) { + return new DelayedReveal(this.contractWrapper, this.storage); + } + return undefined; + } } diff --git a/src/core/classes/index.ts b/src/core/classes/index.ts index 47d4d4ba8..dbc44297e 100644 --- a/src/core/classes/index.ts +++ b/src/core/classes/index.ts @@ -5,6 +5,7 @@ export * from "./contract-metadata"; export * from "./contract-roles"; export * from "./contract-royalty"; export * from "./contract-sales"; +export * from "./delayed-reveal"; export * from "./drop-claim-conditions"; export * from "./drop-erc1155-claim-conditions"; export * from "./drop-erc1155-history"; diff --git a/src/types/eips.ts b/src/types/eips.ts index e426f93bc..508649e13 100644 --- a/src/types/eips.ts +++ b/src/types/eips.ts @@ -1,4 +1,6 @@ import { + DelayedReveal, + DropERC721, IERC1155, IERC1155Metadata, IERC1155Supply, @@ -6,8 +8,13 @@ import { IERC20Metadata, IERC721, IERC721Metadata, + LazyMintERC721, } from "contracts"; export type BaseERC20 = IERC20 & IERC20Metadata; export type BaseERC721 = IERC721 & IERC721Metadata; export type BaseERC1155 = IERC1155 & IERC1155Metadata & IERC1155Supply; +export type BaseDelayedRevealERC721 = BaseERC721 & + DelayedReveal & + LazyMintERC721 & + DropERC721; diff --git a/test/custom.test.ts b/test/custom.test.ts index 184954f25..8619e826a 100644 --- a/test/custom.test.ts +++ b/test/custom.test.ts @@ -2,7 +2,6 @@ import { signers } from "./before-setup"; import { expect } from "chai"; import invariant from "tiny-invariant"; import { - DropERC721__factory, SignatureDrop__factory, TokenERC1155__factory, TokenERC20__factory, @@ -253,6 +252,27 @@ describe("Custom Contracts", async () => { expect(nfts[0].metadata.name).to.eq("Custom NFT"); }); + it("should detect feature: erc721 delay reveal", async () => { + const c = await sdk.getContractFromAbi( + sigDropContractAddress, + SignatureDrop__factory.abi, + ); + invariant(c, "Contract undefined"); + invariant(c.nft, "ERC721 undefined"); + invariant(c.nft.revealer, "ERC721 query undefined"); + + await c.nft.revealer.createDelayedRevealBatch( + { + name: "Placeholder #1", + }, + [{ name: "NFT #1" }, { name: "NFT #2" }, { name: "NFT #3" }], + "password", + ); + + await c.nft.revealer.reveal(0, "password"); + expect((await c.nft.get(0)).metadata.name).to.be.equal("NFT #1"); + }); + it("should detect feature: erc1155", async () => { const c = await sdk.getContractFromAbi( editionContractAddress,