Uniswap V2发布了许多新功能,包括:
代币:代币流动性对(代替ETH / DAI和ETH / MKR,Uniswap V2现在原生支持MKR / DAI)
内置多跳路由( ETH – gt; DAI-gt; MKR-gt; USDT,如果这是价格ETH – gt; USDT)
ERC777兼容性
累积价格预言机
在本文中,我们将讨论这种“累积价格的预言机”的工作方式,使用方式,并介绍一个Solidity库,该库可用于将预言机集成到您自己的以太坊项目中。本文假设您对Uniswap等Defi产品的市场有深入的了解。
如果您已经了解了本文的主要思想,则可以在这里找到代码示例和可靠性库:https://github.com/Keydonix/Uniswap-预言机。
尽管我们通常将预言机视为通过受信任/受约束方的交易(例如Maker Price Feed,ChainLink)将脱链信息馈入区块链的系统,但Uniswap V2 预言机不需要任何特定的交互即可提供此数据。相反,每个交换事务都将信息提供给预言机。
为了说明Uniswap V2通过此预言机新功能解决的问题,让我们首先讨论Uniswap V1的问题。
不要将Uniswap V1用作预言机
Uniswap团队从未将Uniswap V1推广为可行的链上预言机。正是由于Uniswap的简单,无许可,链上式和面向市场的功能,它吸引了有创造力的人们将其整体使用。 Uniswap V1 预言机的警报很简单:
uint256tokenPrice = token.balanceOf(UniswapMarket)/地址(UniswapMarket).balance
由于Uniswap V1市场的当前“价格”仅是代币与以太坊余额之比,因此这些项目的天然气计算非常高效且非常简单。但是问题在于它非常不安全。有许多与使用Uniswap V1作为预言机的项目有关的攻击,但最**的攻击可能是bZx / Fulcrum / Compound攻击,它在24小时内净赚近100万美元。
Uniswap V1的问题在于价格反馈是即时的,并且易于在短时间内操作。让我们看下面的伪代码示例:
send100 // ETH erandreceiesometokensUniswapMarket。 ETH ToTokenSwapInput.alue(100 ETH ER)(100 ETH ER); exploitTarget.doSom ETH ingThatUsesUniswapV1As预言机(); //将所有的代币发送回UniswapMarket.tokenTo ETH SwapInput(token.balanceOf(address(this)));
在上述攻击中,您将向流动性提供者支付少量的以太ETH (约0.6 ETH )(双向为0.3%)。但是,当调用exploitTarget时,它将认为代币比实际值要有价值得多。如果exploitTarget使用Uniswap V1 预言机来确保您存入的抵押品的价值足以提取其他代币,则系统将允许您提取比存入凭证更多的借出代币。
Uniswap V2如何像预言机一样工作
在上面的示例中,Uniswap V1的价格读取存在问题,因为它们是即时的。 V2部署了一个智能系统,用于在链上记录价格和时间数据。该方法在短时间内具有较高的运营成本,并且不可能在单个交易中进行操作。通过使用“累积”价格时间值,可以将价格的可用时间加权为一个特殊值,并且每个代币交换将花费少量天然气以保持这些值同步。
以下是Uniswap市场代码的摘要:
注意:与V1不同,V2是两个代币之间的市场。在内部,这些代币之一需要表示为代币0,另一个需要表示为代币1。他们的余额由相应的resere0和resere1跟踪。 Uniswap Docs具有有关代币排序的更多信息。
contractUniswapV2Pair {// ContractStorageVariables:uintpublicprice0CumulatieLast; uintpublicprice1CumulatieLast; … //已更新唯一的ETH esestorage变量:function_update(uintbalance0,uintbalance1,uint112_resere0,uint112_reuatie1_resere1)timelapse(price) ; price1CumulatieLast + = uint(UQ112x112.encode(_resere0).uqdi(_resere1))* timeElapsed;} blockTimestampLast =} blockTimestamp
price(0 | 1)CumulatieLast是一个独立的存储变量,它们累积“ price-time” UQ112x112使得代码有点难以阅读,但是在概念上并不重要。它仅用作高精度分割的包装。这些累积弹性值的“ 0”和“ 1”版本之间的唯一区别是价格方向。
price0CumulatieLast是“ token1中token0的价格”
price1CumulatieLast是“ token0中token1的价格”
由于此累加中的数学运算方式,price0CumulatieLast不是price1CumulatieLast的倒数。对于本文档的其余部分,我们将仅引用price0CumulatieLast,但是对这两个值也是如此。另外,price0CumulatieLast不一定在每个块上都是**的,因此您需要在市场上运行sync()或自行调整值。
price0CumulatieLast是一个值,它仅更新块上的第一个交易,获取**一个已知的resere0和resere1值(token0和token1的代币余额),计算它们的比率(价格),并在**一次按下price0CumulatieLast时,将对更新的秒进行缩放。 price0CumulatieLast是一个以每秒两次保留的速度增加的值。要将此值转换回价格,需要使用以下公式,两个时间点值price0CumulatieLast:
(price0CumulatieLATEST-price0CumulatieFIRST)/(timestampOfLATEST-timestampOfFIRST)
通过将两个样本之间的价格累计差异除以两个样本之间的秒数,可以逆转该过程,结果是该时间段内的时间加权价格。您选择的窗口是重要的安全注意事项:
两个样本之间的秒数越少,更新的时间就越多,但操作起来也更容易。
两个样本之间的秒数越长,更新时间越短,但是操作起来就越困难。
在防篡改和**之间找到适当的平衡,应该仔细考虑您的项目。
现在我们有了一个计算价格的公式,仍然存在一个问题:如何检索链上的历史价格累积信息?
使用智能合约检索历史累计值
要将V2用作链上的预言机,您需要“证明”以下各项的先验值:price0CumulatieLast及其相应的块时间戳。
检索每个值的当前值很容易(block.timstamp和UniswapMarket.price0CumulatieLast()),但是如何检索旧值?最直接的方法是部署一个智能合约,该合约在其自己的存储中记录price0CumulatieLast和时间戳的当前值,以供以后调用以作为历史值。尽管这可行,但它有一些缺点:
如果您希望将来继续提供价格供稿,则必须定期调用以存储快照值。
如果不定期调用它,则必须预先计划事务,首先存储当前值,等待一段时间,然后再启动使用历史值的事务。
您总是会激励机器人在一定程度上更新储值(机器人费用来自系统中其他地方的利润),或者要求用户发送两笔交易,一笔交易获得累计价值,从而延迟了他们想要执行的交易。重要时间(以秒为单位)以达到平均价格供稿。
如果您不希望为机器人设计经济系统,并且怀疑用户是否愿意等待发送两笔交易,那么有更好的方法将Uniswap V2用作价格来源:Merkle Patricia Proof!
使用存储证明来检索历史累计值
以太坊合约状态存储在“ Merkle Trie”中,这是一种特殊的数据结构,该结构允许一个32字节的哈希值表示每个以太坊合约中的每个存储值(带有单独的收据和交易数据尝试)。这个32字节的值称为stateRoot,每个以太坊块(您可能对块更熟悉,例如块号,块哈希和时间戳)的属性。
使用以太坊节点的ON-RPC接口,可以调用ETH _getProof来检索有效负载,当与stateRoot值结合使用时,该值可以显示在C上。A地址存储插槽B
使用链上逻辑,可以将stateRoot和存储证明结合起来以验证存储插槽的值。如果我们针对Uniswap V2市场和price0CumulatieLast的存储插槽,则可以实现所需的基于证明的历史搜索。
但是stateRoot查找不能用作EVM操作码。唯一相关的操作码是BLOCKHASH,它使用blockNumber并返回32字节的块哈希。块的blockhash是一个简单的Keccak256哈希,它具有rlp编码的所有各种属性。通过提供一个块的所有属性(包括stateRoot),我们可以通过散列并将其与链上的blockHash查找进行比较,从而验证原始块数据是否有效。验证之后,我们可以使用块的必需属性(时间戳和stateRoot)。
//注意:非功能性伪代码功能erifyBlock(parentBlock,stateRoot,blockNumber,timestamp,…)返回(bool)(bytes32_realBlockHash = blockhash(blockNumber); bytes32_proposedBlockHash = keccek256(rlpEncode(parentBlock,stateRoot,blockNumber,timestamp,…) ); return_proposedBlockHash == _ realBlockHash;}
1.上面的函数可以验证完整块的详细信息,并确认该块的所有字段正确
2.使用stateRoot(已在上面验证)解析提供的证明(通过ON-RPC getProof调用)以从块中检索历史存储值
3.从Uniswap市场获取当前价格并累积**值
4.通过将price0CumulatieLast的增量除以自验证时间戳记以来的秒数,计算提供的块(来自已验证的时间戳记)与当前价格之间的平均价格。
此时,您可以从完全基于市场动态的完全去中心化的系统中,在可配置的时间内获得平均价格。为了操纵这种价格反馈,攻击者不仅需要将价格推向一个方向,而且还需要在区块之间长时间保持价格不变,以便任何买家都有机会购买价格低廉的资产。价格,即将来临将纠正价格上涨。
注意:链上的BLOCKHASH查找仅适用于过去的256个块。当交易落在链上时,可用于存储证明的最旧块必须在**256个块之内。
介绍Uniswap 预言机库
上面的策略包括少量的客户代码(用于处理证明)和大量相当复杂的Solidity,包括YUL / assemb和Merkle Trie验证。作为Keydonix开发团队的成员,我和Micah Zoltu开发并发布了Uniswap-预言机,它是一个Solidity库,使其他智能合约可以利用此预言机功能。
要与您自己的智能合约集成,只需从基本合约Uniswap预言机.SOL(合约HelloWorld为Uniswap预言机)继承,您的合约将继承getPrice函数:
functiongetPrice(IUniswapV2PairUniswapV2Pair,地址面额代币,uint8minBlockack,uint8maxBlockack,ProofDatamemoryproofData)publiciewreturns(uint256price,uint256blockNumber)
您自己需要访问此Uniswap价格的函数将需要接收此证明数据作为此内部getPrice函数调用的参数。请参考Uniswap 预言机自述文件.me集成文档。
Uniswap-预言机库尚未经过审核。对主网有价值的任何应用程序都应经过全面审核;请确保对您的应用程序的审核还包括Uniswap的预言机代码。
————————————
原作者:Scott Bigelow
原始链接:https://medium.com/@epheph/using-Uniswap-2-预言机-with-storage-proofs-3530e699e1d3
译者:连三峰
翻译来源:http://bitoken.world
——————————————
通过绘制下面的QR码添加我,并将您拖到技术交流组中
扫描码
跟着我们
获得
文章标题:Uniswap V2 Oracle与链上累积价格存储证明的结合
文章链接:https://www.btchangqing.cn/49650.html
更新时间:2020年07月06日
本站大部分内容均收集于网络,若内容若侵犯到您的权益,请联系我们,我们将第一时间处理。