首页 > 编程语言 >使用 Anchor 和 QuickNode 在 Solana 上创建NFT: 2024 版指南

使用 Anchor 和 QuickNode 在 Solana 上创建NFT: 2024 版指南

时间:2024-09-29 10:23:45浏览次数:11  
标签:program account Solana pub 2024 NFT accounts authority mint

gg

欢迎来到本教程。今天,我们将使用 Solana PlaygroundQuickNode RPC 和一个 IPFS 服务,在 Anchor/Rust 中创建一个 Solana 程序,以直接在链上铸造 NFT。

作为预备步骤,我们将在去中心化存储服务中准备我们的 NFT 图像和元数据。我们将使用 QuickNode IPFS,这是一个 IPFS 存储服务,适用于需要快速、高可用访问和高级功能的解决方案。

让我们上传将成为我们 NFT 的图像。在 QuickNode 仪表板上,转到 IPFS 然后是文件,你可以拖放你的图像。

让我们复制出现在我们上传的文件名旁边的内容标识符 (CID)。然后我们将其附加到地址 quicknode.myfilebase.com/ipfs/ 以获得我们将在元数据中使用的 URL。测试此 URL 以确保一切正确是很重要的。

为我们的 NFT 创建元数据

为此,我们将基于 Metaplex 为 Solana 上的 NFT 元数据创建的标准。你可以访问 Metaplex 文档 了解更多信息。

我们的元数据将是一个 JSON 文件,包含我们为 NFT 选择的参数 Name、Symbol 和 Description。

{  
    "name": "Name",  
    "symbol": "symbol",  
    "description": "your description",  
    "image": "quicknode.myfilebase.com/ipfs/CID",  
    "attributes": [  
        {  
            "trait_type": "type",  
            "value": "value"  
        }  
    ],  
    "properties": {  
        "creators": [  
            {  
                "address": "creators's wallet address",  
                "share": royalty  
            }  
        ],  
        "files": [  
            {  
                "type": "image/png",  
                "uri": "ipfs://CID"  
            }  
        ]  
    },  
    "collection": {  
        "name": "name",  
        "family": "family"  
    }  
}

一旦我们的 .json 文件准备好,我们将返回 QuickNode 的文件选项卡并像处理图像文件一样拖放它。现在我们已经准备好继续。

设置 Solana Playground

我们将前往 Solana Playground 并通过创建一个新钱包进行连接。

Solana Playground

点击显示“not connected”的地方,然后保存密钥对并继续。

保存密钥对

在这里我们可以看到创建的钱包地址。

创建的钱包地址

创建一个新项目,给它命名,选择 Anchor (Rust) 并创建。

创建的 Anchor 项目

注意:我们将需要大约 10 个 DevNet 的 Solana 来部署合约。要为我们的钱包充值,我们必须在 Sol Faucet 中输入我们刚刚创建的钱包地址,该网站允许我们每 24 小时充值 3 个 Solana。这意味着我们需要几天时间来完成充值。作为替代方案,我们建议使用其他钱包地址和 VPN 来加快进程。_

现在是时候配置 solpg 以使用 QuickNode RPC 在 Solana DevNet 上工作了。点击设置选项卡,然后点击端点选项卡。

设置 Solana Playground

选择自定义并粘贴你的 QuickNode 私有 RPC 用于 Solana DevNet。

自定义 RPC

完成后,回到我们新创建的项目中,我们将删除不需要的代码,并保留来自 anchor_lang 的主要导入、程序 id 的默认声明以及程序和派生账户属性的声明。

应该看起来像这样:

use anchor_lang::prelude::*;

declare_id!("11111111111111111111111111111111");

#[program]

#[derive(Accounts)]

现在,我们将导入所有这些库和必要的元素。这些库具有在 Solana 上铸造新 NFT 所需的所有标准功能,我们将在过程中需要每一个。

use anchor_spl::associated_token::AssociatedToken;
use anchor_spl::metadata::{
    create_master_edition_v3, create_metadata_accounts_v3, CreateMasterEditionV3,
    CreateMetadataAccountsV3, Metadata,
};
use anchor_spl::token::{mint_to, Mint, MintTo, Token, TokenAccount};
use mpl_token_metadata::types::{Collection, Creator, DataV2};

现在你的代码应该看起来像这样:

use anchor_lang::prelude::*;
use anchor_spl::associated_token::AssociatedToken;
use anchor_spl::metadata::{
    create_master_edition_v3, create_metadata_accounts_v3, CreateMasterEditionV3,
    CreateMetadataAccountsV3, Metadata,
};
use anchor_spl::token::{mint_to, Mint, MintTo, Token, TokenAccount};
use mpl_token_metadata::types::{Collection, Creator, DataV2};

declare_id!("11111111111111111111111111111111");

#[program]

#[derive(Accounts)]

在 [derive(Accounts)] 属性下,我们将定义一个包含与程序交互所需的所有账户的结构。

我们将定义一个名为 CreateNFT 的公共结构。它将有一个参数 'info,表示在使用此结构作为上下文的指令执行期间它将保留在内存中。

我们在结构中定义的第一个账户将是 authority。我们将其定义为类型为 Signer 的公共账户,具有 'info 类型的生命周期。我们在此结构中定义的所有账户都将是公共类型并具有此生命周期。

#[derive(Accounts)]
pub struct CreateNFT<'info> {
   pub authority: Signer<'info>,
}

我们将为此账户定义一个属性。放置以下行,并在括号内放置 mut 这个词,表示可变。这意味着 NFT 的权限将来可以更改。

#[derive(Accounts)]
pub struct CreateNFT<'info> {
  #[account(mut)]
  pub authority: Signer<'info>,
}

同样,我们将定义 payer 账户及其相应的可变属性 mut

#[derive(Accounts)]
pub struct CreateNFT<'info> {
  #[account(mut)]
  pub authority: Signer<'info>,
  #[account(mut)]
  pub payer: Signer<'info>,
}

现在我们声明 mint 账户,类型为 Account,它将携带类型为 mint 的结构。我们已经从 anchor_spl 代币库中导入了这个结构。

#[derive(Accounts)]
pub struct CreateNFT<'info> {
  #[account(mut)]
  pub authority: Signer<'info>,
  #[account(mut)]
  pub payer: Signer<'info>,
  pub mint: Account<'info, Mint>,
}

我们的 mint 账户将携带以下参数:

  • init: 因为我们必须初始化它
  • payer 账户作为支付初始化的账户
  • 内部 mint 结构:小数位为 0,因为它是不可替代代币 (NFT)
  • 然后我们将 authority 账户作为 mint 结构的权限传递
  • freeze_authority 参数同样
#[derive(Accounts)]  
pub struct CreateNFT<'info> {  
  #[account(mut)]  
  pub authority: Signer<'info>,  
  #[account(mut)]  
  pub payer: Signer<'info>,  
  #[account(   
    init,  
    payer = payer,   
    mint::decimals = 0,  
    mint::authority = authority,  
    mint::freeze_authority = authority,  
    )]  
  pub mint: Account<'info, Mint>,  
}

现在我们设置种子来创建派生地址,首先将以字节形式携带单词 mint 和 id 参数的字节形式。

#[derive(Accounts)]  
pub struct CreateNFT<'info> {  
  #[account(mut)]  
  pub authority: Signer<'info>,  
  #[account(mut)]  
  pub payer: Signer<'info>,  
  #[account(   
    init,  
    payer = payer,   
    mint::decimals = 0,  
    mint::authority = authority,  
    mint::freeze_authority = authority,  
    seeds = ["mint".as_bytes(), id.to_le_bytes().as_ref()],   
    bump,  
    )]  
  pub mint: Account<'info, Mint>,  
}

我们将从合约中创建的指令中获取此 id 参数,并在调用时接收它以铸造 NFT。因此,我们必须声明此属性,将从以下指令中获取。

#[derive(Accounts)]  
#[instruction(id: u64)]  
  
pub struct CreateNFT<'info> {  
  #[account(mut)]  
  pub authority: Signer<'info>,  
  #[account(mut)]  
  pub payer: Signer<'info>,  
  #[account(   
    init,  
    payer = payer,   
    mint::decimals = 0,  
    mint::authority = authority,  
    mint::freeze_authority = authority,  
    seeds = ["mint".as_bytes(), id.to_le_bytes().as_ref()],   
    bump,  
    )]  
  pub mint: Account<'info, Mint>,  
}

现在我们将创建 token_account,它将携带一种代币账户类型的结构,允许我们将铸造账户与将接收铸造 NFT 的账户关联起来。

#[derive(Accounts)]  
#[instruction(id: u64)]  
  
pub struct CreateNFT<'info> {  
  #[account(mut)]  
  pub authority: Signer<'info>,  
  #[account(mut)]  
  pub payer: Signer<'info>,  
  #[account(   
    init,  
    payer = payer,   
    mint::decimals = 0,  
    mint::authority = authority,  
    mint::freeze_authority = authority,  
    seeds = ["mint".as_bytes(), id.to_le_bytes().as_ref()],   
    bump,  
    )]  
  pub mint: Account<'info, Mint>,  
  pub token_account: Account<'info, TokenAccount>,  
}

其参数将是 init_if_needed,即如果不存在将创建一个新账户。我们将发送 payer 作为我们的支付账户。对于 associated_token 结构中存在的 mint 参数,我们将发送铸造账户和支付账户作为权限。

#[derive(Accounts)]  
#[instruction(id: u64)]  
  
pub struct CreateNFT<'info> {  
  #[account(mut)]  
  pub authority: Signer<'info>,  
  #[account(mut)]  
  pub payer: Signer<'info>,  
  #[account(   
    init,  
    payer = payer,   
    mint::decimals = 0,  
    mint::authority = authority,  
    mint::freeze_authority = authority,  
    seeds = ["mint".as_bytes(), id.to_le_bytes().as_ref()],   
    bump,  
    )]  
  pub mint: Account<'info, Mint>,  
  #[account(  
        init_if_needed,  
        payer = payer,  
        associated_token::mint = mint,  
        associated_token::authority = payer,  
    )]  
  pub token_account: Account<'info, TokenAccount>,  
  
}

现在我们声明以下过程所需的账户:

  • associated_token_program:Anchor SPL 库程序,用于关联账户。
  • rent:Sysvar 类型程序,帮助确定租金成本。
  • system_program:Solana 的主要程序。
  • token_program:允许在 Solana 上创建代币的程序。
  • metadata_program:用于根据 Metaplex 标准将元数据关联到账户的程序。
#[derive(Accounts)]  
#[instruction(id: u64)]  
  
pub struct CreateNFT<'info> {  
  #[account(mut)]  
  pub authority: Signer<'info>,  
  #[account(mut)]  
  pub payer: Signer<'info>,  
  #[account(   
    init,  
    payer = payer,   
    mint::decimals = 0,  
    mint::authority = authority,  
    mint::freeze_authority = authority,  
    seeds = ["mint".as_bytes(), id.to_le_bytes().as_ref()],   
    bump,  
    )]  
  pub mint: Account<'info, Mint>,  
  #[account(  
        init_if_needed,  
        payer = payer,  
        associated_token::mint = mint,  
        associated_token::authority = payer,  
    )]  
   pub token_account: Account<'info, TokenAccount>,  
   pub associated_token_program: Program<'info, AssociatedToken>,  
   pub rent: Sysvar<'info, Rent>,  
   pub system_program: Program<'info, System>,  
   pub token_program: Program<'info, Token>,  
   pub metadata_program: Program<'info, Metadata>,  
}

最后,我们声明 UncheckedAccount 类型的账户,master_edition_account 和 nft_metadata。我们还必须添加带有 ///CHECK 的行,以通知编译器我们正在处理未经检查的账户类型。

#[derive(Accounts)]  
#[instruction(id: u64)]  
  
pub struct CreateNFT<'info> {  
  #[account(mut)]  
  pub authority: Signer<'info>,  
  #[account(mut)]  
  pub payer: Signer<'info>,  
  #[account(   
    init,  
    payer = payer,   
    mint::decimals = 0,  
    mint::authority = authority,  
    mint::freeze_authority = authority,  
    seeds = ["mint".as_bytes(), id.to_le_bytes().as_ref()],   
    bump,  
    )]  
  pub mint: Account<'info, Mint>,  
  #[account(  
        init_if_needed,  
        payer = payer,  
        associated_token::mint = mint,  
        associated_token::authority = payer,  
    )]  
   pub token_account: Account<'info, TokenAccount>,  
   pub associated_token_program: Program<'info, AssociatedToken>,  
   pub rent: Sysvar<'info, Rent>,  
   pub system_program: Program<'info, System>,  
   pub token_program: Program<'info, Token>,  
   pub metadata_program: Program<'info, Metadata>,  
   /// CHECK:  
   pub master_edition_account: UncheckedAccount<'info>,  
   /// CHECK:  
   pub nft_metadata: UncheckedAccount<'info>,  
}

master_edition_account 是必要的,以确保 Solana 网络上的代币通过将其与主版本账户关联来有效地是不可替代的。通过它可以创建更多的代币信息实例,所有这些实例都与这个唯一的账户关联,以创建不同类型的 NFT。

我们将为此账户定义以下参数:

  • 可变:其种子将按照文档要求精确定义如下。
  • Bump:我们必须放置这一行,因为这个 PDA 账户将由元数据程序生成。
#[derive(Accounts)]  
#[instruction(id: u64)]  
  
pub struct CreateNFT<'info> {  
  #[account(mut)]  
  pub authority: Signer<'info>,  
  #[account(mut)]  
  pub payer: Signer<'info>,  
  #[account(   
    init,  
    payer = payer,   
    mint::decimals = 0,  
    mint::authority = authority,  
    mint::freeze_authority = authority,  
    seeds = ["mint".as_bytes(), id.to_le_bytes().as_ref()],   
    bump,  
    )]  
  pub mint: Account<'info, Mint>,  
  #[account(  
        init_if_needed,  
        payer = payer,  
        associated_token::mint = mint,  
        associated_token::authority = payer,  
    )]  
   pub token_account: Account<'info, TokenAccount>,  
   pub associated_token_program: Program<'info, AssociatedToken>,  
   pub rent: Sysvar<'info, Rent>,  
   pub system_program: Program<'info, System>,  
   pub token_program: Program<'info, Token>,  
   pub metadata_program: Program<'info, Metadata>,  
   #[account(  
        mut,  
        seeds = [  
            b"metadata".as_ref(),  
            metadata_program.key().as_ref(),  
            mint.key().as_ref(),  
            b"edition".as_ref(),  
        ],  
        bump,  
        seeds::program = metadata_program.key()  
      )]    
   /// 检查:  
   pub master_edition_account: UncheckedAccount<'info>,  
   /// 检查:  
   pub nft_metadata: UncheckedAccount<'info>,  
}

对于 nft_metadata 账户,我们将设置以下参数,方式与 master_edition_account 类似。

#[derive(Accounts)]  
#[instruction(id: u64)]  
  
pub struct CreateNFT<'info> {  
  #[account(mut)]  
  pub authority: Signer<'info>,  
  #[account(mut)]  
  pub payer: Signer<'info>,  
  #[account(   
    init,  
    payer = payer,   
    mint::decimals = 0,  
    mint::authority = authority,  
    mint::freeze_authority = authority,  
    seeds = ["mint".as_bytes(), id.to_le_bytes().as_ref()],   
    bump,  
    )]  
  pub mint: Account<'info, Mint>,  
  #[account(  
        init_if_needed,  
        payer = payer,  
        associated_token::mint = mint,  
        associated_token::authority = payer,  
    )]  
   pub token_account: Account<'info, TokenAccount>,  
   pub associated_token_program: Program<'info, AssociatedToken>,  
   pub rent: Sysvar<'info, Rent>,  
   pub system_program: Program<'info, System>,  
   pub token_program: Program<'info, Token>,  
   pub metadata_program: Program<'info, Metadata>,  
   #[account(  
        mut,  
        seeds = [  
            b"metadata".as_ref(),  
            metadata_program.key().as_ref(),  
            mint.key().as_ref(),  
            b"edition".as_ref(),  
        ],  
        bump,  
        seeds::program = metadata_program.key()  
      )]  
  
   /// 检查:  
   pub master_edition_account: UncheckedAccount<'info>,  
   #[account(  
        mut,  
        seeds = [  
            b"metadata".as_ref(),  
            metadata_program.key().as_ref(),  
            mint.key().as_ref(),  
        ],  
        bump,  
        seeds::program = metadata_program.key()  
    )]  
   /// 检查:  
   pub nft_metadata: UncheckedAccount<'info>,  
}

有了我们的 CreateNFT 结构,我们将开始创建我们的程序。在程序属性下,我们创建公共模块 nft_program

#[program]  
pub mod nft_program {}

我们使用以下指令导入整个父模块:

#[program]  
pub mod nft_program {  
 use super::*;  
}

我们将以下列方式创建公共函数 create_single_nft

#[program]  
pub mod nft_program {  
 use super::*;  
 pub fn create_single_nft() -> Result<()> {  
}

然后我们将指定函数将接收的参数:

  • Context: 我们将在其中引入我们在创建 NFT 结构中创建的所有账户
  • NFT ID
  • 名称
  • 符号
  • URI
  • 价格
  • 数量
#[program]  
pub mod nft_program {  
 use super::*;  
 pub fn create_single_nft(  
 ctx: Context<CreateNFT>,  
 id: u64,  
 name: String,  
 symbol: String,  
 uri: String,  
 price: f32,  
 cant: u64,  
) -> Result<()> {  
}

我们的函数将按以下特定顺序调用以下 3 个函数:

  • 我们从 anchor_spl 代币库导入的 mint_to 函数
  • create_metadata_accounts_v3
  • create_master_edition_v3 从 anchor_spl 元数据库导入
#[program]  
pub mod nft_program {  
 use super::*;  
 pub fn create_single_nft(  
 ctx: Context<CreateNFT>,  
 id: u64,  
 name: String,  
 symbol: String,  
 uri: String,  
 price: f32,  
 cant: u64,  
) -> Result<()> {  
  mint_to()?;  
  create_metadata_accounts_v3()?;  
  create_master_edition_v3()?;  
}

如果我们查看 Rust 文档 中的 mint_to 函数,我们会看到它接收一个 CpiContext,其中包含一个在同一库中定义的 mint_to 结构类型和一个数量。

rust 文档 mint_to

CpiContext 用于我们的程序与网络上的其他程序交互。此上下文必须通过 new_with_signer() 函数定义,因为它需要一个种子来签署交易。

 mint_to(  
            CpiContext::new_with_signer(  
                ctx.accounts.token_program.to_account_info(),  
                MintTo {  
                    authority: ctx.accounts.authority.to_account_info(),  
                    to: ctx.accounts.token_account.to_account_info(),  
                    mint: ctx.accounts.mint.to_account_info(),  
  
                &[&seeds[..]],  
             ),

new_with_signer() 函数接收我们将与之交互的程序。此程序已在我们的 CreateNFT 结构中定义,因此我们通过上下文 ctx.account 代币程序访问它,并使用 to_account_info() 函数读取其地址。这是 Solana 上的代币创建程序。

new_with_signer() 函数的下一个参数,用于创建 CpiContext,将是 MintTo 账户数组。它将包含:

  • authority,也在我们的上下文中定义,我们通过 ctx.account_authority 访问它,也将其转换为账户信息
  • to,接收 NFT 的账户,这将是与代币 mint 账户关联的钱包 payer 的账户,或支付交易的账户
  • mint,网络上的代币身份

所有这些账户都来自我们已经创建的上下文。

最后,函数将接收形成我们 seeds 的字节列表。

此种子必须以前以以下方式声明:

  • 我们将作为参数接收的 id 转换为字节 id_bytes = id.to_le_bytes
  • 然后,我们将我们的 seeds 声明为一个数组,首先将字符串 "mint" 转换为字节
  • 我们的 id_bytes 变量转换为引用,并从我们的上下文中动态获取并引用的 mint 账户的 bump

这将是签署所有后续函数调用的种子。

 ) -> Result<()> {  
        let id_bytes = id.to_le_bytes();  
        let seeds = &["mint".as_bytes(),id_bytes.as_ref(),&[ctx.bumps.mint],  
        ];  
  
mint_to(  
            CpiContext::new_with_signer(  
                ctx.accounts.token_program.to_account_info(),  
                MintTo {  
                    authority: ctx.accounts.authority.to_account_info(),  
                    to: ctx.accounts.token_account.to_account_info(),  
                    mint: ctx.accounts.mint.to_account_info(),  
  
                &[&seeds[..]],  
             ),

现在让我们构建对 create_metadata_accounts_v3 函数的调用。这次,参数将是:

  • 再次使用 new_with_signer() 函数创建的 CpiContext
  • 一个 DataV2 结构
  • 以及我们将发送的两个布尔值,第一个设置为授权账户为相同的签名者,第二个使元数据可变
  • 最后,集合详细信息的单词 none

我们将首先使用来自我们上下文的元数据程序账户构建 CpiContext。这将是我们在此函数中将与之交互的程序。

然后,我们将发送 CreateMetadataAccountsV3 结构。使用以下参数,均从我们的 CreateNFT 结构中获取:

  • payer
  • mint 账户
  • NFT metadata 账户
  • 铸造和更新授权,将与授权账户一起使用
  • system_program 也在上下文中可用
  • rent
  • 最后,函数将接收我们已经创建并可用的 seeds
create_metadata_accounts_v3(  
            CpiContext::new_with_signer(  
                ctx.accounts.metadata_program.to_account_info(),  
                CreateMetadataAccountsV3 {  
                    payer: ctx.accounts.payer.to_account_info(),  
                    mint: ctx.accounts.mint.to_account_info(),  
                    metadata: ctx.accounts.nft_metadata.to_account_info(),  
                    mint_authority: ctx.accounts.authority.to_account_info(),  
                    update_authority: ctx.accounts.authority.to_account_info(),  
                    system_program: ctx.accounts.system_program.to_account_info(),  
                    rent: ctx.accounts.rent.to_account_info(),  
                },  
                &[&seeds[..]],

我们仍然需要填写 DataV2 结构的参数,这些参数将是:

  • namesymbol 和 URI 都将作为参数传递给我们的 create_single_nft 函数
  • seller_fee_basis_points 参数的值为 0
  • 参数 creatorscollection 和 use 都设置为 none

我们将放置消息 msg,表示正在执行创建元数据账户的函数,我们的代码将如下所示:

  msg!("Run create metadata accounts v3");  
  
        create_metadata_accounts_v3(  
            CpiContext::new_with_signer(  
                ctx.accounts.metadata_program.to_account_info(),  
                CreateMetadataAccountsV3 {  
                    payer: ctx.accounts.payer.to_account_info(),  
                    mint: ctx.accounts.mint.to_account_info(),  
                    metadata: ctx.accounts.nft_metadata.to_account_info(),  
                    mint_authority: ctx.accounts.authority.to_account_info(),  
                    update_authority: ctx.accounts.authority.to_account_info(),  
                    system_program: ctx.accounts.system_program.to_account_info(),  
                    rent: ctx.accounts.rent.to_account_info(),  
                },  
                &[&seeds[..]],  
            ),  
            DataV2 {  
                name,  
                symbol,  
                uri,  
                seller_fee_basis_points: 0,  
                creators: None,  
                collection: None,  
                uses: None,  
            },  
            true,  
            true,  
            None,  
        )?;

我们还将以相同的方式调用 create_master_edition_v3 函数,使用 new_with_signer() 函数创建的 CpiContext,最大供应量为 1,方式如下 some(1)

new_with_signer() 函数接收元数据程序作为程序,CreateMetadataAccountsV3 结构包含上下文中的所有这些账户:

  • edition 账户,payer 账户作为付款人,mint 账户,包含代币 metadata 的账户
  • mint_authorityupdate_authoritysystem_programtoken_program 和 rent
  • 最后,我们放置我们的 seeds 进行签名。我们将放置消息 msg “Executing the create master edition function”,最后放置消息 msg “The NFT has been successfully created”,并用 ok 完成函数

我们的代码将如下所示:

  msg!("Run create master edition v3");  
  
        create_master_edition_v3(  
            CpiContext::new_with_signer(  
                ctx.accounts.metadata_program.to_account_info(),  
                CreateMasterEditionV3 {  
                    edition: ctx.accounts.master_edition_account.to_account_info(),  
                    payer: ctx.accounts.payer.to_account_info(),  
                    mint: ctx.accounts.mint.to_account_info(),  
                    metadata: ctx.accounts.nft_metadata.to_account_info(),  
                    mint_authority: ctx.accounts.authority.to_account_info(),  
                    update_authority: ctx.accounts.authority.to_account_info(),  
                    system_program: ctx.accounts.system_program.to_account_info(),  
                    token_program: ctx.accounts.token_program.to_account_info(),  
                    rent: ctx.accounts.rent.to_account_info(),  
                },  
                &[&seeds[..]],  
            ),  
      Some(1),  
          )?;  
  
          msg!("Minted NFT successfully");  
  
          Ok(())  
    }

这将是整个代码:

use anchor_lang::prelude::*;  
use anchor_spl::associated_token::AssociatedToken;  
use anchor_spl::metadata::{  
    create_master_edition_v3, create_metadata_accounts_v3, CreateMasterEditionV3,  
    CreateMetadataAccountsV3, Metadata,  
};  
use anchor_spl::token::{mint_to, Mint, MintTo, Token, TokenAccount};  
use mpl_token_metadata::types::{Collection, Creator, DataV2};  
  
declare_id!("nuvdhmYq5Z2Eg4nBi29Tu2VcbpE9nuiCQ68rkyAB3A1");  
  
#[program]  
pub mod nft_program {  
 use super::*;  
 pub fn create_single_nft(  
 ctx: Context<CreateNFT>,  
 id: u64,  
 name: String,  
 symbol: String,  
 uri: String,  
 price: f32,  
 cant: u64,  
) -> Result<()> {  
 msg!("Creating seeds");  
        let id_bytes = id.to_le_bytes();  
        let seeds = &["mint".as_bytes(),id_bytes.as_ref(),&[ctx.bumps.mint],  
        ];  
  
        msg!("Run mint_to");  
  
        mint_to(  
            CpiContext::new_with_signer(  
                ctx.accounts.token_program.to_account_info(),  
                MintTo {  
                    authority: ctx.accounts.authority.to_account_info(),  
                    to: ctx.accounts.token_account.to_account_info(),  
                    mint: ctx.accounts.mint.to_account_info(),  
                },  
                &[&seeds[..]],  
            ),  
            1, // 1 token  
        )?;  
  
        msg!("Run create metadata accounts v3");  
  
        create_metadata_accounts_v3(  
            CpiContext::new_with_signer(  
                ctx.accounts.metadata_program.to_account_info(),  
                CreateMetadataAccountsV3 {  
                    payer: ctx.accounts.payer.to_account_info(),  
                    mint: ctx.accounts.mint.to_account_info(),  
                    metadata: ctx.accounts.nft_metadata.to_account_info(),  
                    mint_authority: ctx.accounts.authority.to_account_info(),  
                    update_authority: ctx.accounts.authority.to_account_info(),  
                    system_program: ctx.accounts.system_program.to_account_info(),  
                    rent: ctx.accounts.rent.to_account_info(),  
                },  
                &[&seeds[..]],  
            ),  
            DataV2 {  
                name,  
                symbol,  
                uri,  
                seller_fee_basis_points: 0,  
                creators: None,  
                collection: None,  
                uses: None,  
            },  
            true,  
            true,  
            None,  
        )?;  
  
        msg!("Run create master edition v3");  
  
        create_master_edition_v3(  
            CpiContext::new_with_signer(  
                ctx.accounts.metadata_program.to_account_info(),  
                CreateMasterEditionV3 {  
                    edition: ctx.accounts.master_edition_account.to_account_info(),  
                    payer: ctx.accounts.payer.to_account_info(),  
                    mint: ctx.accounts.mint.to_account_info(),  
                    metadata: ctx.accounts.nft_metadata.to_account_info(),  
                    mint_authority: ctx.accounts.authority.to_account_info(),  
                    update_authority: ctx.accounts.authority.to_account_info(),  
                    system_program: ctx.accounts.system_program.to_account_info(),  
                    token_program: ctx.accounts.token_program.to_account_info(),  
                    rent: ctx.accounts.rent.to_account_info(),  
                },  
                &[&seeds[..]],  
            ),  
            Some(1),  
        )?;

        msg!("铸造NFT成功");  
  
        Ok(())  
    }  
  
    pub fn mint_to_collection(  
        ctx: Context<MintToCollection>,  
        id_collection: u64,  
        id_nft: u64,  
        name: String,  
        symbol: String,  
        uri: String,  
        price: f32,  
        cant: u64,  
    ) -> Result<()> {  
        msg!("创建种子");  
        let id_bytes = id_collection.to_le_bytes();  
        let id_nft_bytes = id_nft.to_le_bytes();  
        let seeds = &[  
            "mint".as_bytes(),  
            id_bytes.as_ref(),  
            id_nft_bytes.as_ref(),  
            &[ctx.bumps.mint],  
        ];  
  
        msg!("运行mint_to");  
  
        mint_to(  
            CpiContext::new_with_signer(  
                ctx.accounts.token_program.to_account_info(),  
                MintTo {  
                    authority: ctx.accounts.authority.to_account_info(),  
                    to: ctx.accounts.token_account.to_account_info(),  
                    mint: ctx.accounts.mint.to_account_info(),  
                },  
                &[&seeds[..]],  
            ),  
            1, // 1 代币  
        )?;  
  
        msg!("运行创建元数据账户v3");  
  
        create_metadata_accounts_v3(  
            CpiContext::new_with_signer(  
                ctx.accounts.metadata_program.to_account_info(),  
                CreateMetadataAccountsV3 {  
                    payer: ctx.accounts.payer.to_account_info(),  
                    mint: ctx.accounts.mint.to_account_info(),  
                    metadata: ctx.accounts.nft_metadata.to_account_info(),  
                    mint_authority: ctx.accounts.authority.to_account_info(),  
                    update_authority: ctx.accounts.authority.to_account_info(),  
                    system_program: ctx.accounts.system_program.to_account_info(),  
                    rent: ctx.accounts.rent.to_account_info(),  
                },  
                &[&seeds[..]],  
            ),  
            DataV2 {  
                name,  
                symbol,  
                uri,  
                seller_fee_basis_points: 0,  
                creators: Some(vec![Creator {  
                    address: ctx.accounts.payer.key(),  
                    verified: true,  
                    share: 100,  
                }]),  
                collection: Some(Collection {  
                    key: ctx.accounts.collection.key(),  
                    verified: false,  
                }),  
                uses: None,  
            },  
            true,  
            true,  
            None,  
        )?;  
  
        msg!("运行创建主版本v3");  
  
        create_master_edition_v3(  
            CpiContext::new_with_signer(  
                ctx.accounts.metadata_program.to_account_info(),  
                CreateMasterEditionV3 {  
                    edition: ctx.accounts.master_edition_account.to_account_info(),  
                    payer: ctx.accounts.payer.to_account_info(),  
                    mint: ctx.accounts.mint.to_account_info(),  
                    metadata: ctx.accounts.nft_metadata.to_account_info(),  
                    mint_authority: ctx.accounts.authority.to_account_info(),  
                    update_authority: ctx.accounts.authority.to_account_info(),  
                    system_program: ctx.accounts.system_program.to_account_info(),  
                    token_program: ctx.accounts.token_program.to_account_info(),  
                    rent: ctx.accounts.rent.to_account_info(),  
                },  
                &[&seeds[..]],  
            ),  
            Some(1),  
        )?;  
  
        msg!("铸造NFT成功");  
  
        Ok(())  
    }  
  
}  
  
#[derive(Accounts)]  
#[instruction(id: u64)]  
pub struct CreateNFT<'info> {  
    #[account(mut)]  
    pub authority: Signer<'info>,  
    #[account(mut)]  
    pub payer: Signer<'info>,  
    #[account(   
    init,  
    payer = payer,   
    mint::decimals = 0,  
    mint::authority = authority,  
    mint::freeze_authority = authority,  
    seeds = ["mint".as_bytes(), id.to_le_bytes().as_ref()],   
    bump,  
    )]  
    pub mint: Account<'info, Mint>,  
    #[account(  
        init_if_needed,  
        payer = payer,  
        associated_token::mint = mint,  
        associated_token::authority = payer,  
    )]  
    pub token_account: Account<'info, TokenAccount>,  
    pub associated_token_program: Program<'info, AssociatedToken>,  
    pub rent: Sysvar<'info, Rent>,  
    pub system_program: Program<'info, System>,  
    pub token_program: Program<'info, Token>,  
    pub metadata_program: Program<'info, Metadata>,  
    #[account(  
        mut,  
        seeds = [  
            b"metadata".as_ref(),  
            metadata_program.key().as_ref(),  
            mint.key().as_ref(),  
            b"edition".as_ref(),  
        ],  
        bump,  
        seeds::program = metadata_program.key()  
    )]  
    /// CHECK:  
    pub master_edition_account: UncheckedAccount<'info>,  
    #[account(  
        mut,  
        seeds = [  
            b"metadata".as_ref(),  
            metadata_program.key().as_ref(),  
            mint.key().as_ref(),  
        ],  
        bump,  
        seeds::program = metadata_program.key()  
    )]  
    /// CHECK:  
    pub nft_metadata: UncheckedAccount<'info>,  
}  
  
#[derive(Accounts)]  
#[instruction(id_collection: u64, id_nft: u64)]  
pub struct MintToCollection<'info> {  
    #[account(mut)]  
    pub authority: Signer<'info>,  
    #[account(mut)]  
    pub payer: Signer<'info>,  
    #[account(   
    init,  
    payer = payer,   
    mint::decimals = 0,  
    mint::authority = authority,  
    mint::freeze_authority = authority,  
    seeds = ["mint".as_bytes(),   
             id_collection.to_le_bytes().as_ref(),  
             id_nft.to_le_bytes().as_ref()],   
    bump,  
    )]  
    pub mint: Account<'info, Mint>,  
    #[account(  
        init_if_needed,  
        payer = payer,  
        associated_token::mint = mint,  
        associated_token::authority = payer,  
    )]  
    pub token_account: Account<'info, TokenAccount>,  
    pub associated_token_program: Program<'info, AssociatedToken>,  
    pub rent: Sysvar<'info, Rent>,  
    pub system_program: Program<'info, System>,  
    pub token_program: Program<'info, Token>,  
    pub metadata_program: Program<'info, Metadata>,  
    #[account(  
        mut,  
        seeds = [  
            b"metadata".as_ref(),  
            metadata_program.key().as_ref(),  
            mint.key().as_ref(),  
            b"edition".as_ref(),  
        ],  
        bump,  
        seeds::program = metadata_program.key()  
    )]  
    /// CHECK:  
    pub master_edition_account: UncheckedAccount<'info>,  
    #[account(  
        mut,  
        seeds = [  
            b"metadata".as_ref(),  
            metadata_program.key().as_ref(),  
            mint.key().as_ref(),  
        ],  
        bump,  
        seeds::program = metadata_program.key()  
    )]  
    /// CHECK:  
    pub nft_metadata: UncheckedAccount<'info>,  
    /// CHECK:  
    pub collection: UncheckedAccount<'info>,  
}  

我们将在终端中执行Build并获得“Build Succesful”的结果。

现在我们使用Deploy部署我们的程序,并在完成此过程后。

我们现在准备测试我们的程序。

Solana大家也可以使用这个工具, https://www.ggg.dog

标签:program,account,Solana,pub,2024,NFT,accounts,authority,mint
From: https://blog.csdn.net/2402_87505616/article/details/142616191

相关文章

  • 【2024计算机毕业设计】基于jsp+mysql的JSP酒店预定管理系统
    运行环境:最好是javajdk1.8,我在这个平台上运行的。其他版本理论上也可以。IDE环境:Eclipse,Myeclipse,IDEA或者SpringToolSuite都可以,如果编译器的版本太低,需要升级下编译器,不要弄太低的版本tomcat服务器环境:Tomcat7.x,8.x,9.x版本均可操作系统环境:WindowsXP/7......
  • 【2024计算机毕业设计】基于JSP酒店预定管理系统
    运行环境:最好是javajdk1.8,我在这个平台上运行的。其他版本理论上也可以。IDE环境:Eclipse,Myeclipse,IDEA或者SpringToolSuite都可以,如果编译器的版本太低,需要升级下编译器,不要弄太低的版本tomcat服务器环境:Tomcat7.x,8.x,9.x版本均可操作系统环境:WindowsXP/7......
  • 【2024-09-28】连岳摘抄
    23:59吾日三省吾身:为人谋而不忠乎?与朋友交而不信乎?传不习乎?                                              ——《论语》人生,并不是追求天天做选择,事事做选择。选择伴随着混......
  • 迅雷加速器兑换口令2024最新免费会员
    对于热爱游戏的玩家来说,网络延迟和卡顿无疑是游戏体验的致命伤。迅雷加速器以其专业和高效的服务,为全球游戏爱好者提供了解决方案。现在,新用户有机会免费领取迅雷加速器的VIP会员,享受长达7+30天的加速服务。以下是详细的领取攻略:领取7天免费会员领取7天免费会员的步骤非常简......
  • 【2024-09-27】时常陪伴
    20:00我们要用自己的腿去走路,我们要用自己的双手去劳动,我们要说出自己的思想。                                              ——拉尔夫沃尔多·爱默生现在没能陪何太上下......
  • 2024全网最为详细的红帽系列(1)【RHCE-LJS之Linux高端骚操作实战篇】保姆级别实战教程
    欢迎各位彦祖与热巴畅游本人专栏与博客你的三连是我最大的动力以下图片仅代表专栏特色 专栏跑道一 ➡️ MYSQLREDISAdvanceoperation专栏跑道二➡️ 24NetworkSecurity-LJS ​​ ​专栏跑道三➡️HCIP;H3C-SE;CCIP——LJS[华为、华三、思科高级网络]​......
  • 2024-2025-1 20241328《计算机基础与程序设计》第壹周学习总结
    2024-2025-120241328《计算机基础与程序设计》第壹周学习总结作业信息计算机基础与程序设计2024-2025-1-计算机基础与程序设计作业要求2024-2025-1计算机基础与程序设计第一周作业作业目标1、参考教程安装Linux系统;2、快速浏览一遍教材计算机科学概论(第七版),课本......
  • 2024-2025-1 20241310 《计算机基础与程序设计》第一周学习总结
    2024-2025-120241300《计算机基础与程序设计》第一周学习总结作业信息这个作业属于哪个课程2024-2025-1-计算机基础与程序设计这个作业要求在哪里2024-2025-1计算机基础与程序设计第一周作业这个作业的目标1.基于VirtualBox虚拟机安装Ubuntu图文教程安装Linux系......
  • R机械设计V4.2(2024.09.28)
    下载:https://pan.baidu.com/s/1Dphz0m8BQWcg-T-AaeoaYA提取码:0520R机械设计V4.2(2024.09.28)更新:1、新增齿轮计算模块2、新增同步带计算模块3、新增耗气量计算模块4、全新自定义模块,(可导入旧版本数据)5、更新螺钉数据6、修正“一般设计资料-过程”速比参数  ......
  • 2024.9.28 bisect 模块
    bisect模块是Python标准库中的一个模块,主要用于维护已排序的列表。它提供了一些函数,帮助你在一个有序序列中查找元素的插入位置,以便保持序列的有序性。以下是bisect模块的一些常用功能:常用函数bisect.bisect_left(a,x,lo=0,hi=len(a)):返回元素x应该插入到列表a......