-
Notifications
You must be signed in to change notification settings - Fork 3
/
MintButton.tsx
179 lines (156 loc) · 6.26 KB
/
MintButton.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
import { useState } from "react";
import { Button } from "@chakra-ui/react";
import { useConnection, useWallet } from "@solana/wallet-adapter-react";
import {
Keypair,
PublicKey,
SystemProgram,
Transaction,
} from "@solana/web3.js";
import {
MINT_SIZE,
TOKEN_PROGRAM_ID,
createAssociatedTokenAccountInstruction,
createInitializeMint2Instruction,
createMintToInstruction,
getAssociatedTokenAddressSync,
getMinimumBalanceForRentExemptMint,
createSetAuthorityInstruction,
} from "@solana/spl-token";
import {
PROGRAM_ID as METADATA_PROGRAM_ID,
createCreateMetadataAccountV3Instruction,
} from "@metaplex-foundation/mpl-token-metadata";
import useToastHook from "@/hooks/useToastHook";
import { getRandomUri } from "@/utils/utils";
export default function MintButton() {
const { publicKey, sendTransaction } = useWallet();
const { connection } = useConnection();
const [isLoading, setIsLoading] = useState(false);
const displayToast = useToastHook();
const onClick = async () => {
if (!publicKey) return;
setIsLoading(true);
try {
// Generate keypair to use as address of mint account
const mintKeypair = Keypair.generate();
// Calculate minimum lamports for space required by mint account
const lamportsForMintAccount = await getMinimumBalanceForRentExemptMint(
connection
);
// 1) Instruction to invoke System Program to create new account with space for new mint account
const createMintAccountInstruction = SystemProgram.createAccount({
fromPubkey: publicKey, // payer
newAccountPubkey: mintKeypair.publicKey, // mint account address
space: MINT_SIZE, // space (in bytes) required by mint account
lamports: lamportsForMintAccount, // lamports to transfer to mint account
programId: TOKEN_PROGRAM_ID, // new program owner
});
// 2) Instruction to invoke Token Program to initialize mint account
const initializeMintInstruction = createInitializeMint2Instruction(
mintKeypair.publicKey, // mint address
0, // decimals of mint (0 for NFTs)
publicKey, // mint authority
null // freeze authority
);
// Derive associated token account address from mint address and token account owner
// This address is a PDA (Program Derived Address) and is generated deterministically
const associatedTokenAccountAddress = getAssociatedTokenAddressSync(
mintKeypair.publicKey, // mint address
publicKey // token account owner
);
// 3) Instruction to invoke Associated Token Program to create associated token account
// The Associated Token Program invokes the Token Program to create the token account with a PDA as the address of the token account
const createTokenAccountInstruction =
createAssociatedTokenAccountInstruction(
publicKey, // payer
associatedTokenAccountAddress, // associated token account address
publicKey, // owner
mintKeypair.publicKey // mint address
);
// 4) Instruction to invoke Token Program to mint 1 token to associated token account
const mintTokenInstruction = createMintToInstruction(
mintKeypair.publicKey, // mint address
associatedTokenAccountAddress, // destination
publicKey, // mint authority
1 // amount
);
// Derive the Metadata account address
const [metadataAccountAddress] = PublicKey.findProgramAddressSync(
[
Buffer.from("metadata"), // hard-coded string "metadata"
METADATA_PROGRAM_ID.toBuffer(), // metadata program address
mintKeypair.publicKey.toBuffer(), // mint address
],
METADATA_PROGRAM_ID // metadata program address
);
// 5) Instruction invoke Metaplex Token Metadata Program to create the Metadata account
const createMetadataInstruction =
createCreateMetadataAccountV3Instruction(
{
metadata: metadataAccountAddress, // metadata account address
mint: mintKeypair.publicKey, // mint address
mintAuthority: publicKey, // authority to mint tokens
payer: publicKey, // payer
updateAuthority: publicKey, // authority to update metadata account
},
{
createMetadataAccountArgsV3: {
data: {
creators: null, // creators of the NFT (optional)
name: "OPOS", // on-chain name
symbol: "OPOS", // on-chain symbol
uri: getRandomUri(), // off-chain metadata
sellerFeeBasisPoints: 0, // royalty fee
collection: null, // collection the NFT belongs to (optional)
uses: null, // uses (optional)
},
collectionDetails: null, // collection details (optional)
isMutable: false, // whether the metadata can be changed
},
}
);
// 6) Instruction to invoke Token Program to set mint authority to null
const setAuthorityInstruction = createSetAuthorityInstruction(
mintKeypair.publicKey, // mint address
publicKey, // current authority (mint authority)
0, // authority type (mint authority)
null // new authority
);
// Create new transaction and add instructions
const transaction = new Transaction().add(
// 1) Create mint account
createMintAccountInstruction,
// 2) Initialize mint account
initializeMintInstruction,
// 3) Create associated token account
createTokenAccountInstruction,
// 4) Mint 1 token to associated token account
mintTokenInstruction,
// 5) Create metadata account
createMetadataInstruction,
// 6) Set mint authority to null
setAuthorityInstruction
);
// Send transaction
const transactionSignature = await sendTransaction(
transaction,
connection,
{
signers: [mintKeypair],
}
);
// Toast notification
displayToast(transactionSignature);
} catch (error) {
console.log(error);
} finally {
setIsLoading(false);
}
};
return (
<Button onClick={onClick} isLoading={isLoading} isDisabled={!publicKey}>
Mint NFT
</Button>
);
}