围炉网

一行代码,一篇日志,一个梦想,一个世界

uniswap代码阅读笔记

  • 核心合约

    • uniswap核心合约分为3个合约,工厂合约,配对合约,ERC20合约

    • 工厂合约

      • UniswapV2Factory工厂合约负责部署UniswapV2Pair配对合约,核心合约部署时只需要部署工厂合约(配对合约由工厂合约创建)

      • 配对合约继承UniswapV2ERC20合约,为了生成流动性token

      • 工厂合约部署时构造参数只需要设置一个手续费管理员(可以打开收费开关)

        • 合约中手续费管理员地址以及收费地址都可以由管理员进行设置

    • 在交易所中进行交易操作顺序:创建交易对,添加流动性,交易

    • 添加流动性时路由合约会判断这个交易对是否存在,如果存在则直接把带来的token补充进流动性。

    • 配对合约

      • 配对合约有三个主要方法:铸造方法,销毁方法,交换方法。

      • 为了防止可能更新配对合约余额的定制令牌实现并更优雅地处理总供应量大于2^112的令牌,还有两个辅助方法:强制平衡以匹配储备,强制准备金与余额匹配。

      • 添加配对时需要提供两个token的地址,随后工厂合约会为这个交易对部署一个新的配对合约,配对合约通过create2进行部署

      • 两个token地址按二进制大小排序后一起进行hash,以这个hash值作为create2的salt进行部署

        • create函数部署出来的地址是以nonce值来确定的

        • create2部署以salt作为参数来确定,salt和code固定就可以预测部署出来的地址。这样下次调用时只需要拿两个token就可以算出配对合约的地址

        • create2是opcode,需要采用内联汇编的方式调用

      • 用户可以将两个token存入配对合约,然后在配对合约中生成一种兼容ERC20的token

      • 配对合约中的token可以成为流动性,用户可以将自己的流动性余额兑换成配对合约中的任何一种token

      • 用户可以取出流动性,配对合约将销毁流动性,并将两种token同时返还给用户

      • 返还的数量将根据流动性数量和两种token的储备量重新计算,如果有手续费收益,用户也将得到收益

      • 用户可以通过一种token交换另一种token,配对合约将扣除千分之三的手续费

  • 路由合约

    • 在uniswap核心合约的基础上,路由合约用来更好的操作核心合约

    • 路由合约有三部分操作方法:添加流动性,移除流动性,交换

    • 虽然配对合约已经可以完成所有交易操作,但路由合约将所有操作配合,配合前端更好的完成交易。比如用100个weth换dai可以换到多少就由路由合约计算

    • 因为路由合约代码量大,部署时会超过gas限制,所以路由合约被分为两个部分,功能互相补充

    • 可以通过这个URL查看路由合约:https://cn.etherscan.com/address/0x7a250d5630b4cf539739df2c5dacb4c659f2488d#code

  • Uniswap流动性

    • Uniswap最初铸造的份额等于所存金额的几何平均值。这确保任何时候的流动资金池份额的值基本上与最初存入流动资金的比率无关。

    • Uniswap公式确保了流动性池的份额的价值永远不会低于该池中储备的几何平均值。但是,流动资金池份额的可能随着时间的推移而增长(交易费的累计)。理论上讲,可能导致最小数量的流动性池份额(1e-18)的价值过高,以至于小型流动性提供者无法提供任何流动性。为了缓解这种情况,uniswap刻意创建了第一个1e-15池份额(池份额最小数量的1000倍),然后将他们发送到零地址,而不是发送到铸造者。对于所有的令牌对来说,这是微不足道的成本。但是这大大增加了攻击成本:为了将流动资金池份额的价值提高到100美元,攻击者需要向该池捐赠100,000美元,该资金将永久锁定为流动资金。

    • Uniswap v1作为链上价格预言并不安全,因为它非常容易被操纵。假设有其他合约使用当前的ETH-DAI价格来结算衍生品。希望操纵测算价格的攻击者可以从ETH-DAI对中买入ETH,触发衍生合约的结算(使其基于虚高的价格进行结算),然后将ETH卖回该对,将其交易回归真实价格。这甚至可以以原子交易的方式进行,或者由矿工控制区块内的交易顺序。

    • Uniswap v2通过测量和记录每个区块第一次交易前(或者等价于上一个区块的最后一次交易后)的价格,改进了这一预言功能。这个价格比一个区块中的价格更难操纵。如果攻击者提交了一笔交易,试图在一个区块结束时操纵价格,其他一些套利者可能会在同一区块中提交另一笔交易,紧接着再进行交易。矿工(或使用足够燃料填满整个区块的攻击者)可以在一个区块结束时操纵价格,但除非他们也在下一个区块进行开采,否则他们可能在套利交易中没有特别的优势

    • 预言机的用户可以选择何时开始和结束这个时期。选择一个较长的时期,虽然会导致价格不那么最新,但攻击者操纵TWAP的成本会更高。

    • 一个复杂的问题是:我们应该用资产B来衡量资产A的价格,还是用资产A来衡量资产B的价格?虽然以B为单位的A的现货价格总是以A为单位的B的现货价格的倒数,但以B为单位的资产A在特定时间段内的平均价格并不等于以A为单位的资产B的平均价格的倒数。例如,如果第1区块的USD/ETH价格是100,第2区块的USD/ETH价格是300,那么USD/ETH的平均价格将是200 USD/ETH,但ETH/USD的平均价格将是1/150 ETH/USD。由于合约无法知道用户想用这两种资产中的哪一种作为记账单位,所以Uniswap v2会跟踪两种价格。

    • 另一个复杂的问题是,有人有可能在不与合约互动的情况下,向配对合约发送资产–从而改变其余额和边际价格,从而不触发预言更新。如果合约只是简单地检查自己的余额,并根据当前的价格更新预言,那么攻击者就可以通过在一个区块中第一次调用合约之前立即向合约发送资产来操纵预言。如果最后一次交易是在一个时间戳为X秒前的区块中进行的,那么合约就会在累积新价格之前错误地将其乘以X,即使没有人有机会在该价格进行交易。为了防止这种情况发生,核心合约在每次交互后都会缓存其储备,并使用从缓存储备中得出的价格而不是当前储备更新预言。

  • 闪电贷

    • 在Uniswap v1中,用户用XYZ购买ABC时,需要先将XYZ发送到合约中,然后才能收到ABC。如果该用户需要他们购买的ABC来获得他们支付的XYZ,这就不方便了。例如,该用户可能会使用该ABC在其他合约中购买XYZ,以便从Uniswap套利价格差,或者他们可能会通过出售抵押品来偿还Uniswap来平仓Maker或Compound。

    • Uniswap v2增加了一个新的功能,允许用户在支付资产之前接收和使用该资产,只要他们在同一个原子交易中进行支付即可。swap函数在转出用户请求的代币和执行不变性之间调用一个用户指定的可选回调合约。一旦回调完成,合约就会检查新的余额,并确认不变性得到满足(在调整了支付金额的费用后)。如果合约没有足够的资金,就会还原整个交易。

    • 用户也可以使用相同的代币来偿还Uniswap池,而不是完成交换。这实际上等于让任何人可以闪借Uniswap池中存储的任何资产(与Uniswap对交易收取0.30%的费用一样)

  • 元交易

    • Uniswap v2对所铸成的池子份额原生支持元交易,这意味着用户可以通过签名授权转让他们的池子份额 ,而不是从他们的地址进行链上交易。任何人都可以通过调用许可功能代表用户提交此签名,支付燃气费,并可能在同一交易中进行其他操作。

  • 最大代币余额

    • 为了有效地实现预言机制,Uniswap v2只支持高达2^112-1的储备余额。这个数字足以支持18个小数点位的代币,总供应量超过1万亿。

    • 如果储备金余额超过2^112-1,任何对swap()函数的调用都将失败(由于_update()函数的检查)。为了从这种情况中恢复过来,任何用户都可以调用skim()函数从流动性池中移除多余的资产。

    • 为了防止定制的代币实现能够更新配对合约的余额,并更优雅地处理总供应量可能大于2^112的代币,Uniswap v2有两个保底函数:sync()和skim()。

    • sync()在代币异步降低交易对的余额的情况下,作为一种恢复机制发挥作用。在这种情况下,交易将获得次优的利率,如果没有流动性提供者愿意纠正这种情况,该货币对就会被卡住。sync()的存在是为了将合约的储备金设置为当前的余额,为这种情况提供某种程度上的宽松恢复。

    • skim()的功能是作为一个恢复机制,当有足够的代币被发送到一个货币对,溢出两个uint112存储槽的储备,否则会导致交易失败。如果当前余额和2^112-1之间的差额大于0,则shim()函数允许用户把这些差额交给调用者。

  • PS

    • 以太坊智能合约开源

      • 部署到以太坊网络上的是bytecode, 是EVM可以执行的代码

      • 通过编译可以取得智能合约的ABI,如果把ABI也部署上去那么其他人可以根据ABI描述的方法名等信息进行合约调用

      • 如果把源代码也提交到浏览器中,浏览器根据源代码编译,如果编译后的bytecode与部署到网络上的bytecode一样,才叫做智能合约的开源

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注

沪ICP备15009335号-2