From d566008ffec5fcfa057e8483098087a5a5b10e20 Mon Sep 17 00:00:00 2001 From: Joaquim Verges Date: Wed, 22 Jun 2022 14:47:54 -0700 Subject: [PATCH 1/2] more flexible API for setting batch claim conditions on edition drops --- docs/sdk.claimconditionsfortoken.md | 16 ++++ docs/sdk.droperc1155claimconditions.md | 2 +- ...sdk.droperc1155claimconditions.setbatch.md | 34 ++++---- docs/sdk.md | 1 + etc/sdk.api.md | 8 +- src/common/error.ts | 4 +- .../classes/drop-erc1155-claim-conditions.ts | 84 +++++++++++-------- .../claim-conditions/claim-conditions.ts | 7 +- test/edition-drop.test.ts | 35 +++++--- 9 files changed, 128 insertions(+), 63 deletions(-) create mode 100644 docs/sdk.claimconditionsfortoken.md diff --git a/docs/sdk.claimconditionsfortoken.md b/docs/sdk.claimconditionsfortoken.md new file mode 100644 index 000000000..80cbfe85c --- /dev/null +++ b/docs/sdk.claimconditionsfortoken.md @@ -0,0 +1,16 @@ + + +[Home](./index.md) > [@thirdweb-dev/sdk](./sdk.md) > [ClaimConditionsForToken](./sdk.claimconditionsfortoken.md) + +## ClaimConditionsForToken type + +Signature: + +```typescript +export declare type ClaimConditionsForToken = { + tokenId: BigNumberish; + claimConditions: ClaimConditionInput[]; +}; +``` +References: [ClaimConditionInput](./sdk.claimconditioninput.md) + diff --git a/docs/sdk.droperc1155claimconditions.md b/docs/sdk.droperc1155claimconditions.md index a1a894586..7090d8a4c 100644 --- a/docs/sdk.droperc1155claimconditions.md +++ b/docs/sdk.droperc1155claimconditions.md @@ -27,6 +27,6 @@ export declare class DropErc1155ClaimConditions | [getAll(tokenId)](./sdk.droperc1155claimconditions.getall.md) | | Get all the claim conditions | | [getClaimIneligibilityReasons(tokenId, quantity, addressToCheck)](./sdk.droperc1155claimconditions.getclaimineligibilityreasons.md) | | For any claim conditions that a particular wallet is violating, this function returns human-readable information about the breaks in the condition that can be used to inform the user. | | [set(tokenId, claimConditionInputs, resetClaimEligibilityForAll)](./sdk.droperc1155claimconditions.set.md) | | Set claim conditions on a single NFT | -| [setBatch(tokenIds, claimConditionInputs, resetClaimEligibilityForAll)](./sdk.droperc1155claimconditions.setbatch.md) | | Set claim conditions on multiple NFTs at once | +| [setBatch(claimConditionsForToken, resetClaimEligibilityForAll)](./sdk.droperc1155claimconditions.setbatch.md) | | Set claim conditions on multiple NFTs at once | | [update(tokenId, index, claimConditionInput)](./sdk.droperc1155claimconditions.update.md) | | Update a single claim condition with new data. | diff --git a/docs/sdk.droperc1155claimconditions.setbatch.md b/docs/sdk.droperc1155claimconditions.setbatch.md index f3ad054ed..d5d77426a 100644 --- a/docs/sdk.droperc1155claimconditions.setbatch.md +++ b/docs/sdk.droperc1155claimconditions.setbatch.md @@ -9,7 +9,7 @@ Set claim conditions on multiple NFTs at once Signature: ```typescript -setBatch(tokenIds: BigNumberish[], claimConditionInputs: ClaimConditionInput[], resetClaimEligibilityForAll?: boolean): Promise<{ +setBatch(claimConditionsForToken: ClaimConditionsForToken[], resetClaimEligibilityForAll?: boolean): Promise<{ receipt: ethers.providers.TransactionReceipt; }>; ``` @@ -18,8 +18,7 @@ setBatch(tokenIds: BigNumberish[], claimConditionInputs: ClaimConditionInput[], | Parameter | Type | Description | | --- | --- | --- | -| tokenIds | BigNumberish\[\] | the token ids to set the claim conditions on | -| claimConditionInputs | [ClaimConditionInput](./sdk.claimconditioninput.md)\[\] | The claim conditions | +| claimConditionsForToken | [ClaimConditionsForToken](./sdk.claimconditionsfortoken.md)\[\] | The claim conditions for each NFT | | resetClaimEligibilityForAll | boolean | (Optional) Whether to reset the state of who already claimed NFTs previously | Returns: @@ -34,22 +33,25 @@ Sets the claim conditions that need to be fulfilled by users to claim the given ```javascript -const presaleStartTime = new Date(); -const publicSaleStartTime = new Date(Date.now() + 60 * 60 * 24 * 1000); -const claimConditions = [ +const claimConditionsForTokens = [ { - 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 + tokenId: 0, + claimConditions: [{ + startTime: new Date(), // start the claim phase now + maxQuantity: 2, // limit how many mints for this tokenId + price: 0.01, // price for this tokenId + snapshot: ['0x...', '0x...'], // limit minting to only certain addresses + }] }, { - startTime: publicSaleStartTime, // 24h after presale, start public sale - price: 0.08, // public sale price - } -]); + tokenId: 1, + claimConditions: [{ + startTime: new Date(), + price: 0.08, // different price for this tokenId + }] + }, +]; -const tokenIds = [0,1,2]; // the ids of the NFTs to set claim conditions on -await dropContract.claimConditions.setBatch(tokenIds, claimConditions); +await dropContract.claimConditions.setBatch(claimConditionsForTokens); ``` diff --git a/docs/sdk.md b/docs/sdk.md index 2c06517cc..609b7f399 100644 --- a/docs/sdk.md +++ b/docs/sdk.md @@ -131,6 +131,7 @@ | [BufferOrStringWithName](./sdk.bufferorstringwithname.md) | | | [ClaimCondition](./sdk.claimcondition.md) | Represents a claim condition fetched from the SDK | | [ClaimConditionInput](./sdk.claimconditioninput.md) | Input model to create a claim condition with optional snapshot of wallets | +| [ClaimConditionsForToken](./sdk.claimconditionsfortoken.md) | | | [ClaimVerification](./sdk.claimverification.md) | | | [ContractEvent](./sdk.contractevent.md) | | | [ContractForContractType](./sdk.contractforcontracttype.md) | | diff --git a/etc/sdk.api.md b/etc/sdk.api.md index cdd5441e7..b07738e61 100644 --- a/etc/sdk.api.md +++ b/etc/sdk.api.md @@ -407,6 +407,12 @@ export const ClaimConditionOutputSchema: z.ZodObject; +// @public (undocumented) +export type ClaimConditionsForToken = { + tokenId: BigNumberish; + claimConditions: ClaimConditionInput[]; +}; + // @public (undocumented) export enum ClaimEligibility { // (undocumented) @@ -912,7 +918,7 @@ export class DropErc1155ClaimConditions { getAll(tokenId: BigNumberish): Promise; getClaimIneligibilityReasons(tokenId: BigNumberish, quantity: BigNumberish, addressToCheck?: string): Promise; set(tokenId: BigNumberish, claimConditionInputs: ClaimConditionInput[], resetClaimEligibilityForAll?: boolean): Promise; - setBatch(tokenIds: BigNumberish[], claimConditionInputs: ClaimConditionInput[], resetClaimEligibilityForAll?: boolean): Promise<{ + setBatch(claimConditionsForToken: ClaimConditionsForToken[], resetClaimEligibilityForAll?: boolean): Promise<{ receipt: ethers.providers.TransactionReceipt; }>; update(tokenId: BigNumberish, index: number, claimConditionInput: ClaimConditionInput): Promise; diff --git a/src/common/error.ts b/src/common/error.ts index 305a79993..b1dd31c7f 100644 --- a/src/common/error.ts +++ b/src/common/error.ts @@ -319,7 +319,9 @@ export async function convertToTWError( // not sure what this is, just throw it back return error; } - const reason = parseMessageParts(/.*?"message[^a-zA-Z0-9]*([^"\\]*).*?/, raw); + const reason = + error.reason || + parseMessageParts(/.*?"message[^a-zA-Z0-9]*([^"\\]*).*?/, raw); const data = parseMessageParts(/.*?"data[^a-zA-Z0-9]*([^"\\]*).*?/, raw); const rpcUrl = parseMessageParts(/.*?"url[^a-zA-Z0-9]*([^"\\]*).*?/, raw); let from = parseMessageParts(/.*?"from[^a-zA-Z0-9]*([^"\\]*).*?/, raw); diff --git a/src/core/classes/drop-erc1155-claim-conditions.ts b/src/core/classes/drop-erc1155-claim-conditions.ts index ef330a9bf..251348df0 100644 --- a/src/core/classes/drop-erc1155-claim-conditions.ts +++ b/src/core/classes/drop-erc1155-claim-conditions.ts @@ -5,7 +5,11 @@ import { DropERC1155, IERC20, IERC20__factory } from "contracts"; import { BigNumber, BigNumberish, constants, ethers } from "ethers"; import { isNativeToken } from "../../common/currency"; import { ContractWrapper } from "./contract-wrapper"; -import { ClaimCondition, ClaimConditionInput } from "../../types"; +import { + ClaimCondition, + ClaimConditionInput, + ClaimConditionsForToken, +} from "../../types"; import deepEqual from "fast-deep-equal"; import { ClaimEligibility } from "../../enums"; import { TransactionResult } from "../index"; @@ -302,8 +306,12 @@ export class DropErc1155ClaimConditions { resetClaimEligibilityForAll = false, ): Promise { return this.setBatch( - [tokenId], - claimConditionInputs, + [ + { + tokenId, + claimConditions: claimConditionInputs, + }, + ], resetClaimEligibilityForAll, ); } @@ -315,47 +323,57 @@ export class DropErc1155ClaimConditions { * * @example * ```javascript - * const presaleStartTime = new Date(); - * const publicSaleStartTime = new Date(Date.now() + 60 * 60 * 24 * 1000); - * const claimConditions = [ + * const claimConditionsForTokens = [ * { - * 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 + * tokenId: 0, + * claimConditions: [{ + * startTime: new Date(), // start the claim phase now + * maxQuantity: 2, // limit how many mints for this tokenId + * price: 0.01, // price for this tokenId + * snapshot: ['0x...', '0x...'], // limit minting to only certain addresses + * }] * }, * { - * startTime: publicSaleStartTime, // 24h after presale, start public sale - * price: 0.08, // public sale price - * } - * ]); + * tokenId: 1, + * claimConditions: [{ + * startTime: new Date(), + * price: 0.08, // different price for this tokenId + * }] + * }, + * ]; * - * const tokenIds = [0,1,2]; // the ids of the NFTs to set claim conditions on - * await dropContract.claimConditions.setBatch(tokenIds, claimConditions); + * await dropContract.claimConditions.setBatch(claimConditionsForTokens); * ``` * - * @param tokenIds - the token ids to set the claim conditions on - * @param claimConditionInputs - The claim conditions + * @param claimConditionsForToken - The claim conditions for each NFT * @param resetClaimEligibilityForAll - Whether to reset the state of who already claimed NFTs previously */ public async setBatch( - tokenIds: BigNumberish[], - claimConditionInputs: ClaimConditionInput[], + claimConditionsForToken: ClaimConditionsForToken[], resetClaimEligibilityForAll = false, ) { - // process inputs - const { snapshotInfos, sortedConditions } = - await processClaimConditionInputs( - claimConditionInputs, - 0, - this.contractWrapper.getProvider(), - this.storage, - ); - const merkleInfo: { [key: string]: string } = {}; - snapshotInfos.forEach((s) => { - merkleInfo[s.merkleRoot] = s.snapshotUri; - }); + const processedClaimConditions = await Promise.all( + claimConditionsForToken.map(async ({ tokenId, claimConditions }) => { + // process inputs + const { snapshotInfos, sortedConditions } = + await processClaimConditionInputs( + claimConditions, + 0, + this.contractWrapper.getProvider(), + this.storage, + ); + + snapshotInfos.forEach((s) => { + merkleInfo[s.merkleRoot] = s.snapshotUri; + }); + return { + tokenId, + sortedConditions, + }; + }), + ); + const metadata = await this.metadata.get(); const encoded = []; @@ -382,7 +400,7 @@ export class DropErc1155ClaimConditions { ); } - tokenIds.forEach((tokenId) => { + processedClaimConditions.forEach(({ tokenId, sortedConditions }) => { encoded.push( this.contractWrapper.readContract.interface.encodeFunctionData( "setClaimConditions", diff --git a/src/types/claim-conditions/claim-conditions.ts b/src/types/claim-conditions/claim-conditions.ts index fcb2a130f..08cc68637 100644 --- a/src/types/claim-conditions/claim-conditions.ts +++ b/src/types/claim-conditions/claim-conditions.ts @@ -9,7 +9,7 @@ import { ClaimConditionOutputSchema, PartialClaimConditionInputSchema, } from "../../schema/contracts/common/claim-conditions"; -import { BigNumber, BytesLike, CallOverrides } from "ethers"; +import { BigNumber, BigNumberish, BytesLike, CallOverrides } from "ethers"; /** * Represents a claim condition fetched from the SDK @@ -56,3 +56,8 @@ export type ClaimVerification = { price: BigNumber; currencyAddress: string; }; + +export type ClaimConditionsForToken = { + tokenId: BigNumberish; + claimConditions: ClaimConditionInput[]; +}; diff --git a/test/edition-drop.test.ts b/test/edition-drop.test.ts index bddce7895..2abb16249 100644 --- a/test/edition-drop.test.ts +++ b/test/edition-drop.test.ts @@ -387,16 +387,31 @@ describe("Edition Drop Contract", async () => { description: "test2", }, ]); - await bdContract.claimConditions.setBatch( - [0, 1], - [ - { - price: 1, - }, - ], - ); - await bdContract.claim("0", 1); - await bdContract.claim("1", 1); + await bdContract.claimConditions.setBatch([ + { + tokenId: 0, + claimConditions: [ + { + price: 1, + }, + ], + }, + { + tokenId: 1, + claimConditions: [ + { + price: 0.1, + maxQuantity: 1, + }, + ], + }, + ]); + await bdContract.claim("0", 2); + try { + await bdContract.claim("1", 2); + } catch (e) { + expectError(e, "exceed max mint supply"); + } }); describe("eligibility", () => { From 1f2a00928eba27fa32b9c6d6a9902b521becd02e Mon Sep 17 00:00:00 2001 From: Joaquim Verges Date: Wed, 22 Jun 2022 15:56:43 -0700 Subject: [PATCH 2/2] fix contract call with no params that want to override txOptions --- src/contracts/smart-contract.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/contracts/smart-contract.ts b/src/contracts/smart-contract.ts index a7f96fa87..c0fd4e3c6 100644 --- a/src/contracts/smart-contract.ts +++ b/src/contracts/smart-contract.ts @@ -180,7 +180,7 @@ export class SmartContract< // parse last arg as tx options if present let txOptions: CallOverrides | undefined; try { - if (args.length > 1 && typeof args[args.length - 1] === "object") { + if (args.length > 0 && typeof args[args.length - 1] === "object") { const last = args[args.length - 1]; txOptions = CallOverrideSchema.parse(last); // if call overrides found, remove it from args array