From f9c351c22d9c869770dad13332f9c23d8cb84ed7 Mon Sep 17 00:00:00 2001 From: jnsdls Date: Wed, 4 Sep 2024 23:19:30 +0000 Subject: [PATCH] [Update] Refactor claim condition retrieval and add support check (#4391) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## PR-Codex overview This PR enhances ERC721 Drop ClaimCondition functionality by adding new methods and improving existing ones. ### Detailed summary - Added `claimCondition` method to `IDrop.json` - Updated `getClaimParams` in `get-claim-params.ts` to use `ZERO_ADDRESS` - Added `isClaimToSupported` and `isLazyMintSupported` functions - Updated `lazyMint` and `claimTo` functions - Added support check functions for various methods > The following files were skipped due to too many changes: `packages/thirdweb/src/extensions/erc721/__generated__/IDrop/read/claimCondition.ts`, `packages/thirdweb/src/extensions/erc721/drops/read/getActiveClaimCondition.ts`, `packages/thirdweb/src/extensions/erc721/drops/read/getClaimConditions.ts`, `packages/thirdweb/src/extensions/erc721/drop721.test.ts` > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` --- .changeset/curly-plums-cheer.md | 5 + .../scripts/generate/abis/erc721/IDrop.json | 3 +- .../thirdweb/src/exports/extensions/erc721.ts | 39 ++++++- .../IDrop/read/claimCondition.ts | 76 +++++++++++++ .../src/extensions/erc721/drop721.test.ts | 104 +++++++++++++++++- .../drops/read/getActiveClaimCondition.ts | 38 ++++++- .../erc721/drops/read/getClaimConditions.ts | 93 ++++++++++++++++ .../extensions/erc721/drops/write/claimTo.ts | 26 ++++- .../drops/write/resetClaimEligibility.ts | 70 ++++++++++++ .../erc721/drops/write/setClaimConditions.ts | 31 +++++- .../src/extensions/erc721/write/lazyMint.ts | 29 ++++- .../extensions/drops/get-claim-params.ts | 8 +- 12 files changed, 501 insertions(+), 21 deletions(-) create mode 100644 .changeset/curly-plums-cheer.md create mode 100644 packages/thirdweb/src/extensions/erc721/__generated__/IDrop/read/claimCondition.ts create mode 100644 packages/thirdweb/src/extensions/erc721/drops/read/getClaimConditions.ts create mode 100644 packages/thirdweb/src/extensions/erc721/drops/write/resetClaimEligibility.ts diff --git a/.changeset/curly-plums-cheer.md b/.changeset/curly-plums-cheer.md new file mode 100644 index 00000000000..024d79aa157 --- /dev/null +++ b/.changeset/curly-plums-cheer.md @@ -0,0 +1,5 @@ +--- +"thirdweb": minor +--- + +[Extensions] Erc721 Drop ClaimCondition enhancements diff --git a/packages/thirdweb/scripts/generate/abis/erc721/IDrop.json b/packages/thirdweb/scripts/generate/abis/erc721/IDrop.json index d57cc10dd1c..ffcbb962f07 100644 --- a/packages/thirdweb/scripts/generate/abis/erc721/IDrop.json +++ b/packages/thirdweb/scripts/generate/abis/erc721/IDrop.json @@ -6,5 +6,6 @@ "function getClaimConditionById(uint256 _conditionId) view returns ((uint256 startTimestamp, uint256 maxClaimableSupply, uint256 supplyClaimed, uint256 quantityLimitPerWallet, bytes32 merkleRoot, uint256 pricePerToken, address currency, string metadata) condition)", "function setClaimConditions((uint256 startTimestamp, uint256 maxClaimableSupply, uint256 supplyClaimed, uint256 quantityLimitPerWallet, bytes32 merkleRoot, uint256 pricePerToken, address currency, string metadata)[] phases, bool resetClaimEligibility)", "function nextTokenIdToClaim() view returns (uint256)", - "function baseURIIndices(uint256 index) view returns (uint256)" + "function baseURIIndices(uint256 index) view returns (uint256)", + "function claimCondition() view returns (uint256 currentStartId, uint256 count)" ] diff --git a/packages/thirdweb/src/exports/extensions/erc721.ts b/packages/thirdweb/src/exports/extensions/erc721.ts index b0709d471c9..d89a4484acb 100644 --- a/packages/thirdweb/src/exports/extensions/erc721.ts +++ b/packages/thirdweb/src/exports/extensions/erc721.ts @@ -82,21 +82,54 @@ export { /** * DROPS extension for ERC721 */ -export { getClaimConditionById } from "../../extensions/erc721/__generated__/IDrop/read/getClaimConditionById.js"; -export { claimCondition } from "../../extensions/erc721/__generated__/DropSinglePhase/read/claimCondition.js"; -export { getActiveClaimCondition } from "../../extensions/erc721/drops/read/getActiveClaimCondition.js"; +// READ +// multi-phase -- +export { + getClaimConditionById, + isGetClaimConditionByIdSupported, +} from "../../extensions/erc721/__generated__/IDrop/read/getClaimConditionById.js"; +export { + getActiveClaimConditionId, + isGetActiveClaimConditionIdSupported, +} from "../../extensions/erc721/__generated__/IDrop/read/getActiveClaimConditionId.js"; +export { + getClaimConditions, + isGetClaimConditionsSupported, +} from "../../extensions/erc721/drops/read/getClaimConditions.js"; +// -- +// single phase -- +export { + claimCondition, + isClaimConditionSupported, +} from "../../extensions/erc721/__generated__/DropSinglePhase/read/claimCondition.js"; +// -- +// multi and single phase -- +export { + getActiveClaimCondition, + isGetActiveClaimConditionSupported, +} from "../../extensions/erc721/drops/read/getActiveClaimCondition.js"; +// -- + +// WRITE export { claimTo, type ClaimToParams, + isClaimToSupported, } from "../../extensions/erc721/drops/write/claimTo.js"; export { lazyMint, type LazyMintParams, + isLazyMintSupported, } from "../../extensions/erc721/write/lazyMint.js"; export { setClaimConditions, type SetClaimConditionsParams, + isSetClaimConditionsSupported, } from "../../extensions/erc721/drops/write/setClaimConditions.js"; +export { + resetClaimEligibility, + isResetClaimEligibilitySupported, +} from "../../extensions/erc721/drops/write/resetClaimEligibility.js"; /** * SIGNATURE extension for ERC721 diff --git a/packages/thirdweb/src/extensions/erc721/__generated__/IDrop/read/claimCondition.ts b/packages/thirdweb/src/extensions/erc721/__generated__/IDrop/read/claimCondition.ts new file mode 100644 index 00000000000..3dc1ac6f853 --- /dev/null +++ b/packages/thirdweb/src/extensions/erc721/__generated__/IDrop/read/claimCondition.ts @@ -0,0 +1,76 @@ +import { readContract } from "../../../../../transaction/read-contract.js"; +import type { BaseTransactionOptions } from "../../../../../transaction/types.js"; + +import { decodeAbiParameters } from "viem"; +import type { Hex } from "../../../../../utils/encoding/hex.js"; +import { detectMethod } from "../../../../../utils/bytecode/detectExtension.js"; + +export const FN_SELECTOR = "0xd637ed59" as const; +const FN_INPUTS = [] as const; +const FN_OUTPUTS = [ + { + type: "uint256", + name: "currentStartId", + }, + { + type: "uint256", + name: "count", + }, +] as const; + +/** + * Checks if the `claimCondition` method is supported by the given contract. + * @param availableSelectors An array of 4byte function selectors of the contract. You can get this in various ways, such as using "whatsabi" or if you have the ABI of the contract available you can use it to generate the selectors. + * @returns A boolean indicating if the `claimCondition` method is supported. + * @extension ERC721 + * @example + * ```ts + * import { isClaimConditionSupported } from "thirdweb/extensions/erc721"; + * + * const supported = isClaimConditionSupported(["0x..."]); + * ``` + */ +export function isClaimConditionSupported(availableSelectors: string[]) { + return detectMethod({ + availableSelectors, + method: [FN_SELECTOR, FN_INPUTS, FN_OUTPUTS] as const, + }); +} + +/** + * Decodes the result of the claimCondition function call. + * @param result - The hexadecimal result to decode. + * @returns The decoded result as per the FN_OUTPUTS definition. + * @extension ERC721 + * @example + * ```ts + * import { decodeClaimConditionResult } from "thirdweb/extensions/erc721"; + * const result = decodeClaimConditionResult("..."); + * ``` + */ +export function decodeClaimConditionResult(result: Hex) { + return decodeAbiParameters(FN_OUTPUTS, result); +} + +/** + * Calls the "claimCondition" function on the contract. + * @param options - The options for the claimCondition function. + * @returns The parsed result of the function call. + * @extension ERC721 + * @example + * ```ts + * import { claimCondition } from "thirdweb/extensions/erc721"; + * + * const result = await claimCondition({ + * contract, + * }); + * + * ``` + */ +export async function claimCondition(options: BaseTransactionOptions) { + return readContract({ + contract: options.contract, + method: [FN_SELECTOR, FN_INPUTS, FN_OUTPUTS] as const, + params: [], + }); +} diff --git a/packages/thirdweb/src/extensions/erc721/drop721.test.ts b/packages/thirdweb/src/extensions/erc721/drop721.test.ts index 745a2826632..43e513becc9 100644 --- a/packages/thirdweb/src/extensions/erc721/drop721.test.ts +++ b/packages/thirdweb/src/extensions/erc721/drop721.test.ts @@ -6,6 +6,7 @@ import { TEST_ACCOUNT_A, TEST_ACCOUNT_B, TEST_ACCOUNT_C, + TEST_ACCOUNT_D, } from "../../../test/src/test-wallets.js"; import { type ThirdwebContract, getContract } from "../../contract/contract.js"; import { sendAndConfirmTransaction } from "../../transaction/actions/send-and-confirm-transaction.js"; @@ -16,7 +17,9 @@ import { deployERC20Contract } from "../prebuilts/deploy-erc20.js"; import { deployERC721Contract } from "../prebuilts/deploy-erc721.js"; import { balanceOf } from "./__generated__/IERC721A/read/balanceOf.js"; import { nextTokenIdToMint } from "./__generated__/IERC721Enumerable/read/nextTokenIdToMint.js"; +import { getClaimConditions } from "./drops/read/getClaimConditions.js"; import { claimTo } from "./drops/write/claimTo.js"; +import { resetClaimEligibility } from "./drops/write/resetClaimEligibility.js"; import { setClaimConditions } from "./drops/write/setClaimConditions.js"; import { getNFT } from "./read/getNFT.js"; import { lazyMint } from "./write/lazyMint.js"; @@ -83,6 +86,12 @@ describe.runIf(process.env.TW_SECRET_KEY)( { name: "Test NFT 2" }, { name: "Test NFT 3" }, { name: "Test NFT 4" }, + { name: "Test NFT 5" }, + { name: "Test NFT 6" }, + { name: "Test NFT 7" }, + { name: "Test NFT 8" }, + { name: "Test NFT 9" }, + { name: "Test NFT 10" }, ], }); await sendAndConfirmTransaction({ @@ -90,7 +99,7 @@ describe.runIf(process.env.TW_SECRET_KEY)( account: TEST_ACCOUNT_A, }); - await expect(nextTokenIdToMint({ contract })).resolves.toBe(4n); + await expect(nextTokenIdToMint({ contract })).resolves.toBe(10n); await expect( getNFT({ contract, tokenId: 0n }), ).resolves.toMatchInlineSnapshot(` @@ -100,7 +109,7 @@ describe.runIf(process.env.TW_SECRET_KEY)( "name": "Test NFT", }, "owner": null, - "tokenURI": "ipfs://QmUfspS2uU9roYLJveebbY5geYaNR4KkZAsMkb5pPRtc7a/0", + "tokenURI": "ipfs://QmY1Rr4C7cYVPAaXykMMxg3AVbatDZ6Rd7u3gzt79CiDSB/0", "type": "ERC721", } `); @@ -332,5 +341,96 @@ describe.runIf(process.env.TW_SECRET_KEY)( balanceOf({ contract, owner: TEST_ACCOUNT_A.address }), ).resolves.toBe(3n); }); + + it("should be able to retrieve multiple phases", async () => { + await sendAndConfirmTransaction({ + transaction: setClaimConditions({ + contract, + phases: [ + { + maxClaimablePerWallet: 1n, + startTime: new Date(0), + }, + { + maxClaimablePerWallet: 2n, + startTime: new Date(), + }, + ], + }), + account: TEST_ACCOUNT_A, + }); + + const phases = await getClaimConditions({ contract }); + expect(phases).toHaveLength(2); + expect(phases[0]?.quantityLimitPerWallet).toBe(1n); + expect(phases[1]?.quantityLimitPerWallet).toBe(2n); + }); + + it("should be able to reset claim eligibility", async () => { + // set claim conditions to only allow one claim + await sendAndConfirmTransaction({ + transaction: setClaimConditions({ + contract, + phases: [ + { + maxClaimablePerWallet: 1n, + }, + ], + }), + account: TEST_ACCOUNT_A, + }); + // claim one token + await sendAndConfirmTransaction({ + transaction: claimTo({ + contract, + // fresh account to avoid any previous claims + to: TEST_ACCOUNT_D.address, + quantity: 1n, + }), + // fresh account to avoid any previous claims + account: TEST_ACCOUNT_D, + }); + // check that the account has claimed one token + await expect( + balanceOf({ contract, owner: TEST_ACCOUNT_D.address }), + ).resolves.toBe(1n); + // attempt to claim another token (this should fail) + await expect( + sendAndConfirmTransaction({ + transaction: claimTo({ + contract, + to: TEST_ACCOUNT_D.address, + quantity: 1n, + }), + account: TEST_ACCOUNT_D, + }), + ).rejects.toThrowErrorMatchingInlineSnapshot(` + [TransactionError: Error - !Qty + + contract: 0x6AA2E0148a57EcDdb025C856c4e68682CFfcac78 + chainId: 31337] + `); + + // reset claim eligibility + await sendAndConfirmTransaction({ + transaction: resetClaimEligibility({ + contract, + }), + account: TEST_ACCOUNT_A, + }); + // attempt to claim another token (this should succeed) + await sendAndConfirmTransaction({ + transaction: claimTo({ + contract, + to: TEST_ACCOUNT_D.address, + quantity: 1n, + }), + account: TEST_ACCOUNT_D, + }); + // check that the account has claimed two tokens + await expect( + balanceOf({ contract, owner: TEST_ACCOUNT_D.address }), + ).resolves.toBe(2n); + }); }, ); diff --git a/packages/thirdweb/src/extensions/erc721/drops/read/getActiveClaimCondition.ts b/packages/thirdweb/src/extensions/erc721/drops/read/getActiveClaimCondition.ts index 5e971841bc6..2e19c0e3cac 100644 --- a/packages/thirdweb/src/extensions/erc721/drops/read/getActiveClaimCondition.ts +++ b/packages/thirdweb/src/extensions/erc721/drops/read/getActiveClaimCondition.ts @@ -1,8 +1,8 @@ import type { BaseTransactionOptions } from "../../../../transaction/types.js"; import type { ClaimCondition } from "../../../../utils/extensions/drops/types.js"; -import { claimCondition } from "../../__generated__/DropSinglePhase/read/claimCondition.js"; -import { getActiveClaimConditionId } from "../../__generated__/IDrop/read/getActiveClaimConditionId.js"; -import { getClaimConditionById } from "../../__generated__/IDrop/read/getClaimConditionById.js"; +import * as SinglePhase from "../../__generated__/DropSinglePhase/read/claimCondition.js"; +import * as GetActiveId from "../../__generated__/IDrop/read/getActiveClaimConditionId.js"; +import * as ById from "../../__generated__/IDrop/read/getClaimConditionById.js"; /** * Retrieves the active claim condition. @@ -20,8 +20,8 @@ export async function getActiveClaimCondition( options: BaseTransactionOptions, ): Promise { const getActiveClaimConditionMultiPhase = async () => { - const conditionId = await getActiveClaimConditionId(options); - return getClaimConditionById({ ...options, conditionId }); + const conditionId = await GetActiveId.getActiveClaimConditionId(options); + return ById.getClaimConditionById({ ...options, conditionId }); }; const getActiveClaimConditionSinglePhase = async () => { const [ @@ -33,7 +33,7 @@ export async function getActiveClaimCondition( pricePerToken, currency, metadata, - ] = await claimCondition(options); + ] = await SinglePhase.claimCondition(options); return { startTimestamp, maxClaimableSupply, @@ -58,3 +58,29 @@ export async function getActiveClaimCondition( } throw new Error("Claim condition not found"); } + +/** + * Checks if the `getActiveClaimCondition` method is supported by the given contract. + * @param availableSelectors An array of 4byte function selectors of the contract. You can get this in various ways, such as using "whatsabi" or if you have the ABI of the contract available you can use it to generate the selectors. + * @returns A boolean indicating if the `getActiveClaimCondition` method is supported. + * @extension ERC721 + * @example + * ```ts + * import { isGetActiveClaimConditionSupported } from "thirdweb/extensions/erc721"; + * + * const supported = isGetActiveClaimConditionSupported(["0x..."]); + * ``` + */ +export function isGetActiveClaimConditionSupported( + availableSelectors: string[], +) { + // if single phase is supported, return true + if (SinglePhase.isClaimConditionSupported(availableSelectors)) { + return true; + } + // otherwise check that both multi phase functions are supported + return ( + GetActiveId.isGetActiveClaimConditionIdSupported(availableSelectors) && + ById.isGetClaimConditionByIdSupported(availableSelectors) + ); +} diff --git a/packages/thirdweb/src/extensions/erc721/drops/read/getClaimConditions.ts b/packages/thirdweb/src/extensions/erc721/drops/read/getClaimConditions.ts new file mode 100644 index 00000000000..423297e016e --- /dev/null +++ b/packages/thirdweb/src/extensions/erc721/drops/read/getClaimConditions.ts @@ -0,0 +1,93 @@ +import type { BaseTransactionOptions } from "../../../../transaction/types.js"; +import type { ClaimCondition } from "../../../../utils/extensions/drops/types.js"; +import * as SinglePhase from "../../__generated__/DropSinglePhase/read/claimCondition.js"; +import * as MultiPhase from "../../__generated__/IDrop/read/claimCondition.js"; +import * as ById from "../../__generated__/IDrop/read/getClaimConditionById.js"; + +/** + * Retrieves all claim conditions. + * @param options - The transaction options. + * @returns A promise that resolves to all claim conditions. + * @throws An error if the claim conditions are unsupported by the contract. + * @extension ERC721 + * @example + * ```ts + * import { getClaimConditions } from "thirdweb/extensions/erc721"; + * const conditions = await getClaimConditions({ contract }); + * ``` + */ +export async function getClaimConditions( + options: BaseTransactionOptions, +): Promise { + const [multi, single] = await Promise.allSettled([ + (async () => { + const [startId, count] = await MultiPhase.claimCondition(options); + + const conditionPromises: Array< + ReturnType + > = []; + for (let i = startId; i < count; i++) { + conditionPromises.push( + ById.getClaimConditionById({ + ...options, + conditionId: i, + }), + ); + } + return Promise.all(conditionPromises); + })(), + SinglePhase.claimCondition(options).then( + ([ + startTimestamp, + maxClaimableSupply, + supplyClaimed, + quantityLimitPerWallet, + merkleRoot, + pricePerToken, + currency, + metadata, + ]) => ({ + startTimestamp, + maxClaimableSupply, + supplyClaimed, + quantityLimitPerWallet, + merkleRoot, + pricePerToken, + currency, + metadata, + }), + ), + ]); + if (multi.status === "fulfilled") { + return multi.value; + } + if (single.status === "fulfilled") { + return [single.value]; + } + + throw new Error("Claim condition not found"); +} + +/** + * Checks if the `getClaimConditions` method is supported by the given contract. + * @param availableSelectors An array of 4byte function selectors of the contract. You can get this in various ways, such as using "whatsabi" or if you have the ABI of the contract available you can use it to generate the selectors. + * @returns A boolean indicating if the `getClaimConditions` method is supported. + * @extension ERC721 + * @example + * ```ts + * import { isGetClaimConditionsSupported } from "thirdweb/extensions/erc721"; + * + * const supported = isGetClaimConditionsSupported(["0x..."]); + * ``` + */ +export function isGetClaimConditionsSupported(availableSelectors: string[]) { + // if single phase is supported, return true + if (SinglePhase.isClaimConditionSupported(availableSelectors)) { + return true; + } + // if multi phase is supported, return true + return ( + MultiPhase.isClaimConditionSupported(availableSelectors) && + ById.isGetClaimConditionByIdSupported(availableSelectors) + ); +} diff --git a/packages/thirdweb/src/extensions/erc721/drops/write/claimTo.ts b/packages/thirdweb/src/extensions/erc721/drops/write/claimTo.ts index fba925da381..6a767accb20 100644 --- a/packages/thirdweb/src/extensions/erc721/drops/write/claimTo.ts +++ b/packages/thirdweb/src/extensions/erc721/drops/write/claimTo.ts @@ -1,7 +1,11 @@ import type { Address } from "abitype"; import type { BaseTransactionOptions } from "../../../../transaction/types.js"; import { getClaimParams } from "../../../../utils/extensions/drops/get-claim-params.js"; -import { claim } from "../../__generated__/IDrop/write/claim.js"; +import { + claim, + isClaimSupported, +} from "../../__generated__/IDrop/write/claim.js"; +import { isGetActiveClaimConditionSupported } from "../read/getActiveClaimCondition.js"; /** * Represents the parameters for claiming an ERC721 token. @@ -46,3 +50,23 @@ export function claimTo(options: BaseTransactionOptions) { }), }); } + +/** + * Checks if the `claimTo` method is supported by the given contract. + * @param availableSelectors An array of 4byte function selectors of the contract. You can get this in various ways, such as using "whatsabi" or if you have the ABI of the contract available you can use it to generate the selectors. + * @returns A boolean indicating if the `claimTo` method is supported. + * @extension ERC721 + * @example + * ```ts + * import { isClaimToSupported } from "thirdweb/extensions/erc721"; + * + * const supported = isClaimToSupported(["0x..."]); + * ``` + */ +export function isClaimToSupported(availableSelectors: string[]) { + return ( + isClaimSupported(availableSelectors) && + // required to check if the contract supports the getActiveClaimCondition method + isGetActiveClaimConditionSupported(availableSelectors) + ); +} diff --git a/packages/thirdweb/src/extensions/erc721/drops/write/resetClaimEligibility.ts b/packages/thirdweb/src/extensions/erc721/drops/write/resetClaimEligibility.ts new file mode 100644 index 00000000000..ece1717c2cf --- /dev/null +++ b/packages/thirdweb/src/extensions/erc721/drops/write/resetClaimEligibility.ts @@ -0,0 +1,70 @@ +import type { Hex } from "viem"; +import type { BaseTransactionOptions } from "../../../../transaction/types.js"; +import type { ClaimCondition } from "../../../../utils/extensions/drops/types.js"; +import { + isSetClaimConditionsSupported, + setClaimConditions, +} from "../../__generated__/IDrop/write/setClaimConditions.js"; +import { + getClaimConditions, + isGetClaimConditionsSupported, +} from "../read/getClaimConditions.js"; + +/** + * Reset the claim eligibility for all users. + * @param options + * @returns the prepared transaction + * @extension ERC721 + * @example + * ```ts + * import { resetClaimEligibility } from "thirdweb/extensions/erc721"; + * import { sendTransaction } from "thirdweb"; + * + * const transaction = resetClaimEligibility({ + * contract, + * }); + * + * await sendTransaction({ transaction, account }); + * ``` + */ +export function resetClaimEligibility(options: BaseTransactionOptions) { + // download existing conditions + return setClaimConditions({ + contract: options.contract, + asyncParams: async () => { + // get existing conditions + const existingConditions = await getClaimConditions(options); + + // then simply return the exact same ones, but with the resetClaimEligibility flag set to true + return { + // type is necessary because of viem hex shenanigans (strict vs non-strict `0x` prefix string) + phases: existingConditions as Array< + ClaimCondition & { + currency: Hex; + merkleRoot: Hex; + } + >, + resetClaimEligibility: true, + }; + }, + }); +} + +/** + * Checks if the `resetClaimEligibility` method is supported by the given contract. + * @param availableSelectors An array of 4byte function selectors of the contract. You can get this in various ways, such as using "whatsabi" or if you have the ABI of the contract available you can use it to generate the selectors. + * @returns A boolean indicating if the `resetClaimEligibility` method is supported. + * @extension ERC721 + * @example + * ```ts + * import { isResetClaimEligibilitySupported } from "thirdweb/extensions/erc721"; + * + * const supported = isResetClaimEligibilitySupported(["0x..."]); + * ``` + */ +export function isResetClaimEligibilitySupported(availableSelectors: string[]) { + return ( + isGetClaimConditionsSupported(availableSelectors) && + isSetClaimConditionsSupported(availableSelectors) + ); +} diff --git a/packages/thirdweb/src/extensions/erc721/drops/write/setClaimConditions.ts b/packages/thirdweb/src/extensions/erc721/drops/write/setClaimConditions.ts index 20914961637..a070744c001 100644 --- a/packages/thirdweb/src/extensions/erc721/drops/write/setClaimConditions.ts +++ b/packages/thirdweb/src/extensions/erc721/drops/write/setClaimConditions.ts @@ -1,7 +1,13 @@ import type { BaseTransactionOptions } from "../../../../transaction/types.js"; import { getMulticallSetClaimConditionTransactions } from "../../../../utils/extensions/drops/get-multicall-set-claim-claim-conditon-transactions.js"; import type { ClaimConditionsInput } from "../../../../utils/extensions/drops/types.js"; -import { multicall } from "../../../common/__generated__/IMulticall/write/multicall.js"; +import { isSetContractURISupported } from "../../../common/__generated__/IContractMetadata/write/setContractURI.js"; +import { + isMulticallSupported, + multicall, +} from "../../../common/__generated__/IMulticall/write/multicall.js"; +import { isGetContractMetadataSupported } from "../../../common/read/getContractMetadata.js"; +import { isSetClaimConditionsSupported as isSetClaimConditionsSupportedGenerated } from "../../__generated__/IDrop/write/setClaimConditions.js"; /** * @extension ERC721 @@ -54,3 +60,26 @@ export function setClaimConditions( }, }); } + +/** + * Checks if the `setClaimConditions` method is supported by the given contract. + * @param availableSelectors An array of 4byte function selectors of the contract. You can get this in various ways, such as using "whatsabi" or if you have the ABI of the contract available you can use it to generate the selectors. + * @returns A boolean indicating if the `setClaimConditions` method is supported. + * @extension ERC721 + * @example + * ```ts + * import { isSetClaimConditionsSupported } from "thirdweb/extensions/erc721"; + * + * const supported = isSetClaimConditionsSupported(["0x..."]); + * ``` + */ +export function isSetClaimConditionsSupported(availableSelectors: string[]) { + return ( + isMulticallSupported(availableSelectors) && + // needed for setting contract metadata + isGetContractMetadataSupported(availableSelectors) && + isSetContractURISupported(availableSelectors) && + // needs to actually be able to set the claim Conditions + isSetClaimConditionsSupportedGenerated(availableSelectors) + ); +} diff --git a/packages/thirdweb/src/extensions/erc721/write/lazyMint.ts b/packages/thirdweb/src/extensions/erc721/write/lazyMint.ts index 72d17630dc1..075017094c2 100644 --- a/packages/thirdweb/src/extensions/erc721/write/lazyMint.ts +++ b/packages/thirdweb/src/extensions/erc721/write/lazyMint.ts @@ -5,8 +5,11 @@ import { uploadOrExtractURIs, } from "../../../utils/ipfs.js"; import type { Prettify } from "../../../utils/type-utils.js"; -import { nextTokenIdToMint } from "../__generated__/IERC721Enumerable/read/nextTokenIdToMint.js"; -import { lazyMint as generatedLazyMint } from "../__generated__/ILazyMint/write/lazyMint.js"; +import { + isNextTokenIdToMintSupported, + nextTokenIdToMint, +} from "../__generated__/IERC721Enumerable/read/nextTokenIdToMint.js"; +import * as LazyMint from "../__generated__/ILazyMint/write/lazyMint.js"; /** * Represents the input data for creating an NFT (Non-Fungible Token). @@ -57,7 +60,7 @@ export type LazyMintParams = { * ``` */ export function lazyMint(options: BaseTransactionOptions) { - return generatedLazyMint({ + return LazyMint.lazyMint({ contract: options.contract, asyncParams: async () => { const startFileNumber = await nextTokenIdToMint({ @@ -81,3 +84,23 @@ export function lazyMint(options: BaseTransactionOptions) { }, }); } + +/** + * Checks if the `lazyMint` method is supported by the given contract. + * @param availableSelectors An array of 4byte function selectors of the contract. You can get this in various ways, such as using "whatsabi" or if you have the ABI of the contract available you can use it to generate the selectors. + * @returns A boolean indicating if the `lazyMint` method is supported. + * @extension ERC721 + * @example + * ```ts + * import { isLazyMintSupported } from "thirdweb/extensions/erc721"; + * + * const supported = isLazyMintSupported(["0x..."]); + * ``` + */ +export function isLazyMintSupported(availableSelectors: string[]) { + return ( + LazyMint.isLazyMintSupported(availableSelectors) && + // required because we use it in the lazyMint function + isNextTokenIdToMintSupported(availableSelectors) + ); +} diff --git a/packages/thirdweb/src/utils/extensions/drops/get-claim-params.ts b/packages/thirdweb/src/utils/extensions/drops/get-claim-params.ts index 9e2477d5085..971337402c5 100644 --- a/packages/thirdweb/src/utils/extensions/drops/get-claim-params.ts +++ b/packages/thirdweb/src/utils/extensions/drops/get-claim-params.ts @@ -1,6 +1,6 @@ import { maxUint256, padHex } from "viem"; import { - ADDRESS_ZERO, + ZERO_ADDRESS, isNativeTokenAddress, } from "../../../constants/addresses.js"; import type { ThirdwebContract } from "../../../contract/contract.js"; @@ -85,7 +85,7 @@ export async function getClaimParams(options: GetClaimParamsOptions) { // early exit if no merkle root is set if (!cc.merkleRoot || cc.merkleRoot === padHex("0x", { size: 32 })) { return { - currency: ADDRESS_ZERO, + currency: ZERO_ADDRESS, proof: [], quantityLimitPerWallet: 0n, pricePerToken: maxUint256, @@ -105,7 +105,7 @@ export async function getClaimParams(options: GetClaimParamsOptions) { // if no proof is found, we'll try the empty proof if (!allowListProof) { return { - currency: ADDRESS_ZERO, + currency: ZERO_ADDRESS, proof: [], quantityLimitPerWallet: 0n, pricePerToken: maxUint256, @@ -118,7 +118,7 @@ export async function getClaimParams(options: GetClaimParamsOptions) { // currency and price need to match the allowlist proof if set // if default values in the allowlist proof, fallback to the claim condition const currency = - allowlistProof.currency && allowlistProof.currency !== ADDRESS_ZERO + allowlistProof.currency && allowlistProof.currency !== ZERO_ADDRESS ? allowlistProof.currency : cc.currency; const pricePerToken =