diff --git a/cache/solidity-files-cache.json b/cache/solidity-files-cache.json new file mode 100644 index 000000000..cc85d8d51 --- /dev/null +++ b/cache/solidity-files-cache.json @@ -0,0 +1,12 @@ +{ + "_format": "ethers-rs-sol-cache-3", + "paths": { + "artifacts": "out", + "sources": "src", + "tests": "test", + "libraries": [ + "lib" + ] + }, + "files": {} +} \ No newline at end of file diff --git a/docs/sdk.all_roles.md b/docs/sdk.all_roles.md index a1b90d774..1e2c343b4 100644 --- a/docs/sdk.all_roles.md +++ b/docs/sdk.all_roles.md @@ -8,5 +8,5 @@ Signature: ```typescript -ALL_ROLES: ("transfer" | "lister" | "minter" | "admin" | "pauser" | "editor" | "asset")[] +ALL_ROLES: ("transfer" | "lister" | "admin" | "minter" | "pauser" | "editor" | "asset")[] ``` diff --git a/docs/sdk.contractdeployer.deploysignaturedrop.md b/docs/sdk.contractdeployer.deploysignaturedrop.md new file mode 100644 index 000000000..fa1335823 --- /dev/null +++ b/docs/sdk.contractdeployer.deploysignaturedrop.md @@ -0,0 +1,26 @@ + + +[Home](./index.md) > [@thirdweb-dev/sdk](./sdk.md) > [ContractDeployer](./sdk.contractdeployer.md) > [deploySignatureDrop](./sdk.contractdeployer.deploysignaturedrop.md) + +## ContractDeployer.deploySignatureDrop() method + +Deploys a new SignatureDrop contract + +Signature: + +```typescript +deploySignatureDrop(metadata: NFTContractDeployMetadata): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| metadata | [NFTContractDeployMetadata](./sdk.nftcontractdeploymetadata.md) | the contract metadata | + +Returns: + +Promise<string> + +the address of the deployed contract + diff --git a/docs/sdk.contractdeployer.md b/docs/sdk.contractdeployer.md index f234f5f3e..e790c1c7d 100644 --- a/docs/sdk.contractdeployer.md +++ b/docs/sdk.contractdeployer.md @@ -29,6 +29,7 @@ export declare class ContractDeployer extends RPCConnectionHandler | [deployNFTCollection(metadata)](./sdk.contractdeployer.deploynftcollection.md) | | Deploys a new NFTCollection contract | | [deployNFTDrop(metadata)](./sdk.contractdeployer.deploynftdrop.md) | | Deploys a new NFTDrop contract | | [deployPack(metadata)](./sdk.contractdeployer.deploypack.md) | | Deploys a new Pack contract | +| [deploySignatureDrop(metadata)](./sdk.contractdeployer.deploysignaturedrop.md) | | Deploys a new SignatureDrop contract | | [deploySplit(metadata)](./sdk.contractdeployer.deploysplit.md) | | Deploys a new Split contract | | [deployToken(metadata)](./sdk.contractdeployer.deploytoken.md) | | Deploys a new Token contract | | [deployTokenDrop(metadata)](./sdk.contractdeployer.deploytokendrop.md) | | Deploys a new Token Drop contract | diff --git a/docs/sdk.contractmetadata.set.md b/docs/sdk.contractmetadata.set.md index 0317d3253..3d89736ae 100644 --- a/docs/sdk.contractmetadata.set.md +++ b/docs/sdk.contractmetadata.set.md @@ -7,13 +7,13 @@ Signature: ```typescript -set(metadata: z.input): Promise<((() => A extends never ? 1 : 0) extends () => A_1 extends z.output ? 1 : 0 ? 1 : 0) extends infer T ? T extends ((() => A extends never ? 1 : 0) extends () => A_1 extends z.output ? 1 : 0 ? 1 : 0) ? T extends 1 ? Omit<{ +set(metadata: z.input): Promise<(() => A extends never ? 1 : 0 extends () => A_1 extends z.output ? 1 : 0 ? 1 : 0) extends 1 ? Omit<{ receipt: import("@ethersproject/abstract-provider").TransactionReceipt; data: () => Promise; }, "data"> : { receipt: import("@ethersproject/abstract-provider").TransactionReceipt; data: () => Promise>; - } : never : never>; + }>; ``` ## Parameters @@ -24,6 +24,6 @@ set(metadata: z.input): Promise<((() => A extends never ? 1 Returns: -Promise<((<A>() => A extends never ? 1 : 0) extends <A\_1>() => A\_1 extends z.output<TSchema\["output"\]> ? 1 : 0 ? 1 : 0) extends infer T ? T extends ((<A>() => A extends never ? 1 : 0) extends <A\_1>() => A\_1 extends z.output<TSchema\["output"\]> ? 1 : 0 ? 1 : 0) ? T extends 1 ? Omit<{ receipt: import("@ethersproject/abstract-provider").TransactionReceipt; data: () => Promise<unknown>; }, "data"> : { receipt: import("@ethersproject/abstract-provider").TransactionReceipt; data: () => Promise<z.output<TSchema\["output"\]>>; } : never : never> +Promise<(<A>() => A extends never ? 1 : 0 extends <A\_1>() => A\_1 extends z.output<TSchema\["output"\]> ? 1 : 0 ? 1 : 0) extends 1 ? Omit<{ receipt: import("@ethersproject/abstract-provider").TransactionReceipt; data: () => Promise<unknown>; }, "data"> : { receipt: import("@ethersproject/abstract-provider").TransactionReceipt; data: () => Promise<z.output<TSchema\["output"\]>>; }> diff --git a/docs/sdk.contractmetadata.update.md b/docs/sdk.contractmetadata.update.md index 767303418..19cd2f602 100644 --- a/docs/sdk.contractmetadata.update.md +++ b/docs/sdk.contractmetadata.update.md @@ -7,13 +7,13 @@ Signature: ```typescript -update(metadata: Partial>): Promise<((() => A extends never ? 1 : 0) extends () => A_1 extends z.output ? 1 : 0 ? 1 : 0) extends infer T ? T extends ((() => A extends never ? 1 : 0) extends () => A_1 extends z.output ? 1 : 0 ? 1 : 0) ? T extends 1 ? Omit<{ +update(metadata: Partial>): Promise<(() => A extends never ? 1 : 0 extends () => A_1 extends z.output ? 1 : 0 ? 1 : 0) extends 1 ? Omit<{ receipt: import("@ethersproject/abstract-provider").TransactionReceipt; data: () => Promise; }, "data"> : { receipt: import("@ethersproject/abstract-provider").TransactionReceipt; data: () => Promise>; - } : never : never>; + }>; ``` ## Parameters @@ -24,5 +24,5 @@ update(metadata: Partial>): Promise<((() => A exten Returns: -Promise<((<A>() => A extends never ? 1 : 0) extends <A\_1>() => A\_1 extends z.output<TSchema\["output"\]> ? 1 : 0 ? 1 : 0) extends infer T ? T extends ((<A>() => A extends never ? 1 : 0) extends <A\_1>() => A\_1 extends z.output<TSchema\["output"\]> ? 1 : 0 ? 1 : 0) ? T extends 1 ? Omit<{ receipt: import("@ethersproject/abstract-provider").TransactionReceipt; data: () => Promise<unknown>; }, "data"> : { receipt: import("@ethersproject/abstract-provider").TransactionReceipt; data: () => Promise<z.output<TSchema\["output"\]>>; } : never : never> +Promise<(<A>() => A extends never ? 1 : 0 extends <A\_1>() => A\_1 extends z.output<TSchema\["output"\]> ? 1 : 0 ? 1 : 0) extends 1 ? Omit<{ receipt: import("@ethersproject/abstract-provider").TransactionReceipt; data: () => Promise<unknown>; }, "data"> : { receipt: import("@ethersproject/abstract-provider").TransactionReceipt; data: () => Promise<z.output<TSchema\["output"\]>>; }> diff --git a/docs/sdk.delayedreveal.md b/docs/sdk.delayedreveal.md index 4026cfd15..c2f6bfa2c 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 diff --git a/docs/sdk.dropclaimconditions.md b/docs/sdk.dropclaimconditions.md index 5deee5ad3..c36469e81 100644 --- a/docs/sdk.dropclaimconditions.md +++ b/docs/sdk.dropclaimconditions.md @@ -9,7 +9,7 @@ Manages claim conditions for NFT Drop contracts Signature: ```typescript -export declare class DropClaimConditions +export declare class DropClaimConditions ``` ## Constructors diff --git a/docs/sdk.erc721.md b/docs/sdk.erc721.md index f03d3edff..96ae5a03b 100644 --- a/docs/sdk.erc721.md +++ b/docs/sdk.erc721.md @@ -9,7 +9,7 @@ Standard ERC721 NFT functions Signature: ```typescript -export declare class Erc721 implements UpdateableNetwork, DetectableFeature +export declare class Erc721 implements UpdateableNetwork, DetectableFeature ``` Implements: UpdateableNetwork, DetectableFeature diff --git a/docs/sdk.filledsignature721withquantity.md b/docs/sdk.filledsignature721withquantity.md new file mode 100644 index 000000000..e0a2bdc35 --- /dev/null +++ b/docs/sdk.filledsignature721withquantity.md @@ -0,0 +1,12 @@ + + +[Home](./index.md) > [@thirdweb-dev/sdk](./sdk.md) > [FilledSignature721WithQuantity](./sdk.filledsignature721withquantity.md) + +## FilledSignature721WithQuantity type + + +Signature: + +```typescript +export declare type FilledSignature721WithQuantity = z.output; +``` diff --git a/docs/sdk.md b/docs/sdk.md index af3606a15..dac5be6c4 100644 --- a/docs/sdk.md +++ b/docs/sdk.md @@ -45,6 +45,7 @@ | [NFTCollection](./sdk.nftcollection.md) | Create a collection of one-of-one NFTs. | | [NFTDrop](./sdk.nftdrop.md) | Setup a collection of one-of-one NFTs that are minted as users claim them. | | [Pack](./sdk.pack.md) | Create lootboxes of NFTs with rarity based open mechanics. | +| [SignatureDrop](./sdk.signaturedrop.md) | Setup a collection of NFTs where when it comes to minting, you can authorize some external party to mint tokens on your contract, and specify what exactly will be minted by that external party.. | | [SmartContract](./sdk.smartcontract.md) | (BETA) Custom contract dynamic class with feature detection | | [Split](./sdk.split.md) | Create custom royalty splits to distribute funds. | | [ThirdwebSDK](./sdk.thirdwebsdk.md) | The main entry point for the thirdweb SDK | @@ -114,6 +115,7 @@ | [MintRequest1155](./sdk.mintrequest1155.md) | | | [MintRequest20](./sdk.mintrequest20.md) | | | [MintRequest721](./sdk.mintrequest721.md) | | +| [MintRequest721withQuantity](./sdk.mintrequest721withquantity.md) | | | [NATIVE\_TOKEN\_ADDRESS](./sdk.native_token_address.md) | | | [NATIVE\_TOKENS](./sdk.native_tokens.md) | | | [SDKOptionsSchema](./sdk.sdkoptionsschema.md) | | @@ -139,6 +141,7 @@ | [EditionMetadataOwner](./sdk.editionmetadataowner.md) | | | [FileOrBuffer](./sdk.fileorbuffer.md) | | | [FilledConditionInput](./sdk.filledconditioninput.md) | | +| [FilledSignature721WithQuantity](./sdk.filledsignature721withquantity.md) | | | [FilledSignaturePayload1155](./sdk.filledsignaturepayload1155.md) | | | [FilledSignaturePayload20](./sdk.filledsignaturepayload20.md) | | | [FilledSignaturePayload721](./sdk.filledsignaturepayload721.md) | | @@ -152,9 +155,11 @@ | [PayloadToSign1155](./sdk.payloadtosign1155.md) | | | [PayloadToSign20](./sdk.payloadtosign20.md) | | | [PayloadToSign721](./sdk.payloadtosign721.md) | | +| [PayloadToSign721withQuantity](./sdk.payloadtosign721withquantity.md) | | | [PayloadWithUri1155](./sdk.payloadwithuri1155.md) | | | [PayloadWithUri20](./sdk.payloadwithuri20.md) | | | [PayloadWithUri721](./sdk.payloadwithuri721.md) | | +| [PayloadWithUri721withQuantity](./sdk.payloadwithuri721withquantity.md) | | | [PermitRequestMessage](./sdk.permitrequestmessage.md) | EIP-2612 token permit message for gasless transaction | | [Price](./sdk.price.md) | Represents a currency price already formatted. ie. "1" for 1 ether. | | [Role](./sdk.role.md) | | @@ -162,6 +167,7 @@ | [SignedPayload1155](./sdk.signedpayload1155.md) | | | [SignedPayload20](./sdk.signedpayload20.md) | | | [SignedPayload721](./sdk.signedpayload721.md) | | +| [SignedPayload721WithQuantitySignature](./sdk.signedpayload721withquantitysignature.md) | | | [SignerOrProvider](./sdk.signerorprovider.md) | | | [Snapshot](./sdk.snapshot.md) | | | [SnapshotInfo](./sdk.snapshotinfo.md) | | diff --git a/docs/sdk.mintrequest721withquantity.md b/docs/sdk.mintrequest721withquantity.md new file mode 100644 index 000000000..97ae8a4bd --- /dev/null +++ b/docs/sdk.mintrequest721withquantity.md @@ -0,0 +1,14 @@ + + +[Home](./index.md) > [@thirdweb-dev/sdk](./sdk.md) > [MintRequest721withQuantity](./sdk.mintrequest721withquantity.md) + +## MintRequest721withQuantity variable + +Signature: + +```typescript +MintRequest721withQuantity: { + name: string; + type: string; +}[] +``` diff --git a/docs/sdk.payloadtosign721withquantity.md b/docs/sdk.payloadtosign721withquantity.md new file mode 100644 index 000000000..275755254 --- /dev/null +++ b/docs/sdk.payloadtosign721withquantity.md @@ -0,0 +1,12 @@ + + +[Home](./index.md) > [@thirdweb-dev/sdk](./sdk.md) > [PayloadToSign721withQuantity](./sdk.payloadtosign721withquantity.md) + +## PayloadToSign721withQuantity type + + +Signature: + +```typescript +export declare type PayloadToSign721withQuantity = z.input; +``` diff --git a/docs/sdk.payloadwithuri721withquantity.md b/docs/sdk.payloadwithuri721withquantity.md new file mode 100644 index 000000000..6548bee9d --- /dev/null +++ b/docs/sdk.payloadwithuri721withquantity.md @@ -0,0 +1,12 @@ + + +[Home](./index.md) > [@thirdweb-dev/sdk](./sdk.md) > [PayloadWithUri721withQuantity](./sdk.payloadwithuri721withquantity.md) + +## PayloadWithUri721withQuantity type + + +Signature: + +```typescript +export declare type PayloadWithUri721withQuantity = z.output; +``` diff --git a/docs/sdk.signaturedrop._constructor_.md b/docs/sdk.signaturedrop._constructor_.md new file mode 100644 index 000000000..dabfeffa3 --- /dev/null +++ b/docs/sdk.signaturedrop._constructor_.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@thirdweb-dev/sdk](./sdk.md) > [SignatureDrop](./sdk.signaturedrop.md) > [(constructor)](./sdk.signaturedrop._constructor_.md) + +## SignatureDrop.(constructor) + +Constructs a new instance of the `SignatureDrop` class + +Signature: + +```typescript +constructor(network: NetworkOrSignerOrProvider, address: string, storage: IStorage, options?: SDKOptions, contractWrapper?: ContractWrapper); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| network | [NetworkOrSignerOrProvider](./sdk.networkorsignerorprovider.md) | | +| address | string | | +| storage | [IStorage](./sdk.istorage.md) | | +| options | [SDKOptions](./sdk.sdkoptions.md) | (Optional) | +| contractWrapper | ContractWrapper<SignatureDropContract> | (Optional) | + diff --git a/docs/sdk.signaturedrop.analytics.md b/docs/sdk.signaturedrop.analytics.md new file mode 100644 index 000000000..5d6ab9083 --- /dev/null +++ b/docs/sdk.signaturedrop.analytics.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@thirdweb-dev/sdk](./sdk.md) > [SignatureDrop](./sdk.signaturedrop.md) > [analytics](./sdk.signaturedrop.analytics.md) + +## SignatureDrop.analytics property + +Signature: + +```typescript +analytics: ContractAnalytics; +``` diff --git a/docs/sdk.signaturedrop.burn.md b/docs/sdk.signaturedrop.burn.md new file mode 100644 index 000000000..395f2b483 --- /dev/null +++ b/docs/sdk.signaturedrop.burn.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@thirdweb-dev/sdk](./sdk.md) > [SignatureDrop](./sdk.signaturedrop.md) > [burn](./sdk.signaturedrop.burn.md) + +## SignatureDrop.burn() method + +Burn a single NFT + +Signature: + +```typescript +burn(tokenId: BigNumberish): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| tokenId | BigNumberish | the token Id to burn | + +Returns: + +Promise<[TransactionResult](./sdk.transactionresult.md)> + diff --git a/docs/sdk.signaturedrop.claim.md b/docs/sdk.signaturedrop.claim.md new file mode 100644 index 000000000..7dc624d0d --- /dev/null +++ b/docs/sdk.signaturedrop.claim.md @@ -0,0 +1,31 @@ + + +[Home](./index.md) > [@thirdweb-dev/sdk](./sdk.md) > [SignatureDrop](./sdk.signaturedrop.md) > [claim](./sdk.signaturedrop.claim.md) + +## SignatureDrop.claim() method + +Claim NFTs to the connected wallet. + +Signature: + +```typescript +claim(quantity: BigNumberish, proofs?: BytesLike[]): Promise[]>; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| quantity | BigNumberish | | +| proofs | BytesLike\[\] | (Optional) | + +Returns: + +Promise<[TransactionResultWithId](./sdk.transactionresultwithid.md)<[NFTMetadataOwner](./sdk.nftmetadataowner.md)>\[\]> + +- an array of results containing the id of the token claimed, the transaction receipt and a promise to optionally fetch the nft metadata + +## Remarks + +See [NFTDrop.claimTo()](./sdk.nftdrop.claimto.md) + diff --git a/docs/sdk.signaturedrop.claimconditions.md b/docs/sdk.signaturedrop.claimconditions.md new file mode 100644 index 000000000..85fc08416 --- /dev/null +++ b/docs/sdk.signaturedrop.claimconditions.md @@ -0,0 +1,39 @@ + + +[Home](./index.md) > [@thirdweb-dev/sdk](./sdk.md) > [SignatureDrop](./sdk.signaturedrop.md) > [claimConditions](./sdk.signaturedrop.claimconditions.md) + +## SignatureDrop.claimConditions property + +Configure claim conditions + +Signature: + +```typescript +claimConditions: DropClaimConditions; +``` + +## Remarks + +Define who can claim NFTs in the collection, when and how many. + +## Example + + +```javascript +const presaleStartTime = new Date(); +const publicSaleStartTime = new Date(Date.now() + 60 * 60 * 24 * 1000); +const claimConditions = [ + { + startTime: presaleStartTime, // start the presale now + maxQuantity: 2, // limit how many mints for this presale + price: 0.01, // presale price + snapshot: ['0x...', '0x...'], // limit minting to only certain addresses + }, + { + startTime: publicSaleStartTime, // 24h after presale, start public sale + price: 0.08, // public sale price + } +]); +await contract.claimConditions.set(claimConditions); +``` + diff --git a/docs/sdk.signaturedrop.claimto.md b/docs/sdk.signaturedrop.claimto.md new file mode 100644 index 000000000..f045e3adc --- /dev/null +++ b/docs/sdk.signaturedrop.claimto.md @@ -0,0 +1,45 @@ + + +[Home](./index.md) > [@thirdweb-dev/sdk](./sdk.md) > [SignatureDrop](./sdk.signaturedrop.md) > [claimTo](./sdk.signaturedrop.claimto.md) + +## SignatureDrop.claimTo() method + +Claim unique NFTs to a specific Wallet + +Signature: + +```typescript +claimTo(destinationAddress: string, quantity: BigNumberish, proofs?: BytesLike[]): Promise[]>; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| destinationAddress | string | Address you want to send the token to | +| quantity | BigNumberish | Quantity of the tokens you want to claim | +| proofs | BytesLike\[\] | (Optional) Array of proofs | + +Returns: + +Promise<[TransactionResultWithId](./sdk.transactionresultwithid.md)<[NFTMetadataOwner](./sdk.nftmetadataowner.md)>\[\]> + +- an array of results containing the id of the token claimed, the transaction receipt and a promise to optionally fetch the nft metadata + +## Remarks + +Let the specified wallet claim NFTs. + +## Example + + +```javascript +const address = "{{wallet_address}}"; // address of the wallet you want to claim the NFTs +const quantity = 1; // how many unique NFTs you want to claim + +const tx = await contract.claimTo(address, quantity); +const receipt = tx.receipt; // the transaction receipt +const claimedTokenId = tx.id; // the id of the NFT claimed +const claimedNFT = await tx.data(); // (optional) get the claimed NFT metadata +``` + diff --git a/docs/sdk.signaturedrop.contractabi.md b/docs/sdk.signaturedrop.contractabi.md new file mode 100644 index 000000000..7d3a500e5 --- /dev/null +++ b/docs/sdk.signaturedrop.contractabi.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@thirdweb-dev/sdk](./sdk.md) > [SignatureDrop](./sdk.signaturedrop.md) > [contractAbi](./sdk.signaturedrop.contractabi.md) + +## SignatureDrop.contractAbi property + +Signature: + +```typescript +static contractAbi: any; +``` diff --git a/docs/sdk.signaturedrop.contractroles.md b/docs/sdk.signaturedrop.contractroles.md new file mode 100644 index 000000000..9834249bb --- /dev/null +++ b/docs/sdk.signaturedrop.contractroles.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@thirdweb-dev/sdk](./sdk.md) > [SignatureDrop](./sdk.signaturedrop.md) > [contractRoles](./sdk.signaturedrop.contractroles.md) + +## SignatureDrop.contractRoles property + +Signature: + +```typescript +static contractRoles: readonly ["admin", "minter", "transfer"]; +``` diff --git a/docs/sdk.signaturedrop.contracttype.md b/docs/sdk.signaturedrop.contracttype.md new file mode 100644 index 000000000..3e80d46d2 --- /dev/null +++ b/docs/sdk.signaturedrop.contracttype.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@thirdweb-dev/sdk](./sdk.md) > [SignatureDrop](./sdk.signaturedrop.md) > [contractType](./sdk.signaturedrop.contracttype.md) + +## SignatureDrop.contractType property + +Signature: + +```typescript +static contractType: "signature-drop"; +``` diff --git a/docs/sdk.signaturedrop.createbatch.md b/docs/sdk.signaturedrop.createbatch.md new file mode 100644 index 000000000..ac8689885 --- /dev/null +++ b/docs/sdk.signaturedrop.createbatch.md @@ -0,0 +1,48 @@ + + +[Home](./index.md) > [@thirdweb-dev/sdk](./sdk.md) > [SignatureDrop](./sdk.signaturedrop.md) > [createBatch](./sdk.signaturedrop.createbatch.md) + +## SignatureDrop.createBatch() method + +Create a batch of unique NFTs to be claimed in the future + +Signature: + +```typescript +createBatch(metadatas: NFTMetadataInput[]): Promise[]>; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| metadatas | [NFTMetadataInput](./sdk.nftmetadatainput.md)\[\] | The metadata to include in the batch. | + +Returns: + +Promise<[TransactionResultWithId](./sdk.transactionresultwithid.md)<[NFTMetadata](./sdk.nftmetadata.md)>\[\]> + +## 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.createBatch(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 +``` + diff --git a/docs/sdk.signaturedrop.encoder.md b/docs/sdk.signaturedrop.encoder.md new file mode 100644 index 000000000..56a50594a --- /dev/null +++ b/docs/sdk.signaturedrop.encoder.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@thirdweb-dev/sdk](./sdk.md) > [SignatureDrop](./sdk.signaturedrop.md) > [encoder](./sdk.signaturedrop.encoder.md) + +## SignatureDrop.encoder property + +Signature: + +```typescript +encoder: ContractEncoder; +``` diff --git a/docs/sdk.signaturedrop.estimator.md b/docs/sdk.signaturedrop.estimator.md new file mode 100644 index 000000000..c7b6e5416 --- /dev/null +++ b/docs/sdk.signaturedrop.estimator.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@thirdweb-dev/sdk](./sdk.md) > [SignatureDrop](./sdk.signaturedrop.md) > [estimator](./sdk.signaturedrop.estimator.md) + +## SignatureDrop.estimator property + +Signature: + +```typescript +estimator: GasCostEstimator; +``` diff --git a/docs/sdk.signaturedrop.events.md b/docs/sdk.signaturedrop.events.md new file mode 100644 index 000000000..277f7c255 --- /dev/null +++ b/docs/sdk.signaturedrop.events.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@thirdweb-dev/sdk](./sdk.md) > [SignatureDrop](./sdk.signaturedrop.md) > [events](./sdk.signaturedrop.events.md) + +## SignatureDrop.events property + +Signature: + +```typescript +events: ContractEvents; +``` diff --git a/docs/sdk.signaturedrop.getall.md b/docs/sdk.signaturedrop.getall.md new file mode 100644 index 000000000..452ce11eb --- /dev/null +++ b/docs/sdk.signaturedrop.getall.md @@ -0,0 +1,40 @@ + + +[Home](./index.md) > [@thirdweb-dev/sdk](./sdk.md) > [SignatureDrop](./sdk.signaturedrop.md) > [getAll](./sdk.signaturedrop.getall.md) + +## SignatureDrop.getAll() method + +Get All Minted NFTs + +Signature: + +```typescript +getAll(queryParams?: QueryAllParams): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| queryParams | [QueryAllParams](./sdk.queryallparams.md) | (Optional) optional filtering to only fetch a subset of results. | + +Returns: + +Promise<[NFTMetadataOwner](./sdk.nftmetadataowner.md)\[\]> + +The NFT metadata for all NFTs queried. + +## Remarks + +Get all the data associated with every NFT in this contract. + +By default, returns the first 100 NFTs, use queryParams to fetch more. + +## Example + + +```javascript +const nfts = await contract.getAll(); +console.log(nfts); +``` + diff --git a/docs/sdk.signaturedrop.getallclaimed.md b/docs/sdk.signaturedrop.getallclaimed.md new file mode 100644 index 000000000..dc6f73e5c --- /dev/null +++ b/docs/sdk.signaturedrop.getallclaimed.md @@ -0,0 +1,40 @@ + + +[Home](./index.md) > [@thirdweb-dev/sdk](./sdk.md) > [SignatureDrop](./sdk.signaturedrop.md) > [getAllClaimed](./sdk.signaturedrop.getallclaimed.md) + +## SignatureDrop.getAllClaimed() method + +Get All Claimed NFTs + +Signature: + +```typescript +getAllClaimed(queryParams?: QueryAllParams): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| queryParams | [QueryAllParams](./sdk.queryallparams.md) | (Optional) optional filtering to only fetch a subset of results. | + +Returns: + +Promise<[NFTMetadataOwner](./sdk.nftmetadataowner.md)\[\]> + +The NFT metadata and their ownersfor all NFTs queried. + +## Remarks + +Fetch all the NFTs (and their owners) that have been claimed in this Drop. + +\* + +## Example + + +```javascript +const claimedNFTs = await contract.getAllClaimed(); +const firstOwner = claimedNFTs[0].owner; +``` + diff --git a/docs/sdk.signaturedrop.getallunclaimed.md b/docs/sdk.signaturedrop.getallunclaimed.md new file mode 100644 index 000000000..cbe4aba60 --- /dev/null +++ b/docs/sdk.signaturedrop.getallunclaimed.md @@ -0,0 +1,40 @@ + + +[Home](./index.md) > [@thirdweb-dev/sdk](./sdk.md) > [SignatureDrop](./sdk.signaturedrop.md) > [getAllUnclaimed](./sdk.signaturedrop.getallunclaimed.md) + +## SignatureDrop.getAllUnclaimed() method + +Get All Unclaimed NFTs + +Signature: + +```typescript +getAllUnclaimed(queryParams?: QueryAllParams): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| queryParams | [QueryAllParams](./sdk.queryallparams.md) | (Optional) optional filtering to only fetch a subset of results. | + +Returns: + +Promise<[NFTMetadata](./sdk.nftmetadata.md)\[\]> + +The NFT metadata for all NFTs queried. + +## Remarks + +Fetch all the NFTs that have been not been claimed yet in this Drop. + +\* + +## Example + + +```javascript +const unclaimedNFTs = await contract.getAllUnclaimed(); +const firstUnclaimedNFT = unclaimedNFTs[0].name; +``` + diff --git a/docs/sdk.signaturedrop.getowned.md b/docs/sdk.signaturedrop.getowned.md new file mode 100644 index 000000000..02fbbc035 --- /dev/null +++ b/docs/sdk.signaturedrop.getowned.md @@ -0,0 +1,40 @@ + + +[Home](./index.md) > [@thirdweb-dev/sdk](./sdk.md) > [SignatureDrop](./sdk.signaturedrop.md) > [getOwned](./sdk.signaturedrop.getowned.md) + +## SignatureDrop.getOwned() method + +Get Owned NFTs + +Signature: + +```typescript +getOwned(walletAddress?: string): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| walletAddress | string | (Optional) the wallet address to query, defaults to the connected wallet | + +Returns: + +Promise<[NFTMetadataOwner](./sdk.nftmetadataowner.md)\[\]> + +The NFT metadata for all NFTs in the contract. + +## Remarks + +Get all the data associated with the NFTs owned by a specific wallet. + +## Example + + +```javascript +// Address of the wallet to get the NFTs of +const address = "{{wallet_address}}"; +const nfts = await contract.getOwned(address); +console.log(nfts); +``` + diff --git a/docs/sdk.signaturedrop.getownedtokenids.md b/docs/sdk.signaturedrop.getownedtokenids.md new file mode 100644 index 000000000..3bac5b56f --- /dev/null +++ b/docs/sdk.signaturedrop.getownedtokenids.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [@thirdweb-dev/sdk](./sdk.md) > [SignatureDrop](./sdk.signaturedrop.md) > [getOwnedTokenIds](./sdk.signaturedrop.getownedtokenids.md) + +## SignatureDrop.getOwnedTokenIds() method + + +Signature: + +```typescript +getOwnedTokenIds(walletAddress?: string): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| walletAddress | string | (Optional) | + +Returns: + +Promise<BigNumber\[\]> + diff --git a/docs/sdk.signaturedrop.istransferrestricted.md b/docs/sdk.signaturedrop.istransferrestricted.md new file mode 100644 index 000000000..5355d916e --- /dev/null +++ b/docs/sdk.signaturedrop.istransferrestricted.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [@thirdweb-dev/sdk](./sdk.md) > [SignatureDrop](./sdk.signaturedrop.md) > [isTransferRestricted](./sdk.signaturedrop.istransferrestricted.md) + +## SignatureDrop.isTransferRestricted() method + +Get whether users can transfer NFTs from this contract + +Signature: + +```typescript +isTransferRestricted(): Promise; +``` +Returns: + +Promise<boolean> + diff --git a/docs/sdk.signaturedrop.md b/docs/sdk.signaturedrop.md new file mode 100644 index 000000000..35091981b --- /dev/null +++ b/docs/sdk.signaturedrop.md @@ -0,0 +1,69 @@ + + +[Home](./index.md) > [@thirdweb-dev/sdk](./sdk.md) > [SignatureDrop](./sdk.signaturedrop.md) + +## SignatureDrop class + +Setup a collection of NFTs where when it comes to minting, you can authorize some external party to mint tokens on your contract, and specify what exactly will be minted by that external party.. + +Signature: + +```typescript +export declare class SignatureDrop extends Erc721 +``` +Extends: [Erc721](./sdk.erc721.md)<SignatureDropContract + +## Example + + +```javascript +import { ThirdwebSDK } from "@thirdweb-dev/sdk"; + +const sdk = new ThirdwebSDK("rinkeby"); +const contract = sdk.getSignatureDrop("{{contract_address}}"); +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(network, address, storage, options, contractWrapper)](./sdk.signaturedrop._constructor_.md) | | Constructs a new instance of the SignatureDrop class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [analytics](./sdk.signaturedrop.analytics.md) | | ContractAnalytics<SignatureDropContract> | | +| [claimConditions](./sdk.signaturedrop.claimconditions.md) | | [DropClaimConditions](./sdk.dropclaimconditions.md)<SignatureDropContract> | Configure claim conditions | +| [contractAbi](./sdk.signaturedrop.contractabi.md) | static | any | | +| [contractRoles](./sdk.signaturedrop.contractroles.md) | static | readonly \["admin", "minter", "transfer"\] | | +| [contractType](./sdk.signaturedrop.contracttype.md) | static | "signature-drop" | | +| [encoder](./sdk.signaturedrop.encoder.md) | | [ContractEncoder](./sdk.contractencoder.md)<SignatureDropContract> | | +| [estimator](./sdk.signaturedrop.estimator.md) | | [GasCostEstimator](./sdk.gascostestimator.md)<SignatureDropContract> | | +| [events](./sdk.signaturedrop.events.md) | | [ContractEvents](./sdk.contractevents.md)<SignatureDropContract> | | +| [metadata](./sdk.signaturedrop.metadata.md) | | [ContractMetadata](./sdk.contractmetadata.md)<SignatureDropContract, typeof SignatureDrop.schema> | | +| [platformFee](./sdk.signaturedrop.platformfee.md) | | [ContractPlatformFee](./sdk.contractplatformfee.md)<SignatureDropContract> | | +| [primarySale](./sdk.signaturedrop.primarysale.md) | | [ContractPrimarySale](./sdk.contractprimarysale.md)<SignatureDropContract> | | +| [revealer](./sdk.signaturedrop.revealer.md) | | [DelayedReveal](./sdk.delayedreveal.md)<SignatureDropContract> | Delayed reveal | +| [roles](./sdk.signaturedrop.roles.md) | | [ContractRoles](./sdk.contractroles.md)<SignatureDropContract, typeof [SignatureDrop.contractRoles](./sdk.signaturedrop.contractroles.md)\[number\]> | | +| [royalty](./sdk.signaturedrop.royalty.md) | | [ContractRoyalty](./sdk.contractroyalty.md)<SignatureDropContract, typeof SignatureDrop.schema> | Configure royalties | +| [signature](./sdk.signaturedrop.signature.md) | | Erc721WithQuantitySignatureMinting | | + +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [burn(tokenId)](./sdk.signaturedrop.burn.md) | | Burn a single NFT | +| [claim(quantity, proofs)](./sdk.signaturedrop.claim.md) | | Claim NFTs to the connected wallet. | +| [claimTo(destinationAddress, quantity, proofs)](./sdk.signaturedrop.claimto.md) | | Claim unique NFTs to a specific Wallet | +| [createBatch(metadatas)](./sdk.signaturedrop.createbatch.md) | | Create a batch of unique NFTs to be claimed in the future | +| [getAll(queryParams)](./sdk.signaturedrop.getall.md) | | Get All Minted NFTs | +| [getAllClaimed(queryParams)](./sdk.signaturedrop.getallclaimed.md) | | Get All Claimed NFTs | +| [getAllUnclaimed(queryParams)](./sdk.signaturedrop.getallunclaimed.md) | | Get All Unclaimed NFTs | +| [getOwned(walletAddress)](./sdk.signaturedrop.getowned.md) | | Get Owned NFTs | +| [getOwnedTokenIds(walletAddress)](./sdk.signaturedrop.getownedtokenids.md) | | | +| [isTransferRestricted()](./sdk.signaturedrop.istransferrestricted.md) | | Get whether users can transfer NFTs from this contract | +| [totalClaimedSupply()](./sdk.signaturedrop.totalclaimedsupply.md) | | Get the claimed supply | +| [totalSupply()](./sdk.signaturedrop.totalsupply.md) | | Get the total count NFTs in this drop contract, both claimed and unclaimed | +| [totalUnclaimedSupply()](./sdk.signaturedrop.totalunclaimedsupply.md) | | Get the unclaimed supply | + diff --git a/docs/sdk.signaturedrop.metadata.md b/docs/sdk.signaturedrop.metadata.md new file mode 100644 index 000000000..58046f6da --- /dev/null +++ b/docs/sdk.signaturedrop.metadata.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@thirdweb-dev/sdk](./sdk.md) > [SignatureDrop](./sdk.signaturedrop.md) > [metadata](./sdk.signaturedrop.metadata.md) + +## SignatureDrop.metadata property + +Signature: + +```typescript +metadata: ContractMetadata; +``` diff --git a/docs/sdk.signaturedrop.platformfee.md b/docs/sdk.signaturedrop.platformfee.md new file mode 100644 index 000000000..2d0fefa5d --- /dev/null +++ b/docs/sdk.signaturedrop.platformfee.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@thirdweb-dev/sdk](./sdk.md) > [SignatureDrop](./sdk.signaturedrop.md) > [platformFee](./sdk.signaturedrop.platformfee.md) + +## SignatureDrop.platformFee property + +Signature: + +```typescript +platformFee: ContractPlatformFee; +``` diff --git a/docs/sdk.signaturedrop.primarysale.md b/docs/sdk.signaturedrop.primarysale.md new file mode 100644 index 000000000..501be352f --- /dev/null +++ b/docs/sdk.signaturedrop.primarysale.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@thirdweb-dev/sdk](./sdk.md) > [SignatureDrop](./sdk.signaturedrop.md) > [primarySale](./sdk.signaturedrop.primarysale.md) + +## SignatureDrop.primarySale property + +Signature: + +```typescript +primarySale: ContractPrimarySale; +``` diff --git a/docs/sdk.signaturedrop.revealer.md b/docs/sdk.signaturedrop.revealer.md new file mode 100644 index 000000000..22ed058ed --- /dev/null +++ b/docs/sdk.signaturedrop.revealer.md @@ -0,0 +1,48 @@ + + +[Home](./index.md) > [@thirdweb-dev/sdk](./sdk.md) > [SignatureDrop](./sdk.signaturedrop.md) > [revealer](./sdk.signaturedrop.revealer.md) + +## SignatureDrop.revealer property + +Delayed reveal + +Signature: + +```typescript +revealer: DelayedReveal; +``` + +## Remarks + +Create a batch of encrypted NFTs that can be revealed at a later time. + +## Example + + +```javascript +// the real NFTs, these will be encrypted until you reveal them +const realNFTs = [{ + name: "Common NFT #1", + description: "Common NFT, one of many.", + image: fs.readFileSync("path/to/image.png"), +}, { + name: "Super Rare NFT #2", + description: "You got a Super Rare NFT!", + image: fs.readFileSync("path/to/image.png"), +}]; +// A placeholder NFT that people will get immediately in their wallet, and will be converted to the real NFT at reveal time +const placeholderNFT = { + name: "Hidden NFT", + description: "Will be revealed next week!" +}; +// Create and encrypt the NFTs +await contract.revealer.createDelayedRevealBatch( + placeholderNFT, + realNFTs, + "my secret password", +); +// Whenever you're ready, reveal your NFTs at any time +const batchId = 0; // the batch to reveal +await contract.revealer.reveal(batchId, "my secret password"); +``` + diff --git a/docs/sdk.signaturedrop.roles.md b/docs/sdk.signaturedrop.roles.md new file mode 100644 index 000000000..8305b1457 --- /dev/null +++ b/docs/sdk.signaturedrop.roles.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@thirdweb-dev/sdk](./sdk.md) > [SignatureDrop](./sdk.signaturedrop.md) > [roles](./sdk.signaturedrop.roles.md) + +## SignatureDrop.roles property + +Signature: + +```typescript +roles: ContractRoles; +``` diff --git a/docs/sdk.signaturedrop.royalty.md b/docs/sdk.signaturedrop.royalty.md new file mode 100644 index 000000000..6708d24fb --- /dev/null +++ b/docs/sdk.signaturedrop.royalty.md @@ -0,0 +1,34 @@ + + +[Home](./index.md) > [@thirdweb-dev/sdk](./sdk.md) > [SignatureDrop](./sdk.signaturedrop.md) > [royalty](./sdk.signaturedrop.royalty.md) + +## SignatureDrop.royalty property + +Configure royalties + +Signature: + +```typescript +royalty: ContractRoyalty; +``` + +## Remarks + +Set your own royalties for the entire contract or per token + +## Example + + +```javascript +// royalties on the whole contract +contract.royalty.setDefaultRoyaltyInfo({ + seller_fee_basis_points: 100, // 1% + fee_recipient: "0x..." +}); +// override royalty for a particular token +contract.royalty.setTokenRoyaltyInfo(tokenId, { + seller_fee_basis_points: 500, // 5% + fee_recipient: "0x..." +}); +``` + diff --git a/docs/sdk.signaturedrop.signature.md b/docs/sdk.signaturedrop.signature.md new file mode 100644 index 000000000..124eb079c --- /dev/null +++ b/docs/sdk.signaturedrop.signature.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@thirdweb-dev/sdk](./sdk.md) > [SignatureDrop](./sdk.signaturedrop.md) > [signature](./sdk.signaturedrop.signature.md) + +## SignatureDrop.signature property + +Signature: + +```typescript +signature: Erc721WithQuantitySignatureMinting; +``` diff --git a/docs/sdk.signaturedrop.totalclaimedsupply.md b/docs/sdk.signaturedrop.totalclaimedsupply.md new file mode 100644 index 000000000..92a9fb1e7 --- /dev/null +++ b/docs/sdk.signaturedrop.totalclaimedsupply.md @@ -0,0 +1,33 @@ + + +[Home](./index.md) > [@thirdweb-dev/sdk](./sdk.md) > [SignatureDrop](./sdk.signaturedrop.md) > [totalClaimedSupply](./sdk.signaturedrop.totalclaimedsupply.md) + +## SignatureDrop.totalClaimedSupply() method + +Get the claimed supply + +Signature: + +```typescript +totalClaimedSupply(): Promise; +``` +Returns: + +Promise<BigNumber> + +the claimed supply + +## Remarks + +Get the number of claimed NFTs in this Drop. + +\* + +## Example + + +```javascript +const claimedNFTCount = await contract.totalClaimedSupply(); +console.log(`NFTs claimed so far: ${claimedNFTCount}`); +``` + diff --git a/docs/sdk.signaturedrop.totalsupply.md b/docs/sdk.signaturedrop.totalsupply.md new file mode 100644 index 000000000..418323219 --- /dev/null +++ b/docs/sdk.signaturedrop.totalsupply.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [@thirdweb-dev/sdk](./sdk.md) > [SignatureDrop](./sdk.signaturedrop.md) > [totalSupply](./sdk.signaturedrop.totalsupply.md) + +## SignatureDrop.totalSupply() method + +Get the total count NFTs in this drop contract, both claimed and unclaimed + +Signature: + +```typescript +totalSupply(): Promise; +``` +Returns: + +Promise<BigNumber> + diff --git a/docs/sdk.signaturedrop.totalunclaimedsupply.md b/docs/sdk.signaturedrop.totalunclaimedsupply.md new file mode 100644 index 000000000..348ada80e --- /dev/null +++ b/docs/sdk.signaturedrop.totalunclaimedsupply.md @@ -0,0 +1,33 @@ + + +[Home](./index.md) > [@thirdweb-dev/sdk](./sdk.md) > [SignatureDrop](./sdk.signaturedrop.md) > [totalUnclaimedSupply](./sdk.signaturedrop.totalunclaimedsupply.md) + +## SignatureDrop.totalUnclaimedSupply() method + +Get the unclaimed supply + +Signature: + +```typescript +totalUnclaimedSupply(): Promise; +``` +Returns: + +Promise<BigNumber> + +the unclaimed supply + +## Remarks + +Get the number of unclaimed NFTs in this Drop. + +\* + +## Example + + +```javascript +const unclaimedNFTCount = await contract.totalUnclaimedSupply(); +console.log(`NFTs left to claim: ${unclaimedNFTCount}`); +``` + diff --git a/docs/sdk.signedpayload721withquantitysignature.md b/docs/sdk.signedpayload721withquantitysignature.md new file mode 100644 index 000000000..de7475592 --- /dev/null +++ b/docs/sdk.signedpayload721withquantitysignature.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [@thirdweb-dev/sdk](./sdk.md) > [SignedPayload721WithQuantitySignature](./sdk.signedpayload721withquantitysignature.md) + +## SignedPayload721WithQuantitySignature type + + +Signature: + +```typescript +export declare type SignedPayload721WithQuantitySignature = { + payload: PayloadWithUri721withQuantity; + signature: string; +}; +``` +References: [PayloadWithUri721withQuantity](./sdk.payloadwithuri721withquantity.md) + diff --git a/docs/sdk.thirdwebsdk.getcontractlist.md b/docs/sdk.thirdwebsdk.getcontractlist.md index 3e8c743ba..78c8c68b7 100644 --- a/docs/sdk.thirdwebsdk.getcontractlist.md +++ b/docs/sdk.thirdwebsdk.getcontractlist.md @@ -11,7 +11,7 @@ Return all the contracts deployed by the specified address ```typescript getContractList(walletAddress: string): Promise<{ address: string; - contractType: "split" | "custom" | "token" | "pack" | "edition" | "edition-drop" | "token-drop" | "vote" | "marketplace" | "nft-drop" | "nft-collection"; + contractType: "split" | "custom" | "token" | "pack" | "edition" | "edition-drop" | "token-drop" | "vote" | "marketplace" | "nft-drop" | "signature-drop" | "nft-collection"; metadata: () => Promise; }[]>; ``` @@ -24,5 +24,5 @@ getContractList(walletAddress: string): Promise<{ Returns: -Promise<{ address: string; contractType: "split" \| "custom" \| "token" \| "pack" \| "edition" \| "edition-drop" \| "token-drop" \| "vote" \| "marketplace" \| "nft-drop" \| "nft-collection"; metadata: () => Promise<any>; }\[\]> +Promise<{ address: string; contractType: "split" \| "custom" \| "token" \| "pack" \| "edition" \| "edition-drop" \| "token-drop" \| "vote" \| "marketplace" \| "nft-drop" \| "signature-drop" \| "nft-collection"; metadata: () => Promise<any>; }\[\]> diff --git a/docs/sdk.thirdwebsdk.getsignaturedrop.md b/docs/sdk.thirdwebsdk.getsignaturedrop.md new file mode 100644 index 000000000..f671659b8 --- /dev/null +++ b/docs/sdk.thirdwebsdk.getsignaturedrop.md @@ -0,0 +1,26 @@ + + +[Home](./index.md) > [@thirdweb-dev/sdk](./sdk.md) > [ThirdwebSDK](./sdk.thirdwebsdk.md) > [getSignatureDrop](./sdk.thirdwebsdk.getsignaturedrop.md) + +## ThirdwebSDK.getSignatureDrop() method + +Get an instance of a SignatureDrop contract + +Signature: + +```typescript +getSignatureDrop(contractAddress: string): SignatureDrop; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| contractAddress | string | the address of the deployed contract | + +Returns: + +[SignatureDrop](./sdk.signaturedrop.md) + +the contract + diff --git a/docs/sdk.thirdwebsdk.md b/docs/sdk.thirdwebsdk.md index 6a9044098..9ba59d348 100644 --- a/docs/sdk.thirdwebsdk.md +++ b/docs/sdk.thirdwebsdk.md @@ -42,6 +42,7 @@ export declare class ThirdwebSDK extends RPCConnectionHandler | [getNFTCollection(address)](./sdk.thirdwebsdk.getnftcollection.md) | | Get an instance of a NFT Collection contract | | [getNFTDrop(contractAddress)](./sdk.thirdwebsdk.getnftdrop.md) | | Get an instance of a Drop contract | | [getPack(address)](./sdk.thirdwebsdk.getpack.md) | | Get an instance of a Pack contract | +| [getSignatureDrop(contractAddress)](./sdk.thirdwebsdk.getsignaturedrop.md) | | Get an instance of a SignatureDrop contract | | [getSplit(address)](./sdk.thirdwebsdk.getsplit.md) | | Get an instance of a Splits contract | | [getToken(address)](./sdk.thirdwebsdk.gettoken.md) | | Get an instance of a Token contract | | [getTokenDrop(address)](./sdk.thirdwebsdk.gettokendrop.md) | | Get an instance of a Token Drop contract | diff --git a/docs/snippets.json b/docs/snippets.json index 8ec7d07f3..d32237fbd 100644 --- a/docs/snippets.json +++ b/docs/snippets.json @@ -626,6 +626,145 @@ ], "reference": "https://docs.thirdweb.com/typescript/sdk.Pack" }, + "SignatureDrop": { + "name": "SignatureDrop", + "summary": "Setup a collection of NFTs where when it comes to minting, you can authorize some external party to mint tokens on your contract, and specify what exactly will be minted by that external party..\n\n", + "remarks": null, + "examples": { + "javascript": "import { ThirdwebSDK } from \"@thirdweb-dev/sdk\";\n\nconst sdk = new ThirdwebSDK(\"rinkeby\");\nconst contract = sdk.getSignatureDrop(\"{{contract_address}}\");" + }, + "methods": [ + { + "name": "claimTo", + "summary": "Claim unique NFTs to a specific Wallet\n\n", + "remarks": "\n\nLet the specified wallet claim NFTs.\n\n", + "examples": { + "javascript": "const address = \"{{wallet_address}}\"; // address of the wallet you want to claim the NFTs\nconst quantity = 1; // how many unique NFTs you want to claim\n\nconst tx = await contract.claimTo(address, quantity);\nconst receipt = tx.receipt; // the transaction receipt\nconst claimedTokenId = tx.id; // the id of the NFT claimed\nconst claimedNFT = await tx.data(); // (optional) get the claimed NFT metadata" + }, + "reference": "https://docs.thirdweb.com/typescript/sdk.SignatureDrop.claimTo" + }, + { + "name": "createBatch", + "summary": "Create a batch of unique NFTs to be claimed in the future\n\n", + "remarks": "\n\nCreate batch allows you to create a batch of many unique NFTs in one transaction.\n\n", + "examples": { + "javascript": "// Custom metadata of the NFTs to create\nconst metadatas = [{\n name: \"Cool NFT\",\n description: \"This is a cool NFT\",\n image: fs.readFileSync(\"path/to/image.png\"), // This can be an image url or file\n}, {\n name: \"Cool NFT\",\n description: \"This is a cool NFT\",\n image: fs.readFileSync(\"path/to/image.png\"),\n}];\n\nconst results = await contract.createBatch(metadatas); // uploads and creates the NFTs on chain\nconst firstTokenId = results[0].id; // token id of the first created NFT\nconst firstNFT = await results[0].data(); // (optional) fetch details of the first created NFT" + }, + "reference": "https://docs.thirdweb.com/typescript/sdk.SignatureDrop.createBatch" + }, + { + "name": "getAll", + "summary": "Get All Minted NFTs\n\n", + "remarks": "\n\nGet all the data associated with every NFT in this contract.\n\nBy default, returns the first 100 NFTs, use queryParams to fetch more.\n\n", + "examples": { + "javascript": "const nfts = await contract.getAll();\nconsole.log(nfts);" + }, + "reference": "https://docs.thirdweb.com/typescript/sdk.SignatureDrop.getAll" + }, + { + "name": "getAllClaimed", + "summary": "Get All Claimed NFTs\n\n", + "remarks": "\n\nFetch all the NFTs (and their owners) that have been claimed in this Drop.\n\n*\n\n", + "examples": { + "javascript": "const claimedNFTs = await contract.getAllClaimed();\nconst firstOwner = claimedNFTs[0].owner;" + }, + "reference": "https://docs.thirdweb.com/typescript/sdk.SignatureDrop.getAllClaimed" + }, + { + "name": "getAllUnclaimed", + "summary": "Get All Unclaimed NFTs\n\n", + "remarks": "\n\nFetch all the NFTs that have been not been claimed yet in this Drop.\n\n*\n\n", + "examples": { + "javascript": "const unclaimedNFTs = await contract.getAllUnclaimed();\nconst firstUnclaimedNFT = unclaimedNFTs[0].name;" + }, + "reference": "https://docs.thirdweb.com/typescript/sdk.SignatureDrop.getAllUnclaimed" + }, + { + "name": "getOwned", + "summary": "Get Owned NFTs\n\n", + "remarks": "\n\nGet all the data associated with the NFTs owned by a specific wallet.\n\n", + "examples": { + "javascript": "// Address of the wallet to get the NFTs of\nconst address = \"{{wallet_address}}\";\nconst nfts = await contract.getOwned(address);\nconsole.log(nfts);" + }, + "reference": "https://docs.thirdweb.com/typescript/sdk.SignatureDrop.getOwned" + }, + { + "name": "totalClaimedSupply", + "summary": "Get the claimed supply\n\n", + "remarks": "\n\nGet the number of claimed NFTs in this Drop.\n\n*\n\n", + "examples": { + "javascript": "const claimedNFTCount = await contract.totalClaimedSupply();\nconsole.log(`NFTs claimed so far: ${claimedNFTCount}`);" + }, + "reference": "https://docs.thirdweb.com/typescript/sdk.SignatureDrop.totalClaimedSupply" + }, + { + "name": "totalUnclaimedSupply", + "summary": "Get the unclaimed supply\n\n", + "remarks": "\n\nGet the number of unclaimed NFTs in this Drop.\n\n*\n\n", + "examples": { + "javascript": "const unclaimedNFTCount = await contract.totalUnclaimedSupply();\nconsole.log(`NFTs left to claim: ${unclaimedNFTCount}`);" + }, + "reference": "https://docs.thirdweb.com/typescript/sdk.SignatureDrop.totalUnclaimedSupply" + }, + { + "name": "balanceOf", + "summary": "Get NFT Balance\n\n", + "remarks": "\n\nGet a wallets NFT balance (number of NFTs in this contract owned by the wallet).\n\n", + "examples": { + "javascript": "const walletAddress = \"{{wallet_address}}\";\nconst balance = await contract.nft.balanceOf(walletAddress);\nconsole.log(balance);" + }, + "reference": "https://docs.thirdweb.com/typescript/sdk.Erc721.balanceOf" + }, + { + "name": "get", + "summary": "Get a single NFT Metadata\n\n", + "remarks": null, + "examples": { + "javascript": "const tokenId = 0;\nconst nft = await contract.nft.get(tokenId);" + }, + "reference": "https://docs.thirdweb.com/typescript/sdk.Erc721.get" + }, + { + "name": "transfer", + "summary": "Transfer a single NFT\n\n", + "remarks": "\n\nTransfer an NFT from the connected wallet to another wallet.\n\n", + "examples": { + "javascript": "const walletAddress = \"{{wallet_address}}\";\nconst tokenId = 0;\nawait contract.nft.transfer(walletAddress, tokenId);" + }, + "reference": "https://docs.thirdweb.com/typescript/sdk.Erc721.transfer" + } + ], + "properties": [ + { + "name": "claimConditions", + "summary": "Configure claim conditions\n\n", + "remarks": "\n\nDefine who can claim NFTs in the collection, when and how many.\n\n", + "examples": { + "javascript": "const presaleStartTime = new Date();\nconst publicSaleStartTime = new Date(Date.now() + 60 * 60 * 24 * 1000);\nconst claimConditions = [\n {\n startTime: presaleStartTime, // start the presale now\n maxQuantity: 2, // limit how many mints for this presale\n price: 0.01, // presale price\n snapshot: ['0x...', '0x...'], // limit minting to only certain addresses\n },\n {\n startTime: publicSaleStartTime, // 24h after presale, start public sale\n price: 0.08, // public sale price\n }\n]);\nawait contract.claimConditions.set(claimConditions);" + }, + "reference": "https://docs.thirdweb.com/typescript/sdk.dropclaimconditions" + }, + { + "name": "revealer", + "summary": "Delayed reveal\n\n", + "remarks": "\n\nCreate a batch of encrypted NFTs that can be revealed at a later time.\n\n", + "examples": { + "javascript": "// the real NFTs, these will be encrypted until you reveal them\nconst realNFTs = [{\n name: \"Common NFT #1\",\n description: \"Common NFT, one of many.\",\n image: fs.readFileSync(\"path/to/image.png\"),\n}, {\n name: \"Super Rare NFT #2\",\n description: \"You got a Super Rare NFT!\",\n image: fs.readFileSync(\"path/to/image.png\"),\n}];\n// A placeholder NFT that people will get immediately in their wallet, and will be converted to the real NFT at reveal time\nconst placeholderNFT = {\n name: \"Hidden NFT\",\n description: \"Will be revealed next week!\"\n};\n// Create and encrypt the NFTs\nawait contract.revealer.createDelayedRevealBatch(\n placeholderNFT,\n realNFTs,\n \"my secret password\",\n);\n// Whenever you're ready, reveal your NFTs at any time\nconst batchId = 0; // the batch to reveal\nawait contract.revealer.reveal(batchId, \"my secret password\");" + }, + "reference": "https://docs.thirdweb.com/typescript/sdk.delayedreveal" + }, + { + "name": "royalty", + "summary": "Configure royalties\n\n", + "remarks": "\n\nSet your own royalties for the entire contract or per token\n\n", + "examples": { + "javascript": "// royalties on the whole contract\ncontract.royalty.setDefaultRoyaltyInfo({\n seller_fee_basis_points: 100, // 1%\n fee_recipient: \"0x...\"\n});\n// override royalty for a particular token\ncontract.royalty.setTokenRoyaltyInfo(tokenId, {\n seller_fee_basis_points: 500, // 5%\n fee_recipient: \"0x...\"\n});" + }, + "reference": "https://docs.thirdweb.com/typescript/sdk.contractroyalty" + } + ], + "reference": "https://docs.thirdweb.com/typescript/sdk.SignatureDrop" + }, "SmartContract": { "name": "SmartContract", "summary": "Custom contract dynamic class with feature detection\n\n", diff --git a/etc/sdk.api.md b/etc/sdk.api.md index 138040d99..793109b69 100644 --- a/etc/sdk.api.md +++ b/etc/sdk.api.md @@ -60,7 +60,7 @@ export class AdminRoleMissingError extends Error { export type AirdropInput = z.input; // @public (undocumented) -export const ALL_ROLES: ("transfer" | "lister" | "minter" | "admin" | "pauser" | "editor" | "asset")[]; +export const ALL_ROLES: ("transfer" | "lister" | "admin" | "minter" | "pauser" | "editor" | "asset")[]; // Warning: (ae-forgotten-export) The symbol "PriceSchema" needs to be exported by the entry point index.d.ts // @@ -560,6 +560,7 @@ export const CONTRACT_ADDRESSES: Record; // Warning: (ae-forgotten-export) The symbol "RPCConnectionHandler" needs to be exported by the entry point index.d.ts @@ -575,6 +576,7 @@ export class ContractDeployer extends RPCConnectionHandler { deployNFTCollection(metadata: NFTContractDeployMetadata): Promise; deployNFTDrop(metadata: NFTContractDeployMetadata): Promise; deployPack(metadata: NFTContractDeployMetadata): Promise; + deploySignatureDrop(metadata: NFTContractDeployMetadata): Promise; deploySplit(metadata: SplitContractDeployMetadata): Promise; deployToken(metadata: TokenContractDeployMetadata): Promise; deployTokenDrop(metadata: TokenContractDeployMetadata): Promise; @@ -636,21 +638,21 @@ export class ContractMetadata; // (undocumented) - set(metadata: z.input): Promise<((() => A extends never ? 1 : 0) extends () => A_1 extends z.output ? 1 : 0 ? 1 : 0) extends infer T ? T extends ((() => A extends never ? 1 : 0) extends () => A_1 extends z.output ? 1 : 0 ? 1 : 0) ? T extends 1 ? Omit<{ + set(metadata: z.input): Promise<(() => A extends never ? 1 : 0 extends () => A_1 extends z.output ? 1 : 0 ? 1 : 0) extends 1 ? Omit<{ receipt: TransactionReceipt; data: () => Promise; }, "data"> : { receipt: TransactionReceipt; data: () => Promise>; - } : never : never>; + }>; // (undocumented) - update(metadata: Partial>): Promise<((() => A extends never ? 1 : 0) extends () => A_1 extends z.output ? 1 : 0 ? 1 : 0) extends infer T ? T extends ((() => A extends never ? 1 : 0) extends () => A_1 extends z.output ? 1 : 0 ? 1 : 0) ? T extends 1 ? Omit<{ + update(metadata: Partial>): Promise<(() => A extends never ? 1 : 0 extends () => A_1 extends z.output ? 1 : 0 ? 1 : 0) extends 1 ? Omit<{ receipt: TransactionReceipt; data: () => Promise; }, "data"> : { receipt: TransactionReceipt; data: () => Promise>; - } : never : never>; + }>; } // Warning: (ae-forgotten-export) The symbol "IPlatformFee" needs to be exported by the entry point index.d.ts @@ -747,6 +749,7 @@ export class ContractRoyalty { +export class DelayedReveal { constructor(contractWrapper: ContractWrapper, storage: IStorage); createDelayedRevealBatch(placeholder: NFTMetadataInput, metadatas: NFTMetadataInput[], password: string): Promise; getBatchesToReveal(): Promise; @@ -878,7 +882,7 @@ export interface DirectListing { // Warning: (ae-forgotten-export) The symbol "DropERC20" needs to be exported by the entry point index.d.ts // // @public -export class DropClaimConditions { +export class DropClaimConditions { constructor(contractWrapper: ContractWrapper, metadata: ContractMetadata, storage: IStorage); canClaim(quantity: Amount, addressToCheck?: string): Promise; getActive(): Promise; @@ -1647,7 +1651,7 @@ export class Erc20SignatureMinting { // Warning: (ae-forgotten-export) The symbol "BaseERC721" needs to be exported by the entry point index.d.ts // // @public -export class Erc721 implements UpdateableNetwork, DetectableFeature { +export class Erc721 implements UpdateableNetwork, DetectableFeature { constructor(contractWrapper: ContractWrapper, storage: IStorage, options?: SDKOptions); balance(): Promise; balanceOf(address: string): Promise; @@ -1797,6 +1801,11 @@ export type FileOrBuffer = File | Buffer | BufferOrStringWithName; // @public (undocumented) export type FilledConditionInput = z.output; +// Warning: (ae-incompatible-release-tags) The symbol "FilledSignature721WithQuantity" is marked as @public, but its signature references "Signature721WithQuantityInput" which is marked as @internal +// +// @public (undocumented) +export type FilledSignature721WithQuantity = z.output; + // Warning: (ae-incompatible-release-tags) The symbol "FilledSignaturePayload1155" is marked as @public, but its signature references "Signature1155PayloadInput" which is marked as @internal // // @public (undocumented) @@ -1998,6 +2007,7 @@ export type JsonObject = { // @internal (undocumented) export const KNOWN_CONTRACTS_MAP: { readonly "nft-drop": typeof NFTDrop; + readonly "signature-drop": typeof SignatureDrop; readonly "nft-collection": typeof NFTCollection; readonly "edition-drop": typeof EditionDrop; readonly edition: typeof Edition; @@ -2052,7 +2062,7 @@ export class Marketplace implements UpdateableNetwork { // (undocumented) getAddress(): string; // @internal (undocumented) - getAll: (filter?: MarketplaceFilter) => Promise<(AuctionListing | DirectListing)[]>; + getAll: (filter?: MarketplaceFilter | undefined) => Promise<(AuctionListing | DirectListing)[]>; getAllListings(filter?: MarketplaceFilter): Promise<(AuctionListing | DirectListing)[]>; getBidBufferBps(): Promise; getListing(listingId: BigNumberish): Promise; @@ -2230,6 +2240,12 @@ export const MintRequest721: { type: string; }[]; +// @public (undocumented) +export const MintRequest721withQuantity: { + name: string; + type: string; +}[]; + // Warning: (ae-internal-missing-underscore) The name "MissingOwnerRoleError" should be prefixed with an underscore because the declaration is marked as @internal // // @internal (undocumented) @@ -2909,6 +2925,11 @@ export type PayloadToSign20 = z.input; // @public (undocumented) export type PayloadToSign721 = z.input; +// Warning: (ae-incompatible-release-tags) The symbol "PayloadToSign721withQuantity" is marked as @public, but its signature references "Signature721WithQuantityInput" which is marked as @internal +// +// @public (undocumented) +export type PayloadToSign721withQuantity = z.input; + // Warning: (ae-incompatible-release-tags) The symbol "PayloadWithUri1155" is marked as @public, but its signature references "Signature1155PayloadOutput" which is marked as @internal // // @public (undocumented) @@ -2924,6 +2945,11 @@ export type PayloadWithUri20 = z.output; // @public (undocumented) export type PayloadWithUri721 = z.output; +// Warning: (ae-incompatible-release-tags) The symbol "PayloadWithUri721withQuantity" is marked as @public, but its signature references "Signature721WithQuantityOutput" which is marked as @internal +// +// @public (undocumented) +export type PayloadWithUri721withQuantity = z.output; + // @public export type PermitRequestMessage = { to: string; @@ -3024,6 +3050,7 @@ export interface QueryAllParams { // @internal (undocumented) export const REMOTE_CONTRACT_NAME: { readonly "nft-drop": "DropERC721"; + readonly "signature-drop": "SignatureDrop"; readonly "nft-collection": "TokenERC721"; readonly "edition-drop": "DropERC1155"; readonly edition: "TokenERC1155"; @@ -3041,6 +3068,7 @@ export const REMOTE_CONTRACT_NAME: { // @internal (undocumented) export const REMOTE_CONTRACT_TO_CONTRACT_TYPE: { readonly DropERC721: "nft-drop"; + readonly SignatureDrop: "signature-drop"; readonly TokenERC721: "nft-collection"; readonly DropERC1155: "edition-drop"; readonly TokenERC1155: "edition"; @@ -3680,6 +3708,403 @@ export const Signature721PayloadOutput: z.ZodObject; +// Warning: (ae-internal-missing-underscore) The name "Signature721WithQuantityInput" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal (undocumented) +export const Signature721WithQuantityInput: z.ZodObject; + price: z.ZodDefault, string, string | number>>; + currencyAddress: z.ZodDefault; + mintStartTime: z.ZodDefault>; + mintEndTime: z.ZodDefault>; + uid: z.ZodEffects, string, string | undefined>; + primarySaleRecipient: z.ZodDefault; +}, { + metadata: z.ZodUnion<[z.ZodObject; + description: z.ZodOptional>; + image: z.ZodOptional>>; + external_url: z.ZodOptional>>; + }, { + animation_url: z.ZodOptional>; /** + * @internal + */ + background_color: z.ZodOptional, z.ZodString]>>; + properties: z.ZodOptional>, "many">, z.ZodRecord>]>>; + attributes: z.ZodOptional>, "many">, z.ZodRecord>]>>; + }>, "strip", z.ZodLazy>, { + [x: string]: Json; + name?: string | undefined; + description?: string | null | undefined; + image?: any; + external_url?: any; + animation_url?: any; + background_color?: string | undefined; + properties?: Record | Record[] | undefined; + attributes?: Record | Record[] | undefined; + }, { + [x: string]: Json; + name?: string | undefined; + description?: string | null | undefined; + image?: any; + external_url?: any; + animation_url?: any; + background_color?: string | undefined; + properties?: Record | Record[] | undefined; + attributes?: Record | Record[] | undefined; + }>, z.ZodString]>; + royaltyRecipient: z.ZodDefault; + royaltyBps: z.ZodDefault; +}>, { + metadata: z.ZodDefault; + description: z.ZodOptional>; + image: z.ZodOptional>>; + external_url: z.ZodOptional>>; + }, { + animation_url: z.ZodOptional>; /** + * @internal + */ + background_color: z.ZodOptional, z.ZodString]>>; + properties: z.ZodOptional>, "many">, z.ZodRecord>]>>; + attributes: z.ZodOptional>, "many">, z.ZodRecord>]>>; + }>, "strip", z.ZodLazy>, { + [x: string]: Json; + name?: string | undefined; + description?: string | null | undefined; + image?: any; + external_url?: any; + animation_url?: any; + background_color?: string | undefined; + properties?: Record | Record[] | undefined; + attributes?: Record | Record[] | undefined; + }, { + [x: string]: Json; + name?: string | undefined; + description?: string | null | undefined; + image?: any; + external_url?: any; + animation_url?: any; + background_color?: string | undefined; + properties?: Record | Record[] | undefined; + attributes?: Record | Record[] | undefined; + }>, z.ZodString]>>; + quantity: z.ZodEffects]>, ethers.BigNumber, string | number | bigint | ethers.BigNumber>, string, string | number | bigint | ethers.BigNumber>; +}>, "strip", z.ZodTypeAny, { + to: string; + primarySaleRecipient: string; + royaltyRecipient: string; + royaltyBps: number; + quantity: string; + uid: string; + price: string; + currencyAddress: string; + mintStartTime: ethers.BigNumber; + mintEndTime: ethers.BigNumber; + metadata: string | { + [x: string]: Json; + name?: string | undefined; + description?: string | null | undefined; + image?: any; + external_url?: any; + animation_url?: any; + background_color?: string | undefined; + properties?: Record | Record[] | undefined; + attributes?: Record | Record[] | undefined; + }; +}, { + to?: string | undefined; + primarySaleRecipient?: string | undefined; + royaltyRecipient?: string | undefined; + royaltyBps?: number | undefined; + uid?: string | undefined; + price?: string | number | undefined; + currencyAddress?: string | undefined; + mintStartTime?: Date | undefined; + mintEndTime?: Date | undefined; + metadata?: string | { + [x: string]: Json; + name?: string | undefined; + description?: string | null | undefined; + image?: any; + external_url?: any; + animation_url?: any; + background_color?: string | undefined; + properties?: Record | Record[] | undefined; + attributes?: Record | Record[] | undefined; + } | undefined; + quantity: string | number | bigint | ethers.BigNumber; +}>; + +// Warning: (ae-internal-missing-underscore) The name "Signature721WithQuantityOutput" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal (undocumented) +export const Signature721WithQuantityOutput: z.ZodObject; + price: z.ZodDefault, string, string | number>>; + currencyAddress: z.ZodDefault; + mintStartTime: z.ZodDefault>; + mintEndTime: z.ZodDefault>; + uid: z.ZodEffects, string, string | undefined>; + primarySaleRecipient: z.ZodDefault; +}, { + metadata: z.ZodUnion<[z.ZodObject; + description: z.ZodOptional>; + image: z.ZodOptional>>; + external_url: z.ZodOptional>>; + }, { + animation_url: z.ZodOptional>; /** + * @internal + */ + background_color: z.ZodOptional, z.ZodString]>>; + properties: z.ZodOptional>, "many">, z.ZodRecord>]>>; + attributes: z.ZodOptional>, "many">, z.ZodRecord>]>>; + }>, "strip", z.ZodLazy>, { + [x: string]: Json; + name?: string | undefined; + description?: string | null | undefined; + image?: any; + external_url?: any; + animation_url?: any; + background_color?: string | undefined; + properties?: Record | Record[] | undefined; + attributes?: Record | Record[] | undefined; + }, { + [x: string]: Json; + name?: string | undefined; + description?: string | null | undefined; + image?: any; + external_url?: any; + animation_url?: any; + background_color?: string | undefined; + properties?: Record | Record[] | undefined; + attributes?: Record | Record[] | undefined; + }>, z.ZodString]>; + royaltyRecipient: z.ZodDefault; + royaltyBps: z.ZodDefault; +}>, { + uri: z.ZodString; + royaltyBps: z.ZodEffects]>, ethers.BigNumber, string | number | bigint | ethers.BigNumber>; + mintStartTime: z.ZodEffects]>, ethers.BigNumber, string | number | bigint | ethers.BigNumber>; + mintEndTime: z.ZodEffects]>, ethers.BigNumber, string | number | bigint | ethers.BigNumber>; +}>, { + quantity: z.ZodEffects]>, ethers.BigNumber, string | number | bigint | ethers.BigNumber>; +}>, "strip", z.ZodTypeAny, { + to: string; + primarySaleRecipient: string; + uri: string; + royaltyRecipient: string; + royaltyBps: ethers.BigNumber; + quantity: ethers.BigNumber; + uid: string; + price: string; + currencyAddress: string; + mintStartTime: ethers.BigNumber; + mintEndTime: ethers.BigNumber; + metadata: string | { + [x: string]: Json; + name?: string | undefined; + description?: string | null | undefined; + image?: any; + external_url?: any; + animation_url?: any; + background_color?: string | undefined; + properties?: Record | Record[] | undefined; + attributes?: Record | Record[] | undefined; + }; +}, { + to?: string | undefined; + primarySaleRecipient?: string | undefined; + royaltyRecipient?: string | undefined; + uid?: string | undefined; + price?: string | number | undefined; + currencyAddress?: string | undefined; + uri: string; + royaltyBps: string | number | bigint | ethers.BigNumber; + quantity: string | number | bigint | ethers.BigNumber; + mintStartTime: string | number | bigint | ethers.BigNumber; + mintEndTime: string | number | bigint | ethers.BigNumber; + metadata: string | { + [x: string]: Json; + name?: string | undefined; + description?: string | null | undefined; + image?: any; + external_url?: any; + animation_url?: any; + background_color?: string | undefined; + properties?: Record | Record[] | undefined; + attributes?: Record | Record[] | undefined; + }; +}>; + +// @public +export class SignatureDrop extends Erc721 { + constructor(network: NetworkOrSignerOrProvider, address: string, storage: IStorage, options?: SDKOptions, contractWrapper?: ContractWrapper); + // (undocumented) + analytics: ContractAnalytics; + burn(tokenId: BigNumberish): Promise; + claim(quantity: BigNumberish, proofs?: BytesLike[]): Promise[]>; + claimConditions: DropClaimConditions; + claimTo(destinationAddress: string, quantity: BigNumberish, proofs?: BytesLike[]): Promise[]>; + // (undocumented) + static contractAbi: any; + // (undocumented) + static contractRoles: readonly ["admin", "minter", "transfer"]; + // (undocumented) + static contractType: "signature-drop"; + createBatch(metadatas: NFTMetadataInput[]): Promise[]>; + // (undocumented) + encoder: ContractEncoder; + // (undocumented) + estimator: GasCostEstimator; + // (undocumented) + events: ContractEvents; + getAll(queryParams?: QueryAllParams): Promise; + getAllClaimed(queryParams?: QueryAllParams): Promise; + getAllUnclaimed(queryParams?: QueryAllParams): Promise; + getOwned(walletAddress?: string): Promise; + // Warning: (ae-unresolved-inheritdoc-reference) The @inheritDoc reference could not be resolved: No member was found with name "tokendIds" + // + // (undocumented) + getOwnedTokenIds(walletAddress?: string): Promise; + // @internal (undocumented) + interceptor: ContractInterceptor; + isTransferRestricted(): Promise; + // (undocumented) + metadata: ContractMetadata; + // (undocumented) + platformFee: ContractPlatformFee; + // (undocumented) + primarySale: ContractPrimarySale; + revealer: DelayedReveal; + // (undocumented) + roles: ContractRoles; + royalty: ContractRoyalty; + // @internal (undocumented) + static schema: { + deploy: ZodObject; + image: ZodOptional>; + external_link: ZodOptional; + }, { + seller_fee_basis_points: ZodDefault; + fee_recipient: ZodDefault>; + }>, { + merkle: ZodDefault>; + }>, { + symbol: ZodDefault>; + }>, { + platform_fee_basis_points: ZodDefault; + platform_fee_recipient: ZodDefault>; + }>, { + primary_sale_recipient: ZodEffects; + }>, { + trusted_forwarders: ZodDefault, "many">>; + }>, "strip", ZodTypeAny, { + description?: string | undefined; + image?: any; + external_link?: string | undefined; + symbol: string; + name: string; + merkle: Record; + seller_fee_basis_points: number; + fee_recipient: string; + primary_sale_recipient: string; + platform_fee_basis_points: number; + platform_fee_recipient: string; + trusted_forwarders: string[]; + }, { + symbol?: string | undefined; + description?: string | undefined; + merkle?: Record | undefined; + image?: any; + external_link?: string | undefined; + seller_fee_basis_points?: number | undefined; + fee_recipient?: string | undefined; + platform_fee_basis_points?: number | undefined; + platform_fee_recipient?: string | undefined; + trusted_forwarders?: string[] | undefined; + name: string; + primary_sale_recipient: string; + }>; + output: ZodObject; + image: ZodOptional>; + external_link: ZodOptional; + }, { + image: ZodOptional; + }>, { + seller_fee_basis_points: ZodDefault; + fee_recipient: ZodDefault>; + }>, { + merkle: ZodDefault>; + }>, { + symbol: ZodDefault>; + }>, "strip", ZodLazy>, { + [x: string]: Json; + description?: string | undefined; + image?: string | undefined; + external_link?: string | undefined; + symbol: string; + name: string; + merkle: Record; + seller_fee_basis_points: number; + fee_recipient: string; + }, { + [x: string]: Json; + symbol?: string | undefined; + description?: string | undefined; + merkle?: Record | undefined; + image?: string | undefined; + external_link?: string | undefined; + seller_fee_basis_points?: number | undefined; + fee_recipient?: string | undefined; + name: string; + }>; + input: ZodObject; + image: ZodOptional>; + external_link: ZodOptional; + }, { + seller_fee_basis_points: ZodDefault; + fee_recipient: ZodDefault>; + }>, { + merkle: ZodDefault>; + }>, { + symbol: ZodDefault>; + }>, "strip", ZodTypeAny, { + description?: string | undefined; + image?: any; + external_link?: string | undefined; + symbol: string; + name: string; + merkle: Record; + seller_fee_basis_points: number; + fee_recipient: string; + }, { + symbol?: string | undefined; + description?: string | undefined; + merkle?: Record | undefined; + image?: any; + external_link?: string | undefined; + seller_fee_basis_points?: number | undefined; + fee_recipient?: string | undefined; + name: string; + }>; + }; + // 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; + totalUnclaimedSupply(): Promise; +} + // @public (undocumented) export type SignedPayload1155 = { payload: PayloadWithUri1155; @@ -3698,6 +4123,12 @@ export type SignedPayload721 = { signature: string; }; +// @public (undocumented) +export type SignedPayload721WithQuantitySignature = { + payload: PayloadWithUri721withQuantity; + signature: string; +}; + // @public (undocumented) export type SignerOrProvider = Signer | providers.Provider; @@ -4242,7 +4673,7 @@ export class ThirdwebSDK extends RPCConnectionHandler { getContractFromAbi(address: string, abi: ContractInterface): SmartContract; getContractList(walletAddress: string): Promise<{ address: string; - contractType: "split" | "custom" | "token" | "pack" | "edition" | "edition-drop" | "token-drop" | "vote" | "marketplace" | "nft-drop" | "nft-collection"; + contractType: "split" | "custom" | "token" | "pack" | "edition" | "edition-drop" | "token-drop" | "vote" | "marketplace" | "nft-drop" | "signature-drop" | "nft-collection"; metadata: () => Promise; }[]>; getEdition(address: string): Edition; @@ -4255,6 +4686,7 @@ export class ThirdwebSDK extends RPCConnectionHandler { // // @internal (undocumented) getPublisher(): Promise; + getSignatureDrop(contractAddress: string): SignatureDrop; getSplit(address: string): Split; getToken(address: string): Token; getTokenDrop(address: string): TokenDrop; diff --git a/package.json b/package.json index 2949bbc50..90d52d52e 100644 --- a/package.json +++ b/package.json @@ -82,7 +82,7 @@ "ethers": "^5" }, "dependencies": { - "@thirdweb-dev/contracts": "2.3.8", + "@thirdweb-dev/contracts": "2.3.9-1", "@web-std/file": "^3.0.0", "buffer": "^6.0.3", "cross-fetch": "^3.1.5", diff --git a/src/constants/addresses.ts b/src/constants/addresses.ts index 99ce9d268..0cc55e933 100644 --- a/src/constants/addresses.ts +++ b/src/constants/addresses.ts @@ -23,6 +23,7 @@ export const CONTRACT_ADDRESSES: Record< twBYOCRegistry: string; contractDeployer: string; contractMetadataRegistry: string; + sigMint: string; } > = { [ChainId.Mainnet]: { @@ -32,6 +33,7 @@ export const CONTRACT_ADDRESSES: Record< twBYOCRegistry: constants.AddressZero, contractDeployer: constants.AddressZero, contractMetadataRegistry: constants.AddressZero, + sigMint: constants.AddressZero, }, [ChainId.Rinkeby]: { biconomyForwarder: "0xFD4973FeB2031D4409fB57afEE5dF2051b171104", @@ -40,6 +42,7 @@ export const CONTRACT_ADDRESSES: Record< twBYOCRegistry: "0x3E6eE864f850F5e5A98bc950B68E181Cf4010F23", contractDeployer: "0xBD9fdebD651733e7EEAB8A83536D57023c3d3225", contractMetadataRegistry: "0x1e474395f58418e9c594a79abb0152D04C229E8e", + sigMint: constants.AddressZero, }, [ChainId.Goerli]: { biconomyForwarder: constants.AddressZero, @@ -48,6 +51,7 @@ export const CONTRACT_ADDRESSES: Record< twBYOCRegistry: "0xB1Bd9d7942A250BA2Dce27DD601F2ED4211A60C4", contractDeployer: "0x25F2Ea750BF8bE10e1139C3a19F7B4e46557D04B", contractMetadataRegistry: "0x520B80B85a3B9abfF75F77068116D759a11a455D", + sigMint: constants.AddressZero, }, [ChainId.Polygon]: { biconomyForwarder: "0x86C80a8aa58e0A4fa09A69624c31Ab2a6CAD56b8", @@ -56,6 +60,7 @@ export const CONTRACT_ADDRESSES: Record< twBYOCRegistry: "0x308473Be900F4185A56587dE54bDFF5E8f7a6AE7", contractDeployer: "0x06312720bB2aa22346510c28bf8b4F5df20c71eb", contractMetadataRegistry: "0xB67D404478d91F1C94bc607b8945cBe159B86Df8", + sigMint: constants.AddressZero, }, [ChainId.Mumbai]: { biconomyForwarder: "0x9399BB24DBB5C4b782C70c2969F58716Ebbd6a3b", @@ -64,6 +69,7 @@ export const CONTRACT_ADDRESSES: Record< twBYOCRegistry: "0x3F17972CB27506eb4a6a3D59659e0B57a43fd16C", contractDeployer: "0x14905281051Cc0Cf1064Ad16c319DBe324C62196", contractMetadataRegistry: "0x25F2Ea750BF8bE10e1139C3a19F7B4e46557D04B", + sigMint: constants.AddressZero, }, [ChainId.Avalanche]: { biconomyForwarder: "0x64CD353384109423a966dCd3Aa30D884C9b2E057", @@ -72,6 +78,7 @@ export const CONTRACT_ADDRESSES: Record< twBYOCRegistry: constants.AddressZero, contractDeployer: constants.AddressZero, contractMetadataRegistry: constants.AddressZero, + sigMint: constants.AddressZero, }, [ChainId.AvalancheFujiTestnet]: { biconomyForwarder: "0x6271Ca63D30507f2Dcbf99B52787032506D75BBF", @@ -80,6 +87,7 @@ export const CONTRACT_ADDRESSES: Record< twBYOCRegistry: "0x3E6eE864f850F5e5A98bc950B68E181Cf4010F23", contractDeployer: "0xBD9fdebD651733e7EEAB8A83536D57023c3d3225", contractMetadataRegistry: "0x1e474395f58418e9c594a79abb0152D04C229E8e", + sigMint: constants.AddressZero, }, [ChainId.Fantom]: { biconomyForwarder: constants.AddressZero, @@ -88,6 +96,7 @@ export const CONTRACT_ADDRESSES: Record< twBYOCRegistry: constants.AddressZero, contractDeployer: constants.AddressZero, contractMetadataRegistry: constants.AddressZero, + sigMint: constants.AddressZero, }, [ChainId.FantomTestnet]: { biconomyForwarder: constants.AddressZero, @@ -96,6 +105,7 @@ export const CONTRACT_ADDRESSES: Record< twBYOCRegistry: "0x3E6eE864f850F5e5A98bc950B68E181Cf4010F23", contractDeployer: "0xBD9fdebD651733e7EEAB8A83536D57023c3d3225", contractMetadataRegistry: "0x1e474395f58418e9c594a79abb0152D04C229E8e", + sigMint: constants.AddressZero, }, }; @@ -118,6 +128,8 @@ export function getContractAddressByChainId( return process.env.contractDeployerAddress as string; } else if (contractName === "contractMetadataRegistry") { return process.env.contractMetadataRegistryAddress as string; + } else if (contractName === "sigMint") { + return process.env.sigMintDeployerAddress as string; } else { return constants.AddressZero; } diff --git a/src/contracts/index.ts b/src/contracts/index.ts index 58068837b..e2a9ad111 100644 --- a/src/contracts/index.ts +++ b/src/contracts/index.ts @@ -8,5 +8,6 @@ export * from "./split"; export * from "./marketplace"; export * from "./pack"; export * from "./nft-drop"; +export * from "./signature-drop"; export * from "./maps"; export * from "./smart-contract"; diff --git a/src/contracts/maps.ts b/src/contracts/maps.ts index a01f33803..baca82af2 100644 --- a/src/contracts/maps.ts +++ b/src/contracts/maps.ts @@ -9,12 +9,14 @@ import { Pack } from "./pack"; import { NFTDrop } from "./nft-drop"; import { TokenDrop } from "./token-drop"; import { SmartContract } from "./smart-contract"; +import { SignatureDrop } from "./signature-drop"; /** * @internal */ export const KNOWN_CONTRACTS_MAP = { [NFTDrop.contractType]: NFTDrop, + [SignatureDrop.contractType]: SignatureDrop, [NFTCollection.contractType]: NFTCollection, [EditionDrop.contractType]: EditionDrop, [Edition.contractType]: Edition, @@ -39,6 +41,7 @@ export const CONTRACTS_MAP = { */ export const REMOTE_CONTRACT_NAME = { [NFTDrop.contractType]: "DropERC721", + [SignatureDrop.contractType]: "SignatureDrop", [NFTCollection.contractType]: "TokenERC721", [EditionDrop.contractType]: "DropERC1155", [Edition.contractType]: "TokenERC1155", @@ -56,6 +59,7 @@ export const REMOTE_CONTRACT_NAME = { */ export const REMOTE_CONTRACT_TO_CONTRACT_TYPE = { DropERC721: NFTDrop.contractType, + SignatureDrop: SignatureDrop.contractType, TokenERC721: NFTCollection.contractType, DropERC1155: EditionDrop.contractType, TokenERC1155: Edition.contractType, diff --git a/src/contracts/signature-drop.ts b/src/contracts/signature-drop.ts new file mode 100644 index 000000000..cca62675a --- /dev/null +++ b/src/contracts/signature-drop.ts @@ -0,0 +1,608 @@ +import { ContractRoles } from "../core/classes/contract-roles"; +import { SignatureDrop as SignatureDropContract } from "contracts"; +import { + BigNumber, + BigNumberish, + BytesLike, + constants, + ethers, + utils, +} from "ethers"; +import { ContractMetadata } from "../core/classes/contract-metadata"; +import { ContractRoyalty } from "../core/classes/contract-royalty"; +import { ContractWrapper } from "../core/classes/contract-wrapper"; +import { IStorage } from "../core/interfaces/IStorage"; +import { + NetworkOrSignerOrProvider, + TransactionResult, + TransactionResultWithId, +} from "../core/types"; +import { DropErc721ContractSchema } from "../schema/contracts/drop-erc721"; +import { SDKOptions } from "../schema/sdk-options"; +import { + CommonNFTInput, + NFTMetadata, + NFTMetadataInput, + NFTMetadataOwner, +} from "../schema/tokens/common"; +import { DEFAULT_QUERY_ALL_COUNT, QueryAllParams } from "../types/QueryParams"; +import { DropClaimConditions } from "../core/classes/drop-claim-conditions"; +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, + GasCostEstimator, +} from "../core/classes"; +import { ClaimVerification } from "../types"; +import { ContractEvents } from "../core/classes/contract-events"; +import { ContractPlatformFee } from "../core/classes/contract-platform-fee"; +import { ContractInterceptor } from "../core/classes/contract-interceptor"; +import { getRoleHash } from "../common"; +import { + TokensClaimedEvent, + TokensLazyMintedEvent, +} from "contracts/DropERC721"; +import { ContractAnalytics } from "../core/classes/contract-analytics"; +import { Erc721WithQuantitySignatureMinting } from "../core/classes/erc-721-with-quantity-signature-minting"; + +/** + * Setup a collection of NFTs where when it comes to minting, you can authorize + * some external party to mint tokens on your contract, and specify what exactly + * will be minted by that external party.. + * + * @example + * + * ```javascript + * import { ThirdwebSDK } from "@thirdweb-dev/sdk"; + * + * const sdk = new ThirdwebSDK("rinkeby"); + * const contract = sdk.getSignatureDrop("{{contract_address}}"); + * ``` + * + * @public + */ +export class SignatureDrop extends Erc721 { + static contractType = "signature-drop" as const; + static contractRoles = ["admin", "minter", "transfer"] as const; + static contractAbi = require("../../abis/SignatureDrop.json"); + /** + * @internal + */ + static schema = DropErc721ContractSchema; + + public encoder: ContractEncoder; + public estimator: GasCostEstimator; + public metadata: ContractMetadata< + SignatureDropContract, + typeof SignatureDrop.schema + >; + public primarySale: ContractPrimarySale; + public platformFee: ContractPlatformFee; + public events: ContractEvents; + public roles: ContractRoles< + SignatureDropContract, + typeof SignatureDrop.contractRoles[number] + >; + public analytics: ContractAnalytics; + /** + * @internal + */ + public interceptor: ContractInterceptor; + /** + * Configure royalties + * @remarks Set your own royalties for the entire contract or per token + * @example + * ```javascript + * // royalties on the whole contract + * contract.royalty.setDefaultRoyaltyInfo({ + * seller_fee_basis_points: 100, // 1% + * fee_recipient: "0x..." + * }); + * // override royalty for a particular token + * contract.royalty.setTokenRoyaltyInfo(tokenId, { + * seller_fee_basis_points: 500, // 5% + * fee_recipient: "0x..." + * }); + * ``` + */ + public royalty: ContractRoyalty< + SignatureDropContract, + typeof SignatureDrop.schema + >; + /** + * Configure claim conditions + * @remarks Define who can claim NFTs in the collection, when and how many. + * @example + * ```javascript + * const presaleStartTime = new Date(); + * const publicSaleStartTime = new Date(Date.now() + 60 * 60 * 24 * 1000); + * const claimConditions = [ + * { + * startTime: presaleStartTime, // start the presale now + * maxQuantity: 2, // limit how many mints for this presale + * price: 0.01, // presale price + * snapshot: ['0x...', '0x...'], // limit minting to only certain addresses + * }, + * { + * startTime: publicSaleStartTime, // 24h after presale, start public sale + * price: 0.08, // public sale price + * } + * ]); + * await contract.claimConditions.set(claimConditions); + * ``` + */ + public claimConditions: DropClaimConditions; + /** + * Delayed reveal + * @remarks Create a batch of encrypted NFTs that can be revealed at a later time. + * @example + * ```javascript + * // the real NFTs, these will be encrypted until you reveal them + * const realNFTs = [{ + * name: "Common NFT #1", + * description: "Common NFT, one of many.", + * image: fs.readFileSync("path/to/image.png"), + * }, { + * name: "Super Rare NFT #2", + * description: "You got a Super Rare NFT!", + * image: fs.readFileSync("path/to/image.png"), + * }]; + * // A placeholder NFT that people will get immediately in their wallet, and will be converted to the real NFT at reveal time + * const placeholderNFT = { + * name: "Hidden NFT", + * description: "Will be revealed next week!" + * }; + * // Create and encrypt the NFTs + * await contract.revealer.createDelayedRevealBatch( + * placeholderNFT, + * realNFTs, + * "my secret password", + * ); + * // Whenever you're ready, reveal your NFTs at any time + * const batchId = 0; // the batch to reveal + * await contract.revealer.reveal(batchId, "my secret password"); + * ``` + */ + public revealer: DelayedReveal; + + public signature: Erc721WithQuantitySignatureMinting; + + private _query = this.query as Erc721Supply; + private _owned = this._query.owned as Erc721Enumerable; + + constructor( + network: NetworkOrSignerOrProvider, + address: string, + storage: IStorage, + options: SDKOptions = {}, + contractWrapper = new ContractWrapper( + network, + address, + SignatureDrop.contractAbi, + options, + ), + ) { + super(contractWrapper, storage, options); + this.metadata = new ContractMetadata( + this.contractWrapper, + SignatureDrop.schema, + this.storage, + ); + this.roles = new ContractRoles( + this.contractWrapper, + SignatureDrop.contractRoles, + ); + this.royalty = new ContractRoyalty(this.contractWrapper, this.metadata); + this.primarySale = new ContractPrimarySale(this.contractWrapper); + this.analytics = new ContractAnalytics(this.contractWrapper); + this.encoder = new ContractEncoder(this.contractWrapper); + this.estimator = new GasCostEstimator(this.contractWrapper); + this.events = new ContractEvents(this.contractWrapper); + this.platformFee = new ContractPlatformFee(this.contractWrapper); + this.revealer = new DelayedReveal( + this.contractWrapper, + this.storage, + ); + this.interceptor = new ContractInterceptor(this.contractWrapper); + this.signature = new Erc721WithQuantitySignatureMinting( + this.contractWrapper, + this.roles, + this.storage, + ); + this.claimConditions = new DropClaimConditions( + this.contractWrapper, + this.metadata, + this.storage, + ); + } + + /** ****************************** + * READ FUNCTIONS + *******************************/ + + /** + * Get All Minted NFTs + * + * @remarks Get all the data associated with every NFT in this contract. + * + * By default, returns the first 100 NFTs, use queryParams to fetch more. + * + * @example + * ```javascript + * const nfts = await contract.getAll(); + * console.log(nfts); + * ``` + * @param queryParams - optional filtering to only fetch a subset of results. + * @returns The NFT metadata for all NFTs queried. + */ + public async getAll( + queryParams?: QueryAllParams, + ): Promise { + return this._query.all(queryParams); + } + + /** + * Get Owned NFTs + * + * @remarks Get all the data associated with the NFTs owned by a specific wallet. + * + * @example + * ```javascript + * // Address of the wallet to get the NFTs of + * const address = "{{wallet_address}}"; + * const nfts = await contract.getOwned(address); + * console.log(nfts); + * ``` + * @param walletAddress - the wallet address to query, defaults to the connected wallet + * @returns The NFT metadata for all NFTs in the contract. + */ + public async getOwned(walletAddress?: string): Promise { + return this._owned.all(walletAddress); + } + + /** + * {@inheritDoc Erc721Enumerable.tokendIds} + */ + public async getOwnedTokenIds(walletAddress?: string): Promise { + return this._owned.tokenIds(walletAddress); + } + + /** + * Get the total count NFTs in this drop contract, both claimed and unclaimed + */ + public async totalSupply() { + const claimed = await this.totalClaimedSupply(); + const unclaimed = await this.totalUnclaimedSupply(); + return claimed.add(unclaimed); + } + + /** + * Get All Claimed NFTs + * + * @remarks Fetch all the NFTs (and their owners) that have been claimed in this Drop. + * + * * @example + * ```javascript + * const claimedNFTs = await contract.getAllClaimed(); + * const firstOwner = claimedNFTs[0].owner; + * ``` + * + * @param queryParams - optional filtering to only fetch a subset of results. + * @returns The NFT metadata and their ownersfor all NFTs queried. + */ + public async getAllClaimed( + queryParams?: QueryAllParams, + ): Promise { + const start = BigNumber.from(queryParams?.start || 0).toNumber(); + const count = BigNumber.from( + queryParams?.count || DEFAULT_QUERY_ALL_COUNT, + ).toNumber(); + const maxId = Math.min( + (await this.contractWrapper.readContract.nextTokenIdToMint()).toNumber(), + start + count, + ); + return await Promise.all( + Array.from(Array(maxId).keys()).map((i) => this.get(i.toString())), + ); + } + + /** + * Get All Unclaimed NFTs + * + * @remarks Fetch all the NFTs that have been not been claimed yet in this Drop. + * + * * @example + * ```javascript + * const unclaimedNFTs = await contract.getAllUnclaimed(); + * const firstUnclaimedNFT = unclaimedNFTs[0].name; + * ``` + * + * @param queryParams - optional filtering to only fetch a subset of results. + * @returns The NFT metadata for all NFTs queried. + */ + public async getAllUnclaimed( + queryParams?: QueryAllParams, + ): Promise { + const start = BigNumber.from(queryParams?.start || 0).toNumber(); + const count = BigNumber.from( + queryParams?.count || DEFAULT_QUERY_ALL_COUNT, + ).toNumber(); + const firstTokenId = BigNumber.from( + Math.max( + ( + await this.contractWrapper.readContract.nextTokenIdToMint() + ).toNumber(), + start, + ), + ); + const maxId = BigNumber.from( + Math.min( + ( + await this.contractWrapper.readContract.nextTokenIdToMint() + ).toNumber(), + firstTokenId.toNumber() + count, + ), + ); + + return await Promise.all( + Array.from(Array(maxId.sub(firstTokenId).toNumber()).keys()).map((i) => + this.getTokenMetadata(firstTokenId.add(i).toString()), + ), + ); + } + + /** + * Get the claimed supply + * + * @remarks Get the number of claimed NFTs in this Drop. + * + * * @example + * ```javascript + * const claimedNFTCount = await contract.totalClaimedSupply(); + * console.log(`NFTs claimed so far: ${claimedNFTCount}`); + * ``` + * @returns the claimed supply + */ + public async totalClaimedSupply(): Promise { + const claimCondition = + await this.contractWrapper.readContract.claimCondition(); + const startId = claimCondition.currentStartId.toNumber(); + const count = claimCondition.count.toNumber(); + const conditions = []; + for (let i = startId; i < startId + count; i++) { + conditions.push( + await this.contractWrapper.readContract.getClaimConditionById(i), + ); + } + + const totalClaimed = conditions.reduce(function (total, condition) { + return total + condition.supplyClaimed.toNumber(); + }, 0); + + return ethers.BigNumber.from(totalClaimed); + } + + /** + * Get the unclaimed supply + * + * @remarks Get the number of unclaimed NFTs in this Drop. + * + * * @example + * ```javascript + * const unclaimedNFTCount = await contract.totalUnclaimedSupply(); + * console.log(`NFTs left to claim: ${unclaimedNFTCount}`); + * ``` + * @returns the unclaimed supply + */ + public async totalUnclaimedSupply(): Promise { + const claimCondition = + await this.contractWrapper.readContract.claimCondition(); + const startId = claimCondition.currentStartId.toNumber(); + const count = claimCondition.count.toNumber(); + const conditions = []; + for (let i = startId; i < startId + count; i++) { + conditions.push( + await this.contractWrapper.readContract.getClaimConditionById(i), + ); + } + + const maxSupply = + await this.contractWrapper.readContract.nextTokenIdToMint(); + + const totalClaimed = conditions.reduce(function (total, condition) { + return total + condition.supplyClaimed.toNumber(); + }, 0); + + return BigNumber.from(maxSupply).sub(totalClaimed); + } + + /** + * Get whether users can transfer NFTs from this contract + */ + public async isTransferRestricted(): Promise { + const anyoneCanTransfer = await this.contractWrapper.readContract.hasRole( + getRoleHash("transfer"), + constants.AddressZero, + ); + return !anyoneCanTransfer; + } + + /** ****************************** + * WRITE FUNCTIONS + *******************************/ + + /** + * 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.createBatch(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. + */ + public async createBatch( + metadatas: NFTMetadataInput[], + ): 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(), + ); + const baseUri = batch.baseUri; + const receipt = await this.contractWrapper.sendTransaction("lazyMint", [ + batch.metadataUris.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.getTokenMetadata(id), + }); + } + return results; + } + + /** + * Claim unique NFTs to a specific Wallet + * + * @remarks Let the specified wallet claim NFTs. + * + * @example + * ```javascript + * const address = "{{wallet_address}}"; // address of the wallet you want to claim the NFTs + * const quantity = 1; // how many unique NFTs you want to claim + * + * const tx = await contract.claimTo(address, quantity); + * const receipt = tx.receipt; // the transaction receipt + * const claimedTokenId = tx.id; // the id of the NFT claimed + * const claimedNFT = await tx.data(); // (optional) get the claimed NFT metadata + * ``` + * + * @param destinationAddress - Address you want to send the token to + * @param quantity - Quantity of the tokens you want to claim + * @param proofs - Array of proofs + * + * @returns - an array of results containing the id of the token claimed, the transaction receipt and a promise to optionally fetch the nft metadata + */ + public async claimTo( + destinationAddress: string, + quantity: BigNumberish, + proofs: BytesLike[] = [utils.hexZeroPad([0], 32)], + ): Promise[]> { + const claimVerification = await this.prepareClaim(quantity, proofs); + const receipt = await this.contractWrapper.sendTransaction( + "claim", + [ + destinationAddress, + quantity, + claimVerification.currencyAddress, + claimVerification.price, + { + proof: claimVerification.proofs, + maxQuantityInAllowlist: claimVerification.maxQuantityPerTransaction, + }, + ethers.utils.toUtf8Bytes(""), + ], + claimVerification.overrides, + ); + const event = this.contractWrapper.parseLogs( + "TokensClaimed", + receipt?.logs, + ); + const startingIndex: BigNumber = event[0].args.startTokenId; + const endingIndex = startingIndex.add(quantity); + const results = []; + for (let id = startingIndex; id.lt(endingIndex); id = id.add(1)) { + results.push({ + id, + receipt, + data: () => this.get(id), + }); + } + return results; + } + + /** + * Claim NFTs to the connected wallet. + * + * @remarks See {@link NFTDrop.claimTo} + * + * @returns - an array of results containing the id of the token claimed, the transaction receipt and a promise to optionally fetch the nft metadata + */ + public async claim( + quantity: BigNumberish, + proofs: BytesLike[] = [utils.hexZeroPad([0], 32)], + ): Promise[]> { + return this.claimTo( + await this.contractWrapper.getSignerAddress(), + quantity, + proofs, + ); + } + + /** + * Burn a single NFT + * @param tokenId - the token Id to burn + */ + public async burn(tokenId: BigNumberish): Promise { + return { + receipt: await this.contractWrapper.sendTransaction("burn", [tokenId]), + }; + } + + /** ****************************** + * PRIVATE FUNCTIONS + *******************************/ + + /** + * Returns proofs and the overrides required for the transaction. + * + * @returns - `overrides` and `proofs` as an object. + */ + private async prepareClaim( + quantity: BigNumberish, + proofs: BytesLike[] = [utils.hexZeroPad([0], 32)], + ): Promise { + return prepareClaim( + quantity, + await this.claimConditions.getActive(), + (await this.metadata.get()).merkle, + 0, + this.contractWrapper, + this.storage, + proofs, + ); + } +} diff --git a/src/core/classes/contract-deployer.ts b/src/core/classes/contract-deployer.ts index e94ce611b..e601db388 100644 --- a/src/core/classes/contract-deployer.ts +++ b/src/core/classes/contract-deployer.ts @@ -12,6 +12,7 @@ import { Marketplace, NFTCollection, NFTDrop, + SignatureDrop, Pack, Split, Token, @@ -77,6 +78,17 @@ export class ContractDeployer extends RPCConnectionHandler { return await this.deployBuiltInContract(NFTDrop.contractType, metadata); } + /** + * Deploys a new SignatureDrop contract + * @param metadata - the contract metadata + * @returns the address of the deployed contract + */ + public async deploySignatureDrop( + metadata: NFTContractDeployMetadata, + ): Promise { + return await this.deployBuiltInContract(SignatureDrop.contractType, metadata); + } + /** * Deploys a new Edition contract * @param metadata - the contract metadata diff --git a/src/core/classes/delayed-reveal.ts b/src/core/classes/delayed-reveal.ts index e0f8ce006..6f262a574 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 } from "contracts"; +import { DropERC721, SignatureDrop } from "contracts"; import { CommonNFTInput, NFTMetadata, @@ -15,7 +15,7 @@ import { TokensLazyMintedEvent } from "contracts/DropERC721"; * Handles delayed reveal logic * @public */ -export class DelayedReveal { +export class DelayedReveal { private contractWrapper: ContractWrapper; private storage: IStorage; @@ -162,10 +162,12 @@ 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.contractWrapper.readContract.baseURIIndices(i), + this.isSignatureDrop(this.contractWrapper.readContract, contractType) ? this.contractWrapper.readContract.getBatchIdAtIndex(i) : this.contractWrapper.readContract.baseURIIndices(i) ), ); @@ -224,4 +226,8 @@ 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/drop-claim-conditions.ts b/src/core/classes/drop-claim-conditions.ts index 406cfff3e..789f5e8a0 100644 --- a/src/core/classes/drop-claim-conditions.ts +++ b/src/core/classes/drop-claim-conditions.ts @@ -1,6 +1,12 @@ import { IStorage } from "../interfaces/IStorage"; import { ContractMetadata } from "./contract-metadata"; -import { DropERC20, DropERC721, IERC20, IERC20Metadata } from "contracts"; +import { + DropERC20, + DropERC721, + IERC20, + IERC20Metadata, + SignatureDrop, +} from "contracts"; import { BigNumber, constants, ethers } from "ethers"; import { isNativeToken } from "../../common/currency"; import { ContractWrapper } from "./contract-wrapper"; @@ -25,7 +31,9 @@ import { isNode } from "../../common/utils"; * Manages claim conditions for NFT Drop contracts * @public */ -export class DropClaimConditions { +export class DropClaimConditions< + TContract extends SignatureDrop | DropERC721 | DropERC20, +> { private contractWrapper; private metadata; private storage: IStorage; @@ -197,15 +205,32 @@ export class DropClaimConditions { metadata.merkle, this.storage, ); + + const contractType = ethers.utils.toUtf8String( + await this.contractWrapper.readContract.contractType(), + ); + try { - const [validMerkleProof] = - await this.contractWrapper.readContract.verifyClaimMerkleProof( - activeConditionIndex, - addressToCheck, - quantity, - proofs.proof, - proofs.maxClaimable, - ); + const [validMerkleProof] = this.isSignatureDrop( + this.contractWrapper.readContract, + contractType, + ) + ? await this.contractWrapper.readContract.verifyClaimMerkleProof( + activeConditionIndex, + addressToCheck, + quantity, + { + proof: proofs.proof, + maxQuantityInAllowlist: proofs.maxClaimable, + }, + ) + : await this.contractWrapper.readContract.verifyClaimMerkleProof( + activeConditionIndex, + addressToCheck, + quantity, + proofs.proof, + proofs.maxClaimable, + ); if (!validMerkleProof) { reasons.push(ClaimEligibility.AddressNotAllowed); return reasons; @@ -330,12 +355,24 @@ export class DropClaimConditions { ), ); } + const contractType = ethers.utils.toUtf8String( + await this.contractWrapper.readContract.contractType(), + ); encoded.push( - this.contractWrapper.readContract.interface.encodeFunctionData( - "setClaimConditions", - [sortedConditions, resetClaimEligibilityForAll], - ), + this.isSignatureDrop(this.contractWrapper.readContract, contractType) + ? this.contractWrapper.readContract.interface.encodeFunctionData( + "setClaimConditions", + [ + sortedConditions, + resetClaimEligibilityForAll, + ethers.utils.toUtf8Bytes(""), + ], + ) + : this.contractWrapper.readContract.interface.encodeFunctionData( + "setClaimConditions", + [sortedConditions, resetClaimEligibilityForAll], + ), ); return { @@ -373,4 +410,11 @@ export class DropClaimConditions { return Promise.resolve(0); } } + + private isSignatureDrop( + _contract: SignatureDrop | any, + contractType: string, + ): _contract is SignatureDrop { + return contractType.includes("SignatureDrop"); + } } diff --git a/src/core/classes/erc-721-with-quantity-signature-minting.ts b/src/core/classes/erc-721-with-quantity-signature-minting.ts new file mode 100644 index 000000000..202e3d5c3 --- /dev/null +++ b/src/core/classes/erc-721-with-quantity-signature-minting.ts @@ -0,0 +1,286 @@ +import { + FilledSignature721WithQuantity, + MintRequest721withQuantity, + PayloadToSign721withQuantity, + PayloadWithUri721withQuantity, + Signature721WithQuantityInput, + Signature721WithQuantityOutput, + SignedPayload721WithQuantitySignature, +} from "../../schema/contracts/common/signature"; +import { TransactionResultWithId } from "../types"; +import { normalizePriceValue, setErc20Allowance } from "../../common/currency"; +import invariant from "tiny-invariant"; +import { ContractWrapper } from "./contract-wrapper"; +import { + ISignatureMintERC721, + SignatureDrop as SignatureDropContract, +} from "contracts"; +import { IStorage } from "../interfaces"; +import { ContractRoles } from "./contract-roles"; +import { SignatureDrop } from "../../contracts"; +import { BigNumber } from "ethers"; +import { uploadOrExtractURIs } from "../../common/nft"; +import { TokensMintedWithSignatureEvent } from "contracts/SigMint"; + +/** + * Enables generating dynamic ERC721 NFTs. You can authorize then some external party to mint tokens on your contract + * @public + */ +export class Erc721WithQuantitySignatureMinting { + private contractWrapper: ContractWrapper; + private storage: IStorage; + private roles: ContractRoles< + SignatureDropContract, + typeof SignatureDrop.contractRoles[number] + >; + + constructor( + contractWrapper: ContractWrapper, + roles: ContractRoles< + SignatureDropContract, + typeof SignatureDrop.contractRoles[number] + >, + storage: IStorage, + ) { + this.contractWrapper = contractWrapper; + this.storage = storage; + this.roles = roles; + } + + /** + * Mint a dynamically generated NFT + * + * @remarks Mint a dynamic NFT with a previously generated signature. + * + * @example + * ```javascript + * // see how to craft a payload to sign in the `generate()` documentation + * const signedPayload = contract.signature.generate(payload); + * + * // now anyone can mint the NFT + * const tx = contract.signature.mint(signedPayload); + * const receipt = tx.receipt; // the mint transaction receipt + * const mintedId = tx.id; // the id of the NFT minted + * ``` + * @param signedPayload - the previously generated payload and signature with {@link Erc721SignatureMinting.generate} + */ + public async mint( + signedPayload: SignedPayload721WithQuantitySignature, + ): Promise { + const mintRequest = signedPayload.payload; + const signature = signedPayload.signature; + const message = await this.mapPayloadToContractStruct(mintRequest); + const overrides = await this.contractWrapper.getCallOverrides(); + await setErc20Allowance( + this.contractWrapper, + message.pricePerToken.mul(message.quantity), + mintRequest.currencyAddress, + overrides, + ); + const receipt = await this.contractWrapper.sendTransaction( + "mintWithSignature", + [message, signature], + overrides, + ); + const t = this.contractWrapper.parseLogs( + "TokensMintedWithSignature", + receipt.logs, + ); + if (t.length === 0) { + throw new Error("No MintWithSignature event found"); + } + const id = t[0].args.tokenIdMinted; + return { + id, + receipt, + }; + } + + /** + * Mint any number of dynamically generated NFT at once + * @remarks Mint multiple dynamic NFTs in one transaction. Note that this is only possible for free mints (cannot batch mints with a price attached to it for security reasons) + * @param signedPayloads - the array of signed payloads to mint + */ + public async mintBatch( + signedPayloads: SignedPayload721WithQuantitySignature[], + ): Promise { + const contractPayloads = await Promise.all( + signedPayloads.map(async (s) => { + const message = await this.mapPayloadToContractStruct(s.payload); + const signature = s.signature; + const price = s.payload.price; + if (BigNumber.from(price).gt(0)) { + throw new Error( + "Can only batch free mints. For mints with a price, use regular mint()", + ); + } + return { + message, + signature, + }; + }), + ); + const encoded = contractPayloads.map((p) => { + return this.contractWrapper.readContract.interface.encodeFunctionData( + "mintWithSignature", + [p.message, p.signature], + ); + }); + const receipt = await this.contractWrapper.multiCall(encoded); + const events = + this.contractWrapper.parseLogs( + "TokensMintedWithSignature", + receipt.logs, + ); + if (events.length === 0) { + throw new Error("No MintWithSignature event found"); + } + return events.map((log) => ({ + id: log.args.tokenIdMinted, + receipt, + })); + } + + /** + * Verify that a payload is correctly signed + * @param signedPayload - the payload to verify + */ + public async verify( + signedPayload: SignedPayload721WithQuantitySignature, + ): Promise { + const mintRequest = signedPayload.payload; + const signature = signedPayload.signature; + const message = await this.mapPayloadToContractStruct(mintRequest); + const verification: [boolean, string] = + await this.contractWrapper.readContract.verify(message, signature); + return verification[0]; + } + + /** + * Generate a signature that can be used to mint a dynamic NFT + * + * @remarks Takes in an NFT and some information about how it can be minted, uploads the metadata and signs it with your private key. The generated signature can then be used to mint an NFT using the exact payload and signature generated. + * + * @example + * ```javascript + * const nftMetadata = { + * name: "Cool NFT #1", + * description: "This is a cool NFT", + * image: fs.readFileSync("path/to/image.png"), // This can be an image url or file + * }; + * + * const startTime = new Date(); + * const endTime = new Date(Date.now() + 60 * 60 * 24 * 1000); + * const payload = { + * metadata: nftMetadata, // The NFT to mint + * to: {{wallet_address}}, // Who will receive the NFT (or AddressZero for anyone) + * quantity: 2, // the quantity of NFTs to mint + * price: 0.5, // the price per NFT + * currencyAddress: NATIVE_TOKEN_ADDRESS, // the currency to pay with + * mintStartTime: startTime, // can mint anytime from now + * mintEndTime: endTime, // to 24h from now + * royaltyRecipient: "0x...", // custom royalty recipient for this NFT + * royaltyBps: 100, // custom royalty fees for this NFT (in bps) + * primarySaleRecipient: "0x...", // custom sale recipient for this NFT + * }; + * + * const signedPayload = contract.signature.generate(payload); + * // now anyone can use these to mint the NFT using `contract.signature.mint(signedPayload)` + * ``` + * @param mintRequest - the payload to sign + * @returns the signed payload and the corresponding signature + */ + public async generate( + mintRequest: PayloadToSign721withQuantity, + ): Promise { + return (await this.generateBatch([mintRequest]))[0]; + } + + /** + * Genrate a batch of signatures that can be used to mint many dynamic NFTs. + * + * @remarks See {@link Erc721SignatureMinting.generate} + * + * @param payloadsToSign - the payloads to sign + * @returns an array of payloads and signatures + */ + public async generateBatch( + payloadsToSign: PayloadToSign721withQuantity[], + ): Promise { + await this.roles.verify( + ["minter"], + await this.contractWrapper.getSignerAddress(), + ); + const parsedRequests: FilledSignature721WithQuantity[] = payloadsToSign.map( + (m) => Signature721WithQuantityInput.parse(m), + ); + + const metadatas = parsedRequests.map((r) => r.metadata); + const uris = await uploadOrExtractURIs(metadatas, this.storage); + + const chainId = await this.contractWrapper.getChainID(); + const signer = this.contractWrapper.getSigner(); + invariant(signer, "No signer available"); + + return await Promise.all( + parsedRequests.map(async (m, i) => { + const uri = uris[i]; + const finalPayload = Signature721WithQuantityOutput.parse({ + ...m, + uri, + }); + const signature = await this.contractWrapper.signTypedData( + signer, + { + name: "SignatureMintERC721", + version: "1", + chainId, + verifyingContract: + await this.contractWrapper.readContract.sigMint(), + }, + { MintRequest: MintRequest721withQuantity }, // TYPEHASH + await this.mapPayloadToContractStruct(finalPayload), + ); + return { + payload: finalPayload, + signature: signature.toString(), + }; + }), + ); + } + + /** ****************************** + * PRIVATE FUNCTIONS + *******************************/ + + /** + * Maps a payload to the format expected by the contract + * + * @internal + * + * @param mintRequest - The payload to map. + * @returns - The mapped payload. + */ + private async mapPayloadToContractStruct( + mintRequest: PayloadWithUri721withQuantity, + ): Promise { + const normalizedPricePerToken = await normalizePriceValue( + this.contractWrapper.getProvider(), + mintRequest.price, + mintRequest.currencyAddress, + ); + return { + to: mintRequest.to, + royaltyRecipient: mintRequest.royaltyRecipient, + royaltyBps: mintRequest.royaltyBps, + primarySaleRecipient: mintRequest.primarySaleRecipient, + uri: mintRequest.uri, + quantity: mintRequest.quantity, + pricePerToken: normalizedPricePerToken, + currency: mintRequest.currencyAddress, + validityStartTimestamp: mintRequest.mintStartTime, + validityEndTimestamp: mintRequest.mintEndTime, + uid: mintRequest.uid, + } as ISignatureMintERC721.MintRequestStructOutput; + } +} diff --git a/src/core/classes/erc-721.ts b/src/core/classes/erc-721.ts index 37689138b..8c2c00f99 100644 --- a/src/core/classes/erc-721.ts +++ b/src/core/classes/erc-721.ts @@ -11,6 +11,7 @@ import { DropERC721, IERC721Supply, IMintableERC721, + SignatureDrop, TokenERC721, } from "contracts"; import { Erc721Supply } from "./erc-721-supply"; @@ -30,7 +31,7 @@ import { DetectableFeature } from "../interfaces/DetectableFeature"; * @public */ export class Erc721< - T extends DropERC721 | TokenERC721 | BaseERC721 = BaseERC721, + T extends SignatureDrop | DropERC721 | TokenERC721 | BaseERC721 = BaseERC721, > implements UpdateableNetwork, DetectableFeature { featureName = FEATURE_NFT.name; diff --git a/src/core/classes/factory.ts b/src/core/classes/factory.ts index c69765de1..b2081ee81 100644 --- a/src/core/classes/factory.ts +++ b/src/core/classes/factory.ts @@ -8,6 +8,7 @@ import { Marketplace, NFTCollection, NFTDrop, + SignatureDrop, Pack, REMOTE_CONTRACT_NAME, Split, @@ -22,6 +23,7 @@ import { ContractWrapper } from "./contract-wrapper"; import { ChainlinkVrf } from "../../constants/chainlink"; import { CONTRACT_ADDRESSES, + getContractAddressByChainId, OZ_DEFENDER_FORWARDER_ADDRESS, SUPPORTED_CHAIN_IDS, } from "../../constants"; @@ -120,6 +122,23 @@ export class ContractFactory extends ContractWrapper { erc721metadata.platform_fee_basis_points, erc721metadata.platform_fee_recipient, ]; + case SignatureDrop.contractType: + const signatureDropmetadata = + SignatureDrop.schema.deploy.parse(metadata); + const chainId = await this.getChainID(); + return [ + await this.getSignerAddress(), + signatureDropmetadata.name, + signatureDropmetadata.symbol, + contractURI, + trustedForwarders, + signatureDropmetadata.primary_sale_recipient, + signatureDropmetadata.fee_recipient, + signatureDropmetadata.seller_fee_basis_points, + signatureDropmetadata.platform_fee_basis_points, + signatureDropmetadata.platform_fee_recipient, + getContractAddressByChainId(chainId, "sigMint"), + ]; case EditionDrop.contractType: case Edition.contractType: const erc1155metadata = EditionDrop.schema.deploy.parse(metadata); diff --git a/src/core/sdk.ts b/src/core/sdk.ts index 48b7ff2fa..e49c92558 100644 --- a/src/core/sdk.ts +++ b/src/core/sdk.ts @@ -7,6 +7,7 @@ import { Marketplace, NFTCollection, NFTDrop, + SignatureDrop, Pack, REMOTE_CONTRACT_TO_CONTRACT_TYPE, Split, @@ -156,6 +157,18 @@ export class ThirdwebSDK extends RPCConnectionHandler { ) as NFTDrop; } + /** + * Get an instance of a SignatureDrop contract + * @param contractAddress - the address of the deployed contract + * @returns the contract + */ + public getSignatureDrop(contractAddress: string): SignatureDrop { + return this.getBuiltInContract( + contractAddress, + SignatureDrop.contractType, + ) as SignatureDrop; + } + /** * Get an instance of a NFT Collection contract * @param address - the address of the deployed contract diff --git a/src/schema/contracts/common/signature.ts b/src/schema/contracts/common/signature.ts index 488b5d250..1ea5e4dfe 100644 --- a/src/schema/contracts/common/signature.ts +++ b/src/schema/contracts/common/signature.ts @@ -79,6 +79,21 @@ export const Signature1155PayloadOutput = Signature721PayloadOutput.extend({ quantity: BigNumberSchema, }); +/** + * @internal + */ + export const Signature721WithQuantityInput = Signature721PayloadInput.extend({ + metadata: NFTInputOrUriSchema.default(""), + quantity: BigNumberishSchema, +}); + +/** + * @internal + */ +export const Signature721WithQuantityOutput = Signature721PayloadOutput.extend({ + quantity: BigNumberSchema, +}); + /** * @public */ @@ -127,14 +142,28 @@ export type SignedPayload721 = { export type FilledSignaturePayload1155 = z.output< typeof Signature1155PayloadInput >; +/** + * @public + */ + export type FilledSignature721WithQuantity = z.output< + typeof Signature721WithQuantityInput +>; /** * @public */ export type PayloadWithUri1155 = z.output; +/** + * @public + */ +export type PayloadWithUri721withQuantity = z.output; /** * @public */ export type PayloadToSign1155 = z.input; +/** + * @public + */ +export type PayloadToSign721withQuantity = z.input; /** * @public */ @@ -143,6 +172,14 @@ export type SignedPayload1155 = { signature: string; }; +/** + * @public + */ +export type SignedPayload721WithQuantitySignature = { + payload: PayloadWithUri721withQuantity; + signature: string; +}; + export const MintRequest20 = [ { name: "to", type: "address" }, { name: "primarySaleRecipient", type: "address" }, @@ -181,3 +218,17 @@ export const MintRequest1155 = [ { name: "validityEndTimestamp", type: "uint128" }, { name: "uid", type: "bytes32" }, ]; + +export const MintRequest721withQuantity = [ + { name: "to", type: "address" }, + { name: "royaltyRecipient", type: "address" }, + { name: "royaltyBps", type: "uint256" }, + { name: "primarySaleRecipient", type: "address" }, + { name: "uri", type: "string" }, + { name: "quantity", type: "uint256" }, + { name: "pricePerToken", type: "uint256" }, + { name: "currency", type: "address" }, + { name: "validityStartTimestamp", type: "uint128" }, + { name: "validityEndTimestamp", type: "uint128" }, + { name: "uid", type: "bytes32" }, +]; diff --git a/test/before-setup.ts b/test/before-setup.ts index 9bc807dba..527007f34 100644 --- a/test/before-setup.ts +++ b/test/before-setup.ts @@ -1,15 +1,17 @@ import { ContractDeployer, ContractDeployer__factory, - ContractPublisher, - ContractPublisher__factory, ContractMetadataRegistry, ContractMetadataRegistry__factory, + ContractPublisher, + ContractPublisher__factory, DropERC1155__factory, DropERC20__factory, DropERC721__factory, Marketplace__factory, Pack__factory, + SigMint__factory, + SignatureDrop__factory, Split__factory, TokenERC1155__factory, TokenERC20__factory, @@ -35,6 +37,7 @@ import { NFTCollection, NFTDrop, Pack, + SignatureDrop, Split, ThirdwebSDK, Token, @@ -141,6 +144,14 @@ before(async () => { .deploy(trustedForwarderAddress)) as ContractPublisher; await contractPublisher.deployed(); + const sigMintDeployer = await new ethers.ContractFactory( + SigMint__factory.abi, + SigMint__factory.bytecode, + ) + .connect(signer) + .deploy(); + await sigMintDeployer.deployed(); + await registryContract.grantRole( await registryContract.OPERATOR_ROLE(), contactDeployer.address, @@ -157,6 +168,7 @@ before(async () => { ): Promise { switch (contractType) { case Vote.contractType: + case SignatureDrop.contractType: return await contractFactory.deploy(); case Marketplace.contractType: const nativeTokenWrapperAddress = getNativeTokenByChainId( @@ -196,6 +208,9 @@ before(async () => { case NFTDrop.contractType: factory = DropERC721__factory; break; + case SignatureDrop.contractType: + factory = SignatureDrop__factory; + break; case Edition.contractType: factory = TokenERC1155__factory; break; @@ -241,6 +256,7 @@ before(async () => { process.env.contractPublisherAddress = contractPublisher.address; process.env.contractDeployerAddress = contactDeployer.address; process.env.contractMetadataRegistryAddress = metadataRegistry.address; + process.env.sigMintDeployerAddress = sigMintDeployer.address; storage = new MockStorage(); sdk = new ThirdwebSDK( diff --git a/test/signature-mint-721-with-quantity-signature-minting.test.ts b/test/signature-mint-721-with-quantity-signature-minting.test.ts new file mode 100644 index 000000000..b02c24d6f --- /dev/null +++ b/test/signature-mint-721-with-quantity-signature-minting.test.ts @@ -0,0 +1,774 @@ +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; +import { assert, expect } from "chai"; +import { BigNumber, ethers } from "ethers"; +import { + createSnapshot, + PayloadToSign721withQuantity, + SignatureDrop, + Token, +} from "../src"; +import { expectError, sdk, signers, storage } from "./before-setup"; +import { SignedPayload721WithQuantitySignature } from "../src/schema/contracts/common/signature"; +import { NATIVE_TOKEN_ADDRESS } from "../src/constants/currency"; +import invariant from "tiny-invariant"; +import { MerkleTree } from "merkletreejs"; +import keccak256 from "keccak256"; + +global.fetch = require("cross-fetch"); + +describe("ERC 721 with Signature minting", async () => { + let signatureDropContract: SignatureDrop; + let customTokenContract: Token; + let tokenAddress: string; + + let adminWallet: SignerWithAddress, + samWallet: SignerWithAddress, + abbyWallet: SignerWithAddress, + bobWallet: SignerWithAddress, + w1: SignerWithAddress, + w2: SignerWithAddress, + w3: SignerWithAddress, + w4: SignerWithAddress; + + let meta: PayloadToSign721withQuantity; + + before(() => { + [adminWallet, samWallet, bobWallet, abbyWallet, w1, w2, w3, w4] = signers; + }); + + beforeEach(async () => { + sdk.updateSignerOrProvider(adminWallet); + + signatureDropContract = sdk.getSignatureDrop( + await sdk.deployer.deployBuiltInContract(SignatureDrop.contractType, { + name: "OUCH VOUCH", + symbol: "VOUCH", + primary_sale_recipient: adminWallet.address, + seller_fee_basis_points: 0, + }), + ); + + meta = { + currencyAddress: NATIVE_TOKEN_ADDRESS, + metadata: { + name: "OUCH VOUCH", + }, + price: "1", + quantity: 1, + to: samWallet.address, + }; + + customTokenContract = sdk.getToken( + await sdk.deployer.deployBuiltInContract(Token.contractType, { + name: "Test", + symbol: "TEST", + primary_sale_recipient: adminWallet.address, + }), + ); + await customTokenContract.mintBatchTo([ + { + toAddress: samWallet.address, + amount: 1000, + }, + { + toAddress: adminWallet.address, + amount: 1000, + }, + ]); + tokenAddress = customTokenContract.getAddress(); + }); + + describe("Generating Signatures", () => { + let goodPayload: SignedPayload721WithQuantitySignature; + let badPayload: SignedPayload721WithQuantitySignature; + + beforeEach(async () => { + goodPayload = await signatureDropContract.signature.generate(meta); + badPayload = await signatureDropContract.signature.generate(meta); + badPayload.payload.price = "0"; + }); + + it("should generate a valid signature", async () => { + const valid = await signatureDropContract.signature.verify(goodPayload); + assert.isTrue(valid, "This voucher should be valid"); + }); + + it("should reject invalid signatures", async () => { + const invalid = await signatureDropContract.signature.verify(badPayload); + assert.isFalse( + invalid, + "This voucher should be invalid because the signature is invalid", + ); + }); + + it("should reject invalid vouchers", async () => { + goodPayload.payload.price = "0"; + const invalidModified = await signatureDropContract.signature.verify( + goodPayload, + ); + assert.isFalse( + invalidModified, + "This voucher should be invalid because the price was changed", + ); + }); + + it("should generate a valid batch of signatures", async () => { + const input = [ + { + ...meta, + metadata: { + name: "OUCH VOUCH 0", + }, + }, + { + ...meta, + metadata: { + name: "OUCH VOUCH 1", + }, + }, + { + ...meta, + metadata: { + name: "OUCH VOUCH 2", + }, + }, + ]; + + await signatureDropContract.createBatch([ + { + name: "OUCH VOUCH 0", + }, + { + name: "OUCH VOUCH 1", + }, + { + name: "OUCH VOUCH 2", + }, + ]); + + const batch = await signatureDropContract.signature.generateBatch(input); + + for (let i = 0; i < batch.length; i++) { + const entry = batch[i]; + const tx = await signatureDropContract.signature.mint(entry); + const mintedId = (await signatureDropContract.get(tx.id)).metadata.id; + const nft = await signatureDropContract.get(mintedId); + assert.equal(input[i].metadata.name, nft.metadata.name); + } + }); + }); + + describe("Claiming", async () => { + let v1: SignedPayload721WithQuantitySignature, + v2: SignedPayload721WithQuantitySignature; + + beforeEach(async () => { + v1 = await signatureDropContract.signature.generate(meta); + v2 = await signatureDropContract.signature.generate(meta); + + await signatureDropContract.createBatch([ + { + name: "Test1", + }, + { + name: "Test2", + }, + ]); + }); + + it("should mint with URI", async () => { + const uri = await storage.uploadMetadata({ + name: "Test1", + }); + const toSign = { + metadata: uri, + quantity: 1, + }; + const payload = await signatureDropContract.signature.generate(toSign); + const tx = await signatureDropContract.signature.mint(payload); + const nft = await signatureDropContract.get(tx.id); + assert.equal(nft.metadata.name, "Test1"); + }); + + it("should mint batch with URI", async () => { + const uri1 = await storage.uploadMetadata({ + name: "Test1", + }); + const uri2 = await storage.uploadMetadata({ + name: "Test2", + }); + const toSign1 = { + metadata: uri1, + quantity: 1, + }; + const toSign2 = { + metadata: uri2, + quantity: 1, + }; + const payloads = await signatureDropContract.signature.generateBatch([ + toSign1, + toSign2, + ]); + const tx = await signatureDropContract.signature.mintBatch(payloads); + const nft1 = await signatureDropContract.get(tx[0].id); + assert.equal(nft1.metadata.name, "Test1"); + const nft2 = await signatureDropContract.get(tx[1].id); + assert.equal(nft2.metadata.name, "Test2"); + }); + + it("should allow a valid voucher to mint", async () => { + await sdk.updateSignerOrProvider(samWallet); + const tx = await signatureDropContract.signature.mint(v1); + const newId = (await signatureDropContract.get(tx.id)).metadata.id; + assert.equal(newId.toString(), "0"); + + await sdk.updateSignerOrProvider(samWallet); + const tx2 = await signatureDropContract.signature.mint(v2); + const newId2 = (await signatureDropContract.get(tx2.id)).metadata.id; + assert.equal(newId2.toString(), "1"); + }); + + it("should mint the right metadata", async () => { + const tx = await signatureDropContract.signature.mint(v1); + const nft = await signatureDropContract.get(tx.id); + assert.equal(nft.metadata.name, "Test1"); + + const tx2 = await signatureDropContract.signature.mint(v2); + const nft2 = await signatureDropContract.get(tx2.id); + assert.equal(nft2.metadata.name, "Test2"); + }); + + it("should mint the right custom token price", async () => { + const oldBalance = await samWallet.getBalance(); + const payload = await signatureDropContract.signature.generate({ + price: 1, + currencyAddress: tokenAddress, + metadata: { + name: "custom token test", + }, + quantity: 1, + mintEndTime: new Date(Date.now() + 60 * 60 * 24 * 1000 * 1000), + mintStartTime: new Date(), + }); + await sdk.updateSignerOrProvider(samWallet); + await signatureDropContract.signature.mint(payload); + const newBalance = await samWallet.getBalance(); + assert( + oldBalance.sub(newBalance).gte(BigNumber.from(1)), + "balance doesn't match", + ); + }); + + it("should mint the right native price", async () => { + const oldBalance = await samWallet.getBalance(); + const payload = await signatureDropContract.signature.generate({ + price: 1, + metadata: { + name: "native token test", + }, + quantity: 1, + mintEndTime: new Date(Date.now() + 60 * 60 * 24 * 1000 * 1000), + mintStartTime: new Date(), + }); + await sdk.updateSignerOrProvider(samWallet); + await signatureDropContract.signature.mint(payload); + const newBalance = await samWallet.getBalance(); + assert( + oldBalance.sub(newBalance).gte(BigNumber.from(1)), + "balance doesn't match", + ); + }); + + it("should mint the right native price with multiple tokens", async () => { + const oldBalance = await samWallet.getBalance(); + const payload = await signatureDropContract.signature.generate({ + price: 1, + metadata: { + name: "native token test with quantity", + }, + quantity: 2, + mintEndTime: new Date(Date.now() + 60 * 60 * 24 * 1000 * 1000), + mintStartTime: new Date(), + }); + await sdk.updateSignerOrProvider(samWallet); + await signatureDropContract.signature.mint(payload); + const newBalance = await samWallet.getBalance(); + assert( + oldBalance.sub(newBalance).gte(BigNumber.from(2)), + "balance doesn't match", + ); + }); + }); + + describe("Delay Reveal", () => { + it("metadata should reveal correctly", async () => { + await signatureDropContract.revealer.createDelayedRevealBatch( + { + name: "Placeholder #1", + }, + [{ name: "NFT #1" }, { name: "NFT #2" }], + "my secret password", + ); + + expect((await signatureDropContract.get("0")).metadata.name).to.be.equal( + "Placeholder #1", + ); + + await signatureDropContract.revealer.reveal(0, "my secret password"); + + expect((await signatureDropContract.get("0")).metadata.name).to.be.equal( + "NFT #1", + ); + }); + + it("different reveal order and should return correct unreveal list", async () => { + await signatureDropContract.revealer.createDelayedRevealBatch( + { + name: "Placeholder #1", + }, + [ + { + name: "NFT #1", + }, + { + name: "NFT #2", + }, + ], + "my secret key", + ); + + await signatureDropContract.revealer.createDelayedRevealBatch( + { + name: "Placeholder #2", + }, + [ + { + name: "NFT #3", + }, + { + name: "NFT #4", + }, + ], + "my secret key", + ); + + await signatureDropContract.createBatch([ + { + name: "NFT #00", + }, + { + name: "NFT #01", + }, + ]); + + await signatureDropContract.revealer.createDelayedRevealBatch( + { + name: "Placeholder #3", + }, + [ + { + name: "NFT #5", + }, + { + name: "NFT #6", + }, + { + name: "NFT #7", + }, + ], + "my secret key", + ); + + let unrevealList = + await signatureDropContract.revealer.getBatchesToReveal(); + expect(unrevealList.length).to.be.equal(3); + expect(unrevealList[0].batchId.toNumber()).to.be.equal(0); + expect(unrevealList[0].placeholderMetadata.name).to.be.equal( + "Placeholder #1", + ); + expect(unrevealList[1].batchId.toNumber()).to.be.equal(1); + expect(unrevealList[1].placeholderMetadata.name).to.be.equal( + "Placeholder #2", + ); + // skipped 2 because it is a revealed batch + expect(unrevealList[2].batchId.toNumber()).to.be.equal(3); + expect(unrevealList[2].placeholderMetadata.name).to.be.equal( + "Placeholder #3", + ); + + await signatureDropContract.revealer.reveal( + unrevealList[0].batchId, + "my secret key", + ); + + unrevealList = await signatureDropContract.revealer.getBatchesToReveal(); + expect(unrevealList.length).to.be.equal(2); + expect(unrevealList[0].batchId.toNumber()).to.be.equal(1); + expect(unrevealList[0].placeholderMetadata.name).to.be.equal( + "Placeholder #2", + ); + expect(unrevealList[1].batchId.toNumber()).to.be.equal(3); + expect(unrevealList[1].placeholderMetadata.name).to.be.equal( + "Placeholder #3", + ); + + await signatureDropContract.revealer.reveal( + unrevealList[unrevealList.length - 1].batchId, + "my secret key", + ); + + unrevealList = await signatureDropContract.revealer.getBatchesToReveal(); + expect(unrevealList.length).to.be.equal(1); + expect(unrevealList[0].batchId.toNumber()).to.be.equal(1); + expect(unrevealList[0].placeholderMetadata.name).to.be.equal( + "Placeholder #2", + ); + }); + + it("should not be able to re-used published password for next batch", async () => { + await signatureDropContract.revealer.createDelayedRevealBatch( + { + name: "Placeholder #1", + }, + [{ name: "NFT #1" }, { name: "NFT #2" }], + "my secret password", + ); + await signatureDropContract.revealer.createDelayedRevealBatch( + { + name: "Placeholder #2", + }, + [{ name: "NFT #3" }, { name: "NFT #4" }], + "my secret password", + ); + await signatureDropContract.revealer.reveal(0, "my secret password"); + const transactions = + (await adminWallet.provider?.getBlockWithTransactions("latest")) + ?.transactions ?? []; + + const { _index, _key } = signatureDropContract.encoder.decode( + "reveal", + transactions[0].data, + ); + + // re-using broadcasted _key to decode :) + try { + await signatureDropContract.revealer.reveal(_index.add(1), _key); + assert.fail("should not be able to re-used published password"); + } catch (e) { + expect((e as Error).message).to.be.equal("invalid password"); + } + + // original password should work + await signatureDropContract.revealer.reveal(1, "my secret password"); + }); + }); + + describe("Claim Conditions", () => { + it("should allow a snapshot to be set", async () => { + await signatureDropContract.createBatch([ + { name: "test", description: "test" }, + { name: "test", description: "test" }, + ]); + + await signatureDropContract.claimConditions.set([ + { + snapshot: [samWallet.address], + }, + ]); + const conditions = await signatureDropContract.claimConditions.getAll(); + assert.lengthOf(conditions, 1); + invariant(conditions[0].snapshot); + expect(conditions[0].snapshot[0].address).to.eq(samWallet.address); + }); + + it("should remove merkles from the metadata when claim conditions are removed", async () => { + await signatureDropContract.claimConditions.set([ + { + startTime: new Date(), + waitInSeconds: 10, + snapshot: [bobWallet.address, samWallet.address, abbyWallet.address], + }, + { + startTime: new Date(Date.now() + 60 * 60 * 1000), + snapshot: [bobWallet.address], + }, + ]); + + const metadata = await signatureDropContract.metadata.get(); + const merkles = metadata.merkle; + + expect(merkles).have.property( + "0xd89eb21bf7ee4dd07d88e8f90a513812d9d38ac390a58722762c9f3afc4e0feb", + ); + + expect(merkles).have.property( + "0xb1a60ad68b77609a455696695fbdd02b850d03ec285e7fe1f4c4093797457b24", + ); + + const roots = (await signatureDropContract.claimConditions.getAll()).map( + (c) => c.merkleRootHash, + ); + expect(roots).length(2); + + await signatureDropContract.claimConditions.set([{}]); + const newMetadata = await signatureDropContract.metadata.get(); + const newMerkles = newMetadata.merkle; + expect(JSON.stringify(newMerkles)).to.eq("{}"); + }); + + it("allow all addresses in the merkle tree to claim", async () => { + const testWallets: SignerWithAddress[] = [ + bobWallet, + samWallet, + abbyWallet, + w1, + w2, + w3, + w4, + ]; + const members = testWallets.map((w, i) => + i % 3 === 0 + ? w.address.toLowerCase() + : i % 3 === 1 + ? w.address.toUpperCase().replace("0X", "0x") + : w.address, + ); + + await signatureDropContract.claimConditions.set([ + { + snapshot: members, + }, + ]); + const metadata = []; + for (let i = 0; i < 10; i++) { + metadata.push({ + name: `test ${i}`, + }); + } + await signatureDropContract.createBatch(metadata); + + /** + * Claiming 1 tokens with proofs: 0xe9707d0e6171f728f7473c24cc0432a9b07eaaf1efed6a137a4a8c12c79552d9,0xb1a5bda84b83f7f014abcf0cf69cab5a4de1c3ececa8123a5e4aaacb01f63f83 + */ + + for (const member of testWallets) { + await sdk.updateSignerOrProvider(member); + await signatureDropContract.claim(1); + } + }); + + it("allow one address in the merkle tree to claim", async () => { + const testWallets: SignerWithAddress[] = [bobWallet]; + const members = testWallets.map((w) => w.address); + + await signatureDropContract.claimConditions.set([ + { + snapshot: members, + }, + ]); + + const metadata = []; + for (let i = 0; i < 2; i++) { + metadata.push({ + name: `test ${i}`, + }); + } + await signatureDropContract.createBatch(metadata); + + /** + * Claiming 1 tokens with proofs: 0xe9707d0e6171f728f7473c24cc0432a9b07eaaf1efed6a137a4a8c12c79552d9,0xb1a5bda84b83f7f014abcf0cf69cab5a4de1c3ececa8123a5e4aaacb01f63f83 + */ + + for (const member of testWallets) { + await sdk.updateSignerOrProvider(member); + await signatureDropContract.claim(1); + } + + try { + await sdk.updateSignerOrProvider(samWallet); + await signatureDropContract.claim(1); + assert.fail("should have thrown"); + } catch (e) { + // expected + } + }); + + it("should correctly upload metadata for each nft", async () => { + const metadatas = []; + for (let i = 0; i < 100; i++) { + metadatas.push({ + name: `test ${i}`, + }); + } + await signatureDropContract.createBatch(metadatas); + const all = await signatureDropContract.getAll(); + expect(all.length).to.eq(100); + await signatureDropContract.claimConditions.set([{}]); + await signatureDropContract.claim(1); + const claimed = await signatureDropContract.totalClaimedSupply(); + const unclaimed = await signatureDropContract.totalUnclaimedSupply(); + expect(claimed.toNumber()).to.eq(1); + expect(unclaimed.toNumber()).to.eq(99); + }); + + it("should correctly update total supply after burning", async () => { + const metadatas = []; + for (let i = 0; i < 20; i++) { + metadatas.push({ + name: `test ${i}`, + }); + } + await signatureDropContract.createBatch(metadatas); + await signatureDropContract.claimConditions.set([{}]); + await signatureDropContract.claim(10); + const ts = await signatureDropContract.totalSupply(); + expect(ts.toNumber()).to.eq(20); + await signatureDropContract.burn(0); + const ts2 = await signatureDropContract.totalSupply(); + expect(ts2.toNumber()).to.eq(20); + }); + + it("should not allow claiming to someone not in the merkle tree", async () => { + await signatureDropContract.claimConditions.set( + [ + { + snapshot: [ + bobWallet.address, + samWallet.address, + abbyWallet.address, + ], + }, + ], + false, + ); + await signatureDropContract.createBatch([ + { name: "name", description: "description" }, + ]); + + await sdk.updateSignerOrProvider(w1); + try { + await signatureDropContract.claim(1); + } catch (err: any) { + expect(err).to.have.property( + "message", + "No claim found for this address", + "", + ); + return; + } + assert.fail("should not reach this point, claim should have failed"); + }); + + it("should allow claims with default settings", async () => { + await signatureDropContract.createBatch([ + { name: "name", description: "description" }, + ]); + await signatureDropContract.claimConditions.set([{}]); + await signatureDropContract.claim(1); + }); + + it("should allow setting max claims per wallet", async () => { + await signatureDropContract.createBatch([ + { name: "name", description: "description" }, + { name: "name2", description: "description" }, + { name: "name3", description: "description" }, + { name: "name4", description: "description" }, + ]); + await signatureDropContract.claimConditions.set([ + { + snapshot: [ + { address: w1.address, maxClaimable: 2 }, + { address: w2.address, maxClaimable: 1 }, + ], + }, + ]); + await sdk.updateSignerOrProvider(w1); + const tx = await signatureDropContract.claim(2); + expect(tx.length).to.eq(2); + try { + await sdk.updateSignerOrProvider(w2); + await signatureDropContract.claim(2); + } catch (e) { + expectError(e, "invalid quantity proof"); + } + }); + + it("should generate valid proofs", async () => { + const members = [ + bobWallet.address, + samWallet.address, + abbyWallet.address, + w1.address, + w2.address, + w3.address, + w4.address, + ]; + + const hashedLeafs = members.map((l) => + ethers.utils.solidityKeccak256(["address", "uint256"], [l, 0]), + ); + const tree = new MerkleTree(hashedLeafs, keccak256, { + sort: true, + sortLeaves: true, + sortPairs: true, + }); + const input = members.map((address) => ({ + address, + maxClaimable: 0, + })); + const snapshot = await createSnapshot(input, 0, storage); + for (const leaf of members) { + const expectedProof = tree.getHexProof( + ethers.utils.solidityKeccak256(["address", "uint256"], [leaf, 0]), + ); + + const actualProof = snapshot.snapshot.claims.find( + (c) => c.address === leaf, + ); + assert.isDefined(actualProof); + expect(actualProof?.proof).to.include.ordered.members(expectedProof); + + const verified = tree.verify( + actualProof?.proof as string[], + ethers.utils.solidityKeccak256(["address", "uint256"], [leaf, 0]), + tree.getHexRoot(), + ); + expect(verified).to.eq(true); + } + }); + + it("should return the newly claimed token", async () => { + await signatureDropContract.claimConditions.set([{}]); + await signatureDropContract.createBatch([ + { + name: "test 0", + }, + { + name: "test 1", + }, + { + name: "test 2", + }, + ]); + + try { + await signatureDropContract.createBatch([ + { + name: "test 0", + }, + { + name: "test 1", + }, + { + name: "test 2", + }, + ]); + } catch (err) { + expect(err).to.have.property("message", "Batch already created!", ""); + } + + const token = await signatureDropContract.claim(2); + assert.lengthOf(token, 2); + }); + }); +}); diff --git a/yarn.lock b/yarn.lock index 357643bd8..c8de650e1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -802,10 +802,10 @@ "@swc/core-win32-ia32-msvc" "1.2.194" "@swc/core-win32-x64-msvc" "1.2.194" -"@thirdweb-dev/contracts@2.3.8": - version "2.3.8" - resolved "https://registry.yarnpkg.com/@thirdweb-dev/contracts/-/contracts-2.3.8.tgz#7c565b4d4f1917bbbc76dc48c9f6bffc8c6eabdd" - integrity sha512-cSVwIzPSYaS8gFlkq4r+G9UyVwr/62uE+p6SzrZFXYCbeCJwJTml3VnrVEptJ7g8RukL4Lh6uwQ6IPtgMTY4cQ== +"@thirdweb-dev/contracts@2.3.9-1": + version "2.3.9-1" + resolved "https://registry.yarnpkg.com/@thirdweb-dev/contracts/-/contracts-2.3.9-1.tgz#98dd38ed67ee505a38cf888d5ccd36e27c606674" + integrity sha512-AMbTw92sKZgIS5udTClX+RNRVASbcrVNjQzlivLoxJR25Zhi91Ul+OcllYFANI0WTslYR6Riq9NGsAZRW8I1FA== "@tsconfig/node10@^1.0.7": version "1.0.8"