从Uniswap代码中学到的那些合约开发技巧,比教科书有意思多了



最近在做去中心化交易所的开发教学时,深入研究了Uniswap V3的实现方式,才发现有些写法真的很妙。之前只写过简单的NFT合约,这次算是第一次认真啃DeFi合约的代码,发现了不少东西值得分享给想学习uniswap教学的朋友。

先说一个我最惊讶的——合约地址居然可以预测?

一般部署合约得到的地址看起来都是随机的,但Uniswap用了一個聪明的办法。它通过在建立合约时加入salt参数,使用CREATE2操作码,让生成的地址变成可预测的。具体做法是「pool = address(new UniswapV3Pool{salt: keccak256(abi.encode(token0, token1, fee))}());」这样的代码。这样做的好处是,只要知道交易对和手续费信息,就能推算出池子的合约地址,不用真的部署后才知道。这在验证交易权限或快速定位池子时特别有用。

回调函数的设计也挺精妙的。

在Uniswap中,当你调用swap方法进行交易时,它会回调swapCallback函数,把计算好的所需Token数量传回给你,然后你在回调里把Token转入。这样做的好处是整个交易逻辑被完整执行,不需要拆成多步或用复杂的变量记录来确保安全性。简单来说就是A调用B,B在被调用时又呼叫A,形成一个闭环。

预估交易的方法也挺Hack的——用异常来传递信息。

因为预估交易时并不会真的产生Token交换,所以会报错。Uniswap的做法是在回调中抛出特殊错误,然后用try-catch捕获,并从错误信息中解析出需要的数据。看起来有点野蛮,但确实很实用,不用特别改造swap方法就能实现预估功能。

精度问题的解决方案值得学习。

在计算交换Token时,如果直接做除法会丢失精度。Uniswap的做法是先左移96位(相当于乘以2^96),然后再做除法运算,最后在计算过程中把2^96约掉。这样在uint256的范围内既能保证精度,又不会溢位。代码中的sqrtRatioAX96和sqrtRatioBX96就是这个思路,看起来复杂但逻辑清晰。

手续费收益的计算方式也很聪明。

与其在每次交易时都给每个LP记录手续费(这样会消耗大量Gas),不如记录总手续费和每个流动性应该分配的比例。LP提取时只需要用自己持有的流动性乘以历史每股手续费就能算出收益,就像持股人按照每股收益来提取分红一样。这个Share计算方式在很多项目中都有应用。

不是所有信息都要上链这点也很重要。

交易池的清单、基本信息等完全可以存在传统数据库中,定期从链上同步就行,不需要即时调用RPC接口。现在很多区块链服务商都提供了高级接口,让你能更快速、更便宜地获取数据,这就是同样的思路。

最后是代码组织的方式。

一个复杂项目通常会把合约拆分成多個部分来维护,Uniswap就继承了很多合约。同时也要善用已有的标准合约,比如直接使用OpenZeppelin的ERC721来管理头寸,这样既能提高开发效率,也能保证代码质量。

说实话,看再多文章不如自己动手。在实现一个简易版去中心化交易所的过程中,你才能真正理解Uniswap为什么要这样设计。WTF-DApp课程就是基于这个想法,带你一步步完成uniswap教学的实战项目,相信会对你的合约开发有不少帮助。
查看原文
此页面可能包含第三方内容,仅供参考(非陈述/保证),不应被视为 Gate 认可其观点表述,也不得被视为财务或专业建议。详见声明
  • 赞赏
  • 评论
  • 转发
  • 分享
评论
请输入评论内容
请输入评论内容
暂无评论