跳转到主要内容
Helius 独家功能 - getTransactionsForAddress 仅通过 Helius RPC 节点提供,并不属于标准的 Solana RPC。此端点需要 开发者计划 或更高计划,每次请求耗费 100 积分。返回 100 笔完整交易或 1,000 个签名。

概述

getTransactionsForAddress 提供强大的交易历史查询,具备高级过滤功能、灵活的排序和高效的分页。

主要功能

灵活排序

按时间顺序排序(最早优先)或倒序(最新优先)

高级过滤

按时间范围、插槽、签名和交易状态过滤

完整交易数据

一次调用获取完整交易详情

代币账户

包括地址关联的代币账户交易

常见用例

此方法在多个场景中特别有用。代币发行分析有助于跟踪新项目的首次铸币交易和早期代币持有者。钱包资金历史允许您识别特定地址的资金来源和交易模式。交易分析让您可以按成功/失败状态进行过滤,专注于已完成的交易并排除失败的尝试。 该 API 还支持 审计与合规 工作流,通过状态过滤为特定时间段生成交易报告。分析仪表板可以利用历史重播功能构建全面的交易分析。最后,投资组合跟踪应用程序可以访问完整的成功交易历史,用于 DeFi 投资组合管理。

关联代币账户

在 Solana 上,您的钱包实际上并不直接持有代币。相反,您的钱包拥有代币账户,这些代币账户持有您的代币。 当有人向您发送 USDC 时,它会进入您的 USDC 代币账户,而不是您的主钱包地址。
此方法独特之处在于它允许您查询完整的代币历史。您可以查询钱包的完整历史,包括关联代币地址 (ATA)。 本地 RPC 方法如 getSignaturesForAddress 不包括 ATA。 tokenAccounts 过滤器让您可以控制此行为:
  • none(默认):仅返回直接引用钱包地址的交易。当您只关心直接的钱包交互时使用此选项。
  • balanceChanged(推荐):返回引用钱包地址或修改钱包所拥有的代币账户余额的交易。这可以过滤掉垃圾邮件和不相关的操作,如费用收集或委托行为,为您提供清晰的有意义钱包活动视图。
  • all:返回引用钱包地址或任何由钱包拥有的代币账户的所有交易。
遗留交易的限制:此功能不支持 2022 年 12 月之前的交易。 它依赖于 Solana 在 slot 111,491,819 中引入的一项功能(代币转账元数据)。 如果您需要支持这些遗留交易,请使用我们的解决方法

网络支持

网络支持保留期
主网无限
Devnet2 周
测试网不适用

快速开始

1

获取您的 API 密钥

Helius Dashboard 获取您的 API 密钥。
2

使用高级功能查询

获取在两个日期之间,按时间顺序排序的钱包的所有成功交易:
// Get successful transactions between Jan 1-31, 2025 in chronological order
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: 'getTransactionsForAddress',
    params: [
      'YOUR_ADDRESS_HERE',
      {
        transactionDetails: 'full',
        sortOrder: 'asc',
        limit: 100,
        filters: {
          blockTime: {
            gte: 1735689600,   // Jan 1, 2025
            lte: 1738368000    // Jan 31, 2025
          },
          status: 'succeeded',  // Only successful transactions
          tokenAccounts: 'balanceChanged' // Include associated token accounts
        }
      }
    ]
  })
});

const data = await response.json();
console.log('Successful transactions in January:', data.result.data);
3

了解参数

此示例显示了关键特性:
  • transactionDetails: 设置为 'full' 以在一次调用中获取完整的交易数据
  • sortOrder: 使用 'asc' 按时间顺序排序(最旧优先)或 'desc' 按最新优先排序
  • filters.blockTime: 使用 gte(大于或等于)和 lte(小于或等于)设置时间范围
  • filters.status: 仅筛选 'succeeded''failed' 的交易
  • filters.tokenAccounts: 包含关联代币账户的转账、铸造和销毁

请求参数

address
string
必填
要查询交易历史的账户的 Base-58 编码公钥
transactionDetails
string
默认值:"signatures"
要返回的交易详细级别:
  • signatures: 基本签名信息(较快)
  • full: 完整的交易数据(消除对 getTransaction 调用的需求,要求限制 ≤ 100)
sortOrder
string
默认值:"desc"
结果的排序顺序:
  • desc: 最新优先(默认)
  • asc: 最旧优先(按时间顺序,适合历史分析)
limit
number
默认值:"1000"
最大返回交易数:
  • transactionDetails: "signatures" 时最多 1000
  • transactionDetails: "full" 时最多 100
paginationToken
string
来自上一个响应的分页令牌(格式:"slot:position"
commitment
string
默认值:"finalized"
承诺级别:finalizedconfirmed。不支持 processed 承诺。
filters
object
用于缩小结果范围的高级过滤选项。
filters.slot
object
使用比较运算符筛选插槽号:gte, gt, lte, lt示例:{ "slot": { "gte": 1000, "lte": 2000 } }
filters.blockTime
object
使用比较运算符按 Unix 时间戳筛选:gte, gt, lte, lt, eq示例:{ "blockTime": { "gte": 1640995200, "lte": 1641081600 } }
filters.signature
object
使用比较运算符按交易签名筛选:gte, gt, lte, lt示例:{ "signature": { "lt": "SIGNATURE_STRING" } }
filters.status
string
根据交易成功/失败状态进行过滤:
  • succeeded: 仅成功交易
  • failed: 仅失败交易
  • any: 成功和失败均包含(默认)
示例: { "status": "succeeded" }
filters.tokenAccounts
string
默认值:"none"
对相关代币账户的交易进行过滤:
  • none: 仅返回引用提供地址的交易(默认)
  • balanceChanged: 返回引用提供地址或修改由提供地址持有的代币账户余额的交易(推荐)
  • all: 返回引用提供地址或任何由提供地址持有的代币账户的交易
示例: { "tokenAccounts": "balanceChanged" }
encoding
string
交易数据的编码格式(仅适用于 transactionDetails: "full")。与 getTransaction API 相同。选项: json, jsonParsed, base64, base58
maxSupportedTransactionVersion
number
设置要返回的最大交易版本。如果省略,仅返回旧版交易。设置为 0 以包含所有版本的交易。
minContextSlot
number
请求可以评估的最小槽位

过滤器

使用过滤器时,可以使用比较运算符针对 slot, blockTime, 或 signature,加上特殊的 statustokenAccounts 过滤器。

比较运算符

这些运算符像数据库查询一样工作,使您能够精确控制数据范围。
运算符全名描述示例
gte大于或等于包含 ≥ 指定值的值slot: { gte: 100 }
gt大于包含 > 指定值的值blockTime: { gt: 1641081600 }
lte小于或等于包含 ≤ 指定值的值slot: { lte: 2000 }
lt小于包含 < 指定值的值blockTime: { lt: 1641168000 }
eq等于仅包含完全等于(仅限 blockTime)的值blockTime: { eq: 1641081600 }

枚举过滤器

过滤器描述
status按成功/失败过滤交易succeeded, failed, 或 any
tokenAccounts过滤相关令牌账户的交易none, balanceChanged, 或 all
组合过滤器示例:
// Time range with successful transactions only
"filters": {
  "blockTime": {
    "gte": 1640995200,
    "lte": 1641081600
  },
  "status": "succeeded"
}

// Slot range
"filters": {
  "slot": {
    "gte": 1000,
    "lte": 2000
  }
}

// Only failed transactions
"filters": {
  "status": "failed"
}

响应格式

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "data": [
      {
        "signature": "5h6xBEauJ3PK6SWCZ1PGjBvj8vDdWG3KpwATGy1ARAXFSDwt8GFXM7W5Ncn16wmqokgpiKRLuS83KUxyZyv2sUYv",
        "slot": 1054,
        "transactionIndex": 42,
        "err": null,
        "memo": null,
        "blockTime": 1641038400,
        "confirmationStatus": "finalized"
      }
    ],
    "paginationToken": "1055:5"
  }
}

响应字段

字段类型描述
signaturestring交易签名(base-58 编码)。仅在签名模式下。
slotnumber包含此交易的区块的插槽。
transactionIndexnumber交易在其区块内的零基索引。用于交易排序和区块重建。
blockTimenumber | null估计的生产时间,以 Unix 时间戳表示(自纪元开始的秒数)。
errobject | null如果交易失败则为错误,否则为 null。仅在签名模式下。
memostring | null交易相关的备忘录。仅在签名模式下。
confirmationStatusstring交易的集群确认状态。仅在签名模式下。
transactionobject完整交易数据。仅在完整模式下。
metaobject交易状态元数据。仅在完整模式下。
paginationTokenstring | null用于获取下一页的令牌,如果没有更多结果则为 null。
transactionIndex 字段是 getTransactionsForAddress 独有的。其他类似的端点,如 getSignaturesForAddressgetTransactiongetTransactions 都不包含此字段。

实用示例

基于时间的分析

生成每月交易报告:
// Get all successful transactions for January 2025
const startTime = Math.floor(new Date('2025-01-01').getTime() / 1000);
const endTime = Math.floor(new Date('2025-02-01').getTime() / 1000);

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "getTransactionsForAddress",
  "params": [
    "WALLET_OR_PROGRAM_ADDRESS",
    {
      "transactionDetails": "signatures",
      "filters": {
        "blockTime": {
          "gte": startTime,
          "lt": endTime
        },
        "status": "succeeded"
      },
      "limit": 1000
    }
  ]
}
分析流程:
// Calculate daily transaction volume
const dailyStats = {};
response.result.data.forEach(tx => {
  const date = new Date(tx.blockTime * 1000).toISOString().split('T')[0];
  dailyStats[date] = (dailyStats[date] || 0) + 1;
});

console.log('Daily Transaction Counts:', dailyStats);

代币铸造创建

查找特定代币的铸造创建交易:
{
  "jsonrpc": "2.0",
  "id": "find-first-mints",
  "method": "getTransactionsForAddress",
  "params": [
    MINT_ADDRESS, // Token mint address
    {
      "encoding": "jsonParsed",
      "maxSupportedTransactionVersion": 0,
      "sortOrder": "asc",  // Chronological order from the beginning
      "limit": 10,
      "transactionDetails": "full"
    }
  ]
}
对于流动资金池创建,查询池地址:
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "getTransactionsForAddress", 
  "params": [
    "POOL_ADDRESS_HERE", // Raydium/Meteora pool address
    {
      "transactionDetails": "full",
      "sortOrder": "asc",  // First transaction is usually pool creation
      "limit": 1
    }
  ]
}
用例:查找代币铸造或流动资金池创建的确切时刻,包括创建者地址和初始参数。

资金交易

查找谁为特定地址提供资金:
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "getTransactionsForAddress",
  "params": [
    "TARGET_WALLET_ADDRESS",
    {
      "transactionDetails": "full",
      "sortOrder": "asc",  // Oldest first
      "limit": 10
    }
  ]
}
然后分析交易数据以找出SOL转账:
response.result.data.forEach(tx => {
  // Look for SOL transfers in preBalances/postBalances
  const balanceChanges = tx.meta.preBalances.map((pre, index) => 
    tx.meta.postBalances[index] - pre
  );
  
  // Positive balance change = incoming SOL
  balanceChanges.forEach((change, index) => {
    if (change > 0) {
      console.log(`Received ${change} lamports from ${tx.transaction.message.accountKeys[index]}`);
    }
  });
});
前几笔交易通常会揭示资金来源,并帮助识别相关地址或资金模式。

分页

当交易数量超过您的限制时,使用响应中的paginationToken获取下一页。令牌是一个简单的字符串,格式为"slot:position",指示API从何处继续。

如何分页

使用每次响应的分页令牌获取下一页:
// First request
let paginationToken = null;
let allTransactions = [];

const getNextPage = async (paginationToken = null) => {
  const params = [
    'ADDRESS',
    {
      transactionDetails: 'signatures',
      limit: 100,
      ...(paginationToken && { paginationToken })
    }
  ];

  const response = await fetch(rpcUrl, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      jsonrpc: '2.0',
      id: 1,
      method: 'getTransactionsForAddress',
      params
    })
  });

  const data = await response.json();
  return data.result;
};

// Paginate through all results
do {
  const result = await getNextPage(paginationToken);
  allTransactions.push(...result.data);
  paginationToken = result.paginationToken;
  
  console.log(`Fetched ${result.data.length} transactions, total: ${allTransactions.length}`);
} while (paginationToken);

多个地址

在单个请求中无法查询多个地址。要获取多个地址的交易,请在相同的时间或插槽窗口内查询每个地址,然后合并并排序:
const addresses = ['Address1...', 'Address2...', 'Address3...'];

// Query all addresses in parallel with slot filter
const results = await Promise.all(
  addresses.map(address => 
    fetch(rpcUrl, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        jsonrpc: '2.0',
        id: 1,
        method: 'getTransactionsForAddress',
        params: [address, {
          sortOrder: 'desc',
          filters: { slot: { gt: 250000000 } }
        }]
      })
    }).then(r => r.json())
  )
);

// Merge and sort by slot
const allTransactions = results
  .flatMap(r => r.result.data)
  .sort((a, b) => b.slot - a.slot);
对于较大的历史扫描,请遍历时间或插槽窗口(例如,每次1000个插槽)并重复此模式。
每个地址查询都算作单独的API请求(每个地址100个积分)。

最佳实践

性能

为了获得最佳性能,当您不需要完整的交易数据时,请使用transactionDetails: "signatures"。实施合理的页面大小以获得更好的响应时间,并考虑按时间范围或特定插槽过滤以进行更有针对性的查询。

过滤

从广泛的过滤器开始,逐步缩小范围以找到所需的数据。在分析和报告工作流程中使用基于时间的过滤器。您可以结合多个过滤器来进行针对特定交易类型或时间段的精确查询。

分页

当需要稍后恢复大型查询时,请存储分页键。监控分页深度以进行性能规划,并在需要按时间顺序重播历史事件的情况下使用升序。

错误处理

使用指数退避策略优雅地处理速率限制。始终在发出请求之前验证地址,并在适当的情况下缓存结果以减少API使用量并提高应用程序性能。

与getSignaturesForAddress有什么不同?

如果您熟悉标准的getSignaturesForAddress方法,这里是关键区别:

在一次调用中获取完整交易

使用getSignaturesForAddress,需要两个步骤:
// Step 1: Get signatures
const signatures = await connection.getSignaturesForAddress(address, { limit: 100 });

// Step 2: Get transaction details (100 additional calls!)
const transactions = await Promise.all(
  signatures.map(sig => connection.getTransaction(sig.signature))
);
使用getTransactionsForAddress,只需一次调用:
const response = await fetch(heliusRpcUrl, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    id: 1,
    method: 'getTransactionsForAddress',
    params: [
      address,
      {
        transactionDetails: 'full',
        limit: 100
      }
    ]
  })
});

一次调用获取令牌历史

使用 getSignaturesForAddress,您需要先调用 getTokenAccountsByOwner,然后查询每个令牌账户:
// OLD WAY (with getSignaturesForAddress)
// Step 1: Get all token accounts owned by this wallet
const tokenAccounts = await connection.getTokenAccountsByOwner(
  new PublicKey(walletAddress),
  { programId: TOKEN_PROGRAM_ID }
);

// Step 2: Fetch signatures for the wallet itself
const walletSignatures = await connection.getSignaturesForAddress(
  new PublicKey(walletAddress),
  { limit: 1000 }
);

// Step 3: Fetch signatures for EVERY token account (this is the painful part)
const tokenAccountSignatures = await Promise.all(
  tokenAccounts.value.map(async (account) => {
    return connection.getSignaturesForAddress(
      account.pubkey,
      { limit: 1000 }
    );
  })
);

// Step 4: Merge all results together
const allSignatures = [
  ...walletSignatures,
  ...tokenAccountSignatures.flat()
];

// Step 5: Deduplicate (many transactions touch multiple accounts)
const seen = new Set();
const uniqueSignatures = allSignatures.filter((sig) => {
  if (seen.has(sig.signature)) {
    return false;
  }
  seen.add(sig.signature);
  return true;
});

// Step 6: Sort chronologically
const sortedSignatures = uniqueSignatures.sort(
  (a, b) => a.slot - b.slot
);

return sortedSignatures;
使用 getTransactionsForAddress,您只需要设置 filters.tokenAccounts
// NEW WAY (with getTransactionsForAddress)
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: "helius-example",
    method: "getTransactionsForAddress",
    params: [
      walletAddress,
      {
        filters: {
          tokenAccounts: "all"
        }
        sortOrder: "asc",
        limit: 100

      }
    ]
  })
});

const { result } = await response.json();
return result;

附加功能

按时间顺序排序

使用 sortOrder: 'asc' 从旧到新排序交易

基于时间的过滤

使用 blockTime 过滤器按时间范围过滤

状态过滤

使用 status 过滤器仅获取成功或失败的交易

更简单的分页

使用 paginationToken 而不是令人困惑的 before/until 签名

不支持的地址

路由到旧档案系统

对这些地址的请求被路由到我们的旧档案系统。
AddressName
Stake11111111111111111111111111111111111111Stake Program
StakeConfig11111111111111111111111111111111Stake Config
Sysvar1111111111111111111111111111111111111Sysvar Owner
AddressLookupTab1e1111111111111111111111111Address Lookup Table
BPFLoaderUpgradeab1e11111111111111111111111BPF Loader Upgradeable

插槽扫描回退

对这些地址的请求被转发到我们的新档案系统,并且可以通过插槽扫描方法(最多100个插槽)进行查询。但是,这些数据没有被索引。
AddressName
11111111111111111111111111111111System Program
ComputeBudget111111111111111111111111111111Compute Budget
MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHrMemo Program
Vote111111111111111111111111111111111111111Vote Program

is_reserved_address

请求被转发到我们的新档案系统,然而数据未被索引,查询返回为空。
AddressName
BPFLoader1111111111111111111111111111111111BPF Loader (deprecated)
BPFLoader2111111111111111111111111111111111BPF Loader
Config1111111111111111111111111111111111111Config Program
Ed25519SigVerify111111111111111111111111111Ed25519 Program
Feature111111111111111111111111111111111111Feature Program
KeccakSecp256k11111111111111111111111111111Secp256k1 Program
LoaderV411111111111111111111111111111111111Loader V4
NativeLoader1111111111111111111111111111111Native Loader
SysvarC1ock11111111111111111111111111111111Clock Sysvar
SysvarEpochSchedu1e111111111111111111111111Epoch Schedule Sysvar
SysvarFees111111111111111111111111111111111Fees Sysvar
Sysvar1nstructions1111111111111111111111111Instructions Sysvar
SysvarRecentB1ockHashes11111111111111111111Recent Blockhashes Sysvar
SysvarRent111111111111111111111111111111111Rent Sysvar
SysvarRewards111111111111111111111111111111Rewards Sysvar
SysvarS1otHashes111111111111111111111111111Slot Hashes Sysvar
SysvarS1otHistory11111111111111111111111111Slot History Sysvar
SysvarStakeHistory1111111111111111111111111Stake History Sysvar
SysvarEpochRewards11111111111111111111111111Epoch Rewards Sysvar
SysvarLastRestartS1ot1111111111111111111111Last Restart Slot Sysvar

解决方法:历史代币账户发现

对于在槽位111,491,819之前有代币账户活动的地址,由于代币余额元数据中的owner字段尚不存在,tokenAccounts筛选器无法确定所有权。要获得完整结果,可以通过解析早期交易指令手动发现这些代币账户,然后对每一个代币账户并行查询gTFA。
const HELIUS_RPC = "https://mainnet.helius-rpc.com/?api-key=YOUR_API_KEY";
const OWNER_CUTOFF_SLOT = 111_491_819;

async function rpcCall(method, params) {
  const res = await fetch(HELIUS_RPC, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ jsonrpc: "2.0", id: "1", method, params }),
  });
  const json = await res.json();
  if (json.error) throw new Error(json.error.message);
  return json.result;
}

// Step 1: Discover token accounts owned by the address before the cutoff slot
// by parsing initializeAccount instructions and transfer authorities.
async function discoverHistoricalTokenAccounts(address) {
  const tokenAccounts = new Set();
  let paginationToken = null;

  do {
    const result = await rpcCall("getTransactionsForAddress", [
      address,
      {
        transactionDetails: "full",
        encoding: "jsonParsed",
        maxSupportedTransactionVersion: 0,
        sortOrder: "asc",
        limit: 100,
        filters: { slot: { lt: OWNER_CUTOFF_SLOT } },
        ...(paginationToken && { paginationToken }),
      },
    ]);
    if (!result?.data?.length) break;

    for (const entry of result.data) {
      const tx = entry.transaction;
      const meta = entry.meta;
      if (!tx || !meta) continue;

      const allInstructions = [
        ...(tx.message?.instructions ?? []),
        ...(meta.innerInstructions ?? []).flatMap((inner) => inner.instructions ?? []),
      ];

      for (const ix of allInstructions) {
        // AToken program "create" instruction
        if (ix.program === "spl-associated-token-account") {
          if (ix.parsed?.type === "create" && ix.parsed.info?.wallet === address && ix.parsed.info?.account) {
            tokenAccounts.add(ix.parsed.info.account);
          }
          continue;
        }

        if (ix.program !== "spl-token" && ix.program !== "spl-token-2022") continue;
        const type = ix.parsed?.type;
        const info = ix.parsed?.info;

        // Token account initialization
        if (type === "initializeAccount" || type === "initializeAccount2" || type === "initializeAccount3") {
          if (info?.owner === address && info?.account) tokenAccounts.add(info.account);
        }

        // Transfers where our address is the authority (source account is ours)
        if (type === "transfer" || type === "transferChecked") {
          if (info?.authority === address && info?.source) tokenAccounts.add(info.source);
        }
      }
    }
    paginationToken = result.paginationToken;
  } while (paginationToken);

  return Array.from(tokenAccounts);
}

// Step 2: Fetch all signatures for an address with pagination
async function fetchAllSignatures(address, filters) {
  const allSignatures = [];
  let paginationToken = null;

  do {
    const result = await rpcCall("getTransactionsForAddress", [
      address,
      {
        transactionDetails: "signatures",
        sortOrder: "asc",
        limit: 1000,
        ...(filters && { filters }),
        ...(paginationToken && { paginationToken }),
      },
    ]);
    if (!result?.data?.length) break;
    allSignatures.push(...result.data);
    paginationToken = result.paginationToken;
  } while (paginationToken);

  return allSignatures;
}

// Step 3: Get complete history by combining tokenAccounts:"all" with
// individual queries for historical token accounts
async function getCompleteHistory(address) {
  const historicalAccounts = await discoverHistoricalTokenAccounts(address);

  if (historicalAccounts.length === 0) {
    return fetchAllSignatures(address, { tokenAccounts: "all" });
  }

  // Query main address with tokenAccounts:"all" + each historical account in parallel
  const results = await Promise.all([
    fetchAllSignatures(address, { tokenAccounts: "all" }),
    ...historicalAccounts.map((addr) => fetchAllSignatures(addr)),
  ]);

  // Merge and deduplicate by signature
  const seen = new Set();
  const merged = [];
  for (const batch of results) {
    for (const tx of batch) {
      if (!seen.has(tx.signature)) {
        seen.add(tx.signature);
        merged.push(tx);
      }
    }
  }
  return merged.sort((a, b) => a.slot - b.slot);
}

支持与社区