去中心化交易所
第二章:去中心化交易所
“耶稣回答说:因为天国的奥秘只叫你们知道,不叫他们知道。 凡有的,还要加给他,叫他有余;凡没有的,连他所有的也要夺去。”——《马太福音》
2.1 做市
想象一下,你拥有一大片土地。而土地里只种植了一种农作物,土豆。你周围村庄的种植者也只种植土豆,换句话说你的资产是大量的土豆。你唯一的机会是在每天上午6:30(在其余的时间你要去耕作)开船去一个小岛和其他人换取你想要的东西,比如说番茄、青菜、牛奶、贝壳。但是每一次去小岛上你总是无法以合适的价格得到自己想要的东西,你希望用10颗土豆去换取20颗番茄,可是大部分番茄种植商往往会在下午登岛。你希望用20颗土豆换取200颗青菜,可是青菜种植者希望一次性成交20000颗青菜。你希望以合适的价格来换取贝壳,可是贝壳铸造商总是在随机的时间释放出贝壳。大家都很对此不满,因为人们总是希望以合适的价格在合适的时间进行快速成交。这时,出现了一位中间人,他拥有许多的贝壳和其他农作物。他在早上将自己的番茄换成你的土豆,并且在下午将土豆换给番茄种植商。他将青菜种植商的2万颗青菜一次性买入,并且将其拆成100份零售卖给有需要的人。人们愿意与他进行交易,是因为他能够及时的满足种植商的订单。而作为回报,他只收取0.3%的手续费用。这位中间人就是做市商,当然他还有另外一个名字流动性提供者。流动性很重要,尤其是在金融领域,股票、期货、证券领域都有做市商这一角色存在,他们通常是由交易所列出并且拥有证券做市牌照的人。如果你对他们的收益感兴趣可以看这里。
订单簿
中心化交易所往往采取一种叫订单簿的交易模型,“买卖双方的利益将通过订单簿呈现。订单簿实时显示特定资产的未结订单列表,体现买卖双方的动态关联。”
在很多情况下,订单簿中的限价单是由做市商放置的,用以提供流动性。也就是说,你在中心化的交易所开出一个买入或是卖出订单,必须要有一个对手去接受你的订单才能成交。
AMM
让我们回到stepn上面来,当你赚取一定的GST之后,你可能会将其换成USDC。如果你适应了中心化交易所的模式,一定会对他的swap感到好奇。
在这里,GST的价格是由谁来决定的呢?我卖出一个GST,对面会有一个人恰好想要买入一个GST吗?什么是滑点容忍度,为什么我收到的token数量和我预先计算的不同?
首先,如果你在solscan上观察你的钱包地址,你会发现STEPN内部的swap实际上是调用了solana链上的ORCA协议,ORCA协议就是一个经典的去中心化交易所。
要回答上面的问题,我们就要引出一个经典的自动做市商(AMM)模型,恒定乘积做市商。
2.2恒定乘积做市商
大部分的去中心化交易所都使用了这个机制,而这个机制最早是由Uniswap普及并且推广。通过这种方式可以将原本零散的流动性聚集起来,每一个人都可以成为流动性提供者并且在每一次交易中获得收益。
首先,记住这一个公式:X*Y=K ------(1)
我们以GST和USDC这两个代币为例,假设在Orca上面已经有相应的交易对了,在这个池子里面分别有1000个GST和3000个USDC。
在这里X=1000、Y=3000,因此 K=1000*3000=3000000;
恰好此时你有 5个gst,你需要在这个池子里换相应的USDC出来,那么在不考虑手续费的情况下公式如下:
(X+ΔX)*(Y-ΔY)= X* Y = K ------(2)
ΔY=Y-((X*Y)/(X+ΔX))=(ΔX*Y)/(X+ΔX) ------ (3)
在这里ΔX=5GST,导入公式得到 (1000+5)*(3000-ΔY)=3000000;
ΔY = 3000-(3000000/(1000+5))=14.925373 ;
你通过这个池子换取了约14.925个USDC,而此时池子里的GST数量变成了1005个,USDC数量变成了2985.07463个。
从这里你会发现,你实际换取的GST均价为ΔY/ΔX=2.9850 这和原先池子里的Y/X=3是有一些微小的价格差异的。
这一部分偏差我们称之为滑点,即预设成交价格与真实成交价格的偏差,其原因在于交易者本身的交易行为会影响池子中资产的储备变化,而储备变化导致实际交易的执行价格变化。
我们来看一个极端情况:假设你有500个GST,想要通过池子兑换。我们通过公式得出
ΔY1=3000-(3000000/(1000+500))=1000;
实际换取的GST均价为ΔY1/ΔX1=2;
在这个情况下实际的滑点就会很高,在这里我们可以得出结论即——交易量越大产生的滑点也就越大。
根据公式(3)我们可以得出实际成交单价为:(为了方便后续测算我们将成交单价展示为:ΔX/ΔY,实际上代币的相对价格是互为倒数的。但如果你以ΔY/ΔX来计算会发现实际交易滑点并不等于SlippageY的倒数)
ΔX/ΔY=(X+ΔX)/Y ------(4)
那么我们可以简单测算交易X或Y单价的的滑点即
SlippageY=ΔX/ΔY-X/Y= ΔX/Y ------(5)
SlippageX=ΔY/ΔX-X/Y=(ΔX*Y)/(X*(X+ΔX) ) ------(6)
从图像的表达上也是如此,我们画一个X*Y=3000000的反比例函数,并且在坐标: (1000,3000)上画一个切线,池子中代币的数量反映在反曲线上。
我们以刚才ΔX=500为例:每一次代币的兑换都等同于在这个曲线函数上滑动。当ΔX过大时,就产生了交易滑点。在图中(1500,2000)和(1500,1500)这两个点的距离可表示交易滑点的价差。
如果你经常在dex上面交易token你可能会有疑惑:我在交易的时候没有遇到过无常损失呀?我们为什么要画一条切线来表示价格呢?
那让我们把函数图再放大一点看看,发现了没有,在X=982到1020这两个位置反比例函数和切线函数是非常接近的,也就是说在这个区间内进行swap你的交易滑点可以忽略不记。
在数学上我们将其称之为线性近似:在这个例子中当X无限接近1000的时候,这两个函数的重合度越高,这个过程称之为切线近似。
回到刚才的问题,在现实去中心化交易所交易的过程中我们没有感受到交易滑点的原因是因为池子的深度远高于你想要swap的数量,你可以试试把K改成30亿。交易滑点的不可避免确实影响了一些用户体验,因此uniswap推出了V3 集中流动性方案。在这里我们只探讨V2的解决方案。
如何避免交易滑点和价差?
根据上面的公式我们不难得出结论,你所换取token的数量是由池子的深度(K)和池子里代币的比例决定的(X/Y),尽量选择K大的池子,不要一口气换取大量token,并且在Swap右下角选择滑点公差。
每一个dex的池子深浅不同导致在某些情况下token的价格会有差异,这也就造成了套利机会。对于新手而言选择dex聚合交易平台是一个比较好的选择,例如Jupiter Aggregater 等。
2.3 流动性挖矿
我们以orca为例,当你交换token的时候,你会发现在底下有一个0.3%的交易手续费。这个手续费就是给提供池子的做市商的奖励。在defi的世界里,每个人都可以成为流动性提供者——通过在池子里面提供两种不同数量的交易代币来实现。
通过进入pool,你可以选择不同的池子来为代币提供流动性。在这里我们可以看到你存入的代币比例和池子里面的代币比例相互对应的。假设池子里代币比例是1:3,那么你只能按照1:3的比例存入,当然有些dex也支持自动兑换功能。
对于新人来说,不要被这些高收益和惊人的APY所迷惑,我们需要了解其背后的原理和数学公式。换句话说“如果你不知道你挖的东西是什么,那么你可能就是被挖的那一个。”
接下来进入我们的数学课堂环节:
LP手续费计算
由于大部分的dex都用的是UniswapV2的模型,我们就以V2公式为例子来进行一个模拟。
根据其代码我们可以看到公式是这样的
// given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset
function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) internal pure returns (uint amountOut) {
require(amountIn > 0, 'UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT');
require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
uint amountInWithFee = amountIn.mul(997);
uint numerator = amountInWithFee.mul(reserveOut);
uint denominator = reserveIn.mul(1000).add(amountInWithFee);
amountOut = numerator / denominator;
}
amountOut=numerator / denominator
=amountInWithFee*reserveOut / (reserveIn + amountInWithFee)
他就是上文提到的公式3的变种:
ΔY=(ΔX‘*Y)/(X+ΔX’)
这里的ΔX‘=0.997*ΔX ,根据代码(uint amountInWithFee = amountIn.mul(997))
由此我们可以得知,手续费是以input token开始计算的,收取的是0.3%的手续费,所以是
ΔX‘=(1-0.003)*ΔX
我们回到最初的例子:
以GST和USDC这两个代币为例,假设在Orca上面已经有相应的交易对了,在这个池子里面分别有1000个GST和3000个USDC。
在这里X=1000、Y=3000,因此 K=1000*3000=3000000;
恰好此时你有 5个gst,你需要在这个池子里换相应的USDC出来。
在考虑手续费的情况下我们要这么计算:
ΔY‘=(ΔX‘*Y)/(X+ΔX’) =4.985*3000/(1000+4.985)=14.88081912;
在有手续费的情况下你换取14.88081912个USDC;在没有手续费的情况下你换取了14.925373个USDC。
那我能不能简单的把原先ΔY*0.997来计算呢?比如说这个例子里14.925373乘以0.997?
答案是不可以,我们在V2的白皮书里看到,因为计算的是input 所以实际的output为:
(1/(1-0.003))-1=0.3009203%,即ΔY‘=ΔY*(1-0.003009203)你可以将两个ΔY和ΔY’ 带入计算一下
那么在池子中的token 的数量 X依旧是1005个, Y变成了2985.11918个。
在这个情况下,新的K会大于旧的K。实际的手续费被存入了池子中,他并不会直接的打到流动性提供者的账户上,而是在池子中不断累积知道你移除流动性。
我们可以将这一笔交易继续下去试试会发生什么情况,在这里我默认LP token的数量为开平方根的K,实际LP的数量会根据代币本身设定的descimal有关,感兴趣的可以看看这个帖子。
实际后来添加的LPtoken 在实际环境中会稍微复杂一点,可以看这个链接了解。
在这里,我假设初始的池子不变,并且在之中进行了三次不同的交易,在考虑交易费用的情况下你会发现K实际上是在不断增加的。在0.3%的手续费中,考虑到0.05%会给协议金库本身,我按照Uniswap给的公式计算之后,再将总手续费减去协议费用,得到实际的LP手续费。
实际得到的LPtoken为: 1732.050808+0.068120058 = 1732.118928;
当你撤出池子的时候,你还需要将你的LPtoken 移除来重新获得你的两种代币,其比例是按照撤出池子的池子代币比例计算的。
在这里新的K就是 (1732.118928)^2=3000236;此时池子里的代币比例为 Y3/X3=3.011319
我们按照池子里的token比例来计算即,Xr= squareroot(3000235/3.011319)=998.1579
最后我们得到998.1579个GST 和 30005.772862 个USDC。
无常损失
接着刚才的例子,我们来计算一下无常损失。
我们一开始提供了 1000个GST 和 3000个USDC,在进行一系列的交换和算入手续费之后我们撤出池子得到了 998.1579个GST 和 30005.772862 个USDC。
在撤出时,GST的价格为 3.01132,我们新的token总价值为6011.546USDC 。
假设我们当初不打算提供流动性了,而是选择将1000个GST和3000个USDC放到钱包里不管他,按照GST3.01132的价格,我们当初token的总价值为6011.32。在这个情况下我们是获利的。
事实上只要你你放入代币对的相对价格(价格是相对的假设存在一个GMT/GST的池子)发生了变化,无常损失就一定会发生。除非你能保证一年之后你放入代币的相对价格一直稳定。而在上面的情况下,我们获得的交易费用大于所造成的无常损失。这一张图讲述了无常损失的计算。
对于新人来说,我们只要保证赚取的交易费用大于未来价格波动所造成的无常损失就行了。
2.4总结
在Crypto的世界里,我们总是能够看到层出不穷的项目,眼花缭乱的概念以及高得惊人的APY。不要被这些表象所迷惑,我们要从数学层面和代码层面去分析它、去拆解它。从而不被FOMO的情绪所掌控。
在任何一个时代,由数学公式和定律所表达的真理永远不会被任何一种意识形态所扭曲和改造。
红衣神父和他们的信徒的谩骂,不能影响傅科摆的转动。
人们烧死了哥白尼,可是地球依旧不会变成宇宙的中心。
Solana生态的Dex随着其NFT和相关gamefi的大规模采用,其交易量和活跃度越来越高。而接入Stepn的Orca已经成为了目前最活跃的去中心化交易所。正如我在前言所推论的,新的上层应用会带来大量的新人和玩家,从而给defi带来更多的用户和资产,借贷协议和DEX作为其底层会直接获益。
我认为在不遥远的未来,Dex的交易量会越来越高并且成为我们生活中不可替代的一部分,而他就建立在这些看似基础的反比例函数、泰勒一级展开等数学公式上,并且不断的更新演进。