目录
1.编写目的
2.账户结构
3.环境及使用依赖
4.步骤分解
4.1.导入相关依赖
4.2. 初始化变量
4.3. 创建并初始化Mint Account
4.4. 创建并初始化Metadata Account
4.5. 发送创建和初始化mint Account
4.6 铸造代币
5.源码分享
Welcome to Code Block's blog
本篇文章主要介绍了
[小试牛刀-SOL链创建Token代币]
❤博主广交技术好友,喜欢文章的可以关注一下❤
1.编写目的
最近需要编写SOL合约进行SPL代币的转账,因为在测试网上需要自己部署测试代币,同时为了更加美观,代币需携带metadata数据(对代币名称、代币头像等)进行定义.在此对创建过程进行记录,希望帮助到有需要实现相关功能的朋友.
2.账户结构
SOL链内的所有数据都存储在账户中,创建代币需要使用不同的程序(合约)创建三个账户,结构图如下:
Mint Account:使用TOKEN_PROGRAM(代币相关操作)程序,创建一个Mint Account,这个账户的作用是用来铸造代币.
MetaData Account:使用METADATA_PROGRAM(metadata数据相关操作)程序,创建一个MetaData账户,用来存储代币基础信息(名称、图标/头像).
ACT Account: 铸造出的代币需要ACT Account进行接收,这需要使用用户和mint Account进行计算然后进行创建,用于接收铸造完成的代币.
3.环境及使用依赖
{
"scripts": {
"test": "ts-node ./test/createmint.test.ts"
},
"dependencies": {
"@metaplex-foundation/mpl-token-metadata": "^2.1.1",
"@solana/spl-token": "^0.4.8",
"@solana/web3.js": "^1.95.3",
},
"devDependencies": {
"@types/node": "^22.5.0",
"ts-node": "^10.9.2",
"typescript": "^5.5.4"
}
}
这里使用TypeScript和node环境进行代码编写,主要需要用到@metaplex-foundation/mpl-token-metadata(用于metadata Account初始化),@solana/spl-token(mint Account初始化和ACT Account创建),@solana/web3.js(用于基础Account创建和一些工具类).
注:这里尽量保持引入版本一致,因为不同版本的方法名称可能不同.
4.步骤分解
4.1.导入相关依赖
import { Keypair, PublicKey, SystemProgram,Connection,sendAndConfirmTransaction, Transaction } from "@solana/web3.js";
import { MINT_SIZE, TOKEN_PROGRAM_ID, createInitializeMint2Instruction, getOrCreateAssociatedTokenAccount,mintTo } from "@solana/spl-token";
import {
PROGRAM_ID as METADATA_PROGRAM_ID,
createCreateMetadataAccountV3Instruction,
} from "@metaplex-foundation/mpl-token-metadata";
import * as fs from 'fs'
这里的fs用于读取本地密钥文件,用于生成payer.
4.2. 初始化变量
const connection = new Connection('https://api.devnet.solana.com', 'confirmed');
const secretKeyPath='./wallet/id.json';
const secretKeyJSON = fs.readFileSync(secretKeyPath, 'utf-8');
// 创建测试账户
const secretKeyArray = JSON.parse(secretKeyJSON);
const secretKey = new Uint8Array(secretKeyArray);
const payer = Keypair.fromSecretKey(secretKey);
// 打印payer地址
console.log("Payer address:", payer.publicKey.toBase58());
// 定义token名称等
const tokenConfig = {
//小数位数
decimals: 2,
//代币名称
name: "BOGGY",
//代币符号
symbol: "Boggy Coin",
//metadata json地址
uri: "https://bafkreibyxbbl2jba2ry6ds2wgc6phdlhm2u6sox3neltfrdth7ocgkbqfm.ipfs.nftstorage.link",
};
这里connection定义了使用SOL测试网进行连接,同时通过读取本地的id.json文件创建一个交易费用支付者并进行打印。
4.3. 创建并初始化Mint Account
//创建一个密钥对,将其公钥作为Mint地址
const mintKeypair = Keypair.generate();
//输出Mint Account地址
console.log("Mint address:", mintKeypair.publicKey.toBase58());
这里创建一个密钥对,并打印,其公钥作为Mint Account地址在后面进行初始化.
//创建基础Account
const createMintAccountInstruction = SystemProgram.createAccount({
fromPubkey: payer.publicKey,
newAccountPubkey: mintKeypair.publicKey,
space: MINT_SIZE,
lamports: await connection.getMinimumBalanceForRentExemption(MINT_SIZE),
programId: TOKEN_PROGRAM_ID,
});
//将Account初始化为一个mint Account
const initializeMintInstruction = createInitializeMint2Instruction(
mintKeypair.publicKey,
tokenConfig.decimals,
payer.publicKey,
payer.publicKey,
);
这里进行了mint Account的创建和初始化两条命令:
>SystemProgram.createAccount
fromPubkey:将作为该账户的拥有者和交易费用支付者.
newAccountPubkey:即为创建mint地址,
space:为组件内提供的MINT_SIZE(MINT Account必须使用的空间大小),
lamports:作为免租费用.使用提供的方法根据空间进行计算.
TOKEN_PROGRAM_ID: TOKEN_PROGRAM(代币相关操作)程序
>createInitializeMint2Instruction
mintKeypair.publicKey:指定作为mint Account的账户.
tokenConfig.decimals:设置小数位数为2位,
payer.publicKey:分别指定铸造权限拥有者和冻结权限拥有者,当设置为null时代币将不能继续被铸造.
注:这两个命令其实就可以创建代币,但代币没有名称和头像.
4.4. 创建并初始化Metadata Account
const metadataAccount = PublicKey.findProgramAddressSync(
[Buffer.from("metadata"), METADATA_PROGRAM_ID.toBuffer(), mintKeypair.publicKey.toBuffer()],
METADATA_PROGRAM_ID,
)[0];
console.log("Metadata address:", metadataAccount.toBase58());
这里先创建了一个PDA账户(即METADATA_PROGRAM作为操作执行者),并进行打印.
注:根据官方要求必须使用派生(PDA)账户初始化MetaData Account.
const createMetadataInstruction = createCreateMetadataAccountV3Instruction(
{
metadata: metadataAccount,
mint: mintKeypair.publicKey,
mintAuthority: payer.publicKey,
payer: payer.publicKey,
updateAuthority: payer.publicKey,
},
{
createMetadataAccountArgsV3: {
data: {
creators: null,
name: tokenConfig.name,
symbol: tokenConfig.symbol,
uri: tokenConfig.uri,
//费用
sellerFeeBasisPoints: 0,
collection: null,
uses: null,
},
collectionDetails: null,
isMutable: true,
},
},
);
使用createCreateMetadataAccountV3Instruction进行metadata Account的创建和初始化:
metadata:metadata Account(即PDA账户)
mint:关联MINT账户,
mintAuthority: mint的权限用户,
payer: 费用支付者和拥有者,
updateAuthority: metadata的更新操作权限拥有者,
在createMetadataAccountArgsV3中分别设置了代币名称、图像、简称地址等参数。
4.5. 发送创建和初始化mint Account
const transaction=new Transaction().add(createMintAccountInstruction,initializeMintInstruction,createMetadataInstruction);
const tx=await sendAndConfirmTransaction(connection,transaction,[payer,mintKeypair]);
console.log("创建代币mint地址,交易tx:"+tx);
这里将上面的 createMintAccountInstruction、initializeMintInstruction、createMetadataInstruction添加到一个transaction中并使用sendAndConfirmTransaction发送到链上,即可完成带有metadata的SPL代币创建.
测试截图:
4.6 铸造代币
const actAccount=await getOrCreateAssociatedTokenAccount(connection,payer,mintKeypair.publicKey,payer.publicKey);
const mintSig=await mintTo(connection,payer,mintKeypair.publicKey,actAccount.address,payer,1000_000_000_000);
console.log("向我的账户mint代币:"+mintSig);
这里使用 getOrCreateAssociatedTokenAccount方法创建一个actAccount,同时通过mintTo方法向actAccount铸造代币.
测试截图:
因为小数位数设置为两位所以铸造了 1000_000_000_000将会铸造10_000_000_000个代币.
5.源码分享
import { Keypair, PublicKey, SystemProgram,Connection,sendAndConfirmTransaction, Transaction } from "@solana/web3.js";
import { MINT_SIZE, TOKEN_PROGRAM_ID, createInitializeMint2Instruction, getOrCreateAssociatedTokenAccount,mintTo } from "@solana/spl-token";
import {
PROGRAM_ID as METADATA_PROGRAM_ID,
createCreateMetadataAccountV3Instruction,
} from "@metaplex-foundation/mpl-token-metadata";
import * as fs from 'fs'
const connection = new Connection('https://api.devnet.solana.com', 'confirmed');
const secretKeyPath='./wallet/id.json';
const secretKeyJSON = fs.readFileSync(secretKeyPath, 'utf-8');
// 创建测试账户
const secretKeyArray = JSON.parse(secretKeyJSON);
const secretKey = new Uint8Array(secretKeyArray);
const payer = Keypair.fromSecretKey(secretKey);
(async () => {
console.log("Payer address:", payer.publicKey.toBase58());
const mintKeypair = Keypair.generate();
console.log("Mint address:", mintKeypair.publicKey.toBase58());
const tokenConfig = {
decimals: 2,
name: "BOGGY",
symbol: "Boggy Coin",
uri: "https://bafkreibyxbbl2jba2ry6ds2wgc6phdlhm2u6sox3neltfrdth7ocgkbqfm.ipfs.nftstorage.link",
};
const createMintAccountInstruction = SystemProgram.createAccount({
fromPubkey: payer.publicKey,
newAccountPubkey: mintKeypair.publicKey,
space: MINT_SIZE,
lamports: await connection.getMinimumBalanceForRentExemption(MINT_SIZE),
programId: TOKEN_PROGRAM_ID,
});
const initializeMintInstruction = createInitializeMint2Instruction(
mintKeypair.publicKey,
tokenConfig.decimals,
payer.publicKey,
payer.publicKey,
);
const metadataAccount = PublicKey.findProgramAddressSync(
[Buffer.from("metadata"), METADATA_PROGRAM_ID.toBuffer(), mintKeypair.publicKey.toBuffer()],
METADATA_PROGRAM_ID,
)[0];
console.log("Metadata address:", metadataAccount.toBase58());
const createMetadataInstruction = createCreateMetadataAccountV3Instruction(
{
metadata: metadataAccount,
mint: mintKeypair.publicKey,
mintAuthority: payer.publicKey,
payer: payer.publicKey,
updateAuthority: payer.publicKey,
},
{
createMetadataAccountArgsV3: {
data: {
creators: null,
name: tokenConfig.name,
symbol: tokenConfig.symbol,
uri: tokenConfig.uri,
sellerFeeBasisPoints: 0,
collection: null,
uses: null,
},
collectionDetails: null,
isMutable: true,
},
},
);
const transaction=new Transaction().add(createMintAccountInstruction,initializeMintInstruction,createMetadataInstruction);
const tx=await sendAndConfirmTransaction(connection,transaction,[payer,mintKeypair]);
console.log("创建代币mint地址,交易tx:"+tx);
const actAccount=await getOrCreateAssociatedTokenAccount(connection,payer,mintKeypair.publicKey,payer.publicKey);
const mintSig=await mintTo(connection,payer,mintKeypair.publicKey,actAccount.address,payer,1000_000_000_000);
console.log("向我的账户mint代币:"+mintSig);
})();
感谢您的关注和收藏!!!!!!