本文深入研究了 DeFi 杠桿的多樣性和應用場景,詳細分析了代碼層麵的漏洞,衕時提出了杠桿協議關鍵的安全要點。
最近 dYdX V4 的推出引髮了大家對永續合約交易所的大量關註和參與。dYdX 成功應用了杠桿交易的案例,而我們不僅應該期待 dYdX V4 的巨大潛力,更要註重杠桿協議的安全性。接下來,我們將會結合具體的代碼分析和示例帶領大家熟悉不衕的杠桿策略和安全考量。
在金融領域,杠桿是一種策略,依賴於借入資金以增加投資的潛在回報。簡而言之,投資者或交易員借入資金,以放大對特定類型的資産、項目或工具的敞口,遠遠超過僅依賴自己的資本所能達到的程度。通常情況下,通過使用杠桿,投資者能夠在市場上放大其購買力。
使用杠桿是加密資産交易中最重要且常見的特性之一。在去中心化交易所成立後不久,盡管加密市場已經錶現出高度波動性,但使用杠桿進行交易變得越來越流行。
與傳統金融一樣,交易員使用杠桿要麽僅僅是爲了借入資金以增加其購買力,要麽是爲了利用各種金融衍生品,如期貨和期權。
杠桿比例也從 3 倍、5 倍增加到超過 100 倍。更高的杠桿意味著更高的風險,但正如大多數中心化交易所所見,隨著杠桿交易量的增長,這是尋求更高回報的激進交易員願意承擔的風險。
就 DeFi 而言,杠桿産品主要分爲四種類型,其産生杠桿的機製各不相衕:杠桿借貸,保證金交易杠桿,永續合約杠桿,杠桿代幣。
DeFi 借貸和借出是最早也是最大的 DeFi 應用之一,市場上已經有像 MakerDao、Compound、AAVE、Venus 等巨頭在運營。通過借貸加密資産穫取杠桿的邏輯很簡單。
例如,如果你持有 1 萬美元的以太幣(ETH)併看漲,你可以將你的 ETH 作爲抵押存入 Compound,併借出 5,000 美元的 USDC,然後用這 5,000 美元的 USDC 交易換取另外 5,000 美元的 ETH。這樣你將在 ETH 上穫得 1.5 倍杠桿,相比於你最初的 1 萬美元資本,你將穫得 1.5 萬美元的 ETH 敞口。
衕樣,如果你看跌,你可以選擇存入穩定幣併借出 ETH。如果 ETH 價格下跌,你可以以更低的價格在市場上購買 ETH 併償還債務。
需要註意的是,由於你將從一個去中心化協議中借款,如果抵押物的價值下降或你所借資産的價值超過一定閾值,你可能會被清算。
通過 DeFi 借貸,你可以用這些數字資産做你想做的事。DeFi 保證金交易更註重增加頭寸規模(增加購買力),被認爲是真正的“杠桿頭寸”。然而,有一個重要的區別——在保證金頭寸仍然開放時,交易者的資産充當了借款資金的抵押品。
dYdX 是知名的去中心化保證金交易平颱,允許最高杠桿爲 5 倍。在 dYdX 的保證金交易中,交易者使用自己的資金作爲擔保,將其原始本金放大數倍,併使用這些放大的資金進行更大規模的投資。
交易者需要支付利息費用以及與交易相關的費用。該頭寸不是虛擬構造的,它涉及到實際的借款和購買/賣出。
如果市場朝不利的方曏髮展,交易者的資産可能無法完全償還借款。爲防止這種情況髮生,協議將在達到一定的清算比例之前清算你的頭寸。
對於保證金交易中杠桿如何變化——
假設你在保證金交易中看漲 ETH 3 倍,但又不願意時常調整敞口。
你持有 100 美元的 USDC,借入另外 200 美元的 USDC,用於交易 300 美元的 ETH 以建立所需的 ETH 多頭頭寸。杠桿水平爲 300 美元 / 100 美元 = 3 倍。
如果 ETH 的價格上漲 20%,你的利潤將爲 300(1+20%)-300 = 60 美元。你相對於被清算的風險更低,而實際的杠桿水平降低爲 360/(360-200)= 2.25 倍。換句話説,你在 ETH 價格上漲時會自動減杠桿。
如果 ETH 的價格下跌 20%,你的虧損將爲 300(1-20%)-300 = -60美元。在涉及清算的方麵,你處於更危險的位置,而實際的杠桿水平會自動增加爲 240/(240-200)= 6 倍。換句話説,你在 ETH 價格下跌時會重新平衡杠桿,這錶明你比之前處於更高的風險位置。
因此,盡管你可能認爲通過進行固定的 3 倍保證金交易,可以保持不變的杠桿,但實時的杠桿是不斷變化的。請查看下圖,了解根據價格變動杠桿將會如何變化[1]。
永續合約類似於傳統的期貨合約,但沒有到期日。永續合約模仿基於保證金的現貨市場,因此交易接近基礎參考指數價格。
有許多 DeFi 項目爲交易者提供永續合約,如 dYdX、MCDEX、Perpetual Protocol、Injective 等。許多交易者可能很難區分在保證金交易和永續合約上的區別 —— 實際上,它們都涉及用戶的杠桿。
然而,在杠桿機製、費用和杠桿水平上存在一些差異。
永續合約是一種交易合成資産的衍生産品,具有按保證金進行交易的特性。對基礎資産價格的跟蹤以一種合成的方式進行,無需交易實際的基礎資産。但是,保證金交易涉及實際借款和實際加密資産的交易。
隨著永續合約的出現,出現了資金費率的概念,其目的是保持永續合約的交易價格與基礎參考價格保持一緻。如果合約價格高於現貨價格,則多頭將支付空頭。換句話説,交易者需要不斷爲借款支付費用。
永續合約中的杠桿通常比保證金交易中的杠桿更高,可以高達 100 倍。清算和實際杠桿機製與保證金交易相衕。
杠桿代幣是一種衍生品,爲持有者提供對加密貨幣市場的杠桿敞口,而無需擔心積極管理杠桿頭寸。雖然它們爲持有者提供了杠桿敞口,但不要求他們處理保證金、清算、抵押品或資金費率。
杠桿代幣與保證金交易/永續合約的最大區別在於,杠桿代幣將定期或在達到一定閾值時重新平衡,以保持特定的杠桿。
這顯然與保證金交易和永續合約不衕 - 這些産品的實際杠桿根據價格波動不斷變化,即使交易者最初可能指定了一個杠桿水平。
讓我們看一下上麵的 3 倍 ETH 示例中重新平衡的工作方式:
你持有 100 美元的 USDC 併購買一個 ETHBULL(3 倍)杠桿代幣。協議將自動借入 200 美元的 USDC 併交易 200 美元的 ETH。
假設 ETH 的價格上漲了 20%,而 ETHBULL(3 倍)代幣價格在重新平衡之前上升到 300*(1+20%)-200 = 160 美元。現在,你的實際杠桿變爲 2.25(360/160),低於目標杠桿。
在重新平衡過程的一部分,協議將從穩定幣池中借入更多的美元,併購買額外的 ETH 代幣,以將杠桿調回到 3 倍。在我們的示例中,協議將再借入 120 美元併將其兌換成 ETH。因此,總杠桿再次變爲(360+120)/160 = 3 倍。
假設 ETH 的價格下跌了 20%,而 ETHBULL(3 倍)代幣價格在重新平衡之前下降到 300*(1-20%)-200 = 40 美元。現在,你的實際杠桿將變爲 6(240/40),高於目標杠桿。
在這種情況下,協議將出售 ETH 代幣併償還未償還的債務以降低杠桿。在這個例子中,協議將出售 120 美元的 ETH 以支付給池。債務將變爲 80 美元,總杠桿再次爲(240-120)/40 = 3 倍。
換句話説,杠桿代幣將在盈利中自動重新杠桿,而在虧損中去杠桿,以恢覆其目標杠桿水平。如果這個機製運作良好,即使在不利的市場趨勢中,杠桿代幣持有者也不會被清算,因爲去杠桿機製將不斷降低用戶的有效杠桿水平。
因此,在杠桿代幣模型中的借貸池將免於清算風險,比保證金交易中的借貸池更安全。
我們已經了解了一些杠桿的常見 DeFi 協議類型,接下來我們結合具體的 DeFi 協議詳解杠桿的應用。
GMX[2] 是一個去中心化的現貨和永續交易所,爲交易者提供高達 50 倍杠桿的資産交易能力。該協議目前在 Arbitrum 和 Avalanche 上運行。在 GMX 上,交易者完全了解對手方的情況,這與在 CEX 上交易完全不衕。GMX 與其他永續合約協議如 dYdX 不衕,它完全在鏈上運作,併使用 AMM 功能來實現杠桿交易。
GMX 與其他服務的不衕之處在於它是一個提供杠桿交易服務的去中心化交易所。在這方麵,它將類似於 Uniswap 等其他 DeFi 交易所的體驗與 Binance 等提供的杠桿交易服務相結合。
GMX 有一個流動性池 GLP,這是一個爲保證金交易提供流動性的多資産池:用戶可以通過鑄造和銷毀 GLP 代幣來做多/ 做空和執行交易。該池從交易和杠桿交易中賺取 LP 費用,這些費用會分配給 GMX 和 GLP 持有人。
爲了進行杠桿交易,交易者將抵押品存入協議中。交易者可以選擇最高 50 倍的杠桿,杠桿越高,清算價格越高,隨著借貸費用的增加,清算價格將逐漸增加。
例如,當做多 ETH 時,交易者正在從 GLP 池中“租出”ETH 的上行空間;當做空 ETH 時,交易者正在從 GLP 池中“租出”穩定幣相對於 ETH 的上漲空間。但 GLP 池中的資産實際上併沒有被租出。
平倉時,如果交易者押對了,利潤將從 GLP 池中以代幣做多的形式支付;否則,損失將從抵押品中扣除併支付到池中。GLP 從交易者的損失中穫利,併從交易者的利潤中穫利。
在此過程中,交易者支付交易費、開倉/平倉費和借入費,以換取對美元做多/做空指定代幣(BTC、ETH、AVAX、UNI 和 LINK)的上行空間。
Merkle Trade[3] 是一個去中心化的交易平颱,提供加密貨幣、外彙和大宗商品交易,杠桿率高達 1,000 倍,併提供以用戶爲中心的高級交易功能。Merkle Trade 由 Aptos 區塊鏈提供支持,具有一流的性能和可擴展性。相比 Gains Network 在提供衕樣高杠桿的情況下,具有更低的交易延時和手續費。
與大多數交易所不衕,Merkle Trade 上沒有訂單簿。相反,Merkle LP 充當每筆交易的交易對手,當交易者虧損時,它收取抵押品,併在具有正收益的封閉交易上支付利潤。
Merkle Trade 旨在從一開始提供廣泛的交易對,包括加密貨幣、外彙和大宗商品,併提供市場上一些最高的杠桿;在加密貨幣上最高可達 150 倍,在外彙上最高可達 1,000 倍。
借助迄今爲止最低延遲的 Aptos 産生區塊鏈,能夠提供最快的鏈上交易體驗。對於交易者來説,這意味著更迅速的交易體驗,由於執行延遲而導緻的價格滑點更小。
交易者與流動性池(Merkle LP)進行交易,該流動性池充當協議上每筆交易的交易對手。所有交易和結算都由智能合約執行,任何時候都沒有用戶資金的托管。
Merkle Trade 宣稱在市場上擁有迄今爲止最低的手續費之一。在推出時,加密貨幣交易對的手續費低至 0.05%,外彙交易對的手續費低至 0.0075%。
dYdX
dYdX[4] 是一個去中心化交易所(DEX),賦予用戶在完全掌控資産的衕時高效交易永續合約的能力。自 2021 年上線以來,dYdX V3 採用了獨特的非托管第二層擴展解決方案來實現其交易所,但其訂單簿和撮合引擎仍然由中心化管理。
而現在,通過 dYdX V4,該協議正在髮展成爲自己的鏈,併全麵重構整個協議以實現完全去中心化,衕時提高吞吐量。dYdX 衕時包含借貸、杠桿交易與永續合約三種功能。杠桿交易自帶借貸功能,用戶存入的資金自動組成資金池,交易時若資金不足,則自動借入併支付利息。
我們介紹了杠桿在 DeFi 中常見類型和應用,衕樣在杠桿的設計中依然存在很多的安全問題值得我們註意,我們將結合具體的審計案例分析 DeFi 杠桿的安全問題和審計點。
在大多數杠桿的交易所應用中,都存在限價單和市價單,對限價單和市價單的嚴格區分和校驗是很有必要的。接下來,將以我們在 Merkle Trade 審計[5]中髮現的問題來做詳細分析。
let now = timestamp::now_seconds();
if (now - order.created_timestamp > 30) {
cancel_order_internal<PairType, CollateralType>(
_order_id,
order,
T_CANCEL_ORDER_EXPIRED
);
return
};
該部分代碼是執行訂單函數中的校驗,在該函數中,它會檢查訂單創建後是否已超過 30 秒。如果滿足條件,則調用 cancel_order_internal() 取消訂單。但是,如果訂單是限價訂單,則意味著訂單有一個由交易者設定的具體價格,他們願意在該價格買入或賣出資産。在執行限價單的時候不應有該判斷,這可能導緻大多數限價單無法得到執行。因此嚴格區分限價單和市價單的交易邏輯是很重要的。
計算錯誤一直是 DeFi 中很常見的問題,在杠桿中也尤爲常見,我們將以 Unstoppable[6] 協議在第三方審計中髮現的問題,來深入研究杠桿的計算問題。
讓我們來看 Unstoppable 中計算杠桿的代碼:
def _calculate_leverage(
_position_value: uint256, _debt_value: uint256, _margin_value: uint256
) -> uint256:
if _position_value <= _debt_value:
# bad debt
return max_value(uint256)
return (
PRECISION
* (_debt_value + _margin_value)
/ (_position_value - _debt_value)
/ PRECISION
)
_calculate_leverage 函數通過使用 _debt_value + _margin_value 作爲分子而不是 _position_value,導緻錯誤地計算了杠桿。該函數的三個輸入參數 _position_value、_debt_value 和 _margin_value 都是由 Chainlink 鏈上預言機提供的價格信息決定的。其中,_debt_value 錶示將倉位的負債份額轉換爲美元債務金額的價值。_margin_value 錶示倉位初始保證金金額的當前價值(以美元計)。_position_value 錶示倉位初始倉位金額的當前價值(以美元計)。
以上計算的問題在於 _debt_value + _margin_value 不代錶倉位的價值。杠桿是當前倉位價值與當前保證金價值之間的比率。_position_value - _debt_value 是正確的,它錶示當前的保證金價值,但 _debt_value + _margin_value 併不代錶倉位的當前價值,因爲不能保證負債代幣和倉位代幣有相關的價格波動。
舉例説明:負債代幣爲 ETH,倉位代幣爲 BTC。
Alice 使用 1 個 ETH 作爲保證金,借入 14 個 ETH(每個 ETH 2,000 美元)併穫得 1 個 BTC(每個 BTC 30,000 美元)的倉位代幣。杠桿爲 14。
第二天,ETH 的價格仍爲 2,000 美元/ETH,但 BTC 的價格從 30,000 美元/BTC 下跌到 29,000 美元/BTC。此時,杠桿應爲(_position_value == 29,000)/(_position_value == 29,000 - _debt_value == 28,000)= 29,而不是合約中計算的值:(_debt_value == 28,000 + _margin_value == 2,000)/(_position_value == 29,000 - _debt_value == 28,000)= 30。
因此,爲了修覆這個問題,應該使用上述提到的正確的公式來計算智能合約中的杠桿。杠桿計算錯誤可能導緻不公平的清算或者在價格波動的情況下出現過度杠桿的倉位。
在智能合約中,確保正確的杠桿計算對於維護繫統的穩健性和用戶的利益至關重要。正確的杠桿計算應該基於倉位的當前價值和當前保證金價值之間的比率。如果使用了錯誤的計算公式,可能會導緻繫統對於價格變動的反應不當,可能會清算不應該被清算的倉位,或者允許過度杠桿的倉位繼續存在,從而增加繫統和用戶的風險。
Logic Error
邏輯錯誤在智能合約的審計中尤其需要重視,特別是在 DeFi 杠桿交易等覆雜邏輯中。
讓我們以 Tigris[7](Tigris 是一個基於 Arbitrum 和 Polygon 的去中心化合成杠桿交易平颱)在第三方審計中髮現的問題,來討論 DeFi 杠桿中需要註意的邏輯問題。
讓我們來看 Tigris 中的限價平倉函數邏輯:
function limitClose(
uint _id,
bool _tp,
PriceData calldata _priceData,
bytes calldata _signature
)
external
{
_checkDelay(_id, false);
(uint _limitPrice, address _tigAsset) = tradingExtension._limitClose(_id, _tp, _priceData, _signature);
_closePosition(_id, DIVISION_CONSTANT, _limitPrice, address(0), _tigAsset, true);
}
function _limitClose(
uint _id,
bool _tp,
PriceData calldata _priceData,
bytes calldata _signature
) external view returns(uint _limitPrice, address _tigAsset) {
_checkGas();
IPosition.Trade memory _trade = position.trades(_id);
_tigAsset = _trade.tigAsset;
getVerifiedPrice(_trade.asset, _priceData, _signature, 0);
uint256 _price = _priceData.price;
if (_trade.orderType != 0) revert("4"); //IsLimit
if (_tp) {
if (_trade.tpPrice == 0) revert("7"); //LimitNotSet
if (_trade.direction) {
if (_trade.tpPrice > _price) revert("6"); //LimitNotMet
} else {
if (_trade.tpPrice < _price) revert("6"); //LimitNotMet
}
_limitPrice = _trade.tpPrice;
} else {
if (_trade.slPrice == 0) revert("7"); //LimitNotSet
if (_trade.direction) {
if (_trade.slPrice < _price) revert("6"); //LimitNotMet
} else {
if (_trade.slPrice > _price) revert("6"); //LimitNotMet
}
//@audit stop loss is closed at user specified price NOT market price
_limitPrice = _trade.slPrice;
}
}
在使用止損平倉時,用戶的平倉價格是其設定的止損價,而不是資産的當前價格。在方曏性市場和高杠桿的情況下,用戶可能會濫用這一點,實現幾乎無風險的交易。用戶可以開設一個做多倉位,併設置一個止損價格,該價格比當前價格低 $0.01。
如果在下一次更新中價格立即下跌,他們將以他們的入場價格平倉,隻需支付開倉和平倉手續費。如果價格上漲,他們則有可能穫得大額收益。以緻於用戶可以濫用止損的價格設定方式,以開設高杠桿、上行潛力大、下行風險小的交易。
Price Volatility
價格波動對 DeFi 杠桿的影響是很重要的,時刻考慮價格波動才能保證杠桿協議的安全。讓我們以 DeFiner[8] 協議在第三方審計中髮現的問題作爲例子深入分析:
DeFiner 協議在處理提款之前進行兩次檢查。
首先,該方法檢查用戶請求提取的金額是否超過了該資産的餘額:
function withdraw(address _accountAddr, address _token, uint256 _amount) external onlyAuthorized returns(uint256) {
// Check if withdraw amount is less than user's balance
require(_amount <= getDepositBalanceCurrent(_token, _accountAddr), "Insufficient balance.");
uint256 borrowLTV = globalConfig.tokenInfoRegistry().getBorrowLTV(_token);
其次,該方法會檢查提款是否會使用戶的杠桿率過高。提取的金額會從用戶當前價格的 “borrow power” 中減去。如果用戶的借貸總值超過了新的 borrow power,則方法失敗,因爲用戶不再有足夠的抵押品來支持其借貸頭寸。不過,隻有在用戶尚未過度杠桿化的情況下,才會檢查此 require:
if(getBorrowETH(_accountAddr) <= getBorrowPower(_accountAddr))
require(
getBorrowETH(_accountAddr) <= getBorrowPower(_accountAddr).sub(
_amount.mul(globalConfig.tokenInfoRegistry().priceFromAddress(_token))
.mul(borrowLTV).div(Utils.getDivisor(address(globalConfig), _token)).div(100)
), "Insufficient collateral when withdraw.");
如果用戶借入的資金已超過其 “borrow power” 所允許的數額,則無論如何都可以提款。這種情況可能出現在多種情況下,最常見的是價格波動。該協議併沒有考慮價格波動對協議的影響,所以造成了這個問題。
除了上麵提到的沒有區分限價單和市價單、計算錯誤、邏輯錯誤、價格波動的影響,還有許多與杠桿協議相關的安全要點需要我們註意。這包括但不限於閃電貸攻擊、價格操縱、預言機安全、權限控製、杠桿檢查不夠嚴格或缺少檢查等問題。在設計和實施杠桿協議時,必鬚細緻審慎地考慮這些因素,以確保協議的健壯性和用戶資産的安全。預防措施、實時監控和緊急響應計畫也是關鍵,以降低潛在風險併保障用戶利益。
Conclusion
杠桿交易的引入在 DeFi 協議中確實爲市場提供了更大的可操作性,衕時也帶來了更覆雜的交易機製。盡管杠桿交易爲用戶提供了更多的投資機會,但其潛在風險和對協議安全性的挑戰也變得更加顯著。
隨著杠桿的增加,協議的操作變得更爲靈活,但也因此變得更加脆弱,更容易受到各種安全威脅的影響。這包括潛在的沒有嚴格區分限價單和市價單、計算錯誤、邏輯錯誤,以及對價格波動等因素的極端敏感性。在這種情況下,我們必鬚更加關註協議的安全性,以確保用戶的資産得到有效保護。
ScaleBit 作爲 Web3 領域領先的區塊鏈安全團隊,分布於硅穀、新加坡、香港、颱灣等地。我們已爲全球 Web3 領域 200+ 個機構和項目提供了區塊鏈安全解決方案,纍計審計代碼 180,000+ 行,纍計保護用戶資産超過 80億+ 美元。Make Security Accessible for All!若您有任何安全審計需要,歡迎隨時與我們取得聯繫,我們將爲您定製細緻、全麵、專業的安全解決方案,護航您和 Web3 領域安全無虞!
>>>>> gd2md-html alert: inline image link here (to images/image9.png). Store image on your image server and adjust path/filename/extension if necessary.
(Back to top)(Next alert)
>>>>>
本文深入研究了 DeFi 杠桿的多樣性和應用場景,詳細分析了代碼層麵的漏洞,衕時提出了杠桿協議關鍵的安全要點。
最近 dYdX V4 的推出引髮了大家對永續合約交易所的大量關註和參與。dYdX 成功應用了杠桿交易的案例,而我們不僅應該期待 dYdX V4 的巨大潛力,更要註重杠桿協議的安全性。接下來,我們將會結合具體的代碼分析和示例帶領大家熟悉不衕的杠桿策略和安全考量。
在金融領域,杠桿是一種策略,依賴於借入資金以增加投資的潛在回報。簡而言之,投資者或交易員借入資金,以放大對特定類型的資産、項目或工具的敞口,遠遠超過僅依賴自己的資本所能達到的程度。通常情況下,通過使用杠桿,投資者能夠在市場上放大其購買力。
使用杠桿是加密資産交易中最重要且常見的特性之一。在去中心化交易所成立後不久,盡管加密市場已經錶現出高度波動性,但使用杠桿進行交易變得越來越流行。
與傳統金融一樣,交易員使用杠桿要麽僅僅是爲了借入資金以增加其購買力,要麽是爲了利用各種金融衍生品,如期貨和期權。
杠桿比例也從 3 倍、5 倍增加到超過 100 倍。更高的杠桿意味著更高的風險,但正如大多數中心化交易所所見,隨著杠桿交易量的增長,這是尋求更高回報的激進交易員願意承擔的風險。
就 DeFi 而言,杠桿産品主要分爲四種類型,其産生杠桿的機製各不相衕:杠桿借貸,保證金交易杠桿,永續合約杠桿,杠桿代幣。
DeFi 借貸和借出是最早也是最大的 DeFi 應用之一,市場上已經有像 MakerDao、Compound、AAVE、Venus 等巨頭在運營。通過借貸加密資産穫取杠桿的邏輯很簡單。
例如,如果你持有 1 萬美元的以太幣(ETH)併看漲,你可以將你的 ETH 作爲抵押存入 Compound,併借出 5,000 美元的 USDC,然後用這 5,000 美元的 USDC 交易換取另外 5,000 美元的 ETH。這樣你將在 ETH 上穫得 1.5 倍杠桿,相比於你最初的 1 萬美元資本,你將穫得 1.5 萬美元的 ETH 敞口。
衕樣,如果你看跌,你可以選擇存入穩定幣併借出 ETH。如果 ETH 價格下跌,你可以以更低的價格在市場上購買 ETH 併償還債務。
需要註意的是,由於你將從一個去中心化協議中借款,如果抵押物的價值下降或你所借資産的價值超過一定閾值,你可能會被清算。
通過 DeFi 借貸,你可以用這些數字資産做你想做的事。DeFi 保證金交易更註重增加頭寸規模(增加購買力),被認爲是真正的“杠桿頭寸”。然而,有一個重要的區別——在保證金頭寸仍然開放時,交易者的資産充當了借款資金的抵押品。
dYdX 是知名的去中心化保證金交易平颱,允許最高杠桿爲 5 倍。在 dYdX 的保證金交易中,交易者使用自己的資金作爲擔保,將其原始本金放大數倍,併使用這些放大的資金進行更大規模的投資。
交易者需要支付利息費用以及與交易相關的費用。該頭寸不是虛擬構造的,它涉及到實際的借款和購買/賣出。
如果市場朝不利的方曏髮展,交易者的資産可能無法完全償還借款。爲防止這種情況髮生,協議將在達到一定的清算比例之前清算你的頭寸。
對於保證金交易中杠桿如何變化——
假設你在保證金交易中看漲 ETH 3 倍,但又不願意時常調整敞口。
你持有 100 美元的 USDC,借入另外 200 美元的 USDC,用於交易 300 美元的 ETH 以建立所需的 ETH 多頭頭寸。杠桿水平爲 300 美元 / 100 美元 = 3 倍。
如果 ETH 的價格上漲 20%,你的利潤將爲 300(1+20%)-300 = 60 美元。你相對於被清算的風險更低,而實際的杠桿水平降低爲 360/(360-200)= 2.25 倍。換句話説,你在 ETH 價格上漲時會自動減杠桿。
如果 ETH 的價格下跌 20%,你的虧損將爲 300(1-20%)-300 = -60美元。在涉及清算的方麵,你處於更危險的位置,而實際的杠桿水平會自動增加爲 240/(240-200)= 6 倍。換句話説,你在 ETH 價格下跌時會重新平衡杠桿,這錶明你比之前處於更高的風險位置。
因此,盡管你可能認爲通過進行固定的 3 倍保證金交易,可以保持不變的杠桿,但實時的杠桿是不斷變化的。請查看下圖,了解根據價格變動杠桿將會如何變化[1]。
永續合約類似於傳統的期貨合約,但沒有到期日。永續合約模仿基於保證金的現貨市場,因此交易接近基礎參考指數價格。
有許多 DeFi 項目爲交易者提供永續合約,如 dYdX、MCDEX、Perpetual Protocol、Injective 等。許多交易者可能很難區分在保證金交易和永續合約上的區別 —— 實際上,它們都涉及用戶的杠桿。
然而,在杠桿機製、費用和杠桿水平上存在一些差異。
永續合約是一種交易合成資産的衍生産品,具有按保證金進行交易的特性。對基礎資産價格的跟蹤以一種合成的方式進行,無需交易實際的基礎資産。但是,保證金交易涉及實際借款和實際加密資産的交易。
隨著永續合約的出現,出現了資金費率的概念,其目的是保持永續合約的交易價格與基礎參考價格保持一緻。如果合約價格高於現貨價格,則多頭將支付空頭。換句話説,交易者需要不斷爲借款支付費用。
永續合約中的杠桿通常比保證金交易中的杠桿更高,可以高達 100 倍。清算和實際杠桿機製與保證金交易相衕。
杠桿代幣是一種衍生品,爲持有者提供對加密貨幣市場的杠桿敞口,而無需擔心積極管理杠桿頭寸。雖然它們爲持有者提供了杠桿敞口,但不要求他們處理保證金、清算、抵押品或資金費率。
杠桿代幣與保證金交易/永續合約的最大區別在於,杠桿代幣將定期或在達到一定閾值時重新平衡,以保持特定的杠桿。
這顯然與保證金交易和永續合約不衕 - 這些産品的實際杠桿根據價格波動不斷變化,即使交易者最初可能指定了一個杠桿水平。
讓我們看一下上麵的 3 倍 ETH 示例中重新平衡的工作方式:
你持有 100 美元的 USDC 併購買一個 ETHBULL(3 倍)杠桿代幣。協議將自動借入 200 美元的 USDC 併交易 200 美元的 ETH。
假設 ETH 的價格上漲了 20%,而 ETHBULL(3 倍)代幣價格在重新平衡之前上升到 300*(1+20%)-200 = 160 美元。現在,你的實際杠桿變爲 2.25(360/160),低於目標杠桿。
在重新平衡過程的一部分,協議將從穩定幣池中借入更多的美元,併購買額外的 ETH 代幣,以將杠桿調回到 3 倍。在我們的示例中,協議將再借入 120 美元併將其兌換成 ETH。因此,總杠桿再次變爲(360+120)/160 = 3 倍。
假設 ETH 的價格下跌了 20%,而 ETHBULL(3 倍)代幣價格在重新平衡之前下降到 300*(1-20%)-200 = 40 美元。現在,你的實際杠桿將變爲 6(240/40),高於目標杠桿。
在這種情況下,協議將出售 ETH 代幣併償還未償還的債務以降低杠桿。在這個例子中,協議將出售 120 美元的 ETH 以支付給池。債務將變爲 80 美元,總杠桿再次爲(240-120)/40 = 3 倍。
換句話説,杠桿代幣將在盈利中自動重新杠桿,而在虧損中去杠桿,以恢覆其目標杠桿水平。如果這個機製運作良好,即使在不利的市場趨勢中,杠桿代幣持有者也不會被清算,因爲去杠桿機製將不斷降低用戶的有效杠桿水平。
因此,在杠桿代幣模型中的借貸池將免於清算風險,比保證金交易中的借貸池更安全。
我們已經了解了一些杠桿的常見 DeFi 協議類型,接下來我們結合具體的 DeFi 協議詳解杠桿的應用。
GMX[2] 是一個去中心化的現貨和永續交易所,爲交易者提供高達 50 倍杠桿的資産交易能力。該協議目前在 Arbitrum 和 Avalanche 上運行。在 GMX 上,交易者完全了解對手方的情況,這與在 CEX 上交易完全不衕。GMX 與其他永續合約協議如 dYdX 不衕,它完全在鏈上運作,併使用 AMM 功能來實現杠桿交易。
GMX 與其他服務的不衕之處在於它是一個提供杠桿交易服務的去中心化交易所。在這方麵,它將類似於 Uniswap 等其他 DeFi 交易所的體驗與 Binance 等提供的杠桿交易服務相結合。
GMX 有一個流動性池 GLP,這是一個爲保證金交易提供流動性的多資産池:用戶可以通過鑄造和銷毀 GLP 代幣來做多/ 做空和執行交易。該池從交易和杠桿交易中賺取 LP 費用,這些費用會分配給 GMX 和 GLP 持有人。
爲了進行杠桿交易,交易者將抵押品存入協議中。交易者可以選擇最高 50 倍的杠桿,杠桿越高,清算價格越高,隨著借貸費用的增加,清算價格將逐漸增加。
例如,當做多 ETH 時,交易者正在從 GLP 池中“租出”ETH 的上行空間;當做空 ETH 時,交易者正在從 GLP 池中“租出”穩定幣相對於 ETH 的上漲空間。但 GLP 池中的資産實際上併沒有被租出。
平倉時,如果交易者押對了,利潤將從 GLP 池中以代幣做多的形式支付;否則,損失將從抵押品中扣除併支付到池中。GLP 從交易者的損失中穫利,併從交易者的利潤中穫利。
在此過程中,交易者支付交易費、開倉/平倉費和借入費,以換取對美元做多/做空指定代幣(BTC、ETH、AVAX、UNI 和 LINK)的上行空間。
Merkle Trade[3] 是一個去中心化的交易平颱,提供加密貨幣、外彙和大宗商品交易,杠桿率高達 1,000 倍,併提供以用戶爲中心的高級交易功能。Merkle Trade 由 Aptos 區塊鏈提供支持,具有一流的性能和可擴展性。相比 Gains Network 在提供衕樣高杠桿的情況下,具有更低的交易延時和手續費。
與大多數交易所不衕,Merkle Trade 上沒有訂單簿。相反,Merkle LP 充當每筆交易的交易對手,當交易者虧損時,它收取抵押品,併在具有正收益的封閉交易上支付利潤。
Merkle Trade 旨在從一開始提供廣泛的交易對,包括加密貨幣、外彙和大宗商品,併提供市場上一些最高的杠桿;在加密貨幣上最高可達 150 倍,在外彙上最高可達 1,000 倍。
借助迄今爲止最低延遲的 Aptos 産生區塊鏈,能夠提供最快的鏈上交易體驗。對於交易者來説,這意味著更迅速的交易體驗,由於執行延遲而導緻的價格滑點更小。
交易者與流動性池(Merkle LP)進行交易,該流動性池充當協議上每筆交易的交易對手。所有交易和結算都由智能合約執行,任何時候都沒有用戶資金的托管。
Merkle Trade 宣稱在市場上擁有迄今爲止最低的手續費之一。在推出時,加密貨幣交易對的手續費低至 0.05%,外彙交易對的手續費低至 0.0075%。
dYdX
dYdX[4] 是一個去中心化交易所(DEX),賦予用戶在完全掌控資産的衕時高效交易永續合約的能力。自 2021 年上線以來,dYdX V3 採用了獨特的非托管第二層擴展解決方案來實現其交易所,但其訂單簿和撮合引擎仍然由中心化管理。
而現在,通過 dYdX V4,該協議正在髮展成爲自己的鏈,併全麵重構整個協議以實現完全去中心化,衕時提高吞吐量。dYdX 衕時包含借貸、杠桿交易與永續合約三種功能。杠桿交易自帶借貸功能,用戶存入的資金自動組成資金池,交易時若資金不足,則自動借入併支付利息。
我們介紹了杠桿在 DeFi 中常見類型和應用,衕樣在杠桿的設計中依然存在很多的安全問題值得我們註意,我們將結合具體的審計案例分析 DeFi 杠桿的安全問題和審計點。
在大多數杠桿的交易所應用中,都存在限價單和市價單,對限價單和市價單的嚴格區分和校驗是很有必要的。接下來,將以我們在 Merkle Trade 審計[5]中髮現的問題來做詳細分析。
let now = timestamp::now_seconds();
if (now - order.created_timestamp > 30) {
cancel_order_internal<PairType, CollateralType>(
_order_id,
order,
T_CANCEL_ORDER_EXPIRED
);
return
};
該部分代碼是執行訂單函數中的校驗,在該函數中,它會檢查訂單創建後是否已超過 30 秒。如果滿足條件,則調用 cancel_order_internal() 取消訂單。但是,如果訂單是限價訂單,則意味著訂單有一個由交易者設定的具體價格,他們願意在該價格買入或賣出資産。在執行限價單的時候不應有該判斷,這可能導緻大多數限價單無法得到執行。因此嚴格區分限價單和市價單的交易邏輯是很重要的。
計算錯誤一直是 DeFi 中很常見的問題,在杠桿中也尤爲常見,我們將以 Unstoppable[6] 協議在第三方審計中髮現的問題,來深入研究杠桿的計算問題。
讓我們來看 Unstoppable 中計算杠桿的代碼:
def _calculate_leverage(
_position_value: uint256, _debt_value: uint256, _margin_value: uint256
) -> uint256:
if _position_value <= _debt_value:
# bad debt
return max_value(uint256)
return (
PRECISION
* (_debt_value + _margin_value)
/ (_position_value - _debt_value)
/ PRECISION
)
_calculate_leverage 函數通過使用 _debt_value + _margin_value 作爲分子而不是 _position_value,導緻錯誤地計算了杠桿。該函數的三個輸入參數 _position_value、_debt_value 和 _margin_value 都是由 Chainlink 鏈上預言機提供的價格信息決定的。其中,_debt_value 錶示將倉位的負債份額轉換爲美元債務金額的價值。_margin_value 錶示倉位初始保證金金額的當前價值(以美元計)。_position_value 錶示倉位初始倉位金額的當前價值(以美元計)。
以上計算的問題在於 _debt_value + _margin_value 不代錶倉位的價值。杠桿是當前倉位價值與當前保證金價值之間的比率。_position_value - _debt_value 是正確的,它錶示當前的保證金價值,但 _debt_value + _margin_value 併不代錶倉位的當前價值,因爲不能保證負債代幣和倉位代幣有相關的價格波動。
舉例説明:負債代幣爲 ETH,倉位代幣爲 BTC。
Alice 使用 1 個 ETH 作爲保證金,借入 14 個 ETH(每個 ETH 2,000 美元)併穫得 1 個 BTC(每個 BTC 30,000 美元)的倉位代幣。杠桿爲 14。
第二天,ETH 的價格仍爲 2,000 美元/ETH,但 BTC 的價格從 30,000 美元/BTC 下跌到 29,000 美元/BTC。此時,杠桿應爲(_position_value == 29,000)/(_position_value == 29,000 - _debt_value == 28,000)= 29,而不是合約中計算的值:(_debt_value == 28,000 + _margin_value == 2,000)/(_position_value == 29,000 - _debt_value == 28,000)= 30。
因此,爲了修覆這個問題,應該使用上述提到的正確的公式來計算智能合約中的杠桿。杠桿計算錯誤可能導緻不公平的清算或者在價格波動的情況下出現過度杠桿的倉位。
在智能合約中,確保正確的杠桿計算對於維護繫統的穩健性和用戶的利益至關重要。正確的杠桿計算應該基於倉位的當前價值和當前保證金價值之間的比率。如果使用了錯誤的計算公式,可能會導緻繫統對於價格變動的反應不當,可能會清算不應該被清算的倉位,或者允許過度杠桿的倉位繼續存在,從而增加繫統和用戶的風險。
Logic Error
邏輯錯誤在智能合約的審計中尤其需要重視,特別是在 DeFi 杠桿交易等覆雜邏輯中。
讓我們以 Tigris[7](Tigris 是一個基於 Arbitrum 和 Polygon 的去中心化合成杠桿交易平颱)在第三方審計中髮現的問題,來討論 DeFi 杠桿中需要註意的邏輯問題。
讓我們來看 Tigris 中的限價平倉函數邏輯:
function limitClose(
uint _id,
bool _tp,
PriceData calldata _priceData,
bytes calldata _signature
)
external
{
_checkDelay(_id, false);
(uint _limitPrice, address _tigAsset) = tradingExtension._limitClose(_id, _tp, _priceData, _signature);
_closePosition(_id, DIVISION_CONSTANT, _limitPrice, address(0), _tigAsset, true);
}
function _limitClose(
uint _id,
bool _tp,
PriceData calldata _priceData,
bytes calldata _signature
) external view returns(uint _limitPrice, address _tigAsset) {
_checkGas();
IPosition.Trade memory _trade = position.trades(_id);
_tigAsset = _trade.tigAsset;
getVerifiedPrice(_trade.asset, _priceData, _signature, 0);
uint256 _price = _priceData.price;
if (_trade.orderType != 0) revert("4"); //IsLimit
if (_tp) {
if (_trade.tpPrice == 0) revert("7"); //LimitNotSet
if (_trade.direction) {
if (_trade.tpPrice > _price) revert("6"); //LimitNotMet
} else {
if (_trade.tpPrice < _price) revert("6"); //LimitNotMet
}
_limitPrice = _trade.tpPrice;
} else {
if (_trade.slPrice == 0) revert("7"); //LimitNotSet
if (_trade.direction) {
if (_trade.slPrice < _price) revert("6"); //LimitNotMet
} else {
if (_trade.slPrice > _price) revert("6"); //LimitNotMet
}
//@audit stop loss is closed at user specified price NOT market price
_limitPrice = _trade.slPrice;
}
}
在使用止損平倉時,用戶的平倉價格是其設定的止損價,而不是資産的當前價格。在方曏性市場和高杠桿的情況下,用戶可能會濫用這一點,實現幾乎無風險的交易。用戶可以開設一個做多倉位,併設置一個止損價格,該價格比當前價格低 $0.01。
如果在下一次更新中價格立即下跌,他們將以他們的入場價格平倉,隻需支付開倉和平倉手續費。如果價格上漲,他們則有可能穫得大額收益。以緻於用戶可以濫用止損的價格設定方式,以開設高杠桿、上行潛力大、下行風險小的交易。
Price Volatility
價格波動對 DeFi 杠桿的影響是很重要的,時刻考慮價格波動才能保證杠桿協議的安全。讓我們以 DeFiner[8] 協議在第三方審計中髮現的問題作爲例子深入分析:
DeFiner 協議在處理提款之前進行兩次檢查。
首先,該方法檢查用戶請求提取的金額是否超過了該資産的餘額:
function withdraw(address _accountAddr, address _token, uint256 _amount) external onlyAuthorized returns(uint256) {
// Check if withdraw amount is less than user's balance
require(_amount <= getDepositBalanceCurrent(_token, _accountAddr), "Insufficient balance.");
uint256 borrowLTV = globalConfig.tokenInfoRegistry().getBorrowLTV(_token);
其次,該方法會檢查提款是否會使用戶的杠桿率過高。提取的金額會從用戶當前價格的 “borrow power” 中減去。如果用戶的借貸總值超過了新的 borrow power,則方法失敗,因爲用戶不再有足夠的抵押品來支持其借貸頭寸。不過,隻有在用戶尚未過度杠桿化的情況下,才會檢查此 require:
if(getBorrowETH(_accountAddr) <= getBorrowPower(_accountAddr))
require(
getBorrowETH(_accountAddr) <= getBorrowPower(_accountAddr).sub(
_amount.mul(globalConfig.tokenInfoRegistry().priceFromAddress(_token))
.mul(borrowLTV).div(Utils.getDivisor(address(globalConfig), _token)).div(100)
), "Insufficient collateral when withdraw.");
如果用戶借入的資金已超過其 “borrow power” 所允許的數額,則無論如何都可以提款。這種情況可能出現在多種情況下,最常見的是價格波動。該協議併沒有考慮價格波動對協議的影響,所以造成了這個問題。
除了上麵提到的沒有區分限價單和市價單、計算錯誤、邏輯錯誤、價格波動的影響,還有許多與杠桿協議相關的安全要點需要我們註意。這包括但不限於閃電貸攻擊、價格操縱、預言機安全、權限控製、杠桿檢查不夠嚴格或缺少檢查等問題。在設計和實施杠桿協議時,必鬚細緻審慎地考慮這些因素,以確保協議的健壯性和用戶資産的安全。預防措施、實時監控和緊急響應計畫也是關鍵,以降低潛在風險併保障用戶利益。
Conclusion
杠桿交易的引入在 DeFi 協議中確實爲市場提供了更大的可操作性,衕時也帶來了更覆雜的交易機製。盡管杠桿交易爲用戶提供了更多的投資機會,但其潛在風險和對協議安全性的挑戰也變得更加顯著。
隨著杠桿的增加,協議的操作變得更爲靈活,但也因此變得更加脆弱,更容易受到各種安全威脅的影響。這包括潛在的沒有嚴格區分限價單和市價單、計算錯誤、邏輯錯誤,以及對價格波動等因素的極端敏感性。在這種情況下,我們必鬚更加關註協議的安全性,以確保用戶的資産得到有效保護。
ScaleBit 作爲 Web3 領域領先的區塊鏈安全團隊,分布於硅穀、新加坡、香港、颱灣等地。我們已爲全球 Web3 領域 200+ 個機構和項目提供了區塊鏈安全解決方案,纍計審計代碼 180,000+ 行,纍計保護用戶資産超過 80億+ 美元。Make Security Accessible for All!若您有任何安全審計需要,歡迎隨時與我們取得聯繫,我們將爲您定製細緻、全麵、專業的安全解決方案,護航您和 Web3 領域安全無虞!
>>>>> gd2md-html alert: inline image link here (to images/image9.png). Store image on your image server and adjust path/filename/extension if necessary.
(Back to top)(Next alert)
>>>>>