如何在Solana上销毁SPL代币

  • QuickNode
  • 发布于 2025-01-12 11:56
  • 阅读 36

本文详细介绍了如何使用TypeScript和Solana SPL Token Program在Solana网络上销毁SPL代币。文章提供了从项目设置到执行销毁操作的完整代码示例和步骤。

概述

🔥🔥🔥

构建一个通缩代币协议?想要销毁一个已被盗版的 NFT?只是想和你的社区一起玩得开心?Solana SPL Token Program 的销毁功能是你提升 Solana 社区的绝佳选择。

在本指南中,你将使用 TypeScript 和 Solana SPL Token Program 来销毁你钱包中的 SPL 代币。

你需要准备的

设置你的项目

在终端中创建一个新项目目录,输入以下命令:

mkdir burn-spl
cd burn-spl

为你的应用创建一个文件,命名为 app.ts

echo > app.ts

初始化你的项目,使用 “yes” 标志来使用默认值创建新包:

yarn init --yes
#or
npm init --yes

创建一个启用 .json 导入的 tsconfig.json

tsc -init --resolveJsonModule true

将你的纸钱包保存为 guideSecret.json,其中包含 devnet SOL 和一个或多个 SPL 代币(格式应该是 8 位整数的数组,例如 [27,218,103, ...])。如果你的钱包中还没有 devnet SOL,你可以通过以下表单请求:

🪂请求 Devnet SOL

空投 1 SOL(Devnet)

安装 Solana Web3 依赖

我们需要添加 Solana Web3 和 SPL Token 库。在终端中输入:

yarn add @solana/web3.js@1 @solana/spl-token
#or
npm install @solana/web3.js@1 @solana/spl-token

我们需要从这些库和我们的密钥获取一些组件。在 app.ts 的第 1 行,添加以下导入:

import { Connection, PublicKey, Keypair, TransactionMessage, VersionedTransaction, SignatureStatus, TransactionConfirmationStatus, TransactionSignature } from "@solana/web3.js";
import { createBurnCheckedInstruction, TOKEN_PROGRAM_ID, getAssociatedTokenAddress } from "@solana/spl-token";
import secret from './guideSecret.json';

const WALLET = Keypair.fromSecretKey(new Uint8Array(secret));

设置你的 QuickNode 端点

要在 Solana 上构建,你需要一个 API 端点来连接到网络。你可以使用公共节点或部署和管理自己的基础设施;但是,如果你希望响应时间快 8 倍,可以将繁重的工作交给我们。

New Node

app.ts 的导入语句下,声明你的 RPC 并建立与 Solana 的 Connection

const QUICKNODE_RPC = 'https://example.solana-devnet.quiknode.pro/0123456/';
const SOLANA_CONNECTION = new Connection(QUICKNODE_RPC);

你的环境应该如下所示。

Ready to Build

准备好了吗?让我们开始构建吧!

设置一个销毁应用

声明铸币地址

要销毁一个 SPL 代币,你必须在正确的集群(在本例中为 Devnet)中拥有一些代币。我们需要获取代币的铸造地址。要找到这一点,如果你还没有,请访问 Solana Explorer。你应该能够看到你所有代币持有的列表。

User Token List

复制你希望使用的代币的铸币地址。点击铸币地址,并记下小数位数。这对于进行任何与 SPL Token 程序的交互非常重要。如果你的 devnet 钱包中没有任何 SPL 代币(如果你已经有一个,可以跳过此步骤),你可以在 SPL Token Faucet 为这个演示铸造一个。你需要连接你的钱包,所以你可能需要将纸钱包导入 Phantom 来实现这一点:

  • 在 Phantom 中,点击左上角的圆圈以进入“设置”
  • 点击你的钱包以查看所有钱包的列表,然后点击“添加/连接钱包” Add Wallet
  • 为你的新钱包命名并粘贴密钥(guideSecret.json 的内容) New Wallet

按照站点说明铸造 1,000 USDC-DEV。该代币的铸币地址是 Gh9ZwEmdLJ8DscKNTkTqPbNwLNNBjuSzaG9Vp2KGtKJr(它有 6 位小数)。

一旦你拥有了计划要销毁的代币及其铸币地址,打开 app.ts 并添加以下行:

const MINT_ADDRESS = 'Gh9ZwEmdLJ8DscKNTkTqPbNwLNNBjuSzaG9Vp2KGtKJr'; // 从 spl-token-faucet.com 获取的 USDC-Dev | 替换为你想要销毁的铸币
const MINT_DECIMALS = 6; // 从 spl-token-faucet.com 获取的 USDC-Dev 的值 | 替换为你想要销毁的铸币的小数位数
const BURN_QUANTITY = 1; // 要销毁的代币数量(随意替换为任何数字 - 只需确保你有足够的代币)

定义助手函数

我们需要一个事务来确认该事务已经成功通过区块链处理。在,你的 Connection 定义下,添加以下代码:

async function confirmTransaction(
    connection: Connection,
    signature: TransactionSignature,
    desiredConfirmationStatus: TransactionConfirmationStatus = 'confirmed',
    timeout: number = 30000,
    pollInterval: number = 1000,
    searchTransactionHistory: boolean = false
): Promise<SignatureStatus> {
    const start = Date.now();

    while (Date.now() - start < timeout) {
        const { value: statuses } = await connection.getSignatureStatuses([signature], { searchTransactionHistory });

        if (!statuses || statuses.length === 0) {
            throw new Error('未能获取签名状态');
        }

        const status = statuses[0];

        if (status === null) {
            await new Promise(resolve => setTimeout(resolve, pollInterval));
            continue;
        }

        if (status.err) {
            throw new Error(`事务失败: ${JSON.stringify(status.err)}`);
        }

        if (status.confirmationStatus && status.confirmationStatus === desiredConfirmationStatus) {
            return status;
        }

        if (status.confirmationStatus === 'finalized') {
            return status;
        }

        await new Promise(resolve => setTimeout(resolve, pollInterval));
    }

    throw new Error(`事务确认超时,超出了 ${timeout}ms`);
}

此函数将查询 Solana 网络以获取事务状态,直到它被确认或超时。我们包含了一些超时和查询间隔的默认值,但你可以根据需要进行调整。

创建一个异步代码块

app.ts 中添加以下 async 代码块。这将构建我们的应用程序并创建一些步骤,我们将一起完成:

(async () => {
    console.log(`尝试从所有者钱包销毁 ${BURN_QUANTITY} [${MINT_ADDRESS}] 代币: ${WALLET.publicKey.toString()}`);
    // 第一步 - 获取关联代币账户地址

    // 第二步 - 创建销毁指令

    // 第三步 - 获取最新块哈希

    // 第四步 - 组装事务

    // 第五步 - 执行并确认事务

})()

第一步 - 获取关联代币地址

在代码块的第一步中,我们需要找到保存我们要销毁代币的账户地址。提醒一下,用户的钱包并不直接保留 SPL 代币。用户的钱包拥有一个单独的 SPL 代币账户以存储这些代币。我们必须找到该地址(即关联代币地址 ATA)。SPL 代币账户是根据用户钱包的公钥和代币铸造地址生成的程序派生地址。我们可以将这两者传入 getAssociatedTokenAddress 来找到我们的地址:

    // 第一步 - 获取关联代币账户地址
    console.log(`步骤 1 - 获取代币账户`);
    const account = await getAssociatedTokenAddress(new PublicKey(MINT_ADDRESS), WALLET.publicKey);
    console.log(`    ✅ - 关联代币账户地址: ${account.toString()}`);

注意:你也可以使用 Solana Explorer 找到账户的 ATA。如果你查看代币账户的详细信息,你将看到“铸币地址”和“账户地址”。账户地址是 ATA。你可以点击以获取更多信息,或者直接复制/粘贴到你的 account 变量中,而不是使用 getAssociatedTokenAddress。我们的代币地址是以 F9Se... 开头的: Explorer Token Account

你可以通过在终端中运行 ts-node app 来测试你的代码。你应该能看到有关你的 ATA 的日志。

第二步 - 创建销毁指令

Solana SPL 代币程序提供了一个便利的方法,createBurnCheckedInstruction,该方法会根据我们收集的输入生成 TransactionInstruction。在应用程序的第二步中,添加以下代码:

    // 第二步 - 创建销毁指令
    console.log(`步骤 2 - 创建销毁指令`);
    const burnIx = createBurnCheckedInstruction(
      account, // 公共密钥 - 所有者的关联代币账户
      new PublicKey(MINT_ADDRESS), // 代币铸造地址的公共密钥
      WALLET.publicKey, // 所有者钱包的公共密钥
      BURN_QUANTITY * (10**MINT_DECIMALS), // 要销毁的代币数量
      MINT_DECIMALS // 代币铸造的小数位数
    );
    console.log(`    ✅ - 销毁指令创建成功`);

这里最大的技巧是确保你的类型和参数顺序正确。在这个指令中的地址是 公共密钥,所以我们需要将我们的 铸币地址钱包 转换为公共密钥。你可以将鼠标悬停在 createBurnCheckedInstruction 上查看参数输入,或按 ctrl/cmd + 点击以获取关于此方法的更多信息。

TypeScript 应该会检查你是否输入了所有正确类型,但你需要确保你的参数按正确顺序输入。

重要提示,注意我们将 BURN_QUANTITY 乘以 10 的小数位数。这对于与 SPL 代币程序的交互是必要的,因为它们用于表征代币价值的精度级别。如果没有小数值,这些操作可能无法产生准确结果,并可能导致代币价值中的错误。

第三步 - 获取最新块哈希

为了确保你的交易有效传播到网络中,你需要最新的块哈希。使用 getLatestBlockhash 方法从集群获取最新的块哈希信息。这是确认事务成功(而不是超时)所必要的。在应用程序的第三步中,添加以下代码:

    // 第三步 - 获取块哈希
    console.log(`步骤 3 - 获取块哈希`);
    const { blockhash, lastValidBlockHeight } = await SOLANA_CONNECTION.getLatestBlockhash('finalized');
    console.log(`    ✅ - 最新块哈希: ${blockhash}`);

你可以运行你的代码以确保一切正常。在终端中输入 ts-node app。你应该会看到最新的块哈希!

第四步 - 组装事务

我们将使用版本化交易来销毁我们的代币(尽管遗留方法也同样有效)。如果版本化交易对你来说是新的,请查看我们的 如何使用版本化交易的指南。在 app.ts 的第四步中添加:

    // 第四步 - 组装事务
    console.log(`步骤 4 - 组装事务`);
    const messageV0 = new TransactionMessage({
      payerKey: WALLET.publicKey,
      recentBlockhash: blockhash,
      instructions: [burnIx]
    }).compileToV0Message();
    const transaction = new VersionedTransaction(messageV0);
    transaction.sign([WALLET]);
    console.log(`    ✅ - 事务创建并签名成功`);

使用我们的 burnIx 参数和 latestBlockhash,我们可以通过构造新的 Message 并执行 .compileToV0Message() 方法来创建一个新的 MessageV0。然后,我们将消息传递给 VersionedTransaction 的新实例。最后,我们通过将 WALLET 传递给 transaction.sign 进行签名,以形成一个 数组(这允许多个签名者在更复杂的事务中使用)。

如果你在代码中遇到任何错误,TypeScript 应该会提醒你,但如果你遇到问题,可以加入我们的 Discord,我们会很乐意提供帮助。

第五步 - 执行并确认交易

好的,一切都准备好了。我们只需将交易发送到集群并确认它已经成功添加到链上。我们可以使用 sendTransaction 方法和之前定义的 confirmTransaction 函数来实现。在 app.ts 的第五步,添加:

    // 第五步 - 执行与确认事务
    console.log(`步骤 5 - 执行与确认事务`);
    const txid = await SOLANA_CONNECTION.sendTransaction(transaction);
    console.log("    ✅ - 事务发送到网络");
    const confirmation = await confirmTransaction(SOLANA_CONNECTION, txid);
    if (confirmation.err) { throw new Error("    ❌ - 事务未确认.") }
    console.log('🔥 成功销毁!🔥', '\n', `https://explorer.solana.com/tx/${txid}?cluster=devnet`);

以下是发生了什么的简要总结:

  • sendTransaction 将我们的事务发送到网络,并将返回一个事务 ID 的 promise,txid
  • 我们将 txid 传递给 confirmTransaction 以检查我们的事务是否已被确认。
  • 如果我们的确认返回错误,则抛出一个错误。

运行你的代码

你应该准备得差不多了--让我们来点火吧!在终端中输入:

ts-node app.ts

Burn

你应该能在终端中看到事务进展:

Burn Confirmation

你应该能够打开你的 Solana Explorer 链接,并向下滚动到指令中以验证燃烧操作成功!

Burn Confirmation 2

干得不错!

总结

让它燃烧。你现在拥有在 Solana 上构建销毁工具的工具!这适用于任何 SPL 代币,甚至是 NFTs(NFT 的小数位数 = 0)。以下是一些扩展此示例的起始想法:

我们喜欢构建,想听听你的想法。请在 DiscordTwitter 上给我们留言,告诉我们你正在做什么!

  • 原文链接: quicknode.com/guides/sol...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
QuickNode
QuickNode
江湖只有他的大名,没有他的介绍。