这几天我写了一个DEX交易聚合器(2)

简介: 这几天我写了一个DEX交易聚合器

view函数的限制

刚开始的时候,我写过下面这个函数,用来获取两个代币间的所有路径,不包括三级路径。但实际上,这里面存在着一些问题。

WechatIMG66

首先,从业务逻辑上来说,tokenA > bases[i] > tokenB 路径,缺失了是否可配对的检查,应该对 tokenA-bases[i]bases[i]-tokenB 这两对分别检查 pair 是否都存在,通过 factory.getPair() 函数得到币对的 pair,如果 pair 不为零地址就说明是匹配的,如果不匹配就说明该路径是无效的。示例代码如下:

if (factory.getPair(tokenA, bases[i]) != address(0) 
    && factory.getPair(bases[i], tokenB) != address(0)) {
  paths.push([tokenA, bases[i], tokenB]);
}

其次,从 solidity 层面来说,数组的 push() 函数是不能在 view 函数中使用的,因为调用 push 函数会有固定的 gas 成本,但 view 函数是不能产生 gas 的,所以就用不了。因此,在 view 函数中使用数组只能用下标的方式进行赋值,如以下代码:

address[] memory tempPath = new address[](3);
tempPath[0] = tokenIn;
tempPath[1] = baseTokens[i];
tempPath[2] = tokenOut;

最后,返回二维数组,在默认情况下是不支持的,要用 ABIEncoderV2 才能支持,需要对合约添加以下指令才能使用:

pragma experimental ABIEncoderV2;

experimental 说明这还是实验性的,所以建议尽量少用,因为可能存在未知的 bug。

最后,我就完全抛弃了该函数,将路径的遍历和价格对比的逻辑都放在了同一个函数去完成。

链式授权转账

而我遇到的第二个错误则是关于授权转账的,也是因为我对授权转账的原理没真正理解导致的。

在我的实现中,兑换函数存在着几层不同合约之间的链式调用。假设我编写了 A 和 B 两个合约,两个合约都分别定义了 swap() 函数,在 A 合约的 swap() 函数中会调用 B 合约的 swap() 函数,而 B 合约的 swap() 函数再去调用 Uniswap 的路由合约的 swap() 函数。

A.swap() -> B.swap() -> Router.swap()

而在 Router.swap() 中会调用代币的 transferFrom() 函数将调用者 msg.sender 的代币转入 Pair 合约。所以,在兑换之前,还需要调用者对合约进行授权。一开始我以为,只要对 A 合约进行授权就可以了,当然,结果就是兑换失败了。后来,我又添加了链式授权,即调用者授权给 A,A 再授权给 B,B 再授权给 Router,但结果依然还是失败。最后,真正理解了授权转账的原理之后,只需要调用者授权给A,B 授权给 Router,并在 A 合约增加一步操作,调用代币的 transferFrom() 函数将调用者 msg.sender 的代币转入 B 合约,整个链条的兑换就能成功了。

首先,先搞清楚在整个链条中,每一步的 msg.sender 是谁?像这种合约之间的直接调用,msg.sender 都是上一步的调用者,如下图:

而在 Router.swap() 中会调用代币的 transferFrom() 函数将 msg.sender 的代币转入 Pair 合约,即是说,Router 会从 B 合约中将代币转入 Pair 合约,所以 B 合约中必须有代币才能完成转账。那 B 合约的代币从哪来呢?自然是要从 Caller 中来。只要 Caller 授权给 A,A 再用 transferFrom 将 Caller 的代币转给 B,如此就解决问题了。

总结

虽然这个 DEX 交易聚合器功能很简单,只有查询和兑换功能,但扩展起来很简单,后续还会接入 UniswapV3、Bancor、DODO 等,功能上也还可以再加入添加流动性、移除流动性等功能。

相关文章
|
3月前
|
人工智能 自然语言处理 搜索推荐
知识蒸馏(KD)深度落地:解锁GEO生成式优化轻量化高性能方案
知识蒸馏作为大模型轻量化的核心底层技术,有效解决了大模型落地贵、运行慢、部署难的痛点,让轻量化模型也能具备接近大模型的搜索语义理解与高质量内容生成能力。
|
12月前
|
存储 Rust IDE
小试牛刀-Solana合约账户详解
开发语言上,Solana合约使用Rust为主要开发语言,其次是Solana合约并不像其它链那样将数据直接存到合约里,而是使用了更加独立的账户来代币转移和存储数据。按功能可以分为以下账户
413 1
|
存储 分布式计算 资源调度
Hadoop【环境搭建 04】【hadoop-2.6.0-cdh5.15.2.tar.gz 基于ZooKeeper搭建高可用集群】(部分图片来源于网络)
【4月更文挑战第1天】Hadoop【环境搭建 04】【hadoop-2.6.0-cdh5.15.2.tar.gz 基于ZooKeeper搭建高可用集群】(部分图片来源于网络)
794 3
神奇的「小黄鸭调试法」
什么意思?就是说,当你被一段代码整不会了,怎么看也看不出问题出在哪里的时候。你可以对着一只小黄鸭,一行一行解释你的代码。不仅解释代码的功能,你最好还要告诉鸭子你到底想实现什么目标,以及你打算通过什么样的方法实现,为什么要这么实现等等。当你说着说着说着,哎,突然你就明白问题在哪了。就是这么的神奇。
|
人工智能 自然语言处理 安全
Purple AI 变革安全运营(SecOps)
SentinelOne 推出 Purple AI,将威胁搜寻变得如同向同事提问般简单。Purple AI 是一款先进的人工智能安全工具,可将自然语言问题转化为复杂查询,助力团队快速发现、响应并预防威胁。它大幅简化了安全运营流程,将威胁搜寻时间从小时缩短至分钟,同时提供全面的数据可见性和协作功能。Purple AI 以透明、开放和隐私保护为核心,赋能企业应对复杂威胁,提升整体网络安全态势。
294 0
Purple AI 变革安全运营(SecOps)
|
存储 Go 开发者
Go语言中的并发编程与通道(Channel)的深度探索
本文旨在深入探讨Go语言中并发编程的核心概念和实践,特别是通道(Channel)的使用。通过分析Goroutines和Channels的基本工作原理,我们将了解如何在Go语言中高效地实现并行任务处理。本文不仅介绍了基础语法和用法,还深入讨论了高级特性如缓冲通道、选择性接收以及超时控制等,旨在为读者提供一个全面的并发编程视角。
458 50
|
机器学习/深度学习 人工智能 自然语言处理
人工智能与ai有什么区别
本文探讨了“人工智能”与“AI”在语义、使用场景及技术侧重点上的差异,强调理解这些差异对把握技术发展的重要性。文中分析了两者的学术与通俗应用场景,并结合生成式人工智能认证项目(由培生于2024年推出),说明如何通过理论与实践结合,规避AI局限性,推动技术创新。最终呼吁在概念辨析中探索人工智能的未来潜力。
|
安全 Cloud Native 网络安全
阿里云飞天企业版PaaS平台通过等保四级能力评估
近日,阿里云飞天企业版PaaS平台(专有云平台)安全防护能力获得权威机构公安部第三研究所认可。阿里云飞天企业版PaaS平台(专有云平台)参照等级保护第四级开展安全能力建设,在近日的安全评估活动中,获得优异成绩。本次评估验证了飞天企业版PaaS平台及容器安全能力,证明阿里云可以为政企客户提供更高水平的云原生安全能力。
885 1
|
网络协议
FTP(文件传送协议)和TELNET(远程终端协议)
FTP(文件传送协议)和TELNET(远程终端协议)
895 1