From b078af1f8ee3144d0b803871348ad153eb6a7566 Mon Sep 17 00:00:00 2001 From: Joaquim Verges Date: Mon, 6 Jun 2022 22:40:41 -0700 Subject: [PATCH 1/5] add lazy mint feature detection --- docs/sdk.erc721.lazy.md | 11 +++ docs/sdk.erc721.md | 1 + docs/sdk.nftdrop.createbatch.md | 2 +- etc/sdk.api.md | 6 +- src/constants/contract-features.ts | 2 + src/constants/erc721-features.ts | 13 +++ src/contracts/nft-drop.ts | 1 + src/core/classes/erc-721-lazy-mintable.ts | 105 ++++++++++++++++++++++ src/core/classes/erc-721.ts | 20 ++++- test/publisher.test.ts | 20 +++++ yarn.lock | 6 +- 11 files changed, 178 insertions(+), 9 deletions(-) create mode 100644 docs/sdk.erc721.lazy.md create mode 100644 src/core/classes/erc-721-lazy-mintable.ts diff --git a/docs/sdk.erc721.lazy.md b/docs/sdk.erc721.lazy.md new file mode 100644 index 000000000..c6cd50b7d --- /dev/null +++ b/docs/sdk.erc721.lazy.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@thirdweb-dev/sdk](./sdk.md) > [Erc721](./sdk.erc721.md) > [lazy](./sdk.erc721.lazy.md) + +## Erc721.lazy property + +Signature: + +```typescript +lazy: Erc721LazyMintable | undefined; +``` diff --git a/docs/sdk.erc721.md b/docs/sdk.erc721.md index a4b3eb94e..7547069af 100644 --- a/docs/sdk.erc721.md +++ b/docs/sdk.erc721.md @@ -37,6 +37,7 @@ await contract.nft.transfer(walletAddress, tokenId); | --- | --- | --- | --- | | [contractWrapper](./sdk.erc721.contractwrapper.md) | | ContractWrapper<T> | | | [featureName](./sdk.erc721.featurename.md) | | "ERC721" | | +| [lazy](./sdk.erc721.lazy.md) | | Erc721LazyMintable \| undefined | | | [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 | | diff --git a/docs/sdk.nftdrop.createbatch.md b/docs/sdk.nftdrop.createbatch.md index d343455f8..3c7134192 100644 --- a/docs/sdk.nftdrop.createbatch.md +++ b/docs/sdk.nftdrop.createbatch.md @@ -19,7 +19,7 @@ createBatch(metadatas: NFTMetadataInput[], options?: { | Parameter | Type | Description | | --- | --- | --- | | metadatas | [NFTMetadataInput](./sdk.nftmetadatainput.md)\[\] | The metadata to include in the batch. | -| options | { onProgress: (event: [UploadProgressEvent](./sdk.uploadprogressevent.md)) => void; } | (Optional) | +| 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 476c86d81..1044ea3fa 100644 --- a/etc/sdk.api.md +++ b/etc/sdk.api.md @@ -1698,8 +1698,12 @@ export class Erc721; + getTokenMetadata(tokenId: BigNumberish): Promise; isApproved(address: string, operator: string): Promise; + // Warning: (ae-forgotten-export) The symbol "Erc721LazyMintable" needs to be exported by the entry point index.d.ts + // + // (undocumented) + lazy: Erc721LazyMintable | undefined; // (undocumented) mint: Erc721Mintable | undefined; // @internal (undocumented) diff --git a/src/constants/contract-features.ts b/src/constants/contract-features.ts index ad3d1c1ec..0c5909480 100644 --- a/src/constants/contract-features.ts +++ b/src/constants/contract-features.ts @@ -2,6 +2,7 @@ import { FEATURE_NFT, FEATURE_NFT_BATCH_MINTABLE, FEATURE_NFT_ENUMERABLE, + FEATURE_NFT_LAZY_MINTABLE, FEATURE_NFT_MINTABLE, FEATURE_NFT_SUPPLY, } from "./erc721-features"; @@ -36,6 +37,7 @@ export type Feature = | typeof FEATURE_NFT_ENUMERABLE | typeof FEATURE_NFT_MINTABLE | typeof FEATURE_NFT_BATCH_MINTABLE + | typeof FEATURE_NFT_LAZY_MINTABLE | typeof FEATURE_EDITION | typeof FEATURE_EDITION_ENUMERABLE | typeof FEATURE_EDITION_MINTABLE diff --git a/src/constants/erc721-features.ts b/src/constants/erc721-features.ts index 0a77f636f..c4a92537b 100644 --- a/src/constants/erc721-features.ts +++ b/src/constants/erc721-features.ts @@ -3,6 +3,18 @@ import Erc721EnumerableAbi from "../../abis/IERC721Enumerable.json"; import Erc721SupplyAbi from "../../abis/IERC721Supply.json"; import IMintableERC721Abi from "../../abis/IMintableERC721.json"; import MulticallAbi from "../../abis/IMulticall.json"; +import LazyMintERC721Abi from "../../abis/LazyMintERC721.json"; + +export const FEATURE_NFT_LAZY_MINTABLE = { + name: "ERC721LazyMintable", + namespace: "nft.lazyMint", + docLinks: { + sdk: "sdk.erc721lazymintable", + contracts: "LazyMintERC721", + }, + abis: [Erc721Abi, LazyMintERC721Abi], + features: {}, +} as const; export const FEATURE_NFT_BATCH_MINTABLE = { name: "ERC721BatchMintable", @@ -63,5 +75,6 @@ export const FEATURE_NFT = { features: { [FEATURE_NFT_SUPPLY.name]: FEATURE_NFT_SUPPLY, [FEATURE_NFT_MINTABLE.name]: FEATURE_NFT_MINTABLE, + [FEATURE_NFT_LAZY_MINTABLE.name]: FEATURE_NFT_LAZY_MINTABLE, }, } as const; diff --git a/src/contracts/nft-drop.ts b/src/contracts/nft-drop.ts index 68b9a4210..5496df633 100644 --- a/src/contracts/nft-drop.ts +++ b/src/contracts/nft-drop.ts @@ -411,6 +411,7 @@ export class NFTDrop extends Erc721 { * ``` * * @param metadatas - The metadata to include in the batch. + * @param options - optional upload progress callback */ public async createBatch( metadatas: NFTMetadataInput[], diff --git a/src/core/classes/erc-721-lazy-mintable.ts b/src/core/classes/erc-721-lazy-mintable.ts new file mode 100644 index 000000000..84961d9f1 --- /dev/null +++ b/src/core/classes/erc-721-lazy-mintable.ts @@ -0,0 +1,105 @@ +import { ContractWrapper } from "./contract-wrapper"; +import { LazyMintERC721 } from "contracts"; +import { CommonNFTInput, NFTMetadata, NFTMetadataInput } from "../../schema"; +import { TransactionResultWithId } from "../types"; +import { IStorage } from "../interfaces"; +import { Erc721 } from "./erc-721"; +import { Erc721BatchMintable } from "./erc-721-batch-mintable"; +import { FEATURE_NFT_LAZY_MINTABLE } from "../../constants/erc721-features"; +import { DetectableFeature } from "../interfaces/DetectableFeature"; +import { UploadProgressEvent } from "../../types"; +import { ethers } from "ethers"; +import { TokensLazyMintedEvent } from "contracts/LazyMintERC721"; + +/** + * LazyMint ERC721 NFTs + * @remarks NFT lazy minting functionality that handles IPFS batch uploads for you. + * @example + * ```javascript + * const contract = await sdk.getContract("{{contract_address}}"); + * await contract.nft.lazy.mint(walletAddress, nftMetadata); + * ``` + * @public + */ +export class Erc721LazyMintable implements DetectableFeature { + featureName = FEATURE_NFT_LAZY_MINTABLE.name; + private contractWrapper: ContractWrapper; + private storage: IStorage; + private erc721: Erc721; + + public batch: Erc721BatchMintable | undefined; + + constructor( + erc721: Erc721, + contractWrapper: ContractWrapper, + storage: IStorage, + ) { + this.erc721 = erc721; + this.contractWrapper = contractWrapper; + this.storage = storage; + } + + /** + * Create a batch of unique NFTs to be claimed in the future + * + * @remarks Create batch allows you to create a batch of many unique NFTs in one transaction. + * + * @example + * ```javascript + * // Custom metadata of the NFTs to create + * const metadatas = [{ + * name: "Cool NFT", + * description: "This is a cool NFT", + * image: fs.readFileSync("path/to/image.png"), // This can be an image url or file + * }, { + * name: "Cool NFT", + * description: "This is a cool NFT", + * image: fs.readFileSync("path/to/image.png"), + * }]; + * + * const results = await contract.nft.lazy.mint(metadatas); // uploads and creates the NFTs on chain + * 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 mint( + metadatas: NFTMetadataInput[], + 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)), + startFileNumber.toNumber(), + this.contractWrapper.readContract.address, + await this.contractWrapper.getSigner()?.getAddress(), + options, + ); + const baseUri = batch.baseUri; + const receipt = await this.contractWrapper.sendTransaction("lazyMint", [ + batch.uris.length, + baseUri.endsWith("/") ? baseUri : `${baseUri}/`, + ethers.utils.toUtf8Bytes(""), + ]); + const event = this.contractWrapper.parseLogs( + "TokensLazyMinted", + receipt?.logs, + ); + const startingIndex = event[0].args.startTokenId; + const endingIndex = event[0].args.endTokenId; + const results = []; + for (let id = startingIndex; id.lte(endingIndex); id = id.add(1)) { + results.push({ + id, + receipt, + data: () => this.erc721.getTokenMetadata(id), + }); + } + return results; + } +} diff --git a/src/core/classes/erc-721.ts b/src/core/classes/erc-721.ts index 24902c589..2e290ed0a 100644 --- a/src/core/classes/erc-721.ts +++ b/src/core/classes/erc-721.ts @@ -15,6 +15,7 @@ import { DropERC721, IERC721Supply, IMintableERC721, + LazyMintERC721, Multiwrap, SignatureDrop, TokenERC721, @@ -24,6 +25,7 @@ import { Erc721Mintable } from "./erc-721-mintable"; import { BaseERC721 } from "../../types/eips"; import { FEATURE_NFT } from "../../constants/erc721-features"; import { DetectableFeature } from "../interfaces/DetectableFeature"; +import { Erc721LazyMintable } from "./erc-721-lazy-mintable"; /** * Standard ERC721 NFT functions @@ -51,6 +53,7 @@ export class Erc721< public query: Erc721Supply | undefined; public mint: Erc721Mintable | undefined; + public lazy: Erc721LazyMintable | undefined; constructor( contractWrapper: ContractWrapper, @@ -70,6 +73,7 @@ export class Erc721< } this.query = this.detectErc721Enumerable(); this.mint = this.detectErc721Mintable(); + this.lazy = this.detectErc721LazyMintable(); } /** @@ -225,9 +229,7 @@ export class Erc721< /** * @internal */ - protected async getTokenMetadata( - tokenId: BigNumberish, - ): Promise { + async getTokenMetadata(tokenId: BigNumberish): Promise { const tokenUri = await this.contractWrapper.readContract.tokenURI(tokenId); if (!tokenUri) { throw new NotFoundError(); @@ -259,4 +261,16 @@ export class Erc721< } return undefined; } + + private detectErc721LazyMintable(): Erc721LazyMintable | undefined { + if ( + detectContractFeature( + this.contractWrapper, + "ERC721LazyMintable", + ) + ) { + return new Erc721LazyMintable(this, this.contractWrapper, this.storage); + } + return undefined; + } } diff --git a/test/publisher.test.ts b/test/publisher.test.ts index f308ded30..9d18e5141 100644 --- a/test/publisher.test.ts +++ b/test/publisher.test.ts @@ -220,4 +220,24 @@ describe("Publishing", async () => { }); expect(tx).to.not.eq(undefined); }); + + it("Custom drop contract lazy mint", async () => { + const realSDK = new ThirdwebSDK(adminWallet); + const pub = await realSDK.getPublisher(); + const ipfsUri = "ipfs://QmQRVqUkM3DBXQjEbXARQPZbRcp6A6unkqBabSUDMSPK7v/0"; + const addr = await pub.deployContract(ipfsUri, []); + const c = await sdk.getContract(addr); + invariant(c.nft, "no nft detected"); + invariant(c.nft.query, "no query detected"); + invariant(c.nft.lazy, "no lazy detected"); + await c.nft.lazy.mint([ + { + name: "cool nft", + }, + { + name: "cool nft2", + }, + ]); + console.log(await c.nft.query.all()); + }); }); diff --git a/yarn.lock b/yarn.lock index 3ebbc17f8..723a7a4df 100644 --- a/yarn.lock +++ b/yarn.lock @@ -815,10 +815,8 @@ "@swc/core-win32-ia32-msvc" "1.2.197" "@swc/core-win32-x64-msvc" "1.2.197" -"@thirdweb-dev/contracts@2.3.9": - version "2.3.9" - resolved "https://registry.yarnpkg.com/@thirdweb-dev/contracts/-/contracts-2.3.9.tgz#1493c7b12133b3a23733eb9bfa1355ad68954d6f" - integrity sha512-MmOVqWKdlFsZRk/Ym/fFfrCo/mzJyWL+EWsLYjpa3CUvwt6DffED+LB8pfTo8DkoTkC0Y9J4yzsdbJSgoa0tFw== +"@thirdweb-dev/contracts@file:.yalc/@thirdweb-dev/contracts": + version "2.3.11" "@tsconfig/node10@^1.0.7": version "1.0.8" From 8d169303f7cf53f3726e044f812ee981d8670804 Mon Sep 17 00:00:00 2001 From: Joaquim Verges Date: Tue, 7 Jun 2022 10:01:18 -0700 Subject: [PATCH 2/5] contracts dev build --- package.json | 2 +- test/publisher.test.ts | 4 ++-- yarn.lock | 6 ++++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 2bbdd8291..54c2d0235 100644 --- a/package.json +++ b/package.json @@ -85,7 +85,7 @@ "ethers": "^5" }, "dependencies": { - "@thirdweb-dev/contracts": "2.3.9", + "@thirdweb-dev/contracts": "2.3.12-0", "@web-std/file": "^3.0.0", "cbor": "^8.1.0", "cross-fetch": "^3.1.5", diff --git a/test/publisher.test.ts b/test/publisher.test.ts index 9d18e5141..f852ec4d4 100644 --- a/test/publisher.test.ts +++ b/test/publisher.test.ts @@ -230,7 +230,7 @@ describe("Publishing", async () => { invariant(c.nft, "no nft detected"); invariant(c.nft.query, "no query detected"); invariant(c.nft.lazy, "no lazy detected"); - await c.nft.lazy.mint([ + const tx = await c.nft.lazy.mint([ { name: "cool nft", }, @@ -238,6 +238,6 @@ describe("Publishing", async () => { name: "cool nft2", }, ]); - console.log(await c.nft.query.all()); + expect(tx).to.not.eq(undefined); }); }); diff --git a/yarn.lock b/yarn.lock index 723a7a4df..391f6d93d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -815,8 +815,10 @@ "@swc/core-win32-ia32-msvc" "1.2.197" "@swc/core-win32-x64-msvc" "1.2.197" -"@thirdweb-dev/contracts@file:.yalc/@thirdweb-dev/contracts": - version "2.3.11" +"@thirdweb-dev/contracts@2.3.12-0": + version "2.3.12-0" + resolved "https://registry.yarnpkg.com/@thirdweb-dev/contracts/-/contracts-2.3.12-0.tgz#da54146b2a2e625bafa04a7333d0f38e22d975cb" + integrity sha512-Rfkz9FF4AR9349P6vwffMlRFBEiKNat7HbeJLndb1XBE2WqLU8XmgdPBw/hCz2sxGiGbrfToB7Ex6i3lG69okQ== "@tsconfig/node10@^1.0.7": version "1.0.8" From f4063c474b587667c7ada064c3ee83a3140a5567 Mon Sep 17 00:00:00 2001 From: Joaquim Verges Date: Tue, 7 Jun 2022 10:44:47 -0700 Subject: [PATCH 3/5] add test for sigdrop detection --- test/custom.test.ts | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/test/custom.test.ts b/test/custom.test.ts index ff5383ad1..184954f25 100644 --- a/test/custom.test.ts +++ b/test/custom.test.ts @@ -2,6 +2,8 @@ import { signers } from "./before-setup"; import { expect } from "chai"; import invariant from "tiny-invariant"; import { + DropERC721__factory, + SignatureDrop__factory, TokenERC1155__factory, TokenERC20__factory, TokenERC721__factory, @@ -18,6 +20,7 @@ global.fetch = require("cross-fetch"); describe("Custom Contracts", async () => { let customContractAddress: string; let nftContractAddress: string; + let sigDropContractAddress: string; let tokenContractAddress: string; let editionContractAddress: string; let adminWallet: SignerWithAddress, @@ -71,6 +74,10 @@ describe("Custom Contracts", async () => { platform_fee_basis_points: 10, platform_fee_recipient: adminWallet.address, }); + sigDropContractAddress = await sdk.deployer.deploySignatureDrop({ + name: "sigdrop", + primary_sale_recipient: adminWallet.address, + }); }); it("should call raw ABI functions and read deployer address", async () => { @@ -224,6 +231,28 @@ describe("Custom Contracts", async () => { expect(nfts[0].metadata.name).to.eq("Custom NFT"); }); + it("should detect feature: erc721 lazy mint", async () => { + const c = await sdk.getContractFromAbi( + sigDropContractAddress, + SignatureDrop__factory.abi, + ); + invariant(c, "Contract undefined"); + invariant(c.nft, "ERC721 undefined"); + invariant(c.nft.query, "ERC721 query undefined"); + invariant(c.nft.lazy, "ERC721 lazy undefined"); + await c.nft.lazy.mint([ + { + name: "Custom NFT", + }, + { + name: "Another one", + }, + ]); + const nfts = await c.nft.query.all(); + expect(nfts.length).to.eq(2); + expect(nfts[0].metadata.name).to.eq("Custom NFT"); + }); + it("should detect feature: erc1155", async () => { const c = await sdk.getContractFromAbi( editionContractAddress, From 4009cf88a871a6a912bb2fb4c494c326ca04d483 Mon Sep 17 00:00:00 2001 From: Joaquim Verges Date: Tue, 7 Jun 2022 20:17:51 -0700 Subject: [PATCH 4/5] working get all after lazy mint in tests --- src/core/classes/ipfs-storage.ts | 3 ++- test/publisher.test.ts | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/core/classes/ipfs-storage.ts b/src/core/classes/ipfs-storage.ts index 602eb562c..670ae181c 100644 --- a/src/core/classes/ipfs-storage.ts +++ b/src/core/classes/ipfs-storage.ts @@ -202,8 +202,9 @@ export class IpfsStorage implements IStorage { * * @internal * - * @param metadata - The metadata to recursively process * @returns - The processed metadata with properties pointing at ipfs in place of `File | Buffer` + * @param metadatas + * @param options */ private async batchUploadProperties( metadatas: JsonObject[], diff --git a/test/publisher.test.ts b/test/publisher.test.ts index f852ec4d4..defd38e1e 100644 --- a/test/publisher.test.ts +++ b/test/publisher.test.ts @@ -224,7 +224,7 @@ describe("Publishing", async () => { it("Custom drop contract lazy mint", async () => { const realSDK = new ThirdwebSDK(adminWallet); const pub = await realSDK.getPublisher(); - const ipfsUri = "ipfs://QmQRVqUkM3DBXQjEbXARQPZbRcp6A6unkqBabSUDMSPK7v/0"; + const ipfsUri = "ipfs://QmfKR3MMsE8AtXnoDZPHj7Z9SdNkyDTVhHEd1D9cDHDn1o/0"; const addr = await pub.deployContract(ipfsUri, []); const c = await sdk.getContract(addr); invariant(c.nft, "no nft detected"); @@ -239,5 +239,7 @@ describe("Publishing", async () => { }, ]); expect(tx).to.not.eq(undefined); + const all = await c.nft.query.all(); + expect(all).length(2); }); }); From 27aa0bcf8fbc194225f026cb7d74e7a66bd40819 Mon Sep 17 00:00:00 2001 From: Joaquim Verges Date: Tue, 7 Jun 2022 20:58:08 -0700 Subject: [PATCH 5/5] update to latest contracts --- package.json | 2 +- src/common/claim-conditions.ts | 3 +++ src/common/snapshots.ts | 1 + src/core/classes/drop-claim-conditions.ts | 6 +----- yarn.lock | 8 ++++---- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 54c2d0235..97ee83edc 100644 --- a/package.json +++ b/package.json @@ -85,7 +85,7 @@ "ethers": "^5" }, "dependencies": { - "@thirdweb-dev/contracts": "2.3.12-0", + "@thirdweb-dev/contracts": "2.3.12", "@web-std/file": "^3.0.0", "cbor": "^8.1.0", "cross-fetch": "^3.1.5", diff --git a/src/common/claim-conditions.ts b/src/common/claim-conditions.ts index 29fd5d44c..0370b7856 100644 --- a/src/common/claim-conditions.ts +++ b/src/common/claim-conditions.ts @@ -227,6 +227,9 @@ export async function getClaimerProofs( /** * Create and uploads snapshots + converts claim conditions to contract format * @param claimConditionInputs + * @param tokenDecimals + * @param provider + * @param storage * @internal */ export async function processClaimConditionInputs( diff --git a/src/common/snapshots.ts b/src/common/snapshots.ts index 105d83556..570e6b9e4 100644 --- a/src/common/snapshots.ts +++ b/src/common/snapshots.ts @@ -14,6 +14,7 @@ import { BigNumber, BigNumberish, utils } from "ethers"; /** * Create a snapshot (merkle tree) from a list of addresses and uploads it to IPFS * @param snapshotInput - the list of addresses to hash + * @param tokenDecimals - the token decimals * @param storage - the storage to upload to * @returns the generated snapshot and URI * @internal diff --git a/src/core/classes/drop-claim-conditions.ts b/src/core/classes/drop-claim-conditions.ts index 022e08240..99e91d449 100644 --- a/src/core/classes/drop-claim-conditions.ts +++ b/src/core/classes/drop-claim-conditions.ts @@ -362,11 +362,7 @@ export class DropClaimConditions< this.isSignatureDrop(this.contractWrapper.readContract, contractType) ? this.contractWrapper.readContract.interface.encodeFunctionData( "setClaimConditions", - [ - sortedConditions, - resetClaimEligibilityForAll, - ethers.utils.toUtf8Bytes(""), - ], + [sortedConditions, resetClaimEligibilityForAll], ) : this.contractWrapper.readContract.interface.encodeFunctionData( "setClaimConditions", diff --git a/yarn.lock b/yarn.lock index 391f6d93d..ebdf64eab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -815,10 +815,10 @@ "@swc/core-win32-ia32-msvc" "1.2.197" "@swc/core-win32-x64-msvc" "1.2.197" -"@thirdweb-dev/contracts@2.3.12-0": - version "2.3.12-0" - resolved "https://registry.yarnpkg.com/@thirdweb-dev/contracts/-/contracts-2.3.12-0.tgz#da54146b2a2e625bafa04a7333d0f38e22d975cb" - integrity sha512-Rfkz9FF4AR9349P6vwffMlRFBEiKNat7HbeJLndb1XBE2WqLU8XmgdPBw/hCz2sxGiGbrfToB7Ex6i3lG69okQ== +"@thirdweb-dev/contracts@2.3.12": + version "2.3.12" + resolved "https://registry.yarnpkg.com/@thirdweb-dev/contracts/-/contracts-2.3.12.tgz#8f02f9cbb3e4909bb09963a5059946fc9ea92f18" + integrity sha512-FfAcCKz7cXwukhzbwl909rC2ASNYJpgLC7Ke1kn4b/YF9LJ3hWVRjO2X3Q5OkgyoDCFwU+tTp9sn+cmFEN88dA== "@tsconfig/node10@^1.0.7": version "1.0.8"