不使用價格預言機直接從交易所讀取 USD / ETH 匯率是否安全?
是否可以在沒有預言機的情況下直接從某些已知的智能合約中讀取 USD/ETH 匯率?
我有一個通過甲骨文的工作解決方案。但是,我的客戶說應該可以直接從交換合約中讀取目前匯率,例如 Pancake swap 或 Uniswap,其中會有一對 ETH:USD 並且雙方都有資訊(傳播?)所以我們可以讀取這兩個值並得出匯率。
或者從像 DAI 這樣的錨定穩定幣中提取這樣的利率。
有沒有人成功實施這種直讀?
TL;DR:從交易所或其他協議讀取定價資訊是不安全的。始終使用去中心化的預言機。
在建構智能合約時,您將自己設置為被黑客入侵。從去中心化的預言機獲取價格資訊始終是您應該做的事情。從像Chainlink 這樣的去中心化預言機中讀取定價資訊特別容易。
這是一個很好的問題,對於新工程師來說真的很難掌握,讓我們分解一下。
@Mikko 在另一個答案中還提供了許多有用的連結以獲取更多資訊。
天真的方法
從DEX(去中心化交易所)讀取價格很容易,尤其是從AMM讀取價格。最受歡迎的 DEX 之一的Uniswap有一個名為
getReserves
. 您可以“基本上”呼叫此函式並使用 this 計算資產的“價格”,其中資產為reserve0
和reserve1
。function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
要獲得 的價格
reserve0
,您只需除以。price = reserve0 / reserve1
現在你有了資產的價格。但是,在使用智能合約時,這可能會遇到重大問題。你的“價格”現在取決於交易所的流動性,或者換句話說,你的價格取決於一個集中的故障點。
什麼意思,中心化?
現在你可能會想“哦,DEX 是一個‘去中心化交易所’,那麼你的意思是價格是中心化的嗎?”。好問題。當您從 DEX 獲得價格時,您從 1 DEX 獲得價格。資產的交易是去中心化的,但資產的定價來自 1 DEX,一個集中的位置。
如果你的 DEX 的儲備發生了巨大的變化,即使只是一瞬間,DEX 也可能會提供一個非常不准確的價格。
Defi 中最常見的兩種攻擊是重入攻擊和預言機操縱攻擊
困擾並摧毀了無數協議的簡單攻擊向量稱為“閃貸攻擊”或更準確地說是“價格預言機攻擊”。通常,黑客會看到另一個協議正在使用 DEX 作為他們的定價資訊,並會使用閃電貸來臨時大幅增加或減少資產的儲備以抬高或降低價格,然後使用該價格來攻擊您的協議。讓我們看一個例子。
讓我們使用與上面相同的數學,假設一個協議有一個 100 USDT 和 1 ETH 的交易對。如果我們在這裡做數學:
price = reserve0 / reserve1
這意味著,100 USDT = 1 ETH。
現在讓我們假設一個 ETH 鯨魚或使用閃電貸款的人出現並將 49 ETH 轉儲到我們的交易所。嗯,池子現在已經改變了,價格現在看起來像這樣:
price = 100 / 50
現在 ETH 的價格是 $ 2. $ 50 -> 2 美元,立即。如果你有基於這個價格的清算、獎勵或任何東西,你現在已經因為這種操縱讓你的使用者陷入了錯誤的行為。
事實上,街道上到處都是協議,這些協議正是受此影響,或某種衍生協議。
更安全、更可靠、更準確的方法
使用去中心化的區塊鏈預言機將保護您免受這些傷害。在上面的場景中,即使有人閃貸攻擊 DEX,你的定價資訊仍然是安全的,因為**數據來自去中心化的集體。**因此,如果一兩個數據源嚴重不正確,也沒關係。
此外,從 oracle 提要中提取非常容易。以下是一些有效的 Solidity 程式碼,它們將從 kovan 測試網中提取最新的 ETH / USD 提要。
// SPDX-License-Identifier: MIT pragma solidity ^0.8.7; import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; contract PriceConsumerV3 { AggregatorV3Interface internal priceFeed; /** * Network: Kovan * Aggregator: ETH/USD * Address: 0x9326BFA02ADD2366b30bacB125260Af641031331 */ constructor() { priceFeed = AggregatorV3Interface(0x9326BFA02ADD2366b30bacB125260Af641031331); } /** * Returns the latest price */ function getLatestPrice() public view returns (int) { ( uint80 roundID, int price, uint startedAt, uint timeStamp, uint80 answeredInRound ) = priceFeed.latestRoundData(); return price; } }
更多資訊: