Skip to content

Commit

Permalink
[Update] Refactor claim condition retrieval and add support check (#4391
Browse files Browse the repository at this point in the history
)

<!-- start pr-codex -->

## 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}`

<!-- end pr-codex -->
  • Loading branch information
jnsdls committed Sep 4, 2024
1 parent 257e428 commit f9c351c
Show file tree
Hide file tree
Showing 12 changed files with 501 additions and 21 deletions.
5 changes: 5 additions & 0 deletions .changeset/curly-plums-cheer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"thirdweb": minor
---

[Extensions] Erc721 Drop ClaimCondition enhancements
3 changes: 2 additions & 1 deletion packages/thirdweb/scripts/generate/abis/erc721/IDrop.json
Original file line number Diff line number Diff line change
Expand Up @@ -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)"
]
39 changes: 36 additions & 3 deletions packages/thirdweb/src/exports/extensions/erc721.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

104 changes: 102 additions & 2 deletions packages/thirdweb/src/extensions/erc721/drop721.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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";
Expand Down Expand Up @@ -83,14 +86,20 @@ 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({
transaction: mintTx,
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(`
Expand All @@ -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",
}
`);
Expand Down Expand Up @@ -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);
});
},
);
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -20,8 +20,8 @@ export async function getActiveClaimCondition(
options: BaseTransactionOptions,
): Promise<ClaimCondition> {
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 [
Expand All @@ -33,7 +33,7 @@ export async function getActiveClaimCondition(
pricePerToken,
currency,
metadata,
] = await claimCondition(options);
] = await SinglePhase.claimCondition(options);
return {
startTimestamp,
maxClaimableSupply,
Expand All @@ -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)
);
}

Check warning on line 86 in packages/thirdweb/src/extensions/erc721/drops/read/getActiveClaimCondition.ts

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/extensions/erc721/drops/read/getActiveClaimCondition.ts#L75-L86

Added lines #L75 - L86 were not covered by tests
Loading

0 comments on commit f9c351c

Please sign in to comment.