如何使用@solana/web3调用Solana智能合约的指定端点
Solana多指令智能合约调用实现方案
核心原理
Solana智能合约没有传统后端的"接口端点"概念,你提到的不同调用入口对应合约里的MetadataInstruction枚举变体。合约会将你传入交易的data字段反序列化为枚举实例,再匹配到对应的处理逻辑。
枚举序列化后的二进制结构固定为:【1字节指令标识】+【该指令对应参数的序列化数据】,其中指令标识和枚举定义顺序一一对应,比如你贴出的代码里第一个变体CreateMetadataAccount标识为0,第二个UpdateMetadataAccount标识为1,以此类推,合约就是靠这个标识判断你要调用的逻辑。
方案1:使用官方封装SDK(推荐,无需手动处理序列化)
Metaplex官方已经封装了所有Token Metadata合约的指令构造逻辑,直接调用即可,无需手动处理序列化和账户顺序问题。
首先安装依赖:npm install @solana/web3.js @metaplex-foundation/mpl-token-metadata
调用示例(以创建元数据账户为例):
import { Connection, Keypair, Transaction, sendAndConfirmTransaction } from "@solana/web3.js"; import { createCreateMetadataAccountV3Instruction } from "@metaplex-foundation/mpl-token-metadata"; // 初始化链上连接和签名钱包 const connection = new Connection("你的Solana RPC节点地址"); const payer = Keypair.fromSecretKey(Uint8Array.from(你的钱包私钥数组)); // 提前计算元数据账户PDA地址、准备mint相关地址 const metadataPda = // 用findProgramAddressSync计算得到的元数据账户地址 const mintAddress = // 对应NFT的mint公钥 const mintAuthority = payer.publicKey; const updateAuthority = payer.publicKey; // 直接调用封装好的指令构造方法 const instruction = createCreateMetadataAccountV3Instruction( { metadata: metadataPda, mint: mintAddress, mintAuthority: mintAuthority, payer: payer.publicKey, updateAuthority: updateAuthority, }, { createMetadataAccountArgsV3: { data: { name: "NFT显示名称", symbol: "NFT符号", uri: "链下元数据存储地址", sellerFeeBasisPoints: 500, // 版税比例,单位0.01%,500对应5% creators: null, collection: null, uses: null, }, isMutable: true, collectionDetails: null, } } ); // 发送交易上链确认 const txHash = await sendAndConfirmTransaction( connection, new Transaction().add(instruction), [payer] );
其他指令比如更新元数据、创建主版本等,直接找SDK中对应的createXXXInstruction前缀方法,按照参数要求填充即可。
方案2:手动构造指令(无额外SDK依赖)
如果不想引入mpl专属SDK,也可以手动按合约要求构造指令:
- 确认要调用的指令对应的标识序号,和合约
MetadataInstruction枚举的定义顺序完全对应 - 用Borsh序列化工具,按合约定义的参数结构序列化参数,拼在指令标识字节的后面作为交易的data字段
- 按合约要求的顺序填充
keys账户数组,每个账户的isSigner、isWritable属性必须和合约要求完全一致
首先安装基础依赖:npm install @solana/web3.js borsh buffer
手动构造示例:
import * as borsh from "borsh"; import { Connection, Keypair, PublicKey, Transaction, TransactionInstruction, sendAndConfirmTransaction } from "@solana/web3.js"; import { Buffer } from "buffer"; // 1. 定义和合约完全匹配的参数结构体 class CreateMetadataAccountArgs { constructor(args) { this.data = args.data; this.is_mutable = args.is_mutable; } } // 2. 定义Borsh序列化规则,字段顺序、类型必须和Rust合约完全一致 const CreateMetadataAccountArgsSchema = new Map([ [CreateMetadataAccountArgs, { kind: "struct", fields: [ ["data", [ "struct", [ ["name", "string"], ["symbol", "string"], ["uri", "string"], ["seller_fee_basis_points", "u16"], ["creators", { kind: "option", type: [ "struct", [ ["address", "pubkey"], ["verified", "u8"], ["share", "u8"] ] ]}], ["collection", { kind: "option", type: "pubkey" }], ["uses", { kind: "option", type: "u64" }], ] ]], ["is_mutable", "u8"], ] }] ]); // 3. 填充参数并序列化 const args = new CreateMetadataAccountArgs({ data: { name: "测试NFT", symbol: "TEST", uri: "https://你的元数据地址.json", seller_fee_basis_points: 500, creators: null, collection: null, uses: null }, is_mutable: 1 }); const serializedArgs = borsh.serialize(CreateMetadataAccountArgsSchema, args); // 4. 拼接指令标识:CreateMetadataAccount是第一个枚举变体,标识为0(1字节) const instructionData = Buffer.concat([Buffer.from([0]), Buffer.from(serializedArgs)]); // 5. 构造交易指令,账户顺序必须和合约要求完全一致 const instruction = new TransactionInstruction({ keys: [ { pubkey: metadataPda, isSigner: false, isWritable: true }, { pubkey: mintAddress, isSigner: false, isWritable: false }, { pubkey: mintAuthority, isSigner: true, isWritable: false }, { pubkey: payer.publicKey, isSigner: true, isWritable: true }, { pubkey: updateAuthority, isSigner: false, isWritable: false }, { pubkey: new PublicKey("SysvarRent111111111111111111111111111111111"), isSigner: false, isWritable: false }, ], programId: new PublicKey("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"), // Token Metadata固定程序ID data: instructionData }); // 6. 发送交易 const txHash = await sendAndConfirmTransaction( connection, new Transaction().add(instruction), [payer] );
注意事项
keys数组的账户顺序、是否签名、是否可写三个属性必须和合约要求完全匹配,否则会执行失败- 参数序列化的结构、字段顺序、类型必须和Rust合约的定义完全一致,否则会触发反序列化错误
- 指令标识必须和枚举定义的顺序对应,填错会匹配到错误的处理逻辑
内容的提问来源于stack exchange,提问作者Max Campbell
相关产品推荐
相关产品推荐

