diff --git a/.changeset/silent-eels-explain.md b/.changeset/silent-eels-explain.md new file mode 100644 index 00000000000..0bc9efbb8e3 --- /dev/null +++ b/.changeset/silent-eels-explain.md @@ -0,0 +1,5 @@ +--- +"thirdweb": minor +--- + +Add new ERC1155 extension: mintAdditionalSupplyToBatch diff --git a/packages/thirdweb/src/exports/extensions/erc1155.ts b/packages/thirdweb/src/exports/extensions/erc1155.ts index 88e4c40f5ec..4aec0b6b9ce 100644 --- a/packages/thirdweb/src/exports/extensions/erc1155.ts +++ b/packages/thirdweb/src/exports/extensions/erc1155.ts @@ -203,3 +203,8 @@ export { mintToBatch, type MintToBatchParams, } from "../../extensions/erc1155/write/mintToBatch.js"; + +export { + mintAdditionalSupplyToBatch, + type MintAdditionalSupplyToBatchParams, +} from "../../extensions/erc1155/write/mintAdditionalSupplyToBatch.js"; diff --git a/packages/thirdweb/src/extensions/erc1155/write/mintAdditionalSupplyToBatch.test.ts b/packages/thirdweb/src/extensions/erc1155/write/mintAdditionalSupplyToBatch.test.ts new file mode 100644 index 00000000000..72b814fe2a3 --- /dev/null +++ b/packages/thirdweb/src/extensions/erc1155/write/mintAdditionalSupplyToBatch.test.ts @@ -0,0 +1,87 @@ +import { describe, expect, it } from "vitest"; +import { ANVIL_CHAIN } from "~test/chains.js"; +import { TEST_CONTRACT_URI } from "~test/ipfs-uris.js"; +import { TEST_CLIENT } from "~test/test-clients.js"; +import { TEST_ACCOUNT_C } from "~test/test-wallets.js"; +import { getContract } from "../../../contract/contract.js"; +import { deployERC1155Contract } from "../../../extensions/prebuilts/deploy-erc1155.js"; +import { sendAndConfirmTransaction } from "../../../transaction/actions/send-and-confirm-transaction.js"; +import { getNFTs } from "../read/getNFTs.js"; +import { mintAdditionalSupplyToBatch } from "./mintAdditionalSupplyToBatch.js"; +import { mintToBatch } from "./mintToBatch.js"; + +const chain = ANVIL_CHAIN; +const client = TEST_CLIENT; +const account = TEST_ACCOUNT_C; + +describe("ERC1155 Edition: mintToBatch", () => { + it("should mint multiple tokens in one tx", async () => { + const contract = getContract({ + chain, + client, + address: await deployERC1155Contract({ + chain, + client, + account, + type: "TokenERC1155", + params: { + name: "edition", + contractURI: TEST_CONTRACT_URI, + }, + }), + }); + + await sendAndConfirmTransaction({ + account, + transaction: mintToBatch({ + contract, + to: account.address, + nfts: [ + { metadata: { name: "token 0" }, supply: 1n }, + { metadata: { name: "token 1" }, supply: 2n }, + { metadata: { name: "token 2" }, supply: 3n }, + ], + }), + }); + + await sendAndConfirmTransaction({ + account, + transaction: mintAdditionalSupplyToBatch({ + contract, + nfts: [ + { tokenId: 0n, supply: 99n, to: account.address }, + { tokenId: 1n, supply: 98n, to: account.address }, + { tokenId: 2n, supply: 97n, to: account.address }, + ], + }), + }); + + const nfts = await getNFTs({ contract }); + expect(nfts).toStrictEqual([ + { + metadata: { name: "token 0" }, + owner: null, + id: 0n, + tokenURI: "ipfs://QmPZ6LpGqMuFbHKTXrNW1NRNLHf1nrxS4dtoFqdZZTKvPX/0", + type: "ERC1155", + supply: 100n, + }, + { + metadata: { name: "token 1" }, + owner: null, + id: 1n, + tokenURI: "ipfs://QmRFPyc3yEYxR4pQxwyTQWTine51TxWCoD6nzJWR3eX45b/0", + type: "ERC1155", + supply: 100n, + }, + { + metadata: { name: "token 2" }, + owner: null, + id: 2n, + tokenURI: "ipfs://QmesQiRLHCgqWZM2GFCs7Nb7rr2S72hU1BVQc7xiTyKZtT/0", + type: "ERC1155", + supply: 100n, + }, + ]); + }); +}); diff --git a/packages/thirdweb/src/extensions/erc1155/write/mintAdditionalSupplyToBatch.ts b/packages/thirdweb/src/extensions/erc1155/write/mintAdditionalSupplyToBatch.ts new file mode 100644 index 00000000000..e3108eef1fa --- /dev/null +++ b/packages/thirdweb/src/extensions/erc1155/write/mintAdditionalSupplyToBatch.ts @@ -0,0 +1,58 @@ +import { multicall } from "../../../extensions/common/__generated__/IMulticall/write/multicall.js"; +import type { BaseTransactionOptions } from "../../../transaction/types.js"; +import { uri } from "../__generated__/IERC1155/read/uri.js"; +import { encodeMintTo } from "../__generated__/IMintableERC1155/write/mintTo.js"; +import type { MintAdditionalSupplyToParams } from "./mintAdditionalSupplyTo.js"; + +/** + * @extension ERC1155 + */ +export type MintAdditionalSupplyToBatchParams = { + nfts: MintAdditionalSupplyToParams[]; +}; + +/** + * This extension batches multiple `mintAdditionalSupplyToBatch` extensions into one single multicall. + * Keep in mind that there is a limit of how many NFTs you can mint per transaction. + * This limit varies depends on the network that you are transacting on. + * + * You are recommended to experiment with the number to figure out the best number for your chain of choice. + * @extension ERC1155 + * @example + * ```ts + * import { mintAdditionalSupplyToBatch } from "thirdweb/extensions/erc1155"; + * + * const transaction = mintAdditionalSupplyToBatch({ + * contract, + * nfts: [ + * { tokenId: 0n, supply: 99n, to: account.address }, + * { tokenId: 1n, supply: 98n, to: account.address }, + * { tokenId: 2n, supply: 97n, to: account.address }, + * ], + * }); + * ``` + */ +export function mintAdditionalSupplyToBatch( + options: BaseTransactionOptions, +) { + return multicall({ + contract: options.contract, + asyncParams: async () => { + const data = await Promise.all( + options.nfts.map(async (nft) => { + const tokenUri = await uri({ + contract: options.contract, + tokenId: nft.tokenId, + }); + return encodeMintTo({ + to: nft.to, + tokenId: nft.tokenId, + amount: nft.supply, + uri: tokenUri, + }); + }), + ); + return { data }; + }, + }); +} diff --git a/packages/thirdweb/src/extensions/erc1155/write/mintToBatch.ts b/packages/thirdweb/src/extensions/erc1155/write/mintToBatch.ts index 4aae5da2b26..28a8fa9c1a0 100644 --- a/packages/thirdweb/src/extensions/erc1155/write/mintToBatch.ts +++ b/packages/thirdweb/src/extensions/erc1155/write/mintToBatch.ts @@ -83,33 +83,24 @@ export function mintToBatch( return multicall({ contract: options.contract, asyncParams: async () => { - const uris = await Promise.all( - options.nfts.map((item) => { - if (typeof item.metadata === "string") { - return item.metadata; - } - return upload({ - client: options.contract.client, - files: [item.metadata], + const data = await Promise.all( + options.nfts.map(async (nft) => { + const uri = + typeof nft.metadata === "string" + ? nft.metadata + : await upload({ + client: options.contract.client, + files: [nft.metadata], + }); + return encodeMintTo({ + to: options.to, + // maxUint256 is used to indicate that this is a NEW token! + tokenId: maxUint256, + uri, + amount: nft.supply, }); }), ); - - const data = uris.map((uri, index) => { - const item = options.nfts[index]; - if (!item) { - // Should not happen - throw new Error("Index mismatch"); - } - return encodeMintTo({ - to: options.to, - // maxUint256 is used to indicate that this is a NEW token! - tokenId: maxUint256, - uri, - amount: item.supply, - }); - }); - return { data }; }, overrides: options.overrides,