Solana 开发笔记

admin 2025年3月6日15:08:15评论18 views字数 18944阅读63分8秒阅读模式

学习完以太坊基础知识和比特币白皮书后接着来学习 Solana,官方文档 https://solana.com/zh/docs?locale=docs

关于 Solana 采用的加密算法,在 Solana 中,采用了了 Ed25519 curve 非对称加密算法,用于生成数字签名和验证数字签名。与对称加密算法不同,非对称加密使用一对密钥:pubkey公钥和secretkey私钥,如果使用公钥加密,则只有对应的私钥能够进行解密;如果使用私钥加密,则可以使用对应的公钥验证签名,即判断该签名是否由私钥的持有者发起。

Solana 使用 Rust 语言进行开发,如果您之前没接触过 Rust 建议花点时间看看相关资料,以下是我整理的一些 Rust 教程:

  • • https://cheats.rs/
  • • https://course.rs/first-try/editor.html
  • • https://cirno.me/note/597.html

Introduction

Solana 的高性能网络是它的一个亮点。为了实现高吞吐量,Solana 采用了许多独特的技术,其中最为关键的是它的时间戳技术——Proof of History(PoH)。PoH 是 Solana 的一个创新,它通过将时间序列作为区块链的一部分,允许节点在没有全网同步的情况下验证交易的顺序,从而大大提升了系统的处理效率,Solana 的区块链与其他区块链不同,它采用了"单链架构",这一点有别于以太坊和比特币的多链架构。Solana 的设计允许它在每个区块内处理大量的交易,不需要进行链间的通信或者合并,这使得 Solana 可以达到每秒数万笔交易的吞吐量,Solana 还支持智能合约和去中心化应用(DApps),但与以太坊相比,Solana 的智能合约运行更加高效且低成本。Solana 的智能合约使用 Rust 或者 C 语言编写,而不是以太坊上的 Solidity 语言,这给开发者带来了一些新的挑战,但也提供了更大的灵活性和更强的性能。

Solana 白皮书:https://solana.com/solana-whitepaper.pdf

关于 Solana 的网络

Solana的网络环境分成开发网、测试网、主网三类,开发网为Solana节点开发使用,更新频繁,测试网主要 给到DApp开发者使用,相对稳定

  • • DevNet: https://api.devnet.solana.com
  • • TestNet: https://api.testnet.solana.com
  • • MainNet: https://api.mainnet-beta.solana.com

Develop Environment

首先安装 Rust 环境

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y

接着下载 Solana 的 CLI

sh -c "$(curl -sSfL https://release.anza.xyz/stable/install)"

如果下载完成,会提示让你配置环境变量,简单配置下就好了

❯ sh -c "$(curl -sSfL https://release.anza.xyz/stable/install)"downloading stable installer  ✨ stable commit 035c4eb initializedAdding export PATH="/Users/icecliffs/.local/share/solana/install/active_release/bin:$PATH" to /Users/icecliffs/.profileAdding export PATH="/Users/icecliffs/.local/share/solana/install/active_release/bin:$PATH" to /Users/icecliffs/.zprofileAdding export PATH="/Users/icecliffs/.local/share/solana/install/active_release/bin:$PATH" to /Users/icecliffs/.bash_profileClose and reopen your terminal to apply the PATH changes or run the following in your existing shell:export PATH="/Users/icecliffs/.local/share/solana/install/active_release/bin:$PATH"

接着输入 solana --version 查看版本

这里列举几条常用的命令

  • • 配置网络
solana config set --url https://api.mainnet-beta.solana.com # 主网# 或者solana config set --url mainnet-betasolana config set --url devnetsolana config set --url localhostsolana config set --url testnet
  • • 创建钱包
❯ solana-keygen new --outfile ~/icecliffs_test.json # 保管好Generating a new keypairFor added security, enter a BIP39 passphraseNOTE! This passphrase improves security of the recovery seed phrase NOT thekeypair file itself, which is stored as insecure plain textBIP39 Passphrase (empty for none): Wrote new keypair to /Users/icecliffs/icecliffs_test.json==============================================================================pubkey: XXXXXXXXXxcrXXXXXXXXXXXXXXXXdMNUxM==============================================================================Save this seed phrase and your BIP39 passphrase to recover your new keypair:sun isolate mystery flock yellow stool entry ability since topple wealth input==============================================================================

这里创建好的助记词对应 BIP39 里

https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki

  • • 查看账户信息,第一次创建可以到这里领水 https://faucet.solana.com/
❯ solana-keygen pubkey # 看看你的DxBvK8JrWVn26TWpmfUTr1Xdzyz9U64VP7UPFw8ALp6e

这里连接 Github 可以领到很多的 Solana

Solana 开发笔记
image-20250211164309534

如果是真金白银就好了()

Solana 开发笔记
image-20250211164332301

也可以通过下面这条命令来获取

solana airdrop
  • • 转账
solana transfer <destination-public-key> <amount> --from ~/my-wallet.json
  • • 创建代币账户(创建一个新的代币账户(用于接收和持有 SPL Token))
solana spl-token create-account <mint-address>
  • • 获取区块链的最新区块信息
solana block
  • • 部署智能合约
solana program deploy <path-to-your-program>
  • • 更新 Solana
solana update

需求一:假设我们现在有一个需求,拥有两个钱包,想要转来转去,可以用下面这条命令

❯ solana config set --keypair ~/icecliffs_a.jsonConfig File: /Users/icecliffs/.config/solana/cli/config.ymlRPC URL: https://api.devnet.solana.com WebSocket URL: wss://api.devnet.solana.com/ (computed)Keypair Path: /Users/icecliffs/icecliffs_a.json Commitment: confirmed ❯ solana address3sM7RwuEjzxcrCDTKfVoFiuKq8S84nXS5XpZiadMNUxM❯ solana config set --keypair ~/icecliffs_b.jsonConfig File: /Users/icecliffs/.config/solana/cli/config.ymlRPC URL: https://api.devnet.solana.com WebSocket URL: wss://api.devnet.solana.com/ (computed)Keypair Path: /Users/icecliffs/icecliffs_b.json Commitment: confirmed ❯ solana addressDxBvK8JrWVn26TWpmfUTr1Xdzyz9U64VP7UPFw8ALp6e

需求二:B 钱包给 A 钱包打币

❯ solana addressDxBvK8JrWVn26TWpmfUTr1Xdzyz9U64VP7UPFw8ALp6e❯ solana balance10 SOL❯ solana transfer 3sM7RwuEjzxcrCDTKfVoFiuKq8S84nXS5XpZiadMNUxM 5 --from icecliffs_b.jsonError: The recipient address (3sM7RwuEjzxcrCDTKfVoFiuKq8S84nXS5XpZiadMNUxM) is not funded. Add `--allow-unfunded-recipient` to complete the transfer ❯ solana transfer 3sM7RwuEjzxcrCDTKfVoFiuKq8S84nXS5XpZiadMNUxM 5 --from icecliffs_b.json --allow-unfunded-recipientSignature: 4ks6o14TXUVkGpHY1P7NvPudeCiCAXrWg3mJS8JXVrseCm7pCnsWSnKAKNfqmSq8nZKJUJAQ1nY4kNZUbue4ASpd❯ solana config set --keypair ~/icecliffs_a.jsonConfig File: /Users/icecliffs/.config/solana/cli/config.ymlRPC URL: https://api.devnet.solana.com WebSocket URL: wss://api.devnet.solana.com/ (computed)Keypair Path: /Users/icecliffs/icecliffs_a.json Commitment: confirmed ❯ solana balance5 SOL

这里交易信息可以在开发网上看到

https://explorer.solana.com/tx/4ks6o14TXUVkGpHY1P7NvPudeCiCAXrWg3mJS8JXVrseCm7pCnsWSnKAKNfqmSq8nZKJUJAQ1nY4kNZUbue4ASpd?cluster=devnet

Solana 开发笔记
image-20250211165354086

Solana Concept

账户模型

结构体

pubstructAccount {/// lamports in the accountpub lamports: u64,/// data held in this account#[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]pub data: Vec<u8>,/// the program that owns this account. If executable, the program that loads this account.pub owner: Pubkey,/// this account's data contains a loaded program (and is now read-only)pub executable: bool,/// the epoch at which this account will next owe rentpub rent_epoch: Epoch,}

这里有好多好多东西,自己看官网吧

https://solana.com/zh/docs/core/accounts

Solana RPC

这里关于 RPC 的详细介绍就不多说了,长话短说,在 Solana 中,RPC(Remote Procedure Call) 是与 Solana 区块链进行交互的核心方式,RPC 接口允许开发者通过调用预定义的 API 来获取区块链上的数据、提交交易或执行其他操作,Solana 的 RPC API 提供了高效的接口,能够支持快速读取链上数据及执行交易

这一部分在最开始的介绍有些到这里给出几条通过 CURL 调用的命令

  • • getAccountInfo,查询账户信息,包括账户余额、持有的代币等
❯ curl -X POST https://api.devnet.solana.com     -H "Content-Type: application/json"    -d '{        "jsonrpc": "2.0",        "id": 1,        "method": "getAccountInfo",        "params": ["DxBvK8JrWVn26TWpmfUTr1Xdzyz9U64VP7UPFw8ALp6e"]    }'{"jsonrpc":"2.0","result":{"context":{"apiVersion":"2.1.11","slot":360308523},"value":{"data":"","executable":false,"lamports":4999995000,"owner":"11111111111111111111111111111111","rentEpoch":18446744073709551615,"space":0}},"id":1}
  • • getBalance,获取指定账户的 SOL 余额
curl -X POST https://api.devnet.solana.com     -H "Content-Type: application/json"    -d '{        "jsonrpc": "2.0",        "id": 1,        "method": "getBalance",        "params": ["DxBvK8JrWVn26TWpmfUTr1Xdzyz9U64VP7UPFw8ALp6e"]    }'{"jsonrpc":"2.0","result":{"context":{"apiVersion":"2.1.11","slot":360308809},"value":4999995000},"id":1}

详见

https://solana.com/zh/docs/rpc

Solana Hello World

这里直接以官方 Hello World 项目为例子

git clone https://github.com/solana-labs/example-helloworld

比较基础的环境搭建这里就不说明了,把 URL 先设置成 localhost 集群,然后启动一下

solana config set --url localhostsolana-test-validator # 启动本地集群
Solana 开发笔记
image-20250213185235030

这里可以发包测试一下

curl -X POST http://127.0.0.1:8899         -H "Content-Type: application/json"    -d '{        "jsonrpc": "2.0",        "id": 1,        "method": "getAccountInfo",        "params": ["DxBvK8JrWVn26TWpmfUTr1Xdzyz9U64VP7UPFw8ALp6e"]    }'

这里引入 Web3.js 这个是啥具体就不多说了 npm install --save @solana/web3.js@1

https://github.com/solana-labs/solana-web3.js

https://solana-labs.github.io/solana-web3.js/

构建项目

npm run build:program-rust

启动客户端

npm run startsolana program deploy dist/program/helloworld.so # 不熟链上程序

代码解析,lib.rs 用于验证账户被调用的次数

// lib.rs// 引入本地作用域,用于序列化和反序列化use borsh::{BorshDeserialize, BorshSerialize};// 引入 Solanause solana_program::{    account_info::{next_account_info, AccountInfo},    entrypoint,    entrypoint::ProgramResult,    msg,    program_error::ProgramError,    pubkey::Pubkey,};/// Define the type of state stored in accounts#[derive(BorshSerialize, BorshDeserialize, Debug)]pubstructGreetingAccount {/// number of greetingspub counter: u32,}// 程序入口点entrypoint!(process_instruction);// Program entrypoint's implementationpubfnprocess_instruction(// 交互地址    program_id: &Pubkey, // Public key of the account the hello world program was loaded into//程序交互的账户列表      accounts: &[AccountInfo], // The account to say hello to    _instruction_data: &[u8], // Ignored, all helloworld instructions are hellos-> ProgramResult {    msg!("Hello World Rust program entrypoint");// Iterating accounts is safer than indexingletaccounts_iter = &mut accounts.iter();// Get the account to say hello toletaccount = next_account_info(accounts_iter)?;// The account must be owned by the program in order to modify its dataif account.owner != program_id {        msg!("Greeted account does not have the correct program id");returnErr(ProgramError::IncorrectProgramId);    }// Increment and store the number of times the account has been greetedletmut greeting_account = GreetingAccount::try_from_slice(&account.data.borrow())?;    greeting_account.counter += 1;    greeting_account.serialize(&mut &mut account.data.borrow_mut()[..])?;    msg!("Greeted {} time(s)!", greeting_account.counter);// 成功则返回Ok(())}// Sanity tests#[cfg(test)]mod test {use super::*;use solana_program::clock::Epoch;use std::mem;#[test]fntest_sanity() {letprogram_id = Pubkey::default();letkey = Pubkey::default();letmut lamports = 0;letmut data = vec![0; mem::size_of::<u32>()];letowner = Pubkey::default();letaccount = AccountInfo::new(            &key,false,true,            &mut lamports,            &mut data,            &owner,false,            Epoch::default(),        );letinstruction_dataVec<u8> = Vec::new();letaccounts = vec![account];// 搞了几个断言,可以自己跑起来理解一下assert_eq!(            GreetingAccount::try_from_slice(&accounts[0].data.borrow())                .unwrap()                .counter,0        );process_instruction(&program_id, &accounts, &instruction_data).unwrap();assert_eq!(            GreetingAccount::try_from_slice(&accounts[0].data.borrow())                .unwrap()                .counter,1        );process_instruction(&program_id, &accounts, &instruction_data).unwrap();assert_eq!(            GreetingAccount::try_from_slice(&accounts[0].data.borrow())                .unwrap()                .counter,2        );    }}

如您对上述步骤感到繁琐,可以到 Solana 的 Playground 进行线上编译 https://beta.solpg.io/,从上面程序来看,可以总结一下 Solana 程序的一个大致逻辑,我们首先用到了 solana_program 这个标准库,并且将下面这传代码纳入了作用域

use solana_program::{    account_info::{next_account_info, AccountInfo},    entrypoint,    entrypoint::ProgramResult,    msg,    program_error::ProgramError,    pubkey::Pubkey,};
  • • AccountInfo:account_info 模块中的一个结构体,允许我们访问帐户信息。
  • • entrypoint:声明程序入口点的宏,类似于 Rust 中的 main 函数。
  • • ProgramResult:entrypoint 模块中的返回值类型。
  • • Pubkey:pubkey 模块中的一个结构体,允许我们将地址作为公钥访问。
  • • msg:一个允许我们将消息打印到程序日志的宏,类似于 Rust 中的 println宏。

Solana 使用的入口点为 entrypoint! 声明的宏,相对应的我们可以这样声明一个入口点

entrypoint!(process_instruction_one);fnprocess_instruction_one(    program_id: &Pubkey,    accounts: &[AccountInfo],    instruction_data: &[u8],-> ProgramResult {// do somethingOk(())}

正常来说我们写一个累加程序都需要定义一个变量用于存储我们的 cnt,Solana 定义了一个 Account 数据账户来存储数据,这一点可以看看开发文档里的(链接在上面)

/// 定义数据账户的结构#[derive(BorshSerialize, BorshDeserialize, Debug)]pubstructCounterAccount {pub count: u32,}

之后通过 #[derive(BorshSerialize, BorshDeserialize, Debug)] 这两个派生宏实现

Solana 开发笔记
image-20250213205842013

这里 CounterAccount 结构体对应的为数据账户,只能由 Owner 所有者更改,总的来说

先创建一个 AccountInfo 迭代器,用于遍历 accounts 数组。

letaccounts_iter = &mut accounts.iter();

然后从迭代器中获取下一个账户信息,即存储计数器的数据账户

letaccount = next_account_info(accounts_iter)?;

接着加载数据账户数据

letmut counter_account = CounterAccount::try_from_slice(&account.data.borrow())?;

从数据账户的 data 字段中反序列化出 CounterAccount 结构体。try_from_slice 方法会将字节数组转换为 CounterAccount 结构体,然后 +=1 接着写会账户

counter_account.serialize(&mut &mut account.data.borrow_mut()[..])?;Ok(()) /// 成功

剩下的例子可以看官方文档,我就不重复了

Anchor

Anchor 是 Solana Sealevel 运行时的一个框架,为编写智能合约提供了几个方便的开发工具。

  • • Rust eDSL 用于编写 Solana 程序
  • • IDL 规范
  • • 用于从 IDL 生成客户端的 TypeScript 包
  • • 用于开发完整应用程序的 CLI 和工作区管理

机翻自 Github https://github.com/coral-xyz/anchor,官方文档:https://www.anchor-lang.com/docs/installation

安装

cargo install --git https://github.com/coral-xyz/anchor avm --forceavm --versionavm install latest

创建项目 https://www.anchor-lang.com/docs/references/cli#init

anchor init anchor_solana_test# 创建程序anchor new xxx# 验证链上部署的程序是否与本地匹配anchor verify

构建智能合约,会生成一个二进制文件在 target/deploy

anchor build [anchor_solana_test]# 目录下就是anchor build

测试程序

anchor test

部署程序

anchor deploy --env devnet # 部署到测试网加个 --env 就行了

这里 Anchor 同样有一大堆宏,可以看看文档,补一个项目结构(基于 Playground)

Solana 开发笔记
image-20250213212621076

还得是 DevOps 方便啊

Solana NFT

  • • 有单独的数量,超过了就铸造不了了

使用 Anchor 脚手架进行配置

use anchor_spl::{    associated_token::AssociatedToken,    metadata::{/// 这两个函数用于创建 NFT 的元数据账户和主版本账户(Master Edition),这些是 Metaplex Token Metadata 程序中的核心功能。        create_master_edition_v3, create_metadata_accounts_v3, CreateMasterEditionV3,        CreateMetadataAccountsV3, Metadata    },    token::{mint_to, Mint, MintTo, Token, TokenAccount},};use mpl_token_metadata::types::DataV2;use mpl_token_metadata::accounts::{MasterEdition, Metadata as MetadataAccount};

铸造 mint_to(cpi_context, 1)?;,补一张图

Solana 开发笔记
image-20250214122107376

Release

切换网络

solana config set --url devnet

构建项目

anchor init icecliffs_nft

导入依赖

[package]name = "icecliffs_nft"version = "0.1.0"description = "Created with Anchor"edition = "2021"[lib]crate-type = ["cdylib""lib"]name = "icecliffs_nft"[features]default = []cpi = ["no-entrypoint"]no-entrypoint = []no-idl = []no-log-ix-name = []idl-build = ["anchor-lang/idl-build""anchor-spl/idl-build"][dependencies]anchor-lang = "0.30.1"anchor-spl = {version = "0.30.1",features = ["metadata"]}mpl-token-metadata = "5.1.0"

源代码

/// lib.rsuse anchor_lang::prelude::*;use anchor_spl::{    associated_token::AssociatedToken,    metadata::{        create_master_edition_v3, create_metadata_accounts_v3, CreateMasterEditionV3,        CreateMetadataAccountsV3, Metadata    },     token::{mint_to, Mint, MintTo, Token, TokenAccount},};use mpl_token_metadata::types::DataV2;use mpl_token_metadata::accounts::{ MasterEdition, Metadata as MetadataAccount};declare_id!("6kPhe2z5A5Qw17qniDTHXuQV1vD9DMWD6KSBCEBj88Gv");#[program]pubmod metaplex_nft {use super::*;pubfnmint_nft(        ctx: Context<MintNFT>,        name: String,          symbol: String        uri: String,    ) ->Result<()> {letcpi_program = ctx.accounts.token_program.to_account_info();letcpi_accounts = MintTo {            mint: ctx.accounts.mint.to_account_info(),            to: ctx.accounts.associated_token_account.to_account_info(),            authority: ctx.accounts.signer.to_account_info(),        };letcpi_context = CpiContext::new(            cpi_program,            cpi_accounts,        );mint_to(cpi_context, 1)?;letcpi_context = CpiContext::new(            ctx.accounts.token_metadata_program.to_account_info(),            CreateMetadataAccountsV3 {                metadata: ctx.accounts.metadata_account.to_account_info(),                mint: ctx.accounts.mint.to_account_info(),                mint_authority: ctx.accounts.signer.to_account_info(),                update_authority: ctx.accounts.signer.to_account_info(),                payer: ctx.accounts.signer.to_account_info(),                system_program: ctx.accounts.system_program.to_account_info(),                rent: ctx.accounts.rent.to_account_info(),            },        );letdata_v2 = DataV2 {            name: name,            symbol: symbol,            uri: uri,            seller_fee_basis_points: 0,            creators: None,            collection: None,            uses: None,        };create_metadata_accounts_v3(cpi_context, data_v2, falsetrueNone)?;letcpi_context = CpiContext::new(            ctx.accounts.token_metadata_program.to_account_info(),            CreateMasterEditionV3 {                edition: ctx.accounts.master_edition_account.to_account_info(),                mint: ctx.accounts.mint.to_account_info(),                update_authority: ctx.accounts.signer.to_account_info(),                mint_authority: ctx.accounts.signer.to_account_info(),                payer: ctx.accounts.signer.to_account_info(),                metadata: ctx.accounts.metadata_account.to_account_info(),                token_program: ctx.accounts.token_program.to_account_info(),                system_program: ctx.accounts.system_program.to_account_info(),                rent: ctx.accounts.rent.to_account_info(),            },        );create_master_edition_v3(cpi_context, None)?;Ok(())    }}#[derive(Accounts)]pubstructMintNFT<'info> {/// CHECK: The signer field is safe because it is verified elsewhere in the program.#[account(mut, signer)]pub signer: AccountInfo<'info>,#[account(        init,        payer = signer,        mint::decimals = 0,        mint::authority = signer.key(),        mint::freeze_authority = signer.key(),    )]pub mint: Account<'info, Mint>,#[account(        init_if_needed,        payer = signer,        associated_token::mint = mint,        associated_token::authority = signer    )]pub associated_token_account: Account<'info, TokenAccount>,/// CHECK - address#[account(        mut,        address = MetadataAccount::find_pda(&mint.key()).0,    )]pub metadata_account: AccountInfo<'info>, /// CHECK - address#[account(        mut,        address = MasterEdition::find_pda(&mint.key()).0,    )]pub master_edition_account: AccountInfo<'info>,pub token_program: Program<'info, Token>,pub associated_token_program: Program<'info, AssociatedToken>,pub token_metadata_program: Program<'info, Metadata>,pub system_program: Program<'info, System>,pub rent: Sysvar<'info, Rent>,}

部署程序

anchor build && anchor deploy

交互环节

import * as anchor from "@coral-xyz/anchor";import { Program } from "@coral-xyz/anchor";import { MetaplexNft } from ".https://bfs.iloli.moe/2025/02/14/target/types/metaplex_nft";import { walletAdapterIdentity } from "@metaplex-foundation/umi-signer-wallet-adapters";import { getAssociatedTokenAddress } from "@solana/spl-token";import {  findMasterEditionPda,  findMetadataPda,  mplTokenMetadata,  MPL_TOKEN_METADATA_PROGRAM_ID,} from "@metaplex-foundation/mpl-token-metadata";import { createUmi } from "@metaplex-foundation/umi-bundle-defaults";import { publicKey } from "@metaplex-foundation/umi";import {  TOKEN_PROGRAM_ID,  ASSOCIATED_TOKEN_PROGRAM_ID,} from "@solana/spl-token";describe("solana-nft-anchor"async () => {// Configured the client to use the devnet cluster.const provider = anchor.AnchorProvider.env();  anchor.setProvider(provider);const program = anchor.workspace    .MetaplexNft as Program<MetaplexNft>;const signer = provider.wallet;const umi = createUmi("https://api.devnet.solana.com")    .use(walletAdapterIdentity(signer))    .use(mplTokenMetadata());const mint = anchor.web3.Keypair.generate();// Derive the associated token address account for the mintconst associatedTokenAccount = awaitgetAssociatedTokenAddress(    mint.publicKey,    signer.publicKey  );// derive the metadata accountletmetadataAccount = findMetadataPda(umi, {    mint: publicKey(mint.publicKey),  })[0];//derive the master edition pdaletmasterEditionAccount = findMasterEditionPda(umi, {    mint: publicKey(mint.publicKey),  })[0];const metadata = {    name: "HackQuest",    symbol: "HQ",    uri: "https://raw.githubusercontent.com/Louis-XWB/metaplex_nft/chore_branch/meta.json",  };it("mints nft!"async () => {const tx = await program.methods      .mintNft(metadata.name, metadata.symbol, metadata.uri)      .accounts({        signer: provider.publicKey,        mint: mint.publicKey,        associatedTokenAccount,        metadataAccount,        masterEditionAccount,        tokenProgram: TOKEN_PROGRAM_ID,        associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,        tokenMetadataProgram: MPL_TOKEN_METADATA_PROGRAM_ID,        systemProgram: anchor.web3.SystemProgram.programId,        rent: anchor.web3.SYSVAR_RENT_PUBKEY,      })      .signers([mint])      .rpc();    console.log(      `mint nft tx: https://explorer.solana.com/tx/${tx}?cluster=devnet`    );    console.log(      `minted nft: https://explorer.solana.com/address/${mint.publicKey}?cluster=devnet`    );  });});

铸造 NFT

anchor test

查看 NFT

minted nft

差不多就这样吧,未完待续。

Reference

  • • https://solana.com/zh
  • • https://solana.com/zh/docs?locale=docs
  • • https://www.ironforge.cloud/,DevOps
  • • https://discord.com/invite/qcMZEgydXP,Solana
  • • https://rustmagazine.github.io/rust_magazine_2021/chapter_10/solana-learn-part1.html

原文始发于微信公众号(Gh0xE9):Solana 开发笔记

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年3月6日15:08:15
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Solana 开发笔记https://cn-sec.com/archives/3804765.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息