# eth_getLogs เทียบกับ Token Transfers API: ประวัติการโอน ERC-20

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

สำหรับประวัติกระเป๋าเงินหรือการกระทบยอดการโอน ERC-20 ให้เริ่มต้นด้วย [Token Transfers API](https://blockvectra.com/en/data/transfers/) ใช้ `eth_getLogs` เมื่อคุณต้องการ event log ของสัญญา นักพัฒนาและ AI Agent สามารถคิวรีการโอนของแอดเดรสที่ทำดัชนีไว้ผ่าน blockchain data API เดียวกันได้ [คู่มือสินทรัพย์กระเป๋าเงิน](https://docs.blockvectra.com/en/guides/wallet-assets/) รวมยอดคงเหลือของโทเค็น, ประวัติการโอน และเมทาดาตาไว้ด้วยกัน; [ข้อมูลอ้างอิง Data API](https://docs.blockvectra.com/en/api/data/) ระบุพารามิเตอร์คำขอและ schema การตอบกลับ

## งานที่คู่มือนี้ช่วยให้คุณทำสำเร็จ

* [คิวรี event log ของสัญญา](#querying-logs-with-eth_getlogs) ผ่าน RPC ที่มีการยืนยันตัวตนในช่วงบล็อกที่จำกัดขอบเขต สำหรับการตรวจสอบหรือการดึงข้อมูล log ย้อนหลัง
* [คิวรีประวัติการโอน ERC-20 ที่ทำดัชนีไว้](#querying-transfers-with-the-data-api) ผ่าน blockchain data API ตามแอดเดรสหรือสัญญาโทเค็น พร้อมการแบ่งหน้าด้วยเคอร์เซอร์และการตรวจสอบความครอบคลุม

## สองวิธีในการอ่าน log และการโอน

`eth_getLogs` เป็นเมธอด JSON-RPC: ส่งกลับ block log ผ่านทาง JSON-RPC endpoint ส่วน Data API เปิดเผยประวัติการโอนโทเค็นผ่านสอง endpoints ในขอบเขตของเชน:

* `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`
* **การ prune ของโหนด**: การอ่านบล็อกและ log ไม่ได้ถูกจำกัดโดยกรอบเวลาสถานะ แต่จะถูกจำกัดโดยประวัติที่โหนดเก็บรักษาไว้ ข้อมูลที่ถูก prune ไปแล้วจะส่งกลับ `4444 pruned history unavailable` (ไม่คิดค่าบริการ)

เมื่อละเว้นฟิลด์ตัวกรอง `fromBlock` และ `toBlock` หรือเป็น `null` ค่าเริ่มต้นจะเป็น `latest`

การเรียก `eth_subscribe` ผ่าน HTTP จะส่งกลับ `-32601 method not available` บนเชนที่ `ws` เป็น `true` ใน `/v1/chains` เมธอด `eth_subscribe` จะพร้อมใช้งานผ่าน WebSocket (ดู [เชนที่รองรับ](https://docs.blockvectra.com/en/chains/)); มิฉะนั้น ให้โพลล์ `eth_getLogs` ในบล็อกใหม่ล่าสุด

## สิ่งที่ Transfers Endpoints ของ Data API ให้บริการ

ทั้งสอง endpoints ต้องการพารามิเตอร์ที่แตกต่างกัน:

| Endpoint                                     | `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` ที่สูงกว่านั้นอย่างชัดเจนจะส่งกลับ `409 not_indexed_yet` ทันที โดยไม่มีตัวเลือก `clamp`          |

### การแบ่งหน้า

ทั้งสอง endpoints ใช้การแบ่งหน้าแบบ keyset:

* `limit` มีค่าเริ่มต้นเป็น 50; ค่าที่เกิน 500 จะถูกตัดทอนเหลือ 500 และค่า `0` หรือค่าที่ไม่ใช่จำนวนเต็มจะส่งกลับ `400 bad_request`
* `next_cursor` จะปรากฏขึ้นเมื่อมีหน้าถัดไปเท่านั้น ในหน้าสุดท้ายคีย์นี้จะไม่มีอยู่เลย และจะไม่เป็น `null` เด็ดขาด
* ส่งค่าที่ได้รับกลับมาเป็น `cursor` โดยไม่เปลี่ยนแปลงค่า เพื่อดึงข้อมูลหน้าถัดไป เคอร์เซอร์จะใช้ได้เฉพาะกับเชน, endpoint และพารามิเตอร์การคิวรีที่ออกให้เท่านั้น

### ความครอบคลุมและความเป็นที่สิ้นสุด

การโอนของ Data API จะทำดัชนีประวัติการโอนโทเค็นตั้งแต่ `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` (การโพลล์) | สมัครรับข้อมูล head หรือ log ใหม่ผ่าน WebSocket ในจุดที่รองรับ หรือโพลล์ช่วงบล็อกล่าสุด                                                                          |

## การคิวรี log ด้วย 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
