跳转到主要内容

Documentation Index

Fetch the complete documentation index at: https://www.helius.dev/docs/llms.txt

Use this file to discover all available pages before exploring further.

Helius 独家功能 - getTransfersByAddress 可通过 Helius RPC 节点使用,并非标准 Solana RPC 的一部分。此端点需要 开发者计划 或更高级别,并且每次请求消耗 10 个积分。
数据保留 - 目前仅提供最近 1 年的转账历史记录。

概览

getTransfersByAddress 返回钱包地址的人类可读的代币和原生 SOL 转账对象。它专注于转账活动,因此返回的是简化的转账记录而不是完整的交易负载。 当您需要钱包转账历史记录用于支付、投资组合活动、代币移动分析、余额对账或特定交易对方的转账监控时,可以使用 getTransfersByAddress。您可以通过 mint、区块时间、金额、槽、方向和交易对方过滤转账历史。当您需要完整的交易数据、仅签名历史或非转账活动时,可以使用 getTransactionsForAddress

关键特性

解析的转账对象

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

对账准备就绪

模型化 SOL、WSOL、Token-2022 费用、铸造、销毁和账户所有权更改,以便余额可以精确对账。

Mint、时间和金额过滤器

按 mint 地址、区块时间范围或原始金额范围缩小转账历史。

交易对方过滤器

使用 withdirection 按发件人或收件人过滤转账。

准确性和对账

getTransfersByAddress 专为需要可靠转账历史记录的应用程序而构建,用于分类账、支付跟踪、投资组合活动和余额对账。API 返回的是规范化的转账对象,包含解析的所有者账户、代币账户、铸造、原始金额、小数、UI 金额、指令位置和确认状态,而不是返回原始交易负载并将每个边缘情况留给您的解析器处理。 响应显式建模了使 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);

请求参数

address 参数是钱包所有者地址,而不是关联的代币账户 (ATA)。API 查找该钱包拥有的代币账户的转移活动。
address
string
必填
Base58 编码的所有者钱包地址以查询转移。传递钱包所有者地址,而不是关联的代币账户 (ATA)。
config
object
可选配置对象,用于筛选、分页、承诺、排序和 SOL/WSOL 行为。
with
string
通过交易对手地址进行筛选。仅返回与此地址有关的转移。
direction
string
默认值:"any"
根据相对于 address 的转移方向进行筛选。
  • in: 接收到的转移 address
  • out: 发送的转移 address
  • any: 输入和输出转移
mint
string
根据代币铸币地址过滤。使用 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
sortOrder
string
默认值:"desc"
结果排序。
  • desc: 最新优先
  • asc: 最早优先

过滤器

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

转账类型

type 字段标识每行代表的转账行为。
类型描述fromUserAccounttoUserAccount
transfer标准代币或 SOL 在两个钱包之间的转账。发送者接收者
mint向钱包铸造新代币。null接收者
burn代币被永久销毁。发送者null
wrapSOL 包装为 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, 以及 feeAmountfeeUiAmount 字段始终
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始终

响应字段详情

  • fromUserAccounttoUserAccount 始终存在。当一侧不存在时,值为 null
  • fromTokenAccounttoTokenAccount 仅在代币账户端点对该行有意义时才包含。在原生 SOL 转账中,它们完全省略。
  • 铸币转账是单侧的:fromUserAccountnull,并且它们只能作为收件人的入站转账返回。
  • 代币燃烧转账是单侧的:toUserAccountnull,并且它们只能作为燃烧所有者的出站转账返回。

SOL 和 wSOL 的行为

SOL 在 Solana 上有两种形式,常常在真实用户活动中一起出现:
  • 原生 SOL 是链的原生资产。它直接在钱包或账户中以 lamports 存在。一 SOL 等于 1,000,000,000 lamports。
  • 包装 SOL (WSOL,通常写作 wSOL) 是 SOL 的一个 SPL 代币表示。它使用 WSOL 铸币 So11111111111111111111111111111111111111112 并存在于一个代币账户中,类似 USDC 或其他任何 SPL 代币。
用户和应用通常在需要 SOL 表现为 SPL 代币时将其包装,通常用于 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 账户关闭,unwrap 的记录代表返回为 SOL 的剩余 WSOL 代币余额。从已关闭的代币账户退款的租金作为单独的本地 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
}

限制

  • 失败的交易不包含在 V1 中。
  • 从余额变动中推断出的隐藏 SOL mouvements 在 V1 中不支持。
  • harvestWithheldTokensToMint 在 V1 中不支持,因为它不表示收集的金额。
  • 中间账户流不减少。如果交易通过中间账户转移资金,将返回底层转账记录。

示例

按 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`);

响应

代码占位符 121649f78fe93814_END