跳转到主要内容

概述

getTransfersByAddress 是一个 Helius 独有的 RPC 方法,用于返回钱包地址的解析、可读的代币和本地 SOL 转账对象。它不属于标准 Solana RPC。 它专注于转账活动,因此返回简明的转账记录而非完整的交易数据载荷。每条记录都经过标准化处理,包含解析的所有者和代币账户、发行、原始金额、小数、UI 金额、指令位置和确认状态,从而无需重新实现 Solana 代币解析即可对余额变动进行调和。 此方法需要 开发者计划 或更高版本,每次请求需耗费 10 个积分。

解析的转账对象

返回包含解析账户、金额、小数和转账类型的人类可读转账记录。

调和就绪

模型化 SOL、WSOL、Token-2022 费用、发行、销毁和账户所有者变更,以便准确调和余额。

发行、时间和金额过滤器

可以通过发行地址、区块时间范围或原始金额范围来缩小转账历史。

交易对手过滤器

通过 withdirection 按发送者或接收者过滤转账。

何时使用

在需要以下情况时使用 getTransfersByAddress
  • 支付或转账监控的钱包转账历史
  • 投资组合活动和代币流动分析
  • 可以信赖的账本和会计对账余额
  • 特定交易对手的转账报告(谁发送或接收了什么)
  • 标准化的 SOL/WSOL、Token-2022 费用、发行和燃烧处理,不需要编写解析器
当需要完整交易数据、仅限签名的历史或非转账活动时,使用 getTransactionsForAddress。一种常见模式是通过此处的转账记录分页,然后使用批量 getTransaction 调用获取基础完整交易(参见获取转账行的完整交易)。

精确性和对账

getTransfersByAddress 为需要可靠的分类账、支付跟踪、投资组合活动和余额对账的应用构建。API 返回的是规范化的转账对象,而不是返回原始交易有效负载并将每个极端案例留给你的解析器处理。 响应明确地建模了通常使 Solana 历史难以对账的转账案例:
  • 标准 SPL 代币和原生 SOL 转账。
  • Token-2022 转账附带扣留费用,表现为普通的 transfer 行,具有单独的费用字段。
  • 铸造和销毁,表现为具有 null 发送者或接收者的转账。
  • SOL 的包装和展开行为,默认模式设计为避免嘈杂的生命周期行。
  • 通过 SetAuthority 更改代币账户所有者。
  • Token-2022 扣留费用的提取。
  • 中介账户流动,返回为基础转账记录而不是被合并为猜测的净移动。
对于支持的可见转账事件,这使您可以在不重新实现 Solana 代币解析逻辑的情况下对余额进行对账。已知的排除项,例如仅从余额变化推断的隐藏 SOL 流动,在限制中指出。

快速开始

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: ["86xCnPeV69n6t3DnyGvkKobf9FdN2H9oiVDdaMpo2MMY"]
  })
});

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

请求参数

传递钱包所有者地址,而不是关联的代币账户 (ATA)。API 会查找由该钱包拥有的代币账户的转账活动。
address
string
必填
Base58 编码的所有者钱包地址,用于查询转账。传递钱包所有者地址,而不是关联的代币账户 (ATA)。
config
object
可选的配置对象,用于过滤、分页、承诺、排序和 SOL/WSOL 行为。
with
string
通过对手地址进行过滤。仅返回与该地址有关的转账。
direction
string
默认值:"any"
按相对于address的转账方向进行筛选。
  • in:由address接收的转账
  • out:由address发出的转账
  • any:传入和传出的转账
mint
string
按令牌mint地址筛选。使用So11111111111111111111111111111111111111111表示本机SOL,So11111111111111111111111111111111111111112表示WSOL。
solMode
string
默认值:"merged"
控制本机SOL和WSOL的表示方式。
  • merged:WSOL被视为本机SOL。包装和打开周期行被排除,WSOL铸造值被重写为本机SOL铸造。
  • separate:WSOL保留为不同的铸造,并包括包装和打开周期行。
filters
object
额度、区块时间和插槽的其他筛选器。
limit
number
默认值:"100"
返回的最大转账数。范围:1到100。
paginationToken
string
用于分页的上一个响应的光标。
commitment
string
默认值:"finalized"
数据承诺级别。
  • finalized
  • confirmed
minContextSlot
number
请求可评估的最小插槽
sortOrder
string
默认值:"desc"
结果排序。
  • desc:最新最先
  • asc:最旧最先

响应

{
  "jsonrpc": "2.0",
  "id": "1",
  "result": {
    "data": [
      {
        "signature": "5GEX7Q3X5Q8yJGbKYoR7mtzQmG8tpoEwzjPgqVmn3y5xg3yKwqXcDdN5YVcc9V6vA4TuH5iM6FHRVhTxvz4AX2zG",
        "slot": 315073428,
        "blockTime": 1736159420,
        "type": "transfer",
        "fromUserAccount": "7hPhaUpydpvm8wtiS3k4LPZKUmivQRs7YQmpE1hFshHx",
        "toUserAccount": "86xCnPeV69n6t3DnyGvkKobf9FdN2H9oiVDdaMpo2MMY",
        "fromTokenAccount": "HcvK3EJ74iM9g11cUgsaPvLSrhCvCwcrWxBNd87LsC1x",
        "toTokenAccount": "CBcYniR9G9CN3zGMnwNE4SWbqkYWvCFVreEob9xHnQCY",
        "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
        "amount": "2500000",
        "decimals": 6,
        "uiAmount": "2.5",
        "confirmationStatus": "finalized",
        "transactionIdx": 35,
        "instructionIdx": 1,
        "innerInstructionIdx": 0
      }
    ],
    "paginationToken": "315073428:35:1:0:splTransfer"
  }
}

响应字段详情

  • fromUserAccounttoUserAccount始终存在。当一方不存在时,值为null
  • fromTokenAccounttoTokenAccount仅在标记账户端点对行有意义时包含。对于本机SOL转账,它们完全被省略。
  • 铸造转账是单方面的:fromUserAccountnull,它们只能作为接收者的传入转账返回。
  • 燃烧转账是单方面的:toUserAccountnull,它们只能作为燃烧所有者的传出转账返回。

过滤器

使用比较过滤器进行数值范围查询。所有比较字段都是可选的且可以组合使用。
{
  "gt": 1000000,
  "gte": 1000000,
  "lt": 1000000000,
  "lte": 1000000000
}
过滤器类型描述
amountComparisonFilter原始转账金额,不是UI金额。
blockTimeComparisonFilter区块时间戳,单位为Unix秒。
slotComparisonFilter槽号。

转账类型

type字段标识每一行代表的转账行为。
类型描述fromUserAccounttoUserAccount
transfer两个钱包之间的标准代币或SOL转账。发送者接收者
mint新代币铸造到某个钱包。null接收者
burn代币被永久销毁。发送者null
wrap将SOL包装成WSOL。默认在solMode: "merged"中排除。null拥有者
unwrapWSOL解包装回本地SOL,或从关闭代币账户中恢复租金。默认在solMode: "merged"中排除。拥有者null或lamport目的地
changeOwner使用SetAuthority更改代币账户所有权。原所有者新所有者
withdrawWithheldFee从铸币或账户中收取的Token-2022扣留费用。null费用接收者

转账类型和指令

转账类型涵盖的指令默认可见性
transferSystemProgram::Transfer, SystemProgram::TransferWithSeed, SystemProgram::WithdrawNonceAccount, SystemProgram::CreateAccount, SystemProgram::CreateAccountWithSeed, SystemProgram::CreateAccountAllowPrefund始终
transferToken::Transfer, Token::TransferChecked, Token-2022::Transfer, Token-2022::TransferChecked始终
transferToken-2022::TransferCheckedWithFee, with feeAmount and feeUiAmount fields始终
mintToken::MintTo, Token::MintToChecked始终
burnToken::Burn, Token::BurnChecked始终
wrapToken::SyncNative, Token::InitializeAccount, Token::InitializeAccount2, Token::InitializeAccount3用于WSOL生命周期处理时solMode: "separate"
unwrapToken::CloseAccount在余额大于零的WSOL上solMode: "separate"
unwrapToken::CloseAccount非WSOL代币账户的租金恢复,或零代币余额的WSOL账户solMode: "separate"
changeOwnerToken::SetAuthority(AccountOwner)始终
withdrawWithheldFeeToken-2022::WithdrawWithheldTokensFromMint, Token-2022::WithdrawWithheldTokensFromAccounts始终

SOL 和 wSOL 行为

SOL 在 Solana 上以两种形式存在,这两种形式通常在用户实际活动中同时出现:
  • 原生 SOL 是链的原生资产。它以 lamports 形式直接存在于钱包或账户中。一个 SOL 是 1,000,000,000 lamports。
  • 封装 SOL (WSOL,通常写作 wSOL) 是 SOL 的 SPL 代币表示。它使用 WSOL 铸币 So11111111111111111111111111111111111111112 并像 USDC 或任何其他 SPL 代币一样存在于一个代币账户中。
当需要 SOL 像 SPL 代币一样运作时,用户和应用程序通常会封装 SOL,通常用于 DeFi、交换、基于代币账户的会计或仅接受 SPL 代币的程序接口。封装通常使用原生 SOL 为一个代币账户提供资金,并将其同步为 WSOL。拆封则会关闭 WSOL 代币账户,并将 SOL 返回到一个 lamport 目的地。 这种生命周期可能会造成混乱的历史记录,如果你试图回答一个简单的问题,比如“有多少 SOL 在这个钱包和另一个人之间移动了?”封装或拆封通常会在同一所有者控制的账户之间移动 SOL。如果这些生命周期行被默认显示为普通转账,应用程序可能会重复计算活动或将内部记账显示为外部支付。 默认情况下,getTransfersByAddress 使用 solMode: "merged"。在这种模式下:
  • 当通过 So11111111111111111111111111111111111111111 查询时,原生 SOL 和 WSOL 被视为一个 SOL 资产。
  • WSOL 转账行被归一化为原生 SOL 铸币,以便 SOL 计账记录更易于调整。
  • 封装和拆封的生命周期行被排除,因为它们通常代表同一所有者控制的账户之间的移动,而不是对其他用户的支付。
  • SOL 和 WSOL 在不同所有者之间的转账仍然被表示为转账。
  • CloseAccount 恢复的租金在返回关闭账户生命周期行时被表示为一个原生 SOL unwrap 行。
在需要 WSOL 作为独立 SPL 代币铸币时或想检查包装和解包生命周期记录时使用 solMode: "separate"。在这种模式下,WSOL 保持铸币 So11111111111111111111111111111111111111112,并且包装/解包记录与 type: "wrap"type: "unwrap" 一起返回。 对于 solMode: "separate" 中的 WSOL 账户关闭,WSOL 铸币的 unwrap 记录表示剩余的 WSOL 代币余额以 SOL 形式返回。从已关闭代币账户中退还的租金以单独的本机 SOL unwrap 行返回。

Token-2022 转账费用

Token-2022 TransferCheckedWithFee 指令表示为一个传输记录,带有 type: "transfer"。目的地金额在 amount 返回;扣留的费用详细信息在 feeAmountfeeUiAmount 中返回。 对于带有费用的转账,来源被扣款 amount + feeAmount,而目的地被记入 amount
{
  "signature": "WcvF2eFxArpqRJySzDuiP6Xw8BMprWytMpYCxk2ExBt5C1WyxWzDWcCWXW8iKQVYR9AtdQxPE1uu1SMEZvbbhdr",
  "slot": 409259683,
  "blockTime": 1774635210,
  "type": "transfer",
  "fromUserAccount": "5aZZ4duJUKiMsJN9vRsoAn4SDX7agvKu7Q3QdFWRfWze",
  "toUserAccount": "FESSvM1cVUchc13XQY8e41oeYxMnyqQNYVZwoznfJsTo",
  "fromTokenAccount": "3VUYGjYktCzNhDVymNb3Z1iHewtfPFRvdA53qSWuxdXy",
  "toTokenAccount": "51cEFBA1virMuPqHXvNGs8FKKTMqeEVKzugv1hqPU2Zc",
  "mint": "CKfatsPMUf8SkiURsDXs7eK6GWb4Jsd6UDbs7twMCWxo",
  "amount": "48650000",
  "decimals": 5,
  "uiAmount": "486.5",
  "feeAmount": "13450000",
  "feeUiAmount": "134.5",
  "confirmationStatus": "finalized",
  "transactionIdx": 1315,
  "instructionIdx": 4,
  "innerInstructionIdx": 0
}

例子

通过 USDC 进行过滤

{
  "jsonrpc": "2.0",
  "id": "1",
  "method": "getTransfersByAddress",
  "params": [
    "86xCnPeV69n6t3DnyGvkKobf9FdN2H9oiVDdaMpo2MMY",
    {
      "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
    }
  ]
}

来自发送者的传入转账

{
  "jsonrpc": "2.0",
  "id": "1",
  "method": "getTransfersByAddress",
  "params": [
    "86xCnPeV69n6t3DnyGvkKobf9FdN2H9oiVDdaMpo2MMY",
    {
      "with": "7hPhaUpydpvm8wtiS3k4LPZKUmivQRs7YQmpE1hFshHx",
      "direction": "in"
    }
  ]
}

金额和时间范围

{
  "jsonrpc": "2.0",
  "id": "1",
  "method": "getTransfersByAddress",
  "params": [
    "86xCnPeV69n6t3DnyGvkKobf9FdN2H9oiVDdaMpo2MMY",
    {
      "mint": "So11111111111111111111111111111111111111112",
      "filters": {
        "amount": {
          "gte": 1000000000,
          "lt": 10000000000
        },
        "blockTime": {
          "gte": 1735718400,
          "lt": 1738396800
        }
      }
    }
  ]
}

分页请求

{
  "jsonrpc": "2.0",
  "id": "1",
  "method": "getTransfersByAddress",
  "params": [
    "86xCnPeV69n6t3DnyGvkKobf9FdN2H9oiVDdaMpo2MMY",
    {
      "limit": 50,
      "paginationToken": "315069220:308:2:1:splTransfer"
    }
  ]
}

获取转账行的完整交易

getTransfersByAddress 返回已解析的转账行,而不是完整的交易负载。如果需要每次转账的完整交易,首先通过传输分页,根据 signature 去重,然后使用批量 getTransaction 调用获取完整交易。 getTransfersByAddress 不能跨多个所有者地址进行批量操作。一次查询一个所有者地址,然后按签名批量处理结果的 getTransaction 请求。单个交易可以发出多个转账行,因此在获取交易之前始终去重签名。
const API_KEY = "YOUR_API_KEY";
const RPC_URL = `https://mainnet.helius-rpc.com/?api-key=${API_KEY}`;
const OWNER_ADDRESS = "86xCnPeV69n6t3DnyGvkKobf9FdN2H9oiVDdaMpo2MMY";

async function rpc(method, params) {
  const response = await fetch(RPC_URL, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: "1",
      method,
      params
    })
  });

  const body = await response.json();
  if (body.error) {
    throw new Error(body.error.message);
  }
  return body.result;
}

async function getAllTransfers(address) {
  const transfers = [];
  let paginationToken;

  do {
    const result = await rpc("getTransfersByAddress", [
      address,
      {
        limit: 100,
        ...(paginationToken ? { paginationToken } : {})
      }
    ]);

    transfers.push(...result.data);
    paginationToken = result.paginationToken;
  } while (paginationToken);

  return transfers;
}

async function getTransactionsInBatches(signatures, batchSize = 100) {
  const transactions = [];

  for (let i = 0; i < signatures.length; i += batchSize) {
    const batch = signatures.slice(i, i + batchSize).map((signature, index) => ({
      jsonrpc: "2.0",
      id: `${i + index}`,
      method: "getTransaction",
      params: [
        signature,
        {
          encoding: "jsonParsed",
          maxSupportedTransactionVersion: 0
        }
      ]
    }));

    const response = await fetch(RPC_URL, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(batch)
    });

    const results = await response.json();
    for (const item of results) {
      if (item.error) {
        throw new Error(item.error.message);
      }
      transactions.push(item.result);
    }
  }

  return transactions;
}

const transfers = await getAllTransfers(OWNER_ADDRESS);
const signatures = [...new Set(transfers.map((transfer) => transfer.signature))];
const transactions = await getTransactionsInBatches(signatures);

console.log(`Fetched ${transfers.length} transfer rows`);
console.log(`Fetched ${transactions.length} unique transactions`);

限制

  • V1 中不包括失败的交易。
  • V1 中不支持仅从余额变化推断的隐藏 SOL 流动。
  • harvestWithheldTokensToMint 在 V1 中不支持,因为它不指示收集的金额。
  • 中间账户流动不减少。如果交易通过中间账户移动资金,将返回基础转账记录。
  • 不能跨多个所有者地址批处理。一次查询一个所有者。

下一步

getTransactionsForAddress

提供完整的交易历史记录,支持过滤、排序和代币账户。

API 参考

getTransfersByAddress 的完整请求和响应模式。

索引指南

将转移数据回填并同步到您自己的索引中。

历史数据概览

比较所有 Solana 历史数据方法。