getTransfersByAddress:一次调用获取解析后的Solana转账历史

  • Helius
  • 发布于 3 天前
  • 阅读 74

Helius推出了Solana RPC新方法getTransfersByAddress,返回解析后的代币和SOL转账记录,支持按铸币、金额、时间、对手方等过滤器查询,并处理SOL/WSOL合并、Token-2022手续费、铸造/销毁等边缘情况,简化了开发者获取转账历史的工作。

getTransfersByAddress 是 Helius 独占的全新 Solana RPC 方法,可返回钱包地址的已解析、人类可读的代币和 SOL 转账记录,并原生支持按铸币、时间、金额、插槽、方向和对手方进行过滤。

它是 getTransactionsForAddress(gTFA)的完美补充。gTFA 返回完整的交易负载,而 getTransfersByAddress 返回简洁的转账对象:谁发送了什么、给谁、何时以及多少。

为什么需要专门处理转账的 RPC 方法?

大多数钱包、支付和投资组合产品并不需要完整的交易负载。它们需要的是转账。

那么,它们是怎么做的呢?每个团队都编写了相同转账解析器的不同版本,不幸的是,大多数版本在处理边界情况时会出错。

在此之前,构建清晰的 Solana 转账历史记录需要开发者:

  1. 使用 getSignaturesForAddress 拉取签名
  2. 使用 getTransaction 获取每个签名
  3. 解析交易前后的余额、代币余额和内部指令
  4. 重建转账,处理 SPL Token 与 Token-2022 的费用语义,并理清 WSOL 包装/解包带来的干扰
  5. 跨多个页面重复操作,处理重试,并存储结果。

即使使用 getTransactionsForAddress 方法 将步骤 1 和 2 合并为一次调用,步骤 3–5 仍然需要由开发者完成。

现在,getTransfersByAddress 为你完成这些工作,并将结果以结构化列表的形式返回。

getTransfersByAddress 响应

每个转账对象包含签名、插槽、区块时间、转账类型、发送方、接收方、铸币、金额(原始和 UI 格式)、小数位数、确认状态以及精确的指令索引,以便将每笔转账映射回原始交易。

{
  "signature": "<TX_SIGNATURE>",
  "slot": 315073428,
  "blockTime": 1736159420,
  "type": "transfer",
  "fromUserAccount": "<SENDER_WALLET>",
  "toUserAccount": "<RECIPIENT_WALLET>",
  "fromTokenAccount": "<SENDER_TOKEN_ACCOUNT>",
  "toTokenAccount": "<RECIPIENT_TOKEN_ACCOUNT>",
  "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
  "amount": "2500000",
  "decimals": 6,
  "uiAmount": "2.5",
  "confirmationStatus": "finalized",
  "transactionIdx": 35,
  "instructionIdx": 1,
  "innerInstructionIdx": 0
}

type 字段精确告诉你发生了什么——transfertransferFeemintburnwrapunwrapchangeAccountOwnerwithdrawWithheldFee——因此你无需从原始程序数据推断行为。

为什么解析 Solana 转账很困难?

Solana 交易中的转账并非单一概念。

它是一个隐藏着多种边界情况的类别,任何一处出错都会破坏你的数据。

SOL vs. WSOL

原生 SOL 和包装 SOL(WSOL)对用户来说看起来是同一种资产,但它们在交易中处于不同位置。

原生 SOL 通过系统账户的交易前后 lamport 余额移动。WSOL 通过代币账户 的 SPL 代币余额移动。

在 Jupiter 上进行交换的用户可能会将 SOL 包装为 WSOL,将 WSOL 交换为 USDC,然后永远不解包——从而留下一个 WSOL 代币账户。

从用户的角度看,他们花费了 SOL。从网络的角度看,存在三笔转账和一笔包装操作。

更糟糕的是,包装本身并非向不同所有者的转账——它是同一钱包将 lamports 转入自己的代币账户。将其计为转账会重复计算用户的活动。

Token-2022 转账费用

Token-2022 引入了 TransferCheckedWithFee,其中发送方的扣款与接收方的入账不匹配。

差额作为费用保留在接收方的代币账户中,日后通过 withdrawWithheldFee 支付给费用授权方。

一个简单的解析器只看到一笔转账就会算错金额。一个仔细的解析器会检测费用扩展,将指令拆分为一笔转账和一笔预扣费用累积,并单独跟踪费用账户。

铸造和销毁

铸造到账户的代币没有发送方。销毁的代币没有接收方。两者在交易前后余额变化中都像是“转账”,但将它们与钱包间的转账混为一谈会扭曲对手方分析——你会看到钱包从零地址“接收”资金并向虚空“发送”资金。

getTransfersByAddress 将这些表示为 mintburn 类型,fromUserAccounttoUserAccount 设置为 null,因此你可以根据构建的内容选择包含或排除它们。

getTransfersByAddress 的优势

getTransfersByAddress 方法接受此前需要客户端拉取并解析整个交易历史才能实现的过滤器。

按铸币搜索

只返回特定代币的转账。

{
  "jsonrpc": "2.0",
  "id": "1",
  "method": "getTransfersByAddress",
  "params": [
    "<WALLET_ADDRESS>",
    { "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" }
  ]
}

按金额搜索

使用 gtgteltlte 比较方式按原始金额过滤。适用于发现大户、忽略灰尘(即金额微不足道的账户)或标记异常活动。

{
  "params": [
    "<WALLET_ADDRESS>",
    {
      "mint": "So11111111111111111111111111111111111111112",
      "filters": {
        "amount": { "gte": 1000000000, "lt": 10000000000 }
      }
    }
  ]
}

按时间搜索

支持以 Unix 时间戳范围的形式进行区块时间过滤。插槽范围适用于需要精确插槽的查询。

{
  "params": [
    "<WALLET_ADDRESS>",
    {
      "filters": {
        "blockTime": { "gte": 1735718400, "lt": 1738396800 }
      }
    }
  ]
}

按对手方搜索

结合 withdirection 参数来查询两个特定钱包之间的转账(任意方向)。

{
  "params": [
    "<WALLET_ADDRESS>",
    {
      "with": "<COUNTERPARTY_WALLET>",
      "direction": "in"
    }
  ]
}

SOL 模式

由于原生 SOL 和 WSOL 在 Solana 上表现不同,但对用户而言通常含义相同,因此 getTransfersByAddress 方法提供了 solMode 参数。

merged(默认)

WSOL 被视为原生 SOL。

包装和解包行被排除,按原生 SOL 铸币查询将同时返回原生 SOL 和 WSOL 转账。

separate

在此模式下,WSOL 作为独立的铸币保留,包装和解包的生命周期行被包含在内,以实现完整的可审计性。

大多数产品用例通常需要 merged。对账、会计和协议级分析通常需要 separate

分页与排序

通过 paginationToken 实现标准游标分页,每页最多 100 条记录。sortOrder 接受 ascdesc

{
  "jsonrpc": "2.0",
  "id": "1",
  "method": "getTransfersByAddress",
  "params": [
    "<WALLET_ADDRESS>",
    { "limit": 50, "paginationToken": "315069220:308:2:1" }
  ]
}

何时使用 getTransfersByAddress

getTransfersByAddressgetTransactionsForAddress 类似,但用途不同。

需求 方法
解析后的代币和 SOL 转账,带过滤器 getTransfersByAddress
完整的交易负载或非转账活动 getTransactionsForAddress
仅签名 使用 transactionDetails: 'signatures'getTransactionsForAddress
实时传输流 LaserStream

开始使用

getTransfersByAddress 方法自 开发者计划 起的所有付费计划中均已提供。每次请求消耗 10 个积分,并且属于标准 RPC 速率限制组 的一部分。

使用你现有的 Helius RPC URL:

const response = await fetch("https://mainnet.helius-rpc.com/?api-key=YOUR_API_KEY", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    jsonrpc: "2.0",
    id: "1",
    method: "getTransfersByAddress",
    params: ["<WALLET_ADDRESS>"]
  })
});

const data = await response.json();
console.log(data.result.data);

阅读 API 参考文档 获取完整的参数和响应详情。

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

0 条评论

请先 登录 后评论
Helius
Helius
https://www.helius.dev/