🎉【Gate 3000万纪念】晒出我的Gate时刻,解锁限量好礼!
Gate用户突破3000万!这不仅是数字,更是我们共同的故事。
还记得第一次开通账号的激动,抢购成功的喜悦,或陪伴你的Gate周边吗?
📸 参与 #我的Gate时刻# ,在Gate广场晒出你的故事,一起见证下一个3000万!
✅ 参与方式:
1️⃣ 带话题 #我的Gate时刻# ,发布包含Gate元素的照片或视频
2️⃣ 搭配你的Gate故事、祝福或感言更佳
3️⃣ 分享至Twitter(X)可参与浏览量前10额外奖励
推特回链请填表单:https://www.gate.com/questionnaire/6872
🎁 独家奖励:
🏆 创意大奖(3名):Gate × F1红牛联名赛车模型一辆
👕 共创纪念奖(10名): 国际米兰同款球员卫衣
🥇 参与奖(50名):Gate 品牌抱枕
📣 分享奖(10名):Twitter前10浏览量,送Gate × 国米小夜灯!
*海外用户红牛联名赛车折合为 $200 合约体验券,国米同款球衣折合为 $50 合约体验券,国米小夜灯折合为 $30 合约体验券,品牌抱枕折合为 $20 合约体验券发放
🧠 创意提示:不限元素内容风格,晒图带有如Gate logo、Gate色彩、周边产品、GT图案、活动纪念品、活动现场图等均可参与!
活动截止于7月25日 24:00 UTC+8
3
Uniswap V3代码解析:7个实用合约开发技巧
从 Uniswap 代码中学到的合约开发小技巧
最近在编写去中心化交易所开发教程时,参考了 Uniswap V3 的代码实现,学到了许多宝贵的知识点。作为一名之前只开发过简单 NFT 合约的开发者,这次尝试 Defi 合约开发让我有了不少新的收获。相信这些小技巧对想要学习合约开发的新手会很有帮助。
接下来让我们一起来看看这些实用的开发技巧,其中有些甚至可以称得上是奇技淫巧。
可预测的合约部署地址
通常部署合约得到的是一个看似随机的地址,因为与 nonce 相关,所以合约地址难以预测。但在某些情况下,我们需要通过交易对和相关信息推断出合约地址,比如判断交易权限或获取池子地址等。
Uniswap 通过添加 salt 参数使用 CREATE2 方式创建合约,使得创建的合约地址可预测。地址生成逻辑为:新地址 = hash("0xFF",创建者地址, salt, initcode)。
善用回调函数
Solidity 中合约之间可以互相调用。在某些场景下,A 调用 B 的方法,B 在被调用的方法中回调 A 是很有用的。
Uniswap 中,调用 UniswapV3Pool 合约的 swap 方法交易时,它会回调 swapCallback,回调会传入计算出的本次交易实际需要的 Token。调用方需要在回调中将交易所需 Token 转入 UniswapV3Pool,而不是将 swap 方法拆分为两部分让调用方调用。这确保了 swap 方法的安全性,保证整个逻辑被完整执行,无需繁琐的变量记录来确保安全性。
用异常传递信息,用 try catch 实现交易预估
Uniswap 的某些合约中,将 UniswapV3Pool 的 swap 方法用 try catch 包裹执行。这是为了模拟 swap 方法来预估交易所需 Token。由于预估时不会实际产生 Token 交换,所以会报错。Uniswap 通过在交易回调函数中抛出特殊错误,然后捕获该错误,从错误信息中解析出所需信息。
这种方法看似有些取巧,但很实用。无需为预估交易需求改造 swap 方法,逻辑也更简单。
用大数解决精度问题
Uniswap 代码中有大量计算逻辑,如根据当前价格和流动性计算交换的 Token。为避免除法操作导致精度损失,计算过程中经常使用 << FixedPoint96.RESOLUTION 操作,即左移 96 位,相当于乘以 2^96。左移后再进行除法运算,在正常交易不溢出的情况下保证精度。
虽然理论上仍会有精度损失,但通常只是最小单位的损失,可以接受。
用 Share 方式计算收益
Uniswap 中需要记录 LP(流动性提供者)的手续费收益。显然不能在每次交易时都给每个 LP 记录各自的手续费,这会消耗大量 Gas。
Uniswap 的解决方案是在 Position 结构体中定义 feeGrowthInside0LastX128 和 feeGrowthInside1LastX128,记录每个头寸上次提取手续费时每个流动性应得的手续费。
简而言之,只需记录总手续费和每个流动性应分配的手续费即可。LP 提取手续费时,根据持有的流动性计算可提取的手续费。这类似于持有公司股票,提取股票收益时只需知道公司历史每股收益和上次提取时的收益。
不是所有信息都需要从链上获取
链上存储相对昂贵,并非所有信息都需要上链或从链上获取。例如,Uniswap 前端网站调用的许多接口是传统 Web2 接口。
交易池列表、交易池信息等可存储在普通数据库中,有些可能需要定期从链上同步,但无需实时调用链或节点服务提供的 RPC 接口获取相关数据。
当然,关键交易必须在链上进行。
学会合约拆分,利用已有标准合约
一个项目可能包含多个实际部署的合约。即使实际部署只有一个合约,我们也可以通过继承方式将合约拆分为多个合约来维护。
例如,Uniswap 中的某些合约继承了多个合约。在实现时,直接使用了 @openzeppelin/contracts/token/ERC721/ERC721.sol 合约,这样既方便通过 NFT 方式管理头寸,又可以利用已有标准合约提高开发效率。
总结
亲自动手开发比阅读文章更能加深理解。尝试实现一个简易版去中心化交易所的过程能让你更深入理解 Uniswap 的代码实现,也能学习到更多实际项目中的知识点。