# WINkLink 價格服務

集成前請參考 開發者使用須知

# 概覽

為確保智能合約能反映真實世界的代幣價格,需經常對合約的價格信息進行更新。 其中,DeFi 資產的價格尤其需要和現實世界資產緊密掛鉤, 否則可能導致套利或合約攻擊,給用戶和開發者造成損失。

WINkLink 價格服務專註於數字貨幣價格對,為去中心化應用(DApp)提供準確穩定的外部世界數字貨幣價格信息。 WINkLink 打造了聚合多個預言機節點價格數據的解決方案,從而提供穩定的價格服務,稱作餵價合約(Price Feed Contract)。

上述合約稱為聚合器(Aggregator) —— 持有 WINkLink 預言機節點聚合價格數據的鏈上合約。每個價格餵價(如 BTC/USD)由一個聚合器合約實現,消費合約通過它讀取價格。

# 支持的價格對列表及配置

# 主網

價格對 合約地址(代理)
BTC-TRX TX4rin6u2SaF3TqANqRgzfSCGi95azQNVY (opens new window)
BTC-USD TQoijQ1iZKRgJsAAWNPMu6amgtCJ3WMUV7 (opens new window)
BTT-TRX TS26cn4GmmipyGTcgvRRwqL6AyEU6vK4rw (opens new window)
BTT-USD TBAAW545oJ6iTxqzezGvagrSUzCpz1S8eR (opens new window)
BTTOLD-TRX TUjTmKMxGmH78t5DmY7YsfJFoGw6XyX9VZ (opens new window)
BTTOLD-USD TEEnwU47Fgx4Ehii7Xs9bLWK3XKo4fs6sV (opens new window)
ETH-TRX TXZ9AUk6nC2454NSDGUWvPB72JxSNJrezX (opens new window)
ETH-USD TR2yWYWovJaSM7TfZq7L7sT7ZRugdJJQmL (opens new window)
HTX-TRX TJCCcAqSc1hK5DrJF8v26QW5LQbmXayPhJ (opens new window)
JST-TRX TC19QPF2mjP1ZhXxD8JNKJs4ksxMZkCuNP (opens new window)
JST-USD TE5rKoDzKmpVAQp1sn7x6V8biivR3d5r47 (opens new window)
KGST-TRX TGt2qPTTCv6PvWRzXj12VVoG9kebzYP7WZ (opens new window)
LTC-TRX TVJPFXKMysYsRWEXJ3JkSnAUPucinUFUB6 (opens new window)
LTC-USD TGxGL85kN3W5sGdBiobgWabWFcMEtoqRJJ (opens new window)
NFT-TRX TKtc1V6QAY1Gpy511QjzXkLUphG8Dre8CY (opens new window)
NFT-USD TEC8b2oL6sAQFMiea73tTgjtTLwyV1GuZU (opens new window)
STRX-TRX TW9bNueyJZA9iZnNXGYkJuPJJ7KFN3o5qw (opens new window)
SUN-TRX TLLyqXr5cbYEMjzeThe1esss1SVBbxxubu (opens new window)
SUN-USD TRMgzSPsuWEcVpd5hv19XtLeCk8Z799sZa (opens new window)
SUNOLD-TRX TWAob1YsNzh7bfgkjfAD9MAdarcoSWScWw (opens new window)
SUNOLD-USD TEEuSdqyv2NFREtNoUXMTDSmJVK3KCuLac (opens new window)
TRX-USD TR5HtpPK4gX4RFC4DCBUHfFgsGkGFEzSAb (opens new window)
TUSD-TRX TLXMULb1SRpv841Q54C4DhWkmmGfRA2rUH (opens new window)
TUSD-USD TBc3yBP8xcyQ1E3hDTUhRxToMrgekLH2kh (opens new window)
U-TRX TJyxgoVxr6a5uaBMaT8TazZMaK9fsSFNw3 (opens new window)
U-USD TX6DsYNoMurRqnY9tRHuj4MnBoW76jVKa3 (opens new window)
USDC-TRX TNTm5ezUGHxYc9Mvst58yYTAjxDmqWWGZc (opens new window)
USDC-USD TNu3zS55MP4KnBBP6Maw1nHSzRpc3CXAxm (opens new window)
USDD-TRX TWW4P2pck8rFcxx3H8NfnH4qhNPu1V35Pb (opens new window)
USDD-USD TJ7jEgoYVaeymVfYZ3bS57dYArwVDS1mhW (opens new window)
USDJ-TRX TCBKyYMP4YQFHxYznuUaResHDTaEWLuJNW (opens new window)
USDJ-USD TB1MyT7pDCNg8w7cSW1QvYKs4WPzErzP5k (opens new window)
USDT-TRX TUfV7S4RYtdmBvtHzedfFPVsK9nvndtETp (opens new window)
USDT-USD TKePc46n5CiUCR8LL788TFeKA4kjvNnuem (opens new window)
WBTC-USD TCYS6aj9shB6rZNpTCqSkN1aTwkSnz1wHq (opens new window)
WIN-TRX TQvCG1U2jGTVwXLqvFWR27LDtEJZVgRbEg (opens new window)
WIN-USD TSCef3LT3jpLwwXCWhZe3hZoMsYk1ZLif2 (opens new window)
WSTUSDT-TRX TKcTU5vCPqBBfuULEGXg9kkLx6Tzs2Zo3x (opens new window)
USDDOLD-USD TJrtqTiWkzsdCSU7Jz4JiBswc3Sv8FCw1i (opens new window)
USDDOLD-TRX TGJ7FspL9bftnXKrRFah1yU7VxHkgBpzVB (opens new window)
USD1-TRX TVRD5vMZDnmBXF3AxBiHVJYeykVFzC8eLB (opens new window)

# Nile 測試網

  • WIN 代幣合約地址: TNDSHKGBmgRx9mDYA9CnxPx55nu672yQw2
  • WinkMid 合約地址: TLDU7C8K3Gd3pXrAj9gtpVVNRHZHuHHZ8P

價格服務合約地址列表:

價格對 Nile(代理)
BTC-TRX TFETSL1Yc8dCJM7z6uBkHhAsPbqP5UaCDE (opens new window)
BTC-USD TAX8Pm3FgN74za72TFZrn5gPBxJTKgnnpE (opens new window)
BTT-TRX TKbeHN2hdrgSShG6iF3mDsJTu9fFzNrHjo (opens new window)
BTT-USD TJdzg4wqBt4JkP1ehbYQufg1cLjbomT2j7 (opens new window)
BTTOLD-TRX TETkTRbnyB4ptWiK9qXgiyFxQQ9d8ZacT6 (opens new window)
BTTOLD-USD TRpRfFzubR7oheDCwHRbwJRfeFa85L6tWE (opens new window)
ETH-TRX TSVJwLrhWBF7K6BkEG6hStjMxQJXAzBABQ (opens new window)
ETH-USD TQGyY3mWTTzKKBLBg3wQTSbAGqBnGqYSzX (opens new window)
HTX-TRX TGcVt5g4ExZNVz9gXPWBbnsAN1DPkg3RbG (opens new window)
JST-TRX TSf6ZwFrDg5Jvyci1PnRHrrZvPpCCKNTjj (opens new window)
JST-USD TJ7SizJiCAjMPAri1CFAxzg4xCRLycumMj (opens new window)
KGST-TRX TY9vDmskSFkahX7LESSe5cF5twZHerKgUd (opens new window)
LTC-TRX TWProfbHdGBCf7HVNys5KAbVT4vhUwpu22 (opens new window)
LTC-USD TRSFTb2seuQxQqUsyeJ8Wg8XhX1e2g3T19 (opens new window)
NFT-TRX TWP99RnyMVKFjzuu9XT5J21qBZu8DwhCum (opens new window)
NFT-USD TX5KVe4sp24w5HJ4nfk2ZstRhV8RTFm67W (opens new window)
STRX-TRX TEbUQ4gohuK5wdtKmpnGd2kvyzhhznJDCx (opens new window)
SUN-TRX TTxxeWGpSDV3zPDxnYXzG1ue7RpTYTvDpY (opens new window)
SUN-USD TJjENuVH7TD8RJdGtj22ac6Bt1ktpBGURR (opens new window)
SUNOLD-TRX TNfn4qt4QJ7LAndM2aWbxrGGH8CRGvzxui (opens new window)
SUNOLD-USD TMKzWKMA1gSwjYSL6VpfCUXLuwPKdjEsQ2 (opens new window)
TRX-USD TCeXRh9vcb78j2Eb2oJk4YwwnoHQDT64T1 (opens new window)
TUSD-TRX TM1bvBzHkRrQqvvHGi1CC1Heb8ESWreiNW (opens new window)
TUSD-USD TUuxMFxv6qPn1ymZoYY45SSK1hhEVAvyKz (opens new window)
U-TRX TRGQuwRsH84b2GZ4U2AGfK8GZg36nKJ5dP (opens new window)
U-USD TBzWFDDQkzE4vxhw4tExSVibPRRRKVoLWX (opens new window)
USDC-TRX TWio8JqYx2aey49ua2ohLoyBPbVVWos8RB (opens new window)
USDC-USD TF5a2qhfxtWzUQnAocPoxgKXLe1vEE8oER (opens new window)
USDD-TRX TFr7TWdb5RWPNCfecr3HNfnCmNNL8qvgmJ (opens new window)
USDD-USD TX264fxRmdhNfUgkruk9orzAVvtCehyowq (opens new window)
USDJ-TRX TDJtnT7JRNqmNaqY1mK9i1xWN4GnX1UfGd (opens new window)
USDJ-USD TKZUQTYAhH1LTG67QmhX4HxTWZdvLfH9d1 (opens new window)
USDT-TRX TVZjuqiJNNuLQAQoPAFfUqvYUxhZYkUX5Z (opens new window)
USDT-USD TT2ETLY1Mmx2DKYT9S6fMvKGPqbWH3LDEJ (opens new window)
WBTC-USD TSBoimXw9Mgx7u5A6eC3yfTimn1jfUWAKE (opens new window)
WIN-TRX TP7aHYuXUkKPKsojs9BNJDVyAJeQ2KtfCj (opens new window)
WIN-USD TYYMqsRNZTwsiFkRtn2NewvXT9GnnsPBH9 (opens new window)
WSTUSDT-TRX TZGEUihByCHG79Hbpider6pGZfY9S8ct6P (opens new window)
USDDOLD-USD THoshmYmTfmcNTqYLKAhMYTDNYBhJH3mbt (opens new window)
USDDOLD-TRX TKxxkv4CkJANRVNay8C2eY9E2zMjmWViE7 (opens new window)
USD1-TRX TNeep6gumLAPU4ZfPkyhjJyyu7akMKKjrT (opens new window)

# 申請新價格對

如您想在 WINkLink 申請添加新價格對,請填寫並提交此表格。 (opens new window)

# 獲取最新價格

以下示例演示如何通過 AggregatorV3Interface 讀取 WINkLink 餵價:使用 latestRoundData() 讀取最新價、按 heartbeat 校驗時效性、調用 decimals() 處理精度。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;

/**
 * @title AggregatorV3Interface (WINkLink Price Feed)
 * @notice Interface for reading WINkLink price feeds.
 */
interface AggregatorV3Interface {
  function decimals() external view returns (uint8);
  function description() external view returns (string memory);
  function getRoundData(uint80 _roundId)
    external view
    returns (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );
  function latestRoundData()
    external view
    returns (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );
}

/**
 * THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE.
 * DO NOT USE THIS CODE IN PRODUCTION.
 */
contract PriceConsumer {
  AggregatorV3Interface internal priceFeed;
  uint256 public constant MAX_PRICE_AGE = 1 hours; // Adjust to your business tolerance

  /**
   * Network: TRON Mainnet / Nile Testnet
   * Pass the feed address for the price pair you want to read.
   * Find supported pairs and addresses in the Price Feed Service documentation.
   */
  constructor(address feedAddress) {
    priceFeed = AggregatorV3Interface(feedAddress);
  }

  /// @notice Returns the latest price together with its decimals.
  /// @dev Reverts if the round is incomplete or the price is stale.
  function getLatestPrice() public view returns (int256 answer, uint8 decimals) {
    uint256 updatedAt;
    (
      /* uint80  roundId         */,
      answer,
      /* uint256 startedAt       */,
      updatedAt,
      /* uint80  answeredInRound */
    ) = priceFeed.latestRoundData();

    require(updatedAt > 0, "Round not complete");
    require(block.timestamp - updatedAt <= MAX_PRICE_AGE, "Price too stale");

    decimals = priceFeed.decimals();
  }
}

若你只需在鏈下讀取價格(如前端、後端或腳本),無需部署任何合約 —— 用 TronWeb 直接讀取代理:

// Off-chain read with TronWeb — no contract deployment required.
// npm install tronweb
const { TronWeb } = require('tronweb');

// Mainnet: https://api.trongrid.io  |  Nile testnet: https://nile.trongrid.io
// For production, use a free TronGrid API key to avoid rate limits:
//   new TronWeb({ fullHost: 'https://api.trongrid.io', headers: { 'TRON-PRO-API-KEY': 'your-key' } })
const tronWeb = new TronWeb({ fullHost: 'https://api.trongrid.io' });
// A constant (read-only) call still needs a from-address set; any valid address works:
tronWeb.setAddress('T9yD14Nj9j7xAB4dbGeiX9h8unkKHxuWwb');

// Proxy address of the price pair you want to read (see Supported Price Pairs List).
const FEED_ADDRESS = 'TXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';

async function getLatestPrice() {
  const feed = await tronWeb.contract().at(FEED_ADDRESS);
  const data = await feed.latestRoundData().call();
  const decimals = await feed.decimals().call();
  // Normalize the price as answer / 10 ** decimals
  console.log('answer:', data.answer.toString());
  console.log('updatedAt:', data.updatedAt.toString());
  console.log('decimals:', decimals.toString());
}

getLatestPrice();

或用 tronpy(Python):

# Off-chain read with tronpy — no contract deployment required.
# pip install tronpy
from tronpy import Tron

# Mainnet: Tron()  |  Nile testnet: Tron(network='nile')
# For production, use a free TronGrid API key to avoid rate limits:
#   from tronpy.providers import HTTPProvider
#   client = Tron(HTTPProvider(api_key='your-key'))
client = Tron()

# Proxy address of the price pair you want to read (see Supported Price Pairs List).
FEED_ADDRESS = 'TXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'

feed = client.get_contract(FEED_ADDRESS)
round_id, answer, started_at, updated_at, answered_in_round = feed.functions.latestRoundData()
decimals = feed.functions.decimals()
# Normalize the price as answer / 10 ** decimals
print('answer:', answer)
print('updatedAt:', updated_at)
print('decimals:', decimals)

# 獲取價格歷史

Round / RoundId —— 每次價格更新形成一個新的 Round(輪次),由唯一的 RoundId(uint80)標識。任意歷史輪次的價格可通過 getRoundData(roundId) 查詢。

以下代碼演示如何通過 getRoundData()roundId 讀取歷史價。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;

/**
 * DO NOT USE THIS CODE IN PRODUCTION.
 *
 * Continued from the AggregatorV3Interface defined above.
 */
contract HistoricalPriceConsumer {
  AggregatorV3Interface internal priceFeed;

  constructor(address feedAddress) {
    priceFeed = AggregatorV3Interface(feedAddress);
  }

  /// @notice Returns the historical price for a specific roundId.
  /// @param  _roundId The round to query (must be a valid completed round).
  function getHistoricalPrice(uint80 _roundId)
    public view
    returns (int256 answer, uint256 updatedAt)
  {
    (
      /* uint80  roundId         */,
      answer,
      /* uint256 startedAt       */,
      updatedAt,
      /* uint80  answeredInRound */
    ) = priceFeed.getRoundData(_roundId);

    require(updatedAt > 0, "Round not complete");
  }
}

# 設置餵價合約

# 部署合約

WINkLink 生態採用去中心化架構,所有智能合約開源,任何組織和個人都可以部署自己的 WINkLink 預言機合約, 並對外公布所提供的服務。

用戶可以從各個公開 WINkLink 服務中選擇自己所需的組合,創建自己的聚合數據合約,享受去中心化機制帶來的便利。

項目合約可點此查看:https://github.com/tron-oracle/winklink-libocr/tree/main/tvm-contracts (opens new window) - 連接您的 Github 賬戶

您可以使用以下任一工具或程序庫進行合約部署和調用測試::

# 聚合器合約

聚合器合約部署在波場 TRON 公鏈上,具有以下功能:

  • 接受來自 WINkLink 節點的鏈下聚合傳輸
  • 計算數據請求的 WIN 代幣費用,準許預言機節點領取獎勵;
  • 實現 Owned 接口, 進而對聚合器合約暴露的不同方法提供權限控制。

合約代碼可於 AccessControlledOCRAggregator.sol 查看。

# 為節點添加任務

節點的任務代表其所支持的數據服務,每個任務都有一個 32 字節的唯一 ID。 對終端用戶而言, (預言機地址,任務 ID) 唯一標識了一個 WINkLink 節點提供的數據服務。 每個 WINkLink 節點都可以提供多項數據服務。

WINkLink 節點正常運行後,就可以通過 Operator UI 為節點添加任務:

示例:(將下列參數修改為上述步驟中部署的預言機合約地址)

TIP

如涉及引導節點,請在配置文件中設置 DefaultBootstrapPeers。

Example: DefaultBootstrapPeers = ['/ip4/127.0.0.1/tcp/6788/p2p/12D3KooWMrKGdnH6nBrf7hDz25NXFSFNk7vgsTxj9bHskWEct4xh']

# 引導節點

type               = "offchainreporting"
schemaVersion      = 1
tvmChainID         = 1
name               = "TUSD-TRX"
contractAddress    = "ACCESS-CONTROLLED-OCR-AGGREGATOR-ADDRESS"
p2pBootstrapPeers  = [
  "/ip4/127.0.0.1/tcp/6788/p2p/P2P-PEER-ID",
]
isBootstrapPeer = true
keyBundleID = "NODE-KEY-BUNDLE"
forwardingAllowed = false
maxTaskDuration = "0s"

TIP

確保引導節點和預言機節點被配置為單獨的實體,每個實體鏈接到其唯一的數據庫實例。 在本地進行操作時,通過修改 config.toml 文件為每個實例指定不同的端口號,以便訪問每個實例各自的 Operator UI。

[WebServer]
HTTPPort = 3000
SecureCookies = false # Default

# 預言機節點

type               = "offchainreporting"
schemaVersion      = 1
tvmChainID         = 2
name               = "OCR: TUSD-TRX"
contractAddress    = "ACCESS-CONTROLLED-OCR-AGGREGATOR-ADDRESS"
p2pBootstrapPeers  = [
"/ip4/127.0.0.1/tcp/6788/p2p/P2P-PEER-ID",
]
isBootstrapPeer    = false
keyBundleID        = "NODE-KEY-BUNDLE"
transmitterAddress = "THE-CURRENT-NODE-EIP55-ADDRESS"
observationTimeout = "10s"
blockchainTimeout  = "20s"
contractConfigTrackerSubscribeInterval = "2m"
contractConfigTrackerPollInterval = "1m"
contractConfigConfirmations = 3
observationSource   = """
ds_http           [type="http" method=GET url="https://www.okx.com/api/v5/market/index-tickers?instId=TUSD-USD"]
    ds_parse          [type="jsonparse" path="data,0,idxPx"]
ds_converttrx     [type="converttrx" url="https://www.okx.com/api/v5/market/index-tickers?instId=TRX-USD" path="data.0.idxPx"]
ds_multiply       [type="multiply" times=1000000]

    ds_http -> ds_parse -> ds_converttrx -> ds_multiply
"""

# 查詢任務

開發者可以在 Operator UI 的任務選項卡下檢索任務。

子選項卡如下:

  • 概覽:最近的運行任務和任務流列表
  • 定義:任務規範
  • 錯誤:自任務創建以來累計遇到的不同錯誤
  • 運行:所有任務運行的列表

job-page-ui.png

# API 參考

集成 WINkLink 價格餵價時,通過 AggregatorV3Interface 接口 + 代理(proxy)合約地址讀取餵價。如需獲取代理背後的聚合器信息,可調用 AccessControlledOffchainAggregator 合約中的變量與函數。

代理對外提供一個穩定地址,其底層聚合器可無縫升級而不影響你的集成。

# AggregatorV3Interface

在你的合約中導入本接口並指向代理地址使用(在上文「支持的價格對列表」中查詢可用價格對與代理地址)。示例:

/**
 * Network: TRON Mainnet / Nile Testnet
 * Pass the proxy address of the price pair you want to read.
 */
constructor(address feedAddress) {
  priceFeed = AggregatorV3Interface(feedAddress);
}

使用示例見上文「獲取最新價格」段。

方法名 說明
decimals 響應值的小數位數
description 代理指向的聚合器描述
getRoundData 獲取指定輪次的數據
latestRoundData 獲取最新輪次的數據
version 代理指向的聚合器版本號

decimals

獲取響應值的小數位數。

function decimals() external view returns (uint8);
  • 返回:小數位數

description

獲取代理指向的底層聚合器的描述。

function description() external view returns (string memory);
  • 返回:底層聚合器的描述文本

getRoundData

roundId 獲取指定輪次的數據。

function getRoundData(
  uint80 _roundId
) external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);

參數:

  • _roundId:輪次 ID

返回:

  • roundId:輪次 ID
  • answer:本輪聚合後的價格
  • startedAt:本輪起始時間戳
  • updatedAt:本輪完成時間戳
  • answeredInRound:已棄用 —— 此前用於答案需跨多輪計算的場景;在 WINkLink 餵價上恒等於 roundId

latestRoundData

獲取最新輪次的數據。

function latestRoundData()
  external
  view
  returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);

返回:

  • roundId:輪次 ID
  • answer:本餵價為所配置價格對提供的聚合價格
  • startedAt:本輪起始時間戳
  • updatedAt:本輪完成時間戳;請結合該餵價的 heartbeat 校驗時效性
  • answeredInRound:已棄用 —— 此前用於答案需跨多輪計算的場景;在 WINkLink 餵價上恒等於 roundId

WINkLink OCR 餵價說明:每次上報都會把 startedAtupdatedAt 寫為該次上報的區塊時間戳,兩者相同。代理會把 phase 編碼進 roundId 高位(phaseId << 64 | aggregatorRoundId),因此 roundId 並非從 1 連續遞增,且在底層聚合器升級時會跳號。調用 getRoundData() 時請傳入帶 phase 的 roundId(例如 latestRoundData() 返回的那個)。

version

代理指向的聚合器版本號。

function version() external view returns (uint256);
  • 返回:版本號。WINkLink 價格餵價當前返回 4

# AccessControlledOffchainAggregator

這是聚合器(aggregator)合約本身。最佳實踐是通過 AggregatorV3Interface 代理調用,避免聚合器升級時影響你的應用。僅在需要代理未暴露的方法時才直接調用聚合器合約。

聚合器合約提供了若干對應用可能有用的變量與函數。各餵價的聚合器結構相似,但部分聚合器的變量有所不同。可調用聚合器的 typeAndVersion() 函數確認其類型與版本。

集成時請實地查看合約源碼與配置,了解具體餵價的運作方式。參考合約:TU5dpGzXAZwrpFfCrFzDBPWNxPm4diREqu (opens new window)(TronScan)。

本合約導入了 OffchainAggregatorSimpleReadAccessController(二者又各自有導入項)。下面的變量與函數列表包含這些被導入合約中可公開訪問的項。

讀取這些變量或函數的簡便方式:從區塊瀏覽器(TronScan)獲取 ABI,並將 ABI 指向聚合器地址。

變量:

名稱 說明
checkEnabled 布爾值,是否僅允許內部白名單地址訪問
maxAnswer 聚合器允許上報的價格上限;中位數高於此值的上報會被合約拒絕
minAnswer 聚合器允許上報的價格下限;中位數低於此值的上報會被合約拒絕
owner 聚合器合約的擁有者地址,決定哪些地址可執行特定函數

函數:

名稱 說明
billingAccessController billingAccessController 地址,限制聚合器 billing 配置的訪問權限
decimals 返回價格的精度位數(定點格式)
description 返回本餵價的描述(因價格對而異)
getAnswer 已棄用 —— 不要使用
getBilling 獲取當前 billing 配置
getRoundData 獲取指定輪次的完整信息(價格 + 時間戳)
getTimestamp 已棄用 —— 不要使用
getWinToken 獲取支付預言機所用 WIN 代幣合約的地址
hasAccess 檢查地址是否有內部訪問權限
latestAnswer 返回最新價格(無時間戳,無法校驗時效性)
latestConfigDetails 返回當前 OCR 協議配置信息
latestRound 已棄用 —— 不要使用
latestRoundData 獲取最新輪次的完整信息
latestTimestamp 已棄用 —— 不要使用
latestTransmissionDetails 獲取最新價格的詳細信息
oracleObservationCount 返回某預言機待結算的 observation 數量
owedPayment 返回某預言機因其 observation 應得的 WIN 數量
requesterAccessController 返回 requester 訪問控制合約地址
transmitters 可向本聚合器上報價格的預言機地址列表
typeAndVersion 返回聚合器類型與版本;此版本指聚合器類型版本,與合約 version 不同
validatorConfig 返回 validator 合約地址及其 gas 上限
version 返回合約版本號,與聚合器的 typeAndVersion 不同
winAvailableForPayment 返回本合約當前可用於支付預言機的 WIN 餘額;若存在未結清的支付義務,該值可能為負

billingAccessController

billingAccessController 地址,限制聚合器 billing 配置的訪問權限。

function billingAccessController() external view returns (AccessControllerInterface) {
  return s_billingAccessController;
}

decimals

返回價格的精度位數。價格以定點格式存儲。

function decimals() external view returns (uint8 decimalPlaces);

description

返回本餵價的描述,價格餵價通常為資產對名稱。

function description() public view override checkAccess() returns (string memory) {
  return super.description();
}

getAnswer

已棄用 —— 不要使用此函數,請改用 getRoundData()

getBilling

獲取當前 billing 配置。在 TRON 上,前三個字段(maximumGasPricereasonableGasPricemicroWinPerTrx)未啟用,恒為 0

function getBilling()
  external
  view
  returns (
    uint32 maximumGasPrice,
    uint32 reasonableGasPrice,
    uint32 microWinPerTrx,
    uint32 winPerObservation,
    uint32 winPerTransmission
  )
{
  Billing memory billing = s_billing;
  return (
    billing.maximumGasPrice,
    billing.reasonableGasPrice,
    billing.microWinPerTrx,
    billing.winPerObservation,
    billing.winPerTransmission
  );
}

getRoundData

獲取指定輪次的完整信息(價格 + 時間戳)。用於讀取某一輪次的完整歷史數據。

function getRoundData(uint80 _roundId)
  public
  view
  override
  checkAccess()
  returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)
{
  return super.getRoundData(_roundId);
}

getTimestamp

已棄用 —— 不要使用此函數,請改用 getRoundData()

getWinToken

獲取支付預言機所用 WIN 代幣合約的地址。

function getWinToken() external view returns (WinTokenInterface winToken) {
  return s_winToken;
}

hasAccess

檢查地址是否有內部訪問權限。

function hasAccess(address _user, bytes memory _calldata) public view virtual override returns (bool) {
  return super.hasAccess(_user, _calldata) || _user == tx.origin;
}

latestAnswer

返回本餵價的最新價格。不含時間戳,無法校驗時效性;若尚無寫入則返回 0latestRoundData 同時返回最新價格與時間戳,可用於校驗時效性。

latestConfigDetails

返回當前 OCR 協議配置信息。

function latestConfigDetails() external view returns (uint32 configCount, uint32 blockNumber, bytes16 configDigest) {
  return (s_configCount, s_latestConfigBlockNumber, s_hotVars.latestConfigDigest);
}

latestRound

已棄用 —— 不要使用此函數,請改用 latestRoundData()

latestRoundData

獲取最新輪次的完整信息(價格 + 時間戳)。

function latestRoundData()
  public
  view
  override
  checkAccess()
  returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)
{
  return super.latestRoundData();
}

latestTimestamp

已棄用 —— 不要使用此函數,請改用 latestRoundData()

latestTransmissionDetails

獲取最新價格的詳細信息。僅外部賬戶(EOA)可調用。

function latestTransmissionDetails()
  external
  view
  returns (bytes16 configDigest, uint32 epoch, uint8 round, int256 latestAnswer, uint64 latestTimestamp)
{
  require(msg.sender == tx.origin, "Only callable by EOA");
  return (
    s_hotVars.latestConfigDigest,
    uint32(s_hotVars.latestEpochAndRound >> 8),
    uint8(s_hotVars.latestEpochAndRound),
    s_transmissions[s_hotVars.latestAggregatorRoundId].answer,
    s_transmissions[s_hotVars.latestAggregatorRoundId].timestamp
  );
}

oracleObservationCount

返回某預言機待結算的 observation 數量。

function oracleObservationCount(address _signerOrTransmitter) external view returns (uint16) {
  Oracle memory oracle = s_oracles[_signerOrTransmitter];
  if (oracle.role == Role.Unset) {
    return 0;
  }
  return s_oracleObservationsCounts[oracle.index] - 1;
}

owedPayment

返回某預言機因其 observation 應得的 WIN 數量。

function owedPayment(address _transmitter) public view returns (uint256) {
  Oracle memory oracle = s_oracles[_transmitter];
  if (oracle.role == Role.Unset) {
    return 0;
  }
  Billing memory billing = s_billing;
  uint256 winAmount = uint256(s_oracleObservationsCounts[oracle.index] - 1) * uint256(billing.winPerObservation);
  winAmount += s_gasReimbursementsWin[oracle.index] - 1;
  return winAmount;
}

requesterAccessController

返回 requester 訪問控制合約地址。

function requesterAccessController() external view returns (AccessControllerInterface) {
  return s_requesterAccessController;
}

transmitters

可向本聚合器上報價格的預言機地址列表。

function transmitters() external view returns (address[] memory) {
  return s_transmitters;
}

typeAndVersion

返回聚合器類型與版本。此版本指聚合器類型版本,與合約 version 不同。

function typeAndVersion() external pure virtual override returns (string memory) {
  return "AccessControlledOffchainAggregator 4.0.0";
}

validatorConfig

返回 validator 合約地址及其 gas 上限。

function validatorConfig() external view returns (AggregatorValidatorInterface validator, uint32 gasLimit) {
  ValidatorConfig memory vc = s_validatorConfig;
  return (vc.validator, vc.gasLimit);
}

version

返回合約版本號。與聚合器的 typeAndVersion 不同。

function version() external view returns (uint256);

winAvailableForPayment

返回本合約當前可用於支付預言機的 WIN 餘額。若存在未結清的支付義務,該值可能為負。

function winAvailableForPayment() external view returns (int256 availableBalance) {
  int256 balance = int256(s_winToken.balanceOf(address(this)));
  int256 due = int256(totalWINDue());
  return int256(balance) - int256(due);
}

# 開發者使用須知

WINkLink 價格餵價通過 7 個獨立預言機節點的去中心化網絡為智能合約提供鏈下價格數據。餵價數據在 DApp 中的表現,取決於外部市場情況應用集成代碼兩方面的因素。在集成 WINkLink 價格餵價時,請關注以下兩類要點:市場完整性應用代碼

# 市場完整性

WINkLink 價格餵價反映真實市場情況,受外部市場環境的影響。集成方在使用餵價數據時,應理解所反映資產的市場特性,並在業務邏輯中考慮極端情況。

外部市場環境對餵價的影響包括:

  • 所反映資產的市場結構(交易所分布、深度、流動性等)會影響價格穩定性
  • 流動性較低的資產,對外部市場環境變化更敏感
  • 在極端市場情況(劇烈波動、流動性驟降、市場異常)下,餵價的更新行為可能出現非預期變化

集成時建議結合自身業務場景考慮極端情況下的處理邏輯,並在上線後持續觀察餵價行為。

# 應用代碼

應用代碼的質量、可靠性以及對外部依賴的處理,直接影響 DApp 使用價格數據的穩健性。

校驗價格時效性

每個 WINkLink 價格餵價都有一個心跳週期(heartbeat),表示在該週期內鏈上價格必有更新。建議在使用價格前主動校驗時間戳:

(uint80 roundId, int256 answer, , uint256 updatedAt, ) = priceFeed.latestRoundData();
require(updatedAt > 0, "Round not complete");
require(block.timestamp - updatedAt <= MAX_PRICE_AGE, "Price too stale");

MAX_PRICE_AGE 應大於該價格對的 heartbeat 週期,具體取值按業務容忍度選擇。

理解精度

WINkLink 價格餵價的精度(小數位數)可能因價格對而異。在做任何價格計算前,調用 decimals() 獲取精度:

uint8 dec = priceFeed.decimals();
// 在計算中使用 dec 進行歸一化

不要假設精度為固定值。

保持合約可升級

避免將餵價合約地址硬編碼到不可變更的業務邏輯中。建議將其存儲為可由合約所有者或治理機制更新的變量,以便在 WINkLink 升級或棄用底層聚合器時,能夠平滑遷移到新的餵價合約。

先在 Nile 測試網測試

在把你的消費合約部署到主網前,請先在 Nile 測試網、指向 WINkLink 的 Nile 餵價地址完成完整集成測試:

關注 WINkLink 資訊變化

WINkLink 通過兩個渠道發佈價格餵價相關的更新,建議集成方持續關注與自身業務相關的內容:

建議在集成前記錄所用價格對的配置參數基線,便於上線後定期對比,及時發現參數變更。

上線後持續監控

集成完成上線後,建議持續監控以下信號:

  • 餵價合約 latestRoundData() 返回是否按心跳週期定期更新
  • 業務合約中 stale check 或相關校驗是否被觸發
  • 上述異常事件的頻率與時間分布

可結合 TRON 區塊瀏覽器的事件訂閱或自建鏈下監控腳本實現。