这几天我写了一个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 等,功能上也还可以再加入添加流动性、移除流动性等功能。

相关文章
|
10月前
|
存储 Rust IDE
小试牛刀-Solana合约账户详解
开发语言上,Solana合约使用Rust为主要开发语言,其次是Solana合约并不像其它链那样将数据直接存到合约里,而是使用了更加独立的账户来代币转移和存储数据。按功能可以分为以下账户
370 1
神奇的「小黄鸭调试法」
什么意思?就是说,当你被一段代码整不会了,怎么看也看不出问题出在哪里的时候。你可以对着一只小黄鸭,一行一行解释你的代码。不仅解释代码的功能,你最好还要告诉鸭子你到底想实现什么目标,以及你打算通过什么样的方法实现,为什么要这么实现等等。当你说着说着说着,哎,突然你就明白问题在哪了。就是这么的神奇。
|
开发框架 负载均衡 算法
SpringCloud微服务实战——搭建企业级开发框架(十二):OpenFeign+Ribbon实现负载均衡
Ribbon是Netflix下的负载均衡项目,它主要实现中间层应用程序的负载均衡。为Ribbon配置服务提供者地址列表后,Ribbon就会基于某种负载均衡算法,自动帮助服务调用者去请求。Ribbon默认提供的负载均衡算法有多种,例如轮询、随即、加权轮训等,也可以为Ribbon实现自定义的负载均衡算法。 Ribbon有以下特性:
885 57
SpringCloud微服务实战——搭建企业级开发框架(十二):OpenFeign+Ribbon实现负载均衡
|
Docker 容器
docker 镜像源大全
docker 镜像源大全
114272 3
docker 镜像源大全
|
Java Linux Docker
使用Dockerfile部署springboot项目 (入门级教程 简单易懂)
以前我们部署项目 打包->传到服务器->java -jar运行 而且服务器还得先配置好java运行环境
|
11天前
|
人工智能 开发工具 iOS开发
Claude Code 新手完全上手指南:安装、国产模型配置与常用命令全解
Claude Code 是一款运行在终端环境中的 AI 编程助手,能够直接在命令行中完成代码生成、项目分析、文件修改、命令执行、Git 管理等开发全流程工作。它最大的特点是**任务驱动、终端原生、轻量高效、多模型兼容**,无需图形界面、不依赖 IDE 插件,能够深度融入开发者日常工作流。
3267 9
|
3天前
|
人工智能 自然语言处理 文字识别
阿里云百炼Qwen3.7-Max简介:能力、优势、支持订阅计划参考
Qwen3.7-Max是阿里云百炼面向智能体时代推出的新一代旗舰模型,对标GPT-5.5、Claude Opus 4.7等闭源旗舰。该模型支持百万级token上下文窗口,具备顶级推理能力、多模态搜索与视觉理解增强、流式输出低延迟响应等核心优势,覆盖编程、办公、长周期自主执行等复杂场景。同时支持OpenAI接口兼容,便于系统快速迁移。用户可通过Token Plan团队或节省计划等订阅方式灵活调用,适合企业级高要求场景使用。
阿里云百炼Qwen3.7-Max简介:能力、优势、支持订阅计划参考
|
13天前
|
Shell API 开发工具
Claude Code 快速上手指南(新手友好版)
AI编程工具卷疯啦!Claude Code凭借任务驱动+终端原生的特性,成了开发者的效率搭子。本文从安装、登录、切换国产模型到常用命令,手把手带新手快速上手,全程避坑,30分钟独立用起来。
3325 23

热门文章

最新文章