# eth_getLogs vs Token Transfers API: ERC-20 전송 내역 조회

> Source: https://docs.blockvectra.com/ko/guides/logs-vs-transfers/

지갑 내역 조회나 ERC-20 전송 대사 작업에는 [Token Transfers API](https://blockvectra.com/en/data/transfers/)로 시작하는 것이 좋습니다. 컨트랙트 이벤트 로그가 필요한 경우에는 `eth_getLogs`를 사용하세요. 개발자와 AI 에이전트는 동일한 블록체인 Data API를 통해 인덱싱된 주소 전송 내역을 조회할 수 있습니다. [지갑 자산 가이드](https://docs.blockvectra.com/en/guides/wallet-assets/)는 토큰 잔액, 전송 내역 및 메타데이터를 결합하며, [Data API 레퍼런스](https://docs.blockvectra.com/en/api/data/)에서 요청 파라미터와 응답 스키마를 정의합니다.

## 이 가이드에서 다루는 작업

* 모니터링 또는 로그 백필을 위해 제한된 블록 범위에서 인증된 RPC로 [컨트랙트 이벤트 로그 조회](#querying-logs-with-eth_getlogs).
* 커서 페이지네이션 및 커버리지 확인을 거쳐 블록체인 Data API를 통해 주소 또는 토큰 컨트랙트별로 [인덱싱된 ERC-20 전송 내역 조회](#querying-transfers-with-the-data-api).

## 로그 및 전송을 읽는 두 가지 방식

`eth_getLogs`는 JSON-RPC 메서드입니다. JSON-RPC 엔드포인트를 통해 블록 로그를 반환합니다. Data API는 체인 범위의 두 가지 엔드포인트를 통해 토큰 전송 내역을 제공합니다:

* `GET /{chain}/addresses/{address}/transfers` — 특정 주소와 관련된 전송 내역.
* `GET /{chain}/tokens/{token}/transfers` — 단일 토큰 컨트랙트의 전송 내역.

두 방식 모두 동일한 API key를 사용하며, 메서드 가중치에 따라 CU로 측정됩니다(아래 가중치 참조). 어떤 방식이 적합한지는 데이터의 최신성, 블록 윈도우 필요 여부, 페이지네이션 방식에 따라 달라집니다.

## eth\_getLogs에 적용되는 제한

`eth_getLogs`는 공개 `GET /v1/chains` 응답에 게시되는 체인별 한도의 적용을 받습니다:

* **블록 범위**: `max_logs_block_range`는 단일 `eth_getLogs` 요청이 포함할 수 있는 최대 블록 수입니다. 체인마다 다르므로 코드에 직접 하드코딩하지 말고 `GET /v1/chains`에서 확인하세요(체인 목록은 [지원 체인](https://docs.blockvectra.com/en/chains/) 참조). 범위를 초과하면 JSON-RPC 오류 `-32602 eth_getLogs block range too large`로 거부됩니다(과금되지 않음).
* **노드 동기화**: 체인 노드가 동기화되지 않은 상태에서 `eth_getLogs`는 `-32010`을 반환합니다(과금되지 않음).
* **상태 윈도우**: `GET /v1/chains`에서 `state_window_blocks`로 보고하는 상태 윈도우는 `eth_call`, `eth_getBalance` 같은 상태 조회 메서드에 적용되며, `eth_getLogs`에는 적용되지 않습니다.
* **노드 프루닝(Pruning)**: 블록 및 로그 조회는 상태 윈도우의 제한을 받지 않지만, 노드에 보관된 기록에 의해 제한됩니다. 프루닝되어 보관되지 않은 데이터는 `4444 pruned history unavailable`을 반환합니다(과금되지 않음).

`fromBlock` 및 `toBlock` 필터 필드를 생략하거나 `null`로 지정하면 기본값인 `latest`가 적용됩니다.

HTTP를 통해 `eth_subscribe`를 호출하면 `-32601 method not available`이 반환됩니다. `/v1/chains`에서 `ws`가 `true`인 체인에서는 WebSocket을 통해 `eth_subscribe`를 사용할 수 있으며([지원 체인](https://docs.blockvectra.com/en/chains/) 참조), 그렇지 않은 경우 최신 블록에 대해 `eth_getLogs`를 폴링하세요.

## Data API 전송 엔드포인트가 제공하는 기능

두 엔드포인트는 요구하는 파라미터가 다릅니다:

| 엔드포인트                                        | `standard`                                               | 블록 윈도우                                                                                                                                                                        |
| -------------------------------------------- | -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GET /{chain}/addresses/{address}/transfers` | 필수: `erc20` 또는 `erc721`. `erc1155`는 `422 no_coverage` 반환 | `from_block` 및 `to_block` 모두 필수. 결과는 `(block_number, log_index)` 내림차순으로 정렬됩니다. `direction`(`in`, `out`, `any`; 기본값 `any`)은 방향별로 필터링하며, `token`을 사용하여 특정 컨트랙트로 결과를 제한할 수 있습니다. |
| `GET /{chain}/tokens/{token}/transfers`      | 필수: `erc20`, `erc721`, 또는 `erc1155`                      | `from_block` 및 `to_block`은 선택 사항입니다. `to_block`을 생략하면 `as_of_block`이 기본값으로 사용됩니다. 이보다 큰 `to_block` 또는 `from_block`을 명시하면 `clamp` 대체 없이 `409 not_indexed_yet` 오류가 반환됩니다.       |

### 페이지네이션

두 엔드포인트 모두 키셋 페이지네이션(keyset pagination)을 사용합니다:

* `limit` 기본값은 50입니다. 500을 초과하는 값은 500으로 제한되며, `0`이나 정수가 아닌 값은 `400 bad_request`를 반환합니다.
* `next_cursor`는 다음 페이지가 존재할 때만 표시됩니다. 마지막 페이지에서는 키가 `null`이 아니라 완전히 생략됩니다.
* 다음 페이지를 가져오려면 반환된 값을 변경 없이 `cursor`로 다시 전달하세요. 커서는 이를 발급한 체인, 엔드포인트 및 쿼리 파라미터 조합에 대해서만 유효합니다.

### 커버리지 및 완결성

Data API transfers는 각 체인의 `coverage.from_block`부터 `meta.as_of_block`까지의 과거 토큰 전송을 인덱싱합니다. 이를 지원하는 체인은 [지원 체인](https://docs.blockvectra.com/en/chains/)에서 확인하세요.

각 전송 항목에는 `token`, `standard`, `from`, `to`, `block_number`, `block_timestamp`, `tx_hash`, `tx_index`, `log_index`가 포함됩니다. ERC-20 항목에는 `amount`가 추가되고, ERC-721 항목에는 `token_id`가 추가되며, ERC-1155 항목에는 `operator`, `token_id`, `value`, `batch_index`가 추가됩니다.

## 상황별 권장 방식

| 일반적인 작업           | 권장 방식                                                  | 이유                                                                                                            |
| ----------------- | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------- |
| 최근 수백 개 블록 내의 이벤트 | `eth_getLogs`                                          | 해당 체인의 `max_logs_block_range`를 초과하지 않는 한 단일 요청으로 최근 범위를 조회할 수 있습니다.                                           |
| 특정 주소의 과거 전송 내역   | `GET /{chain}/addresses/{address}/transfers`           | `from_block`/`to_block` 윈도우, `direction` 및 `token` 필터, 커서 페이지네이션을 지원하는 주소 범위 쿼리로, `as_of_block`까지의 결과를 제공합니다. |
| 특정 토큰의 전체 전송 내역   | `GET /{chain}/tokens/{token}/transfers`                | `erc20`, `erc721`, `erc1155`를 지원하는 토큰 컨트랙트 범위 쿼리로, 선택적 윈도우와 전체 결과 세트를 위한 커서 페이지네이션을 제공합니다.                    |
| 새로운 이벤트 실시간 모니터링  | `eth_subscribe` (WebSocket 지원 체인) / `eth_getLogs` (폴링) | 지원되는 체인에서 WebSocket을 통해 newHeads 또는 logs를 구독하거나, 최근 블록 범위를 폴링합니다.                                             |

## eth\_getLogs로 로그 조회하기

**cURL**

```bash
export BLOCKVECTRA_API_KEY=rgw_your_api_key

# fromBlock / toBlock default to latest. Set an explicit recent range to follow
# new events, and keep its span within the chain's max_logs_block_range.
curl -s "https://api.blockvectra.com/v1/robinhood_mainnet" \
  -H 'Content-Type: application/json' \
  -H "x-api-key: $BLOCKVECTRA_API_KEY" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "eth_getLogs",
    "params": [{
      "address": "0x1111111111111111111111111111111111111111",
      "fromBlock": "latest",
      "toBlock": "latest"
    }]
  }'
```


  **TypeScript**

```ts
const res = await fetch("https://api.blockvectra.com/v1/robinhood_mainnet", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "x-api-key": process.env.BLOCKVECTRA_API_KEY!,
  },
  body: JSON.stringify({
    jsonrpc: "2.0",
    id: 1,
    method: "eth_getLogs",
    params: [{
      address: "0x1111111111111111111111111111111111111111",
      fromBlock: "latest",
      toBlock: "latest",
    }],
  }),
});

const { result } = await res.json();
console.log(result);

// npx tsx example.mts
```


  **Python**

```python
import os
import requests

res = requests.post(
    "https://api.blockvectra.com/v1/robinhood_mainnet",
    headers={
        "Content-Type": "application/json",
        "x-api-key": os.environ["BLOCKVECTRA_API_KEY"],
    },
    json={
        "jsonrpc": "2.0",
        "id": 1,
        "method": "eth_getLogs",
        "params": [{
            "address": "0x1111111111111111111111111111111111111111",
            "fromBlock": "latest",
            "toBlock": "latest",
        }],
    },
)
res.raise_for_status()
print(res.json())
```


## Data API로 전송 내역 조회하기

**cURL**

```bash
export BLOCKVECTRA_API_KEY=rgw_your_api_key

# from_block / to_block are optional here; omitting to_block defaults to as_of_block.
curl -s "https://api.blockvectra.com/v1/data/robinhood_mainnet/tokens/0x1111111111111111111111111111111111111111/transfers?standard=erc20" \
  -H "x-api-key: $BLOCKVECTRA_API_KEY"
```


  **TypeScript**

```ts
let cursor: string | undefined;

do {
  const url = new URL(
    "https://api.blockvectra.com/v1/data/robinhood_mainnet/tokens/0x1111111111111111111111111111111111111111/transfers",
  );
  url.searchParams.set("standard", "erc20");
  if (cursor) url.searchParams.set("cursor", cursor);

  const res = await fetch(url, {
    headers: { "x-api-key": process.env.BLOCKVECTRA_API_KEY! },
  });
  const body = await res.json();
  console.log(body.data);
  cursor = body.next_cursor; // absent on the last page
} while (cursor);

// npx tsx example.mts
```


  **Python**

```python
import os
import requests

url = "https://api.blockvectra.com/v1/data/robinhood_mainnet/tokens/0x1111111111111111111111111111111111111111/transfers"
cursor = None

while True:
    params = {"standard": "erc20"}
    if cursor:
        params["cursor"] = cursor
    res = requests.get(
        url,
        params=params,
        headers={"x-api-key": os.environ["BLOCKVECTRA_API_KEY"]},
    )
    res.raise_for_status()
    body = res.json()
    print(body["data"])
    cursor = body.get("next_cursor")  # absent on the last page
    if not cursor:
        break
```


주소별로 조회하려면 `from_block`과 `to_block`이 필수입니다:

```bash
# clamp=true truncates a too-wide window, or a to_block above as_of_block,
# instead of returning 409.
curl -s "https://api.blockvectra.com/v1/data/robinhood_mainnet/addresses/0x1111111111111111111111111111111111111111/transfers?standard=erc20&from_block=0&to_block=73000000&direction=any&clamp=true" \
  -H "x-api-key: $BLOCKVECTRA_API_KEY"
```

## 호출당 CU

모든 메서드는 CU 가중치에 따라 과금됩니다. 아래 가중치는 플랫폼 플랜 API에서 읽어옵니다:

**호출당 CU 가중치**

| 메서드 | 호출당 CU |
| --- | --- |
| `eth_getLogs` | 30 |
| `data.address_transfers` | 25 |
| `data.token_transfers` | 25 |

현재 요금 및 충전 옵션은 [요금 페이지](https://blockvectra.com/en/pricing/)를 참조하세요.

## 다음 단계

* [데이터셋 디렉터리 둘러보기](https://blockvectra.com/en/data/): BlockVectra가 인덱싱하는 모든 데이터셋 확인.
* [무료 플랜 및 요금 확인](https://blockvectra.com/en/pricing/#free): 계정에 포함된 혜택 확인.
* [콘솔 로그인](https://console.blockvectra.com/login/?next=%2Fkeys%2F): API key 생성.
