Skip to content
This repository has been archived by the owner on Aug 30, 2022. It is now read-only.

Commit

Permalink
Allow creating batches from existing URIs for NFT/Edition Drop (#479)
Browse files Browse the repository at this point in the history
* Allow creating batches from existing URIs for NFT/Edition Drop

* add test
  • Loading branch information
joaquim-verges committed Jun 22, 2022
1 parent 9e4f8c8 commit 798b586
Show file tree
Hide file tree
Showing 8 changed files with 85 additions and 26 deletions.
7 changes: 5 additions & 2 deletions docs/sdk.editiondrop.createbatch.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,17 @@ Create a batch of NFTs to be claimed in the future
<b>Signature:</b>

```typescript
createBatch(metadatas: NFTMetadataInput[]): Promise<TransactionResultWithId<NFTMetadata>[]>;
createBatch(metadatas: NFTMetadataOrUri[], options?: {
onProgress: (event: UploadProgressEvent) => void;
}): Promise<TransactionResultWithId<NFTMetadata>[]>;
```

## 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)<!-- -->) =&gt; void; } | <i>(Optional)</i> optional upload progress callback |

<b>Returns:</b>

Expand Down
2 changes: 1 addition & 1 deletion docs/sdk.editiondrop.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down
4 changes: 2 additions & 2 deletions docs/sdk.nftdrop.createbatch.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Create a batch of unique NFTs to be claimed in the future
<b>Signature:</b>

```typescript
createBatch(metadatas: NFTMetadataInput[], options?: {
createBatch(metadatas: NFTMetadataOrUri[], options?: {
onProgress: (event: UploadProgressEvent) => void;
}): Promise<TransactionResultWithId<NFTMetadata>[]>;
```
Expand All @@ -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)<!-- -->) =&gt; void; } | <i>(Optional)</i> optional upload progress callback |

<b>Returns:</b>
Expand Down
8 changes: 5 additions & 3 deletions etc/sdk.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1101,7 +1101,10 @@ export class EditionDrop extends Erc1155<DropERC1155> {
static contractRoles: readonly ["admin", "minter", "transfer"];
// (undocumented)
static contractType: "edition-drop";
createBatch(metadatas: NFTMetadataInput[]): Promise<TransactionResultWithId<NFTMetadata>[]>;
// 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<TransactionResultWithId<NFTMetadata>[]>;
// (undocumented)
encoder: ContractEncoder<DropERC1155>;
// (undocumented)
Expand Down Expand Up @@ -1723,7 +1726,6 @@ export class Erc721BatchMintable implements DetectableFeature {
constructor(erc721: Erc721, contractWrapper: ContractWrapper<IMintableERC721 & IMulticall>, 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<TransactionResultWithId<NFTMetadataOwner>[]>;
}

Expand Down Expand Up @@ -2704,7 +2706,7 @@ export class NFTDrop extends Erc721<DropERC721> {
static contractRoles: readonly ["admin", "minter", "transfer"];
// (undocumented)
static contractType: "nft-drop";
createBatch(metadatas: NFTMetadataInput[], options?: {
createBatch(metadatas: NFTMetadataOrUri[], options?: {
onProgress: (event: UploadProgressEvent) => void;
}): Promise<TransactionResultWithId<NFTMetadata>[]>;
// (undocumented)
Expand Down
15 changes: 15 additions & 0 deletions src/common/nft.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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<string[]> {
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 {
Expand Down
41 changes: 30 additions & 11 deletions src/contracts/edition-drop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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.
Expand Down Expand Up @@ -253,21 +254,39 @@ export class EditionDrop extends Erc1155<DropERC1155> {
* 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<TransactionResultWithId<NFTMetadata>[]> {
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<TokensLazyMintedEvent>(
"TokensLazyMinted",
Expand Down
24 changes: 17 additions & 7 deletions src/contracts/nft-drop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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.
Expand Down Expand Up @@ -413,23 +413,33 @@ export class NFTDrop extends Erc721<DropERC721> {
* @param options - optional upload progress callback
*/
public async createBatch(
metadatas: NFTMetadataInput[],
metadatas: NFTMetadataOrUri[],
options?: {
onProgress: (event: UploadProgressEvent) => void;
},
): Promise<TransactionResultWithId<NFTMetadata>[]> {
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(""),
]);
Expand Down
10 changes: 10 additions & 0 deletions test/nft-drop.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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([
{
Expand Down

0 comments on commit 798b586

Please sign in to comment.