diff --git a/docs/sdk.editiondrop.createbatch.md b/docs/sdk.editiondrop.createbatch.md index 546500288..19b2a712d 100644 --- a/docs/sdk.editiondrop.createbatch.md +++ b/docs/sdk.editiondrop.createbatch.md @@ -9,14 +9,17 @@ Create a batch of NFTs to be claimed in the future Signature: ```typescript -createBatch(metadatas: NFTMetadataInput[]): Promise[]>; +createBatch(metadatas: NFTMetadataOrUri[], options?: { + onProgress: (event: UploadProgressEvent) => void; + }): Promise[]>; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| metadatas | [NFTMetadataInput](./sdk.nftmetadatainput.md)\[\] | | +| metadatas | NFTMetadataOrUri\[\] | The metadata to include in the batch. | +| options | { onProgress: (event: [UploadProgressEvent](./sdk.uploadprogressevent.md)) => void; } | (Optional) optional upload progress callback | Returns: diff --git a/docs/sdk.editiondrop.md b/docs/sdk.editiondrop.md index c16da8d01..088c7358e 100644 --- a/docs/sdk.editiondrop.md +++ b/docs/sdk.editiondrop.md @@ -54,7 +54,7 @@ const contract = sdk.getEditionDrop("{{contract_address}}"); | [burn(tokenId, amount)](./sdk.editiondrop.burn.md) | | Burn a specified amount of a NFT | | [claim(tokenId, quantity, proofs)](./sdk.editiondrop.claim.md) | | Claim a token to the connected wallet | | [claimTo(destinationAddress, tokenId, quantity, proofs)](./sdk.editiondrop.claimto.md) | | Claim NFTs to a specific Wallet | -| [createBatch(metadatas)](./sdk.editiondrop.createbatch.md) | | Create a batch of NFTs to be claimed in the future | +| [createBatch(metadatas, options)](./sdk.editiondrop.createbatch.md) | | Create a batch of NFTs to be claimed in the future | | [getAll(queryParams)](./sdk.editiondrop.getall.md) | | Get All Minted NFTs | | [getOwned(walletAddress)](./sdk.editiondrop.getowned.md) | | Get Owned NFTs | | [getTotalCount()](./sdk.editiondrop.gettotalcount.md) | | Get the number of NFTs minted | diff --git a/docs/sdk.nftdrop.createbatch.md b/docs/sdk.nftdrop.createbatch.md index 3c7134192..9a3ff5819 100644 --- a/docs/sdk.nftdrop.createbatch.md +++ b/docs/sdk.nftdrop.createbatch.md @@ -9,7 +9,7 @@ Create a batch of unique NFTs to be claimed in the future Signature: ```typescript -createBatch(metadatas: NFTMetadataInput[], options?: { +createBatch(metadatas: NFTMetadataOrUri[], options?: { onProgress: (event: UploadProgressEvent) => void; }): Promise[]>; ``` @@ -18,7 +18,7 @@ createBatch(metadatas: NFTMetadataInput[], options?: { | Parameter | Type | Description | | --- | --- | --- | -| metadatas | [NFTMetadataInput](./sdk.nftmetadatainput.md)\[\] | The metadata to include in the batch. | +| metadatas | NFTMetadataOrUri\[\] | The metadata to include in the batch. | | options | { onProgress: (event: [UploadProgressEvent](./sdk.uploadprogressevent.md)) => void; } | (Optional) optional upload progress callback | Returns: diff --git a/etc/sdk.api.md b/etc/sdk.api.md index 83a9f8ecb..817f49fbe 100644 --- a/etc/sdk.api.md +++ b/etc/sdk.api.md @@ -1101,7 +1101,10 @@ export class EditionDrop extends Erc1155 { static contractRoles: readonly ["admin", "minter", "transfer"]; // (undocumented) static contractType: "edition-drop"; - createBatch(metadatas: NFTMetadataInput[]): Promise[]>; + // Warning: (ae-forgotten-export) The symbol "NFTMetadataOrUri" needs to be exported by the entry point index.d.ts + createBatch(metadatas: NFTMetadataOrUri[], options?: { + onProgress: (event: UploadProgressEvent) => void; + }): Promise[]>; // (undocumented) encoder: ContractEncoder; // (undocumented) @@ -1723,7 +1726,6 @@ export class Erc721BatchMintable implements DetectableFeature { constructor(erc721: Erc721, contractWrapper: ContractWrapper, storage: IStorage); // (undocumented) featureName: "ERC721BatchMintable"; - // Warning: (ae-forgotten-export) The symbol "NFTMetadataOrUri" needs to be exported by the entry point index.d.ts to(to: string, metadatas: NFTMetadataOrUri[]): Promise[]>; } @@ -2704,7 +2706,7 @@ export class NFTDrop extends Erc721 { static contractRoles: readonly ["admin", "minter", "transfer"]; // (undocumented) static contractType: "nft-drop"; - createBatch(metadatas: NFTMetadataInput[], options?: { + createBatch(metadatas: NFTMetadataOrUri[], options?: { onProgress: (event: UploadProgressEvent) => void; }): Promise[]>; // (undocumented) diff --git a/src/common/nft.ts b/src/common/nft.ts index 181c75c02..84263daac 100644 --- a/src/common/nft.ts +++ b/src/common/nft.ts @@ -16,6 +16,7 @@ import { import ERC721MetadataAbi from "../../abis/IERC721Metadata.json"; import ERC1155MetadataAbi from "../../abis/IERC1155Metadata.json"; import ERC165MetadataAbi from "../../abis/IERC165.json"; +import { UploadProgressEvent } from "../types/index"; const FALLBACK_METADATA = { name: "Failed to load NFT metadata", @@ -124,16 +125,30 @@ export async function uploadOrExtractURI( * @internal * @param metadatas * @param storage + * @param startNumber + * @param contractAddress + * @param signerAddress + * @param options */ export async function uploadOrExtractURIs( metadatas: NFTMetadataOrUri[], storage: IStorage, + startNumber?: number, + contractAddress?: string, + signerAddress?: string, + options?: { + onProgress: (event: UploadProgressEvent) => void; + }, ): Promise { if (isUriList(metadatas)) { return metadatas; } else if (isMetadataList(metadatas)) { const { uris } = await storage.uploadMetadataBatch( metadatas.map((m) => CommonNFTInput.parse(m)), + startNumber, + contractAddress, + signerAddress, + options, ); return uris; } else { diff --git a/src/contracts/edition-drop.ts b/src/contracts/edition-drop.ts index 2c360b36b..90d3c64b2 100644 --- a/src/contracts/edition-drop.ts +++ b/src/contracts/edition-drop.ts @@ -13,18 +13,18 @@ import { } from "../core/types"; import { SDKOptions } from "../schema/sdk-options"; import { ContractWrapper } from "../core/classes/contract-wrapper"; -import { - CommonNFTInput, - NFTMetadata, - NFTMetadataInput, -} from "../schema/tokens/common"; +import { NFTMetadata, NFTMetadataOrUri } from "../schema/tokens/common"; import { BigNumber, BigNumberish, BytesLike, constants, utils } from "ethers"; import { prepareClaim } from "../common/claim-conditions"; import { DropErc1155ClaimConditions } from "../core/classes/drop-erc1155-claim-conditions"; import { DropErc1155ContractSchema } from "../schema/contracts/drop-erc1155"; import { ContractEncoder } from "../core/classes/contract-encoder"; import { GasCostEstimator } from "../core/classes/gas-cost-estimator"; -import { ClaimVerification, QueryAllParams } from "../types"; +import { + ClaimVerification, + QueryAllParams, + UploadProgressEvent, +} from "../types"; import { DropErc1155History } from "../core/classes/drop-erc1155-history"; import { ContractEvents } from "../core/classes/contract-events"; import { ContractPlatformFee } from "../core/classes/contract-platform-fee"; @@ -34,6 +34,7 @@ import { getRoleHash } from "../common"; import { EditionMetadata, EditionMetadataOwner } from "../schema"; import { ContractAnalytics } from "../core/classes/contract-analytics"; +import { uploadOrExtractURIs } from "../common/nft"; /** * Setup a collection of NFTs with a customizable number of each NFT that are minted as users claim them. @@ -253,21 +254,39 @@ export class EditionDrop extends Erc1155 { * const firstTokenId = results[0].id; // token id of the first created NFT * const firstNFT = await results[0].data(); // (optional) fetch details of the first created NFT * ``` + * + * @param metadatas - The metadata to include in the batch. + * @param options - optional upload progress callback */ public async createBatch( - metadatas: NFTMetadataInput[], + metadatas: NFTMetadataOrUri[], + options?: { + onProgress: (event: UploadProgressEvent) => void; + }, ): Promise[]> { const startFileNumber = await this.contractWrapper.readContract.nextTokenIdToMint(); - const batch = await this.storage.uploadMetadataBatch( - metadatas.map((m) => CommonNFTInput.parse(m)), + const batch = await uploadOrExtractURIs( + metadatas, + this.storage, startFileNumber.toNumber(), this.contractWrapper.readContract.address, await this.contractWrapper.getSigner()?.getAddress(), + options, ); + // ensure baseUri is the same for the entire batch + const baseUri = batch[0].substring(0, batch[0].lastIndexOf("/")); + for (let i = 0; i < batch.length; i++) { + const uri = batch[i].substring(0, batch[i].lastIndexOf("/")); + if (baseUri !== uri) { + throw new Error( + `Can only create batches with the same base URI for every entry in the batch. Expected '${baseUri}' but got '${uri}'`, + ); + } + } const receipt = await this.contractWrapper.sendTransaction("lazyMint", [ - batch.uris.length, - `${batch.baseUri.endsWith("/") ? batch.baseUri : `${batch.baseUri}/`}`, + batch.length, + `${baseUri.endsWith("/") ? baseUri : `${baseUri}/`}`, ]); const event = this.contractWrapper.parseLogs( "TokensLazyMinted", diff --git a/src/contracts/nft-drop.ts b/src/contracts/nft-drop.ts index 20701564b..b0cc781a8 100644 --- a/src/contracts/nft-drop.ts +++ b/src/contracts/nft-drop.ts @@ -19,9 +19,8 @@ import { import { DropErc721ContractSchema } from "../schema/contracts/drop-erc721"; import { SDKOptions } from "../schema/sdk-options"; import { - CommonNFTInput, NFTMetadata, - NFTMetadataInput, + NFTMetadataOrUri, NFTMetadataOwner, } from "../schema/tokens/common"; import { DEFAULT_QUERY_ALL_COUNT, QueryAllParams } from "../types/QueryParams"; @@ -46,6 +45,7 @@ import { } from "contracts/DropERC721"; import { ContractAnalytics } from "../core/classes/contract-analytics"; import { UploadProgressEvent } from "../types/events"; +import { uploadOrExtractURIs } from "../common/nft"; /** * Setup a collection of one-of-one NFTs that are minted as users claim them. @@ -413,23 +413,33 @@ export class NFTDrop extends Erc721 { * @param options - optional upload progress callback */ public async createBatch( - metadatas: NFTMetadataInput[], + metadatas: NFTMetadataOrUri[], options?: { onProgress: (event: UploadProgressEvent) => void; }, ): Promise[]> { const startFileNumber = await this.contractWrapper.readContract.nextTokenIdToMint(); - const batch = await this.storage.uploadMetadataBatch( - metadatas.map((m) => CommonNFTInput.parse(m)), + const batch = await uploadOrExtractURIs( + metadatas, + this.storage, startFileNumber.toNumber(), this.contractWrapper.readContract.address, await this.contractWrapper.getSigner()?.getAddress(), options, ); - const baseUri = batch.baseUri; + // ensure baseUri is the same for the entire batch + const baseUri = batch[0].substring(0, batch[0].lastIndexOf("/")); + for (let i = 0; i < batch.length; i++) { + const uri = batch[i].substring(0, batch[i].lastIndexOf("/")); + if (baseUri !== uri) { + throw new Error( + `Can only create batches with the same base URI for every entry in the batch. Expected '${baseUri}' but got '${uri}'`, + ); + } + } const receipt = await this.contractWrapper.sendTransaction("lazyMint", [ - batch.uris.length, + batch.length, baseUri.endsWith("/") ? baseUri : `${baseUri}/`, ethers.utils.toUtf8Bytes(""), ]); diff --git a/test/nft-drop.test.ts b/test/nft-drop.test.ts index e219af06f..3639e2379 100644 --- a/test/nft-drop.test.ts +++ b/test/nft-drop.test.ts @@ -40,6 +40,16 @@ describe("NFT Drop Contract", async () => { dropContract = sdk.getNFTDrop(address); }); + it("should lazy mint with URI", async () => { + const uri = await storage.uploadMetadata({ + name: "Test1", + }); + await dropContract.createBatch([uri]); + const nft = await dropContract.get("0"); + assert.isNotNull(nft); + assert.equal(nft.metadata.name, "Test1"); + }); + it("should allow a snapshot to be set", async () => { await dropContract.claimConditions.set([ {