# eth_getLogs và Token Transfers API: lịch sử chuyển token ERC-20

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

Đối với lịch sử ví hoặc đối chiếu chuyển token ERC-20, hãy bắt đầu với [Token Transfers API](https://blockvectra.com/en/data/transfers/). Sử dụng `eth_getLogs` khi bạn cần log sự kiện của hợp đồng. Nhà phát triển và AI Agent có thể truy vấn các chuyển giao địa chỉ đã lập chỉ mục thông qua cùng một API dữ liệu blockchain. [Hướng dẫn tài sản ví](https://docs.blockvectra.com/en/guides/wallet-assets/) kết hợp số dư token, lịch sử chuyển giao và siêu dữ liệu; [tài liệu tham khảo Data API](https://docs.blockvectra.com/en/api/data/) xác định các tham số yêu cầu và lược đồ phản hồi.

## Các nhiệm vụ hướng dẫn này giúp bạn hoàn thành

* [Truy vấn log sự kiện hợp đồng](#querying-logs-with-eth_getlogs) thông qua RPC có xác thực trong các khoảng khối có giới hạn để giám sát hoặc backfill log.
* [Truy vấn lịch sử chuyển token ERC-20 đã lập chỉ mục](#querying-transfers-with-the-data-api) thông qua API dữ liệu blockchain theo địa chỉ hoặc hợp đồng token, với phân trang bằng cursor và kiểm tra độ bao phủ.

## Hai cách đọc log và chuyển giao

`eth_getLogs` là một phương thức JSON-RPC: nó trả về log khối thông qua endpoint JSON-RPC. Data API hiển thị lịch sử chuyển token thông qua hai endpoint theo phạm vi chuỗi:

* `GET /{chain}/addresses/{address}/transfers` — các chuyển giao liên quan đến một địa chỉ.
* `GET /{chain}/tokens/{token}/transfers` — các chuyển giao cho một hợp đồng token đơn lẻ.

Cả hai đều sử dụng cùng một API key và được đo lường bằng CU theo trọng số phương thức (xem bảng trọng số bên dưới). Việc lựa chọn phương thức nào phù hợp tùy thuộc vào độ mới của dữ liệu, việc bạn có cần một cửa sổ khối hay không, và cách bạn phân trang.

## Các giới hạn áp dụng cho eth\_getLogs

`eth_getLogs` bị ràng buộc bởi các giới hạn theo từng chuỗi mà phản hồi công khai `GET /v1/chains` công bố:

* **Khoảng khối**: `max_logs_block_range` là số lượng khối tối đa mà một yêu cầu `eth_getLogs` đơn lẻ có thể kéo dài. Nó khác nhau tùy theo từng chuỗi — hãy đọc giá trị này từ `GET /v1/chains` (các chuỗi được liệt kê trên trang [Chuỗi được hỗ trợ](https://docs.blockvectra.com/en/chains/)) thay vì hardcode. Khoảng rộng hơn sẽ bị từ chối với lỗi JSON-RPC `-32602 eth_getLogs block range too large` (không tính phí).
* **Đồng bộ hóa nút**: trong khi nút của chuỗi chưa được đồng bộ, `eth_getLogs` trả về `-32010` (không tính phí).
* **Cửa sổ trạng thái**: cửa sổ trạng thái mà `GET /v1/chains` báo cáo dưới dạng `state_window_blocks` áp dụng cho các phương thức đọc trạng thái như `eth_call` và `eth_getBalance`, không áp dụng cho `eth_getLogs`.
* **Cắt tỉa dữ liệu nút**: các truy vấn đọc khối và log không bị giới hạn bởi cửa sổ trạng thái, nhưng chúng bị giới hạn bởi lịch sử được lưu giữ của nút. Dữ liệu đã bị cắt tỉa trả về `4444 pruned history unavailable` (không tính phí).

Khi các trường bộ lọc `fromBlock` và `toBlock` bị bỏ qua hoặc là `null`, chúng mặc định là `latest`.

Gọi `eth_subscribe` qua HTTP trả về `-32601 method not available`. Trên các chuỗi có `ws` là `true` trong `/v1/chains`, `eth_subscribe` khả dụng qua WebSocket (xem [Chuỗi được hỗ trợ](https://docs.blockvectra.com/en/chains/)); nếu không, hãy polling `eth_getLogs` qua các khối mới nhất.

## Những gì các endpoint chuyển giao Data API cung cấp

Hai endpoint yêu cầu các tham số khác nhau:

| Endpoint                                     | `standard`                                                          | Cửa sổ khối                                                                                                                                                                                                                                     |
| -------------------------------------------- | ------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GET /{chain}/addresses/{address}/transfers` | Bắt buộc: `erc20` hoặc `erc721`. `erc1155` trả về `422 no_coverage` | Cả `from_block` và `to_block` đều bắt buộc. Kết quả được sắp xếp theo `(block_number, log_index)` giảm dần. `direction` (`in`, `out`, hoặc `any`; mặc định `any`) lọc theo hướng, và `token` có thể tùy chọn giới hạn kết quả cho một hợp đồng. |
| `GET /{chain}/tokens/{token}/transfers`      | Bắt buộc: `erc20`, `erc721`, hoặc `erc1155`                         | `from_block` và `to_block` là tùy chọn. Khi thiếu `to_block`, mặc định là `as_of_block`; một `to_block` hoặc `from_block` rõ ràng nằm trên mức này là lỗi cứng `409 not_indexed_yet`, không thể dùng `clamp` để tránh.                          |

### Phân trang

Cả hai endpoint đều được phân trang theo keyset:

* `limit` mặc định là 50; các giá trị trên 500 được cắt về 500, và `0` hoặc không phải số nguyên trả về `400 bad_request`.
* `next_cursor` chỉ xuất hiện khi còn trang tiếp theo. Trên trang cuối cùng, key hoàn toàn không xuất hiện, không bao giờ là `null`.
* Truyền giá trị trả về dưới dạng `cursor`, không thay đổi, để lấy trang tiếp theo. Một cursor chỉ hợp lệ cho chuỗi, endpoint và các tham số truy vấn đã phát hành nó.

### Độ bao phủ và tính bất biến sau cùng

Các chuyển giao của Data API lập chỉ mục các giao dịch chuyển token lịch sử từ `coverage.from_block` của mỗi chuỗi cho đến `meta.as_of_block`. Xem [Chuỗi được hỗ trợ](https://docs.blockvectra.com/en/chains/) để biết các chuỗi nào cung cấp tính năng này.

Mỗi mục chuyển giao chứa `token`, `standard`, `from`, `to`, `block_number`, `block_timestamp`, `tx_hash`, `tx_index`, và `log_index`. Các mục ERC-20 bổ sung `amount`; các mục ERC-721 bổ sung `token_id`; các mục ERC-1155 bổ sung `operator`, `token_id`, `value`, và `batch_index`.

## Nên sử dụng phương thức nào

| Tác vụ thông thường                          | Lựa chọn phù hợp hơn                                            | Lý do                                                                                                                                                               |
| -------------------------------------------- | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Các sự kiện trong vài trăm khối gần đây nhất | `eth_getLogs`                                                   | Một yêu cầu có thể bao phủ một khoảng gần đây miễn là nó không vượt quá `max_logs_block_range` của chuỗi đó.                                                        |
| Lịch sử chuyển giao của một địa chỉ          | `GET /{chain}/addresses/{address}/transfers`                    | Truy vấn theo phạm vi địa chỉ với cửa sổ `from_block`/`to_block`, các bộ lọc `direction` và `token`, cùng phân trang cursor; kết quả phục vụ lên đến `as_of_block`. |
| Tất cả các chuyển giao của một token         | `GET /{chain}/tokens/{token}/transfers`                         | Truy vấn theo phạm vi hợp đồng token bao gồm `erc20`, `erc721`, và `erc1155`, với cửa sổ tùy chọn và phân trang cursor cho toàn bộ tập kết quả.                     |
| Giám sát trực tiếp các sự kiện mới           | `eth_subscribe` (các chuỗi WebSocket) / `eth_getLogs` (polling) | Đăng ký nhận head hoặc log mới qua WebSocket khi được hỗ trợ, hoặc polling các khoảng khối gần đây.                                                                 |

## Truy vấn log với 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())
```


## Truy vấn chuyển giao với 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
```


Để truy vấn theo địa chỉ thay thế, `from_block` và `to_block` là bắt buộc:

```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 cho mỗi lệnh gọi

Mỗi phương thức được tính phí theo trọng số CU của nó. Các trọng số bên dưới được đọc từ API gói dịch vụ của nền tảng:

**Trọng số CU mỗi lệnh gọi**

| Phương thức | CU mỗi lệnh gọi |
| --- | --- |
| `eth_getLogs` | 30 |
| `data.address_transfers` | 25 |
| `data.token_transfers` | 25 |

Để biết giá hiện tại và các tùy chọn nạp tiền, hãy xem [Trang bảng giá](https://blockvectra.com/en/pricing/).

## Các bước tiếp theo

* [Khám phá danh mục bộ dữ liệu](https://blockvectra.com/en/data/) để xem mọi bộ dữ liệu mà BlockVectra lập chỉ mục.
* [Xem gói miễn phí và bảng giá](https://blockvectra.com/en/pricing/#free) để kiểm tra những gì tài khoản của bạn bao gồm.
* [Đăng nhập vào console](https://console.blockvectra.com/login/?next=%2Fkeys%2F) để tạo API key.
