了解 pancakeSwap Pair 合約中的 Swap 功能
嗨,我正在嘗試了解
swap
pancakeSwap DEX 上煎餅對的功能背後的原因以及它所做的檢查。這是功能:
require(amount0Out > 0 || amount1Out > 0, 'Pancake: INSUFFICIENT_OUTPUT_AMOUNT'); (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings require(amount0Out < _reserve0 && amount1Out < _reserve1, 'Pancake: INSUFFICIENT_LIQUIDITY'); uint balance0; uint balance1; { // scope for _token{0,1}, avoids stack too deep errors address _token0 = token0; address _token1 = token1; require(to != _token0 && to != _token1, 'Pancake: INVALID_TO'); if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); // optimistically transfer tokens if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); // optimistically transfer tokens if (data.length > 0) IPancakeCallee(to).pancakeCall(msg.sender, amount0Out, amount1Out, data); balance0 = IERC20(_token0).balanceOf(address(this)); balance1 = IERC20(_token1).balanceOf(address(this)); } uint amount0In = balance0 > _reserve0 - amount0Out ? balance0 - (_reserve0 - amount0Out) : 0; uint amount1In = balance1 > _reserve1 - amount1Out ? balance1 - (_reserve1 - amount1Out) : 0; require(amount0In > 0 || amount1In > 0, 'Pancake: INSUFFICIENT_INPUT_AMOUNT'); { // scope for reserve{0,1}Adjusted, avoids stack too deep errors uint balance0Adjusted = balance0.mul(1000).sub(amount0In.mul(2)); uint balance1Adjusted = balance1.mul(1000).sub(amount1In.mul(2)); require(balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(1000**2), 'Pancake: K'); } _update(balance0, balance1, _reserve0, _reserve1); emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to);
balance0
Q1:在上面的程式碼中, and 和 and之間有區別,它們不一樣嗎reserveO
?對合約的餘額是費用的累積嗎?balance1``reserve1
Q2 : 我也想知道amountOIn,為什麼會這樣計算?
Q3 :
Pancake: K
錯誤是什麼,為什麼會這樣計算?
balance0
和balance1
是函式的局部變數,而 和mint
是burn
全域swap
聲明的變數。reserve0``reserve1
為什麼需要計算
balance
和reserve
?另一個複雜因素是,有人可以將資產發送到配對合約——從而改變其餘額和邊際價格——而不與它互動,因此不會觸發預言機更新。如果合約只是檢查自己的餘額並根據目前價格更新預言機,攻擊者可以通過在區塊中第一次呼叫合約之前立即向合約發送資產來操縱預言機。如果最後一筆交易發生在時間戳為 X 秒前的區塊中,則合約會在累積之前錯誤地將新價格乘以 X,即使沒有人有機會以該價格進行交易。為了防止這種情況,核心合約在每次互動後記憶體其儲備,並使用從緩儲存備而不是目前儲備中得出的價格更新預言機。
用簡單的話來說,
在每次
swap
呼叫中,首先獲取兩個代幣的儲備金,然後樂觀地轉移代幣(以允許快速交換),然後有條件檢查交換是否合法,包括費用和代幣數量變化後的調整餘額。最後,_update
呼叫該函式根據交換後的新余額和舊準備金更新準備金。儲備金也會在
mint
、burn
和上更新sync
。如果僅在 AMM 功能之後才沒有提供更新儲備,則會導致不使用 AMM 功能直接轉移代幣(它們是對的一部分)的攻擊向量,並可能使貨幣更便宜並導致無法使用的不平衡作為神諭。
是如何計算
amount0In
的?amount1In
amount0In
並且amount1In
是局部變數,用於跟踪特定交換期間每個代幣流入的數量。簡單來說,它正在檢查餘額(在轉移/接收之後)是否比儲備大 amount0Out(如果該代幣被存入,因為儲備在最後一次交換後沒有更新),那麼 amount0In 是balance0 (total after inflow) + amount0Out(outflow) - _reserve0(last_updated)
。通常,只有 pair 的任何一個令牌從 AMM 發送出去,在這種情況下,
amount0Out
或者amount1Out
,假設amount0Out
為 0,所以 amount0In 變為balance0 (total after inflow) - _reserve0(last_updated)
。情況並非總是如此,而且兩者都不能為 0,但更容易視覺化。如果餘額(在轉移/接收之後)不大於儲備,
amount0Out
那麼我們可以確定其淨代幣數量已被轉出,因此amount0In
在這種情況下將為 0。這並不意味著特定的代幣沒有轉移到 AMM,而是從 AMM 轉出的數量多於轉入 AMM 的數量。無論哪種方式,如果轉出多於轉入,則認為0
它不會對費用和預言機產生影響,因為它們是根據淨變化計算的!什麼是
Pancake: K
(或UniswapV2: K
)誤差,為什麼這樣計算?Uniswap 常數乘積公式為“X * Y = K”。其中 X 和 Y 代表兩個 ERC-20 代幣各自的儲備餘額,“K”代表儲備的乘積。“K”錯誤所指的正是這個“K”。
本質上,“K”錯誤意味著嘗試進行的交易以某種方式使交易對的儲備金少於應有的儲備金,因此交易被撤銷。
這可能有幾個不同的原因。