目录
Welcome to Code Block's blog
本篇文章主要介绍了
[小试牛刀-SOL链创建Token]
❤博主广交技术好友,喜欢文章的可以关注一下❤
文章为在测试网络进行,不涉及任何其他建议!!
1.编写目的
最近需要编写SOL合约进行SPL Token的转移,因为在测试网上需要自己部署测试Token,同时为了更加美观,Token需携带metadata数据(对名称、头像等)进行定义.在此对创建过程进行记录,希望帮助到有需要实现相关功能的朋友.
2.账户结构
SOL链内的所有数据都存储在账户中,创建Token需要使用不同的程序(合约)创建三个Account,结构图如下:
编辑
Mint Account:使用TOKEN_PROGRAM(Token相关操作)程序,创建一个Mint Account,这个账户的作用是用来铸造Token.
MetaData Account:使用METADATA_PROGRAM(metadata数据相关操作)程序,创建一个MetaData账户,用来存储Token基础信息(名称、图标/头像).
ACT Account: 铸造出的Token需要ACT Account进行接收,这需要使用用户和mint Account进行计算然后进行创建,用于接收铸造完成的Token.
3.环境及使用依赖
依赖名 | 版本号 |
@metaplex-foundation/mpl-token-metadata | 2.1.1 |
@solana/spl-token | 0.4.8 |
@solana/web3.js | 1.95.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, //Token名称 name: "BOGGY", //Token符号 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:将作为该Wallet的拥有者和交易费用支付者.
- newAccountPubkey:即为创建mint地址,
- space:为组件内提供的MINT_SIZE(MINT Account必须使用的空间大小),
- lamports:作为免租费用.使用提供的方法根据空间进行计算.
- TOKEN_PROGRAM_ID: TOKEN_PROGRAM(Token相关操作)程序
>createInitializeMint2Instruction
- mintKeypair.publicKey:指定作为mint Account的Wallet.
- tokenConfig.decimals:设置小数位数为2位,
- payer.publicKey:分别指定铸造权限拥有者和冻结权限拥有者,当设置为null时Token将不能继续被铸造.
注:这两个命令其实就可以创建Token,但Token没有名称和头像.
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的生成是[seeds]种子和持续的变动bump查看生成的地址是否在Ed25519椭圆曲线上,直到找到一个未在曲线上的值,则结束,并返回地址,以保持Pda地址的唯一性.
曲线方程为:
编辑
注:根据官方要求必须使用派生(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 Account)
- mint:关联MINT Address,
- mintAuthority: mint的权限用户,
- payer: 费用支付者和拥有者,
- updateAuthority: metadata的更新操作权限拥有者,
在createMetadataAccountArgsV3中分别设置了Token名称、图像、简称地址等参数。
4.5. 发送创建和初始化mint Account
const transaction=new Transaction().add(createMintAccountInstruction,initializeMintInstruction,createMetadataInstruction); const tx=await sendAndConfirmTransaction(connection,transaction,[payer,mintKeypair]); console.log("创建Token mint地址,交易tx:"+tx);
这里将上面的 createMintAccountInstruction、initializeMintInstruction、createMetadataInstruction添加到一个transaction中并使用sendAndConfirmTransaction发送到链上,即可完成带有metadata的SPL Token创建.
测试截图:
编辑
编辑
4.6 铸造Token
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 Token:"+mintSig);
这里使用 getOrCreateAssociatedTokenAccount方法创建一个actAccount,同时通过mintTo方法向actAccount铸造Token.
测试截图:
编辑
因为小数位数设置为两位所以铸造了 1000_000_000_000将会铸造10_000_000_000个Token.
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("创建Token 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 Token:"+mintSig); })();
本代码均在测试网络进行,不涉及任何如投资等方面的建议!
如果你对区块链感兴趣,可以浏览我的专栏:区块链
感谢您的关注和收藏!!!!!!
编辑