Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
198 changes: 164 additions & 34 deletions packages/thirdweb/src/extensions/erc1155/drop1155.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,28 @@ import { VITALIK_WALLET } from "../../../test/src/addresses.js";
import { ANVIL_CHAIN } from "../../../test/src/chains.js";
import { TEST_CLIENT } from "../../../test/src/test-clients.js";
import {
TEST_ACCOUNT_A,
TEST_ACCOUNT_B,
TEST_ACCOUNT_C,
TEST_ACCOUNT_D,
} from "../../../test/src/test-wallets.js";
import { NATIVE_TOKEN_ADDRESS } from "../../constants/addresses.js";
import { resolveContractAbi } from "../../contract/actions/resolve-abi.js";
import { type ThirdwebContract, getContract } from "../../contract/contract.js";
import { sendAndConfirmTransaction } from "../../transaction/actions/send-and-confirm-transaction.js";
import { resolvePromisedValue } from "../../utils/promise/resolve-promised-value.js";
import { toEther } from "../../utils/units.js";
import { generateMerkleTreeInfoERC1155 } from "../airdrop/write/merkleInfoERC1155.js";
import { deployERC20Contract } from "../prebuilts/deploy-erc20.js";
import { deployERC1155Contract } from "../prebuilts/deploy-erc1155.js";
import { balanceOf } from "./__generated__/IERC1155/read/balanceOf.js";
import { totalSupply } from "./__generated__/IERC1155/read/totalSupply.js";
import { nextTokenIdToMint } from "./__generated__/IERC1155Enumerable/read/nextTokenIdToMint.js";
import { getActiveClaimCondition } from "./drops/read/getActiveClaimCondition.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 { updateMetadata } from "./drops/write/updateMetadata.js";
import { getNFT } from "./read/getNFT.js";
import { isGetNFTsSupported } from "./read/getNFTs.js";
import { lazyMint } from "./write/lazyMint.js";
Expand All @@ -35,7 +41,7 @@ describe.runIf(process.env.TW_SECRET_KEY)(

beforeAll(async () => {
const contractAddress = await deployERC1155Contract({
account: TEST_ACCOUNT_A,
account: TEST_ACCOUNT_C,
chain: ANVIL_CHAIN,
client: TEST_CLIENT,
params: {
Expand Down Expand Up @@ -67,7 +73,7 @@ describe.runIf(process.env.TW_SECRET_KEY)(
});
await sendAndConfirmTransaction({
transaction: mintTx,
account: TEST_ACCOUNT_A,
account: TEST_ACCOUNT_C,
});

await expect(nextTokenIdToMint({ contract })).resolves.toBe(6n);
Expand All @@ -87,36 +93,50 @@ describe.runIf(process.env.TW_SECRET_KEY)(
`);
});

it("should update metadata", async () => {
const updateTx = updateMetadata({
contract,
targetTokenId: 0n,
newMetadata: { name: "Test NFT 1" },
});
await sendAndConfirmTransaction({
transaction: updateTx,
account: TEST_ACCOUNT_C,
});
const token0 = await getNFT({ contract, tokenId: 0n });
expect(token0.metadata.name).toBe("Test NFT 1");
});

it("should allow to claim tokens", async () => {
await expect(
balanceOf({ contract, owner: TEST_ACCOUNT_A.address, tokenId: 0n }),
balanceOf({ contract, owner: TEST_ACCOUNT_C.address, tokenId: 0n }),
).resolves.toBe(0n);
await sendAndConfirmTransaction({
transaction: setClaimConditions({
contract,
phases: [{}],
tokenId: 0n,
}),
account: TEST_ACCOUNT_A,
account: TEST_ACCOUNT_C,
});
const claimTx = claimTo({
contract,
to: TEST_ACCOUNT_A.address,
to: TEST_ACCOUNT_C.address,
tokenId: 0n,
quantity: 1n,
});
await sendAndConfirmTransaction({
transaction: claimTx,
account: TEST_ACCOUNT_A,
account: TEST_ACCOUNT_C,
});
await expect(
balanceOf({ contract, owner: TEST_ACCOUNT_A.address, tokenId: 0n }),
balanceOf({ contract, owner: TEST_ACCOUNT_C.address, tokenId: 0n }),
).resolves.toBe(1n);
});

it("should allow to claim tokens with price", async () => {
await expect(
balanceOf({ contract, owner: TEST_ACCOUNT_A.address, tokenId: 0n }),
balanceOf({ contract, owner: TEST_ACCOUNT_C.address, tokenId: 0n }),
).resolves.toBe(1n);
await sendAndConfirmTransaction({
transaction: setClaimConditions({
Expand All @@ -128,11 +148,11 @@ describe.runIf(process.env.TW_SECRET_KEY)(
],
tokenId: 0n,
}),
account: TEST_ACCOUNT_A,
account: TEST_ACCOUNT_C,
});
const claimTx = claimTo({
contract,
to: TEST_ACCOUNT_A.address,
to: TEST_ACCOUNT_C.address,
tokenId: 0n,
quantity: 1n,
});
Expand All @@ -143,10 +163,10 @@ describe.runIf(process.env.TW_SECRET_KEY)(
expect(toEther(value)).toBe("0.001");
await sendAndConfirmTransaction({
transaction: claimTx,
account: TEST_ACCOUNT_A,
account: TEST_ACCOUNT_C,
});
await expect(
balanceOf({ contract, owner: TEST_ACCOUNT_A.address, tokenId: 0n }),
balanceOf({ contract, owner: TEST_ACCOUNT_C.address, tokenId: 0n }),
).resolves.toBe(2n);
});

Expand All @@ -159,26 +179,26 @@ describe.runIf(process.env.TW_SECRET_KEY)(
phases: [
{
overrideList: [
{ address: TEST_ACCOUNT_A.address, maxClaimable: "100" },
{ address: TEST_ACCOUNT_C.address, maxClaimable: "100" },
{ address: VITALIK_WALLET, maxClaimable: "100" },
],
maxClaimablePerWallet: 0n,
},
],
tokenId,
}),
account: TEST_ACCOUNT_A,
account: TEST_ACCOUNT_C,
});

await expect(
balanceOf({ contract, owner: TEST_ACCOUNT_B.address, tokenId }),
).resolves.toBe(0n);

await sendAndConfirmTransaction({
account: TEST_ACCOUNT_A,
account: TEST_ACCOUNT_C,
transaction: claimTo({
contract,
from: TEST_ACCOUNT_A.address,
from: TEST_ACCOUNT_C.address,
to: TEST_ACCOUNT_B.address,
tokenId,
quantity: 1n,
Expand Down Expand Up @@ -215,27 +235,27 @@ describe.runIf(process.env.TW_SECRET_KEY)(
phases: [
{
overrideList: [
{ address: TEST_ACCOUNT_A.address, maxClaimable: "1" },
{ address: TEST_ACCOUNT_C.address, maxClaimable: "1" },
{ address: VITALIK_WALLET, maxClaimable: "3" },
],
maxClaimablePerWallet: 0n,
},
],
tokenId,
}),
account: TEST_ACCOUNT_A,
account: TEST_ACCOUNT_C,
});

await expect(
balanceOf({ contract, owner: TEST_ACCOUNT_A.address, tokenId }),
balanceOf({ contract, owner: TEST_ACCOUNT_C.address, tokenId }),
).resolves.toBe(0n);

await expect(
sendAndConfirmTransaction({
account: TEST_ACCOUNT_A,
account: TEST_ACCOUNT_C,
transaction: claimTo({
contract,
to: TEST_ACCOUNT_A.address,
to: TEST_ACCOUNT_C.address,
tokenId,
quantity: 2n,
}),
Expand All @@ -248,17 +268,17 @@ describe.runIf(process.env.TW_SECRET_KEY)(
`);

await sendAndConfirmTransaction({
account: TEST_ACCOUNT_A,
account: TEST_ACCOUNT_C,
transaction: claimTo({
contract,
to: TEST_ACCOUNT_A.address,
to: TEST_ACCOUNT_C.address,
tokenId,
quantity: 1n,
}),
});

await expect(
balanceOf({ contract, owner: TEST_ACCOUNT_A.address, tokenId }),
balanceOf({ contract, owner: TEST_ACCOUNT_C.address, tokenId }),
).resolves.toBe(1n);
});
});
Expand All @@ -272,7 +292,7 @@ describe.runIf(process.env.TW_SECRET_KEY)(
{
overrideList: [
{
address: TEST_ACCOUNT_A.address,
address: TEST_ACCOUNT_C.address,
maxClaimable: "10",
price: "0",
},
Expand All @@ -283,25 +303,25 @@ describe.runIf(process.env.TW_SECRET_KEY)(
],
tokenId,
}),
account: TEST_ACCOUNT_A,
account: TEST_ACCOUNT_C,
});

await expect(
balanceOf({ contract, owner: TEST_ACCOUNT_A.address, tokenId }),
balanceOf({ contract, owner: TEST_ACCOUNT_C.address, tokenId }),
).resolves.toBe(0n);

await sendAndConfirmTransaction({
account: TEST_ACCOUNT_A,
account: TEST_ACCOUNT_C,
transaction: claimTo({
contract,
to: TEST_ACCOUNT_A.address,
to: TEST_ACCOUNT_C.address,
tokenId,
quantity: 1n,
}),
});

await expect(
balanceOf({ contract, owner: TEST_ACCOUNT_A.address, tokenId }),
balanceOf({ contract, owner: TEST_ACCOUNT_C.address, tokenId }),
).resolves.toBe(1n);
});

Expand All @@ -321,7 +341,7 @@ describe.runIf(process.env.TW_SECRET_KEY)(
},
],
}),
account: TEST_ACCOUNT_A,
account: TEST_ACCOUNT_C,
});

const phases = await getClaimConditions({ contract, tokenId: 5n });
Expand All @@ -342,7 +362,7 @@ describe.runIf(process.env.TW_SECRET_KEY)(
},
],
}),
account: TEST_ACCOUNT_A,
account: TEST_ACCOUNT_C,
});
// claim one token
await sendAndConfirmTransaction({
Expand Down Expand Up @@ -384,7 +404,7 @@ describe.runIf(process.env.TW_SECRET_KEY)(
tokenId: 6n,
contract,
}),
account: TEST_ACCOUNT_A,
account: TEST_ACCOUNT_C,
});
// attempt to claim another token (this should succeed)
await sendAndConfirmTransaction({
Expand All @@ -409,5 +429,115 @@ describe.runIf(process.env.TW_SECRET_KEY)(
.map((f) => toFunctionSelector(f));
expect(isGetNFTsSupported(selectors)).toBe(true);
});

/**
* This is to document the behavior where one can claim without paying if the claiming address
* is the same as the PrimaryRecipientAddress, because of this Solidity code:
* ```solidity
* // CurrencyTransferLib.sol
* function safeTransferERC20(address _currency, address _from, address _to, uint256 _amount) internal {
* if (_from == _to) {
* return;
* }
* ...
* }
* ```
*/
it("address that is the same with PrimaryFeeRecipient can claim without paying ERC20", async () => {
const tokenAddress = await deployERC20Contract({
client: TEST_CLIENT,
chain: ANVIL_CHAIN,
account: TEST_ACCOUNT_C,
type: "TokenERC20",
params: {
name: "token20",
contractURI: TEST_CONTRACT_URI,
},
});
const tokenId = 5n;
const setClaimTx = setClaimConditions({
contract,
tokenId,
phases: [
{
maxClaimableSupply: 100n,
maxClaimablePerWallet: 100n,
currencyAddress: tokenAddress,
price: 1000,
startTime: new Date(),
},
],
});
await sendAndConfirmTransaction({
transaction: setClaimTx,
account: TEST_ACCOUNT_C,
});

const transaction = claimTo({
contract,
tokenId,
quantity: 50n,
to: TEST_ACCOUNT_C.address,
});
await sendAndConfirmTransaction({
transaction,
account: TEST_ACCOUNT_C,
});
const supplyCount = await totalSupply({ contract, id: tokenId });
expect(supplyCount).toBe(50n);
});

it("getActiveClaimCondition should work", async () => {
// Create a public allowlist claim phase
const snapshot = [
{
recipient: TEST_ACCOUNT_B.address,
tokenId: 4,
amount: 5,
},
{
recipient: TEST_ACCOUNT_D.address,
tokenId: 4,
amount: 5,
},
];

const { merkleRoot } = await generateMerkleTreeInfoERC1155({
contract,
tokenAddress: NATIVE_TOKEN_ADDRESS,
snapshot,
});

const startTime = new Date();
const setCC = setClaimConditions({
contract,
tokenId: 4n,
phases: [
{
maxClaimableSupply: 100n,
maxClaimablePerWallet: 5n,
currencyAddress: NATIVE_TOKEN_ADDRESS,
price: 0.006,
startTime,
merkleRootHash: merkleRoot,
},
],
});

await sendAndConfirmTransaction({
transaction: setCC,
account: TEST_ACCOUNT_C,
});

const activeCC = await getActiveClaimCondition({ contract, tokenId: 4n });
expect(activeCC.currency.toLowerCase()).toBe(
NATIVE_TOKEN_ADDRESS.toLowerCase(),
);
expect(activeCC.merkleRoot).toBe(
"0x5baa4423af7125448ad7ca6913cdee7dd952a8d10a44f4fad4c50eea65c5c92d",
);
expect(activeCC.pricePerToken).toBe(6000000000000000n);
expect(activeCC.quantityLimitPerWallet).toBe(5n);
});
},
);
Loading
Loading