iT邦幫忙

2023 iThome 鐵人賽

DAY 30
0
Web 3

Web3新手初探筆記系列 第 31

實作簡易DEX ep.4

  • 分享至 

  • xImage
  •  

Uniswap最主要的公式 xy=k

而計算發行LP token會需要將k開根號以維持線性關係

當每次有人添加流動性時都會有手續費,因此多出的手續費資金將會填入流動性中

k1=原本投入的流動性 k2=收取手續費後的添加的流動性

S1=原本發給流動性提供者的LP token數量

Sm=增發的LP token數量

目標:求Sm

項目方全拿

當項目方全拿的話,會按比例增發token且全部都項目方拿走

Z為比例 = 100% = 1

因此可以得到公式:
https://chart.googleapis.com/chart?cht=tx&chl=%7BSm%5Cover%20Sm%2BS1%7D%3D%7B%5Csqrt%20k2-%5Csqrt%20k1%20%5Cover%20%5Csqrt%20k2%20%7DZ
帶入值可以得出Sm

流動性提供者全拿

當流動性提供者全拿的話,不會增發token,因此每一token可以換得的資金變多

Z為比例 = 0% = 0

https://chart.googleapis.com/chart?cht=tx&chl=%7BSm%5Cover%20Sm%2BS1%7D%3D%7B%5Csqrt%20k2-%5Csqrt%20k1%20%5Cover%20%5Csqrt%20k2%20%7DZ

因此Sm = 0

項目方拿走部分獎勵

當項目方拿走部分的話,會按比例增發token且全部都項目方拿走

Z為比例 = Z%

因此可以得到公式:
https://chart.googleapis.com/chart?cht=tx&chl=%7BSm%5Cover%20Sm%2BS1%7D%3D%7B%5Csqrt%20k2-%5Csqrt%20k1%20%5Cover%20%5Csqrt%20k2%20%7DZ
可看成LP token增發比例 等於 流動性增加比例乘上%數 (乘上%數讓流動性增加比例下降了,類似於打折)

將%數帶入Z可以得到Sm,即增發的token

V2中項目方拿走的是%數是1/6,因此帶入1/6

V2中求增發token公式

https://chart.googleapis.com/chart?cht=tx&chl=Sm%3D%7B(%5Csqrt%20k2%20-%20%5Csqrt%20k1)S1%20%5Cover%205%5Csqrt%20k2%20%2B%20%5Csqrt%20k1%7D

增發LPtoken程式碼(如果是兌換時收手續費)

兌換函式中添加

//扣除手續費
_newAmountIn = (_amountIn * 997) / 1000;
//計算原始換出量
_amountOut = (_newAmountIn * reserveOut) / (_newAmountIn + reserveIn);
//兌換
function swap (address _tokenIn, address _tokenOut, uint _amountIn) 
external 
returns (uint _amountOut){
require(_tokenIn != address(0) && _tokenOut != address(0),"Address can not be 0x00");
require(_tokenIn != _tokenOut,"Address can not be the same");
//LPtoken池子地址
address LPtokenAddress = checkLPtokenAddress[_tokenIn][_tokenOut];
//返回數量
uint reserveIn = reserve[LPtokenAddress][_tokenIn];
uint reserveOut = reserve[LPtokenAddress][_tokenOut];
//原始流動性
uint k = reserveIn * reserveOut;
//扣除手續費
uint _newAmountIn = (_amountIn * 997) / 1000;
//計算換出量
_amountOut = (_newAmountIn * reserveOut) / (_newAmountIn + reserveIn); 
//轉移ERC20
ERC20 token0 = ERC20(_tokenIn);
ERC20 token1 = ERC20(_tokenOut);
token0.transferFrom(msg.sender, address(this), _amountIn);
token1.transfer(msg.sender, _amountOut);
//池內數量調整
reserve[LPtokenAddress][_tokenIn] += _amountIn;
reserve[LPtokenAddress][_tokenOut] -= _amountOut;
//執行增發代幣
caculateFee(LPtokenAddress, _tokenIn, _tokenOut, k);
emit Swap(_tokenIn, _tokenOut, _amountIn, _amountOut);
}      

由於Solidity沒有浮點數,當開根號時有小數點會被無條件捨去,因此在添加流動性公式時先將token數量都先放大,都乘上1e8,在兌換函式中最後調用了計算增發代幣的函式

    //計算手續費並紀錄
    function caculateFee (address LPtokenAddress, address _tokenIn, address _tokenOut, uint k) private returns(uint){
    //返回數量
    uint reserve0 = reserve[LPtokenAddress][_tokenIn];
    uint reserve1 = reserve[LPtokenAddress][_tokenOut];
    //新的流動性
    uint newK = reserve0 * reserve1;
    //增發數量
    ERC20 LP = ERC20(LPtokenAddress);
    uint totalSupply = LP.totalSupply();
    uint sm = ((_sqrt(newK) - _sqrt(k)) * totalSupply) / ((5 * _sqrt(newK)) + _sqrt(k));
    //紀錄獎勵代幣
    checkLPtokenOwner[LPtokenAddress] += sm;
    return sm;
}

這部分得出的數字先用mapping記錄下來,LPtoken小數部分為八位數,因此一顆為1e8單位。所以會另外寫一個發行的函式。

    //執行增發獎勵
    function mintLPtokenForOwner (address LPtokenAddress) public onlyOwner(){
    //取得增發獎勵
    uint sm = checkLPtokenOwner[LPtokenAddress];
    //鑄造獎勵代幣
    LPtoken lpTokenInstance = LPtoken(LPtokenAddress);
    lpTokenInstance.mint(owner,sm);
    //減去發行數量
    checkLPtokenOwner[LPtokenAddress] -= sm;
    emit MintLPtokenForOwner(sm);
}

此函式要加上onlyOwner。

完整版合約:
https://github.com/AaaronJasper/Smart-contract-practice/blob/main/contract/AMM.sol
有更新的再話新增新的episode

更:從10月17日開始,對一組特定代幣的交易(換幣)收取0.15%的固定interface fee(界面費用),即使用錢堆收費,使用協議則不改變。


上一篇
零知識證明
下一篇
模塊化區塊鏈
系列文
Web3新手初探筆記32
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言