gill:与 Solana 区块链交互的 JavaScript/TypeScript 客户端库
什么是 gill?
gill 是一个用于与 Solana 区块链交互的 JavaScript/TypeScript 客户端库。无论你是在 Node.js、Web 端、React Native 还是其他 JavaScript 环境中构建应用,gill 都能帮助你轻松接入 Solana 网络。
gill 基于 Anza 开发的现代 JavaScript 库 👉@solana/kit(之前称为 “web3.js v2”)构建,使用了相同的类型和函数,因此与 kit 完全兼容。
为什么选择 gill?
与直接使用 @solana/kit 相比,gill 提供了更简洁的 API 和更多实用功能,比如内置的事务构建器、调试模式支持,以及对常见操作(如创建代币、转账等)的高级抽象。这些功能可以显著减少样板代码,提升开发效率。
如何安装 gill?
你可以使用常用的包管理器来安装 gill:
npm install gill
pnpm add gill
yarn add gill
如果你之前使用的是 @solana/kit,只需将导入路径替换为 gill 即可,所有功能保持不变,同时还能享受 gill 的额外功能。
快速开始指南
创建 Solana RPC 连接
首先,你需要建立一个与 Solana 网络的连接。gill 提供了 createSolanaClient
函数来创建 RPC 客户端:
import { createSolanaClient } from "gill";
const { rpc, rpcSubscriptions, sendAndConfirmTransaction } = createSolanaClient({
urlOrMoniker: "mainnet", // 可以是 "devnet", "localnet" 或自定义 RPC URL
});
使用网络简称(如 “mainnet”)会连接到公共 RPC 端点,但这些端点有速率限制,不建议在生产环境中使用。生产应用应该使用自己的 RPC 提供商提供的 URL。
进行 Solana RPC 调用
建立连接后,你可以使用 rpc
对象调用所有 👉JSON RPC 方法:
// 获取当前 slot
const slot = await rpc.getSlot().send();
// 获取最新区块哈希
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
RPC 调用需要调用 .send()
方法才会实际发送请求到 RPC 提供商并获取响应。
你还可以在调用中包含自定义配置,比如使用 JavaScript 的 👉AbortController:
const abortController = new AbortController();
// 当用户离开当前页面时中止请求
function onUserNavigateAway() {
abortController.abort();
}
const slot = await rpc.getSlot().send({ abortSignal: abortController.signal });
生成密钥对和签名器
大多数”签名”操作都需要一个 KeyPairSigner
实例,用于签署交易和消息。
生成一个随机的 KeyPairSigner
:
import { generateKeyPairSigner } from "gill";
const signer = await generateKeyPairSigner();
这些签名器是不可提取的,意味着无法从实例中获取私钥材料。这是更安全的做法,除非你确实需要保存密钥对,否则强烈推荐使用这种方式。
如果你确实需要可提取的密钥对,gill 也提供了相应功能:
import { generateExtractableKeyPairSigner } from "gill";
const signer = await generateExtractableKeyPairSigner();
警告:使用可提取密钥对本质上不太安全,因为它们允许提取私钥材料。因此,只有在确实需要提取密钥材料(比如要将密钥保存到文件)时才应该使用它们。
创建交易
使用 gill 可以快速创建 Solana 交易:
import { createTransaction } from "gill";
const transaction = createTransaction({
version,
feePayer, // 可以是 Address 或 TransactionSigner
instructions,
// 强烈建议设置计算预算值以提高交易成功率
// computeUnitLimit: number,
// computeUnitPrice: number,
});
创建交易时设置最新区块哈希:
import { createTransaction } from "gill";
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
const transaction = createTransaction({
version,
feePayer,
instructions,
latestBlockhash,
});
签名交易
如果你的交易已经通过 createTransaction
设置了最新区块哈希生命周期:
import { createTransaction, signTransactionMessageWithSigners } from "gill";
const transaction = createTransaction(...);
const signedTransaction = await signTransactionMessageWithSigners(transaction);
如果交易没有设置最新区块哈希生命周期,你必须在签名操作之前或期间设置:
import {
createTransaction,
createSolanaClient,
signTransactionMessageWithSigners,
setTransactionMessageLifetimeUsingBlockhash,
} from "gill";
const { rpc } = createSolanaClient(...);
const transaction = createTransaction(...);
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
const signedTransaction = await signTransactionMessageWithSigners(
setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, transaction),
);
模拟交易
在实际发送交易前,你可以先模拟交易以检查可能的问题:
import { createSolanaClient, createTransaction } from "gill";
const { simulateTransaction } = createSolanaClient({
urlOrMoniker: "mainnet",
});
const transaction = createTransaction(...);
const simulation = await simulateTransaction(transaction);
提供给 simulateTransaction()
的交易可以是已签名或未签名的。
发送和确认交易
使用 sendAndConfirmTransaction
函数发送并确认交易:
import { createSolanaClient, createTransaction, signTransactionMessageWithSigners } from "gill";
const { sendAndConfirmTransaction } = createSolanaClient({
urlOrMoniker: "mainnet",
});
const transaction = createTransaction(...);
const signedTransaction = await signTransactionMessageWithSigners(transaction);
// 默认确认级别为 'confirmed'
await sendAndConfirmTransaction(signedTransaction);
如果你需要更精细地控制发送和确认过程的配置:
await sendAndConfirmTransaction(signedTransaction, {
commitment: "confirmed",
skipPreflight: true,
maxRetries: 10n,
// ... 其他配置
});
从已签名交易获取签名
交易被 feePayer 签名后(无论是部分签名还是完全签名),你可以获取交易签名:
import { getSignatureFromTransaction } from "gill";
const signature = getSignatureFromTransaction(signedTransaction);
console.log(signature);
// 示例输出: 4nzNU7YxPtPsVzeg16oaZvLz4jMPtbAzavDfEFmemHNv93iYXKKYAaqBJzFCwEVxiULqTYYrbjPwQnA1d9ZCTELg
由于 Solana 交易 ID 是交易签名数组中的第一项,客户端应用甚至可以在交易发送到网络确认之前就知道签名。
获取 Solana Explorer 链接
gill 提供了便捷的函数来生成 Solana Explorer 链接,用于查看交易、账户或区块信息。
获取交易链接
import { getExplorerLink } from "gill";
const link = getExplorerLink({
transaction: "4nzNU7YxPtPsVzeg16oaZvLz4jMPtbAzavDfEFmemHNv93iYXKKYAaqBJzFCwEVxiULqTYYrbjPwQnA1d9ZCTELg",
});
如果你有部分或完全签名的交易,甚至可以在发送交易之前获取 Explorer 链接:
import {
getExplorerLink,
getSignatureFromTransaction,
signTransactionMessageWithSigners,
} from "gill";
const signedTransaction = await signTransactionMessageWithSigners(...);
const link = getExplorerLink({
transaction: getSignatureFromTransaction(signedTransaction),
});
获取账户链接
import { getExplorerLink } from "gill";
const link = getExplorerLink({
cluster: "devnet",
account: "nick6zJc6HpW3kfBm4xS2dmbuVRyb5F3AnUvj5ymzR5",
});
获取区块链接
import { getExplorerLink } from "gill";
const link = getExplorerLink({
cluster: "mainnet",
block: "242233124",
});
当没有指定 cluster
时,getExplorerLink
函数默认使用 mainnet
。
计算账户最低租金
计算账户的最低租金余额(数据存储存款费用):
import { getMinimumBalanceForRentExemption } from "gill";
// 不提供 `space` 参数时默认为 0
const rent = getMinimumBalanceForRentExemption();
// 期望值: 890_880n
// 等同于
// getMinimumBalanceForRentExemption(0);
// 也等同于以下网络调用,但这是本地计算不需要网络请求
// const rent = await rpc.getMinimumBalanceForRentExemption(0n).send();
计算特定大小账户的租金:
import { getMinimumBalanceForRentExemption } from "gill";
const rent = getMinimumBalanceForRentExemption(50); // 50 字节
// 期望值: 1_238_880n
目前,账户的最低租金金额是基于 Solana 运行时的静态值计算的。虽然你可以使用 RPC 调用 getMinimumBalanceForRentExemption
来获取这个值,但这会导致网络调用并受到延迟影响。
Node.js 特定功能
gill 包提供了专门用于 Node.js 服务器后端和/或无服务器环境的导入,这些环境可以访问 Node 特定 API(如通过 node:fs
访问文件系统)。
import { ... } from "gill/node"
从文件加载密钥对
import { loadKeypairSignerFromFile } from "gill/node";
// 默认文件路径: ~/.config/solana/id.json
const signer = await loadKeypairSignerFromFile();
console.log("address:", signer.address);
从文件系统钱包 JSON 文件加载 KeyPairSigner
,如 👉Solana CLI 输出的文件(即数字 JSON 数组)。
默认加载的是 Solana CLI 的默认密钥对:~/.config/solana/id.json
。
从特定文件路径加载签名器:
import { loadKeypairSignerFromFile } from "gill/node";
const signer = await loadKeypairSignerFromFile("/path/to/your/keypair.json");
console.log("address:", signer.address);
保存密钥对到文件
将可提取的 KeyPairSigner
保存到本地 JSON 文件(例如 keypair.json
)。
import { generateExtractableKeyPairSigner } from "gill";
import { saveKeypairSignerToFile } from "gill/node";
const extractableSigner = await generateExtractableKeyPairSigner();
await saveKeypairSignerToFile(extractableSigner, "/path/to/keypair.json");
从环境变量加载密钥对
从环境进程中的字节加载 KeyPairSigner
(例如 process.env[variableName]
)
import { loadKeypairSignerFromEnvironment } from "gill/node";
// 从 `process.env[variableName]` 存储的字节加载签名器
const signer = await loadKeypairSignerFromEnvironment("MY_KEYPAIR");
console.log("address:", signer.address);
保存密钥对到环境变量文件
将可提取的 KeyPairSigner
保存到本地环境变量文件(例如 .env
)。
import { generateExtractableKeyPairSigner } from "gill";
import { saveKeypairSignerToEnvFile } from "gill/node";
const extractableSigner = await generateExtractableKeyPairSigner();
// 默认: envPath = `.env`(在当前工作目录中)
await saveKeypairSignerToEnvFile(extractableSigner, "MY_KEYPAIR");
从环境变量加载 base58 编码的密钥对
从环境进程中存储的 base58 编码密钥对加载 KeyPairSigner
。
import { loadKeypairSignerFromEnvironmentBase58 } from "gill/node";
// 从 `process.env[variableName]` 存储的 base58 密钥对加载签名器
const signer = await loadKeypairSignerFromEnvironmentBase58("MY_KEYPAIR_BASE58");
console.log("address:", signer.address);
事务构建器
为了简化常见事务的创建,gill 包含了各种”事务构建器”,帮助轻松组装即签即用的事务,这些事务通常需要同时与多个程序交互。
由于每个事务构建器都专注于单个任务,它们可以轻松抽象出各种样板代码,同时帮助创建优化的事务,包括:
-
设置/推荐默认计算单元限制(当然可以轻松覆盖)以优化事务并提高成功率 -
在需要时自动派生所需地址 -
通常推荐安全的默认值和回退设置
所有自动填充的信息也可以手动覆盖,确保你始终有逃生舱口来实现所需的功能。
创建带有元数据的代币
构建一个可以创建带有元数据的代币的事务,可以使用👉原始代币或👉代币扩展(token22)程序。
-
使用原始代币程序( TOKEN_PROGRAM_ADDRESS
,默认)创建的代币将使用 Metaplex 的代币元数据程序来处理链上元数据 -
使用代币扩展程序( TOKEN_2022_PROGRAM_ADDRESS
)创建的代币将使用元数据指针扩展
相关指令构建器:getCreateTokenInstructions
import { buildCreateTokenTransaction } from "gill/programs/token";
const createTokenTx = await buildCreateTokenTransaction({
feePayer: signer,
latestBlockhash,
mint,
// mintAuthority, // 默认=与 `feePayer` 相同
metadata: {
isMutable: true, // 如果 `updateAuthority` 将来可以更改此元数据
name: "Only Possible On Solana",
symbol: "OPOS",
uri: "https://raw.githubusercontent.com/solana-developers/opos-asset/main/assets/Climate/metadata.json",
},
// updateAuthority, // 默认=与 `feePayer` 相同
decimals: 2, // 默认=9
tokenProgram, // 默认=TOKEN_PROGRAM_ADDRESS,也支持 token22
// 默认计算单元限制设置为优化值,但可以在这里覆盖
// computeUnitLimit?: number,
// 从你喜欢的优先费用 API 获取
// computeUnitPrice?: number, // 未设置默认值
});
铸造代币到目标钱包
构建一个将新代币铸造到 destination
钱包地址的事务(提高代币的总供应量)。
-
确保设置了 mint
本身使用的正确tokenProgram
-
如果 destination
所有者没有为mint
创建关联代币账户(ata),将自动为他们创建一个 -
在设置事务中的 amount
时,确保考虑了mint
的decimals
相关指令构建器:getMintTokensInstructions
import { buildMintTokensTransaction } from "gill/programs/token";
const mintTokensTx = await buildMintTokensTransaction({
feePayer: signer,
latestBlockhash,
mint,
mintAuthority: signer,
amount: 1000, // 注意:务必考虑 mint 的 `decimals` 值
// 如果 decimals=2 => 这将铸造 10.00 个代币
// 如果 decimals=4 => 这将铸造 0.100 个代币
destination,
// 使用适用于 `mint` 的正确代币程序
tokenProgram, // 默认=TOKEN_PROGRAM_ADDRESS
// 默认计算单元限制设置为优化值,但可以在这里覆盖
// computeUnitLimit?: number,
// 从你喜欢的优先费用 API 获取
// computeUnitPrice?: number, // 未设置默认值
});
转账代币到目标钱包
构建一个将代币从 source
转账到 destination
钱包地址的事务(即从 sourceAta
到 destinationAta
)。
-
确保设置了 mint
本身使用的正确tokenProgram
-
如果 destination
所有者没有为mint
创建关联代币账户(ata),将自动为他们创建一个 -
在设置事务中的 amount
时,确保考虑了mint
的decimals
相关指令构建器:getTransferTokensInstructions
import { buildTransferTokensTransaction } from "gill/programs/token";
const transferTokensTx = await buildTransferTokensTransaction({
feePayer: signer,
latestBlockhash,
mint,
authority: signer,
// sourceAta, // 默认=从 `authority` 派生
/**
* 如果 `sourceAta` 不是从 `authority` 派生的(例如多签钱包),
* 使用 `getAssociatedTokenAccountAddress()` 手动派生
*/
amount: 900, // 注意:务必考虑 mint 的 `decimals` 值
// 如果 decimals=2 => 这将转账 9.00 个代币
// 如果 decimals=4 => 这将转账 0.090 个代币
destination: address(...),
// 使用适用于 `mint` 的正确代币程序
tokenProgram, // 默认=TOKEN_PROGRAM_ADDRESS
// 默认计算单元限制设置为优化值,但可以在这里覆盖
// computeUnitLimit?: number,
// 从你喜欢的优先费用 API 获取
// computeUnitPrice?: number, // 未设置默认值
});
调试模式
在 gill 中,你可以启用”调试模式”来自动记录额外信息,这些信息将有助于排查事务问题。
调试模式默认禁用,以最小化应用程序的额外日志。但通过其灵活的调试控制器,你可以从代码运行的最常见位置启用它,包括你的代码本身、Node.js 后端、无服务器函数,甚至 Web 浏览器控制台本身。
gill 已经内置的一些调试日志示例:
-
在发送事务时记录 Solana Explorer 链接 -
记录 base64 事务字符串,以便通过 👉 mucho inspect
或 Solana Explorer 的👉事务检查器进行故障排除
如何启用调试模式
要启用调试模式,将以下任何一项设置为 true
或 1
:
-
process.env.GILL_DEBUG
-
global.__GILL_DEBUG__
-
window.__GILL_DEBUG__
(即在 Web 浏览器控制台中) -
或手动设置任何调试日志级别(见下文)
要设置应用程序中输出的日志级别,设置以下一项的值(默认:info
):
-
process.env.GILL_DEBUG_LEVEL
-
global.__GILL_DEBUG_LEVEL__
-
window.__GILL_DEBUG_LEVEL__
(即在 Web 浏览器控制台中)
支持的日志级别(按优先级顺序):
-
debug
(最低) -
info
(默认) -
warn
-
error
自定义调试日志
Gill 还导出了它内部使用的相同调试函数,允许你实现与 Solana 事务相关的自定义调试逻辑,并使用与 gill 相同的控制器。
-
isDebugEnabled()
– 检查是否启用了调试模式 -
debug()
– 如果达到设置的日志级别,打印调试消息
import { debug, isDebugEnabled } from "gill";
if (isDebugEnabled()) {
// 你的自定义逻辑
}
// 如果启用了 "info" 或更高级别的日志级别,记录此消息
debug("custom message");
// 如果启用了 "debug" 或更高级别的日志级别,记录此消息
debug("custom message", "debug");
// 如果启用了 "warn" 或更高级别的日志级别,记录此消息
debug("custom message", "warn");
// 如果启用了 "error" 或更高级别的日志级别,记录此消息
debug("custom message", "error");
程序客户端
使用 gill,你还可以导入一些最常用程序的客户端。这些也是完全可树摇的,因此如果你没有在项目中导入它们,它们将在构建时被 JavaScript 打包器(如 Webpack)移除。
导入任何这些程序客户端:
import { ... } from "gill/programs";
import { ... } from "gill/programs/token";
gill 中包含的程序客户端有:
-
系统程序 – 从 👉 @solana-program/system
重新导出 -
计算预算程序 – 从 👉 @solana-program/compute-budget
重新导出 -
备忘录程序 – 从 👉 @solana-program/memo
重新导出 -
代币程序和代币扩展程序(又名 Token22) – 从 👉 @solana-program/token-2022
重新导出,这是一个与原始代币程序完全向后兼容的客户端 -
地址查找表程序 – 从 👉 @solana-program/address-lookup-table
重新导出 -
Metaplex 的代币元数据程序(仅 v3 功能) – 通过 Codama 其 IDL 生成(👉来源)
如果现有的客户端没有从 gill/programs
或其子路径导出,你当然可以手动将它们的兼容客户端添加到你的仓库中。
总结
gill 是一个功能强大且易于使用的 Solana 区块链客户端库,为 JavaScript/TypeScript 开发者提供了简洁的 API 和丰富的功能。无论是初学者还是经验丰富的区块链开发者,gill 都能帮助你更高效地构建 Solana 应用程序。
通过本文的介绍,你应该对 gill 的主要功能有了基本了解,包括如何建立连接、进行 RPC 调用、创建和签名交易、使用事务构建器以及调试应用程序。gill 的模块化设计和树摇支持确保了你可以只使用需要的功能,保持应用程序的轻量性。
无论你是要构建去中心化金融应用、NFT 市场还是其他区块链解决方案,gill 都提供了必要的工具和抽象来简化开发过程。