Skip to content

Commit cb4a302

Browse files
committed
Update
1 parent e67a300 commit cb4a302

File tree

6 files changed

+287
-98
lines changed

6 files changed

+287
-98
lines changed

.changeset/silent-eels-explain.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"thirdweb": minor
3+
---
4+
5+
Add new ERC1155 extension: mintAdditionalSupplyToBatch

apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/mint-supply-tab.tsx

Lines changed: 117 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,101 +1,144 @@
11
"use client";
22

3-
import { FormControl, Input } from "@chakra-ui/react";
3+
import {
4+
Form,
5+
FormControl,
6+
FormDescription,
7+
FormField,
8+
FormItem,
9+
FormLabel,
10+
FormMessage,
11+
} from "@/components/ui/form";
12+
import { Input } from "@/components/ui/input";
13+
import {} from "@chakra-ui/react";
14+
import { zodResolver } from "@hookform/resolvers/zod";
415
import { TransactionButton } from "components/buttons/TransactionButton";
516
import { useTrack } from "hooks/analytics/useTrack";
6-
import { useTxNotifications } from "hooks/useTxNotifications";
717
import { useForm } from "react-hook-form";
8-
import type { ThirdwebContract } from "thirdweb";
18+
import { toast } from "sonner";
19+
import { type ThirdwebContract, isAddress } from "thirdweb";
920
import { mintAdditionalSupplyTo } from "thirdweb/extensions/erc1155";
1021
import { useActiveAccount, useSendAndConfirmTransaction } from "thirdweb/react";
11-
import { FormErrorMessage, FormHelperText, FormLabel } from "tw-components";
22+
import {} from "tw-components";
23+
import { z } from "zod";
1224

1325
interface MintSupplyTabProps {
1426
contract: ThirdwebContract;
1527
tokenId: string;
1628
}
1729

30+
const mintAdditionalSupplyFormSchema = z.object({
31+
to: z
32+
.string()
33+
.refine((value) => isAddress(value), {
34+
message: "Invalid Ethereum address",
35+
})
36+
// otherwise the type is casted as "Hex"
37+
.transform((value) => value as string),
38+
amount: z.number().int().min(1, "Amount must be at least 1"),
39+
});
40+
1841
const MintSupplyTab: React.FC<MintSupplyTabProps> = ({ contract, tokenId }) => {
1942
const trackEvent = useTrack();
20-
const {
21-
register,
22-
handleSubmit,
23-
formState: { errors },
24-
reset,
25-
} = useForm<{ to: string; amount: string }>({
26-
defaultValues: { amount: "1" },
43+
const address = useActiveAccount()?.address;
44+
45+
const form = useForm<z.infer<typeof mintAdditionalSupplyFormSchema>>({
46+
resolver: zodResolver(mintAdditionalSupplyFormSchema),
47+
defaultValues: {
48+
amount: 1,
49+
to: address || "",
50+
},
2751
});
2852

29-
const address = useActiveAccount()?.address;
30-
const { mutate, isPending } = useSendAndConfirmTransaction();
31-
const { onSuccess, onError } = useTxNotifications(
32-
"Mint successful",
33-
"Error minting additional supply",
34-
contract,
35-
);
53+
const sendAndConfirmTx = useSendAndConfirmTransaction();
54+
55+
function onSubmit(values: z.infer<typeof mintAdditionalSupplyFormSchema>) {
56+
trackEvent({
57+
category: "nft",
58+
action: "mint-supply",
59+
label: "attempt",
60+
});
61+
const transaction = mintAdditionalSupplyTo({
62+
contract,
63+
to: values.to,
64+
tokenId: BigInt(tokenId),
65+
supply: BigInt(values.amount),
66+
});
67+
const promise = sendAndConfirmTx.mutateAsync(transaction, {
68+
onSuccess: () => {
69+
trackEvent({
70+
category: "nft",
71+
action: "mint-supply",
72+
label: "success",
73+
});
74+
form.reset();
75+
},
76+
onError: (error) => {
77+
trackEvent({
78+
category: "nft",
79+
action: "mint-supply",
80+
label: "error",
81+
error,
82+
});
83+
console.error(error);
84+
},
85+
});
86+
toast.promise(promise, {
87+
loading: "Minting NFT",
88+
success: "Minted successfully",
89+
error: "Failed to mint",
90+
});
91+
}
3692

3793
return (
38-
<div className="flex w-full flex-col gap-2">
94+
<Form {...form}>
3995
<form
40-
onSubmit={handleSubmit((data) => {
41-
if (address) {
42-
trackEvent({
43-
category: "nft",
44-
action: "mint-supply",
45-
label: "attempt",
46-
});
47-
const transaction = mintAdditionalSupplyTo({
48-
contract,
49-
to: address,
50-
tokenId: BigInt(tokenId),
51-
supply: BigInt(data.amount),
52-
});
53-
mutate(transaction, {
54-
onSuccess: () => {
55-
trackEvent({
56-
category: "nft",
57-
action: "mint-supply",
58-
label: "success",
59-
});
60-
onSuccess();
61-
reset();
62-
},
63-
onError: (error) => {
64-
trackEvent({
65-
category: "nft",
66-
action: "mint-supply",
67-
label: "error",
68-
error,
69-
});
70-
onError(error);
71-
},
72-
});
73-
}
74-
})}
96+
onSubmit={form.handleSubmit(onSubmit)}
97+
className="flex w-full flex-col gap-2"
7598
>
76-
<div className="flex flex-col gap-3">
77-
<div className="flex w-full flex-col gap-6 md:flex-row">
78-
<FormControl isRequired isInvalid={!!errors.to}>
99+
<FormField
100+
control={form.control}
101+
name="amount"
102+
render={({ field }) => (
103+
<FormItem>
79104
<FormLabel>Amount</FormLabel>
80-
<Input placeholder="1" {...register("amount")} />
81-
<FormHelperText>How many would you like to mint?</FormHelperText>
82-
<FormErrorMessage>{errors.to?.message}</FormErrorMessage>
83-
</FormControl>
84-
</div>
105+
<FormControl>
106+
<Input {...field} />
107+
</FormControl>
108+
<FormDescription>
109+
How many would you like to mint?
110+
</FormDescription>
111+
<FormMessage />
112+
</FormItem>
113+
)}
114+
/>
115+
116+
<FormField
117+
control={form.control}
118+
name="to"
119+
render={({ field }) => (
120+
<FormItem>
121+
<FormLabel>Recipient</FormLabel>
122+
<FormControl>
123+
<Input {...field} />
124+
</FormControl>
125+
<FormMessage />
126+
</FormItem>
127+
)}
128+
/>
85129

86-
<TransactionButton
87-
txChainID={contract.chain.id}
88-
transactionCount={1}
89-
isLoading={isPending}
90-
type="submit"
91-
colorScheme="primary"
92-
alignSelf="flex-end"
93-
>
94-
Mint
95-
</TransactionButton>
96-
</div>
130+
<TransactionButton
131+
txChainID={contract.chain.id}
132+
transactionCount={1}
133+
isLoading={sendAndConfirmTx.isPending}
134+
type="submit"
135+
colorScheme="primary"
136+
alignSelf="flex-end"
137+
>
138+
Mint
139+
</TransactionButton>
97140
</form>
98-
</div>
141+
</Form>
99142
);
100143
};
101144

packages/thirdweb/src/exports/extensions/erc1155.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,3 +203,8 @@ export {
203203
mintToBatch,
204204
type MintToBatchParams,
205205
} from "../../extensions/erc1155/write/mintToBatch.js";
206+
207+
export {
208+
mintAdditionalSupplyToBatch,
209+
type MintAdditionalSupplyToBatchParams,
210+
} from "../../extensions/erc1155/write/mintAdditionalSupplyToBatch.js";
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { describe, expect, it } from "vitest";
2+
import { ANVIL_CHAIN } from "~test/chains.js";
3+
import { TEST_CONTRACT_URI } from "~test/ipfs-uris.js";
4+
import { TEST_CLIENT } from "~test/test-clients.js";
5+
import { TEST_ACCOUNT_C } from "~test/test-wallets.js";
6+
import { getContract } from "../../../contract/contract.js";
7+
import { deployERC1155Contract } from "../../../extensions/prebuilts/deploy-erc1155.js";
8+
import { sendAndConfirmTransaction } from "../../../transaction/actions/send-and-confirm-transaction.js";
9+
import { getNFTs } from "../read/getNFTs.js";
10+
import { mintAdditionalSupplyToBatch } from "./mintAdditionalSupplyToBatch.js";
11+
import { mintToBatch } from "./mintToBatch.js";
12+
13+
const chain = ANVIL_CHAIN;
14+
const client = TEST_CLIENT;
15+
const account = TEST_ACCOUNT_C;
16+
17+
describe("ERC1155 Edition: mintToBatch", () => {
18+
it("should mint multiple tokens in one tx", async () => {
19+
const contract = getContract({
20+
chain,
21+
client,
22+
address: await deployERC1155Contract({
23+
chain,
24+
client,
25+
account,
26+
type: "TokenERC1155",
27+
params: {
28+
name: "edition",
29+
contractURI: TEST_CONTRACT_URI,
30+
},
31+
}),
32+
});
33+
34+
await sendAndConfirmTransaction({
35+
account,
36+
transaction: mintToBatch({
37+
contract,
38+
to: account.address,
39+
nfts: [
40+
{ metadata: { name: "token 0" }, supply: 1n },
41+
{ metadata: { name: "token 1" }, supply: 2n },
42+
{ metadata: { name: "token 2" }, supply: 3n },
43+
],
44+
}),
45+
});
46+
47+
await sendAndConfirmTransaction({
48+
account,
49+
transaction: mintAdditionalSupplyToBatch({
50+
contract,
51+
nfts: [
52+
{ tokenId: 0n, supply: 99n, to: account.address },
53+
{ tokenId: 1n, supply: 98n, to: account.address },
54+
{ tokenId: 2n, supply: 97n, to: account.address },
55+
],
56+
}),
57+
});
58+
59+
const nfts = await getNFTs({ contract });
60+
expect(nfts).toStrictEqual([
61+
{
62+
metadata: { name: "token 0" },
63+
owner: null,
64+
id: 0n,
65+
tokenURI: "ipfs://QmPZ6LpGqMuFbHKTXrNW1NRNLHf1nrxS4dtoFqdZZTKvPX/0",
66+
type: "ERC1155",
67+
supply: 100n,
68+
},
69+
{
70+
metadata: { name: "token 1" },
71+
owner: null,
72+
id: 1n,
73+
tokenURI: "ipfs://QmRFPyc3yEYxR4pQxwyTQWTine51TxWCoD6nzJWR3eX45b/0",
74+
type: "ERC1155",
75+
supply: 100n,
76+
},
77+
{
78+
metadata: { name: "token 2" },
79+
owner: null,
80+
id: 2n,
81+
tokenURI: "ipfs://QmesQiRLHCgqWZM2GFCs7Nb7rr2S72hU1BVQc7xiTyKZtT/0",
82+
type: "ERC1155",
83+
supply: 100n,
84+
},
85+
]);
86+
});
87+
});
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { multicall } from "../../../extensions/common/__generated__/IMulticall/write/multicall.js";
2+
import type { BaseTransactionOptions } from "../../../transaction/types.js";
3+
import { uri } from "../__generated__/IERC1155/read/uri.js";
4+
import { encodeMintTo } from "../__generated__/IMintableERC1155/write/mintTo.js";
5+
import type { MintAdditionalSupplyToParams } from "./mintAdditionalSupplyTo.js";
6+
7+
/**
8+
* @extension ERC1155
9+
*/
10+
export type MintAdditionalSupplyToBatchParams = {
11+
nfts: MintAdditionalSupplyToParams[];
12+
};
13+
14+
/**
15+
* This extension batches multiple `mintAdditionalSupplyToBatch` extensions into one single multicall.
16+
* Keep in mind that there is a limit of how many NFTs you can mint per transaction.
17+
* This limit varies depends on the network that you are transacting on.
18+
*
19+
* You are recommended to experiment with the number to figure out the best number for your chain of choice.
20+
* @extension ERC1155
21+
* @example
22+
* ```ts
23+
* import { mintAdditionalSupplyToBatch } from "thirdweb/extensions/erc1155";
24+
*
25+
* const transaction = mintAdditionalSupplyToBatch({
26+
* contract,
27+
* nfts: [
28+
* { tokenId: 0n, supply: 99n, to: account.address },
29+
* { tokenId: 1n, supply: 98n, to: account.address },
30+
* { tokenId: 2n, supply: 97n, to: account.address },
31+
* ],
32+
* });
33+
* ```
34+
*/
35+
export function mintAdditionalSupplyToBatch(
36+
options: BaseTransactionOptions<MintAdditionalSupplyToBatchParams>,
37+
) {
38+
return multicall({
39+
contract: options.contract,
40+
asyncParams: async () => {
41+
const data = await Promise.all(
42+
options.nfts.map(async (nft) => {
43+
const tokenUri = await uri({
44+
contract: options.contract,
45+
tokenId: nft.tokenId,
46+
});
47+
return encodeMintTo({
48+
to: nft.to,
49+
tokenId: nft.tokenId,
50+
amount: nft.supply,
51+
uri: tokenUri,
52+
});
53+
}),
54+
);
55+
return { data };
56+
},
57+
});
58+
}

0 commit comments

Comments
 (0)