Contract-Design

了解 pancakeSwap Pair 合約中的 Swap 功能

  • June 30, 2021

嗨,我正在嘗試了解swappancakeSwap 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);

balance0Q1:在上面的程式碼中, and 和 and之間有區別,它們不一樣嗎reserveO?對合約的餘額是費用的累積嗎?balance1``reserve1

Q2 : 我也想知道amountOIn,為什麼會這樣計算?

Q3 :Pancake: K錯誤是什麼,為什麼會這樣計算?

balance0balance1是函式的局部變數,而 和mintburn全域swap聲明的變數。reserve0``reserve1

為什麼需要計算balancereserve

UniswapV2 白皮書中所述

另一個複雜因素是,有人可以將資產發送到配對合約——從而改變其餘額和邊際價格——而不與它互動,因此不會觸發預言機更新。如果合約只是檢查自己的餘額並根據目前價格更新預言機,攻擊者可以通過在區塊中第一次呼叫合約之前立即向合約發送資產來操縱預言機。如果最後一筆交易發生在時間戳為 X 秒前的區塊中,則合約會在累積之前錯誤地將新價格乘以 X,即使沒有人有機會以該價格進行交易。為了防止這種情況,核心合約在每次互動後記憶體其儲備,並使用從緩儲存備而不是目前儲備中得出的價格更新預言機。

用簡單的話來說,

在每次swap呼叫中,首先獲取兩個代幣的儲備金,然後樂觀地轉移代幣(以允許快速交換),然後有條件檢查交換是否合法,包括費用和代幣數量變化後的調整餘額。最後,_update呼叫該函式根據交換後的新余額和舊準備金更新準備金。

儲備金也會在mintburn和上更新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)誤差,為什麼這樣計算?

來自UniswapV2 文件

Uniswap 常數乘積公式為“X * Y = K”。其中 X 和 Y 代表兩個 ERC-20 代幣各自的儲備餘額,“K”代表儲備的乘積。“K”錯誤所指的正是這個“K”。

本質上,“K”錯誤意味著嘗試進行的交易以某種方式使交易對的儲備金少於應有的儲備金,因此交易被撤銷。

這可能有幾個不同的原因。

引用自:https://ethereum.stackexchange.com/questions/102355