> ## 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.

# TypeScript SDK 最佳实践

> 针对使用 Helius TypeScript SDK 的 AI 代理推荐的模式。涵盖交易历史、发送交易、批处理、实时数据、分页、增量获取、常见错误和错误处理。

为使用 [Helius TypeScript SDK](https://github.com/helius-labs/helius-sdk) 的代理提供最佳实践和推荐模式。有关安装和入门，请参阅[概览](/zh/agents/typescript-sdk)。

## 代理的建议

### 使用 `getTransactionsForAddress` 代替两步查找

`getTransactionsForAddress` 将签名查找和交易获取合并为一个调用，并带有服务器端过滤。它支持时间/插槽范围、代币账户过滤和分页。

```typescript theme={"system"}
// GOOD: Single call, server-side filtering
const txs = await helius.getTransactionsForAddress([
  "address",
  {
    transactionDetails: "full",
    limit: 100,
    filters: {
      tokenAccounts: "balanceChanged",
      blockTime: { gte: Math.floor(Date.now() / 1000) - 86400 },
    },
  },
]);

// BAD: Two calls, client-side filtering, no token account support
const sigs = await helius.raw.getSignaturesForAddress(address).send();
const txs = await Promise.all(sigs.map(s => helius.raw.getTransaction(s.signature).send()));
```

### 使用 `sendSmartTransaction` 进行标准发送

它会自动模拟、估算计算单元、获取优先费用并确认。不要手动构建 ComputeBudget 指令——SDK 会自动添加。

```typescript theme={"system"}
const sig = await helius.tx.sendSmartTransaction({
  instructions: [yourInstruction],
  signers: [walletSigner],
  commitment: "confirmed",
  priorityFeeCap: 100_000,   // Optional: cap fees in microlamports/CU
  bufferPct: 0.1,            // 10% compute unit headroom (default)
});
```

### 使用 Helius Sender 实现超低延迟

对于时间敏感的交易（套利、狙击、清算），使用 `sendTransactionWithSender`。它通过 Helius 的多区域基础设施和 Jito 路由。

```typescript theme={"system"}
const sig = await helius.tx.sendTransactionWithSender({
  instructions: [yourInstruction],
  signers: [walletSigner],
  region: "US_EAST",          // Default, US_SLC, US_EAST, EU_WEST, EU_CENTRAL, EU_NORTH, AP_SINGAPORE, AP_TOKYO
  swqosOnly: true,            // Route through SWQOS only (lower tip requirement)
  pollTimeoutMs: 60_000,
  pollIntervalMs: 2_000,
});
```

### 对多个资产使用 `getAssetBatch`

在获取多个资产时，将它们批量处理。不要在循环中调用 `getAsset`。

```typescript theme={"system"}
// GOOD: Single request
const assets = await helius.getAssetBatch({
  ids: ["mint1", "mint2", "mint3"],
  options: { showFungible: true, showCollectionMetadata: true },
});

// BAD: N requests
const assets = await Promise.all(mints.map(id => helius.getAsset({ id })));
```

### 使用 webhooks 或 WebSockets 代替轮询

不要在循环中轮询 `getTransactionsForAddress`。使用 webhooks 进行服务器到服务器的通知或 WebSockets 进行实时客户端流。

```typescript theme={"system"}
// Webhook: server receives POST on matching transactions
const webhook = await helius.webhooks.create({
  webhookURL: "https://your-server.com/webhook",
  webhookType: "enhanced",
  transactionTypes: ["TRANSFER", "NFT_SALE", "SWAP"],
  accountAddresses: ["address_to_monitor"],
  authHeader: "Bearer your-secret",
});

// WebSocket: stream logs in real-time
const req = await helius.ws.logsNotifications({ mentions: ["address"] });
const stream = await req.subscribe({ abortSignal: controller.signal });
for await (const log of stream) {
  console.log(log);
}
```

## 分页

SDK 根据方法使用不同的分页策略。

### 基于令牌/游标（RPC V2 方法）

```typescript theme={"system"}
// getTransactionsForAddress uses paginationToken
let paginationToken = null;
const allTxs = [];
do {
  const result = await helius.getTransactionsForAddress([
    "address",
    { limit: 100, paginationToken },
  ]);
  allTxs.push(...result.data);
  paginationToken = result.paginationToken;
} while (paginationToken);

// getProgramAccountsV2 uses paginationKey
let paginationKey = null;
do {
  const result = await helius.getProgramAccountsV2([
    programId,
    { limit: 1000, paginationKey },
  ]);
  // process result.accounts
  paginationKey = result.paginationKey;
} while (paginationKey);
```

### 基于页面（DAS API）

```typescript theme={"system"}
let page = 1;
const allAssets = [];
while (true) {
  const result = await helius.getAssetsByOwner({ ownerAddress: "...", page, limit: 1000 });
  allAssets.push(...result.items);
  if (result.items.length < 1000) break;
  page++;
}
```

## `tokenAccounts` 过滤器

当查询 `getTransactionsForAddress` 时，`tokenAccounts` 过滤器控制是否包括代币账户活动：

| 值                  | 行为           | 使用时机                          |
| ------------------ | ------------ | ----------------------------- |
| 省略 / `"none"`      | 仅涉及地址的交易     | 只关心 SOL 转账和程序调用时使用            |
| `"balanceChanged"` | 还包括改变余额的代币交易 | **推荐大多数代理使用** — 显示代币发送/接收而无噪音 |
| `"all"`            | 包括所有代币账户交易   | 需要完整代币活动时使用（可能返回大量结果）         |

## `changedSinceSlot` — 增量账户获取

`changedSinceSlot` 仅返回在给定插槽后修改的账户。对于同步或索引工作流很有用。由 `getProgramAccountsV2`、`getTokenAccountsByOwnerV2`、`getAccountInfo`、`getMultipleAccounts`、`getProgramAccounts` 和 `getTokenAccountsByOwner` 支持。

```typescript theme={"system"}
// First fetch: get all accounts
const baseline = await helius.getProgramAccountsV2([programId, { limit: 10_000 }]);
const lastSlot = currentSlot;

// Later: only get accounts that changed since your last fetch
const updates = await helius.getProgramAccountsV2([
  programId,
  { limit: 10_000, changedSinceSlot: lastSlot },
]);
```

## 常见错误

1. **`transactionDetails: "full"` 不是默认设置** — 默认情况下，`getTransactionsForAddress` 仅返回签名。设置 `transactionDetails: "full"` 以获取完整的交易数据。

2. **不要使用 `sendSmartTransaction` 添加 ComputeBudget 指令** — SDK 会自动添加。自己添加会导致重复指令和交易失败。

3. **优先费用单位是每计算单元的微 lamports** — 不是 lamports。`getPriorityFeeEstimate` 的值已经是 `SetComputeUnitPrice` 的正确单位。

4. **DAS 分页从 1 开始** — `page: 1` 是第一页，而不是 `page: 0`。

5. **`blockTime` 是 Unix 秒，不是毫秒** — 过滤时使用 `Math.floor(Date.now() / 1000)`。

6. **`getAsset` 默认隐藏可替代代币** — 传递 `options: { showFungible: true }` 以包括它们。

7. **WebSocket 流需要清理** — 使用 AbortController 信号并在完成时调用 `helius.ws.close()` 以避免连接泄漏。

## 错误处理和重试

SDK抛出包含HTTP状态码的原生`Error`对象在消息字符串中（例如，`"API error (429): ..."`）。错误对象上没有`.status`属性，因此状态检测需要解析消息。

```typescript theme={"system"}
async function withRetry<T>(fn: () => Promise<T>, maxRetries = 3): Promise<T> {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      const msg = error instanceof Error ? error.message : "";
      const status = msg.match(/\b(\d{3})\b/)?.[1];
      const retryable = status === "429" || (status && status.startsWith("5"));
      if (!retryable || attempt === maxRetries) throw error;
      await new Promise(r => setTimeout(r, 1000 * 2 ** attempt));
    }
  }
  throw new Error("Unreachable");
}
```

| 状态  | 含义          | 操作       |
| --- | ----------- | -------- |
| 401 | API密钥无效或缺失  | 检查API密钥  |
| 429 | 速率限制或超出信用额度 | 后退并重试    |
| 5xx | 服务器错误       | 使用指数回退重试 |
