Odak modu
Time for the tokens to meet their maker (you). We'll resume where we left off in our previous build section. If you need, you can get the starter code from here (make sure you're on the solution-without-burn
branch).
Start by adding the new dependencies:
npm install @metaplex-foundation/js fs
We'll be using the Metaplex SDK to add the metadata and the fs
library is so we can read the token logo image. Create a new folder called assets
and add in your logo. This will be on the testnet so have fun with it! I'm going with a pizza emoji so I named my file pizza.png lol
Metaplex will be doing all the heavy lifting for us so add these imports at the top in index.ts
:
import {
Metaplex,
keypairIdentity,
bundlrStorage,
toMetaplexFile,
findMetadataPda,
} from "@metaplex-foundation/js"
import {
DataV2,
createCreateMetadataAccountV2Instruction,
createUpdateMetadataAccountV2Instruction,
} from "@metaplex-foundation/mpl-token-metadata"
import * as fs from "fs"
Now that we have everything set up, we'll start the metadata bits. We'll do the off-chain part first, then create the token metadata account.
At a high level here's what'll need to happen:
toMetaplexFile()
metaplex.storage().upload
metaplex.uploadMetadata()
findMetadataPda()
DataV2
createCreateMetadataAccountV2Instruction
(not a typo lol)There's a lot happening here, but it's all basic stuff. Take a moment to go through this, you know everything that's going on!
We'll create a single function to do all this:
async function createTokenMetadata(
connection: web3.Connection,
metaplex: Metaplex,
mint: web3.PublicKey,
user: web3.Keypair,
name: string,
symbol: string,
description: string
) {
// file to buffer
const buffer = fs.readFileSync("assets/pizza.png")
// buffer to metaplex file
const file = toMetaplexFile(buffer, "pizza.png")
// upload image and get image uri
const imageUri = await metaplex.storage().upload(file)
console.log("image uri:", imageUri)
// upload metadata and get metadata uri (off chain metadata)
const { uri } = await metaplex
.nfts()
.uploadMetadata({
name: name,
description: description,
image: imageUri,
})
console.log("metadata uri:", uri)
// get metadata account address
const metadataPDA = await findMetadataPda(mint)
// onchain metadata format
const tokenMetadata = {
name: name,
symbol: symbol,
uri: uri,
sellerFeeBasisPoints: 0,
creators: null,
collection: null,
uses: null,
} as DataV2
// transaction to create metadata account
const transaction = new web3.Transaction().add(
createCreateMetadataAccountV2Instruction(
{
metadata: metadataPDA,
mint: mint,
mintAuthority: user.publicKey,
payer: user.publicKey,
updateAuthority: user.publicKey,
},
{
createMetadataAccountArgsV2: {
data: tokenMetadata,
isMutable: true,
},
}
)
)
// send transaction
const transactionSignature = await web3.sendAndConfirmTransaction(
connection,
transaction,
[user]
)
console.log(
`Create Metadata Account: https://explorer.solana.com/tx/${transactionSignature}?cluster=devnet`
)
}
Make sure you update the file name! Also, don't worry about the nfts()
call - Metaplex was initially built for NFTs and was recently expanded to work for fungible tokens too.
You'll notice we're leaving a bunch of stuff null here - that's cause you don't need to set that stuff when making fungible tokens. Non-fungible tokens have more specific behaviour that you need to define.
I could go over this function bit by bit but I'd just be repeating myself lol. More important than how this works, is knowing how to get to it. You need to read docs to use the APIs to make functions like these.
I'm talking about learning how to fish instead of just taking this fish.
Your first resource should always be the docs. But the docs won't exist when the code is just being written. So you do just that - look at the code as it's being written. If you were to poke around in the Metaplex repo, you'd see these:
There's not much science to this, you need to get in the code and find what you need. You'll have to understand the primitives that the code is built on (Solana instructions in this case) and it'll take a few tries, but the rewards will be massive.
Generally what I try to do is:
Hopefully that gives you an idea of how the pioneers do it :)
Back to our regularly scheduled building!
Remember the token mint address you saved earlier? We'll be using that when we call this new function in main
. If you've lost the token mint account address, you can always just look up the wallet address using the explorer and check Tokens tab.
Here's what our updated main()
function will look like when calling the createTokenMetadata
function:
async function main() {
const connection = new web3.Connection(web3.clusterApiUrl("devnet"))
const user = await initializeKeypair(connection)
console.log("PublicKey:", user.publicKey.toBase58())
// MAKE SURE YOU REPLACE THIS ADDRESS WITH YOURS!
const MINT_ADDRESS = "87MGWR6EbAqegYXr3LoZmKKC9fSFXQx4EwJEAczcMpMF"
// metaplex setup
const metaplex = Metaplex.make(connection)
.use(keypairIdentity(user))
.use(
bundlrStorage({
address: "https://devnet.bundlr.network",
providerUrl: "https://api.devnet.solana.com",
timeout: 60000,
})
)
// Calling the token
await createTokenMetadata(
connection,
metaplex,
new web3.PublicKey(MINT_ADDRESS),
user,
"Pizza", // Token name - REPLACE THIS WITH YOURS
"PZA", // Token symbol - REPLACE THIS WITH YOURS
"Whoever holds this token is invited to my pizza party" // Token description - REPLACE THIS WITH YOURS
)
}
Update the mint address and the token details and smash npm run dev
, you'll see something like this:
> solana-course-client@1.0.0 start
> ts-node src/index.ts
Current balance is 1.996472479
PublicKey: 5y3G3Rz5vgK9rKRxu3BaC3PvhsMKGyAmtcizgrxojYAA
image uri: https://arweave.net/7sDCnvGRJAqfgEuGOYWhIshfgTC-hNfG4NSjwsKunQs
metadata uri: https://arweave.net/-2vGrM69PNtb2YaHnOErh1_006D28JJa825CIcEGIok
Create Metadata Account: https://explorer.solana.com/tx/4w8XEGCJY82MnBnErW9F5r1i5UL5ffJCCujcgFeXS8TTdZ6tHBEMznWnPoQXVcsPY3WoPbL2Nb1ubXCUJWWt2GWi?cluster=devnet
Finished successfully
Everything necessary has been done all at once! Feel free to hit the Arweave links - it's like decentralised & permanent AWS S3/Google Cloud storage and will show you what the uploaded assets look like.
If you head back to your token mint account on the explorer, you'll see the fancy new icon and name. Here's mine:
As a wise philosopher once said,
One of the coolenst parts of the token metadata program is how easy it is to update. All you need to do is change the transaction from createCreateMetadataAccountV2Instruction
to createUpdateMetadataAccountV2Instruction
:
async function updateTokenMetadata(
connection: web3.Connection,
metaplex: Metaplex,
mint: web3.PublicKey,
user: web3.Keypair,
name: string,
symbol: string,
description: string
) {
...
// transaction to update metadata account
const transaction = new web3.Transaction().add(
createUpdateMetadataAccountV2Instruction(
{
metadata: metadataPDA,
updateAuthority: user.publicKey,
},
{
updateMetadataAccountArgsV2: {
data: tokenMetadata,
updateAuthority: user.publicKey,
primarySaleHappened: true,
isMutable: true,
},
}
)
)
// Everything else remains the same
...
}
Your token is now complete! Make sure you spread the love. Maybe send some tokens to your friends or other builders in the Discord server. Share your address in #progress so people can airdrop you their tokens :D
Young glass chewer, it's time to reimplement the lesson concepts from scratch.
Try to build a single transaction that includes the following instructions:
getOrCreateAssociatedTokenAccount
This is pretty much exactly how you'll be doing it in production - everything together all at once.
Note This is a bit more freeform than normal. Push yourself. Experiment. Really try to understand each piece of the puzzle.
To do this the way we're envisioning, you will need to build each instruction and then add all of them to a single transaction. Once you've wrestled with this on your own, you can view one possible implementation on the challenge branch of this repository.
Extra hint: https://solana-labs.github.io/solana-program-library/token/js/modules.html - look at the source code, don't use the helper functions.
Sektörde en çok aranan yazılım becerilerini kazan
Yapay zeka desteği, birebir mentörlük saatleri, canlı dersler ve senin için özel hazırlanmış içeriklerle eksiklerini tamamla, düzenli geri bildirimler al ve öğrenme sürecini en verimli hale getir.
Yunus Emre Kabakcı
Patika+ mezunuPatika+ Fullstack Web Development Bootcamp mezunumuz Yunus Emre,
3 ay içinde Katar’dan aldığı teklif ile, global bir şirket olan Pavo Group’da işe başladı!
“İçerik zenginliği, mentor desteği, ileriye dönük bir network sağlaması ve dünyada en çok tercih edilen frameworkler üzerinden bir eğitim veriyor olması Patika+’ı tercih etmemin temel sebepleri oldu!“
Yorum yapabilmek için derse kayıt olmalısın!