首页 > 其他分享 >你的第一个Solana SPL

你的第一个Solana SPL

时间:2024-10-31 20:22:06浏览次数:5  
标签:account const 第一个 Solana pub token SPL mint metadata

简介 TFT

你的第一个SPL The first token

技术栈和库

  • Rust
  • Anchor框架
  • Typescript(测试)

开发环境和其它网络地址

开发环境设置

1.本教程使用的时 DevNet
2.浏览器打开 https://beta.solpg.io/
3.创建项目
4.请求空头

请求空投

Sol程序开发

// ========= Step 1 引用框架

// 1.管理账户的
use anchor_lang::prelude::*;

// 2.管理代币的
use anchor_spl::{
    associated_token::AssociatedToken, // 处理关联代币账户的功能
    metadata::{
        create_metadata_accounts_v3,       // 创建元数据账户的功能
        mpl_token_metadata::types::DataV2, // 元数据的结构体定义
        CreateMetadataAccountsV3,          // 创建元数据账户的指令结构体
        Metadata as Metaplex,              // 将 Metadata 重命名为 Metaplex,以便于使用
    },
    token::{
        mint_to,      // 铸币功能
        Mint,         // 代币铸造的结构体
        MintTo,       // 铸币指令的结构体
        Token,        // 代币的基本功能
        TokenAccount, // 代币账户的结构体
    },
};

// 2.加载程序id(自己获取,或者系统生成)
declare_id!("7CR9ATZRxzEmCSM91UkumMJ6b8h5ompMcxTnUKLc8z4e");

// 3.代币主程序
#[program]
mod token_minter {
    use super::*;

    // 3.1初始化 SPL
    pub fn init_token(ctx: Context<InitToken>, metadata: InitTokenParams) -> Result<()> {
        let seeds = &["mint".as_bytes(), &[ctx.bumps.mint]];
        let signer = [&seeds[..]];

        let token_data: DataV2 = DataV2 {
            name: metadata.name,
            symbol: metadata.symbol,
            uri: metadata.uri,
            seller_fee_basis_points: 0,
            creators: None,
            collection: None,
            uses: None,
        };

        let metadata_ctx = CpiContext::new_with_signer(
            ctx.accounts.token_metadata_program.to_account_info(),
            CreateMetadataAccountsV3 {
                payer: ctx.accounts.payer.to_account_info(),
                update_authority: ctx.accounts.mint.to_account_info(),
                mint: ctx.accounts.mint.to_account_info(),
                metadata: ctx.accounts.metadata.to_account_info(),
                mint_authority: ctx.accounts.mint.to_account_info(),
                system_program: ctx.accounts.system_program.to_account_info(),
                rent: ctx.accounts.rent.to_account_info(),
            },
            &signer,
        );

        create_metadata_accounts_v3(metadata_ctx, token_data, false, true, None)?;

        msg!("Token mint created successfully.");

        Ok(())
    }

    // 3.2 铸造 SPL
    pub fn mint_tokens(ctx: Context<MintTokens>, quantity: u64) -> Result<()> {
        let seeds = &["mint".as_bytes(), &[ctx.bumps.mint]];
        let signer = [&seeds[..]];

        mint_to(
            CpiContext::new_with_signer(
                ctx.accounts.token_program.to_account_info(),
                MintTo {
                    authority: ctx.accounts.mint.to_account_info(),
                    to: ctx.accounts.destination.to_account_info(),
                    mint: ctx.accounts.mint.to_account_info(),
                },
                &signer,
            ),
            quantity,
        )?;

        Ok(())
    }
}

// 4.主程序需要的账户
#[derive(Accounts)]
#[instruction(params: InitTokenParams)]
pub struct InitToken<'info> {
    // Metaplex 账户
    #[account(mut)]
    pub metadata: UncheckedAccount<'info>,
    #[account(
        init,
        seeds = [b"mint"],
        bump,
        payer = payer,
        mint::decimals = params.decimals,
        mint::authority = mint,
    )]
    pub mint: Account<'info, Mint>,
    #[account(mut)]
    pub payer: Signer<'info>,
    pub rent: Sysvar<'info, Rent>,
    pub system_program: Program<'info, System>,
    pub token_program: Program<'info, Token>,
    pub token_metadata_program: Program<'info, Metaplex>,
}

#[derive(Accounts)]
pub struct MintTokens<'info> {
    #[account(
        mut,
        seeds = [b"mint"],
        bump,
        mint::authority = mint,
    )]
    pub mint: Account<'info, Mint>,
    #[account(
        init_if_needed,
        payer = payer,
        associated_token::mint = mint,
        associated_token::authority = payer,
    )]
    pub destination: Account<'info, TokenAccount>,
    #[account(mut)]
    pub payer: Signer<'info>,
    pub rent: Sysvar<'info, Rent>,
    pub system_program: Program<'info, System>,
    pub token_program: Program<'info, Token>,
    pub associated_token_program: Program<'info, AssociatedToken>,
}

// 5.账户的数据
// 5. 定义init令牌参数
#[derive(AnchorSerialize, AnchorDeserialize, Debug, Clone)]
pub struct InitTokenParams {
    pub name: String,
    pub symbol: String,
    pub uri: String,
    pub decimals: u8,
}

部署

部署完成


测试

替换anchor.test.ts内容

describe("Test Minter", () => {
  const METADATA_SEED = "metadata";
  const TOKEN_METADATA_PROGRAM_ID = new web3.PublicKey(
    "F64uG9fPnEZYZ6G4Nbbuz6D715gYAKw1j71etHLNjHx2"
  ); // 你的程序 ID,和程序相同

  const MINT_SEED = "mint";

  // SPL基础信息
  const payer = pg.wallet.publicKey;
  const metadata = {
    name: "My The first token",
    symbol: "TFT",
    uri: "https://5vfxc4tr6xoy23qefqbj4qx2adzkzapneebanhcalf7myvn5gzja.arweave.net/7UtxcnH13Y1uBCwCnkL6APKsge0hAgacQFl-zFW9NlI",
    decimals: 9,
  };
  const mintAmount = 1000;
  const [mint] = web3.PublicKey.findProgramAddressSync(
    [Buffer.from(MINT_SEED)],
    pg.PROGRAM_ID
  );

  const [metadataAddress] = web3.PublicKey.findProgramAddressSync(
    [
      Buffer.from(METADATA_SEED),
      TOKEN_METADATA_PROGRAM_ID.toBuffer(),
      mint.toBuffer(),
    ],
    TOKEN_METADATA_PROGRAM_ID
  );

  // 测试初始化
  it("initialize", async () => {
    const info = await pg.connection.getAccountInfo(mint);
    if (info) {
      return;
    }
    console.log("  Mint not found. Attempting to initialize.");

    const context = {
      metadata: metadataAddress,
      mint,
      payer,
      rent: web3.SYSVAR_RENT_PUBKEY,
      systemProgram: web3.SystemProgram.programId,
      tokenProgram: anchor.utils.token.TOKEN_PROGRAM_ID,
      tokenMetadataProgram: TOKEN_METADATA_PROGRAM_ID,
    };

    const tx = await pg.program.methods
      .initToken(metadata)
      .accounts(context)
      .transaction();

    const txHash = await web3.sendAndConfirmTransaction(
      pg.connection,
      tx,
      [pg.wallet.keypair],
      { skipPreflight: true }
    );
    console.log(`  https://explorer.solana.com/tx/${txHash}?cluster=devnet`);
    const newInfo = await pg.connection.getAccountInfo(mint);
    assert(newInfo, "  Mint should be initialized.");
  });

  // 测试铸造
  it("mint tokens", async () => {
    const destination = await anchor.utils.token.associatedAddress({
      mint: mint,
      owner: payer,
    });

    let initialBalance: number;
    try {
      const balance = await pg.connection.getTokenAccountBalance(destination);
      initialBalance = balance.value.uiAmount;
    } catch {
      // Token account not yet initiated has 0 balance
      initialBalance = 0;
    }

    const context = {
      mint,
      destination,
      payer,
      rent: web3.SYSVAR_RENT_PUBKEY,
      systemProgram: web3.SystemProgram.programId,
      tokenProgram: anchor.utils.token.TOKEN_PROGRAM_ID,
      associatedTokenProgram: anchor.utils.token.ASSOCIATED_PROGRAM_ID,
    };

    const txHsh = await pg.program.methods
      .mintTokens(new BN(mintAmount * 10 ** metadata.decimals))
      .accounts(context)
      .signers([pg.wallet.keypair])
      .rpc();

    // const txHash = await web3.sendAndConfirmTransaction(
    //   pg.connection,
    //   tx,
    //   [pg.wallet.keypair],
    //   { skipPreflight: true }
    // );
    console.log(`mint Hash =>`, txHsh);

    const postBalance = (
      await pg.connection.getTokenAccountBalance(destination)
    ).value.uiAmount;
    assert.equal(
      initialBalance + mintAmount,
      postBalance,
      "Post balance should equal initial plus mint amount"
    );
  });
});


铸造

运行测试代码,进行SPL铸造, 记得把密钥导入 Phantom(切换网络)

增发

注释初始化代码,增加第二次SPL铸造

总结

Anchor框架总结

// 1.管理账户的
use anchor_lang::prelude::*;

// 管理代币的
use anchor_spl::{
    associated_token::AssociatedToken, // 处理关联代币账户的功能
    metadata::{
        create_metadata_accounts_v3,       // 创建元数据账户的功能
        mpl_token_metadata::types::DataV2, // 元数据的结构体定义
        CreateMetadataAccountsV3,          // 创建元数据账户的指令结构体
        Metadata as Metaplex,              // 将 Metadata 重命名为 Metaplex,以便于使用
    },
    token::{
        mint_to,      // 铸币功能
        Mint,         // 代币铸造的结构体
        MintTo,       // 铸币指令的结构体
        Token,        // 代币的基本功能
        TokenAccount, // 代币账户的结构体
    },
};

补充

标签:account,const,第一个,Solana,pub,token,SPL,mint,metadata
From: https://www.cnblogs.com/sjie/p/18518801

相关文章

  • 构建第一个ArkTS应用(Stage模型)
    copy官网的留个记号:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/start-with-ets-stage-V5创建ArkTS工程若首次打开DevEcoStudio,请点击CreateProject创建工程。如果已经打开了一个工程,请在菜单栏选择File>New>CreateProject来创建一个新工程。选......
  • HDMI和DisplayPort接口哪个更好_1
    在数字显示技术领域,HDMI和DisplayPort是两种主要的视频接口标准。本文将对比这两种接口在不同方面的特性和性能,包括:1.视频和音频传输质量;2.分辨率和刷新率支持;3.兼容性和通用性;4.适用场景和应用;5.未来发展趋势。HDMI广泛用于家庭娱乐和商用显示设备,而DisplayPort则在高端显示和电......
  • 总结 JavaScript 中8种数组常用的操作 API,array.push,pop,shift,unshift,slice,splice
    前言JavaScript中数组是一个重要的数据结构,它相比于字符串有更多的方法,在一些算法题中我们经常需要将字符串转化为数组,使用数组里面的API进行操作。本篇文章总结了JavaScript中有许多数组常用的操作API,以下是一些常见的操作及其示例:1.push():在数组末尾添加一个或多个元素,并......
  • CSS(块级,行级,行块级,display,div和span,盒子模型,文档流,浮动)
    块级,行级,行块级块级:无论内容都是,都会独自占据一行的.可以设置宽高,若没有设置宽高,默认于父级标签相同.例如:<p>,<h1>,<ul>,<ol>,<hr/>等.行级:只占自身大小的标签,不会占一行.设置宽高无效.例如:<font>,<b>,<i>,<a>等.行块级:不会占一行,而且可以设置宽高.例如:<inp......
  • 3D Gaussian Splatting代码详解(一):模型训练、数据加载
    1模型训练deftraining(dataset,opt,pipe,testing_iterations,saving_iterations,checkpoint_iterations,checkpoint,debug_from):first_iter=0#初始化高斯模型,用于表示场景中的每个点的3D高斯分布gaussians=GaussianModel(dataset.sh_degree)......
  • TetSphere Splatting——非常粗糙的阅读笔记分享,欢迎讨论~
       源代码主页:gmh14/tssplat:TetSphereSplatting:RepresentingHigh-QualityGeometrywithLagrangianVolumetricMeshes原文地址:[2405.20283]TetSphereSplatting:RepresentingHigh-QualityGeometrywithLagrangianVolumetricMeshes  与NeRF类似,DMTet......
  • FreGS: 3D Gaussian Splatting with Progressive Frequency Regularization
    Abstract3DGShasachievedveryimpressiveperformanceinreal-timenovelviewsynthesis.However,itoftensuffersfromover-reconstructionduringGaussiandensificationwherehigh-varianceimageregionsarecoveredbyafewlargeGaussiansonly,leading......
  • Autofac 解释第一个例子 《第一篇》
    Autofac解释第一个例子《第一篇》 Autofac是一个轻量级的依赖注入的框架,同类型的框架还有Spring.NET,Unity,Castle等。Autofac的使用有一个非常让人郁闷的地方,就是服务器要求安装有Microsoft.NETFramework4KB2468871。该补丁的地址是:http://www.microsoft.com/zh-cn......
  • 【Linux学习】(8)第一个Linux编程进度条程序|git三板斧
    前言第一个Linux编程——进度条git的简单使用一、第一个Linux编程——进度条在写进度条之前我们需要两个基础知识:回车换行缓冲区1.回车换行首先我们需要知道回车换行它是两个概念,回车是回车,换行是换行换行:光标从上往下,直接到下一行(例如光标现在在当前行的第5个......
  • C# SuperSocket 基础七【CountSpliterReceiveFilte-固定数量分隔符协议】不使用COMMAN
    publicclassCountSpliterReceiveFilterSession:AppSession<CountSpliterReceiveFilterSession>{publicoverridevoidSend(stringmessage){Console.WriteLine("发送消息:"+message);base.Send(message)......