阅读时间 14 分钟
概述
“机器支付协议”(MPP)允许您在 HTTP 请求中直接支付 API 访问费用。无需账户,无需 API 密钥,只需一个拥有稳定币余额的钱包即可。
会话机制在此基础上更进一步。您只需打开一次支付通道,随后即可在每次请求中重复使用该通道。每次调用都通过一张小额的签名凭证进行支付,因此无需每次都进行链上交易,从而确保请求速度始终保持快速。
在本指南中,您将编写一个 TypeScript 脚本,该脚本将打开一个 MPP 会话,通过Quicknode 的 MPP 端点查询 15 条 EVM 链上的原生代币余额,并输出包含费用明细的格式化报告。
这非常适合会话场景,因为该脚本在单次运行中会跨不同网络发起多次 RPC 调用。您无需为每次调用单独付费,所有请求都会通过一个支付通道处理。您只需支付一次设置费用,之后即可重复使用该通道。
Quicknode 支持在所有受支持的链上建立 MPP 会话。用于查询以太坊的同一会话,无需额外配置即可查询 Base、Arbitrum、Polygon 及其他链。
每个钱包在 Quicknode 的代理支付接口(包括MPP和x402)上均可共享每月 1,000,000 个 API 积分的免费额度。标准 RPC 请求每次消耗1 个 API 积分,而 SQL Explorer 则消耗查询所使用的 API 积分。
Quicknode 支持两种基于钱包的支付协议,用于无账户 RPC 访问:MPP(本指南中使用)和x402(Coinbase 制定的开放标准)。这两种协议都允许脚本和 AI 代理在内联方式下支付 API 调用费用,无需账户或 API 密钥,但在数据传输格式、会话机制以及支持的支付方式方面存在差异。 请参阅《Agentic Payments 概述》以获取并列对比,或跳转至x402 文档和 MPP 文档查看协议详情。
您将学到什么
- 机器支付协议的工作原理以及会话存在的理由
- 完整的会话生命周期:402 挑战、通道存入、凭证签名和结算
- 如何使用该
mppx用于创建和管理支付会话的 SDK - 如何使用 Quicknode 的 MPP 端点在单个会话中跨不同链发起多次 RPC 调用
您需要准备的物品
- Node.jsv20 或更高版本
- 对 TypeScript 和 EVM 链有基本了解
- 无需 Quicknode 账户或 API 密钥(MPP 负责访问管理和计费)
- Tempo 测试网上的少量 PathUSD(通过脚本中的水龙头自动充值)或 Tempo 主网上的 PathUSD/USDC.e(用于生产环境)
什么是“机器支付协议”?
“机器支付协议”(Machine Payments Protocol)是一项提交给 IETF 的开放标准,旨在为任何 HTTP 端点添加内联支付功能。该协议旨在解决一个具体问题:如果没有人工事先设置计费,程序化客户端(代理、脚本、机器人)无法通过有效方式为 API 访问付费。
传统的支付流程依赖于结账表单、浏览器会话和可视化验证码。MPP 通过基于 HTTP 的机器可读支付协商机制取代了这些组件 402 需付款.
工作原理
该协议遵循挑战-响应模式:
- 请求:客户端向付费端点发送一个普通的 HTTP 请求。
- 挑战: 服务器返回
402 需付款以及一个WWW-Authenticate: 支付描述价格、接受的货币以及可用支付方式的标题。 - 支付:客户选择一种支付方式并完成支付(签署稳定币转账、开启支付通道等)。
- 重试: 客户端重新发送原始请求,其中包含一个
授权:付款包含付款凭证的页眉。 - 交付: 服务器验证付款后,返回包含一个
付款收据页眉。
诸如以下这类客户端库: mppx 自动处理第 2 至第 4 步。从代码的角度来看,请求就这么顺利完成了。
“费用”与“会话”
MPP 定义了两个支付意图,用于控制计费机制:
| 指控 | 会话 | |
|---|---|---|
| 图案 | 每次请求仅需支付一次 | 通过支付渠道按次付费 |
| 结算 | 每次请求的链上交易次数 | 链下凭证,仅在开盘/收盘时进行链上结算 |
| 验证延迟 | 数百毫秒(链上) | 微秒(受CPU限制 ecrecover) |
| 最适合 | 偶尔的查询,简单的集成 | 高频请求、批处理工作流、代理 |
关键区别在于支付验证的方式。在“charge”模式下,每次请求都会触发一条链上交易;而在“sessions”模式下,服务器会使用单个 ecrecover 调用,其关键路径中不包含任何 RPC 调用或数据库写入操作。这意味着服务器在会话期间不会接触区块链,因此吞吐量由服务器 CPU 决定,而非区块链共识机制。
在 Quicknode 的 MPP 端点上,这也带来了切实的成本节约:每次计费请求的费用为 0.001 美元,而每次会话请求的费用仅为 0.00001 美元。对于一个在单次运行中需跨不同网络发起 15 次及以上 RPC 调用的脚本而言,会话机制无疑是最佳选择:
| 查询的链 | 费用(0.001 美元/次请求) | 会话(0.00001 美元/次请求) |
|---|---|---|
| 10 | $0.010 | $0.0001 |
| 25 | $0.025 | $0.00025 |
| 50 | $0.050 | $0.0005 |
了解会话的生命周期
在深入研究代码之前,了解会话在底层的运作原理非常重要。下图展示了我们多链余额查询工具的完整流程:
关键概念
支付渠道:一种链上托管合约,客户需在会话开始时预先存入资金。此操作仅在会话开始时进行一次。
累积凭证:每个请求都包含一条签名消息,内容为“我目前已累计消耗了X”。凭证是累积的,而非递增的。服务器只需最新的一张凭证即可索取所有应付资金。例如,在发出三个请求(每个请求金额为0.00001)后:凭证(1) = 10、凭证(2) = 20、凭证(3) = 30个原子单位。
多网络会话: 单个会话实例处理来自不同 /session/:network简体中文(大陆) 端点。会话负责管理支付通道;网络标识符决定将请求代理到哪个 Quicknode RPC 后端。
结算:当会话结束时,服务器会在链上结算最终凭证。客户端将收到任何未使用押金的退款。
项目设置
完整的脚本位于 qn-guide-示例 存储库 (直接查看源文件). 克隆该项目并安装依赖项:
git clone https://github.com/quiknode-labs/qn-guide-examples.git
cd qn-guide-examples/mpp/multichain-balance-checker
npm 安装
您也可以复制该 index.ts 文件 将其添加到您自己的项目中,并安装所需的依赖项。
该项目使用了两个运行时依赖项:
| 包装 | 目的 |
|---|---|
mppx | MPP协议客户端(会话管理、凭证签名) |
viem | 钱包操作与余额格式设置 |
还有两个开发依赖项: tsx 用于直接运行 TypeScript,以及 TypeScript 用于类型检查。
定义链注册表
该脚本查询多个 EVM 链上的原生余额。每条链都通过其 MPP 网络别名、显示名称、原生代币符号和小数位数进行定义:
const CHAINS = [
{ slug: 'ethereum-mainnet', name: 'Ethereum', symbol: 'ETH', decimals: 18 },
{ slug: 'base-mainnet', name: 'Base', symbol: 'ETH', decimals: 18 },
{ slug: 'arbitrum-mainnet', name: 'Arbitrum', symbol: 'ETH', decimals: 18 },
{ slug: 'optimism-mainnet', name: 'Optimism', symbol: 'ETH', decimals: 18 },
{ slug: 'matic-mainnet', name: 'Polygon', symbol: 'POL', decimals: 18 },
{ slug: 'worldchain-mainnet', name: 'World Chain', symbol: 'ETH', decimals: 18 },
{ slug: 'bsc-mainnet', name: 'BNB Chain', symbol: 'BNB', decimals: 18 },
{ slug: 'fantom-mainnet', name: 'Fantom', symbol: 'FTM', decimals: 18 },
{ slug: 'celo-mainnet', name: 'Celo', symbol: 'CELO', decimals: 18 },
{ slug: 'xdai-mainnet', name: 'Gnosis', symbol: 'xDAI', decimals: 18 },
{ slug: 'zksync-mainnet', name: 'zkSync Era', symbol: 'ETH', decimals: 18 },
{ slug: 'scroll-mainnet', name: 'Scroll', symbol: 'ETH', decimals: 18 },
{ slug: 'linea-mainnet', name: 'Linea', symbol: 'ETH', decimals: 18 },
{ slug: 'mantle-mainnet', name: 'Mantle', symbol: 'MNT', decimals: 18 },
{ slug: 'blast-mainnet', name: 'Blast', symbol: 'ETH', decimals: 18 },
]
网络标识符是此处的关键。它映射到位于 https://mpp.quicknode.com/session/:slug. 该 /session/ URL 中的前缀用于指示服务器使用会话意图(session intent)而非计费意图(charge intent)。
您可以根据需要添加或移除链。Quicknode 的 MPP 端点支持所有受支持的网络。您还可以动态获取受支持的 slug 的完整列表:
curl https://mpp.quicknode.com/networks
请注意,您查询的链与支付网络是解耦的。您的会话运行在 Tempo 区块链(测试网或主网)上,但可以访问任何受支持链上的 RPC 端点。
设置钱包
该脚本需要一个钱包来签署支付通道的凭证。它可以使用环境中的现有私钥,也可以自动生成一个新的私钥:
import { 生成私钥, 将私钥关联到账户 } 来自 'viem/accounts'
const privateKey = (process.env.MPPX_PRIVATE_KEY 为 `0x${字符串}`) || 生成私钥()
const account = 账户私钥(privateKey)
当密钥被自动生成时,脚本会将其保存到一个 .env 文件(包含 chmod 600 权限),以便后续运行时能复用同一个钱包:
import { existsSync, writeFileSync } from 'fs'
if (isGenerated && !existsSync(envPath)) {
writeFileSync(
envPath,
[
'# Auto-generated by multichain balance checker',
'# This file is gitignored — never commit private keys.',
`MPPX_PRIVATE_KEY=${privateKey}`,
'',
].join('\n'),
{ mode: 0o600 },
)
}
通过测试网水龙头获取资金
在测试网环境中,该脚本会通过 Tempo 测试网水龙头自动为钱包充值。这将为钱包提供 PathUSD(一种测试网稳定币)以及在 Tempo Moderato(链 ID 42431)上的 gas 费:
async function fundWallet(address: string): Promise<void> {
const res = await fetch('https://docs.tempo.xyz/api/faucet', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ address }),
})
if (res.ok) {
console.log('Faucet: Request accepted — waiting for onchain confirmation...')
await waitForFunding(address)
}
}
该 等待资金到位 该函数会向Tempo Moderato上的PathUSD合约发起查询,以确认余额已到账,然后才继续执行:
const TEMPO_RPC = 'https://rpc.moderato.tempo.xyz'
const PATHUSD_ADDRESS = '0x20c0000000000000000000000000000000000000'
async function getPathUSDBalance(walletAddress: string): Promise<bigint> {
const data = '0x70a08231' + walletAddress.slice(2).padStart(64, '0')
const res = await fetch(TEMPO_RPC, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
jsonrpc: '2.0', id: 1,
method: 'eth_call',
params: [{ to: PATHUSD_ADDRESS, data }, 'latest'],
}),
})
const json = (await res.json()) as { result?: string }
return BigInt(json.result ?? '0x0')
}
这种零门槛的设置意味着任何人都可以克隆该代码库并运行脚本,无需注册任何账户,也无需手动向钱包充值。
Quicknode 的 MPP 会话流程使用与 x402 相同的免费套餐配额:每个钱包每月 1,000,000 个 API 积分。每次 RPC 请求消耗 1 个 API 积分,而 SQL Explorer 则消耗查询所用的 API 积分。免费套餐用完后,请切换至主网充值过的钱包。请参阅“投入生产环境”。
创建会话并查询余额
这里是核心会话逻辑所在的位置。该脚本使用以下内容创建了一个 MPP 会话: tempo.session(),然后遍历每条链,发送 eth_getBalance 通过会话发出的请求:
import { tempo } from 'mppx/client'
const session = tempo.session({
account,
maxDeposit: '1', // 1 PathUSD — covers 100,000 requests at $0.00001/each
})
该 maxDeposit 该参数用于设置支付渠道的总预算。会话结束时,未使用的金额将退还给您。
如果交易时段中途通道保证金用尽,则 mppx 客户端无需关闭并重新打开会话,即可通过额外的链上存款为通道充值。对于我们的 15-chain 脚本而言,即使是一笔小额存款也绰绰有余,但如果你正在构建需要发出数千次请求的长期运行型应用程序,此功能将非常有用。
通过会话进行 RPC 调用
对于每条链,脚本都会调用 session.fetch() 使用标准的 JSON-RPC eth_getBalance 有效载荷。该会话会自动处理所有 MPP 机制:
const MPP_BASE_URL = 'https://mpp.quicknode.com/session'
for (const chain of CHAINS) {
const response = await session.fetch(`${MPP_BASE_URL}/${chain.slug}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
jsonrpc: '2.0',
id: 1,
method: 'eth_getBalance',
params: [WALLET_ADDRESS, 'latest'],
}),
})
const data = (await response.json()) as { result?: string; error?: { message: string } }
// Parse hex balance to human-readable format
const balance = formatBalance(data.result!, chain.decimals)
}
在 首次请求, session.fetch() 处理完整的 402 挑战-响应流程:它接收挑战,将 PathUSD 存入链上托管合约,并使用已签名的凭证重试。整个过程大约需要 500 毫秒。
关于 随后的每个请求, 该会话仅将累计凭证递增,并将其纳入 授权 标头。服务器通过单次验证来验证优惠券 ecrecover 该操作(无需链上交易),因此响应的支付开销几乎为零。
该脚本还包含针对 404 响应的重试机制,用于处理 MPP 路由的临时问题:
if (response.status === 404) {
response = await session.fetch(`${MPP_BASE_URL}/${chain.slug}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: rpcBody,
})
}
余额的格式设置
该 eth_getBalance 响应返回一个十六进制字符串,表示以wei(或该链上等效的最小单位)为单位的余额。该脚本将此值转换为易于人类阅读的十进制数:
import { formatUnits } from 'viem'
function formatBalance(hexBalance: string, decimals: number): string {
const value = BigInt(hexBalance)
return parseFloat(formatUnits(value, decimals)).toFixed(4)
}
结束本次会议
所有查询完成后,脚本会关闭会话以触发链上结算。服务器对最终累计凭证进行结算,并将未使用的押金退还至钱包:
// 在关闭前捕获会话数据
const channelId = session.channelId
const 累计消费 = session.累计
控制台.日志('正在关闭会话并进行链上结算...')
const 收据 = await session.close()
随后,该脚本会输出一个摘要,显示总费用、退款金额和结算详情:
printSummary(
结果,
总请求数,
channelId,
累计支出,
收据?.txHash,
);
该脚本还将整个查询循环包裹在 try/catch 块中,即使出现错误也会尝试关闭会话,因此支付通道不会无限期保持打开状态:
try {
// ... query loop ...
await session.close()
} catch (err) {
console.error('Fatal error:', err instanceof Error ? err.message : err)
try {
await session.close()
console.log('Session closed after error.')
} catch {
console.log('Could not close session.')
}
process.exit(1)
}
运行脚本
一切准备就绪后,运行该脚本:
# 使用自动生成的测试网钱包运行(无需任何配置)
npx tsx index.ts
# 或者通过 .env 文件提供您自己的私钥
echo "MPPX_PRIVATE_KEY=0x..." > .env
npx tsx --env-file=.env index.ts
你应该会看到类似这样的输出:
多链余额查询工具(通过 MPP 会话)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
钱包:0x1234...abcd(自动生成)
目标: 0xd8dA...6045
链 | 余额 | 状态
──────────────────┼───────────────────────┼──────────
以太坊 | 1.2345 ETH | 正常
Base | 0.5000 ETH | 正常
Arbitrum | 0.0000 ETH | 正常
Optimism | 2.1000 ETH | 正常
Polygon | 150.0000 POL | 通过
BNB Chain | 0.0312 BNB | 正常
... | ... | ...
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
会话摘要
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
查询的链: 15(15 成功,0 错误)
RPC 调用总数: 15
会话成本: 0.000150 PathUSD(0.00015 美元)
通道存款: 1.000000 PathUSD
退款: 0.999850 PathUSD
通道 ID: 0xdef456...abc123
结算交易: 0xabc123...def456
交易确认后,您可以在区块浏览器上核对结算情况。该 结算交易 输出中的哈希值指向最终的链上交易。请使用 测试网浏览器 用于“中板”(Tempo Moderato)或 主网浏览器 用于批量生产。
下图展示了结算交易的具体样式:

红色方框显示了与该支付通道相关的两笔余额转账:会话开启时向托管合约发送的 1 PathUSD,以及结算时作为退款返还的 0.99985 PathUSD。橙色方框显示了作为结算交易的一部分支付给服务器的实际会话费用(0.00015 PathUSD)。
您可以通过编辑 WALLET_ADDRESS 位于顶部的常量 index.ts:
const WALLET_ADDRESS = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045' // 任意地址
投入生产
该脚本默认使用测试网,以实现无缝入门。以下是生产环境使用时的变更内容:
| 测试网(默认) | 主网 | |
|---|---|---|
| 支付网络 | Tempo Moderato(链ID 42431) | Tempo 主网(链 ID 4217) |
| 货币 | PathUSD | PathUSD 或 USDC.e |
| 免费套餐 | 每个钱包每月在 MPP 和 x402 上的 API 积分总计为 1,000,000 | 无限 |
| 钱包充值 | 通过水龙头自动充值 | 需要手动拨款 |
要切换到主网,请提供一个在Tempo主网上持有PathUSD或USDC.e的钱包密钥。该 mppx 客户端会自动检测可用余额,并从402挑战中选择合适的支付方式。
扩展脚本
余额查询工具的设计刻意保持简单,以便将重点放在会话机制上。关键要点在于这一模式:打开一个会话,在不同链上发起多次 RPC 调用,然后关闭会话并结算。由于单个 MPP 会话可处理所有网络分片上的请求,因此无需更改会话配置,即可扩展该脚本以调用任何受支持链上的任意 RPC 方法。例如,你可以通过替换来添加 ERC-20 代币余额查询功能 eth_getBalance 用于一个 eth_call 到代币合约的 balanceOf 函数。
结论
你开发了一个多链余额查询工具,它能够通过单个 MPP 会话查询 15 条 EVM 链。需要掌握的关键模式是:打开一个支付通道,利用链下凭证在支持的任意链组合上进行所需数量的 RPC 调用,然后关闭通道并进行结算。该会话自动处理了所有支付机制,从初始的 402 挑战、凭证签名,到最终结算和退款。
该模式适用于任何需要发起多次 RPC 调用的工作流:投资组合追踪工具、监控脚本、数据管道,或是需要跨网络读取链上状态的 AI 代理。会话机制使这些工具能够通过编程方式支付访问费用,无需账户或 API 密钥,同时确保支付开销不影响关键路径。
下一步
如需进一步了解 MPP、Tempo 以及 Quicknode 的 MPP 集成,请参阅以下资源:
- Machine Payments Protocol 文档
- MPP 按会话计费指南
- npm 上的 mppx SDK
- Quicknode MPP 协议参考
- IETF 支付认证规范
- 在 Foundry 和 Tempo 中使用 MPP(面向 Solidity 开发者的配套指南)
常见问题解答
什么是 MPP?它与传统的 API 密钥认证有何不同?
机器支付协议(MPP)是一项开放标准,它利用 402“需要支付”状态码,为 HTTP 端点添加内联支付功能。用户无需注册账户或管理 API 密钥,而是通过钱包使用稳定币按请求付费。这使得脚本、代理和机器人能够立即开始使用 API,而无需人工干预进行计费设置。
在什么情况下应该使用 MPP 会话,而不是按请求计费?
当您的脚本或应用程序在单次运行中发起多次 RPC 调用时,请使用会话。会话会在链上打开一个支付通道,并使用链下凭证处理后续请求。凭证验证是一项受 CPU 性能限制的签名检查,会增加几微秒的开销,而链上收费支付则需要数百毫秒。在 Quicknode 的 MPP 端点上,会话的每次请求成本也显著更低。如果您发起的请求少于 10 次,使用收费方式会更简单。
使用 MPP 端点需要 Quicknode 账户吗?
不。MPP 端点无需账户、API 密钥,也无需注册。您只需拥有一个存有稳定币(或测试网代币)的钱包即可。支付将在每次 HTTP 请求中内联完成。
我能否通过 MPP 会话查询 Solana 等非 EVM 链?
Quicknode 的 MPP 端点支持 Solana 及其他非 EVM 链。您只需在 URL 中更改网络别名,即可使用同一会话实例查询任何受支持的链。例如,使用 /session/solana-mainnet 即可通过同一支付通道查询 Solana RPC 端点。
如果我的脚本在关闭会话之前崩溃了,会发生什么情况?
如果未显式关闭会话,支付通道将保持打开状态,直到超时为止。服务器仍可结算其收到的最后一张凭证。为避免这种情况,脚本将查询循环包裹在 try/catch 块中,即使出现错误也会尝试关闭会话。
免费套餐是如何运作的?我该如何迁移到主网?
Quicknode 的 MPP 端点使用与 x402 共享的同一免费层配额,即每个钱包每月 1,000,000 个 API 积分。每个 RPC 请求消耗 1 个 API 积分,而 SQL Explorer 则消耗查询所用的 API 积分。如需无限使用,请提供一个在 Tempo 主网(链 ID 4217)上充值了 PathUSD 或 USDC.e 的钱包。
我们 ❤️ 您的反馈!
如果您有任何反馈或对新主题的建议,请告诉我们。我们非常期待您的来信。
