> ## 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)를 사용하는 에이전트에 대한 모범 사례 및 권장 패턴입니다. 설치 및 시작 방법에 대해서는 [개요](/docs/ko/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 })));
```

### 폴링 대신 웹훅 또는 WebSocket 사용

루프에서 `getTransactionsForAddress`를 폴링하지 마세요. 서버 간 알림을 위해서는 웹훅을, 클라이언트 측 실시간 스트리밍을 위해서는 WebSocket을 사용하세요.

```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. **우선 수수료는 계산 단위당 마이크로람포트 단위입니다** — 람포트가 아닙니다. `getPriorityFeeEstimate`에서 가져온 값은 `SetComputeUnitPrice`에 이미 적합한 단위입니다.

4. **DAS 페이지 매김은 1을 기준으로 시작합니다** — `page: 1`는 첫 번째 페이지이며, `page: 0`가 아닙니다.

5. **`blockTime`는 밀리초가 아닌 유닉스 초입니다** — `blockTime`에 따라 필터링할 때 `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 | 서버 오류            | 지수 백오프를 사용하여 재시도 |
