
从 JavaScript 读取链上数据,一直是这份工作里相对麻烦的一半。拿到一个干净的 Bitcoin 余额 —— 像 explorer 那样把已确认和未确认余额都呈现出来 —— 通常意味着自建 indexer 或者一直敲打一个不太稳定的公开 API。列出一个 Ethereum 或 EVM 地址持有的 NFT 与代币,则需要把三四个 explorer 端点拼接在一起,各家有各家的 schema、各家的 rate limit,还各自缺一些字段。等你真要 broadcast 一笔交易时,又得回到 Bitcoin 用 bitcoinjs-lib、EVM 这边用 ethers 或 viem、Dogecoin 与 Litecoin 还得靠半维护的 fork 的老路。ChainGate 把这一切收进一套 SDK 里。
ChainGate 把这部分工作收纳进一套 JavaScript SDK。它作为一套多链钱包 SDK 来构建,同一种调用形式就能发送 Bitcoin、Ethereum、Avalanche(以及任意 EVM 链)、Litecoin、Dogecoin 与 Bitcoin Cash,读取地址余额,并列出任意地址上的 assets。同一份包既能在 Node.js 也能在浏览器中无改动运行 —— 每一项 crypto 原语都以原生方式随包提供,不需要任何 polyfill。
下面是一段基本的五行 send。指南其余部分会在每条链上重复同一个 pattern,然后再加上 broadcast 之前的 fee 估算与余额查询:
import { ChainGate, importWallet } from 'chaingate';
const cg = new ChainGate({ apiKey: 'your-api-key' });
const wallet = importWallet({ phrase: process.env.MNEMONIC });
const btc = cg.connect(cg.networks.bitcoin, wallet);
const tx = await btc.transfer(cg.networks.bitcoin.amount('0.001'), 'bc1qRecipient...');
await tx.signAndBroadcast();简要版
- 一套 SDK,所有链。 同一个 transfer() API 覆盖 Bitcoin、Ethereum、Avalanche、Litecoin、Dogecoin、Bitcoin Cash 以及任何 EVM 兼容网络。
- 在 Node.js 22+ 与现代浏览器中无需修改即可运行 —— 不必配置 Buffer 或 crypto polyfill。
- Fee、余额与 broadcast 全部内置 —— 这些都不需要再引第二个库。
- 签名始终在本地完成。私钥不会离开 SDK 边界。
1. Setup:安装并初始化 ChainGate
选好你的包管理器。SDK 作为单个 npm 包发布 —— 安装一次,就可以从同一个 import 中发送 Bitcoin、Ethereum 以及其他链,类型已包含:
# pick your package manager
npm install chaingate
pnpm add chaingate
yarn add chaingate然后到 ChainGate dashboard 拿一个免费 API key。注册大约一分钟,免费 tier 足以把原型推到生产。用这个 key 初始化 client 一次,然后在每条链上复用:
import { ChainGate } from 'chaingate';
const cg = new ChainGate({ apiKey: process.env.CHAINGATE_API_KEY });Node.js 与浏览器中代码相同。 包支持 Node 22+、现代浏览器(Vite、Next.js、Webpack 5+)与 Bun,开箱即用,因此你在服务端 import 的那一份 Node.js 区块链库,正是用户浏览器里运行的那一份。Buffer、randomBytes 等 crypto 原语已经原生地包含在 SDK 内 —— 没有什么要 polyfill 的,也没有什么要配置的。
TypeScript 类型随包提供。 不存在 @types/chaingate 包,因为不需要 —— 每个公开 class、method 与 event 都在源代码中完整类型化。
2. 发送 Bitcoin、Litecoin、Dogecoin 与 Bitcoin Cash
Bitcoin、Litecoin、Dogecoin 与 Bitcoin Cash 共享同一种记账模型 —— UTXO。钱包余额是锁在其地址下的 unspent transaction outputs 总和。要发送资金,钱包挑出足以覆盖金额的 UTXO,构造一笔以它们为 input 的新交易,给收件人一个 output,把找零作为 change output 退回自己,并按每千字节多少 satoshi(或对应链的等价单位)支付 fee。
ChainGate 把每一步 —— UTXO 选择、change output、fee tier 与签名 —— 都藏在单次 transfer() 调用之后。给它一个目标地址和金额,它返回一笔已签名、随时可以 broadcast 的交易。
在 Node.js 或浏览器中发送 Bitcoin
基础示例。把钱包接到 Bitcoin 网络,创建 transfer,选一个 fee tier,然后 broadcast:
import { ChainGate, importWallet, NotEnoughFundsError } from 'chaingate';
const cg = new ChainGate({ apiKey: process.env.CHAINGATE_API_KEY });
const wallet = importWallet({ phrase: process.env.MNEMONIC });
const btc = cg.connect(cg.networks.bitcoin, wallet);
const amount = cg.networks.bitcoin.amount('0.001'); // 0.001 BTC
try {
const tx = await btc.transfer(amount, 'bc1qRecipient...');
tx.setFee(tx.recommendedFees().normal);
const broadcast = await tx.signAndBroadcast();
console.log('BTC txid:', broadcast.transactionId);
} catch (err) {
if (err instanceof NotEnoughFundsError) {
console.error('Not enough BTC for this transfer + fee');
}
throw err;
}开发阶段想指向 testnet,把 cg.networks.bitcoin 换成 cg.networks.bitcoinTestnet 即可,其余代码原样不动。
在 Node.js 或浏览器中发送 Litecoin
代码相同,网络不同。和 Bitcoin 片段相比,diff 字面意义上只是一个标识符:
const ltc = cg.connect(cg.networks.litecoin, wallet);
const amount = cg.networks.litecoin.amount('0.1'); // 0.1 LTC
const tx = await ltc.transfer(amount, 'ltc1qRecipient...');
tx.setFee(tx.recommendedFees().normal);
await tx.signAndBroadcast();Litecoin 默认使用 ltc1q… 形式的 SegWit 地址,每两分半钟出一个块 —— 大约比 Bitcoin 快四倍 —— 因此在更看重 settlement 速度而非安全深度的小额支付里很受欢迎。
在 Node.js 或浏览器中发送 Dogecoin
同样的 diff,不同的链:
const doge = cg.connect(cg.networks.dogecoin, wallet);
const amount = cg.networks.dogecoin.amount('100'); // 100 DOGE
const tx = await doge.transfer(amount, 'DRecipientAddress...');
tx.setFee(tx.recommendedFees().normal);
await tx.signAndBroadcast();Dogecoin 的 JavaScript 生态落后于 UTXO 家族其他成员。大多数热门 bitcoinjs-lib fork 已多年未更新,少数仍在维护的替代方案是私有的。在今天,ChainGate 是为数不多在 JavaScript 中提供生产级 Dogecoin 支持的方案之一。
在 Node.js 或浏览器中发送 Bitcoin Cash
Bitcoin Cash 默认使用 CashAddr 地址,但 SDK 也接受 legacy 格式 —— 把任意一种粘进 transfer() 都能正常确认:
const bch = cg.connect(cg.networks.bitcoincash, wallet);
const amount = cg.networks.bitcoincash.amount('0.05'); // 0.05 BCH
// CashAddr or legacy — both work
const tx = await bch.transfer(amount, 'bitcoincash:qpRecipient...');
tx.setFee(tx.recommendedFees().normal);
await tx.signAndBroadcast();地址校验是双向的:cg.networks.bitcoincash.isValidAddress() 对两种格式都返回 true,因此发送前不需要先把用户输入做归一化。
在浏览器里发送 UTXO
上面四个 UTXO 片段在 Vite、Next.js 或 Webpack 5 的 bundle 中无需修改即可运行 —— 同一个 import 在客户端工作,同一个钱包在本地签名,无需联系 API。
一条安全提示:绝不要把生产环境的私钥直接装进前端。要么把签名留在服务器、只让 broadcast 从浏览器发出,要么让用户通过自己掌控的 wallet 风格流程提供私钥。
3. 发送 Ethereum 与 EVM 链
Ethereum 的账户模型不一样 —— 没有 UTXO,只有一个带余额和单调递增 nonce 的地址。ChainGate 把这种差异藏在与 Bitcoin 相同的 transfer() 调用之后。差异(nonce 管理、EIP-1559 base fee + tip、gas limit)由它替你计算:
const eth = cg.connect(cg.networks.ethereum, wallet);
const amount = cg.networks.ethereum.amount('0.01'); // 0.01 ETH
const tx = await eth.transfer(amount, '0xRecipient...');
tx.setFee(tx.recommendedFees().normal);
const broadcast = await tx.signAndBroadcast();
console.log('ETH txHash:', broadcast.transactionId);在 Node.js 或浏览器中发送 Avalanche
Avalanche 的 C-Chain 作为与 Ethereum 并列的一等公民网络接入 —— 同样的调用形式、同样的钱包,只是金额以 AVAX 计。和 Ethereum 片段相比的 diff 就一个标识符:
const avax = cg.connect(cg.networks.avalanche, wallet);
const amount = cg.networks.avalanche.amount('1'); // 1 AVAX
const tx = await avax.transfer(amount, '0xRecipient...');
tx.setFee(tx.recommendedFees().normal);
await tx.signAndBroadcast();C-Chain 大约两秒就能终结一个区块,运行的也是和 Ethereum 一致的 EIP-1559 fee 模型,因此 AVAX 转账确认得很快,recommendedFees() 的输出形态也一致。这让 Avalanche 在那些被 Ethereum gas 价格挤走用户的高吞吐支付场景里成为一个常见选择。
同一个钱包、同一套 SDK,所有 EVM 链。Hosted RPC URL 开箱即用地覆盖 Polygon、Arbitrum、Base、BNB Chain 与 Sonic;对其他任意 EVM 网络 —— Optimism、Linea、Scroll、Gnosis、自建 node、testnet —— 直接把 JSON-RPC URL 传给 evmRpc()。在 Polygon 上发送 POL 只是一行 diff:
const polygon = cg.networks.evmRpc({
rpcUrl: cg.rpcUrls.polygon,
chainId: 137,
name: 'Polygon',
symbol: 'POL',
});
const pol = cg.connect(polygon, wallet);
const amount = polygon.amount('5'); // 5 POL
const tx = await pol.transfer(amount, '0xRecipient...');
tx.setFee(tx.recommendedFees().normal);
await tx.signAndBroadcast();Nonce 管理是自动的。 SDK 在签名前会从所选 RPC 读取最新的 pending nonce,你不必自己维护。Fee 构造在支持 EIP-1559 的链上默认走 EIP-1559(maxFeePerGas + maxPriorityFeePerGas),在不支持的链上回退到 legacy gas pricing。
传给 evmRpc() 的 chainId 必须是 RPC 端点自己报告的那个值,否则 SDK 会为错误的网络签名。EVM 链 ID 列表里有 Polygon、Arbitrum、Base、Optimism 及另外 35 条网络和测试网的编号,并展示了如何直接从任意 RPC URL 读取它。
如果节点在广播时返回“nonce too low”“replacement transaction underpriced”或“insufficient funds for gas * price + value”,以太坊交易错误指南解释了每条消息的含义以及哪些会消耗 gas。
浏览器支持与 UTXO 链一致。这就是你会放在 dApp 里某个 'Send' 按钮背后的代码。
4. 发送前估算交易 fee
永远在 broadcast 之前展示 fee。用户讨厌惊喜,卡住的交易耗时间,而 'review and confirm' 一步会把单次提交的 UI 流程变成用户真正放心的体验。
ChainGate 在每条链上都暴露同一个 recommendedFees() 方法 —— 它返回四档 tier(low、normal、high、maximum),并附上每档需要的数字。Bitcoin 上你拿到 feePerKbSat 速率与以 satoshi 计的预估总额;Ethereum 与其他 EVM 链上你拿到 EIP-1559 的 maxFeePerGas 与 maxPriorityFeePerGas(均为 wei 的 bigint),以及估算的确认窗口:
// Bitcoin — fee rate in sat/kB, plus the total estimated fee in satoshis
const btcTx = await btc.transfer(cg.networks.bitcoin.amount('0.01'), 'bc1q...');
const btcFees = btcTx.recommendedFees();
console.table({
low: { satPerKb: btcFees.low.feePerKbSat, feeSat: btcFees.low.estimatedFeeSat, enough: btcFees.low.enoughFunds },
normal: { satPerKb: btcFees.normal.feePerKbSat, feeSat: btcFees.normal.estimatedFeeSat, enough: btcFees.normal.enoughFunds },
high: { satPerKb: btcFees.high.feePerKbSat, feeSat: btcFees.high.estimatedFeeSat, enough: btcFees.high.enoughFunds },
});
// Ethereum — EIP-1559 numbers in gwei
const ethTx = await eth.transfer(cg.networks.ethereum.amount('0.1'), '0x...');
const ethFees = ethTx.recommendedFees();
const toGwei = (wei) => Number(wei) / 1e9;
console.table({
low: { gwei: toGwei(ethFees.low.maxFeePerGas), sec: ethFees.low.estimatedConfirmationSecs },
normal: { gwei: toGwei(ethFees.normal.maxFeePerGas), sec: ethFees.normal.estimatedConfirmationSecs },
high: { gwei: toGwei(ethFees.high.maxFeePerGas), sec: ethFees.high.estimatedConfirmationSecs },
maximum: { gwei: toGwei(ethFees.maximum.maxFeePerGas), sec: ethFees.maximum.estimatedConfirmationSecs },
});在生产环境行得通的 pattern:构造交易、调用 recommendedFees()、把四档 tier 给用户看、让其选择,然后调用 signAndBroadcast()。从估算到 broadcast,交易对象一直留在内存里 —— 不需要重新构造。
5. 发送前查询余额
余额查询应当排在任何 send 流程的最前面。它能拦下错字、避免把 gas 浪费在注定失败的交易上,并给用户一个可以对得上的数字。每条链都暴露相同的一行调用 —— 传入一个地址(或使用已连接钱包),你就能得到已确认余额,以及在支持的链上获得未确认部分:
// Ethereum — confirmed balance + live USD price
const eth = cg.connect(cg.networks.ethereum, wallet);
const ethBalance = await eth.addressBalance();
const ethUsd = await ethBalance.confirmed.toCurrency('usd');
console.log(`${ethBalance.confirmed.base()} ETH ($${ethUsd})`);
// Bitcoin — confirmed and unconfirmed split
const btc = cg.connect(cg.networks.bitcoin, wallet);
const btcBalance = await btc.addressBalance();
console.log(`Confirmed: ${btcBalance.confirmed.base()} BTC`);
console.log(`Unconfirmed: ${btcBalance.unconfirmed.base()} BTC`);和 fee 估算搭配,完整的 pre-flight 是三次调用 —— 余额、fee、再 send。一次接好,你应用里的每个 send 流程都能免费拥有同样的 pre-send 校验,无论你是在服务端的 Node.js 还是在浏览器中查询加密余额。
6. ChainGate 与 bitcoinjs-lib、ethers 等替代方案的对比
ChainGate 不打算取代它之前的每一个底层库。每个库都有清晰的职责。当你需要在多条链上发送交易时,trade-off 大致如下:
| 能力 | ChainGate | bitcoinjs-lib | ethers / viem | 自研逐链实现 |
|---|---|---|---|---|
| 支持链 | BTC、ETH、AVAX+EVM、LTC、DOGE、BCH | 仅 Bitcoin | 仅 EVM | 每库 1 条 |
| 发送 BTC 行数 | ~5 | ~40+ | 不适用 | 不一定 |
| 浏览器就绪 | 是 —— 开箱即用 | 需要 polyfill | 是 | 不一定 |
| 内置 fee 估算 | 是 | 否 | 部分 | 否 |
| TypeScript 类型 | First-class | 社区 | 是 | 不一定 |
如果你需要对交易格式有逐字节级的控制,或者应用只对接一条链而 bundle 体积是决定性因素,继续用 bitcoinjs-lib 或 ethers。如果你需要同时处理多条链、追赶 deadline,或者带的是一个无法按区块链分工的小团队,就转向 ChainGate。
7. 定价与免费 tier
免费 tier 故意做得慷慨 —— 包括免费在内的每个 plan 都附带完整的 SDK:每条受支持的链上的余额、assets 列表、fee 估算、签名与 broadcast。它足以推出原型、跑个人项目或把内部工具搬上生产。注册无需信用卡。
付费 tier 提升 rate limit,并加入 priority support 与 burst 容量。在 dashboard 里点一下即可升级。
实时数字与升级路径见 定价页面。
8. 下一步去哪里
你已经掌握了模型:一套 SDK、多条链,在每条链上都是同样的五行结构。从这里开始,文档 涵盖剩下的功能 —— 消息签名、ERC-20 转账、NFT 操作、解码后的交易历史。GitHub 上的示例 是端到端 recipe 的不错起点,可以直接复制。当你准备拿一个 key 发布点东西时,API dashboard 只在一击之外。要更深入地研究 fee 背后的机制,姊妹篇 Ethereum gas 拆解了 recommendedFees() 中的数字到底从哪儿来。另一篇交易哈希(TXID)指南则解释了 signAndBroadcast() 返回的 transactionId 在每条链上长什么样,以及如何在区块浏览器上查询它。
常见问题
ChainGate 会保存我的私钥吗?
可以在浏览器里使用 ChainGate 吗?
可以 —— 本指南中的每个示例在 Vite、Next.js 或 Webpack 5 的 bundle 中无需修改都能跑。SDK 自带浏览器兼容的 crypto,所以没有需要额外安装的东西。
支持哪些网络?
ChainGate 对 Bitcoin、Bitcoin Testnet、Litecoin、Dogecoin、Bitcoin Cash、Ethereum 与 Avalanche 提供完整 API 支持,对 Polygon、Arbitrum、Base、BNB Chain 与 Sonic 提供 hosted JSON-RPC endpoint,对其他任意 EVM 链提供 bring-your-own-RPC 支持 —— 包括 Optimism、Linea、Scroll、Gnosis、各种 testnet 与自建 node。完整矩阵在文档里。
可以发送 testnet 交易吗?
可以 —— 没有专门的 testnet flag。把 testnet 网络传给与 mainnet 相同的 cg.connect() 调用即可:Bitcoin 这边是 cg.networks.bitcoinTestnet;EVM testnet 则把它的 JSON-RPC URL 与 chain ID 传给 cg.networks.evmRpc({ rpcUrl, chainId, name, symbol })。其余代码与本指南中的 mainnet 示例完全一致。
交易失败时会怎样?
SDK 会暴露一个可以捕获并处理的类型化错误。broadcast 之前的大多数问题都是可恢复的 —— 重试时,交易对象会用新的 nonce 或新的 UTXO 选择重新构造,所以第二次尝试通常都能通过。
ChainGate 是开源的吗?
在你机器上签名并 broadcast 的 JavaScript SDK 已在 GitHub 上以 MIT 许可发布 —— 阅读源码、vendor、fork 都行。背后的 ChainGate API 是一项托管服务,负责把其余的事情都替你打理好。多数团队把开源 SDK 与托管 API 搭配使用,但如果你更愿意自托管基础设施,可以把 SDK 指向任何你掌控的 RPC URL。
为什么 ChainGate 需要 API key?
使用 ChainGate 构建
把这些想法落地 —— ChainGate 提供钱包、交易与托管 RPC,一次安装即可覆盖以太坊以及 20 多条其他区块链。