API ยอดคงเหลือโทเค็นของกระเป๋าเงิน: สินทรัพย์ ERC-20 และประวัติการโอน

สร้างหน้าสินทรัพย์ของกระเป๋าเงินด้วยยอดคงเหลือโทเค็น ERC-20 ที่ไม่เป็นศูนย์, ประวัติการโอนโทเค็น และเมทาดาตาแบบแบตช์ ตรวจสอบความครอบคลุมของเชน, แบ่งหน้าผลลัพธ์ และปรับสเกลจำนวนเต็มด้วย decimals

สร้างหน้าสินทรัพย์ของกระเป๋าเงินด้วย API ข้อมูลกระเป๋าเงินบล็อกเชน: ใช้ Token Balances API สำหรับการถือครอง ERC-20 ที่ไม่เป็นศูนย์ และใช้ Token Transfers API สำหรับประวัติของกระเป๋าเงิน ทั้งนักพัฒนาและ AI agent ต่างใช้คำขอที่ผ่านการยืนยันตัวตนแบบเดียวกัน ก่อนสืบค้น ให้อ่าน GET /v1/status และตรวจสอบ data_features และ data_status ของเชนที่เลือก; ความครอบคลุมของยอดคงเหลือจะแตกต่างกันไปตามแต่ละเชน ดูพารามิเตอร์คำขอและสคีมาการตอบกลับได้ใน เอกสารอ้างอิง Data API

งานที่คู่มือนี้จะช่วยคุณดำเนินการ

ข้อมูลสามประเภทที่หน้าสินทรัพย์ของกระเป๋าเงินต้องการ

หน้าสินทรัพย์ของกระเป๋าเงินสามารถแสดงยอดคงเหลือโทเค็น ERC-20, ประวัติการโอนโทเค็น และเมทาดาตาของโทเค็นของแอดเดรสได้ โดย Data API มี endpoint สำหรับแต่ละรายการ:

  • ยอดคงเหลือ: GET /{chain}/addresses/{address}/balances ส่งคืนยอดคงเหลือ ERC-20 ที่ไม่เป็นศูนย์ของแอดเดรส เรียงลำดับตามแอดเดรส token จากน้อยไปมาก พร้อมทั้งระบุ symbol และ decimals ของโทเค็นหากมีข้อมูล แอดเดรสที่ไม่มีการถือครองโทเค็นจะส่งคืน 200 พร้อม data: []
  • การโอน: GET /{chain}/addresses/{address}/transfers ส่งคืนการโอนโทเค็นที่เกี่ยวข้องกับแอดเดรสภายในหน้าต่างบล็อกที่จำเป็น เรียงลำดับตาม (block_number, log_index) จากมากไปน้อย
  • เมทาดาตาของโทเค็น: GET /{chain}/tokens/{token} อ่านชื่อ, สัญลักษณ์, decimals และอุปทานรวมของหนึ่งโทเค็นตามแอดเดรสสัญญา; POST /{chain}/tokens:batch อ่านเมทาดาตาเดียวกันนี้สำหรับสูงสุด 100 แอดเดรสในคำขอเดียว

ทั้งสามรายการใช้ https://api.blockvectra.com/v1/data เป็น base URL และใช้ส่วนหัวคำขอ x-api-key โดยใช้ robinhood_mainnet เป็นเชนตัวอย่าง ทั้งหมดอยู่ภายใต้ความสามารถ balances, transfers และ token_metadata ตามลำดับ; สำหรับเชนที่ให้บริการแต่ละความสามารถ โปรดดูหน้า เชนที่รองรับ บนเชนที่ไม่มีความสามารถดังกล่าว endpoint จะส่งคืน 422 no_coverage

คำขอที่ 1: ยอดคงเหลือของแอดเดรส

endpoint นี้รับพารามิเตอร์น้อยกว่า จึงเหมาะเป็นคำขอแรกสำหรับหน้าสินทรัพย์:

  • {chain} (พารามิเตอร์พาธ, จำเป็น): ตัวระบุเชน ซึ่งเป็นค่า chain ของรายการใน GET /chains (ตัวอย่างเช่น robinhood_mainnet) การจับคู่จะตรงกันทุกตัวอักษรและคำนึงถึงตัวพิมพ์ใหญ่-เล็ก; ไม่ยอมรับนามแฝงหรือ chain ID แบบตัวเลข
  • {address} (พารามิเตอร์พาธ, จำเป็น): แอดเดรสขนาด 20 ไบต์; คำนำหน้า 0x เป็นตัวเลือก และยอมรับตัวพิมพ์ใหญ่หรือเล็กก็ได้
  • limit (พารามิเตอร์แบบสอบถาม, ไม่บังคับ): ขนาดหน้า ค่าเริ่มต้นคือ 50; ค่าที่มากกว่า 500 จะถูกปรับลดลงเหลือ 500; การส่งค่า 0 หรือไม่ใช่จำนวนเต็มจะส่งคืน 400 bad_request
  • cursor (พารามิเตอร์แบบสอบถาม, ไม่บังคับ): next_cursor ของการตอบกลับครั้งก่อนหน้า ซึ่งส่งกลับมาโดยไม่เปลี่ยนแปลงเพื่อดึงข้อมูลหน้าถัดไป โดยเคอร์เซอร์จะใช้ได้เฉพาะกับเชน, endpoint และพารามิเตอร์แบบสอบถามที่ออกเคอร์เซอร์นั้นเท่านั้น; การนำไปใช้ซ้ำที่อื่นจะส่งคืน 400 bad_request

การแบ่งหน้าเป็นแบบ keyset: next_cursor จะปรากฏเฉพาะเมื่อมีหน้าถัดไป ในหน้าสุดท้ายคีย์นี้จะไม่มีอยู่เลย โดยไม่เคยเป็น null

export BLOCKVECTRA_API_KEY=rgw_your_api_key

curl -s "https://api.blockvectra.com/v1/data/robinhood_mainnet/addresses/0x1111111111111111111111111111111111111111/balances?limit=50" \
  -H "x-api-key: $BLOCKVECTRA_API_KEY"

โครงสร้างการตอบกลับคือ AddressBalanceListEnvelope ซึ่งประกอบด้วย data และ meta โดยแต่ละรายการใน data คือ AddressBalance:

ฟิลด์ชนิดคำอธิบาย
tokenstring (address)แอดเดรสสัญญาโทเค็น; รูปแบบมาตรฐานคือ 0x ตามด้วยเลขฐานสิบหกตัวพิมพ์เล็ก 40 หลัก
balancestring (decimal)ยอดคงเหลือจำนวนเต็มดิบ ซึ่งอาจเกิน 2^53 โดยส่งคืนเป็นสตริงฐานสิบธรรมดา — ไม่เป็นตัวเลข JSON, สัญกรณ์วิทยาศาสตร์ หรือเลขฐานสิบหก
symbolstring หรือ nullสัญลักษณ์โทเค็น หรือ null เมื่อไม่มีข้อมูล
decimalsinteger หรือ nullทศนิยมของโทเค็น 0–255 หรือ null เมื่อไม่มีข้อมูล

คำขอที่ 2: การโอนของแอดเดรส

endpoint สำหรับการโอนจำเป็นต้องระบุหน้าต่างบล็อกอย่างชัดเจน: ทั้ง from_block และ to_block จำเป็นต้องระบุ และต้องเป็นไปตามเงื่อนไข from_block <= to_block โดยรับพารามิเตอร์เพิ่มเติมอีกสองสามรายการ:

  • standard (พารามิเตอร์แบบสอบถาม, จำเป็น): erc20 หรือ erc721 คำค้นหาในขอบเขตแอดเดรสไม่ครอบคลุม erc1155; การส่งค่าดังกล่าวจะส่งคืน 422 no_coverage
  • direction (พารามิเตอร์แบบสอบถาม, ไม่บังคับ): in, out หรือ any; ค่าเริ่มต้นคือ any และกรองตามทิศทางที่สัมพันธ์กับแอดเดรส
  • token (พารามิเตอร์แบบสอบถาม, ไม่บังคับ): จำกัดผลลัพธ์ให้อยู่ในสัญญาโทเค็นเดียว
  • clamp (พารามิเตอร์แบบสอบถาม, ไม่บังคับ): มีเพียงสตริง true ตรงตัวเท่านั้นที่จะเปิดใช้งาน; ค่าอื่นๆ ทั้งหมดจะถือว่าเป็น false

ขอบเขตหน้าต่างและสถานะสิ้นสุด: ค่า to_block ที่ชัดเจนซึ่งสูงกว่า as_of_block จะส่งคืน 409 not_indexed_yet เว้นแต่ว่า clamp=true จะตัดค่าลงมาอยู่ที่ as_of_block; หน้าต่างที่กว้างกว่าขีดจำกัดของเชน (limits.max_window_blocks จาก GET /chains) จะส่งคืน 409 window_too_large เว้นแต่ว่า clamp=true จะตัดทอนจากฝั่งที่เก่ากว่า (เพิ่มค่า from_block และคงค่า to_block ไว้) หาก from_block เองเกิน as_of_block ไปแล้ว จะยังคงส่งคืน 409 อย่างเข้มงวดแม้จะใช้ clamp=true ก็ตาม เมื่อหน้าต่างถูก clamp หรือครอบคลุมเพียงบางส่วน ค่า meta.coverage ในการตอบกลับจะเป็น "partial"; มิฉะนั้นจะเป็น "full"

ในระเบียนการโอน รายการ ERC-20 จะเพิ่ม amount; รายการ ERC-721 จะเพิ่ม token_id โดยทั้งสองจะประกอบด้วย token, standard, from, to, block_number, block_timestamp, tx_hash, tx_index และ log_index

export BLOCKVECTRA_API_KEY=rgw_your_api_key

# 1) Read as_of_block from the balances response.
AS_OF_BLOCK=$(curl -s "https://api.blockvectra.com/v1/data/robinhood_mainnet/addresses/0x1111111111111111111111111111111111111111/balances" \
  -H "x-api-key: $BLOCKVECTRA_API_KEY" | grep -o '"as_of_block":[0-9]*' | cut -d: -f2)

# 2) 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=$AS_OF_BLOCK&direction=any&clamp=true" \
  -H "x-api-key: $BLOCKVECTRA_API_KEY"

การแบ่งหน้าเพื่อดึงข้อมูลการโอนทั้งหมด

next_cursor ของ endpoint การโอนของแอดเดรสเป็นแบบคาดการณ์ในแง่ดี: ค่านี้จะปรากฏเฉพาะเมื่อหน้านั้นส่งคืนจำนวนแถวเท่ากับ limit พอดี ดังนั้นหน้าที่มี next_cursor อาจกลายเป็นหน้าสุดท้ายได้ อย่าหยุดทำงานเมื่อพบหน้าที่ว่างเปล่า; ให้ติดตาม next_cursor ไปเรื่อยๆ จนกระทั่งไม่มีคีย์นี้ปรากฏ

โค้ดด้านล่างนี้จะดึงข้อมูลการโอนทุกรายการในหน้าต่างบล็อก:

const address = "0x1111111111111111111111111111111111111111";
const head = await fetch(
  `https://api.blockvectra.com/v1/data/robinhood_mainnet/addresses/${address}/balances`,
  { headers: { "x-api-key": process.env.BLOCKVECTRA_API_KEY! } },
).then((r) => r.json());
const asOfBlock = head.meta.as_of_block;
const transfers: unknown[] = [];
let cursor: string | undefined;

do {
  const url = new URL(
    `https://api.blockvectra.com/v1/data/robinhood_mainnet/addresses/${address}/transfers`,
  );
  url.searchParams.set("standard", "erc20");
  url.searchParams.set("from_block", "0");
  url.searchParams.set("to_block", String(asOfBlock));
  url.searchParams.set("limit", "500");
  // clamp truncates from the older end
  url.searchParams.set("clamp", "true");
  if (cursor) url.searchParams.set("cursor", cursor);

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

คำขอที่ 3: เมทาดาตาของโทเค็นและ tokens:batch

อ่านโทเค็นเดี่ยวด้วย GET /{chain}/tokens/{token}; พาธนี้รับเฉพาะ {chain} และ {token} โดยไม่มีการแบ่งหน้า โครงสร้างการตอบกลับคือ TokenEnvelope และ data คือ Token:

ฟิลด์ชนิดคำอธิบาย
addressstring (address)แอดเดรสสัญญาโทเค็น
standardstringerc20, erc721 หรือ unknown
namestring หรือ nullชื่อโทเค็น หรือ null เมื่อไม่มีข้อมูล
symbolstring หรือ nullสัญลักษณ์โทเค็น หรือ null เมื่อไม่มีข้อมูล
decimalsinteger หรือ nullทศนิยมของโทเค็น 0–255 หรือ null เมื่อไม่มีข้อมูล
total_supplystring หรือ nullอุปทานรวมดิบ; API ไม่ได้ใช้การปรับสเกล decimals มีค่าเป็น null เมื่อไม่มีข้อมูล
first_seen_blockinteger (int64)ความสูงของบล็อกที่พบโทเค็นนี้เป็นครั้งแรก
metadata_updated_atstring (timestamp)เวลา UTC เมื่อเมทาดาตาได้รับการอัปเดตล่าสุด
metadata_blockinteger (int64)ความสูงของบล็อกที่มีการอ่านเมทาดาตา
metadata_statusstringok, partial หรือ unavailable
metadata_issuesobjectบันทึกปัญหาแยกตามฟิลด์ โดยมีคีย์เป็น name, symbol, decimals, total_supply พร้อมค่า reverted, no_data, invalid_encoding หรือ temporarily_unavailable

{token} ที่ไม่ใช่แอดเดรสขนาด 20 ไบต์ที่ถูกต้องจะส่งคืน 400 bad_request; {token} ที่ไม่รู้จักจะส่งคืน 404 not_found; {chain} ที่ไม่รู้จักจะส่งคืน 404 unknown_chain

endpoint ยอดคงเหลือมี symbol และ decimals รวมอยู่ด้วยแล้วในกรณีที่มีข้อมูล แต่ทั้งสองค่าอาจเป็น null ได้ หากต้องการเติมชื่อและ decimals สำหรับทุกโทเค็นในกระเป๋าเงิน ให้ใช้ POST /{chain}/tokens:batch:

  • เนื้อหาคำขอคือ {"addresses": [...]} โดยมีแอดเดรสได้สูงสุด 100 แอดเดรสต่อหนึ่งคำขอ; รายการที่มากกว่า 100 รายการ หรือรายการที่ไม่ใช่แอดเดรสขนาด 20 ไบต์ที่ถูกต้อง จะส่งคืน 400 bad_request (จะล้มเหลวทันทีที่พบแอดเดรสแรกที่ไม่ถูกต้อง)
  • แอดเดรสที่ไม่พบจะไม่ทำให้เกิดข้อผิดพลาด; แอดเดรสเหล่านั้นจะแสดงอยู่ใน data.missing ขณะที่ data.tokens จะมีเฉพาะโทเค็นที่พบเมทาดาตาเท่านั้น
  • แอดเดรสที่ซ้ำกันจะถูกขจัดรายการซ้ำออกทั้งใน tokens และ missing โดยคงลำดับการปรากฏครั้งแรกในคำขอไว้
export BLOCKVECTRA_API_KEY=rgw_your_api_key

# Single token
curl -s "https://api.blockvectra.com/v1/data/robinhood_mainnet/tokens/0x1111111111111111111111111111111111111111" \
  -H "x-api-key: $BLOCKVECTRA_API_KEY"

# Batch: up to 100 addresses per request
curl -s -X POST "https://api.blockvectra.com/v1/data/robinhood_mainnet/tokens:batch" \
  -H "x-api-key: $BLOCKVECTRA_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"addresses":["0x1111111111111111111111111111111111111111","0x2222222222222222222222222222222222222222"]}'

การปรับสเกลจำนวนเงินด้วย decimals

ฟิลด์ยอดคงเหลือ balance และฟิลด์การโอน ERC-20 amount เป็นจำนวนเต็มดิบที่แสดงผลเป็นสตริงฐานสิบ (UInt256String); อุปทานรวม total_supply ของโทเค็นก็เป็นจำนวนเต็มดิบบนเชนโดยไม่ได้ใช้การปรับสเกล decimals เช่นกัน หากต้องการแสดงปริมาณในรูปแบบที่มนุษย์อ่านได้ ให้หารด้วย decimals ของโทเค็นนั้น

  • decimals มาจาก symbol/decimals ของรายการยอดคงเหลือเอง หรือมาจาก GET /{chain}/tokens/{token} และ POST /{chain}/tokens:batch; โดยสามารถเป็น null ได้
  • ค่าเหล่านี้อาจเกิน 2^53 ดังนั้นอย่าคำนวณเลขคณิตด้วยตัวเลข JSON: ให้ใช้ BigInt ใน TypeScript และ Decimal ใน Python โดยแปลงค่าสตริงฐานสิบตามสภาพเดิมเพื่อหลีกเลี่ยงการสูญเสียความแม่นยำ
function toDisplayAmount(raw: string, decimals: number | null): string {
  if (decimals === null) return raw; // no decimals metadata: keep the raw integer
  const value = BigInt(raw);
  const base = 10n ** BigInt(decimals);
  const whole = value / base;
  const fraction = (value % base)
    .toString()
    .padStart(decimals, "0")
    .replace(/0+$/, "");
  return fraction ? `${whole}.${fraction}` : whole.toString();
}

// balance.balance is a raw decimal string; decimals comes from the same item or tokens:batch.
const display = toDisplayAmount(balance.balance, balance.decimals);

ความสดใหม่ของข้อมูล

ทุกการตอบกลับที่สำเร็จในระดับเชนจะมี meta รวมอยู่ด้วย:

  • as_of_block: บล็อกใหม่ล่าสุดของเชนที่เขียนข้อมูลเสร็จสมบูรณ์แล้ว โดย endpoint ในระดับบล็อกจะให้บริการข้อมูลจนถึงความสูงนี้
  • safe_block: เครื่องหมายระบุแท็กบล็อกฉันทามติ safe ของโหนด (เป็น null เมื่อยังไม่ทราบ) ค่านี้จะไม่ต่ำกว่า finalized_block และไม่ตัดทอน ปฏิเสธ หรือทำให้การตอบกลับล่าช้า
  • finalized_block: เครื่องหมายระบุแท็กบล็อกฉันทามติ finalized ของโหนด (เป็น null เมื่อยังไม่ทราบ) ค่านี้ไม่ตัดทอน ปฏิเสธ หรือทำให้การตอบกลับล่าช้า; ไคลเอนต์สามารถตัดสินใจเลือกระดับความปลอดภัยที่ต้องการจากเครื่องหมายนี้ได้เอง (เช่น สถานะการยืนยัน)
  • coverage: "full" หรือ "partial" endpoint การโอนของแอดเดรสและ endpoint ที่คล้ายกันจะรายงาน "partial" เมื่อ clamp ได้ตัดทอนหน้าต่างที่ให้บริการให้แคบลง หรือเมื่อหน้าต่างเริ่มต้นก่อนบล็อกแรกที่ทำดัชนีของเชน
  • refreshed_at: เวลาที่ข้อมูลเบื้องหลังการตอบกลับได้รับการอัปเดตล่าสุด (UTC) อาจเป็น null: null หมายความว่าไม่ทราบเวลาอัปเดตของข้อมูลนี้และควรได้รับการปฏิบัติเสมือนข้อมูลล้าสมัย; ส่วน endpoint ที่อิงตามบล็อกจะส่งคืนค่าเสมอ
  • นอกจากนี้ยังระบุซ้ำถึง chain, chain_slug และ chain_external_id

รูปแบบที่ใช้กันทั่วไป: ให้อ่าน meta.as_of_block จากการตอบกลับแรกใดๆ เพื่ออ่านข้อมูลจนถึงบล็อกที่ทำดัชนีใหม่ล่าสุด และตรวจสอบ meta.safe_block / meta.finalized_block หากคุณต้องการแสดงสถานะที่ได้รับการยืนยันแล้ว

ประมาณการ CU สำหรับการโหลดหน้าหนึ่งครั้ง

ทุกเมธอดจะถูกคิดค่าบริการตามค่าน้ำหนัก CU ซึ่งอ่านมาจาก API แผนของแพลตฟอร์ม:

น้ำหนัก CU ต่อการเรียก

เมธอดCU ต่อการเรียก
data.address_balances25
data.address_transfers25
data.tokens_batch10

การโหลดหน้าหนึ่งครั้ง (โดยประมาณ)

คำขอ balances 1 ครั้ง + หน้า transfer 3 หน้า + คำขอ tokens:batch 1 ครั้ง, รวมทั้งหมด 5 การเรียก, ประมาณ 110 CU. การใช้งานจริงขึ้นอยู่กับจำนวนหน้าและโทเค็น

สำหรับการตัดสินใจเกี่ยวกับการเรียกเก็บเงินและการตอบกลับข้อผิดพลาดที่ไม่คิดค่าบริการ โปรดดู กฎการเรียกเก็บเงิน หากสิ่งที่คุณต้องการไม่ใช่ประวัติการโอนที่ทำดัชนีไว้ แต่เป็น log จากบล็อกล่าสุด โปรดอ่าน ข้อมูลโหนดล่าสุดเทียบกับประวัติที่ทำดัชนี ก่อนที่จะตัดสินใจว่าจะเปลี่ยนไปใช้ eth_getLogs หรือไม่

ขั้นตอนถัดไป

อัปเดตล่าสุด:

ในหน้านี้