中心化交易所的安全風(fēng)險
在中心化交易所中注冊賬戶時,是由交易所生成一個地址,用戶可以向地址充幣,充到地址之后交易所就會根據(jù)用戶充幣的數(shù)量顯示在管理界面中。但是充幣的地址是掌管在交易所之中的,資產(chǎn)的控制權(quán)還是在交易所。
案例:Mt Gox、FTX 等…
中心化交易所交易模式-訂單薄模式
中心化交易所會提供一個交易對,價格由市場決定。買賣分開排序。
- 買面板 價格按照遞減排序 買入價格越高就在面板最高位置 27520.48
- 賣面板 價格按照遞減排序 賣出價格最低放在面板最低位置 27520.49
- 買面板最高位置(買入最高價)和賣面板最低位置(賣出最低價)會組合在一起,這時市場價就會在這兩者之間的價格
如果市場開始波動 - 市場上漲時(希望買入),買入市場用戶發(fā)現(xiàn)自己的買入價格和最低的賣出價格只差0.1,可能就會加錢將賣出價格最低的幾個賣出訂單買入,價格會更加偏向更高的賣出訂單價格
- 市場下跌時(希望賣出),賣出用戶可能會選擇將自己的價格下調(diào)0.1,從而達(dá)成賣出貨幣,這時價格會更加傾向于買入價格。
DEX
去中心化交易所(Decentralized exchange)簡稱為DEX
Uniswap
uniswap的核心是Automated market Maker。
例如構(gòu)建一個 TokenA/USDT兌換對
market maker:做市商
Liquidity 流動性:向兩邊都注入資金才能維護(hù)交易的正常運(yùn)行
Liqyuidity Provider(LP):注入資金的人
去中心化交易所核心要素:
- 任何人都可以添加流動性,成為LP,并拿到LP token
- LP在任意時間可以移除流動性并銷毀LP Token,拿回自己的Token
- 用戶可以基于交易池來進(jìn)行交易
- 交易時收取一定的手續(xù)費(fèi),并且分配給LP
恒定乘積自動做商 Constant Product Automated Market Maker
x*y=k=(x+Δx) * (y-Δy)
-
交換 交易數(shù)量的確定
買Δx 求Δy
xy=(x+Δx) * (Y-Δy) = xy- xΔy +Δx * y-ΔxΔy=k
xy= xy- xΔy +Δx * y-ΔxΔy
xΔy+ΔxΔy=Δx * y
Δy=Δx * y/(x+Δx) -
添加流動性
x+Δx ,y+Δy
添加之后x/y=(x+Δx)/(y+Δy)如果手中有Δx,怎么添加Δy呢?
公式變形:xy+xΔy=xy+Δxy => xΔy=yΔx
Δy=(yΔx)/x
也就是Δx/Δy=x/y 也就是對于xy兩邊從池子添加的比例要相同怎么衡量池子中的流動性?答: x y \sqrt{xy} xy?
添加liquidity之后能夠拿到LPtoken作為憑證,稱為share,在添加之后所有l(wèi)iquidity的share是T(total supply),之后對liquidity添加了S的流動性
L0:添加之前的流動性 T
L1:添加之后的流動性 T+S
L0/L1=T/(T+S)
引起流動性變化,能夠拿到多少share?
S=(L1-L0)T/L0 =( ( x + Δ x ) ( y + Δ y ) ? x y x y \sqrt{(x+Δx)(y+Δy)}-\sqrt{xy}\over \sqrt{xy} xy?(x+Δx)(y+Δy)??xy??)T=( ( x + Δ x ) ( y + Δ y ) ? x y x y \sqrt{(x+Δx)(y+Δy)}-\sqrt{xy}\over \sqrt{xy} xy?(x+Δx)(y+Δy)??xy??)T= Δ x x T \frac{Δx}{x}T xΔx?T= Δ y y T \frac{Δy}{y}T yΔy?T -
移除流動性(例如手中有x份share,在移除的時候,能拿到多少x和y的幣)
其中需要知道 S 、T(移除之前l(fā)iquidity的total supply)、L(當(dāng)前l(fā)iquidity)
也就是移除之前的流動性和流行性token以及手中的share,放入之后能夠兌換多少xy的數(shù)量Δ x ? Δ y x y \sqrt{Δx*Δy}\over{\sqrt{xy}} xy?Δx?Δy??= S T S\over{T} TS?
Δx=x S T S\over{T} TS?
Δy=y S T S\over{T} TS?
代碼
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "IERC20.sol";
contract CPAMM{
IERC20 public immutable token0;
IERC20 public immutable token1;
uint public reserve0;//token0 amount in contract == x
uint public reserve1;//token1 amount in contract == y
uint public totalSupply; //lp token amount of all
mapping(address=>uint) public balanceOf; //每個地址對應(yīng)的LP余額
constructor(address _token0,address _token1){
token0=IERC20(_token0);
token1=IERC20(_token1);
}
// 更新余額表
function _updata(uint _reserve0,uint _reserve1) private {
reserve0=_reserve0;
reserve1=_reserve1;
}
function _sqrt(uint y) internal pure returns(uint z){
if(y>3){
z=y;
uint x=y/2+1;
while(x<z){
z=x;
x=(y/x+x)/2;
}
}else if(y!=0){
z=1;
}
}
function _mint(address _to,uint _amount) private {
balanceOf[_to]+=_amount;
totalSupply+=_amount;
}
function _burn(address _from,uint _amount) private {
balanceOf[_from]-=_amount;
totalSupply-=_amount;
}
function swap(address _tokenIn,uint _amountIn) external returns(uint amountOut){
require(_amountIn>0,"Invalid Amount");
require(_tokenIn==address(token0)||_tokenIn ==address(token1),"Invalid token type");
bool isToken0=_tokenIn==address(token0);
(IERC20 _tokenIn,IERC20 tokenOut)= isToken0?(token0,token1):(token1,token0);
//定義順序
(uint reserveIn,uint reserveOut)=isToken0?(reserve0,reserve1):(reserve1,reserve0);
//轉(zhuǎn)幣到合約
_tokenIn.transferFrom(msg.sender, address(this), _amountIn);
//計算輸出的數(shù)量 注:沒有考慮手續(xù)費(fèi)
amountOut=(_amountIn*reserveOut)/(_amountIn+reserveIn);
//轉(zhuǎn)幣給用戶
tokenOut.transfer(msg.sender, amountOut);
//更新余額表
_updata(token0.balanceOf(address(this)),token0.balanceOf(address(this)));
}
function _min(uint _x,uint _y) private pure returns(uint) {
return _x>_y?_y:_x;
}
//用戶提供的是Δx,Δy,拿到的是Share
function addLiquidity(uint _amount0,uint _amount1) external returns (uint shares){
require(_amount0>0&&_amount1>0,"Invaiid amount");
//將token0、token1轉(zhuǎn)入合約
token0.transferFrom(msg.sender, address(this), _amount0);
token1.transferFrom(msg.sender, address(this), _amount1);
//計算并mint share給用戶
if(reserve0>0||reserve1>0){
require(_amount0*reserve1==_amount1*reserve0,"dy/dx != y/x");
}
if(totalSupply==0){
//沒有添加過流動性//添加過流動性
shares=_sqrt(_amount0*_amount1);
}else{
shares=_min((_amount0*totalSupply)/reserve0,(_amount1*totalSupply)/reserve1);
}
require(shares>0,"share is zero");
_mint(msg.sender, shares);
//更新余額表
_updata(token0.balanceOf(address(this)),token0.balanceOf(address(this)));
}
function removeLiquidity(uint _shares) external returns(uint _amount0,uint _amount1){
require(_shares>0,"Invalid shares");
//計算Δx,Δy數(shù)量
_amount0=(reserve0*_shares)/totalSupply;
_amount1=(reserve1*_shares)/totalSupply;
//銷毀用戶的share
_burn(msg.sender, _shares);
//將兩個幣轉(zhuǎn)回給用戶
token0.transfer(msg.sender, _amount0);
token1.transfer(msg.sender, _amount1);
//更新余額表
_updata(token0.balanceOf(address(this)),token0.balanceOf(address(this)));
}
}
操作流程
-
在remix上先部署兩個ERC20合約
地址0:0xd9145CCE52D386f254917e481eB44e9943F39138
地址1:0xd8b934580fcE35a11B58C6D73aDeE468a2833fa8 -
部署所編寫的CPAMM合約
合約地址為:0xf8e81D47203A594245E36C48e151709F0C19fBe8 -
添加流動性
注意:要確保地址有足夠的余額,沒有在構(gòu)造函數(shù)中mint的也可以部署之后mint
appreve合約地址
0approve:
1approve:addLiquidity:
4.進(jìn)行交換 swap
tokenIn是地址0文章來源:http://www.zghlxwxcb.cn/news/detail-847251.html
5.移除liquidity文章來源地址http://www.zghlxwxcb.cn/news/detail-847251.html
到了這里,關(guān)于3.恒定乘積自動做市商算法及代碼的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!